Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+419
@@ -0,0 +1,419 @@
|
||||
/**
|
||||
**
|
||||
** @RDE_HEADER@
|
||||
** Implementation of 2d matrix operations
|
||||
** $Id$
|
||||
** $HeadURL$
|
||||
** $Author$
|
||||
**
|
||||
** -----------------------------------------------------------------------------------------------
|
||||
**
|
||||
** Hardware-Environment: Intel-PC
|
||||
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
|
||||
**
|
||||
** Language ........: C++ 11
|
||||
**
|
||||
** -----------------------------------------------------------------------------------------------
|
||||
**
|
||||
** Author ...........: Jörg Turla, SPD G, Tel. 3132
|
||||
**
|
||||
** -----------------------------------------------------------------------------------------------
|
||||
**
|
||||
** Notes .........:
|
||||
**
|
||||
** 22.06.2016, JT : First edition for use with Qt5
|
||||
**
|
||||
** -----------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "geometry_engine.h"
|
||||
|
||||
|
||||
#include <Map/coord_translation_functor.h>
|
||||
#include <Map/map.h>
|
||||
#include <MapImpl/map_impl.h>
|
||||
#include <MapImpl/abstract_map_object_impl.h>
|
||||
|
||||
#include <MathUtils/clipper.hpp>
|
||||
|
||||
// ESRI
|
||||
#include <arcgis/Geometry.h>
|
||||
#include <arcgis/GeometryEngine.h>
|
||||
//#include <arcgis/Line.h>
|
||||
#include <arcgis/Polyline.h>
|
||||
#include <arcgis/PolylineBuilder.h>
|
||||
#include <arcgis/Polygon.h>
|
||||
#include <arcgis/PolygonBuilder.h>
|
||||
|
||||
#include <QPointF>
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace SituationMap { namespace MathUtils
|
||||
{
|
||||
//
|
||||
// Line
|
||||
//
|
||||
|
||||
GeometryEngine::Geometry *GeometryEngine::createLine(Map *map, const QPointF &p1, const QPointF &p2)
|
||||
{
|
||||
QVector<QPointF> vertices;
|
||||
vertices << p1 << p2;
|
||||
|
||||
return SituationMap::MathUtils::GeometryEngine::createPolyline(map, vertices);
|
||||
} // GeometryEngine::Geometry *GeometryEngine::createLine( Map * map, QPointF p1, QPointF p2 )
|
||||
|
||||
|
||||
template<class T>
|
||||
inline GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies)
|
||||
{
|
||||
MapImpl * mapImpl = dynamic_cast<MapImpl*>(map);
|
||||
if (! mapImpl)
|
||||
return nullptr;
|
||||
|
||||
SituationMap::CoordTranslationFunctor *mapCtFunctor = map->coordTranslationFunctor();
|
||||
if (! mapCtFunctor)
|
||||
return nullptr;
|
||||
|
||||
Esri::ArcGISRuntime::PolylineBuilder builder ( mapImpl->getEsriSpatialReference() );
|
||||
|
||||
for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit)
|
||||
{
|
||||
const QPointF p(mapCtFunctor->geographicToMap(*pit));
|
||||
builder.addPoint(p.x(),p.y());
|
||||
}
|
||||
|
||||
Esri::ArcGISRuntime::Polyline * esriPolyline = new Esri::ArcGISRuntime::Polyline( builder.toGeometry() );
|
||||
|
||||
return esriPolyline;
|
||||
} // GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies)
|
||||
|
||||
|
||||
|
||||
GeometryEngine::Geometry *GeometryEngine::createPolyline(Map *map, const QVector<QPointF> &vertices )
|
||||
{
|
||||
return SituationMap::MathUtils::createPolyline<QVector<QPointF>>(map, vertices);
|
||||
} // GeometryEngine::Geometry * GeometryEngine::createPolyline( Map * map, const QVector<QPointF> & verticies )
|
||||
|
||||
GeometryEngine::Geometry *GeometryEngine::createPolyline(Map *map, const QList<QPointF> &vertices)
|
||||
{
|
||||
return SituationMap::MathUtils::createPolyline<QList<QPointF>>(map, vertices);
|
||||
} // GeometryEngine::Geometry * GeometryEngine::createPolyline( Map * map, const QVector<QPointF> & verticies )
|
||||
|
||||
|
||||
//
|
||||
// Polygon
|
||||
//
|
||||
|
||||
template<class T>
|
||||
inline GeometryEngine::Geometry *createPolygon(Map *map, const T &verticies)
|
||||
{
|
||||
MapImpl * mapImpl = dynamic_cast<MapImpl*>(map);
|
||||
|
||||
if (! mapImpl) return nullptr;
|
||||
|
||||
SituationMap::CoordTranslationFunctor * mapCtFunctor = map->coordTranslationFunctor();
|
||||
|
||||
if (! mapCtFunctor) return nullptr;
|
||||
|
||||
Esri::ArcGISRuntime::PolygonBuilder builder( mapImpl->getEsriSpatialReference() );
|
||||
|
||||
for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit )
|
||||
{
|
||||
const QPointF p(mapCtFunctor->geographicToMap(*pit));
|
||||
builder.addPoint(p.x(),p.y());
|
||||
}
|
||||
|
||||
Esri::ArcGISRuntime::Polygon * esriPolygon = new Esri::ArcGISRuntime::Polygon( builder.toGeometry() );
|
||||
|
||||
return esriPolygon;
|
||||
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
|
||||
|
||||
|
||||
GeometryEngine::Geometry * GeometryEngine::createPolygon(Map * map, const QVector<QPointF> & vertices )
|
||||
{
|
||||
return SituationMap::MathUtils::createPolygon<QVector<QPointF>>( map, vertices );
|
||||
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
|
||||
|
||||
GeometryEngine::Geometry * GeometryEngine::createPolygon(Map * map, const QList<QPointF> & vertices )
|
||||
{
|
||||
return SituationMap::MathUtils::createPolygon<QList<QPointF>>( map, vertices );
|
||||
} // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector<QPointF> & verticies )
|
||||
|
||||
|
||||
//
|
||||
// cleanup etc.
|
||||
//
|
||||
|
||||
void GeometryEngine::deleteGeometry(Geometry *geom)
|
||||
{
|
||||
delete geom;
|
||||
} // void GeometryEngine::deleteGeoemtry( Geometry * geom )
|
||||
|
||||
|
||||
bool GeometryEngine::contains( AbstractMapObject *mapObj, const Geometry &hull)
|
||||
{
|
||||
if (nullptr == mapObj) return false;
|
||||
return mapObj->geometryIsInHull( hull );
|
||||
|
||||
} //bool GeometryEngine::contains( AbstractMapObject * mapObj, const Geometry & hull )
|
||||
|
||||
|
||||
bool GeometryEngine::contains(const Geometry &geom, const Geometry &hull)
|
||||
{
|
||||
if (geom.isEmpty() || hull.isEmpty())
|
||||
return false;
|
||||
return Esri::ArcGISRuntime::GeometryEngine::contains(hull, geom);
|
||||
} // bool GeometryEngine::contains(const Geometry & geom, const Geometry & hull)
|
||||
|
||||
|
||||
bool GeometryEngine::intersect( AbstractMapObject *mapObj, const Geometry &intersector)
|
||||
{
|
||||
if (! mapObj)
|
||||
return false;
|
||||
return mapObj->geometryIntersect(intersector);
|
||||
} // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector )
|
||||
|
||||
|
||||
bool GeometryEngine::intersect(const Geometry &inputGeom, const Geometry &intersector)
|
||||
{
|
||||
if (inputGeom.isEmpty() || intersector.isEmpty())
|
||||
return false;
|
||||
|
||||
#ifdef DEBUGGERTEST
|
||||
if (inputGeom.type() == EsriRuntimeQt::GeometryType::Polyline)
|
||||
{
|
||||
const EsriRuntimeQt::Polyline & l = inputGeom;
|
||||
int numPoints = l.pointCount();
|
||||
|
||||
for (int i = 0; i < numPoints; ++i)
|
||||
{
|
||||
QPointF p = l.qxy(i);
|
||||
__asm int 3
|
||||
}
|
||||
}
|
||||
|
||||
if (intersector.type() == EsriRuntimeQt::GeometryType::Line)
|
||||
{
|
||||
const EsriRuntimeQt::Line & l = intersector;
|
||||
double startx = l.startX();
|
||||
double starty = l.startY();
|
||||
double endx = l.endX();
|
||||
double endy = l.endY();
|
||||
|
||||
__asm int 3
|
||||
}
|
||||
#endif
|
||||
|
||||
return Esri::ArcGISRuntime::GeometryEngine::crosses(inputGeom, intersector);
|
||||
} // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector )
|
||||
|
||||
QVector<QPointF> GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector<QPointF> &line, double distance)
|
||||
{
|
||||
/* ~ first lokal functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
auto length = [](const QPointF &v) -> double { return sqrt(v.x()*v.x() + v.y()*v.y()); };
|
||||
|
||||
|
||||
auto normalize = [](const QPointF &v) -> QPointF { return v / sqrt(v.x()*v.x() + v.y()*v.y()); };
|
||||
|
||||
|
||||
auto intersection = [](const QPointF &p1, const QPointF &r1, const QPointF &p2, const QPointF &r2) -> QPointF
|
||||
{
|
||||
double f = r2.y()*r1.x() - r1.y()*r2.x();
|
||||
|
||||
double a = (p1.x() + r1.x()) * p1.y() - p1.x() * (p1.y() + r1.y());
|
||||
double b = (p2.x() + r2.x()) * p2.y() - p2.x() * (p2.y() + r2.y());
|
||||
|
||||
double sx = (r2.x() * a - r1.x() * b) / f;
|
||||
double sy = (-r1.y() * b + r2.y() * a) / f;
|
||||
|
||||
return QPointF(sx, sy);
|
||||
|
||||
}; // intersection
|
||||
|
||||
|
||||
auto orientation = [](const QPointF &p, const QPointF &q, const QPointF &r) -> int
|
||||
{
|
||||
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
|
||||
// for details of below formula.
|
||||
double f = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y());
|
||||
|
||||
if (qFuzzyIsNull(f)) return 0; // colinear
|
||||
|
||||
return (f > 0) ? 1 : 2; // clock or counterclock wise
|
||||
}; // orientation
|
||||
|
||||
|
||||
auto testIntersection = [&orientation, &intersection, &normalize](const QPointF &p1, const QPointF &q1, const QPointF &p2, const QPointF &q2) -> bool
|
||||
{
|
||||
// Find the four orientations needed for general and
|
||||
// special cases
|
||||
int o1 = orientation(p1, q1, p2);
|
||||
int o2 = orientation(p1, q1, q2);
|
||||
int o3 = orientation(p2, q2, p1);
|
||||
int o4 = orientation(p2, q2, q1);
|
||||
|
||||
// General case
|
||||
if (o1 != o2 && o3 != o4)
|
||||
{
|
||||
const QPointF & n1 = q1 - p1;
|
||||
const QPointF & n2 = q2 - p2;
|
||||
const QPointF & s = intersection(p1, normalize(n1), p2, normalize(n2));
|
||||
|
||||
double f1 = (!qFuzzyIsNull(n1.x())) ? (s.x() - p1.x()) / n1.x() : (s.y() - p1.y()) / n1.y();
|
||||
double f2 = (!qFuzzyIsNull(n2.x())) ? (s.x() - p2.x()) / n2.x() : (s.y() - p2.y()) / n2.y();
|
||||
|
||||
return ((f1 >= 0) && (f1 <= 1.0) && (f2 >= 0) && (f2 <= 1.0));
|
||||
}
|
||||
|
||||
return false; // Doesn't fall in any of the above cases
|
||||
|
||||
}; // testIntersection
|
||||
|
||||
|
||||
/* ~ now let's do the job ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
if (line.size() < 2)
|
||||
{
|
||||
// Nothing to do --> return input line
|
||||
return line;
|
||||
}
|
||||
|
||||
QVector<QPointF> transformedLine;
|
||||
|
||||
transformedLine.reserve(line.size());
|
||||
|
||||
for (auto &point : qAsConst(line))
|
||||
{
|
||||
// Let's transform the coordinates to maps coordinate system ...
|
||||
transformedLine << mapCtFunctor->geographicToMap(point);
|
||||
}
|
||||
|
||||
struct LineSegment
|
||||
{
|
||||
QPointF m_p1;
|
||||
QPointF m_p2;
|
||||
QPointF m_t;
|
||||
|
||||
LineSegment() = default;
|
||||
LineSegment(const QPointF &p1, const QPointF &p2, const QPointF &t)
|
||||
: m_p1(p1), m_p2(p2), m_t(t)
|
||||
{}
|
||||
};
|
||||
|
||||
// build single line segments to translate along it's normal
|
||||
QVector<LineSegment> multiSegmLine;
|
||||
|
||||
multiSegmLine.reserve(transformedLine.size() - 1);
|
||||
|
||||
for (int i = 1; i < transformedLine.size(); ++i)
|
||||
{
|
||||
const QPointF & p1 = transformedLine[i - 1];
|
||||
const QPointF & p2 = transformedLine[i];
|
||||
const QPointF & t = normalize(p2 - p1);
|
||||
QPointF n = QPointF(t.y()*distance, -t.x()*distance);
|
||||
multiSegmLine << LineSegment(p1 + n, p2 + n, t);
|
||||
}
|
||||
|
||||
|
||||
transformedLine.clear();
|
||||
transformedLine << multiSegmLine.first().m_p1;
|
||||
|
||||
for (int i = 1; i < multiSegmLine.size(); ++i)
|
||||
{
|
||||
const LineSegment & l1 = multiSegmLine[i - 1];
|
||||
const LineSegment & l2 = multiSegmLine[i];
|
||||
|
||||
const QPointF & dt = l1.m_t - l2.m_t;
|
||||
|
||||
transformedLine << ((qFuzzyIsNull(dt.x()) && qFuzzyIsNull(dt.y())) ? l1.m_p2 : intersection(l1.m_p2, l1.m_t, l2.m_p1, l2.m_t));
|
||||
}
|
||||
|
||||
transformedLine << multiSegmLine.last().m_p2;
|
||||
|
||||
// check result
|
||||
for (int i = 1; i < transformedLine.size(); ++i)
|
||||
{
|
||||
const QPointF & p1 = transformedLine[i - 1];
|
||||
const QPointF & p2 = transformedLine[i];
|
||||
|
||||
if (length(p2 - p1) < distance)
|
||||
{
|
||||
// failure segment is to short relative to distance
|
||||
return QVector<QPointF>();
|
||||
}
|
||||
for (int j = 1; j < transformedLine.size(); ++j)
|
||||
{
|
||||
if (j == i) continue;
|
||||
|
||||
const QPointF & g1 = transformedLine[j-1];
|
||||
const QPointF & g2 = transformedLine[j];
|
||||
|
||||
if (testIntersection(p1, p2, g1, g2))
|
||||
{
|
||||
// failure resulting line has self intersections
|
||||
return QVector<QPointF>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QPointF> pLine;
|
||||
|
||||
// Transform coordinates back to geographic ...
|
||||
pLine.reserve(transformedLine.size());
|
||||
for (auto &p : qAsConst(transformedLine))
|
||||
{
|
||||
pLine << mapCtFunctor->mapToGeographic(p);
|
||||
}
|
||||
|
||||
return pLine;
|
||||
}
|
||||
// QVector<QPointF> GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector<QPointF> &line, double distance)
|
||||
|
||||
QVector<Point3D> GeometryEngine::buffer(AbstractMapObject *mapObj, double distance)
|
||||
{
|
||||
if (!mapObj)
|
||||
return QVector<Point3D>();
|
||||
|
||||
AbstractMapObjectImpl *mapObjImpl = dynamic_cast<AbstractMapObjectImpl*>(mapObj);
|
||||
|
||||
if (!mapObjImpl)
|
||||
return QVector<Point3D>();
|
||||
|
||||
|
||||
const Esri::ArcGISRuntime::Geometry &esriGeometry = mapObjImpl->getEsriGeometry();
|
||||
|
||||
// Esri::ArcGISRuntime::GeometryEngine::buffer -> works for 2D
|
||||
// Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic -> works for 2D and 3D!?
|
||||
const Esri::ArcGISRuntime::Polygon polyline = Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic(esriGeometry,
|
||||
distance,
|
||||
Esri::ArcGISRuntime::LinearUnitId::Meters,
|
||||
1,
|
||||
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
|
||||
if (polyline.parts().isEmpty())
|
||||
return QVector<Point3D>();
|
||||
|
||||
|
||||
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polyline.parts().part(0);
|
||||
const int size = immutablePart.pointCount();
|
||||
|
||||
QVector<Point3D> resultVec;
|
||||
resultVec.reserve(size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
const Esri::ArcGISRuntime::Point &esriP = immutablePart.point(i);
|
||||
resultVec.append(Point3D(esriP.x(), esriP.y(), esriP.z()));
|
||||
}
|
||||
|
||||
// qCritical() << " resultVec = " << resultVec.size();
|
||||
// qCritical() << " res = " << polyline.toJson();
|
||||
|
||||
return resultVec;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace SituationMap::MathUtils
|
||||
|
||||
Reference in new issue
Block a user