= !MapGuide RFC 161 - Geo-Processing Services = This page contains a change request (RFC) for the !MapGuide Open Source project. More !MapGuide RFCs can be found on the [wiki:MapGuideRfcs RFCs] page. == Status == ||RFC Template Version||(1.0)|| ||Submission Date||14 Jun 2017|| ||Last Modified||14 Jun 2017|| ||Author||Jackie Ng|| ||RFC Status||draft|| ||Implementation Status|||| ||Proposed Milestone||3.3|| ||Assigned PSC guide(s)||(when determined)|| ||'''Voting History'''||(vote date)|| ||+1|||| ||+0|||| ||-0|||| ||-1|||| ||no vote|| || == Overview == This RFC proposes to add geo-processing capabilities to the mapagent and enhance MgGeometry with additional capabilities offered by the underlying GEOS library we're wrapping. == Motivation == The mapagent in its current form offers no services for geo-processing. Such capabilities require custom code using MgGeometry from the MapGuide Web API. For basic geo-processing such as geometry manipulation (union/buffer/etc) or testing of common spatial predicates (contains/intersects/etc), we should be able to tap into such functionality in the mapagent. == Proposed Solution == This RFC enhances the mapagent with support for the following operations: === Buffer === Performs a buffer of the given input geometry in with the specified distance. This is the mapagent equivalent of `MgGeometry::Buffer()` || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.BUFFER || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRY || string || Yes || The Well-Known Text of the geometry to buffer || || DISTANCE || number || Yes || The buffer distance in the specific units || || UNITS || mi/km/ft/m || Yes || miles/kilometers/feet/meters || || FORMAT || WKT/GEOJSON || Yes || Output the buffer result as WKT or GeoJSON || || COORDINATESYSTEM || string || Yes || The coordinate system (CS-Map code) of the input geometry || || TRANSFORMTO || string || Optional || The coordinate system (CS-Map code) to transform the buffer geometry into. If not specified, the buffer geometry will be in the input coordinate system || === Simplify === Returns a simplified form of the given input geometry. This is the mapagent equivalent of `MgGeometrySimplifier::Simplify()` || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.SIMPLIFY || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRY || string || Yes || The Well-Known Text of the geometry to simplify || || TOLERANCE || number || Yes || The simplification tolerance || || ALGORITHM || 0/1 || Yes || 0=Douglas-Peucker, 1=Topology Preserving || || FORMAT || WKT/GEOJSON || Yes || Output the simplification result as WKT or GeoJSON || || COORDINATESYSTEM || string || Optional || The coordinate system (CS-Map code) of the input geometry. Only required in combination with `TRANSFORMTO` if you intend to transform the simplified geometry || || TRANSFORMTO || string || Optional || The coordinate system (CS-Map code) to transform the simplified geometry into. Only required in combination with `COORDINATESYSTEM ` if you intend to transform the simplified geometry || === Binary Operation === Performs the given binary operation against the 2 input geometries specified (in WKT) as WKT or GeoJSON with optional transformation. The following binary operations are supported: * Union * Difference * Intersection * SymmetricDifference || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.BINARYOPERATION || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRYA || string || Yes || The Well-Known Text of the first input geometry || || GEOMETRYB || string || Yes || The Well-Known Text of the second input geometry || || OPERATOR || UNION/DIFFERENCE/INTERSECTION/SYMMETRICDIFFERENCE || Yes || The desired operator for this operation || || FORMAT || WKT/GEOJSON || Yes || Output the operation result as WKT or GeoJSON || || COORDINATESYSTEM || string || Optional || The coordinate system (CS-Map code) of the input geometries. Only required in combination with `TRANSFORMTO` if you intend to transform the geometry result || || TRANSFORMTO || string || Optional || The coordinate system (CS-Map code) to transform the simplified geometry into. Only required in combination with `COORDINATESYSTEM ` if you intend to transform the geometry result || All operators are invoked in the form of {{{GEOMETRYA OPERATOR GEOMETRYB}}}. Where ordering of input geometries matters (the operator is not commutative), refer to this rule. === Spatial Predicate === Tests and returns whether the 2 input geometries specified (in WKT) passes the given spatial predicate. The following spatial predicates are supported: * Contains * Crosses * Disjoint * Equals * Intersects * Overlaps * Touches * Within || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.SPATIALPREDICATE || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRYA || string || Yes || The Well-Known Text of the first input geometry || || GEOMETRYB || string || Yes || The Well-Known Text of the second input geometry || || OPERATOR || CONTAINS/CROSSES/DISJOINT/EQUALS/INTERSECTS/OVERLAPS/TOUCHES/WITHIN || Yes || The desired operator for this spatial predicate test ||result || All operators are invoked in the form of {{{GEOMETRYA OPERATOR GEOMETRYB}}}. Where ordering of input geometries matters (the operator is not commutative), refer to this rule. === Convex Hull === Returns the smallest convex Polygon that contains all the points in the given Geometry || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.CONVEXHULL || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRY || string || Yes || The Well-Known Text of the input geometry || || FORMAT || WKT/GEOJSON || Yes || Output the operation result as WKT or GeoJSON || || COORDINATESYSTEM || string || Optional || The coordinate system (CS-Map code) of the input geometries. Only required in combination with `TRANSFORMTO` if you intend to transform the geometry result || || TRANSFORMTO || string || Optional || The coordinate system (CS-Map code) to transform the simplified geometry into. Only required in combination with `COORDINATESYSTEM ` if you intend to transform the geometry result || === Distance === Returns the shortest distance between this geometry and another || '''Name''' || '''Value''' || '''Required''' || '''Description''' || || OPERATION || GEO.DISTANCE || Yes || Operation to execute || || VERSION || 3.3.0 || Yes || Operation version || || CLIENTAGENT || text || Optional || Descriptive text for client || || GEOMETRY || string || Yes || The Well-Known Text of the input geometry || || OTHERGEOMETRY || string || Yes || The Well-Known Text of the other geometry (to compute distance to) || || COORDINATESYSTEM || string || Optional || The coordinate system (CS-Map code) to use for measuring distance. If provided, the distance result with be returned in meters. If not provided, a linear distance measurement is used and the result will be returned in unknown units. || || FORMAT || string || Yes || {{{text/xml}}} for XML, {{{application/json}}} for JSON || || CLEAN || 1/0 || Optional || If requested format is {{{application/json}}}, returns a clean JSON structure per [wiki:MapGuideRfc158] || The result adheres to the new {{{UnitOfMeasure-3.3.0.xsd}}} XML schema {{{ Unit of measure }}} == MgGeometry enhancements == In addition to mapagent enhancements, this RFC enhances the `MgGeometry` class with support for "prepared" geometries. Prepared geometries is a performance optimization that improves the performance of operations between a single target geometry and a batch of test geometries. By preparing the target geometry, various internal data structures of the prepared geometry can be pre-computed, which then allows subsequent geometric operations to be evaluated with maximum efficiency. The RFC introduces a new `MgPreparedGeometry` class to the MapGuide API {{{ //////////////////////////////////////////////////////////////// /// \brief /// MgPreparedGeometry is an prepared form of MgGeometry optimized for the case of /// repeated evaluation of spatial predicates against it and any other geometry /// class MG_GEOMETRY_API MgPreparedGeometry : public MgGuardDisposable { PUBLISHED_API: //////////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief /// This is a convenience method. Given 2 geometries a and b, /// a.Contains(b) is true if and only if b.MgGeometry::Within(a) /// is true. /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Contains(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Contains(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Contains(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this one. /// /// \return /// True if the other geometry is within this geometry, false /// otherwise. /// virtual bool Contains(MgGeometry* other); ////////////////////////////////////////////////////////////////////// /// \brief /// Given 2 geometries a and b, a.Crosses(b) is true if and only /// if the dimension of the intersection of the interior of a and /// the interior of b is less than the greater of the dimension /// of the interior of a and the dimension of the interior of b /// and the intersection of a and b is neither a nor b. /// /// \remarks /// A Point cannot cross another geometry because the /// intersection of the Point with the other geometry is the /// Point. /// \n /// Two MultiPoint geometries cannot cross one another because /// the dimension of the intersection of their interiors, namely /// the 0-dimension, is not less than the greater of the /// dimensions of their interiors, namely the 0-dimension. /// \n /// [\link OGC99049 OGC99-049 \endlink] implicitly excludes a Crosses /// relationship between 2 polygons. According to the definition, /// the possibility of such a relationship would require that the /// intersection of the interior of one polygon with that of /// another be a Point or Line. ///

Illustration

/// d, e and f are MultiPoint geometries. /// \n /// \image html crosses.png /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Crosses(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Crosses(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Crosses(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this /// one. /// /// \return /// True if this geometry spatially crosses the other geometry, /// false otherwise. /// virtual bool Crosses(MgGeometry* other); ///////////////////////////////////////////////////////////////////////// /// \brief /// Given 2 geometries a and b, a.Disjoint(b)is true if and only /// if the intersection of a and b is empty. /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Disjoint(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Disjoint(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Disjoint(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this one. /// /// \return /// True if this geometry is spatially disjoint from the other /// geometry, false otherwise. /// virtual bool Disjoint(MgGeometry* other); ////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// \brief /// This is a convenience method. Given 2 geometries a and b, /// a.Intersects(b) is true if and only if a.\link MgGeometry::Disjoint Disjoint \endlink(b) /// is false. /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Intersects(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Intersects(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Intersects(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this one. /// /// \return /// True if this geometry is not disjoint with respect to the /// other geometry, false otherwise. /// virtual bool Intersects(MgGeometry* other); ///////////////////////////////////////////////////////////////////////// /// \brief /// Given 2 geometries a and b, a.Overlaps(b) is true if and only /// if the dimension of the interior of a equals the dimension of /// the interior of b equals the dimension of the intersection of /// the interior of a and the interior of b and the intersection /// of a and b is neither a nor b. /// /// \remarks /// A Point cannot overlap a Point, and a MultiPoint cannot /// overlap a Point but a MultiPoint can overlap a MultiPoint. ///

Illustration

/// c and d are MultiPoint geometries. /// \image html overlaps.png /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Overlaps(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Overlaps(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Overlaps(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this one. /// /// \return /// True if this geometry spatially overlaps the other geometry, /// false otherwise. /// virtual bool Overlaps(MgGeometry* other); ////////////////////////////////////////////////////////////////////// /// \brief /// Given 2 geometries a and b, a.Touches(b) is true if and only /// if the intersection of the interior of a and the interior of /// b is empty and the intersection of a and b is not empty. /// /// \remarks /// A Point cannot touch a Point because a Point has no boundary /// and so the intersection of the interiors of the two /// geometries is not empty. /// \n /// A Point can touch a non-closed Line at one of the end points /// of the Line. /// \n /// A Point cannot touch a closed Line because all of the points /// in the Line are interior to it. ///

Illustration

/// e are MultiPoint geometries and f is a LineString. /// \image html touches.png /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Touches(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Touches(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Touches(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this /// one. /// /// \return /// True if this geometry spatially touches the other geometry, /// false otherwise. /// virtual bool Touches(MgGeometry* other); ////////////////////////////////////////////////////////////////////// /// \brief /// Given 2 geometries a and b, a.Within(b) is true if and only /// if the intersection of a and b is a and the intersection of /// the interior of a and the interior of b is not empty. /// /// \remarks /// If the entire point-set of a geometry intersects the boundary /// of another geometry, the former is not within the latter. ///

Illustration

/// The end point of d and the end point of e intersect. a, b, i, /// j, k, and m are MultiPoints. The concentric circles represent /// intersecting points. The polygon n1n2n3n4 is within the /// polygon p1p2p3p4 and vice versa. The LineString q1q2 is /// within the LineString r1r2 and vice versa. The MultiPoint j /// is within the MultiPoint k and vice versa. The Point f is /// within the point g and vice versa. /// \n /// \image html within.png /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual bool Within(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual boolean Within(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual bool Within(MgGeometry other); /// \htmlinclude SyntaxBottom.html /// /// \param other (MgGeometry) /// The MgGeometry to test against this /// one. /// /// \return /// True if this geometry is spatially within the other geometry, /// false otherwise. /// virtual bool Within(MgGeometry* other); }; }}} `MgPreparedGeometry` instances are obtained by calling a new `Prepare` method on a `MgGeometry` instance {{{ class MG_GEOMETRY_API MgGeometry : public MgGeometricEntity { PUBLISHED_API: ... /////////////////////////////////////////////////////////////////////// /// \brief /// Returns a prepared version of this geometry that is optimized for /// repeated evaluation of spatial predicate operations /// /// /// \htmlinclude DotNetSyntaxTop.html /// virtual MgGeometry Prepare(); /// \htmlinclude SyntaxBottom.html /// \htmlinclude JavaSyntaxTop.html /// virtual MgGeometry Prepare(); /// \htmlinclude SyntaxBottom.html /// \htmlinclude PHPSyntaxTop.html /// virtual MgGeometry Prepare(); /// \htmlinclude SyntaxBottom.html /// /// \return /// An MgPreparedGeometry representing the prepared version of this geometry /// MgPreparedGeometry* Prepare(); }; }}} It is important to note that the use-case of `MgPreparedGeometry` is primarily for fast/repeated evaluation of spatial predicates. As such, `MgPreparedGeometry` does not follow the geometry class hierarchy like its `MgGeometry` counterpart. If one wants to know the actual geometric information about the `MgPreparedGeometry`, they should retain a reference to the original `MgGeometry` that they `Prepare()`'d from. Implementation-wise, `MgPreparedGeometry` wraps the underlying `PreparedGeometry` class provided by GEOS. We use the pimpl pattern to avoid leaking out the `PreparedGeometry` dependency (and consequently any geos headers) in the public header of `MgPreparedGeometry`. == Implications == These are new API additions. == Test Plan == Test new mapagent geo-processing operations and verify that their output is the same as the results provided by using `MgGeometry` APIs directly. Benchmark repeated evaluation of spatial operations using `MgGeometry` vs `MgPreparedGeometry`. Verify that using `MgPreparedGeometry` provides noticeable performance over using `MgGeometry` == Funding / Resources == Community