Skip to content

Commit

Permalink
[asf] Fix parsing of polygons
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Mar 5, 2019
1 parent 2ff0d59 commit 33af405
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 34 deletions.
82 changes: 51 additions & 31 deletions src/providers/arcgisrest/qgsarcgisrestutils.cpp
Expand Up @@ -18,15 +18,6 @@
#include "qgslogger.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsrectangle.h"
#include "geometry/qgsabstractgeometry.h"
#include "geometry/qgscircularstring.h"
#include "geometry/qgscompoundcurve.h"
#include "geometry/qgscurvepolygon.h"
#include "geometry/qgslinestring.h"
#include "geometry/qgsmultipoint.h"
#include "geometry/qgsmulticurve.h"
#include "geometry/qgspolygon.h"
#include "geometry/qgspoint.h"
#include "qgsfeedback.h"
#include "qgspallabeling.h"
#include "qgssymbol.h"
Expand All @@ -35,7 +26,6 @@
#include "qgssettings.h"
#include "qgslinesymbollayer.h"
#include "qgsfillsymbollayer.h"
#include "qgsmarkersymbollayer.h"
#include "qgsrenderer.h"
#include "qgsrulebasedlabeling.h"
#include "qgssinglesymbolrenderer.h"
Expand Down Expand Up @@ -91,7 +81,7 @@ QgsWkbTypes::Type QgsArcGisRestUtils::mapEsriGeometryType( const QString &esriGe
else if ( esriGeometryType == QLatin1String( "esriGeometryPolyline" ) )
return QgsWkbTypes::MultiCurve;
else if ( esriGeometryType == QLatin1String( "esriGeometryPolygon" ) )
return QgsWkbTypes::Polygon;
return QgsWkbTypes::MultiPolygon;
else if ( esriGeometryType == QLatin1String( "esriGeometryEnvelope" ) )
return QgsWkbTypes::Polygon;
// Unsupported (either by qgis, or format unspecified by the specification)
Expand All @@ -112,7 +102,7 @@ QgsWkbTypes::Type QgsArcGisRestUtils::mapEsriGeometryType( const QString &esriGe
return QgsWkbTypes::Unknown;
}

static std::unique_ptr< QgsPoint > parsePoint( const QVariantList &coordList, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsPoint > QgsArcGisRestUtils::parsePoint( const QVariantList &coordList, QgsWkbTypes::Type pointType )
{
int nCoords = coordList.size();
if ( nCoords < 2 )
Expand All @@ -127,7 +117,7 @@ static std::unique_ptr< QgsPoint > parsePoint( const QVariantList &coordList, Qg
return qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
}

static std::unique_ptr< QgsCircularString > parseCircularString( const QVariantMap &curveData, QgsWkbTypes::Type pointType, const QgsPoint &startPoint )
std::unique_ptr< QgsCircularString > QgsArcGisRestUtils::parseCircularString( const QVariantMap &curveData, QgsWkbTypes::Type pointType, const QgsPoint &startPoint )
{
const QVariantList coordsList = curveData[QStringLiteral( "c" )].toList();
if ( coordsList.isEmpty() )
Expand All @@ -148,7 +138,7 @@ static std::unique_ptr< QgsCircularString > parseCircularString( const QVariantM
return curve;
}

static std::unique_ptr< QgsCompoundCurve > parseCompoundCurve( const QVariantList &curvesList, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsCompoundCurve > QgsArcGisRestUtils::parseCompoundCurve( const QVariantList &curvesList, QgsWkbTypes::Type pointType )
{
// [[6,3],[5,3],{"b":[[3,2],[6,1],[2,4]]},[1,2],{"c": [[3,3],[1,4]]}]
std::unique_ptr< QgsCompoundCurve > compoundCurve = qgis::make_unique< QgsCompoundCurve >();
Expand Down Expand Up @@ -189,7 +179,7 @@ static std::unique_ptr< QgsCompoundCurve > parseCompoundCurve( const QVariantLis
return compoundCurve;
}

static std::unique_ptr< QgsPoint > parseEsriGeometryPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsPoint > QgsArcGisRestUtils::parseEsriGeometryPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
{
// {"x" : <x>, "y" : <y>, "z" : <z>, "m" : <m>}
bool xok = false, yok = false;
Expand All @@ -202,7 +192,7 @@ static std::unique_ptr< QgsPoint > parseEsriGeometryPoint( const QVariantMap &ge
return qgis::make_unique< QgsPoint >( pointType, x, y, z, m );
}

static std::unique_ptr< QgsMultiPoint > parseEsriGeometryMultiPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsMultiPoint > QgsArcGisRestUtils::parseEsriGeometryMultiPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
{
// {"points" : [[ <x1>, <y1>, <z1>, <m1> ] , [ <x2>, <y2>, <z2>, <m2> ], ... ]}
const QVariantList coordsList = geometryData[QStringLiteral( "points" )].toList();
Expand Down Expand Up @@ -233,7 +223,7 @@ static std::unique_ptr< QgsMultiPoint > parseEsriGeometryMultiPoint( const QVari
return multiPoint;
}

static std::unique_ptr< QgsMultiCurve > parseEsriGeometryPolyline( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsMultiCurve > QgsArcGisRestUtils::parseEsriGeometryPolyline( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
{
// {"curvePaths": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
QVariantList pathsList;
Expand All @@ -256,7 +246,7 @@ static std::unique_ptr< QgsMultiCurve > parseEsriGeometryPolyline( const QVarian
return multiCurve;
}

static std::unique_ptr< QgsCurvePolygon > parseEsriGeometryPolygon( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
std::unique_ptr< QgsMultiSurface > QgsArcGisRestUtils::parseEsriGeometryPolygon( const QVariantMap &geometryData, QgsWkbTypes::Type pointType )
{
// {"curveRings": [[[0,0], {"c": [[3,3],[1,4]]} ]]}
QVariantList ringsList;
Expand All @@ -266,26 +256,56 @@ static std::unique_ptr< QgsCurvePolygon > parseEsriGeometryPolygon( const QVaria
ringsList = geometryData[QStringLiteral( "ringPaths" )].toList();
if ( ringsList.isEmpty() )
return nullptr;
std::unique_ptr< QgsCurvePolygon > polygon = qgis::make_unique< QgsCurvePolygon >();
std::unique_ptr< QgsCompoundCurve > ext = parseCompoundCurve( ringsList.front().toList(), pointType );
if ( !ext )
{
return nullptr;
}
polygon->setExteriorRing( ext.release() );
for ( int i = 1, n = ringsList.size(); i < n; ++i )

QList< QgsCompoundCurve * > curves;
for ( int i = 0, n = ringsList.size(); i < n; ++i )
{
std::unique_ptr< QgsCompoundCurve > curve = parseCompoundCurve( ringsList[i].toList(), pointType );
if ( !curve )
{
return nullptr;
continue;
}
polygon->addInteriorRing( curve.release() );
curves.append( curve.release() );
}
return polygon;
if ( curves.count() == 0 )
return nullptr;

std::sort( curves.begin(), curves.end(), []( const QgsCompoundCurve * a, const QgsCompoundCurve * b )->bool{ double a_area = 0.0; double b_area = 0.0; a->sumUpArea( a_area ); b->sumUpArea( b_area ); return std::abs( a_area ) > std::abs( b_area ); } );
std::unique_ptr< QgsMultiSurface > result = qgis::make_unique< QgsMultiSurface >();
while ( !curves.isEmpty() )
{
QgsCompoundCurve *exterior = curves.takeFirst();
QgsCurvePolygon *newPolygon = new QgsCurvePolygon();
newPolygon->setExteriorRing( exterior );
std::unique_ptr<QgsGeometryEngine> engine( QgsGeometry::createGeometryEngine( newPolygon ) );
engine->prepareGeometry();

QMutableListIterator< QgsCompoundCurve * > it( curves );
while ( it.hasNext() )
{
QgsCompoundCurve *curve = it.next();
QgsRectangle boundingBox = newPolygon->boundingBox();
if ( boundingBox.intersects( curve->boundingBox() ) )
{
QgsPoint point = curve->startPoint();
if ( engine->contains( &point ) )
{
newPolygon->addInteriorRing( curve );
it.remove();
engine.reset( QgsGeometry::createGeometryEngine( newPolygon ) );
engine->prepareGeometry();
}
}
}
result->addGeometry( newPolygon );
}
if ( result->numGeometries() == 0 )
return nullptr;

return result;
}

static std::unique_ptr< QgsPolygon > parseEsriEnvelope( const QVariantMap &geometryData )
std::unique_ptr< QgsPolygon > QgsArcGisRestUtils::parseEsriEnvelope( const QVariantMap &geometryData )
{
// {"xmin" : -109.55, "ymin" : 25.76, "xmax" : -86.39, "ymax" : 49.94}
bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false;
Expand Down Expand Up @@ -651,7 +671,7 @@ std::unique_ptr<QgsFillSymbol> QgsArcGisRestUtils::parseEsriPictureFillSymbolJso
return symbol;
}

QgsSimpleMarkerSymbolLayerBase::Shape parseEsriMarkerShape( const QString &style )
QgsSimpleMarkerSymbolLayerBase::Shape QgsArcGisRestUtils::parseEsriMarkerShape( const QString &style )
{
if ( style == QLatin1String( "esriSMSCircle" ) )
return QgsSimpleMarkerSymbolLayerBase::Circle;
Expand Down
27 changes: 25 additions & 2 deletions src/providers/arcgisrest/qgsarcgisrestutils.h
Expand Up @@ -15,10 +15,23 @@
#ifndef QGSARCGISRESTUTILS_H
#define QGSARCGISRESTUTILS_H

#include <QStringList>
#include <QVariant>
#include "qgswkbtypes.h"
#include "qgsrectangle.h"
#include "qgsmarkersymbollayer.h"
#include "geometry/qgsabstractgeometry.h"
#include "geometry/qgscircularstring.h"
#include "geometry/qgscompoundcurve.h"
#include "geometry/qgscurvepolygon.h"
#include "geometry/qgsgeometryengine.h"
#include "geometry/qgslinestring.h"
#include "geometry/qgsmultipoint.h"
#include "geometry/qgsmulticurve.h"
#include "geometry/qgsmultisurface.h"
#include "geometry/qgspolygon.h"
#include "geometry/qgspoint.h"

#include <QStringList>
#include <QVariant>

class QNetworkReply;
class QgsNetworkAccessManager;
Expand Down Expand Up @@ -52,6 +65,15 @@ class QgsArcGisRestUtils
static QByteArray queryService( const QUrl &url, const QString &authcfg, QString &errorTitle, QString &errorText, const QgsStringMap &requestHeaders = QgsStringMap(), QgsFeedback *feedback = nullptr );
static QVariantMap queryServiceJSON( const QUrl &url, const QString &authcfg, QString &errorTitle, QString &errorText, const QgsStringMap &requestHeaders = QgsStringMap(), QgsFeedback *feedback = nullptr );

static std::unique_ptr< QgsPoint > parsePoint( const QVariantList &coordList, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsCircularString > parseCircularString( const QVariantMap &curveData, QgsWkbTypes::Type pointType, const QgsPoint &startPoint );
static std::unique_ptr< QgsCompoundCurve > parseCompoundCurve( const QVariantList &curvesList, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsPoint > parseEsriGeometryPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsMultiPoint > parseEsriGeometryMultiPoint( const QVariantMap &geometryData, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsMultiCurve > parseEsriGeometryPolyline( const QVariantMap &geometryData, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsMultiSurface > parseEsriGeometryPolygon( const QVariantMap &geometryData, QgsWkbTypes::Type pointType );
static std::unique_ptr< QgsPolygon > parseEsriEnvelope( const QVariantMap &geometryData );

static std::unique_ptr< QgsSymbol > parseEsriSymbolJson( const QVariantMap &symbolData );
static std::unique_ptr< QgsLineSymbol > parseEsriLineSymbolJson( const QVariantMap &symbolData );
static std::unique_ptr< QgsFillSymbol > parseEsriFillSymbolJson( const QVariantMap &symbolData );
Expand All @@ -65,6 +87,7 @@ class QgsArcGisRestUtils
static QColor parseEsriColorJson( const QVariant &colorData );
static Qt::PenStyle parseEsriLineStyle( const QString &style );
static Qt::BrushStyle parseEsriFillStyle( const QString &style );
static QgsSimpleMarkerSymbolLayerBase::Shape parseEsriMarkerShape( const QString &style );

static QDateTime parseDateTime( const QVariant &value );

Expand Down
20 changes: 19 additions & 1 deletion tests/src/providers/testqgsarcgisrestutils.cpp
Expand Up @@ -26,6 +26,8 @@
#include "qgsrulebasedlabeling.h"
#include "qgssinglesymbolrenderer.h"
#include "qgscategorizedsymbolrenderer.h"
#include "geometry/qgsmultisurface.h"

#include <QObject>

class TestQgsArcGisRestUtils : public QObject
Expand All @@ -40,6 +42,7 @@ class TestQgsArcGisRestUtils : public QObject
void testMapEsriFieldType();
void testParseSpatialReference();
void testMapEsriGeometryType();
void testParseEsriGeometryPolygon();
void testParseEsriFillStyle();
void testParseEsriLineStyle();
void testParseEsriColorJson();
Expand Down Expand Up @@ -113,12 +116,27 @@ void TestQgsArcGisRestUtils::testMapEsriGeometryType()
QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral( "esriGeometryMultipoint" ) ), QgsWkbTypes::MultiPoint );
//unsure why this maps to multicurve and not multilinestring
//QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral("esriGeometryPolyline") ),QgsWkbTypes::MultiCurve );
QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral( "esriGeometryPolygon" ) ), QgsWkbTypes::Polygon );
QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral( "esriGeometryPolygon" ) ), QgsWkbTypes::MultiPolygon );
QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral( "esriGeometryEnvelope" ) ), QgsWkbTypes::Polygon );

QCOMPARE( QgsArcGisRestUtils::mapEsriGeometryType( QStringLiteral( "xxx" ) ), QgsWkbTypes::Unknown );
}

void TestQgsArcGisRestUtils::testParseEsriGeometryPolygon()
{
QVariantMap map = jsonStringToMap( "{"
"\"rings\": ["
"[[12,0],[13,0],[13,10],[12,10],[12,0]],"
"[[3,3],[9,3],[6,9],[3,3]],"
"[[0,0],[10,0],[10,10],[0,10],[0,0]]"
"]"
"}" );
QCOMPARE( map[QStringLiteral( "rings" )].isValid(), true );
std::unique_ptr<QgsMultiSurface> geometry = QgsArcGisRestUtils::parseEsriGeometryPolygon( map, QgsWkbTypes::Point );
QVERIFY( geometry.get() );
QCOMPARE( geometry->asWkt(), QStringLiteral( "MultiSurface (CurvePolygon (CompoundCurve ((0 0, 10 0, 10 10, 0 10, 0 0)),CompoundCurve ((3 3, 9 3, 6 9, 3 3))),CurvePolygon (CompoundCurve ((12 0, 13 0, 13 10, 12 10, 12 0))))" ) );
}

void TestQgsArcGisRestUtils::testParseEsriFillStyle()
{
QCOMPARE( QgsArcGisRestUtils::parseEsriFillStyle( QStringLiteral( "esriSFSBackwardDiagonal" ) ), Qt::BDiagPattern );
Expand Down

0 comments on commit 33af405

Please sign in to comment.