Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'linear_referencing'
  • Loading branch information
mhugent committed Mar 31, 2012
2 parents c4aed92 + dd9b98a commit 33b7e82
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 14 deletions.
3 changes: 2 additions & 1 deletion python/analysis/qgsgeometryanalyzer.sip
Expand Up @@ -60,7 +60,8 @@ class QgsGeometryAnalyzer
@param p progress dialog or 0 if no progress dialog should be shown
*/
bool eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QList<int>& unlocatedFeatureIds /Out/,
const QString& outputLayer, const QString& outputFormat, int locationField1, int locationField2 = -1, QgsVectorDataProvider* memoryProvider = 0, QProgressDialog* p = 0 );
const QString& outputLayer, const QString& outputFormat, int locationField1, int locationField2 = -1, int offsetField = -1, double offsetScale = 1.0,
bool forceSingleGeometry = false, QgsVectorDataProvider* memoryProvider = 0, QProgressDialog* p = 0 );

/**Returns multilinestring*/
QgsGeometry* locateBetweenMeasures( double fromMeasure, double toMeasure, QgsGeometry* lineGeom );
Expand Down
243 changes: 232 additions & 11 deletions src/analysis/vector/qgsgeometryanalyzer.cpp
Expand Up @@ -910,7 +910,8 @@ void QgsGeometryAnalyzer::bufferFeature( QgsFeature& f, int nProcessedFeatures,
}

bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QList<int>& unlocatedFeatureIds, const QString& outputLayer,
const QString& outputFormat, int locationField1, int locationField2, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
const QString& outputFormat, int locationField1, int locationField2, int offsetField, double offsetScale,
bool forceSingleGeometry, QgsVectorDataProvider* memoryProvider, QProgressDialog* p )
{
if ( !lineLayer || !eventLayer || !lineLayer->isValid() || !eventLayer->isValid() )
{
Expand All @@ -932,10 +933,19 @@ bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer*
QgsFeatureList memoryProviderFeatures;
if ( !memoryProvider )
{
QGis::WkbType memoryProviderType = QGis::WKBMultiLineString;
if ( locationField2 == -1 )
{
memoryProviderType = forceSingleGeometry ? QGis::WKBPoint : QGis::WKBMultiPoint;
}
else
{
memoryProviderType = forceSingleGeometry ? QGis::WKBLineString : QGis::WKBMultiLineString;
}
fileWriter = new QgsVectorFileWriter( outputLayer,
eventLayer->dataProvider()->encoding(),
eventLayer->pendingFields(),
locationField2 == -1 ? QGis::WKBMultiPoint : QGis::WKBMultiLineString,
memoryProviderType,
&( lineLayer->crs() ),
outputFormat );
}
Expand Down Expand Up @@ -1003,15 +1013,7 @@ bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer*
if ( lrsGeom )
{
++nOutputFeatures;
fet.setGeometry( lrsGeom );
if ( memoryProvider )
{
memoryProviderFeatures << fet;
}
else if ( fileWriter )
{
fileWriter->addFeature( fet );
}
addEventLayerFeature( fet, lrsGeom, lineFeature.geometry(), fileWriter, memoryProviderFeatures, offsetField, offsetScale, forceSingleGeometry );
}
}
if ( nOutputFeatures < 1 )
Expand All @@ -1033,6 +1035,225 @@ bool QgsGeometryAnalyzer::eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer*
return true;
}

void QgsGeometryAnalyzer::addEventLayerFeature( QgsFeature& feature, QgsGeometry* geom, QgsGeometry* lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures,
int offsetField, double offsetScale, bool forceSingleType )
{
if ( !geom )
{
return;
}

QList<QgsGeometry*> geomList;
if ( forceSingleType )
{
geomList = geom->asGeometryCollection();
}
else
{
geomList.push_back( geom );
}

QList<QgsGeometry*>::iterator geomIt = geomList.begin();
for ( ; geomIt != geomList.end(); ++geomIt )
{
//consider offset
if ( offsetField >= 0 )
{
double offsetVal = feature.attributeMap()[offsetField].toDouble();
offsetVal *= offsetScale;
createOffsetGeometry( *geomIt, lineGeom, offsetVal );
}

feature.setGeometry( *geomIt );
if ( fileWriter )
{
fileWriter->addFeature( feature );
}
else
{
memoryFeatures << feature;
}
}

if ( forceSingleType )
{
delete geom;
}
}

