Skip to content
Permalink
Browse files

[pal] Improved test for candidate against polygon obstacles

Previous test was just checking point in polygon for the corner,
mid points and center. This test was not sufficient for narrow
or small polygons which were not covered by these points
but were still covering parts of the label candidate.

Now, the area of the intersection between the obstacle polygon
and the label candidate is used to calculate the obstacle
cost.
  • Loading branch information
nyalldawson committed Aug 31, 2015
1 parent fe3e07e commit f9fa9793f0f505fc03f8d18ddc5f96d76b9bdf33
@@ -61,7 +61,7 @@ namespace pal
{
case PolygonInterior:
// n ranges from 0 -> 12
n = lp->getNumPointsInPolygon( obstacle );
n = lp->polygonIntersectionCost( obstacle );
break;
case PolygonBoundary:
// penalty may need tweaking, given that interior mode ranges up to 12
@@ -574,36 +574,52 @@ namespace pal
return false;
}

int LabelPosition::getNumPointsInPolygon( PointSet *polygon ) const
int LabelPosition::polygonIntersectionCost( PointSet *polygon ) const
{
int a, k, count = 0;
double px, py;
if ( !mGeos )
createGeosGeom();

if ( !polygon->mGeos )
polygon->createGeosGeom();

// check each corner
for ( k = 0; k < 4; k++ )
GEOSContextHandle_t geosctxt = geosContext();

int cost = 0;
//check the label center. if covered by polygon, initial cost of 4
if ( polygon->containsPoint(( x[0] + x[2] ) / 2.0, ( y[0] + y[2] ) / 2.0 ) )
cost += 4;

try
{
px = x[k];
py = y[k];
//calculate proportion of label candidate which is covered by polygon
GEOSGeometry* intersectionGeom = GEOSIntersection_r( geosctxt, mGeos, polygon->mGeos );
if ( !intersectionGeom )
return cost;

for ( a = 0; a < 2; a++ ) // and each middle of segment
double positionArea = 0;
if ( GEOSArea_r( geosctxt, mGeos, &positionArea ) != 1 )
{
if ( polygon->containsPoint( px, py ) )
count++;
px = ( x[k] + x[( k+1 ) %4] ) / 2.0;
py = ( y[k] + y[( k+1 ) %4] ) / 2.0;
GEOSGeom_destroy_r( geosctxt, intersectionGeom );
return cost;
}
}

px = ( x[0] + x[2] ) / 2.0;
py = ( y[0] + y[2] ) / 2.0;

// and the label center
if ( polygon->containsPoint( px, py ) )
count += 4; // virtually 4 points
double intersectionArea = 0;
if ( GEOSArea_r( geosctxt, intersectionGeom, &intersectionArea ) != 1 )
{
intersectionArea = 0;
}

// TODO: count with nextFeature
GEOSGeom_destroy_r( geosctxt, intersectionGeom );

return count;
double portionCovered = intersectionArea / positionArea;
cost += ceil( portionCovered * 8.0 ); //cost of 8 if totally covered
return cost;
}
catch ( GEOSException &e )
{
QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
return cost;
}
}

} // end namespace
@@ -133,8 +133,10 @@ namespace pal
/** Returns true if this label crosses the boundary of the specified polygon */
bool crossesBoundary( PointSet* polygon ) const;

/** Returns number of intersections with polygon (testing border and center) */
int getNumPointsInPolygon( PointSet* polygon ) const;
/** Returns cost of position intersection with polygon (testing area of intersection and center).
* Cost ranges between 0 and 12, with extra cost if center of label position is covered.
*/
int polygonIntersectionCost( PointSet* polygon ) const;

/** Shift the label by specified offset */
void offsetPosition( double xOffset, double yOffset );
@@ -123,6 +123,16 @@ def test_point_placement_around_obstacle(self):
self.removeMapLayer(self.layer)
self.layer = None

def test_point_placement_narrow_polygon_obstacle(self):
# Default point label placement with narrow polygon obstacle
self.layer = TestQgsPalLabeling.loadFeatureLayer('point')
polyLayer = TestQgsPalLabeling.loadFeatureLayer('narrow_polygon')
self._TestMapSettings = self.cloneMapSettings(self._MapSettings)
self.checkTest()
self.removeMapLayer(self.layer)
self.removeMapLayer(polyLayer)
self.layer = None

if __name__ == '__main__':
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
# SEE: test_qgspallabeling_tests.suiteTests() to define suite
Binary file not shown.
Binary file not shown.

0 comments on commit f9fa979

Please sign in to comment.
You can’t perform that action at this time.