| 1 | |
| 2 | = !MapGuide RFC XYZ - Geo-Processing Services = |
| 3 | |
| 4 | This page contains a change request (RFC) for the !MapGuide Open Source project. |
| 5 | More !MapGuide RFCs can be found on the [wiki:MapGuideRfcs RFCs] page. |
| 6 | |
| 7 | == Status == |
| 8 | |
| 9 | ||RFC Template Version||(1.0)|| |
| 10 | ||Submission Date||14 Jun 2017|| |
| 11 | ||Last Modified|||| |
| 12 | ||Author||Jackie Ng|| |
| 13 | ||RFC Status||draft|| |
| 14 | ||Implementation Status|||| |
| 15 | ||Proposed Milestone||3.3|| |
| 16 | ||Assigned PSC guide(s)||(when determined)|| |
| 17 | ||'''Voting History'''||(vote date)|| |
| 18 | ||+1|||| |
| 19 | ||+0|||| |
| 20 | ||-0|||| |
| 21 | ||-1|||| |
| 22 | ||no vote|| || |
| 23 | |
| 24 | == Overview == |
| 25 | |
| 26 | 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. |
| 27 | |
| 28 | == Motivation == |
| 29 | |
| 30 | The mapagent in its current form offers no services for geo-processing. Such capabilities require custom code using MgGeometry from the MapGuide Web API. |
| 31 | |
| 32 | 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. |
| 33 | |
| 34 | == Proposed Solution == |
| 35 | |
| 36 | This RFC enhances the mapagent with support for the following operations: |
| 37 | |
| 38 | === Buffer === |
| 39 | |
| 40 | Performs a buffer of the given input geometry in with the specified distance. |
| 41 | |
| 42 | This is the mapagent equivalent of `MgGeometry::Buffer()` |
| 43 | |
| 44 | || '''Name''' || '''Value''' || '''Required''' || '''Description''' || |
| 45 | || OPERATION || GEO.BUFFER || Yes || Operation to execute || |
| 46 | || VERSION || 3.3.0 || Yes || Operation version || |
| 47 | || CLIENTAGENT || text || Optional || Descriptive text for client || |
| 48 | || GEOMETRY || string || Yes || The Well-Known Text of the geometry to buffer || |
| 49 | || DISTANCE || number || Yes || The buffer distance in the specific units || |
| 50 | || UNITS || mi/km/ft/m || Yes || miles/kilometers/feet/meters || |
| 51 | || FORMAT || WKT/GEOJSON || Yes || Output the buffer result as WKT or GeoJSON || |
| 52 | || COORDINATESYSTEM || string || Yes || The coordinate system (CS-Map code) of the input geometry || |
| 53 | || 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 || |
| 54 | |
| 55 | === Simplify === |
| 56 | |
| 57 | Returns a simplified form of the given input geometry. |
| 58 | |
| 59 | This is the mapagent equivalent of `MgGeometrySimplifier::Simplify()` |
| 60 | |
| 61 | || '''Name''' || '''Value''' || '''Required''' || '''Description''' || |
| 62 | || OPERATION || GEO.SIMPLIFY || Yes || Operation to execute || |
| 63 | || VERSION || 3.3.0 || Yes || Operation version || |
| 64 | || CLIENTAGENT || text || Optional || Descriptive text for client || |
| 65 | || GEOMETRY || string || Yes || The Well-Known Text of the geometry to simplify || |
| 66 | || TOLERANCE || number || Yes || The simplification tolerance || |
| 67 | || ALGORITHM || 0/1 || Yes || 0=Douglas-Peucker, 1=Topology Preserving || |
| 68 | || FORMAT || WKT/GEOJSON || Yes || Output the simplification result as WKT or GeoJSON || |
| 69 | || 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 || |
| 70 | || 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 || |
| 71 | |
| 72 | === Binary Operation === |
| 73 | |
| 74 | Performs the given binary operation against the 2 input geometries specified (in WKT) as WKT or GeoJSON with optional transformation. |
| 75 | |
| 76 | The following binary operations are supported: |
| 77 | |
| 78 | * Union |
| 79 | * Difference |
| 80 | * Intersection |
| 81 | * SymmetricDifference |
| 82 | |
| 83 | || '''Name''' || '''Value''' || '''Required''' || '''Description''' || |
| 84 | || OPERATION || GEO.BINARYOPERATION|| Yes || Operation to execute || |
| 85 | || VERSION || 3.3.0 || Yes || Operation version || |
| 86 | || CLIENTAGENT || text || Optional || Descriptive text for client || |
| 87 | || GEOMETRYA || string || Yes || The Well-Known Text of the first input geometry || |
| 88 | || GEOMETRYB || string || Yes || The Well-Known Text of the second input geometry || |
| 89 | || OPERATOR || UNION/DIFFERENCE/INTERSECTION/SYMMETRICDIFFERENCE || Yes || The desired operator for this operation || |
| 90 | || FORMAT || WKT/GEOJSON || Yes || Output the operation result as WKT or GeoJSON || |
| 91 | || 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 || |
| 92 | || 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 || |
| 93 | |
| 94 | === Spatial Predicate === |
| 95 | |
| 96 | Tests and returns whether the 2 input geometries specified (in WKT) passes the given spatial predicate. |
| 97 | |
| 98 | The following spatial predicates are supported: |
| 99 | |
| 100 | * Contains |
| 101 | * Crosses |
| 102 | * Disjoint |
| 103 | * Equals |
| 104 | * Intersects |
| 105 | * Overlaps |
| 106 | * Touches |
| 107 | * Within |
| 108 | |
| 109 | || '''Name''' || '''Value''' || '''Required''' || '''Description''' || |
| 110 | || OPERATION || GEO.BINARYOPERATION|| Yes || Operation to execute || |
| 111 | || VERSION || 3.3.0 || Yes || Operation version || |
| 112 | || CLIENTAGENT || text || Optional || Descriptive text for client || |
| 113 | || GEOMETRYA || string || Yes || The Well-Known Text of the first input geometry || |
| 114 | || GEOMETRYB || string || Yes || The Well-Known Text of the second input geometry || |
| 115 | || OPERATOR || CONTAINS/CROSSES/DISJOINT/EQUALS/INTERSECTS/OVERLAPS/TOUCHES/WITHIN || Yes || The desired operator for this spatial predicate test ||result || |
| 116 | |
| 117 | == MgGeometry enhancements == |
| 118 | |
| 119 | In addition to mapagent enhancements, this RFC enhances the `MgGeometry` class with support for "prepared" geometries. |
| 120 | |
| 121 | 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. |
| 122 | |
| 123 | The RFC introduces a new `MgPreparedGeometry` class to the MapGuide API |
| 124 | |
| 125 | {{{ |
| 126 | //////////////////////////////////////////////////////////////// |
| 127 | /// \brief |
| 128 | /// MgPreparedGeometry is an prepared form of MgGeometry optimized for the case of |
| 129 | /// repeated evaluation of spatial predicates against it and any other geometry |
| 130 | /// |
| 131 | class MG_GEOMETRY_API MgPreparedGeometry : public MgGuardDisposable |
| 132 | { |
| 133 | PUBLISHED_API: |
| 134 | //////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 135 | /// \brief |
| 136 | /// This is a convenience method. Given 2 geometries a and b, |
| 137 | /// a.Contains(b) is true if and only if b.MgGeometry::Within(a) |
| 138 | /// is true. |
| 139 | /// |
| 140 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 141 | /// \htmlinclude DotNetSyntaxTop.html |
| 142 | /// virtual bool Contains(MgGeometry other); |
| 143 | /// \htmlinclude SyntaxBottom.html |
| 144 | /// \htmlinclude JavaSyntaxTop.html |
| 145 | /// virtual boolean Contains(MgGeometry other); |
| 146 | /// \htmlinclude SyntaxBottom.html |
| 147 | /// \htmlinclude PHPSyntaxTop.html |
| 148 | /// virtual bool Contains(MgGeometry other); |
| 149 | /// \htmlinclude SyntaxBottom.html |
| 150 | /// |
| 151 | /// \param other (MgGeometry) |
| 152 | /// The MgGeometry to test against this one. |
| 153 | /// |
| 154 | /// \return |
| 155 | /// True if the other geometry is within this geometry, false |
| 156 | /// otherwise. |
| 157 | /// |
| 158 | virtual bool Contains(MgGeometry* other); |
| 159 | |
| 160 | ////////////////////////////////////////////////////////////////////// |
| 161 | /// \brief |
| 162 | /// Given 2 geometries a and b, a.Crosses(b) is true if and only |
| 163 | /// if the dimension of the intersection of the interior of a and |
| 164 | /// the interior of b is less than the greater of the dimension |
| 165 | /// of the interior of a and the dimension of the interior of b |
| 166 | /// and the intersection of a and b is neither a nor b. |
| 167 | /// |
| 168 | /// \remarks |
| 169 | /// A Point cannot cross another geometry because the |
| 170 | /// intersection of the Point with the other geometry is the |
| 171 | /// Point. |
| 172 | /// \n |
| 173 | /// Two MultiPoint geometries cannot cross one another because |
| 174 | /// the dimension of the intersection of their interiors, namely |
| 175 | /// the 0-dimension, is not less than the greater of the |
| 176 | /// dimensions of their interiors, namely the 0-dimension. |
| 177 | /// \n |
| 178 | /// [\link OGC99049 OGC99-049 \endlink] implicitly excludes a Crosses |
| 179 | /// relationship between 2 polygons. According to the definition, |
| 180 | /// the possibility of such a relationship would require that the |
| 181 | /// intersection of the interior of one polygon with that of |
| 182 | /// another be a Point or Line. |
| 183 | /// <h2>Illustration</h2> |
| 184 | /// d, e and f are MultiPoint geometries. |
| 185 | /// \n |
| 186 | /// \image html crosses.png |
| 187 | /// |
| 188 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 189 | /// \htmlinclude DotNetSyntaxTop.html |
| 190 | /// virtual bool Crosses(MgGeometry other); |
| 191 | /// \htmlinclude SyntaxBottom.html |
| 192 | /// \htmlinclude JavaSyntaxTop.html |
| 193 | /// virtual boolean Crosses(MgGeometry other); |
| 194 | /// \htmlinclude SyntaxBottom.html |
| 195 | /// \htmlinclude PHPSyntaxTop.html |
| 196 | /// virtual bool Crosses(MgGeometry other); |
| 197 | /// \htmlinclude SyntaxBottom.html |
| 198 | /// |
| 199 | /// \param other (MgGeometry) |
| 200 | /// The MgGeometry to test against this |
| 201 | /// one. |
| 202 | /// |
| 203 | /// \return |
| 204 | /// True if this geometry spatially crosses the other geometry, |
| 205 | /// false otherwise. |
| 206 | /// |
| 207 | virtual bool Crosses(MgGeometry* other); |
| 208 | |
| 209 | ///////////////////////////////////////////////////////////////////////// |
| 210 | /// \brief |
| 211 | /// Given 2 geometries a and b, a.Disjoint(b)is true if and only |
| 212 | /// if the intersection of a and b is empty. |
| 213 | /// |
| 214 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 215 | /// \htmlinclude DotNetSyntaxTop.html |
| 216 | /// virtual bool Disjoint(MgGeometry other); |
| 217 | /// \htmlinclude SyntaxBottom.html |
| 218 | /// \htmlinclude JavaSyntaxTop.html |
| 219 | /// virtual boolean Disjoint(MgGeometry other); |
| 220 | /// \htmlinclude SyntaxBottom.html |
| 221 | /// \htmlinclude PHPSyntaxTop.html |
| 222 | /// virtual bool Disjoint(MgGeometry other); |
| 223 | /// \htmlinclude SyntaxBottom.html |
| 224 | /// |
| 225 | /// \param other (MgGeometry) |
| 226 | /// The MgGeometry to test against this one. |
| 227 | /// |
| 228 | /// \return |
| 229 | /// True if this geometry is spatially disjoint from the other |
| 230 | /// geometry, false otherwise. |
| 231 | /// |
| 232 | virtual bool Disjoint(MgGeometry* other); |
| 233 | |
| 234 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 235 | /// \brief |
| 236 | /// This is a convenience method. Given 2 geometries a and b, |
| 237 | /// a.Intersects(b) is true if and only if a.\link MgGeometry::Disjoint Disjoint \endlink(b) |
| 238 | /// is false. |
| 239 | /// |
| 240 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 241 | /// \htmlinclude DotNetSyntaxTop.html |
| 242 | /// virtual bool Intersects(MgGeometry other); |
| 243 | /// \htmlinclude SyntaxBottom.html |
| 244 | /// \htmlinclude JavaSyntaxTop.html |
| 245 | /// virtual boolean Intersects(MgGeometry other); |
| 246 | /// \htmlinclude SyntaxBottom.html |
| 247 | /// \htmlinclude PHPSyntaxTop.html |
| 248 | /// virtual bool Intersects(MgGeometry other); |
| 249 | /// \htmlinclude SyntaxBottom.html |
| 250 | /// |
| 251 | /// \param other (MgGeometry) |
| 252 | /// The MgGeometry to test against this one. |
| 253 | /// |
| 254 | /// \return |
| 255 | /// True if this geometry is not disjoint with respect to the |
| 256 | /// other geometry, false otherwise. |
| 257 | /// |
| 258 | virtual bool Intersects(MgGeometry* other); |
| 259 | |
| 260 | ///////////////////////////////////////////////////////////////////////// |
| 261 | /// \brief |
| 262 | /// Given 2 geometries a and b, a.Overlaps(b) is true if and only |
| 263 | /// if the dimension of the interior of a equals the dimension of |
| 264 | /// the interior of b equals the dimension of the intersection of |
| 265 | /// the interior of a and the interior of b and the intersection |
| 266 | /// of a and b is neither a nor b. |
| 267 | /// |
| 268 | /// \remarks |
| 269 | /// A Point cannot overlap a Point, and a MultiPoint cannot |
| 270 | /// overlap a Point but a MultiPoint can overlap a MultiPoint. |
| 271 | /// <h2>Illustration</h2> |
| 272 | /// c and d are MultiPoint geometries. |
| 273 | /// \image html overlaps.png |
| 274 | /// |
| 275 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 276 | /// \htmlinclude DotNetSyntaxTop.html |
| 277 | /// virtual bool Overlaps(MgGeometry other); |
| 278 | /// \htmlinclude SyntaxBottom.html |
| 279 | /// \htmlinclude JavaSyntaxTop.html |
| 280 | /// virtual boolean Overlaps(MgGeometry other); |
| 281 | /// \htmlinclude SyntaxBottom.html |
| 282 | /// \htmlinclude PHPSyntaxTop.html |
| 283 | /// virtual bool Overlaps(MgGeometry other); |
| 284 | /// \htmlinclude SyntaxBottom.html |
| 285 | /// |
| 286 | /// \param other (MgGeometry) |
| 287 | /// The MgGeometry to test against this one. |
| 288 | /// |
| 289 | /// \return |
| 290 | /// True if this geometry spatially overlaps the other geometry, |
| 291 | /// false otherwise. |
| 292 | /// |
| 293 | virtual bool Overlaps(MgGeometry* other); |
| 294 | |
| 295 | ////////////////////////////////////////////////////////////////////// |
| 296 | /// \brief |
| 297 | /// Given 2 geometries a and b, a.Touches(b) is true if and only |
| 298 | /// if the intersection of the interior of a and the interior of |
| 299 | /// b is empty and the intersection of a and b is not empty. |
| 300 | /// |
| 301 | /// \remarks |
| 302 | /// A Point cannot touch a Point because a Point has no boundary |
| 303 | /// and so the intersection of the interiors of the two |
| 304 | /// geometries is not empty. |
| 305 | /// \n |
| 306 | /// A Point can touch a non-closed Line at one of the end points |
| 307 | /// of the Line. |
| 308 | /// \n |
| 309 | /// A Point cannot touch a closed Line because all of the points |
| 310 | /// in the Line are interior to it. |
| 311 | /// <h2>Illustration</h2> |
| 312 | /// e are MultiPoint geometries and f is a LineString. |
| 313 | /// \image html touches.png |
| 314 | /// |
| 315 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 316 | /// \htmlinclude DotNetSyntaxTop.html |
| 317 | /// virtual bool Touches(MgGeometry other); |
| 318 | /// \htmlinclude SyntaxBottom.html |
| 319 | /// \htmlinclude JavaSyntaxTop.html |
| 320 | /// virtual boolean Touches(MgGeometry other); |
| 321 | /// \htmlinclude SyntaxBottom.html |
| 322 | /// \htmlinclude PHPSyntaxTop.html |
| 323 | /// virtual bool Touches(MgGeometry other); |
| 324 | /// \htmlinclude SyntaxBottom.html |
| 325 | /// |
| 326 | /// \param other (MgGeometry) |
| 327 | /// The MgGeometry to test against this |
| 328 | /// one. |
| 329 | /// |
| 330 | /// \return |
| 331 | /// True if this geometry spatially touches the other geometry, |
| 332 | /// false otherwise. |
| 333 | /// |
| 334 | virtual bool Touches(MgGeometry* other); |
| 335 | |
| 336 | ////////////////////////////////////////////////////////////////////// |
| 337 | /// \brief |
| 338 | /// Given 2 geometries a and b, a.Within(b) is true if and only |
| 339 | /// if the intersection of a and b is a and the intersection of |
| 340 | /// the interior of a and the interior of b is not empty. |
| 341 | /// |
| 342 | /// \remarks |
| 343 | /// If the entire point-set of a geometry intersects the boundary |
| 344 | /// of another geometry, the former is not within the latter. |
| 345 | /// <h2>Illustration</h2> |
| 346 | /// The end point of d and the end point of e intersect. a, b, i, |
| 347 | /// j, k, and m are MultiPoints. The concentric circles represent |
| 348 | /// intersecting points. The polygon n1n2n3n4 is within the |
| 349 | /// polygon p1p2p3p4 and vice versa. The LineString q1q2 is |
| 350 | /// within the LineString r1r2 and vice versa. The MultiPoint j |
| 351 | /// is within the MultiPoint k and vice versa. The Point f is |
| 352 | /// within the point g and vice versa. |
| 353 | /// \n |
| 354 | /// \image html within.png |
| 355 | /// |
| 356 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 357 | /// \htmlinclude DotNetSyntaxTop.html |
| 358 | /// virtual bool Within(MgGeometry other); |
| 359 | /// \htmlinclude SyntaxBottom.html |
| 360 | /// \htmlinclude JavaSyntaxTop.html |
| 361 | /// virtual boolean Within(MgGeometry other); |
| 362 | /// \htmlinclude SyntaxBottom.html |
| 363 | /// \htmlinclude PHPSyntaxTop.html |
| 364 | /// virtual bool Within(MgGeometry other); |
| 365 | /// \htmlinclude SyntaxBottom.html |
| 366 | /// |
| 367 | /// \param other (MgGeometry) |
| 368 | /// The MgGeometry to test against this |
| 369 | /// one. |
| 370 | /// |
| 371 | /// \return |
| 372 | /// True if this geometry is spatially within the other geometry, |
| 373 | /// false otherwise. |
| 374 | /// |
| 375 | virtual bool Within(MgGeometry* other); |
| 376 | }; |
| 377 | }}} |
| 378 | |
| 379 | `MgPreparedGeometry` instances are obtained by calling a new `Prepare` method on a `MgGeometry` instance |
| 380 | |
| 381 | {{{ |
| 382 | class MG_GEOMETRY_API MgGeometry : public MgGeometricEntity |
| 383 | { |
| 384 | PUBLISHED_API: |
| 385 | ... |
| 386 | |
| 387 | /////////////////////////////////////////////////////////////////////// |
| 388 | /// \brief |
| 389 | /// Returns a prepared version of this geometry that is optimized for |
| 390 | /// repeated evaluation of spatial predicate operations |
| 391 | /// |
| 392 | /// <!-- Syntax in .Net, Java, and PHP --> |
| 393 | /// \htmlinclude DotNetSyntaxTop.html |
| 394 | /// virtual MgGeometry Prepare(); |
| 395 | /// \htmlinclude SyntaxBottom.html |
| 396 | /// \htmlinclude JavaSyntaxTop.html |
| 397 | /// virtual MgGeometry Prepare(); |
| 398 | /// \htmlinclude SyntaxBottom.html |
| 399 | /// \htmlinclude PHPSyntaxTop.html |
| 400 | /// virtual MgGeometry Prepare(); |
| 401 | /// \htmlinclude SyntaxBottom.html |
| 402 | /// |
| 403 | /// \return |
| 404 | /// An MgPreparedGeometry representing the prepared version of this geometry |
| 405 | /// |
| 406 | MgPreparedGeometry* Prepare(); |
| 407 | }; |
| 408 | }}} |
| 409 | |
| 410 | 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. |
| 411 | |
| 412 | 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`. |
| 413 | |
| 414 | == Implications == |
| 415 | |
| 416 | These are new API additions. |
| 417 | |
| 418 | == Test Plan == |
| 419 | |
| 420 | Test new mapagent geo-processing operations and verify that their output is the same as the results provided by using `MgGeometry` APIs directly. |
| 421 | |
| 422 | Benchmark repeated evaluation of spatial operations using `MgGeometry` vs `MgPreparedGeometry`. Verify that using `MgPreparedGeometry` provides noticeable performance over using `MgGeometry` |
| 423 | |
| 424 | == Funding / Resources == |
| 425 | |
| 426 | Community |