void QgsGeometryAnalyzer::createOffsetGeometry( QgsGeometry* geom, QgsGeometry* lineGeom, double offset )
{
if( !geom || !lineGeom )
{
return;
}

QList<QgsGeometry*> inputGeomList;

if( geom->isMultipart() )
{
inputGeomList = geom->asGeometryCollection();
}
else
{
inputGeomList.push_back( geom );
}

QList<GEOSGeometry*> outputGeomList;
QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin();
for(; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
{
if( geom->type() == QGis::Line )
{
//geos 3.3 needed for line offsets
#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
outputGeomList.push_back( GEOSOffsetCurve( (*inputGeomIt)->asGeos(), offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ) );
#else
outputGeomList.push_back( 0 );
#endif
}
else if( geom->type() == QGis::Point )
{
QgsPoint p = (*inputGeomIt)->asPoint();
p = createPointOffset( p.x(), p.y(), offset, lineGeom );
GEOSCoordSequence* ptSeq = GEOSCoordSeq_create( 1, 2 );
GEOSCoordSeq_setX( ptSeq, 0, p.x() );
GEOSCoordSeq_setY( ptSeq, 0, p.y() );
GEOSGeometry* geosPt = GEOSGeom_createPoint( ptSeq );
outputGeomList.push_back( geosPt );
}
}

if( !geom->isMultipart() )
{
GEOSGeometry* outputGeom = outputGeomList.at( 0 );
if( outputGeom )
{
geom->fromGeos( outputGeom );
}
}
else
{
GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
for( int i = 0; i < outputGeomList.size(); ++i )
{
geomArray[i] = outputGeomList.at( i );
}
GEOSGeometry* collection = 0;
if( geom->type() == QGis::Point )
{
collection = GEOSGeom_createCollection( GEOS_MULTIPOINT, geomArray, outputGeomList.size() );
}
else if( geom->type() == QGis::Line )
{
collection = GEOSGeom_createCollection( GEOS_MULTILINESTRING, geomArray, outputGeomList.size() );
}
geom->fromGeos( collection );
delete[] geomArray;
}
}

#if 0
if( geom->type() == QGis::Line )
{
QList<GEOSGeometry*> outputGeomList;
QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin();
for(; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
{
//geos 3.3 needed for line offsets
#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \
((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)))
outputGeomList.push_back( GEOSOffsetCurve( (*inputGeomIt)->asGeos(), offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ) );
#else
outputGeomList.push_back( 0 );
#endif
}

if( !geom->isMultipart() )
{
GEOSGeometry* outputGeom = outputGeomList.at( 0 );
if( outputGeom )
{
geom->fromGeos( outputGeom );
}
}
else
{
GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
for( int i = 0; i < outputGeomList.size(); ++i )
{
geomArray[i] = outputGeomList.at( i );
}
GEOSGeometry* collection = GEOSGeom_createCollection( GEOS_MULTILINESTRING, geomArray, outputGeomList.size() );
geom->fromGeos( collection );
delete[] geomArray;
}
}
else if( geom->type() == QGis::Point )
{
QList<GEOSGeometry*> outputGeomList;
QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin();
for(; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt )
{
QgsPoint p = (*inputGeomIt)->asPoint();
p = createPointOffset( p.x(), p.y(), offset, lineGeom );
GEOSCoordSequence* ptSeq = GEOSCoordSeq_create( 1, 2 );
GEOSCoordSeq_setX( ptSeq, 0, p.x() );
GEOSCoordSeq_setY( ptSeq, 0, p.y() );
GEOSGeometry* geosPt = GEOSGeom_createPoint( ptSeq );
outputGeomList.push_back( geosPt );
//geom->fromGeos( geosPt );
}

if( !geom->isMultipart() )
{
GEOSGeometry* outputGeom = outputGeomList.at( 0 );
if( outputGeom )
{
geom->fromGeos( outputGeom );
}
}
else
{
GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()];
for( int i = 0; i < outputGeomList.size(); ++i )
{
geomArray[i] = outputGeomList.at( i );
}
GEOSGeometry* collection = GEOSGeom_createCollection( GEOS_MULTIPOINT, geomArray, outputGeomList.size() );
geom->fromGeos( collection );
delete[] geomArray;
}
}
}
#endif //0

