2121#include " main.hpp"
2222#include " options.hpp"
2323#include " errors.hpp"
24+ #include " projection.hpp"
2425
2526static int clip (double *x0, double *y0, double *x1, double *y1, double xmin, double ymin, double xmax, double ymax);
2627
@@ -1481,25 +1482,117 @@ draw centerOfMass(const drawvec &dv, size_t start, size_t end, draw centre) {
14811482 }
14821483}
14831484
1484- double label_goodness (const drawvec &dv, size_t start, size_t count, long long x, long long y) {
1485- if (!pnpoly (dv, start, count, x, y)) {
1485+ double label_goodness (const drawvec &dv, long long x, long long y) {
1486+ int nesting = 0 ;
1487+
1488+ for (size_t i = 0 ; i < dv.size (); i++) {
1489+ if (dv[i].op == VT_MOVETO ) {
1490+ size_t j;
1491+ for (j = i + 1 ; j < dv.size (); j++) {
1492+ if (dv[j].op != VT_LINETO ) {
1493+ break ;
1494+ }
1495+ }
1496+
1497+ // if it's inside the ring, and it's an outer ring,
1498+ // we are nested more; if it's an inner ring, we are
1499+ // nested less.
1500+ if (pnpoly (dv, i, j - i, x, y)) {
1501+ if (get_area (dv, i, j) >= 0 ) {
1502+ nesting++;
1503+ } else {
1504+ nesting--;
1505+ }
1506+ }
1507+
1508+ i = j - 1 ;
1509+ }
1510+ }
1511+
1512+ if (nesting < 1 ) {
14861513 return 0 ; // outside the polygon is as bad as it gets
14871514 }
14881515
14891516 double closest = INFINITY ; // square of closest distance to the border
14901517
1491- for (size_t i = start ; i < start + count ; i++) {
1518+ for (size_t i = 0 ; i < dv. size () ; i++) {
14921519 double dx = dv[i].x - x;
14931520 double dy = dv[i].y - y;
14941521 double squared = dx * dx + dy * dy;
14951522 if (squared < closest) {
14961523 closest = squared;
14971524 }
1525+
1526+ if (i > 0 && dv[i].op == VT_LINETO ) {
1527+ squared = square_distance_from_line (x, y, dv[i - 1 ].x , dv[i - 1 ].y , dv[i].x , dv[i].y );
1528+ if (squared < closest) {
1529+ closest = squared;
1530+ }
1531+ }
14981532 }
14991533
15001534 return sqrt (closest);
15011535}
15021536
1537+ struct sorty {
1538+ long long x;
1539+ long long y;
1540+ };
1541+
1542+ struct sorty_sorter {
1543+ int kind;
1544+ sorty_sorter (int k) : kind(k) {};
1545+
1546+ bool operator ()(const sorty &a, const sorty &b) const {
1547+ long long xa, ya, xb, yb;
1548+
1549+ if (kind == 0 ) { // Y first
1550+ xa = a.x ;
1551+ ya = a.y ;
1552+
1553+ xb = b.x ;
1554+ yb = b.y ;
1555+ } else if (kind == 1 ) { // X first
1556+ xa = a.y ;
1557+ ya = a.x ;
1558+
1559+ xb = b.y ;
1560+ yb = b.x ;
1561+ } else if (kind == 2 ) { // diagonal
1562+ xa = a.x + a.y ;
1563+ ya = a.x - a.y ;
1564+
1565+ xb = b.x + b.y ;
1566+ yb = b.x - b.y ;
1567+ } else { // other diagonal
1568+ xa = a.x - a.y ;
1569+ ya = a.x + a.y ;
1570+
1571+ xb = b.x - b.y ;
1572+ yb = b.x + b.y ;
1573+ }
1574+
1575+ if (ya < yb) {
1576+ return true ;
1577+ } else if (ya == yb && xa < xb) {
1578+ return true ;
1579+ } else {
1580+ return false ;
1581+ }
1582+ };
1583+ };
1584+
1585+ struct candidate {
1586+ long long x;
1587+ long long y;
1588+ double dist;
1589+
1590+ bool operator <(const candidate &c) const {
1591+ // largest distance sorts first
1592+ return dist > c.dist ;
1593+ };
1594+ };
1595+
15031596// Generate a label point for a polygon feature.
15041597//
15051598// A good label point will be near the center of the feature and far from any border.
@@ -1521,6 +1614,10 @@ double label_goodness(const drawvec &dv, size_t start, size_t count, long long x
15211614drawvec polygon_to_anchor (const drawvec &geom) {
15221615 size_t start = 0 , end = 0 ;
15231616 size_t best_area = 0 ;
1617+ std::vector<sorty> points;
1618+
1619+ // find the largest outer ring, which will be the best thing
1620+ // to label if we can do it.
15241621
15251622 for (size_t i = 0 ; i < geom.size (); i++) {
15261623 if (geom[i].op == VT_MOVETO ) {
@@ -1529,6 +1626,11 @@ drawvec polygon_to_anchor(const drawvec &geom) {
15291626 if (geom[j].op != VT_LINETO ) {
15301627 break ;
15311628 }
1629+
1630+ sorty sy;
1631+ sy.x = geom[j].x ;
1632+ sy.y = geom[j].y ;
1633+ points.push_back (sy);
15321634 }
15331635
15341636 double area = get_area (geom, i, j);
@@ -1542,13 +1644,15 @@ drawvec polygon_to_anchor(const drawvec &geom) {
15421644 }
15431645 }
15441646
1647+ // If there are no outer rings, don't generate a label point
1648+
15451649 if (best_area > 0 ) {
15461650 long long xsum = 0 ;
15471651 long long ysum = 0 ;
15481652 size_t count = 0 ;
15491653 long long xmin = LLONG_MAX , ymin = LLONG_MAX , xmax = LLONG_MIN , ymax = LLONG_MIN ;
15501654
1551- // Calculate centroid and bounding box.
1655+ // Calculate centroid and bounding box of biggest ring .
15521656 // start + 1 to exclude the first point, which is duplicated as the last
15531657 for (size_t k = start + 1 ; k < end; k++) {
15541658 xsum += geom[k].x ;
@@ -1562,17 +1666,79 @@ drawvec polygon_to_anchor(const drawvec &geom) {
15621666 }
15631667
15641668 if (count > 0 ) {
1565- draw centroid (VT_MOVETO , xsum / count, ysum / count);
1566- draw d = centerOfMass (geom, start, end, centroid);
1669+ // We want label points that are at least a moderate distance from
1670+ // the edge of the feature. The threshold for what is too close
1671+ // is derived from the area of the feature.
15671672
15681673 double radius = sqrt (best_area / M_PI );
15691674 double goodness_threshold = radius / 5 ;
15701675
1571- double goodness = label_goodness (geom, start, end - start - 1 , d.x , d.y );
1676+ // First choice: Turf's center of mass.
1677+
1678+ draw centroid (VT_MOVETO , xsum / count, ysum / count);
1679+ draw d = centerOfMass (geom, start, end, centroid);
1680+ double goodness = label_goodness (geom, d.x , d.y );
1681+ const char *kind = " mass" ;
1682+
15721683 if (goodness < goodness_threshold) {
15731684 // Label is too close to the border or outside it,
1574- // so try some other possible points
1685+ // so try some other possible points. Sort the vertices
1686+ // both by Y and X coordinate and then by diagonals,
1687+ // and walk through each set
1688+ // in sorted order. Adjacent pairs of coordinates should
1689+ // tend to bounce back and forth between rings, so the
1690+ // midpoint of each pair will hopefully be somewhere in the
1691+ // interior of the polygon.
1692+
1693+ std::vector<candidate> candidates;
15751694
1695+ for (size_t pass = 0 ; pass < 4 ; pass++) {
1696+ std::sort (points.begin (), points.end (), sorty_sorter (pass));
1697+
1698+ for (size_t i = 1 ; i < points.size (); i++) {
1699+ double dx = points[i].x - points[i - 1 ].x ;
1700+ double dy = points[i].y - points[i - 1 ].y ;
1701+
1702+ double dist = sqrt (dx * dx + dy * dy);
1703+ if (dist > 2 * goodness_threshold) {
1704+ candidate c;
1705+
1706+ c.x = (points[i].x + points[i - 1 ].x ) / 2 ;
1707+ c.y = (points[i].y + points[i - 1 ].y ) / 2 ;
1708+ c.dist = dist;
1709+
1710+ candidates.push_back (c);
1711+ }
1712+ }
1713+ }
1714+
1715+ // Now sort the accumulate list of segment midpoints by the lengths
1716+ // of the segments. Starting from the longest
1717+ // segment, if we find one whose midpoint is inside the polygon and
1718+ // far enough from any edge to be good enough, stop looking.
1719+
1720+ std::sort (candidates.begin (), candidates.end ());
1721+ // only check the top 50 stride midpoints, since this list can be quite large
1722+ for (size_t i = 0 ; i < candidates.size () && i < 50 ; i++) {
1723+ double maybe_goodness = label_goodness (geom, candidates[i].x , candidates[i].y );
1724+
1725+ if (maybe_goodness > goodness) {
1726+ d.x = candidates[i].x ;
1727+ d.y = candidates[i].y ;
1728+
1729+ goodness = maybe_goodness;
1730+ kind = " diagonal" ;
1731+ if (goodness > goodness_threshold) {
1732+ break ;
1733+ }
1734+ }
1735+ }
1736+ }
1737+
1738+ // We may still not have anything decent, so the next thing to look at
1739+ // is points from gridding the bounding box of the largest ring.
1740+
1741+ if (goodness < goodness_threshold) {
15761742 for (long long sub = 2 ;
15771743 sub < 32 && (xmax - xmin) > 2 * sub && (ymax - ymin) > 2 * sub;
15781744 sub *= 2 ) {
@@ -1582,11 +1748,12 @@ drawvec polygon_to_anchor(const drawvec &geom) {
15821748 xmin + x * (xmax - xmin) / sub,
15831749 ymin + y * (ymax - ymin) / sub);
15841750
1585- double maybe_goodness = label_goodness (geom, start, end - start, maybe.x , maybe.y );
1751+ double maybe_goodness = label_goodness (geom, maybe.x , maybe.y );
15861752 if (maybe_goodness > goodness) {
15871753 // better than the previous
15881754 d = maybe;
15891755 goodness = maybe_goodness;
1756+ kind = " grid" ;
15901757 }
15911758 }
15921759 }
@@ -1598,8 +1765,17 @@ drawvec polygon_to_anchor(const drawvec &geom) {
15981765
15991766 // There is nothing really good. Is the centroid maybe better?
16001767 // If not, we're stuck with whatever the best we found was.
1601- if (label_goodness (geom, start, end - start, centroid.x , centroid.y ) > goodness) {
1768+ double maybe_goodness = label_goodness (geom, centroid.x , centroid.y );
1769+ if (maybe_goodness > goodness) {
16021770 d = centroid;
1771+ goodness = maybe_goodness;
1772+ kind = " centroid" ;
1773+ }
1774+
1775+ if (goodness <= 0 ) {
1776+ double lon, lat;
1777+ tile2lonlat (d.x , d.y , 32 , &lon, &lat);
1778+ fprintf (stderr, " could not find label point: %s %f,%f\n " , kind, lat, lon);
16031779 }
16041780 }
16051781
@@ -1676,31 +1852,18 @@ drawvec checkerboard_anchors(drawvec const &geom, int tx, int ty, int z, unsigne
16761852 continue ;
16771853 }
16781854
1679- for (size_t a = 0 ; a < geom.size (); a++) {
1680- if (geom[a].op == VT_MOVETO ) {
1681- size_t b;
1682- for (b = a + 1 ; b < geom.size (); b++) {
1683- if (geom[b].op != VT_LINETO ) {
1684- break ;
1685- }
1686- }
1687-
1688- // If it's the central label, it's the best we've got,
1689- // so accept it in any case. If it's from the outer spiral,
1690- // don't use it if it's too close to a border.
1691- if (lx == 0 && ly == 0 ) {
1692- out.push_back (draw (VT_MOVETO , x - tx1, y - ty1));
1693- break ;
1694- } else {
1695- double tilesize = 1LL << (32 - z);
1696- double goodness_threshold = tilesize / 100 ;
1697- if (label_goodness (geom, a, b - a, x - tx1, y - ty1) > goodness_threshold) {
1698- out.push_back (draw (VT_MOVETO , x - tx1, y - ty1));
1699- break ;
1700- }
1701- }
1702-
1703- a = b - 1 ;
1855+ // If it's the central label, it's the best we've got,
1856+ // so accept it in any case. If it's from the outer spiral,
1857+ // don't use it if it's too close to a border.
1858+ if (lx == 0 && ly == 0 ) {
1859+ out.push_back (draw (VT_MOVETO , x - tx1, y - ty1));
1860+ break ;
1861+ } else {
1862+ double tilesize = 1LL << (32 - z);
1863+ double goodness_threshold = tilesize / 100 ;
1864+ if (label_goodness (geom, x - tx1, y - ty1) > goodness_threshold) {
1865+ out.push_back (draw (VT_MOVETO , x - tx1, y - ty1));
1866+ break ;
17041867 }
17051868 }
17061869 }
0 commit comments