[pal] Use GEOS methods for label position conflict tests

This commit is contained in:
Nyall Dawson 2015-07-21 16:00:00 +10:00
parent eaddba62bf
commit 268feb5c16
6 changed files with 69 additions and 120 deletions

View File

@ -42,6 +42,8 @@ namespace pal
if ( dist < 0 )
n = 2;
else if ( dist < distlabel )
//note this never happens at the moment - points are not obstacles if they don't fall
//within the label
n = 1;
else
n = 0;

View File

@ -1381,13 +1381,11 @@ namespace pal
return false;
}
if ( mOwnsGeom ) // delete old geometry if we own it
GEOSGeom_destroy_r( ctxt, mGeos );
GEOSPreparedGeom_destroy_r( ctxt, mPreparedGeom );
invalidateGeos();
// set up new geometry
mGeos = gTmp;
mOwnsGeom = true;
mPreparedGeom = 0;
deleteCoords();
qDeleteAll( mHoles );

View File

@ -48,7 +48,8 @@
namespace pal
{
LabelPosition::LabelPosition( int id, double x1, double y1, double w, double h, double alpha, double cost, FeaturePart *feature, bool isReversed, Quadrant quadrant )
: id( id )
: PointSet()
, id( id )
, cost( cost )
, feature( feature )
, probFeat( 0 )
@ -62,6 +63,10 @@ namespace pal
, upsideDown( false )
, quadrant( quadrant )
{
type = GEOS_POLYGON;
nbPoints = 4;
x = new double[nbPoints];
y = new double[nbPoints];
// alpha take his value bw 0 and 2*pi rad
while ( this->alpha > 2*M_PI )
@ -148,9 +153,18 @@ namespace pal
upsideDown = true;
}
}
for ( int i = 0; i < nbPoints; ++i )
{
xmin = qMin( xmin, x[i] );
xmax = qMax( xmax, x[i] );
ymin = qMin( ymin, y[i] );
ymax = qMax( ymax, y[i] );
}
}
LabelPosition::LabelPosition( const LabelPosition& other )
: PointSet( other )
{
id = other.id;
cost = other.cost;
@ -250,39 +264,15 @@ namespace pal
bool LabelPosition::isInConflictSinglePart( LabelPosition* lp )
{
// TODO: add bounding box test to possibly avoid cross product calculation
if ( !mGeos )
createGeosGeom();
int i, i2, j;
int d1, d2;
double cp1, cp2;
if ( !lp->mGeos )
lp->createGeosGeom();
for ( i = 0; i < 4; i++ )
{
i2 = ( i + 1 ) % 4;
d1 = -1;
d2 = -1;
for ( j = 0; j < 4; j++ )
{
cp1 = cross_product( x[i], y[i], x[i2], y[i2], lp->x[j], lp->y[j] );
if ( cp1 > 0 )
{
d1 = 1;
}
cp2 = cross_product( lp->x[i], lp->y[i],
lp->x[i2], lp->y[i2],
x[j], y[j] );
if ( cp2 > 0 )
{
d2 = 1;
}
}
if ( d1 == -1 || d2 == -1 ) // disjoint
return false;
}
return true;
GEOSContextHandle_t geosctxt = geosContext();
bool result = ( GEOSPreparedIntersects_r( geosctxt, preparedGeom(), lp->mGeos ) == 1 );
return result;
}
bool LabelPosition::isInConflictMultiPart( LabelPosition* lp )
@ -315,8 +305,9 @@ namespace pal
if ( nextPart )
nextPart->offsetPosition( xOffset, yOffset );
}
invalidateGeos();
}
int LabelPosition::getId() const
{
@ -500,88 +491,35 @@ namespace pal
return true;
}
double LabelPosition::getDistanceToPoint( double xp, double yp )
double LabelPosition::getDistanceToPoint( double xp, double yp ) const
{
int i;
int j;
//first check if inside, if so then distance is -1
double distance = ( containsPoint( xp, yp ) ? -1
: sqrt( minDistanceToPoint( xp, yp ) ) );
double mx[4];
double my[4];
if ( nextPart && distance > 0 )
return qMin( distance, nextPart->getDistanceToPoint( xp, yp ) );
double dist_min = DBL_MAX;
double dist;
for ( i = 0; i < 4; i++ )
{
j = ( i + 1 ) % 4;
mx[i] = ( x[i] + x[j] ) / 2.0;
my[i] = ( y[i] + y[j] ) / 2.0;
return distance;
}
if ( qAbs( cross_product( mx[0], my[0], mx[2], my[2], xp, yp ) / h ) < w / 2 )
bool LabelPosition::isBorderCrossingLine( PointSet* line ) const
{
dist = cross_product( x[1], y[1], x[0], y[0], xp, yp ) / w;
if ( qAbs( dist ) < qAbs( dist_min ) )
dist_min = dist;
if ( !mGeos )
createGeosGeom();
dist = cross_product( x[3], y[3], x[2], y[2], xp, yp ) / w;
if ( qAbs( dist ) < qAbs( dist_min ) )
dist_min = dist;
}
if ( !line->mGeos )
line->createGeosGeom();
if ( qAbs( cross_product( mx[1], my[1], mx[3], my[3], xp, yp ) / w ) < h / 2 )
GEOSContextHandle_t geosctxt = geosContext();
if ( GEOSPreparedIntersects_r( geosctxt, preparedGeom(), line->mGeos ) == 1 )
{
dist = cross_product( x[2], y[2], x[1], y[1], xp, yp ) / h;
if ( qAbs( dist ) < qAbs( dist_min ) )
dist_min = dist;
dist = cross_product( x[0], y[0], x[3], y[3], xp, yp ) / h;
if ( qAbs( dist ) < qAbs( dist_min ) )
dist_min = dist;
}
for ( i = 0; i < 4; i++ )
{
dist = dist_euc2d( x[i], y[i], xp, yp );
if ( qAbs( dist ) < qAbs( dist_min ) )
dist_min = dist;
}
if ( nextPart && dist_min > 0 )
return qMin( dist_min, nextPart->getDistanceToPoint( xp, yp ) );
return dist_min;
}
bool LabelPosition::isBorderCrossingLine( PointSet* feat )
{
double ca, cb;
for ( int i = 0; i < 4; i++ )
{
for ( int j = 0; j < feat->getNumPoints() - 1; j++ )
{
ca = cross_product( x[i], y[i], x[( i+1 ) %4], y[( i+1 ) %4],
feat->x[j], feat->y[j] );
cb = cross_product( x[i], y[i], x[( i+1 ) %4], y[( i+1 ) %4],
feat->x[j+1], feat->y[j+1] );
if (( ca < 0 && cb > 0 ) || ( ca > 0 && cb < 0 ) )
{
ca = cross_product( feat->x[j], feat->y[j], feat->x[j+1], feat->y[j+1],
x[i], y[i] );
cb = cross_product( feat->x[j], feat->y[j], feat->x[j+1], feat->y[j+1],
x[( i+1 ) %4], y[( i+1 ) %4] );
if (( ca < 0 && cb > 0 ) || ( ca > 0 && cb < 0 ) )
return true;
}
else if ( nextPart )
{
return nextPart->isBorderCrossingLine( line );
}
}
if ( nextPart )
return nextPart->isBorderCrossingLine( feat );
return false;
}

View File

@ -45,7 +45,7 @@ namespace pal
/**
* \brief LabelPosition is a candidate feature label position
*/
class CORE_EXPORT LabelPosition
class CORE_EXPORT LabelPosition : public PointSet
{
friend class CostCalculator;
friend class PolygonCostCalculator;
@ -126,10 +126,10 @@ namespace pal
void getBoundingBox( double amin[2], double amax[2] ) const;
/** Get distance from this label to a point. If point lies inside, returns negative number. */
double getDistanceToPoint( double xp, double yp );
double getDistanceToPoint( double xp, double yp ) const;
/** Returns true if this label crosses the specified line */
bool isBorderCrossingLine( PointSet* feat );
bool isBorderCrossingLine( PointSet* line ) const;
/** Returns number of intersections with polygon (testing border and center) */
int getNumPointsInPolygon( PointSet* polygon ) const;
@ -258,7 +258,6 @@ namespace pal
int nbOverlap;
double x[4], y[4];
double alpha;
double w;
double h;

View File

@ -109,7 +109,7 @@ namespace pal
type = GEOS_POINT;
}
PointSet::PointSet( PointSet &ps )
PointSet::PointSet( const PointSet &ps )
: mGeos( 0 )
, mOwnsGeom( false )
, parent( 0 )
@ -211,6 +211,17 @@ namespace pal
return mPreparedGeom;
}
void PointSet::invalidateGeos()
{
GEOSContextHandle_t geosctxt = geosContext();
if ( mOwnsGeom ) // delete old geometry if we own it
GEOSGeom_destroy_r( geosctxt, mGeos );
GEOSPreparedGeom_destroy_r( geosctxt, mPreparedGeom );
mOwnsGeom = false;
mGeos = 0;
mPreparedGeom = 0;
}
PointSet::~PointSet()
{
GEOSContextHandle_t geosctxt = geosContext();
@ -867,7 +878,7 @@ namespace pal
return finalBb;
}
double PointSet::minDistanceToPoint( double px, double py, double *rx, double *ry )
double PointSet::minDistanceToPoint( double px, double py, double *rx, double *ry ) const
{
if ( !mGeos )
createGeosGeom();

View File

@ -98,7 +98,7 @@ namespace pal
QLinkedList<PointSet *> &shapes_final,
double xrm, double yrm, const QString &uid );
/** Returns the minimum distance between the point set geometry and the point (px,py)
/** Returns the squared minimum distance between the point set geometry and the point (px,py)
* Optionally, the nearest point is stored in (rx,ry).
* @param px x coordinate of the point
* @param py y coordinate of the points
@ -106,7 +106,7 @@ namespace pal
* @param ry pointer to y coorinates of the nearest point (can be NULL)
* @returns minimum distance
*/
double minDistanceToPoint( double px, double py, double *rx = 0, double *ry = 0 );
double minDistanceToPoint( double px, double py, double *rx = 0, double *ry = 0 ) const;
void getCentroid( double &px, double &py, bool forceInside = false ) const;
@ -156,11 +156,12 @@ namespace pal
PointSet( double x, double y );
PointSet( PointSet &ps );
PointSet( const PointSet &ps );
void deleteCoords();
void createGeosGeom() const;
const GEOSPreparedGeometry* preparedGeom() const;
void invalidateGeos();
double xmin;
double xmax;