QgsPoint QgsGeometryAnalyzer::createPointOffset( double x, double y, double dist, QgsGeometry* lineGeom ) const
{
QgsPoint p( x, y );
QgsPoint minDistPoint;
int beforeVertexNr;
lineGeom->closestSegmentWithContext( p, minDistPoint, beforeVertexNr );

int afterVertexNr = beforeVertexNr + 1;
QgsPoint beforeVertex = lineGeom->vertexAt( beforeVertexNr );
QgsPoint afterVertex = lineGeom->vertexAt( afterVertexNr );

//get normal vector
double dx = afterVertex.x() - beforeVertex.x();
double dy = afterVertex.y() - beforeVertex.y();
double normalX = -dy;
double normalY = dx;
double normalLength = sqrt( normalX * normalX + normalY * normalY );
normalX *= ( dist / normalLength );
normalY *= ( dist / normalLength );

double debugLength = sqrt( normalX * normalX + normalY * normalY ); //control

return QgsPoint( x - normalX, y - normalY ); //negative values -> left side, positive values -> right side
}

QgsGeometry* QgsGeometryAnalyzer::locateBetweenMeasures( double fromMeasure, double toMeasure, QgsGeometry* lineGeom )
{
if ( !lineGeom )
Expand Down
13 changes: 11 additions & 2 deletions src/analysis/vector/qgsgeometryanalyzer.h
Expand Up @@ -111,11 +111,13 @@ class ANALYSIS_EXPORT QgsGeometryAnalyzer
@param unlocatedFeatureIds out: ids of event features where linear referencing was not successful
@param locationField1 attribute index of location field in event layer
@param locationField2 attribute index of location end field (or -1 for point layer)
@param forceSingleGeometry force layer to single point/line type. Feature attributes are copied in case of multiple matches
@param memoryProvider memory provider to write output to (can be 0 if output is written to a file)
@param p progress dialog or 0 if no progress dialog should be shown
*/
bool eventLayer( QgsVectorLayer* lineLayer, QgsVectorLayer* eventLayer, int lineField, int eventField, QList<int>& unlocatedFeatureIds, const QString& outputLayer,
const QString& outputFormat, int locationField1, int locationField2 = -1, QgsVectorDataProvider* memoryProvider = 0, QProgressDialog* p = 0 );
const QString& outputFormat, int locationField1, int locationField2 = -1, int offsetField = -1, double offsetScale = 1.0,
bool forceSingleGeometry = false, QgsVectorDataProvider* memoryProvider = 0, QProgressDialog* p = 0 );

/**Returns linear reference geometry as a multiline (or 0 if no match). Currently, the z-coordinates are considered to be the measures (no support for m-values in QGIS)*/
QgsGeometry* locateBetweenMeasures( double fromMeasure, double toMeasure, QgsGeometry* lineGeom );
Expand All @@ -140,7 +142,14 @@ class ANALYSIS_EXPORT QgsGeometryAnalyzer
void dissolveFeature( QgsFeature& f, int nProcessedFeatures, QgsGeometry** dissolveGeometry );

//helper functions for event layer

void addEventLayerFeature( QgsFeature& feature, QgsGeometry* geom, QgsGeometry* lineGeom, QgsVectorFileWriter* fileWriter, QgsFeatureList& memoryFeatures, int offsetField = -1, double offsetScale = 1.0,
bool forceSingleType = false );
/**Create geometry offset relative to line geometry.
@param geom the geometry to modify
@param the line geometry to wich the feature is referenced
@param offset the offset value in layer unit. Negativ value means offset towards left, positive value is offset to the right side*/
void createOffsetGeometry( QgsGeometry* geom, QgsGeometry* lineGeom, double offset );
QgsPoint createPointOffset( double x, double y, double dist, QgsGeometry* lineGeom ) const;
unsigned char* locateBetweenWkbString( unsigned char* ptr, QgsMultiPolyline& result, double fromMeasure, double toMeasure );
unsigned char* locateAlongWkbString( unsigned char* ptr, QgsMultiPoint& result, double measure );
static bool clipSegmentByRange( double x1, double y1, double m1, double x2, double y2, double m2, double range1, double range2, QgsPoint& pt1, QgsPoint& pt2, bool& secondPointClipped );
Expand Down

0 comments on commit 33b7e82

Please sign in to comment.