diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5505d4f..b2e32e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,16 +8,6 @@ on: branches: [main] jobs: - podspec: - name: Lint Podspec for ${{ matrix.platform }} - runs-on: macos-14 - strategy: - matrix: - platform: [ios, osx, tvos] - steps: - - uses: actions/checkout@v3 - - name: Lint Podspec - run: pod lib lint --platforms=${{ matrix.platform }} xcodebuild: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf1b2bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +Packages/ +Package.pins +# Package.resolved +*.xcodeproj + +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# AI Tools +CLAUDE.md +.claude \ No newline at end of file diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..da33d33 --- /dev/null +++ b/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "geos", + platforms: [.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4), .visionOS(.v1)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "geos", + type: .dynamic, + targets: ["geos"] + ), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "geos", + publicHeadersPath: "public", + cxxSettings: [ + .define("USE_UNSTABLE_GEOS_CPP_API"), + .define("NDEBUG"), + .headerSearchPath("include"), + .headerSearchPath("src/deps") + ] + ) + ], + cxxLanguageStandard: .cxx17 +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..e68cad7 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# GEOS + +This library is an SPM-compatible repackaging of the source code from [libgeos/geos](https://github.com/libgeos/geos) for use on Apple and Linux systems. + +> GEOS is a C++ library for performing operations on two-dimensional vector +> geometries. It is primarily a port of the [JTS Topology +> Suite](https://github.com/locationtech/jts) Java library. It provides many of +> the algorithms used by [PostGIS](http://www.postgis.net/), the +> [Shapely](https://pypi.org/project/Shapely/) package for Python, the +> [sf](https://github.com/r-spatial/sf) package for R, and others. + +## Requirements + +- Swift 5.9+ +- iOS 12.0+ / macOS 10.13+ / tvOS 12.0+ / watchOS 4.0+ / visionOS 1.0+ +- Linux + +## Usage + +### Swift Package Manager (SPM) + +Add the following to your `Package.swift`: + +```swift +let package = Package( + name: "YourPackage", + dependencies: [ + .package(url: "https://github.com/GEOSwift/geos.git", from: "11.0.0") + ], + targets: [ + .target( + name: "YourTarget", + dependencies: [ + .product(name: "geos", package: "geos") + ] + ) + ] +) +``` + +### Basic Usage + +```swift +import geos + +// The geos C API is now available for use +``` + +> [!NOTE] +> `geos` is built as a dynamically-linked library for maximum compliance with the LGPL 2.1 license. Use of statically-linked `geos` is discouraged. + +## Versioning + +Currently version 11.0.0 packages [libgeos/geos 3.14.1](https://github.com/libgeos/geos/releases/tag/3.14.1). + +This package follows [SemVer](https://semver.org) principles and therefore its versions don't map 1:1 with the underlying geos library. When a geos release identifies any changes as breaking, we will release a new major version of this library. + +## Licensing + +The source code contained in this repo is released under a dual license: +* The contents of the `Sources` directory are taken from [libgeos/geos](https://github.com/libgeos/geos) and distributed under the LGPL 2.1 license (see [COPYING](https://github.com/GEOSwift/geos/blob/main/Sources/COPYING)). +* The other content of this repo is distributed under the MIT [LICENSE](https://github.com/GEOSwift/geos/blob/main/LICENSE). + +## Releasing a new version + +* Create a new branch. +* Modify `update.sh` to successfully pull the new version of geos and construct an SPM-compatible `Sources` directory. This only entails pointing at a new source archive unless geos releases a new version with different build structure (rare). +* Run `update.sh`. +* Modify `Package.swift` if necessary. +* Test the supported platforms (Apple Devices/Linux) with both CLI and Xcode. +* Update this `README.md` with any relevant information (e.g. new version numbers). +* Commit all modifications and open a PR. +* Once merged, release a new version numbered according to [SemVer](https://semver.org) principles. If the underlying geos release contains *any* breaking changes, increment the major release here. Minor and patch version increments are at your discretion. + diff --git a/Sources/COPYING b/Sources/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/Sources/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Sources/geos/capi/geos_c.cpp b/Sources/geos/capi/geos_c.cpp new file mode 100644 index 0000000..35d14fd --- /dev/null +++ b/Sources/geos/capi/geos_c.cpp @@ -0,0 +1,2077 @@ +/************************************************************************ + * + * + * C-Wrapper for GEOS library + * + * Copyright (C) 2010 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * Author: Sandro Santilli + * + ***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4099) +#endif + +// Some extra magic to make type declarations in geos_c.h work - +// for cross-checking of types in header. +// NOTE: the below defines or struct definition must be kept in exact +// sync between geos_c.cpp and geos_ts_c.cpp to avoid C++ One Definition Rule +// violations. +#define GEOSGeometry geos::geom::Geometry +#define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry +#define GEOSClusterInfo geos::operation::cluster::Clusters +#define GEOSCoordSequence geos::geom::CoordinateSequence +#define GEOSBufferParams geos::operation::buffer::BufferParameters +#define GEOSSTRtree geos::index::strtree::TemplateSTRtree +#define GEOSWKTReader geos::io::WKTReader +#define GEOSWKTWriter geos::io::WKTWriter +#define GEOSWKBReader geos::io::WKBReader +#define GEOSWKBWriter geos::io::WKBWriter +#define GEOSGeoJSONReader geos::io::GeoJSONReader +#define GEOSGeoJSONWriter geos::io::GeoJSONWriter + +// Implementation struct for the GEOSCoverageCleanParams object +typedef struct { + double snappingDistance; + int overlapMergeStrategy; + double gapMaximumWidth; +} GEOSCoverageCleanParams; + +// Implementation struct for the GEOSMakeValidParams object +typedef struct { + int method; + int keepCollapsed; +} GEOSMakeValidParams; + + +#include "geos_c.h" + +/// Define this if you want operations triggering Exceptions to +/// be printed (will use the NOTIFY channel - only implemented for GEOSUnion so far) +/// +#undef VERBOSE_EXCEPTIONS + +#include + +/* +#if defined(_MSC_VER) +# define GEOS_DLL __declspec(dllexport) +#else +# define GEOS_DLL +#endif +*/ + +// import the most frequently used definitions globally +using geos::geom::Geometry; +using geos::geom::LineString; +using geos::geom::Polygon; +using geos::geom::CoordinateSequence; +using geos::geom::GeometryFactory; + +using geos::io::WKTReader; +using geos::io::WKTWriter; +using geos::io::WKBReader; +using geos::io::WKBWriter; +using geos::io::GeoJSONReader; +using geos::io::GeoJSONWriter; + + +typedef std::unique_ptr GeomPtr; + +//## GLOBALS ################################################ + +// NOTE: SRID will have to be changed after geometry creation +GEOSContextHandle_t handle = NULL; + +extern "C" { + + void + initGEOS(GEOSMessageHandler nf, GEOSMessageHandler ef) + { + if(! handle) { + handle = initGEOS_r(nf, ef); + } + else { + GEOSContext_setNoticeHandler_r(handle, nf); + GEOSContext_setErrorHandler_r(handle, ef); + } + + geos::util::Interrupt::cancel(); + } + + void + finishGEOS() + { + if(handle != NULL) { + finishGEOS_r(handle); + handle = NULL; + } + } + + GEOSInterruptCallback* + GEOS_interruptRegisterCallback(GEOSInterruptCallback* cb) + { + return geos::util::Interrupt::registerCallback(cb); + } + + void + GEOS_interruptRequest() + { + geos::util::Interrupt::request(); + } + + void + GEOS_interruptCancel() + { + geos::util::Interrupt::cancel(); + } + + void + GEOSFree(void* buffer) + { + GEOSFree_r(handle, buffer); + } + + /**************************************************************** + ** relate()-related functions + ** return 0 = false, 1 = true, 2 = error occurred + ** + */ + char + GEOSDisjoint(const Geometry* g1, const Geometry* g2) + { + return GEOSDisjoint_r(handle, g1, g2); + } + + char + GEOSTouches(const Geometry* g1, const Geometry* g2) + { + return GEOSTouches_r(handle, g1, g2); + } + + char + GEOSIntersects(const Geometry* g1, const Geometry* g2) + { + return GEOSIntersects_r(handle, g1, g2); + } + + char + GEOSCrosses(const Geometry* g1, const Geometry* g2) + { + return GEOSCrosses_r(handle, g1, g2); + } + + char + GEOSWithin(const Geometry* g1, const Geometry* g2) + { + return GEOSWithin_r(handle, g1, g2); + } + +// call g1->contains(g2) +// returns 0 = false +// 1 = true +// 2 = error was trapped + char + GEOSContains(const Geometry* g1, const Geometry* g2) + { + return GEOSContains_r(handle, g1, g2); + } + + char + GEOSOverlaps(const Geometry* g1, const Geometry* g2) + { + return GEOSOverlaps_r(handle, g1, g2); + } + + char + GEOSCovers(const Geometry* g1, const Geometry* g2) + { + return GEOSCovers_r(handle, g1, g2); + } + + char + GEOSCoveredBy(const Geometry* g1, const Geometry* g2) + { + return GEOSCoveredBy_r(handle, g1, g2); + } + + +//------------------------------------------------------------------- +// low-level relate functions +//------------------------------------------------------------------ + + char + GEOSRelatePattern(const Geometry* g1, const Geometry* g2, const char* imPattern) + { + return GEOSRelatePattern_r(handle, g1, g2, imPattern); + } + + char + GEOSRelatePatternMatch(const char* intMatrix, const char* imPattern) + { + return GEOSRelatePatternMatch_r(handle, intMatrix, imPattern); + } + + char* + GEOSRelate(const Geometry* g1, const Geometry* g2) + { + return GEOSRelate_r(handle, g1, g2); + } + + char* + GEOSRelateBoundaryNodeRule(const Geometry* g1, const Geometry* g2, int bnr) + { + return GEOSRelateBoundaryNodeRule_r(handle, g1, g2, bnr); + } + + +//----------------------------------------------------------------- +// isValid +//----------------------------------------------------------------- + + + char + GEOSisValid(const Geometry* g) + { + return GEOSisValid_r(handle, g); + } + + char* + GEOSisValidReason(const Geometry* g) + { + return GEOSisValidReason_r(handle, g); + } + + char + GEOSisValidDetail(const Geometry* g, int flags, + char** reason, Geometry** location) + { + return GEOSisValidDetail_r(handle, g, flags, reason, location); + } + +//----------------------------------------------------------------- +// general purpose +//----------------------------------------------------------------- + + char + GEOSEquals(const Geometry* g1, const Geometry* g2) + { + return GEOSEquals_r(handle, g1, g2); + } + + char + GEOSEqualsExact(const Geometry* g1, const Geometry* g2, double tolerance) + { + return GEOSEqualsExact_r(handle, g1, g2, tolerance); + } + + char + GEOSEqualsIdentical(const Geometry* g1, const Geometry* g2) + { + return GEOSEqualsIdentical_r(handle, g1, g2); + } + + int + GEOSDistance(const Geometry* g1, const Geometry* g2, double* dist) + { + return GEOSDistance_r(handle, g1, g2, dist); + } + + char + GEOSDistanceWithin(const Geometry* g1, const Geometry* g2, double dist) + { + return GEOSDistanceWithin_r(handle, g1, g2, dist); + } + + int + GEOSDistanceIndexed(const Geometry* g1, const Geometry* g2, double* dist) + { + return GEOSDistanceIndexed_r(handle, g1, g2, dist); + } + + int + GEOSHausdorffDistance(const Geometry* g1, const Geometry* g2, double* dist) + { + return GEOSHausdorffDistance_r(handle, g1, g2, dist); + } + + int + GEOSHausdorffDistanceDensify(const Geometry* g1, const Geometry* g2, double densifyFrac, double* dist) + { + return GEOSHausdorffDistanceDensify_r(handle, g1, g2, densifyFrac, dist); + } + + int + GEOSFrechetDistance(const Geometry* g1, const Geometry* g2, double* dist) + { + return GEOSFrechetDistance_r(handle, g1, g2, dist); + } + + int + GEOSFrechetDistanceDensify(const Geometry* g1, const Geometry* g2, double densifyFrac, double* dist) + { + return GEOSFrechetDistanceDensify_r(handle, g1, g2, densifyFrac, dist); + } + + int + GEOSArea(const Geometry* g, double* area) + { + return GEOSArea_r(handle, g, area); + } + + int + GEOSLength(const Geometry* g, double* length) + { + return GEOSLength_r(handle, g, length); + } + + CoordinateSequence* + GEOSNearestPoints(const Geometry* g1, const Geometry* g2) + { + return GEOSNearestPoints_r(handle, g1, g2); + } + + GEOSClusterInfo* + GEOSClusterDBSCAN(const GEOSGeometry* g, double eps, unsigned minPoints) + { + return GEOSClusterDBSCAN_r(handle, g, eps, minPoints); + } + + GEOSClusterInfo* + GEOSClusterGeometryDistance(const GEOSGeometry* g, double d) + { + return GEOSClusterGeometryDistance_r(handle, g, d); + } + + GEOSClusterInfo* + GEOSClusterGeometryIntersects(const GEOSGeometry* g) + { + return GEOSClusterGeometryIntersects_r(handle, g); + } + + GEOSClusterInfo* + GEOSClusterEnvelopeDistance(const GEOSGeometry* g, double d) + { + return GEOSClusterEnvelopeDistance_r(handle, g, d); + } + + GEOSClusterInfo* + GEOSClusterEnvelopeIntersects(const GEOSGeometry* g) + { + return GEOSClusterEnvelopeIntersects_r(handle, g); + } + + std::size_t GEOSClusterInfo_getNumClusters(const GEOSClusterInfo* clusters) + { + return GEOSClusterInfo_getNumClusters_r(handle, clusters); + } + + std::size_t GEOSClusterInfo_getClusterSize(const GEOSClusterInfo* clusters, size_t i) + { + return GEOSClusterInfo_getClusterSize_r(handle, clusters, i); + } + + const std::size_t* GEOSClusterInfo_getInputsForClusterN(const GEOSClusterInfo* clusters, size_t i) + { + return GEOSClusterInfo_getInputsForClusterN_r(handle, clusters, i); + } + + std::size_t* GEOSClusterInfo_getClustersForInputs(const GEOSClusterInfo* clusters) + { + return GEOSClusterInfo_getClustersForInputs_r(handle, clusters); + } + + void GEOSClusterInfo_destroy(GEOSClusterInfo* info) + { + GEOSClusterInfo_destroy_r(handle, info); + } + + Geometry* + GEOSGeomFromWKT(const char* wkt) + { + return GEOSGeomFromWKT_r(handle, wkt); + } + + char* + GEOSGeomToWKT(const Geometry* g) + { + return GEOSGeomToWKT_r(handle, g); + } + +// Remember to free the result! + unsigned char* + GEOSGeomToWKB_buf(const Geometry* g, std::size_t* size) + { + return GEOSGeomToWKB_buf_r(handle, g, size); + } + + Geometry* + GEOSGeomFromWKB_buf(const unsigned char* wkb, std::size_t size) + { + return GEOSGeomFromWKB_buf_r(handle, wkb, size); + } + + /* Read/write wkb hex values. Returned geometries are + owned by the caller.*/ + unsigned char* + GEOSGeomToHEX_buf(const Geometry* g, std::size_t* size) + { + return GEOSGeomToHEX_buf_r(handle, g, size); + } + + Geometry* + GEOSGeomFromHEX_buf(const unsigned char* hex, std::size_t size) + { + return GEOSGeomFromHEX_buf_r(handle, hex, size); + } + + char + GEOSisEmpty(const Geometry* g) + { + return GEOSisEmpty_r(handle, g); + } + + char + GEOSisSimple(const Geometry* g) + { + return GEOSisSimple_r(handle, g); + } + + char + GEOSisSimpleDetail(const Geometry* g, int returnAllPoints, Geometry** result) + { + return GEOSisSimpleDetail_r(handle, g, returnAllPoints, result); + } + + char + GEOSisRing(const Geometry* g) + { + return GEOSisRing_r(handle, g); + } + + + +//free the result of this + char* + GEOSGeomType(const Geometry* g) + { + return GEOSGeomType_r(handle, g); + } + +// Return postgis geometry type index + int + GEOSGeomTypeId(const Geometry* g) + { + return GEOSGeomTypeId_r(handle, g); + } + + + + +//------------------------------------------------------------------- +// GEOS functions that return geometries +//------------------------------------------------------------------- + + Geometry* + GEOSEnvelope(const Geometry* g) + { + return GEOSEnvelope_r(handle, g); + } + + Geometry* + GEOSIntersection(const Geometry* g1, const Geometry* g2) + { + return GEOSIntersection_r(handle, g1, g2); + } + + Geometry* + GEOSIntersectionPrec(const Geometry* g1, const Geometry* g2, double gridSize) + { + return GEOSIntersectionPrec_r(handle, g1, g2, gridSize); + } + + Geometry* + GEOSBuffer(const Geometry* g, double width, int quadrantsegments) + { + return GEOSBuffer_r(handle, g, width, quadrantsegments); + } + + Geometry* + GEOSBufferWithStyle(const Geometry* g, double width, int quadsegs, + int endCapStyle, int joinStyle, double mitreLimit) + { + return GEOSBufferWithStyle_r(handle, g, width, quadsegs, endCapStyle, + joinStyle, mitreLimit); + } + + Geometry* + GEOSDensify(const Geometry* g, double tolerance) + { + return GEOSDensify_r(handle, g, tolerance); + } + + + Geometry* + GEOSSingleSidedBuffer(const Geometry* g, double width, int quadsegs, + int joinStyle, double mitreLimit, int leftSide) + { + return GEOSSingleSidedBuffer_r(handle, g, width, quadsegs, + joinStyle, mitreLimit, leftSide); + } + + Geometry* + GEOSOffsetCurve(const Geometry* g, double width, int quadsegs, + int joinStyle, double mitreLimit) + { + return GEOSOffsetCurve_r(handle, g, width, quadsegs, + joinStyle, mitreLimit); + } + + Geometry* + GEOSConvexHull(const Geometry* g) + { + return GEOSConvexHull_r(handle, g); + } + + Geometry* + GEOSConcaveHull(const Geometry* g, + double ratio, + unsigned int allowHoles) + + { + return GEOSConcaveHull_r(handle, g, ratio, allowHoles); + } + + Geometry* + GEOSConcaveHullByLength(const Geometry* g, + double length, + unsigned int allowHoles) + + { + return GEOSConcaveHullByLength_r(handle, g, length, allowHoles); + } + + Geometry* + GEOSPolygonHullSimplify(const Geometry* g, + unsigned int isOuter, + double vertexNumFraction) + { + return GEOSPolygonHullSimplify_r(handle, g, isOuter, vertexNumFraction); + } + + Geometry* + GEOSPolygonHullSimplifyMode(const Geometry* g, + unsigned int isOuter, + unsigned int parameterMode, + double parameter) + { + return GEOSPolygonHullSimplifyMode_r(handle, g, isOuter, parameterMode, parameter); + } + + Geometry* + GEOSConcaveHullOfPolygons(const Geometry* g, + double lengthRatio, + unsigned int isTight, + unsigned int isHolesAllowed) + { + return GEOSConcaveHullOfPolygons_r(handle, + g, lengthRatio, isTight, isHolesAllowed); + } + + Geometry* + GEOSMinimumRotatedRectangle(const Geometry* g) + { + return GEOSMinimumRotatedRectangle_r(handle, g); + } + + Geometry* + GEOSMaximumInscribedCircle(const Geometry* g, double tolerance) + { + return GEOSMaximumInscribedCircle_r(handle, g, tolerance); + } + + Geometry* + GEOSLargestEmptyCircle(const Geometry* g, const Geometry* boundary, double tolerance) + { + return GEOSLargestEmptyCircle_r(handle, g, boundary, tolerance); + } + + Geometry* + GEOSMinimumWidth(const Geometry* g) + { + return GEOSMinimumWidth_r(handle, g); + } + + Geometry* + GEOSMinimumClearanceLine(const Geometry* g) + { + return GEOSMinimumClearanceLine_r(handle, g); + } + + int + GEOSMinimumClearance(const Geometry* g, double* d) + { + return GEOSMinimumClearance_r(handle, g, d); + } + + Geometry* + GEOSDifference(const Geometry* g1, const Geometry* g2) + { + return GEOSDifference_r(handle, g1, g2); + } + + Geometry* + GEOSDifferencePrec(const Geometry* g1, const Geometry* g2, double gridSize) + { + return GEOSDifferencePrec_r(handle, g1, g2, gridSize); + } + + Geometry* + GEOSBoundary(const Geometry* g) + { + return GEOSBoundary_r(handle, g); + } + + Geometry* + GEOSSymDifference(const Geometry* g1, const Geometry* g2) + { + return GEOSSymDifference_r(handle, g1, g2); + } + + Geometry* + GEOSSymDifferencePrec(const Geometry* g1, const Geometry* g2, double gridSize) + { + return GEOSSymDifferencePrec_r(handle, g1, g2, gridSize); + } + + Geometry* + GEOSUnion(const Geometry* g1, const Geometry* g2) + { + return GEOSUnion_r(handle, g1, g2); + } + + Geometry* + GEOSUnionPrec(const Geometry* g1, const Geometry* g2, double gridSize) + { + return GEOSUnionPrec_r(handle, g1, g2, gridSize); + } + + Geometry* + GEOSUnaryUnion(const Geometry* g) + { + return GEOSUnaryUnion_r(handle, g); + } + + Geometry* + GEOSUnaryUnionPrec(const Geometry* g, double gridSize) + { + return GEOSUnaryUnionPrec_r(handle, g, gridSize); + } + + Geometry* + GEOSCoverageUnion(const Geometry* g) + { + return GEOSCoverageUnion_r(handle, g); + } + + Geometry* + GEOSDisjointSubsetUnion(const Geometry* g) + { + return GEOSDisjointSubsetUnion_r(handle, g); + } + + Geometry* + GEOSNode(const Geometry* g) + { + return GEOSNode_r(handle, g); + } + + Geometry* + GEOSUnionCascaded(const Geometry* g) + { + return GEOSUnionCascaded_r(handle, g); + } + + Geometry* + GEOSPointOnSurface(const Geometry* g) + { + return GEOSPointOnSurface_r(handle, g); + } + + + Geometry* + GEOSClipByRect(const Geometry* g, double xmin, double ymin, double xmax, double ymax) + { + return GEOSClipByRect_r(handle, g, xmin, ymin, xmax, ymax); + } + + int + GEOSGridIntersectionFractions(const Geometry* g, double xmin, double ymin, double xmax, double ymax, + unsigned nx, unsigned ny, float* buf) + { + return GEOSGridIntersectionFractions_r(handle, g, xmin, ymin, xmax, ymax, nx, ny, buf); + } + + Geometry* + GEOSGeom_transformXY(const GEOSGeometry* g, GEOSTransformXYCallback callback, void* userdata) { + return GEOSGeom_transformXY_r(handle, g, callback, userdata); + } + + + Geometry* + GEOSGeom_transformXYZ(const GEOSGeometry* g, GEOSTransformXYZCallback callback, void* userdata) { + return GEOSGeom_transformXYZ_r(handle, g, callback, userdata); + } + + +//------------------------------------------------------------------- +// memory management functions +//------------------------------------------------------------------ + + + void + GEOSGeom_destroy(Geometry* a) + { + return GEOSGeom_destroy_r(handle, a); + } + + + int + GEOSGetNumCoordinates(const Geometry* g) + { + return GEOSGetNumCoordinates_r(handle, g); + } + + /* + * Return -1 on exception, 0 otherwise. + * Converts Geometry to normal form (or canonical form). + */ + int + GEOSNormalize(Geometry* g) + { + return GEOSNormalize_r(handle, g); + } + + int + GEOSOrientPolygons(Geometry* g, int exteriorCW) + { + return GEOSOrientPolygons_r(handle, g, exteriorCW); + } + + int + GEOSGetNumInteriorRings(const Geometry* g) + { + return GEOSGetNumInteriorRings_r(handle, g); + } + + +// returns -1 on error and 1 for non-multi geometries + int + GEOSGetNumGeometries(const Geometry* g) + { + return GEOSGetNumGeometries_r(handle, g); + } + + + /* + * Call only on GEOMETRYCOLLECTION or MULTI*. + * Return a pointer to the internal Geometry. + */ + const Geometry* + GEOSGetGeometryN(const Geometry* g, int n) + { + return GEOSGetGeometryN_r(handle, g, n); + } + + /* + * Call only on LINESTRING + * Returns NULL on exception + */ + Geometry* + GEOSGeomGetPointN(const Geometry* g, int n) + { + return GEOSGeomGetPointN_r(handle, g, n); + } + + /* + * Call only on LINESTRING + */ + Geometry* + GEOSGeomGetStartPoint(const Geometry* g) + { + return GEOSGeomGetStartPoint_r(handle, g); + } + + /* + * Call only on LINESTRING + */ + Geometry* + GEOSGeomGetEndPoint(const Geometry* g) + { + return GEOSGeomGetEndPoint_r(handle, g); + } + + /* + * Call only on LINESTRING + * return 2 on exception, 1 on true, 0 on false + */ + char + GEOSisClosed(const Geometry* g) + { + return GEOSisClosed_r(handle, g); + } + + /* + * Call only on LINESTRING + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetLength(const Geometry* g, double* length) + { + return GEOSGeomGetLength_r(handle, g, length); + } + + /* + * Call only on LINESTRING + * returns -1 on exception + */ + int + GEOSGeomGetNumPoints(const Geometry* g) + { + return GEOSGeomGetNumPoints_r(handle, g); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetX(const Geometry* g, double* x) + { + return GEOSGeomGetX_r(handle, g, x); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetY(const Geometry* g, double* y) + { + return GEOSGeomGetY_r(handle, g, y); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetZ(const Geometry* g1, double* z) + { + return GEOSGeomGetZ_r(handle, g1, z); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetM(const Geometry* g1, double* m) + { + return GEOSGeomGetM_r(handle, g1, m); + } + + /* + * Call only on polygon + * Return a copy of the internal Geometry. + */ + const Geometry* + GEOSGetExteriorRing(const Geometry* g) + { + return GEOSGetExteriorRing_r(handle, g); + } + + /* + * Call only on polygon + * Return a pointer to internal storage, do not destroy it. + */ + const Geometry* + GEOSGetInteriorRingN(const Geometry* g, int n) + { + return GEOSGetInteriorRingN_r(handle, g, n); + } + + Geometry* + GEOSGetCentroid(const Geometry* g) + { + return GEOSGetCentroid_r(handle, g); + } + + int + GEOSHilbertCode(const GEOSGeometry *geom, const GEOSGeometry* extent, + unsigned int level, unsigned int *code) + { + return GEOSHilbertCode_r(handle, geom, extent, level, code); + } + + Geometry* + GEOSMinimumBoundingCircle(const Geometry* g, double* radius, Geometry** center) + { + return GEOSMinimumBoundingCircle_r(handle, g, radius, center); + } + + Geometry* + GEOSGeom_createCollection(int type, Geometry** geoms, unsigned int ngeoms) + { + return GEOSGeom_createCollection_r(handle, type, geoms, ngeoms); + } + + Geometry** + GEOSGeom_releaseCollection(Geometry* collection, unsigned int * ngeoms) + { + return GEOSGeom_releaseCollection_r(handle, collection, ngeoms); + } + + Geometry* + GEOSPolygonize(const Geometry* const* g, unsigned int ngeoms) + { + return GEOSPolygonize_r(handle, g, ngeoms); + } + + Geometry* + GEOSPolygonize_valid(const Geometry* const* g, unsigned int ngeoms) + { + return GEOSPolygonize_valid_r(handle, g, ngeoms); + } + + Geometry* + GEOSPolygonizer_getCutEdges(const Geometry* const* g, unsigned int ngeoms) + { + return GEOSPolygonizer_getCutEdges_r(handle, g, ngeoms); + } + + GEOSGeometry* + GEOSPolygonize_full(const GEOSGeometry* input, + GEOSGeometry** cuts, GEOSGeometry** dangles, GEOSGeometry** invalid) + { + return GEOSPolygonize_full_r(handle, input, cuts, dangles, invalid); + } + + Geometry* + GEOSBuildArea(const Geometry* g) + { + return GEOSBuildArea_r(handle, g); + } + + Geometry* + GEOSMakeValid(const Geometry* g) + { + return GEOSMakeValid_r(handle, g); + } + + GEOSMakeValidParams* + GEOSMakeValidParams_create() + { + return GEOSMakeValidParams_create_r(handle); + } + + void + GEOSMakeValidParams_destroy(GEOSMakeValidParams* parms) + { + return GEOSMakeValidParams_destroy_r(handle, parms); + } + + int + GEOSMakeValidParams_setMethod( + GEOSMakeValidParams* p, + GEOSMakeValidMethods method) + { + return GEOSMakeValidParams_setMethod_r(handle, p, method); + } + + int + GEOSMakeValidParams_setKeepCollapsed( + GEOSMakeValidParams* p, + int keepCollapsed) + { + return GEOSMakeValidParams_setKeepCollapsed_r(handle, p, keepCollapsed); + } + + Geometry* + GEOSMakeValidWithParams( + const Geometry* g, + const GEOSMakeValidParams* params) + { + return GEOSMakeValidWithParams_r(handle, g, params); + } + + GEOSCoverageCleanParams* + GEOSCoverageCleanParams_create() + { + return GEOSCoverageCleanParams_create_r(handle); + } + + void + GEOSCoverageCleanParams_destroy( + GEOSCoverageCleanParams* params) + { + return GEOSCoverageCleanParams_destroy_r(handle, params); + } + + int + GEOSCoverageCleanParams_setSnappingDistance( + GEOSCoverageCleanParams* params, double snappingDistance) + { + return GEOSCoverageCleanParams_setSnappingDistance_r( + handle, params, snappingDistance); + } + + int + GEOSCoverageCleanParams_setGapMaximumWidth( + GEOSCoverageCleanParams* params, double gapMaximumWidth) + { + return GEOSCoverageCleanParams_setGapMaximumWidth_r( + handle, params, gapMaximumWidth); + } + + int + GEOSCoverageCleanParams_setOverlapMergeStrategy( + GEOSCoverageCleanParams* params, int overlapMergeStrategy) + { + return GEOSCoverageCleanParams_setOverlapMergeStrategy_r( + handle, params, overlapMergeStrategy); + } + + GEOSGeometry * + GEOSCoverageCleanWithParams( + const GEOSGeometry* input, + const GEOSCoverageCleanParams* params) + { + return GEOSCoverageCleanWithParams_r( + handle, input, params); + } + + GEOSGeometry * + GEOSCoverageClean( + const GEOSGeometry * input) + { + return GEOSCoverageClean_r( + handle, input); + } + + Geometry* + GEOSRemoveRepeatedPoints( + const Geometry* g, + double tolerance) + { + return GEOSRemoveRepeatedPoints_r(handle, g, tolerance); + } + + Geometry* + GEOSLineMerge(const Geometry* g) + { + return GEOSLineMerge_r(handle, g); + } + + Geometry* + GEOSLineMergeDirected(const Geometry* g) + { + return GEOSLineMergeDirected_r(handle, g); + } + + Geometry* + GEOSLineSubstring(const Geometry* g, double start_fraction, double end_fraction) + { + return GEOSLineSubstring_r(handle, g, start_fraction, end_fraction); + } + + Geometry* + GEOSReverse(const Geometry* g) + { + return GEOSReverse_r(handle, g); + } + + int + GEOSGetSRID(const Geometry* g) + { + return GEOSGetSRID_r(handle, g); + } + + void + GEOSSetSRID(Geometry* g, int srid) + { + return GEOSSetSRID_r(handle, g, srid); + } + + void* + GEOSGeom_getUserData(const Geometry* g) + { + return GEOSGeom_getUserData_r(handle, g); + } + + void + GEOSGeom_setUserData(Geometry* g, void* userData) + { + return GEOSGeom_setUserData_r(handle, g, userData); + } + + char + GEOSHasZ(const Geometry* g) + { + return GEOSHasZ_r(handle, g); + } + + char + GEOSHasM(const Geometry* g) + { + return GEOSHasM_r(handle, g); + } + + int + GEOS_getWKBOutputDims() + { + return GEOS_getWKBOutputDims_r(handle); + } + + int + GEOS_setWKBOutputDims(int newdims) + { + return GEOS_setWKBOutputDims_r(handle, newdims); + } + + int + GEOS_getWKBByteOrder() + { + return GEOS_getWKBByteOrder_r(handle); + } + + int + GEOS_setWKBByteOrder(int byteOrder) + { + return GEOS_setWKBByteOrder_r(handle, byteOrder); + } + + CoordinateSequence* + GEOSCoordSeq_create(unsigned int size, unsigned int dims) + { + return GEOSCoordSeq_create_r(handle, size, dims); + } + + CoordinateSequence* + GEOSCoordSeq_createWithDimensions(unsigned int size, int hasZ, int hasM) + { + return GEOSCoordSeq_createWithDimensions_r(handle, size, hasZ, hasM); + } + + CoordinateSequence* + GEOSCoordSeq_copyFromBuffer(const double* buf, unsigned int size, int hasZ, int hasM) + { + return GEOSCoordSeq_copyFromBuffer_r(handle, buf, size, hasZ, hasM); + } + + int + GEOSCoordSeq_copyToBuffer(const CoordinateSequence* s, double* buf, int hasZ, int hasM) + { + return GEOSCoordSeq_copyToBuffer_r(handle, s, buf, hasZ, hasM); + } + + CoordinateSequence* + GEOSCoordSeq_copyFromArrays(const double* x, const double* y, const double* z, const double* m, unsigned int size) + { + return GEOSCoordSeq_copyFromArrays_r(handle, x, y, z, m, size); + } + + int + GEOSCoordSeq_copyToArrays(const CoordinateSequence* s, double* x, double* y, double* z, double* m) + { + return GEOSCoordSeq_copyToArrays_r(handle, s, x, y, z, m); + } + + char + GEOSCoordSeq_hasZ(CoordinateSequence* s) + { + return GEOSCoordSeq_hasZ_r(handle, s); + } + + char + GEOSCoordSeq_hasM(CoordinateSequence* s) + { + return GEOSCoordSeq_hasM_r(handle, s); + } + + int + GEOSCoordSeq_setOrdinate(CoordinateSequence* s, unsigned int idx, unsigned int dim, double val) + { + return GEOSCoordSeq_setOrdinate_r(handle, s, idx, dim, val); + } + + int + GEOSCoordSeq_setX(CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate(s, idx, CoordinateSequence::X, val); + } + + int + GEOSCoordSeq_setY(CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate(s, idx, CoordinateSequence::Y, val); + } + + int + GEOSCoordSeq_setZ(CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate(s, idx, CoordinateSequence::Z, val); + } + + int + GEOSCoordSeq_setM(CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate(s, idx, CoordinateSequence::M, val); + } + + int + GEOSCoordSeq_setXY(CoordinateSequence* s, unsigned int idx, double x, double y) + { + return GEOSCoordSeq_setXY_r(handle, s, idx, x, y); + } + + int + GEOSCoordSeq_setXYZ(CoordinateSequence* s, unsigned int idx, double x, double y, double z) + { + return GEOSCoordSeq_setXYZ_r(handle, s, idx, x, y, z); + } + + CoordinateSequence* + GEOSCoordSeq_clone(const CoordinateSequence* s) + { + return GEOSCoordSeq_clone_r(handle, s); + } + + int + GEOSCoordSeq_getOrdinate(const CoordinateSequence* s, unsigned int idx, unsigned int dim, double* val) + { + return GEOSCoordSeq_getOrdinate_r(handle, s, idx, dim, val); + } + + int + GEOSCoordSeq_getX(const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate(s, idx, CoordinateSequence::X, val); + } + + int + GEOSCoordSeq_getY(const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate(s, idx, CoordinateSequence::Y, val); + } + + int + GEOSCoordSeq_getZ(const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate(s, idx, CoordinateSequence::Z, val); + } + + int + GEOSCoordSeq_getM(const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate(s, idx, CoordinateSequence::M, val); + } + + int + GEOSCoordSeq_getXY(const CoordinateSequence* s, unsigned int idx, double* x, double* y) + { + return GEOSCoordSeq_getXY_r(handle, s, idx, x, y); + } + + int + GEOSCoordSeq_getXYZ(const CoordinateSequence* s, unsigned int idx, double* x, double* y, double* z) + { + return GEOSCoordSeq_getXYZ_r(handle, s, idx, x, y, z); + } + + int + GEOSCoordSeq_getSize(const CoordinateSequence* s, unsigned int* size) + { + return GEOSCoordSeq_getSize_r(handle, s, size); + } + + int + GEOSCoordSeq_getDimensions(const CoordinateSequence* s, unsigned int* dims) + { + return GEOSCoordSeq_getDimensions_r(handle, s, dims); + } + + int + GEOSCoordSeq_isCCW(const CoordinateSequence* s, char* is_ccw) + { + return GEOSCoordSeq_isCCW_r(handle, s, is_ccw); + } + + void + GEOSCoordSeq_destroy(CoordinateSequence* s) + { + return GEOSCoordSeq_destroy_r(handle, s); + } + + const CoordinateSequence* + GEOSGeom_getCoordSeq(const Geometry* g) + { + return GEOSGeom_getCoordSeq_r(handle, g); + } + + Geometry* + GEOSGeom_createPoint(CoordinateSequence* cs) + { + return GEOSGeom_createPoint_r(handle, cs); + } + + Geometry* + GEOSGeom_createPointFromXY(double x, double y) + { + return GEOSGeom_createPointFromXY_r(handle, x, y); + } + + Geometry* + GEOSGeom_createLinearRing(CoordinateSequence* cs) + { + return GEOSGeom_createLinearRing_r(handle, cs); + } + + Geometry* + GEOSGeom_createLineString(CoordinateSequence* cs) + { + return GEOSGeom_createLineString_r(handle, cs); + } + + Geometry* + GEOSGeom_createPolygon(Geometry* shell, Geometry** holes, unsigned int nholes) + { + return GEOSGeom_createPolygon_r(handle, shell, holes, nholes); + } + + Geometry* + GEOSGeom_createCircularString(CoordinateSequence* cs) + { + return GEOSGeom_createCircularString_r(handle, cs); + } + + Geometry* + GEOSGeom_createCompoundCurve(Geometry** curves, unsigned int ngeoms) + { + return GEOSGeom_createCompoundCurve_r(handle, curves, ngeoms); + } + + Geometry* + GEOSGeom_createCurvePolygon(Geometry* shell, Geometry** holes, unsigned int nholes) + { + return GEOSGeom_createCurvePolygon_r(handle, shell, holes, nholes); + } + + Geometry* + GEOSGeom_clone(const Geometry* g) + { + return GEOSGeom_clone_r(handle, g); + } + + GEOSGeometry* + GEOSGeom_setPrecision(const GEOSGeometry* g, double gridSize, int flags) + { + return GEOSGeom_setPrecision_r(handle, g, gridSize, flags); + } + + double + GEOSGeom_getPrecision(const GEOSGeometry* g) + { + return GEOSGeom_getPrecision_r(handle, g); + } + + int + GEOSGeom_getDimensions(const Geometry* g) + { + return GEOSGeom_getDimensions_r(handle, g); + } + + int + GEOSGeom_getCoordinateDimension(const Geometry* g) + { + return GEOSGeom_getCoordinateDimension_r(handle, g); + } + + int GEOS_DLL GEOSGeom_getXMin(const GEOSGeometry* g, double* value) + { + return GEOSGeom_getXMin_r(handle, g, value); + } + + int GEOS_DLL GEOSGeom_getYMin(const GEOSGeometry* g, double* value) + { + return GEOSGeom_getYMin_r(handle, g, value); + } + + int GEOS_DLL GEOSGeom_getXMax(const GEOSGeometry* g, double* value) + { + return GEOSGeom_getXMax_r(handle, g, value); + } + + int GEOS_DLL GEOSGeom_getYMax(const GEOSGeometry* g, double* value) + { + return GEOSGeom_getYMax_r(handle, g, value); + } + + int GEOS_DLL GEOSGeom_getExtent(const GEOSGeometry* g, double* xmin, double* ymin, double* xmax, double* ymax) + { + return GEOSGeom_getExtent_r(handle, g, xmin, ymin, xmax, ymax); + } + + Geometry* + GEOSSimplify(const Geometry* g, double tolerance) + { + return GEOSSimplify_r(handle, g, tolerance); + } + + Geometry* + GEOSTopologyPreserveSimplify(const Geometry* g, double tolerance) + { + return GEOSTopologyPreserveSimplify_r(handle, g, tolerance); + } + + + /* WKT Reader */ + WKTReader* + GEOSWKTReader_create() + { + return GEOSWKTReader_create_r(handle); + } + + void + GEOSWKTReader_destroy(WKTReader* reader) + { + GEOSWKTReader_destroy_r(handle, reader); + } + + void + GEOSWKTReader_setFixStructure(WKTReader* reader, char doFix) + { + GEOSWKTReader_setFixStructure_r(handle, reader, doFix); + } + + Geometry* + GEOSWKTReader_read(WKTReader* reader, const char* wkt) + { + return GEOSWKTReader_read_r(handle, reader, wkt); + } + + /* WKT Writer */ + WKTWriter* + GEOSWKTWriter_create() + { + return GEOSWKTWriter_create_r(handle); + } + + void + GEOSWKTWriter_destroy(WKTWriter* Writer) + { + GEOSWKTWriter_destroy_r(handle, Writer); + } + + char* + GEOSWKTWriter_write(WKTWriter* writer, const Geometry* geom) + { + return GEOSWKTWriter_write_r(handle, writer, geom); + } + + void + GEOSWKTWriter_setTrim(WKTWriter* writer, char trim) + { + GEOSWKTWriter_setTrim_r(handle, writer, trim); + } + + void + GEOSWKTWriter_setRoundingPrecision(WKTWriter* writer, int precision) + { + return GEOSWKTWriter_setRoundingPrecision_r(handle, writer, precision); + } + + void + GEOSWKTWriter_setOutputDimension(WKTWriter* writer, int dim) + { + GEOSWKTWriter_setOutputDimension_r(handle, writer, dim); + } + + int + GEOSWKTWriter_getOutputDimension(WKTWriter* writer) + { + return GEOSWKTWriter_getOutputDimension_r(handle, writer); + } + + void + GEOSWKTWriter_setOld3D(WKTWriter* writer, int useOld3D) + { + GEOSWKTWriter_setOld3D_r(handle, writer, useOld3D); + } + + /* WKB Reader */ + WKBReader* + GEOSWKBReader_create() + { + return GEOSWKBReader_create_r(handle); + } + + void + GEOSWKBReader_destroy(WKBReader* reader) + { + GEOSWKBReader_destroy_r(handle, reader); + } + + void + GEOSWKBReader_setFixStructure(WKBReader* reader, char doFix) + { + GEOSWKBReader_setFixStructure_r(handle, reader, doFix); + } + + Geometry* + GEOSWKBReader_read(WKBReader* reader, const unsigned char* wkb, std::size_t size) + { + return GEOSWKBReader_read_r(handle, reader, wkb, size); + } + + Geometry* + GEOSWKBReader_readHEX(WKBReader* reader, const unsigned char* hex, std::size_t size) + { + return GEOSWKBReader_readHEX_r(handle, reader, hex, size); + } + + /* WKB Writer */ + WKBWriter* + GEOSWKBWriter_create() + { + return GEOSWKBWriter_create_r(handle); + } + + void + GEOSWKBWriter_destroy(WKBWriter* Writer) + { + GEOSWKBWriter_destroy_r(handle, Writer); + } + + + /* The caller owns the result */ + unsigned char* + GEOSWKBWriter_write(WKBWriter* writer, const Geometry* geom, std::size_t* size) + { + return GEOSWKBWriter_write_r(handle, writer, geom, size); + } + + /* The caller owns the result */ + unsigned char* + GEOSWKBWriter_writeHEX(WKBWriter* writer, const Geometry* geom, std::size_t* size) + { + return GEOSWKBWriter_writeHEX_r(handle, writer, geom, size); + } + + int + GEOSWKBWriter_getOutputDimension(const GEOSWKBWriter* writer) + { + return GEOSWKBWriter_getOutputDimension_r(handle, writer); + } + + void + GEOSWKBWriter_setOutputDimension(GEOSWKBWriter* writer, int newDimension) + { + GEOSWKBWriter_setOutputDimension_r(handle, writer, newDimension); + } + + int + GEOSWKBWriter_getByteOrder(const GEOSWKBWriter* writer) + { + return GEOSWKBWriter_getByteOrder_r(handle, writer); + } + + void + GEOSWKBWriter_setByteOrder(GEOSWKBWriter* writer, int newByteOrder) + { + GEOSWKBWriter_setByteOrder_r(handle, writer, newByteOrder); + } + + int + GEOSWKBWriter_getFlavor(const GEOSWKBWriter* writer) + { + return GEOSWKBWriter_getFlavor_r(handle, writer); + } + + void + GEOSWKBWriter_setFlavor(GEOSWKBWriter* writer, int newFlavor) + { + GEOSWKBWriter_setFlavor_r(handle, writer, newFlavor); + } + + char + GEOSWKBWriter_getIncludeSRID(const GEOSWKBWriter* writer) + { + return GEOSWKBWriter_getIncludeSRID_r(handle, writer); + } + + void + GEOSWKBWriter_setIncludeSRID(GEOSWKBWriter* writer, const char newIncludeSRID) + { + GEOSWKBWriter_setIncludeSRID_r(handle, writer, newIncludeSRID); + } + + int + GEOS_printDouble(double d, unsigned int precision, char *result) { + return WKTWriter::writeTrimmedNumber(d, precision, result); + } + + /* GeoJSON Reader */ + GeoJSONReader* + GEOSGeoJSONReader_create() + { + return GEOSGeoJSONReader_create_r(handle); + } + + void + GEOSGeoJSONReader_destroy(GeoJSONReader* reader) + { + GEOSGeoJSONReader_destroy_r(handle, reader); + } + + Geometry* + GEOSGeoJSONReader_readGeometry(GeoJSONReader* reader, const char* geojson) + { + return GEOSGeoJSONReader_readGeometry_r(handle, reader, geojson); + } + + /* GeoJSON Writer */ + GeoJSONWriter* + GEOSGeoJSONWriter_create() + { + return GEOSGeoJSONWriter_create_r(handle); + } + + void + GEOSGeoJSONWriter_destroy(GEOSGeoJSONWriter* writer) + { + GEOSGeoJSONWriter_destroy_r(handle, writer); + } + + char* + GEOSGeoJSONWriter_writeGeometry(GEOSGeoJSONWriter* writer, const GEOSGeometry* g, int indent) + { + return GEOSGeoJSONWriter_writeGeometry_r(handle, writer, g, indent); + } + + void + GEOSGeoJSONWriter_setOutputDimension(GeoJSONWriter* writer, int dim) + { + GEOSGeoJSONWriter_setOutputDimension_r(handle, writer, dim); + } + + int + GEOSGeoJSONWriter_getOutputDimension(GeoJSONWriter* writer) + { + return GEOSGeoJSONWriter_getOutputDimension_r(handle, writer); + } + + +//----------------------------------------------------------------- +// Prepared Geometry +//----------------------------------------------------------------- + + const geos::geom::prep::PreparedGeometry* + GEOSPrepare(const Geometry* g) + { + return GEOSPrepare_r(handle, g); + } + + void + GEOSPreparedGeom_destroy(const geos::geom::prep::PreparedGeometry* a) + { + GEOSPreparedGeom_destroy_r(handle, a); + } + + char + GEOSPreparedContains(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedContains_r(handle, pg1, g2); + } + + char + GEOSPreparedContainsXY(const geos::geom::prep::PreparedGeometry* pg1, double x, double y) + { + return GEOSPreparedContainsXY_r(handle, pg1, x, y); + } + + char + GEOSPreparedContainsProperly(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedContainsProperly_r(handle, pg1, g2); + } + + char + GEOSPreparedCoveredBy(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedCoveredBy_r(handle, pg1, g2); + } + + char + GEOSPreparedCovers(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedCovers_r(handle, pg1, g2); + } + + char + GEOSPreparedCrosses(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedCrosses_r(handle, pg1, g2); + } + + char + GEOSPreparedDisjoint(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedDisjoint_r(handle, pg1, g2); + } + + char + GEOSPreparedIntersects(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedIntersects_r(handle, pg1, g2); + } + + char + GEOSPreparedIntersectsXY(const geos::geom::prep::PreparedGeometry* pg1, double x, double y) + { + return GEOSPreparedIntersectsXY_r(handle, pg1, x, y); + } + + char + GEOSPreparedOverlaps(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedOverlaps_r(handle, pg1, g2); + } + + char + GEOSPreparedTouches(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedTouches_r(handle, pg1, g2); + } + + char + GEOSPreparedWithin(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedWithin_r(handle, pg1, g2); + } + + char * + GEOSPreparedRelate(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2) + { + return GEOSPreparedRelate_r(handle, pg1, g2); + } + + char + GEOSPreparedRelatePattern(const geos::geom::prep::PreparedGeometry* pg1, const Geometry* g2, const char* imPattern) + { + return GEOSPreparedRelatePattern_r(handle, pg1, g2, imPattern); + } + + CoordinateSequence* + GEOSPreparedNearestPoints(const geos::geom::prep::PreparedGeometry* g1, const Geometry* g2) + { + return GEOSPreparedNearestPoints_r(handle, g1, g2); + } + + int + GEOSPreparedDistance(const geos::geom::prep::PreparedGeometry* g1, const Geometry* g2, double *dist) + { + return GEOSPreparedDistance_r(handle, g1, g2, dist); + } + + char + GEOSPreparedDistanceWithin(const geos::geom::prep::PreparedGeometry* g1, const Geometry* g2, double dist) + { + return GEOSPreparedDistanceWithin_r(handle, g1, g2, dist); + } + + GEOSSTRtree* + GEOSSTRtree_create(std::size_t nodeCapacity) + { + return GEOSSTRtree_create_r(handle, nodeCapacity); + } + + int + GEOSSTRtree_build(GEOSSTRtree* tree) + { + return GEOSSTRtree_build_r(handle, tree); + } + + void + GEOSSTRtree_insert(GEOSSTRtree* tree, + const geos::geom::Geometry* g, + void* item) + { + GEOSSTRtree_insert_r(handle, tree, g, item); + } + + void + GEOSSTRtree_query(GEOSSTRtree* tree, + const geos::geom::Geometry* g, + GEOSQueryCallback cb, + void* userdata) + { + GEOSSTRtree_query_r(handle, tree, g, cb, userdata); + } + + const GEOSGeometry* + GEOSSTRtree_nearest(GEOSSTRtree* tree, + const geos::geom::Geometry* g) + { + return GEOSSTRtree_nearest_r(handle, tree, g); + } + + const void* GEOSSTRtree_nearest_generic(GEOSSTRtree* tree, + const void* item, + const GEOSGeometry* itemEnvelope, + GEOSDistanceCallback distancefn, + void* userdata) + { + return GEOSSTRtree_nearest_generic_r(handle, tree, item, itemEnvelope, distancefn, userdata); + } + + void + GEOSSTRtree_iterate(GEOSSTRtree* tree, + GEOSQueryCallback callback, + void* userdata) + { + GEOSSTRtree_iterate_r(handle, tree, callback, userdata); + } + + char + GEOSSTRtree_remove(GEOSSTRtree* tree, + const geos::geom::Geometry* g, + void* item) + { + return GEOSSTRtree_remove_r(handle, tree, g, item); + } + + void + GEOSSTRtree_destroy(GEOSSTRtree* tree) + { + GEOSSTRtree_destroy_r(handle, tree); + } + + double + GEOSProject(const geos::geom::Geometry* g, + const geos::geom::Geometry* p) + { + return GEOSProject_r(handle, g, p); + } + + geos::geom::Geometry* + GEOSInterpolate(const geos::geom::Geometry* g, + double d) + { + return GEOSInterpolate_r(handle, g, d); + } + + double + GEOSProjectNormalized(const geos::geom::Geometry* g, + const geos::geom::Geometry* p) + { + return GEOSProjectNormalized_r(handle, g, p); + } + + geos::geom::Geometry* + GEOSInterpolateNormalized(const geos::geom::Geometry* g, + double d) + { + return GEOSInterpolateNormalized_r(handle, g, d); + } + + geos::geom::Geometry* + GEOSGeom_extractUniquePoints(const geos::geom::Geometry* g) + { + return GEOSGeom_extractUniquePoints_r(handle, g); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCollection(int type) + { + return GEOSGeom_createEmptyCollection_r(handle, type); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyPoint() + { + return GEOSGeom_createEmptyPoint_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyLineString() + { + return GEOSGeom_createEmptyLineString_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyPolygon() + { + return GEOSGeom_createEmptyPolygon_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCircularString() + { + return GEOSGeom_createEmptyCircularString_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCompoundCurve() + { + return GEOSGeom_createEmptyCompoundCurve_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createEmptyCurvePolygon() + { + return GEOSGeom_createEmptyCurvePolygon_r(handle); + } + + geos::geom::Geometry* + GEOSGeom_createRectangle(double xmin, double ymin, double xmax, + double ymax) + { + return GEOSGeom_createRectangle_r(handle, xmin, ymin, xmax, ymax); + } + + int + GEOSOrientationIndex(double Ax, double Ay, double Bx, double By, + double Px, double Py) + { + return GEOSOrientationIndex_r(handle, Ax, Ay, Bx, By, Px, Py); + } + + GEOSGeometry* + GEOSSharedPaths(const GEOSGeometry* g1, const GEOSGeometry* g2) + { + return GEOSSharedPaths_r(handle, g1, g2); + } + + GEOSGeometry* + GEOSSnap(const GEOSGeometry* g1, const GEOSGeometry* g2, double tolerance) + { + return GEOSSnap_r(handle, g1, g2, tolerance); + } + + GEOSBufferParams* + GEOSBufferParams_create() + { + return GEOSBufferParams_create_r(handle); + } + + void + GEOSBufferParams_destroy(GEOSBufferParams* p) + { + return GEOSBufferParams_destroy_r(handle, p); + } + + int + GEOSBufferParams_setEndCapStyle(GEOSBufferParams* p, int style) + { + return GEOSBufferParams_setEndCapStyle_r(handle, p, style); + } + + int + GEOSBufferParams_setJoinStyle(GEOSBufferParams* p, int joinStyle) + { + return GEOSBufferParams_setJoinStyle_r(handle, p, joinStyle); + } + + int + GEOSBufferParams_setMitreLimit(GEOSBufferParams* p, double l) + { + return GEOSBufferParams_setMitreLimit_r(handle, p, l); + } + + int + GEOSBufferParams_setQuadrantSegments(GEOSBufferParams* p, int joinStyle) + { + return GEOSBufferParams_setQuadrantSegments_r(handle, p, joinStyle); + } + + int + GEOSBufferParams_setSingleSided(GEOSBufferParams* p, int singleSided) + { + return GEOSBufferParams_setSingleSided_r(handle, p, singleSided); + } + + Geometry* + GEOSBufferWithParams(const Geometry* g, const GEOSBufferParams* p, double w) + { + return GEOSBufferWithParams_r(handle, g, p, w); + } + + Geometry* + GEOSDelaunayTriangulation(const Geometry* g, double tolerance, int onlyEdges) + { + return GEOSDelaunayTriangulation_r(handle, g, tolerance, onlyEdges); + } + + Geometry* + GEOSConstrainedDelaunayTriangulation(const Geometry* g) + { + return GEOSConstrainedDelaunayTriangulation_r(handle, g); + } + + Geometry* + GEOSVoronoiDiagram(const Geometry* g, const Geometry* env, double tolerance, int flags) + { + return GEOSVoronoiDiagram_r(handle, g, env, tolerance, flags); + } + + int + GEOSSegmentIntersection(double ax0, double ay0, double ax1, double ay1, + double bx0, double by0, double bx1, double by1, + double* cx, double* cy) + { + return GEOSSegmentIntersection_r(handle, + ax0, ay0, ax1, ay1, + bx0, by0, bx1, by1, + cx, cy); + } + + int + GEOSCoverageIsValid( + const Geometry* input, + double gapWidth, + Geometry** invalidEdges) + { + return GEOSCoverageIsValid_r(handle, input, gapWidth, invalidEdges); + } + + Geometry* + GEOSCoverageSimplifyVW(const Geometry* input, double tolerance, int preserveBoundary) + { + return GEOSCoverageSimplifyVW_r(handle, input, tolerance, preserveBoundary); + } + + +} /* extern "C" */ diff --git a/Sources/geos/capi/geos_ts_c.cpp b/Sources/geos/capi/geos_ts_c.cpp new file mode 100644 index 0000000..f4690f0 --- /dev/null +++ b/Sources/geos/capi/geos_ts_c.cpp @@ -0,0 +1,4759 @@ +/************************************************************************ + * + * + * C-Wrapper for GEOS library + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2010-2012 Sandro Santilli + * Copyright (C) 2016-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * Author: Sandro Santilli + * Thread Safety modifications: Chuck Thibert + * + ***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This should go away +#include // finite +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4099) +#endif + +// Some extra magic to make type declarations in geos_c.h work - +// for cross-checking of types in header. +// NOTE: the below defines or struct definition must be kept in exact +// sync between geos_c.cpp and geos_ts_c.cpp to avoid C++ One Definition Rule +// violations. +#define GEOSGeometry geos::geom::Geometry +#define GEOSPreparedGeometry geos::geom::prep::PreparedGeometry +#define GEOSClusterInfo geos::operation::cluster::Clusters +#define GEOSCoordSequence geos::geom::CoordinateSequence +#define GEOSBufferParams geos::operation::buffer::BufferParameters +#define GEOSSTRtree geos::index::strtree::TemplateSTRtree +#define GEOSWKTReader geos::io::WKTReader +#define GEOSWKTWriter geos::io::WKTWriter +#define GEOSWKBReader geos::io::WKBReader +#define GEOSWKBWriter geos::io::WKBWriter +#define GEOSGeoJSONReader geos::io::GeoJSONReader +#define GEOSGeoJSONWriter geos::io::GeoJSONWriter + +// Implementation struct for the GEOSCoverageCleanParams object +typedef struct { + double snappingDistance; + int overlapMergeStrategy; + double gapMaximumWidth; +} GEOSCoverageCleanParams; + +// Implementation struct for the GEOSMakeValidParams object +typedef struct { + int method; + int keepCollapsed; +} GEOSMakeValidParams; + +#include "geos_c.h" + +// Intentional, to allow non-standard C elements like C99 functions to be +// imported through C++ headers of C library, like . +using namespace std; + +/// Define this if you want operations triggering Exceptions to +/// be printed. +/// (will use the NOTIFY channel - only implemented for GEOSUnion so far) +/// +#undef VERBOSE_EXCEPTIONS + +#include +#include + + +// import the most frequently used definitions globally +using geos::geom::Coordinate; +using geos::geom::CoordinateXY; +using geos::geom::CoordinateXYM; +using geos::geom::CoordinateXYZM; +using geos::geom::CoordinateSequence; +using geos::geom::Curve; +using geos::geom::Envelope; +using geos::geom::Geometry; +using geos::geom::GeometryCollection; +using geos::geom::GeometryFactory; +using geos::geom::LineString; +using geos::geom::LinearRing; +using geos::geom::MultiCurve; +using geos::geom::MultiLineString; +using geos::geom::MultiPolygon; +using geos::geom::Point; +using geos::geom::Polygon; +using geos::geom::PrecisionModel; +using geos::geom::SimpleCurve; +using geos::geom::Surface; + +using geos::geom::prep::PreparedGeometry; + +using geos::io::WKTReader; +using geos::io::WKTWriter; +using geos::io::WKBReader; +using geos::io::WKBWriter; +using geos::io::GeoJSONReader; +using geos::io::GeoJSONWriter; + +using geos::algorithm::distance::DiscreteFrechetDistance; +using geos::algorithm::distance::DiscreteHausdorffDistance; +using geos::algorithm::hull::ConcaveHull; +using geos::algorithm::hull::ConcaveHullOfPolygons; + +using geos::operation::buffer::BufferBuilder; +using geos::operation::buffer::BufferParameters; +using geos::operation::buffer::OffsetCurve; +using geos::operation::cluster::Clusters; +using geos::operation::distance::IndexedFacetDistance; +using geos::operation::geounion::CascadedPolygonUnion; +using geos::operation::overlayng::OverlayNG; +using geos::operation::overlayng::UnaryUnionNG; +using geos::operation::overlayng::OverlayNGRobust; +using geos::operation::relateng::RelateNG; +using geos::operation::valid::TopologyValidationError; + +using geos::precision::GeometryPrecisionReducer; + +using geos::simplify::PolygonHullSimplifier; + +using geos::util::IllegalArgumentException; + +typedef struct GEOSContextHandle_HS { + const GeometryFactory* geomFactory; + char msgBuffer[1024]; + GEOSMessageHandler noticeMessageOld; + GEOSMessageHandler_r noticeMessageNew; + void* noticeData; + GEOSMessageHandler errorMessageOld; + GEOSMessageHandler_r errorMessageNew; + GEOSContextInterruptCallback* interrupt_cb; + void* interrupt_cb_data; + void* errorData; + uint8_t WKBOutputDims; + int WKBByteOrder; + int initialized; + std::unique_ptr point2d; + + GEOSContextHandle_HS() + : + geomFactory(nullptr), + noticeMessageOld(nullptr), + noticeMessageNew(nullptr), + noticeData(nullptr), + errorMessageOld(nullptr), + errorMessageNew(nullptr), + interrupt_cb(nullptr), + interrupt_cb_data(nullptr), + errorData(nullptr), + point2d(nullptr) + { + memset(msgBuffer, 0, sizeof(msgBuffer)); + geomFactory = GeometryFactory::getDefaultInstance(); + point2d = geomFactory->createPoint(CoordinateXY{0, 0}); + WKBOutputDims = 2; + WKBByteOrder = getMachineByteOrder(); + setNoticeHandler(nullptr); + setErrorHandler(nullptr); + initialized = 1; + } + + GEOSMessageHandler + setNoticeHandler(GEOSMessageHandler nf) + { + GEOSMessageHandler f = noticeMessageOld; + noticeMessageOld = nf; + noticeMessageNew = nullptr; + noticeData = nullptr; + + return f; + } + + GEOSMessageHandler + setErrorHandler(GEOSMessageHandler nf) + { + GEOSMessageHandler f = errorMessageOld; + errorMessageOld = nf; + errorMessageNew = nullptr; + errorData = nullptr; + + return f; + } + + GEOSMessageHandler_r + setNoticeHandler(GEOSMessageHandler_r nf, void* userData) + { + GEOSMessageHandler_r f = noticeMessageNew; + noticeMessageOld = nullptr; + noticeMessageNew = nf; + noticeData = userData; + + return f; + } + + GEOSMessageHandler_r + setErrorHandler(GEOSMessageHandler_r ef, void* userData) + { + GEOSMessageHandler_r f = errorMessageNew; + errorMessageOld = nullptr; + errorMessageNew = ef; + errorData = userData; + + return f; + } + + GEOSContextInterruptCallback* + setInterruptHandler(GEOSContextInterruptCallback* cb, void* userData) + { + auto old = interrupt_cb; + interrupt_cb = cb; + interrupt_cb_data = userData; + return old; + } + + void + NOTICE_MESSAGE(GEOS_PRINTF_FORMAT const char *fmt, ...) GEOS_PRINTF_FORMAT_ATTR(2, 3) + { + if(nullptr == noticeMessageOld && nullptr == noticeMessageNew) { + return; + } + + va_list args; + va_start(args, fmt); + #ifdef __MINGW32__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + #endif + int result = vsnprintf(msgBuffer, sizeof(msgBuffer) - 1, fmt, args); + #ifdef __MINGW32__ + #pragma GCC diagnostic pop + #endif + va_end(args); + + if(result > 0) { + if(noticeMessageOld) { + noticeMessageOld("%s", msgBuffer); + } + else { + noticeMessageNew(msgBuffer, noticeData); + } + } + } + + void + ERROR_MESSAGE(GEOS_PRINTF_FORMAT const char *fmt, ...) GEOS_PRINTF_FORMAT_ATTR(2, 3) + { + if(nullptr == errorMessageOld && nullptr == errorMessageNew) { + return; + } + + va_list args; + va_start(args, fmt); + #ifdef __MINGW32__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" + #endif + int result = vsnprintf(msgBuffer, sizeof(msgBuffer) - 1, fmt, args); + #ifdef __MINGW32__ + #pragma GCC diagnostic pop + #endif + va_end(args); + + if(result > 0) { + if(errorMessageOld) { + errorMessageOld("%s", msgBuffer); + } + else { + errorMessageNew(msgBuffer, errorData); + } + } + } +} GEOSContextHandleInternal_t; + +// CAPI_ItemVisitor is used internally by the CAPI STRtree +// wrappers. It's defined here just to keep it out of the +// extern "C" block. +class CAPI_ItemVisitor : public geos::index::ItemVisitor { + GEOSQueryCallback callback; + void* userdata; +public: + CAPI_ItemVisitor(GEOSQueryCallback cb, void* ud) + : ItemVisitor(), callback(cb), userdata(ud) {} + + void operator()(void* item) { + callback(item, userdata); + } + + void + visitItem(void* item) override + { + callback(item, userdata); + } +}; + + +//## PROTOTYPES ############################################# + +extern "C" const char GEOS_DLL* GEOSjtsport(); +extern "C" char GEOS_DLL* GEOSasText(Geometry* g1); + + +namespace { // anonymous + +char* +gstrdup_s(const char* str, const std::size_t size) +{ + char* out = static_cast(malloc(size + 1)); + if(nullptr != out) { + // as no strlen call necessary, memcpy may be faster than strcpy + std::memcpy(out, str, size + 1); + } + + assert(nullptr != out); + + // we haven't been checking allocation before ticket #371 + if(nullptr == out) { + throw(std::runtime_error("Failed to allocate memory for duplicate string")); + } + + return out; +} + +char* +gstrdup(std::string const& str) +{ + return gstrdup_s(str.c_str(), str.size()); +} + +struct InterruptManager { + InterruptManager(GEOSContextHandle_t handle) : + cb(handle->interrupt_cb), + cb_data(handle->interrupt_cb_data) { + if (cb) { + geos::util::CurrentThreadInterrupt::registerCallback(cb, cb_data); + } + } + + ~InterruptManager() { + if (cb != nullptr) { + geos::util::CurrentThreadInterrupt::registerCallback(nullptr, nullptr); + } + } + + GEOSContextInterruptCallback* cb; + void* cb_data; +}; + +struct NotInterruptible { + NotInterruptible(GEOSContextHandle_t handle) { + (void) handle; + } +}; + +} // namespace anonymous + +// Execute a lambda, using the given context handle to process errors. +// Return errval on error. +// Errval should be of the type returned by f, unless f returns a bool in which case we promote to char. +template +inline auto execute( + GEOSContextHandle_t extHandle, + typename std::conditional()()),bool>::value, + char, + decltype(std::declval()())>::type errval, + F&& f) -> decltype(errval) { + if (extHandle == nullptr) { + throw std::runtime_error("GEOS context handle is uninitialized, call initGEOS"); + } + + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if (!handle->initialized) { + return errval; + } + + InterruptManagerType ic(handle); + + try { + return f(); + } catch (const std::exception& e) { + handle->ERROR_MESSAGE("%s", e.what()); + } catch (...) { + handle->ERROR_MESSAGE("Unknown exception thrown"); + } + + return errval; +} + +// Execute a lambda, using the given context handle to process errors. +// Return nullptr on error. +template()())>::value, std::nullptr_t>::type = nullptr> +inline auto execute(GEOSContextHandle_t extHandle, F&& f) -> decltype(f()) { + if (extHandle == nullptr) { + throw std::runtime_error("context handle is uninitialized, call initGEOS"); + } + + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if (!handle->initialized) { + return nullptr; + } + + InterruptManagerType ic(handle); + + try { + return f(); + } catch (const std::exception& e) { + handle->ERROR_MESSAGE("%s", e.what()); + } catch (...) { + handle->ERROR_MESSAGE("Unknown exception thrown"); + } + + return nullptr; +} + +// Execute a lambda, using the given context handle to process errors. +// No return value. +template()())>::value, std::nullptr_t>::type = nullptr> +inline void execute(GEOSContextHandle_t extHandle, F&& f) { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + if (handle != nullptr) { + InterruptManagerType ic(handle); + } + + try { + f(); + } catch (const std::exception& e) { + handle->ERROR_MESSAGE("%s", e.what()); + } catch (...) { + handle->ERROR_MESSAGE("Unknown exception thrown"); + } +} + +extern "C" { + + GEOSContextHandle_t + initGEOS_r(GEOSMessageHandler nf, GEOSMessageHandler ef) + { + GEOSContextHandle_t handle = GEOS_init_r(); + + if(nullptr != handle) { + GEOSContext_setNoticeHandler_r(handle, nf); + GEOSContext_setErrorHandler_r(handle, ef); + } + + return handle; + } + + GEOSContextHandle_t + GEOS_init_r() + { + GEOSContextHandleInternal_t* handle = new GEOSContextHandleInternal_t(); + + geos::util::Interrupt::cancel(); + + return static_cast(handle); + } + + GEOSMessageHandler + GEOSContext_setNoticeHandler_r(GEOSContextHandle_t extHandle, GEOSMessageHandler nf) + { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if(0 == handle->initialized) { + return nullptr; + } + + return handle->setNoticeHandler(nf); + } + + GEOSMessageHandler + GEOSContext_setErrorHandler_r(GEOSContextHandle_t extHandle, GEOSMessageHandler nf) + { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if(0 == handle->initialized) { + return nullptr; + } + + return handle->setErrorHandler(nf); + } + + GEOSMessageHandler_r + GEOSContext_setNoticeMessageHandler_r(GEOSContextHandle_t extHandle, GEOSMessageHandler_r nf, void* userData) + { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if(0 == handle->initialized) { + return nullptr; + } + + return handle->setNoticeHandler(nf, userData); + } + + GEOSMessageHandler_r + GEOSContext_setErrorMessageHandler_r(GEOSContextHandle_t extHandle, GEOSMessageHandler_r ef, void* userData) + { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if(0 == handle->initialized) { + return nullptr; + } + + return handle->setErrorHandler(ef, userData); + } + + GEOSContextInterruptCallback* + GEOSContext_setInterruptCallback_r(GEOSContextHandle_t extHandle, GEOSContextInterruptCallback* cb, void* userData) + { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + if(0 == handle->initialized) { + return nullptr; + } + + return handle->setInterruptHandler(cb, userData); + } + + void + finishGEOS_r(GEOSContextHandle_t extHandle) + { + // Fix up freeing handle w.r.t. malloc above + delete extHandle; + extHandle = nullptr; + } + + void + GEOS_finish_r(GEOSContextHandle_t extHandle) + { + finishGEOS_r(extHandle); + } + + void + GEOSFree_r(GEOSContextHandle_t extHandle, void* buffer) + { + assert(nullptr != extHandle); + geos::ignore_unused_variable_warning(extHandle); + + free(buffer); + } + +//----------------------------------------------------------- +// relate()-related functions +// return 0 = false, 1 = true, 2 = error occurred +//----------------------------------------------------------- + + char + GEOSDisjoint_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->disjoint(g2); + }); + } + + char + GEOSTouches_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->touches(g2); + }); + } + + char + GEOSIntersects_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->intersects(g2); + }); + } + + char + GEOSCrosses_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->crosses(g2); + }); + } + + char + GEOSWithin_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->within(g2); + }); + } + + char + GEOSContains_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->contains(g2); + }); + } + + char + GEOSOverlaps_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->overlaps(g2); + }); + } + + char + GEOSCovers_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->covers(g2); + }); + } + + char + GEOSCoveredBy_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->coveredBy(g2); + }); + } + + char + GEOSEquals_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->equals(g2); + }); + } + + +//------------------------------------------------------------------- +// low-level relate functions +//------------------------------------------------------------------ + + char + GEOSRelatePattern_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, const char* imPattern) + { + return execute(extHandle, 2, [&]() { + std::string s(imPattern); + return g1->relate(g2, s); + }); + } + + char + GEOSRelatePatternMatch_r(GEOSContextHandle_t extHandle, const char* intMatrix, + const char* imPattern) + { + return execute(extHandle, 2, [&]() { + using geos::geom::IntersectionMatrix; + + std::string m(intMatrix); + std::string p(imPattern); + IntersectionMatrix im(m); + + return im.matches(p); + }); + } + + char* + GEOSRelate_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() { + using geos::geom::IntersectionMatrix; + + auto im = g1->relate(g2); + if(im == nullptr) { + return (char*) nullptr; + } + + return gstrdup(im->toString()); + }); + } + + char* + GEOSRelateBoundaryNodeRule_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, int bnr) + { + using geos::operation::relate::RelateOp; + using geos::geom::IntersectionMatrix; + using geos::algorithm::BoundaryNodeRule; + + return execute(extHandle, [&]() -> char* { + std::unique_ptr im; + + switch (bnr) { + case GEOSRELATE_BNR_MOD2: /* same as OGC */ + im = RelateNG::relate(g1, g2, + BoundaryNodeRule::getBoundaryRuleMod2()); + break; + case GEOSRELATE_BNR_ENDPOINT: + im = RelateNG::relate(g1, g2, + BoundaryNodeRule::getBoundaryEndPoint()); + break; + case GEOSRELATE_BNR_MULTIVALENT_ENDPOINT: + im = RelateNG::relate(g1, g2, + BoundaryNodeRule::getBoundaryMultivalentEndPoint()); + break; + case GEOSRELATE_BNR_MONOVALENT_ENDPOINT: + im = RelateNG::relate(g1, g2, + BoundaryNodeRule::getBoundaryMonovalentEndPoint()); + break; + default: + std::ostringstream ss; + ss << "Invalid boundary node rule " << bnr; + throw std::runtime_error(ss.str()); + } + + if(!im) { + return nullptr; + } + + char* result = gstrdup(im->toString()); + + return result; + }); + } + + + +//----------------------------------------------------------------- +// isValid +//----------------------------------------------------------------- + + + char + GEOSisValid_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, 2, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + using geos::operation::valid::IsValidOp; + + IsValidOp ivo(g1); + const TopologyValidationError* err = ivo.getValidationError(); + + if(err) { + handle->NOTICE_MESSAGE("%s", err->toString().c_str()); + return false; + } + else { + return true; + } + }); + } + + char* + GEOSisValidReason_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + using geos::operation::valid::IsValidOp; + + char* result = nullptr; + char const* const validstr = "Valid Geometry"; + + IsValidOp ivo(g1); + const TopologyValidationError* err = ivo.getValidationError(); + + if(err) { + std::ostringstream ss; + ss.precision(15); + ss << err->getCoordinate(); + const std::string errloc = ss.str(); + std::string errmsg(err->getMessage()); + errmsg += "[" + errloc + "]"; + result = gstrdup(errmsg); + } + else { + result = gstrdup(std::string(validstr)); + } + + return result; + }); + } + + char + GEOSisValidDetail_r(GEOSContextHandle_t extHandle, const Geometry* g, + int flags, char** reason, Geometry** location) + { + using geos::operation::valid::IsValidOp; + + return execute(extHandle, 2, [&]() { + IsValidOp ivo(g); + if(flags & GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE) { + ivo.setSelfTouchingRingFormingHoleValid(true); + } + const TopologyValidationError* err = ivo.getValidationError(); + if(err != nullptr) { + if(location) { + *location = g->getFactory()->createPoint(err->getCoordinate()).release(); + } + if(reason) { + std::string errmsg(err->getMessage()); + *reason = gstrdup(errmsg); + } + return false; + } + + if(location) { + *location = nullptr; + } + if(reason) { + *reason = nullptr; + } + return true; /* valid */ + + }); + } + +//----------------------------------------------------------------- +// general purpose +//----------------------------------------------------------------- + + char + GEOSEqualsExact_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double tolerance) + { + return execute(extHandle, 2, [&]() { + return g1->equalsExact(g2, tolerance); + }); + } + + char + GEOSEqualsIdentical_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, 2, [&]() { + return g1->equalsIdentical(g2); + }); + } + + int + GEOSDistance_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = g1->distance(g2); + return 1; + }); + } + + char + GEOSDistanceWithin_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double dist) + { + return execute(extHandle, 2, [&]() { + return g1->isWithinDistance(g2, dist); + }); + } + + int + GEOSDistanceIndexed_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = IndexedFacetDistance::distance(g1, g2); + return 1; + }); + } + + int + GEOSHausdorffDistance_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = DiscreteHausdorffDistance::distance(*g1, *g2); + return 1; + }); + } + + int + GEOSHausdorffDistanceDensify_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, + double densifyFrac, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = DiscreteHausdorffDistance::distance(*g1, *g2, densifyFrac); + return 1; + }); + } + + int + GEOSFrechetDistance_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = DiscreteFrechetDistance::distance(*g1, *g2); + return 1; + }); + } + + int + GEOSFrechetDistanceDensify_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double densifyFrac, + double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = DiscreteFrechetDistance::distance(*g1, *g2, densifyFrac); + return 1; + }); + } + + int + GEOSArea_r(GEOSContextHandle_t extHandle, const Geometry* g, double* area) + { + return execute(extHandle, 0, [&]() { + *area = g->getArea(); + return 1; + }); + } + + int + GEOSLength_r(GEOSContextHandle_t extHandle, const Geometry* g, double* length) + { + return execute(extHandle, 0, [&]() { + *length = g->getLength(); + return 1; + }); + } + + CoordinateSequence* + GEOSNearestPoints_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() -> CoordinateSequence* { + if(g1->isEmpty() || g2->isEmpty()) { + return nullptr; + } + return geos::operation::distance::DistanceOp::nearestPoints(g1, g2).release(); + }); + } + + static Clusters* capi_clusters(const Geometry* g, + geos::operation::cluster::AbstractClusterFinder& finder) + { + std::vector input{g->getNumGeometries()}; + for (std::size_t i = 0; i < input.size(); i++) { + input[i] = g->getGeometryN(i); + } + + return new Clusters(finder.cluster(input)); + } + + Clusters* + GEOSClusterDBSCAN_r(GEOSContextHandle_t extHandle, const Geometry* g, double eps, unsigned minPoints) + { + return execute(extHandle, [&]() { + geos::operation::cluster::DBSCANClusterFinder finder(eps, minPoints); + + return capi_clusters(g, finder); + }); + } + + Clusters* + GEOSClusterGeometryIntersects_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + geos::operation::cluster::GeometryIntersectsClusterFinder finder; + return capi_clusters(g, finder); + }); + } + + Clusters* + GEOSClusterEnvelopeIntersects_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + geos::operation::cluster::EnvelopeIntersectsClusterFinder finder; + return capi_clusters(g, finder); + }); + } + + Clusters* + GEOSClusterEnvelopeDistance_r(GEOSContextHandle_t extHandle, const Geometry* g, double d) + { + return execute(extHandle, [&]() { + geos::operation::cluster::EnvelopeDistanceClusterFinder finder(d); + return capi_clusters(g, finder); + }); + } + + Clusters* + GEOSClusterGeometryDistance_r(GEOSContextHandle_t extHandle, const Geometry* g, double d) + { + return execute(extHandle, [&]() { + geos::operation::cluster::GeometryDistanceClusterFinder finder(d); + return capi_clusters(g, finder); + }); + } + + std::size_t GEOSClusterInfo_getNumClusters_r(GEOSContextHandle_t extHandle, const Clusters* clusters) + { + return execute(extHandle, 0, [&]() -> std::size_t { + return static_cast(clusters->getNumClusters()); + }); + } + + std::size_t GEOSClusterInfo_getClusterSize_r(GEOSContextHandle_t extHandle, const Clusters* clusters, std::size_t i) + { + return execute(extHandle, 0, [&]() { + return static_cast(clusters->getSize(i)); + }); + } + + const std::size_t* GEOSClusterInfo_getInputsForClusterN_r(GEOSContextHandle_t extHandle, const Clusters* clusters, std::size_t i) + { + return execute(extHandle, [&]() { + return &*clusters->begin(i); + }); + } + + std::size_t* GEOSClusterInfo_getClustersForInputs_r(GEOSContextHandle_t extHandle, const Clusters* clusters) + { + return execute(extHandle, [&]() { + auto ids = clusters->getClusterIds(GEOS_CLUSTER_NONE); + std::size_t* ids_buf = (size_t*) malloc(ids.size() * sizeof(std::size_t)); + std::copy(ids.begin(), ids.end(), ids_buf); + return ids_buf; + }); + } + + void GEOSClusterInfo_destroy_r(GEOSContextHandle_t, Clusters* clusters) + { + delete clusters; + } + + Geometry* + GEOSGeomFromWKT_r(GEOSContextHandle_t extHandle, const char* wkt) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + const std::string wktstring(wkt); + WKTReader r(static_cast(handle->geomFactory)); + + auto g = r.read(wktstring); + return g.release(); + }); + } + + char* + GEOSGeomToWKT_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + // Deprecated, show untrimmed 2D output + geos::io::WKTWriter writer; + writer.setTrim(false); + writer.setOutputDimension(2); + char* result = gstrdup(writer.write(g1)); + return result; + }); + } + + // Remember to free the result! + unsigned char* + GEOSGeomToWKB_buf_r(GEOSContextHandle_t extHandle, const Geometry* g, std::size_t* size) + { + using geos::io::WKBWriter; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + int byteOrder = handle->WKBByteOrder; + WKBWriter w(handle->WKBOutputDims, byteOrder); + std::ostringstream os(std::ios_base::binary); + w.write(*g, os); + std::string wkbstring(os.str()); + const std::size_t len = wkbstring.length(); + + unsigned char* result = static_cast(malloc(len)); + if(result) { + std::memcpy(result, wkbstring.c_str(), len); + *size = len; + } + return result; + }); + } + + Geometry* + GEOSGeomFromWKB_buf_r(GEOSContextHandle_t extHandle, const unsigned char* wkb, std::size_t size) + { + using geos::io::WKBReader; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + WKBReader r(*(static_cast(handle->geomFactory))); + return r.read(wkb, size).release(); + }); + } + + /* Read/write wkb hex values. Returned geometries are + owned by the caller.*/ + unsigned char* + GEOSGeomToHEX_buf_r(GEOSContextHandle_t extHandle, const Geometry* g, std::size_t* size) + { + using geos::io::WKBWriter; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + int byteOrder = handle->WKBByteOrder; + WKBWriter w(handle->WKBOutputDims, byteOrder); + std::ostringstream os(std::ios_base::binary); + w.writeHEX(*g, os); + std::string hexstring(os.str()); + + char* result = gstrdup(hexstring); + if(result) { + *size = hexstring.length(); + } + + return reinterpret_cast(result); + }); + } + + Geometry* + GEOSGeomFromHEX_buf_r(GEOSContextHandle_t extHandle, const unsigned char* hex, std::size_t size) + { + using geos::io::WKBReader; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + std::string hexstring(reinterpret_cast(hex), size); + WKBReader r(*(static_cast(handle->geomFactory))); + std::istringstream is(std::ios_base::binary); + is.str(hexstring); + is.seekg(0, std::ios::beg); // rewind reader pointer + + auto g = r.readHEX(is); + return g.release(); + }); + } + + char + GEOSisEmpty_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, 2, [&]() { + return g1->isEmpty(); + }); + } + + char + GEOSisSimple_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, 2, [&]() { + return g1->isSimple(); + }); + } + + char + GEOSisSimpleDetail_r(GEOSContextHandle_t extHandle, const Geometry* g1, int returnAllPoints, Geometry** result) + { + return execute(extHandle, 2, [&]() { + geos::operation::valid::IsSimpleOp iso(g1); + iso.setFindAllLocations(returnAllPoints); + + *result = nullptr; + + bool simple = iso.isSimple(); + if (!simple) { + auto locations = iso.getNonSimpleLocations(); + if (locations.size() == 1 || !returnAllPoints) { + *result = extHandle->geomFactory->createPoint(locations.front()).release(); + } else { + *result = extHandle->geomFactory->createMultiPoint(locations).release(); + } + } + + return simple; + }); + } + + char + GEOSisRing_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + const Curve* ls = dynamic_cast(g); + if(ls) { + return ls->isRing(); + } + else { + return false; + } + }); + } + + //free the result of this + char* + GEOSGeomType_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + std::string s = g1->getGeometryType(); + + char* result = gstrdup(s); + return result; + }); + } + + // Return postgis geometry type index + int + GEOSGeomTypeId_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, -1, [&]() { + return static_cast(g1->getGeometryTypeId()); + }); + } + +//------------------------------------------------------------------- +// GEOS functions that return geometries +//------------------------------------------------------------------- + + Geometry* + GEOSEnvelope_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + Geometry* g3 = g1->getEnvelope().release(); + g3->setSRID(g1->getSRID()); + return g3; + }); + } + + Geometry* + GEOSIntersection_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() { + auto g3 = g1->intersection(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSIntersectionPrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double gridSize) + { + return execute(extHandle, [&]() { + std::unique_ptr pm; + if(gridSize != 0) { + pm.reset(new PrecisionModel(1.0 / gridSize)); + } + else { + pm.reset(new PrecisionModel()); + } + auto g3 = gridSize != 0 ? + OverlayNG::overlay(g1, g2, OverlayNG::INTERSECTION, pm.get()) + : + OverlayNGRobust::Overlay(g1, g2, OverlayNG::INTERSECTION); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSBuffer_r(GEOSContextHandle_t extHandle, const Geometry* g1, double width, int quadrantsegments) + { + return execute(extHandle, [&]() { + auto g3 = g1->buffer(width, quadrantsegments); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSBufferWithStyle_r(GEOSContextHandle_t extHandle, const Geometry* g1, double width, int quadsegs, int endCapStyle, + int joinStyle, double mitreLimit) + { + using geos::operation::buffer::BufferParameters; + using geos::operation::buffer::BufferOp; + using geos::util::IllegalArgumentException; + + return execute(extHandle, [&]() { + BufferParameters bp; + bp.setQuadrantSegments(quadsegs); + + if(endCapStyle > BufferParameters::CAP_SQUARE) { + throw IllegalArgumentException("Invalid buffer endCap style"); + } + bp.setEndCapStyle( + static_cast(endCapStyle) + ); + + if(joinStyle > BufferParameters::JOIN_BEVEL) { + throw IllegalArgumentException("Invalid buffer join style"); + } + bp.setJoinStyle( + static_cast(joinStyle) + ); + bp.setMitreLimit(mitreLimit); + BufferOp op(g1, bp); + std::unique_ptr g3 = op.getResultGeometry(width); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSDensify_r(GEOSContextHandle_t extHandle, const Geometry* g, double tolerance) + { + using geos::geom::util::Densifier; + + return execute(extHandle, [&]() { + Densifier densifier(g); + densifier.setDistanceTolerance(tolerance); + auto g3 = densifier.getResultGeometry(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSOffsetCurve_r(GEOSContextHandle_t extHandle, const Geometry* g1, double width, int quadsegs, int joinStyle, + double mitreLimit) + { + return execute(extHandle, [&]() { + BufferParameters bp; + //-- use default cap style ROUND + bp.setQuadrantSegments(quadsegs); + + if(joinStyle > BufferParameters::JOIN_BEVEL) { + throw IllegalArgumentException("Invalid buffer join style"); + } + bp.setJoinStyle( + static_cast(joinStyle) + ); + bp.setMitreLimit(mitreLimit); + + OffsetCurve oc(*g1, width, bp); + std::unique_ptr g3 = oc.getCurve(); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + /* @deprecated in 3.3.0 */ + Geometry* + GEOSSingleSidedBuffer_r(GEOSContextHandle_t extHandle, const Geometry* g1, double width, int quadsegs, int joinStyle, + double mitreLimit, int leftSide) + { + return execute(extHandle, [&]() { + BufferParameters bp; + bp.setEndCapStyle(BufferParameters::CAP_FLAT); + bp.setQuadrantSegments(quadsegs); + + if(joinStyle > BufferParameters::JOIN_BEVEL) { + throw IllegalArgumentException("Invalid buffer join style"); + } + bp.setJoinStyle( + static_cast(joinStyle) + ); + bp.setMitreLimit(mitreLimit); + + bool isLeftSide = leftSide == 0 ? false : true; + BufferBuilder bufBuilder(bp); + std::unique_ptr g3 = bufBuilder.bufferLineSingleSided(g1, width, isLeftSide); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSConvexHull_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + auto g3 = g1->convexHull(); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSConcaveHull_r(GEOSContextHandle_t extHandle, + const Geometry* g1, + double ratio, + unsigned int allowHoles) + { + return execute(extHandle, [&]() { + ConcaveHull hull(g1); + hull.setMaximumEdgeLengthRatio(ratio); + hull.setHolesAllowed(allowHoles); + std::unique_ptr g3 = hull.getHull(); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSConcaveHullByLength_r(GEOSContextHandle_t extHandle, + const Geometry* g1, + double length, + unsigned int allowHoles) + { + return execute(extHandle, [&]() { + ConcaveHull hull(g1); + hull.setMaximumEdgeLength(length); + hull.setHolesAllowed(allowHoles); + std::unique_ptr g3 = hull.getHull(); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSPolygonHullSimplify_r(GEOSContextHandle_t extHandle, + const Geometry* g1, + unsigned int isOuter, + double vertexNumFraction) + { + return execute(extHandle, [&]() { + std::unique_ptr g3 = PolygonHullSimplifier::hull(g1, isOuter, vertexNumFraction); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSPolygonHullSimplifyMode_r(GEOSContextHandle_t extHandle, + const Geometry* g1, + unsigned int isOuter, + unsigned int parameterMode, + double parameter) + { + return execute(extHandle, [&]() { + if (parameterMode == GEOSHULL_PARAM_AREA_RATIO) { + std::unique_ptr g3 = PolygonHullSimplifier::hullByAreaDelta(g1, isOuter, parameter); + g3->setSRID(g1->getSRID()); + return g3.release(); + } + else if (parameterMode == GEOSHULL_PARAM_VERTEX_RATIO) { + std::unique_ptr g3 = PolygonHullSimplifier::hull(g1, isOuter, parameter); + g3->setSRID(g1->getSRID()); + return g3.release(); + } + else { + throw IllegalArgumentException("GEOSPolygonHullSimplifyMode_r: Unknown parameterMode"); + } + }); + } + + Geometry* + GEOSConcaveHullOfPolygons_r(GEOSContextHandle_t extHandle, + const Geometry* g1, + double lengthRatio, + unsigned int isTight, + unsigned int isHolesAllowed) + { + return execute(extHandle, [&]() { + std::unique_ptr g3 = + ConcaveHullOfPolygons::concaveHullByLengthRatio( + g1, lengthRatio, + isTight > 0, + isHolesAllowed > 0); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSMinimumRotatedRectangle_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + using geos::algorithm::MinimumAreaRectangle; + + return execute(extHandle, [&]() { + auto g3 = MinimumAreaRectangle::getMinimumRectangle(g); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSMaximumInscribedCircle_r(GEOSContextHandle_t extHandle, const Geometry* g, double tolerance) + { + return execute(extHandle, [&]() { + geos::algorithm::construct::MaximumInscribedCircle mic(g, tolerance); + auto g3 = mic.getRadiusLine(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSLargestEmptyCircle_r(GEOSContextHandle_t extHandle, const Geometry* g, const GEOSGeometry* boundary, double tolerance) + { + return execute(extHandle, [&]() { + geos::algorithm::construct::LargestEmptyCircle lec(g, boundary, tolerance); + auto g3 = lec.getRadiusLine(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSMinimumWidth_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + geos::algorithm::MinimumDiameter m(g); + auto g3 = m.getDiameter(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSMinimumClearanceLine_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + geos::precision::MinimumClearance mc(g); + auto g3 = mc.getLine(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + int + GEOSMinimumClearance_r(GEOSContextHandle_t extHandle, const Geometry* g, double* d) + { + return execute(extHandle, 2, [&]() { + geos::precision::MinimumClearance mc(g); + double res = mc.getDistance(); + *d = res; + return 0; + }); + } + + + Geometry* + GEOSDifference_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() { + auto g3 = g1->difference(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSDifferencePrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double gridSize) + { + return execute(extHandle, [&]() { + + std::unique_ptr pm; + if(gridSize != 0) { + pm.reset(new PrecisionModel(1.0 / gridSize)); + } + else { + pm.reset(new PrecisionModel()); + } + auto g3 = gridSize != 0 ? + OverlayNG::overlay(g1, g2, OverlayNG::DIFFERENCE, pm.get()) + : + OverlayNGRobust::Overlay(g1, g2, OverlayNG::DIFFERENCE); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSBoundary_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + auto g3 = g1->getBoundary(); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSSymDifference_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() { + auto g3 = g1->symDifference(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSSymDifferencePrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double gridSize) + { + return execute(extHandle, [&]() { + + std::unique_ptr pm; + if(gridSize != 0) { + pm.reset(new PrecisionModel(1.0 / gridSize)); + } + else { + pm.reset(new PrecisionModel()); + } + auto g3 = gridSize != 0 ? + OverlayNG::overlay(g1, g2, OverlayNG::SYMDIFFERENCE, pm.get()) + : + OverlayNGRobust::Overlay(g1, g2, OverlayNG::SYMDIFFERENCE); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSUnion_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2) + { + return execute(extHandle, [&]() { + auto g3 = g1->Union(g2); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSUnionPrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* g2, double gridSize) + { + return execute(extHandle, [&]() { + + std::unique_ptr pm; + if(gridSize != 0) { + pm.reset(new PrecisionModel(1.0 / gridSize)); + } + else { + pm.reset(new PrecisionModel()); + } + auto g3 = gridSize != 0 ? + OverlayNG::overlay(g1, g2, OverlayNG::UNION, pm.get()) + : + OverlayNGRobust::Overlay(g1, g2, OverlayNG::UNION); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSCoverageUnion_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + auto g3 = geos::coverage::CoverageUnion::Union(g); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSDisjointSubsetUnion_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + auto g3 = geos::operation::geounion::DisjointSubsetUnion::Union(g); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSUnaryUnion_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + std::unique_ptr g3(g->Union()); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSUnaryUnionPrec_r(GEOSContextHandle_t extHandle, const Geometry* g1, double gridSize) + { + return execute(extHandle, [&]() { + + std::unique_ptr pm; + if(gridSize != 0) { + pm.reset(new PrecisionModel(1.0 / gridSize)); + } + else { + pm.reset(new PrecisionModel()); + } + auto g3 = gridSize != 0 ? + UnaryUnionNG::Union(g1, *pm) + : + OverlayNGRobust::Union(g1); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSNode_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + auto g3 = geos::noding::GeometryNoder::node(*g); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSUnionCascaded_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + // CascadedUnion is the same as UnaryUnion, except that + // CascadedUnion only works on MultiPolygon, so we just delegate + // now and retain a check on MultiPolygon type. + const MultiPolygon *p = dynamic_cast(g1); + if (!p) { + throw IllegalArgumentException("Invalid argument (must be a MultiPolygon)"); + } + return GEOSUnaryUnion_r(extHandle, g1); + }); + } + + Geometry* + GEOSPointOnSurface_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + auto ret = g1->getInteriorPoint(); + ret->setSRID(g1->getSRID()); + return ret.release(); + }); + } + + Geometry* + GEOSClipByRect_r(GEOSContextHandle_t extHandle, const Geometry* g, double xmin, double ymin, double xmax, double ymax) + { + return execute(extHandle, [&]() { + using geos::operation::intersection::Rectangle; + using geos::operation::intersection::RectangleIntersection; + Rectangle rect(xmin, ymin, xmax, ymax); + std::unique_ptr g3 = RectangleIntersection::clip(*g, rect); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + int + GEOSGridIntersectionFractions_r(GEOSContextHandle_t extHandle, const Geometry* g, double xmin, double ymin, + double xmax, double ymax, unsigned nx, unsigned ny, float* buf) + { + return execute(extHandle, 0, [&]() { + Envelope env(xmin, xmax, ymin, ymax); + double dx = env.getWidth() / static_cast(nx); + double dy = env.getHeight() / static_cast(ny); + geos::operation::grid::Grid grid(env, dx, dy); + + // Matrix wants a shared_ptr, but we don't actually want anything to be freed because + // buf is externally owned. So we give it an empty deleter. + std::shared_ptr bufPtr(buf, [](float*) {}); + + auto cov = std::make_shared>(ny, nx, bufPtr); + geos::operation::grid::GridIntersection isect(grid, *g, cov); + + return 1; + }); + } + + Geometry* + GEOSGeom_transformXY_r(GEOSContextHandle_t handle, const GEOSGeometry* g, GEOSTransformXYCallback callback, void* userdata) { + + struct TransformFilter final: public geos::geom::CoordinateFilter { + TransformFilter(GEOSTransformXYCallback p_callback, + void* p_userdata) : + m_callback(p_callback), + m_userdata(p_userdata) {} + + void filter_rw(CoordinateXY* c) const override { + if (!m_callback(&(c->x), &(c->y), m_userdata)) { + throw std::runtime_error(std::string("Failed to transform coordinates.")); + } + } + + GEOSTransformXYCallback m_callback; + void* m_userdata; + }; + + return execute(handle, [&]() { + TransformFilter filter(callback, userdata); + auto ret = g->clone(); + ret->apply_rw(&filter); + ret->geometryChanged(); + return ret.release(); + }); + } + + + Geometry* + GEOSGeom_transformXYZ_r(GEOSContextHandle_t handle, const GEOSGeometry* g, GEOSTransformXYZCallback callback, void* userdata) { + + struct TransformFilter final: public geos::geom::CoordinateFilter { + TransformFilter(GEOSTransformXYZCallback p_callback, + void* p_userdata) : + m_callback(p_callback), + m_userdata(p_userdata) {} + + void filter_rw(Coordinate* c) const override { + if (!m_callback(&(c->x), &(c->y), &(c->z), m_userdata)) { + throw std::runtime_error(std::string("Failed to transform coordinates.")); + } + } + + GEOSTransformXYZCallback m_callback; + void* m_userdata; + }; + + return execute(handle, [&]() { + TransformFilter filter(callback, userdata); + auto ret = g->clone(); + ret->apply_rw(&filter); + ret->geometryChanged(); + return ret.release(); + }); + } + + +//------------------------------------------------------------------- +// memory management functions +//------------------------------------------------------------------ + + void + GEOSGeom_destroy_r(GEOSContextHandle_t extHandle, Geometry* a) + { + execute(extHandle, [&]() { + // FIXME: mloskot: Does this try-catch around delete means that + // destructors in GEOS may throw? If it does, this is a serious + // violation of "never throw an exception from a destructor" principle + delete a; + }); + } + + void + GEOSGeom_setUserData_r(GEOSContextHandle_t extHandle, Geometry* g, void* userData) + { + execute(extHandle, [&]() { + g->setUserData(userData); + }); + } + + void + GEOSSetSRID_r(GEOSContextHandle_t extHandle, Geometry* g, int srid) + { + execute(extHandle, [&]() { + g->setSRID(srid); + }); + } + + + int + GEOSGetNumCoordinates_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, -1, [&]() { + return static_cast(g->getNumPoints()); + }); + } + + /* + * Return -1 on exception, 0 otherwise. + * Converts Geometry to normal form (or canonical form). + */ + int + GEOSNormalize_r(GEOSContextHandle_t extHandle, Geometry* g) + { + return execute(extHandle, -1, [&]() { + g->normalize(); + return 0; // SUCCESS + }); + } + + int + GEOSOrientPolygons_r(GEOSContextHandle_t extHandle, Geometry* g, int exteriorCW) + { + return execute(extHandle, -1, [&]() { + class OrientPolygons : public geos::geom::GeometryComponentFilter { + public: + OrientPolygons(bool isExteriorCW) : exteriorCW(isExteriorCW) {} + + void filter_rw(Geometry* g) override { + if (g->getGeometryTypeId() == geos::geom::GeometryTypeId::GEOS_POLYGON) { + auto p = geos::detail::down_cast(g); + p->orientRings(exteriorCW); + } else if (g->getGeometryTypeId() == geos::geom::GeometryTypeId::GEOS_CURVEPOLYGON) { + throw geos::util::UnsupportedOperationException("Curved geometries not supported."); + } + } + + private: + bool exteriorCW; + }; + + OrientPolygons op(exteriorCW); + g->apply_rw(&op); + + return 0; + }); + } + + int + GEOSGetNumInteriorRings_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, -1, [&]() { + const Surface* p = dynamic_cast(g1); + if(!p) { + throw IllegalArgumentException("Argument is not a Surface"); + } + return static_cast(p->getNumInteriorRing()); + }); + } + + + // returns -1 on error and 1 for non-multi geometries + int + GEOSGetNumGeometries_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, -1, [&]() { + return static_cast(g1->getNumGeometries()); + }); + } + + + /* + * Call only on GEOMETRYCOLLECTION or MULTI*. + * Return a pointer to the internal Geometry. + */ + const Geometry* + GEOSGetGeometryN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) + { + return execute(extHandle, [&]() { + if(n < 0) { + throw IllegalArgumentException("Index must be non-negative."); + } + return g1->getGeometryN(static_cast(n)); + }); + } + + /* + * Call only on LINESTRING + * Returns NULL on exception + */ + Geometry* + GEOSGeomGetPointN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) + { + return execute(extHandle, [&]() { + const SimpleCurve* ls = dynamic_cast(g1); + if(!ls) { + throw IllegalArgumentException("Argument is not a SimpleCurve"); + } + if(n < 0) { + throw IllegalArgumentException("Index must be non-negative."); + } + return ls->getPointN(static_cast(n)).release(); + }); + } + + /* + * Call only on LINESTRING + */ + Geometry* + GEOSGeomGetStartPoint_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + const SimpleCurve* ls = dynamic_cast(g1); + if(!ls) { + throw IllegalArgumentException("Argument is not a SimpleCurve"); + } + + return ls->getStartPoint().release(); + }); + } + + /* + * Call only on LINESTRING + */ + Geometry* + GEOSGeomGetEndPoint_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + const SimpleCurve* ls = dynamic_cast(g1); + if(!ls) { + throw IllegalArgumentException("Argument is not a SimpleCurve"); + } + return ls->getEndPoint().release(); + }); + } + + /* + * Call only on LINESTRING or MULTILINESTRING + * return 2 on exception, 1 on true, 0 on false + */ + char + GEOSisClosed_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, 2, [&]() { + const Curve* ls = dynamic_cast(g1); + if(ls) { + return ls->isClosed(); + } + + const MultiLineString* mls = dynamic_cast(g1); + if(mls) { + return mls->isClosed(); + } + + const MultiCurve* mc = dynamic_cast(g1); + if(mc) { + return mc->isClosed(); + } + + throw IllegalArgumentException("Argument is not a Curve, MultiLineString, or MultiCurve"); + }); + } + + /* + * Call only on LINESTRING + * return 0 on exception, otherwise 1 + */ + int + GEOSGeomGetLength_r(GEOSContextHandle_t extHandle, const Geometry* g1, double* length) + { + return execute(extHandle, 0, [&]() { + const LineString* ls = dynamic_cast(g1); + if(!ls) { + throw IllegalArgumentException("Argument is not a LineString"); + } + *length = ls->getLength(); + return 1; + }); + } + + /* + * Call only on LINESTRING + */ + int + GEOSGeomGetNumPoints_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, -1, [&]() { + const SimpleCurve* ls = dynamic_cast(g1); + if(!ls) { + throw IllegalArgumentException("Argument is not a SimpleCurve"); + } + return static_cast(ls->getNumPoints()); + }); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetX_r(GEOSContextHandle_t extHandle, const Geometry* g1, double* x) + { + return execute(extHandle, 0, [&]() { + const Point* po = dynamic_cast(g1); + if(!po) { + throw IllegalArgumentException("Argument is not a Point"); + } + *x = po->getX(); + return 1; + }); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetY_r(GEOSContextHandle_t extHandle, const Geometry* g1, double* y) + { + return execute(extHandle, 0, [&]() { + const Point* po = dynamic_cast(g1); + if(!po) { + throw IllegalArgumentException("Argument is not a Point"); + } + *y = po->getY(); + return 1; + }); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetZ_r(GEOSContextHandle_t extHandle, const Geometry* g1, double* z) + { + return execute(extHandle, 0, [&]() { + const Point* po = dynamic_cast(g1); + if(!po) { + throw IllegalArgumentException("Argument is not a Point"); + } + *z = po->getZ(); + return 1; + }); + } + + /* + * For POINT + * returns 0 on exception, otherwise 1 + */ + int + GEOSGeomGetM_r(GEOSContextHandle_t extHandle, const Geometry* g1, double* m) + { + return execute(extHandle, 0, [&]() { + const Point* po = dynamic_cast(g1); + if(!po) { + throw IllegalArgumentException("Argument is not a Point"); + } + *m = po->getM(); + return 1; + }); + } + + /* + * Call only on polygon + * Return a pointer to the internal Geometry. + */ + const Geometry* + GEOSGetExteriorRing_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + return execute(extHandle, [&]() { + const Surface* p = dynamic_cast(g1); + if(!p) { + throw IllegalArgumentException("Invalid argument (must be a Surface)"); + } + return p->getExteriorRing(); + }); + } + + /* + * Call only on polygon + * Return a pointer to internal storage, do not destroy it. + */ + const Geometry* + GEOSGetInteriorRingN_r(GEOSContextHandle_t extHandle, const Geometry* g1, int n) + { + return execute(extHandle, [&]() { + const Surface* p = dynamic_cast(g1); + if(!p) { + throw IllegalArgumentException("Invalid argument (must be a Surface)"); + } + if(n < 0) { + throw IllegalArgumentException("Index must be non-negative."); + } + return p->getInteriorRingN(static_cast(n)); + }); + } + + Geometry* + GEOSGetCentroid_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() -> Geometry* { + auto ret = g->getCentroid(); + ret->setSRID(g->getSRID()); + return ret.release(); + }); + } + + int + GEOSHilbertCode_r(GEOSContextHandle_t extHandle, const GEOSGeometry *geom, + const GEOSGeometry* extent, unsigned int level, + unsigned int *code) + { + using geos::shape::fractal::HilbertEncoder; + + return execute(extHandle, 0, [&]() { + Envelope e = *extent->getEnvelopeInternal(); + HilbertEncoder encoder(level, e); + *code = encoder.encode(geom->getEnvelopeInternal()); + return 1; + }); + } + + Geometry* + GEOSMinimumBoundingCircle_r(GEOSContextHandle_t extHandle, const Geometry* g, + double* radius, Geometry** center) + { + return execute(extHandle, [&]() -> Geometry* { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + geos::algorithm::MinimumBoundingCircle mc(g); + std::unique_ptr ret = mc.getCircle(); + const GeometryFactory* gf = handle->geomFactory; + if (center) *center = gf->createPoint(mc.getCentre()).release(); + if (radius) *radius = mc.getRadius(); + ret->setSRID(g->getSRID()); + return ret.release(); + }); + } + + Geometry* + GEOSGeom_createEmptyCollection_r(GEOSContextHandle_t extHandle, int type) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + std::unique_ptr g = 0; + switch(type) { + case GEOS_GEOMETRYCOLLECTION: + g = gf->createGeometryCollection(); + break; + case GEOS_MULTIPOINT: + g = gf->createMultiPoint(); + break; + case GEOS_MULTILINESTRING: + g = gf->createMultiLineString(); + break; + case GEOS_MULTIPOLYGON: + g = gf->createMultiPolygon(); + break; + case GEOS_MULTICURVE: + g = gf->createMultiCurve(); + break; + case GEOS_MULTISURFACE: + g = gf->createMultiSurface(); + break; + default: + throw IllegalArgumentException("Unsupported type request for GEOSGeom_createEmptyCollection_r"); + } + + return g.release(); + }); + } + + Geometry* + GEOSGeom_createCollection_r(GEOSContextHandle_t extHandle, int type, Geometry** geoms, unsigned int ngeoms) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + const GeometryFactory* gf = handle->geomFactory; + + std::vector> vgeoms(ngeoms); + for (std::size_t i = 0; i < ngeoms; i++) { + vgeoms[i].reset(geoms[i]); + } + + std::unique_ptr g; + switch(type) { + case GEOS_GEOMETRYCOLLECTION: + g = gf->createGeometryCollection(std::move(vgeoms)); + break; + case GEOS_MULTIPOINT: + g = gf->createMultiPoint(std::move(vgeoms)); + break; + case GEOS_MULTILINESTRING: + g = gf->createMultiLineString(std::move(vgeoms)); + break; + case GEOS_MULTIPOLYGON: + g = gf->createMultiPolygon(std::move(vgeoms)); + break; + case GEOS_MULTICURVE: + g = gf->createMultiCurve(std::move(vgeoms)); + break; + case GEOS_MULTISURFACE: + g = gf->createMultiSurface(std::move(vgeoms)); + break; + default: + handle->ERROR_MESSAGE("Unsupported type request for GEOSGeom_createCollection_r"); + } + + return g.release(); + }); + } + + Geometry** + GEOSGeom_releaseCollection_r(GEOSContextHandle_t extHandle, Geometry* collection, unsigned int * ngeoms) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + if (ngeoms == nullptr) { + handle->ERROR_MESSAGE("Parameter ngeoms of GEOSGeom_releaseCollection_r must not be null"); + } + + GeometryCollection *col = dynamic_cast(collection); + if (!col) { + handle->ERROR_MESSAGE("Parameter collection of GEOSGeom_releaseCollection_r must not be a collection"); + } else { + *ngeoms = static_cast(col->getNumGeometries()); + } + + // Early exit on empty/null input + if (!col || *ngeoms == 0) { + return static_cast(nullptr); + } + + std::vector> subgeoms = col->releaseGeometries(); + + Geometry** subgeomArray = static_cast(malloc(sizeof(Geometry*) * subgeoms.size())); + for (std::size_t i = 0; i < subgeoms.size(); i++) { + subgeomArray[i] = subgeoms[i].release(); + } + + return subgeomArray; + }); + } + + Geometry* + GEOSPolygonize_r(GEOSContextHandle_t extHandle, const Geometry* const* g, unsigned int ngeoms) + { + using geos::operation::polygonize::Polygonizer; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + // Polygonize + Polygonizer plgnzr; + for(std::size_t i = 0; i < ngeoms; ++i) { + plgnzr.add(g[i]); + } + + auto polys = plgnzr.getPolygons(); + const GeometryFactory* gf = handle->geomFactory; + return gf->createGeometryCollection(std::move(polys)).release(); + }); + } + + Geometry* + GEOSPolygonize_valid_r(GEOSContextHandle_t extHandle, const Geometry* const* g, unsigned int ngeoms) + { + using geos::operation::polygonize::Polygonizer; + + return execute(extHandle, [&]() -> Geometry* { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + Geometry* out; + + // Polygonize + Polygonizer plgnzr(true); + int srid = 0; + for(std::size_t i = 0; i < ngeoms; ++i) { + plgnzr.add(g[i]); + srid = g[i]->getSRID(); + } + + auto polys = plgnzr.getPolygons(); + if (polys.empty()) { + out = handle->geomFactory->createGeometryCollection().release(); + } else if (polys.size() == 1) { + return polys[0].release(); + } else { + return handle->geomFactory->createMultiPolygon(std::move(polys)).release(); + } + + out->setSRID(srid); + return out; + }); + } + + Geometry* + GEOSBuildArea_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + using geos::operation::polygonize::BuildArea; + + return execute(extHandle, [&]() { + BuildArea builder; + auto out = builder.build(g); + out->setSRID(g->getSRID()); + return out.release(); + }); + } + +/************************************************************************ + * Make Valid + */ + + Geometry* + GEOSMakeValid_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + GEOSMakeValidParams params; + params.method = GEOS_MAKE_VALID_LINEWORK; + params.keepCollapsed = 1; + return GEOSMakeValidWithParams_r(extHandle, g, ¶ms); + } + + GEOSMakeValidParams* + GEOSMakeValidParams_create_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSMakeValidParams* p = new GEOSMakeValidParams(); + p->method = GEOS_MAKE_VALID_LINEWORK; + p->keepCollapsed = 0; + return p; + }); + } + + void + GEOSMakeValidParams_destroy_r(GEOSContextHandle_t extHandle, GEOSMakeValidParams* parms) + { + (void)extHandle; + delete parms; + } + + int + GEOSMakeValidParams_setKeepCollapsed_r(GEOSContextHandle_t extHandle, + GEOSMakeValidParams* p, int keepCollapsed) + { + return execute(extHandle, 0, [&]() { + p->keepCollapsed = keepCollapsed; + return 1; + }); + } + + int + GEOSMakeValidParams_setMethod_r(GEOSContextHandle_t extHandle, + GEOSMakeValidParams* p, GEOSMakeValidMethods method) + { + return execute(extHandle, 0, [&]() { + p->method = method; + return 1; + }); + } + + Geometry* + GEOSMakeValidWithParams_r( + GEOSContextHandle_t extHandle, + const Geometry* g, + const GEOSMakeValidParams* params) + { + using geos::geom::util::GeometryFixer; + using geos::operation::valid::MakeValid; + + if (params && params->method == GEOS_MAKE_VALID_LINEWORK) { + return execute(extHandle, [&]() { + MakeValid makeValid; + auto out = makeValid.build(g); + out->setSRID(g->getSRID()); + return out.release(); + }); + } + else if (params && params->method == GEOS_MAKE_VALID_STRUCTURE) { + return execute(extHandle, [&]() { + GeometryFixer fixer(g); + fixer.setKeepCollapsed(params->keepCollapsed == 0 ? false : true); + auto out = fixer.getResult(); + out->setSRID(g->getSRID()); + return out.release(); + }); + } + else { + extHandle->ERROR_MESSAGE("Unknown method in GEOSMakeValidParams"); + return nullptr; + } + } + + Geometry* + GEOSRemoveRepeatedPoints_r( + GEOSContextHandle_t extHandle, + const Geometry* g, + double tolerance) + { + using geos::operation::valid::RepeatedPointRemover; + + return execute(extHandle, [&]() { + auto out = RepeatedPointRemover::removeRepeatedPoints(g, tolerance); + out->setSRID(g->getSRID()); + return out.release(); + }); + } + + Geometry* + GEOSPolygonizer_getCutEdges_r(GEOSContextHandle_t extHandle, const Geometry* const* g, unsigned int ngeoms) + { + using geos::operation::polygonize::Polygonizer; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + // Polygonize + Polygonizer plgnzr; + int srid = 0; + for(std::size_t i = 0; i < ngeoms; ++i) { + plgnzr.add(g[i]); + srid = g[i]->getSRID(); + } + + const std::vector& lines = plgnzr.getCutEdges(); + + // We need a vector of Geometry pointers, not Polygon pointers. + // STL vector doesn't allow transparent upcast of this + // nature, so we explicitly convert. + // (it's just a waste of processor and memory, btw) + // XXX mloskot: See comment for GEOSPolygonize_r + + std::vector> linevec(lines.size()); + + for(std::size_t i = 0, n = lines.size(); i < n; ++i) { + linevec[i] = lines[i]->clone(); + } + + auto out = gf->createGeometryCollection(std::move(linevec)); + out->setSRID(srid); + + return out.release(); + }); + } + + Geometry* + GEOSPolygonize_full_r(GEOSContextHandle_t extHandle, const Geometry* g, + Geometry** cuts, Geometry** dangles, Geometry** invalid) + { + using geos::operation::polygonize::Polygonizer; + + return execute(extHandle, [&]() { + // Polygonize + Polygonizer plgnzr; + for(std::size_t i = 0; i < g->getNumGeometries(); ++i) { + plgnzr.add(g->getGeometryN(i)); + } + + const GeometryFactory* gf = g->getFactory(); + + if(cuts) { + const std::vector& lines = plgnzr.getCutEdges(); + std::vector> linevec(lines.size()); + for(std::size_t i = 0, n = lines.size(); i < n; ++i) { + linevec[i] = lines[i]->clone(); + } + + *cuts = gf->createGeometryCollection(std::move(linevec)).release(); + } + + if(dangles) { + const std::vector& lines = plgnzr.getDangles(); + std::vector> linevec(lines.size()); + for(std::size_t i = 0, n = lines.size(); i < n; ++i) { + linevec[i] = lines[i]->clone(); + } + + *dangles = gf->createGeometryCollection(std::move(linevec)).release(); + } + + if(invalid) { + const std::vector>& lines = plgnzr.getInvalidRingLines(); + std::vector> linevec(lines.size()); + for(std::size_t i = 0, n = lines.size(); i < n; ++i) { + linevec[i] = lines[i]->clone(); + } + + *invalid = gf->createGeometryCollection(std::move(linevec)).release(); + } + + auto polys = plgnzr.getPolygons(); + Geometry* out = gf->createGeometryCollection(std::move(polys)).release(); + out->setSRID(g->getSRID()); + return out; + }); + } + + Geometry* + GEOSLineMerge_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + using geos::operation::linemerge::LineMerger; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + LineMerger lmrgr; + lmrgr.add(g); + + auto lines = lmrgr.getMergedLineStrings(); + + auto out = gf->buildGeometry(std::move(lines)); + out->setSRID(g->getSRID()); + + return out.release(); + }); + } + + Geometry* + GEOSLineMergeDirected_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + using geos::operation::linemerge::LineMerger; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + LineMerger lmrgr(true); + lmrgr.add(g); + + auto lines = lmrgr.getMergedLineStrings(); + + auto out = gf->buildGeometry(std::move(lines)); + out->setSRID(g->getSRID()); + + return out.release(); + }); + } + + Geometry* + GEOSLineSubstring_r(GEOSContextHandle_t extHandle, const Geometry* g, double start_fraction, double end_fraction) + { + using geos::linearref::LengthIndexedLine; + + return execute(extHandle, [&]() { + if (start_fraction < 0 || end_fraction < 0) { + throw IllegalArgumentException("start fraction must be >= 0"); + } + if (start_fraction > 1 || end_fraction > 1) { + throw IllegalArgumentException("end fraction must be <= 1"); + } + + LengthIndexedLine lil(g); + + auto length = g->getLength(); + + auto out = lil.extractLine(start_fraction * length, end_fraction * length); + out->setSRID(g->getSRID()); + + return out.release(); + }); + } + + Geometry* + GEOSReverse_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + auto g3 = g->reverse(); + g3->setSRID(g->getSRID()); + return g3.release(); + }); + } + + void* + GEOSGeom_getUserData_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + return g->getUserData(); + }); + } + + int + GEOSGetSRID_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 0, [&]() { + return g->getSRID(); + }); + } + + const char* GEOSversion() + { + static char version[256]; + snprintf(version, 256, "%s", GEOS_CAPI_VERSION); + return version; + } + + const char* GEOSjtsport() + { + return GEOS_JTS_PORT; + } + + char + GEOSHasZ_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return g->hasZ(); + }); + } + + char + GEOSHasM_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return g->hasM(); + }); + } + + int + GEOS_getWKBOutputDims_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, -1, [&]() -> int { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + return handle->WKBOutputDims; + }); + } + + int + GEOS_setWKBOutputDims_r(GEOSContextHandle_t extHandle, int newdims) + { + return execute(extHandle, -1, [&]() { + GEOSContextHandleInternal_t *handle = reinterpret_cast(extHandle); + + if (newdims < 2 || newdims > 4) { + handle->ERROR_MESSAGE("WKB output dimensions out of range 2..4"); + } + + const int olddims = handle->WKBOutputDims; + handle->WKBOutputDims = static_cast(newdims); + + return olddims; + }); + } + + int + GEOS_getWKBByteOrder_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, -1, [&]() { + GEOSContextHandleInternal_t *handle = reinterpret_cast(extHandle); + return handle->WKBByteOrder; + }); + } + + int + GEOS_setWKBByteOrder_r(GEOSContextHandle_t extHandle, int byteOrder) + { + return execute(extHandle, -1, [&]() { + GEOSContextHandleInternal_t *handle = reinterpret_cast(extHandle); + const int oldByteOrder = handle->WKBByteOrder; + handle->WKBByteOrder = byteOrder; + + return oldByteOrder; + }); + } + + + CoordinateSequence* + GEOSCoordSeq_create_r(GEOSContextHandle_t extHandle, unsigned int size, unsigned int dims) + { + return execute(extHandle, [&]() { + return new CoordinateSequence(size, dims); + }); + } + + CoordinateSequence* + GEOSCoordSeq_createWithDimensions_r(GEOSContextHandle_t extHandle, unsigned int size, int hasZ, int hasM) + { + return execute(extHandle, [&]() { + return new CoordinateSequence(size, hasZ, hasM); + }); + } + + CoordinateSequence* + GEOSCoordSeq_copyFromBuffer_r(GEOSContextHandle_t extHandle, const double* buf, unsigned int size, int hasZ, int hasM) + { + return execute(extHandle, [&]() { + std::ptrdiff_t stride = 2 + hasZ + hasM; + auto coords = geos::detail::make_unique(size, hasZ, hasM, false); + if (hasZ) { + if (hasM) { + // XYZM + assert(coords->getCoordinateType() == geos::geom::CoordinateType::XYZM); + std::memcpy(coords->data(), buf, size * sizeof(CoordinateXYZM)); + } else { + // XYZ + assert(coords->getCoordinateType() == geos::geom::CoordinateType::XYZ); + std::memcpy(coords->data(), buf, size * sizeof(Coordinate)); + } + } else { + if (hasM) { + // XYM + for (std::size_t i = 0; i < size; i++) { + coords->setAt(CoordinateXYM{ *buf, *(buf + 1), *(buf + 2)}, i); + buf += stride; + } + } else { + // XY + for (std::size_t i = 0; i < size; i++) { + coords->setAt(Coordinate{ *buf, *(buf + 1) }, i); + buf += stride; + } + } + } + + return coords.release(); + }); + } + + CoordinateSequence* + GEOSCoordSeq_copyFromArrays_r(GEOSContextHandle_t extHandle, const double* x, const double* y, const double* z, const double* m, unsigned int size) + { + return execute(extHandle, [&]() { + bool hasZ = z != nullptr; + bool hasM = m != nullptr; + + auto coords = geos::detail::make_unique(size, hasZ, hasM, false); + + CoordinateXYZM c; + for (std::size_t i = 0; i < size; i++) { + c.x = x[i]; + c.y = y[i]; + if (z) { + c.z = z[i]; + } + if (m) { + c.m = m[i]; + } + + coords->setAt(c, i); + } + + return coords.release(); + }); + } + + int + GEOSCoordSeq_copyToArrays_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, + double* x, double* y, double* z, double* m) + { + return execute(extHandle, 0, [&]() { + CoordinateXYZM c; + for (std::size_t i = 0; i < cs->size(); i++) { + cs->getAt(i, c); + x[i] = c.x; + y[i] = c.y; + if (z) { + z[i] = c.z; + } + if (m) { + m[i] = c.m; + } + } + + return 1; + }); + } + + int + GEOSCoordSeq_copyToBuffer_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, + double* buf, int hasZ, int hasM) + { + using geos::geom::CoordinateType; + + return execute(extHandle, 0, [&]() { + CoordinateType srcType = cs->getCoordinateType(); + CoordinateType dstType; + std::size_t stride; + if (hasZ) { + if (hasM) { + dstType = CoordinateType::XYZM; + stride = 4; + } else { + dstType = CoordinateType::XYZ; + stride = 3; + } + } else { + if (hasM) { + dstType = CoordinateType::XYM; + stride = 3; + } else { + dstType = CoordinateType::XY; + stride = 2; + } + } + + if (srcType == dstType) { + std::memcpy(buf, cs->data(), cs->size() * stride * sizeof(double)); + } else { + switch(dstType) { + case CoordinateType::XY: { + for (std::size_t i = 0; i < cs->size(); i++) { + CoordinateXY* c = reinterpret_cast(buf + i*stride); + cs->getAt(i, *c); + } + break; + } + case CoordinateType::XYZ: { + for (std::size_t i = 0; i < cs->size(); i++) { + Coordinate* c = reinterpret_cast(buf + i*stride); + cs->getAt(i, *c); + } + break; + } + case CoordinateType::XYM: { + for (std::size_t i = 0; i < cs->size(); i++) { + CoordinateXYM* c = reinterpret_cast(buf + i*stride); + cs->getAt(i, *c); + } + break; + } + case CoordinateType::XYZM: { + for (std::size_t i = 0; i < cs->size(); i++) { + CoordinateXYZM* c = reinterpret_cast(buf + i*stride); + cs->getAt(i, *c); + } + break; + } + } + } + + return 1; + }); + } + + char + GEOSCoordSeq_hasZ_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, 2, [&]() { + return cs->hasZ(); + }); + } + + char + GEOSCoordSeq_hasM_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, 2, [&]() { + return cs->hasM(); + }); + } + + int + GEOSCoordSeq_setOrdinate_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, + unsigned int idx, unsigned int dim, double val) + { + return execute(extHandle, 0, [&]() { + cs->setOrdinate(idx, dim, val); + return 1; + }); + } + + int + GEOSCoordSeq_setX_r(GEOSContextHandle_t extHandle, CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate_r(extHandle, s, idx, CoordinateSequence::X, val); + } + + int + GEOSCoordSeq_setY_r(GEOSContextHandle_t extHandle, CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate_r(extHandle, s, idx, CoordinateSequence::Y, val); + } + + int + GEOSCoordSeq_setZ_r(GEOSContextHandle_t extHandle, CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate_r(extHandle, s, idx, CoordinateSequence::Z, val); + } + + int + GEOSCoordSeq_setM_r(GEOSContextHandle_t extHandle, CoordinateSequence* s, unsigned int idx, double val) + { + return GEOSCoordSeq_setOrdinate_r(extHandle, s, idx, CoordinateSequence::M, val); + } + + int + GEOSCoordSeq_setXY_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, unsigned int idx, double x, double y) + { + return execute(extHandle, 0, [&]() { + cs->setAt(CoordinateXY{x, y}, idx); + return 1; + }); + } + + int + GEOSCoordSeq_setXYZ_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs, unsigned int idx, double x, double y, double z) + { + return execute(extHandle, 0, [&]() { + cs->setAt(Coordinate{x, y, z}, idx); + return 1; + }); + } + + CoordinateSequence* + GEOSCoordSeq_clone_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + return cs->clone().release(); + }); + } + + int + GEOSCoordSeq_getOrdinate_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, + unsigned int idx, unsigned int dim, double* val) + { + return execute(extHandle, 0, [&]() { + *val = cs->getOrdinate(idx, dim); + return 1; + }); + } + + int + GEOSCoordSeq_getX_r(GEOSContextHandle_t extHandle, const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate_r(extHandle, s, idx, CoordinateSequence::X, val); + } + + int + GEOSCoordSeq_getY_r(GEOSContextHandle_t extHandle, const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate_r(extHandle, s, idx, CoordinateSequence::Y, val); + } + + int + GEOSCoordSeq_getZ_r(GEOSContextHandle_t extHandle, const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate_r(extHandle, s, idx, CoordinateSequence::Z, val); + } + + int + GEOSCoordSeq_getM_r(GEOSContextHandle_t extHandle, const CoordinateSequence* s, unsigned int idx, double* val) + { + return GEOSCoordSeq_getOrdinate_r(extHandle, s, idx, CoordinateSequence::M, val); + } + + int + GEOSCoordSeq_getXY_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int idx, double* x, double* y) + { + return execute(extHandle, 0, [&]() { + auto& c = cs->getAt(idx); + *x = c.x; + *y = c.y; + return 1; + }); + } + + int + GEOSCoordSeq_getXYZ_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int idx, double* x, double* y, double* z) + { + return execute(extHandle, 0, [&]() { + auto& c = cs->getAt(idx); + *x = c.x; + *y = c.y; + *z = c.z; + return 1; + }); + } + + int + GEOSCoordSeq_getSize_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int* size) + { + return execute(extHandle, 0, [&]() { + const std::size_t sz = cs->getSize(); + *size = static_cast(sz); + return 1; + }); + } + + int + GEOSCoordSeq_getDimensions_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, unsigned int* dims) + { + return execute(extHandle, 0, [&]() { + const std::size_t dim = cs->getDimension(); + *dims = static_cast(dim); + + return 1; + }); + } + + int + GEOSCoordSeq_isCCW_r(GEOSContextHandle_t extHandle, const CoordinateSequence* cs, char* val) + { + return execute(extHandle, 0, [&]() { + *val = geos::algorithm::Orientation::isCCW(cs); + return 1; + }); + } + + void + GEOSCoordSeq_destroy_r(GEOSContextHandle_t extHandle, CoordinateSequence* s) + { + return execute(extHandle, [&]() { + delete s; + }); + } + + const CoordinateSequence* + GEOSGeom_getCoordSeq_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + const SimpleCurve* ls = dynamic_cast(g); + if(ls) { + return ls->getCoordinatesRO(); + } + + const Point* p = dynamic_cast(g); + if(p) { + return p->getCoordinatesRO(); + } + + throw IllegalArgumentException("Geometry must be a Point or LineString"); + }); + } + + Geometry* + GEOSGeom_createEmptyPoint_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + return gf->createPoint().release(); + }); + } + + Geometry* + GEOSGeom_createPoint_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createPoint(std::unique_ptr(cs)).release(); + }); + } + + Geometry* + GEOSGeom_createPointFromXY_r(GEOSContextHandle_t extHandle, double x, double y) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + CoordinateXY c(x, y); + return gf->createPoint(c).release(); + }); + } + + Geometry* + GEOSGeom_createLinearRing_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createLinearRing(std::unique_ptr(cs)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyLineString_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createLineString().release(); + }); + } + + Geometry* + GEOSGeom_createLineString_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createLineString(std::unique_ptr(cs)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyPolygon_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + return gf->createPolygon().release(); + }); + } + + Geometry* + GEOSGeom_createPolygon_r(GEOSContextHandle_t extHandle, Geometry* shell, Geometry** holes, unsigned int nholes) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + bool good_holes = true, good_shell = true; + + // Validate input before taking ownership + for (std::size_t i = 0; i < nholes; i++) { + if ((!holes) || (!dynamic_cast(holes[i]))) { + good_holes = false; + break; + } + } + if (!dynamic_cast(shell)) { + good_shell = false; + } + + // Contract for GEOSGeom_createPolygon is to take ownership of arguments + // which implies freeing them on exception, + // see https://trac.osgeo.org/geos/ticket/1111 + if (!(good_holes && good_shell)) { + if (shell) delete shell; + for (std::size_t i = 0; i < nholes; i++) { + if (holes && holes[i]) + delete holes[i]; + } + if (!good_shell) + throw IllegalArgumentException("Shell is not a LinearRing"); + else + throw IllegalArgumentException("Hole is not a LinearRing"); + } + + std::unique_ptr tmpshell(static_cast(shell)); + if (nholes) { + std::vector> tmpholes(nholes); + for (size_t i = 0; i < nholes; i++) { + tmpholes[i].reset(static_cast(holes[i])); + } + + return gf->createPolygon(std::move(tmpshell), std::move(tmpholes)).release(); + } + + return gf->createPolygon(std::move(tmpshell)).release(); + }); + } + + Geometry* + GEOSGeom_createRectangle_r(GEOSContextHandle_t extHandle, + double xmin, double ymin, + double xmax, double ymax) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + Envelope env(xmin, xmax, ymin, ymax); + return (gf->toGeometry(&env)).release(); + }); + } + + Geometry* + GEOSGeom_createCircularString_r(GEOSContextHandle_t extHandle, CoordinateSequence* cs) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCircularString(std::unique_ptr(cs)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyCircularString_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCircularString(false, false).release(); + }); + } + + Geometry* + GEOSGeom_createCompoundCurve_r(GEOSContextHandle_t extHandle, Geometry** geoms, unsigned int ngeoms) + { + return execute(extHandle, [&]() -> Geometry* { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + bool invalid_input = false; + std::vector> geom_vec(ngeoms); + for (std::size_t i = 0; i < ngeoms; i++) { + if (SimpleCurve* c = dynamic_cast(geoms[i])) { + geom_vec[i].reset(c); + } else { + delete geoms[i]; + invalid_input = true; + } + } + + if (invalid_input) { + throw IllegalArgumentException("Input is not a SimpleCurve"); + } + + return gf->createCompoundCurve(std::move(geom_vec)).release(); + }); + } + + Geometry* + GEOSGeom_createEmptyCompoundCurve_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + + return gf->createCompoundCurve().release(); + }); + } + + Geometry* + GEOSGeom_createCurvePolygon_r(GEOSContextHandle_t extHandle, Geometry* p_shell, Geometry** p_holes, unsigned int nholes) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + bool good_holes = true, good_shell = true; + + std::unique_ptr shell; + std::vector> holes(nholes); + + if (Curve* c = dynamic_cast(p_shell)) { + shell.reset(c); + } else { + good_shell = false; + delete p_shell; + } + + for (std::size_t i = 0; i < nholes; i++) { + if (Curve* c = dynamic_cast(p_holes[i])) { + holes[i].reset(c); + } else { + good_shell = false; + delete p_holes[i]; + } + } + + if (good_shell && good_holes) { + return gf->createCurvePolygon(std::move(shell), std::move(holes)).release(); + } else if (!good_shell) { + throw IllegalArgumentException("Shell is not a Curve"); + } else { + throw IllegalArgumentException("Hole is not a Curve"); + } + }); + } + + + Geometry* + GEOSGeom_createEmptyCurvePolygon_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + const GeometryFactory* gf = handle->geomFactory; + return gf->createCurvePolygon(false, false).release(); + }); + } + + Geometry* + GEOSGeom_clone_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + return g->clone().release(); + }); + } + + Geometry* + GEOSGeom_setPrecision_r(GEOSContextHandle_t extHandle, const GEOSGeometry* g, + double gridSize, int flags) + { + return execute(extHandle, [&]() { + PrecisionModel newpm; + if(gridSize != 0) { + // Convert gridSize to scale factor + double scale = 1.0 / std::abs(gridSize); + newpm = PrecisionModel(scale); + } + + const PrecisionModel* pm = g->getPrecisionModel(); + double cursize = pm->isFloating() ? 0 : 1.0 / pm->getScale(); + std::unique_ptr ret; + GeometryFactory::Ptr gf = + GeometryFactory::create(&newpm, g->getSRID()); + if(gridSize != 0 && cursize != gridSize) { + GeometryPrecisionReducer reducer(*gf); + reducer.setChangePrecisionModel(true); + reducer.setUseAreaReducer(!(flags & GEOS_PREC_NO_TOPO)); + reducer.setPointwise(flags & GEOS_PREC_NO_TOPO); + reducer.setRemoveCollapsedComponents(!(flags & GEOS_PREC_KEEP_COLLAPSED)); + ret = reducer.reduce(*g); + } + else { + // No need or willing to snap, just change the factory + ret = gf->createGeometry(g); + } + return ret.release(); + }); + } + + double + GEOSGeom_getPrecision_r(GEOSContextHandle_t extHandle, const GEOSGeometry* g) + { + return execute(extHandle, -1.0, [&]() { + const PrecisionModel* pm = g->getPrecisionModel(); + double cursize = pm->isFloating() ? 0 : 1.0 / pm->getScale(); + return cursize; + }); + } + + int + GEOSGeom_getDimensions_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 0, [&]() { + return (int) g->getDimension(); + }); + } + + int + GEOSGeom_getCoordinateDimension_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, 0, [&]() { + return (int)(g->getCoordinateDimension()); + }); + } + + int + GEOSGeom_getXMin_r(GEOSContextHandle_t extHandle, const Geometry* g, double* value) + { + return execute(extHandle, 0, [&]() { + if(g->isEmpty()) { + return 0; + } + + *value = g->getEnvelopeInternal()->getMinX(); + return 1; + }); + } + + int + GEOSGeom_getXMax_r(GEOSContextHandle_t extHandle, const Geometry* g, double* value) + { + return execute(extHandle, 0, [&]() { + if(g->isEmpty()) { + return 0; + } + + *value = g->getEnvelopeInternal()->getMaxX(); + return 1; + }); + } + + int + GEOSGeom_getYMin_r(GEOSContextHandle_t extHandle, const Geometry* g, double* value) + { + return execute(extHandle, 0, [&]() { + if(g->isEmpty()) { + return 0; + } + + *value = g->getEnvelopeInternal()->getMinY(); + return 1; + }); + } + + int + GEOSGeom_getYMax_r(GEOSContextHandle_t extHandle, const Geometry* g, double* value) + { + return execute(extHandle, 0, [&]() { + if(g->isEmpty()) { + return 0; + } + + *value = g->getEnvelopeInternal()->getMaxY(); + return 1; + }); + } + + int + GEOSGeom_getExtent_r(GEOSContextHandle_t extHandle, const Geometry* g, double* xmin, double* ymin, double* xmax, double* ymax) + { + return execute(extHandle, 0, [&]() { + if(g->isEmpty()) { + return 0; + } + const Envelope* extent = g->getEnvelopeInternal(); + *xmin = extent->getMinX(); + *ymin = extent->getMinY(); + *xmax = extent->getMaxX(); + *ymax = extent->getMaxY(); + return 1; + }); + } + + Geometry* + GEOSSimplify_r(GEOSContextHandle_t extHandle, const Geometry* g1, double tolerance) + { + return execute(extHandle, [&]() { + Geometry::Ptr g3(geos::simplify::DouglasPeuckerSimplifier::simplify(g1, tolerance)); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSTopologyPreserveSimplify_r(GEOSContextHandle_t extHandle, const Geometry* g1, double tolerance) + { + return execute(extHandle, [&]() { + Geometry::Ptr g3(geos::simplify::TopologyPreservingSimplifier::simplify(g1, tolerance)); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + + /* WKT Reader */ + WKTReader* + GEOSWKTReader_create_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t *handle = reinterpret_cast(extHandle); + return new WKTReader((GeometryFactory *) handle->geomFactory); + }); + } + + void + GEOSWKTReader_destroy_r(GEOSContextHandle_t extHandle, WKTReader* reader) + { + return execute(extHandle, [&]() { + delete reader; + }); + } + + void + GEOSWKTReader_setFixStructure_r(GEOSContextHandle_t extHandle, WKTReader* reader, char doFix) + { + return execute(extHandle, [&]() { + return reader->setFixStructure(doFix); + }); + } + + Geometry* + GEOSWKTReader_read_r(GEOSContextHandle_t extHandle, WKTReader* reader, const char* wkt) + { + return execute(extHandle, [&]() { + const std::string wktstring(wkt); + return reader->read(wktstring).release(); + }); + } + + /* WKT Writer */ + WKTWriter* + GEOSWKTWriter_create_r(GEOSContextHandle_t extHandle) + { + using geos::io::WKTWriter; + + return execute(extHandle, [&]() { + return new WKTWriter(); + }); + } + + void + GEOSWKTWriter_destroy_r(GEOSContextHandle_t extHandle, WKTWriter* Writer) + { + execute(extHandle, [&]() { + delete Writer; + }); + } + + + char* + GEOSWKTWriter_write_r(GEOSContextHandle_t extHandle, WKTWriter* writer, const Geometry* geom) + { + return execute(extHandle, [&]() { + std::string sgeom(writer->write(geom)); + char* result = gstrdup(sgeom); + return result; + }); + } + + void + GEOSWKTWriter_setTrim_r(GEOSContextHandle_t extHandle, WKTWriter* writer, char trim) + { + execute(extHandle, [&]() { + writer->setTrim(0 != trim); + }); + } + + void + GEOSWKTWriter_setRoundingPrecision_r(GEOSContextHandle_t extHandle, WKTWriter* writer, int precision) + { + execute(extHandle, [&]() { + writer->setRoundingPrecision(precision); + }); + } + + void + GEOSWKTWriter_setOutputDimension_r(GEOSContextHandle_t extHandle, WKTWriter* writer, int dim) + { + execute(extHandle, [&]() { + writer->setOutputDimension(static_cast(dim)); + }); + } + + int + GEOSWKTWriter_getOutputDimension_r(GEOSContextHandle_t extHandle, WKTWriter* writer) + { + return execute(extHandle, -1, [&]() { + return writer->getOutputDimension(); + }); + } + + void + GEOSWKTWriter_setOld3D_r(GEOSContextHandle_t extHandle, WKTWriter* writer, int useOld3D) + { + execute(extHandle, [&]() { + writer->setOld3D(0 != useOld3D); + }); + } + + /* WKB Reader */ + WKBReader* + GEOSWKBReader_create_r(GEOSContextHandle_t extHandle) + { + using geos::io::WKBReader; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + return new WKBReader(*(GeometryFactory*)handle->geomFactory); + }); + } + + void + GEOSWKBReader_destroy_r(GEOSContextHandle_t extHandle, WKBReader* reader) + { + execute(extHandle, [&]() { + delete reader; + }); + } + + void + GEOSWKBReader_setFixStructure_r(GEOSContextHandle_t extHandle, WKBReader* reader, char doFix) + { + return execute(extHandle, [&]() { + return reader->setFixStructure(doFix); + }); + } + + struct membuf : public std::streambuf { + membuf(char* s, std::size_t n) + { + setg(s, s, s + n); + } + }; + + Geometry* + GEOSWKBReader_read_r(GEOSContextHandle_t extHandle, WKBReader* reader, const unsigned char* wkb, std::size_t size) + { + return execute(extHandle, [&]() { + return reader->read(wkb, size).release(); + }); + } + + Geometry* + GEOSWKBReader_readHEX_r(GEOSContextHandle_t extHandle, WKBReader* reader, const unsigned char* hex, std::size_t size) + { + return execute(extHandle, [&]() { + std::string hexstring(reinterpret_cast(hex), size); + std::istringstream is(std::ios_base::binary); + is.str(hexstring); + is.seekg(0, std::ios::beg); // rewind reader pointer + + return reader->readHEX(is).release(); + }); + } + + /* WKB Writer */ + WKBWriter* + GEOSWKBWriter_create_r(GEOSContextHandle_t extHandle) + { + using geos::io::WKBWriter; + + return execute(extHandle, [&]() { + return new WKBWriter(); + }); + } + + void + GEOSWKBWriter_destroy_r(GEOSContextHandle_t extHandle, WKBWriter* Writer) + { + execute(extHandle, [&]() { + delete Writer; + }); + } + + + /* The caller owns the result */ + unsigned char* + GEOSWKBWriter_write_r(GEOSContextHandle_t extHandle, WKBWriter* writer, const Geometry* geom, std::size_t* size) + { + return execute(extHandle, [&]() { + std::ostringstream os(std::ios_base::binary); + writer->write(*geom, os); + + const std::string& wkbstring = os.str(); + const std::size_t len = wkbstring.length(); + + unsigned char* result = (unsigned char*) malloc(len); + std::memcpy(result, wkbstring.c_str(), len); + *size = len; + return result; + }); + } + + /* The caller owns the result */ + unsigned char* + GEOSWKBWriter_writeHEX_r(GEOSContextHandle_t extHandle, WKBWriter* writer, const Geometry* geom, std::size_t* size) + { + return execute(extHandle, [&]() { + std::ostringstream os(std::ios_base::binary); + writer->writeHEX(*geom, os); + std::string wkbstring(os.str()); + const std::size_t len = wkbstring.length(); + + unsigned char* result = (unsigned char*) malloc(len); + std::memcpy(result, wkbstring.c_str(), len); + *size = len; + return result; + }); + } + + int + GEOSWKBWriter_getOutputDimension_r(GEOSContextHandle_t extHandle, const GEOSWKBWriter* writer) + { + return execute(extHandle, 0, [&]() { + return writer->getOutputDimension(); + }); + } + + void + GEOSWKBWriter_setOutputDimension_r(GEOSContextHandle_t extHandle, GEOSWKBWriter* writer, int newDimension) + { + execute(extHandle, [&]() { + writer->setOutputDimension(static_cast(newDimension)); + }); + } + + int + GEOSWKBWriter_getByteOrder_r(GEOSContextHandle_t extHandle, const GEOSWKBWriter* writer) + { + return execute(extHandle, 0, [&]() { + return writer->getByteOrder(); + }); + } + + void + GEOSWKBWriter_setByteOrder_r(GEOSContextHandle_t extHandle, GEOSWKBWriter* writer, int newByteOrder) + { + execute(extHandle, [&]() { + writer->setByteOrder(newByteOrder); + }); + } + + char + GEOSWKBWriter_getIncludeSRID_r(GEOSContextHandle_t extHandle, const GEOSWKBWriter* writer) + { + return execute(extHandle, 2, [&]{ + return writer->getIncludeSRID(); + }); + } + + void + GEOSWKBWriter_setIncludeSRID_r(GEOSContextHandle_t extHandle, GEOSWKBWriter* writer, const char newIncludeSRID) + { + execute(extHandle, [&]{ + writer->setIncludeSRID(newIncludeSRID); + }); + } + + int + GEOSWKBWriter_getFlavor_r(GEOSContextHandle_t extHandle, const GEOSWKBWriter* writer) + { + return execute(extHandle, -1, [&]{ + return writer->getFlavor(); + }); + } + + void + GEOSWKBWriter_setFlavor_r(GEOSContextHandle_t extHandle, GEOSWKBWriter* writer, int flavor) + { + execute(extHandle, [&]{ + writer->setFlavor(flavor); + }); + } + + /* GeoJSON Reader */ + GeoJSONReader* + GEOSGeoJSONReader_create_r(GEOSContextHandle_t extHandle) + { + using geos::io::GeoJSONReader; + + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t *handle = reinterpret_cast(extHandle); + return new GeoJSONReader(*(GeometryFactory*)handle->geomFactory); + }); + } + + void + GEOSGeoJSONReader_destroy_r(GEOSContextHandle_t extHandle, GEOSGeoJSONReader* reader) + { + return execute(extHandle, [&]() { + delete reader; + }); + } + + Geometry* + GEOSGeoJSONReader_readGeometry_r(GEOSContextHandle_t extHandle, GEOSGeoJSONReader* reader, const char* geojson) + { + return execute(extHandle, [&]() { + const std::string geojsonstring(geojson); + return reader->read(geojsonstring).release(); + }); + } + + /* GeoJSON Writer */ + GeoJSONWriter* + GEOSGeoJSONWriter_create_r(GEOSContextHandle_t extHandle) + { + using geos::io::GeoJSONWriter; + + return execute(extHandle, [&]() { + return new GeoJSONWriter(); + }); + } + + void + GEOSGeoJSONWriter_destroy_r(GEOSContextHandle_t extHandle, GEOSGeoJSONWriter* writer) + { + return execute(extHandle, [&]() { + delete writer; + }); + } + + char* + GEOSGeoJSONWriter_writeGeometry_r(GEOSContextHandle_t extHandle, GEOSGeoJSONWriter* writer, const GEOSGeometry* g, int indent) + { + return execute(extHandle, [&]() { + std::string geojson; + if (indent >= 0) { + geojson = writer->writeFormatted(g, geos::io::GeoJSONType::GEOMETRY, indent); + } else { + geojson = writer->write(g, geos::io::GeoJSONType::GEOMETRY); + } + char* result = gstrdup(geojson); + return result; + }); + } + + void + GEOSGeoJSONWriter_setOutputDimension_r(GEOSContextHandle_t extHandle, GeoJSONWriter* writer, int dim) + { + execute(extHandle, [&]() { + writer->setOutputDimension(static_cast(dim)); + }); + } + + int + GEOSGeoJSONWriter_getOutputDimension_r(GEOSContextHandle_t extHandle, GeoJSONWriter* writer) + { + return execute(extHandle, -1, [&]() { + return writer->getOutputDimension(); + }); + } + + +//----------------------------------------------------------------- +// Prepared Geometry +//----------------------------------------------------------------- + + const PreparedGeometry* + GEOSPrepare_r(GEOSContextHandle_t extHandle, const Geometry* g) + { + return execute(extHandle, [&]() { + return geos::geom::prep::PreparedGeometryFactory::prepare(g).release(); + }); + } + + void + GEOSPreparedGeom_destroy_r(GEOSContextHandle_t extHandle, const PreparedGeometry* a) + { + execute(extHandle, [&]() { + delete a; + }); + } + + char + GEOSPreparedContains_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->contains(g); + }); + } + + char + GEOSPreparedContainsXY_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, double x, double y) + { + extHandle->point2d->setXY(x, y); + + return GEOSPreparedContains_r(extHandle, pg, extHandle->point2d.get()); + } + + char + GEOSPreparedContainsProperly_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->containsProperly(g); + }); + } + + char + GEOSPreparedCoveredBy_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->coveredBy(g); + }); + } + + char + GEOSPreparedCovers_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->covers(g); + }); + } + + char + GEOSPreparedCrosses_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->crosses(g); + }); + } + + char + GEOSPreparedDisjoint_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->disjoint(g); + }); + } + + char + GEOSPreparedIntersects_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->intersects(g); + }); + } + + char + GEOSPreparedIntersectsXY_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, double x, double y) + { + extHandle->point2d->setXY(x, y); + + return GEOSPreparedIntersects_r(extHandle, pg, extHandle->point2d.get()); + } + + char + GEOSPreparedOverlaps_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->overlaps(g); + }); + } + + char + GEOSPreparedTouches_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->touches(g); + }); + } + + char + GEOSPreparedWithin_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, 2, [&]() { + return pg->within(g); + }); + } + + char * + GEOSPreparedRelate_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, [&]() -> char * { + return gstrdup(pg->relate(g)->toString()); + }); + } + + char + GEOSPreparedRelatePattern_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g, const char* imPattern) + { + return execute(extHandle, 2, [&]() { + return pg->relate(g, std::string(imPattern)); + }); + } + + CoordinateSequence* + GEOSPreparedNearestPoints_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, const Geometry* g) + { + return execute(extHandle, [&]() -> geos::geom::CoordinateSequence* { + return pg->nearestPoints(g).release(); + }); + } + + int + GEOSPreparedDistance_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, + const Geometry* g, double* dist) + { + return execute(extHandle, 0, [&]() { + *dist = pg->distance(g); + return 1; + }); + } + + char + GEOSPreparedDistanceWithin_r(GEOSContextHandle_t extHandle, + const PreparedGeometry* pg, + const Geometry* g, double dist) + { + return execute(extHandle, 2, [&]() { + return pg->isWithinDistance(g, dist); + }); + } + +//----------------------------------------------------------------- +// STRtree +//----------------------------------------------------------------- + + GEOSSTRtree* + GEOSSTRtree_create_r(GEOSContextHandle_t extHandle, + std::size_t nodeCapacity) + { + return execute(extHandle, [&]() { + return new GEOSSTRtree(nodeCapacity); + }); + } + + int + GEOSSTRtree_build_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree) + { + return execute(extHandle, 0, [&]() { + tree->build(); + return 1; + }); + } + + void + GEOSSTRtree_insert_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + const Geometry* g, + void* item) + { + execute(extHandle, [&]() { + tree->insert(g->getEnvelopeInternal(), item); + }); + } + + void + GEOSSTRtree_query_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + const Geometry* g, + GEOSQueryCallback callback, + void* userdata) + { + execute(extHandle, [&]() { + CAPI_ItemVisitor visitor(callback, userdata); + tree->query(g->getEnvelopeInternal(), visitor); + }); + } + + const GEOSGeometry* + GEOSSTRtree_nearest_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + const Geometry* geom) + { + return (const GEOSGeometry*) GEOSSTRtree_nearest_generic_r(extHandle, tree, geom, geom, nullptr, nullptr); + } + + const void* + GEOSSTRtree_nearest_generic_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + const void* item, + const Geometry* itemEnvelope, + GEOSDistanceCallback distancefn, + void* userdata) + { + struct CustomItemDistance { + CustomItemDistance(GEOSDistanceCallback p_distancefn, void* p_userdata) + : m_distancefn(p_distancefn), m_userdata(p_userdata) {} + + GEOSDistanceCallback m_distancefn; + void* m_userdata; + + double operator()(const void* a, const void* b) const + { + double d; + + if(!m_distancefn(a, b, &d, m_userdata)) { + throw std::runtime_error(std::string("Failed to compute distance.")); + } + + return d; + } + }; + + struct GeometryDistance { + double operator()(void* a, void* b) const { + return static_cast(a)->distance(static_cast(b)); + } + }; + + return execute(extHandle, [&]() { + if(distancefn) { + CustomItemDistance itemDistance(distancefn, userdata); + return tree->nearestNeighbour(*itemEnvelope->getEnvelopeInternal(), (void*) item, itemDistance); + } + else { + return tree->nearestNeighbour(*itemEnvelope->getEnvelopeInternal(), (void*) item); + } + }); + } + + void + GEOSSTRtree_iterate_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + GEOSQueryCallback callback, + void* userdata) + { + return execute(extHandle, [&]() { + CAPI_ItemVisitor visitor(callback, userdata); + tree->iterate(visitor); + }); + } + + char + GEOSSTRtree_remove_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree, + const Geometry* g, + void* item) { + return execute(extHandle, 2, [&]() { + return tree->remove(g->getEnvelopeInternal(), item); + }); + } + + void + GEOSSTRtree_destroy_r(GEOSContextHandle_t extHandle, + GEOSSTRtree* tree) + { + return execute(extHandle, [&]() { + delete tree; + }); + } + + double + GEOSProject_r(GEOSContextHandle_t extHandle, + const Geometry* g, + const Geometry* p) + { + return execute(extHandle, -1.0, [&]() { + const Point* point = dynamic_cast(p); + if(!point) { + throw std::runtime_error("third argument of GEOSProject_r must be Point"); + } + const geos::geom::Coordinate inputPt(*p->getCoordinate()); + return geos::linearref::LengthIndexedLine(g).project(inputPt); + }); + } + + + Geometry* + GEOSInterpolate_r(GEOSContextHandle_t extHandle, const Geometry* g, double d) + { + return execute(extHandle, [&]() { + GEOSContextHandleInternal_t* handle = reinterpret_cast(extHandle); + + geos::linearref::LengthIndexedLine lil(g); + geos::geom::Coordinate coord = lil.extractPoint(d); + const GeometryFactory* gf = handle->geomFactory; + auto point = coord.isNull() ? gf->createPoint(g->getCoordinateDimension()) : gf->createPoint(coord); + point->setSRID(g->getSRID()); + return point.release(); + }); + } + + + double + GEOSProjectNormalized_r(GEOSContextHandle_t extHandle, const Geometry* g, + const Geometry* p) + { + + double length; + double distance; + if(GEOSLength_r(extHandle, g, &length) != 1) { + return -1.0; + }; + + distance = GEOSProject_r(extHandle, g, p); + + if (distance == 0.0 && length == 0.0) + return 0.0; + + /* Meaningless projection? error */ + if (distance < 0.0 || ! std::isfinite(distance) || length == 0.0) { + return -1.0; + } else { + return distance / length; + } + } + + + Geometry* + GEOSInterpolateNormalized_r(GEOSContextHandle_t extHandle, const Geometry* g, + double d) + { + double length; + if (GEOSLength_r(extHandle, g, &length) != 1) { + return 0; + } + return GEOSInterpolate_r(extHandle, g, d * length); + } + + GEOSGeometry* + GEOSGeom_extractUniquePoints_r(GEOSContextHandle_t extHandle, + const GEOSGeometry* g) + { + return execute(extHandle, [&]() { + /* 1: extract points */ + std::vector coords; + geos::util::UniqueCoordinateArrayFilter filter(coords); + g->apply_ro(&filter); + + /* 2: for each point, create a geometry and put into a vector */ + std::vector> points; + points.reserve(coords.size()); + const GeometryFactory* factory = g->getFactory(); + for(std::vector::iterator it = coords.begin(), + itE = coords.end(); + it != itE; ++it) { + auto point = factory->createPoint(*(*it)); + points.push_back(std::move(point)); + } + + /* 3: create a multipoint */ + auto out = factory->createMultiPoint(std::move(points)); + out->setSRID(g->getSRID()); + return out.release(); + + }); + } + + int GEOSOrientationIndex_r(GEOSContextHandle_t extHandle, + double Ax, double Ay, double Bx, double By, double Px, double Py) + { + return execute(extHandle, 2, [&]() { + Coordinate A(Ax, Ay); + Coordinate B(Bx, By); + Coordinate P(Px, Py); + return geos::algorithm::Orientation::index(A, B, P); + }); + } + + GEOSGeometry* + GEOSSharedPaths_r(GEOSContextHandle_t extHandle, const GEOSGeometry* g1, const GEOSGeometry* g2) + { + using geos::operation::sharedpaths::SharedPathsOp; + + if(nullptr == extHandle) { + return nullptr; + } + GEOSContextHandleInternal_t* handle = + reinterpret_cast(extHandle); + if(handle->initialized == 0) { + return nullptr; + } + + SharedPathsOp::PathList forw, back; + try { + SharedPathsOp::sharedPathsOp(*g1, *g2, forw, back); + } + catch(const std::exception& e) { + SharedPathsOp::clearEdges(forw); + SharedPathsOp::clearEdges(back); + handle->ERROR_MESSAGE("%s", e.what()); + return nullptr; + } + catch(...) { + SharedPathsOp::clearEdges(forw); + SharedPathsOp::clearEdges(back); + handle->ERROR_MESSAGE("Unknown exception thrown"); + return nullptr; + } + + // Now forw and back have the geoms we want to use to construct + // our output GeometryCollections... + + const GeometryFactory* factory = g1->getFactory(); + std::size_t count; + + std::vector> out1; + count = forw.size(); + out1.reserve(count); + for(std::size_t i = 0; i < count; ++i) { + out1.emplace_back(forw[i]); + } + std::unique_ptr out1g( + factory->createMultiLineString(std::move(out1)) + ); + + std::vector> out2; + count = back.size(); + out2.reserve(count); + for(std::size_t i = 0; i < count; ++i) { + out2.emplace_back(back[i]); + } + std::unique_ptr out2g( + factory->createMultiLineString(std::move(out2)) + ); + + std::vector> out; + out.reserve(2); + out.push_back(std::move(out1g)); + out.push_back(std::move(out2g)); + + std::unique_ptr outg( + factory->createGeometryCollection(std::move(out)) + ); + + outg->setSRID(g1->getSRID()); + return outg.release(); + } + + GEOSGeometry* + GEOSSnap_r(GEOSContextHandle_t extHandle, const GEOSGeometry* g1, + const GEOSGeometry* g2, double tolerance) + { + return execute(extHandle, [&]() { + geos::operation::overlay::snap::GeometrySnapper snapper(*g1); + std::unique_ptr ret = snapper.snapTo(*g2, tolerance); + ret->setSRID(g1->getSRID()); + return ret.release(); + }); + } + + BufferParameters* + GEOSBufferParams_create_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + return new BufferParameters(); + }); + } + + void + GEOSBufferParams_destroy_r(GEOSContextHandle_t extHandle, BufferParameters* p) + { + (void)extHandle; + delete p; + } + + int + GEOSBufferParams_setEndCapStyle_r(GEOSContextHandle_t extHandle, + GEOSBufferParams* p, int style) + { + return execute(extHandle, 0, [&]() { + if(style > BufferParameters::CAP_SQUARE) { + throw IllegalArgumentException("Invalid buffer endCap style"); + } + p->setEndCapStyle(static_cast(style)); + return 1; + }); + } + + int + GEOSBufferParams_setJoinStyle_r(GEOSContextHandle_t extHandle, + GEOSBufferParams* p, int style) + { + return execute(extHandle, 0, [&]() { + if(style > BufferParameters::JOIN_BEVEL) { + throw IllegalArgumentException("Invalid buffer join style"); + } + p->setJoinStyle(static_cast(style)); + + return 1; + }); + } + + int + GEOSBufferParams_setMitreLimit_r(GEOSContextHandle_t extHandle, + GEOSBufferParams* p, double limit) + { + return execute(extHandle, 0, [&]() { + p->setMitreLimit(limit); + return 1; + }); + } + + int + GEOSBufferParams_setQuadrantSegments_r(GEOSContextHandle_t extHandle, + GEOSBufferParams* p, int segs) + { + return execute(extHandle, 0, [&]() { + p->setQuadrantSegments(segs); + return 1; + }); + } + + int + GEOSBufferParams_setSingleSided_r(GEOSContextHandle_t extHandle, + GEOSBufferParams* p, int ss) + { + return execute(extHandle, 0, [&]() { + p->setSingleSided((ss != 0)); + return 1; + }); + } + + Geometry* + GEOSBufferWithParams_r(GEOSContextHandle_t extHandle, const Geometry* g1, const BufferParameters* bp, double width) + { + using geos::operation::buffer::BufferOp; + + return execute(extHandle, [&]() { + BufferOp op(g1, *bp); + std::unique_ptr g3 = op.getResultGeometry(width); + g3->setSRID(g1->getSRID()); + return g3.release(); + }); + } + + Geometry* + GEOSDelaunayTriangulation_r(GEOSContextHandle_t extHandle, const Geometry* g1, double tolerance, int onlyEdges) + { + using geos::triangulate::DelaunayTriangulationBuilder; + + return execute(extHandle, [&]() -> Geometry* { + DelaunayTriangulationBuilder builder; + builder.setTolerance(tolerance); + builder.setSites(*g1); + + if(onlyEdges) { + Geometry* out = builder.getEdges(*g1->getFactory()).release(); + out->setSRID(g1->getSRID()); + return out; + } + else { + Geometry* out = builder.getTriangles(*g1->getFactory()).release(); + out->setSRID(g1->getSRID()); + return out; + } + }); + } + + Geometry* + GEOSConstrainedDelaunayTriangulation_r(GEOSContextHandle_t extHandle, const Geometry* g1) + { + using geos::triangulate::polygon::ConstrainedDelaunayTriangulator; + + return execute(extHandle, [&]() -> Geometry* { + return ConstrainedDelaunayTriangulator::triangulate(g1).release(); + }); + } + + Geometry* + GEOSVoronoiDiagram_r(GEOSContextHandle_t extHandle, const Geometry* g1, const Geometry* env, double tolerance, + int flags) + { + using geos::triangulate::VoronoiDiagramBuilder; + + return execute(extHandle, [&]() -> Geometry* { + VoronoiDiagramBuilder builder; + builder.setSites(*g1); + builder.setTolerance(tolerance); + builder.setOrdered(flags & GEOS_VORONOI_PRESERVE_ORDER); + std::unique_ptr out; + if(env) { + builder.setClipEnvelope(env->getEnvelopeInternal()); + } + if(flags & GEOS_VORONOI_ONLY_EDGES) { + out = builder.getDiagramEdges(*g1->getFactory()); + } + else { + out = builder.getDiagram(*g1->getFactory()); + } + + out->setSRID(g1->getSRID()); + return out.release(); + }); + } + + int + GEOSSegmentIntersection_r(GEOSContextHandle_t extHandle, + double ax0, double ay0, double ax1, double ay1, + double bx0, double by0, double bx1, double by1, + double* cx, double* cy) + { + return execute(extHandle, 0, [&]() { + geos::geom::LineSegment a(ax0, ay0, ax1, ay1); + geos::geom::LineSegment b(bx0, by0, bx1, by1); + geos::geom::Coordinate isect = a.intersection(b); + + if(isect.isNull()) { + return -1; + } + + *cx = isect.x; + *cy = isect.y; + + return 1; + }); + } + + int + GEOSCoverageIsValid_r(GEOSContextHandle_t extHandle, + const Geometry* input, + double gapWidth, + Geometry** invalidEdges) + { + using geos::coverage::CoverageValidator; + + return execute(extHandle, 2, [&]() { + const GeometryCollection* col = dynamic_cast(input); + if (!col) + throw geos::util::IllegalArgumentException("input is not a collection"); + + // Initialize to nullptr + if (invalidEdges) *invalidEdges = nullptr; + + std::vector coverage; + for (const auto& g : *col) { + coverage.push_back(g.get()); + } + + CoverageValidator cov(coverage); + cov.setGapWidth(gapWidth); + std::vector> invalid = cov.validate(); + bool hasInvalid = CoverageValidator::hasInvalidResult(invalid); + + if (invalidEdges) { + const GeometryFactory* gf = input->getFactory(); + for (auto& g : invalid) { + // Replace nullptr with 'MULTILINESTRING EMPTY' + if (g == nullptr) { + auto empty = gf->createEmpty(1); + g.reset(empty.release()); + } + } + auto r = gf->createGeometryCollection(std::move(invalid)); + *invalidEdges = r.release(); + } + + return hasInvalid ? 0 : 1; + }); + } + + Geometry* + GEOSCoverageSimplifyVW_r(GEOSContextHandle_t extHandle, + const Geometry* input, + double tolerance, + int preserveBoundary) + { + using geos::coverage::CoverageSimplifier; + + return execute(extHandle, [&]() -> Geometry* { + const GeometryCollection* col = dynamic_cast(input); + if (!col) + return nullptr; + + std::vector coverage; + for (const auto& g : *col) { + coverage.push_back(g.get()); + } + CoverageSimplifier cov(coverage); + std::vector> simple; + if (preserveBoundary == 1) { + simple = cov.simplifyInner(tolerance); + } + else if (preserveBoundary == 0) { + simple = cov.simplify(tolerance); + } + else return nullptr; + + const GeometryFactory* gf = input->getFactory(); + std::unique_ptr r = gf->createGeometryCollection(std::move(simple)); + return r.release(); + }); + } + + +/************************************************************************ + * Coverage Cleaner + */ + + GEOSCoverageCleanParams* + GEOSCoverageCleanParams_create_r(GEOSContextHandle_t extHandle) + { + return execute(extHandle, [&]() { + GEOSCoverageCleanParams* p = new GEOSCoverageCleanParams(); + p->overlapMergeStrategy = geos::coverage::CoverageCleaner::MERGE_LONGEST_BORDER; + p->snappingDistance = -1.0; + p->gapMaximumWidth = 0.0; + return p; + }); + } + + void + GEOSCoverageCleanParams_destroy_r(GEOSContextHandle_t extHandle, + GEOSCoverageCleanParams* params) + { + (void)extHandle; + delete params; + } + + int + GEOSCoverageCleanParams_setSnappingDistance_r(GEOSContextHandle_t extHandle, + GEOSCoverageCleanParams* params, double snappingDistance) + { + return execute(extHandle, 0, [&]() { + params->snappingDistance = snappingDistance; + return 1; + }); + } + + int + GEOSCoverageCleanParams_setGapMaximumWidth_r(GEOSContextHandle_t extHandle, + GEOSCoverageCleanParams* params, double gapMaximumWidth) + { + return execute(extHandle, 0, [&]() { + params->gapMaximumWidth = gapMaximumWidth; + return 1; + }); + } + + int + GEOSCoverageCleanParams_setOverlapMergeStrategy_r(GEOSContextHandle_t extHandle, + GEOSCoverageCleanParams* params, int overlapMergeStrategy) + { + return execute(extHandle, 0, [&]() { + if ( overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_LONGEST_BORDER + && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MAX_AREA + && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MIN_AREA + && overlapMergeStrategy != geos::coverage::CoverageCleaner::MERGE_MIN_INDEX) + { + extHandle->ERROR_MESSAGE("GEOSCoverageCleanParams_setOverlapMergeStrategy: Invalid overlapMergeStrategy"); + return 0; + } + params->overlapMergeStrategy = overlapMergeStrategy; + return 1; + }); + } + + GEOSGeometry * + GEOSCoverageCleanWithParams_r(GEOSContextHandle_t extHandle, + const GEOSGeometry* input, + const GEOSCoverageCleanParams* params) + { + using geos::coverage::CoverageCleaner; + + return execute(extHandle, [&]() -> Geometry* { + const GeometryCollection* col = dynamic_cast(input); + if (!col) + return nullptr; + + std::vector coverage; + for (const auto& g : *col) { + coverage.push_back(g.get()); + } + geos::coverage::CoverageCleaner c(coverage); + if (params) { + c.setSnappingDistance(params->snappingDistance); + c.setGapMaximumWidth(params->gapMaximumWidth); + c.setOverlapMergeStrategy(params->overlapMergeStrategy); + } + c.clean(); + + auto cleanCov = c.getResult(); + const GeometryFactory* gf = input->getFactory(); + std::unique_ptr r = gf->createGeometryCollection(std::move(cleanCov)); + return r.release(); + }); + } + + GEOSGeometry * + GEOSCoverageClean_r(GEOSContextHandle_t extHandle, + const GEOSGeometry* input) + { + return GEOSCoverageCleanWithParams_r(extHandle, input, nullptr); + } + + +} /* extern "C" */ diff --git a/Sources/geos/include/geos.h b/Sources/geos/include/geos.h new file mode 100644 index 0000000..31be3a9 --- /dev/null +++ b/Sources/geos/include/geos.h @@ -0,0 +1,39 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +/* + * \file geos.h + * \brief + * This file is intended as an include wrapper for client application. + * It includes commonly needed GEOS headers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Basic namespace for all GEOS functionalities. +namespace geos { +} diff --git a/Sources/geos/include/geos/algorithm/Angle.h b/Sources/geos/include/geos/algorithm/Angle.h new file mode 100644 index 0000000..6f86f3d --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Angle.h @@ -0,0 +1,270 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Angle.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for constants + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/// Utility functions for working with angles. +// +/// Unless otherwise noted, methods in this class express angles in radians. +/// +class GEOS_DLL Angle { +public: + + static constexpr double PI_TIMES_2 = 2.0 * MATH_PI; + static constexpr double PI_OVER_2 = MATH_PI / 2.0; + static constexpr double PI_OVER_4 = MATH_PI / 4.0; + + /// Constant representing counterclockwise orientation + static const int COUNTERCLOCKWISE = Orientation::COUNTERCLOCKWISE; + + /// Constant representing clockwise orientation + static const int CLOCKWISE = Orientation::CLOCKWISE; + + /// Constant representing no orientation + static const int NONE = Orientation::COLLINEAR; + + /// Converts from radians to degrees. + /// + /// @param radians an angle in radians + /// @return the angle in degrees + /// + static double toDegrees(double radians); + + /// Converts from degrees to radians. + /// + /// @param angleDegrees an angle in degrees + /// @return the angle in radians + /// + static double toRadians(double angleDegrees); + + /// \brief + /// Returns the angle of the vector from p0 to p1, + /// relative to the positive X-axis. + /// + /// The angle is normalized to be in the range [ -Pi, Pi ]. + /// + /// @return the normalized angle (in radians) that p0-p1 makes + /// with the positive x-axis. + /// + static double angle(const geom::CoordinateXY& p0, + const geom::CoordinateXY& p1); + + /// \brief + /// Returns the angle that the vector from (0,0) to p, + /// relative to the positive X-axis. + // + /// The angle is normalized to be in the range ( -Pi, Pi ]. + /// + /// @return the normalized angle (in radians) that p makes + /// with the positive x-axis. + /// + static double angle(const geom::CoordinateXY& p); + + /// Tests whether the angle between p0-p1-p2 is acute. + /// + /// An angle is acute if it is less than 90 degrees. + /// + /// Note: this implementation is not precise (deterministic) for + /// angles very close to 90 degrees. + /// + /// @param p0 an endpoint of the angle + /// @param p1 the base of the angle + /// @param p2 the other endpoint of the angle + /// + static bool isAcute(const geom::CoordinateXY& p0, + const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + /// Tests whether the angle between p0-p1-p2 is obtuse. + /// + /// An angle is obtuse if it is greater than 90 degrees. + /// + /// Note: this implementation is not precise (deterministic) for + /// angles very close to 90 degrees. + /// + /// @param p0 an endpoint of the angle + /// @param p1 the base of the angle + /// @param p2 the other endpoint of the angle + /// + static bool isObtuse(const geom::CoordinateXY& p0, + const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + /// Returns the unoriented smallest angle between two vectors. + /// + /// The computed angle will be in the range [0, Pi). + /// + /// @param tip1 the tip of one vector + /// @param tail the tail of each vector + /// @param tip2 the tip of the other vector + /// @return the angle between tail-tip1 and tail-tip2 + /// + static double angleBetween(const geom::CoordinateXY& tip1, + const geom::CoordinateXY& tail, + const geom::CoordinateXY& tip2); + + /// Returns the oriented smallest angle between two vectors. + /// + /// The computed angle will be in the range (-Pi, Pi]. + /// A positive result corresponds to a counterclockwise rotation + /// from v1 to v2; + /// a negative result corresponds to a clockwise rotation. + /// + /// @param tip1 the tip of v1 + /// @param tail the tail of each vector + /// @param tip2 the tip of v2 + /// @return the angle between v1 and v2, relative to v1 + /// + static double angleBetweenOriented(const geom::CoordinateXY& tip1, + const geom::CoordinateXY& tail, + const geom::CoordinateXY& tip2); + + /// Computes the angle of the unoriented bisector + /// of the smallest angle between two vectors. + /// The computed angle will be in the range (-Pi, Pi]. + /// Collinear inputs are handled. + /// + /// @param tip1 the tip of v1 + /// @param tail the tail of each vector + /// @param tip2 the tip of v2 + /// @return the angle of the bisector between v1 and v2 + /// + static double bisector(const geom::CoordinateXY& tip1, + const geom::CoordinateXY& tail, + const geom::CoordinateXY& tip2); + + /// Computes the interior angle between two segments of a ring. + /// + /// The ring is assumed to be oriented in a clockwise direction. + /// The computed angle will be in the range [0, 2Pi] + /// + /// @param p0 + /// a point of the ring + /// @param p1 + /// the next point of the ring + /// @param p2 + /// the next point of the ring + /// @return the interior angle based at p1 + /// + static double interiorAngle(const geom::CoordinateXY& p0, + const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + /// \brief + /// Returns whether an angle must turn clockwise or counterclockwise + /// to overlap another angle. + /// + /// @param ang1 an angle (in radians) + /// @param ang2 an angle (in radians) + /// @return whether a1 must turn CLOCKWISE, COUNTERCLOCKWISE or + /// NONE to overlap a2. + /// + static int getTurn(double ang1, double ang2); + + /// \brief + /// Computes the normalized value of an angle, which is the + /// equivalent angle in the range ( -Pi, Pi ]. + /// + /// @param angle the angle to normalize + /// @return an equivalent angle in the range (-Pi, Pi] + /// + static double normalize(double angle); + + /// \brief + /// Computes the normalized positive value of an angle, + /// which is the equivalent angle in the range [ 0, 2*Pi ). + /// + /// E.g.: + /// - normalizePositive(0.0) = 0.0 + /// - normalizePositive(-PI) = PI + /// - normalizePositive(-2PI) = 0.0 + /// - normalizePositive(-3PI) = PI + /// - normalizePositive(-4PI) = 0 + /// - normalizePositive(PI) = PI + /// - normalizePositive(2PI) = 0.0 + /// - normalizePositive(3PI) = PI + /// - normalizePositive(4PI) = 0.0 + /// + /// @param angle the angle to normalize, in radians + /// @return an equivalent positive angle + /// + static double normalizePositive(double angle); + + + /// Computes the unoriented smallest difference between two angles. + /// + /// The angles are assumed to be normalized to the range [-Pi, Pi]. + /// The result will be in the range [0, Pi]. + /// + /// @param ang1 the angle of one vector (in [-Pi, Pi] ) + /// @param ang2 the angle of the other vector (in range [-Pi, Pi] ) + /// @return the angle (in radians) between the two vectors + /// (in range [0, Pi] ) + /// + static double diff(double ang1, double ang2); + + /// \brief + /// Computes both sin and cos of an angle, snapping near-zero values + /// to zero. + /// + /// The angle does not need to be normalized. Unlike std::sin + /// and std::cos, this method will snap near-zero values to zero + /// for (e.g.) sin(pi) and cos(pi/2). + /// + /// @param ang the input angle (in radians) + /// @param rSin the result of sin(ang) + /// @param rCos the result of cos(ang) + /// + static inline void sinCosSnap(const double ang, double& rSin, double& rCos) { + rSin = std::sin(ang); + rCos = std::cos(ang); + // snap near-zero values + if (std::fabs(rSin) < 5e-16) rSin = 0.0; + if (std::fabs(rCos) < 5e-16) rCos = 0.0; + } + + /// Projects a point by a given angle and distance. + /// + /// @param p the point to project + /// @param angle the angle at which to project + /// @param dist the distance to project + /// @return the projected point + /// + static geom::CoordinateXY project(const geom::CoordinateXY& p, + double angle, double dist); + +}; + + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/Area.h b/Sources/geos/include/geos/algorithm/Area.h new file mode 100644 index 0000000..51f188e --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Area.h @@ -0,0 +1,90 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Area.java @ 2017-09-04 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { + +namespace geom { +class Curve; +} + +namespace algorithm { // geos::algorithm + + +class GEOS_DLL Area { +public: + + static double ofClosedCurve(const geom::Curve& ring); + + /** + * Computes the area for a ring. + * + * @param ring the coordinates forming the ring + * @return the area of the ring + */ + static double ofRing(const std::vector& ring); + + /** + * Computes the area for a ring. + * + * @param ring the coordinates forming the ring + * @return the area of the ring + */ + static double ofRing(const std::vector& ring); + + /** + * Computes the area for a ring. + * + * @param ring the coordinates forming the ring + * @return the area of the ring + */ + static double ofRing(const geom::CoordinateSequence* ring); + + /** + * Computes the signed area for a ring. The signed area is positive if the + * ring is oriented CW, negative if the ring is oriented CCW, and zero if the + * ring is degenerate or flat. + * + * @param ring + * the coordinates forming the ring + * @return the signed area of the ring + */ + static double ofRingSigned(const std::vector& ring); + + /** + * Computes the signed area for a ring. The signed area is positive if the + * ring is oriented CW, negative if the ring is oriented CCW, and zero if the + * ring is degenerate or flat. + * + * @param ring + * the coordinates forming the ring + * @return the signed area of the ring + */ + static double ofRingSigned(const geom::CoordinateSequence* ring); + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/BoundaryNodeRule.h b/Sources/geos/include/geos/algorithm/BoundaryNodeRule.h new file mode 100644 index 0000000..19cd6e1 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/BoundaryNodeRule.h @@ -0,0 +1,146 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/BoundaryNodeRule.java rev 1.4 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +// Forward declarations +// ... + +namespace geos { +namespace algorithm { // geos::algorithm + + +/** \brief + * An interface for rules which determine whether node points which are + * in boundaries of lineal geometry components + * are in the boundary of the parent geometry collection. + * + * The SFS specifies a single kind of boundary node rule, + * the `Mod2BoundaryNodeRule` rule. + * However, other kinds of Boundary Node Rules are appropriate + * in specific situations (for instance, linear network topology + * usually follows the `EndPointBoundaryNodeRule`.) + * Some JTS operations allow the BoundaryNodeRule to be specified, + * and respect this rule when computing the results of the operation. + * + * @author Martin Davis + * @version 1.7 + * + * @see operation::relate::RelateOp + * @see operation::valid::IsSimpleOp + * @see algorithm::PointLocator + */ +class GEOS_DLL BoundaryNodeRule { + +public: + + // virtual classes should always have a virtual destructor.. + virtual + ~BoundaryNodeRule() {} + + virtual std::string toString() const = 0; + + /** \brief + * Tests whether a point that lies in `boundaryCount` + * geometry component boundaries is considered to form part of + * the boundary of the parent geometry. + * + * @param boundaryCount the number of component boundaries that + * this point occurs in + * @return `true` if points in this number of boundaries lie in + * the parent boundary + */ + virtual bool isInBoundary(int boundaryCount) const = 0; + + /** \brief + * The Mod-2 Boundary Node Rule (which is the rule specified + * in the OGC SFS). + * + * A BoundaryNodeRule specifies that points are in the boundary of + * a lineal geometry iff the point lies on the boundary of an odd number + * of components. Under this rule LinearRings and closed LineStrings have + * an empty boundary. + * + * This is the rule specified by the OGC SFS, and is the default + * rule used in JTS. + */ + //static const BoundaryNodeRule& MOD2_BOUNDARY_RULE; + static const BoundaryNodeRule& getBoundaryRuleMod2(); + + /** \brief + * The Endpoint Boundary Node Rule. + * + * A BoundaryNodeRule which specifies that any points which are endpoints + * of lineal components are in the boundary of the parent geometry. This + * corresponds to the "intuitive" topological definition of boundary. Under + * this rule LinearRings have a non-empty boundary (the common endpoint + * of the underlying LineString). + * + * This rule is useful when dealing with linear networks. For example, + * it can be used to check whether linear networks are correctly noded. + * The usual network topology constraint is that linear segments may + * touch only at endpoints. In the case of a segment touching a closed + * segment (ring) at one point, the Mod2 rule cannot distinguish between + * the permitted case of touching at the node point and the invalid case + * of touching at some other interior (non-node) point. The EndPoint rule + * does distinguish between these cases, so is more appropriate for use. + */ + //static const BoundaryNodeRule& ENDPOINT_BOUNDARY_RULE; + static const BoundaryNodeRule& getBoundaryEndPoint(); + + /** \brief + * The MultiValent Endpoint Boundary Node Rule. + * + * A BoundaryNodeRule which determines that only endpoints with valency + * greater than 1 are on the boundary. This corresponds to the boundary + * of a MultiLineString being all the "attached" endpoints, but not + * the "unattached" ones. + */ + //static const BoundaryNodeRule& MULTIVALENT_ENDPOINT_BOUNDARY_RULE; + static const BoundaryNodeRule& getBoundaryMultivalentEndPoint(); + + /** \brief + * The Monovalent Endpoint Boundary Node Rule. + * + * A BoundaryNodeRule which determines that only endpoints with valency of + * exactly 1 are on the boundary. This corresponds to the boundary of + * a MultiLineString being all the "unattached" endpoints. + */ + //static const BoundaryNodeRule& MONOVALENT_ENDPOINT_BOUNDARY_RULE; + static const BoundaryNodeRule& getBoundaryMonovalentEndPoint(); + + /** \brief + * The Boundary Node Rule specified by the OGC Simple Features + * Specification, which is the same as the Mod-2 rule. + * + * A BoundaryNodeRule which determines that only endpoints with valency + * greater than 1 are on the boundary. This corresponds to the boundary + * of a MultiLineString being all the "attached" endpoints, but not the + * "unattached" ones. + */ + //static const BoundaryNodeRule& OGC_SFS_BOUNDARY_RULE; + static const BoundaryNodeRule& getBoundaryOGCSFS(); +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/CGAlgorithmsDD.h b/Sources/geos/include/geos/algorithm/CGAlgorithmsDD.h new file mode 100644 index 0000000..fe098fd --- /dev/null +++ b/Sources/geos/include/geos/algorithm/CGAlgorithmsDD.h @@ -0,0 +1,170 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2014 Mateusz Loskot + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/CGAlgorithmsDD.java r789 (JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +class CoordinateSequence; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/// Implements basic computational geometry algorithms using extended precision float-point arithmetic. +class GEOS_DLL CGAlgorithmsDD { + using DD = geos::math::DD; + +public: + + enum { + CLOCKWISE = -1, + COLLINEAR = 0, + COUNTERCLOCKWISE = 1 + }; + + enum { + RIGHT = -1, + LEFT = 1, + STRAIGHT = 0, + FAILURE = 2 + }; + + /** \brief + * Returns the index of the direction of the point `q` relative to + * a vector specified by `p1-p2`. + * + * @param p1 the origin point of the vector + * @param p2 the final point of the vector + * @param q the point to compute the direction to + * + * @return 1 if q is counter-clockwise (left) from p1-p2 + * @return -1 if q is clockwise (right) from p1-p2 + * @return 0 if q is collinear with p1-p2 + */ + static int orientationIndex(const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2, + const geom::CoordinateXY& q); + + + static int orientationIndex(double p1x, double p1y, + double p2x, double p2y, + double qx, double qy); + + /** + * A filter for computing the orientation index of three coordinates. + * + * If the orientation can be computed safely using standard DP arithmetic, + * this routine returns the orientation index. + * + * The filter is fast to compute, so can be used to avoid the use of slower + * robust methods except when they are really needed, thus providing better + * average performance. + * + * Jonathan Shewchuk + * Robust Adaptive Floating-Point Geometric Predicates + * Proceedings of the Twelfth Annual Symposium on Computational Geometry, ACM, + * May 1996 + * + * Ozaki, K., Bünger, F., Ogita, T. et al. + * Simple floating-point filters for the two-dimensional orientation problem. + * BitNumer Math 56, 729–749 (2016). + * https://doi.org/10.1007/s10543-015-0574-9 + * + */ + static inline int orientationIndexFilter( + double pax, double pay, + double pbx, double pby, + double pcx, double pcy) + { + double const detleft = (pax - pcx) * (pby - pcy); + double const detright = (pay - pcy) * (pbx - pcx); + double const det = detleft - detright; + // Coefficient as per Ozaki et al + double const error = std::abs(detleft + detright) + * 3.3306690621773724e-16; + if (std::abs(det) >= error) + return (det > 0) - (det < 0); + return CGAlgorithmsDD::FAILURE; + }; + + static int + orientation(double x) + { + if(x < 0) { + return CGAlgorithmsDD::RIGHT; + } + if(x > 0) { + return CGAlgorithmsDD::LEFT; + } + return CGAlgorithmsDD::STRAIGHT; + }; + + /** + * If the lines are parallel (either identical + * or separate) a null value is returned. + * @param p1 an endpoint of line segment 1 + * @param p2 an endpoint of line segment 1 + * @param q1 an endpoint of line segment 2 + * @param q2 an endpoint of line segment 2 + * @return an intersection point if one exists, or null if the lines are parallel + */ + static geom::CoordinateXY intersection(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, + const geom::CoordinateXY& q1, const geom::CoordinateXY& q2); + + static int signOfDet2x2(double dx1, double dy1, double dx2, double dy2); + + static DD detDD(double x1, double y1, double x2, double y2); + static DD detDD(const DD& x1, const DD& y1, const DD& x2, const DD& y2); + + /** \brief + * Computes the circumcentre of a triangle. + * + * The circumcentre is the centre of the circumcircle, the smallest circle + * which encloses the triangle. It is also the common intersection point of + * the perpendicular bisectors of the sides of the triangle, and is the only + * point which has equal distance to all three vertices of the triangle. + * + * The circumcentre does not necessarily lie within the triangle. For example, + * the circumcentre of an obtuse isosceles triangle lies outside the triangle. + * + * This method uses @ref geos::math::DD extended-precision arithmetic to provide more accurate + * results than geos::geom::Triangle::circumcentre. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return the circumcentre of the triangle + */ + static geom::CoordinateXY circumcentreDD(const geom::CoordinateXY& a, const geom::CoordinateXY& b, const geom::CoordinateXY& c); + +protected: + + static int signOfDet2x2(const DD& x1, const DD& y1, const DD& x2, const DD& y2); + +}; + +} // namespace geos::algorithm +} // namespace geos diff --git a/Sources/geos/include/geos/algorithm/CentralEndpointIntersector.h b/Sources/geos/include/geos/algorithm/CentralEndpointIntersector.h new file mode 100644 index 0000000..ea4e6b1 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/CentralEndpointIntersector.h @@ -0,0 +1,161 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/CentralEndpointIntersector.java rev. 1.1 + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +//class PrecisionModel; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes an approximate intersection of two line segments + * by taking the most central of the endpoints of the segments. + * + * This is effective in cases where the segments are nearly parallel + * and should intersect at an endpoint. + * It is also a reasonable strategy for cases where the + * endpoint of one segment lies on or almost on the interior of another one. + * Taking the most central endpoint ensures that the computed intersection + * point lies in the envelope of the segments. + * Also, by always returning one of the input points, this should result + * in reducing segment fragmentation. + * Intended to be used as a last resort for + * computing ill-conditioned intersection situations which + * cause other methods to fail. + * + * @author Martin Davis + * @version 1.8 + */ +class GEOS_DLL CentralEndpointIntersector { + +public: + + static const geom::Coordinate& + getIntersection(const geom::Coordinate& p00, + const geom::Coordinate& p01, const geom::Coordinate& p10, + const geom::Coordinate& p11) + { + CentralEndpointIntersector intor(p00, p01, p10, p11); + return intor.getIntersection(); + } + + CentralEndpointIntersector(const geom::Coordinate& p00, + const geom::Coordinate& p01, + const geom::Coordinate& p10, + const geom::Coordinate& p11) + : + _pts(4) + { + _pts[0] = p00; + _pts[1] = p01; + _pts[2] = p10; + _pts[3] = p11; + compute(); + } + + const geom::Coordinate& + getIntersection() const + { + return _intPt; + } + + +private: + + // This is likely overkill.. we'll be allocating heap + // memory at every call ! + std::vector _pts; + + geom::Coordinate _intPt; + + void + compute() + { + geom::Coordinate centroid = average(_pts); + _intPt = findNearestPoint(centroid, _pts); + } + + static geom::Coordinate + average( + const std::vector& pts) + { + geom::Coordinate avg(0, 0); + std::size_t n = pts.size(); + if(! n) { + return avg; + } + for(std::size_t i = 0; i < n; ++i) { + avg.x += pts[i].x; + avg.y += pts[i].y; + } + avg.x /= n; + avg.y /= n; + return avg; + } + + /** + * Determines a point closest to the given point. + * + * @param p the point to compare against + * @param p1 a potential result point + * @param p2 a potential result point + * @param q1 a potential result point + * @param q2 a potential result point + * @return the point closest to the input point p + */ + geom::Coordinate + findNearestPoint(const geom::Coordinate& p, + const std::vector& pts) const + { + double minDistSq = DoubleInfinity; + geom::Coordinate result = geom::Coordinate::getNull(); + for(std::size_t i = 0, n = pts.size(); i < n; ++i) { + double distSq = p.distanceSquared(pts[i]); + if(distSq < minDistSq) { + minDistSq = distSq; + result = pts[i]; + } + } + return result; + } +}; + +} // namespace geos::algorithm +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/algorithm/Centroid.h b/Sources/geos/include/geos/algorithm/Centroid.h new file mode 100644 index 0000000..5cef5bd --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Centroid.h @@ -0,0 +1,157 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2013 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Centroid.java r728 (JTS-0.13+) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include // for std::unique_ptr + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Polygon; +class CoordinateSequence; +} +} + + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes the centroid of a [Geometry](@ref geom::Geometry) of any dimension. + * + * If the geometry is nomimally of higher dimension, but contains only + * components having a lower effective dimension (i.e. zero length or area), + * the centroid will be computed appropriately. + * + * ### Algorithm # + * + * - **Dimension = 2** - Based on the usual algorithm for calculating + * the centroid as a weighted sum of the centroids + * of a decomposition of the area into (possibly overlapping) triangles. + * The algorithm has been extended to handle holes and multi-polygons. + * See http://www.faqs.org/faqs/graphics/algorithms-faq/ + * for further details of the basic approach. + * - **Dimension = 1** - Computes the average of the midpoints + * of all line segments weighted by the segment length. + * - **Dimension = 0** - Compute the average coordinate over all points. + * + * If the input geometries are empty, a `null` Coordinate is returned. + * + */ +class GEOS_DLL Centroid { + +public: + + /** \brief + * Computes the centroid point of a geometry. + * + * @param geom the geometry to use + * @param cent will be set to the centroid point, if any + * + * @return `true` if a centroid could be computed, + * `false` otherwise (empty geom) + */ + static bool getCentroid(const geom::Geometry& geom, geom::CoordinateXY& cent); + + /** \brief + * Creates a new instance for computing the centroid of a geometry + */ + Centroid(const geom::Geometry& geom) + : + areasum2(0.0), + totalLength(0.0), + ptCount(0) + { + add(geom); + } + + /** \brief + * Gets the computed centroid. + * + * @param cent will be set to the centroid point, if any + * + * @return `true` if a centroid could be computed, + * `false` otherwise (empty geom) + */ + bool getCentroid(geom::CoordinateXY& cent) const; + +private: + + std::unique_ptr areaBasePt; + geom::CoordinateXY triangleCent3; + geom::CoordinateXY cg3; + geom::CoordinateXY lineCentSum; + geom::CoordinateXY ptCentSum; + double areasum2; + double totalLength; + int ptCount; + + /** + * Adds a Geometry to the centroid total. + * + * @param geom the geometry to add + */ + void add(const geom::Geometry& geom); + + void setAreaBasePoint(const geom::CoordinateXY& basePt); + + void add(const geom::Polygon& poly); + + void addShell(const geom::CoordinateSequence& pts); + + void addHole(const geom::CoordinateSequence& pts); + + void addTriangle(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, + bool isPositiveArea); + + /** + * Computes three times the centroid of the triangle p1-p2-p3. + * The factor of 3 is + * left in to permit division to be avoided until later. + */ + static void centroid3(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, const geom::CoordinateXY& p3, + geom::CoordinateXY& c); + + /** + * Returns twice the signed area of the triangle p1-p2-p3. + * The area is positive if the triangle is oriented CCW, and negative if CW. + */ + static double area2(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, const geom::CoordinateXY& p3); + + /** + * Adds the line segments defined by an array of coordinates + * to the linear centroid accumulators. + * + * @param pts an array of {@link Coordinate}s + */ + void addLineSegments(const geom::CoordinateSequence& pts); + + /** + * Adds a point to the point centroid accumulator. + * @param pt a {@link Coordinate} + */ + void addPoint(const geom::CoordinateXY& pt); +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/CircularArcs.h b/Sources/geos/include/geos/algorithm/CircularArcs.h new file mode 100644 index 0000000..54f0a9b --- /dev/null +++ b/Sources/geos/include/geos/algorithm/CircularArcs.h @@ -0,0 +1,37 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { + +class GEOS_DLL CircularArcs { +public: + + /// Return the circle center of an arc defined by three points + static geom::CoordinateXY getCenter(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + /// Expand an envelope to include an arc defined by three points + static void expandEnvelope(geom::Envelope& e, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); +}; + +} +} diff --git a/Sources/geos/include/geos/algorithm/ConvexHull.h b/Sources/geos/include/geos/algorithm/ConvexHull.h new file mode 100644 index 0000000..e8eba46 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/ConvexHull.h @@ -0,0 +1,211 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/ConvexHull.java r407 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// FIXME: avoid using Coordinate:: typedefs to avoid full include +#include +#include +#include +#include +#include +#include + +#include "geos/util.h" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes the convex hull of a Geometry. + * + * The convex hull is the smallest convex Geometry that contains all the + * points in the input Geometry. + * + * Uses the Graham Scan algorithm. + * + * Last port: algorithm/ConvexHull.java rev. 1.26 (JTS-1.7) + * + */ +class GEOS_DLL ConvexHull { +private: + + static constexpr std::size_t TUNING_REDUCE_SIZE = 50; + + const geom::Geometry* inputGeom; + const geom::GeometryFactory* geomFactory; + geom::Coordinate::ConstVect inputPts; + + /// Create a CoordinateSequence from the Coordinate::ConstVect + /// This is needed to construct the geometries. + /// Here coordinate copies happen + /// The returned object is newly allocated !NO EXCEPTION SAFE! + std::unique_ptr toCoordinateSequence(geom::Coordinate::ConstVect& cv); + + void computeInnerOctolateralPts( + const geom::Coordinate::ConstVect& src, + geom::Coordinate::ConstVect& tgt); + + bool computeInnerOctolateralRing( + const geom::Coordinate::ConstVect& src, + geom::Coordinate::ConstVect& tgt); + + /** + * Uses a heuristic to reduce the number of points scanned + * to compute the hull. + * The heuristic is to find a polygon guaranteed to + * be in (or on) the hull, and eliminate all points inside it. + * A quadrilateral defined by the extremal points + * in the four orthogonal directions + * can be used, but even more inclusive is + * to use an octilateral defined by the points in the + * 8 cardinal directions. + * + * Note that even if the method used to determine the polygon + * vertices is not 100% robust, this does not affect the + * robustness of the convex hull. + * + * To satisfy the requirements of the Graham Scan algorithm, + * the resulting array has at least 3 entries. + * + * @param pts The vector of const Coordinate pointers + * to be reduced (to at least 3 elements) + * + * WARNING: the parameter will be modified + * + */ + void reduce(geom::Coordinate::ConstVect& pts); + + /// parameter will be modified + void padArray3(geom::Coordinate::ConstVect& pts); + + /// parameter will be modified + void preSort(geom::Coordinate::ConstVect& pts); + + /** + * Given two points p and q compare them with respect to their radial + * ordering about point o. First checks radial ordering. + * If points are collinear, the comparison is based + * on their distance to the origin. + * + * p < q iff + * + * - ang(o-p) < ang(o-q) (e.g. o-p-q is CCW) + * - or ang(o-p) == ang(o-q) && dist(o,p) < dist(o,q) + * + * @param o the origin + * @param p a point + * @param q another point + * @return -1, 0 or 1 depending on whether p is less than, + * equal to or greater than q + */ + int polarCompare(const geom::Coordinate& o, + const geom::Coordinate& p, + const geom::Coordinate& q); + + void grahamScan(const geom::Coordinate::ConstVect& c, + geom::Coordinate::ConstVect& ps); + + /** + * @param vertices the vertices of a linear ring, + * which may or may not be + * flattened (i.e. vertices collinear) + * + * @return a 2-vertex LineString if the vertices are + * collinear; otherwise, a Polygon with unnecessary + * (collinear) vertices removed + */ + std::unique_ptr lineOrPolygon(const geom::Coordinate::ConstVect& vertices); + + /** + * Write in 'cleaned' a version of 'input' with collinear + * vertices removed. + */ + void cleanRing(const geom::Coordinate::ConstVect& input, + geom::Coordinate::ConstVect& cleaned); + + /** + * @return whether the three coordinates are collinear + * and c2 lies between c1 and c3 inclusive + */ + bool isBetween( + const geom::Coordinate& c1, + const geom::Coordinate& c2, + const geom::Coordinate& c3); + + bool extractUnique(geom::Coordinate::ConstVect& pts, std::size_t maxPts); + std::unique_ptr createFewPointsResult(); + + + +public: + + /** + * Create a new convex hull construction for the input Geometry. + */ + ConvexHull(const geom::Geometry* newGeometry) + : inputGeom(newGeometry) + , geomFactory(newGeometry->getFactory()) + { + util::ensureNoCurvedComponents(inputGeom); + }; + + ~ConvexHull() {}; + + /** + * Returns a Geometry that represents the convex hull of + * the input geometry. + * The returned geometry contains the minimal number of points + * needed to represent the convex hull. + * In particular, no more than two consecutive points + * will be collinear. + * + * @return if the convex hull contains 3 or more points, + * a Polygon; 2 points, a LineString; + * 1 point, a Point; 0 points, an empty GeometryCollection. + */ + std::unique_ptr getConvexHull(); +}; + +} // namespace geos::algorithm +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + diff --git a/Sources/geos/include/geos/algorithm/Distance.h b/Sources/geos/include/geos/algorithm/Distance.h new file mode 100644 index 0000000..5d82384 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Distance.h @@ -0,0 +1,116 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Distance.java @ 2017-09-04 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Functions to compute distance between basic geometric structures. + * + * @author Martin Davis + * + */ +class GEOS_DLL Distance { +public: + + /** + * Computes the distance from a line segment AB to a line segment CD + * + * Note: NON-ROBUST! + * + * @param A + * a point of one line + * @param B + * the second point of (must be different to A) + * @param C + * one point of the line + * @param D + * another point of the line (must be different to A) + */ + // formerly distanceLineLine + static double segmentToSegment( + const geom::CoordinateXY& A, + const geom::CoordinateXY& B, + const geom::CoordinateXY& C, + const geom::CoordinateXY& D); + + /** + * Computes the distance from a point to a sequence of line segments. + * + * @param p + * a point + * @param seq + * a sequence of contiguous line segments defined by their vertices + * @return the minimum distance between the point and the line segments + */ + static double pointToSegmentString( + const geom::CoordinateXY& p, + const geom::CoordinateSequence* seq); + + /** + * Computes the distance from a point p to a line segment AB + * + * Note: NON-ROBUST! + * + * @param p + * the point to compute the distance for + * @param A + * one point of the line + * @param B + * another point of the line (must be different to A) + * @return the distance from p to line segment AB + */ + // formerly distancePointLine + static double pointToSegment( + const geom::CoordinateXY& p, + const geom::CoordinateXY& A, + const geom::CoordinateXY& B); + + /** + * Computes the perpendicular distance from a point p to the (infinite) line + * containing the points AB + * + * @param p + * the point to compute the distance for + * @param A + * one point of the line + * @param B + * another point of the line (must be different to A) + * @return the distance from p to line AB + */ + // formerly distancePointLinePerpendicular + static double pointToLinePerpendicular( + const geom::CoordinateXY& p, + const geom::CoordinateXY& A, + const geom::CoordinateXY& B); + + static double pointToLinePerpendicularSigned( + const geom::CoordinateXY& p, + const geom::CoordinateXY& A, + const geom::CoordinateXY& B); +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/HCoordinate.h b/Sources/geos/include/geos/algorithm/HCoordinate.h new file mode 100644 index 0000000..294981c --- /dev/null +++ b/Sources/geos/include/geos/algorithm/HCoordinate.h @@ -0,0 +1,100 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/HCoordinate.java r386 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + + +/** \brief + * Represents a homogeneous coordinate in a 2-D coordinate space. + * + * HCoordinate are used as a clean way + * of computing intersections between line segments. + */ +class GEOS_DLL HCoordinate { + +public: + + friend std::ostream& operator<< (std::ostream& o, const HCoordinate& c); + + /** \brief + * Computes the (approximate) intersection point between two line + * segments using homogeneous coordinates. + * + * @note this algorithm is not numerically stable; i.e. it can + * produce intersection points which lie outside the envelope of the + * line segments themselves. In order to increase the precision of + * the calculation input points should be normalized before + * passing them to this routine. + */ + static void intersection(const geom::Coordinate& p1, + const geom::Coordinate& p2, + const geom::Coordinate& q1, + const geom::Coordinate& q2, + geom::Coordinate& ret); + + double x, y, w; + + HCoordinate(); + + HCoordinate(double _x, double _y, double _w); + + HCoordinate(const geom::Coordinate& p); + + /** \brief + * Constructs a homogeneous coordinate which is the intersection + * of the lines define by the homogeneous coordinates represented + * by two [Coordinates](@ref geom::Coordinate). + * + * @param p1 + * @param p2 + */ + HCoordinate(const geom::Coordinate& p1, const geom::Coordinate& p2); + + HCoordinate(const geom::Coordinate& p1, const geom::Coordinate& p2, + const geom::Coordinate& q1, const geom::Coordinate& q2); + + HCoordinate(const HCoordinate& p1, const HCoordinate& p2); + + double getX() const; + + double getY() const; + + void getCoordinate(geom::Coordinate& ret) const; + +}; + +std::ostream& operator<< (std::ostream& o, const HCoordinate& c); + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/InteriorPointArea.h b/Sources/geos/include/geos/algorithm/InteriorPointArea.h new file mode 100644 index 0000000..88efe0e --- /dev/null +++ b/Sources/geos/include/geos/algorithm/InteriorPointArea.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/InteriorPointArea.java (JTS-1.17+) + * https://github.com/locationtech/jts/commit/a140ca30cc51be4f65c950a30b0a8f51a6df75ba + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Polygon; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes a point in the interior of an areal geometry. + * The point will lie in the geometry interior + * in all except certain pathological cases. + * + *

Algorithm

+ * For each input polygon: + *
    + *
  • Determine a horizontal scan line on which the interior + * point will be located. + * To increase the chance of the scan line + * having non-zero-width intersection with the polygon + * the scan line Y ordinate is chosen to be near the centre of the polygon's + * Y extent but distinct from all of vertex Y ordinates. + *
  • Compute the sections of the scan line + * which lie in the interior of the polygon. + *
  • Choose the widest interior section + * and take its midpoint as the interior point. + *
+ * The final interior point is chosen as + * the one occurring in the widest interior section. + *

+ * This algorithm is a tradeoff between performance + * and point quality (where points further from the geometry + * boundary are considered to be higher quality) + * Priority is given to performance. + * This means that the computed interior point + * may not be suitable for some uses + * (such as label positioning). + *

+ * The algorithm handles some kinds of invalid/degenerate geometry, + * including zero-area and self-intersecting polygons. + *

+ * Empty geometry is handled by returning a null point. + * + *

KNOWN BUGS

+ *
    + *
  • If a fixed precision model is used, in some cases this method may return + * a point which does not lie in the interior. + *
  • If the input polygon is extremely narrow the computed point + * may not lie in the interior of the polygon. + *
+ */ +class GEOS_DLL InteriorPointArea { + +public: + /** + * Creates a new interior point finder + * for an areal geometry. + * + * @param g an areal geometry + */ + InteriorPointArea(const geom::Geometry* g); + + /** + * Gets the computed interior point. + * + * @return the coordinate of an interior point + * or null if the input geometry is empty + */ + bool getInteriorPoint(geom::Coordinate& ret) const; + +private: + geom::Coordinate interiorPoint; + double maxWidth; + + void process(const geom::Geometry* geom); + + void processPolygon(const geom::Polygon* polygon); + +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/InteriorPointLine.h b/Sources/geos/include/geos/algorithm/InteriorPointLine.h new file mode 100644 index 0000000..ce04161 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/InteriorPointLine.h @@ -0,0 +1,79 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/InteriorPointLine.java r317 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class CoordinateSequence; +} +} + + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes a point in the interior of an linear geometry. + * + *

Algorithm

+ * + * - Find an interior vertex which is closest to + * the centroid of the linestring. + * - If there is no interior vertex, find the endpoint which is + * closest to the centroid. + */ +class GEOS_DLL InteriorPointLine { +public: + + InteriorPointLine(const geom::Geometry* g); + //Coordinate* getInteriorPoint() const; + + bool getInteriorPoint(geom::Coordinate& ret) const; + +private: + + bool hasInterior; + + geom::Coordinate centroid; + + double minDistance; + + geom::Coordinate interiorPoint; + + void addInterior(const geom::Geometry* geom); + + void addInterior(const geom::CoordinateSequence* pts); + + void addEndpoints(const geom::Geometry* geom); + + void addEndpoints(const geom::CoordinateSequence* pts); + + void add(const geom::Coordinate& point); + +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/InteriorPointPoint.h b/Sources/geos/include/geos/algorithm/InteriorPointPoint.h new file mode 100644 index 0000000..08fe688 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/InteriorPointPoint.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** + * \class InteriorPointPoint + * \brief + * Computes a point in the interior of an point geometry. + * + * Algorithm: + * + * Find a point which is closest to the centroid of the geometry. + */ +class GEOS_DLL InteriorPointPoint { +private: + + bool hasInterior; + + geom::CoordinateXY centroid; + + double minDistance; + + geom::Coordinate interiorPoint; + + /** + * Tests the point(s) defined by a Geometry for the best inside point. + * If a Geometry is not of dimension 0 it is not tested. + * @param geom the geometry to add + */ + void add(const geom::Geometry* geom); + + void add(const geom::CoordinateXY* point); + +public: + + InteriorPointPoint(const geom::Geometry* g); + + ~InteriorPointPoint() {} + + bool getInteriorPoint(geom::CoordinateXY& ret) const; + +}; + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/Interpolate.h b/Sources/geos/include/geos/algorithm/Interpolate.h new file mode 100644 index 0000000..1768c89 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Interpolate.h @@ -0,0 +1,182 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Vivid Solutions Inc. + * Copyright (C) 2023 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace algorithm { + +class GEOS_DLL Interpolate { + +private: + + template + static double + interpolate(const geom::CoordinateXY& p, const CoordType& p1, const CoordType& p2) + { + double p1z = p1.template get(); + double p2z = p2.template get(); + + if (std::isnan(p1z)) { + return p2z; // may be NaN + } + if (std::isnan(p2z)) { + return p1z; // may be NaN + } + if (p.equals2D(p1)) { + return p1z; // not NaN + } + if (p.equals2D(p2)) { + return p2z; // not NaN + } + double dz = p2z - p1z; + if (dz == 0.0) { + return p1z; + } + + // interpolate Z from distance of p along p1-p2 + double dx = (p2.x - p1.x); + double dy = (p2.y - p1.y); + // seg has non-zero length since p1 < p < p2 + double seglen = (dx * dx + dy * dy); + double xoff = (p.x - p1.x); + double yoff = (p.y - p1.y); + double plen = (xoff * xoff + yoff * yoff); + double frac = std::sqrt(plen / seglen); + double zoff = dz * frac; + double zInterpolated = p1z + zoff; + + return zInterpolated; + } + + template + static double + interpolate(const geom::CoordinateXY& p, const C1& p1, const C1& p2, const C2& q1, const C2& q2) + { + double zp = interpolate(p, p1, p2); + double zq = interpolate(p, q1, q2); + + if (std::isnan(zp)) { + return zq; // may be NaN + } + if (std::isnan(zq)) { + return zp; // may be NaN + } + + return (zp + zq) / 2.0; + } + + template + static double + get(const C1& p, const C2& q) + { + double a = p.template get(); + double b = q.template get(); + if (std::isnan(a)) { + return b; + } + return a; + } + + template + static double + getOrInterpolate(const C1& p, const C2& p1, const C2& p2) + { + double z = p.template get(); + if (!std::isnan(z)) return z; + return interpolate(p, p1, p2); + } + + static double + interpolate(const geom::CoordinateXY& p, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2) + { + (void) p; (void) p1; (void) p2; + return DoubleNotANumber; + } + +public: + /// Interpolate a Z value for a coordinate from two other coordinates. + template + static double + zInterpolate(const geom::CoordinateXY& p, const CoordType& p1, const CoordType& p2) + { + return interpolate(p, p1, p2); + } + + /// Calculate an average interpolated Z value from two pairs of other coordinates. + template + static double + zInterpolate(const geom::CoordinateXY& p, const C1& p1, const C1& p2, const C2& q1, const C2& q2) + { + return interpolate(p, p1, p2, q1, q2); + } + + /// Interpolate an M value for a coordinate from two other coordinates. + template + static double + mInterpolate(const geom::CoordinateXY& p, const CoordType& p1, const CoordType& p2) + { + return interpolate(p, p1, p2); + } + + /// Calculate an average interpolated M value from two pairs of other coordinates. + template + static double + mInterpolate(const geom::CoordinateXY& p, const C1& p1, const C1& p2, const C2& q1, const C2& q2) + { + return interpolate(p, p1, p2, q1, q2); + } + + /// Return the first non-NaN Z value from two coordinates, or NaN if both values are NaN. + template + static double + zGet(const C1& p, const C2& q) + { + return get(p, q); + } + + /// Return the first non-NaN M value from two coordinates, or NaN if both values are NaN. + template + static double + mGet(const C1& p, const C2& q) + { + return get(p, q); + } + + /// Return a coordinates's non-NaN Z value or interpolate it from two other coordinates if it is NaN. + template + static double + zGetOrInterpolate(const C1& p, const C2& p1, const C2& p2) + { + return getOrInterpolate(p, p1, p2); + } + + /// Return a coordinates's non-NaN M value or interpolate it from two other coordinates if it is NaN. + template + static double + mGetOrInterpolate(const C1& p, const C2& p1, const C2& p2) + { + return getOrInterpolate(p, p1, p2); + } + +}; + +} +} diff --git a/Sources/geos/include/geos/algorithm/Intersection.h b/Sources/geos/include/geos/algorithm/Intersection.h new file mode 100644 index 0000000..de413d4 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Intersection.h @@ -0,0 +1,84 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { + + +/** \brief + * Functions to compute intersection points between lines and line segments. + * + * In general it is not possible to compute + * the intersection point of two lines exactly, due to numerical roundoff. + * This is particularly true when the lines are nearly parallel. + * These routines uses numerical conditioning on the input values + * to ensure that the computed value is very close to the correct value. + * + * The Z-ordinate is ignored, and not populated. + */ +class GEOS_DLL Intersection { + +public: + +/** \brief + * Computes the intersection point of two lines. + * If the lines are parallel or collinear this case is detected + * and null is returned. + * + * @param p1 an endpoint of line 1 + * @param p2 an endpoint of line 1 + * @param q1 an endpoint of line 2 + * @param q2 an endpoint of line 2 + * @return the intersection point between the lines, if there is one, + * or null if the lines are parallel or collinear + * + * @see CGAlgorithmsDD#intersection(Coordinate, Coordinate, Coordinate, Coordinate) + */ +static geom::CoordinateXY intersection(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, + const geom::CoordinateXY& q1, const geom::CoordinateXY& q2); + +/** +* Computes the intersection point of a line and a line segment (if any). +* There will be no intersection point if: +* +* * the segment does not intersect the line +* * the line or the segment are degenerate (have zero length) +* +* If the segment is collinear with the line the first segment endpoint is returned. +* +* @param line1 a point on the line +* @param line2 a point on the line +* @param seg1 an endpoint of the line segment +* @param seg2 an endpoint of the line segment +* @return the intersection point, or null if it is not possible to find an intersection +*/ +static geom::CoordinateXY intersectionLineSegment( + const geom::CoordinateXY& line1, + const geom::CoordinateXY& line2, + const geom::CoordinateXY& seg1, + const geom::CoordinateXY& seg2); + + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/Length.h b/Sources/geos/include/geos/algorithm/Length.h new file mode 100644 index 0000000..75c0ca1 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Length.h @@ -0,0 +1,60 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Length.java @ 2017-09-04 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Functions for computing length. + * + * @author Martin Davis + */ +class GEOS_DLL Length { +public: + + /** + * Computes the length of a linestring specified by a sequence of points. + * + * @param ring the points specifying the linestring + * @return the length of the linestring + */ + static double ofLine(const geom::CoordinateSequence* ring); + + /** + * Computes the length of a linestring specified by a sequence of points. + * + * @param ring the points specifying the linestring + * @return the length of the linestring + */ + static double ofLine(const std::vector& ring); + +}; + + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/LineIntersector.h b/Sources/geos/include/geos/algorithm/LineIntersector.h new file mode 100644 index 0000000..ebf676b --- /dev/null +++ b/Sources/geos/include/geos/algorithm/LineIntersector.h @@ -0,0 +1,667 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/RobustLineIntersector.java r785 (JTS-1.13+) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * A LineIntersector is an algorithm that can both test whether + * two line segments intersect and compute the intersection point + * if they do. + * + * The intersection point may be computed in a precise or non-precise manner. + * Computing it precisely involves rounding it to an integer. (This assumes + * that the input coordinates have been made precise by scaling them to + * an integer grid.) + * + */ +class GEOS_DLL LineIntersector { +public: + + /// Computes the "edge distance" of an intersection point p in an edge. + /// + /// The edge distance is a metric of the point along the edge. + /// The metric used is a robust and easy to compute metric function. + /// It is not equivalent to the usual Euclidean metric. + /// It relies on the fact that either the x or the y ordinates of the + /// points in the edge are unique, depending on whether the edge is longer in + /// the horizontal or vertical direction. + /// + /// NOTE: This function may produce incorrect distances + /// for inputs where p is not precisely on p1-p2 + /// (E.g. p = (139,9) p1 = (139,10), p2 = (280,1) produces distanct + /// 0.0, which is incorrect. + /// + /// My hypothesis is that the function is safe to use for points which are the + /// result of rounding points which lie on the line, + /// but not safe to use for truncated points. + /// + static double computeEdgeDistance(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + + static double nonRobustComputeEdgeDistance(const geom::Coordinate& p, const geom::Coordinate& p1, + const geom::Coordinate& p2); + + explicit LineIntersector(const geom::PrecisionModel* initialPrecisionModel = nullptr) + : + precisionModel(initialPrecisionModel), + result(0), + inputLines(), + isProperVar(false) + {} + + ~LineIntersector() = default; + + /** \brief + * Tests whether either intersection point is an interior point of + * one of the input segments. + * + * @return true if either intersection point is in + * the interior of one of the input segments + */ + bool isInteriorIntersection() + { + if(isInteriorIntersection(0)) { + return true; + } + if(isInteriorIntersection(1)) { + return true; + } + return false; + }; + + /** \brief + * Tests whether either intersection point is an interior point + * of the specified input segment. + * + * @return true if either intersection point is in + * the interior of the input segment + */ + bool isInteriorIntersection(std::size_t inputLineIndex) + { + for(std::size_t i = 0; i < result; ++i) { + if(!(intPt[i].equals2D(*inputLines[inputLineIndex][0]) + || intPt[i].equals2D(*inputLines[inputLineIndex][1]))) { + return true; + } + } + return false; + }; + + /// Force computed intersection to be rounded to a given precision model. + /// + /// No getter is provided, because the precision model is not required + /// to be specified. + /// @param newPM the PrecisionModel to use for rounding + /// + void + setPrecisionModel(const geom::PrecisionModel* newPM) + { + precisionModel = newPM; + } + + enum intersection_type : uint8_t { + /// Indicates that line segments do not intersect + NO_INTERSECTION = 0, + + /// Indicates that line segments intersect in a single point + POINT_INTERSECTION = 1, + + /// Indicates that line segments intersect in a line segment + COLLINEAR_INTERSECTION = 2 + }; + + /// Computes the intersection of the lines p1-p2 and p3-p4 + template + void computeIntersection(const C1& p1, const C1& p2, + const C2& p3, const C2& p4) + { + inputLines[0][0] = &p1; + inputLines[0][1] = &p2; + inputLines[1][0] = &p3; + inputLines[1][1] = &p4; + result = computeIntersect(p1, p2, p3, p4); + } + + /// Compute the intersection between two segments, given a sequence and starting index of each + void computeIntersection(const geom::CoordinateSequence& p, std::size_t p0, + const geom::CoordinateSequence& q, std::size_t q0); + + std::string toString() const; + + /** + * Tests whether the input geometries intersect. + * + * @return true if the input geometries intersect + */ + bool + hasIntersection() const + { + return result != NO_INTERSECTION; + } + + + /** + * Gets an endpoint of an input segment. + * + * @param segmentIndex the index of the input segment (0 or 1) + * @param ptIndex the index of the endpoint (0 or 1) + * @return the specified endpoint + */ + const geom::CoordinateXY* + getEndpoint(std::size_t segmentIndex, std::size_t ptIndex) const + { + return inputLines[segmentIndex][ptIndex]; + } + + /// Returns the number of intersection points found. + /// + /// This will be either 0, 1 or 2. + /// + size_t + getIntersectionNum() const + { + return result; + } + + + /// Returns the intIndex'th intersection point + /// + /// @param intIndex is 0 or 1 + /// + /// @return the intIndex'th intersection point + /// + const geom::CoordinateXYZM& + getIntersection(std::size_t intIndex) const + { + return intPt[intIndex]; + } + + /// Returns false if both numbers are zero. + /// + /// @return true if both numbers are positive or if both numbers are negative. + /// + static bool isSameSignAndNonZero(double a, double b); + + /** \brief + * Test whether a point is a intersection point of two line segments. + * + * Note that if the intersection is a line segment, this method only tests for + * equality with the endpoints of the intersection segment. + * It does not return true if + * the input point is internal to the intersection segment. + * + * @return true if the input point is one of the intersection points. + */ + bool isIntersection(const geom::Coordinate& pt) const + { + for(std::size_t i = 0; i < result; ++i) { + if(intPt[i].equals2D(pt)) { + return true; + } + } + return false; + }; + + /** \brief + * Tests whether an intersection is proper. + * + * The intersection between two line segments is considered proper if + * they intersect in a single point in the interior of both segments + * (e.g. the intersection is a single point and is not equal to any of the + * endpoints). + * + * The intersection between a point and a line segment is considered proper + * if the point lies in the interior of the segment (e.g. is not equal to + * either of the endpoints). + * + * @return true if the intersection is proper + */ + bool + isProper() const + { + return hasIntersection() && isProperVar; + } + + /** \brief + * Computes the intIndex'th intersection point in the direction of + * a specified input line segment + * + * @param segmentIndex is 0 or 1 + * @param intIndex is 0 or 1 + * + * @return the intIndex'th intersection point in the direction of the + * specified input line segment + */ + const geom::Coordinate& getIntersectionAlongSegment(std::size_t segmentIndex, std::size_t intIndex); + + /** \brief + * Computes the index of the intIndex'th intersection point in the direction of + * a specified input line segment + * + * @param segmentIndex is 0 or 1 + * @param intIndex is 0 or 1 + * + * @return the index of the intersection point along the segment (0 or 1) + */ + std::size_t getIndexAlongSegment(std::size_t segmentIndex, std::size_t intIndex); + + /** \brief + * Computes the "edge distance" of an intersection point along the specified + * input line segment. + * + * @param geomIndex is 0 or 1 + * @param intIndex is 0 or 1 + * + * @return the edge distance of the intersection point + */ + double getEdgeDistance(std::size_t geomIndex, std::size_t intIndex) const; + +private: + + /** + * If makePrecise is true, computed intersection coordinates + * will be made precise using Coordinate#makePrecise + */ + const geom::PrecisionModel* precisionModel; + + std::size_t result; + + const geom::CoordinateXY* inputLines[2][2]; + + /** + * We store real Coordinates here because + * we must compute the Z of intersection point. + */ + geom::CoordinateXYZM intPt[2]; + + /** + * The indexes of the endpoints of the intersection lines, in order along + * the corresponding line + */ + std::size_t intLineIndex[2][2]; + + bool isProperVar; + //Coordinate &pa; + //Coordinate &pb; + + bool + isCollinear() const + { + return result == COLLINEAR_INTERSECTION; + } + + template + uint8_t computeIntersect(const C1& p1, const C1& p2, const C2& q1, const C2& q2) + { + isProperVar = false; + + // first try a fast test to see if the envelopes of the lines intersect + if(!geom::Envelope::intersects(p1, p2, q1, q2)) { + return NO_INTERSECTION; + } + + // for each endpoint, compute which side of the other segment it lies + // if both endpoints lie on the same side of the other segment, + // the segments do not intersect + int Pq1 = Orientation::index(p1, p2, q1); + int Pq2 = Orientation::index(p1, p2, q2); + + if((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) { + return NO_INTERSECTION; + } + + int Qp1 = Orientation::index(q1, q2, p1); + int Qp2 = Orientation::index(q1, q2, p2); + + if((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) { + return NO_INTERSECTION; + } + + /** + * Intersection is collinear if each endpoint lies on the other line. + */ + bool collinear = Pq1 == 0 && Pq2 == 0 && Qp1 == 0 && Qp2 == 0; + if(collinear) { + return computeCollinearIntersection(p1, p2, q1, q2); + } + + /* + * At this point we know that there is a single intersection point + * (since the lines are not collinear). + */ + + /* + * Check if the intersection is an endpoint. + * If it is, copy the endpoint as + * the intersection point. Copying the point rather than + * computing it ensures the point has the exact value, + * which is important for robustness. It is sufficient to + * simply check for an endpoint which is on the other line, + * since at this point we know that the inputLines must + * intersect. + */ + geom::CoordinateXYZM p; + double z = DoubleNotANumber; + double m = DoubleNotANumber; + + if(Pq1 == 0 || Pq2 == 0 || Qp1 == 0 || Qp2 == 0) { + + isProperVar = false; + + /* Check for two equal endpoints. + * This is done explicitly rather than by the orientation tests + * below in order to improve robustness. + * + * (A example where the orientation tests fail + * to be consistent is: + * + * LINESTRING ( 19.850257749638203 46.29709338043669, + * 20.31970698357233 46.76654261437082 ) + * and + * LINESTRING ( -48.51001596420236 -22.063180333403878, + * 19.850257749638203 46.29709338043669 ) + * + * which used to produce the INCORRECT result: + * (20.31970698357233, 46.76654261437082, NaN) + */ + + if (p1.equals2D(q1)) { + p = p1; + z = Interpolate::zGet(p1, q1); + m = Interpolate::mGet(p1, q1); + } + else if (p1.equals2D(q2)) { + p = p1; + z = Interpolate::zGet(p1, q2); + m = Interpolate::mGet(p1, q2); + } + else if (p2.equals2D(q1)) { + p = p2; + z = Interpolate::zGet(p2, q1); + m = Interpolate::mGet(p2, q1); + } + else if (p2.equals2D(q2)) { + p = p2; + z = Interpolate::zGet(p2, q2); + m = Interpolate::mGet(p2, q2); + } + /* + * Now check to see if any endpoint lies on the interior of the other segment. + */ + else if(Pq1 == 0) { + p = q1; + z = Interpolate::zGetOrInterpolate(q1, p1, p2); + m = Interpolate::mGetOrInterpolate(q1, p1, p2); + } + else if(Pq2 == 0) { + p = q2; + z = Interpolate::zGetOrInterpolate(q2, p1, p2); + m = Interpolate::mGetOrInterpolate(q2, p1, p2); + } + else if(Qp1 == 0) { + p = p1; + z = Interpolate::zGetOrInterpolate(p1, q1, q2); + m = Interpolate::mGetOrInterpolate(p1, q1, q2); + } + else if(Qp2 == 0) { + p = p2; + z = Interpolate::zGetOrInterpolate(p2, q1, q2); + m = Interpolate::mGetOrInterpolate(p2, q1, q2); + } + } else { + isProperVar = true; + p = intersection(p1, p2, q1, q2); + z = Interpolate::zInterpolate(p, p1, p2, q1, q2); + m = Interpolate::mInterpolate(p, p1, p2, q1, q2); + } + intPt[0] = geom::CoordinateXYZM(p.x, p.y, z, m); + #if GEOS_DEBUG + std::cerr << " POINT_INTERSECTION; intPt[0]:" << intPt[0].toString() << std::endl; + #endif // GEOS_DEBUG + return POINT_INTERSECTION; + } + + bool + isEndPoint() const + { + return hasIntersection() && !isProperVar; + } + + void computeIntLineIndex(); + + void computeIntLineIndex(std::size_t segmentIndex); + + template + uint8_t computeCollinearIntersection(const C1& p1, const C1& p2, const C2& q1, const C2& q2) + { + bool q1inP = geom::Envelope::intersects(p1, p2, q1); + bool q2inP = geom::Envelope::intersects(p1, p2, q2); + bool p1inQ = geom::Envelope::intersects(q1, q2, p1); + bool p2inQ = geom::Envelope::intersects(q1, q2, p2); + + if(q1inP && q2inP) { + intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2); + intPt[1] = zmGetOrInterpolateCopy(q2, p1, p2); + return COLLINEAR_INTERSECTION; + } + if(p1inQ && p2inQ) { + intPt[0] = zmGetOrInterpolateCopy(p1, q1, q2); + intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2); + return COLLINEAR_INTERSECTION; + } + if(q1inP && p1inQ) { + // if pts are equal Z is chosen arbitrarily + intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2); + intPt[1] = zmGetOrInterpolateCopy(p1, q1, q2); + + return (q1 == p1) && !q2inP && !p2inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION; + } + if(q1inP && p2inQ) { + // if pts are equal Z is chosen arbitrarily + intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2); + intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2); + + return (q1 == p2) && !q2inP && !p1inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION; + } + if(q2inP && p1inQ) { + // if pts are equal Z is chosen arbitrarily + intPt[0] = zmGetOrInterpolateCopy(q2, p1, p2); + intPt[1] = zmGetOrInterpolateCopy(p1, q1, q2); + + return (q2 == p1) && !q1inP && !p2inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION; + } + if(q2inP && p2inQ) { + // if pts are equal Z is chosen arbitrarily + intPt[0] = zmGetOrInterpolateCopy(q2, p1, p2); + intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2); + return (q2 == p2) && !q1inP && !p1inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION; + } + return NO_INTERSECTION; + } + + /** \brief + * This method computes the actual value of the intersection point. + * + * To obtain the maximum precision from the intersection calculation, + * the coordinates are normalized by subtracting the minimum + * ordinate values (in absolute value). This has the effect of + * removing common significant digits from the calculation to + * maintain more bits of precision. + */ + template + geom::CoordinateXYZM intersection (const C1& p1, const C1& p2, const C2& q1, const C2& q2) const { + auto intPtOut = intersectionSafe(p1, p2, q1, q2); + + /* + * Due to rounding it can happen that the computed intersection is + * outside the envelopes of the input segments. Clearly this + * is inconsistent. + * This code checks this condition and forces a more reasonable answer + * + * MD - May 4 2005 - This is still a problem. Here is a failure case: + * + * LINESTRING (2089426.5233462777 1180182.3877339689, + * 2085646.6891757075 1195618.7333999649) + * LINESTRING (1889281.8148903656 1997547.0560044837, + * 2259977.3672235999 483675.17050843034) + * int point = (2097408.2633752143,1144595.8008114607) + */ + + if(! isInSegmentEnvelopes(intPtOut)) { + //intPt = CentralEndpointIntersector::getIntersection(p1, p2, q1, q2); + intPtOut = nearestEndpoint(p1, p2, q1, q2); + } + + if(precisionModel != nullptr) { + precisionModel->makePrecise(intPtOut); + } + + return intPtOut; + } + + /** + * Test whether a point lies in the envelopes of both input segments. + * A correctly computed intersection point should return true + * for this test. + * Since this test is for debugging purposes only, no attempt is + * made to optimize the envelope test. + * + * @return true if the input point lies within both + * input segment envelopes + */ + bool isInSegmentEnvelopes(const geom::CoordinateXY& pt) const + { + geom::Envelope env0(*inputLines[0][0], *inputLines[0][1]); + geom::Envelope env1(*inputLines[1][0], *inputLines[1][1]); + return env0.contains(pt) && env1.contains(pt); + }; + + /** + * Computes a segment intersection. + * Round-off error can cause the raw computation to fail, + * (usually due to the segments being approximately parallel). + * If this happens, a reasonable approximation is computed instead. + * + * @param p1 a segment endpoint + * @param p2 a segment endpoint + * @param q1 a segment endpoint + * @param q2 a segment endpoint + * @return the computed intersection point is stored there + */ + template + geom::CoordinateXYZM intersectionSafe(const C1& p1, const C1& p2, + const C2& q1, const C2& q2) const + { + geom::CoordinateXYZM ptInt(Intersection::intersection(p1, p2, q1, q2)); + if (ptInt.isNull()) { + const geom::CoordinateXY& nearest = nearestEndpoint(p1, p2, q1, q2); +#if __cplusplus >= 201703L + if constexpr (std::is_same::value) { +#else + if (std::is_same::value) { +#endif + ptInt = static_cast(nearest); + } else { + if (&nearest == static_cast(&p1) || &nearest == static_cast(&p2)) { + ptInt = static_cast(nearest); + } else { + ptInt = static_cast(nearest); + } + } + } + return ptInt; + } + + /** + * Finds the endpoint of the segments P and Q which + * is closest to the other segment. + * This is a reasonable surrogate for the true + * intersection points in ill-conditioned cases + * (e.g. where two segments are nearly coincident, + * or where the endpoint of one segment lies almost on the other segment). + *

+ * This replaces the older CentralEndpoint heuristic, + * which chose the wrong endpoint in some cases + * where the segments had very distinct slopes + * and one endpoint lay almost on the other segment. + * + * @param p1 an endpoint of segment P + * @param p2 an endpoint of segment P + * @param q1 an endpoint of segment Q + * @param q2 an endpoint of segment Q + * @return the nearest endpoint to the other segment + */ + static const geom::CoordinateXY& nearestEndpoint(const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2, + const geom::CoordinateXY& q1, + const geom::CoordinateXY& q2); + + + template + static geom::CoordinateXYZM zmGetOrInterpolateCopy( + const C1& p, + const C2& p1, + const C2& p2) + { + geom::CoordinateXYZM pCopy(p); + pCopy.z = Interpolate::zGetOrInterpolate(p, p1, p2); + pCopy.m = Interpolate::mGetOrInterpolate(p, p1, p2); + return pCopy; + } + +}; + + +} // namespace geos::algorithm +} // namespace geos + + + + + + + + + + + + + + + + diff --git a/Sources/geos/include/geos/algorithm/MinimumAreaRectangle.h b/Sources/geos/include/geos/algorithm/MinimumAreaRectangle.h new file mode 100644 index 0000000..434f4c7 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/MinimumAreaRectangle.h @@ -0,0 +1,157 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { + namespace geom { + class CoordinateSequence; + class CoordinateXY; + class Geometry; + class GeometryFactory; + class LineSegment; + class LineString; + class Polygon; + } +} + +namespace geos { +namespace algorithm { // geos::algorithm + + +/** + * Computes the minimum-area rectangle enclosing a Geometry. + * Unlike the Envelope, the rectangle may not be axis-parallel. + * + * The first step in the algorithm is computing the convex hull of the Geometry. + * If the input Geometry is known to be convex, a hint can be supplied to + * avoid this computation. + * + * In degenerate cases the minimum enclosing geometry + * may be a LineString or a Point. + * + * The minimum-area enclosing rectangle does not necessarily + * have the minimum possible width. + * Use MinimumDiameter to compute this. + * + * @see MinimumDiameter + * @see ConvexHull + * + */ +class GEOS_DLL MinimumAreaRectangle { + using CoordinateSequence = geos::geom::CoordinateSequence; + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineSegment = geos::geom::LineSegment; + using LineString = geos::geom::LineString; + using Polygon = geos::geom::Polygon; + +private: + + // Members + const Geometry* m_inputGeom; + bool m_isConvex; + + // Methods + std::unique_ptr getMinimumRectangle(); + + std::unique_ptr computeConvex(const Geometry* convexGeom); + + /** + * Computes the minimum-area rectangle for a convex ring of Coordinate. + * + * This algorithm uses the "dual rotating calipers" technique. + * Performance is linear in the number of segments. + * + * @param ring the convex ring to scan + */ + std::unique_ptr computeConvexRing(const CoordinateSequence* ring); + + std::size_t findFurthestVertex( + const CoordinateSequence* pts, + const LineSegment& baseSeg, + std::size_t startIndex, + int orient); + + bool isFurtherOrEqual(double d1, double d2, int orient); + + static double orientedDistance( + const LineSegment& seg, + const CoordinateXY& p, + int orient); + + static std::size_t getNextIndex( + const CoordinateSequence* ring, + std::size_t index); + + /** + * Creates a line of maximum extent from the provided vertices + * @param pts the vertices + * @param factory the geometry factory + * @return the line of maximum extent + */ + static std::unique_ptr computeMaximumLine( + const CoordinateSequence* pts, + const GeometryFactory* factory); + + +public: + + /** + * Compute a minimum-area rectangle for a given Geometry. + * + * @param inputGeom a Geometry + */ + MinimumAreaRectangle(const Geometry* inputGeom) + : m_inputGeom(inputGeom) + , m_isConvex(false) + {}; + + /** + * Compute a minimum rectangle for a Geometry, + * with a hint if the geometry is convex + * (e.g. a convex Polygon or LinearRing, + * or a two-point LineString, or a Point). + * + * @param inputGeom a Geometry which is convex + * @param isConvex true if the input geometry is convex + */ + MinimumAreaRectangle(const Geometry* inputGeom, bool isConvex) + : m_inputGeom(inputGeom) + , m_isConvex(isConvex) + {}; + + /** + * Gets the minimum-area rectangular Polygon which encloses the input geometry. + * If the convex hull of the input is degenerate (a line or point) + * a LineString or Point is returned. + * + * @param geom the geometry + * @return the minimum rectangle enclosing the geometry + */ + static std::unique_ptr getMinimumRectangle(const Geometry* geom); + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/MinimumBoundingCircle.h b/Sources/geos/include/geos/algorithm/MinimumBoundingCircle.h new file mode 100644 index 0000000..66bc075 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/MinimumBoundingCircle.h @@ -0,0 +1,137 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/MinimumBoundingCircle.java 2019-01-23 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// Forward declarations +// namespace geos { +// namespace geom { +// class GeometryCollection; +// } +// } + + +namespace geos { +namespace algorithm { // geos::algorithm + +class GEOS_DLL MinimumBoundingCircle { + +private: + + // member variables + const geom::Geometry* input; + std::vector extremalPts; + geom::CoordinateXY centre; + double radius; + + void computeCentre(); + void compute(); + void computeCirclePoints(); + geom::CoordinateXY lowestPoint(std::vector& pts); + geom::CoordinateXY pointWitMinAngleWithX(std::vector& pts, geom::CoordinateXY& P); + geom::CoordinateXY pointWithMinAngleWithSegment(std::vector& pts, + geom::CoordinateXY& P, geom::CoordinateXY& Q); + std::vector farthestPoints(std::vector& pts); + + +public: + + MinimumBoundingCircle(const geom::Geometry* geom): + input(nullptr), + radius(0.0) + { + input = geom; + centre.setNull(); + } + + ~MinimumBoundingCircle() {}; + + /** + * Gets a geometry which represents the Minimum Bounding Circle. + * If the input is degenerate (empty or a single unique point), + * this method will return an empty geometry or a single Point geometry. + * Otherwise, a Polygon will be returned which approximates the + * Minimum Bounding Circle. + * (Note that because the computed polygon is only an approximation, + * it may not precisely contain all the input points.) + * + * @return a Geometry representing the Minimum Bounding Circle. + */ + std::unique_ptr getCircle(); + + /** + * Gets a geometry representing a line between the two farthest points + * in the input. + * These points will be two of the extremal points of the Minimum Bounding Circle. + * They also lie on the convex hull of the input. + * + * @return a LineString between the two farthest points of the input + * @return a empty LineString if the input is empty + * @return a Point if the input is a point + */ + std::unique_ptr getMaximumDiameter(); + + /** + * Gets a geometry representing the diameter of the computed Minimum Bounding + * Circle. + * + * @return the diameter LineString of the Minimum Bounding Circle + * @return a empty LineString if the input is empty + * @return a Point if the input is a point + */ + std::unique_ptr getDiameter(); + + /** + * Gets the extremal points which define the computed Minimum Bounding Circle. + * There may be zero, one, two or three of these points, + * depending on the number of points in the input + * and the geometry of those points. + * + * @return the points defining the Minimum Bounding Circle + */ + std::vector getExtremalPoints(); + + /** + * Gets the centre point of the computed Minimum Bounding Circle. + * + * @return the centre point of the Minimum Bounding Circle + * @return null if the input is empty + */ + geom::CoordinateXY getCentre(); + + /** + * Gets the radius of the computed Minimum Bounding Circle. + * + * @return the radius of the Minimum Bounding Circle + */ + double getRadius(); + +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/MinimumDiameter.h b/Sources/geos/include/geos/algorithm/MinimumDiameter.h new file mode 100644 index 0000000..ada2d84 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/MinimumDiameter.h @@ -0,0 +1,185 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +class LineString; +class CoordinateSequence; +} +} + + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Computes the minimum diameter of a geom::Geometry + * + * The minimum diameter is defined to be the width of the smallest band that + * contains the geometry, where a band is a strip of the plane defined + * by two parallel lines. This can be thought of as the smallest hole + * that the geometry can be moved through, with a single rotation. + * + * The first step in the algorithm is computing the convex hull of the Geometry. + * If the input Geometry is known to be convex, a hint can be supplied to + * avoid this computation. + * + * This class can also be used to compute a line segment representing + * the minimum diameter, the supporting line segment of the minimum diameter, + * and a minimum-width rectangle of the input geometry. + * This rectangle will have width equal to the minimum diameter, and have + * one side parallel to the supporting segment. + * + * In degenerate cases the rectangle may be a LineString or a Point. + * (Note that this may not be the enclosing rectangle with minimum area; + * use MinimumAreaRectangle to compute this.) + * + * @see ConvexHull + * @see MinimumAreaRectangle + */ +class GEOS_DLL MinimumDiameter { +private: + const geom::Geometry* inputGeom; + bool isConvex; + + std::unique_ptr convexHullPts; + + geom::LineSegment minBaseSeg; + geom::Coordinate minWidthPt; + std::size_t minPtIndex; + double minWidth; + void computeMinimumDiameter(); + void computeWidthConvex(const geom::Geometry* geom); + + /** + * Compute the width information for a ring of Coordinate. + * Leaves the width information in the instance variables. + * + * @param pts + * @return + */ + void computeConvexRingMinDiameter(const geom::CoordinateSequence* pts); + + unsigned int findMaxPerpDistance(const geom::CoordinateSequence* pts, + const geom::LineSegment* seg, unsigned int startIndex); + + static unsigned int getNextIndex(const geom::CoordinateSequence* pts, + unsigned int index); + + static double computeC(double a, double b, const geom::Coordinate& p); + + static geom::LineSegment computeSegmentForLine(double a, double b, double c); + + static std::unique_ptr computeMaximumLine( + const geom::CoordinateSequence* pts, + const geom::GeometryFactory* factory); + +public: + ~MinimumDiameter() = default; + + /** \brief + * Compute a minimum diameter for a given [Geometry](@ref geom::Geometry). + * + * @param newInputGeom a Geometry + */ + MinimumDiameter(const geom::Geometry* newInputGeom); + + /** \brief + * Compute a minimum diameter for a given Geometry, + * with a hint if the Geometry is convex + * (e.g. a convex Polygon or LinearRing, + * or a two-point LineString, or a Point). + * + * @param newInputGeom a Geometry which is convex + * @param newIsConvex `true` if the input geometry is convex + */ + MinimumDiameter(const geom::Geometry* newInputGeom, + const bool newIsConvex); + + /** \brief + * Gets the length of the minimum diameter of the input Geometry. + * + * @return the length of the minimum diameter + */ + double getLength(); + + /** \brief + * Gets the geom::Coordinate forming one end of the minimum diameter. + * + * @return a coordinate forming one end of the minimum diameter + */ + const geom::Coordinate& getWidthCoordinate(); + + /** \brief + * Gets the segment forming the base of the minimum diameter. + * + * @return the segment forming the base of the minimum diameter + */ + std::unique_ptr getSupportingSegment(); + + /** \brief + * Gets a LineString which is a minimum diameter. + * + * @return a LineString which is a minimum diameter + */ + std::unique_ptr getDiameter(); + + /** \brief + * Gets the rectangular Polygon which encloses the input geometry + * and is based on the minimum diameter supporting segment. + * + * The rectangle has width equal to the minimum diameter, and a longer + * length. If the convex hill of the input is degenerate (a line or point) + * a LineString or Point is returned. + * This is not necessarily the rectangle with minimum area. + * Use MinimumAreaRectangle to compute this. + * + * @return the the minimum-width rectangle enclosing the geometry + * @see MinimumAreaRectangle + */ + std::unique_ptr getMinimumRectangle(); + + /** \brief + * Gets the minimum rectangle enclosing a geometry. + * + * @param geom the geometry + * @return a rectangle enclosing the input (or a line or point if degenerate) + * @see MinimumAreaRectangle + */ + static std::unique_ptr getMinimumRectangle(geom::Geometry* geom); + + /** \brief + * Gets the length of the minimum diameter enclosing a geometry. + * @param geom the geometry + * @return the length of the minimum diameter of the geometry + */ + static std::unique_ptr getMinimumDiameter(geom::Geometry* geom); + +}; + +} // namespace geos::algorithm +} // namespace geos diff --git a/Sources/geos/include/geos/algorithm/NotRepresentableException.h b/Sources/geos/include/geos/algorithm/NotRepresentableException.h new file mode 100644 index 0000000..c61084c --- /dev/null +++ b/Sources/geos/include/geos/algorithm/NotRepresentableException.h @@ -0,0 +1,43 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm + +/** + * \class NotRepresentableException + * \brief + * Indicates that a HCoordinate has been computed which is + * not representable on the Cartesian plane. + * + * @version 1.4 + * @see HCoordinate + */ +class GEOS_DLL NotRepresentableException: public util::GEOSException { +public: + NotRepresentableException(); + NotRepresentableException(std::string msg); + ~NotRepresentableException() noexcept override {} +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/Orientation.h b/Sources/geos/include/geos/algorithm/Orientation.h new file mode 100644 index 0000000..135a4f0 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Orientation.h @@ -0,0 +1,122 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/Orientation.java @ 2017-09-04 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Functions to compute the orientation of basic geometric structures + * including point triplets (triangles) and rings. + * + * Orientation is a fundamental property of planar geometries + * (and more generally geometry on two-dimensional manifolds). + * + * Orientation is notoriously subject to numerical precision errors + * in the case of collinear or nearly collinear points. + * JTS uses extended-precision arithmetic to increase + * the robustness of the computation. + * + * @author Martin Davis + */ +class GEOS_DLL Orientation { +public: + + /* A value that indicates an orientation or turn */ + enum { + CLOCKWISE = -1, + COLLINEAR = 0, + COUNTERCLOCKWISE = 1, + RIGHT = -1, + LEFT = 1, + STRAIGHT = 0 + }; + + /** \brief + * Returns the orientation index of the direction of the point q relative to + * a directed infinite line specified by p1-p2. + * + * The index indicates whether the point lies to the + * `Orientation::LEFT` or `Orientation::RIGHT` + * of the line, or lies on it `Orientation::COLLINEAR`. + * The index also indicates the orientation of the triangle formed + * by the three points + * ( `Orientation::COUNTERCLOCKWISE`, + * `Orientation::CLOCKWISE`, or `Orientation::STRAIGHT` ) + */ + static int index(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, + const geom::CoordinateXY& q); + + /** + * Computes whether a ring defined by a geom::CoordinateSequence is + * oriented counter-clockwise. + * + * * The list of points is assumed to have the first and last points equal. + * * This handles coordinate lists which contain repeated points. + * * This handles rings which contain collapsed segments + * (in particular, along the top of the ring). + * + * This algorithm is guaranteed to work with valid rings. + * It also works with "mildly invalid" rings + * which contain collapsed (coincident) flat segments along the top of the ring. + * If the ring is "more" invalid (e.g. self-crosses or touches), + * the computed result may not be correct. + * + * @param ring a CoordinateSequence forming a ring (with first and last point identical) + * @return true if the ring is oriented counter-clockwise. + * @throws IllegalArgumentException if there are too few points to determine orientation (< 4) + */ + static bool isCCW(const geom::CoordinateSequence* ring); + + /** + * Tests if a ring defined by a CoordinateSequence is + * oriented counter-clockwise, using the signed area of the ring. + * + * * The list of points is assumed to have the first and last points equal. + * * This handles coordinate lists which contain repeated points. + * * This handles rings which contain collapsed segments + * (in particular, along the top of the ring). + * * This handles rings which are invalid due to self-intersection + * + * This algorithm is guaranteed to work with valid rings. + * For invalid rings (containing self-intersections), + * the algorithm determines the orientation of + * the largest enclosed area (including overlaps). + * This provides a more useful result in some situations, such as buffering. + * + * However, this approach may be less accurate in the case of + * rings with almost zero area. + * (Note that the orientation of rings with zero area is essentially + * undefined, and hence non-deterministic.) + * + * @param ring a CoordinateSequence forming a ring (with first and last point identical) + * @return true if the ring is oriented counter-clockwise. + */ + static bool isCCWArea(const geom::CoordinateSequence* ring); + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/PointInRing.h b/Sources/geos/include/geos/algorithm/PointInRing.h new file mode 100644 index 0000000..4f97c7d --- /dev/null +++ b/Sources/geos/include/geos/algorithm/PointInRing.h @@ -0,0 +1,40 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +class GEOS_DLL PointInRing { +public: + virtual + ~PointInRing() {} + virtual bool isInside(const geom::Coordinate& pt) = 0; +}; + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/PointLocation.h b/Sources/geos/include/geos/algorithm/PointLocation.h new file mode 100644 index 0000000..6c3f74b --- /dev/null +++ b/Sources/geos/include/geos/algorithm/PointLocation.h @@ -0,0 +1,107 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/PointLocation.java @ 2017-09-04 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { + +namespace geom { +class Coordinate; +class CoordinateXY; +class CoordinateSequence; +class Curve; +} + +namespace algorithm { // geos::algorithm + +/** \brief + * Functions for locating points within basic geometric + * structures such as lines and rings. + * + * @author Martin Davis + * + */ +class GEOS_DLL PointLocation { +public: + + /** \brief + * Tests whether a point lies on a line segment. + * + * @param p the point to test + * @param p0 a point of the line segment + * @param p1 a point of the line segment + * @return true if the point lies on the line segment + */ + static bool isOnSegment(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + + /** \brief + * Tests whether a point lies on the line defined by a + * [CoordinateSequence](@ref geom::CoordinateSequence). + * + * @param p the point to test + * @param line the line coordinates + * @return true if the point is a vertex of the line or lies in the interior + * of a line segment in the line + */ + static bool isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSequence* line); + + /** \brief + * Tests whether a point lies inside or on a ring. + * + * The ring may be oriented in either direction. A point lying exactly + * on the ring boundary is considered to be inside the ring. + * + * This method does **not** first check the point against the envelope of + * the ring. + * + * @param p point to check for ring inclusion + * @param ring an array of coordinates representing the ring (which must have + * first point identical to last point) + * @return `true` if p is inside ring + * + * @see RayCrossingCounter::locatePointInRing() + */ + static bool isInRing(const geom::CoordinateXY& p, const std::vector& ring); + static bool isInRing(const geom::CoordinateXY& p, const geom::CoordinateSequence* ring); + + /** \brief + * Determines whether a point lies in the interior, on the boundary, or in the + * exterior of a ring. The ring may be oriented in either direction. + * + * This method does *not* first check the point against the envelope of + * the ring. + * + * @param p point to check for ring inclusion + * @param ring an array of coordinates representing the ring (which must have + * first point identical to last point) + * @return the [Location](@ref geom::Location) of p relative to the ring + */ + static geom::Location locateInRing(const geom::CoordinateXY& p, const std::vector& ring); + static geom::Location locateInRing(const geom::CoordinateXY& p, const geom::CoordinateSequence& ring); + static geom::Location locateInRing(const geom::CoordinateXY& p, const geom::Curve& ring); + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/PointLocator.h b/Sources/geos/include/geos/algorithm/PointLocator.h new file mode 100644 index 0000000..6221b5d --- /dev/null +++ b/Sources/geos/include/geos/algorithm/PointLocator.h @@ -0,0 +1,108 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2011 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/PointLocator.java 95fbe34b (JTS-1.15.2-SNAPSHOT) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inlines + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +class Geometry; +class LinearRing; +class LineString; +class Polygon; +class Point; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** + * \class PointLocator + * + * \brief + * Computes the topological relationship (Location) + * of a single point to a Geometry. + * + * The algorithm obeys the SFS boundaryDetermination rule to correctly determine + * whether the point lies on the boundary or not. + * + * Notes: + * - instances of this class are not reentrant. + * - LinearRing objects do not enclose any area + * (points inside the ring are still in the EXTERIOR of the ring.) + * + */ +class GEOS_DLL PointLocator { +public: + PointLocator() {} + ~PointLocator() {} + + /** + * Computes the topological relationship (Location) of a single point + * to a Geometry. It handles both single-element and multi-element Geometries. + * The algorithm for multi-part Geometriestakes into account the SFS + * Boundary Determination rule. + * + * @return the Location of the point relative to the input Geometry + */ + geom::Location locate(const geom::CoordinateXY& p, const geom::Geometry* geom); + + /** + * Convenience method to test a point for intersection with + * a Geometry + * + * @param p the coordinate to test + * @param geom the Geometry to test + * @return true if the point is in the interior or boundary of the Geometry + */ + bool + intersects(const geom::CoordinateXY& p, const geom::Geometry* geom) + { + return locate(p, geom) != geom::Location::EXTERIOR; + } + +private: + + bool isIn; // true if the point lies in or on any Geometry element + + int numBoundaries; // the number of sub-elements whose boundaries the point lies in + + void computeLocation(const geom::CoordinateXY& p, const geom::Geometry* geom); + + void updateLocationInfo(geom::Location loc); + + geom::Location locate(const geom::CoordinateXY& p, const geom::Point* pt); + + geom::Location locate(const geom::CoordinateXY& p, const geom::LineString* l); + + geom::Location locateInPolygonRing(const geom::CoordinateXY& p, const geom::LinearRing* ring); + + geom::Location locate(const geom::CoordinateXY& p, const geom::Polygon* poly); + +}; + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/PolygonNodeTopology.h b/Sources/geos/include/geos/algorithm/PolygonNodeTopology.h new file mode 100644 index 0000000..8a4eb76 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/PolygonNodeTopology.h @@ -0,0 +1,147 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://libgeos.org + * + * Copyright (c) 2021 Martin Davis + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm + +/** + * Functions to compute topological information + * about nodes (ring intersections) in polygonal geometry. + * + * @author mdavis + * + */ +class GEOS_DLL PolygonNodeTopology { + using CoordinateXY = geos::geom::CoordinateXY; + +public: + + /* + * Check if the segments at a node between two rings (or one ring) cross. + * The node is topologically valid if the rings do not cross. + * This function assumes that the segments are not collinear. + * + * @param nodePt the node location + * @param a0 the previous segment endpoint in a ring + * @param a1 the next segment endpoint in a ring + * @param b0 the previous segment endpoint in the other ring + * @param b1 the next segment endpoint in the other ring + * @return true if the rings cross at the node + */ + static bool + isCrossing(const CoordinateXY* nodePt, + const CoordinateXY* a0, const CoordinateXY* a1, + const CoordinateXY* b0, const CoordinateXY* b1); + + + /** + * Tests whether an segment node-b lies in the interior or exterior + * of a corner of a ring formed by the two segments a0-node-a1. + * The ring interior is assumed to be on the right of the corner + * (i.e. a CW shell or CCW hole). + * The test segment must not be collinear with the corner segments. + * + * @param nodePt the node location + * @param a0 the first vertex of the corner + * @param a1 the second vertex of the corner + * @param b the other vertex of the test segment + * @return true if the segment is interior to the ring corner + */ + static bool isInteriorSegment(const CoordinateXY* nodePt, + const CoordinateXY* a0, const CoordinateXY* a1, const CoordinateXY* b); + + /** + * Compares the angles of two vectors + * relative to the positive X-axis at their origin. + * Angles increase CCW from the X-axis. + * + * @param origin the origin of the vectors + * @param p the endpoint of the vector P + * @param q the endpoint of the vector Q + * @return a negative integer, zero, or a positive integer as this vector P has angle less than, equal to, or greater than vector Q + */ + static int compareAngle( + const CoordinateXY* origin, + const CoordinateXY* p, + const CoordinateXY* q); + + +private: + + /** + * Tests if an edge p is between edges e0 and e1, + * where the edges all originate at a common origin. + * The "inside" of e0 and e1 is the arc which does not include the origin. + * The edges are assumed to be distinct (non-collinear). + * + * @param origin the origin + * @param p the destination point of edge p + * @param e0 the destination point of edge e0 + * @param e1 the destination point of edge e1 + * @return true if p is between e0 and e1 + */ + static bool isBetween(const CoordinateXY* origin, + const CoordinateXY* p, + const CoordinateXY* e0, const CoordinateXY* e1); + + /** + * Compares whether an edge p is between or outside the edges e0 and e1, + * where the edges all originate at a common origin. + * The "inside" of e0 and e1 is the arc which does not include + * the positive X-axis at the origin. + * If p is collinear with an edge 0 is returned. + * + * @param origin the origin + * @param p the destination point of edge p + * @param e0 the destination point of edge e0 + * @param e1 the destination point of edge e1 + * @return a negative integer, zero or positive integer as the vector P lies outside, collinear with, or inside the vectors E0 and E1 + */ + static int compareBetween(const CoordinateXY* origin, const CoordinateXY* p, + const CoordinateXY* e0, const CoordinateXY* e1); + + + /** + * Tests if the angle with the origin of a vector P is greater than that of the + * vector Q. + * + * @param origin the origin of the vectors + * @param p the endpoint of the vector P + * @param q the endpoint of the vector Q + * @return true if vector P has angle greater than Q + */ + static bool isAngleGreater(const CoordinateXY* origin, const CoordinateXY* p, const CoordinateXY* q); + + static int quadrant(const CoordinateXY* origin, const CoordinateXY* p); + + +}; + + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/RayCrossingCounter.h b/Sources/geos/include/geos/algorithm/RayCrossingCounter.h new file mode 100644 index 0000000..2120e23 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/RayCrossingCounter.h @@ -0,0 +1,173 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: algorithm/RayCrossingCounter.java rev. 1.2 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateXY; +class CoordinateSequence; +class CircularArc; +class Curve; +} +} + + +namespace geos { +namespace algorithm { + +/** \brief + * Counts the number of segments crossed by a horizontal ray extending to the + * right from a given point, in an incremental fashion. + * + * This can be used to determine whether a point lies in a polygonal geometry. + * The class determines the situation where the point lies exactly on a segment. + * When being used for Point-In-Polygon determination, this case allows + * short-circuiting the evaluation. + * + * This class handles polygonal geometries with any number of shells and holes. + * The orientation of the shell and hole rings is unimportant. + * In order to compute a correct location for a given polygonal geometry, + * it is essential that **all** segments are counted which + * + * - touch the ray + * - lie in in any ring which may contain the point + * + * The only exception is when the point-on-segment situation is detected, in + * which case no further processing is required. + * The implication of the above rule is that segments which can be a priori + * determined to *not* touch the ray (i.e. by a test of their bounding box or + * Y-extent) do not need to be counted. This allows for optimization by indexing. + * + * @author Martin Davis + */ +class GEOS_DLL RayCrossingCounter { +private: + const geom::CoordinateXY& point; + + std::size_t crossingCount; + + // true if the test point lies on an input segment + bool isPointOnSegment; + + // Declare type as noncopyable + RayCrossingCounter(const RayCrossingCounter& other) = delete; + RayCrossingCounter& operator=(const RayCrossingCounter& rhs) = delete; + +public: + /** \brief + * Determines the [Location](@ref geom::Location) of a point in a ring. + * + * This method is an exemplar of how to use this class. + * + * @param p the point to test + * @param ring an array of Coordinates forming a ring + * @return the location of the point in the ring + */ + static geom::Location locatePointInRing(const geom::CoordinateXY& p, + const geom::CoordinateSequence& ring); + + /// Semantically equal to the above, just different args encoding + static geom::Location locatePointInRing(const geom::CoordinateXY& p, + const std::vector& ring); + + static geom::Location locatePointInRing(const geom::CoordinateXY& p, + const geom::Curve& ring); + + RayCrossingCounter(const geom::CoordinateXY& p_point) + : point(p_point), + crossingCount(0), + isPointOnSegment(false) + { } + + /** \brief + * Counts a segment + * + * @param p1 an endpoint of the segment + * @param p2 another endpoint of the segment + */ + void countSegment(const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2); + + void countArc(const geom::CoordinateXY& p1, + const geom::CoordinateXY& p2, + const geom::CoordinateXY& p3); + + /** \brief + * Counts all segments or arcs in the sequence + */ + void processSequence(const geom::CoordinateSequence& seq, bool isLinear); + + /** \brief + * Reports whether the point lies exactly on one of the supplied segments. + * + * This method may be called at any time as segments are processed. + * If the result of this method is `true`, no further segments need + * be supplied, since the result will never change again. + * + * @return `true` if the point lies exactly on a segment + */ + bool + isOnSegment() const + { + return isPointOnSegment; + } + + /** \brief + * Gets the [Location](@ref geom::Location) of the point relative to + * the ring, polygon or multipolygon from which the processed + * segments were provided. + * + * This method only determines the correct location + * if **all** relevant segments must have been processed. + * + * @return the Location of the point + */ + geom::Location getLocation() const; + + /** \brief + * Tests whether the point lies in or on the ring, polygon or + * multipolygon from which the processed segments were provided. + * + * This method only determines the correct location if **all** relevant + * segments must have been processed. + * + * @return `true` if the point lies in or on the supplied polygon + */ + bool isPointInPolygon() const; + + std::size_t getCount() const { return crossingCount; }; + + static bool shouldCountCrossing(const geom::CircularArc& arc, const geom::CoordinateXY& q); + + static std::array + pointsIntersectingHorizontalRay(const geom::CircularArc& arc, const geom::CoordinateXY& origin); + +}; + +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/Rectangle.h b/Sources/geos/include/geos/algorithm/Rectangle.h new file mode 100644 index 0000000..c178653 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/Rectangle.h @@ -0,0 +1,95 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class GeometryFactory; +class Polygon; +} +} + +namespace geos { +namespace algorithm { + +class GEOS_DLL Rectangle { + using CoordinateXY = geos::geom::CoordinateXY; + using GeometryFactory = geos::geom::GeometryFactory; + using LineSegment = geos::geom::LineSegment; + using Polygon = geos::geom::Polygon; + +public: + + /** + * Creates a rectangular {@link Polygon} from a base segment + * defining the position and orientation of one side of the rectangle, and + * three points defining the locations of the line segments + * forming the opposite, left and right sides of the rectangle. + * The base segment and side points must be presented so that the + * rectangle has CW orientation. + * + * The rectangle corners are computed as intersections of + * lines, which generally cannot produce exact values. + * If a rectangle corner is determined to coincide with a side point + * the side point value is used to avoid numerical inaccuracy. + * + * The first side of the constructed rectangle contains the base segment. + * + * @param baseRightPt the right point of the base segment + * @param baseLeftPt the left point of the base segment + * @param oppositePt the point defining the opposite side + * @param leftSidePt the point defining the left side + * @param rightSidePt the point defining the right side + * @param factory the geometry factory to use + * @return the rectangular polygon + */ + static std::unique_ptr + createFromSidePts( + const CoordinateXY& baseRightPt, + const CoordinateXY& baseLeftPt, + const CoordinateXY& oppositePt, + const CoordinateXY& leftSidePt, + const CoordinateXY& rightSidePt, + const GeometryFactory* factory); + +private: + + /** + * Computes the constant C in the standard line equation Ax + By = C + * from A and B and a point on the line. + * + * @param a the X coefficient + * @param b the Y coefficient + * @param p a point on the line + * @return the constant C + */ + static double + computeLineEquationC(double a, double b, const CoordinateXY& p); + + static LineSegment + createLineForStandardEquation(double a, double b, double c); + +}; + + +} // namespace geos::algorithm +} // namespace geos + + diff --git a/Sources/geos/include/geos/algorithm/RobustDeterminant.h b/Sources/geos/include/geos/algorithm/RobustDeterminant.h new file mode 100644 index 0000000..a99f041 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/RobustDeterminant.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/RobustDeterminant.java 1.15 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace algorithm { // geos::algorithm + +/** \brief + * Implements an algorithm to compute the + * sign of a 2x2 determinant for double precision values robustly. + * + * It is a direct translation of code developed by Olivier Devillers. + * + * The original code carries the following copyright notice: + * + *

+ *************************************************************************
+ * The original code carries the following copyright notice:
+ * Author : Olivier Devillers
+ * Olivier.Devillers@sophia.inria.fr
+ * http:/www.inria.fr:/prisme/personnel/devillers/anglais/determinant.html
+ *
+ **************************************************************************
+ *              Copyright (c) 1995  by  INRIA Prisme Project
+ *                  BP 93 06902 Sophia Antipolis Cedex, France.
+ *                           All rights reserved
+ **********************************************************************
+ * 
+ * + */ +class GEOS_DLL RobustDeterminant { +public: + + /** \brief + * Computes the sign of the determinant of the 2x2 matrix + * with the given entries, in a robust way. + * + * @return -1 if the determinant is negative, + * @return 1 if the determinant is positive, + * @return 0 if the determinant is 0. + */ + static int signOfDet2x2(double x1, double y1, double x2, double y2); +}; + +} // namespace geos::algorithm +} // namespace geos + diff --git a/Sources/geos/include/geos/algorithm/construct/ExactMaxInscribedCircle.h b/Sources/geos/include/geos/algorithm/construct/ExactMaxInscribedCircle.h new file mode 100644 index 0000000..c68ddc7 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/construct/ExactMaxInscribedCircle.h @@ -0,0 +1,122 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2020 Martin Davis + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/ExactMaxInscribedCircle.java + * https://github.com/locationtech/jts/commit/8d5ced1b784d232e1eecf5df4b71873e8822336a + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +// #include + + + +namespace geos { +namespace geom { +class CoordinateSequence; +class Geometry; +class Polygon; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * Computes the Maximum Inscribed Circle for some kinds of convex polygons. + * It determines the circle center point by computing Voronoi node points + * and testing them for distance to generating edges. + * This is more precise than iterated approximation, + * and faster for small polygons (such as triangles and convex quadrilaterals). + * + * @author Martin Davis + * + */ +class GEOS_DLL ExactMaxInscribedCircle { + + using CoordinateSequence = geos::geom::CoordinateSequence; + using CoordinateXY = geos::geom::CoordinateXY; + using LineSegment = geos::geom::LineSegment; + using Polygon = geos::geom::Polygon; + +public: + + ExactMaxInscribedCircle(); + + /** + * Tests whether a given geometry is supported by this class. + * Currently only triangles and convex quadrilaterals are supported. + * + * @param geom an areal geometry + * @return true if the geometry shape can be evaluated + */ + static bool isSupported(const geom::Geometry* geom); + + static std::pair computeRadius(const Polygon* polygon); + + +private: + + static bool isTriangle(const Polygon* polygon); + + static bool isQuadrilateral(const Polygon* polygon); + + static std::pair computeTriangle(const CoordinateSequence* ring); + + /** + * The Voronoi nodes of a convex polygon occur at the intersection point + * of two bisectors of each triplet of edges. + * The Maximum Inscribed Circle center is the node + * is the farthest distance from the generating edges. + * For a quadrilateral there are 4 distinct edge triplets, + * at each edge with its adjacent edges. + * + * @param ring the polygon ring + * @return an array containing the incircle center and radius points + */ + static std::pair computeConvexQuadrilateral(const CoordinateSequence* ring); + + static std::array computeBisectors(const CoordinateSequence* ptsCW, + double diameter); + + static CoordinateXY nearestEdgePt(const CoordinateSequence* ring, + const CoordinateXY& pt); + + static LineSegment computeConvexBisector(const CoordinateSequence* pts, + std::size_t index, double len); + + static bool isConvex(const Polygon* polygon); + + static bool isConvex(const CoordinateSequence* ring); + + static bool isConcave(const CoordinateXY& p0, + const CoordinateXY& p1, + const CoordinateXY& p2); + + static bool isPointInConvexRing(const CoordinateSequence* ringCW, + const CoordinateXY& p); + +}; + + +} // geos::algorithm::construct +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h b/Sources/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h new file mode 100644 index 0000000..7509973 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/construct/IndexedDistanceToPoint.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * \brief Computes the distance between a point and a geometry + * (which may be a collection containing any type of geometry). + * + * Also computes the pair of points containing the input + * point and the nearest point on the geometry. + * + * \author Martin Davis + */ +class GEOS_DLL IndexedDistanceToPoint { + using Geometry = geos::geom::Geometry; + using Point = geos::geom::Point; + using IndexedFacetDistance = geos::operation::distance::IndexedFacetDistance; + +public: + /** + * \brief Creates an instance to find the distance from points to a geometry. + * + * \param geom the geometry to compute distances to + */ + IndexedDistanceToPoint(const Geometry& geom); + + /** + * \brief Computes the distance from the base geometry to the given point. + * + * \param pt the point to compute the distance to + * + * \return the computed distance + */ + double distance(const Point& pt); + + /** + * \brief Computes the nearest point on the geometry to the point. + * + * The first location lies on the geometry, + * and the second location is the provided point. + * + * \param pt the point to compute the nearest point for + * + * \return the points that are nearest + */ + std::unique_ptr nearestPoints(const Point& pt); + +private: + void init(); + + bool isInArea(const Point& pt); + + //-- members + const Geometry& targetGeometry; + std::unique_ptr facetDistance; + std::unique_ptr ptLocator; + +}; + +}}} \ No newline at end of file diff --git a/Sources/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h b/Sources/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h new file mode 100644 index 0000000..59325da --- /dev/null +++ b/Sources/geos/include/geos/algorithm/construct/IndexedPointInPolygonsLocator.h @@ -0,0 +1,79 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/IndexedDistanceToPoint.java + * https://github.com/locationtech/jts/commit/d92f783163d9440fcc10c729143787bf7b9fe8f9 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * \brief Determines the location of a point in the polygonal elements of a geometry. + * + * Uses spatial indexing to provide efficient performance. + * + * \author Martin Davis + */ +class GEOS_DLL IndexedPointInPolygonsLocator { + using Geometry = geos::geom::Geometry; + using CoordinateXY = geos::geom::CoordinateXY; + using Location = geos::geom::Location; + template + using TemplateSTRtree = geos::index::strtree::TemplateSTRtree; + using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator; + +public: + /** + * \brief Creates an instance to locate a point in polygonal elements. + * + * \param geom the geometry to locate in + */ + IndexedPointInPolygonsLocator(const Geometry& geom); + + /** \brief + * Determines the [Location](@ref geom::Location) of a point in + * the polygonal elements of a + * [Geometry](@ref geom::Geometry). + * + * @param p the point to test + * @return the location of the point in the geometry + */ + Location locate(const CoordinateXY* /*const*/ p); + +private: + void init(); + + // Declare type as noncopyable + IndexedPointInPolygonsLocator(const IndexedPointInPolygonsLocator& other) = delete; + IndexedPointInPolygonsLocator& operator=(const IndexedPointInPolygonsLocator& rhs) = delete; + + //-- members + const Geometry& geom; + bool isInitialized; + TemplateSTRtree index; + std::vector> locators; +}; + +}}} \ No newline at end of file diff --git a/Sources/geos/include/geos/algorithm/construct/LargestEmptyCircle.h b/Sources/geos/include/geos/algorithm/construct/LargestEmptyCircle.h new file mode 100644 index 0000000..0f4b6d0 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/construct/LargestEmptyCircle.h @@ -0,0 +1,239 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/LargestEmptyCircle.java + * https://github.com/locationtech/jts/commit/98274a7ea9b40651e9de6323dc10fb2cac17a245 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Geometry; +class GeometryFactory; +class LineString; +class Point; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** +* Constructs the Largest Empty Circle for a set of obstacle geometries, +* up to a specified tolerance. +* The obstacles may be any combination of point, linear and polygonal geometries. +* +* The Largest Empty Circle (LEC) is the largest circle +* whose interior does not intersect with any obstacle +* and whose center lies within a polygonal boundary. +* The circle center is the point in the interior of the boundary +* which has the farthest distance from the obstacles +* (up to the accuracy of the distance tolerance). +* The circle itself is determined by the center point +* and a point lying on an obstacle determining the circle radius. +* +* The polygonal boundary may be supplied explicitly. +* If it is not specified the convex hull of the obstacles is used as the boundary. +* +* To compute an LEC which lies wholly within +* a polygonal boundary, include the boundary of the polygon(s) as an obstacle. +* +* The implementation uses a successive-approximation technique +* over a grid of square cells covering the obstacles and boundary. +* The grid is refined using a branch-and-bound algorithm. Point +* containment and distance are computed in a performant way +* by using spatial indexes. +* +* \author Martin Davis +*/ +class GEOS_DLL LargestEmptyCircle { + using IndexedFacetDistance = geos::operation::distance::IndexedFacetDistance; + +public: + + /** + * Creates a new instance of a Largest Empty Circle construction. + * The obstacles may be any collection of points, lines and polygons. + * The constructed circle center lies within the convex hull of the obstacles. + * + * @param p_obstacles a geometry representing the obstacles + * @param p_tolerance the distance tolerance for computing the circle center point + */ + LargestEmptyCircle(const geom::Geometry* p_obstacles, double p_tolerance); + + /** + * Creates a new instance of a Largest Empty Circle construction, + * interior-disjoint to a set of obstacle geometries + * and having its center within a polygonal boundary. + * The obstacles may be any collection of points, lines and polygons. + * If the boundary is null or empty the convex hull + * of the obstacles is used as the boundary. + * + * @param p_obstacles a geometry representing the obstacles + * @param p_boundary a polygonal geometry to contain the LEC center + * @param p_tolerance the distance tolerance for computing the circle center point + */ + LargestEmptyCircle(const geom::Geometry* p_obstacles, const geom::Geometry* p_boundary, double p_tolerance); + + ~LargestEmptyCircle() = default; + + /** + * Computes the center point of the Largest Empty Circle + * within a set of obstacles, up to a given tolerance distance. + * The obstacles may be any collection of points, lines and polygons. + * + * @param p_obstacles a geometry representing the obstacles + * @param p_tolerance the distance tolerance for computing the center point + * @return the center point of the Largest Empty Circle + */ + static std::unique_ptr getCenter(const geom::Geometry* p_obstacles, double p_tolerance); + + /** + * Computes a radius line of the Largest Empty Circle + * within a set of obstacles, up to a given distance tolerance. + * The obstacles may be any collection of points, lines and polygons. + * + * @param p_obstacles a geometry representing the obstacles + * @param p_tolerance the distance tolerance for computing the center point + * @return a line from the center of the circle to a point on the edge + */ + static std::unique_ptr getRadiusLine(const geom::Geometry* p_obstacles, double p_tolerance); + + std::unique_ptr getCenter(); + std::unique_ptr getRadiusPoint(); + std::unique_ptr getRadiusLine(); + + +private: + + /* private members */ + double tolerance; + const geom::Geometry* obstacles; + std::unique_ptr boundary; + const geom::GeometryFactory* factory; + geom::Envelope gridEnv; + bool done; + std::unique_ptr boundaryPtLocater; + IndexedDistanceToPoint obstacleDistance; + std::unique_ptr boundaryDistance; + geom::CoordinateXY centerPt; + geom::CoordinateXY radiusPt; + + /** + * Computes the signed distance from a point to the constraints + * (obstacles and boundary). + * Points outside the boundary polygon are assigned a negative distance. + * Their containing cells will be last in the priority queue + * (but will still end up being tested since they may be refined). + * + * @param c the point to compute the distance for + * @return the signed distance to the constraints (negative indicates outside the boundary) + */ + double distanceToConstraints(const geom::Coordinate& c); + double distanceToConstraints(double x, double y); + void initBoundary(); + void compute(); + + /* private class */ + class Cell { + private: + static constexpr double SQRT2 = 1.4142135623730951; + double x; + double y; + double hSize; + double distance; + double maxDist; + + public: + Cell(double p_x, double p_y, double p_hSize, double p_distanceToConstraints) + : x(p_x) + , y(p_y) + , hSize(p_hSize) + , distance(p_distanceToConstraints) + , maxDist(p_distanceToConstraints + (p_hSize*SQRT2)) + {}; + + geom::Envelope getEnvelope() const + { + geom::Envelope env(x-hSize, x+hSize, y-hSize, y+hSize); + return env; + } + + bool isFullyOutside() const + { + return maxDist < 0.0; + } + bool isOutside() const + { + return distance < 0.0; + } + double getMaxDistance() const + { + return maxDist; + } + double getDistance() const + { + return distance; + } + double getHSize() const + { + return hSize; + } + double getX() const + { + return x; + } + double getY() const + { + return y; + } + bool operator< (const Cell& rhs) const + { + return maxDist < rhs.maxDist; + } + bool operator> (const Cell& rhs) const + { + return maxDist > rhs.maxDist; + } + bool operator==(const Cell& rhs) const + { + return maxDist == rhs.maxDist; + } + }; + + bool mayContainCircleCenter(const Cell& cell, const Cell& farthestCell); + void createInitialGrid(const geom::Envelope* env, std::priority_queue& cellQueue); + Cell createCentroidCell(const geom::Geometry* geom); + +}; + + +} // geos::algorithm::construct +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/construct/MaximumInscribedCircle.h b/Sources/geos/include/geos/algorithm/construct/MaximumInscribedCircle.h new file mode 100644 index 0000000..1a71fb0 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/construct/MaximumInscribedCircle.h @@ -0,0 +1,290 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2020 Martin Davis + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/construct/MaximumInscribedCircle.java + * https://github.com/locationtech/jts/commit/f0b9a808bdf8a973de435f737e37b7a221e231cb + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + + + +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Geometry; +class GeometryFactory; +class LineString; +class Point; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace construct { // geos::algorithm::construct + +/** + * Constructs the Maximum Inscribed Circle for a + * polygonal Geometry, up to a specified tolerance. + * The Maximum Inscribed Circle is determined by a point in the interior of the area + * which has the farthest distance from the area boundary, + * along with a boundary point at that distance. + * + * In the context of geography the center of the Maximum Inscribed Circle + * is known as the **Pole of Inaccessibility**. + * A cartographic use case is to determine a suitable point + * to place a map label within a polygon. + * + * The radius length of the Maximum Inscribed Circle is a + * measure of how "narrow" a polygon is. It is the + * distance at which the negative buffer becomes empty. + * + * The class supports testing whether a polygon is "narrower" + * than a specified distance via + * isRadiusWithin(Geometry, double) or + * isRadiusWithin(double). + * Testing for the maximum radius is generally much faster + * than computing the actual radius value, since short-circuiting + * is used to limit the approximation iterations. + * + * The class supports polygons with holes and multipolygons. + * + * The implementation uses a successive-approximation technique + * over a grid of square cells covering the area geometry. + * The grid is refined using a branch-and-bound algorithm. + * Point containment and distance are computed in a performant + * way by using spatial indexes. + * + * Future Enhancements + * + * * Support a polygonal constraint on placement of center point, + * for example to produce circle-packing constructions, + * or support multiple labels. + * + * @author Martin Davis + * + */ +class GEOS_DLL MaximumInscribedCircle { + + using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator; + using IndexedFacetDistance = geos::operation::distance::IndexedFacetDistance; + +public: + + MaximumInscribedCircle(const geom::Geometry* polygonal, double tolerance); + ~MaximumInscribedCircle() = default; + + /** + * Gets the center point of the maximum inscribed circle + * (up to the tolerance distance). + * + * @return the center point of the maximum inscribed circle + */ + std::unique_ptr getCenter(); + + /** + * Gets a point defining the radius of the Maximum Inscribed Circle. + * This is a point on the boundary which is + * nearest to the computed center of the Maximum Inscribed Circle. + * The line segment from the center to this point + * is a radius of the constructed circle, and this point + * lies on the boundary of the circle. + * + * @return a point defining the radius of the Maximum Inscribed Circle + */ + std::unique_ptr getRadiusPoint(); + + /** + * Gets a line representing a radius of the Largest Empty Circle. + * + * @return a 2-point line from the center of the circle to a point on the edge + */ + std::unique_ptr getRadiusLine(); + + /** + * Tests if the radius of the maximum inscribed circle + * is no longer than the specified distance. + * This method determines the distance tolerance automatically + * as a fraction of the maxRadius value. + * After this method is called the center and radius + * points provide locations demonstrating where + * the radius exceeds the specified maximum. + * + * @param maxRadius the (non-negative) radius value to test + * @return true if the max in-circle radius is no longer than the max radius + */ + bool isRadiusWithin(double maxRadius); + + /** + * Computes the center point of the Maximum Inscribed Circle + * of a polygonal geometry, up to a given tolerance distance. + * + * @param polygonal a polygonal geometry + * @param tolerance the distance tolerance for computing the center point + * @return the center point of the maximum inscribed circle + */ + static std::unique_ptr getCenter(const geom::Geometry* polygonal, double tolerance); + + /** + * Computes a radius line of the Maximum Inscribed Circle + * of a polygonal geometry, up to a given tolerance distance. + * + * @param polygonal a polygonal geometry + * @param tolerance the distance tolerance for computing the center point + * @return a line from the center to a point on the circle + */ + static std::unique_ptr getRadiusLine(const geom::Geometry* polygonal, double tolerance); + + /** + * Tests if the radius of the maximum inscribed circle + * is no longer than the specified distance. + * This method determines the distance tolerance automatically + * as a fraction of the maxRadius value. + * + * @param polygonal a polygonal geometry + * @param maxRadius the radius value to test + * @return true if the max in-circle radius is no longer than the max radius + */ + static bool isRadiusWithin(const geom::Geometry* polygonal, double maxRadius); + + /** + * Computes the maximum number of iterations allowed. + * Uses a heuristic based on the area of the input geometry + * and the tolerance distance. + * The number of tolerance-sized cells that cover the input geometry area + * is computed, times a safety factor. + * This prevents massive numbers of iterations and created cells + * for casees where the input geometry has extremely small area + * (e.g. is very thin). + * + * @param geom the input geometry + * @param toleranceDist the tolerance distance + * @return the maximum number of iterations allowed + */ + static std::size_t computeMaximumIterations(const geom::Geometry* geom, double toleranceDist); + +private: + + /* private members */ + const geom::Geometry* inputGeom; + std::unique_ptr inputGeomBoundary; + double tolerance; + IndexedFacetDistance indexedDistance; + IndexedPointInAreaLocator ptLocator; + const geom::GeometryFactory* factory; + bool done; + geom::CoordinateXY centerPt; + geom::CoordinateXY radiusPt; + double maximumRadius = -1; + + /* private constant */ + static constexpr double MAX_RADIUS_FRACTION = 0.0001; + + /* private methods */ + double distanceToBoundary(const geom::Point& pt); + double distanceToBoundary(double x, double y); + void compute(); + void computeApproximation(); + void createResult(const geom::CoordinateXY& c, const geom::CoordinateXY& r); + + /* private class */ + class Cell { + private: + static constexpr double SQRT2 = 1.4142135623730951; + double x; + double y; + double hSize; + double distance; + double maxDist; + + public: + Cell(double p_x, double p_y, double p_hSize, double p_distanceToBoundary) + : x(p_x) + , y(p_y) + , hSize(p_hSize) + , distance(p_distanceToBoundary) + , maxDist(p_distanceToBoundary+(p_hSize*SQRT2)) + {}; + + geom::Envelope getEnvelope() const + { + geom::Envelope env(x-hSize, x+hSize, y-hSize, y+hSize); + return env; + } + + double getMaxDistance() const + { + return maxDist; + } + double getDistance() const + { + return distance; + } + double getHSize() const + { + return hSize; + } + double getX() const + { + return x; + } + double getY() const + { + return y; + } + + bool operator< (const Cell& rhs) const + { + return maxDist < rhs.maxDist; + } + + bool operator> (const Cell& rhs) const + { + return maxDist > rhs.maxDist; + } + + bool operator==(const Cell& rhs) const + { + return maxDist == rhs.maxDist; + } + + /** + * The Cell priority queue is sorted by the natural order of maxDistance. + * std::priority_queue sorts with largest first, + * which is what is needed for this algorithm. + */ + using CellQueue = std::priority_queue; + }; + + void createInitialGrid(const geom::Envelope* env, Cell::CellQueue& cellQueue); + Cell createInteriorPointCell(const geom::Geometry* geom); + +}; + + +} // geos::algorithm::construct +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/distance/DiscreteFrechetDistance.h b/Sources/geos/include/geos/algorithm/distance/DiscreteFrechetDistance.h new file mode 100644 index 0000000..819a4b1 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/distance/DiscreteFrechetDistance.h @@ -0,0 +1,536 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2021 Felix Obermaier + * Copyright (c) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +class CoordinateSequence; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace distance { // geos::algorithm::distance + +/** \brief + * The Fréchet distance is a measure of similarity between curves. Thus, it can + * be used like the Hausdorff distance. + * + * An analogy for the Fréchet distance taken from + * + * Computing Discrete Fréchet Distance + * + *
A man is walking a dog on a leash: the man can move + * on one curve, the dog on the other; both may vary their + * speed, but backtracking is not allowed.
+ * + * Its metric is better than the Hausdorff distance + * because it takes the directions of the curves into account. + * It is possible that two curves have a small Hausdorff but a large + * Fréchet distance. + * + * This implementation is base on the following optimized Fréchet distance algorithm: + * Thomas Devogele, Maxence Esnault, Laurent Etienne. Distance discrète de Fréchet optimisée. Spatial + * Analysis and Geomatics (SAGEO), Nov 2016, Nice, France. hal-02110055 + * + * Several matrix storage implementations are provided + * + * * Fréchet distance + * * + * Computing Discrete Fréchet Distance + * * Distance discrète de Fréchet optimisée + * * + * Fast Discrete Fréchet Distance + * + * @see DiscreteHausdorffDistance + */ +class GEOS_DLL DiscreteFrechetDistance { + + using DistanceToPairMap = std::unordered_map>; + +public: + + /** + * Creates an instance of this class using the provided geometries. + * + * @param geom0 a geometry + * @param geom1 a geometry + */ + DiscreteFrechetDistance(const geom::Geometry& geom0, const geom::Geometry& geom1) + : g0(geom0) + , g1(geom1) {}; + + + /** + * Abstract base class for storing 2d matrix data + */ + class GEOS_DLL MatrixStorage { + + public: + + std::size_t m_numRows; + std::size_t m_numCols; + double m_defaultValue; + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + MatrixStorage(std::size_t numRows, std::size_t numCols, double defaultValue) + : m_numRows(numRows) + , m_numCols(numCols) + , m_defaultValue(defaultValue) {}; + + virtual ~MatrixStorage() = 0; + + /** + * Gets the matrix value at i, j + * @param i the row index + * @param j the column index + * @return The matrix value at i, j + */ + virtual double get(std::size_t i, std::size_t j) const = 0; + + /** + * Sets the matrix value at i, j + * @param i the row index + * @param j the column index + * @param value The matrix value to set at i, j + */ + virtual void set(std::size_t i, std::size_t j, double value) = 0; + + /** + * Gets a flag indicating if the matrix has a set value, e.g. one that is different + * than MatrixStorage defaultValue. + * @param i the row index + * @param j the column index + * @return a flag indicating if the matrix has a set value + */ + virtual bool isValueSet(std::size_t i, std::size_t j) const = 0; + }; + + + /** + * Straight forward implementation of a rectangular matrix + */ + class RectMatrix : public MatrixStorage { + + private: + std::vector m_matrix; + + public: + + /** + * Creates an instance of this matrix using the given number of rows and columns. + * A default value can be specified + * + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue A default value + */ + RectMatrix(std::size_t numRows, std::size_t numCols, double defaultValue) + : MatrixStorage(numRows, numCols, defaultValue) + { + m_matrix.resize(numRows * numCols, defaultValue); + }; + + double get(std::size_t i, std::size_t j) const override { + return m_matrix[i * m_numCols + j]; + }; + + void set(std::size_t i, std::size_t j, double value) override { + m_matrix[i * m_numCols + j] = value; + }; + + bool isValueSet(std::size_t i, std::size_t j) const override { + return get(i, j) != m_defaultValue; + }; + }; + + + /** + * A matrix implementation that adheres to the + * + * Compressed sparse row format. + * Note: Unfortunately not as fast as hoped. + */ + class CsrMatrix : public MatrixStorage { + + private: + std::vector m_v; + std::vector m_ri; + std::vector m_ci; + + public: + + CsrMatrix(std::size_t numRows, std::size_t numCols, double defaultValue, std::size_t expectedValues) + : MatrixStorage(numRows, numCols, defaultValue) + { + m_v.resize(expectedValues); + m_ci.resize(expectedValues); + m_ri.resize(numRows + 1); + }; + + CsrMatrix(std::size_t numRows, std::size_t numCols, double defaultValue) + : CsrMatrix(numRows, numCols, defaultValue, expectedValuesHeuristic(numRows, numCols)) + {}; + + /** + * Computes an initial value for the number of expected values + * @param numRows the number of rows + * @param numCols the number of columns + * @return the expected number of values in the sparse matrix + */ + static std::size_t expectedValuesHeuristic(std::size_t numRows, std::size_t numCols) { + std::size_t max = std::max(numRows, numCols); + return max * max / 10; + }; + + /** + * A work-alike for Java Arrays.binarySearch(), used + * for searching ordered arrays. This variant searches + * for the key value within a bounded subset of the array. + * @param vec the vector to search + * @param fromIndex the lower bound index for the search + * @param toIndex the upper bound index for the search + * @param key the value to hunt for + * @return a pair with the first item being true if the key + * was found and false otherwise, and the second being the + * index of the key found, or the insertion point of the + * key if not found + */ + std::pair + cppBinarySearch(const std::vector& vec, std::size_t fromIndex, std::size_t toIndex, std::size_t key) const { + // Return not found on invalid input + if (fromIndex > toIndex || toIndex > vec.size()) + return {false, 0}; + + // Define the iterators for the sub-range + auto first_it = vec.begin() + static_cast(fromIndex); + auto last_it = vec.begin() + static_cast(toIndex); + + // Perform the binary search using std::lower_bound + auto it = std::lower_bound(first_it, last_it, key); + + // Calculate the index relative to the *beginning of the original vector* + auto result_index = std::distance(vec.begin(), it); + + // Determine if the element was found and return the appropriate value + if (it != last_it && *it == key) { + // Element found, return its index + return {true, result_index}; + } else { + // Element not found, return the insertion point + return {false, result_index}; + } + }; + + std::pair indexOf(std::size_t i, std::size_t j) const { + std::size_t cLow = m_ri[i]; + std::size_t cHigh = m_ri[i+1]; + if (cHigh <= cLow) return {false, cLow}; + return cppBinarySearch(m_ci, cLow, cHigh, j); + }; + + double get(std::size_t i, std::size_t j) const override { + // get the index in the vector + std::pair vi = indexOf(i, j); + // if the vector index is negative, return default value + if (!vi.first) + return m_defaultValue; + + return m_v[vi.second]; + }; + + void set(std::size_t i, std::size_t j, double value) override { + + // get the index in the vector + std::pair vi = indexOf(i, j); + + // do we already have a value? + if (!vi.first) + { + // no, we don't, we need to ensure space! + ensureCapacity(m_ri[m_numRows] + 1); + + // update row indices + for (std::size_t ii = i + 1; ii <= m_numRows; ii++) + m_ri[ii] += 1; + + // move and update column indices, move values + std::size_t viv = vi.second; + for (std::size_t ii = m_ri[m_numRows]; ii > viv; ii--) { + m_ci[ii] = m_ci[ii - 1]; + m_v[ii] = m_v[ii - 1]; + } + + // insert column index + m_ci[viv] = j; + } + + // set the new value + m_v[vi.second] = value; + }; + + bool isValueSet(std::size_t i, std::size_t j) const override { + auto r = indexOf(i, j); + return r.first; + }; + + /** + * Ensures that the column index vector (m_ci) and value vector (m_v) are sufficiently large. + * @param required the number of items to store in the matrix + */ + void ensureCapacity(std::size_t required) { + if (required < m_v.size()) + return; + + std::size_t increment = std::max(m_numRows, m_numCols); + m_v.resize(m_v.size() + increment); + m_ci.resize(m_v.size() + increment); + }; + }; + + + /** + * A sparse matrix based on java's HashMap. + */ + class HashMapMatrix : public MatrixStorage { + + private: + std::unordered_map m_matrix; + + public: + + /** + * Creates an instance of this class + * @param numRows the number of rows + * @param numCols the number of columns + * @param defaultValue a default value + */ + HashMapMatrix(std::size_t numRows, std::size_t numCols, double defaultValue) + : MatrixStorage(numRows, numCols, defaultValue) + {}; + + double get(std::size_t i, std::size_t j) const override { + static constexpr std::size_t halfsz = sizeof(std::size_t) * 4; + std::size_t key = i << halfsz | j; + auto it_found = m_matrix.find(key); + if (it_found != m_matrix.end()) { + return (*it_found).second; + } + else { + return m_defaultValue; + } + }; + + void set(std::size_t i, std::size_t j, double value) override { + static constexpr std::size_t halfsz = sizeof(std::size_t) * 4; + std::size_t key = i << halfsz | j; + m_matrix[key] = value; + }; + + bool isValueSet(std::size_t i, std::size_t j) const override { + static constexpr std::size_t halfsz = sizeof(std::size_t) * 4; + std::size_t key = i << halfsz | j; + auto it_found = m_matrix.find(key); + return it_found != m_matrix.end(); + }; + }; + + + /** + * Computes the Discrete Fréchet Distance between two Geometrys + * using a Cartesian distance computation function. + * + * @param geom0 the 1st geometry + * @param geom1 the 2nd geometry + * @return the cartesian distance between geom0 and geom1 + */ + static double distance(const geom::Geometry& geom0, const geom::Geometry& geom1); + + /** + * Computes the Discrete Fréchet Distance between two Geometrys + * using a Cartesian distance computation function. + * + * @param geom0 the 1st geometry + * @param geom1 the 2nd geometry + * @param densityFrac the fraction of edge length to target + * when densifying edges + * @return the cartesian distance between geom0 and geom1 + */ + static double distance(const geom::Geometry& geom0, const geom::Geometry& geom1, double densityFrac); + + /** + * Gets the pair of {@link geom::Coordinate}s at which the distance is obtained. + * + * @return the pair of Coordinates at which the distance is obtained + */ + std::array getCoordinates(); + + void setDensifyFraction(double dFrac); + + +private: + + // Members + const geom::Geometry& g0; + const geom::Geometry& g1; + std::unique_ptr ptDist; + double densifyFraction = -1.0; + + /** + * Computes the {@code Discrete Fréchet Distance} between the input geometries + * + * @return the Discrete Fréchet Distance + */ + /* private */ + double distance(); + + /* + * Utility method to ape Java behaviour + */ + void putIfAbsent( + DistanceToPairMap& distanceToPair, + double key, std::array val); + + /** + * Creates a matrix to store the computed distances. + * + * @param rows the number of rows + * @param cols the number of columns + * @return a matrix storage + */ + static std::unique_ptr createMatrixStorage( + std::size_t rows, std::size_t cols); + + /** + * Computes the Fréchet Distance for the given distance matrix. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param diagonal an array of alternating col/row index values for the diagonal of the distance matrix + * @param distances the distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance + * + */ + static std::unique_ptr computeFrechet( + const geom::CoordinateSequence& coords0, + const geom::CoordinateSequence& coords1, + std::vector& diagonal, + MatrixStorage& distances, + DistanceToPairMap& distanceToPair); + + /** + * Returns the minimum distance at the corner ({@code i, j}). + * + * @param matrix A sparse matrix + * @param i the column index + * @param j the row index + * @return the minimum distance + */ + /* private */ + static double getMinDistanceAtCorner( + MatrixStorage& matrix, std::size_t i, std::size_t j); + + /** + * Computes relevant distances between pairs of {@link Coordinate}s for the + * computation of the {@code Discrete Fréchet Distance}. + * + * @param coords0 an array of {@code Coordinate}s. + * @param coords1 an array of {@code Coordinate}s. + * @param diagonal an array of alternating col/row index values for the diagonal of the distance matrix + * @param distances the distance matrix + * @param distanceToPair a lookup for coordinate pairs based on a distance + */ + void computeCoordinateDistances( + const geom::CoordinateSequence& coords0, const geom::CoordinateSequence& coords1, + std::vector& diagonal, + MatrixStorage& distances, DistanceToPairMap& distanceToPair); + + /** + * Computes the indices for the diagonal of a {@code numCols x numRows} grid + * using the + * Bresenham line algorithm. + * + * @param numCols the number of columns + * @param numRows the number of rows + * @return a packed array of column and row indices + */ + static std::vector bresenhamDiagonal( + std::size_t numCols, std::size_t numRows); + + /** + * @param geom the geometry to densify + * @param densifyFrac the amount to split up edges + * @return a coordinate sequence of every vertex and all introduced + * densified vertices for the geometry + */ + static std::unique_ptr getDensifiedCoordinates( + const geom::Geometry& geom, double densifyFrac); + + + class DensifiedCoordinatesFilter : public geom::CoordinateSequenceFilter { + + public: + + DensifiedCoordinatesFilter(double fraction, geom::CoordinateSequence& coords) + : m_numSubSegs(static_cast(util::round(1.0 / fraction))) + , m_coords(coords) + {}; + + void filter_ro( + const geom::CoordinateSequence& seq, + std::size_t index) override; + + bool isGeometryChanged() const override { + return false; + } + + bool isDone() const override { + return false; + } + + private: + + uint32_t m_numSubSegs; + geom::CoordinateSequence& m_coords; + + }; + + + +}; // DiscreteFrechetDistance + +} // geos::algorithm::distance +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h b/Sources/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h new file mode 100644 index 0000000..be136ed --- /dev/null +++ b/Sources/geos/include/geos/algorithm/distance/DiscreteHausdorffDistance.h @@ -0,0 +1,252 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/distance/DiscreteHausdorffDistance.java 1.5 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include // for composition +#include // for inlines +#include // for inlines +#include // for inlines +#include // for inheritance +#include // for inheritance + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +//class CoordinateSequence; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace distance { // geos::algorithm::distance + +/** \brief + * An algorithm for computing a distance metric + * which is an approximation to the Hausdorff Distance + * based on a discretization of the input {@link geom::Geometry}. + * + * The algorithm computes the Hausdorff distance restricted to discrete points + * for one of the geometries. + * The points can be either the vertices of the geometries (the default), + * or the geometries with line segments densified by a given fraction. + * Also determines two points of the Geometries which are separated by the + * computed distance. + * + * This algorithm is an approximation to the standard Hausdorff distance. + * Specifically, + *
+ *    for all geometries a, b:    DHD(a, b) <= HD(a, b)
+ * 
+ * The approximation can be made as close as needed by densifying the + * input geometries. + * In the limit, this value will approach the true Hausdorff distance: + *
+ *    DHD(A, B, densifyFactor) -> HD(A, B) as densifyFactor -> 0.0
+ * 
+ * The default approximation is exact or close enough for a large subset of + * useful cases. + * Examples of these are: + * + * - computing distance between Linestrings that are roughly parallel to + * each other, and roughly equal in length. This occurs in matching + * linear networks. + * - Testing similarity of geometries. + * + * An example where the default approximation is not close is: + *
+ *   A = LINESTRING (0 0, 100 0, 10 100, 10 100)
+ *   B = LINESTRING (0 100, 0 10, 80 10)
+ *
+ *   DHD(A, B) = 22.360679774997898
+ *   HD(A, B) ~= 47.8
+ * 
+ */ +class GEOS_DLL DiscreteHausdorffDistance { +public: + + static double distance(const geom::Geometry& g0, + const geom::Geometry& g1); + + static double distance(const geom::Geometry& g0, + const geom::Geometry& g1, double densifyFrac); + + DiscreteHausdorffDistance(const geom::Geometry& p_g0, + const geom::Geometry& p_g1) + : + g0(p_g0), + g1(p_g1), + ptDist(), + densifyFrac(0.0) + {} + + /** + * Sets the fraction by which to densify each segment. + * Each segment will be (virtually) split into a number of equal-length + * subsegments, whose fraction of the total length is closest + * to the given fraction. + * + * @param dFrac + */ + void setDensifyFraction(double dFrac); + + double + distance() + { + compute(g0, g1); + return ptDist.getDistance(); + } + + double + orientedDistance() + { + computeOrientedDistance(g0, g1, ptDist); + return ptDist.getDistance(); + } + + const std::array + getCoordinates() const + { + return ptDist.getCoordinates(); + } + + class MaxPointDistanceFilter : public geom::CoordinateFilter { + public: + MaxPointDistanceFilter(const geom::Geometry& p_geom) + : + geom(p_geom) + {} + + void + filter_ro(const geom::CoordinateXY* pt) override + { + minPtDist.initialize(); + DistanceToPoint::computeDistance(geom, *pt, + minPtDist); + maxPtDist.setMaximum(minPtDist); + } + + const PointPairDistance& + getMaxPointDistance() const + { + return maxPtDist; + } + + private: + PointPairDistance maxPtDist; + PointPairDistance minPtDist; + DistanceToPoint euclideanDist; + const geom::Geometry& geom; + + // Declare type as noncopyable + MaxPointDistanceFilter(const MaxPointDistanceFilter& other); + MaxPointDistanceFilter& operator=(const MaxPointDistanceFilter& rhs); + }; + + class MaxDensifiedByFractionDistanceFilter + : public geom::CoordinateSequenceFilter { + public: + + MaxDensifiedByFractionDistanceFilter( + const geom::Geometry& p_geom, double fraction) + : + geom(p_geom), + // Validity of the cast to size_t has been verified in setDensifyFraction() + numSubSegs(std::size_t(util::round(1.0 / fraction))) + { + } + + void filter_ro(const geom::CoordinateSequence& seq, + std::size_t index) override; + + bool + isGeometryChanged() const override + { + return false; + } + + bool + isDone() const override + { + return false; + } + + const PointPairDistance& + getMaxPointDistance() const + { + return maxPtDist; + } + + private: + PointPairDistance maxPtDist; + PointPairDistance minPtDist; + const geom::Geometry& geom; + std::size_t numSubSegs; // = 0; + + // Declare type as noncopyable + MaxDensifiedByFractionDistanceFilter(const MaxDensifiedByFractionDistanceFilter& other); + MaxDensifiedByFractionDistanceFilter& operator=(const MaxDensifiedByFractionDistanceFilter& rhs); + }; + +private: + + void + compute(const geom::Geometry& p_g0, + const geom::Geometry& p_g1) + { + computeOrientedDistance(p_g0, p_g1, ptDist); + computeOrientedDistance(p_g1, p_g0, ptDist); + } + + void computeOrientedDistance(const geom::Geometry& discreteGeom, + const geom::Geometry& geom, + PointPairDistance& ptDist); + + const geom::Geometry& g0; + + const geom::Geometry& g1; + + PointPairDistance ptDist; + + /// Value of 0.0 indicates that no densification should take place + double densifyFrac; // = 0.0; + + // Declare type as noncopyable + DiscreteHausdorffDistance(const DiscreteHausdorffDistance& other) = delete; + DiscreteHausdorffDistance& operator=(const DiscreteHausdorffDistance& rhs) = delete; +}; + +} // geos::algorithm::distance +} // geos::algorithm +} // geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/algorithm/distance/DistanceToPoint.h b/Sources/geos/include/geos/algorithm/distance/DistanceToPoint.h new file mode 100644 index 0000000..108b855 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/distance/DistanceToPoint.h @@ -0,0 +1,72 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/distance/DistanceToPoint.java 1.1 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include // for composition + +namespace geos { +namespace algorithm { +namespace distance { +class PointPairDistance; +} +} +namespace geom { +class Geometry; +class Coordinate; +class LineString; +class Polygon; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace distance { // geos::algorithm::distance + +/** + * Computes the Euclidean distance (L2 metric) from a Point to a Geometry. + * + * Also computes two points which are separated by the distance. + */ +class DistanceToPoint { +public: + + DistanceToPoint() {} + + static void computeDistance(const geom::Geometry& geom, + const geom::CoordinateXY& pt, + PointPairDistance& ptDist); + + static void computeDistance(const geom::LineString& geom, + const geom::CoordinateXY& pt, + PointPairDistance& ptDist); + + static void computeDistance(const geom::LineSegment& geom, + const geom::CoordinateXY& pt, + PointPairDistance& ptDist); + + static void computeDistance(const geom::Polygon& geom, + const geom::CoordinateXY& pt, + PointPairDistance& ptDist); + +}; + +} // geos::algorithm::distance +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/distance/PointPairDistance.h b/Sources/geos/include/geos/algorithm/distance/PointPairDistance.h new file mode 100644 index 0000000..35aa135 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/distance/PointPairDistance.h @@ -0,0 +1,149 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: algorithm/distance/PointPairDistance.java 1.1 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include // for DoubleNotANumber +#include // for inlines + +#include +#include + +namespace geos { +namespace algorithm { // geos::algorithm +namespace distance { // geos::algorithm::distance + +/** + * Contains a pair of points and the distance between them. + * Provides methods to update with a new point pair with + * either maximum or minimum distance. + */ +class PointPairDistance { +public: + + PointPairDistance() + : + distanceSquared(DoubleNotANumber), + isNull(true) + {} + + void + initialize() + { + isNull = true; + } + + void + initialize(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) + { + pt[0] = p0; + pt[1] = p1; + distanceSquared = p0.distanceSquared(p1); + isNull = false; + } + + double + getDistance() const + { + return std::sqrt(distanceSquared); + } + + const std::array& + getCoordinates() const + { + return pt; + } + + const geom::CoordinateXY& + getCoordinate(std::size_t i) const + { + assert(i < pt.size()); + return pt[i]; + } + + void + setMaximum(const PointPairDistance& ptDist) + { + setMaximum(ptDist.pt[0], ptDist.pt[1]); + } + + void + setMaximum(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) + { + if(isNull) { + initialize(p0, p1); + return; + } + double distSq = p0.distanceSquared(p1); + if(distSq > distanceSquared) { + initialize(p0, p1, distSq); + } + } + + void + setMinimum(const PointPairDistance& ptDist) + { + setMinimum(ptDist.pt[0], ptDist.pt[1]); + } + + void + setMinimum(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) + { + if(isNull) { + initialize(p0, p1); + return; + } + double distSq = p0.distanceSquared(p1); + if(distSq < distanceSquared) { + initialize(p0, p1, distSq); + } + } + + bool + getIsNull() + { + return isNull; + } + + /** + * Initializes the points, avoiding recomputing the distance. + * @param p0 + * @param p1 + * @param distSquared the squared distance between p0 and p1 + */ + void + initialize(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + double distSquared) + { + pt[0] = p0; + pt[1] = p1; + distanceSquared = distSquared; + isNull = false; + } + +private: + + std::array pt; + double distanceSquared; + bool isNull; + +}; + +} // geos::algorithm::distance +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/hull/ConcaveHull.h b/Sources/geos/include/geos/algorithm/hull/ConcaveHull.h new file mode 100644 index 0000000..ce9de47 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/hull/ConcaveHull.h @@ -0,0 +1,308 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace geos { +namespace geom { +class Coordinate; +class Geometry; +class GeometryFactory; +} +namespace triangulate { +namespace quadedge { +class Quadedge; +class QuadEdgeSubdivision; +} +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace hull { // geos::algorithm::hull + + +typedef std::priority_queue, HullTri::HullTriCompare> HullTriQueue; + + +/** +* Constructs a concave hull of a set of points. +* The hull is constructed by removing border triangles +* of the Delaunay Triangulation of the points +* as long as their "size" is larger than the target criterion. +* The target criteria are: +* +* * Maximum Edge Length Ratio - determines the Maximum Edge Length +* by a fraction of the difference between +* the longest and shortest edge lengths +* in the Delaunay Triangulation. This normalizes the +* Maximum Edge Length to be scale-independent. +* * Maximum Area Ratio - the ratio of the concave hull area to the convex +* hull area will be no larger than this value +* * Alpha - produces Alpha-shapes, by removing border triangles +* with a circumradius greater than alpha. +* Large values produce the convex hull; a value of 0 +* produces maximum concaveness. +* +* The preferred criterium is the Maximum Edge Length Ratio, since it is +* scale-free and local (so that no assumption needs to be made about the +* total amount of concavity present. +* +* Other length criteria can be used by setting the Maximum Edge Length. +* For example, use a length relative to the longest edge length +* in the Minimum Spanning Tree of the point set. +* Or, use a length derived from the uniformGridEdgeLength() value. +* +* The computed hull is always a single connected geom::Polygon +* (unless it is degenerate, in which case it will be a geom::Point or a geom::LineString). +* This constraint may cause the concave hull to fail to meet the target criteria. +* +* Optionally the concave hull can be allowed +* to contain holes by calling setHolesAllowed(boolean). +* +* @author mdavis +*/ +class GEOS_DLL ConcaveHull { + using Coordinate = geos::geom::Coordinate; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Triangle = geos::geom::Triangle; + using QuadEdge = geos::triangulate::quadedge::QuadEdge; + using QuadEdgeSubdivision = geos::triangulate::quadedge::QuadEdgeSubdivision; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + +public: + + ConcaveHull(const Geometry* geom); + + /** + * Computes the approximate edge length of + * a uniform square grid having the same number of + * points as a geometry and the same area as its convex hull. + * This value can be used to determine a suitable length threshold value + * for computing a concave hull. + * A value from 2 to 4 times the uniform grid length + * seems to produce reasonable results. + * + * @param geom a geometry + * @return the approximate uniform grid length + */ + static double uniformEdgeLength(const Geometry* geom); + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length. + * + * @param geom the input geometry + * @param maxLength the target maximum edge length + * @return the concave hull + */ + static std::unique_ptr concaveHullByLength( + const Geometry* geom, double maxLength); + + static std::unique_ptr concaveHullByLength( + const Geometry* geom, double maxLength, bool isHolesAllowed); + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criteria of maximum edge length ratio. + * The edge length ratio is a fraction of the length difference + * between the longest and shortest edges + * in the Delaunay Triangulation of the input points. + * + * @param geom the input geometry + * @param lengthRatio the target edge length factor + * @return the concave hull + */ + static std::unique_ptr concaveHullByLengthRatio( + const Geometry* geom, double lengthRatio); + + /** + * Computes the concave hull of the vertices in a geometry + * using the target criterion of maximum edge length factor, + * and optionally allowing holes. + * The edge length factor is a fraction of the length difference + * between the longest and shortest edges + * in the Delaunay Triangulation of the input points. + * + * @param geom the input geometry + * @param lengthRatio the target maximum edge length + * @param isHolesAllowed whether holes are allowed in the result + * @return the concave hull + */ + static std::unique_ptr concaveHullByLengthRatio( + const Geometry* geom, + double lengthRatio, + bool isHolesAllowed); + + /** + * Computes the alpha shape of a geometry as a polygon. + * The alpha parameter is the radius of the eroding disc. + * + * @param geom the input geometry + * @param alpha the radius of the eroding disc + * @param isHolesAllowed whether holes are allowed in the result + * @return the alpha shape polygon + */ + static std::unique_ptr alphaShape( + const Geometry* geom, + double alpha, + bool isHolesAllowed); + + /** + * Sets the target maximum edge length for the concave hull. + * The length value must be zero or greater. + * + * * The value 0.0 produces the concave hull of smallest area + * that is still connected. + * * Larger values produce less concave results. + * A value equal or greater than the longest Delaunay Triangulation edge length + * produces the convex hull. + * + * The uniformGridEdgeLength value may be used as + * the basis for estimating an appropriate target maximum edge length. + * + * @param edgeLength a non-negative length + */ + void setMaximumEdgeLength(double edgeLength); + + /** + * Sets the target maximum edge length ratio for the concave hull. + * The edge length ratio is a fraction of the length delta + * between the longest and shortest edges + * in the Delaunay Triangulation of the input points. + * A value of 1.0 produces the convex hull. + * A value of 0.0 produces a concave hull of minimum area + * that is still connected. + * + * @param edgeLengthRatio a length ratio value between 0 and 1 + */ + void setMaximumEdgeLengthRatio(double edgeLengthRatio); + + /** + * Sets whether holes are allowed in the concave hull polygon. + * + * @param holesAllowed true if holes are allowed in the result + */ + void setHolesAllowed(bool holesAllowed); + + /** + * Sets the alpha parameter to compute an alpha shape of the input. + * Alpha is the radius of the eroding disc. + * Border triangles with circumradius greater than alpha are removed. + * + * @param newAlpha the alpha radius + */ + void setAlpha(double newAlpha); + + /** + * Gets the computed concave hull. + * + * @return the concave hull + */ + std::unique_ptr getHull(); + + +private: + + // Constants + static constexpr int PARAM_EDGE_LENGTH = 1; + static constexpr int PARAM_ALPHA = 2; + + // Members + const Geometry* inputGeometry; + double maxEdgeLengthRatio; + double alpha; + bool isHolesAllowed; + int criteriaType; + double maxSizeInHull; + const GeometryFactory* geomFactory; + + // Methods + static double computeTargetEdgeLength( + TriList& triList, + double edgeLengthFactor); + + void computeHull(TriList& triList); + void computeHullBorder(TriList& triList); + void createBorderQueue(HullTriQueue& queue, TriList& triList); + + /** + * Adds a Tri to the queue. + * Only add tris with a single border edge. + * since otherwise that would risk isolating a vertex if + * the tri ends up being eroded from the hull. + * Sets the tri size according to the threshold parameter being used. + * + * @param tri the Tri to add + * @param queue the priority queue + */ + void addBorderTri(HullTri* tri, HullTriQueue& queue); + void computeHullHoles(TriList& triList); + void setSize(HullTri* tri); + + + /** + * Finds tris which may be the start of holes. + * Only tris which have a long enough edge and which do not touch the current hull + * boundary are included. + * This avoids the risk of disconnecting the result polygon. + * The list is sorted in decreasing order of edge length. + * The list is sorted in decreasing order of size. + * + * @param triList + * @param maxSizeInHull maximum tri size which is not in a hole + * @return + */ + static std::vector findCandidateHoles( + TriList& triList, double maxSizeInHull); + + void removeHole(TriList& triList, HullTri* triHole); + void setSize(TriList& triList); + + /** + * Tests if a tri is included in the hull. + * Tris with size less than the maximum are included in the hull. + * + * @param tri the tri to test + * @return true if the tri is included in the hull + */ + bool isInHull(const HullTri* tri) const; + + bool isRemovableBorder(const HullTri* tri) const; + bool isRemovableHole(const HullTri* tri) const; + + std::unique_ptr toGeometry( + TriList& triList, + const GeometryFactory* factory); + + +}; + + +} // geos::algorithm::hull +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/hull/ConcaveHullOfPolygons.h b/Sources/geos/include/geos/algorithm/hull/ConcaveHullOfPolygons.h new file mode 100644 index 0000000..f13dd03 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/hull/ConcaveHullOfPolygons.h @@ -0,0 +1,370 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Envelope; +class Geometry; +class GeometryCollection; +class GeometryFactory; +class LinearRing; +class Polygon; +} +namespace triangulate { +namespace tri { +class Tri; +} +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace hull { // geos::algorithm::hull + + +/** + * Constructs a concave hull of a set of polygons, respecting + * the polygons as constraints. + * A concave hull is a possibly non-convex polygon containing all the input polygons. + * A given set of polygons has a sequence of hulls of increasing concaveness, + * determined by a numeric target parameter. + * The computed hull "fills the gap" between the polygons, + * and does not intersect their interior. + * + * The concave hull is constructed by removing the longest outer edges + * of the Delaunay Triangulation of the space between the polygons, + * until the target criterion parameter is reached. + * + * The target criteria are: + * * Maximum Edge Length - the length of the longest edge between the polygons is no larger + * than this value. + * * Maximum Edge Length Ratio - determine the Maximum Edge Length + * as a fraction of the difference between the longest and shortest edge lengths + * between the polygons. This normalizes the Maximum Edge Length to be scale-free. + * A value of 1 produces the convex hull; a value of 0 produces the original polygons. + * + * The preferred criterion is the Maximum Edge Length Ratio, since it is + * scale-free and local (so that no assumption needs to be made about the + * total amount of concaveness present). + * + * Optionally the concave hull can be allowed to contain holes, via setHolesAllowed(). + * + * The hull can be specified as being "tight", which means it follows the outer boundaries + * of the input polygons. + * + * The input polygons must form a valid MultiPolygon + * (i.e. they must be non-overlapping). + * + * \author Martin Davis + * + */ +class GEOS_DLL ConcaveHullOfPolygons { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + using GeometryCollection = geos::geom::GeometryCollection; + using GeometryFactory = geos::geom::GeometryFactory; + using LinearRing = geos::geom::LinearRing; + using Polygon = geos::geom::Polygon; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + +private: + + /* Members */ + + static constexpr int FRAME_EXPAND_FACTOR = 4; + + const Geometry* inputPolygons; + const GeometryFactory* geomFactory; + double maxEdgeLength; + double maxEdgeLengthRatio; + bool isHolesAllowed; + bool isTight; + + std::set hullTris; + std::deque borderTriQue; + std::vector polygonRings; + TriList triList; + + /** + * Records the edge index of the longest border edge for border tris, + * so it can be tested for length and possible removal. + */ + std::map borderEdgeMap; + + /* Methods */ + + std::unique_ptr createEmptyHull(); + + void buildHullTris(); + + /** + * Creates a rectangular "frame" around the input polygons, + * with the input polygons as holes in it. + * The frame is large enough that the constrained Delaunay triangulation + * of it should contain the convex hull of the input as edges. + * The frame corner triangles can be removed to produce a + * triangulation of the space around and between the input polygons. + * + * @param polygonsEnv + * @return the frame polygon + */ + std::unique_ptr createFrame( + const Envelope* polygonsEnv); + + double computeTargetEdgeLength( + TriList& triList, + const CoordinateSequence* frameCorners, + double edgeLengthRatio) const; + + bool isFrameTri( + const Tri* tri, + const CoordinateSequence* frameCorners) const; + + void removeFrameCornerTris( + TriList& tris, + const CoordinateSequence* frameCorners); + + /** + * Get the tri vertex index of some point in a list, + * or -1 if none are vertices. + * + * @param tri the tri to test for containing a point + * @param pts the points to test + * @return the vertex index of a point, or -1 + */ + TriIndex vertexIndex( + const Tri* tri, + const CoordinateSequence* pts) const; + + void removeBorderTris(); + + void removeHoleTris(); + + Tri* findHoleSeedTri() const; + + bool isHoleSeedTri(const Tri* tri) const; + + bool isBorderTri(const Tri* tri) const; + + bool isRemovable(const Tri* tri) const; + + /** + * Tests whether a triangle touches a single polygon at all vertices. + * If so, it is a candidate for removal if the hull polygon + * is being kept tight to the outer boundary of the input polygons. + * Tris which touch more than one polygon are called "bridging". + * + * @param tri + * @return true if the tri touches a single polygon + */ + bool isTouchingSinglePolygon(const Tri* tri) const; + + void addBorderTris(Tri* tri); + + /** + * Adds an adjacent tri to the current border. + * The adjacent edge is recorded as the border edge for the tri. + * Note that only edges adjacent to another tri can become border edges. + * Since constraint-adjacent edges do not have an adjacent tri, + * they can never be on the border and thus will not be removed + * due to being shorter than the length threshold. + * The tri containing them may still be removed via another edge, however. + * + * @param tri the tri adjacent to the tri to be added to the border + * @param index the index of the adjacent tri + */ + void addBorderTri(Tri* tri, TriIndex index); + + void removeBorderTri(Tri* tri); + + bool hasAllVertices(const LinearRing* ring, const Tri* tri) const; + + bool hasVertex(const LinearRing* ring, const Coordinate& v) const; + + void envelope(const Tri* tri, Envelope& env) const; + + std::unique_ptr createHullGeometry(bool isIncludeInput); + + +public: + + /** + * Computes a concave hull of set of polygons + * using the target criterion of maximum edge length. + * + * @param polygons the input polygons + * @param maxLength the target maximum edge length + * @return the concave hull + */ + static std::unique_ptr + concaveHullByLength(const Geometry* polygons, double maxLength); + + /** + * Computes a concave hull of set of polygons + * using the target criterion of maximum edge length, + * and allowing control over whether the hull boundary is tight + * and can contain holes. + * + * @param polygons the input polygons + * @param maxLength the target maximum edge length + * @param isTight true if the hull should be tight to the outside of the polygons + * @param isHolesAllowed true if holes are allowed in the hull polygon + * @return the concave hull + */ + static std::unique_ptr + concaveHullByLength( + const Geometry* polygons, double maxLength, + bool isTight, bool isHolesAllowed); + + /** + * Computes a concave hull of set of polygons + * using the target criterion of maximum edge length ratio. + * + * @param polygons the input polygons + * @param lengthRatio the target maximum edge length ratio + * @return the concave hull + */ + static std::unique_ptr + concaveHullByLengthRatio(const Geometry* polygons, double lengthRatio); + + /** + * Computes a concave hull of set of polygons + * using the target criterion of maximum edge length ratio, + * and allowing control over whether the hull boundary is tight + * and can contain holes. + * + * @param polygons the input polygons + * @param lengthRatio the target maximum edge length ratio + * @param isTight true if the hull should be tight to the outside of the polygons + * @param isHolesAllowed true if holes are allowed in the hull polygon + * @return the concave hull + */ + static std::unique_ptr + concaveHullByLengthRatio( + const Geometry* polygons, double lengthRatio, + bool isTight, bool isHolesAllowed); + + /** + * Computes a concave fill area between a set of polygons, + * using the target criterion of maximum edge length. + * + * @param polygons the input polygons + * @param maxLength the target maximum edge length + * @return the concave fill + */ + static std::unique_ptr + concaveFillByLength(const Geometry* polygons, double maxLength); + + /** + * Computes a concave fill area between a set of polygons, + * using the target criterion of maximum edge length ratio. + * + * @param polygons the input polygons + * @param lengthRatio the target maximum edge length ratio + * @return the concave fill + */ + static std::unique_ptr + concaveFillByLengthRatio(const Geometry* polygons, double lengthRatio); + + /** + * Creates a new instance for a given geometry. + * + * @param geom the input geometry + */ + ConcaveHullOfPolygons(const Geometry* geom); + + /** + * Sets the target maximum edge length for the concave hull. + * The length value must be zero or greater. + * + * * The value 0.0 produces the input polygons. + * * Larger values produce less concave results. + * Above a certain large value the result is the convex hull of the input. + * + * The edge length ratio provides a scale-free parameter which + * is intended to produce similar concave results for a variety of inputs. + * + * @param edgeLength a non-negative length + */ + void setMaximumEdgeLength(double edgeLength); + + /** + * Sets the target maximum edge length ratio for the concave hull. + * The edge length ratio is a fraction of the difference + * between the longest and shortest edge lengths + * in the Delaunay Triangulation of the area between the input polygons. + * (Roughly speaking, it is a fraction of the difference between + * the shortest and longest distances between the input polygons.) + * It is a value in the range 0 to 1. + * + * * The value 0.0 produces the original input polygons. + * * The value 1.0 produces the convex hull. + * + * @param edgeLengthRatio a length factor value between 0 and 1 + */ + void setMaximumEdgeLengthRatio(double edgeLengthRatio); + + /** + * Sets whether holes are allowed in the concave hull polygon. + * + * @param p_isHolesAllowed true if holes are allowed in the result + */ + void setHolesAllowed(bool p_isHolesAllowed); + + /** + * Sets whether the boundary of the hull polygon is kept + * tight to the outer edges of the input polygons. + * + * @param p_isTight true if the boundary is kept tight + */ + void setTight(bool p_isTight); + + /** + * Gets the computed concave hull. + * + * @return the concave hull + */ + std::unique_ptr getHull(); + + /** + * Gets the concave fill, which is the area between the input polygons, + * subject to the concaveness control parameter. + * + * @return the concave fill + */ + std::unique_ptr getFill(); + + +}; + + + +} // geos::algorithm::hull +} // geos::algorithm +} // geos diff --git a/Sources/geos/include/geos/algorithm/hull/HullTri.h b/Sources/geos/include/geos/algorithm/hull/HullTri.h new file mode 100644 index 0000000..0e04dbe --- /dev/null +++ b/Sources/geos/include/geos/algorithm/hull/HullTri.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + + +namespace geos { +namespace geom { +class Coordinate; +} +namespace triangulate { +namespace quadedge { +} +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace hull { // geos::algorithm::hull + + + +class HullTri : public geos::triangulate::tri::Tri { + using Coordinate = geos::geom::Coordinate; + using Triangle = geos::geom::Triangle; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + +private: + + double m_size; + bool m_isMarked = false; + + bool isBoundaryTouch(TriIndex index) const; + + + public: + + HullTri(const Coordinate& c0, const Coordinate& c1, const Coordinate& c2) + : Tri(c0, c1, c2) + , m_size(Triangle::longestSideLength(c0, c1, c2)) + {}; + + class HullTriCompare { + public: + HullTriCompare() {}; + bool operator()(const HullTri* a, const HullTri* b) + { + if (a->getSize() == b->getSize()) + return a->getArea() < b->getArea(); + else + return a->getSize() < b->getSize(); + } + }; + + + double getSize() const; + + /** + * Sets the size to be the length of the boundary edges. + * This is used when constructing hull without holes, + * by erosion from the triangulation border. + */ + void setSizeToBoundary(); + + void setSizeToLongestEdge(); + void setSizeToCircumradius(); + + + bool isMarked() const; + void setMarked(bool marked); + bool isRemoved(); + TriIndex boundaryIndex() const; + TriIndex boundaryIndexCCW() const; + TriIndex boundaryIndexCW() const; + + /** + * Tests if a tri is the only one connecting its 2 adjacents. + * Assumes that the tri is on the border of the triangulation + * and that the triangulation does not contain holes + * + * @return true if the tri is the only connection + */ + bool isConnecting() const; + + /** + * Gets the index of a vertex which is adjacent to two other tris (if any). + * + * @return the vertex index, or -1 + */ + int adjacent2VertexIndex() const; + + /** + * Tests whether some vertex of this Tri has degree = 1. + * In this case it is not in any other Tris. + * + * @param tri + * @param triList + * @return true if a vertex has degree 1 + */ + TriIndex isolatedVertexIndex(TriList& triList) const; + + double lengthOfLongestEdge() const; + + /** + * Tests if this tri has a vertex which is in the boundary, + * but not in a boundary edge. + * + * @return true if the tri touches the boundary at a vertex + */ + bool hasBoundaryTouch() const; + + static HullTri* findTri(TriList& triList, Tri* exceptTri); + static bool isAllMarked(TriList& triList); + static void clearMarks(TriList& triList); + static void markConnected(HullTri* triStart, HullTri* exceptTri); + static bool isConnected(TriList& triList, HullTri* exceptTri); + + friend std::ostream& operator<<(std::ostream& os, const HullTri& ht); + + double lengthOfBoundary() const; + + void remove(TriList& triList); + + +}; // HullTri + + + + + + +} // geos::algorithm::hull +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/hull/HullTriangulation.h b/Sources/geos/include/geos/algorithm/hull/HullTriangulation.h new file mode 100644 index 0000000..5a921dd --- /dev/null +++ b/Sources/geos/include/geos/algorithm/hull/HullTriangulation.h @@ -0,0 +1,136 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class Coordinate; +} +namespace triangulate { +namespace quadedge { +class QuadEdge; +class QuadEdgeSubdivision; +} +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace hull { // geos::algorithm::hull + + +class HullTriangulation +{ + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Coordinate = geos::geom::Coordinate; + template + using TriList = geos::triangulate::tri::TriList; + using QuadEdge = geos::triangulate::quadedge::QuadEdge; + using QuadEdgeSubdivision = geos::triangulate::quadedge::QuadEdgeSubdivision; + using TriangleVisitor = geos::triangulate::quadedge::TriangleVisitor; + +private: + + static void toTris( + QuadEdgeSubdivision& subdiv, + TriList& triList); + + /** + * Extracts the coordinates of the edges along the boundary of a triangulation, + * by tracing CW around the border triangles. + * Assumption: there are at least 2 tris, they are connected, + * and there are no holes. + * So each tri has at least one non-boundary edge, and there is only one boundary. + * + * @param triList the triangulation + * @return the points in the boundary of the triangulation + */ + static geom::CoordinateSequence traceBoundary( + TriList& triList); + + static HullTri* findBorderTri( + TriList& triList); + + + +public: + + HullTriangulation() {}; + + static void createDelaunayTriangulation( + const Geometry* geom, + TriList& triList); + + /** + * Creates a Polygon representing the area of a triangulation + * which is connected and contains no holes. + * + * @param triList the triangulation + * @param geomFactory the geometry factory to use + * @return the area polygon + */ + static std::unique_ptr traceBoundaryPolygon( + TriList& triList, + const GeometryFactory* factory); + + static HullTri* nextBorderTri(HullTri* triStart); + + /** + * Creates a polygonal geometry representing the area of a triangulation + * which may be disconnected or contain holes. + * + * @param triList the triangulation + * @param geomFactory the geometry factory to use + * @return the area polygonal geometry + */ + static std::unique_ptr geomunion( + TriList& triList, + const GeometryFactory* factory); + + + class HullTriVisitor : public TriangleVisitor + { + + private: + + TriList& triList; + + public: + + HullTriVisitor(TriList& p_triList) + : triList(p_triList) + {}; + + void visit(std::array& triEdges) override; + + }; // HullTriVisitor + + +}; // HullTriangulation + + + +} // geos::algorithm::hull +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/hull/OuterShellsExtracter.h b/Sources/geos/include/geos/algorithm/hull/OuterShellsExtracter.h new file mode 100644 index 0000000..3c09267 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/hull/OuterShellsExtracter.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { +class Geometry; +class LinearRing; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace hull { // geos::algorithm::hull + +/** + * Extracts the rings of outer shells from a polygonal geometry. + * Outer shells are the shells of polygon elements which + * are not nested inside holes of other polygons. + * + * \author Martin Davis + */ +class OuterShellsExtracter { + using Geometry = geos::geom::Geometry; + using LinearRing = geos::geom::LinearRing; + +private: + + OuterShellsExtracter(const Geometry& g); + + void extractOuterShells(std::vector& outerShells); + + bool isOuter(const LinearRing& shell, std::vector& outerShells); + + bool covers(const LinearRing& shellA, const LinearRing& shellB); + + bool isPointInRing(const LinearRing& shell, const LinearRing& shellRing); + + static void extractShellRings(const Geometry& polygons, std::vector& shells); + + static bool envelopeAreaComparator( + const LinearRing* g1, + const LinearRing* g2); + + const Geometry& geom; + +public: + static void extractShells(const Geometry* polygons, std::vector& shells); + +}; + +} // geos::algorithm::hull +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h b/Sources/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h new file mode 100644 index 0000000..ac327fe --- /dev/null +++ b/Sources/geos/include/geos/algorithm/locate/IndexedPointInAreaLocator.h @@ -0,0 +1,139 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2018 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include // inherited +#include // inherited +#include + +#include +#include // composition + +namespace geos { +namespace algorithm { +class RayCrossingCounter; +} +namespace geom { +class Geometry; +class Coordinate; +class CoordinateSequence; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace locate { // geos::algorithm::locate + +/** \brief + * Determines the location of [Coordinates](@ref geom::Coordinate) relative to + * an areal geometry, using indexing for efficiency. + * + * The Location is computed precisely, in that points located on the geometry boundary + * or segments will return [geom::Location::BOUNDARY](@ref geom::Location). + * + * Polygonal and [LinearRing](@ref geom::LinearRing) geometries are supported. + * + * The index is lazy-loaded, which allows creating instances even if they are not used. + * + */ +class GEOS_DLL IndexedPointInAreaLocator : public PointOnGeometryLocator { +private: + struct SegmentView { + SegmentView(const geom::CoordinateXY* p0, const geom::CoordinateXY* p1) { + // There is a significant performance benefit in fitting our + // line segment into 8 bytes (about 15-20%). Because we know that + // p1 follows p0 in a CoordinateSequence, we know that the address + // of p1 is 16, 24, or 32 bytes greater than the address of p0. + // By packing this offset into the least significant bits of p0, + // we can retrieve both p0 and p1 while only using 8 bytes. + std::size_t os = static_cast(reinterpret_cast(p1) - reinterpret_cast(p0)) - 2u; + m_p0 = reinterpret_cast(p0) | os; + + assert(&this->p0() == p0); + assert(&this->p1() == p1); + } + + const geom::CoordinateXY& p0() const { + auto ret = reinterpret_cast(m_p0 >> 2 << 2); + return *ret; + } + + const geom::CoordinateXY& p1() const { + auto offset = (m_p0 & 0x03) + 2; + auto ret = reinterpret_cast(reinterpret_cast(m_p0 >> 2 << 2) + offset); + return *ret; + } + + std::size_t m_p0; + }; + + class IntervalIndexedGeometry { + private: + + index::strtree::TemplateSTRtree index; + + void init(const geom::Geometry& g); + void addLine(const geom::CoordinateSequence* pts); + + public: + IntervalIndexedGeometry(const geom::Geometry& g); + + template + void query(double min, double max, Visitor&& f) { + index.query(index::strtree::Interval(min, max), f); + } + }; + + const geom::Geometry& areaGeom; + std::unique_ptr index; + + void buildIndex(const geom::Geometry& g); + + // Declare type as noncopyable + IndexedPointInAreaLocator(const IndexedPointInAreaLocator& other) = delete; + IndexedPointInAreaLocator& operator=(const IndexedPointInAreaLocator& rhs) = delete; + +public: + /** \brief + * Creates a new locator for a given [Geometry](@ref geom::Geometry). + * + * Polygonal and [LinearRing](@ref geom::LinearRing) geometries are supported. + * + * @param g the Geometry to locate in + */ + IndexedPointInAreaLocator(const geom::Geometry& g); + + const geom::Geometry& getGeometry() const { + return areaGeom; + } + + /** \brief + * Determines the [Location](@ref geom::Location) of a point in an areal + * [Geometry](@ref geom::Geometry). + * + * @param p the point to test + * @return the location of the point in the geometry + */ + geom::Location locate(const geom::CoordinateXY* /*const*/ p) override; + +}; + +} // geos::algorithm::locate +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/locate/PointOnGeometryLocator.h b/Sources/geos/include/geos/algorithm/locate/PointOnGeometryLocator.h new file mode 100644 index 0000000..47d8410 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/locate/PointOnGeometryLocator.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { +class CoordinateXY; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace locate { // geos::algorithm::locate + +/** \brief + * An interface for classes which determine the [Location](@ref geom::Location) of + * points in [Polygon](@ref geom::Polygon) or [MultiPolygon](@ref geom::MultiPolygon) geometries. + * + * @author Martin Davis + */ +class GEOS_DLL PointOnGeometryLocator { +private: +protected: +public: + virtual + ~PointOnGeometryLocator() + { } + + /** + * Determines the [Location](@ref geom::Location) of a point in an areal [Geometry](@ref geom::Geometry). + * + * @param p the point to test + * @return the location of the point in the geometry + */ + virtual geom::Location locate(const geom::CoordinateXY* /*const*/ p) = 0; +}; + +} // geos::algorithm::locate +} // geos::algorithm +} // geos + diff --git a/Sources/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h b/Sources/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h new file mode 100644 index 0000000..be77ff9 --- /dev/null +++ b/Sources/geos/include/geos/algorithm/locate/SimplePointInAreaLocator.h @@ -0,0 +1,117 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include // inherited + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +class Surface; +} +} + +namespace geos { +namespace algorithm { // geos::algorithm +namespace locate { // geos::algorithm::locate + +/** \brief + * Computes the location of points relative to a polygonal + * [Geometry](@ref geom::Geometry), using a simple `O(n)` algorithm. + * + * The algorithm used reports if a point lies in the interior, exterior, + * or exactly on the boundary of the Geometry. + * + * Instance methods are provided to implement the interface `PointInAreaLocator`. + * However, they provide no performance advantage over the class methods. + * + * This algorithm is suitable for use in cases where only a few points will be tested. + * If many points will be tested, IndexedPointInAreaLocator may provide better performance. + */ +class GEOS_DLL SimplePointInAreaLocator : public PointOnGeometryLocator { + +public: + + static geom::Location locate(const geom::CoordinateXY& p, + const geom::Geometry* geom); + + /** \brief + * Determines the Location of a point in a [Surface](@ref geom::Surface). + * + * The return value is one of: + * + * - geom::Location::INTERIOR + * if the point is in the geometry interior + * - geom::Location::BOUNDARY + * if the point lies exactly on the boundary + * - geom::Location::EXTERIOR + * if the point is outside the geometry + * + * Computes `geom::Location::BOUNDARY` if the point lies exactly + * on the polygon boundary. + * + * @param p the point to test + * @param poly the geometry to test + * @return the Location of the point in the polygon + */ + static geom::Location locatePointInSurface(const geom::CoordinateXY& p, + const geom::Surface& poly); + + /** \brief + * Determines whether a point is contained in a [Geometry](@ref geom::Geometry), + * or lies on its boundary. + * + * This is a convenience method for + * + * Location::EXTERIOR != locate(p, geom) + * + * @param p the point to test + * @param geom the geometry to test + * @return true if the point lies in or on the geometry + */ + static bool isContained(const geom::CoordinateXY& p, + const geom::Geometry* geom); + + SimplePointInAreaLocator(const geom::Geometry* p_g) + : g(p_g) + { } + + SimplePointInAreaLocator(const geom::Geometry& p_g) + : g(&p_g) + { } + + geom::Location + locate(const geom::CoordinateXY* p) override + { + return locate(*p, g); + } + +private: + + static geom::Location locateInGeometry(const geom::CoordinateXY& p, + const geom::Geometry* geom); + + const geom::Geometry* g; + +}; + +} // geos::algorithm::locate +} // geos::algorithm +} // geos + + diff --git a/Sources/geos/include/geos/constants.h b/Sources/geos/include/geos/constants.h new file mode 100644 index 0000000..7be6921 --- /dev/null +++ b/Sources/geos/include/geos/constants.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * constants.h + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018 Vicky Vergara + * Copyright (C) 2009 Mateusz Loskot + * Copyright (C) 2005-2009 Refractions Research Inc. + * Copyright (C) 2001-2009 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************/ + +#pragma once + +#ifdef _MSC_VER +#ifndef NOMINMAX +#define NOMINMAX 1 +typedef __int64 int64; +#endif +#endif + +#include +#include +#include +#include // for std::size_t + +namespace geos { + +constexpr double MATH_PI = 3.14159265358979323846; + +// Some handy constants +constexpr double DoubleNotANumber = std::numeric_limits::quiet_NaN(); +constexpr double DoubleMax = (std::numeric_limits::max)(); +constexpr double DoubleInfinity = (std::numeric_limits::infinity)(); +constexpr double DoubleNegInfinity = (-(std::numeric_limits::infinity)()); +constexpr double DoubleEpsilon = std::numeric_limits::epsilon(); + +constexpr std::size_t NO_COORD_INDEX = std::numeric_limits::max(); +constexpr std::size_t INDEX_UNKNOWN = std::numeric_limits::max(); + +} // namespace geos + diff --git a/Sources/geos/include/geos/coverage/CleanCoverage.h b/Sources/geos/include/geos/coverage/CleanCoverage.h new file mode 100644 index 0000000..49aee18 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CleanCoverage.h @@ -0,0 +1,245 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2025 Martin Davis + * Copyright (C) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Geometry; + class GeometryFactory; + class LineString; + class LinearRing; + class Polygon; +} +namespace operation { +namespace relateng { + class RelateNG; +} +} +namespace index { +namespace quadtree { +} +} +} + + +namespace geos { // geos. +namespace coverage { // geos.coverage + +class CleanCoverage { + + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineString = geos::geom::LineString; + using LinearRing = geos::geom::LinearRing; + using Polygon = geos::geom::Polygon; + using RelateNG = geos::operation::relateng::RelateNG; + using Quadtree = geos::index::quadtree::Quadtree; + + +public: + + // Classes + + class CleanArea { + + private: + + // Members + std::vector polys; + Envelope env; + + public: + + // Methods + void add(const Polygon* poly); + const Envelope* getEnvelope(); + double getBorderLength(const Polygon* adjPoly); + double getArea(); + bool isAdjacent(RelateNG& rel); + std::unique_ptr getUnion(); + + }; // CleanArea + + + class MergeStrategy { + + public: + + virtual ~MergeStrategy() = default; + + virtual std::size_t getTarget() const = 0; + + virtual void checkMergeTarget( + std::size_t areaIndex, + CleanArea* cleanArea, + const Polygon* poly) = 0; + + }; // MergeStrategy + + + class BorderMergeStrategy : public MergeStrategy { + + private: + + std::size_t m_targetIndex = INDEX_UNKNOWN; + double m_targetBorderLen; + + public: + + BorderMergeStrategy() {}; + + std::size_t getTarget() const override { + return m_targetIndex; + }; + + void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override { + double borderLen = area == nullptr ? 0.0 : area->getBorderLength(poly); + if (m_targetIndex == INDEX_UNKNOWN || borderLen > m_targetBorderLen) { + m_targetIndex = areaIndex; + m_targetBorderLen = borderLen; + } + }; + + }; // BorderStrategy + + + class AreaMergeStrategy : public MergeStrategy { + + private: + + std::size_t m_targetIndex = INDEX_UNKNOWN; + double m_targetArea; + bool m_isMax; + + public: + + AreaMergeStrategy(bool isMax) : m_isMax(isMax) {}; + + std::size_t getTarget() const override { + return m_targetIndex; + } + + void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override { + (void)poly; + double areaVal = area == nullptr ? 0.0 : area->getArea(); + bool isBetter = m_isMax + ? areaVal > m_targetArea + : areaVal < m_targetArea; + if (m_targetIndex == INDEX_UNKNOWN || isBetter) { + m_targetIndex = areaIndex; + m_targetArea = areaVal; + } + } + + }; // AreaMergeStrategy + + + class IndexMergeStrategy : public MergeStrategy { + + private: + + std::size_t m_targetIndex = INDEX_UNKNOWN; + bool m_isMax; + + public: + + IndexMergeStrategy(bool isMax) : m_isMax(isMax) {}; + + std::size_t getTarget() const override { + return m_targetIndex; + } + + void checkMergeTarget(std::size_t areaIndex, CleanArea* area, const Polygon* poly) override { + (void)area; + (void)poly; + bool isBetter = m_isMax + ? areaIndex > m_targetIndex + : areaIndex < m_targetIndex; + if (isBetter) { + m_targetIndex = areaIndex; + } + } + }; // MergeStrategy + + +private: + + // Members + + /** + * The areas in the clean coverage. + * Entries may be null, if no resultant corresponded to the input area. + */ + std::vector> cov; + //-- used for finding areas to merge gaps + std::unique_ptr covIndex = nullptr; + + void mergeGap(const Polygon* gap); + + CleanArea* findMaxBorderLength(const Polygon* poly, std::vector& areas); + + std::vector findAdjacentAreas(const Geometry* poly); + + void createIndex(); + + +public: + + // Methods + + CleanCoverage(std::size_t size); + + void add(std::size_t i, const Polygon* poly); + + void mergeOverlap(const Polygon* overlap, + MergeStrategy& mergeStrategy, + std::vector& parentIndexes); + + static std::size_t findMergeTarget(const Polygon* poly, + MergeStrategy& strategy, + std::vector& parentIndexes, + std::vector>& cov); + + void mergeGaps(std::vector& gaps); + + std::vector> toCoverage(const GeometryFactory* geomFactory); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + CleanCoverage(const CleanCoverage&) = delete; + CleanCoverage& operator=(const CleanCoverage&) = delete; + + +}; + +} // namespace geos.coverage +} // namespace geos + + + + + diff --git a/Sources/geos/include/geos/coverage/Corner.h b/Sources/geos/include/geos/coverage/Corner.h new file mode 100644 index 0000000..373ee04 --- /dev/null +++ b/Sources/geos/include/geos/coverage/Corner.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace geos { +namespace simplify { +class LinkedLine; +} +namespace geom { +class Coordinate; +class LineString; +} +} + +namespace geos { +namespace coverage { // geos::coverage + + +class Corner +{ + using Coordinate = geos::geom::Coordinate; + using Envelope = geos::geom::Envelope; + using LineString = geos::geom::LineString; + using LinkedLine = geos::simplify::LinkedLine; + +public: + + Corner(const LinkedLine* edge, std::size_t i); + + bool isVertex(std::size_t index) const; + + inline std::size_t getIndex() const { + return m_index; + } + + inline double getArea() const { + return m_area; + }; + + const Coordinate& prev() const; + const Coordinate& next() const; + + Envelope envelope() const; + + bool isVertex(const Coordinate& v) const; + bool isBaseline(const Coordinate& p0, const Coordinate& p1) const; + bool intersects(const Coordinate& v) const; + bool isRemoved() const; + + const Coordinate& getCoordinate() { + return m_edge->getCoordinate(m_index); + } + + std::unique_ptr toLineString() const; + + inline int compareTo(const Corner& rhs) const { + double area_lhs = getArea(); + double area_rhs = rhs.getArea(); + + if (area_lhs == area_rhs) { + std::size_t index_lhs = getIndex(); + std::size_t index_rhs = rhs.getIndex(); + if (index_lhs == index_rhs) return 0; + else return index_lhs < index_rhs ? -1 : 1; + } + else + return area_lhs < area_rhs ? -1 : 1; + } + + bool operator< (const Corner& rhs) const { + return compareTo(rhs) < 0; + }; + + bool operator> (const Corner& rhs) const { + return compareTo(rhs) > 0; + }; + + bool operator==(const Corner& rhs) const { + return compareTo(rhs) == 0; + }; + + struct Greater { + inline bool operator()(const Corner & a, const Corner & b) const { + return a.compareTo(b) > 0; + } + }; + + // Order using greater for compatibility with the Java PriorityQueue + // implementation, which returns the smallest item off the top of the + // queue + using PriorityQueue = std::priority_queue, Corner::Greater>; + + +private: + + // members + const LinkedLine* m_edge; + std::size_t m_index; + std::size_t m_prev; + std::size_t m_next; + double m_area; + + // methods + static double area(const LinkedLine& edge, std::size_t index); + +}; // Corner + + + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Corner& c); + + +} // geos::coverage +} // geos diff --git a/Sources/geos/include/geos/coverage/CoverageBoundarySegmentFinder.h b/Sources/geos/include/geos/coverage/CoverageBoundarySegmentFinder.h new file mode 100644 index 0000000..359ea7a --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageBoundarySegmentFinder.h @@ -0,0 +1,81 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace geos { +namespace geom { +class CoordinateSequence; +class Geometry; +} +} + +namespace geos { +namespace coverage { // geos::coverage + +class CoverageBoundarySegmentFinder : public geos::geom::CoordinateSequenceFilter +{ + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using CoordinateSequenceFilter = geos::geom::CoordinateSequenceFilter; + using Geometry = geos::geom::Geometry; + using LineSegment = geos::geom::LineSegment; + +public: + + CoverageBoundarySegmentFinder(LineSegment::UnorderedSet& segs) + : m_boundarySegs(segs) + {}; + + bool isGeometryChanged() const override { + return false; + } + + bool isDone() const override { + return false; + } + + void filter_ro(const CoordinateSequence& seq, std::size_t i) override; + + + static LineSegment::UnorderedSet + findBoundarySegments(const std::vector& geoms); + + static bool isBoundarySegment( + const LineSegment::UnorderedSet& boundarySegs, + const CoordinateSequence* seq, + std::size_t i); + +private: + + static LineSegment + createSegment(const CoordinateSequence& seq, std::size_t i); + + + LineSegment::UnorderedSet& m_boundarySegs; + + +}; // CoverageBoundarySegmentFinder + + + +} // geos::coverage +} // geos diff --git a/Sources/geos/include/geos/coverage/CoverageCleaner.h b/Sources/geos/include/geos/coverage/CoverageCleaner.h new file mode 100644 index 0000000..8452a49 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageCleaner.h @@ -0,0 +1,349 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2025 Martin Davis + * Copyright (C) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class Coordinate; + class CoordinateSequence; + class Geometry; + class GeometryFactory; + class LineString; + class LinearRing; + class Polygon; +} +namespace index { +} +namespace noding { + class SegmentString; +} +namespace coverage { + class CleanCoverage; +} +} + +namespace geos { // geos. +namespace coverage { // geos.coverage + + +/** + * Cleans the linework of a set of polygonal geometries to form a valid polygonal coverage. + * The input is an array of valid Polygon or MultiPolygon geometries + * which may contain topological errors such as overlaps and gaps. + * Empty or non-polygonal inputs are removed. + * Linework is snapped together to eliminate small discrepancies. + * Overlaps are merged with an adjacent polygon, according to a given merge strategy. + * Gaps narrower than a given width are filled and merged with an adjacent polygon. + * The output is an array of polygonal geometries forming a valid polygonal coverage. + * + * ** Snapping ** + * + * Snapping to nearby vertices and line segment snapping + * is used to improve noding robustness + * and eliminate small errors in an efficient way, + * By default this uses a very small snapping distance + * based on the extent of the input data. + * The snapping distance may be specified explicitly. + * This can reduce the number of overlaps and gaps that need to be merged, + * and reduce the risk of spikes formed by merging gaps. + * However, a large snapping distance may introduce undesirable + * data alteration. + * + * ** Overlap Merging ** + * + * Overlaps are merged with an adjacent polygon chosen according to a specified merge strategy. + * The supported strategies are: + * + * * **Longest Border**: (default) merge with the polygon with longest shared border (#MERGE_LONGEST_BORDER.) + * * **Maximum/Minimum Area**: merge with the polygon with largest or smallest area (#MERGE_MAX_AREA, #MERGE_MIN_AREA.) + * * **Minimum Index**: merge with the polygon with the lowest index in the input array (#MERGE_MIN_INDEX.) + * + * This allows sorting the input according to some criteria to provide a priority + * for merging gaps. + * + * ** Gap Merging ** + * + * Gaps which are wider than a given distance are merged with an adjacent polygon. + * Polygon width is determined as twice the radius of the MaximumInscribedCircle + * of the gap polygon. + * Gaps are merged with the adjacent polygon with longest shared border. + * Empty holes in input polygons are treated as gaps, and may be filled in. + * Gaps which are not fully enclosed ("inlets") are not removed. + * + * Cleaning can be run on a valid coverage to remove gaps. + * + * + * The clean result is an array of polygonal geometries + * which match one-to-one with the input array. + * A result item may be null if: + * + * * the input item is non-polygonal or empty + * * the input item is so small it is snapped to collapse + * * the input item is covered by another input item + * (which may be a larger or a duplicate (nearly or exactly) geometry) + * + * The result is a valid coverage according to CoverageValidator#isValid(); + * + * ** Known Issues ** + * + * * Long narrow gaps adjacent to multiple polygons may form spikes when merged with a single polygon. + * + * ** Future Enhancements ** + * + * * Provide an area-based tolerance for gap merging + * * Prevent long narrow gaps from forming spikes by partitioning them before merging. + * * Allow merging narrow parts of a gap while leaving wider portions. + * * Support a priority value for each input polygon to control overlap and gap merging + * (this could also allow blocking polygons from being merge targets) + * + * @see CoverageValidator + * @author Martin Davis + * + */ +class GEOS_DLL CoverageCleaner { + + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Point = geos::geom::Point; + using Polygon = geos::geom::Polygon; + using LineString = geos::geom::LineString; + using LinearRing = geos::geom::LinearRing; + using Envelope = geos::geom::Envelope; + using SegmentString = geos::noding::SegmentString; + +public: + + /** Merge strategy that chooses polygon with longest common border */ + static constexpr int MERGE_LONGEST_BORDER = 0; + /** Merge strategy that chooses polygon with maximum area */ + static constexpr int MERGE_MAX_AREA = 1; + /** Merge strategy that chooses polygon with minimum area */ + static constexpr int MERGE_MIN_AREA = 2; + /** Merge strategy that chooses polygon with smallest input index */ + static constexpr int MERGE_MIN_INDEX = 3; + +private: + + std::vector coverage; + const GeometryFactory* geomFactory; + double snappingDistance; + + double gapMaximumWidth = 0.0; + int overlapMergeStrategy = MERGE_LONGEST_BORDER; + std::unique_ptr> covIndex; + std::vector> resultants; + std::unique_ptr cleanCov; + std::map> overlapParentMap; + std::vector overlaps; + std::vector gaps; + std::vector mergableGaps; + + static constexpr double DEFAULT_SNAPPING_FACTOR = 1.0e8; + + + static double computeDefaultSnappingDistance( + std::vector& geoms); + + static Envelope extent(std::vector& geoms); + + void mergeOverlaps( + std::map>& overlapMap); + + std::unique_ptr mergeStrategy( + int mergeStrategyId); + + void computeResultants(double tolerance); + + void createCoverageIndex(); + + void classifyResult(std::vector>& rs); + + void classifyResultant(std::size_t resultIndex, const Polygon* resPoly); + + static bool covers(const Geometry* poly, const Point* intPt); + + std::vector findMergableGaps( + std::vector gaps); + + bool isMergableGap(const Polygon* gap); + + static std::vector> polygonize( + const Geometry* cleanEdges); + + static bool isPolygonal(const Geometry* geom); + + static std::vector toPolygonArray( + const Geometry* geom); + + +public: + + /** + * Create a new cleaner instance for a set of polygonal geometries. + * + * @param coverage an array of polygonal geometries to clean + */ + CoverageCleaner(std::vector& coverage); + + /** + * Cleans a set of polygonal geometries to form a valid coverage, + * allowing all cleaning parameters to be specified. + * + * @param coverage an array of polygonal geometries to clean + * @param snapDistance the distance tolerance for snapping + * @param mergeStrategy the strategy to use for merging overlaps + * @param maxWidth the maximum width of gaps to merge + * @return the clean coverage + */ + static std::vector> clean( + std::vector& coverage, + double snapDistance, + int mergeStrategy, + double maxWidth); + + /** + * Cleans a set of polygonal geometries to form a valid coverage, + * using the default overlap merge strategy {@link #MERGE_LONGEST_BORDER}. + * + * @param coverage an array of polygonal geometries to clean + * @param snapDistance the distance tolerance for snapping + * @param maxWidth the maximum width of gaps to merge + * @return the clean coverage + */ + static std::vector> clean( + std::vector& coverage, + double snapDistance, + double maxWidth); + + /** + * Cleans a set of polygonal geometries to form a valid coverage, + * using the default snapping distance tolerance. + * + * @param coverage an array of polygonal geometries to clean + * @param mergeStrategy the strategy to use for merging overlaps + * @param maxWidth the maximum width of gaps to merge + * @return the clean coverage + */ + static std::vector> cleanOverlapGap( + std::vector& coverage, + int mergeStrategy, + double maxWidth); + + /** + * Cleans a set of polygonal geometries to form a valid coverage, + * with default snapping tolerance and overlap merging, + * and merging gaps which are narrower than a specified width. + * + * @param coverage an array of polygonal geometries to clean + * @param maxWidth the maximum width of gaps to merge + * @return the clean coverage + */ + static std::vector> cleanGapWidth( + std::vector& coverage, + double maxWidth); + + /** + * Sets the snapping distance tolerance. + * The default is to use a small fraction of the input extent diameter. + * A distance of zero prevents snapping from being used. + * + * @param snapDistance the snapping distance tolerance + */ + void setSnappingDistance(double snapDistance); + + /** + * Sets the overlap merge strategy to use. + * The default is {@link #MERGE_LONGEST_BORDER}. + * + * @param mergeStrategy the merge strategy code + */ + void setOverlapMergeStrategy(int mergeStrategy); + + /** + * Sets the maximum width of the gaps that will be filled and merged. + * The width of a gap is twice the radius of the Maximum Inscribed Circle in the gap polygon, + * A width of zero prevents gaps from being merged. + * + * @param maxWidth the maximum gap width to merge + */ + void setGapMaximumWidth(double maxWidth); + + /** + * Cleans the coverage. + */ + void clean(); + + /** + * Gets the cleaned coverage. + * + * @return the clean coverage + */ + std::vector> getResult(); + + /** + * Gets polygons representing the overlaps in the input, + * which have been merged. + * + * @return a list of overlap polygons + */ + std::vector getOverlaps(); + + /** + * Gets polygons representing the gaps in the input + * which have been merged. + * + * @return a list of gap polygons + */ + std::vector getMergedGaps(); + + std::unique_ptr toGeometry( + std::vector& segStrings, + const GeometryFactory* geomFact); + + std::unique_ptr node( + std::vector& coverage, + double snapDistance); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + CoverageCleaner(const CoverageCleaner&) = delete; + CoverageCleaner& operator=(const CoverageCleaner&) = delete; + +}; + +} // namespace geos.coverage +} // namespace geos + + + + + diff --git a/Sources/geos/include/geos/coverage/CoverageEdge.h b/Sources/geos/include/geos/coverage/CoverageEdge.h new file mode 100644 index 0000000..60afd1c --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageEdge.h @@ -0,0 +1,171 @@ + + + +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class LinearRing; +class LineString; +class MultiLineString; +class GeometryFactory; +} +} + +namespace geos { // geos. +namespace coverage { // geos.coverage + +/** + * An edge of a polygonal coverage formed from all or a section of a polygon ring. + * An edge may be a free ring, which is a ring which has not node points + * (i.e. does not touch any other rings in the parent coverage). + * + * @author mdavis + * + */ +class GEOS_DLL CoverageEdge { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using GeometryFactory = geos::geom::GeometryFactory; + using LinearRing = geos::geom::LinearRing; + using LineString = geos::geom::LineString; + using LineSegment = geos::geom::LineSegment; + using MultiLineString = geos::geom::MultiLineString; + + +private: + + // Members + std::unique_ptr m_pts; + std::size_t m_ringCount ; + bool m_isFreeRing = true; + + // Methods + + static std::unique_ptr + extractEdgePoints(const CoordinateSequence& ring, + std::size_t start, std::size_t end); + + static const Coordinate& + findDistinctPoint( + const CoordinateSequence& pts, + std::size_t index, + bool isForward, + const Coordinate& pt); + + +public: + + CoverageEdge(std::unique_ptr && pts, bool isFreeRing) + : m_pts(pts ? std::move(pts) : detail::make_unique()) + , m_ringCount(0) + , m_isFreeRing(isFreeRing) + {} + + /** + * Computes a key segment for a ring. + * The key is the segment starting at the lowest vertex, + * towards the lowest adjacent distinct vertex. + * + * @param ring a linear ring + * @return a LineSegment representing the key + */ + static LineSegment key( + const CoordinateSequence& ring); + + /** + * Computes a distinct key for a section of a linear ring. + * + * @param ring the linear ring + * @param start index of the start of the section + * @param end end index of the end of the section + * @return a LineSegment representing the key + */ + static LineSegment key( + const CoordinateSequence& ring, + std::size_t start, + std::size_t end); + + static std::unique_ptr createEdge( + const CoordinateSequence& ring); + + static std::unique_ptr createEdge( + const CoordinateSequence& ring, + std::size_t start, + std::size_t end); + + static std::unique_ptr createLines( + const std::vector& edges, + const GeometryFactory* geomFactory); + + std::unique_ptr toLineString( + const GeometryFactory* geomFactory); + + /* public */ + void incRingCount() + { + m_ringCount++; + } + + /* public */ + std::size_t getRingCount() const + { + return m_ringCount; + } + + /** + * Returns whether this edge is a free ring; + * i.e. one with no constrained nodes. + * + * @return true if this is a free ring + */ + bool isFreeRing() const + { + return m_isFreeRing; + } + + void setCoordinates(const CoordinateSequence* pts) + { + m_pts = pts->clone(); + } + + const CoordinateSequence* getCoordinates() const + { + return m_pts.get(); + } + + const Coordinate& getEndCoordinate() const + { + return m_pts->getAt(m_pts->size() - 1); + } + + const Coordinate& getStartCoordinate() const + { + return m_pts->getAt(0); + } + + +}; + +} // namespace geos.coverage +} // namespace geos diff --git a/Sources/geos/include/geos/coverage/CoverageGapFinder.h b/Sources/geos/include/geos/coverage/CoverageGapFinder.h new file mode 100644 index 0000000..4be685f --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageGapFinder.h @@ -0,0 +1,106 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class LinearRing; +} +} + + + +namespace geos { // geos +namespace coverage { // geos::coverage + +/** + * Finds gaps in a polygonal coverage. + * Gaps are holes in the coverage which are narrower than a given width. + * + * The coverage should be valid according to CoverageValidator. + * If this is not the case, some gaps may not be reported, or the invocation may fail. + * + * This is a more accurate way of identifying gaps + * than using CoverageValidator::setGapWidth(double). + * Gaps which separate the coverage into two disjoint regions are not detected. + * Gores are not identified as gaps. + * + * @author mdavis + * + */ +class GEOS_DLL CoverageGapFinder { + using Geometry = geos::geom::Geometry; + using LinearRing = geos::geom::LinearRing; + +private: + + std::vector& m_coverage; + + bool isGap(const LinearRing* hole, double gapWidth); + + +public: + + /** + * Creates a new polygonal coverage gap finder. + * + * @param coverage a set of polygons forming a polygonal coverage + */ + CoverageGapFinder(std::vector& coverage) + : m_coverage(coverage) + {}; + + /** + * Finds gaps in a polygonal coverage. + * Returns lines indicating the locations of the gaps. + * + * @param coverage a set of polygons forming a polygonal coverage + * @param gapWidth the maximum width of gap to detect + * @return a geometry indicating the locations of gaps (which is empty if no gaps were found), or null if the coverage was empty + */ + static std::unique_ptr findGaps( + std::vector& coverage, + double gapWidth); + + /** + * Finds gaps in the coverage. + * Returns lines indicating the locations of the gaps. + * + * @param gapWidth the maximum width of gap to detect + * @return a geometry indicating the locations of gaps (which is empty if no gaps were found), or null if the coverage was empty + */ + std::unique_ptr findGaps(double gapWidth); + + +}; + +} // namespace geos::coverage +} // namespace geos + + + + + + + + + diff --git a/Sources/geos/include/geos/coverage/CoveragePolygon.h b/Sources/geos/include/geos/coverage/CoveragePolygon.h new file mode 100644 index 0000000..4363ea0 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoveragePolygon.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Polygon; +} +} + +namespace geos { // geos +namespace coverage { // geos::coverage + +class GEOS_DLL CoveragePolygon { + using CoordinateXY = geos::geom::CoordinateXY; + using Envelope = geos::geom::Envelope; + using Polygon = geos::geom::Polygon; + using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator; + + // Members + const Polygon* m_polygon; + Envelope polyEnv; + mutable std::unique_ptr m_locator; + +public: + CoveragePolygon(const Polygon* poly); + + bool intersectsEnv(const Envelope& env) const; + bool intersectsEnv(const CoordinateXY& p) const; + bool contains(const CoordinateXY& p) const; + +private: + IndexedPointInAreaLocator& getLocator() const; + +}; + +} // namespace geos::coverage +} // namespace geos + diff --git a/Sources/geos/include/geos/coverage/CoveragePolygonValidator.h b/Sources/geos/include/geos/coverage/CoveragePolygonValidator.h new file mode 100644 index 0000000..8c49bcc --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoveragePolygonValidator.h @@ -0,0 +1,384 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Geometry; +class GeometryFactory; +} +} + + +namespace geos { // geos +namespace coverage { // geos::coverage + + +/** + * Validates that a polygon forms a valid polygonal coverage + * with the set of polygons adjacent to it. + * If the polygon is coverage-valid an empty {@link geos::geom::LineString} is returned. + * Otherwise, the result is a linear geometry containing + * the polygon boundary linework causing the invalidity. + * + * A polygon is coverage-valid if: + * + * * The polygon interior does not intersect the interior of other polygons. + * * If the polygon boundary intersects another polygon boundary, the vertices + * and line segments of the intersection match exactly. + * + * The algorithm detects the following coverage errors: + * + * * the polygon is a duplicate of another one + * * a polygon boundary segment equals an adjacent segment (with same orientation). + * This determines that the polygons overlap + * * a polygon boundary segment is collinear and overlaps an adjacent segment + * but is not equal to it + * * a polygon boundary segment touches an adjacent segment at a non-vertex point + * * a polygon boundary segment crosses into an adjacent polygon + * * a polygon boundary segment is in the interior of an adjacent polygon + * + * If any of these errors is present, the target polygon + * does not form a valid coverage with the adjacent polygons. + * + * The validity rules do not preclude gaps between coverage polygons. + * However, this class can detect narrow gaps, + * by specifying a maximum gap width using {@link #setGapWidth(double)}. + * Note that this will also identify narrow gaps separating disjoint coverage regions, + * and narrow gores. + * In some situations it may also produce false positives + * (i.e. linework identified as part of a gap which is wider than the given width). + * To fully identify gaps it maybe necessary to use {@link CoverageUnion} and analyze + * the holes in the result to see if they are acceptable. + * + * A polygon may be coverage-valid with respect to + * a set of surrounding polygons, but the collection as a whole may not + * form a clean coverage. For example, the target polygon boundary may be fully matched + * by adjacent boundary segments, but the adjacent set contains polygons + * which are not coverage-valid relative to other ones in the set. + * A coverage is valid only if every polygon in the coverage is coverage-valid. + * Use {@link CoverageValidator} to validate an entire set of polygons. + * + * The adjacent set may contain polygons which do not intersect the target polygon. + * These are effectively ignored during validation (but may decrease performance). + * + * @author Martin Davis + * + */ +class GEOS_DLL CoveragePolygonValidator { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using Polygon = geos::geom::Polygon; + using LinearRing = geos::geom::LinearRing; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineSegment = geos::geom::LineSegment; + using IndexedPointInAreaLocator = geos::algorithm::locate::IndexedPointInAreaLocator; + +private: + + /** + * Models a segment in a CoverageRing. + * The segment is normalized so it can be compared with segments + * in any orientation. + * Records valid matching segments in a coverage, + * which must have opposite orientations. + * Also detects equal segments with identical + * orientation, and marks them as coverage-invalid. + */ + class CoverageRingSegment : public LineSegment + { + public: + + // Members + CoverageRing* ringForward; + std::size_t indexForward; + CoverageRing* ringOpp; + std::size_t indexOpp; + + CoverageRingSegment( + const Coordinate& p_p0, const Coordinate& p_p1, + CoverageRing* p_ring, std::size_t p_index) + : LineSegment(p_p0, p_p1) + , ringForward(nullptr) + , indexForward(0) + , ringOpp(nullptr) + , indexOpp(0) + { + if (p_p1.compareTo(p_p0) < 0) { + reverse(); + ringOpp = p_ring; + indexOpp = p_index; + } + else { + ringForward = p_ring; + indexForward = p_index; + } + }; + + void match(const CoverageRingSegment* seg) { + bool isInvalid = checkInvalid(seg); + if (isInvalid) { + return; + } + //-- record the match + if (ringForward == nullptr) { + ringForward = seg->ringForward; + indexForward = seg->indexForward; + } + else { + ringOpp = seg->ringOpp; + indexOpp = seg->indexOpp; + } + //-- mark ring segments as matched + ringForward->markMatched(indexForward); + ringOpp->markMatched(indexOpp); + } + + bool checkInvalid(const CoverageRingSegment* seg) const { + if (ringForward != nullptr && seg->ringForward != nullptr) { + ringForward->markInvalid(indexForward); + seg->ringForward->markInvalid(seg->indexForward); + return true; + } + if (ringOpp != nullptr && seg->ringOpp != nullptr) { + ringOpp->markInvalid(indexOpp); + seg->ringOpp->markInvalid(seg->indexOpp); + return true; + } + return false; + } + + struct CoverageRingSegHash { + std::size_t + operator() (CoverageRingSegment const* s) const { + std::size_t h = std::hash{}(s->p0.x); + h ^= (std::hash{}(s->p0.y) << 1); + h ^= (std::hash{}(s->p1.x) << 1); + return h ^ (std::hash{}(s->p1.y) << 1); + } + }; + + struct CoverageRingSegEq { + bool + operator() (CoverageRingSegment const* lhs, CoverageRingSegment const* rhs) const { + return lhs->p0.x == rhs->p0.x + && lhs->p0.y == rhs->p0.y + && lhs->p1.x == rhs->p1.x + && lhs->p1.y == rhs->p1.y; + } + }; + + }; + + // Members + const Geometry* targetGeom; + std::vector adjGeoms; + //std::vector m_adjPolygons; + const GeometryFactory* geomFactory; + double gapWidth = 0.0; + std::vector> m_adjCovPolygons; + std::deque coverageRingStore; + std::vector> localCoordinateSequences; + std::deque coverageRingSegmentStore; + + typedef std::unordered_map CoverageRingSegmentMap; + + // Declare type as noncopyable + CoveragePolygonValidator(const CoveragePolygonValidator& other) = delete; + CoveragePolygonValidator& operator=(const CoveragePolygonValidator& rhs) = delete; + +public: + + /** + * Validates that a polygon is coverage-valid against the + * surrounding polygons in a polygonal coverage. + * + * @param targetPolygon the polygon to validate + * @param adjPolygons the adjacent polygons + * @return a linear geometry containing the segments causing invalidity (if any) + */ + static std::unique_ptr validate( + const Geometry* targetPolygon, + std::vector& adjPolygons); + + /** + * Validates that a polygon is coverage-valid against the + * surrounding polygons in a polygonal coverage, + * and forms no gaps narrower than a specified width. + *

+ * The set of surrounding polygons should include all polygons which + * are within the gap width distance of the target polygon. + * + * @param targetPolygon the polygon to validate + * @param adjPolygons a collection of the adjacent polygons + * @param gapWidth the maximum width of invalid gaps + * @return a linear geometry containing the segments causing invalidity (if any) + */ + static std::unique_ptr validate( + const Geometry* targetPolygon, + std::vector& adjPolygons, + double gapWidth); + + /** + * Create a new validator. + * + * If the gap width is specified, the set of surrounding polygons + * should include all polygons which + * are within the gap width distance of the target polygon. + * + * @param targetPolygon the geometry to validate + * @param adjPolygons the adjacent polygons in the polygonal coverage + */ + CoveragePolygonValidator( + const Geometry* targetPolygon, + std::vector& adjPolygons); + + /** + * Sets the maximum gap width, if narrow gaps are to be detected. + * + * @param p_gapWidth the maximum width of gaps to detect + */ + void setGapWidth(double p_gapWidth); + + /** + * Validates the coverage polygon against the set of adjacent polygons + * in the coverage. + * + * @return a linear geometry containing the segments causing invalidity (if any) + */ + std::unique_ptr validate(); + +private: + + static std::vector> + toCoveragePolygons(const std::vector polygons); + static std::vector extractPolygons(std::vector& geoms); + + /* private */ + std::unique_ptr createEmptyResult(); + + /** + * Marks matched segments. + * This improves the efficiency of validity testing, since in valid coverages + * all segments (except exterior ones) are matched, + * and hence do not need to be tested further. + * Segments which are equal and have same orientation + * are detected and marked invalid. + * In fact, the entire target polygon may be matched and valid, + * which allows avoiding further tests. + * Segments matched between adjacent polygons are also marked valid, + * since this prevents them from being detected as misaligned, + * if this is being done. + * + * @param targetRings the target rings + * @param adjRings the adjacent rings + * @param targetEnv the tolerance envelope of the target + */ + void markMatchedSegments( + std::vector& targetRings, + std::vector& adjRings, + const Envelope& targetEnv); + + /** + * Adds ring segments to the segment map, + * and detects if they match an existing segment. + * Matched segments are marked. + * + * @param rings + * @param envLimit + * @param segmentMap + */ + void markMatchedSegments( + std::vector& rings, + const Envelope& envLimit, + CoverageRingSegmentMap& segmentMap); + + CoverageRingSegment* createCoverageRingSegment( + CoverageRing* ring, std::size_t index); + + /** + * Marks invalid target segments which cross an adjacent ring segment, + * lie partially in the interior of an adjacent ring, + * or are nearly collinear with an adjacent ring segment up to the distance tolerance + * + * @param targetRings the rings with segments to test + * @param adjRings the adjacent rings + * @param distanceTolerance the gap distance tolerance, if any + */ + void markInvalidInteractingSegments( + std::vector& targetRings, + std::vector& adjRings, + double distanceTolerance); + + /** + * Marks invalid target segments which are fully interior + * to an adjacent polygon. + * + * @param targetRings the rings with segments to test + * @param adjCovPolygons the adjacent polygons + */ + void markInvalidInteriorSegments( + std::vector& targetRings, + std::vector>& adjCovPolygons); + + void markInvalidInteriorSection( + CoverageRing& ring, + std::size_t iStart, + std::size_t iEnd, + std::vector>& adjCovPolygons ); + + void markInvalidInteriorSegment( + CoverageRing& ring, std::size_t i, CoveragePolygon* adjPoly); + + void checkTargetRings( + std::vector& targetRings, + std::vector& adjRngs, + const Envelope& targetEnv); + + std::unique_ptr createInvalidLines(std::vector& rings); + + std::vector createRings(const Geometry* geom); + + std::vector createRings(std::vector& polygons); + + void createRings(const Polygon* poly, std::vector& rings); + + void addRing( + const LinearRing* ring, + bool isShell, + std::vector& rings); + + CoverageRing* createRing(const LinearRing* ring, bool isShell); + + + +}; + +} // namespace geos::coverage +} // namespace geos diff --git a/Sources/geos/include/geos/coverage/CoverageRing.h b/Sources/geos/include/geos/coverage/CoverageRing.h new file mode 100644 index 0000000..f9a78f7 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageRing.h @@ -0,0 +1,204 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class GeometryFactory; +class LineString; +class LinearRing; +class Polygon; +} +} + +namespace geos { // geos. +namespace coverage { // geos.coverage + +class GEOS_DLL CoverageRing : public noding::BasicSegmentString { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Polygon = geos::geom::Polygon; + using LineString = geos::geom::LineString; + using LinearRing = geos::geom::LinearRing; + +private: + + // Members + bool m_isInteriorOnRight; + std::vector m_isInvalid; + std::vector m_isMatched; + + std::size_t findInvalidStart(std::size_t index); + + std::size_t findInvalidEnd(std::size_t index); + + std::size_t nextMarkIndex(std::size_t index); + + /** + * Creates a line from a sequence of ring segments between startIndex and endIndex (inclusive). + * If the endIndex < startIndex the sequence wraps around the ring endpoint. + * + * @param startIndex + * @param endIndex + * @param geomFactory + * @return a line representing the section + */ + std::unique_ptr createLine( + std::size_t startIndex, + std::size_t endIndex, + const GeometryFactory* geomFactory); + + std::unique_ptr extractSection( + std::size_t startIndex, std::size_t endIndex); + + std::unique_ptr extractSectionWrap( + std::size_t startIndex, std::size_t endIndex); + +public: + + CoverageRing(CoordinateSequence* pts, bool interiorOnRight); + + CoverageRing(const LinearRing* ring, bool isShell); + + geom::Envelope getEnvelope(std::size_t start, std::size_t end); + + /** + * Tests if all rings have known status (matched or invalid) + * for all segments. + * + * @param rings a list of rings + * @return true if all ring segments have known status + */ + static bool isKnown(std::vector& rings); + + /** + * Reports if the ring has canonical orientation, + * with the polygon interior on the right (shell is CW). + * + * @return true if the polygon interior is on the right + */ + bool isInteriorOnRight() const; + + /** + * Marks a segment as invalid. + * + * @param i the segment index + */ + void markInvalid(std::size_t index); + + /** + * Marks a segment as matched. + * + * @param i the segment index + */ + void markMatched(std::size_t index); + + /** + * Tests if all segments in the ring have known status + * (matched or invalid). + * + * @return true if all segments have known status + */ + bool isKnown() const; + + /** + * Tests if a segment is marked invalid. + * + * @param index the segment index + * @return true if the segment is invalid + */ + bool isInvalid(std::size_t i) const; + + /** + * Tests whether all segments are invalid. + * + * @return true if all segments are invalid + */ + bool isInvalid() const; + + /** + * Tests whether any segment is invalid. + * + * @return true if some segment is invalid + */ + bool hasInvalid() const; + + /** + * Tests whether the validity state of a ring segment is known. + * + * @param i the index of the ring segment + * @return true if the segment validity state is known + */ + bool isKnown(std::size_t i) const; + + /** + * Finds the previous vertex in the ring which is distinct + * from a given coordinate value. + * + * @param index the index to start the search + * @param pt a coordinate value (which may not be a ring vertex) + * @return the previous distinct vertex in the ring + */ + const Coordinate& findVertexPrev(std::size_t index, const Coordinate& pt) const; + + /** + * Finds the next vertex in the ring which is distinct + * from a given coordinate value. + * + * @param index the index to start the search + * @param pt a coordinate value (which may not be a ring vertex) + * @return the next distinct vertex in the ring + */ + const Coordinate& findVertexNext(std::size_t index, const Coordinate& pt) const; + + /** + * Gets the index of the previous segment in the ring. + * + * @param index a segment index + * @return the index of the previous segment + */ + std::size_t prev(std::size_t index) const; + + /** + * Gets the index of the next segment in the ring. + * + * @param index a segment index + * @return the index of the next segment + */ + std::size_t next(std::size_t index) const; + + void createInvalidLines( + const GeometryFactory* geomFactory, + std::vector>& lines); + +}; + +} // namespace geos.coverage +} // namespace geos + + + + + diff --git a/Sources/geos/include/geos/coverage/CoverageRingEdges.h b/Sources/geos/include/geos/coverage/CoverageRingEdges.h new file mode 100644 index 0000000..5a0f4ac --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageRingEdges.h @@ -0,0 +1,169 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * Copyright (c) 2023 Martin Davis. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // to materialize CoverageEdge + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class Geometry; +class LinearRing; +class MultiPolygon; +class Polygon; +} +namespace coverage { +class CoverageEdge; +} +} + +namespace geos { // geos +namespace coverage { // geos.coverage + +/** + * Models a polygonal coverage as a set of unique {@link CoverageEdge}s, + * linked to the parent rings in the coverage polygons. + * Each edge has either one or two parent rings, depending on whether + * it is an inner or outer edge of the coverage. + * The source coverage is represented as a array of polygonal geometries + * (either {@link geos::geom::Polygon}s or {@link geos::geom::MultiPolygon}s). + * + * @author Martin Davis + */ +class GEOS_DLL CoverageRingEdges { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using LinearRing = geos::geom::LinearRing; + using LineSegment = geos::geom::LineSegment; + using MultiPolygon = geos::geom::MultiPolygon; + using Polygon = geos::geom::Polygon; + +private: + + // Members + const std::vector& m_coverage; + std::map> m_ringEdgesMap; + std::vector m_edges; + std::vector> m_edgeStore; + + /* Turn off copy constructors for MSVC */ + CoverageRingEdges(const CoverageRingEdges&) = delete; + CoverageRingEdges& operator=(const CoverageRingEdges&) = delete; + +public: + + CoverageRingEdges(const std::vector& coverage) + : m_coverage(coverage) + { + build(); + }; + + + std::vector& getEdges() + { + return m_edges; + }; + + /** + * Selects the edges with a given ring count (which can be 1 or 2). + * + * @param ringCount the edge ring count to select (1 or 2) + * @return the selected edges + */ + std::vector selectEdges( + std::size_t ringCount) const; + + /** + * Recreates the polygon coverage from the current edge values. + * + * @return an array of polygonal geometries representing the coverage + */ + std::vector> buildCoverage() const; + + +private: + + void build(); + + void addRingEdges( + const LinearRing* ring, + Coordinate::UnorderedSet& nodes, + LineSegment::UnorderedSet& boundarySegs, + std::map& uniqueEdgeMap); + + void addBoundaryInnerNodes( + const LinearRing* ring, + LineSegment::UnorderedSet& boundarySegs, + Coordinate::UnorderedSet& nodes); + + std::vector extractRingEdges( + const LinearRing* ring, + std::map& uniqueEdgeMap, + Coordinate::UnorderedSet& nodes); + + CoverageEdge* createEdge( + const CoordinateSequence& ring, + std::map& uniqueEdgeMap); + + CoverageEdge* createEdge( + const CoordinateSequence& ring, + std::size_t start, std::size_t end, + std::map& uniqueEdgeMap); + + std::size_t findNextNodeIndex( + const CoordinateSequence& ring, + std::size_t start, + Coordinate::UnorderedSet& nodes) const; + + static std::size_t next( + std::size_t index, + const CoordinateSequence& ring); + + Coordinate::UnorderedSet findMultiRingNodes( + const std::vector& coverage); + + Coordinate::UnorderedSet findBoundaryNodes( + LineSegment::UnorderedSet& lineSegments); + + std::unique_ptr buildPolygonal( + const Geometry* geom) const; + + std::unique_ptr buildMultiPolygon( + const MultiPolygon* geom) const; + + std::unique_ptr buildPolygon( + const Polygon* polygon) const; + + std::unique_ptr buildRing( + const LinearRing* ring) const; + + bool isEdgeDirForward( + const std::vector& ringEdges, + std::size_t index, + const Coordinate& prevPt) const; + + +}; + +} // namespace geos.coverage +} // namespace geos diff --git a/Sources/geos/include/geos/coverage/CoverageSimplifier.h b/Sources/geos/include/geos/coverage/CoverageSimplifier.h new file mode 100644 index 0000000..74df234 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageSimplifier.h @@ -0,0 +1,162 @@ + +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * Copyright (c) 2023 Martin Davis. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class MultiLineString; +} +namespace coverage { +class CoverageEdge; +} +} + +namespace geos { +namespace coverage { // geos::coverage + +/** + * Simplifies the boundaries of the polygons in a polygonal coverage + * while preserving the original coverage topology. + * An area-based simplification algorithm + * (similar to Visvalingam-Whyatt simplification) + * is used to provide high-quality results. + * Also supports simplifying just the inner edges in a coverage, + * which allows simplifying "patches" without affecting their boundary. + * + * The amount of simplification is determined by a tolerance value, + * which is a non-negative quantity. It equates roughly to the maximum + * distance by which a simplified line can change from the original. + * (In fact, it is the square root of the area tolerance used + * in the Visvalingam-Whyatt algorithm.) + * + * The simplified result coverage has the following characteristics: + * + * * It has the same number and types of polygonal geometries as the input + * * Node points (inner vertices shared by three or more polygons, + * or boundary vertices shared by two or more) are not changed + * * If the input is a valid coverage, then so is the result + * + * This class also supports inner simplification, which simplifies + * only edges of the coverage which are adjacent to two polygons. + * This allows partial simplification of a coverage, since a simplified + * subset of a coverage still matches the remainder of the coverage. + * + * The input coverage should be valid according to {@link CoverageValidator}. + * + * @author Martin Davis + */ +class GEOS_DLL CoverageSimplifier { + using CoverageEdge = geos::coverage::CoverageEdge; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using MultiLineString = geos::geom::MultiLineString; + +public: + + /** + * Create a new coverage simplifier instance. + * + * @param coverage a set of polygonal geometries forming a coverage + */ + CoverageSimplifier(const std::vector& coverage); + + /** + * Simplifies the boundaries of a set of polygonal geometries forming a coverage, + * preserving the coverage topology. + * + * @param coverage a set of polygonal geometries forming a coverage + * @param tolerance the simplification tolerance + * @return the simplified polygons + */ + static std::vector> simplify( + std::vector& coverage, + double tolerance); + + static std::vector> simplify( + const std::vector>& coverage, + double tolerance); + + /** + * Simplifies the inner boundaries of a set of polygonal geometries forming a coverage, + * preserving the coverage topology. + * Edges which form the exterior boundary of the coverage are left unchanged. + * + * @param coverage a set of polygonal geometries forming a coverage + * @param tolerance the simplification tolerance + * @return the simplified polygons + */ + static std::vector> simplifyInner( + std::vector& coverage, + double tolerance); + + static std::vector> simplifyInner( + const std::vector>& coverage, + double tolerance); + + /** + * Computes the simplified coverage, preserving the coverage topology. + * + * @param tolerance the simplification tolerance + * @return the simplified polygons + */ + std::vector> simplify( + double tolerance); + + /** + * Computes the inner-boundary simplified coverage, + * preserving the coverage topology, + * and leaving outer boundary edges unchanged. + * + * @param tolerance the simplification tolerance + * @return the simplified polygons + */ + std::vector> simplifyInner( + double tolerance); + + +private: + + // Members + const std::vector& m_input; + const GeometryFactory* m_geomFactory; + + // Methods + void simplifyEdges( + std::vector edges, + const MultiLineString* constraints, + double tolerance); + + void setCoordinates( + std::vector& edges, + const MultiLineString* lines); + + std::vector getFreeRings( + const std::vector& edges) const; + + +}; // CoverageSimplifier + + +} // geos::coverage +} // geos diff --git a/Sources/geos/include/geos/coverage/CoverageUnion.h b/Sources/geos/include/geos/coverage/CoverageUnion.h new file mode 100644 index 0000000..2749488 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageUnion.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos +namespace coverage { // geos::coverage + +/** + * Unions a polygonal coverage in an efficient way. + * + * Valid polygonal coverage topology allows merging polygons in a very efficient way. + * + * @author Martin Davis + * + */ +class GEOS_DLL CoverageUnion { + using Geometry = geos::geom::Geometry; + +private: + + +public: + + /** + * Unions a polygonal coverage. + * + * @param coverage a vector of polygons in the coverage + * @return the union of the coverage polygons + */ + static std::unique_ptr Union(std::vector& coverage); + + /** + * Unions a polygonal coverage. + * + * @param coverage a collection of the polygons in the coverage + * @return the union of the coverage polygons + */ + static std::unique_ptr Union(const Geometry* coverage); + +}; + +} // namespace geos::coverage +} // namespace geos + + + + + + + + + + diff --git a/Sources/geos/include/geos/coverage/CoverageValidator.h b/Sources/geos/include/geos/coverage/CoverageValidator.h new file mode 100644 index 0000000..f285ee6 --- /dev/null +++ b/Sources/geos/include/geos/coverage/CoverageValidator.h @@ -0,0 +1,171 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos +namespace coverage { // geos::coverage + + +/** + * Validates a polygonal coverage, and returns the locations of + * invalid polygon boundary segments if found. + * + * A polygonal coverage is a set of polygons which may be edge-adjacent but do + * not overlap. + * Coverage algorithms (such as {@link CoverageUnion} or simplification) + * generally require the input coverage to be valid to produce correct results. + * A polygonal coverage is valid if: + * + * * The interiors of all polygons do not intersect (are disjoint). + * This is the case if no polygon has a boundary which intersects + * the interior of another polygon, and no two polygons are identical. + * * If the boundaries of polygons intersect, the vertices + * and line segments of the intersection match exactly. + * + * A valid coverage may contain holes (regions of no coverage). + * Sometimes it is desired to detect whether coverages contain + * narrow gaps between polygons + * (which can be a result of digitizing error or misaligned data). + * This class can detect narrow gaps, + * by specifying a maximum gap width using {@link #setGapWidth(double)}. + * Note that this also identifies narrow gaps separating disjoint coverage regions, + * and narrow gores. + * In some situations it may also produce false positives + * (linework identified as part of a gap which is actually wider). + * See CoverageGapFinder for an alternate way to detect gaps which may be more accurate. + * + * @author Martin Davis + * + */ +class GEOS_DLL CoverageValidator { + using Geometry = geos::geom::Geometry; + template + using TemplateSTRtree = geos::index::strtree::TemplateSTRtree; + +private: + + std::vector& m_coverage; + double m_gapWidth = 0.0; + + std::unique_ptr validate( + const Geometry* targetGeom, + TemplateSTRtree& index); + + +public: + + /* + * Creates a new coverage validator + * + * @param coverage a array of polygons representing a polygonal coverage + */ + CoverageValidator(std::vector& coverage) + : m_coverage(coverage) + {}; + + /** + * Sets the maximum gap width, if narrow gaps are to be detected. + * + * @param gapWidth the maximum width of gaps to detect + */ + void setGapWidth(double gapWidth) { + m_gapWidth = gapWidth; + }; + + /** + * Validates the polygonal coverage. + * The result is an array of the same size as the input coverage. + * Each array entry is either null, or if the polygon does not form a valid coverage, + * a linear geometry containing the boundary segments + * which intersect polygon interiors, which are mismatched, + * or form gaps (if checked). + * + * @return an array of nulls or linear geometries + */ + std::vector> validate(); + + /** + * Tests whether a polygonal coverage is valid. + * + * @param coverage an array of polygons forming a coverage + * @return true if the coverage is valid + */ + static bool isValid( + std::vector& coverage); + + /** + * Tests whether a polygonal coverage is valid + * and contains no gaps narrower than a specified width. + * + * @param coverage an array of polygons forming a coverage + * @param gapWidth the maximum width of invalid gaps + * @return true if the coverage is valid with no narrow gaps + */ + static bool isValid( + std::vector& coverage, + double gapWidth); + + /** + * Tests if some element of an array of geometries is a coverage invalidity + * indicator. + * + * @param validateResult an array produced by a polygonal coverage validation + * @return true if the result has at least one invalid indicator + */ + static bool hasInvalidResult( + const std::vector>& validateResult); + + /** + * Validates that a set of polygons forms a valid polygonal coverage. + * The result is a list of the same length as the input, + * containing for each input geometry either + * a linear geometry indicating the locations of invalidities, + * or a null if the geometry is coverage-valid. + * + * @param coverage an array of polygons forming a coverage + * @return an array of linear geometries indicating coverage errors, or nulls + */ + static std::vector> validate( + std::vector& coverage); + + /** + * Validates that a set of polygons forms a valid polygonal coverage + * and contains no gaps narrower than a specified width. + * The result is a list of the same length as the input, + * containing for each input geometry either + * a linear geometry indicating the locations of invalidities, + * or a null if the geometry is coverage-valid. + * + * @param coverage an array of polygons forming a coverage + * @param gapWidth the maximum width of invalid gaps + * @return an array of linear geometries indicating coverage errors, or nulls + */ + static std::vector> validate( + std::vector& coverage, + double gapWidth); +}; + +} // namespace geos::coverage +} // namespace geos diff --git a/Sources/geos/include/geos/coverage/InvalidSegmentDetector.h b/Sources/geos/include/geos/coverage/InvalidSegmentDetector.h new file mode 100644 index 0000000..a1f0850 --- /dev/null +++ b/Sources/geos/include/geos/coverage/InvalidSegmentDetector.h @@ -0,0 +1,129 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace noding { +class SegmentString; +} +namespace coverage { +class CoverageRing; +} +} + +namespace geos { // geos. +namespace coverage { // geos.coverage + +/** + * Detects invalid coverage topology where ring segments interact. + * The inputs to processIntersections(SegmentString, int, SegmentString, int)} + * must be CoverageRing s. + * If an invalid situation is detected the input target segment is + * marked invalid using CoverageRing#markInvalid(int). + * + * This class assumes it is used with SegmentSetMutualIntersector, + * so that segments in the same ring are not evaluated. + * + * @author Martin Davis + * + */ +class GEOS_DLL InvalidSegmentDetector : public geos::noding::SegmentIntersector { + using SegmentString = geos::noding::SegmentString; + using Coordinate = geos::geom::Coordinate; + +private: + + // Members + double distanceTol; + + // Methods + bool isInvalid(const Coordinate& tgt0, const Coordinate& tgt1, + const Coordinate& adj0, const Coordinate& adj1, + CoverageRing* adj, std::size_t indexAdj); + + bool isEqual( + const Coordinate& t0, const Coordinate& t1, + const Coordinate& adj0, const Coordinate& adj1); + + /** + * Checks if the segments are collinear, or if the target segment + * intersects the interior of the adjacent ring. + * Segments which are collinear must be non-equal and hence invalid, + * since matching segments have already been marked as valid and + * are not passed to this code. + * + * @param tgt0 + * @param tgt1 + * @param adj0 + * @param adj1 + * @return + */ + bool isCollinearOrInterior( + const Coordinate& tgt0, const Coordinate& tgt1, + const Coordinate& adj0, const Coordinate& adj1, + CoverageRing* adj, std::size_t indexAdj); + + bool isInteriorSegment( + const Coordinate& intVertex, + const Coordinate& tgt0, const Coordinate& tgt1, + CoverageRing* adj, std::size_t indexAdj); + + static bool isNearlyParallel( + const Coordinate& p00, const Coordinate& p01, + const Coordinate& p10, const Coordinate& p11, + double distanceTol); + + +public: + + /** + * Creates an invalid segment detector. + */ + InvalidSegmentDetector() {}; + + InvalidSegmentDetector(double p_distanceTol) + : distanceTol(p_distanceTol) {}; + + + bool isDone() const override { + // process all intersections + return false; + }; + + /** + * Process interacting segments. + * The input order is important. + * The adjacent segment is first, the target is second. + * The inputs must be CoverageRing. + */ + void processIntersections( + SegmentString* ssAdj, std::size_t iAdj, + SegmentString* ssTarget, std::size_t iTarget) override; + + + +}; + +} // namespace geos.coverage +} // namespace geos + + diff --git a/Sources/geos/include/geos/coverage/TPVWSimplifier.h b/Sources/geos/include/geos/coverage/TPVWSimplifier.h new file mode 100644 index 0000000..0b904c8 --- /dev/null +++ b/Sources/geos/include/geos/coverage/TPVWSimplifier.h @@ -0,0 +1,223 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + + +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Envelope; +class Geometry; +class GeometryFactory; +class LineString; +class MultiLineString; +} +} + +namespace geos { +namespace coverage { // geos::coverage + + +/** + * Computes a Topology-Preserving Visvalingam-Whyatt simplification + * of a set of input lines. + * The simplified lines will contain no more intersections than are present + * in the original input. + * Line and ring endpoints are preserved, except for rings + * which are flagged as "free". + * + * The amount of simplification is determined by a tolerance value, + * which is a non-zero quantity. + * It is the square root of the area tolerance used + * in the Visvalingam-Whyatt algorithm. + * This equates roughly to the maximum + * distance by which a simplified line can change from the original. + * + * @author mdavis + * + */ +class GEOS_DLL TPVWSimplifier +{ + using Corner = geos::coverage::Corner; + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineString = geos::geom::LineString; + using MultiLineString = geos::geom::MultiLineString; + using VertexSequencePackedRtree = geos::index::VertexSequencePackedRtree; + template + using TemplateSTRtree = geos::index::strtree::TemplateSTRtree; + using LinkedLine = geos::simplify::LinkedLine; + +public: + + // Prototype + class EdgeIndex; + + /* private static */ + class Edge { + + public: + + // Members + double areaTolerance; + bool isFreeRing; + const Envelope* envelope; + std::size_t nbPts; + LinkedLine linkedLine; + VertexSequencePackedRtree vertexIndex; + std::size_t minEdgeSize; + + /** + * Creates a new edge. + * The endpoints of the edge are preserved during simplification, + * unless it is a ring and the isFreeRing flag is set. + * + * @param p_inputLine the line or ring + * @param p_isFreeRing whether a ring endpoint can be removed + * @param p_areaTolerance the simplification tolerance + */ + Edge(const LineString* p_inputLine, bool p_isFreeRing, double p_areaTolerance); + + const Coordinate& getCoordinate(std::size_t index) const; + + const Envelope* getEnvelopeInternal() const; + + std::size_t size() const; + + std::unique_ptr simplify(EdgeIndex& edgeIndex); + + void createQueue(Corner::PriorityQueue& pq); + + void addCorner(std::size_t i, Corner::PriorityQueue& cornerQueue); + + bool isRemovable(Corner& corner, EdgeIndex& edgeIndex) const; + + /** + * Tests if any vertices in a line intersect the corner triangle. + * Uses the vertex spatial index for efficiency. + * + * @param corner the corner vertices + * @param cornerEnv the envelope of the corner + * @param edge the hull to test + * @return true if there is an intersecting vertex + */ + bool hasIntersectingVertex(const Corner& corner, + const Envelope& cornerEnv, + const Edge& edge) const; + + std::vector query(const Envelope& cornerEnv) const; + + /** + * Removes a corner by removing the apex vertex from the ring. + * Two new corners are created with apexes + * at the other vertices of the corner + * (if they are non-convex and thus removable). + * + * @param corner the corner to remove + * @param cornerQueue the corner queue + */ + void removeCorner( + Corner& corner, + Corner::PriorityQueue& cornerQueue); + + }; // Edge + + class EdgeIndex + { + public: + + TemplateSTRtree index; + + void add(std::vector& edges); + + std::vector query(const Envelope& queryEnv); + + }; // EdgeIndex + + + /** + * Simplifies a set of lines, preserving the topology of the lines. + * + * @param lines the lines to simplify + * @param distanceTolerance the simplification tolerance + * @return the simplified lines + */ + static std::unique_ptr simplify( + const MultiLineString* lines, + double distanceTolerance); + + /** + * Simplifies a set of lines, preserving the topology of the lines between + * themselves and a set of linear constraints. + * The endpoints of lines are preserved. + * The endpoint of rings are preserved as well, unless + * the ring is indicated as "free" via a bit flag with the same index. + * + * @param lines the lines to simplify + * @param freeRings flags indicating which ring edges do not have node endpoints + * @param constraintLines the linear constraints + * @param distanceTolerance the simplification tolerance + * @return the simplified lines + */ + static std::unique_ptr simplify( + const MultiLineString* lines, + std::vector& freeRings, + const MultiLineString* constraintLines, + double distanceTolerance); + + // Constructor + TPVWSimplifier(const MultiLineString* lines, + double distanceTolerance); + + +private: + + // Members + const MultiLineString* inputLines; + std::vector isFreeRing; + double areaTolerance; + const GeometryFactory* geomFactory; + const MultiLineString* constraintLines; + + + // Methods + void setFreeRingIndices(std::vector& freeRing); + + void setConstraints(const MultiLineString* constraints); + + std::unique_ptr simplify(); + + std::vector createEdges( + const MultiLineString* lines, + std::vector& freeRing); + + +}; // TPVWSimplifier + + +} // geos::coverage +} // geos diff --git a/Sources/geos/include/geos/coverage/VertexRingCounter.h b/Sources/geos/include/geos/coverage/VertexRingCounter.h new file mode 100644 index 0000000..c0982ad --- /dev/null +++ b/Sources/geos/include/geos/coverage/VertexRingCounter.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos { +namespace geom { +class CoordinateSequence; +class Geometry; +} +} + +namespace geos { +namespace coverage { // geos::coverage + +class VertexRingCounter : public geos::geom::CoordinateSequenceFilter +{ + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + +public: + + VertexRingCounter(std::map& counts) + : vertexCounts(counts) + {}; + + bool isGeometryChanged() const override { + return false; + } + + bool isDone() const override { + return false; + } + + void filter_ro(const CoordinateSequence& seq, std::size_t i) override; + + static void count( + const std::vector& geoms, + std::map& counts); + +private: + + std::map& vertexCounts; + +}; // VertexRingCounter + + + +} // geos::coverage +} // geos diff --git a/Sources/geos/include/geos/dissolve/DissolveEdgeGraph.h b/Sources/geos/include/geos/dissolve/DissolveEdgeGraph.h new file mode 100644 index 0000000..0283c97 --- /dev/null +++ b/Sources/geos/include/geos/dissolve/DissolveEdgeGraph.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2025 Martin Davis + * Copyright (C) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateXYZM; +} +namespace edgegraph { + class HalfEdge; +} +namespace dissolve { + // class DissolveHalfEdge; +} +} + + +namespace geos { // geos. +namespace dissolve { // geos.dissolve + + +class GEOS_DLL DissolveEdgeGraph : public edgegraph::EdgeGraph { + + +private: + + std::deque dhEdges; + +public: + + edgegraph::HalfEdge* createEdge(const geom::CoordinateXYZM& p0) override; + + DissolveEdgeGraph() {}; + ~DissolveEdgeGraph() {}; + + /** + * Disable copy construction and assignment. Needed to make this + * class compile under MSVC, because it has a vector + * as a member. (See https://stackoverflow.com/q/29565299) + */ + DissolveEdgeGraph(const DissolveEdgeGraph&) = delete; + DissolveEdgeGraph& operator=(const DissolveEdgeGraph&) = delete; + +}; + +} // namespace geos.dissolve +} // namespace geos + diff --git a/Sources/geos/include/geos/dissolve/DissolveHalfEdge.h b/Sources/geos/include/geos/dissolve/DissolveHalfEdge.h new file mode 100644 index 0000000..0168c19 --- /dev/null +++ b/Sources/geos/include/geos/dissolve/DissolveHalfEdge.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2025 Martin Davis + * Copyright (C) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateXYZM; +} +namespace edgegraph { +} +} + + +namespace geos { // geos. +namespace dissolve { // geos.dissolve + + +class GEOS_DLL DissolveHalfEdge : public edgegraph::MarkHalfEdge { + + +private: + + bool m_isStart = false; + + +public: + + DissolveHalfEdge(const geom::CoordinateXYZM& orig) + : edgegraph::MarkHalfEdge(orig) + , m_isStart(false) + {} + + /** + * Tests whether this edge is the starting segment + * in a LineString being dissolved. + * + * @return true if this edge is a start segment + */ + bool isStart(); + + /** + * Sets this edge to be the start segment of an input LineString. + */ + void setStart(); + +}; + +} // namespace geos.dissolve +} // namespace geos + diff --git a/Sources/geos/include/geos/dissolve/LineDissolver.h b/Sources/geos/include/geos/dissolve/LineDissolver.h new file mode 100644 index 0000000..cfae61f --- /dev/null +++ b/Sources/geos/include/geos/dissolve/LineDissolver.h @@ -0,0 +1,195 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2025 Martin Davis + * Copyright (C) 2025 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +// #include +// #include +// #include +#include +#include + + +// Forward declarations +namespace geos { +namespace dissolve { + class DissolveHalfEdge; +} +namespace edgegraph { + class HalfEdge; +} +namespace geom { + class CoordinateSequence; + class Geometry; + class GeometryFactory; + // class LineString; +} +} + + +namespace geos { // geos. +namespace dissolve { // geos.dissolve + + +/** + * Dissolves the linear components + * from a collection of Geometry + * into a set of maximal-length LineString + * in which every unique segment appears once only. + * The output linestrings run between node vertices + * of the input, which are vertices which have + * either degree 1, or degree 3 or greater. + * + * Use cases for dissolving linear components + * include generalization + * (in particular, simplifying polygonal coverages), + * and visualization + * (in particular, avoiding symbology conflicts when + * depicting shared polygon boundaries). + * + * This class does **not** node the input lines. + * If there are line segments crossing in the input, + * they will still cross in the output. + * + * @author Martin Davis + * + */ +class GEOS_DLL LineDissolver { + + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineString = geos::geom::LineString; + using HalfEdge = geos::edgegraph::HalfEdge; + +private: + + std::unique_ptr result; + const GeometryFactory* factory = nullptr; + DissolveEdgeGraph graph; + std::vector> lines; + std::stack nodeEdgeStack; + DissolveHalfEdge* ringStartEdge = nullptr; + + + void computeResult(); + + void process(HalfEdge* e); + + /** + * Adds edges around this node to the stack. + * + * @param node + */ + void stackEdges(HalfEdge* node); + + /** + * For each edge in stack + * (which must originate at a node) + * extracts the line it initiates. + */ + void buildLines(); + + /** + * Updates the tracked ringStartEdge + * if the given edge has a lower origin + * (using the standard Coordinate ordering). + * + * Identifying the lowest starting node meets two goals: + * + * * It ensures that isolated input rings are created using the original node and orientation + * * For isolated rings formed from multiple input linestrings, + * it provides a canonical node and orientation for the output + * (rather than essentially random, and thus hard to test). + * + * @param e + */ + void updateRingStartEdge(DissolveHalfEdge* e); + + /** + * Builds a line starting from the given edge. + * The start edge origin is a node (valence = 1 or >= 3), + * unless it is part of a pure ring. + * A pure ring has no other incident lines. + * In this case the start edge may occur anywhere on the ring. + * + * The line is built up to the next node encountered, + * or until the start edge is re-encountered + * (which happens if the edges form a ring). + * + * @param eStart + */ + void buildLine(HalfEdge* eStart); + + void buildRing(HalfEdge* eStartRing); + + void addLine(std::unique_ptr& cs); + + +public: + + LineDissolver() : result(nullptr) {}; + + /** + * Dissolves the linear components in a geometry. + * + * @param g the geometry to dissolve + * @return the dissolved lines + */ + static std::unique_ptr dissolve(const Geometry* g); + + /** + * Adds a Geometry to be dissolved. + * Any number of geometries may be added by calling this method multiple times. + * Any type of Geometry may be added. The constituent linework will be + * extracted to be dissolved. + * + * @param geometry geometry to be line-merged + */ + void add(const Geometry* geometry); + + /** + * Adds a collection of Geometries to be processed. May be called multiple times. + * Any dimension of Geometry may be added; the constituent linework will be + * extracted. + * + * @param geometries the geometries to be line-merged + */ + void add(std::vector geometries); + + void add(const LineString* lineString); + + /** + * Gets the dissolved result as a MultiLineString. + * + * @return the dissolved lines + */ + std::unique_ptr getResult(); + + /** + * Disable copy construction and assignment. Needed to make this + * class compile under MSVC, because it has a vector + * as a member. (See https://stackoverflow.com/q/29565299) + */ + LineDissolver(const LineDissolver&) = delete; + LineDissolver& operator=(const LineDissolver&) = delete; + + +}; + +} // namespace geos.dissolve +} // namespace geos + diff --git a/Sources/geos/include/geos/edgegraph/EdgeGraph.h b/Sources/geos/include/geos/edgegraph/EdgeGraph.h new file mode 100644 index 0000000..c6705bd --- /dev/null +++ b/Sources/geos/include/geos/edgegraph/EdgeGraph.h @@ -0,0 +1,138 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#undef EDGEGRAPH_HEAPHACK + +namespace geos { +namespace edgegraph { // geos.edgegraph + + +/** + * A graph comprised of {@link HalfEdge}s. + * It supports tracking the vertices in the graph + * via edges incident on them, + * to allow efficient lookup of edges and vertices. + * + * This class may be subclassed to use a + * different subclass of HalfEdge, + * by overriding {@link createEdge}. + * If additional logic is required to initialize + * edges then {@link addEdge} + * can be overridden as well. + * + * @author Martin Davis + * + */ + +class GEOS_DLL EdgeGraph { + +private: + + std::deque edges; + std::map vertexMap; + + HalfEdge* create(const geom::CoordinateXYZM& p0, const geom::CoordinateXYZM& p1); + + +protected: + + /** + * Creates a single HalfEdge. + * Override to use a different HalfEdge subclass. + * + * @param orig the origin location + * @return a new HalfEdge with the given origin + */ + virtual HalfEdge* createEdge(const geom::CoordinateXYZM& orig); + + /** + * Inserts an edge not already present into the graph. + * + * @param orig the edge origin location + * @param dest the edge destination location + * @param eAdj an existing edge with same orig (if any) + * @return the created edge + */ + HalfEdge* insert(const geom::CoordinateXYZM& orig, const geom::CoordinateXYZM& dest, HalfEdge* eAdj); + + +public: + + /** + * Initialized + */ + EdgeGraph() {}; + virtual ~EdgeGraph() = default; + + /** + * Adds an edge between the coordinates orig and dest + * to this graph. + * Only valid edges can be added (in particular, zero-length segments cannot be added) + * + * @param orig the edge origin location + * @param dest the edge destination location. + * @return the created edge + * @return null if the edge was invalid and not added + * + * @see isValidEdge(Coordinate, Coordinate) + */ + HalfEdge* addEdge(const geom::CoordinateXYZM& orig, const geom::CoordinateXYZM& dest); + + /** + * Tests if the given coordinates form a valid edge (with non-zero length). + * + * @param orig the start coordinate + * @param dest the end coordinate + * @return true if the edge formed is valid + */ + static bool isValidEdge(const geom::CoordinateXY& orig, const geom::CoordinateXY& dest); + + void getVertexEdges(std::vector& edgesOut); + + /** + * Finds an edge in this graph with the given origin + * and destination, if one exists. + * + * @param orig the origin location + * @param dest the destination location. + * @return an edge with the given orig and dest, or null if none exists + */ + HalfEdge* findEdge(const geom::CoordinateXY& orig, const geom::CoordinateXY& dest); + + + + + + +}; + + +} // namespace geos.edgegraph +} // namespace geos + + + diff --git a/Sources/geos/include/geos/edgegraph/EdgeGraphBuilder.h b/Sources/geos/include/geos/edgegraph/EdgeGraphBuilder.h new file mode 100644 index 0000000..38fe035 --- /dev/null +++ b/Sources/geos/include/geos/edgegraph/EdgeGraphBuilder.h @@ -0,0 +1,91 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class LineString; +class Geometry; +class GeometryCollection; +} +} + +namespace geos { +namespace edgegraph { // geos.edgegraph + + +/** + * Builds an edge graph from geometries containing edges. + * + * @author mdavis + * + */ +class GEOS_DLL EdgeGraphBuilder { + +private: + + /* members */ + std::unique_ptr graph; + + + +public: + + EdgeGraphBuilder() : + graph(new EdgeGraph()) + {}; + + static std::unique_ptr build(const geom::GeometryCollection* geoms); + + std::unique_ptr getGraph(); + + /** + * Adds the edges of a Geometry to the graph. + * May be called multiple times. + * Any dimension of Geometry may be added; the constituent edges are + * extracted. + * + * @param geometry geometry to be added + */ + void add(const geom::Geometry* geometry); + void add(const geom::LineString* linestring); + + /** + * Adds the edges in a collection of {@link geom::Geometry} to the graph. + * May be called multiple times. + * Any dimension of Geometry may be added. + * + * @param geometries the geometries to be added + */ + void add(const geom::GeometryCollection* geometries); + +}; + + +} // namespace geos.edgegraph +} // namespace geos + + + + diff --git a/Sources/geos/include/geos/edgegraph/HalfEdge.h b/Sources/geos/include/geos/edgegraph/HalfEdge.h new file mode 100644 index 0000000..20662f7 --- /dev/null +++ b/Sources/geos/include/geos/edgegraph/HalfEdge.h @@ -0,0 +1,315 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace edgegraph { // geos.edgegraph + +/** + * Represents a directed component of an edge in an {@link EdgeGraph}. + * HalfEdges link vertices whose locations are defined by {@link geom::Coordinate}s. + * HalfEdges start at an origin vertex, + * and terminate at a destination vertex. + * HalfEdges always occur in symmetric pairs, with the {@link sym()} method + * giving access to the oppositely-oriented component. + * HalfEdges and the methods on them form an edge algebra, + * which can be used to traverse and query the topology + * of the graph formed by the edges. + * + * To support graphs where the edges are sequences of coordinates + * each edge may also have a direction point supplied. + * This is used to determine the ordering + * of the edges around the origin. + * HalfEdges with the same origin are ordered + * so that the ring of edges formed by them is oriented CCW. + * + * By design HalfEdges carry minimal information + * about the actual usage of the graph they represent. + * They can be subclassed to carry more information if required. + * + * HalfEdges form a complete and consistent data structure by themselves, + * but an {@link EdgeGraph} is useful to allow retrieving edges + * by vertex and edge location, as well as ensuring + * edges are created and linked appropriately. + * + * @author Martin Davis + * + */ +class GEOS_DLL HalfEdge { + +private: + + /* members */ + geom::CoordinateXYZM m_orig; + HalfEdge* m_sym; + HalfEdge* m_next; + + + /** + * Sets the symmetric (opposite) edge to this edge. + * + * @param e the sym edge to set + */ + void setSym(HalfEdge* e) { m_sym = e; }; + + /** + * Finds the insertion edge for a edge + * being added to this origin, + * ensuring that the star of edges + * around the origin remains fully CCW. + * + * @param eAdd the edge being added + * @return the edge to insert after + */ + HalfEdge* insertionEdge(HalfEdge* eAdd); + + /** + * Insert an edge with the same origin after this one. + * Assumes that the inserted edge is in the correct + * position around the ring. + * + * @param e the edge to insert (with same origin) + */ + void insertAfter(HalfEdge* e); + + /** + * Finds the lowest edge around the origin, + * using the standard edge ordering. + * + * @return the lowest edge around the origin + */ + const HalfEdge* findLowest() const; + +protected: + + /** + * Gets the direction point of this edge. + * In the base case this is the dest coordinate + * of the edge. + * Subclasses may override to + * allow a HalfEdge to represent an edge with more than two coordinates. + * + * @return the direction point for the edge + */ + virtual const geom::CoordinateXYZM& directionPt() const { return dest(); }; + + +public: + + /** + * Creates a half-edge originating from a given coordinate. + * + * @param p_orig the origin coordinate + */ + HalfEdge(const geom::CoordinateXYZM& p_orig) : + m_orig(p_orig) + {}; + + virtual ~HalfEdge() {}; + + /** + * Creates a HalfEdge pair, using the HalfEdge type of the graph subclass. + * + * @param p0 a vertex coordinate + * @param p1 a vertex coordinate + * @return the HalfEdge with origin at p0 + */ + static HalfEdge* create(const geom::CoordinateXYZM& p0, const geom::CoordinateXYZM& p1); + + /** + * Links this edge with its sym (opposite) edge. + * This must be done for each pair of edges created. + * + * @param p_sym the sym edge to link. + */ + void link(HalfEdge* p_sym); + + /** + * Gets the origin coordinate of this edge. + * + * @return the origin coordinate + */ + const geom::CoordinateXYZM& orig() const { return m_orig; }; + + /** + * Gets the destination coordinate of this edge. + * + * @return the destination coordinate + */ + const geom::CoordinateXYZM& dest() const { return m_sym->m_orig; } + + /** + * The X component of the direction vector. + * + * @return the X component of the direction vector + */ + double directionX() const { return directionPt().x - m_orig.x; } + + /** + * The Y component of the direction vector. + * + * @return the Y component of the direction vector + */ + double directionY() const { return directionPt().y - m_orig.y; } + + /** + * Gets the symmetric pair edge of this edge. + * + * @return the symmetric pair edge + */ + HalfEdge* sym() const { return m_sym; }; + + /** + * Gets the next edge CCW around the + * destination vertex of this edge, + * with the dest vertex as its origin. + * If the vertex has degree 1 then this is the sym edge. + * + * @return the next edge + */ + HalfEdge* next() const { return m_next; }; + + /** + * Gets the edge previous to this one + * (with dest being the same as this orig). + * + * It is always true that e.next().prev() == e + * + * Note that this requires a scan of the origin edges, + * so may not be efficient for some uses. + * + * @return the previous edge to this one + */ + HalfEdge* prev() const; + + /** + * Gets the next edge CCW around the origin of this edge, + * with the same origin. + * + * e.oNext() is equal to e.sym().next() + * + * @return the next edge around the origin + */ + HalfEdge* oNext() const { return m_sym->m_next; }; + + /** + * Sets the next edge CCW around the destination vertex of this edge. + * + * @param e the next edge + */ + void setNext(HalfEdge* e) { m_next = e; }; + + /** + * Finds the edge starting at the origin of this edge + * with the given dest vertex, + * if any. + * + * @param dest the dest vertex to search for + * @return the edge with the required dest vertex, if it exists, + * or null + */ + HalfEdge* find(const geom::CoordinateXY& dest); + + /** + * Tests whether this edge has the given orig and dest vertices. + * + * @param p0 the origin vertex to test + * @param p1 the destination vertex to test + * @return true if the vertices are equal to the ones of this edge + */ + bool equals(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) const; + + /** + * Inserts an edge + * into the ring of edges around the origin vertex of this edge, + * ensuring that the edges remain ordered CCW. + * The inserted edge must have the same origin as this edge. + * + * @param eAdd the edge to insert + */ + void insert(HalfEdge* eAdd); + + /** + * Tests whether the edges around the origin + * are sorted correctly. + * Note that edges must be strictly increasing, + * which implies no two edges can have the same direction point. + * + * @return true if the origin edges are sorted correctly + */ + bool isEdgesSorted() const; + + /** + * Implements the total order relation: + * + * The angle of edge a is greater than the angle of edge b, + * where the angle of an edge is the angle made by + * the first segment of the edge with the positive x-axis + * + * When applied to a list of edges originating at the same point, + * this produces a CCW ordering of the edges around the point. + * + * Using the obvious algorithm of computing the angle is not robust, + * since the angle calculation is susceptible to roundoff error. + * A robust algorithm is: + * + * * First, compare the quadrants the edge vectors lie in. + * If the quadrants are different, + * it is trivial to determine which edge has a greater angle. + * + * * if the vectors lie in the same quadrant, the + * geom::Orientation::index() function + * can be used to determine the relative orientation of the vectors. + */ + int compareAngularDirection(const HalfEdge* e) const; + int compareTo(const HalfEdge* e) const { return compareAngularDirection(e); }; + + /** + * Computes the degree of the origin vertex. + * The degree is the number of edges + * originating from the vertex. + * + * @return the degree of the origin vertex + */ + int degree(); + + /** + * Finds the first node previous to this edge, if any. + * A node has degree <> 2. + * If no such node exists (i.e. the edge is part of a ring) + * then null is returned. + * + * @return an edge originating at the node prior to this edge, if any, + * or null if no node exists + */ + HalfEdge* prevNode(); + + friend std::ostream& operator<< (std::ostream& os, const HalfEdge& el); + static void toStringNode(const HalfEdge* he, std::ostream& os); + +}; + + +} // namespace geos.edgegraph +} // namespace geos + + + diff --git a/Sources/geos/include/geos/edgegraph/MarkHalfEdge.h b/Sources/geos/include/geos/edgegraph/MarkHalfEdge.h new file mode 100644 index 0000000..11eaeb9 --- /dev/null +++ b/Sources/geos/include/geos/edgegraph/MarkHalfEdge.h @@ -0,0 +1,111 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include + +#include +#include +#include + +namespace geos { +namespace edgegraph { // geos.edgegraph + +class GEOS_DLL MarkHalfEdge : public HalfEdge { + +private: + + bool m_isMarked; + +public: + + /** + * Creates a new marked edge. + * + * @param orig the coordinate of the edge origin + */ + MarkHalfEdge(const geom::CoordinateXYZM& p_orig) : + HalfEdge(p_orig), + m_isMarked(false) + {}; + + /** + * Tests whether the given edge is marked. + * + * @param e the edge to test + * @return true if the edge is marked + */ + static bool isMarked(HalfEdge* e); + + /** + * Marks the given edge. + * + * @param e the edge to mark + */ + static void mark(HalfEdge* e); + + /** + * Sets the mark for the given edge to a boolean value. + * + * @param e the edge to set + * @param isMarked the mark value + */ + static void setMark(HalfEdge* e, bool isMarked); + + /** + * Sets the mark for the given edge pair to a boolean value. + * + * @param e an edge of the pair to update + * @param isMarked the mark value to set + */ + static void setMarkBoth(HalfEdge* e, bool isMarked); + + /** + * Marks the edges in a pair. + * + * @param e an edge of the pair to mark + */ + static void markBoth(HalfEdge* e); + + /** + * Tests whether this edge is marked. + * + * @return true if this edge is marked + */ + bool isMarked() const { return m_isMarked; } + + /** + * Marks this edge. + * + */ + void mark() { m_isMarked = true; } + + /** + * Sets the value of the mark on this edge. + * + * @param isMarked the mark value to set + */ + void setMark(bool p_isMarked) { m_isMarked = p_isMarked; } + +}; + + +} // namespace geos.edgegraph +} // namespace geos + + + diff --git a/Sources/geos/include/geos/geom.h b/Sources/geos/include/geos/geom.h new file mode 100644 index 0000000..5eadd2f --- /dev/null +++ b/Sources/geos/include/geos/geom.h @@ -0,0 +1,146 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +/** \mainpage GEOS - Geometry Engine Open Source + * + * \section intro_sec Introduction + * + * Geometry Engine Open Source is a C++ port of the Java Topology Suite + * released under the LGPL license. + * It has interfaces for C++ and C. + * + * \section getstart_sec Getting Started + * + * The recommended low-level interface to the GEOS library + * is the simplified \ref c_iface. This will ensure stability of the + * API and the ABI of the library during performance improvements + * that will likely change classes definitions. + * + * If you don't care about adapting/rebuilding your client code + * you can still use the \ref cpp_iface. + */ + +/** \page c_iface C wrapper interface + * + * \section overview_c Overview + * + * This is the preferred access method for GEOS. + * + * It is designed to keep binary compatibility across releases. + * + * \section Usage + * + * In order to use the C-API of geos you must link your code against + * libgeos_c.so and include the geos_c.h header file, which also contain + * function-level documentation. + * + */ + +/** \page cpp_iface C++ interface + * + * \section overview_cpp Overview + * + * Main class is geos::geom::Geometry, from which all geometry types + * derive. + * + * Construction and destruction of Geometries is done + * using geos::geom::GeometryFactory. + * + * You'll feed it geos::geom::CoordinateSequence + * for base geometries or vectors of geometries for collections. + * + * If you need to construct geometric shaped geometries, you + * can use geos::geom::GeometricShapeFactory. + * + * GEOS version info (as a string) can be obtained using + * geos::geom::geosversion(). The JTS version this release has been + * ported from is available throu geos::geom::jtsport(). + * + * \section io_sect Input / Output + * + * For WKT input/output you can use geos::io::WKTReader and geos::io::WKTWriter + * + * For WKB input/output you can use geos::io::WKBReader and geos::io::WKBWriter + * + * \section exc_sect Exceptions + * + * Internal exceptions are thrown as instances geos::util::GEOSException or + * derived classes. GEOSException derives from std::exception. + * + * Note that prior to version 3.0.0, GEOSException were thrown by + * pointer, and did not derive from std::exception. + * + */ + + +namespace geos { + +/// Contains the Geometry interface hierarchy and supporting classes. +/// +/// The Java Topology Suite (JTS) is a Java API that implements a core +/// set of spatial data operations using an explicit precision model +/// and robust geometric algorithms. JTS is int ended to be used in the +/// development of applications that support the validation, cleaning, +/// integration and querying of spatial datasets. +/// +/// JTS attempts to implement the OpenGIS Simple Features Specification +/// (SFS) as accurately as possible. In some cases the SFS is unclear +/// or omits a specification; in this case J TS attempts to choose +/// a reasonable and consistent alternative. Differences from and +/// elaborations of the SFS are documented in this specification. +/// +///

Package Specification

+/// +/// - Java Topology Suite Technical Specifications +/// - +/// OpenGIS Simple Features Specification for SQL +/// +namespace geom { // geos::geom + +} // namespace geos::geom +} // namespace geos + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#ifdef __GNUC__ +#warning *** DEPRECATED: You are using deprecated header geom.h. Please, update your sources according to new layout of GEOS headers and namespaces +#endif + +using namespace geos::geom; + + diff --git a/Sources/geos/include/geos/geom/CircularArc.h b/Sources/geos/include/geos/geom/CircularArc.h new file mode 100644 index 0000000..283eaf3 --- /dev/null +++ b/Sources/geos/include/geos/geom/CircularArc.h @@ -0,0 +1,273 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +/// A CircularArc is a reference to three points that define a circular arc. +/// It provides for the lazy calculation of various arc properties such as the center, radius, and orientation +class GEOS_DLL CircularArc { +public: + + using CoordinateXY = geom::CoordinateXY; + + CircularArc(const CoordinateXY& q0, const CoordinateXY& q1, const CoordinateXY& q2) + : p0(q0) + , p1(q1) + , p2(q2) + , m_center_known(false) + , m_radius_known(false) + , m_orientation_known(false) + {} + + const CoordinateXY& p0; + const CoordinateXY& p1; + const CoordinateXY& p2; + + /// Return the orientation of the arc as one of: + /// - algorithm::Orientation::CLOCKWISE, + /// - algorithm::Orientation::COUNTERCLOCKWISE + /// - algorithm::Orientation::COLLINEAR + int orientation() const { + if (!m_orientation_known) { + m_orientation = algorithm::Orientation::index(p0, p1, p2); + m_orientation_known = true; + } + return m_orientation; + } + + /// Return the center point of the circle associated with this arc + const CoordinateXY& getCenter() const { + if (!m_center_known) { + m_center = algorithm::CircularArcs::getCenter(p0, p1, p2); + m_center_known = true; + } + + return m_center; + } + + /// Return the radius of the circle associated with this arc + double getRadius() const { + if (!m_radius_known) { + m_radius = getCenter().distance(p0); + m_radius_known = true; + } + + return m_radius; + } + + /// Return whether this arc forms a complete circle + bool isCircle() const { + return p0.equals(p2); + } + + /// Returns whether this arc forms a straight line (p0, p1, and p2 are collinear) + bool isLinear() const { + return std::isnan(getRadius()); + } + + /// Return the inner angle of the sector associated with this arc + double getAngle() const { + if (isCircle()) { + return 2*MATH_PI; + } + + /// Even Rouault: + /// potential optimization?: using crossproduct(p0 - center, p2 - center) = radius * radius * sin(angle) + /// could yield the result by just doing a single asin(), instead of 2 atan2() + /// actually one should also likely compute dotproduct(p0 - center, p2 - center) = radius * radius * cos(angle), + /// and thus angle = atan2(crossproduct(p0 - center, p2 - center) , dotproduct(p0 - center, p2 - center) ) + auto t0 = theta0(); + auto t2 = theta2(); + + if (orientation() == algorithm::Orientation::COUNTERCLOCKWISE) { + std::swap(t0, t2); + } + + if (t0 < t2) { + t0 += 2*MATH_PI; + } + + auto diff = t0-t2; + + return diff; + } + + /// Return the length of the arc + double getLength() const { + if (isLinear()) { + return p0.distance(p2); + } + + return getAngle()*getRadius(); + } + + /// Return the area enclosed by the arc p0-p1-p2 and the line segment p2-p0 + double getArea() const { + if (isLinear()) { + return 0; + } + + auto R = getRadius(); + auto theta = getAngle(); + return R*R/2*(theta - std::sin(theta)); + } + + /// Return the angle of p0 + double theta0() const { + return std::atan2(p0.y - getCenter().y, p0.x - getCenter().x); + } + + /// Return the angle of p2 + double theta2() const { + return std::atan2(p2.y - getCenter().y, p2.x - getCenter().x); + } + + /// Check to see if a coordinate lies on the arc + /// Only the angle is checked, so it is assumed that the point lies on + /// the circle of which this arc is a part. + bool containsPointOnCircle(const CoordinateXY& q) const { + double theta = std::atan2(q.y - getCenter().y, q.x - getCenter().x); + return containsAngle(theta); + } + + /// Check to see if a coordinate lies on the arc, after testing whether + /// it lies on the circle. + bool containsPoint(const CoordinateXY& q) { + if (q == p0 || q == p1 || q == p2) { + return true; + } + + auto dist = std::abs(q.distance(getCenter()) - getRadius()); + + if (dist > 1e-8) { + return false; + } + + if (triangulate::quadedge::TrianglePredicate::isInCircleNormalized(p0, p1, p2, q) != geom::Location::BOUNDARY) { + return false; + } + + return containsPointOnCircle(q); + } + + /// Check to see if a given angle lies on this arc + bool containsAngle(double theta) const { + auto t0 = theta0(); + auto t2 = theta2(); + + if (theta == t0 || theta == t2) { + return true; + } + + if (orientation() == algorithm::Orientation::COUNTERCLOCKWISE) { + std::swap(t0, t2); + } + + t2 -= t0; + theta -= t0; + + if (t2 < 0) { + t2 += 2*MATH_PI; + } + if (theta < 0) { + theta += 2*MATH_PI; + } + + return theta >= t2; + } + + /// Return true if the arc is pointing positive in the y direction + /// at the location of a specified point. The point is assumed to + /// be on the arc. + bool isUpwardAtPoint(const CoordinateXY& q) const { + auto quad = geom::Quadrant::quadrant(getCenter(), q); + bool isUpward; + + if (orientation() == algorithm::Orientation::CLOCKWISE) { + isUpward = (quad == geom::Quadrant::SW || quad == geom::Quadrant::NW); + } else { + isUpward = (quad == geom::Quadrant::SE || quad == geom::Quadrant::NE); + } + + return isUpward; + } + + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = geom::CoordinateXY; + using pointer = const geom::CoordinateXY*; + using reference = const geom::CoordinateXY&; + + Iterator(const CircularArc& arc, int i) : m_arc(arc), m_i(i) {} + + reference operator*() const { + return m_i == 0 ? m_arc.p0 : (m_i == 1 ? m_arc.p1 : m_arc.p2); + } + + Iterator& operator++() { + m_i++; + return *this; + } + + Iterator operator++(int) { + Iterator ret = *this; + m_i++; + return ret; + } + + bool operator==(const Iterator& other) const { + return m_i == other.m_i; + } + + bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + private: + const CircularArc& m_arc; + int m_i; + + }; + + Iterator begin() const { + return Iterator(*this, 0); + } + + Iterator end() const { + return Iterator(*this, 3); + } + +private: + mutable CoordinateXY m_center; + mutable double m_radius; + mutable int m_orientation; + mutable bool m_center_known = false; + mutable bool m_radius_known = false; + mutable bool m_orientation_known = false; +}; + +} +} diff --git a/Sources/geos/include/geos/geom/CircularString.h b/Sources/geos/include/geos/geom/CircularString.h new file mode 100644 index 0000000..85dc301 --- /dev/null +++ b/Sources/geos/include/geos/geom/CircularString.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CircularString : public SimpleCurve { + +public: + using SimpleCurve::SimpleCurve; + + friend class GeometryFactory; + + ~CircularString() override; + + std::unique_ptr clone() const; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + double getLength() const override; + + bool hasCurvedComponents() const override + { + return true; + } + + bool isCurved() const override { + return true; + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + /// \brief + /// Constructs a CircularString taking ownership the + /// given CoordinateSequence. + CircularString(std::unique_ptr&& pts, + const GeometryFactory& newFactory); + + CircularString* cloneImpl() const override + { + return new CircularString(*this); + } + + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(false); + } + + int + getSortIndex() const override + { + return SORTINDEX_LINESTRING; + }; + + CircularString* reverseImpl() const override; + + void validateConstruction(); + +}; + + +} +} diff --git a/Sources/geos/include/geos/geom/CompoundCurve.h b/Sources/geos/include/geos/geom/CompoundCurve.h new file mode 100644 index 0000000..211a4aa --- /dev/null +++ b/Sources/geos/include/geos/geom/CompoundCurve.h @@ -0,0 +1,123 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CompoundCurve : public Curve { + friend class GeometryFactory; + +public: + using Curve::apply_ro; + using Curve::apply_rw; + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + int compareToSameClass(const Geometry* geom) const override; + + std::unique_ptr clone() const; + + bool equalsExact(const Geometry* other, double tolerance = 0) + const override; + + bool equalsIdentical(const Geometry* other) const override; + + std::unique_ptr getBoundary() const override; + + const CoordinateXY* getCoordinate() const override; + + uint8_t getCoordinateDimension() const override; + + std::unique_ptr getCoordinates() const override; + + /// Returns the nth section of the CompoundCurve + const SimpleCurve* getCurveN(std::size_t) const override; + + const Envelope* getEnvelopeInternal() const override + { + return &envelope; + } + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + double getLength() const override; + + /// Returns the number of sections in the CompoundCurve + std::size_t getNumCurves() const override; + + std::size_t getNumPoints() const override; + + bool hasCurvedComponents() const override; + + bool hasM() const override; + + bool hasZ() const override; + + bool isClosed() const override; + + bool isEmpty() const override; + + void normalize() override; + + std::unique_ptr reverse() const; + + void validateConstruction() const; + +protected: + /// Construct a CompoundCurve, taking ownership of the + /// provided CoordinateSequence + CompoundCurve(std::vector>&&, + const GeometryFactory&); + + CompoundCurve(const CompoundCurve&); + + CompoundCurve& operator=(const CompoundCurve&); + + CompoundCurve* cloneImpl() const override; + + Envelope computeEnvelopeInternal() const; + + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(); + } + + int getSortIndex() const override + { + return SORTINDEX_COMPOUNDCURVE; + } + + CompoundCurve* reverseImpl() const override; + +private: + std::vector> curves; + Envelope envelope; +}; + +} +} diff --git a/Sources/geos/include/geos/geom/Coordinate.h b/Sources/geos/include/geos/geom/Coordinate.h new file mode 100644 index 0000000..0d8bced --- /dev/null +++ b/Sources/geos/include/geos/geom/Coordinate.h @@ -0,0 +1,627 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for DoubleNotANumber +#include +#include +#include +#include // for typedefs +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geom { // geos.geom + +// Forward declarations +struct CoordinateLessThan; +class CoordinateXYZM; +class CoordinateXYM; +class Coordinate; + +enum class CoordinateType : std::uint8_t { + XY, + XYZ, + XYZM, + XYM, +}; + +enum class Ordinate : std::uint8_t { + X, + Y, + Z, + M +}; + +GEOS_DLL std::ostream& operator<< (std::ostream&, const CoordinateType); + +class GEOS_DLL CoordinateXY { + + const static CoordinateXY _nullCoord; + +protected: + constexpr const static double DEFAULT_X = 0.0; + constexpr const static double DEFAULT_Y = 0.0; + constexpr const static double DEFAULT_Z = DoubleNotANumber; + constexpr const static double DEFAULT_M = DoubleNotANumber; + +public: + CoordinateXY() + : x(DEFAULT_X) + , y(DEFAULT_Y) + {} + + CoordinateXY(double xNew, double yNew) + : x(xNew) + , y(yNew) + {} + + template + double get() const; + + /// x-coordinate + double x; + + /// y-coordinate + double y; + + /// Equality operator for Coordinate. 2D only. + GEOS_DLL friend bool operator==(const CoordinateXY& a, const CoordinateXY& b) + { + return a.equals2D(b); + }; + + /// Inequality operator for Coordinate. 2D only. + GEOS_DLL friend bool operator!=(const CoordinateXY& a, const CoordinateXY& b) + { + return ! a.equals2D(b); + }; + + bool isValid() const + { + return std::isfinite(x) && std::isfinite(y); + }; + + bool equals2D(const CoordinateXY& other) const + { + if(x != other.x) { + return false; + } + if(y != other.y) { + return false; + } + return true; + }; + + bool equals2D(const CoordinateXY& other, double tolerance) const + { + if (std::abs(x - other.x) > tolerance) { + return false; + } + if (std::abs(y - other.y) > tolerance) { + return false; + } + return true; + }; + + /// 2D only + bool equals(const CoordinateXY& other) const + { + return equals2D(other); + }; + + /// TODO: deprecate this, move logic to CoordinateLessThan instead + inline int compareTo(const CoordinateXY& other) const + { + if(x < other.x) { + return -1; + } + if(x > other.x) { + return 1; + } + if(y < other.y) { + return -1; + } + if(y > other.y) { + return 1; + } + return 0; + }; + + static const CoordinateXY& getNull(); + + double distance(const CoordinateXY& p) const + { + double dx = x - p.x; + double dy = y - p.y; + return std::sqrt(dx * dx + dy * dy); + }; + + double distanceSquared(const CoordinateXY& p) const + { + double dx = x - p.x; + double dy = y - p.y; + return dx * dx + dy * dy; + }; + + bool isNull() const + { + return (std::isnan(x) && std::isnan(y)); + }; + + void setNull() + { + x = DoubleNotANumber; + y = DoubleNotANumber; + }; + + struct GEOS_DLL HashCode + { + inline std::size_t operator()(const CoordinateXY& c) const + { + size_t h = std::hash{}(c.x); + h ^= std::hash{}(c.y) << 1; + // z ordinate ignored for consistency with operator== + return h; + }; + }; + + using UnorderedSet = std::unordered_set; + + /// Returns a string of the form (x,y,z) . + std::string toString() const; +}; + +/** + * \class Coordinate geom.h geos.h + * + * \brief + * Coordinate is the lightweight class used to store coordinates. + * + * It is distinct from Point, which is a subclass of Geometry. + * Unlike objects of type Point (which contain additional + * information such as an envelope, a precision model, and spatial + * reference system information), a Coordinate only contains + * ordinate values and accessor methods. + * + * Coordinate objects are two-dimensional points, with an additional + * z-ordinate. JTS does not support any operations on the z-ordinate except + * the basic accessor functions. + * + * Constructed coordinates will have a z-ordinate of DoubleNotANumber. + * The standard comparison functions will ignore the z-ordinate. + * + */ +// Define the following to make assignments and copy constructions +// NON-(will let profilers report usages) +//#define PROFILE_COORDINATE_COPIES 1 +class GEOS_DLL Coordinate : public CoordinateXY { + +private: + + static const Coordinate _nullCoord; + +public: + /// A set of const Coordinate pointers + typedef std::set ConstSet; + typedef std::set ConstXYSet; + + /// A vector of const Coordinate pointers + typedef std::vector ConstVect; + + /// A stack of const Coordinate pointers + typedef std::stack ConstStack; + + /// A vector of Coordinate objects (real object, not pointers) + typedef std::vector Vect; + + /// A map of const Coordinate pointers to integers + typedef std::map ConstIntMap; + + /// z-coordinate + double z; + + Coordinate() + : CoordinateXY() + , z(DEFAULT_Z) + {}; + + Coordinate(double xNew, double yNew, double zNew = DEFAULT_Z) + : CoordinateXY(xNew, yNew) + , z(zNew) + {}; + + explicit Coordinate(const CoordinateXY& other) + : CoordinateXY(other) + , z(DEFAULT_Z) + {}; + + template + double get() const; + + void setNull() + { + CoordinateXY::setNull(); + z = DoubleNotANumber; + }; + + static const Coordinate& getNull(); + + bool isNull() const + { + return CoordinateXY::isNull() && std::isnan(z); + }; + + /// 3D comparison + bool equals3D(const Coordinate& other) const + { + return (x == other.x) && (y == other.y) && + ((z == other.z) || (std::isnan(z) && std::isnan(other.z))); + }; + + /// Returns a string of the form (x,y,z) . + std::string toString() const; + + Coordinate& operator=(const CoordinateXY& other){ + x = other.x; + y = other.y; + z = DEFAULT_Z; + + return *this; + } +}; + + +class GEOS_DLL CoordinateXYM : public CoordinateXY { +private: + static const CoordinateXYM _nullCoord; + +public: + CoordinateXYM() : CoordinateXYM(DEFAULT_X, DEFAULT_Y, DEFAULT_M) {} + + explicit CoordinateXYM(const CoordinateXY& c) + : CoordinateXY(c) + , m(DEFAULT_M) {} + + CoordinateXYM(double x_, double y_, double m_) + : CoordinateXY(x_, y_) + , m(m_) {} + + double m; + + template + double get() const; + + static const CoordinateXYM& getNull(); + + void setNull() + { + CoordinateXY::setNull(); + m = DoubleNotANumber; + }; + + bool isNull() const + { + return CoordinateXY::isNull() && std::isnan(m); + } + bool equals3D(const CoordinateXYM& other) const { + return x == other.x && y == other.y && (m == other.m || (std::isnan(m) && std::isnan(other.m))); + } + + CoordinateXYM& operator=(const CoordinateXYZM& other); + + CoordinateXYM& operator=(const CoordinateXY& other) { + x = other.x; + y = other.y; + m = DEFAULT_M; + + return *this; + } + + std::string toString() const; +}; + + +class GEOS_DLL CoordinateXYZM : public Coordinate { +private: + static const CoordinateXYZM _nullCoord; + +public: + CoordinateXYZM() : CoordinateXYZM(DEFAULT_X, DEFAULT_Y, DEFAULT_Z, DEFAULT_M) {} + + explicit CoordinateXYZM(const CoordinateXY& c) + : Coordinate(c) + , m(DEFAULT_M) {} + + explicit CoordinateXYZM(const CoordinateXYM& c) + : Coordinate(c) + , m(c.m) {} + + explicit CoordinateXYZM(const Coordinate& c) + : Coordinate(c) + , m(DEFAULT_M) {} + + CoordinateXYZM(double x_, double y_, double z_, double m_) + : Coordinate(x_, y_, z_) + , m(m_) {} + + double m; + + template + double get() const; + + static const CoordinateXYZM& getNull(); + + void setNull() + { + Coordinate::setNull(); + m = DoubleNotANumber; + }; + + + bool isNull() const + { + return Coordinate::isNull() && std::isnan(m); + } + + bool equals4D(const CoordinateXYZM& other) const { + return x == other.x && y == other.y && + (z == other.z || (std::isnan(z) && std::isnan(other.z))) && + (m == other.m || (std::isnan(m) && std::isnan(other.m))); + } + + CoordinateXYZM& operator=(const CoordinateXY& other) { + x = other.x; + y = other.y; + z = DEFAULT_Z; + m = DEFAULT_M; + + return *this; + } + + CoordinateXYZM& operator=(const Coordinate& other) { + x = other.x; + y = other.y; + z = other.z; + m = DEFAULT_M; + + return *this; + } + + CoordinateXYZM& operator=(const CoordinateXYM& other) { + x = other.x; + y = other.y; + z = DEFAULT_Z; + m = other.m; + + return *this; + } + + std::string toString() const; +}; + +inline CoordinateXYM& +CoordinateXYM::operator=(const CoordinateXYZM& other) { + x = other.x; + y = other.y; + m = other.m; + + return *this; +} + + +/// Strict weak ordering Functor for Coordinate +struct GEOS_DLL CoordinateLessThan { + + bool operator()(const CoordinateXY* a, const CoordinateXY* b) const + { + if(a->compareTo(*b) < 0) { + return true; + } + else { + return false; + } + }; + + bool operator()(const CoordinateXY& a, const CoordinateXY& b) const + { + if(a.compareTo(b) < 0) { + return true; + } + else { + return false; + } + }; + +}; + +/// Strict weak ordering operator for Coordinate +inline bool operator<(const CoordinateXY& a, const CoordinateXY& b) +{ + return CoordinateLessThan()(a, b); +} + + +// Generic accessors, XY + +template<> +inline double CoordinateXY::get() const +{ + return x; +} + +template<> +inline double CoordinateXY::get() const +{ + return y; +} + +template<> +inline double CoordinateXY::get() const +{ + return DEFAULT_Z; +} + +template<> +inline double CoordinateXY::get() const +{ + return DEFAULT_M; +} + +// Generic accessors, XYZ + +template<> +inline double Coordinate::get() const +{ + return x; +} + +template<> +inline double Coordinate::get() const +{ + return y; +} + +template<> +inline double Coordinate::get() const +{ + return z; +} + +template<> +inline double Coordinate::get() const +{ + return DEFAULT_M; +} + +// Generic accessors, XYM + +template<> +inline double CoordinateXYM::get() const +{ + return x; +} + +template<> +inline double CoordinateXYM::get() const +{ + return y; +} + +template<> +inline double CoordinateXYM::get() const +{ + return DEFAULT_Z; +} + +template<> +inline double CoordinateXYM::get() const +{ + return m; +} + +// Generic accessors, XYZM + +template<> +inline double CoordinateXYZM::get() const +{ + return x; +} + +template<> +inline double CoordinateXYZM::get() const +{ + return y; +} + +template<> +inline double CoordinateXYZM::get() const +{ + return z; +} + +template<> +inline double CoordinateXYZM::get() const +{ + return m; +} + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateXY& c); +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Coordinate& c); +GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateXYM& c); +GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateXYZM& c); + +} // namespace geos.geom +} // namespace geos + +// Add specializations of std::common_type for Coordinate types +namespace std { + template<> struct common_type { using type = geos::geom::CoordinateXY; }; + template<> struct common_type { using type = geos::geom::Coordinate; }; + template<> struct common_type { using type = geos::geom::CoordinateXYM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + + template<> struct common_type { using type = geos::geom::Coordinate; }; + template<> struct common_type { using type = geos::geom::Coordinate; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + + template<> struct common_type { using type = geos::geom::CoordinateXYM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; + template<> struct common_type { using type = geos::geom::CoordinateXYZM; }; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/geos/include/geos/geom/CoordinateFilter.h b/Sources/geos/include/geos/geom/CoordinateFilter.h new file mode 100644 index 0000000..2555a79 --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateFilter.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace geom { // geos::geom + +/** \brief + * Geometry classes support the concept of applying a + * coordinate filter to every coordinate in the Geometry. + * + * A coordinate filter can either record information about each coordinate or + * change the coordinate in some way. Coordinate filters implement the + * interface CoordinateFilter. (CoordinateFilter is + * an example of the Gang-of-Four Visitor pattern). Coordinate filters can be + * used to implement such things as coordinate transformations, centroid and + * envelope computation, and many other functions. + * + * A CoordinateFilter should be able to process a CoordinateXY object and can + * optionally provide specialized implementations for higher-dimensionality + * Coordinates. If the behavior can be templated on coordinate type, then + * a filter may inherit from CoordinateInspector or CoordinateMutator to + * generate these implementations from a template. + * + */ +class GEOS_DLL CoordinateFilter { +public: + virtual + ~CoordinateFilter() {} + + virtual bool isDone() const + { + return false; + } + + /** \brief + * Performs an operation on `coord`. + * + * **param** `coord` a Coordinate to which the filter is applied. + */ + virtual void + filter_rw(CoordinateXY* /*coord*/) const + { + assert(0); + } + + /** \brief + * Performs an operation with `coord`. + * + * **param** `coord` a Coordinate to which the filter is applied. + */ + virtual void + filter_ro(const CoordinateXY* /*coord*/) + { + assert(0); + } + + virtual void + filter_rw(Coordinate* c) const + { + filter_rw(static_cast(c)); + } + + virtual void + filter_ro(const Coordinate* c) + { + filter_ro(static_cast(c)); + } + + virtual void + filter_rw(CoordinateXYM* c) const + { + filter_rw(static_cast(c)); + } + + virtual void + filter_ro(const CoordinateXYM* c) + { + filter_ro(static_cast(c)); + } + + virtual void + filter_rw(CoordinateXYZM* c) const + { + filter_rw(static_cast(c)); + } + + virtual void + filter_ro(const CoordinateXYZM* c) + { + filter_ro(static_cast(c)); + } +}; + +template +class CoordinateInspector : public CoordinateFilter +{ +public: + virtual void filter_ro(const CoordinateXY* c) override { static_cast(this)->filter(c); } + virtual void filter_ro(const Coordinate* c) override { static_cast(this)->filter(c); } + virtual void filter_ro(const CoordinateXYM* c) override { static_cast(this)->filter(c); } + virtual void filter_ro(const CoordinateXYZM* c) override { static_cast(this)->filter(c); } +}; + +template +class CoordinateMutator : public CoordinateFilter +{ +public: + virtual void filter_rw(CoordinateXY* c) const override { static_cast(this)->filter(c); } + virtual void filter_rw(Coordinate* c) const override { static_cast(this)->filter(c); } + virtual void filter_rw(CoordinateXYM* c) const override { static_cast(this)->filter(c); } + virtual void filter_rw(CoordinateXYZM* c) const override { static_cast(this)->filter(c); } +}; + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/CoordinateList.h b/Sources/geos/include/geos/geom/CoordinateList.h new file mode 100644 index 0000000..532fd3d --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateList.h @@ -0,0 +1,244 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2010 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/CoordinateList.java ?? (never been in complete sync) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include // for operator<< +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +//class Coordinate; +} +} + + +namespace geos { +namespace geom { // geos::geom + +/** \brief + * A list of {@link Coordinate}s, which may + * be set to prevent repeated coordinates from occurring in the list. + * + * Use this class when fast insertions and removal at arbitrary + * position is needed. + * The class keeps ownership of the Coordinates. + * + */ +class GEOS_DLL CoordinateList { + +public: + + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; + + friend std::ostream& operator<< (std::ostream& os, + const CoordinateList& cl); + + /** \brief + * Constructs a new list from an array of Coordinates, allowing + * repeated points. + * + * (I.e. this constructor produces a {@link CoordinateList} with + * exactly the same set of points as the input array.) + * + * @param v the initial coordinates + */ + template + CoordinateList(const T& v) + : + coords(v.begin(), v.end()) + { + } + + CoordinateList(const CoordinateSequence& v) + : CoordinateList(v.items()) + { + } + + CoordinateList() + : + coords() + { + } + + size_t + size() const + { + return coords.size(); + } + + bool + empty() const + { + return coords.empty(); + } + + iterator + begin() + { + return coords.begin(); + } + + iterator + end() + { + return coords.end(); + } + + const_iterator + begin() const + { + return coords.begin(); + } + + const_iterator + end() const + { + return coords.end(); + } + + /** \brief + * Inserts the specified coordinate at the specified position in this list. + * + * @param pos the position at which to insert + * @param c the coordinate to insert + * @param allowRepeated if set to false, repeated coordinates are collapsed + * + * @return an iterator to the newly installed coordinate + * (or previous, if equal and repeated are not allowed) + * + * NOTE: when allowRepeated is false _next_ point is not checked + * this matches JTS behavior + */ + iterator + insert(iterator pos, const Coordinate& c, bool allowRepeated) + { + if(!allowRepeated && pos != coords.begin()) { + iterator prev = pos; + --prev; + if(c.equals2D(*prev)) { + return prev; + } + } + return coords.insert(pos, c); + } + + iterator + add(const Coordinate& c, bool allowRepeated) + { + return insert(coords.end(), c, allowRepeated); + } + + iterator + insert(iterator pos, const Coordinate& c) + { + return coords.insert(pos, c); + } + + iterator + erase(iterator pos) + { + return coords.erase(pos); + } + + iterator + erase(iterator first, iterator last) + { + return coords.erase(first, last); + } + + std::unique_ptr + toCoordinateArray() const + { + std::unique_ptr ret(new Coordinate::Vect); + ret->assign(coords.begin(), coords.end()); + return ret; + } + + std::unique_ptr + toCoordinateSequence() const + { + auto ret = detail::make_unique(); + ret->add(begin(), end()); + return ret; + } + + void + closeRing() + { + if(!coords.empty() && !(*(coords.begin())).equals(*(coords.rbegin()))) { + const Coordinate& c = *(coords.begin()); + coords.insert(coords.end(), c); + } + } + + static void + closeRing(std::vector& coords) + { + if(!coords.empty() && !(*(coords.begin())).equals(*(coords.rbegin()))) { + const Coordinate& c = *(coords.begin()); + coords.insert(coords.end(), c); + } + } + +private: + + std::list coords; +}; + +inline +std::ostream& +operator<< (std::ostream& os, const CoordinateList& cl) +{ + os << "("; + for(CoordinateList::const_iterator + it = cl.begin(), end = cl.end(); + it != end; + ++it) { + const Coordinate& c = *it; + if(it != cl.begin()) { + os << ", "; + } + os << c; + } + os << ")"; + + return os; +} + +} // namespace geos::geom +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geom/CoordinateSequence.h b/Sources/geos/include/geos/geom/CoordinateSequence.h new file mode 100644 index 0000000..2d952f1 --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateSequence.h @@ -0,0 +1,820 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for applyCoordinateFilter +#include + +#include +#include +#include +#include // ostream +#include // for unique_ptr typedef + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class CoordinateFilter; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \class CoordinateSequence geom.h geos.h + * + * \brief + * The internal representation of a list of coordinates inside a Geometry. + * + * A CoordinateSequence is capable of storing XY, XYZ, XYM, or XYZM Coordinates. For efficient + * storage, the dimensionality of the CoordinateSequence should be specified at creation using + * the constructor with `hasz` and `hasm` arguments. Currently most of the GEOS code base + * stores 2D Coordinates and accesses using the Coordinate type. Sequences used by these parts + * of the code must be created with constructors without `hasz` and `hasm` arguments. + * + * If a high-dimension Coordinate coordinate is read from a low-dimension CoordinateSequence, + * the higher dimensions will be populated with incorrect values or a segfault may occur. + * + */ +class GEOS_DLL CoordinateSequence { + +public: + + /// Standard ordinate index values + enum { X, Y, Z, M }; + + using iterator = CoordinateSequenceIterator; + using const_iterator = CoordinateSequenceIterator; + + typedef std::unique_ptr Ptr; + + /// \defgroup construct Constructors + /// @{ + + /** + * Create an CoordinateSequence capable of storing XY or XYZ coordinates. + */ + CoordinateSequence(); + + /** + * Create a CoordinateSequence capable of storing XY, XYZ or XYZM coordinates. + * + * @param size size of the sequence to create. + * @param dim 2 for 2D, 3 for XYZ, 4 for XYZM, or 0 to determine + * this based on the first coordinate in the sequence + */ + CoordinateSequence(std::size_t size, std::size_t dim = 0); + + /** + * Create a CoordinateSequence that packs coordinates of any dimension. + * Code using a CoordinateSequence constructed in this way must not + * attempt to access references to coordinates with dimensions that + * are not actually stored in the sequence. + * + * @param size size of the sequence to create + * @param hasz true if the stored + * @param hasm + * @param initialize + */ + CoordinateSequence(std::size_t size, bool hasz, bool hasm, bool initialize = true); + + /** + * Create a CoordinateSequence from a list of XYZ coordinates. + * Code using the sequence may only access references to CoordinateXY + * or Coordinate objects. + */ + CoordinateSequence(const std::initializer_list&); + + /** + * Create a CoordinateSequence from a list of XY coordinates. + * Code using the sequence may only access references to CoordinateXY objects. + */ + CoordinateSequence(const std::initializer_list&); + + /** + * Create a CoordinateSequence from a list of XYM coordinates. + * Code using the sequence may only access references to CoordinateXY + * or CoordinateXYM objects. + */ + CoordinateSequence(const std::initializer_list&); + + /** + * Create a CoordinateSequence from a list of XYZM coordinates. + */ + CoordinateSequence(const std::initializer_list&); + + /** + * Create a CoordinateSequence storing XY values only. + * + * @param size size of the sequence to create + */ + static CoordinateSequence XY(std::size_t size) { + return CoordinateSequence(size, false, false); + } + + /** + * Create a CoordinateSequence storing XYZ values only. + * + * @param size size of the sequence to create + */ + static CoordinateSequence XYZ(std::size_t size) { + return CoordinateSequence(size, true, false); + } + + /** + * Create a CoordinateSequence storing XYZM values only. + * + * @param size size of the sequence to create + */ + static CoordinateSequence XYZM(std::size_t size) { + return CoordinateSequence(size, true, true); + } + + /** + * Create a CoordinateSequence storing XYM values only. + * + * @param size size of the sequence to create + */ + static CoordinateSequence XYM(std::size_t size) { + return CoordinateSequence(size, false, true); + } + + /** \brief + * Returns a heap-allocated deep copy of this CoordinateSequence. + */ + std::unique_ptr clone() const; + + /// @} + /// \defgroup prop Properties + /// @{ + + /** + * Return the Envelope containing all points in this sequence. + * The Envelope is not cached and is computed each time the + * method is called. + */ + Envelope getEnvelope() const; + + /** \brief + * Returns the number of Coordinates + */ + std::size_t getSize() const { + return size(); + } + + /** \brief + * Returns the number of Coordinates + */ + size_t size() const + { + assert(stride() == 2 || stride() == 3 || stride() == 4); + switch(stride()) { + case 2: return m_vect.size() / 2; + case 4: return m_vect.size() / 4; + default : return m_vect.size() / 3; + } + } + + /// Returns true if list contains no coordinates. + bool isEmpty() const { + return m_vect.empty(); + } + + /** \brief + * Tests whether an a {@link CoordinateSequence} forms a ring, + * by checking length and closure. Self-intersection is not checked. + * + * @return true if the coordinate form a ring. + */ + bool isRing() const; + + /** + * Returns the dimension (number of ordinates in each coordinate) + * for this sequence. + * + * @return the dimension of the sequence. + */ + std::size_t getDimension() const; + + bool hasZ() const { + return m_hasdim ? m_hasz : (m_vect.empty() || !std::isnan(m_vect[2])); + } + + bool hasM() const { + return m_hasm; + } + + /// Returns true if contains any two consecutive points + bool hasRepeatedPoints() const; + + /// Returns true if contains any NaN/Inf coordinates + bool hasRepeatedOrInvalidPoints() const; + + /// Get the backing type of this CoordinateSequence. This is not necessarily + /// consistent with the dimensionality of the stored Coordinates; 2D Coordinates + /// may be stored as a XYZ coordinates. + CoordinateType getCoordinateType() const { + switch(stride()) { + case 4: return CoordinateType::XYZM; + case 2: return CoordinateType::XY; + default: return hasM() ? CoordinateType::XYM : CoordinateType::XYZ; + } + } + + /// @} + /// \defgroup access Accessors + /// @{ + + /** \brief + * Returns a read-only reference to Coordinate at position i. + */ + template + const T& getAt(std::size_t i) const { + static_assert(std::is_base_of::value, "Must be a Coordinate class"); + assert(sizeof(T) <= sizeof(double) * stride()); + assert(i*stride() < m_vect.size()); + const T* orig = reinterpret_cast(&m_vect[i*stride()]); + return *orig; + } + + /** \brief + * Returns a reference to Coordinate at position i. + */ + template + T& getAt(std::size_t i) { + static_assert(std::is_base_of::value, "Must be a Coordinate class"); + assert(sizeof(T) <= sizeof(double) * stride()); + assert(i*stride() < m_vect.size()); + T* orig = reinterpret_cast(&m_vect[i*stride()]); + return *orig; + } + + /** \brief + * Write Coordinate at position i to given Coordinate. + */ + template + void getAt(std::size_t i, T& c) const { + switch(getCoordinateType()) { + case CoordinateType::XY: c = getAt(i); break; + case CoordinateType::XYZ: c = getAt(i); break; + case CoordinateType::XYZM: c = getAt(i); break; + case CoordinateType::XYM: c = getAt(i); break; + default: getAt(i); + } + } + + void getAt(std::size_t i, CoordinateXY& c) const { + c = getAt(i); + } + + // TODO: change to return CoordinateXY + /** + * Returns a read-only reference to Coordinate at i + */ + const Coordinate& operator[](std::size_t i) const + { + return getAt(i); + } + + // TODO: change to return CoordinateXY + /** + * Returns a reference to Coordinate at i + */ + Coordinate& + operator[](std::size_t i) + { + return getAt(i); + } + + /** + * Returns the ordinate of a coordinate in this sequence. + * Ordinate indices 0 and 1 are assumed to be X and Y. + * Ordinates indices greater than 1 have user-defined semantics + * (for instance, they may contain other dimensions or measure values). + * + * @param index the coordinate index in the sequence + * @param ordinateIndex the ordinate index in the coordinate + * (in range [0, dimension-1]) + */ + double getOrdinate(std::size_t index, std::size_t ordinateIndex) const; + + /** + * Returns ordinate X (0) of the specified coordinate. + * + * @param index + * @return the value of the X ordinate in the index'th coordinate + */ + double getX(std::size_t index) const + { + return m_vect[index * stride()]; + } + + /** + * Returns ordinate Y (1) of the specified coordinate. + * + * @param index + * @return the value of the Y ordinate in the index'th coordinate + */ + double getY(std::size_t index) const + { + return m_vect[index * stride() + 1]; + } + + /// Return last Coordinate in the sequence + template + const T& back() const + { + return getAt(size() - 1); + } + + /// Return last Coordinate in the sequence + template + T& back() + { + return getAt(size() - 1); + } + + /// Return first Coordinate in the sequence + template + const T& front() const + { + return *(reinterpret_cast(m_vect.data())); + } + + /// Return first Coordinate in the sequence + template + T& front() + { + return *(reinterpret_cast(m_vect.data())); + } + + /// Pushes all Coordinates of this sequence into the provided vector. + void toVector(std::vector& coords) const; + + void toVector(std::vector& coords) const; + + + /// @} + /// \defgroup mutate Mutators + /// @{ + + /// Copy Coordinate c to position pos + template + void setAt(const T& c, std::size_t pos) { + switch(getCoordinateType()) { + case CoordinateType::XY: setAtImpl(c, pos); break; + case CoordinateType::XYZ: setAtImpl(c, pos); break; + case CoordinateType::XYZM: setAtImpl(c, pos); break; + case CoordinateType::XYM: setAtImpl(c, pos); break; + default: setAtImpl(c, pos); + } + } + + /** + * Sets the value for a given ordinate of a coordinate in this sequence. + * + * @param index the coordinate index in the sequence + * @param ordinateIndex the ordinate index in the coordinate + * (in range [0, dimension-1]) + * @param value the new ordinate value + */ + void setOrdinate(std::size_t index, std::size_t ordinateIndex, double value); + + /// Substitute Coordinate list with a copy of the given vector + void setPoints(const std::vector& v); + + /// Substitute Coordinate list with a copy of the given vector + void setPoints(const std::vector& v); + + /// @} + /// \defgroup add Adding methods + /// @{ + + /// Adds the specified coordinate to the end of the sequence. Dimensions + /// present in the coordinate but not in the sequence will be ignored. + /// If multiple coordinates are to be added, a multiple-insert method should + /// be used for best performance. + template + void add(const T& c) { + add(c, size()); + } + + /// Adds the specified coordinate to the end of the sequence. Dimensions + /// present in the coordinate but not in the sequence will be ignored. If + /// allowRepeated is false, the coordinate will not be added if it is the + /// same as the last coordinate in the sequence. + /// If multiple coordinates are to be added, a multiple-insert method should + /// be used for best performance. + template + void add(const T& c, bool allowRepeated) + { + if(!allowRepeated && !isEmpty()) { + const CoordinateXY& last = back(); + if(last.equals2D(c)) { + return; + } + } + + add(c); + } + + /** \brief + * Inserts the specified coordinate at the specified position in + * this sequence. If multiple coordinates are to be added, a multiple- + * insert method should be used for best performance. + * + * @param c the coordinate to insert + * @param pos the position at which to insert + */ + template + void add(const T& c, std::size_t pos) + { + static_assert(std::is_base_of::value, "Must be a Coordinate class"); + + // c may be a reference inside m_vect, so we make sure it will not + // grow before adding it + if (m_vect.size() + stride() <= m_vect.capacity()) { + make_space(pos, 1); + setAt(c, static_cast(pos)); + } else { + T tmp{c}; + make_space(pos, 1); + setAt(tmp, static_cast(pos)); + } + } + + /** \brief + * Inserts the specified coordinate at the specified position in + * this list. + * + * @param i the position at which to insert + * @param coord the coordinate to insert + * @param allowRepeated if set to false, repeated coordinates are + * collapsed + */ + template + void add(std::size_t i, const T& coord, bool allowRepeated) + { + // don't add duplicate coordinates + if(! allowRepeated) { + std::size_t sz = size(); + if(sz > 0) { + if(i > 0) { + const CoordinateXY& prev = getAt(i - 1); + if(prev.equals2D(coord)) { + return; + } + } + if(i < sz) { + const CoordinateXY& next = getAt(i); + if(next.equals2D(coord)) { + return; + } + } + } + } + + add(coord, i); + } + + void add(double x, double y) { + CoordinateXY c(x, y); + add(c); + } + + void add(const CoordinateSequence& cs); + + void add(const CoordinateSequence& cs, bool allowRepeated); + + void add(const CoordinateSequence& cl, bool allowRepeated, bool forwardDirection); + + void add(const CoordinateSequence& cs, std::size_t from, std::size_t to); + + void add(const CoordinateSequence& cs, std::size_t from, std::size_t to, bool allowRepeated); + + template + void add(T begin, T end, Args... args) { + for (auto it = begin; it != end; ++it) { + add(*it, args...); + } + } + + template + void add(std::size_t i, T from, T to) { + auto npts = static_cast(std::distance(from, to)); + make_space(i, npts); + + for (auto it = from; it != to; ++it) { + setAt(*it, i); + i++; + } + } + + /// @} + /// \defgroup util Utilities + /// @{ + + void clear() { + m_vect.clear(); + } + + void reserve(std::size_t capacity) { + m_vect.reserve(capacity * stride()); + } + + void resize(std::size_t capacity) { + m_vect.resize(capacity * stride()); + } + + void pop_back(); + + /// Get a string representation of CoordinateSequence + std::string toString() const; + + /// Returns lower-left Coordinate in list + const CoordinateXY* minCoordinate() const; + + /** \brief + * Returns either the given CoordinateSequence if its length + * is greater than the given amount, or an empty CoordinateSequence. + */ + static CoordinateSequence* atLeastNCoordinatesOrNothing(std::size_t n, + CoordinateSequence* c); + + /// Return position of a Coordinate + // + /// or numeric_limits::max() if not found + /// + static std::size_t indexOf(const CoordinateXY* coordinate, + const CoordinateSequence* cl); + + /** + * \brief + * Returns true if the two arrays are identical, both null, + * or pointwise equal in two dimensions + */ + static bool equals(const CoordinateSequence* cl1, + const CoordinateSequence* cl2); + + /** + * \brief + * Returns true if the two sequences are identical (pointwise + * equal in all dimensions, with NaN == NaN). + */ + bool equalsIdentical(const CoordinateSequence& other) const; + + /// Scroll given CoordinateSequence so to start with given Coordinate. + static void scroll(CoordinateSequence* cl, const CoordinateXY* firstCoordinate); + + /** \brief + * Determines which orientation of the {@link Coordinate} array + * is (overall) increasing. + * + * In other words, determines which end of the array is "smaller" + * (using the standard ordering on {@link Coordinate}). + * Returns an integer indicating the increasing direction. + * If the sequence is a palindrome, it is defined to be + * oriented in a positive direction. + * + * @param pts the array of Coordinates to test + * @return 1 if the array is smaller at the start + * or is a palindrome, + * -1 if smaller at the end + * + * NOTE: this method is found in CoordinateArrays class for JTS + */ + static int increasingDirection(const CoordinateSequence& pts); + + /// Reverse Coordinate order in given CoordinateSequence + void reverse(); + + void sort(); + + + /** + * Expands the given Envelope to include the coordinates in the + * sequence. + * @param env the envelope to expand + */ + void expandEnvelope(Envelope& env) const; + + void closeRing(bool allowRepeated = false); + + /// @} + /// \defgroup iterate Iteration + /// @{ + + template + void apply_rw(const Filter* filter) { + switch(getCoordinateType()) { + case CoordinateType::XY: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYZ: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYM: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + case CoordinateType::XYZM: + for (auto& c : items()) { + if (filter->isDone()) break; + filter->filter_rw(&c); + } + break; + } + m_hasdim = m_hasz = false; // re-check (see http://trac.osgeo.org/geos/ticket/435) + } + + template + void apply_ro(Filter* filter) const { + switch(getCoordinateType()) { + case CoordinateType::XY: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYZ: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYM: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + case CoordinateType::XYZM: + for (const auto& c : items()) { + if (filter->isDone()) break; + filter->filter_ro(&c); + } + break; + } + } + + template + auto applyAt(size_t i, F&& fun) const { + switch(getCoordinateType()) { + case CoordinateType::XYZ: return fun(getAt(i)); + case CoordinateType::XYM: return fun(getAt(i)); + case CoordinateType::XYZM: return fun(getAt(i)); + default: return fun(getAt(i)); + } + } + + template + void forEach(F&& fun) const { + switch(getCoordinateType()) { + case CoordinateType::XY: for (const auto& c : items()) { fun(c); } break; + case CoordinateType::XYZ: for (const auto& c : items()) { fun(c); } break; + case CoordinateType::XYM: for (const auto& c : items()) { fun(c); } break; + case CoordinateType::XYZM: for (const auto& c : items()) { fun(c); } break; + } + } + + template + void forEach(F&& fun) const + { + for (std::size_t i = 0; i < size(); i++) { + fun(getAt(i)); + } + } + + template + void forEach(std::size_t from, std::size_t to, F&& fun) const + { + for (std::size_t i = from; i <= to; i++) { + fun(getAt(i)); + } + } + + template + class Coordinates { + public: + using SequenceType = typename std::conditional::value, const CoordinateSequence, CoordinateSequence>::type; + + explicit Coordinates(SequenceType* seq) : m_seq(seq) {} + + CoordinateSequenceIterator begin() { + return {m_seq}; + } + + CoordinateSequenceIterator end() { + return {m_seq, m_seq->getSize()}; + } + + CoordinateSequenceIterator::type> + begin() const { + return CoordinateSequenceIterator::type>{m_seq}; + } + + CoordinateSequenceIterator::type> + end() const { + return CoordinateSequenceIterator::type>{m_seq, m_seq->getSize()}; + } + + CoordinateSequenceIterator::type> + cbegin() const { + return CoordinateSequenceIterator::type>{m_seq}; + } + + CoordinateSequenceIterator::type> + cend() const { + return CoordinateSequenceIterator::type>{m_seq, m_seq->getSize()}; + } + + private: + SequenceType* m_seq; + }; + + template + Coordinates::type> items() const { + return Coordinates::type>(this); + } + + template + Coordinates items() { + return Coordinates(this); + } + + + /// @} + + double* data() { + return m_vect.data(); + } + + const double* data() const { + return m_vect.data(); + } + +private: + std::vector m_vect; // Vector to store values + + uint8_t m_stride; // Stride of stored values, corresponding to underlying type + + mutable bool m_hasdim; // Has the dimension of this sequence been determined? Or was it created with no + // explicit dimensionality, and we're waiting for getDimension() to be called + // after some coordinates have been added? + mutable bool m_hasz; + bool m_hasm; + + void initialize(); + + template + void setAtImpl(const T2& c, std::size_t pos) { + auto& orig = getAt(pos); + orig = c; + } + + void make_space(std::size_t pos, std::size_t n) { + m_vect.insert(std::next(m_vect.begin(), static_cast(pos * stride())), + m_stride * n, + DoubleNotANumber); + } + + std::uint8_t stride() const { + return m_stride; + } + +}; + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const CoordinateSequence& cs); + +GEOS_DLL bool operator== (const CoordinateSequence& s1, const CoordinateSequence& s2); + +GEOS_DLL bool operator!= (const CoordinateSequence& s1, const CoordinateSequence& s2); + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/CoordinateSequenceFilter.h b/Sources/geos/include/geos/geom/CoordinateSequenceFilter.h new file mode 100644 index 0000000..481255c --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateSequenceFilter.h @@ -0,0 +1,117 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/CoordinateSequenceFilter.java rev. 1.3 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** \brief + * Interface for classes which provide operations that can be applied to + * the coordinates in a CoordinateSequence. + * + * A CoordinateSequence filter can either record information about each + * coordinate or change the coordinate in some way. + * CoordinateSequence filters can be used to implement such things as + * coordinate transformations, centroid and envelope computation, and many + * other functions. + * For maximum efficiency, the execution of filters can be short-circuited. + * Geometry classes support the concept of applying a CoordinateSequenceFilter + * to each CoordinateSequences they contain. + * + * CoordinateSequenceFilter is an example of the Gang-of-Four Visitor pattern. + * + * @see Geometry::apply_ro(CoordinateSequenceFilter& filter) const + * @see Geometry::apply_rw(CoordinateSequenceFilter& filter) + * @author Martin Davis + * + */ +class GEOS_DLL CoordinateSequenceFilter { + +public: + + virtual + ~CoordinateSequenceFilter() {} + + /** \brief + * Performs an operation on a coordinate in a CoordinateSequence. + * + * **param** `seq` the CoordinateSequence to which the filter is applied + * **param** `i` the index of the coordinate to apply the filter to + */ + virtual void + filter_rw(CoordinateSequence& /*seq*/, std::size_t /*i*/) + { + assert(0); + } + + /** \brief + * Performs an operation on a coordinate in a CoordinateSequence. + * + * **param** `seq` the CoordinateSequence to which the filter is applied + * **param** `i` the index of the coordinate to apply the filter to + */ + virtual void + filter_ro(const CoordinateSequence& /*seq*/, std::size_t /*i*/) + { + assert(0); + } + + /** \brief + * Reports whether the application of this filter can be terminated. + * + * Once this method returns `false`, it should continue to return + * `false` on every subsequent call. + * + * @return `true` if the application of this filter can be terminated. + */ + virtual bool isDone() const = 0; + + + /** \brief + * Reports whether the execution of this filter has modified + * the coordinates of the geometry. + * + * If so, Geometry::geometryChanged() will be executed after this + * filter has finished being executed. + * + * Most filters can simply return a constant value reflecting whether + * they are able to change the coordinates. + * + * @return `true` if this filter has changed the coordinates of the geometry + */ + virtual bool isGeometryChanged() const = 0; + +}; + +} // namespace geos::geom +} // namespace geos + + diff --git a/Sources/geos/include/geos/geom/CoordinateSequenceIterator.h b/Sources/geos/include/geos/geom/CoordinateSequenceIterator.h new file mode 100644 index 0000000..7a9944a --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateSequenceIterator.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +template +class CoordinateSequenceIterator { + +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = CoordinateType; + using reference = CoordinateType&; + using pointer = CoordinateType; + using difference_type = std::ptrdiff_t; + +private: + SequenceType* m_seq; + difference_type m_pos; + +public: + CoordinateSequenceIterator(SequenceType* seq) : m_seq(seq), m_pos(0) {} + + CoordinateSequenceIterator(SequenceType* seq, std::size_t size) : m_seq(seq), m_pos(static_cast(size)) {} + + reference operator*() const { + return m_seq->template getAt(static_cast(m_pos)); + } + + pointer operator->() const { + return &m_seq->template getAt(static_cast(m_pos)); + } + + CoordinateSequenceIterator& operator++() { + m_pos++; + return *this; + } + + CoordinateSequenceIterator operator++(int) { + CoordinateSequenceIterator ret = *this; + m_pos++; + return ret; + } + + CoordinateSequenceIterator& operator--() { + m_pos--; + return *this; + } + + CoordinateSequenceIterator operator--(int) { + CoordinateSequenceIterator ret = *this; + m_pos--; + return ret; + } + + difference_type operator-(const CoordinateSequenceIterator& other) const { + return this->m_pos - other.m_pos; + } + + CoordinateSequenceIterator operator+(difference_type n) const { + return CoordinateSequenceIterator(m_seq, static_cast(m_pos + n)); + } + + CoordinateSequenceIterator operator+=(difference_type n) { + this->m_pos += n; + return *this; + } + + CoordinateSequenceIterator operator-(difference_type n) const { + return CoordinateSequenceIterator(m_seq, static_cast(m_pos - n)); + } + + CoordinateSequenceIterator operator-=(difference_type n) { + this->m_pos -= n; + return *this; + } + + CoordinateType& operator[](difference_type n) const { + return *(*this + n); + } + + bool operator==(const CoordinateSequenceIterator& other) const { + return this->m_pos == other.m_pos; + } + + bool operator!=(const CoordinateSequenceIterator& other) const { + return !(*this == other); + } + + bool operator<(const CoordinateSequenceIterator& other) const { + return this->m_pos < other.m_pos; + } + + bool operator<=(const CoordinateSequenceIterator& other) const { + return this->m_pos <= other.m_pos; + } + + bool operator>(const CoordinateSequenceIterator& other) const { + return this->m_pos > other.m_pos; + } + + bool operator>=(const CoordinateSequenceIterator& other) const { + return this->m_pos >= other.m_pos; + } + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/CoordinateSequences.h b/Sources/geos/include/geos/geom/CoordinateSequences.h new file mode 100644 index 0000000..93595be --- /dev/null +++ b/Sources/geos/include/geos/geom/CoordinateSequences.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +/// +/// \brief The CoordinateSequences class provides utility methods to operate +/// on CoordinateSequences. Methods that do not benefit from access to the +/// CoordinateSequence internals can be placed here. +/// +class CoordinateSequences { + +private: + template + static constexpr int + type_pair(const CoordinateType& typ1, const CoordinateType& typ2) { + return (static_cast(typ1) << 4) | static_cast(typ2); + } + +public: + /// + /// \brief binaryDispatch calls a functor template, explicitly providing the backing types of two CoordinateSequences. The + /// CoordinateSequences are not provided to the functor as arguments but can be provided along with any other arguments + /// through the `args` argument. + template + static void binaryDispatch(const CoordinateSequence& seq1, const CoordinateSequence& seq2, F& fun, Args... args) + { + using CoordinateXYZ = Coordinate; + + auto typ1 = seq1.getCoordinateType(); + auto typ2 = seq2.getCoordinateType(); + + switch(type_pair(typ1, typ2)) { + case type_pair(CoordinateType::XY, CoordinateType::XY): fun.template operator()(args...); break; + case type_pair(CoordinateType::XY, CoordinateType::XYZ): fun.template operator()(args...); break; + case type_pair(CoordinateType::XY, CoordinateType::XYM): fun.template operator()(args...); break; + case type_pair(CoordinateType::XY, CoordinateType::XYZM): fun.template operator()(args...); break; + + case type_pair(CoordinateType::XYZ, CoordinateType::XY): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZ, CoordinateType::XYZ): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZ, CoordinateType::XYM): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZ, CoordinateType::XYZM): fun.template operator()(args...); break; + + case type_pair(CoordinateType::XYM, CoordinateType::XY): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYM, CoordinateType::XYZ): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYM, CoordinateType::XYM): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYM, CoordinateType::XYZM): fun.template operator()(args...); break; + + case type_pair(CoordinateType::XYZM, CoordinateType::XY): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZM, CoordinateType::XYZ): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZM, CoordinateType::XYM): fun.template operator()(args...); break; + case type_pair(CoordinateType::XYZM, CoordinateType::XYZM): fun.template operator()(args...); break; + } + } + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/Curve.h b/Sources/geos/include/geos/geom/Curve.h new file mode 100644 index 0000000..352dff6 --- /dev/null +++ b/Sources/geos/include/geos/geom/Curve.h @@ -0,0 +1,71 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class SimpleCurve; + +class GEOS_DLL Curve : public Geometry { + +public: + using Geometry::apply_ro; + using Geometry::apply_rw; + + void apply_ro(GeometryComponentFilter* filter) const override; + + void apply_ro(GeometryFilter* filter) const override; + + void apply_rw(GeometryComponentFilter* filter) override; + + void apply_rw(GeometryFilter* filter) override; + + /** + * \brief + * Returns Dimension::False for a closed Curve, + * 0 otherwise (Curve boundary is a MultiPoint) + */ + int + getBoundaryDimension() const override + { + return isClosed() ? Dimension::False : 0; + } + + /// Returns line dimension (1) + Dimension::DimensionType getDimension() const override + { + return Dimension::L; // line + } + + /// Returns true if the first and last coordinate in the Curve are the same + virtual bool isClosed() const = 0; + + /// Returns true if the curve is closed and simple + bool isRing() const; + + virtual std::size_t getNumCurves() const = 0; + + virtual const SimpleCurve* getCurveN(std::size_t) const = 0; + +protected: + Curve(const GeometryFactory& factory) : Geometry(&factory) {} + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/CurvePolygon.h b/Sources/geos/include/geos/geom/CurvePolygon.h new file mode 100644 index 0000000..1a12b48 --- /dev/null +++ b/Sources/geos/include/geos/geom/CurvePolygon.h @@ -0,0 +1,58 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class GEOS_DLL CurvePolygon : public SurfaceImpl { + friend class GeometryFactory; + +public: + ~CurvePolygon() override = default; + + double getArea() const override; + + std::unique_ptr getBoundary() const override; + + std::unique_ptr getCoordinates() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasCurvedComponents() const override; + + void normalize() override; + +protected: + using SurfaceImpl::SurfaceImpl; + + Geometry* cloneImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_CURVEPOLYGON; + } + + Geometry* reverseImpl() const override; +}; + + +} +} diff --git a/Sources/geos/include/geos/geom/Dimension.h b/Sources/geos/include/geos/geom/Dimension.h new file mode 100644 index 0000000..51adc6b --- /dev/null +++ b/Sources/geos/include/geos/geom/Dimension.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { // geos::geom + +/// Constants representing the dimensions of a point, a curve and a surface. +/// +/// Also, constants representing the dimensions of the empty geometry and +/// non-empty geometries, and a wildcard dimension meaning "any dimension". +/// +class GEOS_DLL Dimension { +public: + enum DimensionType { + /// Dimension value for any dimension (= {FALSE, TRUE}). + DONTCARE = -3, + + /// Dimension value of non-empty geometries (= {P, L, A}). + True = -2, + + /// Dimension value of the empty geometry (-1). + False = -1, + + /// Dimension value of a point (0). + P = 0, + + /// Dimension value of a curve (1). + L = 1, + + /// Dimension value of a surface (2). + A = 2 + }; + + //static const int P = 0; /// Dimension value of a point (0). + //static const int L = 1; /// Dimension value of a curve (1). + //static const int A = 2; /// Dimension value of a surface (2). + //static const int False = -1; /// Dimension value of the empty geometry (-1). + //static const int True = -2; /// Dimension value of non-empty geometries (= {P, L, A}). + //static const int DONTCARE = -3; /// Dimension value for any dimension (= {FALSE, TRUE}). + static char toDimensionSymbol(int dimensionValue); + + static int toDimensionValue(char dimensionSymbol); + +}; + +} // namespace geos::geom +} // namespace geos + + diff --git a/Sources/geos/include/geos/geom/Envelope.h b/Sources/geos/include/geos/geom/Envelope.h new file mode 100644 index 0000000..61e074b --- /dev/null +++ b/Sources/geos/include/geos/geom/Envelope.h @@ -0,0 +1,848 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Envelope.java rev 1.46 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + + +#include +#include + +#include +#include +#include // for operator<< +#include +#include +#include + +namespace geos { +namespace geom { // geos::geom + +class Envelope; + +/// Output operator +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Envelope& o); + +class Coordinate; + +/** + * \class Envelope geom.h geos.h + * + * \brief + * An Envelope defines a rectangulare region of the 2D coordinate plane. + * + * It is often used to represent the bounding box of a Geometry, + * e.g. the minimum and maximum x and y values of the Coordinates. + * + * Envelopes allow null values, which are represented with NaN values for ordinates. + * Envelopes support infinite or half-infinite regions, by using + * the values of `Double_POSITIVE_INFINITY` and `Double_NEGATIVE_INFINITY`. + * + * When Envelope objects are created or initialized, the supplies extent + * values are automatically sorted into the correct order. + * + */ +class GEOS_DLL Envelope { + +public: + + friend std::ostream& operator<< (std::ostream& os, const Envelope& o); + + typedef std::unique_ptr Ptr; + + /** \brief + * Creates a null Envelope. + */ + Envelope() + : minx(DoubleNotANumber) + , maxx(DoubleNotANumber) + , miny(DoubleNotANumber) + , maxy(DoubleNotANumber) + {}; + + /** \brief + * Creates an Envelope for a region defined by maximum and minimum values. + * + * @param x1 the first x-value + * @param x2 the second x-value + * @param y1 the first y-value + * @param y2 the second y-value + */ + Envelope(double x1, double x2, double y1, double y2) + { + init(x1, x2, y1, y2); + } + + /** \brief + * Creates an Envelope for a region defined by two Coordinates. + * + * @param p1 the first Coordinate + * @param p2 the second Coordinate + */ + Envelope(const CoordinateXY& p1, const CoordinateXY& p2) + { + init(p1, p2); + } + + /** \brief + * Creates an Envelope for a region defined by a single Coordinate. + * + * @param p the Coordinate + */ + explicit Envelope(const CoordinateXY& p) + : minx(p.x) + , maxx(p.x) + , miny(p.y) + , maxy(p.y) + { + } + + /** \brief + * Create an Envelope from an Envelope string representation produced + * by Envelope::toString() + */ + explicit Envelope(const std::string& str); + + /** \brief + * Test the point `q` to see whether it intersects the Envelope + * defined by `p1-p2`. + * + * @param p1 one extremal point of the envelope + * @param p2 another extremal point of the envelope + * @param q the point to test for intersection + * @return `true` if q intersects the envelope p1-p2 + */ + static bool intersects(const CoordinateXY& p1, const CoordinateXY& p2, + const CoordinateXY& q); + + /** \brief + * Test the envelope defined by `p1-p2` for intersection + * with the envelope defined by `q1-q2`. + * + * @param p1 one extremal point of the envelope P + * @param p2 another extremal point of the envelope P + * @param q1 one extremal point of the envelope Q + * @param q2 another extremal point of the envelope Q + * + * @return `true` if Q intersects P + */ + static bool intersects( + const CoordinateXY& p1, const CoordinateXY& p2, + const CoordinateXY& q1, const CoordinateXY& q2) + { + double minq = std::min(q1.x, q2.x); + double maxq = std::max(q1.x, q2.x); + double minp = std::min(p1.x, p2.x); + double maxp = std::max(p1.x, p2.x); + if(minp > maxq) { + return false; + } + if(maxp < minq) { + return false; + } + minq = std::min(q1.y, q2.y); + maxq = std::max(q1.y, q2.y); + minp = std::min(p1.y, p2.y); + maxp = std::max(p1.y, p2.y); + if(minp > maxq) { + return false; + } + if(maxp < minq) { + return false; + } + return true; + } + + /** \brief + * Check if the extent defined by two extremal points intersects + * the extent of this Envelope. + * + * @param a a point + * @param b another point + * @return `true` if the extents intersect + */ + bool intersects(const CoordinateXY& a, const CoordinateXY& b) const; + + /** \brief + * Initialize to a null Envelope. + */ + void init() + { + setToNull(); + }; + + /** \brief + * Initialize an Envelope for a region defined by maximum and minimum values. + * + * @param x1 the first x-value + * @param x2 the second x-value + * @param y1 the first y-value + * @param y2 the second y-value + */ + void init(double x1, double x2, double y1, double y2) + { + if(x1 < x2) { + minx = x1; + maxx = x2; + } + else { + minx = x2; + maxx = x1; + } + if(y1 < y2) { + miny = y1; + maxy = y2; + } + else { + miny = y2; + maxy = y1; + } + }; + + /** \brief + * Initialize an Envelope to a region defined by two Coordinates. + * + * @param p1 the first Coordinate + * @param p2 the second Coordinate + */ + void init(const CoordinateXY& p1, const CoordinateXY& p2) + { + init(p1.x, p2.x, p1.y, p2.y); + }; + + /** \brief + * Initialize an Envelope to a region defined by a single Coordinate. + * + * @param p the Coordinate + */ + void init(const CoordinateXY& p) + { + init(p.x, p.x, p.y, p.y); + }; + + /** \brief + * Makes this `Envelope` a "null" envelope, that is, the envelope + * of the empty geometry. + */ + void setToNull() + { + minx = maxx = miny = maxy = DoubleNotANumber; + }; + + /** \brief + * Returns `true` if this Envelope is a "null" envelope. + * + * @return `true` if this Envelope is uninitialized or is the + * envelope of the empty geometry. + */ + bool isNull(void) const + { + return std::isnan(maxx); + }; + + /** \brief + * Returns the difference between the maximum and minimum x values. + * + * @return `max x - min x`, or 0 if this is a null Envelope + */ + double getWidth() const + { + if(isNull()) { + return 0; + } + return maxx - minx; + } + + /** \brief + * Returns the difference between the maximum and minimum y values. + * + * @return `max y - min y`, or 0 if this is a null Envelope + */ + double getHeight() const + { + if(isNull()) { + return 0; + } + return maxy - miny; + } + + /** \brief + * Gets the area of this envelope. + * + * @return the area of the envelope + * @return 0.0 if the envelope is null + */ + double + getArea() const + { + return getWidth() * getHeight(); + } + + /** \brief + * Gets the perimeter of this envelope. + * + * @return the perimeter of the envelope + * @return 0.0 if the envelope is null + */ + double + getPerimeter() const + { + return 2*getWidth() + 2*getHeight(); + } + + /** \brief + * Returns true if this Envelope covers a finite region + */ + bool + isFinite() const + { + return std::isfinite(getArea()); + } + + /** \brief + * Returns the Envelope maximum y-value. + * Null envelopes do not have maximum values. + */ + double getMaxY() const + { + assert(!isNull()); + return maxy; + }; + + /** \brief + * Returns the Envelope maximum x-value. + * Null envelopes do not have maximum values. + */ + double getMaxX() const + { + assert(!isNull()); + return maxx; + }; + + /** \brief + * Returns the Envelope minimum y-value. + * Null envelopes do not have maximum values. + */ + double getMinY() const + { + assert(!isNull()); + return miny; + }; + + /** \brief + * Returns the Envelope minimum x-value. + * Null envelopes do not have maximum values. + */ + double getMinX() const + { + assert(!isNull()); + return minx; + }; + + /** + * Gets the length of the diameter (diagonal) of the envelope. + * + * @return the diameter length + */ + double getDiameter() const + { + if (isNull()) { + return 0.0; + } + double w = getWidth(); + double h = getHeight(); + return std::sqrt(w*w + h*h); + } + + /** \brief + * Computes the coordinate of the centre of this envelope + * (as long as it is non-null). + * + * @param centre The coordinate to write results into + * @return `false` if the center could not be found (null envelope). + */ + bool centre(CoordinateXY& centre) const; + + /** \brief + * Computes the intersection of two [Envelopes](@ref Envelope). + * + * @param env the envelope to intersect with + * @param result the envelope representing the intersection of + * the envelopes (this will be the null envelope + * if either argument is null, or they do not intersect) + * @return false if no intersection is found + */ + bool intersection(const Envelope& env, Envelope& result) const; + + /** \brief + * Computes the intersection of two [Envelopes](@ref Envelope). + * + * @param env the envelope to intersect with + * @return the envelope representing the intersection of + * the envelopes (this will be the null envelope + * if either argument is null, or they do not intersect) + */ + Envelope intersection(const Envelope& env) const; + + /** \brief + * Translates this envelope by given amounts in the X and Y direction. + * + * @param transX the amount to translate along the X axis + * @param transY the amount to translate along the Y axis + */ + void translate(double transX, double transY); + + /** \brief + * Expands this envelope by a given distance in all directions. + * Both positive and negative distances are supported. + * + * @param deltaX the distance to expand the envelope along the X axis + * @param deltaY the distance to expand the envelope along the Y axis + */ + void expandBy(double deltaX, double deltaY); + + /** \brief + * Expands this envelope by a given distance in all directions. + * + * Both positive and negative distances are supported. + * + * @param p_distance the distance to expand the envelope + */ + void + expandBy(double p_distance) + { + expandBy(p_distance, p_distance); + }; + + /** \brief + * Enlarges the boundary of the Envelope so that it contains p. Does + * nothing if p is already on or within the boundaries. + * + * @param p the Coordinate to include + */ + void expandToInclude(const CoordinateXY& p) + { + expandToInclude(p.x, p.y); + }; + + /** \brief + * Enlarges the boundary of the Envelope so that it contains (x,y). + * + * Does nothing if (x,y) is already on or within the boundaries. + * + * @param x the value to lower the minimum x + * to or to raise the maximum x to + * @param y the value to lower the minimum y + * to or to raise the maximum y to + */ + void expandToInclude(double x, double y) + { + if(isNull()) { + minx = x; + maxx = x; + miny = y; + maxy = y; + } + else { + if(x < minx) { + minx = x; + } + if(x > maxx) { + maxx = x; + } + if(y < miny) { + miny = y; + } + if(y > maxy) { + maxy = y; + } + } + }; + + /** \brief + * Enlarges the boundary of the Envelope so that it contains `other`. + * + * Does nothing if other is wholly on or within the boundaries. + * + * @param other the Envelope to merge with + */ + void expandToInclude(const Envelope* other) + { + if(isNull()) { + minx = other->minx; + maxx = other->maxx; + miny = other->miny; + maxy = other->maxy; + } + else { + if(std::isless(other->minx, minx)) { + minx = other->minx; + } + if(std::isgreater(other->maxx, maxx)) { + maxx = other->maxx; + } + if(std::isless(other->miny, miny)) { + miny = other->miny; + } + if(std::isgreater(other->maxy, maxy)) { + maxy = other->maxy; + } + } + }; + + void expandToInclude(const Envelope& other) + { + return expandToInclude(&other); + }; + + /** \brief + * Tests if the Envelope `other` lies wholly inside this Envelope + * (inclusive of the boundary). + * + * Note that this is **not** the same definition as the SFS `contains`, + * which would exclude the envelope boundary. + * + * @param other the Envelope to check + * @return `true` if `other` is contained in this Envelope + * + * @see covers(Envelope) + */ + bool + contains(const Envelope& other) const + { + return covers(other); + } + + bool + contains(const Envelope* other) const + { + return contains(*other); + } + + /** \brief + * Returns `true` if the given point lies in or on the envelope. + * + * @param p the point which this Envelope is being checked for containing + * @return `true` if the point lies in the interior or on the boundary + * of this Envelope. + */ + bool + contains(const CoordinateXY& p) const + { + return covers(p.x, p.y); + } + + /** \brief + * Returns `true` if the given point lies in or on the envelope. + * + * @param x the x-coordinate of the point which this Envelope is + * being checked for containing + * @param y the y-coordinate of the point which this Envelope is being + * checked for containing + * @return `true` if `(x, y)` lies in the interior or on the boundary + * of this Envelope. + */ + bool + contains(double x, double y) const + { + return covers(x, y); + } + + /** \brief + * Check if the point p intersects (lies inside) the region of this Envelope. + * + * @param other the Coordinate to be tested + * @return true if the point intersects this Envelope + */ + bool intersects(const CoordinateXY& other) const + { + return (std::islessequal(other.x, maxx) && std::isgreaterequal(other.x, minx) && + std::islessequal(other.y, maxy) && std::isgreaterequal(other.y, miny)); + } + + /** \brief + * Check if the point (x, y) intersects (lies inside) the region of this Envelope. + * + * @param x the x-ordinate of the point + * @param y the y-ordinate of the point + * @return `true` if the point intersects this Envelope + */ + bool intersects(double x, double y) const + { + return std::islessequal(x, maxx) && + std::isgreaterequal(x, minx) && + std::islessequal(y, maxy) && + std::isgreaterequal(y, miny); + } + + /** \brief + * Check if the region defined by other Envelope intersects the region of this Envelope. + * + * @param other the Envelope which this Envelope is being checked for intersection + * @return true if the Envelopes intersects + */ + bool intersects(const Envelope* other) const + { + return std::islessequal(other->minx, maxx) && + std::isgreaterequal(other->maxx, minx) && + std::islessequal(other->miny, maxy) && + std::isgreaterequal(other->maxy, miny); + } + + bool intersects(const Envelope& other) const + { + return intersects(&other); + } + + /** + * Tests if the region defined by other + * is disjoint from the region of this Envelope + * + * @param other the Envelope being checked for disjointedness + * @return true if the Envelopes are disjoint + */ + bool disjoint(const Envelope& other) const + { + return !intersects(other); + } + + bool disjoint(const Envelope* other) const + { + return !intersects(other); + } + + /** \brief + * Tests if the given point lies in or on the envelope. + * + * @param x the x-coordinate of the point which this Envelope is being checked for containing + * @param y the y-coordinate of the point which this Envelope is being checked for containing + * @return `true` if `(x, y)` lies in the interior or on the boundary of this Envelope. + */ + bool covers(double x, double y) const { + return std::isgreaterequal(x, minx) && + std::islessequal(x, maxx) && + std::isgreaterequal(y, miny) && + std::islessequal(y, maxy); + } + + /** \brief + * Tests if the given point lies in or on the envelope. + * + * @param p the point which this Envelope is being checked for containing + * @return `true` if the point lies in the interior or on the boundary of this Envelope. + */ + bool covers(const CoordinateXY* p) const + { + return covers(p->x, p->y); + } + + /** \brief + * Tests if the Envelope `other` lies wholly inside this Envelope (inclusive of the boundary). + * + * @param other the Envelope to check + * @return true if this Envelope covers the `other` + */ + bool covers(const Envelope& other) const; + + bool + covers(const Envelope* other) const + { + return covers(*other); + } + + + /** \brief + * Returns `true` if the Envelope `other` spatially equals this Envelope. + * + * @param other the Envelope which this Envelope is being checked for equality + * @return `true` if this and `other` Envelope objects are spatially equal + */ + bool equals(const Envelope* other) const; + + /** + * Returns `true` if all the extents of the Envelope are finite and defined (not NaN) + * + * @return `true` if envelope has only finite/valid extents, `false` otherwise + */ + bool isfinite() const; + + /** \brief + * Returns a `string` of the form `Env[minx:maxx,miny:maxy]`. + * + * @return a `string` of the form `Env[minx:maxx,miny:maxy]` + */ + std::string toString() const; + + /** \brief + * Computes the distance between this and another Envelope. + * + * The distance between overlapping Envelopes is 0. Otherwise, the + * distance is the Euclidean distance between the closest points. + */ + double distance(const Envelope& env) const + { + return std::sqrt(distanceSquared(env)); + } + + /** \brief + * Computes the maximum distance between points in this and another Envelope. + */ + double maxDistance(const Envelope& other) const + { + Coordinate p(std::min(minx, other.minx), std::min(miny, other.miny)); + Coordinate q(std::max(maxx, other.maxx), std::max(maxy, other.maxy)); + return p.distance(q); + } + + /** \brief + * Computes the square of the distance between this and another Envelope. + * + * The distance between overlapping Envelopes is 0. Otherwise, the + * distance is the Euclidean distance between the closest points. + */ + double distanceSquared(const Envelope& env) const + { + double dx = std::max(0.0, + std::max(maxx, env.maxx) - std::min(minx, env.minx) - (maxx - minx) - + (env.maxx - env.minx)); + double dy = std::max(0.0, + std::max(maxy, env.maxy) - std::min(miny, env.miny) - (maxy - miny) - + (env.maxy - env.miny)); + + return dx * dx + dy * dy; + }; + + /** \brief + * Computes the distance between one Coordinate and an Envelope + * defined by two other Coordinates. The order of the Coordinates + * used to define the envelope is not significant. + * + * @param c the coordinate to from which distance should be found + * @param p0 first coordinate defining an envelope + * @param p1 second coordinate defining an envelope. + */ + static double distanceToCoordinate( + const CoordinateXY& c, + const CoordinateXY& p0, + const CoordinateXY& p1) + { + return std::sqrt(distanceSquaredToCoordinate(c, p0, p1)); + }; + + /** \brief + * Computes the squared distance between one Coordinate and an Envelope + * defined by two other Coordinates. The order of the Coordinates + * used to define the envelope is not significant. + * + * @param c the coordinate to from which distance should be found + * @param p0 first coordinate defining an envelope + * @param p1 second coordinate defining an envelope. + */ + static double distanceSquaredToCoordinate( + const CoordinateXY& c, + const CoordinateXY& p0, + const CoordinateXY& p1) + { + double xa = c.x - p0.x; + double xb = c.x - p1.x; + double ya = c.y - p0.y; + double yb = c.y - p1.y; + + // If sign of a and b are not the same, then Envelope spans c and distance is zero. + double dx = (std::signbit(xa) == std::signbit(xb)) * std::min(std::abs(xa), std::abs(xb)); + double dy = (std::signbit(ya) == std::signbit(yb)) * std::min(std::abs(ya), std::abs(yb)); + + return dx*dx + dy*dy; + } + + std::size_t hashCode() const + { + auto hash = std::hash{}; + + //Algorithm from Effective Java by Joshua Bloch [Jon Aquino] + std::size_t result = 17; + result = 37 * result + hash(minx); + result = 37 * result + hash(maxx); + result = 37 * result + hash(miny); + result = 37 * result + hash(maxy); + return result; + } + + struct GEOS_DLL HashCode + { + std::size_t operator()(const Envelope& e) const + { + return e.hashCode(); + }; + }; + + /// Checks if two Envelopes are equal (2D only check) + // GEOS_DLL bool operator==(const Envelope& a, const Envelope& b); + GEOS_DLL friend bool + operator==(const Envelope& a, const Envelope& b) + { + return a.equals(&b); + } + + // GEOS_DLL bool operator!=(const Envelope& a, const Envelope& b); + GEOS_DLL friend bool + operator!=(const Envelope& a, const Envelope& b) + { + return !(a == b); + } + + /// Strict weak ordering operator for Envelope + /// This is the C++ equivalent of JTS's compareTo + GEOS_DLL friend bool + operator< (const Envelope& a, const Envelope& b); + +private: + + /** \brief + * Splits a string into parts based on the supplied delimiters. + * + * This is a generic function that really belongs in a utility + * file somewhere + */ + static std::vector split(const std::string& str, + const std::string& delimiters = " "); + + static double distance(double x0, double y0, double x1, double y1) + { + double dx = x1 - x0; + double dy = y1 - y0; + return std::sqrt(dx * dx + dy * dy); + } + + /// the minimum x-coordinate + double minx; + + /// the maximum x-coordinate + double maxx; + + /// the minimum y-coordinate + double miny; + + /// the maximum y-coordinate + double maxy; +}; + + + + +} // namespace geos::geom +} // namespace geos diff --git a/Sources/geos/include/geos/geom/Geometry.h b/Sources/geos/include/geos/geom/Geometry.h new file mode 100644 index 0000000..4701c76 --- /dev/null +++ b/Sources/geos/include/geos/geom/Geometry.h @@ -0,0 +1,1048 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 2011 Sandro Santilli + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Geometry.java rev. 1.112 + * + **********************************************************************/ + +#pragma once + +#ifndef USE_UNSTABLE_GEOS_CPP_API +#ifndef _MSC_VER +# warning "The GEOS C++ API is unstable, please use the C API instead" +# warning "HINT: #include geos_c.h" +#else +#pragma message("The GEOS C++ API is unstable, please use the C API instead") +#pragma message("HINT: #include geos_c.h") +#endif +#endif + +#include +#include +#include // for Dimension::DimensionType +#include // for inheritance +#include // to materialize CoordinateSequence + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#pragma warning(disable: 4355) // warning C4355: 'this' : used in base member initializer list +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateFilter; +class CoordinateSequence; +class CoordinateSequenceFilter; +class GeometryComponentFilter; +class GeometryFactory; +class GeometryFilter; +class PrecisionModel; +class Point; +class IntersectionMatrix; +} +namespace io { // geos.io +} // namespace geos.io +} + +namespace geos { // geos +namespace geom { // geos::geom + +/// Geometry types +enum GeometryTypeId : int { + /// a point + GEOS_POINT, + /// a linestring + GEOS_LINESTRING, + /// a linear ring (linestring with 1st point == last point) + GEOS_LINEARRING, + /// a polygon + GEOS_POLYGON, + /// a collection of points + GEOS_MULTIPOINT, + /// a collection of linestrings + GEOS_MULTILINESTRING, + /// a collection of polygons + GEOS_MULTIPOLYGON, + /// a collection of heterogeneus geometries + GEOS_GEOMETRYCOLLECTION, + GEOS_CIRCULARSTRING, + GEOS_COMPOUNDCURVE, + GEOS_CURVEPOLYGON, + GEOS_MULTICURVE, + GEOS_MULTISURFACE, +}; + +enum GeometrySortIndex { + SORTINDEX_POINT = 0, + SORTINDEX_MULTIPOINT = 1, + SORTINDEX_LINESTRING = 2, + SORTINDEX_LINEARRING = 3, + SORTINDEX_MULTILINESTRING = 4, + SORTINDEX_POLYGON = 5, + SORTINDEX_MULTIPOLYGON = 6, + SORTINDEX_GEOMETRYCOLLECTION = 7, + SORTINDEX_CIRCULARSTRING = 8, + SORTINDEX_COMPOUNDCURVE = 9, + SORTINDEX_CURVEPOLYGON = 10, + SORTINDEX_MULTICURVE = 11, + SORTINDEX_MULTISURFACE = 12, +}; + +/** + * \class Geometry geom.h geos.h + * + * \brief Basic implementation of Geometry, constructed and + * destructed by GeometryFactory. + * + * clone returns a deep copy of the object. + * Use GeometryFactory to construct. + * + *

Binary Predicates

+ * Because it is not clear at this time + * what semantics for spatial + * analysis methods involving GeometryCollections would be useful, + * GeometryCollections are not supported as arguments to binary + * predicates (other than convexHull) or the relate + * method. + * + *

Set-Theoretic Methods

+ * + * The spatial analysis methods will + * return the most specific class possible to represent the result. If the + * result is homogeneous, a Point, LineString, or + * Polygon will be returned if the result contains a single + * element; otherwise, a MultiPoint, MultiLineString, + * or MultiPolygon will be returned. If the result is + * heterogeneous a GeometryCollection will be returned.

+ * + * Because it is not clear at this time what semantics for set-theoretic + * methods involving GeometryCollections would be useful, + * GeometryCollections + * are not supported as arguments to the set-theoretic methods. + * + *

Representation of Computed Geometries

+ * + * The SFS states that the result + * of a set-theoretic method is the "point-set" result of the usual + * set-theoretic definition of the operation (SFS 3.2.21.1). However, there are + * sometimes many ways of representing a point set as a Geometry. + *

+ * + * The SFS does not specify an unambiguous representation of a given point set + * returned from a spatial analysis method. One goal of JTS is to make this + * specification precise and unambiguous. JTS will use a canonical form for + * Geometrys returned from spatial analysis methods. The canonical + * form is a Geometry which is simple and noded: + *

    + *
  • Simple means that the Geometry returned will be simple according to + * the JTS definition of isSimple. + *
  • Noded applies only to overlays involving LineStrings. It + * means that all intersection points on LineStrings will be + * present as endpoints of LineStrings in the result. + *
+ * This definition implies that non-simple geometries which are arguments to + * spatial analysis methods must be subjected to a line-dissolve process to + * ensure that the results are simple. + * + *

Constructed Points And The Precision Model

+ * + * The results computed by the set-theoretic methods may + * contain constructed points which are not present in the input Geometry. + * These new points arise from intersections between line segments in the + * edges of the input Geometry. In the general case it is not + * possible to represent constructed points exactly. This is due to the fact + * that the coordinates of an intersection point may contain twice as many bits + * of precision as the coordinates of the input line segments. In order to + * represent these constructed points explicitly, JTS must truncate them to fit + * the PrecisionModel. + * + * Unfortunately, truncating coordinates moves them slightly. Line segments + * which would not be coincident in the exact result may become coincident in + * the truncated representation. This in turn leads to "topology collapses" -- + * situations where a computed element has a lower dimension than it would in + * the exact result. + * + * When JTS detects topology collapses during the computation of spatial + * analysis methods, it will throw an exception. If possible the exception will + * report the location of the collapse. + * + * equals(Object) and hashCode are not overridden, so that when two + * topologically equal Geometries are added to HashMaps and HashSets, they + * remain distinct. This behaviour is desired in many cases. + * + */ +class GEOS_DLL Geometry { + +public: + + friend class GeometryFactory; + + /// A vector of const Geometry pointers + using ConstVect = std::vector; + + /// A vector of non-const Geometry pointers + using NonConstVect = std::vector; + + /// An unique_ptr of Geometry + using Ptr = std::unique_ptr ; + + /// Make a deep-copy of this Geometry + std::unique_ptr clone() const { return std::unique_ptr(cloneImpl()); } + + /// Destroy Geometry and all components + virtual ~Geometry(); + + + /** + * \brief + * Gets the factory which contains the context in which this + * geometry was created. + * + * @return the factory for this geometry + */ + const GeometryFactory* + getFactory() const + { + return _factory; + } + + /** + * \brief + * A simple scheme for applications to add their own custom data to + * a Geometry. + * An example use might be to add an object representing a + * Coordinate Reference System. + * + * Note that user data objects are not present in geometries created + * by construction methods. + * + * @param newUserData an object, the semantics for which are + * defined by the application using this Geometry + */ + void + setUserData(void* newUserData) + { + _userData = newUserData; + } + + /** + * \brief + * Gets the user data object for this geometry, if any. + * + * @return the user data object, or null if none set + */ + void* + getUserData() const + { + return _userData; + } + + /** \brief + * Returns the ID of the Spatial Reference System used by the Geometry. + * + * GEOS supports Spatial Reference System information in the simple way + * defined in the SFS. A Spatial Reference System ID (SRID) is present + * in each Geometry object. Geometry provides basic accessor operations + * for this field, but no others. The SRID is represented as an integer. + * + * @return the ID of the coordinate space in which the Geometry is defined. + */ + int getSRID() const + { + return SRID; + } + + /** \brief + * Sets the ID of the Spatial Reference System used by the Geometry. + */ + virtual void + setSRID(int newSRID) + { + SRID = newSRID; + } + + /** + * \brief + * Get the PrecisionModel used to create this Geometry. + */ + const PrecisionModel* getPrecisionModel() const; + + /// Returns a vertex of this Geometry, or NULL if this is the empty geometry. + virtual const CoordinateXY* getCoordinate() const = 0; //Abstract + + /** + * \brief + * Returns this Geometry vertices. + * Caller takes ownership of the returned object. + */ + virtual std::unique_ptr getCoordinates() const = 0; //Abstract + + /// Returns the count of this Geometrys vertices. + virtual std::size_t getNumPoints() const = 0; //Abstract + + /// Returns false if the Geometry not simple. + virtual bool isSimple() const; + + /// Return a string representation of this Geometry type + virtual std::string getGeometryType() const = 0; //Abstract + + /// Returns whether the Geometry contains curved components + virtual bool hasCurvedComponents() const; + + /// Return an integer representation of this Geometry type + virtual GeometryTypeId getGeometryTypeId() const = 0; //Abstract + + /** + * \brief Returns the number of geometries in this collection, + * or 1 if this is not a collection. + * + * Empty collection or multi-geometry types return 0, + * and empty simple geometry types return 1. + */ + virtual std::size_t + getNumGeometries() const + { + return 1; + } + + /// \brief Returns a pointer to the nth Geometry in this collection + /// (or self if this is not a collection) + virtual const Geometry* + getGeometryN(std::size_t /*n*/) const + { + return this; + } + + /** + * \brief Tests the validity of this Geometry. + * + * Subclasses provide their own definition of "valid". + * + * @return true if this Geometry is valid + * + * @see IsValidOp + */ + bool isValid() const; + + /// Returns whether or not the set of points in this Geometry is empty. + virtual bool isEmpty() const = 0; //Abstract + + /// Polygon overrides to check for actual rectangle + virtual bool + isRectangle() const + { + return false; + } + + /// Returns the dimension of this Geometry (0=point, 1=line, 2=surface) + virtual Dimension::DimensionType getDimension() const = 0; //Abstract + + /// Checks whether any component of this geometry has dimension d + virtual bool hasDimension(Dimension::DimensionType d) const { + return getDimension() == d; + } + + /// Checks whether this Geometry consists only of components having dimension d. + virtual bool isDimensionStrict(Dimension::DimensionType d) const { + return d == getDimension(); + } + + bool isPuntal() const { + return isDimensionStrict(Dimension::P); + } + + bool isLineal() const { + return isDimensionStrict(Dimension::L); + } + + bool isPolygonal() const { + return isDimensionStrict(Dimension::A); + } + + bool isMixedDimension() const; + bool isMixedDimension(Dimension::DimensionType* baseDim) const; + + bool isCollection() const { + int t = getGeometryTypeId(); + return t == GEOS_GEOMETRYCOLLECTION || + t == GEOS_MULTIPOINT || + t == GEOS_MULTILINESTRING || + t == GEOS_MULTIPOLYGON; + } + + static GeometryTypeId multiTypeId(GeometryTypeId typeId) { + switch (typeId) { + case GEOS_POINT: return GEOS_MULTIPOINT; + case GEOS_LINESTRING: return GEOS_MULTILINESTRING; + case GEOS_POLYGON: return GEOS_MULTIPOLYGON; + default: return typeId; + } + } + + /// Returns the coordinate dimension of this Geometry (2=XY, 3=XYZ or XYM, 4=XYZM). + virtual uint8_t getCoordinateDimension() const = 0; //Abstract + + virtual bool hasZ() const = 0; + + virtual bool hasM() const = 0; + + /** + * \brief + * Returns the boundary, or an empty geometry of appropriate + * dimension if this Geometry is empty. + * + * (In the case of zero-dimensional geometries, + * an empty GeometryCollection is returned.) + * For a discussion of this function, see the OpenGIS Simple + * Features Specification. As stated in SFS Section 2.1.13.1, + * "the boundary of a Geometry is a set of Geometries of the + * next lower dimension." + * + * @return the closure of the combinatorial boundary + * of this Geometry. + * Ownershipof the returned object transferred to caller. + */ + virtual std::unique_ptr getBoundary() const = 0; //Abstract + + /// Returns the dimension of this Geometrys inherent boundary. + virtual int getBoundaryDimension() const = 0; //Abstract + + /// Returns this Geometrys bounding box. + virtual std::unique_ptr getEnvelope() const; + + /** \brief + * Returns the minimum and maximum x and y values in this Geometry, + * or a null Envelope if this Geometry is empty. + */ + virtual const Envelope* getEnvelopeInternal() const = 0; + + /** + * Tests whether this geometry is disjoint from the specified geometry. + * + * The disjoint predicate has the following equivalent + * definitions: + * - The two geometries have no point in common + * - The DE-9IM Intersection Matrix for the two geometries matches + * [FF*FF****] + * - ! g.intersects(this) + * (disjoint is the inverse of intersects) + * + * @param other the Geometry with which to compare this Geometry + * @return true if the two Geometrys are disjoint + * + * @see Geometry::intersects + */ + virtual bool disjoint(const Geometry* other) const; + + /** \brief + * Returns true if the DE-9IM intersection matrix for the two + * Geometrys is FT*******, F**T***** or F***T****. + */ + virtual bool touches(const Geometry* other) const; + + /// Returns true if disjoint returns false. + virtual bool intersects(const Geometry* g) const; + + /** + * Tests whether this geometry crosses the specified geometry. + * + * The crosses predicate has the following equivalent + * definitions: + * - The geometries have some but not all interior points in common. + * - The DE-9IM Intersection Matrix for the two geometries matches + * - [T*T******] (for P/L, P/A, and L/A situations) + * - [T*****T**] (for L/P, A/P, and A/L situations) + * - [0********] (for L/L situations) + * For any other combination of dimensions this predicate returns + * false. + * + * The SFS defined this predicate only for P/L, P/A, L/L, and L/A + * situations. + * JTS extends the definition to apply to L/P, A/P and A/L situations + * as well, in order to make the relation symmetric. + * + * @param g the Geometry with which to compare this + * Geometry + *@return true if the two Geometrys cross. + */ + virtual bool crosses(const Geometry* g) const; + + /** \brief + * Returns true if the DE-9IM intersection matrix for the two + * Geometrys is T*F**F***. + */ + virtual bool within(const Geometry* g) const; + + /// Returns true if other.within(this) returns true. + virtual bool contains(const Geometry* g) const; + + /** \brief + * Returns true if the DE-9IM intersection matrix for the two + * Geometrys is T*T***T** (for two points or two surfaces) + * 1*T***T** (for two curves). + */ + virtual bool overlaps(const Geometry* g) const; + + /** + * \brief + * Returns true if the elements in the DE-9IM intersection matrix + * for the two Geometrys match the elements in intersectionPattern. + * + * IntersectionPattern elements may be: 0 1 2 T ( = 0, 1 or 2) + * F ( = -1) * ( = -1, 0, 1 or 2). + * + * For more information on the DE-9IM, see the OpenGIS Simple + * Features Specification. + * + * @throws util::IllegalArgumentException if either arg is a collection + * + */ + bool relate(const Geometry* g, + const std::string& intersectionPattern) const; + + bool + relate(const Geometry& g, const std::string& intersectionPattern) const + { + return relate(&g, intersectionPattern); + } + + /// Returns the DE-9IM intersection matrix for the two Geometrys. + std::unique_ptr relate(const Geometry* g) const; + + std::unique_ptr relate(const Geometry& g) const; + + /** + * \brief + * Returns true if the DE-9IM intersection matrix for the two + * Geometrys is T*F**FFF*. + */ + bool equals(const Geometry* g) const; + + /** \brief + * Returns true if this geometry covers the + * specified geometry. + * + * The covers predicate has the following + * equivalent definitions: + * + * - Every point of the other geometry is a point of this geometry. + * - The DE-9IM Intersection Matrix for the two geometries is + * T*****FF* + * or *T****FF* + * or ***T**FF* + * or ****T*FF* + * - g.coveredBy(this) + * (covers is the inverse of coveredBy) + * + * If either geometry is empty, the value of this predicate + * is false. + * + * This predicate is similar to {@link #contains}, + * but is more inclusive (i.e. returns true for more cases). + * In particular, unlike contains it does not distinguish + * between points in the boundary and in the interior of geometries. + * For most situations, covers should be used in + * preference to contains. + * As an added benefit, covers is more amenable to + * optimization, and hence should be more performant. + * + * @param g + * the Geometry with which to compare this + * Geometry + * + * @return true if this Geometry + * covers g + * + * @see Geometry::contains + * @see Geometry::coveredBy + */ + bool covers(const Geometry* g) const; + + /** \brief + * Tests whether this geometry is covered by the + * specified geometry. + * + * The coveredBy predicate has the following + * equivalent definitions: + * + * - Every point of this geometry is a point of the other geometry. + * - The DE-9IM Intersection Matrix for the two geometries matches + * [T*F**F***] + * or [*TF**F***] + * or [**FT*F***] + * or [**F*TF***] + * - g.covers(this) + * (coveredBy is the converse of covers) + * + * If either geometry is empty, the value of this predicate + * is false. + * + * This predicate is similar to {@link #within}, + * but is more inclusive (i.e. returns true for more cases). + * + * @param g the Geometry with which to compare + * this Geometry + * @return true if this Geometry + * is covered by g + * + * @see Geometry#within + * @see Geometry#covers + */ + bool coveredBy(const Geometry* g) const; + + + /// Returns the Well-known Text representation of this Geometry. + virtual std::string toString() const; + + virtual std::string toText() const; + + /// Returns a buffer region around this Geometry having the given width. + /// + /// @throws util::TopologyException if a robustness error occurs + /// + std::unique_ptr buffer(double distance) const; + + /// \brief + /// Returns a buffer region around this Geometry having the + /// given width and with a specified number of segments used + /// to approximate curves. + /// + /// @throws util::TopologyException if a robustness error occurs + /// + std::unique_ptr buffer(double distance, int quadrantSegments) const; + + /** \brief + * Computes a buffer area around this geometry having the given + * width and with a specified accuracy of approximation for circular + * arcs, and using a specified end cap style. + * + * Buffer area boundaries can contain circular arcs. + * To represent these arcs using linear geometry they must be + * approximated with line segments. + * + * The quadrantSegments argument allows controlling the + * accuracy of the approximation by specifying the number of line + * segments used to represent a quadrant of a circle + * + * The end cap style specifies the buffer geometry that will be + * created at the ends of linestrings. The styles provided are: + * + * - BufferOp::CAP_ROUND - (default) a semi-circle + * - BufferOp::CAP_BUTT - a straight line perpendicular to the + * end segment + * - BufferOp::CAP_SQUARE - a half-square + * + * + * @param distance the width of the buffer + * (may be positive, negative or 0) + * + * @param quadrantSegments the number of line segments used + * to represent a quadrant of a circle + * + * @param endCapStyle the end cap style to use + * + * @return an area geometry representing the buffer region + * + * @throws util::TopologyException if a robustness error occurs + * + * @see BufferOp + */ + std::unique_ptr buffer(double distance, int quadrantSegments, + int endCapStyle) const; + + /// \brief + /// Returns the smallest convex Polygon that contains + /// all the points in the Geometry. + virtual std::unique_ptr convexHull() const; + + /** \brief + * Computes a new geometry which has all component coordinate sequences + * in reverse order (opposite orientation) to this one. + * + * @return a reversed geometry + */ + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + + /** \brief + * Returns a Geometry representing the points shared by + * this Geometry and other. + * + * @throws util::TopologyException if a robustness error occurs + * @throws util::IllegalArgumentException if either input is a + * non-empty GeometryCollection + * + */ + std::unique_ptr intersection(const Geometry* other) const; + + /** \brief + * Returns a Geometry representing all the points in this Geometry + * and other. + * + * @throws util::TopologyException if a robustness error occurs + * @throws util::IllegalArgumentException if either input is a + * non-empty GeometryCollection + * + */ + std::unique_ptr Union(const Geometry* other) const; + // throw(IllegalArgumentException *, TopologyException *); + + /** \brief + * Computes the union of all the elements of this geometry. Heterogeneous + * [GeometryCollections](@ref GeometryCollection) are fully supported. + * + * The result obeys the following contract: + * + * - Unioning a set of [LineStrings](@ref LineString) has the effect of fully noding + * and dissolving the linework. + * - Unioning a set of [Polygons](@ref Polygon) will always + * return a polygonal geometry (unlike Geometry::Union(const Geometry* other) const), + * which may return geometries of lower dimension if a topology collapse + * occurred. + * + * @return the union geometry + * + * @see UnaryUnionOp + */ + Ptr Union() const; + // throw(IllegalArgumentException *, TopologyException *); + + /** + * \brief + * Returns a Geometry representing the points making up this + * Geometry that do not make up other. + * + * @throws util::TopologyException if a robustness error occurs + * @throws util::IllegalArgumentException if either input is a + * non-empty GeometryCollection + * + */ + std::unique_ptr difference(const Geometry* other) const; + + /** \brief + * Returns a set combining the points in this Geometry not in other, + * and the points in other not in this Geometry. + * + * @throws util::TopologyException if a robustness error occurs + * @throws util::IllegalArgumentException if either input is a + * non-empty GeometryCollection + * + */ + std::unique_ptr symDifference(const Geometry* other) const; + + /** \brief + * Returns true iff the two Geometrys are of the same type and their + * vertices corresponding by index are equal up to a specified distance + * tolerance. Geometries are not required to have the same dimemsion; + * any Z/M values are ignored. + */ + virtual bool equalsExact(const Geometry* other, double tolerance = 0) + const = 0; // Abstract + + /** \brief + * Returns true if the two geometries are of the same type and their + * vertices corresponding by index are equal in all dimensions. + */ + virtual bool equalsIdentical(const Geometry* other) const = 0; + + virtual void apply_rw(const CoordinateFilter* filter) = 0; //Abstract + virtual void apply_ro(CoordinateFilter* filter) const = 0; //Abstract + virtual void apply_rw(GeometryFilter* filter); + virtual void apply_ro(GeometryFilter* filter) const; + virtual void apply_rw(GeometryComponentFilter* filter); + virtual void apply_ro(GeometryComponentFilter* filter) const; + + /** + * Performs an operation on the coordinates in this Geometry's + * CoordinateSequences. + * If the filter reports that a coordinate value has been changed, + * {@link #geometryChanged} will be called automatically. + * + * @param filter the filter to apply + */ + virtual void apply_rw(CoordinateSequenceFilter& filter) = 0; + + /** + * Performs a read-only operation on the coordinates in this + * Geometry's CoordinateSequences. + * + * @param filter the filter to apply + */ + virtual void apply_ro(CoordinateSequenceFilter& filter) const = 0; + + /** \brief + * Apply a filter to each component of this geometry. + * The filter is expected to provide a .filter(const Geometry*) + * method. + * + * I intend similar templated methods to replace + * all the virtual apply_rw and apply_ro functions... + * --strk(2005-02-06); + */ + template + void + applyComponentFilter(T& f) const + { + for(std::size_t i = 0, n = getNumGeometries(); i < n; ++i) { + f.filter(getGeometryN(i)); + } + } + + /** + * Reorganizes this Geometry into normal form (or canonical form). + * Starting point of rings is lower left, collections are ordered + * by geometry type, etc. + */ + virtual void normalize() = 0; //Abstract + + /// Comparator for sorting geometry + int compareTo(const Geometry* geom) const; + + /// Returns the area of this Geometry. + virtual double getArea() const; + + /// Returns the length of this Geometry. + virtual double getLength() const; + + /** Returns the minimum distance between this Geometry and the Geometry g + * + * @param g the Geometry to calculate distance to + * @return the distance in Cartesian units + */ + virtual double distance(const Geometry* g) const; + + + /** \brief + * Tests whether the distance from this Geometry to another + * is less than or equal to a specified value. + * + * @param geom the Geometry to check the distance to + * @param cDistance the distance value to compare + * @return true if the geometries are less than + * distance apart. + * + * @todo doesn't seem to need being virtual, make it concrete + */ + virtual bool isWithinDistance(const Geometry* geom, + double cDistance) const; + + /** \brief + * Computes the centroid of this Geometry. + * + * The centroid is equal to the centroid of the set of component + * Geometries of highest dimension (since the lower-dimension geometries + * contribute zero "weight" to the centroid) + * + * @return a {@link Point} which is the centroid of this Geometry + */ + virtual std::unique_ptr getCentroid() const; + + /// Computes the centroid of this Geometry as a Coordinate + // + /// Returns false if centroid cannot be computed (EMPTY geometry) + /// + virtual bool getCentroid(CoordinateXY& ret) const; + + /** \brief + * Computes an interior point of this Geometry. + * + * An interior point is guaranteed to lie in the interior of the Geometry, + * if it possible to calculate such a point exactly. Otherwise, + * the point may lie on the boundary of the geometry. + * + * @return a Point which is in the interior of this Geometry, or + * null if the geometry doesn't have an interior (empty) + */ + std::unique_ptr getInteriorPoint() const; + + /** + * \brief + * Notifies this Geometry that its Coordinates have been changed + * by an external party (using a CoordinateFilter, for example). + */ + void geometryChanged(); + + /** + * \brief + * Notifies this Geometry that its Coordinates have been changed + * by an external party. + */ + virtual void geometryChangedAction() = 0; + +protected: + /// Make a deep-copy of this Geometry + virtual Geometry* cloneImpl() const = 0; + + /// Make a geometry with coordinates in reverse order + virtual Geometry* reverseImpl() const = 0; + + /// Returns true if the array contains any non-empty Geometrys. + template + static bool hasNonEmptyElements(const std::vector* geometries) { + return std::any_of(geometries->begin(), geometries->end(), [](const T& g) { return !g->isEmpty(); }); + } + + /// Returns true if the CoordinateSequence contains any null elements. + static bool hasNullElements(const CoordinateSequence* list); + + /// Returns true if the vector contains any null elements. + template + static bool hasNullElements(const std::vector* geometries) { + return std::any_of(geometries->begin(), geometries->end(), [](const T& g) { return g == nullptr; }); + } + +// static void reversePointOrder(CoordinateSequence* coordinates); +// static Coordinate& minCoordinate(CoordinateSequence* coordinates); +// static void scroll(CoordinateSequence* coordinates,Coordinate* firstCoordinate); +// static int indexOf(Coordinate* coordinate,CoordinateSequence* coordinates); +// + /** \brief + * Returns whether the two Geometrys are equal, from the point + * of view of the equalsExact method. + */ + virtual bool isEquivalentClass(const Geometry* other) const; + + static void checkNotGeometryCollection(const Geometry* g); + + virtual int compareToSameClass(const Geometry* geom) const = 0; //Abstract + + template + static int compare(const T& a, const T& b) + { + std::size_t i = 0; + std::size_t j = 0; + while(i < a.size() && j < b.size()) { + const auto& aGeom = *a[i]; + const auto& bGeom = *b[j]; + + int comparison = aGeom.compareTo(&bGeom); + if(comparison != 0) { + return comparison; + } + + i++; + j++; + } + + if(i < a.size()) { + return 1; + } + + if(j < b.size()) { + return -1; + } + + return 0; + } + + bool equal(const CoordinateXY& a, const CoordinateXY& b, + double tolerance) const; + int SRID; + + Geometry(const Geometry& geom); + + /** \brief + * Construct a geometry with the given GeometryFactory. + * + * Will keep a reference to the factory, so don't + * delete it until al Geometry objects referring to + * it are deleted. + * + * @param factory + */ + Geometry(const GeometryFactory* factory); + + template + static std::vector> toGeometryArray(std::vector> && v) { + static_assert(std::is_base_of::value, ""); + std::vector> gv(v.size()); + for (std::size_t i = 0; i < v.size(); i++) { + gv[i] = std::move(v[i]); + } + return gv; + } + + static std::vector> toGeometryArray(std::vector> && v) { + return std::move(v); + } + +protected: + + virtual int getSortIndex() const = 0; + + +private: + + class GEOS_DLL GeometryChangedFilter : public GeometryComponentFilter { + public: + void filter_rw(Geometry* geom) override; + }; + + static GeometryChangedFilter geometryChangedFilter; + + /// The GeometryFactory used to create this Geometry + /// + /// Externally owned + /// + const GeometryFactory* _factory; + + void* _userData; +}; + +/// \brief +/// Write the Well-known Binary representation of this Geometry +/// as an HEX string to the given output stream +/// +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Geometry& geom); + +struct GEOS_DLL GeometryGreaterThen { + bool operator()(const Geometry* first, const Geometry* second); +}; + + +/// Return current GEOS version +GEOS_DLL std::string geosversion(); + +/** + * \brief + * Return the version of JTS this GEOS + * release has been ported from. + */ +GEOS_DLL std::string jtsport(); + +// We use this instead of std::pair> because C++11 +// forbids that construct: +// http://lwg.github.com/issues/lwg-closed.html#2068 +struct GeomPtrPair { + typedef std::unique_ptr GeomPtr; + GeomPtr first; + GeomPtr second; +}; + +} // namespace geos::geom +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geom/GeometryCollection.h b/Sources/geos/include/geos/geom/GeometryCollection.h new file mode 100644 index 0000000..fd30f92 --- /dev/null +++ b/Sources/geos/include/geos/geom/GeometryCollection.h @@ -0,0 +1,268 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/GeometryCollection.java rev. 1.41 + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for proper use of unique_ptr<> +#include // for Dimension::DimensionType + +#include +#include +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +class CoordinateSequenceFilter; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \class GeometryCollection geom.h geos.h + * + * \brief Represents a collection of heterogeneous Geometry objects. + * + * Collections of Geometry of the same type are + * represented by GeometryCollection subclasses MultiPoint, + * MultiLineString, MultiPolygon. + */ +class GEOS_DLL GeometryCollection : public Geometry { + +public: + friend class GeometryFactory; + + typedef std::vector>::const_iterator const_iterator; + + typedef std::vector>::iterator iterator; + + const_iterator begin() const + { + return geometries.begin(); + }; + + const_iterator end() const + { + return geometries.end(); + }; + + /** + * Creates and returns a full copy of this GeometryCollection object. + * (including all coordinates contained by it). + * + * @return a clone of this instance + */ + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + ~GeometryCollection() override = default; + + void setSRID(int) override; + + /** + * \brief + * Collects all coordinates of all subgeometries into a + * CoordinateSequence. + * + * Note that the returned coordinates are copies, so + * you want be able to use them to modify the geometries + * in place. Also you'll need to delete the CoordinateSequence + * when finished using it. + * + * @return the collected coordinates + * + */ + std::unique_ptr getCoordinates() const override; + + bool isEmpty() const override; + + /** + * \brief + * Returns the maximum dimension of geometries in this collection + * (0=point, 1=line, 2=surface) + * + * @see Dimension::DimensionType + */ + Dimension::DimensionType getDimension() const override; + + bool hasDimension(Dimension::DimensionType d) const override; + + bool isDimensionStrict(Dimension::DimensionType d) const override; + + /// Returns coordinate dimension. + uint8_t getCoordinateDimension() const override; + + bool hasM() const override; + + bool hasZ() const override; + + std::unique_ptr getBoundary() const override; + + /** + * \brief + * Returns the maximum boundary dimension of geometries in + * this collection. + */ + int getBoundaryDimension() const override; + + std::size_t getNumPoints() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool equalsExact(const Geometry* other, + double tolerance = 0) const override; + + bool equalsIdentical(const Geometry* other) const override; + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_rw(const CoordinateFilter* filter) override; + + void apply_ro(GeometryFilter* filter) const override; + + void apply_rw(GeometryFilter* filter) override; + + void apply_ro(GeometryComponentFilter* filter) const override; + + void apply_rw(GeometryComponentFilter* filter) override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void normalize() override; + + const CoordinateXY* getCoordinate() const override; + + /// Returns the total area of this collection + double getArea() const override; + + /// Returns the total length of this collection + double getLength() const override; + + /// Returns the number of geometries in this collection + std::size_t getNumGeometries() const override; + + /// Returns a pointer to the nth Geometry in this collection + const Geometry* getGeometryN(std::size_t n) const override; + + /** + * \brief + * Take ownership of the sub-geometries managed by this GeometryCollection. + * After releasing the sub-geometries, the collection should be considered + * in a moved-from state and should not be accessed. + * @return vector of sub-geometries + */ + std::vector> releaseGeometries(); + + /** + * Creates a GeometryCollection with + * every component reversed. + * The order of the components in the collection are not reversed. + * + * @return a GeometryCollection in the reverse order + */ + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + + const Envelope* getEnvelopeInternal() const override { + if (envelope.isNull()) { + envelope = computeEnvelopeInternal(); + } + return &envelope; + } + +protected: + + struct CollectionFlags { + bool flagsCalculated; + bool hasPoints; + bool hasLines; + bool hasPolygons; + bool hasM; + bool hasZ; + bool hasCurves; + }; + + GeometryCollection(const GeometryCollection& gc); + GeometryCollection& operator=(const GeometryCollection& gc); + + /** \brief + * Construct a GeometryCollection with the given GeometryFactory. + * Will keep a reference to the factory, so don't + * delete it until al Geometry objects referring to + * it are deleted. + * Will take ownership of the Geometry vector. + * + * @param newGeoms + * The Geometrys for this + * GeometryCollection, + * or null or an empty array to + * create the empty geometry. + * Elements may be empty Geometrys, + * but not nulls. + * + * @param newFactory the GeometryFactory used to create this geometry + */ + GeometryCollection(std::vector> && newGeoms, const GeometryFactory& newFactory); + + /// Convenience constructor to build a GeometryCollection from vector of Geometry subclass pointers + template + GeometryCollection(std::vector> && newGeoms, const GeometryFactory& newFactory) : + GeometryCollection(toGeometryArray(std::move(newGeoms)), newFactory) {} + + GeometryCollection* cloneImpl() const override { return new GeometryCollection(*this); } + + GeometryCollection* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_GEOMETRYCOLLECTION; + }; + + std::vector> geometries; + mutable CollectionFlags flags; + mutable Envelope envelope; + + Envelope computeEnvelopeInternal() const; + + void geometryChangedAction() override { + envelope.setToNull(); + } + + int compareToSameClass(const Geometry* gc) const override; + + bool hasCurvedComponents() const override; + + void setFlags() const; + +}; + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/GeometryComponentFilter.h b/Sources/geos/include/geos/geom/GeometryComponentFilter.h new file mode 100644 index 0000000..e745ad5 --- /dev/null +++ b/Sources/geos/include/geos/geom/GeometryComponentFilter.h @@ -0,0 +1,61 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { // geos::geom +class Geometry; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * Geometry classes support the concept of applying + * a GeometryComponentFilter + * filter to the Geometry. + * The filter is applied to every component of the Geometry + * which is itself a Geometry. + * A GeometryComponentFilter filter can either + * record information about the Geometry + * or change the Geometry in some way. + * GeometryComponentFilter + * is an example of the Gang-of-Four Visitor pattern. + * + */ +class GEOS_DLL GeometryComponentFilter { +public: + + /** + * Performs an operation with or on geom. + * + * @param geom a Geometry to which the filter + * is applied. + */ + virtual void filter_rw(Geometry* geom); + virtual void filter_ro(const Geometry* geom); + + virtual bool isDone() { return false; } + + virtual + ~GeometryComponentFilter() {} +}; + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/GeometryFactory.h b/Sources/geos/include/geos/geom/GeometryFactory.h new file mode 100644 index 0000000..10c1e86 --- /dev/null +++ b/Sources/geos/include/geos/geom/GeometryFactory.h @@ -0,0 +1,542 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/GeometryFactory.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace geos { +namespace geom { +class Coordinate; +class CircularString; +class CompoundCurve; +class CoordinateSequence; +class Envelope; +class Geometry; +class GeometryCollection; +class LineString; +class LinearRing; +class MultiLineString; +class MultiCurve; +class MultiPoint; +class MultiPolygon; +class MultiSurface; +class CurvePolygon; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \brief + * Supplies a set of utility methods for building Geometry objects + * from CoordinateSequence or other Geometry objects. + * + * Note that the factory constructor methods do not change the input + * coordinates in any way. + * In particular, they are not rounded to the supplied PrecisionModel. + * It is assumed that input Coordinates meet the given precision. + */ +class GEOS_DLL GeometryFactory { +private: + + struct GeometryFactoryDeleter { + void + operator()(GeometryFactory* p) const + { + p->destroy(); + } + }; + +public: + + using Ptr = std::unique_ptr; + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having a + * floating PrecisionModel and a spatial-reference ID of 0. + */ + static GeometryFactory::Ptr create(); + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having + * the given PrecisionModel and the default CoordinateSequence + * implementation. + * + * @param pm the PrecisionModel to use + */ + static GeometryFactory::Ptr create(const PrecisionModel* pm); + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having + * the given {@link PrecisionModel} and spatial-reference ID, + * and the default CoordinateSequence implementation. + * + * @param pm the PrecisionModel to use, will be copied internally + * @param newSRID the SRID to use + */ + static GeometryFactory::Ptr create(const PrecisionModel* pm, int newSRID); + + /** + * \brief Copy constructor + * + * @param gf the GeometryFactory to clone from + */ + static GeometryFactory::Ptr create(const GeometryFactory& gf); + + /** + * \brief + * Return a pointer to the default GeometryFactory. + * This is a global shared object instantiated + * using default constructor. + */ + static const GeometryFactory* + getDefaultInstance(); + +//Skipped a lot of list to array converters + + static std::unique_ptr createPointFromInternalCoord(const Coordinate* coord, + const Geometry* exemplar); + + /// Converts an Envelope to a Geometry. + /// + /// Returned Geometry can be a Point, a Polygon or an EMPTY geom. + /// + std::unique_ptr toGeometry(const Envelope* envelope) const; + + /// \brief + /// Returns the PrecisionModel that Geometries created by this + /// factory will be associated with. + const PrecisionModel* getPrecisionModel() const + { + return &precisionModel; + }; + + /// Creates an EMPTY Point + std::unique_ptr createPoint(std::size_t coordinateDimension = 2) const; + std::unique_ptr createPoint(bool hasZ, bool hasM) const; + + /// Creates a Point using the given Coordinate + std::unique_ptr createPoint(const Coordinate& coordinate) const; + std::unique_ptr createPoint(const CoordinateXY& coordinate) const; + std::unique_ptr createPoint(const CoordinateXYM& coordinate) const; + std::unique_ptr createPoint(const CoordinateXYZM& coordinate) const; + + /// Creates a Point taking ownership of the given CoordinateSequence + std::unique_ptr createPoint(std::unique_ptr&& coordinates) const; + + /// Creates a Point with a deep-copy of the given CoordinateSequence. + std::unique_ptr createPoint(const CoordinateSequence& coordinates) const; + + /// Construct an EMPTY GeometryCollection + std::unique_ptr createGeometryCollection() const; + + /// Construct the EMPTY Geometry + std::unique_ptr createEmptyGeometry(GeometryTypeId type = GEOS_GEOMETRYCOLLECTION, bool hasZ=false, bool hasM=false) const; + + /// Construct a GeometryCollection taking ownership of given arguments + template + std::unique_ptr createGeometryCollection( + std::vector> && newGeoms) const { + // Can't use make_unique because constructor is protected + return std::unique_ptr(new GeometryCollection(Geometry::toGeometryArray(std::move(newGeoms)), *this)); + } + + /// Constructs a GeometryCollection with a deep-copy of args + std::unique_ptr createGeometryCollection( + const std::vector& newGeoms) const; + + /// Construct an EMPTY MultiLineString + std::unique_ptr createMultiLineString() const; + + /// Construct a MultiLineString with a deep-copy of given arguments + std::unique_ptr createMultiLineString( + const std::vector& fromLines) const; + + /// Construct a MultiLineString taking ownership of given arguments + std::unique_ptr createMultiLineString( + std::vector> && fromLines) const; + + std::unique_ptr createMultiLineString( + std::vector> && fromLines) const; + + /// Construct an EMPTY MultiCurve + std::unique_ptr createMultiCurve() const; + + /// Construct a MultiCurve taking ownership of given arguments + std::unique_ptr createMultiCurve( + std::vector> && fromCurves) const; + + std::unique_ptr createMultiCurve( + std::vector> && fromCurves) const; + + /// Construct an EMPTY MultiPolygon + std::unique_ptr createMultiPolygon() const; + + /// Construct a MultiPolygon with a deep-copy of given arguments + std::unique_ptr createMultiPolygon( + const std::vector& fromPolys) const; + + /// Construct a MultiPolygon taking ownership of given arguments + std::unique_ptr createMultiPolygon( + std::vector> && fromPolys) const; + + std::unique_ptr createMultiPolygon( + std::vector> && fromPolys) const; + + /// Construct an EMPTY MultiSurface + std::unique_ptr createMultiSurface() const; + + /// Construct a MultiSurface taking ownership of given arguments + std::unique_ptr createMultiSurface( + std::vector> && from) const; + + std::unique_ptr createMultiSurface( + std::vector> && from) const; + + /// Construct an EMPTY LinearRing + std::unique_ptr createLinearRing(std::size_t coordinateDimension = 2) const; + std::unique_ptr createLinearRing(bool hasZ, bool hasM) const; + + /// Construct a LinearRing taking ownership of given arguments + std::unique_ptr createLinearRing( + std::unique_ptr && newCoords) const; + + /// Construct a LinearRing with a deep-copy of given arguments + std::unique_ptr createLinearRing( + const CoordinateSequence& coordinates) const; + + /// Constructs an EMPTY MultiPoint. + std::unique_ptr createMultiPoint() const; + + template + std::unique_ptr createMultiPoint(const T& fromCoords) const + { + std::vector> pts; + pts.reserve(fromCoords.size()); + for (const auto& c : fromCoords) { + pts.emplace_back(createPoint(c)); + } + + return createMultiPoint(std::move(pts)); + } + + /// Construct a MultiPoint taking ownership of given arguments + std::unique_ptr createMultiPoint(std::vector> && newPoints) const; + + std::unique_ptr createMultiPoint(std::vector> && newPoints) const; + + /// Construct a MultiPoint with a deep-copy of given arguments + std::unique_ptr createMultiPoint( + const std::vector& fromPoints) const; + + /// \brief + /// Construct a MultiPoint containing a Point geometry + /// for each Coordinate in the given list. + std::unique_ptr createMultiPoint( + const CoordinateSequence& fromCoords) const; + + /// Construct an EMPTY Polygon + std::unique_ptr createPolygon(std::size_t coordinateDimension = 2) const; + std::unique_ptr createPolygon(bool hasZ, bool hasM) const; + + /// Construct a Polygon taking ownership of given arguments + std::unique_ptr createPolygon(std::unique_ptr && shell) const; + + std::unique_ptr createPolygon(std::unique_ptr && shell, + std::vector> && holes) const; + + /// Construct a Polygon from a Coordinate vector, taking ownership of the vector + std::unique_ptr createPolygon(CoordinateSequence && coords) const; + + /// Construct a Polygon with a deep-copy of given arguments + Polygon* createPolygon(const LinearRing& shell, + const std::vector& holes) const; + + + /// Construct an EMPTY CurvePolygon + std::unique_ptr createCurvePolygon(bool hasZ, bool hasM) const; + + /// Construct a CurvePolygon taking ownership of given arguments + std::unique_ptr createCurvePolygon(std::unique_ptr&& shell) const; + + std::unique_ptr createCurvePolygon(std::unique_ptr&& shell, + std::vector> && holes) const; + + /// Construct an EMPTY LineString + std::unique_ptr createLineString(std::size_t coordinateDimension = 2) const; + std::unique_ptr createLineString(bool hasZ, bool hasM) const; + + /// Copy a LineString + std::unique_ptr createLineString(const LineString& ls) const; + + /// Construct a LineString taking ownership of given argument + std::unique_ptr createLineString( + std::unique_ptr && coordinates) const; + + /// Construct a LineString with a deep-copy of given argument + std::unique_ptr createLineString( + const CoordinateSequence& coordinates) const; + + /// Construct an EMPTY CircularString + std::unique_ptr createCircularString(bool hasZ, bool hasM) const; + + /// Copy a CircularString + std::unique_ptr createCircularString(const CircularString& ls) const; + + /// Construct a CircularString taking ownership of given argument + std::unique_ptr createCircularString( + std::unique_ptr && coordinates) const; + + /// Construct a CircularString with a deep-copy of given argument + std::unique_ptr createCircularString( + const CoordinateSequence& coordinates) const; + + /// Construct an EMPTY CompoundCurve + std::unique_ptr createCompoundCurve() const; + + /// Construct a CompoundCurve taking ownership of given argument + std::unique_ptr createCompoundCurve(std::vector>&&) const; + + /** + * Creates an empty atomic geometry of the given dimension. + * If passed a dimension of -1 will create an empty {@link GeometryCollection}. + * + * @param dimension the required dimension (-1, 0, 1 or 2) + * @return an empty atomic geometry of given dimension + */ + std::unique_ptr createEmpty(int dimension) const; + + /** + * Creates an empty atomic geometry of the given type. + * @param typeId the desired GeometryTypeId + * @return an empty atomic geometry of given dimension + */ + std::unique_ptr createEmpty(GeometryTypeId typeId) const; + + std::unique_ptr createMulti(std::unique_ptr && geom) const; + + /** + * Build an appropriate Geometry, MultiGeometry, or + * GeometryCollection to contain the Geometrys in + * it. + * + * For example: + * + * - If geomList contains a single Polygon, + * the Polygon is returned. + * - If geomList contains several Polygons, a + * MultiPolygon is returned. + * - If geomList contains some Polygons and + * some LineStrings, a GeometryCollection is + * returned. + * - If geomList is empty, an empty + * GeometryCollection is returned + * . + * + * Note that this method does not "flatten" Geometries in the input, + * and hence if any MultiGeometries are contained in the input a + * GeometryCollection containing them will be returned. + * + * @param geoms the Geometrys to combine + * + * @return A Geometry of the "smallest", "most type-specific" + * class that can contain the elements of geomList. + * + * NOTE: the returned Geometry will take ownership of the + * given vector AND its elements + */ + std::unique_ptr buildGeometry(std::vector> && geoms) const; + + std::unique_ptr buildGeometry(std::vector> && geoms) const; + + std::unique_ptr buildGeometry(std::vector> && geoms) const; + + std::unique_ptr buildGeometry(std::vector> && geoms) const; + + /// See buildGeometry(std::vector&) for semantics + // + /// Will clone the geometries accessible through the iterator. + /// + /// @tparam T an iterator yielding something which casts to const Geometry* + /// @param from start iterator + /// @param toofar end iterator + /// + template + std::unique_ptr + buildGeometry(T from, T toofar) const + { + bool isHeterogeneous = false; + std::size_t count = 0; + int geomClass = -1; + for(T i = from; i != toofar; ++i) { + ++count; + const Geometry* g = *i; + if(geomClass < 0) { + geomClass = g->getSortIndex(); + } + else if(geomClass != g->getSortIndex()) { + isHeterogeneous = true; + } + } + + // for the empty geometry, return an empty GeometryCollection + if(count == 0) { + return std::unique_ptr(createGeometryCollection()); + } + + // for the single geometry, return a clone + if(count == 1) { + return (*from)->clone(); + } + + // Now we know it is a collection + + // FIXME: + // Until we tweak all the createMulti* interfaces + // to support taking iterators we'll have to build + // a custom vector here. + std::vector> fromGeoms; + for(T i = from; i != toofar; ++i) { + fromGeoms.push_back((*i)->clone()); + } + + // for an heterogeneous ... + if(isHeterogeneous) { + return createGeometryCollection(std::move(fromGeoms)); + } + + // At this point we know the collection is not hetereogenous. + switch((*from)->getDimension()) { + case Dimension::A: return createMultiPolygon(std::move(fromGeoms)); + case Dimension::L: return createMultiLineString(std::move(fromGeoms)); + case Dimension::P: return createMultiPoint(std::move(fromGeoms)); + default: + throw geos::util::IllegalArgumentException(std::string("Invalid geometry type.")); + } + } + + /** \brief + * This function does the same thing of the omonimouse function + * taking vector pointer instead of reference. + * + * The difference is that this version will copy needed data + * leaving ownership to the caller. + */ + std::unique_ptr buildGeometry(const std::vector& geoms) const; + + int getSRID() const + { + return SRID; + }; + + /// Returns a clone of given Geometry. + std::unique_ptr createGeometry(const Geometry* g) const; + + /// Destroy a Geometry, or release it + void destroyGeometry(Geometry* g) const; + + /// Request that the instance is deleted. + /// + /// It will really be deleted only after last child Geometry is + /// deleted. Do not use the instance anymore after calling this function + /// (unless you're a live child!). + /// + void destroy(); + +protected: + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having a + * floating PrecisionModel and a spatial-reference ID of 0. + */ + GeometryFactory(); + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having + * the given PrecisionModel and the default CoordinateSequence + * implementation. + * + * @param pm the PrecisionModel to use + */ + GeometryFactory(const PrecisionModel* pm); + + /** + * \brief + * Constructs a GeometryFactory that generates Geometries having + * the given {@link PrecisionModel} and spatial-reference ID, + * and the default CoordinateSequence implementation. + * + * @param pm the PrecisionModel to use, will be copied internally + * @param newSRID the SRID to use + */ + GeometryFactory(const PrecisionModel* pm, int newSRID); + + /** + * \brief Copy constructor + * + * @param gf the GeometryFactory to clone from + */ + GeometryFactory(const GeometryFactory& gf); + + /// Destructor + virtual ~GeometryFactory(); + +private: + + PrecisionModel precisionModel; + int SRID; + + mutable int _refCount; + bool _autoDestroy; + + friend class Geometry; + + void addRef() const; + void dropRef() const; + +}; + +} // namespace geos::geom +} // namespace geos + + + + + + diff --git a/Sources/geos/include/geos/geom/GeometryFilter.h b/Sources/geos/include/geos/geom/GeometryFilter.h new file mode 100644 index 0000000..32cae49 --- /dev/null +++ b/Sources/geos/include/geos/geom/GeometryFilter.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos { +namespace geom { // geos::geom +class Geometry; +} +} + +namespace geos { +namespace geom { // geos::geom + + +/** \brief + * Geometry classes support the concept of applying a Geometry + * filter to the Geometry. + * + * In the case of GeometryCollection + * subclasses, the filter is applied to every element Geometry. + * A Geometry filter can either record information about the Geometry + * or change the Geometry in some way. + * Geometry filters implement the interface GeometryFilter. + * (GeometryFilter is an example of the Gang-of-Four Visitor pattern). + */ +class GEOS_DLL GeometryFilter { +public: + /* + * Performs an operation with or on geom. + * + * @param geom a Geometry to which the filter + * is applied. + * + * NOTE: this are not pure abstract to allow read-only + * or read-write-only filters to avoid defining a fake + * version of the not-implemented kind. + */ + virtual void + filter_ro(const Geometry* /*geom*/) + { + assert(0); + } + virtual void + filter_rw(Geometry* /*geom*/) + { + assert(0); + } + + virtual + ~GeometryFilter() {} +}; + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/GeometryTypeName.h b/Sources/geos/include/geos/geom/GeometryTypeName.h new file mode 100644 index 0000000..9807e5a --- /dev/null +++ b/Sources/geos/include/geos/geom/GeometryTypeName.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace geom { + +class Curve; +class CurvePolygon; +class GeometryCollection; +class LineString; +class LinearRing; +class MultiCurve; +class MultiLineString; +class MultiPoint; +class MultiPolygon; +class MultiSurface; +class Point; +class Polygon; +class SimpleCurve; +class Surface; + +// These structures allow templates to have compile-time access to a type's human-readable name. +template +struct GeometryTypeName {}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Curve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "CurvePolygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "GeometryCollection"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "LineString"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "LinearRing"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiCurve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiLineString"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiPoint"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiPolygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "MultiSurface"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Point"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Polygon"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "SimpleCurve"; +}; + +template<> +struct GeometryTypeName { + static constexpr const char* name = "Surface"; +}; + +} +} diff --git a/Sources/geos/include/geos/geom/HeuristicOverlay.h b/Sources/geos/include/geos/geom/HeuristicOverlay.h new file mode 100644 index 0000000..e4eb8b7 --- /dev/null +++ b/Sources/geos/include/geos/geom/HeuristicOverlay.h @@ -0,0 +1,111 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2013-2020 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +#include // for unique_ptr +#include + + +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +} +} + + +namespace geos { +namespace geom { // geos::geom + +std::unique_ptr GEOS_DLL +HeuristicOverlay(const Geometry* g0, const Geometry* g1, int opCode); + +class StructuredCollection { + +public: + + static std::unique_ptr overlay(const Geometry* g0, const Geometry* g1, int opCode); + +private: + + const GeometryFactory* factory; + std::vector pts; + std::vector lines; + std::vector polys; + std::unique_ptr pt_union; + std::unique_ptr line_union; + std::unique_ptr poly_union; + Dimension::DimensionType dimension; + + StructuredCollection(const Geometry* g) + : factory(g->getFactory()) + , pt_union(nullptr) + , line_union(nullptr) + , poly_union(nullptr) + , dimension(Dimension::DONTCARE) + { + readCollection(g); + unionByDimension(); + }; + + StructuredCollection() + : factory(nullptr) + , pt_union(nullptr) + , line_union(nullptr) + , poly_union(nullptr) + , dimension(Dimension::DONTCARE) + {}; + + Dimension::DimensionType getDimension() const + { + return dimension; + }; + + void addDimension(Dimension::DimensionType dim); + std::unique_ptr doUnaryUnion(int resultDim) const; + std::unique_ptr computeResult(StructuredCollection& coll, int opCode, + Dimension::DimensionType dimA, Dimension::DimensionType dimB) const; + + void readCollection(const Geometry* g); + const Geometry* getPolyUnion() const { return poly_union.get(); } + const Geometry* getLineUnion() const { return line_union.get(); } + const Geometry* getPointUnion() const { return pt_union.get(); } + + std::unique_ptr doUnion(const StructuredCollection& a) const; + std::unique_ptr doIntersection(const StructuredCollection& a) const; + std::unique_ptr doSymDifference(const StructuredCollection& a) const; + std::unique_ptr doDifference(const StructuredCollection& a) const; + + static void toVector(const Geometry* g, std::vector& v); + void unionByDimension(void); +}; + + + + + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/IntersectionMatrix.h b/Sources/geos/include/geos/geom/IntersectionMatrix.h new file mode 100644 index 0000000..c7ad506 --- /dev/null +++ b/Sources/geos/include/geos/geom/IntersectionMatrix.h @@ -0,0 +1,383 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/IntersectionMatrix.java rev. 1.18 + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos { +namespace geom { // geos::geom + +/** \brief + * Implementation of Dimensionally Extended Nine-Intersection Model + * (DE-9IM) matrix. + * + * Dimensionally Extended Nine-Intersection Model (DE-9IM) matrix. + * This class can used to represent both computed DE-9IM's (like 212FF1FF2) + * as well as patterns for matching them (like T*T******). + * + * Methods are provided to: + * + * - set and query the elements of the matrix in a convenient fashion + * - convert to and from the standard string representation + * (specified in SFS Section 2.1.13.2). + * - test to see if a matrix matches a given pattern string. + * + * For a description of the DE-9IM, see the + * OpenGIS Simple + * Features Specification for SQL. + * + * \todo Suggestion: add equal and not-equal operator to this class. + */ +class GEOS_DLL IntersectionMatrix { + +public: + + /** \brief + * Default constructor. + * + * Creates an IntersectionMatrix with Dimension::False + * dimension values ('F'). + */ + IntersectionMatrix(); + + /** \brief + * Overridden constructor. + * + * Creates an IntersectionMatrix with the given dimension symbols. + * + * @param elements - reference to string containing pattern + * of dimension values for elements. + */ + IntersectionMatrix(const std::string& elements); + + /** \brief + * Copy constructor. + * + * Creates an IntersectionMatrix with the same elements as other. + * + * \todo Add assignment operator to make this class fully copyable. + */ + IntersectionMatrix(const IntersectionMatrix& other); + + /** \brief + * Returns whether the elements of this IntersectionMatrix + * satisfies the required dimension symbols. + * + * @param requiredDimensionSymbols - nine dimension symbols with + * which to compare the elements of this IntersectionMatrix. + * Possible values are {T, F, * , 0, 1, 2}. + * @return true if this IntersectionMatrix matches the required + * dimension symbols. + */ + bool matches(const std::string& requiredDimensionSymbols) const; + + /** \brief + * Tests if given dimension value satisfies the dimension symbol. + * + * @param actualDimensionValue - valid dimension value stored in + * the IntersectionMatrix. + * Possible values are {TRUE, FALSE, DONTCARE, 0, 1, 2}. + * @param requiredDimensionSymbol - a character used in the string + * representation of an IntersectionMatrix. + * Possible values are {T, F, * , 0, 1, 2}. + * @return true if the dimension symbol encompasses the + * dimension value. + */ + static bool matches(int actualDimensionValue, + char requiredDimensionSymbol); + + /** \brief + * Returns true if each of the actual dimension symbols satisfies + * the corresponding required dimension symbol. + * + * @param actualDimensionSymbols - nine dimension symbols to validate. + * Possible values are {T, F, * , 0, 1, 2}. + * @param requiredDimensionSymbols - nine dimension symbols to + * validate against. + * Possible values are {T, F, * , 0, 1, 2}. + * @return true if each of the required dimension symbols encompass + * the corresponding actual dimension symbol. + */ + static bool matches(const std::string& actualDimensionSymbols, + const std::string& requiredDimensionSymbols); + + /** \brief + * Adds one matrix to another. + * + * Addition is defined by taking the maximum dimension value + * of each position in the summand matrices. + * + * @param other - the matrix to add. + * + * \todo Why the 'other' matrix is not passed by const-reference? + */ + void add(IntersectionMatrix* other); + + /** \brief + * Changes the value of one of this IntersectionMatrixs elements. + * + * @param row - the row of this IntersectionMatrix, indicating + * the interior, boundary or exterior of the first Geometry. + * @param column - the column of this IntersectionMatrix, + * indicating the interior, boundary or exterior of the + * second Geometry. + * @param dimensionValue - the new value of the element. + */ + void set(Location row, Location column, int dimensionValue); + + /** \brief + * Changes the elements of this IntersectionMatrix to the dimension + * symbols in dimensionSymbols. + * + * @param dimensionSymbols - nine dimension symbols to which to + * set this IntersectionMatrix elements. + * Possible values are {T, F, * , 0, 1, 2}. + */ + void set(const std::string& dimensionSymbols); + + /** \brief + * Changes the specified element to minimumDimensionValue if the + * element is less. + * + * @param row - the row of this IntersectionMatrix, indicating + * the interior, boundary or exterior of the first Geometry. + * @param column - the column of this IntersectionMatrix, indicating + * the interior, boundary or exterior of the second Geometry. + * @param minimumDimensionValue - the dimension value with which + * to compare the element. The order of dimension values + * from least to greatest is {DONTCARE, TRUE, FALSE, 0, 1, 2}. + */ + void setAtLeast(Location row, Location column, int minimumDimensionValue); + + /** \brief + * If row >= 0 and column >= 0, changes the specified element + * to minimumDimensionValue if the element is less. + * Does nothing if row <0 or column < 0. + * + * @param row - + * the row of this IntersectionMatrix, + * indicating the interior, boundary or exterior of the + * first Geometry. + * + * @param column - + * the column of this IntersectionMatrix, + * indicating the interior, boundary or exterior of the + * second Geometry. + * + * @param minimumDimensionValue - + * the dimension value with which + * to compare the element. The order of dimension values + * from least to greatest is {DONTCARE, TRUE, FALSE, 0, 1, 2}. + */ + void setAtLeastIfValid(Location row, Location column, int minimumDimensionValue); + + /** \brief + * For each element in this IntersectionMatrix, changes the element to + * the corresponding minimum dimension symbol if the element is less. + * + * @param minimumDimensionSymbols - + * nine dimension symbols with which + * to compare the elements of this IntersectionMatrix. + * The order of dimension values from least to greatest is + * {DONTCARE, TRUE, FALSE, 0, 1, 2} . + */ + void setAtLeast(std::string minimumDimensionSymbols); + + /** \brief + * Changes the elements of this IntersectionMatrix to dimensionValue. + * + * @param dimensionValue - + * the dimension value to which to set this + * IntersectionMatrix elements. Possible values {TRUE, + * FALSE, DONTCARE, 0, 1, 2}. + */ + void setAll(int dimensionValue); + + /** \brief + * Returns the value of one of this IntersectionMatrixs elements. + * + * @param row - + * the row of this IntersectionMatrix, indicating the + * interior, boundary or exterior of the first Geometry. + * + * @param column - + * the column of this IntersectionMatrix, indicating the + * interior, boundary or exterior of the second Geometry. + * + * @return the dimension value at the given matrix position. + */ + int get(Location row, Location column) const { + return matrix[static_cast(row)][static_cast(column)]; + } + + /** \brief + * Returns true if this IntersectionMatrix is FF*FF****. + * + * @return true if the two Geometrys related by this + * IntersectionMatrix are disjoint. + */ + bool isDisjoint() const; + + /** \brief + * Returns true if isDisjoint returns false. + * + * @return true if the two Geometrys related by this + * IntersectionMatrix intersect. + */ + bool isIntersects() const; + + /** \brief + * Returns true if this IntersectionMatrix is FT*******, F**T***** + * or F***T****. + * + * @param dimensionOfGeometryA - the dimension of the first Geometry. + * + * @param dimensionOfGeometryB - the dimension of the second Geometry. + * + * @return true if the two Geometry's related by this + * IntersectionMatrix touch, false if both Geometrys + * are points. + */ + bool isTouches(int dimensionOfGeometryA, int dimensionOfGeometryB) + const; + + /** \brief + * Returns true if this IntersectionMatrix is: + * - T*T****** (for a point and a curve, a point and an area or + * a line and an area) + * - 0******** (for two curves) + * + * @param dimensionOfGeometryA - he dimension of the first Geometry. + * + * @param dimensionOfGeometryB - the dimension of the second Geometry. + * + * @return true if the two Geometry's related by this + * IntersectionMatrix cross. + * + * For this function to return true, the Geometrys must be a point + * and a curve; a point and a surface; two curves; or a curve and + * a surface. + */ + bool isCrosses(int dimensionOfGeometryA, int dimensionOfGeometryB) + const; + + /** \brief + * Returns true if this IntersectionMatrix is T*F**F***. + * + * @return true if the first Geometry is within the second. + */ + bool isWithin() const; + + /** \brief + * Returns true if this IntersectionMatrix is T*****FF*. + * + * @return true if the first Geometry contains the second. + */ + bool isContains() const; + + /** \brief + * Returns true if this IntersectionMatrix is T*F**FFF*. + * + * @param dimensionOfGeometryA - he dimension of the first Geometry. + * @param dimensionOfGeometryB - the dimension of the second Geometry. + * @return true if the two Geometry's related by this + * IntersectionMatrix are equal; the Geometrys must have + * the same dimension for this function to return true + */ + bool isEquals(int dimensionOfGeometryA, int dimensionOfGeometryB) + const; + + /** \brief + * Returns true if this IntersectionMatrix is: + * - T*T***T** (for two points or two surfaces) + * - 1*T***T** (for two curves) + * + * @param dimensionOfGeometryA - he dimension of the first Geometry. + * @param dimensionOfGeometryB - the dimension of the second Geometry. + * @return true if the two Geometry's related by this + * IntersectionMatrix overlap. + * + * For this function to return true, the Geometrys must be two points, + * two curves or two surfaces. + */ + bool isOverlaps(int dimensionOfGeometryA, int dimensionOfGeometryB) + const; + + /** \brief + * Returns true if this IntersectionMatrix is T*****FF* + * or *T****FF* or ***T**FF* + * or ****T*FF* + * + * @return true if the first Geometry covers the + * second + */ + bool isCovers() const; + + + /** \brief + * Returns true if this IntersectionMatrix is T*F**F*** + * *TF**F*** or **FT*F*** + * or **F*TF*** + * + * @return true if the first Geometry is covered by + * the second + */ + bool isCoveredBy() const; + + /** \brief + * Transposes this IntersectionMatrix. + * + * @return this IntersectionMatrix as a convenience. + * + * \todo It returns 'this' pointer so why not to return const-pointer? + * \todo May be it would be better to return copy of transposed matrix? + */ + IntersectionMatrix* transpose(); + + /** \brief + * Returns a nine-character String representation of this + * IntersectionMatrix. + * + * @return the nine dimension symbols of this IntersectionMatrix + * in row-major order. + */ + std::string toString() const; + + +private: + + static const int firstDim; // = 3; + + static const int secondDim; // = 3; + + // Internal buffer for 3x3 matrix. + std::array, 3> matrix; + +}; // class IntersectionMatrix + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const IntersectionMatrix& im); + + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/LineSegment.h b/Sources/geos/include/geos/geom/LineSegment.h new file mode 100644 index 0000000..aea0fef --- /dev/null +++ b/Sources/geos/include/geos/geom/LineSegment.h @@ -0,0 +1,576 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/LineSegment.java r18 (JTS-1.11) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include +#include +#include + +#include +#include // for ostream +#include // for std::hash +#include // for unique_ptr +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class GeometryFactory; +class LineString; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * Represents a line segment defined by two Coordinate. + * Provides methods to compute various geometric properties + * and relationships of line segments. + * + * This class is designed to be easily mutable (to the extent of + * having its contained points public). + * This supports a common pattern of reusing a single LineSegment + * object as a way of computing segment properties on the + * segments defined by arrays or lists of {@link Coordinate}s. + * + * TODO: have this class keep pointers rather then real Coordinates ? + */ +class GEOS_DLL LineSegment { +public: + + + Coordinate p0; /// Segment start + Coordinate p1; /// Segment end + + LineSegment(const Coordinate& c0, const Coordinate& c1) + : p0(c0) + , p1(c1) + {}; + + LineSegment(double x0, double y0, double x1, double y1) + : p0(x0, y0) + , p1(x1, y1) + {}; + + LineSegment() {}; + + + void setCoordinates(const Coordinate& c0, const Coordinate& c1) + { + p0 = c0; + p1 = c1; + }; + + void setCoordinates(const LineSegment& ls) + { + setCoordinates(ls.p0, ls.p1); + }; + + // obsoleted, use operator[] instead + //const Coordinate& getCoordinate(std::size_t i) const; + + const Coordinate& operator[](std::size_t i) const + { + if(i == 0) { + return p0; + } + assert(i == 1); + return p1; + }; + + Coordinate& operator[](std::size_t i) + { + if(i == 0) { + return p0; + } + assert(i == 1); + return p1; + }; + + /// gets the minimum X ordinate value + double minX() const + { + return std::min(p0.x, p1.x); + }; + + /// gets the maximum X ordinate value + double maxX() const + { + return std::max(p0.x, p1.x); + }; + + /// gets the minimum Y ordinate value + double minY() const + { + return std::min(p0.y, p1.y); + }; + + /// gets the maximum Y ordinate value + double maxY() const + { + return std::max(p0.y, p1.y); + }; + + /// Computes the length of the line segment. + double getLength() const + { + return p0.distance(p1); + }; + + /// Tests whether the segment is horizontal. + /// + /// @return true if the segment is horizontal + /// + bool isHorizontal() const + { + return p0.y == p1.y; + }; + + /// Tests whether the segment is vertical. + /// + /// @return true if the segment is vertical + /// + bool isVertical() const + { + return p0.x == p1.x; + }; + + /** + * Determines the orientation of a LineSegment relative to this segment. + * The concept of orientation is specified as follows: + * Given two line segments A and L, + *
    + *
  • A is to the left of a segment L if A lies wholly in the + * closed half-plane lying to the left of L + *
  • A is to the right of a segment L if A lies wholly in the + * closed half-plane lying to the right of L + *
  • otherwise, A has indeterminate orientation relative to L. + * This happens if A is collinear with L or if A crosses + * the line determined by L. + *
+ * + * @param seg the LineSegment to compare + * + * @return 1 if seg is to the left of this segment + * @return -1 if seg is to the right of this segment + * @return 0 if seg has indeterminate orientation relative + * to this segment + */ + int orientationIndex(const LineSegment& seg) const; + + // TODO deprecate this + int orientationIndex(const LineSegment* seg) const + { + assert(seg); + return orientationIndex(*seg); + }; + + /** + * Determines the orientation index of a Coordinate relative to this segment. + * The orientation index is as defined in Orientation::index(Coordinate, Coordinate, Coordinate). + * + * @param p the coordinate to compare + * + * @return 1 (LEFT) if "p" is to the left of this segment + * @return -1 (RIGHT) if "p" is to the right of this segment + * @return 0 (COLLINEAR) if "p" is collinear with this segment + * + */ + int orientationIndex(const CoordinateXY& p) const + { + return algorithm::Orientation::index(p0, p1, p); + } + + /** \brief + * Determines the orientation index of a Coordinate + * relative to this segment. + * + * The orientation index is as defined in + * Orientation::index. + * + * @param p the Coordinate to compare + * + * @return 1 if p is to the left of this segment + * @return -1 if p is to the right of this segment + * @return 0 if p is collinear with this segment + * + * @see Orientation::index(Coordinate, Coordinate, + * Coordinate) + */ + int orientationIndex(const Coordinate& p) const + { + return algorithm::Orientation::index(p0, p1, p); + }; + + /// Reverses the direction of the line segment. + void reverse(); + + /// Puts the line segment into a normalized form. + // + /// This is useful for using line segments in maps and indexes when + /// topological equality rather than exact equality is desired. + /// + void normalize() + { + if(p1.compareTo(p0) < 0) { + reverse(); + } + }; + + /// @return the angle this segment makes with the x-axis (in radians) + double angle() const + { + return std::atan2(p1.y - p0.y, p1.x - p0.x); + }; + + /** \brief + * Computes the midpoint of the segment + * + * @return the midpoint of the segment + */ + CoordinateXY midPoint() const + { + return midPoint(p0, p1); + }; + + static CoordinateXY midPoint(const CoordinateXY& pt0, const CoordinateXY& pt1) + { + return CoordinateXY( + (pt0.x + pt1.x) / 2, + (pt0.y + pt1.y) / 2); + } + + /// Computes the distance between this line segment and another one. + double distance(const LineSegment& ls) const + { + return algorithm::Distance::segmentToSegment(p0, p1, ls.p0, ls.p1); + }; + + /// Computes the distance between this line segment and a point. + double distance(const CoordinateXY& p) const + { + return algorithm::Distance::pointToSegment(p, p0, p1); + }; + + /** + * Computes the perpendicular distance between the (infinite) + * line defined by this line segment and a point. + * If the segment has zero length this returns the distance between + * the segment and the point. + * + * @param p the point to compute the distance to + * @return the perpendicular distance between the line and point + */ + double distancePerpendicular(const CoordinateXY& p) const + { + if (p0.equals2D(p1)) + return p0.distance(p); + return algorithm::Distance::pointToLinePerpendicular(p, p0, p1); + }; + + /** + * Computes the oriented perpendicular distance between the (infinite) line + * defined by this line segment and a point. + * The oriented distance is positive if the point on the left of the line, + * and negative if it is on the right. + * If the segment has zero length this returns the distance between + * the segment and the point. + * + * @param p the point to compute the distance to + * @return the oriented perpendicular distance between the line and point + */ + double distancePerpendicularOriented(const CoordinateXY& p) const; + + /** \brief + * Computes the Coordinate that lies a given + * fraction along the line defined by this segment. + * + * A fraction of 0.0 returns the start point of + * the segment; a fraction of 1.0 returns the end + * point of the segment. + * If the fraction is < 0.0 or > 1.0 the point returned + * will lie before the start or beyond the end of the segment. + * + * @param segmentLengthFraction the fraction of the segment length + * along the line + * @param ret will be set to the point at that distance + */ + void pointAlong(double segmentLengthFraction, Coordinate& ret) const + { + ret = Coordinate( + p0.x + segmentLengthFraction * (p1.x - p0.x), + p0.y + segmentLengthFraction * (p1.y - p0.y), + p0.z + segmentLengthFraction * (p1.z - p0.z)); + }; + + /** \brief + * Computes the {@link Coordinate} that lies a given + * fraction along the line defined by this segment and offset from + * the segment by a given distance. + * + * A fraction of 0.0 offsets + * from the start point of the segment; + * a fraction of 1.0 offsets + * from the end point of the segment. + * + * The computed point is offset to the left of the line + * if the offset distance is positive, to the right if negative. + * + * @param segmentLengthFraction the fraction of the segment + * length along the line + * + * @param offsetDistance the distance the point is offset + * from the segment + * (positive is to the left, negative is to the right) + * + * @param ret will be set to the point at that distance and offset + * + * @throws IllegalStateException if the segment has zero length + */ + void pointAlongOffset(double segmentLengthFraction, + double offsetDistance, + Coordinate& ret) const; + + + /** + * Computes the {@link LineSegment} that is offset from + * the segment by a given distance. + * The computed segment is offset to the left of the line if the offset distance is + * positive, to the right if negative. + * + * @param offsetDistance the distance the point is offset from the segment + * (positive is to the left, negative is to the right) + * @return a line segment offset by the specified distance + * + * @throws IllegalStateException if the segment has zero length + */ + LineSegment offset(double offsetDistance); + + + /** \brief + * Compute the projection factor for the projection of the point p + * onto this LineSegment. + * + * The projection factor is the constant r + * by which the vector for this segment must be multiplied to + * equal the vector for the projection of p on the line + * defined by this segment. + * + * The projection factor returned will be in the range + * (-inf, +inf) + * + * @param p the point to compute the factor for + * + * @return the projection factor for the point + * + */ + double projectionFactor(const CoordinateXY& p) const; + + /** \brief + * Computes the fraction of distance (in [0.0, 1.0]) + * that the projection of a point occurs along this line segment. + * + * If the point is beyond either ends of the line segment, + * the closest fractional value (0.0 or 1.0) + * is returned. + * + * Essentially, this is the {@link #projectionFactor} clamped to + * the range [0.0, 1.0]. + * + * @param inputPt the point + * @return the fraction along the line segment the projection + * of the point occurs + */ + double segmentFraction(const CoordinateXY& inputPt) const; + + /** \brief + * Compute the projection of a point onto the line determined + * by this line segment. + * + * Note that the projected point + * may lie outside the line segment. If this is the case, + * the projection factor will lie outside the range [0.0, 1.0]. + */ + void project(const Coordinate& p, Coordinate& ret) const; + + CoordinateXY project(const CoordinateXY& p) const; + + /** \brief + * Project a line segment onto this line segment and return the resulting + * line segment. + * + * The returned line segment will be a subset of + * the target line line segment. This subset may be null, if + * the segments are oriented in such a way that there is no projection. + * + * Note that the returned line may have zero length (i.e. the same endpoints). + * This can happen for instance if the lines are perpendicular to one another. + * + * @param seg the line segment to project + * @param ret the projected line segment + * @return true if there is an overlap, false otherwise + */ + bool project(const LineSegment& seg, LineSegment& ret) const; + + /// Computes the closest point on this line segment to another point. + // + /// @param p the point to find the closest point to + /// @param ret the Coordinate to which the closest point on the line segment + /// to the point p will be written + /// + void closestPoint(const CoordinateXY& p, CoordinateXY& ret) const; + + /** \brief + * Returns true if other is + * topologically equal to this LineSegment (e.g. irrespective + * of orientation). + * + * @param other a LineSegment with which to do the comparison. + * @return true if other is a LineSegment + * with the same values for the x and y ordinates. + */ + bool equalsTopo(const LineSegment& other) const; + + /** + * Computes the closest points on two line segments. + * @param line the line segment to find the closest points to + * @return a pair of Coordinates which are the closest points on + * the line segments. + */ + std::array closestPoints(const LineSegment& line); + + std::array closestPoints(const LineSegment* line) + { + assert(line); + return closestPoints(*line); + } + + /** + * Computes an intersection point between two segments, + * if there is one. + * There may be 0, 1 or many intersection points between two segments. + * If there are 0, null is returned. If there is 1 or more, a single + * one is returned (chosen at the discretion of the algorithm). + * If more information is required about the details of the + * intersection, the LineIntersector class should be used. + * + * @param line + * @return intersection if found, setNull() otherwise + */ + Coordinate intersection(const LineSegment& line) const; + + /** \brief + * Computes the intersection point of the lines defined + * by two segments, if there is one. + * + * There may be 0, 1 or an infinite number of intersection points + * between two lines. + * If there is a unique intersection point, it is returned. + * Otherwise, null is returned. + * If more information is required about the details of the + * intersection, the algorithms::LineIntersector class should + * be used. + * + * @param line a line segment defining a straight line + * @return intersection if found, setNull() otherwise + * + */ + Coordinate lineIntersection(const LineSegment& line) const; + + /** + * Creates a LineString with the same coordinates as this segment + * + * @param gf the geometery factory to use + * @return a LineString with the same geometry as this segment + */ + std::unique_ptr toGeometry(const GeometryFactory& gf) const; + + + /** \brief + * Compares this object with the specified object for order. + * + * Uses the standard lexicographic ordering for the points in the LineSegment. + * + * @param other the LineSegment with which this LineSegment + * is being compared + * @return a negative integer, zero, or a positive integer as this + * LineSegment is less than, equal to, or greater than the + * specified LineSegment + */ + inline int compareTo(const LineSegment& other) const + { + int comp0 = p0.compareTo(other.p0); + if (comp0 != 0) { + return comp0; + } + return p1.compareTo(other.p1); + } + + std::ostream& operator<< (std::ostream& o); + + inline bool operator==(const LineSegment& rhs) const { + return compareTo(rhs) == 0; + }; + + inline bool operator<(const LineSegment& rhs) const { + return compareTo(rhs) < 0; + }; + + inline bool operator>(const LineSegment& rhs) const { + return compareTo(rhs) > 0; + }; + + struct HashCode { + inline std::size_t operator()(const LineSegment & s) const { + std::size_t h = std::hash{}(s.p0.x); + h ^= (std::hash{}(s.p0.y) << 1); + h ^= (std::hash{}(s.p1.x) << 1); + return h ^ (std::hash{}(s.p1.y) << 1); + } + + inline std::size_t operator()(const LineSegment * s) const { + std::size_t h = std::hash{}(s->p0.x); + h ^= (std::hash{}(s->p0.y) << 1); + h ^= (std::hash{}(s->p1.x) << 1); + return h ^ (std::hash{}(s->p1.y) << 1); + } + + }; + + using UnorderedSet = std::unordered_set; + + +private: + void project(double factor, CoordinateXY& ret) const; + +}; + + +// std::ostream& operator<< (std::ostream& o, const LineSegment& l); + + +} // namespace geos::geom +} // namespace geos diff --git a/Sources/geos/include/geos/geom/LineString.h b/Sources/geos/include/geos/geom/LineString.h new file mode 100644 index 0000000..4789708 --- /dev/null +++ b/Sources/geos/include/geos/geom/LineString.h @@ -0,0 +1,151 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/LineString.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for proper use of unique_ptr<> +#include // for proper use of unique_ptr<> +#include // for Dimension::DimensionType +#include + +#include +#include +#include // for unique_ptr + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequenceFilter; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * Models an OGC-style LineString. + * + * A LineString consists of a sequence of two or more vertices, + * along with all points along the linearly-interpolated curves + * (line segments) between each + * pair of consecutive vertices. + * Consecutive vertices may be equal. + * The line segments in the line may intersect each other (in other words, + * the linestring may "curl back" in itself and self-intersect). + * Linestrings with exactly two identical points are invalid. + * + * A linestring must have either 0 or 2 or more points. + * If these conditions are not met, the constructors throw + * an {@link util::IllegalArgumentException}. + */ +class GEOS_DLL LineString: public SimpleCurve { + +public: + + friend class GeometryFactory; + + /// A vector of const LineString pointers + typedef std::vector ConstVect; + + ~LineString() override; + + /** + * \brief + * Creates and returns a full copy of this {@link LineString} object + * (including all coordinates contained by it) + * + * @return A clone of this instance + */ + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + double getLength() const override; + + bool isCurved() const override { + return false; + } + + /** + * Creates a LineString whose coordinates are in the reverse + * order of this object's + * + * @return a LineString with coordinates in the reverse order + */ + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + +protected: + + LineString(const LineString& ls); + + /// \brief + /// Constructs a LineString taking ownership the + /// given CoordinateSequence. + LineString(CoordinateSequence::Ptr && pts, + const GeometryFactory& newFactory); + + LineString* cloneImpl() const override { return new LineString(*this); } + + LineString* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_LINESTRING; + }; + + void geometryChangedAction() override + { + envelope = computeEnvelopeInternal(true); + } + +private: + + void validateConstruction(); +}; + +struct GEOS_DLL LineStringLT { + bool + operator()(const LineString* ls1, const LineString* ls2) const + { + return ls1->compareTo(ls2) < 0; + } +}; + +} // namespace geos::geom +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geom/LinearRing.h b/Sources/geos/include/geos/geom/LinearRing.h new file mode 100644 index 0000000..b04b3bd --- /dev/null +++ b/Sources/geos/include/geos/geom/LinearRing.h @@ -0,0 +1,128 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/LinearRing.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \brief + * Models an OGC SFS LinearRing. A LinearRing is a LineString which is both + * closed and simple. + * + * In other words, the first and last coordinate in the ring must be equal, + * and the ring must not self-intersect. Either orientation + * of the ring is allowed. + * + * A ring must have either 0 or 3 or more points. The first and last points + * must be equal (in 2D). If these conditions are not met, the constructors + * throw an {@link geos::util::IllegalArgumentException} + * A ring with 3 points is invalid, because it is collapsed + * and thus has a self-intersection. It is allowed to be constructed + * so that it can be represented, and repaired if needed. + */ +class GEOS_DLL LinearRing : public LineString { + +public: + + /** \brief + * The minimum number of vertices allowed in a valid non-empty ring. + * Empty rings with 0 vertices are also valid. + */ + static const unsigned int MINIMUM_VALID_SIZE = 3; + + LinearRing(const LinearRing& lr); + + /** + * \brief Constructs a LinearRing with the given points. + * + * @param points points forming a closed and simple linestring, or + * null or an empty array to create the empty + * geometry. + * This array must not contain null elements. + * If not null LinearRing will take ownership of points. + * + * @param newFactory the GeometryFactory used to create this geometry + * + */ + LinearRing(CoordinateSequence::Ptr && points, + const GeometryFactory& newFactory); + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + ~LinearRing() override = default; + + /** \brief + * Returns Dimension.FALSE, since by definition + * LinearRings do not have a boundary. + * + * @return Dimension::False + */ + int getBoundaryDimension() const override; + + bool isClosed() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + void setPoints(const CoordinateSequence* cl); + + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + + void orient(bool isCW); + +protected: + + int + getSortIndex() const override + { + return SORTINDEX_LINEARRING; + }; + + LinearRing* cloneImpl() const override { return new LinearRing(*this); } + + LinearRing* reverseImpl() const override; + +private: + + void validateConstruction(); +}; + + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/Location.h b/Sources/geos/include/geos/geom/Location.h new file mode 100644 index 0000000..c42367e --- /dev/null +++ b/Sources/geos/include/geos/geom/Location.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for ostream + + +namespace geos { +namespace geom { // geos::geom + +/** \brief + * Constants representing the location of a point relative to a geometry. + * + * They can also be thought of as the row or column index of a DE-9IM matrix. + * For a description of the DE-9IM, see the + * [OpenGIS Simple Features Specification for SQL](http://www.opengis.org/techno/specs.htm"). + */ + +enum class Location : char { + /** + * Used for uninitialized location values. + */ + NONE = (char)(-1), // Instead of NULL + + /** + * DE-9IM row index of the interior of the first geometry and + * column index of the interior of the second geometry. + * Location value for the interior of a geometry. + */ + INTERIOR = 0, + + /** + * DE-9IM row index of the boundary of the first geometry and + * column index of the boundary of the second geometry. + * Location value for the boundary of a geometry. + */ + BOUNDARY = 1, + + /** + * DE-9IM row index of the exterior of the first geometry and + * column index of the exterior of the second geometry. + * Location value for the exterior of a geometry. + */ + EXTERIOR = 2 +}; + +GEOS_DLL std::ostream& operator<<(std::ostream& os, const Location& loc); + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/MultiCurve.h b/Sources/geos/include/geos/geom/MultiCurve.h new file mode 100644 index 0000000..5505f5d --- /dev/null +++ b/Sources/geos/include/geos/geom/MultiCurve.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL MultiCurve : public GeometryCollection { + friend class GeometryFactory; + +public: + ~MultiCurve() override = default; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + /// Returns a (possibly empty) [MultiPoint](@ref geom::MultiPoint) + std::unique_ptr getBoundary() const override; + + /** + * \brief + * Returns Dimension::False if all [Curves](@ref geom::Curve) in the collection + * are closed, 0 otherwise. + */ + int getBoundaryDimension() const override; + + /// Returns line dimension (1) + Dimension::DimensionType getDimension() const override; + + const Curve* getGeometryN(std::size_t n) const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasDimension(Dimension::DimensionType d) const override + { + return d == Dimension::L; + } + + /// Returns true if the MultiCurve is not empty, and every included + /// Curve is also closed. + bool isClosed() const; + + bool isDimensionStrict(Dimension::DimensionType d) const override + { + return d == Dimension::L; + } + + /** + * Creates a MultiCurve in the reverse + * order to this object. + * Both the order of the component Curves + * and the order of their coordinate sequences + * are reversed. + * + * @return a MultiCurve in the reverse order + */ + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + /** + * \brief Constructs a MultiCurve. + * + * @param newLines The [Curves](@ref geom::Curve) for this + * MultiCurve, or `null` + * or an empty array to create the empty geometry. + * Elements may be empty Curve, + * but not `null`s. + * + * @param newFactory The GeometryFactory used to create this geometry. + * Caller must keep the factory alive for the life-time + * of the constructed MultiCurve. + * + * @note Constructed object will take ownership of + * the vector and its elements. + * + */ + MultiCurve(std::vector>&& newLines, + const GeometryFactory& newFactory); + + MultiCurve(std::vector>&& newLines, + const GeometryFactory& newFactory); + + MultiCurve(const MultiCurve& mp) + : GeometryCollection(mp) + {} + + MultiCurve* cloneImpl() const override + { + return new MultiCurve(*this); + } + + MultiCurve* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_MULTICURVE; + }; + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/MultiLineString.h b/Sources/geos/include/geos/geom/MultiLineString.h new file mode 100644 index 0000000..2b6cc76 --- /dev/null +++ b/Sources/geos/include/geos/geom/MultiLineString.h @@ -0,0 +1,160 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/MultiLineString.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include +#include +#include + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +} +} + +namespace geos { +namespace geom { // geos::geom + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4250) // T1 inherits T2 via dominance +#endif + +/// Models a collection of [LineStrings](@ref geom::LineString). +class GEOS_DLL MultiLineString: public GeometryCollection { + +public: + + friend class GeometryFactory; + + ~MultiLineString() override = default; + + /// Returns line dimension (1) + Dimension::DimensionType getDimension() const override; + + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::L; + } + + bool isDimensionStrict(Dimension::DimensionType d) const override { + return d == Dimension::L; + } + + /** + * \brief + * Returns Dimension::False if all [LineStrings](@ref geom::LineString) in the collection + * are closed, 0 otherwise. + */ + int getBoundaryDimension() const override; + + /// Returns a (possibly empty) [MultiPoint](@ref geom::MultiPoint) + std::unique_ptr getBoundary() const override; + + const LineString* getGeometryN(std::size_t n) const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool isClosed() const; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + /** + * Creates a MultiLineString in the reverse + * order to this object. + * Both the order of the component LineStrings + * and the order of their coordinate sequences + * are reversed. + * + * @return a MultiLineString in the reverse order + */ + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + +protected: + + /** + * \brief Constructs a MultiLineString. + * + * @param newLines The [LineStrings](@ref geom::LineString) for this + * MultiLineString, or `null` + * or an empty array to create the empty geometry. + * Elements may be empty LineString, + * but not `null`s. + * + * @param newFactory The GeometryFactory used to create this geometry. + * Caller must keep the factory alive for the life-time + * of the constructed MultiLineString. + * + * @note Constructed object will take ownership of + * the vector and its elements. + * + */ + MultiLineString(std::vector> && newLines, + const GeometryFactory& newFactory); + + MultiLineString(std::vector> && newLines, + const GeometryFactory& newFactory); + + MultiLineString(const MultiLineString& mp) + : GeometryCollection(mp) + {}; + + MultiLineString* cloneImpl() const override { return new MultiLineString(*this); } + + MultiLineString* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_MULTILINESTRING; + }; + + bool hasCurvedComponents() const override + { + return false; + } + +}; + + +} // namespace geos::geom +} // namespace geos + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + + + + diff --git a/Sources/geos/include/geos/geom/MultiPoint.h b/Sources/geos/include/geos/geom/MultiPoint.h new file mode 100644 index 0000000..0e1d564 --- /dev/null +++ b/Sources/geos/include/geos/geom/MultiPoint.h @@ -0,0 +1,150 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/MultiPoint.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for Dimension::DimensionType +#include // for covariant return type + +#include +#include + +namespace geos { +namespace geom { // geos::geom +class Coordinate; +} +} + +namespace geos { +namespace geom { // geos::geom + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4250) // T1 inherits T2 via dominance +#endif + +/** + * Models a collection of Point objects. + * + * Any collection of Points is a valid MultiPoint. + */ +class GEOS_DLL MultiPoint: public GeometryCollection { + +public: + + friend class GeometryFactory; + + ~MultiPoint() override = default; + + /// Returns point dimension (0) + Dimension::DimensionType getDimension() const override; + + bool isDimensionStrict(Dimension::DimensionType d) const override { + return d == Dimension::P; + } + + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::P; + } + + /// Returns Dimension::False (Point has no boundary) + int getBoundaryDimension() const override; + + /** \brief + * Gets the boundary of this geometry. + * + * Zero-dimensional geometries have no boundary by definition, + * so an empty GeometryCollection is returned. + * + * @return an empty GeometryCollection + * @see Geometry#getBoundary + */ + std::unique_ptr getBoundary() const override; + + const Point* getGeometryN(std::size_t n) const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + /** + * \brief Constructs a MultiPoint. + * + * @param newPoints + * the Points for this MultiPoint, + * or null or an empty array to create the empty + * geometry. + * Elements may be empty Points, + * but not nulls. + * + * Constructed object will take ownership of + * the vector and its elements. + * + * @param newFactory + * The GeometryFactory used to create this geometry + * Caller must keep the factory alive for the life-time + * of the constructed MultiPoint. + */ + MultiPoint(std::vector> && newPoints, const GeometryFactory& newFactory); + + MultiPoint(std::vector> && newPoints, const GeometryFactory& newFactory); + + MultiPoint(const MultiPoint& mp): GeometryCollection(mp) {} + + MultiPoint* cloneImpl() const override { return new MultiPoint(*this); } + + MultiPoint* reverseImpl() const override { return new MultiPoint(*this); } + + const CoordinateXY* getCoordinateN(std::size_t n) const; + + int + getSortIndex() const override + { + return SORTINDEX_MULTIPOINT; + }; + + bool hasCurvedComponents() const override + { + return false; + } + +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/MultiPolygon.h b/Sources/geos/include/geos/geom/MultiPolygon.h new file mode 100644 index 0000000..f4cca3a --- /dev/null +++ b/Sources/geos/include/geos/geom/MultiPolygon.h @@ -0,0 +1,155 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/MultiPolygon.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inheritance +#include // for inheritance +#include // for Dimension::DimensionType +#include + + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +class MultiPoint; +class Polygon; +} +} + + +namespace geos { +namespace geom { // geos::geom + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4250) // T1 inherits T2 via dominance +#endif + +/// Models a collection of {@link Polygon}s. +/// +/// As per the OGC SFS specification, +/// the Polygons in a MultiPolygon may not overlap, +/// and may only touch at single points. +/// This allows the topological point-set semantics +/// to be well-defined. +/// +class GEOS_DLL MultiPolygon: public GeometryCollection { +public: + + friend class GeometryFactory; + + ~MultiPolygon() override; + + /// Returns surface dimension (2) + Dimension::DimensionType getDimension() const override; + + bool hasDimension(Dimension::DimensionType d) const override { + return d == Dimension::A; + } + + bool isDimensionStrict(Dimension::DimensionType d) const override { + return d == Dimension::A; + } + + /// Returns 1 (MultiPolygon boundary is MultiLineString) + int getBoundaryDimension() const override; + + /** \brief + * Computes the boundary of this geometry + * + * @return a lineal geometry (which may be empty) + * @see Geometry#getBoundary + */ + std::unique_ptr getBoundary() const override; + + const Polygon* getGeometryN(std::size_t n) const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + +protected: + + /** + * \brief Construct a MultiPolygon + * + * @param newPolys + * the Polygons for this MultiPolygon, + * or null or an empty array to create the empty + * geometry. Elements may be empty Polygons, but + * not nulls. + * The polygons must conform to the assertions specified in the + * + * OpenGIS Simple Features Specification for SQL + * . + * + * Constructed object will take ownership of + * the vector and its elements. + * + * @param newFactory + * The GeometryFactory used to create this geometry + * Caller must keep the factory alive for the life-time + * of the constructed MultiPolygon. + */ + MultiPolygon(std::vector> && newPolys, + const GeometryFactory& newFactory); + + MultiPolygon(std::vector> && newPolys, + const GeometryFactory& newFactory); + + MultiPolygon(const MultiPolygon& mp) + : GeometryCollection(mp) + {}; + + MultiPolygon* cloneImpl() const override { return new MultiPolygon(*this); } + + MultiPolygon* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_MULTIPOLYGON; + }; + + bool hasCurvedComponents() const override + { + return false; + } + +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace geos::geom +} // namespace geos diff --git a/Sources/geos/include/geos/geom/MultiSurface.h b/Sources/geos/include/geos/geom/MultiSurface.h new file mode 100644 index 0000000..73871e4 --- /dev/null +++ b/Sources/geos/include/geos/geom/MultiSurface.h @@ -0,0 +1,94 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { +class GEOS_DLL MultiSurface : public GeometryCollection { + friend class GeometryFactory; + +public: + + ~MultiSurface() override; + + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + }; + + /** \brief + * Computes the boundary of this geometry + * + * @return a lineal geometry (which may be empty) + * @see Geometry#getBoundary + */ + std::unique_ptr getBoundary() const override; + + /// Returns 1 (MultiSurface boundary is MultiCurve) + int getBoundaryDimension() const override; + + /// Returns surface dimension (2) + Dimension::DimensionType getDimension() const override; + + std::string getGeometryType() const override; + + GeometryTypeId getGeometryTypeId() const override; + + bool hasDimension(Dimension::DimensionType d) const override + { + return d == Dimension::A; + } + + bool isDimensionStrict(Dimension::DimensionType d) const override + { + return d == Dimension::A; + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + +protected: + + MultiSurface(std::vector>&& newPolys, + const GeometryFactory& newFactory); + + MultiSurface(std::vector>&& newPolys, + const GeometryFactory& newFactory); + + MultiSurface(const MultiSurface& mp) + : GeometryCollection(mp) + {}; + + MultiSurface* cloneImpl() const override + { + return new MultiSurface(*this); + } + + int + getSortIndex() const override + { + return SORTINDEX_MULTISURFACE; + }; + + MultiSurface* reverseImpl() const override; + +}; +} +} diff --git a/Sources/geos/include/geos/geom/Point.h b/Sources/geos/include/geos/geom/Point.h new file mode 100644 index 0000000..cc944d9 --- /dev/null +++ b/Sources/geos/include/geos/geom/Point.h @@ -0,0 +1,221 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Point.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for proper use of unique_ptr<> +#include // for proper use of unique_ptr<> +#include // for Dimension::DimensionType + +#include +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +class CoordinateFilter; +class CoordinateSequenceFilter; +class GeometryComponentFilter; +class GeometryFilter; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * Implementation of Point. + * + * A Point is valid iff: + * + * - the coordinate which defines it is a valid coordinate + * (i.e does not have an NaN X or Y ordinate) + * + */ +class GEOS_DLL Point : public Geometry { + +public: + + friend class GeometryFactory; + + /// A vector of const Point pointers + typedef std::vector ConstVect; + + ~Point() override = default; + + /** + * Creates and returns a full copy of this {@link Point} object. + * (including all coordinates contained by it). + * + * @return a clone of this instance + */ + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + std::unique_ptr getCoordinates(void) const override; + + const CoordinateSequence* getCoordinatesRO() const; + + std::size_t getNumPoints() const override; + bool isEmpty() const override; + bool isSimple() const override; + + /// Returns point dimension (0) + Dimension::DimensionType getDimension() const override; + + /// Returns coordinate dimension. + uint8_t getCoordinateDimension() const override; + + bool hasM() const override; + + bool hasZ() const override; + + /// Returns Dimension::False (Point has no boundary) + int getBoundaryDimension() const override; + + /** + * Gets the boundary of this geometry. + * Zero-dimensional geometries have no boundary by definition, + * so an empty GeometryCollection is returned. + * + * @return an empty GeometryCollection + * @see Geometry::getBoundary + */ + std::unique_ptr getBoundary() const override; + + void setXY(double x, double y) { + if (isEmpty()) { + coordinates.add(x, y); + } else { + CoordinateXY& prev = coordinates.front(); + prev.x = x; + prev.y = y; + } + geometryChangedAction(); + } + + const CoordinateXY* getCoordinate() const override { + return isEmpty() ? nullptr : &coordinates.getAt(0); + } + + double getX() const; + double getY() const; + double getZ() const; + double getM() const; + + std::string getGeometryType() const override; + GeometryTypeId getGeometryTypeId() const override; + void apply_ro(CoordinateFilter* filter) const override; + void apply_rw(const CoordinateFilter* filter) override; + void apply_ro(GeometryFilter* filter) const override; + void apply_rw(GeometryFilter* filter) override; + void apply_rw(GeometryComponentFilter* filter) override; + void apply_ro(GeometryComponentFilter* filter) const override; + void apply_rw(CoordinateSequenceFilter& filter) override; + void apply_ro(CoordinateSequenceFilter& filter) const override; + + bool equalsExact(const Geometry* other, double tolerance = 0) const override; + + bool equalsIdentical(const Geometry* other) const override; + + void + normalize(void) override + { + // a Point is always in normalized form + } + + std::unique_ptr reverse() const + { + return std::unique_ptr(reverseImpl()); + } + + const Envelope* getEnvelopeInternal() const override { + return &envelope; + } + +protected: + + /** + * \brief + * Creates a Point taking ownership of the given CoordinateSequence + * (must have 1 element) + * + * @param newCoords + * contains the single coordinate on which to base this + * Point or null to create + * the empty geometry. + * + * @param newFactory the GeometryFactory used to create this geometry + */ + Point(CoordinateSequence&& newCoords, const GeometryFactory* newFactory); + + Point(const Coordinate& c, const GeometryFactory* newFactory); + + Point(const CoordinateXY& c, const GeometryFactory* newFactory); + + Point(const CoordinateXYM& c, const GeometryFactory* newFactory); + + Point(const CoordinateXYZM& c, const GeometryFactory* newFactory); + + Point(const Point& p); + + Point* cloneImpl() const override { return new Point(*this); } + + Point* reverseImpl() const override { return new Point(*this); } + + Envelope computeEnvelopeInternal() const; + + int compareToSameClass(const Geometry* p) const override; + + int + getSortIndex() const override + { + return SORTINDEX_POINT; + }; + + void geometryChangedAction() override { + envelope = computeEnvelopeInternal(); + } + +private: + + CoordinateSequence coordinates; + Envelope envelope; +}; + +} // namespace geos::geom +} // namespace geos + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geom/Polygon.h b/Sources/geos/include/geos/geom/Polygon.h new file mode 100644 index 0000000..aabc4d8 --- /dev/null +++ b/Sources/geos/include/geos/geom/Polygon.h @@ -0,0 +1,138 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Polygon.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inheritance +#include // for proper use of unique_ptr<> +#include +#include // for Dimension::DimensionType +#include + +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { // geos::geom +class Coordinate; +class CoordinateSequenceFilter; +class LineString; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \class Polygon geom.h geos.h + * + * \brief Represents a linear polygon, which may include holes. + * + * The shell and holes of the polygon are represented by {@link LinearRing}s. + * In a valid polygon, holes may touch the shell or other holes at a single point. + * However, no sequence of touching holes may split the polygon into two pieces. + * The orientation of the rings in the polygon does not matter. + *

+ * The shell and holes must conform to the assertions specified in the OpenGIS Simple Features + * Specification for SQL . + * + */ +class GEOS_DLL Polygon: public SurfaceImpl { + +public: + + friend class GeometryFactory; + + /// A vector of const Polygon pointers + typedef std::vector ConstVect; + + ~Polygon() override = default; + + std::unique_ptr + getCoordinates() const override; + + /** + * Creates and returns a full copy of this {@link Polygon} object. + * (including all coordinates contained by it). + * + * @return a clone of this instance + */ + std::unique_ptr clone() const + { + return std::unique_ptr(cloneImpl()); + } + + /** \brief + * Computes the boundary of this geometry + * + * @return a lineal geometry (which may be empty) + * @see Geometry#getBoundary + */ + std::unique_ptr getBoundary() const override; + + std::string getGeometryType() const override; + GeometryTypeId getGeometryTypeId() const override; + + void normalize() override; + + std::unique_ptr reverse() const { return std::unique_ptr(reverseImpl()); } + + double getArea() const override; + + bool isRectangle() const override; + + /** + * \brief + * Apply a ring ordering convention to this polygon, with + * interior rings having an opposite orientation to the + * specified exterior orientation. + * + * \param exteriorCW should exterior ring be clockwise? + */ + void orientRings(bool exteriorCW); + +protected: + + using SurfaceImpl::SurfaceImpl; + + Polygon* cloneImpl() const override { return new Polygon(*this); } + + Polygon* reverseImpl() const override; + + int + getSortIndex() const override + { + return SORTINDEX_POLYGON; + }; + + +private: + + void normalize(LinearRing* ring, bool clockwise); + +}; + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/Position.h b/Sources/geos/include/geos/geom/Position.h new file mode 100644 index 0000000..a92c159 --- /dev/null +++ b/Sources/geos/include/geos/geom/Position.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Position.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include + + + +namespace geos { +namespace geom { // geos.geom + +/** \brief + * A Position indicates the position of a Location relative to a graph + * component (Node, Edge, or Area). + */ +class GEOS_DLL Position { +public: + enum { + /** \brief + * An indicator that a Location is *on* + * a GraphComponent + */ + ON = 0, + + /** \brief + * An indicator that a Location is to the + * *left* of a GraphComponent + */ + LEFT, + + /** \brief + * An indicator that a Location is to the + * *right* of a GraphComponent + */ + RIGHT + }; + + /** \brief + * Returns LEFT if the position is RIGHT, RIGHT if + * the position is LEFT, or the position otherwise. + */ + static int opposite(int position); +}; + +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/PrecisionModel.h b/Sources/geos/include/geos/geom/PrecisionModel.h new file mode 100644 index 0000000..d0d8ac6 --- /dev/null +++ b/Sources/geos/include/geos/geom/PrecisionModel.h @@ -0,0 +1,372 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/PrecisionModel.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace io { +} +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace geom { // geos::geom + +/** + * \class PrecisionModel geom.h geos.h + * + * \brief Specifies the precision model of the Coordinate in a Geometry. + * + * In other words, specifies the grid of allowable points for a Geometry. + * A precision model may be floating (PrecisionModel::Type::FLOATING or + * PrecisionModel::Type::FLOATING_SINGLE), in which case normal floating-point value semantics apply. + * + * For a PrecisionModel::Type::FIXED precision model the + * makePrecise(geom::Coordinate) method allows rounding a coordinate to + * a "precise" value; that is, one whose precision is known exactly. + * + * Coordinates are assumed to be precise in geometries. + * That is, the coordinates are assumed to be rounded to the + * precision model given for the geometry. + * All internal operations + * assume that coordinates are rounded to the precision model. + * Constructive methods (such as boolean operations) always round computed + * coordinates to the appropriate precision model. + * + * Three types of precision model are supported: + * - FLOATING - represents full double precision floating point. + * This is the default precision model used in JTS + * - FLOATING_SINGLE - represents single precision floating point. + * - FIXED - represents a model with a fixed number of decimal places. + * A Fixed Precision Model is specified by a scale factor. + * The scale factor specifies the grid which numbers are rounded to. + * Input coordinates are mapped to fixed coordinates according to the + * following equations: + * - jtsPt.x = round( inputPt.x * scale ) / scale + * - jtsPt.y = round( inputPt.y * scale ) / scale + * + * For example, to specify 3 decimal places of precision, use a scale factor + * of 1000. To specify -3 decimal places of precision (i.e. rounding to + * the nearest 1000), use a scale factor of 0.001. + * + * It is also supported to specify a precise grid size + * by providing it as a negative scale factor. + * For example, to specify rounding to the nearest 1000 use a scale factor of -1000. + * + * Coordinates are represented internally as Java double-precision values. + * Java uses the IEEE-394 floating point standard, which + * provides 53 bits of precision. (Thus the maximum precisely representable + * integer is 9,007,199,254,740,992). + * + */ +class GEOS_DLL PrecisionModel { + +public: + + /// The types of Precision Model which GEOS supports. + typedef enum { + + /** + * Fixed Precision indicates that coordinates have a fixed + * number of decimal places. + * The number of decimal places is determined by the log10 + * of the scale factor. + */ + FIXED, + + /** + * Floating precision corresponds to the standard Java + * double-precision floating-point representation, which is + * based on the IEEE-754 standard + */ + FLOATING, + + /** + * Floating single precision corresponds to the standard Java + * single-precision floating-point representation, which is + * based on the IEEE-754 standard + */ + FLOATING_SINGLE + + } Type; + + /// Creates a PrecisionModel with a default precision of FLOATING. + PrecisionModel(void); + + /// Creates a PrecisionModel specifying an explicit precision model type. + /// + /// If the model type is FIXED the scale factor will default to 1. + /// + /// @param nModelType the type of the precision model + /// + PrecisionModel(Type nModelType); + + /** \brief + * Creates a PrecisionModel with Fixed precision. + * + * Fixed-precision coordinates are represented as precise internal + * coordinates, which are rounded to the grid defined by the + * scale factor. + * + * @param newScale amount by which to multiply a coordinate after + * subtracting the offset, to obtain a precise coordinate + * @param newOffsetX not used. + * @param newOffsetY not used. + * + * @deprecated offsets are no longer supported, since internal + * representation is rounded floating point + */ + PrecisionModel(double newScale, double newOffsetX, double newOffsetY); + + /** + * \brief + * Creates a PrecisionModel with Fixed precision. + * + * Fixed-precision coordinates are represented as precise + * internal coordinates which are rounded to the grid defined + * by the scale factor. + * The provided scale may be negative, to specify an exact grid size. + * The scale is then computed as the reciprocal. + * + * @param newScale amount by which to multiply a coordinate + * after subtracting the offset, to obtain a precise coordinate. Must be non-zero. + */ + PrecisionModel(double newScale); + + /// The maximum precise value representable in a double. + /// + /// Since IEE754 double-precision numbers allow 53 bits of mantissa, + /// the value is equal to 2^53 - 1. + /// This provides almost 16 decimal digits of precision. + //// + static const double maximumPreciseValue; + + /** \brief + * Rounds a numeric value to the PrecisionModel grid. + * + * Asymmetric Arithmetic Rounding is used, to provide + * uniform rounding behaviour no matter where the number is + * on the number line. + * + * Note: Java's Math#rint uses the "Banker's Rounding" algorithm, + * which is not suitable for precision operations elsewhere in JTS. + */ + double makePrecise(double val) const; + + /// Rounds the given Coordinate to the PrecisionModel grid. + void makePrecise(CoordinateXY& coord) const + { + // optimization for full precision + if(modelType == FLOATING) { + return; + } + + coord.x = makePrecise(coord.x); + coord.y = makePrecise(coord.y); + }; + + void makePrecise(CoordinateXY* coord) const + { + assert(coord); + return makePrecise(*coord); + }; + + /// Tests whether the precision model supports floating point + /// + /// @return true if the precision model supports + /// floating point + /// + bool isFloating() const; + + /// \brief + /// Returns the maximum number of significant digits provided by + /// this precision model. + /// + /// Intended for use by routines which need to print out precise + /// values. + /// + /// @return the maximum number of decimal places provided by this + /// precision model + /// + int getMaximumSignificantDigits() const; + + /// Gets the type of this PrecisionModel + /// + /// @return the type of this PrecisionModel + /// + Type getType() const + { + return modelType; + }; + + /// Returns the multiplying factor used to obtain a precise coordinate. + double getScale() const + { + assert(!(scale < 0)); + return scale; + }; + + /** + * Computes the grid size for a fixed precision model. + * This is equal to the reciprocal of the scale factor. + * If the grid size has been set explicitly (via a negative scale factor) + * it will be returned. + * + * @return the grid size at a fixed precision scale. + */ + double getGridSize() const + { + if (isFloating()) + return DoubleNotANumber; + + if (gridSize != 0) + return gridSize; + + return 1.0 / scale; + }; + + /// Returns the x-offset used to obtain a precise coordinate. + /// + /// @return the amount by which to subtract the x-coordinate before + /// multiplying by the scale + /// @deprecated Offsets are no longer used + /// + double getOffsetX() const; + + /// Returns the y-offset used to obtain a precise coordinate. + /// + /// @return the amount by which to subtract the y-coordinate before + /// multiplying by the scale + /// @deprecated Offsets are no longer used + /// + double getOffsetY() const; + + /* + * Sets ´internal` to the precise representation of `external`. + * + * @param external the original coordinate + * @param internal the coordinate whose values will be changed to the + * precise representation of external + * @deprecated use makePrecise instead + */ + //void toInternal(const Coordinate& external, Coordinate* internal) const; + + /* + * Returns the precise representation of external. + * + *@param external the original coordinate + *@return + * the coordinate whose values will be changed to the precise + * representation of external + * @deprecated use makePrecise instead + */ + //Coordinate* toInternal(const Coordinate& external) const; + + /* + * Returns the external representation of internal. + * + *@param internal the original coordinate + *@return the coordinate whose values will be changed to the + * external representation of internal + * @deprecated no longer needed, since internal representation is same as external representation + */ + //Coordinate* toExternal(const Coordinate& internal) const; + + /* + * Sets external to the external representation of + * internal. + * + * @param internal the original coordinate + * @param external + * the coordinate whose values will be changed to the + * external representation of internal + * @deprecated no longer needed, since internal representation is same as external representation + */ + //void toExternal(const Coordinate& internal, Coordinate* external) const; + + std::string toString() const; + + /// \brief + /// Compares this PrecisionModel object with the specified object + /// for order. + /// + /// A PrecisionModel is greater than another if it provides greater + /// precision. + /// The comparison is based on the value returned by the + /// getMaximumSignificantDigits method. + /// This comparison is not strictly accurate when comparing floating + /// precision models to fixed models; + /// however, it is correct when both models are either floating or + /// fixed. + /// + /// @param other the PrecisionModel with which this PrecisionModel + /// is being compared + /// @return a negative integer, zero, or a positive integer as this + /// PrecisionModel is less than, equal to, or greater than the + /// specified PrecisionModel. + /// + int compareTo(const PrecisionModel* other) const; + +private: + + /** \brief + * Sets the multiplying factor used to obtain a precise coordinate. + * + * This method is private because PrecisionModel is intended to + * be an immutable (value) type. + * + */ + void setScale(double newScale); + // throw IllegalArgumentException + + /** \brief + * Snaps a value to nearest integer, if within tolerance. + */ + static double snapToInt(double val, double tolerance); + + Type modelType; + + /** + * The scale factor which determines the number of decimal places in fixed precision. + */ + double scale; + + /** + * If non-zero, the precise grid size specified. + * In this case, the scale is also valid and is computed from the grid size. + * If zero, the scale is used to compute the grid size where needed. + */ + double gridSize = 0.0; + +}; + +// Equality operator for PrecisionModel, deprecate it ? +//inline bool operator==(const PrecisionModel& a, const PrecisionModel& b); + +} // namespace geos::geom +} // namespace geos diff --git a/Sources/geos/include/geos/geom/Quadrant.h b/Sources/geos/include/geos/geom/Quadrant.h new file mode 100644 index 0000000..d7a9e95 --- /dev/null +++ b/Sources/geos/include/geos/geom/Quadrant.h @@ -0,0 +1,182 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/Quadrant.java rev. 1.8 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace geom { // geos.geom + +/** \brief + * Utility functions for working with quadrants. + * + * The quadrants are numbered as follows: + *

+ * 1 | 0
+ * --+--
+ * 2 | 3
+ * 
+ * + */ +class GEOS_DLL Quadrant { + +public: + + static const int NE = 0; + static const int NW = 1; + static const int SW = 2; + static const int SE = 3; + + /** + * Returns the quadrant of a directed line segment + * (specified as x and y displacements, which cannot both be 0). + * + * @throws IllegalArgumentException if the displacements are both 0 + */ + static int quadrant(double dx, double dy) + { + if(dx == 0.0 && dy == 0.0) { + std::ostringstream s; + s << "Cannot compute the quadrant for point "; + s << "(" << dx << "," << dy << ")" << std::endl; + throw util::IllegalArgumentException(s.str()); + } + if(dx >= 0) { + if(dy >= 0) { + return NE; + } + else { + return SE; + } + } + else { + if(dy >= 0) { + return NW; + } + else { + return SW; + } + } + }; + + /** + * Returns the quadrant of a directed line segment from p0 to p1. + * + * @throws IllegalArgumentException if the points are equal + */ + static int quadrant(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) + { + if(p1.x == p0.x && p1.y == p0.y) { + throw util::IllegalArgumentException("Cannot compute the quadrant for two identical points " + p0.toString()); + } + + if(p1.x >= p0.x) { + if(p1.y >= p0.y) { + return NE; + } + else { + return SE; + } + } + else { + if(p1.y >= p0.y) { + return NW; + } + else { + return SW; + } + } + }; + + /** Return a measure that increases monotonically with counterclockwise + * angle and avoids trigonometric calculations. Values are consistent + * with the numeric quadrant codes + * + * @param p0 circle center coordinate + * @param p1 coordinate for which pseudoangle should be calculated + */ + static double pseudoAngle(const CoordinateXY& p0, const CoordinateXY& p1) + { + const double dx = p1.x - p0.x; + const double dy = p1.y - p0.y; + + const double k = dx / (std::abs(dx) + std::abs(dy)); + const double w = 2 + (dy > 0 ? 3 - k : 1 + k); + + return w >= 4 ? w - 4 : w; + } + + /** + * Returns true if the quadrants are 1 and 3, or 2 and 4 + */ + static bool isOpposite(int quad1, int quad2) + { + if(quad1 == quad2) { + return false; + } + int diff = (quad1 - quad2 + 4) % 4; + // if quadrants are not adjacent, they are opposite + if(diff == 2) { + return true; + } + return false; + }; + + /* + * Returns the right-hand quadrant of the halfplane defined by + * the two quadrants, + * or -1 if the quadrants are opposite, or the quadrant if they + * are identical. + */ + static int commonHalfPlane(int quad1, int quad2); + + /** + * Returns whether the given quadrant lies within the given halfplane + * (specified by its right-hand quadrant). + */ + static bool isInHalfPlane(int quad, int halfPlane); + + /** + * Returns true if the given quadrant is 0 or 1. + */ + static bool isNorthern(int quad) + { + return quad == NE || quad == NW; + }; + +}; + + +} // namespace geos.geom +} // namespace geos diff --git a/Sources/geos/include/geos/geom/SimpleCurve.h b/Sources/geos/include/geos/geom/SimpleCurve.h new file mode 100644 index 0000000..1bf674f --- /dev/null +++ b/Sources/geos/include/geos/geom/SimpleCurve.h @@ -0,0 +1,142 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { + +class GEOS_DLL SimpleCurve : public Curve { +public: + + using Curve::apply_ro; + using Curve::apply_rw; + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + bool equalsExact(const Geometry* other, double tolerance = 0) + const override; + + bool equalsIdentical(const Geometry* other) const override; + + /** + * \brief + * Returns a MultiPoint. + * Empty for closed Curve, a Point for each vertex otherwise. + */ + std::unique_ptr getBoundary() const override; + + const CoordinateXY* getCoordinate() const override; + + /// Returns coordinate dimension. + uint8_t getCoordinateDimension() const override; + + virtual const Coordinate& getCoordinateN(std::size_t n) const; + + std::unique_ptr getCoordinates() const override; + + /// Returns a read-only pointer to internal CoordinateSequence + const CoordinateSequence* getCoordinatesRO() const; + + const SimpleCurve* getCurveN(std::size_t) const override; + + /// \brief + /// Return the end point of the LineString + /// or NULL if this is an EMPTY LineString. + /// + virtual std::unique_ptr getEndPoint() const; + + const Envelope* getEnvelopeInternal() const override + { + return &envelope; + } + + std::size_t getNumCurves() const override; + + std::size_t getNumPoints() const override; + + virtual std::unique_ptr getPointN(std::size_t n) const; + + /// \brief + /// Return the start point of the LineString + /// or NULL if this is an EMPTY LineString. + /// + virtual std::unique_ptr getStartPoint() const; + + bool hasM() const override; + + bool hasZ() const override; + + bool isClosed() const override; + + virtual bool isCoordinate(CoordinateXY& pt) const; + + virtual bool isCurved() const = 0; + + bool isEmpty() const override; + + /** \brief + * Normalizes a SimpleCurve. + * + * A normalized simple curve + * has the first point which is not equal to its reflected point + * less than the reflected point. + */ + void normalize() override; + + /** + * \brief + * Take ownership of the CoordinateSequence managed by this geometry. + * After releasing the coordinates, the geometry should be considered + * in a moved-from state and should not be accessed. + * @return this Geometry's CoordinateSequence. + */ + std::unique_ptr releaseCoordinates(); + +protected: + + SimpleCurve(const SimpleCurve& other); + + SimpleCurve(std::unique_ptr&& newCoords, + bool isLinear, + const GeometryFactory& factory); + + int compareToSameClass(const Geometry* ls) const override; + + Envelope computeEnvelopeInternal(bool isLinear) const; + + // TODO: hold value or shared_ptr instead of unique_ptr? + std::unique_ptr points; + mutable Envelope envelope; + + +private: + + void normalizeClosed(); +}; + +} +} diff --git a/Sources/geos/include/geos/geom/Surface.h b/Sources/geos/include/geos/geom/Surface.h new file mode 100644 index 0000000..d2dcb38 --- /dev/null +++ b/Sources/geos/include/geos/geom/Surface.h @@ -0,0 +1,115 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geom { + +class Curve; + +/// A Surface is an abstract class representing a Geometry of dimension 2. +/// It is extended by Polygon, which represents a Surface with linear edges, +/// and by CurvePolygon, whose edges may include circular arcs. +class GEOS_DLL Surface : public Geometry { + +private: + +protected: + using Geometry::Geometry; + +public: + + void apply_ro(CoordinateFilter* filter) const override; + + void apply_ro(CoordinateSequenceFilter& filter) const override; + + void apply_ro(GeometryComponentFilter* filter) const override; + + void apply_ro(GeometryFilter* filter) const override; + + void apply_rw(CoordinateSequenceFilter& filter) override; + + void apply_rw(GeometryComponentFilter* filter) override; + + void apply_rw(GeometryFilter* filter) override; + + void apply_rw(const CoordinateFilter* filter) override; + + std::unique_ptr convexHull() const override; + + bool + equalsExact(const Geometry* other, double tolerance = 0.0) const override; + + bool + equalsIdentical(const Geometry* other) const override; + + int + getBoundaryDimension() const override + { + return 1; + } + + const CoordinateXY* getCoordinate() const override; + + uint8_t getCoordinateDimension() const override; + + Dimension::DimensionType + getDimension() const override + { + return Dimension::A; // area + } + + const Envelope* getEnvelopeInternal() const override; + + /// Returns the exterior ring (shell) + virtual const Curve* getExteriorRing() const = 0; + + /// Get nth interior ring (hole) + virtual const Curve* getInteriorRingN(std::size_t n) const = 0; + + /// Returns the perimeter of this Surface + double getLength() const override; + + /// Returns number of interior rings (holes) + virtual size_t getNumInteriorRing() const = 0; + + size_t getNumPoints() const override; + + bool hasM() const override; + + bool hasZ() const override; + + bool isEmpty() const override; + +protected: + + int + compareToSameClass(const Geometry* g) const override; + + // Helper method allowing PolygonImpl to use GeometryFactory without circular imports + static std::unique_ptr createEmptyRing(const GeometryFactory&); + + virtual Curve* getExteriorRing() = 0; + + virtual Curve* getInteriorRingN(std::size_t i) = 0; + + void geometryChangedAction() override {} + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/SurfaceImpl.h b/Sources/geos/include/geos/geom/SurfaceImpl.h new file mode 100644 index 0000000..60da401 --- /dev/null +++ b/Sources/geos/include/geos/geom/SurfaceImpl.h @@ -0,0 +1,159 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2024 ISciences, LLC + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { + +template +class SurfaceImpl : public Surface { + +protected: + + SurfaceImpl(const SurfaceImpl& p) + : + Surface(p), + shell(static_cast(p.shell->clone().release())), + holes(p.holes.size()) + { + for (std::size_t i = 0; i < holes.size(); ++i) { + holes[i].reset(static_cast(p.holes[i]->clone().release())); + } + } + + /** + * Constructs a Surface with the given exterior + * and interior boundaries. + * + * @param newShell the outer boundary of the new Polygon, + * or null or an empty + * Curve if the empty geometry + * is to be created. + * + * @param newHoles the rings defining the inner + * boundaries of the new Surface, or + * null or empty Curve + * if the empty geometry is to be created. + * + * @param newFactory the GeometryFactory used to create this geometry + * + * Polygon will take ownership of shell and hole curves + */ + SurfaceImpl(std::unique_ptr&& newShell, + const GeometryFactory& newFactory) : + Surface(&newFactory), + shell(std::move(newShell)) + { + if (shell == nullptr) { + shell.reset(static_cast(createEmptyRing(newFactory).release())); + } + } + + SurfaceImpl(std::unique_ptr&& newShell, + std::vector>&& newHoles, + const GeometryFactory& newFactory) : + Surface(&newFactory), + shell(std::move(newShell)), + holes(std::move(newHoles)) + { + if (shell == nullptr) { + shell.reset(static_cast(createEmptyRing(newFactory).release())); + } + + if(shell->isEmpty() && hasNonEmptyElements(&holes)) { + throw geos::util::IllegalArgumentException("shell is empty but holes are not"); + } + if (hasNullElements(&holes)) { + throw geos::util::IllegalArgumentException("holes must not contain null elements"); + } + } + +public: + + const RingType* + getExteriorRing() const override + { + return shell.get(); + } + + RingType* + getExteriorRing() override + { + return shell.get(); + } + + const RingType* + getInteriorRingN(std::size_t n) const override + { + return holes[n].get(); + } + + RingType* + getInteriorRingN(std::size_t n) override + { + return holes[n].get(); + } + + size_t getNumInteriorRing() const override + { + return holes.size(); + } + + /** + * \brief + * Take ownership of this Surface's exterior ring. + * After releasing the exterior ring, the Surface should be + * considered in a moved-from state and should not be accessed, + * except to release the interior rings (if desired.) + * @return exterior ring + */ + std::unique_ptr + releaseExteriorRing() + { + return std::move(shell); + } + + /** + * \brief + * Take ownership of this Surfaces's interior rings. + * After releasing the rings, the Surface should be + * considered in a moved-from state and should not be accessed, + * except to release the exterior ring (if desired.) + * @return vector of rings (may be empty) + */ + std::vector> releaseInteriorRings() + { + return std::move(holes); + } + +protected: + std::unique_ptr shell; + std::vector> holes; + +}; + +} +} diff --git a/Sources/geos/include/geos/geom/Triangle.h b/Sources/geos/include/geos/geom/Triangle.h new file mode 100644 index 0000000..0f54cb9 --- /dev/null +++ b/Sources/geos/include/geos/geom/Triangle.h @@ -0,0 +1,235 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { // geos::geom + +/** + * \brief + * Represents a planar triangle, and provides methods for calculating various + * properties of triangles. + */ +class GEOS_DLL Triangle { +public: + CoordinateXY p0, p1, p2; + + Triangle(const CoordinateXY& nP0, const CoordinateXY& nP1, const CoordinateXY& nP2) + : p0(nP0) + , p1(nP1) + , p2(nP2) {} + + + /** \brief + * The inCentre of a triangle is the point which is equidistant + * from the sides of the triangle. + * + * This is also the point at which the bisectors of the angles meet. + * + * @param resultPoint the point into which to write the inCentre of the triangle + */ + void inCentre(CoordinateXY& resultPoint); + + static CoordinateXY inCentre( + const CoordinateXY& p0, + const CoordinateXY& p1, + const CoordinateXY& p2); + + /** \brief + * Computes the circumcentre of a triangle. + * + * The circumcentre is the centre of the circumcircle, the smallest circle + * which encloses the triangle. It is also the common intersection point of + * the perpendicular bisectors of the sides of the triangle, and is the only + * point which has equal distance to all three vertices of the triangle. + * + * The circumcentre does not necessarily lie within the triangle. For example, + * the circumcentre of an obtuse isoceles triangle lies outside the triangle. + * + * This method uses an algorithm due to J.R.Shewchuk which uses normalization + * to the origin to improve the accuracy of computation. (See *Lecture Notes + * on Geometric Robustness*, Jonathan Richard Shewchuk, 1999). + * + * @param resultPoint the point into which to write the inCentre of the triangle + */ + void circumcentre(CoordinateXY& resultPoint); + + /** Calculates the circumcentre using double precision math + * @param resultPoint the point into which to write the inCentre of the triangle + */ + void circumcentreDD(CoordinateXY& resultPoint); + + /** Computes the circumcentre of a triangle. The circumcentre is the centre + * of the circumcircle, the smallest circle which passes through all the triangle vertices. + * It is also the common intersection point of the perpendicular bisectors of the + * @param p0 corner of the triangle + * @param p1 corner of the triangle + * @param p2 corner of the triangle + * @return the center of the the smallest circle that encloses the triangle + */ + static const CoordinateXY circumcentre(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& p2); + + /** + * Computes the radius of the circumcircle of a triangle. + * Formula is as per https://math.stackexchange.com/a/3610959 + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return the circumradius of the triangle + */ + static double circumradius(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c); + + /** + * Computes the radius of the circumcircle of a triangle. + * + * @return the triangle circumradius + */ + double circumradius() const + { + return circumradius(p0, p1, p2); + }; + + bool isIsoceles(); + + /** + * Tests whether a triangle is acute. A triangle is acute if all interior + * angles are acute. This is a strict test - right triangles will return + * false. A triangle which is not acute is either right or obtuse. + *

+ * Note: this implementation is not robust for angles very close to 90 + * degrees. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return true if the triangle is acute + */ + static bool isAcute(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c); + + /** + * Tests whether a triangle is oriented counter-clockwise. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return true if the triangle orientation is counter-clockwise + */ + static bool isCCW(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c); + + + /** + * Tests whether a triangle intersects a point. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point to test + * @return true if the triangle intersects the point + */ + static bool intersects(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); + + + /** + * Tests whether a triangle intersects a point. + * @param p the point to test + * @return true if the triangle intersects the point + */ + bool intersects(const CoordinateXY& p) { return intersects(p0, p1, p2, p); }; + + /** + * Tests whether this triangle is oriented counter-clockwise. + * @return true if the triangle orientation is counter-clockwise + */ + bool isCCW() { return isCCW(p0, p1, p2); }; + + /** + * Tests whether this triangle is acute. + * @return true if this triangle is acute + */ + bool isAcute() { return isAcute(p0, p1, p2); }; + + /** + * Computes the length of the longest side of a triangle + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return the length of the longest side of the triangle + */ + static double longestSideLength( + const CoordinateXY& a, + const CoordinateXY& b, + const CoordinateXY& c); + + /** + * Compute the length of the perimeter of a triangle + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return the length of the triangle perimeter + */ + static double length(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c); + + /** + * Computes the length of the perimeter of this triangle. + * + * @return the length of the perimeter + */ + double length() const; + + /** + * Computes the 2D area of a triangle. The area value is always non-negative. + * + * @param a vertex of the triangle + * @param b vertex of the triangle + * @param c vertex of the triangle + * @return the area of the triangle + * + */ + static double area(const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c); + + double area() const; + +private: + + /** + * Computes the determinant of a 2x2 matrix. Uses standard double-precision + * arithmetic, so is susceptible to round-off error. + * + * @param m00 + * the [0,0] entry of the matrix + * @param m01 + * the [0,1] entry of the matrix + * @param m10 + * the [1,0] entry of the matrix + * @param m11 + * the [1,1] entry of the matrix + * @return the determinant + */ + double det(double m00, double m01, double m10, double m11) const; + +}; + + + +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/AbstractPreparedPolygonContains.h b/Sources/geos/include/geos/geom/prep/AbstractPreparedPolygonContains.h new file mode 100644 index 0000000..a350a78 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/AbstractPreparedPolygonContains.h @@ -0,0 +1,144 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/AbstractPreparedPolygonContains.java r388 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include // inherited + + +// forward declarations +namespace geos { +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +} + + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * A base class containing the logic for computes the contains + * and covers spatial relationship predicates + * for a {@link PreparedPolygon} relative to all other {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * Contains and covers are very similar, and differ only in how certain + * cases along the boundary are handled. These cases require + * full topological evaluation to handle, so all the code in + * this class is common to both predicates. + * + * It is not possible to short-circuit in all cases, in particular + * in the case where line segments of the test geometry touches the polygon + * linework. + * In this case full topology must be computed. + * (However, if the test geometry consists of only points, this + * can be evaluated in an optimized fashion. + * + * @author Martin Davis + * + */ +class AbstractPreparedPolygonContains : public PreparedPolygonPredicate { +private: + // information about geometric situation + bool hasSegmentIntersection; + bool hasProperIntersection; + bool hasNonProperIntersection; + + bool isProperIntersectionImpliesNotContainedSituation(const geom::Geometry* testGeom); + + /** + * Tests whether a geometry consists of a single polygon with no holes. + * + * @return true if the geometry is a single polygon with no holes + */ + bool isSingleShell(const geom::Geometry& geom); + + void findAndClassifyIntersections(const geom::Geometry* geom); + +protected: + /** + * This flag controls a difference between contains and covers. + * + * For contains the value is true. + * For covers the value is false. + */ + bool requireSomePointInInterior; + + /** + * Evaluate the contains or covers relationship + * for the given geometry. + * + * @param geom the test geometry + * @return true if the test geometry is contained + */ + bool eval(const geom::Geometry* geom); + + /** + * Evaluate the contains or covers relationship + * for the given Puntal geometry. + * + * @param geom the test geometry + * @param outermostLoc outermost Location of all points in geom + * @return true if the test geometry is contained/covered in the target + */ + bool evalPointTestGeom(const geom::Geometry* geom, geom::Location outermostLoc); + + /** + * Computes the full topological predicate. + * Used when short-circuit tests are not conclusive. + * + * @param geom the test geometry + * @return true if this prepared polygon has the relationship with the test geometry + */ + virtual bool fullTopologicalPredicate(const geom::Geometry* geom) = 0; + +public: + AbstractPreparedPolygonContains(const PreparedPolygon* const p_prepPoly) + : PreparedPolygonPredicate(p_prepPoly), + hasSegmentIntersection(false), + hasProperIntersection(false), + hasNonProperIntersection(false), + requireSomePointInInterior(true) + { } + + AbstractPreparedPolygonContains(const PreparedPolygon* const p_prepPoly, bool p_requireSomePointInInterior) + : PreparedPolygonPredicate(p_prepPoly), + hasSegmentIntersection(false), + hasProperIntersection(false), + hasNonProperIntersection(false), + requireSomePointInInterior(p_requireSomePointInInterior) + { } + + ~AbstractPreparedPolygonContains() override + { } + +}; + +} // geos::geom::prep +} // geos::geom +} // geos + diff --git a/Sources/geos/include/geos/geom/prep/BasicPreparedGeometry.h b/Sources/geos/include/geos/geom/prep/BasicPreparedGeometry.h new file mode 100644 index 0000000..5e7323b --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/BasicPreparedGeometry.h @@ -0,0 +1,214 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/BasicPreparedGeometry.java rev. 1.5 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // for inheritance +#include +#include + +#include +#include + +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +} +} + + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +// * \class BasicPreparedGeometry + +/** + * + * \brief + * A base class for {@link PreparedGeometry} subclasses. + * + * Contains default implementations for methods, which simply delegate + * to the equivalent {@link Geometry} methods. + * This class may be used as a "no-op" class for Geometry types + * which do not have a corresponding {@link PreparedGeometry} implementation. + * + * @author Martin Davis + * + */ +class BasicPreparedGeometry: public PreparedGeometry { + using RelateNG = geos::operation::relateng::RelateNG; + +private: + const geom::Geometry* baseGeom; + std::vector representativePts; + mutable std::unique_ptr relate_ng; + + RelateNG& getRelateNG() const + { + if (relate_ng == nullptr) + relate_ng = RelateNG::prepare(baseGeom); + + return *relate_ng; + } + +protected: + /** + * Sets the original {@link Geometry} which will be prepared. + */ + void setGeometry(const geom::Geometry* geom); + + /** + * Determines whether a Geometry g interacts with + * this geometry by testing the geometry envelopes. + * + * @param g a Geometry + * @return true if the envelopes intersect + */ + bool envelopesIntersect(const geom::Geometry* g) const; + + /** + * Determines whether the envelope of + * this geometry covers the Geometry g. + * + * + * @param g a Geometry + * @return true if g is contained in this envelope + */ + bool envelopeCovers(const geom::Geometry* g) const; + +public: + BasicPreparedGeometry(const Geometry* geom); + + ~BasicPreparedGeometry() override = default; + + const geom::Geometry& + getGeometry() const override + { + return *baseGeom; + } + + /** + * Gets the list of representative points for this geometry. + * One vertex is included for every component of the geometry + * (i.e. including one for every ring of polygonal geometries) + * + * @return a List of Coordinate + */ + const std::vector* + getRepresentativePoints() const + { + return &representativePts; + } + + /** + * Tests whether any representative of the target geometry + * intersects the test geometry. + * This is useful in A/A, A/L, A/P, L/P, and P/P cases. + * + * @param testGeom the test geometry + * @return true if any component intersects the areal test geometry + */ + bool isAnyTargetComponentInTest(const geom::Geometry* testGeom) const; + + /** + * Default implementation. + */ + bool within(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool contains(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool containsProperly(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool coveredBy(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool covers(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool crosses(const geom::Geometry* g) const override; + + /** + * Standard implementation for all geometries. + * Supports {@link GeometryCollection}s as input. + */ + bool disjoint(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool intersects(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool overlaps(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool touches(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + std::unique_ptr relate(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool relate(const geom::Geometry* g, const std::string& pat) const override; + + /** + * Default implementation. + */ + std::unique_ptr nearestPoints(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + double distance(const geom::Geometry* g) const override; + + /** + * Default implementation. + */ + bool isWithinDistance(const geom::Geometry* geom, double dist) const override; + + std::string toString(); + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedGeometry.h b/Sources/geos/include/geos/geom/prep/PreparedGeometry.h new file mode 100644 index 0000000..6c77513 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedGeometry.h @@ -0,0 +1,260 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/prep/PreparedGeometry.java rev. 1.11 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { + namespace geom { + class Geometry; + class Coordinate; + class CoordinateSequence; + class IntersectionMatrix; + } +} + + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \class PreparedGeometry + * + * \brief + * An interface for classes which prepare {@link Geometry}s + * in order to optimize the performance + * of repeated calls to specific geometric operations. + * + * A given implementation may provide optimized implementations + * for only some of the specified methods, + * and delegate the remaining methods to the original {@link Geometry} operations. + * An implementation may also only optimize certain situations, + * and delegate others. + * See the implementing classes for documentation about which methods and situations + * they optimize. + * + */ +class GEOS_DLL PreparedGeometry { +public: + virtual + ~PreparedGeometry() {} + + /** \brief + * Gets the original {@link Geometry} which has been prepared. + * + * @return the base geometry + */ + virtual const geom::Geometry& getGeometry() const = 0; + + /** \brief + * Tests whether the base {@link Geometry} contains a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry contains the given Geometry + * + * @see Geometry#contains(Geometry) + */ + virtual bool contains(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} properly contains + * a given geometry. + * + * The containsProperly predicate has the following + * equivalent definitions: + * + * - Every point of the other geometry is a point of this + * geometry's interior. + * - The DE-9IM Intersection Matrix for the two geometries matches + * [T**FF*FF*] + * + * In other words, if the test geometry has any interaction with + * the boundary of the target + * geometry the result of containsProperly is false. + * This is different semantics to the {@link Geometry::contains} + * predicate, in which test geometries can intersect the target's + * boundary and still be contained. + * + * The advantage of using this predicate is that it can be computed + * efficiently, since it avoids the need to compute the full + * topological relationship of the input boundaries in cases where + * they intersect. + * + * An example use case is computing the intersections + * of a set of geometries with a large polygonal geometry. + * Since intersection is a fairly slow operation, it can + * be more efficient + * to use containsProperly to filter out test geometries + * which lie + * wholly inside the area. In these cases the intersection is + * known a priori to be exactly the original test geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry properly contains the given Geometry + * + * @see Geometry::contains + * + */ + virtual bool containsProperly(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} is covered by a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry is covered by the given Geometry + * + * @see Geometry#coveredBy(Geometry) + */ + virtual bool coveredBy(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} covers a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry covers the given Geometry + * + * @see Geometry#covers(Geometry) + */ + virtual bool covers(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} crosses a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry crosses the given Geometry + * + * @see Geometry#crosses(Geometry) + */ + virtual bool crosses(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} is disjoint from a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry is disjoint from the given Geometry + * + * @see Geometry#disjoint(Geometry) + */ + virtual bool disjoint(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} intersects a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry intersects the given Geometry + * + * @see Geometry#intersects(Geometry) + */ + virtual bool intersects(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} overlaps a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry overlaps the given Geometry + * + * @see Geometry#overlaps(Geometry) + */ + virtual bool overlaps(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} touches a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry touches the given Geometry + * + * @see Geometry#touches(Geometry) + */ + virtual bool touches(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} is within a given geometry. + * + * @param geom the Geometry to test + * @return true if this Geometry is within the given Geometry + * + * @see Geometry#within(Geometry) + */ + virtual bool within(const geom::Geometry* geom) const = 0; + + /** \brief + * Compute the nearest locations on the base {@link Geometry} and + * the given geometry. + * + * @param geom the Geometry to compute the nearest point to + * @return true the nearest points + * + */ + virtual std::unique_ptr nearestPoints(const geom::Geometry* geom) const = 0; + + /** \brief + * Compute the minimum distance between the base {@link Geometry} and + * the given geometry. + * + * @param geom the Geometry to compute the distance to + * @return the minimum distance between the two geometries + * + */ + virtual double distance(const geom::Geometry* geom) const = 0; + + /** \brief + * Tests whether the base {@link Geometry} is within a given + * distance from the given geometry. + * + * @param geom the Geometry to test the distance to + * @param dist the distance value to compare + * @return true if this Geometry is within the given distance from + * the given Geometry. + * + */ + virtual bool isWithinDistance(const geom::Geometry* geom, double dist) const = 0; + + /** \brief + * Compares the prepared geometry to the given geometry + * and returns the DE9IM intersection matrix as a string. + * + * @param geom the Geometry to test the + * @return the DE9IM matrix + */ + virtual std::unique_ptr relate(const geom::Geometry* geom) const = 0; + + /** \brief + * Compares the prepared geometry to the given geometry + * and the provided DE9IM pattern, and returns true if the + * pattern is consistent with the relationship between the + * prepared and provided geometries. + * + * @param geom the Geometry to test the distance to + * @param pat the DE9IM pattern + * @return true if the patterns are consistent + */ + virtual bool relate(const geom::Geometry* geom, const std::string& pat) const = 0; + +}; + + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedGeometryFactory.h b/Sources/geos/include/geos/geom/prep/PreparedGeometryFactory.h new file mode 100644 index 0000000..0242243 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedGeometryFactory.h @@ -0,0 +1,93 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedGeometryFactory.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace geom { +namespace prep { +class PreparedGeometry; +} +} +} + + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + + +/** + * \brief + * A factory for creating {@link PreparedGeometry}s. + * + * It chooses an appropriate implementation of PreparedGeometry + * based on the geoemtric type of the input geometry. + * In the future, the factory may accept hints that indicate + * special optimizations which can be performed. + * + * @author Martin Davis + * + */ +class GEOS_DLL PreparedGeometryFactory { +public: + + /** + * Creates a new {@link PreparedGeometry} appropriate for the argument {@link Geometry}. + * + * @param geom the geometry to prepare + * @return the prepared geometry + */ + static std::unique_ptr + prepare(const geom::Geometry* geom) + { + PreparedGeometryFactory pf; + return pf.create(geom); + } + + /** + * Destroys {@link PreparedGeometry} allocated with the factory. + * + * @param geom to be deallocated + */ + static void + destroy(const PreparedGeometry* geom) + { + delete geom; + } + + /** + * Creates a new {@link PreparedGeometry} appropriate for the argument {@link Geometry}. + * + * @param geom the geometry to prepare + * @return the prepared geometry + */ + std::unique_ptr create(const geom::Geometry* geom) const; + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedLineString.h b/Sources/geos/include/geos/geom/prep/PreparedLineString.h new file mode 100644 index 0000000..017e06e --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedLineString.h @@ -0,0 +1,70 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedLineString.java rev 1.3 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // for inheritance +#include +#include +#include + +#include + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * A prepared version of {@link LinearRing}, {@link LineString} or {@link MultiLineString} geometries. + * + * @author mbdavis + * + */ +class PreparedLineString : public BasicPreparedGeometry { +private: + std::unique_ptr segIntFinder; + mutable noding::SegmentString::ConstVect segStrings; + mutable std::unique_ptr indexedDistance; + +protected: +public: + PreparedLineString(const Geometry* geom) + : + BasicPreparedGeometry(geom), + segIntFinder(nullptr) + { } + + ~PreparedLineString() override; + + noding::FastSegmentSetIntersectionFinder* getIntersectionFinder(); + + bool intersects(const geom::Geometry* g) const override; + std::unique_ptr nearestPoints(const geom::Geometry* g) const override; + double distance(const geom::Geometry* g) const override; + bool isWithinDistance(const geom::Geometry* g, double d) const override; + operation::distance::IndexedFacetDistance* getIndexedFacetDistance() const; + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedLineStringDistance.h b/Sources/geos/include/geos/geom/prep/PreparedLineStringDistance.h new file mode 100644 index 0000000..04ef279 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedLineStringDistance.h @@ -0,0 +1,60 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace geom { // geos::geom + +class Geometry; + +namespace prep { // geos::geom::prep + +class PreparedLineString; + +class PreparedLineStringDistance { +public: + + static double distance(const PreparedLineString& prep, const geom::Geometry* geom) + { + PreparedLineStringDistance op(prep); + return op.distance(geom); + } + + PreparedLineStringDistance(const PreparedLineString& prep) + : prepLine(prep) + { } + + double distance(const geom::Geometry* g) const; + + bool isWithinDistance(const geom::Geometry* g, double d) const; + +protected: + + const PreparedLineString& prepLine; + + // Declare type as noncopyable + PreparedLineStringDistance(const PreparedLineStringDistance& other) = delete; + PreparedLineStringDistance& operator=(const PreparedLineStringDistance& rhs) = delete; +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedLineStringIntersects.h b/Sources/geos/include/geos/geom/prep/PreparedLineStringIntersects.h new file mode 100644 index 0000000..026455d --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedLineStringIntersects.h @@ -0,0 +1,97 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedLineStringIntersects.java r338 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * Computes the intersects spatial relationship predicate + * for a target {@link PreparedLineString} relative to all other + * {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * @author Martin Davis + * + */ +class PreparedLineStringIntersects { +public: + + /** + * Computes the intersects predicate between a {@link PreparedLineString} + * and a {@link Geometry}. + * + * @param prep the prepared linestring + * @param geom a test geometry + * @return true if the linestring intersects the geometry + */ + static bool + intersects(PreparedLineString& prep, const geom::Geometry* geom) + { + PreparedLineStringIntersects op(prep); + return op.intersects(geom); + } + + /** + * \todo FIXME - mloskot: Why not taking linestring through const reference? + */ + PreparedLineStringIntersects(PreparedLineString& prep) + : prepLine(prep) + { } + + /** + * Tests whether this geometry intersects a given geometry. + * + * @param g the test geometry + * @return true if the test geometry intersects + */ + bool intersects(const geom::Geometry* g) const; + +protected: + PreparedLineString& prepLine; + + /** + * Tests whether any representative point of the test Geometry intersects + * the target geometry. + * Only handles test geometries which are Puntal (dimension 0) + * + * @param testGeom a Puntal geometry to test + * @return true if any point of the argument intersects the prepared geometry + */ + bool isAnyTestPointInTarget(const geom::Geometry* testGeom) const; + + // Declare type as noncopyable + PreparedLineStringIntersects(const PreparedLineStringIntersects& other) = delete; + PreparedLineStringIntersects& operator=(const PreparedLineStringIntersects& rhs) = delete; +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedLineStringNearestPoints.h b/Sources/geos/include/geos/geom/prep/PreparedLineStringNearestPoints.h new file mode 100644 index 0000000..82939fa --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedLineStringNearestPoints.h @@ -0,0 +1,61 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +#include + +#include + +namespace geos { +namespace geom { // geos::geom + +class CoordinateSequence; + +namespace prep { // geos::geom::prep + +class PreparedLineStringNearestPoints { +public: + + static std::unique_ptr + nearestPoints(const PreparedLineString& prep, const geom::Geometry* geom) + { + PreparedLineStringNearestPoints op(prep); + return op.nearestPoints(geom); + } + + PreparedLineStringNearestPoints(const PreparedLineString& prep) + : prepLine(prep) + { } + + std::unique_ptr nearestPoints(const geom::Geometry* g) const; + +protected: + + const PreparedLineString& prepLine; + + // Declare type as noncopyable + PreparedLineStringNearestPoints(const PreparedLineStringNearestPoints& other) = delete; + PreparedLineStringNearestPoints& operator=(const PreparedLineStringNearestPoints& rhs) = delete; +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPoint.h b/Sources/geos/include/geos/geom/prep/PreparedPoint.h new file mode 100644 index 0000000..b191180 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPoint.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPoint.java rev. 1.2 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // for inheritance + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * A prepared version of {@link Point} or {@link MultiPoint} geometries. + * + * @author Martin Davis + * + */ +class PreparedPoint: public BasicPreparedGeometry { +private: +protected: +public: + PreparedPoint(const Geometry* geom) + : BasicPreparedGeometry(geom) + { } + + /** + * Tests whether this point intersects a {@link Geometry}. + * + * The optimization here is that computing topology for the test + * geometry is avoided. This can be significant for large geometries. + */ + bool intersects(const geom::Geometry* g) const override; + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygon.h b/Sources/geos/include/geos/geom/prep/PreparedPolygon.h new file mode 100644 index 0000000..a2feee6 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygon.h @@ -0,0 +1,81 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygon.java rev 1.7 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // for inheritance +#include +#include + +#include + +namespace geos { +namespace noding { +class FastSegmentSetIntersectionFinder; +} +namespace algorithm { +namespace locate { +class PointOnGeometryLocator; +} +} +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * A prepared version of {@link Polygon} or {@link MultiPolygon} geometries. + * + * @author mbdavis + * + */ +class PreparedPolygon : public BasicPreparedGeometry { +private: + bool isRectangle; + mutable std::unique_ptr segIntFinder; + mutable std::unique_ptr ptOnGeomLoc; + mutable std::unique_ptr indexedPtOnGeomLoc; + mutable noding::SegmentString::ConstVect segStrings; + mutable std::unique_ptr indexedDistance; + +protected: +public: + PreparedPolygon(const geom::Geometry* geom); + ~PreparedPolygon() override; + + noding::FastSegmentSetIntersectionFinder* getIntersectionFinder() const; + algorithm::locate::PointOnGeometryLocator* getPointLocator() const; + operation::distance::IndexedFacetDistance* getIndexedFacetDistance() const; + + bool contains(const geom::Geometry* g) const override; + bool containsProperly(const geom::Geometry* g) const override; + bool covers(const geom::Geometry* g) const override; + bool intersects(const geom::Geometry* g) const override; + double distance(const geom::Geometry* g) const override; + bool isWithinDistance(const geom::Geometry* g, double d) const override; + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonContains.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonContains.h new file mode 100644 index 0000000..57454bd --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonContains.h @@ -0,0 +1,105 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygonContains.java rev 1.5 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // inherited + +// forward declarations +namespace geos { +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * Computes the contains spatial relationship predicate + * for a {@link PreparedPolygon} relative to all other {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * It is not possible to short-circuit in all cases, in particular + * in the case where the test geometry touches the polygon linework. + * In this case full topology must be computed. + * + * @author Martin Davis + * + */ +class PreparedPolygonContains : public AbstractPreparedPolygonContains { +public: + + /** + * Creates an instance of this operation. + * + * @param prepPoly the PreparedPolygon to evaluate + */ + PreparedPolygonContains(const PreparedPolygon* const prepPoly); + + /** + * Tests whether this PreparedPolygon contains a given geometry. + * + * @param geom the test geometry + * @return true if the test geometry is contained + */ + bool + contains(const geom::Geometry* geom) + { + return eval(geom); + } + + /** + * Computes the contains predicate between a {@link PreparedPolygon} + * and a {@link Geometry}. + * + * @param prep the prepared polygon + * @param geom a test geometry + * @return true if the polygon contains the geometry + */ + static bool + contains(const PreparedPolygon* const prep, const geom::Geometry* geom) + { + PreparedPolygonContains polyInt(prep); + return polyInt.contains(geom); + } + +protected: + /** + * Computes the full topological contains predicate. + * Used when short-circuit tests are not conclusive. + * + * @param geom the test geometry + * @return true if this prepared polygon contains the test geometry + */ + bool fullTopologicalPredicate(const geom::Geometry* geom) override; + +}; + +} // geos::geom::prep +} // geos::geom +} // geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonContainsProperly.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonContainsProperly.h new file mode 100644 index 0000000..294b27d --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonContainsProperly.h @@ -0,0 +1,101 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygonContainsProperly.java rev 1.5 (JTS-1.10) + * (2007-12-12) + * + **********************************************************************/ + +#pragma once + +#include // inherited + +namespace geos { +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * Computes the containsProperly spatial relationship predicate + * for {@link PreparedPolygon}s relative to all other {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * A Geometry A containsProperly another Geometry B iff + * all points of B are contained in the Interior of A. + * Equivalently, B is contained in A AND B does not intersect + * the Boundary of A. + * + * The advantage to using this predicate is that it can be computed + * efficiently, with no need to compute topology at individual points. + * In a situation with many geometries intersecting the boundary + * of the target geometry, this can make a performance difference. + * + * @author Martin Davis + */ +class PreparedPolygonContainsProperly : public PreparedPolygonPredicate { +private: +protected: +public: + /** \brief + * Computes the containsProperly predicate between a {@link PreparedPolygon} + * and a {@link Geometry}. + * + * @param prep the prepared polygon + * @param geom a test geometry + * @return true if the polygon properly contains the geometry + */ + static + bool + containsProperly(const PreparedPolygon* const prep, const geom::Geometry* geom) + { + PreparedPolygonContainsProperly polyInt(prep); + return polyInt.containsProperly(geom); + } + + /** \brief + * Creates an instance of this operation. + * + * @param prep the PreparedPolygon to evaluate + */ + PreparedPolygonContainsProperly(const PreparedPolygon* const prep) + : PreparedPolygonPredicate(prep) + { } + + /** \brief + * Tests whether this PreparedPolygon containsProperly a given geometry. + * + * @param geom the test geometry + * @return true if the test geometry is contained properly + */ + bool + containsProperly(const geom::Geometry* geom); + +}; + +} // geos::geom::prep +} // geos::geom +} // geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonCovers.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonCovers.h new file mode 100644 index 0000000..3885705 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonCovers.h @@ -0,0 +1,108 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygonCovers.java rev 1.2 (JTS-1.10) + * (2007-12-12) + * + **********************************************************************/ + +#pragma once + +#include // inherited + +// forward declarations +namespace geos { +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * Computes the covers spatial relationship predicate + * for a {@link PreparedPolygon} relative to all other {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * It is not possible to short-circuit in all cases, in particular + * in the case where the test geometry touches the polygon linework. + * In this case full topology must be computed. + * + * @author Martin Davis + * + */ +class PreparedPolygonCovers : public AbstractPreparedPolygonContains { +private: +protected: + /** + * Computes the full topological covers predicate. + * Used when short-circuit tests are not conclusive. + * + * @param geom the test geometry + * @return true if this prepared polygon covers the test geometry + */ + bool fullTopologicalPredicate(const geom::Geometry* geom) override; + +public: + /** \brief + * Computes the covers predicate between a {@link PreparedPolygon} + * and a {@link Geometry}. + * + * @param prep the prepared polygon + * @param geom a test geometry + * @return true if the polygon covers the geometry + */ + static bool + covers(const PreparedPolygon* const prep, const geom::Geometry* geom) + { + PreparedPolygonCovers polyInt(prep); + return polyInt.covers(geom); + } + + /** \brief + * Creates an instance of this operation. + * + * @param prep the PreparedPolygon to evaluate + */ + PreparedPolygonCovers(const PreparedPolygon* const prep) + : AbstractPreparedPolygonContains(prep, false) + { } + + /** \brief + * Tests whether this PreparedPolygon covers a given geometry. + * + * @param geom the test geometry + * @return true if the test geometry is covered + */ + bool + covers(const Geometry* geom) + { + return eval(geom); + } + +}; + +} // geos::geom::prep +} // geos::geom +} // geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonDistance.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonDistance.h new file mode 100644 index 0000000..c429aa0 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonDistance.h @@ -0,0 +1,67 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { + namespace geom { + class Geometry; + namespace prep { + class PreparedPolygon; + } + } +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +class PreparedPolygon; + +class PreparedPolygonDistance : public PreparedPolygonPredicate { +public: + + static double distance(const PreparedPolygon& prep, const geom::Geometry* geom) + { + PreparedPolygonDistance op(prep); + return op.distance(geom); + } + + PreparedPolygonDistance(const PreparedPolygon& prep) + : PreparedPolygonPredicate(&prep) + { } + + double distance(const geom::Geometry* g) const; + + bool isWithinDistance(const geom::Geometry* g, double d) const; + +protected: + + // Declare type as noncopyable + PreparedPolygonDistance(const PreparedPolygonDistance& other) = delete; + PreparedPolygonDistance& operator=(const PreparedPolygonDistance& rhs) = delete; +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonIntersects.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonIntersects.h new file mode 100644 index 0000000..2f478bf --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonIntersects.h @@ -0,0 +1,90 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygonIntersects.java rev 1.6 (JTS-1.10) + * (2007-12-12) + * + **********************************************************************/ + +#pragma once + +#include // inherited + +namespace geos { +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +} + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * Computes the intersects spatial relationship predicate + * for {@link PreparedPolygon}s relative to all other {@link Geometry} classes. + * + * Uses short-circuit tests and indexing to improve performance. + * + * @author Martin Davis + * + */ +class PreparedPolygonIntersects : public PreparedPolygonPredicate { +private: +protected: +public: + /** \brief + * Computes the intersects predicate between a {@link PreparedPolygon} + * and a {@link Geometry}. + * + * @param prep the prepared polygon + * @param geom a test geometry + * @return true if the polygon intersects the geometry + */ + static bool + intersects(const PreparedPolygon* const prep, const geom::Geometry* geom) + { + PreparedPolygonIntersects polyInt(prep); + return polyInt.intersects(geom); + } + + /** \brief + * Creates an instance of this operation. + * + * @param prep the PreparedPolygon to evaluate + */ + PreparedPolygonIntersects(const PreparedPolygon* const prep) + : PreparedPolygonPredicate(prep) + { } + + /** \brief + * Tests whether this PreparedPolygon intersects a given geometry. + * + * @param geom the test geometry + * @return true if the test geometry intersects + */ + bool intersects(const geom::Geometry* geom); + +}; + +} // geos::geom::prep +} // geos::geom +} // geos + diff --git a/Sources/geos/include/geos/geom/prep/PreparedPolygonPredicate.h b/Sources/geos/include/geos/geom/prep/PreparedPolygonPredicate.h new file mode 100644 index 0000000..3103165 --- /dev/null +++ b/Sources/geos/include/geos/geom/prep/PreparedPolygonPredicate.h @@ -0,0 +1,141 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: geom/prep/PreparedPolygonPredicate.java rev. 1.4 (JTS-1.10) + * (2007-12-12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +// forward declarations +namespace geos { +namespace algorithm { +namespace locate { +class PointOnGeometryLocator; +} +} +namespace geom { +class Geometry; + +namespace prep { +class PreparedPolygon; +} +} +namespace noding { +class FastSegmentSetIntersectionFinder; +} +} + + +namespace geos { +namespace geom { // geos::geom +namespace prep { // geos::geom::prep + +/** + * \brief + * A base class for predicate operations on {@link PreparedPolygon}s. + * + * @author mbdavis + * + */ +class PreparedPolygonPredicate { +private: + // Declare type as noncopyable + PreparedPolygonPredicate(const PreparedPolygonPredicate& other) = delete; + PreparedPolygonPredicate& operator=(const PreparedPolygonPredicate& rhs) = delete; + +protected: + const PreparedPolygon* const prepPoly; + + /** \brief + * Returns the outermost Location among a test point from each + * components of the test geometry. + * + * @param testGeom a geometry to test + * @return the outermost Location + */ + geom::Location getOutermostTestComponentLocation(const geom::Geometry* testGeom) const; + + /** \brief + * Tests whether all components of the test Geometry + * are contained in the interior of the target geometry. + * + * Handles both linear and point components. + * + * @param testGeom a geometry to test + * @return true if all componenta of the argument are contained in + * the target geometry interior + */ + bool isAllTestComponentsInTargetInterior(const geom::Geometry* testGeom) const; + + /** \brief + * Tests whether any component of the test Geometry intersects + * the area of the target geometry. + * + * Handles test geometries with both linear and point components. + * + * @param testGeom a geometry to test + * @return true if any component of the argument intersects the + * prepared geometry + */ + bool isAnyTestComponentInTarget(const geom::Geometry* testGeom) const; + + /** \brief + * Tests whether any component of the test Geometry intersects + * the interior of the target geometry. + * + * Handles test geometries with both linear and point components. + * + * @param testGeom a geometry to test + * @return true if any component of the argument intersects the + * prepared area geometry interior + */ + bool isAnyTestComponentInTargetInterior(const geom::Geometry* testGeom) const; + + /** + * Tests whether any component of the target geometry + * intersects the test geometry (which must be an areal geometry) + * + * @param testGeom the test geometry + * @param targetRepPts the representative points of the target geometry + * @return true if any component intersects the areal test geometry + */ + bool isAnyTargetComponentInAreaTest(const geom::Geometry* testGeom, + const std::vector* targetRepPts) const; + +public: + /** \brief + * Creates an instance of this operation. + * + * @param p_prepPoly the PreparedPolygon to evaluate + */ + PreparedPolygonPredicate(const PreparedPolygon* const p_prepPoly) + : prepPoly(p_prepPoly) + { } + + virtual + ~PreparedPolygonPredicate() + { } + +}; + +} // namespace geos::geom::prep +} // namespace geos::geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/ComponentCoordinateExtracter.h b/Sources/geos/include/geos/geom/util/ComponentCoordinateExtracter.h new file mode 100644 index 0000000..d323271 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/ComponentCoordinateExtracter.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include // to be removed when we have the .inl +#include // to be removed when we have the .inl +#include // to be removed when we have the .inl +#include // to be removed when we have the .inl + +namespace geos { +namespace geom { // geos::geom +namespace util { // geos::geom::util + +/** \brief + * Extracts a single representative {@link Coordinate} + * from each connected component of a {@link Geometry}. + * + * @version 1.9 + */ +class ComponentCoordinateExtracter : public GeometryComponentFilter { +public: + /** + * Push the linear components from a single geometry into + * the provided vector. + * If more than one geometry is to be processed, it is more + * efficient to create a single ComponentCoordinateFilter instance + * and pass it to multiple geometries. + */ + static void getCoordinates(const Geometry& geom, std::vector& ret); + + /** + * Constructs a ComponentCoordinateFilter with a list in which + * to store Coordinates found. + */ + ComponentCoordinateExtracter(std::vector& newComps); + + void filter_rw(Geometry* geom) override; + + void filter_ro(const Geometry* geom) override; + +private: + + std::vector& comps; + + // Declare type as noncopyable + ComponentCoordinateExtracter(const ComponentCoordinateExtracter& other) = delete; + ComponentCoordinateExtracter& operator=(const ComponentCoordinateExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/CoordinateOperation.h b/Sources/geos/include/geos/geom/util/CoordinateOperation.h new file mode 100644 index 0000000..7d87909 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/CoordinateOperation.h @@ -0,0 +1,70 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class CoordinateSequence; +class GeometryFactory; +} +} + + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * A GeometryEditorOperation which modifies the coordinate list of a + * Geometry. + * Operates on Geometry subclasses which contains a single coordinate list. + */ +class GEOS_DLL CoordinateOperation: public GeometryEditorOperation { + +public: + + /** + * Return a newly created geometry, ownership to caller + */ + std::unique_ptr edit(const Geometry* geometry, + const GeometryFactory* factory) override; + + /** + * Edits the array of Coordinate from a Geometry. + * + * @param coordinates the coordinate array to operate on + * @param geometry the geometry containing the coordinate list + * @return an edited coordinate array (which may be the same as + * the input) + */ + virtual std::unique_ptr edit(const CoordinateSequence* coordinates, + const Geometry* geometry) = 0; + + + ~CoordinateOperation() override = default; +}; + + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/Densifier.h b/Sources/geos/include/geos/geom/util/Densifier.h new file mode 100644 index 0000000..2e1c8e6 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/Densifier.h @@ -0,0 +1,90 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2010 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/Polygonizer.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +/* warning C4251: needs to have dll-interface to be used by */ +/* clients of class */ +#pragma warning(disable: 4251) +#endif + + +namespace geos { +namespace geom { +namespace util { + +/** + * Densifies a {@link Geometry} by inserting extra vertices along the line segments + * contained in the geometry. + * All segments in the created densified geometry will be no longer than + * than the given distance tolerance. + * Densified polygonal geometries are guaranteed to be topologically correct. + * The coordinates created during densification respect the input geometry's + * {@link PrecisionModel}. + *

+ * Note: At some future point this class will + * offer a variety of densification strategies. + * + * @author Martin Davis + */ +class GEOS_DLL Densifier { +public: + Densifier(const Geometry* inputGeom); + + static Geometry::Ptr densify(const Geometry* geom, double distanceTolerance); + void setDistanceTolerance(double distanceTolerance); + Geometry::Ptr getResultGeometry() const; + +private: + double distanceTolerance; + const Geometry* inputGeom; + static std::unique_ptr densifyPoints(const CoordinateSequence& pts, double distanceTolerance, + const PrecisionModel* precModel); + + class GEOS_DLL DensifyTransformer: public GeometryTransformer { + public: + DensifyTransformer(double distanceTolerance); + double distanceTolerance; + CoordinateSequence::Ptr transformCoordinates(const CoordinateSequence* coords, const Geometry* parent) override; + Geometry::Ptr transformPolygon(const Polygon* geom, const Geometry* parent) override; + Geometry::Ptr transformMultiPolygon(const MultiPolygon* geom, const Geometry* parent) override; + Geometry::Ptr createValidArea(const Geometry* roughAreaGeom); + }; + +}; // Densifier + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geom/util/GeometryCombiner.h b/Sources/geos/include/geos/geom/util/GeometryCombiner.h new file mode 100644 index 0000000..7b47fb4 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryCombiner.h @@ -0,0 +1,151 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006-2011 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryCombiner.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +} +} + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** \brief + * Combines [Geometrys](@ref Geometry) to produce a GeometryCollection + * of the most appropriate type. + * + * Input geometries which are already collections will have their elements + * extracted first. + * No validation of the result geometry is performed. + * (The only case where invalidity is possible is where polygonal geometries + * are combined and result in a self-intersection). + * + * @see GeometryFactory#buildGeometry + */ +class GEOS_DLL GeometryCombiner { +public: + /** \brief + * Copies a collection of geometries and combines the result. + * + * @param geoms the geometries to combine (ownership left to caller) + * @return the combined geometry + */ + static std::unique_ptr combine(std::vector const& geoms); + + /** \brief + * Combines a collection of geometries. + * + * @param geoms the geometries to combine (ownership transferred to combined geometry) + * @return the combined geometry + */ + static std::unique_ptr combine(std::vector> && geoms); + + /** \brief + * Copies two geometries and combines the result. + * + * @param g0 a geometry to combine (ownership left to caller) + * @param g1 a geometry to combine (ownership left to caller) + * @return the combined geometry + */ + static std::unique_ptr combine(const Geometry* g0, const Geometry* g1); + + /** \brief + * Combines two geometries. + * + * @param g0 a geometry to combine (ownership transferred to combined geometry) + * @param g1 a geometry to combine (ownership transferred to combined geometry) + * @return the combined geometry + */ + static std::unique_ptr combine(std::unique_ptr && g0, + std::unique_ptr && g1); + + /** \brief + * Copies three geometries and combines the result. + * + * @param g0 a geometry to combine (ownership left to caller) + * @param g1 a geometry to combine (ownership left to caller) + * @param g2 a geometry to combine (ownership left to caller) + * @return the combined geometry + */ + static std::unique_ptr combine(const Geometry* g0, const Geometry* g1, const Geometry* g2); + + /** \brief + * Combines three geometries. + * + * @param g0 a geometry to combine (ownership transferred to combined geometry) + * @param g1 a geometry to combine (ownership transferred to combined geometry) + * @param g2 a geometry to combine (ownership transferred to combined geometry) + * @return the combined geometry + */ + static std::unique_ptr combine(std::unique_ptr && g0, + std::unique_ptr && g1, + std::unique_ptr && g2); + +private: + std::vector> inputGeoms; + bool skipEmpty; + +public: + /** \brief + * Creates a new combiner for a collection of geometries. + * + * @param geoms the geometries to combine + */ + explicit GeometryCombiner(std::vector const& geoms); + + explicit GeometryCombiner(std::vector> && geoms); + + /** \brief + * Extracts the GeometryFactory used by the geometries in a collection. + * + * @return a GeometryFactory + */ + GeometryFactory const* extractFactory() const; + + /** \brief + * Computes the combination of the input geometries + * to produce the most appropriate Geometry or GeometryCollection. + * + * @return a Geometry which is the combination of the inputs + */ + std::unique_ptr combine(); + + /** \brief + * Set a flag indicating that empty geometries should be omitted from the result. + */ + void setSkipEmpty(bool); + + // Declare type as noncopyable + GeometryCombiner(const GeometryCombiner& other) = delete; + GeometryCombiner& operator=(const GeometryCombiner& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryEditor.h b/Sources/geos/include/geos/geom/util/GeometryEditor.h new file mode 100644 index 0000000..294aad3 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryEditor.h @@ -0,0 +1,127 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryEditor.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class GeometryCollection; +class Polygon; +namespace util { +class GeometryEditorOperation; +} +} +} + + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Supports creating a new Geometry which is a modification of an existing one. + * Geometry objects are intended to be treated as immutable. + * This class allows you to "modify" a Geometry + * by traversing it and creating a new Geometry with the same overall + * structure but possibly modified components. + * + * The following kinds of modifications can be made: + * + * - the values of the coordinates may be changed. + * Changing coordinate values may make the result Geometry invalid; + * this is not checked by the GeometryEditor + * - the coordinate lists may be changed + * (e.g. by adding or deleting coordinates). + * The modified coordinate lists must be consistent with their original + * parent component + * (e.g. a LinearRing must always have at least 4 coordinates, and the + * first and last coordinate must be equal) + * - components of the original geometry may be deleted + * (e.g. holes may be removed from a Polygon, or LineStrings removed + * from a MultiLineString). Deletions will be propagated up the component + * tree appropriately. + * + * Note that all changes must be consistent with the original Geometry's + * structure + * (e.g. a Polygon cannot be collapsed into a LineString). + * + * The resulting Geometry is not checked for validity. + * If validity needs to be enforced, the new Geometry's isValid should + * be checked. + * + * @see Geometry::isValid + * + */ +class GEOS_DLL GeometryEditor { +private: + /** + * The factory used to create the modified Geometry + */ + const GeometryFactory* factory; + + std::unique_ptr editPolygon(const Polygon* polygon, + GeometryEditorOperation* operation); + + std::unique_ptr editGeometryCollection( + const GeometryCollection* collection, + GeometryEditorOperation* operation); + +public: + + /** + * Creates a new GeometryEditor object which will create + * an edited Geometry with the same GeometryFactory as the + * input Geometry. + */ + GeometryEditor(); + + /** + * Creates a new GeometryEditor object which will create + * the edited Geometry with the given GeometryFactory + * + * @param newFactory the GeometryFactory to create the edited + * Geometry with + */ + GeometryEditor(const GeometryFactory* newFactory); + + /** + * Edit the input Geometry with the given edit operation. + * Clients will create subclasses of GeometryEditorOperation or + * CoordinateOperation to perform required modifications. + * + * @param geometry the Geometry to edit + * @param operation the edit operation to carry out + * @return a new Geometry which is the result of the editing + * + */ + std::unique_ptr edit(const Geometry* geometry, + GeometryEditorOperation* operation); // final +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryEditorOperation.h b/Sources/geos/include/geos/geom/util/GeometryEditorOperation.h new file mode 100644 index 0000000..37ae84f --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryEditorOperation.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +} +} + + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + + +/** + * A interface which specifies an edit operation for Geometries. + */ +class GEOS_DLL GeometryEditorOperation { + +public: + /** + * Edits a Geometry by returning a new Geometry with a modification. + * The returned Geometry might be the same as the Geometry passed in. + * + * @param geometry the Geometry to modify + * + * @param factory the factory with which to construct the modified + * Geometry (may be different to the factory of the + * input geometry) + * + * @return a new Geometry which is a modification of the input Geometry + */ + virtual std::unique_ptr edit(const Geometry* geometry, + const GeometryFactory* factory) = 0; + + virtual + ~GeometryEditorOperation() {} +}; + + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryExtracter.h b/Sources/geos/include/geos/geom/util/GeometryExtracter.h new file mode 100644 index 0000000..f1bed3b --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryExtracter.h @@ -0,0 +1,95 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryExtracter.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts the components of a given type from a {@link Geometry}. + */ +class GEOS_DLL GeometryExtracter { + +public: + + /** + * Extracts the components of type clz from a {@link Geometry} + * and adds them to the provided container. + * + * @param geom the geometry from which to extract + * @param lst the list to add the extracted elements to + */ + template + static void + extract(const Geometry& geom, TargetContainer& lst) + { + if(const ComponentType* p_c = dynamic_cast(&geom)) { + lst.push_back(p_c); + } + else if(const GeometryCollection* p_c1 = + dynamic_cast(&geom)) { + GeometryExtracter::Extracter extracter(lst); + p_c1->apply_ro(&extracter); + } + } + +private: + + template + struct Extracter: public GeometryFilter { + + /** + * Constructs a filter with a list in which to store the elements found. + * + * @param comps the container to extract into (will push_back to it) + */ + Extracter(TargetContainer& comps) : comps_(comps) {} + + TargetContainer& comps_; + + void + filter_ro(const Geometry* geom) override + { + if(const ComponentType* c = dynamic_cast(geom)) { + comps_.push_back(c); + } + } + + // Declare type as noncopyable + Extracter(const Extracter& other); + Extracter& operator=(const Extracter& rhs); + }; + + // Declare type as noncopyable + GeometryExtracter(const GeometryExtracter& other) = delete; + GeometryExtracter& operator=(const GeometryExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryFixer.h b/Sources/geos/include/geos/geom/util/GeometryFixer.h new file mode 100644 index 0000000..7896632 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryFixer.h @@ -0,0 +1,169 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Point; +class Polygon; +class LineString; +class LinearRing; +class MultiPoint; +class MultiPolygon; +class MultiLineString; +class GeometryCollection; +} +} + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Fixes a geometry to be a valid geometry, while preserving as much as + * possible of the shape and location of the input. + * Validity is determined according to {@link Geometry#isValid()}. + * + * Input geometries are always processed, so even valid inputs may + * have some minor alterations. The output is always a new geometry object. + * + *

Semantic Rules

+ * + * * Vertices with non-finite X or Y ordinates are removed (as per Coordinate::isValid() ). + * * Repeated points are reduced to a single point + * * Empty atomic geometries are valid and are returned unchanged + * * Empty elements are removed from collections + * * Point: keep valid coordinate, or EMPTY + * * LineString: coordinates are fixed + * * LinearRing: coordinates are fixed, Keep valid ring or else convert into LineString + * * Polygon: transform into a valid polygon, + * * preserving as much of the extent and vertices as possible. + * * Rings are fixed to ensure they are valid< + * * Holes intersecting the shell are subtracted from the shell + * * Holes outside the shell are converted into polygons + * * MultiPolygon: each polygon is fixed, + * then result made non-overlapping (via union) + * * GeometryCollection: each element is fixed + * * Collapsed lines and polygons are handled as follows, + * depending on the keepCollapsed setting: + * * false: (default) collapses are converted to empty geometries + * * true: collapses are converted to a valid geometry of lower dimension + * + * @author Martin Davis +*/ +class GEOS_DLL GeometryFixer { + +private: + + const geom::Geometry* geom; + const geom::GeometryFactory* factory; + bool isKeepCollapsed; // false + +public: + + GeometryFixer(const geom::Geometry* p_geom) + : geom(p_geom) + , factory(p_geom->getFactory()) + , isKeepCollapsed(false) + {}; + + static std::unique_ptr fix(const geom::Geometry* geom); + + /** + * Sets whether collapsed geometries are converted to empty, + * (which will be removed from collections), + * or to a valid geom::Geometry of lower dimension. + * The default is to convert collapses to empty geometries. + * + * @param p_isKeepCollapsed whether collapses should be converted to a lower dimension geometry + */ + void setKeepCollapsed(bool p_isKeepCollapsed); + + /** + * Gets the fixed geometry. + * + * @return the fixed geometry + */ + std::unique_ptr getResult() const; + +private: + + std::unique_ptr fixPoint(const geom::Point* geom) const; + std::unique_ptr fixPointElement(const geom::Point* geom) const; + bool isValidPoint(const geom::Point* pt) const; + std::unique_ptr fixMultiPoint(const geom::MultiPoint* geom) const; + std::unique_ptr fixLinearRing(const geom::LinearRing* geom) const; + std::unique_ptr fixLinearRingElement(const geom::LinearRing* geom) const; + std::unique_ptr fixLineString(const geom::LineString* geom) const; + std::unique_ptr fixLineStringElement(const geom::LineString* geom) const; + + /** + * Returns a clean copy of the input coordinate array. + * + * @param pts coordinates to clean + * @return an array of clean coordinates + */ + std::unique_ptr fixMultiLineString(const MultiLineString* geom) const; + std::unique_ptr fixPolygon(const geom::Polygon* geom) const; + std::unique_ptr fixPolygonElement(const geom::Polygon* geom) const; + std::vector> fixHoles(const geom::Polygon* geom) const; + std::unique_ptr removeHoles(const geom::Geometry* shell, const geom::Geometry* holes) const; + std::unique_ptr fixRing(const geom::LinearRing* ring) const; + std::unique_ptr fixMultiPolygon(const geom::MultiPolygon* geom) const; + std::unique_ptr fixCollection(const geom::GeometryCollection* geom) const; + + void classifyHoles( + const Geometry* shell, + std::vector>& holesFixed, + std::vector& holes, + std::vector& shells) const; + + /** + * Subtracts a list of polygonal geometries from a polygonal geometry. + * + * @param shell polygonal geometry for shell + * @param holes polygonal geometries to subtract + * @return the result geometry + */ + std::unique_ptr difference( + const Geometry* shell, + std::vector& holes) const; + + /** + * Unions a list of polygonal geometries. + * Optimizes case of zero or one input geometries. + * Requires that the inputs are net new objects. + * + * @param polys the polygonal geometries to union + * @return the union of the inputs + */ + std::unique_ptr unionGeometry( + std::vector& polys) const; + +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + + diff --git a/Sources/geos/include/geos/geom/util/GeometryLister.h b/Sources/geos/include/geos/geom/util/GeometryLister.h new file mode 100644 index 0000000..912518e --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryLister.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryExtracter.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts all the components of a collection, or just echoes back a + * pointers to singletons. + */ +class GEOS_DLL GeometryLister { + +public: + + /** + * Extracts the components from a {@link Geometry} + * and adds them to the provided container. + * + * Useful for iterating over components of a collection. + * + * @param geom the geometry from which to extract + * @param lst the list to add the extracted elements to + */ + static void + list(const Geometry* geom, std::vector& lst) + { + if(geom->isCollection()) { + GeometryLister::Lister lister(lst); + geom->apply_ro(&lister); + } + else { + lst.push_back(geom); + } + } + +private: + + struct Lister : public GeometryFilter { + + /** + * Constructs a filter with a list in which to store the elements found. + * + * @param comps the container to extract into (will push_back to it) + */ + Lister(std::vector& p_geoms) : geoms(p_geoms) {} + + std::vector& geoms; + + void + filter_ro(const Geometry* geom) override + { + if(!geom->isCollection()) { + geoms.push_back(geom); + } + } + + // // Declare type as noncopyable + // Lister(const Lister& other); + // Lister& operator=(const Lister& rhs); + }; +}; + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryMapper.h b/Sources/geos/include/geos/geom/util/GeometryMapper.h new file mode 100644 index 0000000..121b6d5 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryMapper.h @@ -0,0 +1,115 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryMapper.java + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryCollection; +class GeometryFactory; +} +} + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + + +/** + * Methods to map various collections + * of {@link Geometry}s + * via defined mapping functions. + * + * @author Martin Davis + * + */ +class GEOS_DLL GeometryMapper { + +public: + + /** + * An interface for geometry functions that map a geometry input to a geometry output. + * The output may be nullptr if there is no valid output value for + * the given input value. + */ + typedef std::function(const Geometry&)> mapOp; + + /** + * Maps the members of a {@link Geometry} + * (which may be atomic or composite) + * into another Geometry of most specific type. + * null results are skipped. + * In the case of hierarchical {@link GeometryCollection}s, + * only the first level of members are mapped. + * + * @param geom the input atomic or composite geometry + * @param op the mapping operation + * @return a result collection or geometry of most specific type + */ + static std::unique_ptr map( + const Geometry& geom, + mapOp op); + + /** + * Maps the atomic elements of a {@link Geometry} + * (which may be atomic or composite) + * using a mapOp mapping operation + * into an atomic Geometry or a flat collection + * of the most specific type. + * null and empty values returned from the mapping operation + * are discarded. + * + * @param geom the geometry to map + * @param emptyDim the dimension of empty geometry to create + * @param op the mapping operation + * @return the mapped result + */ + static std::unique_ptr flatMap( + const Geometry& geom, + int emptyDim, + mapOp op); + + + +private: + + static void flatMap( + const Geometry& geom, + mapOp op, + std::vector>& mapped); + + static void addFlat( + std::unique_ptr& geom, + std::vector>& geomList); + + + +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/GeometryTransformer.h b/Sources/geos/include/geos/geom/util/GeometryTransformer.h new file mode 100644 index 0000000..7cbe0c3 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/GeometryTransformer.h @@ -0,0 +1,181 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geom/util/GeometryTransformer.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + + +#include +#include // destructor visibility for vector +#include // destructor visibility for unique_ptr +#include // destructor visibility for unique_ptr + +#include // for unique_ptr +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class Point; +class LinearRing; +class LineString; +class Polygon; +class MultiPoint; +class MultiPolygon; +class MultiLineString; +class GeometryCollection; +namespace util { +//class GeometryEditorOperation; +} +} +} + + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** \brief + * A framework for processes which transform an input Geometry into + * an output Geometry, possibly changing its structure and type(s). + * + * This class is a framework for implementing subclasses which perform + * transformations on various different Geometry subclasses. + * It provides an easy way of applying specific transformations + * to given geometry types, while allowing unhandled types to be simply copied. + * Also, the framework ensures that if subcomponents change type + * the parent geometries types change appropriately to maintain valid structure. + * Subclasses will override whichever `transformX` methods + * they need to to handle particular Geometry types. + * + * A typically usage would be a transformation that may transform Polygons into + * Polygons, LineStrings or Points. This class would likely need to override the + * `GeometryTransformer::transformMultiPolygon(const MultiPolygon* geom, + * const Geometry* parent)` method to ensure that if input Polygons change type + * the result is a GeometryCollection, not a MultiPolygon + * + * The default behaviour of this class is to simply recursively transform + * each Geometry component into an identical object by copying. + * + * Note that all `transformX` methods may return `null`, to avoid creating + * empty geometry objects. This will be handled correctly by the transformer. + * The `GeometryTransformer::transform(const Geometry* nInputGeom)` method + * itself will always return a geometry object. + * + * @see GeometryEditor + * + * Possible extensions: + * getParent() method to return immediate parent e.g. of LinearRings in Polygons + * + */ +class GEOS_DLL GeometryTransformer { + +public: + + GeometryTransformer(); + + virtual ~GeometryTransformer() = default; + + std::unique_ptr transform(const Geometry* nInputGeom); + + void setSkipTransformedInvalidInteriorRings(bool b); + +protected: + + const GeometryFactory* factory; + + virtual CoordinateSequence::Ptr transformCoordinates( + const CoordinateSequence* coords, + const Geometry* parent); + + virtual Geometry::Ptr transformPoint( + const Point* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformMultiPoint( + const MultiPoint* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformLinearRing( + const LinearRing* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformLineString( + const LineString* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformMultiLineString( + const MultiLineString* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformPolygon( + const Polygon* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformMultiPolygon( + const MultiPolygon* geom, + const Geometry* parent); + + virtual Geometry::Ptr transformGeometryCollection( + const GeometryCollection* geom, + const Geometry* parent); + +private: + + const Geometry* inputGeom; + + // these could eventually be exposed to clients + /** + * `true` if empty geometries should not be included in the result + */ + bool pruneEmptyGeometry; + + /** + * `true` if a homogeneous collection result + * from a {@link GeometryCollection} should still + * be a general GeometryCollection + */ + bool preserveGeometryCollectionType; + + /** + * `true` if the output from a collection argument should still be a collection + */ + // bool preserveCollections; + + /** + * `true` if the type of the input should be preserved + */ + bool preserveType; + + /** + * `true` if transformed invalid interior rings should be skipped + */ + bool skipTransformedInvalidInteriorRings; + + // Declare type as noncopyable + GeometryTransformer(const GeometryTransformer& other) = delete; + GeometryTransformer& operator=(const GeometryTransformer& rhs) = delete; +}; + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/LinearComponentExtracter.h b/Sources/geos/include/geos/geom/util/LinearComponentExtracter.h new file mode 100644 index 0000000..0efcbb7 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/LinearComponentExtracter.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include + +#include +#include // to be removed when we have the .inl +#include // to be removed when we have the .inl + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts all the 1-dimensional (LineString) components from a Geometry. + */ +class GEOS_DLL LinearComponentExtracter: public GeometryComponentFilter { + +private: + + LineString::ConstVect& comps; + + // Declare type as noncopyable + LinearComponentExtracter(const LinearComponentExtracter& other) = delete; + LinearComponentExtracter& operator=(const LinearComponentExtracter& rhs) = delete; + +public: + /** + * Push the linear components from a single geometry into + * the provided vector. + * If more than one geometry is to be processed, it is more + * efficient to create a single LinearComponentExtracterFilter instance + * and pass it to multiple geometries. + */ + static void getLines(const Geometry& geom, std::vector& ret); + /** + * Constructs a LinearComponentExtracterFilter with a list in which + * to store LineStrings found. + */ + LinearComponentExtracter(std::vector& newComps); + + void filter_ro(const Geometry* geom) override; + +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/NoOpGeometryOperation.h b/Sources/geos/include/geos/geom/util/NoOpGeometryOperation.h new file mode 100644 index 0000000..dd1561e --- /dev/null +++ b/Sources/geos/include/geos/geom/util/NoOpGeometryOperation.h @@ -0,0 +1,52 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class CoordinateSequence; +class GeometryFactory; +} +} + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + + +class GEOS_DLL NoOpGeometryOperation: public GeometryEditorOperation { + +public: + + /** + * Return a newly created geometry, ownership to caller + */ + std::unique_ptr edit(const Geometry* geometry, + const GeometryFactory* factory) override; + + ~NoOpGeometryOperation() override = default; +}; + + + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/PointExtracter.h b/Sources/geos/include/geos/geom/util/PointExtracter.h new file mode 100644 index 0000000..f5e487d --- /dev/null +++ b/Sources/geos/include/geos/geom/util/PointExtracter.h @@ -0,0 +1,63 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts all the 0-dimensional (Point) components from a Geometry. + */ +class GEOS_DLL PointExtracter: public GeometryFilter { + +public: + /** + * Returns the Point components from a single geometry. + * If more than one geometry is to be processed, it is more + * efficient to create a single PointExtracter filter instance + * and pass it to multiple geometries. + */ + static void getPoints(const Geometry& geom, Point::ConstVect& ret); + + /** + * Constructs a PointExtracterFilter with a list in which + * to store Points found. + */ + PointExtracter(Point::ConstVect& newComps); + + void filter_rw(Geometry* geom) override; + + void filter_ro(const Geometry* geom) override; + +private: + + Point::ConstVect& comps; + + // Declare type as noncopyable + PointExtracter(const PointExtracter& other) = delete; + PointExtracter& operator=(const PointExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/PolygonExtracter.h b/Sources/geos/include/geos/geom/util/PolygonExtracter.h new file mode 100644 index 0000000..b5b7fb6 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/PolygonExtracter.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * Extracts all the 2-dimensional (Polygon) components from a Geometry. + */ +class GEOS_DLL PolygonExtracter: public GeometryFilter { + +public: + + /** + * Pushes the Polygon components from a single geometry into + * the provided vector. + * If more than one geometry is to be processed, it is more + * efficient to create a single PolygonExtracterFilter instance + * and pass it to multiple geometries. + */ + static void getPolygons(const Geometry& geom, std::vector& ret); + + /** + * Constructs a PolygonExtracterFilter with a list in which + * to store Polygons found. + */ + PolygonExtracter(std::vector& newComps); + + void filter_rw(Geometry* geom) override; + + void filter_ro(const Geometry* geom) override; + +private: + + /// Reference to provided vector + std::vector& comps; + + // Declare type as noncopyable + PolygonExtracter(const PolygonExtracter& other) = delete; + PolygonExtracter& operator=(const PolygonExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/PolygonalExtracter.h b/Sources/geos/include/geos/geom/util/PolygonalExtracter.h new file mode 100644 index 0000000..9dfba65 --- /dev/null +++ b/Sources/geos/include/geos/geom/util/PolygonalExtracter.h @@ -0,0 +1,54 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** + * \brief Extracts the polygonal (Polygon and MultiPolygon) elements from a Geometry. + */ +class GEOS_DLL PolygonalExtracter { + +public: + + /** + * Pushes the polygonal (Polygon and MultiPolygon) elements from a geometry into + * the provided vector. + * + * @param geom the geometry to extract from + * @param polys the vector to add the polygonal elements to + */ + static void getPolygonals(const Geometry& geom, std::vector& polys); + +private: + + static void getPolygonals(const Geometry* geom, std::vector& polys); + + // Declare type as noncopyable + PolygonalExtracter(const PolygonalExtracter& other) = delete; + PolygonalExtracter& operator=(const PolygonalExtracter& rhs) = delete; +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/ShortCircuitedGeometryVisitor.h b/Sources/geos/include/geos/geom/util/ShortCircuitedGeometryVisitor.h new file mode 100644 index 0000000..bd2d31c --- /dev/null +++ b/Sources/geos/include/geos/geom/util/ShortCircuitedGeometryVisitor.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + + +namespace geos { +namespace geom { // geos.geom +namespace util { // geos.geom.util + +/** \brief + * A visitor to Geometry elements which can + * be short-circuited by a given condition + * + * Last port: geom/util/ShortCircuitedGeometryVisitor.java rev. 1.1 (JTS-1.7) + */ +class GEOS_DLL ShortCircuitedGeometryVisitor { + +private: + + bool done; + +protected: + + virtual void visit(const Geometry& element) = 0; + virtual bool isDone() = 0; + +public: + + ShortCircuitedGeometryVisitor() + : + done(false) + {} + + void applyTo(const Geometry& geom); + + virtual + ~ShortCircuitedGeometryVisitor() {} + +}; + +} // namespace geos.geom.util +} // namespace geos.geom +} // namespace geos + diff --git a/Sources/geos/include/geos/geom/util/SineStarFactory.h b/Sources/geos/include/geos/geom/util/SineStarFactory.h new file mode 100644 index 0000000..5700c1f --- /dev/null +++ b/Sources/geos/include/geos/geom/util/SineStarFactory.h @@ -0,0 +1,122 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + +#include // for inheritance + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Polygon; +class GeometryFactory; +class PrecisionModel; +class LineString; +} +} + +namespace geos { +namespace geom { // geos::geom +namespace util { // geos::geom::util + +/** + * Creates geometries which are shaped like multi-armed stars + * with each arm shaped like a sine wave. + * These kinds of geometries are useful as a more complex + * geometry for testing algorithms. + * + * @author Martin Davis + * + */ +class GEOS_DLL SineStarFactory : public geos::util::GeometricShapeFactory { + +protected: + + int numArms; + double armLengthRatio; + +public: + + /** + * Creates a factory which will create sine stars using the given + * {@link GeometryFactory}. + * + * @param fact the factory to use. You need to keep the + * factory alive for the whole SineStarFactory + * life time. + */ + SineStarFactory(const geom::GeometryFactory* fact) + : + geos::util::GeometricShapeFactory(fact), + numArms(8), + armLengthRatio(0.5) + {} + + /** + * Sets the number of arms in the star + * + * @param nArms the number of arms to generate + */ + void + setNumArms(int nArms) + { + numArms = nArms; + } + + /** + * Sets the ration of the length of each arm to the distance from the tip + * of the arm to the centre of the star. + * Value should be between 0.0 and 1.0 + * + * @param armLenRatio + */ + void + setArmLengthRatio(double armLenRatio) + { + armLengthRatio = armLenRatio; + } + + /** + * Generates the geometry for the sine star + * + * @return the geometry representing the sine star + */ + std::unique_ptr createSineStar() const; + + +}; + +} // namespace geos::geom::util +} // namespace geos::geom +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/Depth.h b/Sources/geos/include/geos/geomgraph/Depth.h new file mode 100644 index 0000000..6a533b7 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/Depth.h @@ -0,0 +1,143 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/Depth.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geomgraph { +class Label; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/// \brief A Depth object records the topological depth of the sides of an Edge +/// for up to two Geometries. +class GEOS_DLL Depth { + +private: + + static constexpr int NULL_VALUE = -1; // Replaces NULL + + int depth[2][3]; + + +public: + + static int depthAtLocation(geom::Location location) + { + if(location == geom::Location::EXTERIOR) { + return 0; + } + if(location == geom::Location::INTERIOR) { + return 1; + } + return NULL_VALUE; + }; + + + Depth() + { + // initialize depth array to a sentinel value + for(std::size_t i = 0; i < 2; i++) { + for(std::size_t j = 0; j < 3; j++) { + depth[i][j] = NULL_VALUE; + } + } + }; + + virtual ~Depth() = default; // FIXME: shouldn't be virtual! + + int getDepth(int geomIndex, int posIndex) const + { + return depth[geomIndex][posIndex]; + }; + + void setDepth(int geomIndex, int posIndex, int depthValue) + { + depth[geomIndex][posIndex] = depthValue; + }; + + geom::Location getLocation(int geomIndex, int posIndex) const + { + if(depth[geomIndex][posIndex] <= 0) { + return geom::Location::EXTERIOR; + } + return geom::Location::INTERIOR; + }; + + void add(int geomIndex, int posIndex, geom::Location location) + { + if(location == geom::Location::INTERIOR) { + depth[geomIndex][posIndex]++; + } + }; + + /** + * A Depth object is null (has never been initialized) if all depths are null. + */ + bool isNull() const + { + for(std::size_t i = 0; i < 2; i++) { + for(std::size_t j = 0; j < 3; j++) { + if(depth[i][j] != NULL_VALUE) { + return false; + } + } + } + return true; + }; + + bool isNull(uint8_t geomIndex) const + { + return depth[geomIndex][1] == NULL_VALUE; + }; + + bool isNull(uint8_t geomIndex, uint8_t posIndex) const + { + return depth[geomIndex][posIndex] == NULL_VALUE; + }; + + int getDelta(int geomIndex) const + { + return depth[geomIndex][geom::Position::RIGHT] - depth[geomIndex][geom::Position::LEFT]; + }; + + void normalize(); + + void add(const Label& lbl); + + std::string toString() const; + + +}; + + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/DirectedEdge.h b/Sources/geos/include/geos/geomgraph/DirectedEdge.h new file mode 100644 index 0000000..ab88f62 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/DirectedEdge.h @@ -0,0 +1,236 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/DirectedEdge.java r428 (JTS-1.12) + * + **********************************************************************/ + + +#pragma once + +#include +#include + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geomgraph { +class Edge; +class EdgeRing; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/// A directed EdgeEnd +class GEOS_DLL DirectedEdge final: public EdgeEnd { + +public: + + /** \brief + * Computes the factor for the change in depth when moving from + * one location to another. + * E.g. if crossing from the INTERIOR to the EXTERIOR the depth + * decreases, so the factor is -1 + */ + static int depthFactor(geom::Location currLocation, geom::Location nextLocation); + + //DirectedEdge(); + //virtual ~DirectedEdge(); + + DirectedEdge(Edge* newEdge, bool newIsForward); + + // this is no different from Base class, no need to override + //Edge* getEdge(); + + void setInResult(bool v) + { + isInResultVar = v; + }; + + bool isInResult() const + { + return isInResultVar; + }; + + bool isVisited() const + { + return isVisitedVar; + }; + + + void setVisited(bool v) + { + isVisitedVar = v; + }; + + void setEdgeRing(EdgeRing* er) + { + edgeRing = er; + }; + + EdgeRing* getEdgeRing() const + { + return edgeRing; + }; + + void setMinEdgeRing(EdgeRing* mer) + { + minEdgeRing = mer; + }; + + EdgeRing* getMinEdgeRing() const + { + return minEdgeRing; + }; + + int getDepth(int position) const + { + return depth[position]; + }; + + void setDepth(int position, int newDepth); + + int getDepthDelta() const; + + /// Marks both DirectedEdges attached to a given Edge. + /// + /// This is used for edges corresponding to lines, which will only + /// appear oriented in a single direction in the result. + /// + void setVisitedEdge(bool newIsVisited); + + /** \brief + * Each Edge gives rise to a pair of symmetric DirectedEdges, + * in opposite directions. + * + * @return the DirectedEdge for the same Edge but in the + * opposite direction + */ + DirectedEdge* getSym() const + { + return sym; + }; + + bool isForward() const + { + return isForwardVar; + }; + + void setSym(DirectedEdge* de) + { + sym = de; + }; + + DirectedEdge* getNext() const + { + return next; + }; + + void setNext(DirectedEdge* newNext) + { + next = newNext; + }; + + DirectedEdge* getNextMin() const + { + return nextMin; + }; + + void setNextMin(DirectedEdge* nm) + { + nextMin = nm; + }; + + /** \brief + * Tells whether this edge is a Line + * + * This edge is a line edge if + * - at least one of the labels is a line label + * - any labels which are not line labels have all Locations = EXTERIOR + * + */ + bool isLineEdge(); + + /** \brief + * Tells whether this edge is an Area + * + * This is an interior Area edge if + * - its label is an Area label for both Geometries + * - and for each Geometry both sides are in the interior. + * + * @return true if this is an interior Area edge + */ + bool isInteriorAreaEdge(); + + /** \brief + * Set both edge depths. + * + * One depth for a given side is provided. + * The other is computed depending on the Location transition and the + * depthDelta of the edge. + */ + void setEdgeDepths(int position, int newDepth); + + std::string print() const override; + + std::string printEdge(); + + +protected: + + bool isForwardVar; + + +private: + + bool isInResultVar; + + bool isVisitedVar; + + /// the symmetric edge + DirectedEdge* sym; + + /// the next edge in the edge ring for the polygon containing this edge + DirectedEdge* next; + + /// the next edge in the MinimalEdgeRing that contains this edge + DirectedEdge* nextMin; + + /// the EdgeRing that this edge is part of + EdgeRing* edgeRing; + + /// the MinimalEdgeRing that this edge is part of + EdgeRing* minEdgeRing; + + /** \brief + * The depth of each side (position) of this edge. + * The 0 element of the array is never used. + */ + int depth[3]; + + /// Compute the label in the appropriate orientation for this DirEdge + void computeDirectedLabel(); + + +}; + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/DirectedEdgeStar.h b/Sources/geos/include/geos/geomgraph/DirectedEdgeStar.h new file mode 100644 index 0000000..4400daa --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/DirectedEdgeStar.h @@ -0,0 +1,164 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/DirectedEdgeStar.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +#include // for inheritance +#include // for private member +#include // for p0,p1 + + +// Forward declarations +namespace geos { +namespace geomgraph { +class DirectedEdge; +class EdgeRing; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** + * \brief + * A DirectedEdgeStar is an ordered list of **outgoing** DirectedEdges around a node. + * + * It supports labelling the edges as well as linking the edges to form both + * MaximalEdgeRings and MinimalEdgeRings. + * + */ +class GEOS_DLL DirectedEdgeStar: public EdgeEndStar { + +public: + + DirectedEdgeStar() + : + EdgeEndStar(), + label(), + resultAreaEdgesComputed(false) + {} + + ~DirectedEdgeStar() override = default; + + /// Insert a directed edge in the list + void insert(EdgeEnd* ee) override; + + Label& + getLabel() + { + return label; + } + + int getOutgoingDegree(); + + int getOutgoingDegree(EdgeRing* er); + + DirectedEdge* getRightmostEdge(); + + /** \brief + * Compute the labelling for all dirEdges in this star, as well as the overall labelling + */ + void computeLabelling(const std::vector>&geom) override; // throw(TopologyException *); + + /** \brief + * For each dirEdge in the star, merge the label from the sym dirEdge into the label + */ + void mergeSymLabels(); + + /// \brief Update incomplete dirEdge labels from the labelling for the node + void updateLabelling(const Label& nodeLabel); + + + /** \brief + * Traverse the star of DirectedEdges, linking the included edges together. + * + * To link two dirEdges, the `next` pointer for an incoming dirEdge + * is set to the next outgoing edge. + * + * DirEdges are only linked if: + * + * - they belong to an area (i.e. they have sides) + * - they are marked as being in the result + * + * Edges are linked in CCW order (the order they are stored). This means + * that rings have their face on the Right (in other words, the topological + * location of the face is given by the RHS label of the DirectedEdge) + * + * PRECONDITION: No pair of dirEdges are both marked as being in the result + */ + void linkResultDirectedEdges(); // throw(TopologyException *); + + void linkMinimalDirectedEdges(EdgeRing* er); + + void linkAllDirectedEdges(); + + /** \brief + * Traverse the star of edges, maintaining the current location in the result + * area at this node (if any). + * + * If any L edges are found in the interior of the result, mark them as covered. + */ + void findCoveredLineEdges(); + + /** \brief + * Compute the DirectedEdge depths for a subsequence of the edge array. + */ + void computeDepths(DirectedEdge* de); + + std::string print() const override; + +private: + + /** + * A list of all outgoing edges in the result, in CCW order + */ + std::vector resultAreaEdgeList; + + Label label; + + bool resultAreaEdgesComputed; + + /// \brief + /// Returned vector is owned by DirectedEdgeStar object, but + /// lazily created + const std::vector& getResultAreaEdges(); + + + /// States for linResultDirectedEdges + enum { + SCANNING_FOR_INCOMING = 1, + LINKING_TO_OUTGOING + }; + + int computeDepths(EdgeEndStar::iterator startIt, + EdgeEndStar::iterator endIt, int startDepth); +}; + + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/Edge.h b/Sources/geos/include/geos/geomgraph/Edge.h new file mode 100644 index 0000000..52a9f57 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/Edge.h @@ -0,0 +1,285 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/Edge.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include + +#include // for inheritance +#include // for member +#include // for composition +#include // for inlines +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +class Coordinate; +} +namespace algorithm { +class LineIntersector; +} +namespace geomgraph { +class Node; +class EdgeEndStar; +class Label; +class NodeFactory; +namespace index { +class MonotoneChainEdge; +} +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** The edge component of a geometry graph */ +class GEOS_DLL Edge final: public GraphComponent { + using GraphComponent::updateIM; + +private: + + /// Lazily-created, owned by Edge. + std::unique_ptr mce; + + geom::Envelope env; + + Depth depth; + + int depthDelta; // the change in area depth from the R to L side of this edge + + bool isIsolatedVar; + +public: + + void + testInvariant() const + { + assert(pts); + assert(pts->size() > 1); + } + + friend std::ostream& operator<< (std::ostream& os, const Edge& el); + + static void updateIM(const Label& lbl, geom::IntersectionMatrix& im); + + /// Externally-set, owned by Edge. FIXME: refuse ownership + std::unique_ptr pts; + + EdgeIntersectionList eiList; + + /// Takes ownership of CoordinateSequence + Edge(geom::CoordinateSequence* newPts, const Label& newLabel); + + /// Takes ownership of CoordinateSequence + Edge(geom::CoordinateSequence* newPts); + + ~Edge() override; + + size_t + getNumPoints() const + { + return pts->getSize(); + } + + const geom::CoordinateSequence* + getCoordinates() const + { + testInvariant(); + return pts.get(); + } + + const geom::Coordinate& + getCoordinate(std::size_t i) const + { + testInvariant(); + return pts->getAt(i); + } + + const geom::Coordinate& + getCoordinate() const + { + testInvariant(); + return pts->getAt(0); + } + + + const Depth& + getDepth() const + { + testInvariant(); + return depth; + } + + /** \brief + * The depthDelta is the change in depth as an edge is crossed from R to L + * + * @return the change in depth as the edge is crossed from R to L + */ + int + getDepthDelta() const + { + testInvariant(); + return depthDelta; + } + + void + setDepthDelta(int newDepthDelta) + { + depthDelta = newDepthDelta; + testInvariant(); + } + + size_t + getMaximumSegmentIndex() const + { + testInvariant(); + return getNumPoints() - 1; + } + + EdgeIntersectionList& + getEdgeIntersectionList() + { + testInvariant(); + return eiList; + } + + const EdgeIntersectionList& + getEdgeIntersectionList() const + { + testInvariant(); + return eiList; + } + + /// \brief + /// Return this Edge's index::MonotoneChainEdge, + /// ownership is retained by this object. + /// + index::MonotoneChainEdge* getMonotoneChainEdge(); + + bool + isClosed() const + { + testInvariant(); + return pts->getAt(0) == pts->getAt(getNumPoints() - 1); + } + + /** \brief + * An Edge is collapsed if it is an Area edge and it consists of + * two segments which are equal and opposite (eg a zero-width V). + */ + bool isCollapsed() const; + + Edge* getCollapsedEdge(); + + void + setIsolated(bool newIsIsolated) + { + isIsolatedVar = newIsIsolated; + testInvariant(); + } + + bool + isIsolated() const override + { + testInvariant(); + return isIsolatedVar; + } + + /** \brief + * Adds EdgeIntersections for one or both + * intersections found for a segment of an edge to the edge intersection list. + */ + void addIntersections(algorithm::LineIntersector* li, std::size_t segmentIndex, + std::size_t geomIndex); + + /// Add an EdgeIntersection for intersection intIndex. + // + /// An intersection that falls exactly on a vertex of the edge is normalized + /// to use the higher of the two possible segmentIndexes + /// + void addIntersection(algorithm::LineIntersector* li, std::size_t segmentIndex, + std::size_t geomIndex, std::size_t intIndex); + + /// Update the IM with the contribution for this component. + // + /// A component only contributes if it has a labelling for both + /// parent geometries + /// + void + computeIM(geom::IntersectionMatrix& im) override + { + updateIM(label, im); + testInvariant(); + } + + /// return true if the coordinate sequences of the Edges are identical + bool isPointwiseEqual(const Edge* e) const; + + std::string print() const; + + std::string printReverse() const; + + /** + * equals is defined to be: + * + * e1 equals e2 + * iff + * the coordinates of e1 are the same or the reverse of the coordinates in e2 + */ + bool equals(const Edge& e) const; + + bool + equals(const Edge* e) const + { + assert(e); + return equals(*e); + } + + const geom::Envelope* getEnvelope(); +}; + + +//Operators +inline bool +operator==(const Edge& a, const Edge& b) +{ + return a.equals(b); +} + +std::ostream& operator<< (std::ostream& os, const Edge& el); + + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/EdgeEnd.h b/Sources/geos/include/geos/geomgraph/EdgeEnd.h new file mode 100644 index 0000000..86ac11a --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeEnd.h @@ -0,0 +1,187 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeEnd.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include // for p0,p1 +#include // for composition + +#include + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geomgraph { +class Edge; +class Node; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * Models the end of an edge incident on a node. + * + * EdgeEnds have a direction + * determined by the direction of the ray from the initial + * point to the next point. + * EdgeEnds are comparable under the ordering + * "a has a greater angle with the x-axis than b". + * This ordering is used to sort EdgeEnds around a node. + */ +class GEOS_DLL EdgeEnd /* non-final */ { + +public: + + friend std::ostream& operator<< (std::ostream&, const EdgeEnd&); + + EdgeEnd(); + + virtual + ~EdgeEnd() {} + + /** + * NOTES: + * - Copies the given Label + * - keeps a pointer to given Edge, make sure it's + * not deleted before the EdgeEnd. + * - copies given Coordinates (maybe we should avoid that) + */ + EdgeEnd(Edge* newEdge, const geom::Coordinate& newP0, + const geom::Coordinate& newP1, + const Label& newLabel); + + /** + * NOTES: + * - keeps a pointer to given Edge, make sure it's + * not deleted before the EdgeEnd. + * - copies given Coordinates (maybe we should avoid that) + */ + EdgeEnd(Edge* newEdge, const geom::Coordinate& newP0, + const geom::Coordinate& newP1); + + Edge* + getEdge() + { + return edge; + } + //virtual Edge* getEdge() { return edge; } + + Label& + getLabel() + { + return label; + } + + const Label& + getLabel() const + { + return label; + } + + geom::Coordinate& getCoordinate() { + return p0; + } + + const geom::Coordinate& + getCoordinate() const + { + return p0; + } + + geom::Coordinate& getDirectedCoordinate(); + + int getQuadrant(); + + double getDx(); + + double getDy(); + + void setNode(Node* newNode); + + Node* getNode(); + + int compareTo(const EdgeEnd* e) const; + + /** + * Implements the total order relation: + * + * a has a greater angle with the positive x-axis than b + * + * Using the obvious algorithm of simply computing the angle + * is not robust, since the angle calculation is obviously + * susceptible to roundoff. + * A robust algorithm is: + * - first compare the quadrant. If the quadrants + * are different, it it trivial to determine which vector + * is "greater". + * - if the vectors lie in the same quadrant, the + * computeOrientation function can be used to decide + * the relative orientation of the vectors. + */ + int compareDirection(const EdgeEnd* e) const; + + virtual void computeLabel(const algorithm::BoundaryNodeRule& bnr); + + virtual std::string print() const; + +protected: + + Edge* edge;// the parent edge of this edge end + + Label label; + + EdgeEnd(Edge* newEdge); + + void init(const geom::Coordinate& newP0, + const geom::Coordinate& newP1); + +private: + + /// the node this edge end originates at + Node* node; + + /// points of initial line segment. FIXME: do we need a copy here ? + geom::Coordinate p0, p1; + + /// the direction vector for this edge from its starting point + double dx, dy; + + int quadrant; +}; + +std::ostream& operator<< (std::ostream&, const EdgeEnd&); + +struct GEOS_DLL EdgeEndLT { + bool + operator()(const EdgeEnd* s1, const EdgeEnd* s2) const + { + return s1->compareTo(s2) < 0; + } +}; + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/EdgeEndStar.h b/Sources/geos/include/geos/geomgraph/EdgeEndStar.h new file mode 100644 index 0000000..b249b60 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeEndStar.h @@ -0,0 +1,217 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeEndStar.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include // for EdgeEndLT +#include +#include // for p0,p1 + +#include +#include +#include +#include +#include +#include // for inlines (find) + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geomgraph { +class GeometryGraph; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + + +/** \brief + * A EdgeEndStar is an ordered list of EdgeEnds around a node. + * + * They are maintained in CCW order (starting with the positive x-axis) + * around the node for efficient lookup and topology building. + * + * @version 1.4 + */ +class GEOS_DLL EdgeEndStar /* non-final */ { +public: + + typedef std::set container; + + typedef container::iterator iterator; + typedef container::const_iterator const_iterator; + typedef container::reverse_iterator reverse_iterator; + + EdgeEndStar(); + + virtual + ~EdgeEndStar() {} + + /** \brief + * Insert a EdgeEnd into this EdgeEndStar + */ + virtual void insert(EdgeEnd* e) = 0; + + /** + * @return the coordinate for the node this star is based at + * or NULL if this is still an unbound star. + * Be aware that the returned pointer will point to + * a Coordinate owned by the specific EdgeEnd happening + * to be the first in the star (ordered CCW) + */ + geom::Coordinate& getCoordinate(); + + const geom::Coordinate& getCoordinate() const; + + std::size_t getDegree(); + + iterator begin(); + + iterator end(); + + reverse_iterator rbegin(); + + reverse_iterator rend(); + + const_iterator + begin() const + { + return edgeMap.begin(); + } + + const_iterator + end() const + { + return edgeMap.end(); + } + + container& getEdges(); + + EdgeEnd* getNextCW(EdgeEnd* ee); + + virtual void computeLabelling(const std::vector>&geomGraph); + // throw(TopologyException *); + + bool isAreaLabelsConsistent(const GeometryGraph& geomGraph); + + void propagateSideLabels(uint32_t geomIndex); + // throw(TopologyException *); + + //virtual int findIndex(EdgeEnd *eSearch); + iterator find(EdgeEnd* eSearch); + + virtual std::string print() const; + +protected: + + /** \brief + * A map which maintains the edges in sorted order + * around the node + */ + EdgeEndStar::container edgeMap; + + /** \brief + * Insert an EdgeEnd into the map. + */ + void + insertEdgeEnd(EdgeEnd* e) + { + edgeMap.insert(e); + } + +private: + + geom::Location getLocation(uint32_t geomIndex, + const geom::Coordinate&p, + const std::vector>&geom); + + /** \brief + * The location of the point for this star in + * Geometry i Areas + */ + std::array ptInAreaLocation; + + void computeEdgeEndLabels(const algorithm::BoundaryNodeRule&); + + bool checkAreaLabelsConsistent(uint32_t geomIndex); + +}; + +inline std::size_t +EdgeEndStar::getDegree() +{ + return edgeMap.size(); +} + +inline EdgeEndStar::iterator +EdgeEndStar::begin() +{ + return edgeMap.begin(); +} + +inline EdgeEndStar::container& +EdgeEndStar::getEdges() +{ + return edgeMap; +} + +inline EdgeEndStar::reverse_iterator +EdgeEndStar::rend() +{ + return edgeMap.rend(); +} + +inline EdgeEndStar::iterator +EdgeEndStar::end() +{ + return edgeMap.end(); +} + +inline EdgeEndStar::reverse_iterator +EdgeEndStar::rbegin() +{ + return edgeMap.rbegin(); +} + +inline EdgeEndStar::iterator +EdgeEndStar::find(EdgeEnd* eSearch) +{ + return edgeMap.find(eSearch); +} + +std::ostream& operator<< (std::ostream&, const EdgeEndStar&); + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/EdgeIntersection.h b/Sources/geos/include/geos/geomgraph/EdgeIntersection.h new file mode 100644 index 0000000..6a65131 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeIntersection.h @@ -0,0 +1,149 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeIntersection.java rev. 1.5 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include + +#include // for composition and inlines + +#include + + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * Represents a point on an edge which intersects with another edge. + * + * The intersection may either be a single point, or a line segment + * (in which case this point is the start of the line segment) + * The intersection point must be precise. + * + */ +class GEOS_DLL EdgeIntersection final { +public: + + // the point of intersection + geom::Coordinate coord; + + // the edge distance of this point along the containing line segment + double dist; + + // the index of the containing line segment in the parent edge + std::size_t segmentIndex; + + EdgeIntersection(const geom::Coordinate& newCoord, + std::size_t newSegmentIndex, double newDist) + : + coord(newCoord), + dist(newDist), + segmentIndex(newSegmentIndex) + {} + + bool + isEndPoint(std::size_t maxSegmentIndex) const + { + if(segmentIndex == 0 && dist == 0.0) { + return true; + } + if(segmentIndex == maxSegmentIndex) { + return true; + } + return false; + } + + const geom::Coordinate& + getCoordinate() const + { + return coord; + } + + size_t + getSegmentIndex() const + { + return segmentIndex; + } + + double + getDistance() const + { + return dist; + } + + bool operator==(const EdgeIntersection& other) const { + return segmentIndex == other.segmentIndex && + dist == other.dist; + + // We don't check the coordinate, consistent with operator< + } + +}; + +/// Strict weak ordering operator for EdgeIntersection +/// +/// This is the C++ equivalent of JTS's compareTo +inline bool +operator< (const EdgeIntersection& ei1, const EdgeIntersection& ei2) +{ + if(ei1.segmentIndex < ei2.segmentIndex) { + return true; + } + if(ei1.segmentIndex == ei2.segmentIndex) { + if(ei1.dist < ei2.dist) { + return true; + } + + // TODO: check if the Coordinate matches, or this will + // be a robustness issue in computing distance + // See http://trac.osgeo.org/geos/ticket/350 + } + return false; +} + +// @deprecated, use strict weak ordering operator +struct GEOS_DLL EdgeIntersectionLessThen { + bool + operator()(const EdgeIntersection* ei1, + const EdgeIntersection* ei2) const + { + return *ei1 < *ei2; + } + + bool + operator()(const EdgeIntersection& ei1, + const EdgeIntersection& ei2) const + { + return ei1 < ei2; + } +}; + +/// Output operator +inline std::ostream& +operator<< (std::ostream& os, const EdgeIntersection& e) +{ + os << e.coord << " seg # = " << e.segmentIndex << " dist = " << e.dist; + return os; +} + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/EdgeIntersectionList.h b/Sources/geos/include/geos/geomgraph/EdgeIntersectionList.h new file mode 100644 index 0000000..7c8bdba --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeIntersectionList.h @@ -0,0 +1,129 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeIntersectionList.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include + +#include // for EdgeIntersectionLessThen +#include // for CoordinateLessThan + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + + +/** \brief + * A list of edge intersections along an Edge. + * + * Implements splitting an edge with intersections + * into multiple resultant edges. + */ +class GEOS_DLL EdgeIntersectionList final { +public: + // Instead of storing edge intersections in a set, as JTS does, we store them + // in a vector and then sort the vector if needed before iterating among the + // edges. This is much faster. + using container = std::vector; + using const_iterator = container::const_iterator; + +private: + mutable container nodeMap; + mutable bool sorted; + +public: + + const Edge* edge; + EdgeIntersectionList(const Edge* edge); + ~EdgeIntersectionList() = default; + + /* + * Adds an intersection into the list, if it isn't already there. + * The input segmentIndex and dist are expected to be normalized. + * @return the EdgeIntersection found or added + */ + void add(const geom::Coordinate& coord, std::size_t segmentIndex, double dist); + + const_iterator + begin() const + { + if (!sorted) { + std::sort(nodeMap.begin(), nodeMap.end()); + nodeMap.erase(std::unique(nodeMap.begin(), nodeMap.end()), nodeMap.end()); + sorted = true; + } + + return nodeMap.begin(); + } + const_iterator + end() const + { + return nodeMap.end(); + } + + bool isEmpty() const; + bool isIntersection(const geom::Coordinate& pt) const; + + /* + * Adds entries for the first and last points of the edge to the list + */ + void addEndpoints(); + + /** + * Creates new edges for all the edges that the intersections in this + * list split the parent edge into. + * Adds the edges to the input list (this is so a single list + * can be used to accumulate all split edges for a Geometry). + * + * @param edgeList a list of EdgeIntersections + */ + void addSplitEdges(std::vector* edgeList); + + Edge* createSplitEdge(const EdgeIntersection* ei0, const EdgeIntersection* ei1); + std::string print() const; +}; + +std::ostream& operator<< (std::ostream&, const EdgeIntersectionList&); + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/EdgeList.h b/Sources/geos/include/geos/geomgraph/EdgeList.h new file mode 100644 index 0000000..56fd225 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeList.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeList.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +#include // for map comparator + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace index { +class SpatialIndex; +} +namespace geomgraph { +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * A EdgeList is a list of Edges. + * + * It supports locating edges + * that are pointwise equals to a target edge. + */ +class GEOS_DLL EdgeList final { + +private: + + std::vector edges; + + struct OcaCmp { + bool + operator()( + const noding::OrientedCoordinateArray* oca1, + const noding::OrientedCoordinateArray* oca2) const + { + return *oca1 < *oca2; + } + }; + + /** + * An index of the edges, for fast lookup. + */ + typedef std::unordered_map EdgeMap; + EdgeMap ocaMap; + +public: + friend std::ostream& operator<< (std::ostream& os, const EdgeList& el); + + EdgeList() + : + edges(), + ocaMap() + {} + + ~EdgeList() = default; + + /** + * Insert an edge unless it is already in the list + */ + void add(Edge* e); + + void addAll(const std::vector& edgeColl); + + std::vector& + getEdges() + { + return edges; + } + + Edge* findEqualEdge(const Edge* e) const; + + Edge* get(std::size_t i); + + int findEdgeIndex(const Edge* e) const; + + std::string print(); + + void clearList(); + +}; + +std::ostream& operator<< (std::ostream& os, const EdgeList& el); + + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/EdgeNodingValidator.h b/Sources/geos/include/geos/geomgraph/EdgeNodingValidator.h new file mode 100644 index 0000000..6f844d1 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeNodingValidator.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeNodingValidator.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include + +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace noding { +class SegmentString; +} +namespace geomgraph { +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * Validates that a collection of SegmentStrings is correctly noded. + * + * Throws an appropriate exception if an noding error is found. + */ +class GEOS_DLL EdgeNodingValidator final { + +private: + std::vector& toSegmentStrings(std::vector& edges); + + // Make sure this member is initialized *before* + // the NodingValidator, as initialization of + // NodingValidator will use toSegmentString(), that + // in turn expects this member to be initialized + std::vector segStr; + + // Make sure this member is initialized *before* + // the NodingValidator, as initialization of + // NodingValidator will use toSegmentString(), that + // in turn expects this member to be initialized + std::vector newCoordSeq; + + noding::FastNodingValidator nv; + +public: + + /** \brief + * Checks whether the supplied {@link Edge}s are correctly noded. + * + * Throws a [TopologyException](@ref util::TopologyException) if they are not. + * + * @param edges a collection of Edges. + * @throws TopologyException if the SegmentStrings are not + * correctly noded + */ + static void + checkValid(std::vector& edges) + { + EdgeNodingValidator validator(edges); + validator.checkValid(); + } + + EdgeNodingValidator(std::vector& edges) + : + segStr(), + newCoordSeq(), + nv(toSegmentStrings(edges)) + {} + + ~EdgeNodingValidator(); + + void + checkValid() + { + nv.checkValid(); + } +}; + + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geomgraph/EdgeRing.h b/Sources/geos/include/geos/geomgraph/EdgeRing.h new file mode 100644 index 0000000..257453c --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/EdgeRing.h @@ -0,0 +1,201 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/EdgeRing.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include // for composition +#include +#include + +#include // for testInvariant +#include // for operator<< +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Polygon; +class Coordinate; +} +namespace geomgraph { +class DirectedEdge; +//class Label; +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** EdgeRing */ +class GEOS_DLL EdgeRing /* non-final */ { + +public: + friend std::ostream& operator<< (std::ostream& os, const EdgeRing& er); + + EdgeRing(DirectedEdge* newStart, + const geom::GeometryFactory* newGeometryFactory); + + virtual ~EdgeRing() = default; + + bool isIsolated(); + + bool isHole(); + + /** + * Return a pointer to the LinearRing owned by + * this object. Make a copy if you need it beyond + * this objects's lifetime. + */ + geom::LinearRing* getLinearRing(); + + Label& getLabel(); + + bool isShell(); + + EdgeRing* getShell(); + + void setShell(EdgeRing* newShell); + + void addHole(EdgeRing* edgeRing); + + /** + * Return a Polygon copying coordinates from this + * EdgeRing and its holes. + */ + std::unique_ptr toPolygon(const geom::GeometryFactory* geometryFactory); + + /** + * Compute a LinearRing from the point list previously collected. + * Test if the ring is a hole (i.e. if it is CCW) and set the hole + * flag accordingly. + */ + void computeRing(); + + virtual DirectedEdge* getNext(DirectedEdge* de) = 0; + + virtual void setEdgeRing(DirectedEdge* de, EdgeRing* er) = 0; + + /** + * Returns the list of DirectedEdges that make up this EdgeRing + */ + std::vector& getEdges(); + + int getMaxNodeDegree(); + + void setInResult(); + + /** + * This method will use the computed ring. + * It will also check any holes, if they have been assigned. + */ + bool containsPoint(const geom::Coordinate& p); + + void + testInvariant() const + { + // pts are never NULL + // assert(pts); + +#ifndef NDEBUG + // If this is not an hole, check that + // each hole is not null and + // has 'this' as it's shell + if(! shell) { + for(const auto& hole : holes) { + assert(hole); + assert(hole->getShell() == this); + } + } +#endif // ndef NDEBUG + } + +protected: + + DirectedEdge* startDe; // the directed edge which starts the list of edges for this EdgeRing + + const geom::GeometryFactory* geometryFactory; + + /// @throws util::TopologyException + void computePoints(DirectedEdge* newStart); + + void mergeLabel(const Label& deLabel); + + /** \brief + * Merge the RHS label from a DirectedEdge into the label for + * this EdgeRing. + * + * The DirectedEdge label may be null. + * This is acceptable - it results from a node which is NOT + * an intersection node between the Geometries + * (e.g. the end node of a LinearRing). + * In this case the DirectedEdge label does not contribute any + * information to the overall labelling, and is + * simply skipped. + */ + void mergeLabel(const Label& deLabel, uint8_t geomIndex); + + void addPoints(Edge* edge, bool isForward, bool isFirstEdge); + + /// a list of EdgeRings which are holes in this EdgeRing + std::vector> holes; + +private: + + int maxNodeDegree; + + /// the DirectedEdges making up this EdgeRing + std::vector edges; + + geom::CoordinateSequence pts; + + // label stores the locations of each geometry on the + // face surrounded by this ring + Label label; + + std::unique_ptr ring; // the ring created for this EdgeRing + + bool isHoleVar; + + /// if non-null, the ring is a hole and this EdgeRing is its containing shell + EdgeRing* shell; + + void computeMaxNodeDegree(); + +}; + +std::ostream& operator<< (std::ostream& os, const EdgeRing& er); + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/GeometryGraph.h b/Sources/geos/include/geos/geomgraph/GeometryGraph.h new file mode 100644 index 0000000..78976cd --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/GeometryGraph.h @@ -0,0 +1,251 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/GeometryGraph.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include // for unique_ptr +#include +#include +#include // for LineStringLT + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class LinearRing; +class Polygon; +class Geometry; +class GeometryCollection; +class Point; +class Envelope; +} +namespace algorithm { +class LineIntersector; +class BoundaryNodeRule; +} +namespace geomgraph { +class Edge; +class Node; +namespace index { +class EdgeSetIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * A GeometryGraph is a graph that models a given Geometry. + */ +class GEOS_DLL GeometryGraph final: public PlanarGraph { + using PlanarGraph::add; + using PlanarGraph::findEdge; + +private: + + const geom::Geometry* parentGeom; + + /** + * The lineEdgeMap is a map of the linestring components of the + * parentGeometry to the edges which are derived from them. + * This is used to efficiently perform findEdge queries + * + * Following the above description there's no need to + * compare LineStrings other then by pointer value. + */ + std::unordered_map lineEdgeMap; + + /** + * If this flag is true, the Boundary Determination Rule will + * used when deciding whether nodes are in the boundary or not + */ + bool useBoundaryDeterminationRule; + + const algorithm::BoundaryNodeRule& boundaryNodeRule; + + /** + * the index of this geometry as an argument to a spatial function + * (used for labelling) + */ + uint8_t argIndex; + + /// Cache for fast responses to getBoundaryPoints + std::unique_ptr< geom::CoordinateSequence > boundaryPoints; + + std::unique_ptr< std::vector > boundaryNodes; + + bool hasTooFewPointsVar; + + geom::Coordinate invalidPoint; + + /// Allocates a new EdgeSetIntersector. Remember to delete it! + index::EdgeSetIntersector* createEdgeSetIntersector(); + + void add(const geom::Geometry* g); + // throw(UnsupportedOperationException); + + void addCollection(const geom::GeometryCollection* gc); + + void addPoint(const geom::Point* p); + + void addPolygonRing(const geom::LinearRing* lr, + geom::Location cwLeft, geom::Location cwRight); + + void addPolygon(const geom::Polygon* p); + + void addLineString(const geom::LineString* line); + + void insertPoint(uint8_t p_argIndex, const geom::Coordinate& coord, + geom::Location onLocation); + + /** \brief + * Adds candidate boundary points using the current + * algorithm::BoundaryNodeRule. + * + * This is used to add the boundary + * points of dim-1 geometries (Curves/MultiCurves). + */ + void insertBoundaryPoint(uint8_t p_argIndex, const geom::Coordinate& coord); + + void addSelfIntersectionNodes(uint8_t p_argIndex); + + /** \brief + * Add a node for a self-intersection. + * + * If the node is a potential boundary node (e.g. came from an edge + * which is a boundary) then insert it as a potential boundary node. + * Otherwise, just add it as a regular node. + */ + void addSelfIntersectionNode(uint8_t p_argIndex, + const geom::Coordinate& coord, geom::Location loc); + + // Declare type as noncopyable + GeometryGraph(const GeometryGraph& other) = delete; + GeometryGraph& operator=(const GeometryGraph& rhs) = delete; + +public: + + static bool isInBoundary(int boundaryCount); + + static geom::Location determineBoundary(int boundaryCount); + + static geom::Location determineBoundary( + const algorithm::BoundaryNodeRule& boundaryNodeRule, + int boundaryCount); + + GeometryGraph(uint8_t newArgIndex, const geom::Geometry* newParentGeom); + + GeometryGraph(uint8_t newArgIndex, const geom::Geometry* newParentGeom, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + ~GeometryGraph() override {}; + + const geom::Geometry* getGeometry() + { + return parentGeom; + }; + + /// Returned object is owned by this GeometryGraph + void getBoundaryNodes(std::vector& bdyNodes) + { + nodes->getBoundaryNodes(static_cast(argIndex), bdyNodes); + }; + + std::vector* getBoundaryNodes(); + + /// Returned object is owned by this GeometryGraph + geom::CoordinateSequence* getBoundaryPoints(); + + Edge* findEdge(const geom::LineString* line) const; + + void computeSplitEdges(std::vector* edgelist); + + void addEdge(Edge* e); + + void addPoint(geom::Coordinate& pt); + + /** + * \brief + * Compute self-nodes, taking advantage of the Geometry type to minimize + * the number of intersection tests. (E.g. rings are not tested for + * self-intersection, since they are assumed to be valid). + * + * @param li the LineIntersector to use + * @param computeRingSelfNodes if `false`, intersection checks are optimized + * to not test rings for self-intersection + * @param env an Envelope + * + * @return the SegmentIntersector used, containing information about + * the intersections found + */ + std::unique_ptr + computeSelfNodes( + algorithm::LineIntersector* li, + bool computeRingSelfNodes, + const geom::Envelope* env = nullptr) + { + return computeSelfNodes(*li, computeRingSelfNodes, env); + } + + // Quick inline calling the function above, the above should probably + // be deprecated. + std::unique_ptr computeSelfNodes( + algorithm::LineIntersector& li, + bool computeRingSelfNodes, const geom::Envelope* env = nullptr); + + std::unique_ptr computeEdgeIntersections(GeometryGraph* g, + algorithm::LineIntersector* li, bool includeProper, + const geom::Envelope* env = nullptr); + + std::vector* getEdges(); + + bool hasTooFewPoints(); + + const geom::Coordinate& getInvalidPoint(); + + const algorithm::BoundaryNodeRule& + getBoundaryNodeRule() const + { + return boundaryNodeRule; + } + +}; + + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geomgraph/GraphComponent.h b/Sources/geos/include/geos/geomgraph/GraphComponent.h new file mode 100644 index 0000000..2ad8058 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/GraphComponent.h @@ -0,0 +1,117 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/GraphComponent.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + + +/** \brief + * A GraphComponent is the parent class for the objects' + * that form a graph. + * + * Each GraphComponent can carry a Label. + */ +class GEOS_DLL GraphComponent /* non-final */ { +public: + GraphComponent(); + + /* + * GraphComponent copies the given Label. + */ + GraphComponent(const Label& newLabel); + + virtual ~GraphComponent() = default; + + Label& + getLabel() + { + return label; + } + const Label& + getLabel() const + { + return label; + } + void + setLabel(const Label& newLabel) + { + label = newLabel; + } + + void + setInResult(bool p_isInResult) + { + isInResultVar = p_isInResult; + } + bool + isInResult() const + { + return isInResultVar; + } + void setCovered(bool isCovered); + bool + isCovered() const + { + return isCoveredVar; + } + bool + isCoveredSet() const + { + return isCoveredSetVar; + } + bool + isVisited() const + { + return isVisitedVar; + } + void + setVisited(bool p_isVisited) + { + isVisitedVar = p_isVisited; + } + virtual bool isIsolated() const = 0; + void updateIM(geom::IntersectionMatrix& im); +protected: + Label label; + virtual void computeIM(geom::IntersectionMatrix& im) = 0; +private: + bool isInResultVar; + bool isCoveredVar; + bool isCoveredSetVar; + bool isVisitedVar; +}; + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/Label.h b/Sources/geos/include/geos/geomgraph/Label.h new file mode 100644 index 0000000..b0966db --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/Label.h @@ -0,0 +1,291 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/Label.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include + +#include // for operator<< +#include + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * A Label indicates the topological relationship of a component + * of a topology graph to a given Geometry. + * + * This class supports labels for relationships to two Geometrys, + * which is sufficient for algorithms for binary operations. + * + * Topology graphs support the concept of labeling nodes and edges in the graph. + * The label of a node or edge specifies its topological relationship to one or + * more geometries. (In fact, since JTS operations have only two arguments labels + * are required for only two geometries). A label for a node or edge has one or + * two elements, depending on whether the node or edge occurs in one or both of the + * input Geometrys. Elements contain attributes which categorize the + * topological location of the node or edge relative to the parent + * Geometry; that is, whether the node or edge is in the interior, + * boundary or exterior of the Geometry. Attributes have a value + * from the set {Interior, Boundary, Exterior}. In a node each + * element has a single attribute <On>. For an edge each element has a + * triplet of attributes <Left, On, Right>. + * + * It is up to the client code to associate the 0 and 1 TopologyLocations + * with specific geometries. + * + */ +class GEOS_DLL Label final { + using Position = geos::geom::Position; + +public: + + friend std::ostream& operator<< (std::ostream&, const Label&); + + /** \brief + * Converts a Label to a Line label + * (that is, one with no side Locations) + * + */ + static Label toLineLabel(const Label& label) + { + Label lineLabel(geom::Location::NONE); + for(uint32_t i = 0; i < 2; i++) { + lineLabel.setLocation(i, label.getLocation(i)); + } + return lineLabel; + }; + + /** \brief + * Construct a Label with a single location for both Geometries. + */ + Label(geom::Location onLoc) + : elt{TopologyLocation(onLoc) + , TopologyLocation(onLoc)} + {}; + + /** \brief + * Construct a Label with the location specified + * for the given Geometry. + * + * Other geometry location will be set to + * Location::NONE. + */ + Label(uint32_t geomIndex, geom::Location onLoc) + : elt{TopologyLocation(geom::Location::NONE) + , TopologyLocation(geom::Location::NONE)} + { + assert(geomIndex < 2); + elt[geomIndex].setLocation(onLoc); + }; + + /** \brief + * Construct a Label with On, Left and Right locations for both Geometries. + * + * Initialize the locations for both Geometries to the given values. + */ + Label(geom::Location onLoc, geom::Location leftLoc, geom::Location rightLoc) + : elt {TopologyLocation(onLoc, leftLoc, rightLoc) + , TopologyLocation(onLoc, leftLoc, rightLoc)} + {}; + + /// Copy ctor + Label(const Label& l) + : elt{TopologyLocation(l.elt[0]) + , TopologyLocation(l.elt[1])} + {}; + + /** \brief + * Initialize both locations to Location::NONE + * + * isNull() should return true after this kind of construction + */ + Label() + : elt{TopologyLocation(geom::Location::NONE) + , TopologyLocation(geom::Location::NONE)} + {}; + + /** \brief + * Construct a Label with On, Left and Right locations for the + * given Geometries. + * Initialize the locations for the other Geometry to + * Location::NONE + */ + Label(uint32_t geomIndex, geom::Location onLoc, geom::Location leftLoc, geom::Location rightLoc) + { + elt[0] = TopologyLocation(geom::Location::NONE, geom::Location::NONE, geom::Location::NONE); + elt[1] = TopologyLocation(geom::Location::NONE, geom::Location::NONE, geom::Location::NONE); + elt[geomIndex].setLocations(onLoc, leftLoc, rightLoc); + }; + + Label& + operator=(const Label& l) + { + elt[0] = TopologyLocation(l.elt[0]); + elt[1] = TopologyLocation(l.elt[1]); + return *this; + }; + + void flip() + { + elt[0].flip(); + elt[1].flip(); + }; + + /** \brief + * Merge this label with another one. + * + * Merging updates any null attributes of this label with the attributes + * from lbl + */ + void merge(const Label& lbl) + { + for(int i = 0; i < 2; i++) { + elt[i].merge(lbl.elt[i]); + } + }; + + int getGeometryCount() const + { + int count = 0; + if(!elt[0].isNull()) { + count++; + } + if(!elt[1].isNull()) { + count++; + } + return count; + }; + + geom::Location getLocation(uint32_t geomIndex, uint32_t posIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].get(posIndex); + }; + + geom::Location getLocation(uint32_t geomIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].get(Position::ON); + }; + + void setLocation(uint32_t geomIndex, uint32_t posIndex, geom::Location location) + { + assert(geomIndex < 2); + elt[geomIndex].setLocation(posIndex, location); + }; + + void setLocation(uint32_t geomIndex, geom::Location location) + { + assert(geomIndex < 2); + elt[geomIndex].setLocation(Position::ON, location); + }; + + void setAllLocations(uint32_t geomIndex, geom::Location location) + { + assert(geomIndex < 2); + elt[geomIndex].setAllLocations(location); + }; + + void setAllLocationsIfNull(uint32_t geomIndex, geom::Location location) + { + assert(geomIndex < 2); + elt[geomIndex].setAllLocationsIfNull(location); + }; + + void setAllLocationsIfNull(geom::Location location) + { + setAllLocationsIfNull(0, location); + setAllLocationsIfNull(1, location); + }; + + bool isNull(uint32_t geomIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].isNull(); + }; + + bool isNull() const + { + return elt[0].isNull() && elt[1].isNull(); + }; + + bool isAnyNull(uint32_t geomIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].isAnyNull(); + }; + + bool isArea() const + { + return elt[0].isArea() || elt[1].isArea(); + }; + + bool isArea(uint32_t geomIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].isArea(); + }; + + bool isLine(uint32_t geomIndex) const + { + assert(geomIndex < 2); + return elt[geomIndex].isLine(); + }; + + bool isEqualOnSide(const Label& lbl, uint32_t side) const + { + return elt[0].isEqualOnSide(lbl.elt[0], side) + && elt[1].isEqualOnSide(lbl.elt[1], side); + }; + + bool allPositionsEqual(uint32_t geomIndex, geom::Location loc) const + { + assert(geomIndex < 2); + return elt[geomIndex].allPositionsEqual(loc); + }; + + /** \brief + * Converts one GeometryLocation to a Line location + */ + void toLine(uint32_t geomIndex) + { + assert(geomIndex < 2); + if(elt[geomIndex].isArea()) { + elt[geomIndex] = TopologyLocation(elt[geomIndex].getLocations()[0]); + } + }; + + std::string toString() const; + +private: + + TopologyLocation elt[2]; + +}; + +std::ostream& operator<< (std::ostream&, const Label&); + +} // namespace geos.geomgraph +} // namespace geos + + diff --git a/Sources/geos/include/geos/geomgraph/Node.h b/Sources/geos/include/geos/geomgraph/Node.h new file mode 100644 index 0000000..3f63f64 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/Node.h @@ -0,0 +1,192 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/Node.java r411 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include // for inheritance +#include // for member + +#ifndef NDEBUG +#include // for testInvariant +#include // for testInvariant +#endif // ndef NDEBUG + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +} +namespace geomgraph { +class Node; +class EdgeEndStar; +class EdgeEnd; +class Label; +class NodeFactory; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief The node component of a geometry graph. */ +class GEOS_DLL Node /* non-final */: public GraphComponent { + using GraphComponent::setLabel; + +public: + + friend std::ostream& operator<< (std::ostream& os, const Node& node); + + Node(const geom::Coordinate& newCoord, EdgeEndStar* newEdges); + + ~Node() override; + + const geom::Coordinate& getCoordinate() const; + + EdgeEndStar* getEdges(); + + bool isIsolated() const override; + + /** \brief + * Add the edge to the list of edges at this node + */ + void add(EdgeEnd* e); + + void mergeLabel(const Node& n); + + /** \brief + * To merge labels for two nodes, + * the merged location for each LabelElement is computed. + * + * The location for the corresponding node LabelElement is set + * to the result, as long as the location is non-null. + */ + void mergeLabel(const Label& label2); + + void setLabel(uint8_t argIndex, geom::Location onLocation); + + /** \brief + * Updates the label of a node to BOUNDARY, + * obeying the mod-2 boundaryDetermination rule. + */ + void setLabelBoundary(uint8_t argIndex); + + /** + * The location for a given eltIndex for a node will be one + * of { null, INTERIOR, BOUNDARY }. + * A node may be on both the boundary and the interior of a geometry; + * in this case, the rule is that the node is considered to be + * in the boundary. + * The merged location is the maximum of the two input values. + */ + geom::Location computeMergedLocation(const Label& label2, uint8_t eltIndex); + + std::string print() const; + + const std::vector& getZ() const; + + void addZ(double); + + /** \brief + * Tests whether any incident edge is flagged as + * being in the result. + * + * This test can be used to determine if the node is in the result, + * since if any incident edge is in the result, the node must be in + * the result as well. + * + * @return true if any indicident edge in the in + * the result + */ + bool isIncidentEdgeInResult() const; + +protected: + + void testInvariant() const; + + geom::Coordinate coord; + + EdgeEndStar* edges; + + /** \brief + * Basic nodes do not compute IMs + */ + void + computeIM(geom::IntersectionMatrix& /*im*/) override {} + +private: + + std::vector zvals; + + double ztot; + +}; + +std::ostream& operator<< (std::ostream& os, const Node& node); + +inline void +Node::testInvariant() const +{ +#ifndef NDEBUG + if(edges) { + // Each EdgeEnd in the star has this Node's + // coordinate as first coordinate + for(EdgeEndStar::iterator + it = edges->begin(), itEnd = edges->end(); + it != itEnd; it++) { + EdgeEnd* e = *it; + assert(e); + assert(e->getCoordinate().equals2D(coord)); + } + } + +#if 0 + // We can't rely on numerical stability with FP computations + // ztot is the sum of doubnle sin zvals vector + double ztot_check = 0.0; + for(std::vector::const_iterator + i = zvals.begin(), e = zvals.end(); + i != e; + i++) { + ztot_check += *i; + } + assert(ztot_check == ztot); +#endif // 0 + +#endif +} + + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/NodeFactory.h b/Sources/geos/include/geos/geomgraph/NodeFactory.h new file mode 100644 index 0000000..fe577b6 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/NodeFactory.h @@ -0,0 +1,51 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/NodeFactory.java rev. 1.3 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class Node; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +class GEOS_DLL NodeFactory /* non-final */ { +public: + virtual Node* createNode(const geom::Coordinate& coord) const; + static const NodeFactory& instance(); + virtual + ~NodeFactory() {} +protected: + NodeFactory() {} +}; + + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/NodeMap.h b/Sources/geos/include/geos/geomgraph/NodeMap.h new file mode 100644 index 0000000..a3a769e --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/NodeMap.h @@ -0,0 +1,143 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/NodeMap.java rev. 1.3 (JTS-1.10) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +#include // for CoordinateLessThan +#include // for testInvariant + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geomgraph { +class Node; +class EdgeEnd; +class NodeFactory; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +class GEOS_DLL NodeMap final { +public: + + typedef std::map, geom::CoordinateLessThan> container; + + typedef container::iterator iterator; + + typedef container::const_iterator const_iterator; + + container nodeMap; + + const NodeFactory& nodeFact; + + /// \brief + /// NodeMap will keep a reference to the NodeFactory, + /// keep it alive for the whole NodeMap lifetime + NodeMap(const NodeFactory& newNodeFact); + + Node* addNode(const geom::Coordinate& coord); + + Node* addNode(Node* n); + + /// \brief + /// Adds a node for the start point of this EdgeEnd + /// (if one does not already exist in this map). + /// Adds the EdgeEnd to the (possibly new) node. + /// + /// If ownership of the EdgeEnd should be transferred + /// to the Node, use the unique_ptr overload instead. + void add(EdgeEnd* e); + + void add(std::unique_ptr&& e); + + Node* find(const geom::Coordinate& coord) const; + + const_iterator + begin() const + { + return nodeMap.begin(); + } + + const_iterator + end() const + { + return nodeMap.end(); + } + + iterator + begin() + { + return nodeMap.begin(); + } + + iterator + end() + { + return nodeMap.end(); + } + + void getBoundaryNodes(uint8_t geomIndex, + std::vector& bdyNodes) const; + + std::string print() const; + + void + testInvariant() + { +#ifndef NDEBUG + // Each Coordinate key is a pointer inside the Node value + for(const auto& nodeIt: nodeMap) { + const auto* n = nodeIt.second.get(); + geom::Coordinate* c = const_cast( + &(n->getCoordinate()) + ); + assert(nodeIt.first == c); + (void)c; + } +#endif + } + +private: + + // Declare type as noncopyable + NodeMap(const NodeMap& other) = delete; + NodeMap& operator=(const NodeMap& rhs) = delete; +}; + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/PlanarGraph.h b/Sources/geos/include/geos/geomgraph/PlanarGraph.h new file mode 100644 index 0000000..8684d3b --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/PlanarGraph.h @@ -0,0 +1,203 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/PlanarGraph.java r428 (JTS-1.12+) + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include // for typedefs +#include // for inlines + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class Edge; +class Node; +class EdgeEnd; +class NodeFactory; +} +} + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** + * \brief + * Represents a directed graph which is embeddable in a planar surface. + * + * The computation of the IntersectionMatrix relies on the use of a structure + * called a "topology graph". The topology graph contains nodes and edges + * corresponding to the nodes and line segments of a Geometry. Each + * node and edge in the graph is labeled with its topological location + * relative to the source geometry. + * + * Note that there is no requirement that points of self-intersection + * be a vertex. + * Thus to obtain a correct topology graph, Geometry objects must be + * self-noded before constructing their graphs. + * + * Two fundamental operations are supported by topology graphs: + * + * - Computing the intersections between all the edges and nodes of + * a single graph + * - Computing the intersections between the edges and nodes of two + * different graphs + * + */ +class GEOS_DLL PlanarGraph /* non-final */ { +public: + + /** \brief + * For nodes in the collection (first..last), + * link the DirectedEdges at the node that are in the result. + * + * This allows clients to link only a subset of nodes in the graph, + * for efficiency (because they know that only a subset is of + * interest). + */ + template + static void + linkResultDirectedEdges(It first, It last) + // throw(TopologyException); + { + for(; first != last; ++first) { + Node* node = *first; + assert(node); + + EdgeEndStar* ees = node->getEdges(); + assert(ees); + DirectedEdgeStar* des = dynamic_cast(ees); + assert(des); + + // this might throw an exception + des->linkResultDirectedEdges(); + } + } + + PlanarGraph(const NodeFactory& nodeFact); + + PlanarGraph(); + + virtual ~PlanarGraph(); + + std::vector::iterator getEdgeIterator(); + + std::vector* getEdgeEnds(); + + bool isBoundaryNode(uint8_t geomIndex, const geom::Coordinate& coord); + + void add(EdgeEnd* e); + + NodeMap::iterator getNodeIterator(); + + void getNodes(std::vector&); + + Node* addNode(Node* node); + + Node* addNode(const geom::Coordinate& coord); + + /** + * @return the node if found; null otherwise + */ + Node* find(geom::Coordinate& coord); + + /** \brief + * Add a set of edges to the graph. For each edge two DirectedEdges + * will be created. DirectedEdges are NOT linked by this method. + */ + void addEdges(const std::vector& edgesToAdd); + + void linkResultDirectedEdges(); + + void linkAllDirectedEdges(); + + /** \brief + * Returns the EdgeEnd which has edge e as its base edge + * (MD 18 Feb 2002 - this should return a pair of edges) + * + * @return the edge, if found + * null if the edge was not found + */ + EdgeEnd* findEdgeEnd(Edge* e); + + /** \brief + * Returns the edge whose first two coordinates are p0 and p1 + * + * @return the edge, if found + * null if the edge was not found + */ + Edge* findEdge(const geom::Coordinate& p0, + const geom::Coordinate& p1); + + /** \brief + * Returns the edge which starts at p0 and whose first segment is + * parallel to p1 + * + * @return the edge, if found + * null if the edge was not found + */ + Edge* findEdgeInSameDirection(const geom::Coordinate& p0, + const geom::Coordinate& p1); + + std::string printEdges(); + + NodeMap* getNodeMap(); + +protected: + + std::vector* edges; + + NodeMap* nodes; + + std::vector* edgeEndList; + + void insertEdge(Edge* e); + +private: + + /** \brief + * The coordinate pairs match if they define line segments + * lying in the same direction. + * + * E.g. the segments are parallel and in the same quadrant + * (as opposed to parallel and opposite!). + */ + bool matchInSameDirection(const geom::Coordinate& p0, + const geom::Coordinate& p1, + const geom::Coordinate& ep0, + const geom::Coordinate& ep1); + + PlanarGraph(const PlanarGraph&) = delete; + PlanarGraph& operator=(const PlanarGraph&) = delete; +}; + + + +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/TopologyLocation.h b/Sources/geos/include/geos/geomgraph/TopologyLocation.h new file mode 100644 index 0000000..0c831ef --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/TopologyLocation.h @@ -0,0 +1,237 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/TopologyLocation.java r428 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geomgraph { // geos.geomgraph + +/** \brief + * A TopologyLocation is the labelling of a + * GraphComponent's topological relationship to a single Geometry. + * + * If the parent component is an area edge, each side and the edge itself + * have a topological location. These locations are named + * + * - ON: on the edge + * - LEFT: left-hand side of the edge + * - RIGHT: right-hand side + * + * If the parent component is a line edge or node, there is a single + * topological relationship attribute, ON. + * + * The possible values of a topological location are + * {Location::NONE, Location::EXTERIOR, Location::BOUNDARY, Location::INTERIOR} + * + * The labelling is stored in an array location[j] where + * where j has the values ON, LEFT, RIGHT + */ +class GEOS_DLL TopologyLocation { + using Position = geos::geom::Position; + using Location = geos::geom::Location; + +public: + + friend std::ostream& operator<< (std::ostream&, const TopologyLocation&); + + TopologyLocation() = default; + + /** \brief + * Constructs a TopologyLocation specifying how points on, to the + * left of, and to the right of some GraphComponent relate to some + * Geometry. + * + * Possible values for the + * parameters are Location::NONE, Location::EXTERIOR, Location::BOUNDARY, + * and Location::INTERIOR. + * + * @see Location + */ + TopologyLocation(Location on, Location left, Location right) + : locationSize(3) + { + location[Position::ON] = on; + location[Position::LEFT] = left; + location[Position::RIGHT] = right; + } + + TopologyLocation(Location on) + : locationSize(1) + { + location.fill(Location::NONE); + location[Position::ON] = on; + }; + + TopologyLocation(const TopologyLocation& gl) + : location(gl.location) + , locationSize(gl.locationSize) + {}; + + TopologyLocation& operator= (const TopologyLocation& gl) + { + location = gl.location; + locationSize = gl.locationSize; + return *this; + }; + + Location get(std::size_t posIndex) const + { + // should be an assert() instead ? + if(posIndex < locationSize) { + return location[posIndex]; + } + return Location::NONE; + }; + + /** + * @return true if all locations are Location::NONE + */ + bool isNull() const + { + for(std::size_t i = 0; i < locationSize; ++i) { + if(location[i] != Location::NONE) { + return false; + } + } + return true; + }; + + /** + * @return true if any locations is Location::NONE + */ + bool isAnyNull() const + { + for(std::size_t i = 0; i < locationSize; ++i) { + if(location[i] == Location::NONE) { + return true; + } + } + return false; + }; + + bool isEqualOnSide(const TopologyLocation& le, uint32_t locIndex) const + { + return location[locIndex] == le.location[locIndex]; + }; + + bool isArea() const + { + return locationSize > 1; + }; + + bool isLine() const + { + return locationSize == 1; + }; + + void flip() + { + if(locationSize <= 1) { + return; + } + std::swap(location[Position::LEFT], location[Position::RIGHT]); + }; + + void setAllLocations(Location locValue) + { + location.fill(locValue); + }; + + + void setAllLocationsIfNull(Location locValue) + { + for(std::size_t i = 0; i < locationSize; ++i) { + if(location[i] == Location::NONE) { + location[i] = locValue; + } + } + }; + + void setLocation(std::size_t locIndex, Location locValue) + { + location[locIndex] = locValue; + }; + + void setLocation(Location locValue) + { + setLocation(Position::ON, locValue); + }; + + const std::array& getLocations() const + { + return location; + }; + + void setLocations(Location on, Location left, Location right) + { + assert(locationSize >= 3); + location[Position::ON] = on; + location[Position::LEFT] = left; + location[Position::RIGHT] = right; + }; + + bool allPositionsEqual(Location loc) const + { + for(std::size_t i = 0; i < locationSize; ++i) { + if(location[i] != loc) { + return false; + } + } + return true; + }; + + /** \brief + * merge updates only the UNDEF attributes of this object + * with the attributes of another. + */ + void merge(const TopologyLocation& gl); + + std::string toString() const; + + +private: + + std::array location; + std::uint8_t locationSize; + +}; + +std::ostream& operator<< (std::ostream&, const TopologyLocation&); + +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geomgraph/index/EdgeSetIntersector.h b/Sources/geos/include/geos/geomgraph/index/EdgeSetIntersector.h new file mode 100644 index 0000000..483f8a1 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/EdgeSetIntersector.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geomgraph { +class Edge; +namespace index { +class SegmentIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/** \brief + * An EdgeSetIntersector computes all the intersections between the edges in the set. + * + * \note This is derived from a Java interface. + */ +class GEOS_DLL EdgeSetIntersector { +public: + /** \brief + * Computes all self-intersections between edges in a set of edges, + * allowing client to choose whether self-intersections are computed. + * + * @param edges a list of edges to test for intersections + * @param si the SegmentIntersector to use + * @param testAllSegments true if self-intersections are to be tested as well + */ + virtual void computeIntersections(std::vector* edges, + SegmentIntersector* si, bool testAllSegments) = 0; + + /** \brief + * Computes all mutual intersections between two sets of edges + */ + virtual void computeIntersections(std::vector* edges0, + std::vector* edges1, + SegmentIntersector* si) = 0; + + virtual + ~EdgeSetIntersector() {} +}; + + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/MonotoneChain.h b/Sources/geos/include/geos/geomgraph/index/MonotoneChain.h new file mode 100644 index 0000000..9f84248 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/MonotoneChain.h @@ -0,0 +1,72 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: geomgraph/index/MonotoneChain.java rev. 1.3 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + + +#include +#include // for inheritance +#include // for inline +#include + +// Forward declarations +namespace geos { +namespace geomgraph { +namespace index { +class SegmentIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/** + * A chain in a MonotoneChainEdge + */ +class GEOS_DLL MonotoneChain: public SweepLineEventOBJ { +private: + MonotoneChainEdge* mce; + std::size_t chainIndex; + + MonotoneChain(const MonotoneChain& other) = delete; + MonotoneChain& operator=(const MonotoneChain& rhs) = delete; + +public: + + MonotoneChain(MonotoneChainEdge* newMce, std::size_t newChainIndex): + mce(newMce), + chainIndex(newChainIndex) + {} + + ~MonotoneChain() override {} + + void + computeIntersections(MonotoneChain* mc, SegmentIntersector* si) + { + mce->computeIntersectsForChain(chainIndex, *(mc->mce), mc->chainIndex, *si); + } +}; + + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/MonotoneChainEdge.h b/Sources/geos/include/geos/geomgraph/index/MonotoneChainEdge.h new file mode 100644 index 0000000..8e97a83 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/MonotoneChainEdge.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace geomgraph { +class Edge; +namespace index { +class SegmentIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/// \brief MonotoneChains are a way of partitioning the segments of an edge to +/// allow for fast searching of intersections. +class GEOS_DLL MonotoneChainEdge { +public: + //MonotoneChainEdge(); + ~MonotoneChainEdge() = default; + MonotoneChainEdge(Edge* newE); + const geom::CoordinateSequence* getCoordinates(); + std::vector& getStartIndexes(); + double getMinX(std::size_t chainIndex); + double getMaxX(std::size_t chainIndex); + + void computeIntersects(const MonotoneChainEdge& mce, + SegmentIntersector& si); + + void computeIntersectsForChain(std::size_t chainIndex0, + const MonotoneChainEdge& mce, std::size_t chainIndex1, + SegmentIntersector& si); + +protected: + Edge* e; + const geom::CoordinateSequence* pts; // cache a reference to the coord array, for efficiency + // the lists of start/end indexes of the monotone chains. + // Includes the end point of the edge as a sentinel + std::vector startIndex; + // these envelopes are created once and reused +private: + void computeIntersectsForChain(std::size_t start0, std::size_t end0, + const MonotoneChainEdge& mce, + std::size_t start1, std::size_t end1, + SegmentIntersector& ei); + + bool overlaps(std::size_t start0, std::size_t end0, const MonotoneChainEdge& mce, std::size_t start1, std::size_t end1); + +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geomgraph/index/MonotoneChainIndexer.h b/Sources/geos/include/geos/geomgraph/index/MonotoneChainIndexer.h new file mode 100644 index 0000000..0706948 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/MonotoneChainIndexer.h @@ -0,0 +1,71 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/// \brief MonotoneChains are a way of partitioning the segments of an edge to +/// allow for fast searching of intersections. +/// +/// Specifically, a sequence of contiguous line segments is a monotone chain +/// iff all the vectors defined by the oriented segments lies in the same +/// quadrant. +/// +/// Monotone Chains have the following useful properties: +/// +/// - the segments within a monotone chain will never intersect each other +/// +/// - the envelope of any contiguous subset of the segments in a monotone chain +/// is simply the envelope of the endpoints of the subset. +/// +/// Property 1 means that there is no need to test pairs of segments from +/// within the same monotone chain for intersection. Property 2 allows binary +/// search to be used to find the intersection points of two monotone chains. +/// For many types of real-world data, these properties eliminate a large +/// number of segment comparisons, producing substantial speed gains. +/// +/// \note Due to the efficient intersection test, there is no need to limit the +/// size of chains to obtain fast performance. +class GEOS_DLL MonotoneChainIndexer { + +public: + + MonotoneChainIndexer() {} + + void getChainStartIndices(const geom::CoordinateSequence*, std::vector&); + +private: + + std::size_t findChainEnd(const geom::CoordinateSequence* pts, std::size_t start); + +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/SegmentIntersector.h b/Sources/geos/include/geos/geomgraph/index/SegmentIntersector.h new file mode 100644 index 0000000..9c164fa --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SegmentIntersector.h @@ -0,0 +1,176 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace geomgraph { +class Node; +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/// \brief Computes the intersection of line segments, and adds the +/// intersection to the edges containing the segments. +class GEOS_DLL SegmentIntersector { + +private: + + /** + * These variables keep track of what types of intersections were + * found during ALL edges that have been intersected. + */ + bool hasIntersectionVar; + + bool hasProper; + + bool hasProperInterior; + + /// the proper intersection point found + geom::Coordinate properIntersectionPoint; + + algorithm::LineIntersector* li; + + bool includeProper; + + bool recordIsolated; + + int numIntersections; + + /// Elements are externally owned + std::array*, 2> bdyNodes; + + bool isTrivialIntersection(Edge* e0, std::size_t segIndex0, Edge* e1, std::size_t segIndex1); + + bool isBoundaryPoint(algorithm::LineIntersector* p_li, + std::array*, 2>& tstBdyNodes) + { + return isBoundaryPoint(p_li, tstBdyNodes[0]) || isBoundaryPoint(p_li, tstBdyNodes[1]); + }; + + bool isBoundaryPoint(algorithm::LineIntersector* li, + std::vector* tstBdyNodes); + +public: + + static bool isAdjacentSegments(std::size_t i1, size_t i2) + { + return (i1 > i2 ? i1 - i2 : i2 - i1) == 1; + }; + + // testing only + int numTests; + + //SegmentIntersector(); + + virtual + ~SegmentIntersector() {} + + SegmentIntersector(algorithm::LineIntersector* newLi, + bool newIncludeProper, bool newRecordIsolated) + : + hasIntersectionVar(false), + hasProper(false), + hasProperInterior(false), + li(newLi), + includeProper(newIncludeProper), + recordIsolated(newRecordIsolated), + numIntersections(0), + bdyNodes{{nullptr, nullptr}}, + numTests(0) + {} + + /// \brief + /// Parameters are externally owned. + /// Make sure they live for the whole lifetime of this object + void setBoundaryNodes(std::vector* bdyNodes0, + std::vector* bdyNodes1) + { + bdyNodes[0] = bdyNodes0; + bdyNodes[1] = bdyNodes1; + }; + + /* + * @return the proper intersection point, or null + * if none was found + */ + geom::Coordinate& getProperIntersectionPoint() + { + return properIntersectionPoint; + }; + + bool hasIntersection() const + { + return hasIntersectionVar; + }; + + /** + * A proper intersection is an intersection which is interior to at least two + * line segments. Note that a proper intersection is not necessarily + * in the interior of the entire Geometry, since another edge may have + * an endpoint equal to the intersection, which according to SFS semantics + * can result in the point being on the Boundary of the Geometry. + */ + bool hasProperIntersection() const + { + return hasProper; + }; + + /** + * A proper interior intersection is a proper intersection which is not + * contained in the set of boundary nodes set for this SegmentIntersector. + */ + bool hasProperInteriorIntersection() const + { + return hasProperInterior; + }; + + void addIntersections(Edge* e0, std::size_t segIndex0, Edge* e1, std::size_t segIndex1); + + bool getIsDone() const + { + return false; + }; + + +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/geomgraph/index/SimpleEdgeSetIntersector.h b/Sources/geos/include/geos/geomgraph/index/SimpleEdgeSetIntersector.h new file mode 100644 index 0000000..f11e778 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SimpleEdgeSetIntersector.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geomgraph { +class Edge; +namespace index { +class SegmentIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/// \brief Finds all intersections in one or two sets of edges, using the +/// straightforward method of comparing all segments. +/// +/// \note This algorithm is too slow for production use, but is useful for +/// testing purposes. +class GEOS_DLL SimpleEdgeSetIntersector: public EdgeSetIntersector { + +public: + + SimpleEdgeSetIntersector(); + + void computeIntersections(std::vector* edges, + SegmentIntersector* si, bool testAllSegments) override; + + void computeIntersections(std::vector* edges0, + std::vector* edges1, SegmentIntersector* si) override; + +private: + + int nOverlaps; + + void computeIntersects(Edge* e0, Edge* e1, SegmentIntersector* si); +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/SimpleMCSweepLineIntersector.h b/Sources/geos/include/geos/geomgraph/index/SimpleMCSweepLineIntersector.h new file mode 100644 index 0000000..aebac45 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SimpleMCSweepLineIntersector.h @@ -0,0 +1,108 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include // for inheritance +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geomgraph { +class Edge; +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/** \brief + * Finds all intersections in one or two sets of edges, + * using an x-axis sweepline algorithm in conjunction with Monotone Chains. + * + * While still O(n^2) in the worst case, this algorithm + * drastically improves the average-case time. + * The use of MonotoneChains as the items in the index + * seems to offer an improvement in performance over a sweep-line alone. + */ +class GEOS_DLL SimpleMCSweepLineIntersector: public EdgeSetIntersector { + +public: + + SimpleMCSweepLineIntersector() = default; + + ~SimpleMCSweepLineIntersector() override = default; + + void computeIntersections(std::vector* edges, + SegmentIntersector* si, bool testAllSegments) override; + + void computeIntersections(std::vector* edges0, + std::vector* edges1, + SegmentIntersector* si) override; + +protected: + + // SweepLineEvents need to refer to each other, and to MonotoneChains. + // To avoid individually heap-allocating all of these, we store them + // in deques so that subsequent inserts preserve addresses. However, + // we also need to sort the SweepLineEvents after they have all been + // inserted, so we keep a pointer to each event in a separate vector, + // which can be freely reordered without breaking linkages. + std::vector events; + std::deque eventStore; + std::deque chains; + + // statistics information + int nOverlaps; + +private: + void add(std::vector* edges); + + void add(std::vector* edges, void* edgeSet); + + void add(Edge* edge, void* edgeSet); + + void prepareEvents(); + + void computeIntersections(SegmentIntersector* si); + + void processOverlaps(std::size_t start, std::size_t end, + SweepLineEvent* ev0, + SegmentIntersector* si); + // Declare type as noncopyable + SimpleMCSweepLineIntersector(const SimpleMCSweepLineIntersector& other) = delete; + SimpleMCSweepLineIntersector& operator=(const SimpleMCSweepLineIntersector& rhs) = delete; +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/index/SimpleSweepLineIntersector.h b/Sources/geos/include/geos/geomgraph/index/SimpleSweepLineIntersector.h new file mode 100644 index 0000000..d1dded1 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SimpleSweepLineIntersector.h @@ -0,0 +1,94 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include // for inheritance + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geomgraph { +class Edge; +namespace index { +class SegmentIntersector; +class SweepLineEvent; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +/** \brief + * Finds all intersections in one or two sets of edges, + * using a simple x-axis sweepline algorithm. + * + * While still O(n^2) in the worst case, this algorithm + * drastically improves the average-case time. + */ +class GEOS_DLL SimpleSweepLineIntersector: public EdgeSetIntersector { + +public: + + SimpleSweepLineIntersector(); + + ~SimpleSweepLineIntersector() override; + + void computeIntersections(std::vector* edges, + SegmentIntersector* si, + bool testAllSegments) override; + + void computeIntersections(std::vector* edges0, + std::vector* edges1, + SegmentIntersector* si) override; + +private: + + void add(std::vector* edges); + + std::vector events; + + // statistics information + int nOverlaps; + + void add(std::vector* edges, void* edgeSet); + + void add(Edge* edge, void* edgeSet); + + void prepareEvents(); + + void computeIntersections(SegmentIntersector* si); + + void processOverlaps(std::size_t start, std::size_t end, SweepLineEvent* ev0, + SegmentIntersector* si); +}; + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/geomgraph/index/SweepLineEvent.h b/Sources/geos/include/geos/geomgraph/index/SweepLineEvent.h new file mode 100644 index 0000000..40124d7 --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SweepLineEvent.h @@ -0,0 +1,136 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geomgraph { +namespace index { +class SweepLineEventOBJ; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +//class SweepLineEventLessThen; // needed ?? + +class GEOS_DLL SweepLineEvent final { + friend class SweepLineEventLessThen; + +public: + + enum { + INSERT_EVENT = 1, + DELETE_EVENT + }; + + SweepLineEvent(void* newEdgeSet, double x, + SweepLineEvent* newInsertEvent, + SweepLineEventOBJ* newObj); + + ~SweepLineEvent() = default; + + bool + isInsert() + { + return insertEvent == nullptr; + } + + bool + isDelete() + { + return insertEvent != nullptr; + } + + int + eventType() + { + return insertEvent == nullptr ? INSERT_EVENT : DELETE_EVENT; + } + + SweepLineEvent* + getInsertEvent() + { + return insertEvent; + } + + size_t + getDeleteEventIndex() + { + return deleteEventIndex; + } + + void + setDeleteEventIndex(std::size_t newDeleteEventIndex) + { + deleteEventIndex = newDeleteEventIndex; + } + + SweepLineEventOBJ* + getObject() const + { + return obj; + } + + int compareTo(SweepLineEvent* sle); + + std::string print(); + + void* edgeSet; // used for red-blue intersection detection + +protected: + + SweepLineEventOBJ* obj; + +private: + + double xValue; + + SweepLineEvent* insertEvent; // null if this is an INSERT_EVENT event + + std::size_t deleteEventIndex; +}; + +class GEOS_DLL SweepLineEventLessThen { +public: + template + bool + operator()(const T& f, const T& s) const + { + if(f->xValue < s->xValue) { + return true; + } + if(f->xValue > s->xValue) { + return false; + } + if(f->eventType() < s->eventType()) { + return true; + } + return false; + } +}; + + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/SweepLineEventObj.h b/Sources/geos/include/geos/geomgraph/index/SweepLineEventObj.h new file mode 100644 index 0000000..7f7877d --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SweepLineEventObj.h @@ -0,0 +1,38 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +// This is here so that SweepLineEvent constructor +// can use it as argument type. +// Both SweepLineSegment and MonotoneChain will +// inherit from it. +class GEOS_DLL SweepLineEventOBJ { +public: + virtual + ~SweepLineEventOBJ() {} +}; + + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/geomgraph/index/SweepLineSegment.h b/Sources/geos/include/geos/geomgraph/index/SweepLineSegment.h new file mode 100644 index 0000000..fc2fb5d --- /dev/null +++ b/Sources/geos/include/geos/geomgraph/index/SweepLineSegment.h @@ -0,0 +1,57 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace geomgraph { +class Edge; +namespace index { +class SegmentIntersector; +} +} +} + +namespace geos { +namespace geomgraph { // geos::geomgraph +namespace index { // geos::geomgraph::index + +class GEOS_DLL SweepLineSegment: public SweepLineEventOBJ { +public: + SweepLineSegment(Edge* newEdge, std::size_t newPtIndex); + ~SweepLineSegment() override = default; + double getMinX(); + double getMaxX(); + void computeIntersections(SweepLineSegment* ss, SegmentIntersector* si); +protected: + Edge* edge; + const geom::CoordinateSequence* pts; + std::size_t ptIndex; +}; + + + +} // namespace geos.geomgraph.index +} // namespace geos.geomgraph +} // namespace geos + diff --git a/Sources/geos/include/geos/index/ItemVisitor.h b/Sources/geos/include/geos/index/ItemVisitor.h new file mode 100644 index 0000000..5d935ab --- /dev/null +++ b/Sources/geos/include/geos/index/ItemVisitor.h @@ -0,0 +1,38 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { + +/** \brief + * A visitor for items in an index. + * + * Last port: index/ItemVisitor.java rev. 1.2 (JTS-1.7) + */ +class GEOS_DLL ItemVisitor { +public: + virtual void visitItem(void*) = 0; + + virtual + ~ItemVisitor() {} +}; + +} // namespace geos.index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/SpatialIndex.h b/Sources/geos/include/geos/index/SpatialIndex.h new file mode 100644 index 0000000..b384254 --- /dev/null +++ b/Sources/geos/include/geos/index/SpatialIndex.h @@ -0,0 +1,103 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +} +namespace index { +class ItemVisitor; +} +} + +namespace geos { +namespace index { + +/** \brief + * Abstract class defines basic insertion and query operations supported by + * classes implementing spatial index algorithms. + * + * A spatial index typically provides a primary filter for range rectangle queries. A + * secondary filter is required to test for exact intersection. Of course, this + * secondary filter may consist of other tests besides intersection, such as + * testing other kinds of spatial relationships. + * + * Last port: index/SpatialIndex.java rev. 1.11 (JTS-1.7) + * + */ +class GEOS_DLL SpatialIndex { +public: + + virtual + ~SpatialIndex() {} + + /** \brief + * Adds a spatial item with an extent specified by the given Envelope + * to the index + * + * @param itemEnv + * Envelope of the item, ownership left to caller. + * TODO: Reference hold by this class ? + * + * @param item + * Opaque item, ownership left to caller. + * Reference hold by this class. + */ + virtual void insert(const geom::Envelope* itemEnv, void* item) = 0; + + /** \brief + * Queries the index for all items whose extents intersect the given search Envelope + * + * Note that some kinds of indexes may also return objects which do not in fact + * intersect the query envelope. + * + * @param searchEnv the envelope to query for + */ + //virtual std::vector* query(const geom::Envelope *searchEnv)=0; + virtual void query(const geom::Envelope* searchEnv, std::vector&) = 0; + + /** \brief + * Queries the index for all items whose extents intersect the given search Envelope + * and applies an ItemVisitor to them. + * + * Note that some kinds of indexes may also return objects which do not in fact + * intersect the query envelope. + * + * @param searchEnv the envelope to query for + * @param visitor a visitor object to apply to the items found + */ + virtual void query(const geom::Envelope* searchEnv, ItemVisitor& visitor) = 0; + + /** \brief + * Removes a single item from the tree. + * + * @param itemEnv the Envelope of the item to remove + * @param item the item to remove + * @return true if the item was found + */ + virtual bool remove(const geom::Envelope* itemEnv, void* item) = 0; + +}; + + +} // namespace geos.index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/VertexSequencePackedRtree.h b/Sources/geos/include/geos/index/VertexSequencePackedRtree.h new file mode 100644 index 0000000..8cf0836 --- /dev/null +++ b/Sources/geos/include/geos/index/VertexSequencePackedRtree.h @@ -0,0 +1,151 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Envelope; +} +} + +namespace geos { +namespace index { + + +/** + * A semi-static spatial index for points which occur + * in a spatially-coherent sequence. + * In particular, this is suitable for indexing the vertices + * of a geos::geom::LineString or geos::geom::Polygon ring. + * + * The index is constructed in a batch fashion on a given sequence of coordinates. + * Coordinates can be removed via the remove() method. + * + * Note that this index queries only the individual points + * of the input coordinate sequence, + * **not** any line segments which might be lie between them. + * + * @author Martin Davis + * + */ +class GEOS_DLL VertexSequencePackedRtree { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + +private: + + + /** + * Number of items/nodes in a parent node. + * Determined empirically. Performance is not too sensitive to this. + */ + static constexpr std::size_t NODE_CAPACITY = 16; + + // Members + const CoordinateSequence& items; + std::vector removedItems; + std::vector levelOffset; + std::size_t nodeCapacity = NODE_CAPACITY; + std::vector bounds; + + + // Methods + + void build(); + + /** + * Computes the level offsets. + * This is the position in the bounds array of each level. + * + * The levelOffsets array includes a sentinel value of offset[0] = 0. + * The top level is always of size 1, + * and so also indicates the total number of bounds. + * + * @return the level offsets + */ + std::vector computeLevelOffsets(); + + static std::size_t ceilDivisor(std::size_t num, std::size_t denom); + static std::size_t clampMax(std::size_t x, std::size_t max); + + std::size_t levelNodeCount(std::size_t numNodes); + + std::vector createBounds(); + + void fillItemBounds(std::vector& bounds); + void fillLevelBounds(std::size_t lvl, std::vector& bounds); + + static Envelope computeNodeEnvelope(const std::vector& bounds, + std::size_t start, std::size_t end); + static Envelope computeItemEnvelope(const CoordinateSequence& items, + std::size_t start, std::size_t end); + + void queryNode(const Envelope& queryEnv, + std::size_t level, std::size_t nodeIndex, + std::vector& result) const; + void queryNodeRange(const Envelope& queryEnv, + std::size_t level, std::size_t nodeStartIndex, + std::vector& result) const; + void queryItemRange(const Envelope& queryEnv, std::size_t itemIndex, + std::vector& result) const; + + std::size_t levelSize(std::size_t level) const; + bool isNodeEmpty(std::size_t level, std::size_t index); + bool isItemsNodeEmpty(std::size_t nodeIndex); + + +public: + + /** + * Creates a new tree over the given sequence of coordinates. + * The sequence should be spatially coherent to provide query performance. + * + * @param pts a sequence of points + */ + VertexSequencePackedRtree(const CoordinateSequence& pts); + + std::vector getBounds(); + + /** + * Removes the input item at the given index from the spatial index. + * + * @param index the index of the item in the input + */ + void remove(std::size_t index); + + /** + * Queries the index to find all items which intersect an extent. + * The query result is a list of the indices of input coordinates + * which intersect the extent. + * + * @param queryEnv the query extent + * @param result vector to fill with results + */ + void query(const Envelope& queryEnv, std::vector& result) const; + +}; + + + +} // namespace geos.index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/bintree/Bintree.h b/Sources/geos/include/geos/index/bintree/Bintree.h new file mode 100644 index 0000000..4c41476 --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/Bintree.h @@ -0,0 +1,129 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace index { +namespace bintree { +class Interval; +class Root; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/** \brief + * A BinTree (or "Binary Interval Tree") is a 1-dimensional version of a quadtree. + * + * It indexes 1-dimensional intervals (which of course may be the projection + * of 2-D objects on an axis). It supports range searching (where the range + * may be a single point). + * + * This implementation does not require specifying the extent of the inserted + * items beforehand. It will automatically expand to accommodate any extent + * of dataset. + * + * This index is different to the "Interval Tree of Edelsbrunner" + * or the "Segment Tree of Bentley". + */ +class GEOS_DLL Bintree { + +public: + + /** \brief + * Ensure that the Interval for the inserted item has non-zero extents. + * + * Use the current minExtent to pad it, if necessary. + * + * @note In GEOS this function always return a newly allocated object + * with ownership transferred to caller. TODO: change this ? + * + * @param itemInterval source interval, ownership left to caller, no references hold + * @param minExtent minimal extent + */ + static Interval* ensureExtent(const Interval* itemInterval, + double minExtent); + + Bintree(); + + ~Bintree(); + + int depth(); + + int size(); + + int nodeSize(); + + /// @param itemInterval + /// Ownership left to caller, NO reference hold by this class. + /// + /// @param item + /// Ownership left to caller, reference kept by this class. + /// + void insert(Interval* itemInterval, void* item); + + std::vector* iterator(); + + std::vector* query(double x); + + std::vector* query(Interval* interval); + + void query(Interval* interval, + std::vector* foundItems); + +private: + + std::vectornewIntervals; + + Root* root; + + /** + * Statistics + * + * minExtent is the minimum extent of all items + * inserted into the tree so far. It is used as a heuristic value + * to construct non-zero extents for features with zero extent. + * Start with a non-zero extent, in case the first feature inserted has + * a zero extent in both directions. This value may be non-optimal, but + * only one feature will be inserted with this value. + */ + double minExtent; + + void collectStats(Interval* interval); + + Bintree(const Bintree&) = delete; + Bintree& operator=(const Bintree&) = delete; +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/bintree/Interval.h b/Sources/geos/include/geos/index/bintree/Interval.h new file mode 100644 index 0000000..8c43439 --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/Interval.h @@ -0,0 +1,61 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/// Represents an (1-dimensional) closed interval on the Real number line. +class GEOS_DLL Interval { + +public: + + double min, max; + + Interval(); + + Interval(double nmin, double nmax); + + /// TODO: drop this, rely on copy ctor + Interval(const Interval* interval); + + void init(double nmin, double nmax); + + double getMin() const; + + double getMax() const; + + double getWidth() const; + + void expandToInclude(Interval* interval); + + bool overlaps(const Interval* interval) const; + + bool overlaps(double nmin, double nmax) const; + + bool contains(const Interval* interval) const; + + bool contains(double nmin, double nmax) const; + + bool contains(double p) const; +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/bintree/Key.h b/Sources/geos/include/geos/index/bintree/Key.h new file mode 100644 index 0000000..796ebfd --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/Key.h @@ -0,0 +1,71 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace index { +namespace bintree { +class Interval; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/** \brief + * A Key is a unique identifier for a node in a tree. + * + * It contains a lower-left point and a level number. + * The level number is the power of two for the size of the node envelope + */ +class GEOS_DLL Key { + +public: + + static int computeLevel(Interval* newInterval); + + Key(Interval* newInterval); + + ~Key(); + + double getPoint(); + + int getLevel(); + + Interval* getInterval(); + + void computeKey(Interval* itemInterval); + +private: + + // the fields which make up the key + double pt; + int level; + + // auxiliary data which is derived from the key for use in computation + Interval* interval; + + void computeInterval(int level, Interval* itemInterval); +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/bintree/Node.h b/Sources/geos/include/geos/index/bintree/Node.h new file mode 100644 index 0000000..a10b504 --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/Node.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace index { +namespace bintree { +class Interval; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/// A node of a Bintree. +class GEOS_DLL Node: public NodeBase { + +public: + + static Node* createNode(Interval* itemInterval); + + static Node* createExpanded(Node* node, Interval* addInterval); + + Node(Interval* newInterval, int newLevel); + + ~Node() override; + + Interval* getInterval(); + + Node* getNode(Interval* searchInterval); + + NodeBase* find(Interval* searchInterval); + + void insert(Node* node); + +private: + + Interval* interval; + + double centre; + + int level; + + Node* getSubnode(int index); + + Node* createSubnode(int index); + +protected: + + bool isSearchMatch(Interval* itemInterval) override; +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/bintree/NodeBase.h b/Sources/geos/include/geos/index/bintree/NodeBase.h new file mode 100644 index 0000000..72053bc --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/NodeBase.h @@ -0,0 +1,83 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace index { +namespace bintree { +class Node; +class Interval; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/// The base class for nodes in a Bintree. +class GEOS_DLL NodeBase { + +public: + + static int getSubnodeIndex(Interval* interval, double centre); + + NodeBase(); + + virtual ~NodeBase(); + + virtual std::vector* getItems(); + + virtual void add(void* item); + + virtual std::vector* addAllItems(std::vector* newItems); + + virtual std::vector* addAllItemsFromOverlapping(Interval* interval, + std::vector* resultItems); + + virtual int depth(); + + virtual int size(); + + virtual int nodeSize(); + +protected: + + std::vector* items; + + /** + * subnodes are numbered as follows: + * + * 0 | 1 + */ + Node* subnode[2]; + + virtual bool isSearchMatch(Interval* interval) = 0; + +private: + + NodeBase(const NodeBase&) = delete; + NodeBase& operator=(const NodeBase&) = delete; + +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/bintree/Root.h b/Sources/geos/include/geos/index/bintree/Root.h new file mode 100644 index 0000000..30158d9 --- /dev/null +++ b/Sources/geos/include/geos/index/bintree/Root.h @@ -0,0 +1,77 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace index { +namespace bintree { +class Interval; +class Node; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace bintree { // geos::index::bintree + +/** \brief + * The root node of a single Bintree. + * + * It is centred at the origin, + * and does not have a defined extent. + */ +class GEOS_DLL Root: public NodeBase { + +private: + + // the singleton root node is centred at the origin. + static double origin; + + void insertContained(Node* tree, + Interval* itemInterval, + void* item); + +public: + + Root() {} + + ~Root() override {} + + /// @param itemInterval + /// Ownership left to caller, references kept in this class. + /// + /// @param item + /// Ownership left to caller, references kept in this class. + /// + void insert(Interval* itemInterval, void* item); + +protected: + + bool + isSearchMatch(Interval* /*interval*/) override + { + return true; + } +}; + +} // namespace geos::index::bintree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/chain/MonotoneChain.h b/Sources/geos/include/geos/index/chain/MonotoneChain.h new file mode 100644 index 0000000..a5172f9 --- /dev/null +++ b/Sources/geos/include/geos/index/chain/MonotoneChain.h @@ -0,0 +1,206 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChain.java rev. 1.15 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inline +#include // for inline +#include // for inline + +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { +class LineSegment; +class CoordinateSequence; +} +namespace index { +namespace chain { +class MonotoneChainSelectAction; +class MonotoneChainOverlapAction; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace chain { // geos::index::chain + +/** \brief + * Monotone Chains are a way of partitioning the segments of a linestring to + * allow for fast searching of intersections. + * + * They have the following properties: + * + * - the segments within a monotone chain never intersect each other + * - the envelope of any contiguous subset of the segments in a monotone + * chain is equal to the envelope of the endpoints of the subset. + * + * Property 1 means that there is no need to test pairs of segments from + * within the same monotone chain for intersection. + * Property 2 allows an efficient binary search to be used to find the + * intersection points of two monotone chains. + * + * For many types of real-world data, these properties eliminate + * a large number of segment comparisons, producing substantial speed gains. + * + * One of the goals of this implementation of MonotoneChains is to be + * as space and time efficient as possible. One design choice that aids this + * is that a MonotoneChain is based on a subarray of a list of points. + * This means that new arrays of points (potentially very large) do not + * have to be allocated. + * + * MonotoneChains support the following kinds of queries: + * + * - Envelope select: determine all the segments in the chain which + * intersect a given envelope + * - Overlap: determine all the pairs of segments in two chains whose + * envelopes overlap + * + * This implementation of MonotoneChains uses the concept of internal iterators + * to return the resultsets for the above queries. + * This has time and space advantages, since it + * is not necessary to build lists of instantiated objects to represent the segments + * returned by the query. + * However, it does mean that the queries are not thread-safe. + * + */ +class GEOS_DLL MonotoneChain { +public: + + /// @param pts + /// Ownership left to caller, this class holds a reference. + /// + /// @param start + /// + /// @param end + /// + /// @param context + /// Ownership left to caller, this class holds a reference. + /// + MonotoneChain(const geom::CoordinateSequence& pts, + std::size_t start, std::size_t end, void* context); + + ~MonotoneChain() = default; + + /// Returned envelope is owned by this class + const geom::Envelope& getEnvelope() const; + const geom::Envelope& getEnvelope(double expansionDistance) const; + + size_t + getStartIndex() const + { + return start; + } + + size_t + getEndIndex() const + { + return end; + } + + /** \brief + * Set given LineSegment with points of the segment starting + * at the given index. + */ + void getLineSegment(std::size_t index, geom::LineSegment& ls) const { + pts->getAt(index, ls.p0); + pts->getAt(index + 1, ls.p1); + } + + /** + * Return the subsequence of coordinates forming this chain. + * Allocates a new CoordinateSequence to hold the Coordinates + * + */ + std::unique_ptr getCoordinates() const; + + /** + * Determine all the line segments in the chain whose envelopes overlap + * the searchEnvelope, and process them + */ + void select(const geom::Envelope& searchEnv, + MonotoneChainSelectAction& mcs) const; + + void computeOverlaps(const MonotoneChain* mc, + MonotoneChainOverlapAction* mco) const; + + void computeOverlaps(const MonotoneChain* mc, double overlapTolerance, + MonotoneChainOverlapAction* mco) const; + + void* + getContext() const + { + return context; + } + +private: + + void computeSelect(const geom::Envelope& searchEnv, + std::size_t start0, + std::size_t end0, + MonotoneChainSelectAction& mcs) const; + + void computeOverlaps(std::size_t start0, std::size_t end0, const MonotoneChain& mc, + std::size_t start1, std::size_t end1, + double overlapTolerance, + MonotoneChainOverlapAction& mco) const; + + bool overlaps(std::size_t start0, std::size_t end0, + const MonotoneChain& mc, std::size_t start1, std::size_t end1, + double overlapTolerance) const { + if (overlapTolerance > 0.0) { + return overlaps(pts->getAt(start0), + pts->getAt(end0), + mc.pts->getAt(start1), + mc.pts->getAt(end1), + overlapTolerance); + } + return geom::Envelope::intersects(pts->getAt(start0), + pts->getAt(end0), + mc.pts->getAt(start1), + mc.pts->getAt(end1)); + } + + static bool overlaps(const geom::CoordinateXY& p1, const geom::CoordinateXY& p2, + const geom::CoordinateXY& q1, const geom::CoordinateXY& q2, + double overlapTolerance); + + /// Externally owned + const geom::CoordinateSequence* pts; + + /// user-defined information + void* context; + + /// Index of chain start vertex into the CoordinateSequence, 0 based. + std::size_t start; + + /// Index of chain end vertex into the CoordinateSequence, 0 based. + std::size_t end; + + /// Owned by this class + mutable geom::Envelope env; + +}; + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/chain/MonotoneChainBuilder.h b/Sources/geos/include/geos/index/chain/MonotoneChainBuilder.h new file mode 100644 index 0000000..815c39b --- /dev/null +++ b/Sources/geos/include/geos/index/chain/MonotoneChainBuilder.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChainBuilder.java r388 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace index { +namespace chain { +class MonotoneChain; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace chain { // geos::index::chain + +/** \brief + * Constructs [MonotoneChains](@ref index::chain::MonotoneChain) + * for sequences of [Coordinates](@ref geom::Coordinate). + * + * TODO: use vector instead ? + */ +class GEOS_DLL MonotoneChainBuilder { + +public: + /** \brief + * Computes a list of the {@link MonotoneChain}s for a list of coordinates, + * attaching a context data object to each. + * + * @param pts the list of points to compute chains for + * @param context a data object to attach to each chain + * @param[out] mcList a list of the monotone chains for the points + */ + static void getChains(const geom::CoordinateSequence* pts, + void* context, + std::vector& mcList); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + MonotoneChainBuilder(const MonotoneChainBuilder&) = delete; + MonotoneChainBuilder& operator=(const MonotoneChainBuilder&) = delete; +}; + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/chain/MonotoneChainOverlapAction.h b/Sources/geos/include/geos/index/chain/MonotoneChainOverlapAction.h new file mode 100644 index 0000000..72de737 --- /dev/null +++ b/Sources/geos/include/geos/index/chain/MonotoneChainOverlapAction.h @@ -0,0 +1,86 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChainOverlapAction.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + + +// Forward declarations +namespace geos { +namespace index { +namespace chain { +class MonotoneChain; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace chain { // geos::index::chain + +/** \brief + * The action for the internal iterator for performing + * overlap queries on a MonotoneChain. + */ +class GEOS_DLL MonotoneChainOverlapAction { + +protected: + + geom::LineSegment overlapSeg1; + + geom::LineSegment overlapSeg2; + +public: + + MonotoneChainOverlapAction() {} + + virtual + ~MonotoneChainOverlapAction() {} + + /** \brief + * This function can be overridden if the original chains are needed. + * + * @param mc1 a MonotoneChain + * @param mc2 a MonotoneChain + * @param start1 the index of the start of the overlapping segment + * from mc1 + * @param start2 the index of the start of the overlapping segment + * from mc2 + */ + virtual void overlap(const MonotoneChain& mc1, std::size_t start1, + const MonotoneChain& mc2, std::size_t start2); + + /** \brief + * This is a convenience function which can be overridden to + * obtain the actual line segments which overlap. + * + * **param** `seg1` + * **param** `seg2` + */ + virtual void + overlap(const geom::LineSegment& /*seg1*/, + const geom::LineSegment& /*seg2*/) {} + +}; + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/chain/MonotoneChainSelectAction.h b/Sources/geos/include/geos/index/chain/MonotoneChainSelectAction.h new file mode 100644 index 0000000..91631f2 --- /dev/null +++ b/Sources/geos/include/geos/index/chain/MonotoneChainSelectAction.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChainSelectAction.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // composition +#include // composition + + +// Forward declarations +namespace geos { +namespace index { +namespace chain { +class MonotoneChain; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace chain { // geos::index::chain + +/** + * The action for the internal iterator for performing + * Envelope select queries on a MonotoneChain + * + */ +class GEOS_DLL MonotoneChainSelectAction { + +protected: + + geom::LineSegment selectedSegment; + +public: + + MonotoneChainSelectAction() {} + + virtual + ~MonotoneChainSelectAction() {} + + /// This function can be overridden if the original chain is needed + virtual void select(const MonotoneChain& mc, std::size_t start); + + /** + * This is a convenience function which can be overridden + * to obtain the actual line segment which is selected + * + * @param seg + */ + virtual void select(const geom::LineSegment& seg) = 0; + +}; + + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeBranchNode.h b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeBranchNode.h new file mode 100644 index 0000000..c0d7232 --- /dev/null +++ b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeBranchNode.h @@ -0,0 +1,53 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include // inherited + +#include + +// forward declarations +namespace geos { +namespace index { +class ItemVisitor; +} +} + + +namespace geos { +namespace index { +namespace intervalrtree { + +class IntervalRTreeBranchNode : public IntervalRTreeNode { +private: + const IntervalRTreeNode* node1; + const IntervalRTreeNode* node2; + +protected: +public: + IntervalRTreeBranchNode(const IntervalRTreeNode* n1, const IntervalRTreeNode* n2) + : IntervalRTreeNode(std::min(n1->getMin(), n2->getMin()), std::max(n1->getMax(), n2->getMax())), + node1(n1), + node2(n2) + { } + + void query(double queryMin, double queryMax, index::ItemVisitor* visitor) const override; +}; + +} // geos::intervalrtree +} // geos::index +} // geos + diff --git a/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeLeafNode.h b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeLeafNode.h new file mode 100644 index 0000000..b0c3692 --- /dev/null +++ b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeLeafNode.h @@ -0,0 +1,58 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include // inherited + + +// forward declarations +namespace geos { +namespace index { +class ItemVisitor; +} +} + + +namespace geos { +namespace index { +namespace intervalrtree { + +class IntervalRTreeLeafNode : public IntervalRTreeNode { +private: + /// externally owned + void* item; + +protected: +public: + + /// @param p_item externally owned + IntervalRTreeLeafNode(double p_min, double p_max, void* p_item) + : IntervalRTreeNode(p_min, p_max), + item(p_item) + { } + + ~IntervalRTreeLeafNode() override + { + } + + void query(double queryMin, double queryMax, index::ItemVisitor* visitor) const override; + +}; + +} // geos::intervalrtree +} // geos::index +} // geos + diff --git a/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeNode.h b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeNode.h new file mode 100644 index 0000000..d15d54a --- /dev/null +++ b/Sources/geos/include/geos/index/intervalrtree/IntervalRTreeNode.h @@ -0,0 +1,105 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// forward declarations +namespace geos { +namespace index { +class ItemVisitor; +} +} + + +namespace geos { +namespace index { +namespace intervalrtree { + +class IntervalRTreeNode { +private: +protected: + double min; + double max; + + bool + intersects(double queryMin, double queryMax) const + { + if(min > queryMax || max < queryMin) { + return false; + } + + return true; + } + +public: + typedef std::vector ConstVect; + + IntervalRTreeNode() + : min(DoubleInfinity), + max(DoubleNegInfinity) + { } + + IntervalRTreeNode(double p_min, double p_max) + : min(p_min), + max(p_max) + { } + + virtual + ~IntervalRTreeNode() + { } + + double + getMin() const + { + return min; + } + + double + getMax() const + { + return max; + } + + virtual void query(double queryMin, double queryMax, ItemVisitor* visitor) const = 0; + + //std::string toString() + //{ + // return WKTWriter.toLineString(new Coordinate(min, 0), new Coordinate(max, 0)); + //} + + + //class NodeComparator + //{ + //public: + static bool + compare(const IntervalRTreeNode* n1, const IntervalRTreeNode* n2) + { + double mid1 = n1->getMin() + n1->getMax(); + double mid2 = n2->getMin() + n2->getMax(); + + return mid1 > mid2; + } + //}; + +}; + +} // geos::index::intervalrtree +} // geos::index +} // geos + diff --git a/Sources/geos/include/geos/index/intervalrtree/SortedPackedIntervalRTree.h b/Sources/geos/include/geos/index/intervalrtree/SortedPackedIntervalRTree.h new file mode 100644 index 0000000..ec47261 --- /dev/null +++ b/Sources/geos/include/geos/index/intervalrtree/SortedPackedIntervalRTree.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// forward declarations +namespace geos { +namespace index { +class ItemVisitor; +} +} + +namespace geos { +namespace index { +namespace intervalrtree { + +/** \brief + * A static index on a set of 1-dimensional intervals, + * using an R-Tree packed based on the order of the interval midpoints. + * + * It supports range searching, + * where the range is an interval of the real line (which may be a single point). + * A common use is to index 1-dimensional intervals which + * are the projection of 2-D objects onto an axis of the coordinate system. + * + * This index structure is static + * - items cannot be added or removed once the first query has been made. + * The advantage of this characteristic is that the index performance + * can be optimized based on a fixed set of items. + * + * @author Martin Davis + * + */ +class SortedPackedIntervalRTree { +private: + std::vector leaves; + std::vector branches; + + /** + * If root is null that indicates + * that the tree has not yet been built, + * OR nothing has been added to the tree. + * In both cases, the tree is still open for insertions. + */ + const IntervalRTreeNode* root = nullptr; + int level = 0; + + void init(); + void buildLevel(IntervalRTreeNode::ConstVect& src, IntervalRTreeNode::ConstVect& dest); + const IntervalRTreeNode* buildTree(); + +protected: +public: + SortedPackedIntervalRTree() {} + + SortedPackedIntervalRTree(std::size_t initialCapacity) + { + leaves.reserve(initialCapacity); + } + + /** + * Adds an item to the index which is associated with the given interval + * + * @param min the lower bound of the item interval + * @param max the upper bound of the item interval + * @param item the item to insert, ownership left to caller + * + * @throw IllegalStateException if the index has already been queried + */ + void insert(double min, double max, void* item) { + if(root != nullptr) { + throw util::UnsupportedOperationException("Index cannot be added to once it has been queried"); + } + + leaves.emplace_back(min, max, item); + } + + /** + * Search for intervals in the index which intersect the given closed interval + * and apply the visitor to them. + * + * @param min the lower bound of the query interval + * @param max the upper bound of the query interval + * @param visitor the visitor to pass any matched items to + */ + void query(double min, double max, index::ItemVisitor* visitor); + +}; + +} // geos::intervalrtree +} // geos::index +} // geos + diff --git a/Sources/geos/include/geos/index/kdtree/KdNode.h b/Sources/geos/include/geos/index/kdtree/KdNode.h new file mode 100644 index 0000000..e1238ab --- /dev/null +++ b/Sources/geos/include/geos/index/kdtree/KdNode.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/kdtree/Node.java rev 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include // for composition + +namespace geos { +namespace index { // geos::index +namespace kdtree { // geos::index::kdtree + +/** + * A node of a {@link KdTree}, which represents one or more points in the same location. + */ +class GEOS_DLL KdNode { + +private: + + geom::Coordinate p; + void* data; + KdNode* left; + KdNode* right; + std::size_t count; + +public: + + KdNode(double p_x, double p_y, void* p_data); + KdNode(const geom::Coordinate& p_p, void* p_data); + + double getX() { return p.x; } + double getY() { return p.y; } + const geom::Coordinate& getCoordinate() { return p; } + void* getData() { return data; } + KdNode* getLeft() { return left; } + KdNode* getRight() { return right; } + void increment() { count++; } + std::size_t getCount() { return count; } + bool isRepeated() { return count > 1; } + void setLeft(KdNode* p_left) { left = p_left; } + void setRight(KdNode* p_right) { right = p_right; } + +}; + +} // namespace geos::index::kdtree +} // namespace geos::index +} // namespace geos + + + diff --git a/Sources/geos/include/geos/index/kdtree/KdNodeVisitor.h b/Sources/geos/include/geos/index/kdtree/KdNodeVisitor.h new file mode 100644 index 0000000..17c9a9c --- /dev/null +++ b/Sources/geos/include/geos/index/kdtree/KdNodeVisitor.h @@ -0,0 +1,45 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/kdtree/Node.java rev 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { // geos::index +namespace kdtree { // geos::index::kdtree + +class GEOS_DLL KdNodeVisitor { + +private: + +protected: + +public: + + KdNodeVisitor() {}; + virtual void visit(KdNode *node) = 0; + + +}; + + +} // namespace geos::index::kdtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/kdtree/KdTree.h b/Sources/geos/include/geos/index/kdtree/KdTree.h new file mode 100644 index 0000000..144fbc1 --- /dev/null +++ b/Sources/geos/include/geos/index/kdtree/KdTree.h @@ -0,0 +1,191 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + + +namespace geos { +namespace index { // geos::index +namespace kdtree { // geos::index::kdtree + +/** + * An implementation of a 2-D KD-Tree. KD-trees provide fast range searching on + * point data. + *

+ * This implementation supports detecting and snapping points which are closer + * than a given distance tolerance. + * If the same point (up to tolerance) is inserted + * more than once, it is snapped to the existing node. + * In other words, if a point is inserted which lies within the tolerance of a node already in the index, + * it is snapped to that node. + * When a point is snapped to a node then a new node is not created but the count of the existing node + * is incremented. + * If more than one node in the tree is within tolerance of an inserted point, + * the closest and then lowest node is snapped to. + * + * @author David Skea + * @author Martin Davis + */ +class GEOS_DLL KdTree { + +private: + + std::deque nodeQue; + KdNode *root; + std::size_t numberOfNodes; + double tolerance; + + KdNode* findBestMatchNode(const geom::Coordinate& p); + KdNode* insertExact(const geom::Coordinate& p, void* data); + + void queryNode(KdNode* currentNode, const geom::Envelope& queryEnv, bool odd, KdNodeVisitor& visitor); + KdNode* queryNodePoint(KdNode* currentNode, const geom::Coordinate& queryPt, bool odd); + + /** + * Create a node on a locally managed deque to allow easy + * disposal and hopefully faster allocation as well. + */ + KdNode* createNode(const geom::Coordinate& p, void* data); + + + /** + * BestMatchVisitor used to query the tree for a match + * within tolerance. + */ + class BestMatchVisitor : public KdNodeVisitor { + public: + BestMatchVisitor(const geom::Coordinate& p_p, double p_tolerance); + geom::Envelope queryEnvelope(); + KdNode* getNode(); + void visit(KdNode* node) override; + + private: + // Members + double tolerance; + KdNode* matchNode; + double matchDist; + const geom::Coordinate& p; + // Declare type as noncopyable + BestMatchVisitor(const BestMatchVisitor& other); + BestMatchVisitor& operator=(const BestMatchVisitor& rhs); + }; + + /** + * AccumulatingVisitor used to query the tree and get list + * of matching nodes. + */ + class AccumulatingVisitor : public KdNodeVisitor { + public: + AccumulatingVisitor(std::vector& p_nodeList) : + nodeList(p_nodeList) {}; + void visit(KdNode* node) override { nodeList.push_back(node); } + + private: + // Members + std::vector& nodeList; + // Declare type as noncopyable + AccumulatingVisitor(const AccumulatingVisitor& other); + AccumulatingVisitor& operator=(const AccumulatingVisitor& rhs); + }; + + + +public: + + /** + * Converts a collection of {@link KdNode}s to an vector of {@link geom::Coordinate}s. + * + * @param kdnodes a collection of nodes + * @return an vector of the coordinates represented by the nodes + */ + static std::unique_ptr> toCoordinates(std::vector& kdnodes); + + /** + * Converts a collection of {@link KdNode}s + * to an vector of {@link geom::Coordinate}s, + * specifying whether repeated nodes should be represented + * by multiple coordinates. + * + * @param kdnodes a collection of nodes + * @param includeRepeated true if repeated nodes should + * be included multiple times + * @return an vector of the coordinates represented by the nodes + */ + static std::unique_ptr> toCoordinates(std::vector& kdnodes, bool includeRepeated); + + KdTree() : + root(nullptr), + numberOfNodes(0), + tolerance(0.0) + {}; + + KdTree(double p_tolerance) : + root(nullptr), + numberOfNodes(0), + tolerance(p_tolerance) + {}; + + bool isEmpty() { return root == nullptr; } + + /** + * Inserts a new point in the kd-tree. + */ + KdNode* insert(const geom::Coordinate& p); + KdNode* insert(const geom::Coordinate& p, void* data); + + /** + * Performs a range search of the points in the index and visits all nodes found. + */ + void query(const geom::Envelope& queryEnv, KdNodeVisitor& visitor); + + /** + * Performs a range search of the points in the index. + */ + std::unique_ptr> query(const geom::Envelope& queryEnv); + + /** + * Performs a range search of the points in the index. + */ + void query(const geom::Envelope& queryEnv, std::vector& result); + + /** + * Searches for a given point in the index and returns its node if found. + */ + KdNode* query(const geom::Coordinate& queryPt); + +}; + +} // namespace geos::index::kdtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/quadtree/IntervalSize.h b/Sources/geos/include/geos/index/quadtree/IntervalSize.h new file mode 100644 index 0000000..1aca3b3 --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/IntervalSize.h @@ -0,0 +1,62 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/IntervalSize.java rev 1.7 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \class IntervalSize + * + * \brief + * Provides a test for whether an interval is + * so small it should be considered as zero for the purposes of + * inserting it into a binary tree. + * + * The reason this check is necessary is that round-off error can + * cause the algorithm used to subdivide an interval to fail, by + * computing a midpoint value which does not lie strictly between the + * endpoints. + */ +class GEOS_DLL IntervalSize { +public: + /** + * This value is chosen to be a few powers of 2 less than the + * number of bits available in the double representation (i.e. 53). + * This should allow enough extra precision for simple computations + * to be correct, at least for comparison purposes. + */ + static const int MIN_BINARY_EXPONENT = -50; + + /** + * Computes whether the interval [min, max] is effectively zero width. + * I.e. the width of the interval is so much less than the + * location of the interval that the midpoint of the interval + * cannot be represented precisely. + */ + static bool isZeroWidth(double min, double max); +}; + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/quadtree/Key.h b/Sources/geos/include/geos/index/quadtree/Key.h new file mode 100644 index 0000000..ac7f163 --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/Key.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/Key.java rev 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include // for composition + +// Forward declarations +// ... + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \brief + * A Key is a unique identifier for a node in a quadtree. + * + * It contains a lower-left point and a level number. The level number + * is the power of two for the size of the node envelope + */ +class GEOS_DLL Key { +public: + + // Doesn't touch the Envelope, might as well be const + static int computeQuadLevel(const geom::Envelope& env); + + // Reference to argument won't be used after construction + Key(const geom::Envelope& itemEnv); + + // used to be virtual, but I don't see subclasses... + ~Key() = default; + + /// Returned object ownership retained by this class + const geom::Coordinate& getPoint() const; + + int getLevel() const; + + /// Returned object ownership retained by this class + const geom::Envelope& getEnvelope() const; + + /// Returns newly allocated object (ownership transferred) + geom::Coordinate* getCentre() const; + + /** + * return a square envelope containing the argument envelope, + * whose extent is a power of two and which is based at a power of 2 + */ + void computeKey(const geom::Envelope& itemEnv); + +private: + // the fields which make up the key + + // Owned by this class + geom::Coordinate pt; + + int level; + + // auxiliary data which is derived from the key for use in computation + geom::Envelope env; + + void computeKey(int level, const geom::Envelope& itemEnv); +}; + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/quadtree/Node.h b/Sources/geos/include/geos/index/quadtree/Node.h new file mode 100644 index 0000000..83858fe --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/Node.h @@ -0,0 +1,141 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/Node.java rev 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for composition +#include // for inline + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +//class Coordinate; +class Envelope; +} +} + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \brief + * Represents a node of a Quadtree. + * + * Nodes contain items which have a spatial extent corresponding to + * the node's position in the quadtree. + * + */ +class GEOS_DLL Node: public NodeBase { + +private: + + /// Owned by this class + std::unique_ptr env; + + geom::Coordinate centre; + + int level; + + /** + * Get the subquad for the index. + * If it doesn't exist, create it. + * + * Ownership of the returned object belongs to this class. + */ + Node* getSubnode(int index); + + std::unique_ptr createSubnode(int index); + +protected: + + bool + isSearchMatch(const geom::Envelope& searchEnv) const override + { + return env->intersects(searchEnv); + } + +public: + + // Create a node computing level from given envelope + static std::unique_ptr createNode(const geom::Envelope& env); + + /// Create a node containing the given node and envelope + // + /// @param node if not null, will be inserted to the returned node + /// @param addEnv minimum envelope to use for the node + /// + static std::unique_ptr createExpanded(std::unique_ptr node, + const geom::Envelope& addEnv); + + Node(std::unique_ptr nenv, int nlevel) + : + env(std::move(nenv)), + centre((env->getMinX() + env->getMaxX()) / 2, + (env->getMinY() + env->getMaxY()) / 2), + level(nlevel) + { + } + + ~Node() override {} + + /// Return Envelope associated with this node + /// ownership retained by this object + geom::Envelope* + getEnvelope() + { + return env.get(); + } + + /** \brief + * Returns the subquad containing the envelope. + * Creates the subquad if + * it does not already exist. + */ + Node* getNode(const geom::Envelope* searchEnv); + + /** \brief + * Returns the smallest existing + * node containing the envelope. + */ + NodeBase* find(const geom::Envelope* searchEnv); + + void insertNode(std::unique_ptr node); + + std::string toString() const override; + +}; + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/quadtree/NodeBase.h b/Sources/geos/include/geos/index/quadtree/NodeBase.h new file mode 100644 index 0000000..0044050 --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/NodeBase.h @@ -0,0 +1,161 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/NodeBase.java rev 1.9 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +} +namespace index { +class ItemVisitor; +namespace quadtree { +class Node; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \brief + * The base class for nodes in a Quadtree. + * + */ +class GEOS_DLL NodeBase { + +private: + + void visitItems(const geom::Envelope* searchEnv, + ItemVisitor& visitor); + +public: + + static int getSubnodeIndex(const geom::Envelope* env, + const geom::Coordinate& centre); + + NodeBase(); + + virtual ~NodeBase(); + + std::vector& getItems(); + + /// Add an item to this node. + /// Ownership of the item is left to caller. + void add(void* item); + + /// Push all node items to the given vector, return the argument + std::vector& addAllItems(std::vector& resultItems) const; + + virtual void addAllItemsFromOverlapping(const geom::Envelope& searchEnv, + std::vector& resultItems) const; + + unsigned int depth() const; + + std::size_t size() const; + + std::size_t getNodeCount() const; + + virtual std::string toString() const; + + virtual void visit(const geom::Envelope* searchEnv, ItemVisitor& visitor); + + /** + * Removes a single item from this subtree. + * + * @param itemEnv the envelope containing the item + * @param item the item to remove + * @return true if the item was found and removed + */ + bool remove(const geom::Envelope* itemEnv, void* item); + + bool hasItems() const; + + bool hasChildren() const; + + bool isPrunable() const; + +protected: + + /// Actual items are NOT owned by this class + std::vector items; + + /** + * subquads are numbered as follows: + *

+     *  2 | 3
+     *  --+--
+     *  0 | 1
+     * 
+ * + * Nodes are owned by this class + */ + std::array subnodes; + + virtual bool isSearchMatch(const geom::Envelope& searchEnv) const = 0; +}; + + +// INLINES, To be moved in NodeBase.inl + +inline bool +NodeBase::hasChildren() const +{ + for(const auto& subnode : subnodes) { + if(subnode != nullptr) { + return true; + } + } + + return false; +} + +inline bool +NodeBase::isPrunable() const +{ + return !(hasChildren() || hasItems()); +} + +inline bool +NodeBase::hasItems() const +{ + return ! items.empty(); +} + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/quadtree/Quadtree.h b/Sources/geos/include/geos/index/quadtree/Quadtree.h new file mode 100644 index 0000000..8d92c5e --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/Quadtree.h @@ -0,0 +1,225 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/Quadtree.java rev. 1.16 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // for inheritance +#include // for composition + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace index { +namespace quadtree { +// class Root; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \brief + * A Quadtree is a spatial index structure for efficient querying + * of 2D rectangles. If other kinds of spatial objects + * need to be indexed they can be represented by their + * envelopes + * + * The quadtree structure is used to provide a primary filter + * for range rectangle queries. The query() method returns a list of + * all objects which may intersect the query rectangle. Note that + * it may return objects which do not in fact intersect. + * A secondary filter is required to test for exact intersection. + * Of course, this secondary filter may consist of other tests besides + * intersection, such as testing other kinds of spatial relationships. + * + * This implementation does not require specifying the extent of the inserted + * items beforehand. It will automatically expand to accommodate any extent + * of dataset. + * + * This data structure is also known as an MX-CIF quadtree + * following the usage of Samet and others. + */ +class GEOS_DLL Quadtree: public SpatialIndex { + +private: + + std::vector> newEnvelopes; + + void collectStats(const geom::Envelope& itemEnv); + + Root root; + + /** + * Statistics + * + * minExtent is the minimum envelope extent of all items + * inserted into the tree so far. It is used as a heuristic value + * to construct non-zero envelopes for features with zero X and/or + * Y extent. + * Start with a non-zero extent, in case the first feature inserted has + * a zero extent in both directions. This value may be non-optimal, but + * only one feature will be inserted with this value. + */ + double minExtent; + +public: + /** + * \brief + * Ensure that the envelope for the inserted item has non-zero extents. + * + * Use the current minExtent to pad the envelope, if necessary. + * Can return a new Envelope or the given one (casted to non-const). + */ + static geom::Envelope* ensureExtent(const geom::Envelope* itemEnv, + double minExtent); + + /** + * \brief + * Constructs a Quadtree with zero items. + */ + Quadtree() + : + root(), + minExtent(1.0) + {} + + ~Quadtree() override = default; + + /// Returns the number of levels in the tree. + std::size_t depth(); + + /// Returns the number of items in the tree. + std::size_t size(); + + void insert(const geom::Envelope* itemEnv, void* item) override; + + /** \brief + * Queries the tree and returns items which may lie + * in the given search envelope. + * + * Precisely, the items that are returned are all items in the tree + * whose envelope may intersect the search Envelope. + * Note that some items with non-intersecting envelopes may be + * returned as well; + * the client is responsible for filtering these out. + * In most situations there will be many items in the tree which do not + * intersect the search envelope and which are not returned - thus + * providing improved performance over a simple linear scan. + * + * @param searchEnv the envelope of the desired query area. + * @param ret a vector where items which may intersect the + * search envelope are pushed + */ + void query(const geom::Envelope* searchEnv, std::vector& ret) override; + + + /** \brief + * Queries the tree and visits items which may lie in + * the given search envelope. + * + * Precisely, the items that are visited are all items in the tree + * whose envelope may intersect the search Envelope. + * Note that some items with non-intersecting envelopes may be + * visited as well; + * the client is responsible for filtering these out. + * In most situations there will be many items in the tree which do not + * intersect the search envelope and which are not visited - thus + * providing improved performance over a simple linear scan. + * + * @param searchEnv the envelope of the desired query area. + * @param visitor a visitor object which is passed the visited items + */ + void + query(const geom::Envelope* searchEnv, ItemVisitor& visitor) override + { + /* + * the items that are matched are the items in quads which + * overlap the search envelope + */ + root.visit(searchEnv, visitor); + } + + /** + * Removes a single item from the tree. + * + * @param itemEnv the Envelope of the item to be removed + * @param item the item to remove + * @return true if the item was found (and thus removed) + */ + bool remove(const geom::Envelope* itemEnv, void* item) override; + + /// Return a list of all items in the Quadtree + std::vector* queryAll(); + + std::string toString() const; + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + Quadtree(const Quadtree&) = delete; + Quadtree& operator=(const Quadtree&) = delete; + + + // Move constructor + Quadtree(Quadtree&& other) noexcept : + newEnvelopes(std::move(other.newEnvelopes)), + root(other.root), + minExtent(other.minExtent) + { + // The contents of 'other' are moved to 'this' + // 'other' will be empty after the move + }; + + // Move assignment operator + Quadtree& operator=(Quadtree&& other) noexcept + { + if (this != &other) { + // Move the vector from 'other' to 'this' + // 'newEnvelopes.data' will be empty after this operation + newEnvelopes.clear(); + newEnvelopes = std::move(other.newEnvelopes); + minExtent = other.minExtent; + root = other.root; + } + return *this; + }; + +}; + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/quadtree/Root.h b/Sources/geos/include/geos/index/quadtree/Root.h new file mode 100644 index 0000000..578be33 --- /dev/null +++ b/Sources/geos/include/geos/index/quadtree/Root.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/quadtree/Root.java rev 1.7 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +} +namespace index { +namespace quadtree { +class Node; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace quadtree { // geos::index::quadtree + +/** + * \class Root + * + * \brief + * QuadRoot is the root of a single Quadtree. It is centred at the origin, + * and does not have a defined extent. + */ +class GEOS_DLL Root: public NodeBase { + +private: + + static const geom::Coordinate origin; + + /** + * insert an item which is known to be contained in the tree rooted at + * the given QuadNode root. Lower levels of the tree will be created + * if necessary to hold the item. + */ + void insertContained(Node* tree, const geom::Envelope* itemEnv, + void* item); + +public: + + Root() {} + + ~Root() override {} + + /** + * Insert an item into the quadtree this is the root of. + */ + void insert(const geom::Envelope* itemEnv, void* item); + +protected: + + bool + isSearchMatch(const geom::Envelope& /* searchEnv */) const override + { + return true; + } + +}; + +} // namespace geos::index::quadtree +} // namespace geos::index +} // namespace geos diff --git a/Sources/geos/include/geos/index/strtree/AbstractNode.h b/Sources/geos/include/geos/index/strtree/AbstractNode.h new file mode 100644 index 0000000..e4cacd2 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/AbstractNode.h @@ -0,0 +1,131 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inheritance + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** \brief + * A node of the STR tree. + * + * The children of this node are either more nodes + * (AbstractNodes) or real data (ItemBoundables). + * + * If this node contains real data (rather than nodes), + * then we say that this node is a "leaf node". + * + */ +class GEOS_DLL AbstractNode: public Boundable { +private: + std::vector childBoundables; + int level; +public: + + /* + * Constructs an AbstractNode at the given level in the tree + * @param level 0 if this node is a leaf, 1 if a parent of a leaf, and so on; + * the root node will have the highest level + */ + AbstractNode(int newLevel, std::size_t capacity = 10) : level(newLevel), bounds(nullptr) { + childBoundables.reserve(capacity); + } + + ~AbstractNode() override = default; + + // TODO: change signature to return by ref, + // document ownership of the return + inline std::vector* + getChildBoundables() + { + return &childBoundables; + } + + // TODO: change signature to return by ref, + // document ownership of the return + inline const std::vector* + getChildBoundables() const + { + return &childBoundables; + } + + /** + * Returns a representation of space that encloses this Boundable, + * preferably not much bigger than this Boundable's boundary yet fast to + * test for intersection with the bounds of other Boundables. + * The class of object returned depends on the subclass of + * AbstractSTRtree. + * + * @return an Envelope (for STRtrees), an Interval (for SIRtrees), + * or other object (for other subclasses of AbstractSTRtree) + * + * @see AbstractSTRtree::IntersectsOp + */ + const void* getBounds() const override { + if(bounds == nullptr) { + bounds = computeBounds(); + } + return bounds; + } + + /** + * Returns 0 if this node is a leaf, 1 if a parent of a leaf, and so on; the + * root node will have the highest level + */ + int getLevel() { + return level; + } + + /** + * Adds either an AbstractNode, or if this is a leaf node, a data object + * (wrapped in an ItemBoundable) + */ + void addChildBoundable(Boundable* childBoundable) { + assert(bounds == nullptr); + childBoundables.push_back(childBoundable); + } + + bool isLeaf() const override { + return false; + } + +protected: + + virtual void* computeBounds() const = 0; + + mutable void* bounds; +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/strtree/AbstractSTRtree.h b/Sources/geos/include/geos/index/strtree/AbstractSTRtree.h new file mode 100644 index 0000000..4650146 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/AbstractSTRtree.h @@ -0,0 +1,324 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inlines + +#include +#include +#include // for unique_ptr +#include // for inlines +#include + +// Forward declarations +namespace geos { +namespace index { +class ItemVisitor; +namespace strtree { +class Boundable; +class AbstractNode; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/// A list of boundables. TODO: use a list +typedef std::vector BoundableList; + +/// list contains boundables or lists of boundables. The lists are owned by +/// this class, the plain boundables are held by reference only. +class ItemsList; + +class ItemsListItem { +public: + enum type { + item_is_geometry, + item_is_list + }; + + ItemsListItem(void* item_) + : t(item_is_geometry) + { + item.g = item_; + } + ItemsListItem(ItemsList* item_) + : t(item_is_list) + { + item.l = item_; + } + + type + get_type() const + { + return t; + } + + void* + get_geometry() const + { + assert(t == item_is_geometry); + return item.g; + } + ItemsList* + get_itemslist() const + { + assert(t == item_is_list); + return item.l; + } + + type t; + union { + void* g; + ItemsList* l; + } item; +}; + +class ItemsList : public std::vector { +private: + typedef std::vector base_type; + + static void + delete_item(ItemsListItem& item) + { + if(ItemsListItem::item_is_list == item.t) { + delete item.item.l; + } + } + +public: + ~ItemsList() + { + std::for_each(begin(), end(), &ItemsList::delete_item); + } + + // lists of items need to be deleted in the end + void + push_back(void* item) + { + this->base_type::push_back(ItemsListItem(item)); + } + + // lists of items need to be deleted in the end + void + push_back_owned(ItemsList* itemList) + { + this->base_type::push_back(ItemsListItem(itemList)); + } +}; + +/** \brief + * Base class for STRtree and SIRtree. + * + * STR-packed R-trees are described in: + * P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With + * Application To GIS. Morgan Kaufmann, San Francisco, 2002. + * + * This implementation is based on Boundables rather than just AbstractNodes, + * because the STR algorithm operates on both nodes and + * data, both of which are treated here as Boundables. + * + */ +class GEOS_DLL AbstractSTRtree { + +private: + bool built; + BoundableList* itemBoundables; + + /** + * Creates the levels higher than the given level + * + * @param boundablesOfALevel + * the level to build on + * @param level + * the level of the Boundables, or -1 if the boundables are item + * boundables (that is, below level 0) + * @return the root, which may be a ParentNode or a LeafNode + */ + virtual AbstractNode* createHigherLevels( + BoundableList* boundablesOfALevel, + int level); + + std::unique_ptr sortBoundablesY(const BoundableList* input); + + bool remove(const void* searchBounds, AbstractNode& node, void* item); + bool removeItem(AbstractNode& node, void* item); + + ItemsList* itemsTree(AbstractNode* node); + + AbstractSTRtree(const AbstractSTRtree&) = delete; + AbstractSTRtree& operator=(const AbstractSTRtree&) = delete; + +protected: + + /** \brief + * A test for intersection between two bounds, necessary because + * subclasses of AbstractSTRtree have different implementations of + * bounds. + */ + class GEOS_DLL IntersectsOp { + public: + /** + * For STRtrees, the bounds will be Envelopes; for + * SIRtrees, Intervals; for other subclasses of + * AbstractSTRtree, some other class. + * @param aBounds the bounds of one spatial object + * @param bBounds the bounds of another spatial object + * @return whether the two bounds intersect + */ + virtual bool intersects(const void* aBounds, + const void* bBounds) = 0; + + virtual + ~IntersectsOp() {} + }; + + AbstractNode* root; + + std::vector * nodes; + + // Ownership to caller (TODO: return by unique_ptr) + virtual AbstractNode* createNode(int level) = 0; + + /** \brief + * Sorts the childBoundables then divides them into groups of size M, where + * M is the node capacity. + */ + virtual std::unique_ptr createParentBoundables( + BoundableList* childBoundables, int newLevel); + + virtual AbstractNode* + lastNode(BoundableList* nodeList) + { + assert(!nodeList->empty()); + // Cast from Boundable to AbstractNode + return static_cast(nodeList->back()); + } + + virtual AbstractNode* + getRoot() + { + assert(built); + return root; + } + + /// Also builds the tree, if necessary. + virtual void insert(const void* bounds, void* item); + + /// Also builds the tree, if necessary. + void query(const void* searchBounds, std::vector& foundItems); + + /// Also builds the tree, if necessary. + void query(const void* searchBounds, ItemVisitor& visitor); + + void query(const void* searchBounds, const AbstractNode& node, ItemVisitor& visitor); + + /// Also builds the tree, if necessary. + bool remove(const void* itemEnv, void* item); + + std::unique_ptr boundablesAtLevel(int level); + + // @@ should be size_t, probably + std::size_t nodeCapacity; + + /** + * @return a test for intersection between two bounds, + * necessary because subclasses + * of AbstractSTRtree have different implementations of bounds. + * @see IntersectsOp + */ + virtual IntersectsOp* getIntersectsOp() = 0; + + +public: + + /** \brief + * Constructs an AbstractSTRtree with the specified maximum number of child + * nodes that a node may have. + */ + AbstractSTRtree(std::size_t newNodeCapacity) + : + built(false), + itemBoundables(new BoundableList()), + nodes(new std::vector()), + nodeCapacity(newNodeCapacity) + { + assert(newNodeCapacity > 1); + } + + virtual ~AbstractSTRtree(); + + /** \brief + * Creates parent nodes, grandparent nodes, and so forth up to the root + * node, for the data that has been inserted into the tree. + * + * Can only be + * called once, and thus can be called only after all of the data has been + * inserted into the tree. + */ + virtual void build(); + + /** \brief + * Returns the maximum number of child nodes that a node may have. + */ + virtual std::size_t + getNodeCapacity() + { + return nodeCapacity; + } + + virtual void query(const void* searchBounds, const AbstractNode* node, std::vector* matches); + + /** + * Iterate over all items added thus far. Explicitly does not build + * the tree. + */ + void iterate(ItemVisitor& visitor); + + + /** + * @param level -1 to get items + * @param top an AbstractNode + * @param boundables a BoundableList + */ + virtual void boundablesAtLevel(int level, AbstractNode* top, + BoundableList* boundables); + + /** \brief + * Gets a tree structure (as a nested list) corresponding to the structure + * of the items and nodes in this tree. + * + * The returned Lists contain either Object items, or Lists which + * correspond to subtrees of the tree Subtrees which do not contain + * any items are not included. + * + * Builds the tree if necessary. + * + * @note The caller is responsible for releasing the list + * + * @return a List of items and/or Lists + */ + ItemsList* itemsTree(); +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/strtree/Boundable.h b/Sources/geos/include/geos/index/strtree/Boundable.h new file mode 100644 index 0000000..f3edb03 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/Boundable.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/// A spatial object in an AbstractSTRtree. +class GEOS_DLL Boundable { +public: + /** + * Returns a representation of space that encloses this Boundable, + * preferably not much bigger than this Boundable's boundary yet + * fast to test for intersection with the bounds of other Boundables. + * + * The class of object returned depends + * on the subclass of AbstractSTRtree. + * + * @return an Envelope (for STRtrees), an Interval (for SIRtrees), + * or other object (for other subclasses of AbstractSTRtree) + * + * @see AbstractSTRtree::IntersectsOp + */ + virtual const void* getBounds() const = 0; + + virtual bool isLeaf() const = 0; + virtual ~Boundable() {} +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/strtree/BoundablePair.h b/Sources/geos/include/geos/index/strtree/BoundablePair.h new file mode 100644 index 0000000..1b65c8f --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/BoundablePair.h @@ -0,0 +1,122 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/strtree/BoundablePair.java (JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace index { +namespace strtree { + +/** + * @brief A pair of [Boundables](@ref Boundable), whose + * leaf items support a distance metric between them. + * + * Used to compute the distance between the members, + * and to expand a member relative to the other + * in order to produce new branches of the + * Branch-and-Bound evaluation tree. + * Provides an ordering based on the distance between the members, + * which allows building a priority queue by minimum distance. + * + * @author Martin Davis + * + */ +class BoundablePair { +private: + const Boundable* boundable1; + const Boundable* boundable2; + ItemDistance* itemDistance; + double mDistance; + +public: + struct BoundablePairQueueCompare { + bool + operator()(const BoundablePair* a, const BoundablePair* b) + { + return a->getDistance() > b->getDistance(); + } + }; + + typedef std::priority_queue, BoundablePairQueueCompare> BoundablePairQueue; + BoundablePair(const Boundable* boundable1, const Boundable* boundable2, ItemDistance* itemDistance); + + /** + * Gets one of the member {@link Boundable}s in the pair + * (indexed by [0, 1]). + * + * @param i the index of the member to return (0 or 1) + * @return the chosen member + */ + const Boundable* getBoundable(int i) const; + + /** \brief + * Computes the distance between the {@link Boundable}s in this pair. + * The boundables are either composites or leaves. + * + * If either is composite, the distance is computed as the minimum distance + * between the bounds. + * If both are leaves, the distance is computed by + * ItemDistance::distance(const ItemBoundable* item1, const ItemBoundable* item2). + * + * @return the distance between the items + */ + double distance() const; + + /** \brief + * Gets the minimum possible distance between the Boundables in this pair. + * + * If the members are both items, this will be the + * exact distance between them. + * Otherwise, this distance will be a lower bound on + * the distances between the items in the members. + * + * @return the exact or lower bound distance for this pair + */ + double getDistance() const; + + /** + * Tests if both elements of the pair are leaf nodes + * + * @return true if both pair elements are leaf nodes + */ + bool isLeaves() const; + + /** \brief + * Computes the maximum distance between any + * two items in the pair of nodes. + * + * @return the maximum distance between items in the pair + */ + double maximumDistance(); + + static bool isComposite(const Boundable* item); + + static double area(const Boundable* b); + + void expandToQueue(BoundablePairQueue&, double minDistance); + void expand(const Boundable* bndComposite, const Boundable* bndOther, bool isFlipped, BoundablePairQueue& priQ, + double minDistance); +}; + +} +} +} diff --git a/Sources/geos/include/geos/index/strtree/EnvelopeUtil.h b/Sources/geos/include/geos/index/strtree/EnvelopeUtil.h new file mode 100644 index 0000000..dd767d3 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/EnvelopeUtil.h @@ -0,0 +1,33 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ +#pragma once + +#include + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +class GEOS_DLL EnvelopeUtil { +public: + // EnvelopeUtil(const void* newBounds, void* newItem); + // ~EnvelopeUtil() override = default; + + static double maximumDistance(const geom::Envelope* env1, const geom::Envelope* env2); + +}; + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos diff --git a/Sources/geos/include/geos/index/strtree/GeometryItemDistance.h b/Sources/geos/include/geos/index/strtree/GeometryItemDistance.h new file mode 100644 index 0000000..680975f --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/GeometryItemDistance.h @@ -0,0 +1,44 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/strtree/GeometryItemDistance.java (JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace index { +namespace strtree { +class GEOS_DLL GeometryItemDistance : public ItemDistance { +public: + /** + * Computes the distance between two {@link Geometry} items, + * using the {@link Geometry#distance(Geometry)} method. + * + * @param item1 an item which is a Geometry + * @param item2 an item which is a Geometry + * @return the distance between the geometries + * @throws ClassCastException if either item is not a Geometry + */ + double distance(const ItemBoundable* item1, const ItemBoundable* item2) override; +}; + +} +} +} + diff --git a/Sources/geos/include/geos/index/strtree/Interval.h b/Sources/geos/include/geos/index/strtree/Interval.h new file mode 100644 index 0000000..9842356 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/Interval.h @@ -0,0 +1,60 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/// A contiguous portion of 1D-space. Used internally by SIRtree. +// +/// @see SIRtree +/// +class GEOS_DLL Interval { +public: + Interval(double newMin, double newMax) : imin(newMin), imax(newMax) { + assert(std::isnan(newMin) || std::isnan(newMax) || imin <= imax); + } + + double getMin() const { return imin; } + double getMax() const { return imax; } + double getWidth() const { return imax - imin; } + double getCentre() const { return (imin + imax) / 2; } + Interval* expandToInclude(const Interval* other) { + imax = std::max(imax, other->imax); + imin = std::min(imin, other->imin); + return this; + } + bool intersects(const Interval* other) const { + return !(other->imin > imax || other->imax < imin); + } + bool equals(const Interval* other) const { + return imin == other->imin && imax == other->imax; + } +private: + double imin; + double imax; +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/strtree/ItemBoundable.h b/Sources/geos/include/geos/index/strtree/ItemBoundable.h new file mode 100644 index 0000000..d1838c5 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/ItemBoundable.h @@ -0,0 +1,59 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** + * \brief + * Boundable wrapper for a non-Boundable spatial object. + * Used internally by AbstractSTRtree. + * + * \todo TODO: It's unclear who takes ownership of passed newBounds and newItem objects. + */ +class GEOS_DLL ItemBoundable: public Boundable { +public: + + ItemBoundable(const void* newBounds, void* newItem) : bounds(newBounds), item(newItem) {} + ~ItemBoundable() override = default; + + bool isLeaf() const override { + return true; + } + + const void* getBounds() const override { + return bounds; + } + + void* getItem() const { + return item; + } + +private: + + const void* bounds; + void* item; +}; + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/strtree/ItemDistance.h b/Sources/geos/include/geos/index/strtree/ItemDistance.h new file mode 100644 index 0000000..96a17f7 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/ItemDistance.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/strtree/ItemDistance.java (JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { +namespace strtree { + +/** \brief + * A function method which computes the distance between two [ItemBoundables](\ref ItemBoundable) + * in an STRtree. Used for Nearest Neighbour searches. + * + * \author Martin Davis + */ +class GEOS_DLL ItemDistance { +public: + /** \brief + * Computes the distance between two items. + * + * @param item1 + * @param item2 + * @return the distance between the items + * + * @throws IllegalArgumentException if the metric is not applicable to the arguments + */ + virtual double distance(const ItemBoundable* item1, const ItemBoundable* item2) = 0; +}; +} +} +} + diff --git a/Sources/geos/include/geos/index/strtree/SIRtree.h b/Sources/geos/include/geos/index/strtree/SIRtree.h new file mode 100644 index 0000000..6b2f1f1 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/SIRtree.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance +#include // for inline + +#include +#include + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** \brief + * One-dimensional version of an STR-packed R-tree. + * + * SIR stands for "Sort-Interval-Recursive". + * + * STR-packed R-trees are described in: + * P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With + * Application To GIS. Morgan Kaufmann, San Francisco, 2002. + * + * @see STRtree + */ +class GEOS_DLL SIRtree: public AbstractSTRtree { + using AbstractSTRtree::insert; + using AbstractSTRtree::query; + +public: + + /** \brief + * Constructs an SIRtree with the default node capacity. + */ + SIRtree(); + + /** \brief + * Constructs an SIRtree with the given maximum number of child nodes + * that a node may have + */ + SIRtree(std::size_t nodeCapacity); + + ~SIRtree() override; + + void insert(double x1, double x2, void* item); + + /** + * Returns items whose bounds intersect the given bounds. + * @param x1 possibly equal to x2 + * @param x2 + */ + std::vector* + query(double x1, double x2) + { + std::vector* results = new std::vector(); + Interval interval(std::min(x1, x2), std::max(x1, x2)); + AbstractSTRtree::query(&interval, *results); + return results; + } + + /** + * Returns items whose bounds intersect the given value. + */ + std::vector* + query(double x) + { + return query(x, x); + } + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + SIRtree(const SIRtree&) = delete; + SIRtree& operator=(const SIRtree&) = delete; + +protected: + + class SIRIntersectsOp: public AbstractSTRtree::IntersectsOp { + public: + bool intersects(const void* aBounds, const void* bBounds) override; + }; + + /** \brief + * Sorts the childBoundables then divides them into groups of size M, where + * M is the node capacity. + */ + std::unique_ptr createParentBoundables( + BoundableList* childBoundables, int newLevel) override; + + AbstractNode* createNode(int level) override; + + IntersectsOp* + getIntersectsOp() override + { + return intersectsOp; + } + + std::unique_ptr sortBoundables(const BoundableList* input); + +private: + IntersectsOp* intersectsOp; + std::vector> intervals; +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/strtree/STRtree.h b/Sources/geos/include/geos/index/strtree/STRtree.h new file mode 100644 index 0000000..3647d1c --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/STRtree.h @@ -0,0 +1,177 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/strtree/STRtree.java rev. 1.11 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inheritance +#include // for inheritance +#include // for inlines + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace index { +namespace strtree { +class Boundable; +} +} +} + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** + * \brief + * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. + * For two-dimensional spatial data. + * + * The STR packed R-tree is simple to implement and maximizes space + * utilization; that is, as many leaves as possible are filled to capacity. + * Overlap between nodes is far less than in a basic R-tree. However, once the + * tree has been built (explicitly or on the first call to #query), items may + * not be added or removed. + * + * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial + * Databases With Application To GIS. Morgan Kaufmann, San Francisco, 2002. + * + */ +class GEOS_DLL STRtree: public AbstractSTRtree, public SpatialIndex { + using AbstractSTRtree::insert; + using AbstractSTRtree::query; + +private: + class GEOS_DLL STRIntersectsOp: public AbstractSTRtree::IntersectsOp { + public: + bool intersects(const void* aBounds, const void* bBounds) override; + }; + + /** + * Creates the parent level for the given child level. First, orders the items + * by the x-values of the midpoints, and groups them into vertical slices. + * For each slice, orders the items by the y-values of the midpoints, and + * group them into runs of size M (the node capacity). For each run, creates + * a new (parent) node. + */ + std::unique_ptr createParentBoundables(BoundableList* childBoundables, int newLevel) override; + + std::unique_ptr createParentBoundablesFromVerticalSlices(std::vector* verticalSlices, + int newLevel); + + STRIntersectsOp intersectsOp; + + std::unique_ptr sortBoundablesY(const BoundableList* input); + std::unique_ptr sortBoundablesX(const BoundableList* input); + + std::unique_ptr createParentBoundablesFromVerticalSlice( + BoundableList* childBoundables, + int newLevel); + + /** + * @param childBoundables Must be sorted by the x-value of + * the envelope midpoints + * @return + */ + std::vector* verticalSlices( + BoundableList* childBoundables, + std::size_t sliceCount); + + bool isWithinDistance(BoundablePair* initBndPair, double maxDistance); + +protected: + + AbstractNode* createNode(int level) override; + + IntersectsOp* + getIntersectsOp() override + { + return &intersectsOp; + } + +public: + + ~STRtree() override = default; + + /** + * Constructs an STRtree with the given maximum number of child nodes that + * a node may have + */ + STRtree(std::size_t nodeCapacity = 10); + + void insert(const geom::Envelope* itemEnv, void* item) override; + + //static double centreX(const geom::Envelope *e); + + static double + avg(double a, double b) + { + return (a + b) / 2.0; + } + + static double + centreY(const geom::Envelope* e) + { + return STRtree::avg(e->getMinY(), e->getMaxY()); + } + + void + query(const geom::Envelope* searchEnv, std::vector& matches) override + { + AbstractSTRtree::query(searchEnv, matches); + } + + void + query(const geom::Envelope* searchEnv, ItemVisitor& visitor) override + { + return AbstractSTRtree::query(searchEnv, visitor); + } + + std::pair nearestNeighbour(ItemDistance* itemDist); + const void* nearestNeighbour(const geom::Envelope* env, const void* item, ItemDistance* itemDist); + std::pair nearestNeighbour(STRtree* tree, ItemDistance* itemDist); + std::pair nearestNeighbour(BoundablePair* initBndPair); + std::pair nearestNeighbour(BoundablePair* initBndPair, double maxDistance); + + bool + remove(const geom::Envelope* itemEnv, void* item) override + { + return AbstractSTRtree::remove(itemEnv, item); + } + + bool isWithinDistance(STRtree* tree, ItemDistance* itemDist, double maxDistance); + +}; + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/strtree/SimpleSTRdistance.h b/Sources/geos/include/geos/index/strtree/SimpleSTRdistance.h new file mode 100644 index 0000000..3024f57 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/SimpleSTRdistance.h @@ -0,0 +1,166 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + + + + + +class GEOS_DLL SimpleSTRpair { + +private: + + SimpleSTRnode* node1; + SimpleSTRnode* node2; + ItemDistance* itemDistance; + double m_distance; + + /** \brief + * Computes the distance between the {@link SimpleSTRnode}s in this pair. + * The nodes are either composites or leaves. + * + * If either is composite, the distance is computed as the minimum distance + * between the bounds. + * If both are leaves, the distance is computed by + * ItemDistance::distance(const void* item1, const void* item2). + * + * @return the distance between the items + */ + double distance(); + +public: + + SimpleSTRpair(SimpleSTRnode* p_node1, SimpleSTRnode* p_node2, ItemDistance* p_itemDistance) + : node1(p_node1) + , node2(p_node2) + , itemDistance(p_itemDistance) + { + m_distance = distance(); + } + + SimpleSTRnode* getNode(int i) const; + + /** \brief + * Gets the minimum possible distance between the SimpleSTRnode in this pair. + * + * If the members are both items, this will be the + * exact distance between them. + * Otherwise, this distance will be a lower bound on + * the distances between the items in the members. + * + * @return the exact or lower bound distance for this pair + */ + double getDistance() const; + + /** + * Tests if both elements of the pair are leaf nodes + * + * @return true if both pair elements are leaf nodes + */ + bool isLeaves() const; + + /** \brief + * Computes the maximum distance between any + * two items in the pair of nodes. + * + * @return the maximum distance between items in the pair + */ + double maximumDistance(); + + friend std::ostream& operator<<(std::ostream& os, SimpleSTRpair& pair); + + +}; + + + +class GEOS_DLL SimpleSTRdistance { + + +public: + + struct STRpairQueueCompare { + bool + operator()(const SimpleSTRpair* a, const SimpleSTRpair* b) + { + return a->getDistance() > b->getDistance(); + } + }; + + typedef std::priority_queue, + STRpairQueueCompare> STRpairQueue; + + + /* Initialize class */ + SimpleSTRdistance(SimpleSTRnode* root1, SimpleSTRnode* root2, ItemDistance* p_itemDistance); + + /* Turn over the calculation */ + std::pair nearestNeighbour(); + bool isWithinDistance(double maxDistance); + + +private: + + std::deque pairStore; + SimpleSTRpair* initPair; + ItemDistance* itemDistance; + + /* Utility method to store allocated pairs */ + SimpleSTRpair* createPair(SimpleSTRnode* p_node1, SimpleSTRnode* p_node2, ItemDistance* p_itemDistance); + + std::pair nearestNeighbour(SimpleSTRpair* p_initPair); + std::pair nearestNeighbour(SimpleSTRpair* p_initPair, double maxDistance); + + bool isWithinDistance(SimpleSTRpair* p_initPair, double maxDistance); + + void expandToQueue(SimpleSTRpair* pair, STRpairQueue&, double minDistance); + void expand(SimpleSTRnode* nodeComposite, SimpleSTRnode* nodeOther, + bool isFlipped, STRpairQueue& priQ, double minDistance); + + +}; + + + + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/strtree/SimpleSTRnode.h b/Sources/geos/include/geos/index/strtree/SimpleSTRnode.h new file mode 100644 index 0000000..e67538d --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/SimpleSTRnode.h @@ -0,0 +1,141 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** \brief + * A node of the STR tree. + * + */ +class GEOS_DLL SimpleSTRnode : public ItemBoundable { + +private: + + std::vector childNodes; + void *item; + geom::Envelope bounds; + std::size_t level; + +public: + + /* + * Constructs an AbstractNode at the given level in the tree + */ + SimpleSTRnode(std::size_t newLevel, const geom::Envelope *p_env, void* p_item, std::size_t capacity = 10) + : ItemBoundable(p_env, p_item) + , item(p_item) + , bounds() + , level(newLevel) + { + childNodes.reserve(capacity); + if (p_env) { + bounds = *p_env; + } + + } + + SimpleSTRnode(std::size_t newLevel) + : SimpleSTRnode(newLevel, nullptr, nullptr) + {} + + void toString(std::ostream& os, int indentLevel) const; + + std::size_t getNumNodes() const; + std::size_t getNumLeafNodes() const; + + const std::vector& + getChildNodes() const + { + return childNodes; + } + + void* getItem() const { + return item; + } + + bool removeItem(void *item); + bool removeChild(SimpleSTRnode *child); + + /** + * Returns a representation of space that encloses this Node + */ + const inline geom::Envelope& getEnvelope() const { + return bounds; + } + + const void* getBounds() const override { + return &bounds; + } + + /** + * Returns 0 if this node is a leaf, 1 if a parent of a leaf, and so on; the + * root node will have the highest level + */ + std::size_t getLevel() const { + return level; + } + + std::size_t size() const { + return childNodes.size(); + } + + /** + * Adds either an AbstractNode, or if this is a leaf node, a data object + * (wrapped in an ItemBoundable) + */ + void addChildNode(SimpleSTRnode* childNode); + + bool isLeaf() const override + { + return level == 0; + } + + bool isComposite() const + { + return ! isLeaf(); + } + + double area() const + { + return bounds.getArea(); + } + + +}; + + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/strtree/SimpleSTRtree.h b/Sources/geos/include/geos/index/strtree/SimpleSTRtree.h new file mode 100644 index 0000000..9cc5919 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/SimpleSTRtree.h @@ -0,0 +1,189 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include +#include + +#include +#include + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Envelope; +} +namespace index { +namespace strtree { +class ItemDistance; +} +} +} + + +namespace geos { +namespace index { // geos::index +namespace strtree { // geos::index::strtree + +/** + * \brief + * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. + * For two-dimensional spatial data. + * + * The STR packed R-tree is simple to implement and maximizes space + * utilization; that is, as many leaves as possible are filled to capacity. + * Overlap between nodes is far less than in a basic R-tree. However, once the + * tree has been built (explicitly or on the first call to query), items may + * not be added or removed. + * + * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial + * Databases With Application To GIS. Morgan Kaufmann, San Francisco, 2002. + * + */ +class GEOS_DLL SimpleSTRtree: public SpatialIndex { + + +private: + + /* Members */ + std::deque nodesQue; + std::vector nodes; + std::size_t nodeCapacity; + bool built; + + /* + * Allocate node in nodesQue std::deque for memory locality, + * return reference to node. + */ + SimpleSTRnode* createNode(int newLevel, const geom::Envelope* itemEnv, void* item); + SimpleSTRnode* createNode(int newLevel); + + + void build(); + + static void sortNodesY(std::vector& nodeList); + static void sortNodesX(std::vector& nodeList); + + void query(const geom::Envelope* searchEnv, const SimpleSTRnode* node, ItemVisitor& visitor); + void query(const geom::Envelope* searchEnv, const SimpleSTRnode* node, std::vector& matches); + + /* Turn off copy constructors for MSVC */ + SimpleSTRtree(const SimpleSTRtree&) = delete; + SimpleSTRtree& operator=(const SimpleSTRtree&) = delete; + + std::vector createHigherLevels( + std::vector& nodesOfALevel, int level); + + void addParentNodesFromVerticalSlice( + std::vector& verticalSlice, + int newLevel, + std::vector& parentNodes); + + std::vector createParentNodes( + std::vector& childNodes, + int newLevel); + + bool remove(const geom::Envelope* searchBounds, SimpleSTRnode* node, void* item); + + +public: + + /* Member */ + SimpleSTRnode* root; + + /** + * Constructs an STRtree with the given maximum number of child nodes that + * a node may have + */ + SimpleSTRtree(std::size_t capacity = 10) + : nodeCapacity(capacity) + , built(false) + , root(nullptr) + {}; + + std::size_t getNodeCapacity() const { + return nodeCapacity; + } + + std::size_t getNumLeafNodes() const { + if (!root) + return 0; + else + return root->getNumLeafNodes(); + } + + + + bool getBuilt() const { + return built; + } + + SimpleSTRnode* getRoot() { + build(); + return root; + } + + void insert(geom::Geometry* geom); + + void insert(const geom::Envelope* itemEnv, void* item) override; + + void iterate(ItemVisitor& visitor); + + void query(const geom::Envelope* searchEnv, std::vector& matches) override; + + void query(const geom::Envelope* searchEnv, ItemVisitor& visitor) override; + + bool remove(const geom::Envelope* searchBounds, void* item) override; + + friend std::ostream& operator<<(std::ostream& os, const SimpleSTRtree& tree); + + + /*********************************************************************************/ + /* Nearest neighbor searches, public API */ + + /* Internal nearest node to node */ + std::pair nearestNeighbour(ItemDistance* itemDist); + + /* Nearest to another geometry/item */ + const void* nearestNeighbour(const geom::Envelope* env, const void* item, ItemDistance* itemDist); + + /* Nearest to another tree */ + std::pair nearestNeighbour(SimpleSTRtree& tree, ItemDistance* itemDist); + + /* Radius test */ + bool isWithinDistance(SimpleSTRtree& tree, ItemDistance* itemDist, double maxDistance); + + +}; + +} // namespace geos::index::strtree +} // namespace geos::index +} // namespace geos + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/strtree/TemplateSTRNode.h b/Sources/geos/include/geos/index/strtree/TemplateSTRNode.h new file mode 100644 index 0000000..1212e77 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/TemplateSTRNode.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { +namespace strtree { + +template +class TemplateSTRNode { +private: + using BoundsType = typename BoundsTraits::BoundsType; + + BoundsType bounds; + + union Body { + ItemType item; + const TemplateSTRNode* childrenEnd; + + explicit Body (ItemType&& p_item) : item(std::forward(p_item)) {} + + explicit Body (const ItemType& p_item) : item(p_item) {} + + explicit Body (const TemplateSTRNode* p_childrenEnd) : childrenEnd(p_childrenEnd) {} + + ~Body() = default; + } data; + const TemplateSTRNode* children; + +public: + ~TemplateSTRNode() { + if (isLeaf()) { + data.item.~ItemType(); + } + } + + TemplateSTRNode(ItemType&& p_item, const BoundsType& env) : + bounds(env), + data(std::forward(p_item)), + children(nullptr) + {} + + TemplateSTRNode(const ItemType& p_item, const BoundsType& env) : + bounds(env), + data(p_item), + children(nullptr) + {} + + TemplateSTRNode(const TemplateSTRNode* begin, const TemplateSTRNode* end) : + bounds(boundsFromChildren(begin, end)), + data(end), + children(begin) + {} + + const TemplateSTRNode* beginChildren() const { + return children; + } + + const TemplateSTRNode* endChildren() const { + return data.childrenEnd; + } + + bool isDeleted() const { + return children == this; + } + + bool isLeaf() const { + return children == nullptr || children == this; + } + + bool isComposite() const { + return !isLeaf(); + } + + bool boundsIntersect(const BoundsType& queryBounds) const { + return BoundsTraits::intersects(getBounds(), queryBounds); + } + + double getSize() const { + return BoundsTraits::size(getBounds()); + } + + static BoundsType boundsFromChildren(const TemplateSTRNode* from, const TemplateSTRNode* to) { + BoundsType bnds = from->getBounds(); + + for (auto *child = from + 1; child < to; ++child) { + BoundsTraits::expandToInclude(bnds, child->getBounds()); + } + + return bnds; + } + + BoundsType boundsFromChildren() const { + return boundsFromChildren(children, data.childrenEnd); + } + + const BoundsType& getBounds() const { + return bounds; + } + + std::size_t getNumNodes() const + { + if (isLeaf()) { + return isDeleted() ? 0 : 1; + } + + std::size_t count = 1; + for (const auto* child = beginChildren(); child != endChildren(); ++child) { + count += child->getNumNodes(); + } + + return count; + } + + std::size_t getNumLeafNodes() const + { + if (isLeaf()) { + return isDeleted() ? 0 : 1; + } + + std::size_t count = 0; + for (const auto* child = beginChildren(); child != endChildren(); ++child) { + count += child->getNumNodes(); + } + return count; + } + + const ItemType& getItem() const { + assert(!isDeleted()); + return data.item; + } + + void removeItem() { + children = this; + } + +}; + +} +} +} + diff --git a/Sources/geos/include/geos/index/strtree/TemplateSTRNodePair.h b/Sources/geos/include/geos/index/strtree/TemplateSTRNodePair.h new file mode 100644 index 0000000..f164f89 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/TemplateSTRNodePair.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace index { +namespace strtree { + +template +class TemplateSTRNodePair { +public: + using Node = TemplateSTRNode; + + TemplateSTRNodePair(const Node &node1, const Node &node2, ItemDistance& id) + : m_node1(&node1), m_node2(&node2), m_distance(distance(id)) {} + + bool isLeaves() const { + return getFirst().isLeaf() && getSecond().isLeaf(); + } + + double getDistance() const { + return m_distance; + } + + std::pair getItems() const { + assert(isLeaves()); + return std::make_pair(getFirst().getItem(), getSecond().getItem()); + } + + const Node &getFirst() const { + return *m_node1; + } + + const Node &getSecond() const { + return *m_node2; + } + + double distance(ItemDistance& id) { + if (isLeaves()) { + return id(getFirst().getItem(), getSecond().getItem()); + } else { + return BoundsTraits::distance(getFirst().getBounds(), getSecond().getBounds()); + } + } + + double maximumDistance() { + return BoundsTraits::maxDistance(getFirst().getBounds(), getSecond().getBounds()); + } + +private: + const Node* m_node1; + const Node* m_node2; + double m_distance; +}; + +} +} +} + diff --git a/Sources/geos/include/geos/index/strtree/TemplateSTRtree.h b/Sources/geos/include/geos/index/strtree/TemplateSTRtree.h new file mode 100644 index 0000000..381aeee --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/TemplateSTRtree.h @@ -0,0 +1,817 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace geos { +namespace index { +namespace strtree { + +/** + * \brief + * A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. + * For one- or two-dimensional spatial data. + * + * The STR packed R-tree is simple to implement and maximizes space + * utilization; that is, as many leaves as possible are filled to capacity. + * Overlap between nodes is far less than in a basic R-tree. However, once the + * tree has been built (explicitly or on the first call to `query`), items may + * not be added or removed. + * + * A user will instantiate `TemplateSTRtree` instead of `TemplateSTRtreeImpl`; + * this structure is used so that `TemplateSTRtree` can implement the + * requirements of the `SpatialIndex` interface, which is only possible when + * `ItemType` is a pointer. + * + * Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial + * Databases With Application To GIS. Morgan Kaufmann, San Francisco, 2002. + * + */ +template +class TemplateSTRtreeImpl { +public: + using Node = TemplateSTRNode; + using NodeList = std::vector; + using NodeListIterator = typename NodeList::iterator; + using BoundsType = typename BoundsTraits::BoundsType; + + class Iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = ItemType; + using difference_type = typename NodeList::const_iterator::difference_type; + using pointer = ItemType*; + using reference = ItemType&; + + Iterator(typename NodeList::const_iterator&& iter, + typename NodeList::const_iterator&& end) : m_iter(iter), m_end(end) { + skipDeleted(); + } + + const ItemType& operator*() const { + return m_iter->getItem(); + } + + Iterator& operator++() { + m_iter++; + skipDeleted(); + return *this; + } + + friend bool operator==(const Iterator& a, const Iterator& b) { + return a.m_iter == b.m_iter; + } + + friend bool operator!=(const Iterator& a, const Iterator& b) { + return a.m_iter != b.m_iter; + } + + private: + void skipDeleted() { + while(m_iter != m_end && m_iter->isDeleted()) { + m_iter++; + } + } + + typename NodeList::const_iterator m_iter; + typename NodeList::const_iterator m_end; + }; + + class Items { + public: + explicit Items(TemplateSTRtreeImpl& tree) : m_tree(tree) {} + + Iterator begin() { + return Iterator(m_tree.nodes.cbegin(), + std::next(m_tree.nodes.cbegin(), static_cast(m_tree.numItems))); + } + + Iterator end() { + return Iterator(std::next(m_tree.nodes.cbegin(), static_cast(m_tree.numItems)), + std::next(m_tree.nodes.cbegin(), static_cast(m_tree.numItems))); + } + private: + TemplateSTRtreeImpl& m_tree; + }; + + /// \defgroup construct Constructors + /// @{ + + /** + * Constructs a tree with the given maximum number of child nodes that + * a node may have. + */ + explicit TemplateSTRtreeImpl(size_t p_nodeCapacity = 10) : + root(nullptr), + nodeCapacity(p_nodeCapacity), + numItems(0) + { + validateConstruction(); + } + + /** + * Constructs a tree with the given maximum number of child nodes that + * a node may have, with the expected total number of items in the tree used + * to pre-allocate storage. + */ + TemplateSTRtreeImpl(size_t p_nodeCapacity, size_t itemCapacity) : + root(nullptr), + nodeCapacity(p_nodeCapacity), + numItems(0) + { + auto finalSize = treeSize(itemCapacity); + nodes.reserve(finalSize); + validateConstruction(); + } + + /** + * Copy constructor, needed because mutex is not copyable + */ + TemplateSTRtreeImpl(const TemplateSTRtreeImpl& other) : + root(other.root), + nodeCapacity(other.nodeCapacity), + numItems(other.numItems) { + nodes = other.nodes; + } + + TemplateSTRtreeImpl& operator=(TemplateSTRtreeImpl other) + { + root = other.root; + nodeCapacity = other.nodeCapacity; + numItems = other.numItems; + nodes = other.nodes; + return *this; + } + + /// @} + /// \defgroup insert Insertion + /// @{ + + /** Move the given item into the tree */ + void insert(ItemType&& item) { + insert(BoundsTraits::fromItem(item), std::forward(item)); + } + + /** Insert a copy of the given item into the tree */ + void insert(const ItemType& item) { + insert(BoundsTraits::fromItem(item), item); + } + + /** Move the given item into the tree */ + void insert(const BoundsType& itemEnv, ItemType&& item) { + if (!BoundsTraits::isNull(itemEnv)) { + createLeafNode(std::forward(item), itemEnv); + } + } + + /** Insert a copy of the given item into the tree */ + void insert(const BoundsType& itemEnv, const ItemType& item) { + if (!BoundsTraits::isNull(itemEnv)) { + createLeafNode(item, itemEnv); + } + } + + /// @} + /// \defgroup NN Nearest-neighbor + /// @{ + + /** Determine the two closest items in the tree using distance metric `distance`. */ + template + std::pair nearestNeighbour(ItemDistance& distance) { + return nearestNeighbour(*this, distance); + } + + /** Determine the two closest items in the tree using distance metric `ItemDistance`. */ + template + std::pair nearestNeighbour() { + return nearestNeighbour(*this); + } + + /** Determine the two closest items this tree and `other` tree using distance metric `distance`. */ + template + std::pair nearestNeighbour(TemplateSTRtreeImpl & other, + ItemDistance & distance) { + if (!getRoot() || !other.getRoot()) { + return { nullptr, nullptr }; + } + + TemplateSTRtreeDistance td(distance); + return td.nearestNeighbour(*root, *other.root); + } + + /** Determine the two closest items this tree and `other` tree using distance metric `ItemDistance`. */ + template + std::pair nearestNeighbour(TemplateSTRtreeImpl& other) { + ItemDistance id; + return nearestNeighbour(other, id); + } + + template + ItemType nearestNeighbour(const BoundsType& env, const ItemType& item, ItemDistance& itemDist) { + build(); + + if (getRoot() == nullptr) { + return nullptr; + } + + TemplateSTRNode bnd(item, env); + TemplateSTRNodePair pair(*getRoot(), bnd, itemDist); + + TemplateSTRtreeDistance td(itemDist); + return td.nearestNeighbour(pair).first; + } + + template + ItemType nearestNeighbour(const BoundsType& env, const ItemType& item) { + ItemDistance id; + return nearestNeighbour(env, item, id); + } + + template + bool isWithinDistance(TemplateSTRtreeImpl& other, double maxDistance) { + ItemDistance itemDist; + + if (!getRoot() || !other.getRoot()) { + return false; + } + + TemplateSTRtreeDistance td(itemDist); + return td.isWithinDistance(*root, *other.root, maxDistance); + } + + /// @} + /// \defgroup query Query + /// @{ + + // Query the tree using the specified visitor. The visitor must be callable + // either with a single argument of `const ItemType&` or with the + // arguments `(const BoundsType&, const ItemType&). + // The visitor need not return a value, but if it does return a value, + // false values will be taken as a signal to stop the query. + template + void query(const BoundsType& queryEnv, Visitor &&visitor) { + if (!built()) { + build(); + } + + if (root && root->boundsIntersect(queryEnv)) { + if (root->isLeaf()) { + visitLeaf(visitor, *root); + } else { + query(queryEnv, *root, visitor); + } + } + } + + // Query the tree for all pairs whose bounds intersect. The visitor must + // be callable with arguments (const ItemType&, const ItemType&). + // The visitor will be called for each pair once, with first-inserted + // item used for the first argument. + // The visitor need not return a value, but if it does return a value, + // false values will be taken as a signal to stop the query. + template + void queryPairs(Visitor&& visitor) { + if (!built()) { + build(); + } + + if (numItems < 2) { + return; + } + + for (std::size_t i = 0; i < numItems; i++) { + queryPairs(nodes[i], *root, visitor); + } + } + + // Query the tree and collect items in the provided vector. + void query(const BoundsType& queryEnv, std::vector& results) { + query(queryEnv, [&results](const ItemType& x) { + results.push_back(x); + }); + } + + /** + * Returns a depth-first iterator over all items in the tree. + */ + Items items() { + build(); + return Items(*this); + } + + /** + * Iterate over all items added thus far. Explicitly does not build + * the tree. + */ + template + void iterate(F&& func) { + auto n = built() ? numItems : nodes.size(); + for (size_t i = 0; i < n; i++) { + if (!nodes[i].isDeleted()) { + func(nodes[i].getItem()); + } + } + } + + /// @} + /// \defgroup remove Item removal + /// @{ + + bool remove(const BoundsType& itemEnv, const ItemType& item) { + build(); + + if (root == nullptr) { + return false; + } + + if (root->isLeaf()) { + if (!root->isDeleted() && root->getItem() == item) { + root->removeItem(); + return true; + } + return false; + } + + return remove(itemEnv, *root, item); + } + + /// @} + /// \defgroup introspect Introspection + /// @{ + + /** Determine whether the tree has been built, and no more items may be added. */ + bool built() const { + return root != nullptr; + } + + /** Determine whether the tree has been built, and no more items may be added. */ + const Node* getRoot() { + build(); + return root; + } + + /// @} + + /** Build the tree if it has not already been built. */ + void build() { + std::lock_guard lock(lock_); + + if (built()) { + return; + } + + if (nodes.empty()) { + return; + } + + numItems = nodes.size(); + + // compute final size of tree and set it aside in a single + // block of memory + auto finalSize = treeSize(numItems); + nodes.reserve(finalSize); + + // begin and end define a range of nodes needing parents + auto begin = nodes.begin(); + auto number = static_cast(std::distance(begin, nodes.end())); + + while (number > 1) { + createParentNodes(begin, number); + std::advance(begin, static_cast(number)); // parents just added become children in the next round + number = static_cast(std::distance(begin, nodes.end())); + } + + assert(finalSize == nodes.size()); + + root = &nodes.back(); + } + +protected: + std::mutex lock_; + NodeList nodes; //**< a list of all leaf and branch nodes in the tree. */ + Node* root; //**< a pointer to the root node, if the tree has been built. */ + size_t nodeCapacity; //*< maximum number of children of each node */ + size_t numItems; //*< total number of items in the tree, if it has been built. */ + + // Prevent instantiation of base class. + // ~TemplateSTRtreeImpl() = default; + + void createLeafNode(ItemType&& item, const BoundsType& env) { + nodes.emplace_back(std::forward(item), env); + } + + void createLeafNode(const ItemType& item, const BoundsType& env) { + nodes.emplace_back(item, env); + } + + void createBranchNode(const Node *begin, const Node *end) { + assert(nodes.size() < nodes.capacity()); + nodes.emplace_back(begin, end); + } + + // calculate what the tree size will be when it is build. This is simply + // a version of createParentNodes that doesn't actually create anything. + size_t treeSize(size_t numLeafNodes) { + size_t nodesInTree = numLeafNodes; + + size_t nodesWithoutParents = numLeafNodes; + while (nodesWithoutParents > 1) { + auto numSlices = sliceCount(nodesWithoutParents); + auto nodesPerSlice = sliceCapacity(nodesWithoutParents, numSlices); + + size_t parentNodesAdded = 0; + for (size_t j = 0; j < numSlices; j++) { + auto nodesInSlice = std::min(nodesWithoutParents, nodesPerSlice); + nodesWithoutParents -= nodesInSlice; + + parentNodesAdded += static_cast(std::ceil( + static_cast(nodesInSlice) / static_cast(nodeCapacity))); + } + + nodesInTree += parentNodesAdded; + nodesWithoutParents = parentNodesAdded; + } + + return nodesInTree; + } + + void createParentNodes(const NodeListIterator& begin, size_t number) { + // Arrange child nodes in two dimensions. + // First, divide them into vertical slices of a given size (left-to-right) + // Then create nodes within those slices (bottom-to-top) + auto numSlices = sliceCount(number); + std::size_t nodesPerSlice = sliceCapacity(number, numSlices); + + // We could sort all of the nodes here, but we don't actually need them to be + // completely sorted. They need to be sorted enough for each node to end up + // in the right vertical slice, but their relative position within the slice + // doesn't matter. So we do a partial sort for each slice below instead. + auto end = begin + static_cast(number); + sortNodesX(begin, end); + + auto startOfSlice = begin; + for (decltype(numSlices) j = 0; j < numSlices; j++) { + // end iterator is being invalidated at each iteration + end = begin + static_cast(number); + auto nodesRemaining = static_cast(std::distance(startOfSlice, end)); + auto nodesInSlice = std::min(nodesRemaining, nodesPerSlice); + auto endOfSlice = std::next(startOfSlice, static_cast(nodesInSlice)); + + // Make sure that every node that should be in this slice ends up somewhere + // between startOfSlice and endOfSlice. We don't require any ordering among + // nodes between startOfSlice and endOfSlice. + //partialSortNodes(startOfSlice, endOfSlice, end); + + addParentNodesFromVerticalSlice(startOfSlice, endOfSlice); + + startOfSlice = endOfSlice; + } + } + + void addParentNodesFromVerticalSlice(const NodeListIterator& begin, const NodeListIterator& end) { + if (BoundsTraits::TwoDimensional::value) { + sortNodesY(begin, end); + } + + // Arrange the nodes vertically and full up parent nodes sequentially until they're full. + // A possible improvement would be to rework this such so that if we have 81 nodes we + // put 9 into each parent instead of 10 or 1. + auto firstChild = begin; + while (firstChild != end) { + auto childrenRemaining = static_cast(std::distance(firstChild, end)); + auto childrenForNode = std::min(nodeCapacity, childrenRemaining); + auto lastChild = std::next(firstChild, static_cast(childrenForNode)); + + //partialSortNodes(firstChild, lastChild, end); + + // Ideally we would be able to store firstChild and lastChild instead of + // having to convert them to pointers, but I wasn't sure how to access + // the NodeListIterator type from within Node without creating some weird + // circular dependency. + const Node *ptr_first = &*firstChild; + const Node *ptr_end = ptr_first + childrenForNode; + + createBranchNode(ptr_first, ptr_end); + firstChild = lastChild; + } + } + + void sortNodesX(const NodeListIterator& begin, const NodeListIterator& end) { + std::sort(begin, end, [](const Node &a, const Node &b) { + return BoundsTraits::getX(a.getBounds()) < BoundsTraits::getX(b.getBounds()); + }); + } + + void sortNodesY(const NodeListIterator& begin, const NodeListIterator& end) { + std::sort(begin, end, [](const Node &a, const Node &b) { + return BoundsTraits::getY(a.getBounds()) < BoundsTraits::getY(b.getBounds()); + }); + } + + // Helper function to visit an item using a visitor that has no return value. + // In this case, we will always return true, indicating that querying should + // continue. + template()(std::declval()))>::value, std::nullptr_t>::type = nullptr > + bool visitLeaf(Visitor&& visitor, const Node& node) + { + visitor(node.getItem()); + return true; + } + + template()(std::declval(), std::declval()))>::value, std::nullptr_t>::type = nullptr > + bool visitLeaves(Visitor&& visitor, const Node& node1, const Node& node2) + { + visitor(node1.getItem(), node2.getItem()); + return true; + } + + // MSVC 2015 does not implement C++11 expression SFINAE and considers this a + // redefinition of a previous method +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + template()(std::declval(), std::declval()))>::value, std::nullptr_t>::type = nullptr > + bool visitLeaf(Visitor&& visitor, const Node& node) + { + visitor(node.getBounds(), node.getItem()); + return true; + } +#endif + + // If the visitor function does return a value, we will use this to indicate + // that querying should continue. + template()(std::declval()))>::value, std::nullptr_t>::type = nullptr> + bool visitLeaf(Visitor&& visitor, const Node& node) + { + return visitor(node.getItem()); + } + + template()(std::declval(), std::declval()))>::value, std::nullptr_t>::type = nullptr > + bool visitLeaves(Visitor&& visitor, const Node& node1, const Node& node2) + { + return visitor(node1.getItem(), node2.getItem()); + } + + // MSVC 2015 does not implement C++11 expression SFINAE and considers this a + // redefinition of a previous method +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + template()(std::declval(), std::declval()))>::value, std::nullptr_t>::type = nullptr> + bool visitLeaf(Visitor&& visitor, const Node& node) + { + return visitor(node.getBounds(), node.getItem()); + } +#endif + + template + bool query(const BoundsType& queryEnv, + const Node& node, + Visitor&& visitor) { + + assert(!node.isLeaf()); + + for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) { + if (child->boundsIntersect(queryEnv)) { + if (child->isLeaf()) { + if (!child->isDeleted()) { + if (!visitLeaf(visitor, *child)) { + return false; // abort query + } + } + } else { + if (!query(queryEnv, *child, visitor)) { + return false; // abort query + } + } + } + } + return true; // continue searching + } + + template + bool queryPairs(const Node& queryNode, + const Node& searchNode, + Visitor&& visitor) { + + assert(!searchNode.isLeaf()); + + for (auto* child = searchNode.beginChildren(); child < searchNode.endChildren(); ++child) { + if (child->isLeaf()) { + // Only visit leaf nodes if they have a higher address than the query node, + // to avoid processing the same pairs twice. + if (child > &queryNode && !child->isDeleted() && child->boundsIntersect(queryNode.getBounds())) { + if (!visitLeaves(visitor, queryNode, *child)) { + return false; // abort query + } + } + } else { + if (child->boundsIntersect(queryNode.getBounds())) { + if (!queryPairs(queryNode, *child, visitor)) { + return false; // abort query + } + } + } + } + + return true; // continue searching + } + + bool remove(const BoundsType& queryEnv, + const Node& node, + const ItemType& item) { + + assert(!node.isLeaf()); + + for (auto *child = node.beginChildren(); child < node.endChildren(); ++child) { + if (child->boundsIntersect(queryEnv)) { + if (child->isLeaf()) { + if (!child->isDeleted() && child->getItem() == item) { + // const cast is ugly, but alternative seems to be to remove all + // const qualifiers in Node and open up mutability everywhere? + auto mutableChild = const_cast(child); + mutableChild->removeItem(); + return true; + } + } else { + bool removed = remove(queryEnv, *child, item); + if (removed) { + return true; + } + } + } + } + + return false; + } + + size_t sliceCount(size_t numNodes) const { + double minLeafCount = std::ceil(static_cast(numNodes) / static_cast(nodeCapacity)); + + return static_cast(std::ceil(std::sqrt(minLeafCount))); + } + + static size_t sliceCapacity(size_t numNodes, size_t numSlices) { + return static_cast(std::ceil(static_cast(numNodes) / static_cast(numSlices))); + } + + void validateConstruction() const { + if (nodeCapacity < 2) { + throw util::IllegalArgumentException("STRTree node capacity must be >= 2"); + } + } +}; + +struct EnvelopeTraits { + using BoundsType = geom::Envelope; + using TwoDimensional = std::true_type; + + static bool intersects(const BoundsType& a, const BoundsType& b) { + return a.intersects(b); + } + + static double size(const BoundsType& a) { + return a.getArea(); + } + + static double distance(const BoundsType& a, const BoundsType& b) { + return a.distance(b); + } + + static double maxDistance(const BoundsType& a, const BoundsType& b) { + return a.maxDistance(b); + } + + static BoundsType empty() { + return {}; + } + + template + static const BoundsType& fromItem(const ItemType& i) { + return *(i->getEnvelopeInternal()); + } + + template + static const BoundsType& fromItem(ItemType&& i) { + return *(i->getEnvelopeInternal()); + } + + static double getX(const BoundsType& a) { + return a.getMinX() + a.getMaxX(); + } + + static double getY(const BoundsType& a) { + return a.getMinY() + a.getMaxY(); + } + + static void expandToInclude(BoundsType& a, const BoundsType& b) { + a.expandToInclude(b); + } + + static bool isNull(const BoundsType& a) { + return a.isNull(); + } +}; + +struct IntervalTraits { + using BoundsType = Interval; + using TwoDimensional = std::false_type; + + static bool intersects(const BoundsType& a, const BoundsType& b) { + return a.intersects(&b); + } + + static double size(const BoundsType& a) { + return a.getWidth(); + } + + static double getX(const BoundsType& a) { + return a.getMin() + a.getMax(); + } + + static double getY(const BoundsType& a) { + return a.getMin() + a.getMax(); + } + + static void expandToInclude(BoundsType& a, const BoundsType& b) { + a.expandToInclude(&b); + } + + static bool isNull(const BoundsType& a) { + (void) a; + return false; + } +}; + + +template +class TemplateSTRtree : public TemplateSTRtreeImpl { +public: + using TemplateSTRtreeImpl::TemplateSTRtreeImpl; +}; + +// When ItemType is a pointer and our bounds are geom::Envelope, adopt +// the SpatialIndex interface which requires queries via an envelope +// and items to be representable as void*. +template +class TemplateSTRtree : public TemplateSTRtreeImpl, public SpatialIndex { +public: + using TemplateSTRtreeImpl::TemplateSTRtreeImpl; + using TemplateSTRtreeImpl::insert; + using TemplateSTRtreeImpl::query; + using TemplateSTRtreeImpl::remove; + + // The SpatialIndex methods only work when we are storing a pointer type. + void query(const geom::Envelope* queryEnv, std::vector& results) override { + query(*queryEnv, [&results](const ItemType* x) { + results.push_back(const_cast(static_cast(x))); + }); + } + + void query(const geom::Envelope* queryEnv, ItemVisitor& visitor) override { + query(*queryEnv, [&visitor](const ItemType* x) { + visitor.visitItem(const_cast(static_cast(x))); + }); + } + + bool remove(const geom::Envelope* itemEnv, void* item) override { + return remove(*itemEnv, static_cast(item)); + } + + void insert(const geom::Envelope* itemEnv, void* item) override { + insert(*itemEnv, std::move(static_cast(item))); + } +}; + + +} +} +} diff --git a/Sources/geos/include/geos/index/strtree/TemplateSTRtreeDistance.h b/Sources/geos/include/geos/index/strtree/TemplateSTRtreeDistance.h new file mode 100644 index 0000000..19d7109 --- /dev/null +++ b/Sources/geos/include/geos/index/strtree/TemplateSTRtreeDistance.h @@ -0,0 +1,233 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace geos { +namespace index { +namespace strtree { + +template +class TemplateSTRtreeDistance { + using Node = TemplateSTRNode; + using NodePair = TemplateSTRNodePair; + using ItemPair = std::pair; + + struct PairQueueCompare { + bool operator()(const NodePair& a, const NodePair& b) { + return a.getDistance() > b.getDistance(); + } + }; + + using PairQueue = std::priority_queue, PairQueueCompare>; + +public: + explicit TemplateSTRtreeDistance(ItemDistance& id) : m_id(id) {} + + ItemPair nearestNeighbour(const Node& root1, const Node& root2) { + NodePair initPair(root1, root2, m_id); + return nearestNeighbour(initPair); + } + + ItemPair nearestNeighbour(NodePair& initPair) { + return nearestNeighbour(initPair, DoubleInfinity); + } + + bool isWithinDistance(const Node& root1, const Node& root2, double maxDistance) { + NodePair initPair(root1, root2, m_id); + return isWithinDistance(initPair, maxDistance); + } + +private: + + ItemPair nearestNeighbour(NodePair& initPair, double maxDistance) { + double distanceLowerBound = maxDistance; + std::unique_ptr minPair; + + PairQueue priQ; + priQ.push(initPair); + + while (!priQ.empty() && distanceLowerBound > 0) { + NodePair pair = priQ.top(); + priQ.pop(); + double currentDistance = pair.getDistance(); + + /* + * If the distance for the first node in the queue + * is >= the current minimum distance, all other nodes + * in the queue must also have a greater distance. + * So the current minDistance must be the true minimum, + * and we are done. + */ + if (minPair && currentDistance >= distanceLowerBound) { + break; + } + + /* + * If the pair members are leaves + * then their distance is the exact lower bound. + * Update the distanceLowerBound to reflect this + * (which must be smaller, due to the test + * immediately prior to this). + */ + if (pair.isLeaves()) { + distanceLowerBound = currentDistance; + if (minPair) { + *minPair = pair; + } else { + minPair = detail::make_unique(pair); + } + } else { + /* + * Otherwise, expand one side of the pair, + * (the choice of which side to expand is heuristically determined) + * and insert the new expanded pairs into the queue + */ + expandToQueue(pair, priQ, distanceLowerBound); + } + } + + if (!minPair) { + throw util::GEOSException("Error computing nearest neighbor"); + } + + return minPair->getItems(); + } + + void expandToQueue(const NodePair& pair, PairQueue& priQ, double minDistance) { + const Node& node1 = pair.getFirst(); + const Node& node2 = pair.getSecond(); + + bool isComp1 = node1.isComposite(); + bool isComp2 = node2.isComposite(); + + /** + * HEURISTIC: If both boundables are composite, + * choose the one with largest area to expand. + * Otherwise, simply expand whichever is composite. + */ + if (isComp1 && isComp2) { + if (node1.getSize() > node2.getSize()) { + expand(node1, node2, false, priQ, minDistance); + return; + } else { + expand(node2, node1, true, priQ, minDistance); + return; + } + } else if (isComp1) { + expand(node1, node2, false, priQ, minDistance); + return; + } else if (isComp2) { + expand(node2, node1, true, priQ, minDistance); + return; + } + + throw util::IllegalArgumentException("neither boundable is composite"); + + } + + void expand(const Node &nodeComposite, const Node &nodeOther, bool isFlipped, PairQueue& priQ, + double minDistance) { + for (const auto *child = nodeComposite.beginChildren(); + child < nodeComposite.endChildren(); ++child) { + NodePair sp = isFlipped ? NodePair(nodeOther, *child, m_id) : NodePair(*child, nodeOther, m_id); + + // only add to queue if this pair might contain the closest points + if (minDistance == DoubleInfinity || sp.getDistance() < minDistance) { + priQ.push(sp); + } + } + } + + bool isWithinDistance(const NodePair& initPair, double maxDistance) { + double distanceUpperBound = DoubleInfinity; + + // initialize search queue + PairQueue priQ; + priQ.push(initPair); + + while (! priQ.empty()) { + // pop head of queue and expand one side of pair + NodePair pair = priQ.top(); + double pairDistance = pair.getDistance(); + + /** + * If the distance for the first pair in the queue + * is > maxDistance, all other pairs + * in the queue must have a greater distance as well. + * So can conclude no items are within the distance + * and terminate with result = false + */ + if (pairDistance > maxDistance) + return false; + + priQ.pop(); + + /** + * If the maximum distance between the nodes + * is less than the maxDistance, + * than all items in the nodes must be + * closer than the max distance. + * Then can terminate with result = true. + * + * NOTE: using Envelope MinMaxDistance + * would provide a tighter bound, + * but not much performance improvement has been observed + */ + if (pair.maximumDistance() <= maxDistance) + return true; + /** + * If the pair items are leaves + * then their actual distance is an upper bound. + * Update the distanceUpperBound to reflect this + */ + if (pair.isLeaves()) { + // assert: currentDistance < minimumDistanceFound + distanceUpperBound = pairDistance; + /** + * If the items are closer than maxDistance + * can terminate with result = true. + */ + if (distanceUpperBound <= maxDistance) + return true; + } + else { + /** + * Otherwise, expand one side of the pair, + * and insert the expanded pairs into the queue. + * The choice of which side to expand is determined heuristically. + */ + expandToQueue(pair, priQ, distanceUpperBound); + } + } + return false; + + } + + + ItemDistance& m_id; +}; +} +} +} diff --git a/Sources/geos/include/geos/index/sweepline/SweepLineEvent.h b/Sources/geos/include/geos/index/sweepline/SweepLineEvent.h new file mode 100644 index 0000000..2f88b70 --- /dev/null +++ b/Sources/geos/include/geos/index/sweepline/SweepLineEvent.h @@ -0,0 +1,95 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace index { +namespace sweepline { +class SweepLineInterval; +} +} +} + +namespace geos { +namespace index { // geos.index +namespace sweepline { // geos:index:sweepline + +class GEOS_DLL SweepLineEvent { + +public: + + enum { + INSERT_EVENT = 1, + DELETE_EVENT + }; + + SweepLineEvent(double x, SweepLineEvent* newInsertEvent, + SweepLineInterval* newSweepInt); + + bool isInsert(); + + bool isDelete(); + + SweepLineEvent* getInsertEvent(); + + std::size_t getDeleteEventIndex(); + + void setDeleteEventIndex(std::size_t newDeleteEventIndex); + + SweepLineInterval* getInterval(); + + /** + * ProjectionEvents are ordered first by their x-value, and then by their eventType. + * It is important that Insert events are sorted before Delete events, so that + * items whose Insert and Delete events occur at the same x-value will be + * correctly handled. + */ + int compareTo(const SweepLineEvent* pe) const; + + //int compareTo(void *o) const; + +private: + + double xValue; + + int eventType; + + /// null if this is an INSERT_EVENT event + SweepLineEvent* insertEvent; + + std::size_t deleteEventIndex; + + SweepLineInterval* sweepInt; + +}; + +// temp typedefs for backward compatibility +//typedef SweepLineEvent indexSweepLineEvent; + +struct GEOS_DLL SweepLineEventLessThen { + bool operator()(const SweepLineEvent* first, const SweepLineEvent* second) const; +}; + +//bool isleLessThen(SweepLineEvent *first, SweepLineEvent *second); + + +} // namespace geos:index:sweepline +} // namespace geos:index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/sweepline/SweepLineIndex.h b/Sources/geos/include/geos/index/sweepline/SweepLineIndex.h new file mode 100644 index 0000000..128d6a1 --- /dev/null +++ b/Sources/geos/include/geos/index/sweepline/SweepLineIndex.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace index { +namespace sweepline { +class SweepLineInterval; +class SweepLineEvent; +class SweepLineOverlapAction; +} +} +} + +namespace geos { +namespace index { // geos.index +namespace sweepline { // geos:index:sweepline + +/** \brief + * A sweepline implements a sorted index on a set of intervals. + * + * It is used to compute all overlaps between the interval in the index. + */ +class GEOS_DLL SweepLineIndex { + +public: + + SweepLineIndex(); + + ~SweepLineIndex(); + + void add(SweepLineInterval* sweepInt); + + void computeOverlaps(SweepLineOverlapAction* action); + +private: + + // FIXME: make it a real vector rather then a pointer + std::vector events; + + bool indexBuilt; + + // statistics information + int nOverlaps; + + /** + * Because Delete Events have a link to their corresponding Insert event, + * it is possible to compute exactly the range of events which must be + * compared to a given Insert event object. + */ + void buildIndex(); + + void processOverlaps(std::size_t start, std::size_t end, + SweepLineInterval* s0, + SweepLineOverlapAction* action); +}; + +} // namespace geos:index:sweepline +} // namespace geos:index +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/index/sweepline/SweepLineInterval.h b/Sources/geos/include/geos/index/sweepline/SweepLineInterval.h new file mode 100644 index 0000000..c928306 --- /dev/null +++ b/Sources/geos/include/geos/index/sweepline/SweepLineInterval.h @@ -0,0 +1,37 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace index { // geos.index +namespace sweepline { // geos:index:sweepline + +class GEOS_DLL SweepLineInterval { +public: + SweepLineInterval(double newMin, double newMax, void* newItem = nullptr); + double getMin(); + double getMax(); + void* getItem(); +private: + double min, max; + void* item; +}; + +} // namespace geos:index:sweepline +} // namespace geos:index +} // namespace geos + diff --git a/Sources/geos/include/geos/index/sweepline/SweepLineOverlapAction.h b/Sources/geos/include/geos/index/sweepline/SweepLineOverlapAction.h new file mode 100644 index 0000000..f9e3970 --- /dev/null +++ b/Sources/geos/include/geos/index/sweepline/SweepLineOverlapAction.h @@ -0,0 +1,44 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +// Forward declarations +namespace geos { +namespace index { +namespace sweepline { +class SweepLineInterval; +} +} +} + +namespace geos { +namespace index { // geos.index +namespace sweepline { // geos:index:sweepline + +class GEOS_DLL SweepLineOverlapAction { +public: + virtual void overlap(SweepLineInterval* s0, SweepLineInterval* s1) = 0; + + virtual + ~SweepLineOverlapAction() {} +}; + + +} // namespace geos:index:sweepline +} // namespace geos:index +} // namespace geos + diff --git a/Sources/geos/include/geos/io/ByteOrderDataInStream.h b/Sources/geos/include/geos/io/ByteOrderDataInStream.h new file mode 100644 index 0000000..8a039ef --- /dev/null +++ b/Sources/geos/include/geos/io/ByteOrderDataInStream.h @@ -0,0 +1,125 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/ByteOrderDataInStream.java rev. 1.1 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for getMachineByteOrder + +#include +#include // ostream, istream (if we remove inlines) + +namespace geos { +namespace io { + +/** + * \class ByteOrderDataInStream + * + * \brief Allows reading an stream of primitive datatypes from an underlying + * istream, with the representation being in either common byte ordering. + * + */ +class GEOS_DLL ByteOrderDataInStream { + +public: + + ByteOrderDataInStream() + : ByteOrderDataInStream(nullptr, 0) {}; + + ByteOrderDataInStream(const unsigned char* buff, size_t buffsz) + : byteOrder(getMachineByteOrder()) + , buf(buff) + , end(buff + buffsz) + {}; + + ~ByteOrderDataInStream() {}; + + void setOrder(int order) + { + byteOrder = order; + }; + + unsigned char readByte() // throws ParseException + { + if(size() < 1) { + throw ParseException("Unexpected EOF parsing WKB"); + } + auto ret = buf[0]; + buf++; + return ret; + }; + + int32_t readInt() + { + if(size() < 4) { + throw ParseException("Unexpected EOF parsing WKB"); + } + auto ret = ByteOrderValues::getInt(buf , byteOrder); + buf += 4; + return ret; + }; + + uint32_t readUnsigned() + { + if(size() < 4) { + throw ParseException("Unexpected EOF parsing WKB"); + } + auto ret = ByteOrderValues::getUnsigned(buf , byteOrder); + buf += 4; + return ret; + }; + + int64_t readLong() + { + if(size() < 8) { + throw ParseException("Unexpected EOF parsing WKB"); + } + + auto ret = ByteOrderValues::getLong(buf, byteOrder); + buf += 8; + return ret; + }; + + double readDouble() + { + if(size() < 8) { + throw ParseException("Unexpected EOF parsing WKB"); + } + auto ret = ByteOrderValues::getDouble(buf, byteOrder); + buf += 8; + return ret; + }; + + size_t size() const + { + return static_cast(end - buf); + }; + + +private: + int byteOrder; + const unsigned char* buf; + const unsigned char* end; + +}; + +} // namespace io +} // namespace geos diff --git a/Sources/geos/include/geos/io/ByteOrderValues.h b/Sources/geos/include/geos/io/ByteOrderValues.h new file mode 100644 index 0000000..54b39e6 --- /dev/null +++ b/Sources/geos/include/geos/io/ByteOrderValues.h @@ -0,0 +1,61 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/ByteOrderValues.java rev. 1.3 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace io { + +/** + * \class ByteOrderValues + * + * \brief Methods to read and write primitive datatypes from/to byte + * sequences, allowing the byte order to be specified. + * + * Similar to the standard Java ByteBuffer class. + */ +class GEOS_DLL ByteOrderValues { + +public: + + enum EndianType { + ENDIAN_BIG = 0, + ENDIAN_LITTLE = 1 + }; + + static int32_t getInt(const unsigned char* buf, int byteOrder); + static void putInt(int32_t intValue, unsigned char* buf, int byteOrder); + + static uint32_t getUnsigned(const unsigned char* buf, int byteOrder); + static void putUnsigned(uint32_t intValue, unsigned char* buf, int byteOrder); + + static int64_t getLong(const unsigned char* buf, int byteOrder); + static void putLong(int64_t longValue, unsigned char* buf, int byteOrder); + + static double getDouble(const unsigned char* buf, int byteOrder); + static void putDouble(double doubleValue, unsigned char* buf, int byteOrder); + +}; + +} // namespace io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/CLocalizer.h b/Sources/geos/include/geos/io/CLocalizer.h new file mode 100644 index 0000000..15d9774 --- /dev/null +++ b/Sources/geos/include/geos/io/CLocalizer.h @@ -0,0 +1,53 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2008 Sean Gillies + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace io { + +/** + * \class CLocalizer io.h geos.h + */ +class GEOS_DLL CLocalizer { +public: + + CLocalizer(); + ~CLocalizer(); + +private: + + std::string saved_locale; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/CheckOrdinatesFilter.h b/Sources/geos/include/geos/io/CheckOrdinatesFilter.h new file mode 100644 index 0000000..d4d9a81 --- /dev/null +++ b/Sources/geos/include/geos/io/CheckOrdinatesFilter.h @@ -0,0 +1,68 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace io { + +class CheckOrdinatesFilter : public geom::CoordinateSequenceFilter { +public: + CheckOrdinatesFilter(OrdinateSet checkOrdinateFlags) : + m_checkFlags(checkOrdinateFlags), + m_foundFlags(OrdinateSet::createXY()) + {} + + void filter_ro(const geom::CoordinateSequence& seq, std::size_t i) override { + bool checkZ = m_checkFlags.hasZ() && !m_foundFlags.hasZ(); + bool checkM = m_checkFlags.hasM() && !m_foundFlags.hasM(); + + if (checkZ || checkM) { + seq.getAt(i, m_coord); + + if (checkZ && !std::isnan(m_coord.z)) { + m_foundFlags.setZ(true); + } + + if (checkM && !std::isnan(m_coord.m)) { + m_foundFlags.setM(true); + } + } + } + + bool isGeometryChanged() const override { + return false; + } + + bool isDone() const override { + return m_checkFlags == m_foundFlags; + } + + OrdinateSet getFoundOrdinates() const { + return m_foundFlags; + } + +private: + OrdinateSet m_checkFlags; + OrdinateSet m_foundFlags; + geom::CoordinateXYZM m_coord; +}; + +} +} diff --git a/Sources/geos/include/geos/io/GeoJSON.h b/Sources/geos/include/geos/io/GeoJSON.h new file mode 100644 index 0000000..8281786 --- /dev/null +++ b/Sources/geos/include/geos/io/GeoJSON.h @@ -0,0 +1,154 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Jared Erickson + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace io { + +class GEOS_DLL GeoJSONValue { + +private: + + enum class Type { NUMBER, STRING, NULLTYPE, BOOLEAN, OBJECT, ARRAY }; + + Type type; + + union { + double d; + std::string s; + std::nullptr_t n; + bool b; + std::map o; + std::vector a; + }; + + void cleanup(); + +public: + + struct GeoJSONTypeError {}; + + GeoJSONValue(double); + GeoJSONValue(const std::string&); + GeoJSONValue(); + GeoJSONValue(bool); + GeoJSONValue(const std::map&); + GeoJSONValue(const std::vector&); + + ~GeoJSONValue(); + GeoJSONValue(const GeoJSONValue&); + GeoJSONValue& operator=(const GeoJSONValue&); + + double getNumber() const; + const std::string& getString() const; + std::nullptr_t getNull() const; + bool getBoolean() const; + const std::map& getObject() const; + const std::vector& getArray() const; + + bool isNumber() const; + bool isString() const; + bool isNull() const; + bool isBoolean() const; + bool isObject() const; + bool isArray() const; + +}; + +class GEOS_DLL GeoJSONFeature { + +public: + + GeoJSONFeature(std::unique_ptr g, + const std::map& p); + + GeoJSONFeature(std::unique_ptr g, + std::map&& p); + + GeoJSONFeature(std::unique_ptr g, + const std::map& p, + const std::string& id); + + GeoJSONFeature(std::unique_ptr g, + std::map&& p, std::string id); + + GeoJSONFeature(GeoJSONFeature const& other); + + GeoJSONFeature(GeoJSONFeature&& other); + + GeoJSONFeature& operator=(const GeoJSONFeature&); + + GeoJSONFeature& operator=(GeoJSONFeature&&); + + const geom::Geometry* getGeometry() const; + + const std::map& getProperties() const; + + const std::string& getId() const; + +private: + + std::unique_ptr geometry; + + std::map properties; + + std::string id; + +}; + +class GEOS_DLL GeoJSONFeatureCollection { + +public: + + GeoJSONFeatureCollection(const std::vector& f); + + GeoJSONFeatureCollection(std::vector&& f); + + const std::vector& getFeatures() const; + +private: + + std::vector features; + +}; + +} // namespace geos::io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/io/GeoJSONReader.h b/Sources/geos/include/geos/io/GeoJSONReader.h new file mode 100644 index 0000000..41834f1 --- /dev/null +++ b/Sources/geos/include/geos/io/GeoJSONReader.h @@ -0,0 +1,125 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Jared Erickson + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include "geos/vend/include_nlohmann_json.hpp" + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +class MultiPoint; +class MultiLineString; +class MultiPolygon; +class PrecisionModel; +} +} + +namespace geos { +namespace io { + +/** + * \class GeoJSONReader + * \brief GeoJSON reader class; see also GeoJSONWriter. + */ +class GEOS_DLL GeoJSONReader { +public: + + /** + * \brief Initialize parser with given GeometryFactory. + * + * Note that all Geometry objects created by the + * parser will contain a pointer to the given factory + * so be sure you'll keep the factory alive for the + * whole GeoJSONReader and created Geometry life. + */ + GeoJSONReader(const geom::GeometryFactory& gf); + + /** + * \brief Initialize parser with default GeometryFactory. + * + */ + GeoJSONReader(); + + ~GeoJSONReader() = default; + + /// Parse a GeoJSON string returning a Geometry + std::unique_ptr read(const std::string& geoJsonText) const; + + GeoJSONFeatureCollection readFeatures(const std::string& geoJsonText) const; + +private: + + const geom::GeometryFactory& geometryFactory; + + std::unique_ptr readFeatureForGeometry(const geos_nlohmann::json& j) const; + + GeoJSONFeature readFeature(const geos_nlohmann::json& j) const; + + std::map readProperties(const geos_nlohmann::json& p) const; + + GeoJSONValue readProperty(const geos_nlohmann::json& p) const; + + std::unique_ptr readFeatureCollectionForGeometry( + const geos_nlohmann::json& j) const; + + GeoJSONFeatureCollection readFeatureCollection( + const geos_nlohmann::json& j) const; + + std::unique_ptr readGeometry( + const geos_nlohmann::json& j) const; + + std::unique_ptr readPoint(const geos_nlohmann::json& j) const; + + geom::Coordinate readCoordinate(const std::vector& coords) const; + + std::unique_ptr readLineString( + const geos_nlohmann::json& j) const; + + std::unique_ptr readPolygon( + const geos_nlohmann::json& j) const; + + std::unique_ptr readPolygon( + const std::vector>>& c) const; + + std::unique_ptr readMultiPoint( + const geos_nlohmann::json& j) const; + + std::unique_ptr readMultiLineString( + const geos_nlohmann::json& j) const; + + std::unique_ptr readMultiPolygon( + const geos_nlohmann::json& j) const; + + std::unique_ptr readGeometryCollection( + const geos_nlohmann::json& j) const; + +}; + +} // namespace io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/GeoJSONWriter.h b/Sources/geos/include/geos/io/GeoJSONWriter.h new file mode 100644 index 0000000..d8011b8 --- /dev/null +++ b/Sources/geos/include/geos/io/GeoJSONWriter.h @@ -0,0 +1,153 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Jared Erickson + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include "GeoJSON.h" +#include +#include +#include "geos/vend/include_nlohmann_json.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +class MultiPoint; +class MultiLineString; +class MultiPolygon; +class PrecisionModel; +} +namespace io { +class Writer; +} +} + + +namespace geos { +namespace io { + +enum class GeoJSONType { + GEOMETRY, FEATURE, FEATURE_COLLECTION +}; + +/** + * \class GeoJSONWriter + * + * \brief Outputs the GeoJSON representation of a Geometry. + * See also GeoJSONReader for parsing. + * + */ +class GEOS_DLL GeoJSONWriter { +public: + ~GeoJSONWriter() = default; + + std::string write(const geom::Geometry* geometry, GeoJSONType type = GeoJSONType::GEOMETRY); + + std::string writeFormatted(const geom::Geometry* geometry, GeoJSONType type = GeoJSONType::GEOMETRY, int indent = 4); + + std::string write(const GeoJSONFeature& feature); + + std::string write(const GeoJSONFeatureCollection& features); + + /* + * \brief + * Returns the output dimension used by the + * GeoJSONWriter. + */ + int + getOutputDimension() const + { + return defaultOutputDimension; + } + + /* + * Sets the output dimension used by the GeoJSONWriter. + * + * @param newOutputDimension Supported values are 2 or 3. + * Default since GEOS 3.12 is 3. + * Note that 3 indicates up to 3 dimensions will be + * written but 2D GeoJSON is still produced for 2D geometries. + */ + void setOutputDimension(uint8_t newOutputDimension); + + /* + * Sets whether the GeoJSON should be output following counter-clockwise orientation aka Right Hand Rule defined in + * RFC7946. See RFC 7946 Specification + * for more context. + * + * @param newIsForceCCW true if the GeoJSON should be output following the RFC7946 + * counter-clockwise orientation aka Right Hand Rule + */ + void setForceCCW(bool newIsForceCCW); + +private: + uint8_t defaultOutputDimension = 3; + + bool isForceCCW = false; + + std::vector convertCoordinate(const geom::Coordinate* c); + + std::vector> convertCoordinateSequence(const geom::CoordinateSequence* c); + + std::vector>> convertLinearRings(const geom::Polygon *poly); + + void encode(const geom::Geometry* g, GeoJSONType type, geos_nlohmann::ordered_json& j); + + void encodeGeometry(const geom::Geometry* g, geos_nlohmann::ordered_json& j); + + void encodePoint(const geom::Point* p, geos_nlohmann::ordered_json& j); + + void encodeLineString(const geom::LineString* l, geos_nlohmann::ordered_json& j); + + void encodePolygon(const geom::Polygon* p, geos_nlohmann::ordered_json& j); + + void encodeMultiPoint(const geom::MultiPoint* p, geos_nlohmann::ordered_json& j); + + void encodeMultiLineString(const geom::MultiLineString* l, geos_nlohmann::ordered_json& j); + + void encodeMultiPolygon(const geom::MultiPolygon* m, geos_nlohmann::ordered_json& j); + + void encodeGeometryCollection(const geom::GeometryCollection* g, geos_nlohmann::ordered_json& j); + + void encodeFeature(const geom::Geometry* g, geos_nlohmann::ordered_json& j); + + void encodeFeatureCollection(const geom::Geometry* g, geos_nlohmann::ordered_json& j); + + void encodeFeature(const GeoJSONFeature& feature, geos_nlohmann::ordered_json& j); + + void encodeGeoJSONValue(const std::string& key, const GeoJSONValue& value, geos_nlohmann::ordered_json& j); + +}; + +} // namespace geos::io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/io/OrdinateSet.h b/Sources/geos/include/geos/io/OrdinateSet.h new file mode 100644 index 0000000..ebd2297 --- /dev/null +++ b/Sources/geos/include/geos/io/OrdinateSet.h @@ -0,0 +1,119 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace io { + +/** + * \class OrdinateSet + * \brief Utility class to manipulate a set of flags indicating whether + * X, Y, Z, or M dimensions are present. + * Based on JTS EnumSet. + */ +class GEOS_DLL OrdinateSet { +private: + enum Ordinate : unsigned char { + X = 1, + Y = 2, + Z = 4, + M = 8, + }; + + enum Ordinates : unsigned char { + XY = Ordinate::X | Ordinate::Y, + XYZ = Ordinate::X | Ordinate::Y | Ordinate::Z, + XYM = Ordinate::X | Ordinate::Y | Ordinate::M, + XYZM = Ordinate::X | Ordinate::Y | Ordinate::Z | Ordinate::M + }; + + explicit OrdinateSet(Ordinates o) : m_value(o), m_changesAllowed(true) {} + + Ordinates m_value; + bool m_changesAllowed; + +public: + + static OrdinateSet createXY() { + return OrdinateSet(Ordinates::XY); + } + + static OrdinateSet createXYZ() { + return OrdinateSet(Ordinates::XYZ); + } + + static OrdinateSet createXYM() { + return OrdinateSet(Ordinates::XYM); + } + + static OrdinateSet createXYZM() { + return OrdinateSet(Ordinates::XYZM); + } + + void setZ(bool value) { + if (hasZ() != value) { + if (m_changesAllowed) { + m_value = static_cast(static_cast(m_value) ^ Ordinate::Z); + } else { + throw util::GEOSException("Cannot add additional ordinates."); + } + } + } + + void setM(bool value) { + if (hasM() != value) { + if (m_changesAllowed){ + m_value = static_cast(static_cast(m_value) ^ Ordinate::M); + } else { + throw util::GEOSException("Cannot add additional ordinates."); + } + } + } + + bool hasZ() const { + return static_cast(m_value) & static_cast(Ordinate::Z); + } + + bool hasM() const { + return static_cast(m_value) & static_cast(Ordinate::M); + } + + int size() const { + return 2 + hasZ() + hasM(); + } + + bool changesAllowed() const { + return m_changesAllowed; + } + + void setChangesAllowed(bool allowed) { + m_changesAllowed = allowed; + } + + bool operator==(const OrdinateSet& other) const { + return this->m_value == other.m_value; + } + + bool operator!=(const OrdinateSet& other) const { + return !(*this == other); + } + +}; + +} +} diff --git a/Sources/geos/include/geos/io/ParseException.h b/Sources/geos/include/geos/io/ParseException.h new file mode 100644 index 0000000..f0f56e2 --- /dev/null +++ b/Sources/geos/include/geos/io/ParseException.h @@ -0,0 +1,53 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/ParseException.java rev. 1.13 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +namespace geos { +namespace io { + +/** + * \class ParseException + * \brief Notifies a parsing error + */ +class GEOS_DLL ParseException : public util::GEOSException { + +public: + + ParseException(); + + ParseException(const std::string& msg); + + ParseException(const std::string& msg, const std::string& var); + + ParseException(const std::string& msg, double num); + + ~ParseException() noexcept override {} + +private: + static std::string stringify(double num); +}; + +} // namespace io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/StringTokenizer.h b/Sources/geos/include/geos/io/StringTokenizer.h new file mode 100644 index 0000000..6767a34 --- /dev/null +++ b/Sources/geos/include/geos/io/StringTokenizer.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: ORIGINAL WORK + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace io { + +class GEOS_DLL StringTokenizer { +public: + enum { + TT_EOF, + TT_EOL, + TT_NUMBER, + TT_WORD + }; + //StringTokenizer(); + explicit StringTokenizer(const std::string& txt); + ~StringTokenizer() {} + int nextToken(); + int peekNextToken(); + double getNVal() const; + std::string getSVal() const; +private: + const std::string& str; + std::string stok; + double ntok; + std::string::const_iterator iter; + + // Declare type as noncopyable + StringTokenizer(const StringTokenizer& other) = delete; + StringTokenizer& operator=(const StringTokenizer& rhs) = delete; +}; + +} // namespace io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/io/WKBConstants.h b/Sources/geos/include/geos/io/WKBConstants.h new file mode 100644 index 0000000..4385748 --- /dev/null +++ b/Sources/geos/include/geos/io/WKBConstants.h @@ -0,0 +1,57 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/WKBConstants.java rev. 1.1 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace io { + +/// Constant values used by the WKB format +namespace WKBConstants { + + enum byteOrder { + wkbXDR = 0, + wkbNDR = 1 + }; + + enum wkbType { + wkbPoint = 1, + wkbLineString = 2, + wkbPolygon = 3, + wkbMultiPoint = 4, + wkbMultiLineString = 5, + wkbMultiPolygon = 6, + wkbGeometryCollection = 7, + wkbCircularString = 8, + wkbCompoundCurve = 9, + wkbCurvePolygon = 10, + wkbMultiCurve = 11, + wkbMultiSurface = 12, + }; + + enum wkbFlavour { + wkbExtended = 1, + wkbIso = 2 + }; + +} + +} // namespace geos::io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/WKBReader.h b/Sources/geos/include/geos/io/WKBReader.h new file mode 100644 index 0000000..9d410c2 --- /dev/null +++ b/Sources/geos/include/geos/io/WKBReader.h @@ -0,0 +1,204 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/WKBReader.java rev. 1.1 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for composition + +#include // ostream, istream +#include +// #include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { + +class GeometryFactory; +class Coordinate; +class CircularString; +class CompoundCurve; +class CurvePolygon; +class Geometry; +enum GeometryTypeId : int; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +class MultiCurve; +class MultiPoint; +class MultiLineString; +class MultiPolygon; +class MultiSurface; +class PrecisionModel; +class CoordinateSequence; +class SimpleCurve; + +} // namespace geom +} // namespace geos + + +namespace geos { +namespace io { + +/** + * \class WKBReader + * + * \brief Reads a Geometry from Well-Known Binary format. + * + * This class is designed to support reuse of a single instance to read + * multiple geometries. This class is not thread-safe; each thread should + * create its own instance. + * + * The Well-known Binary format is defined in the OpenGIS Simple Features + * Specification for SQL. + * This implementation supports the extended WKB standard which allows + * representing 3-dimensional coordinates. + * + */ +class GEOS_DLL WKBReader { + +public: + + WKBReader(geom::GeometryFactory const& f); + + /// Initialize parser with default GeometryFactory. + WKBReader(); + + void setFixStructure(bool doFixStructure); + + /** + * \brief Reads a Geometry from an istream. + * + * @param is the stream to read from + * @return the Geometry read + * @throws IOException + * @throws ParseException + */ + std::unique_ptr read(std::istream& is); + + /** + * \brief Reads a Geometry from a buffer + * + * @param buf the buffer to read from + * @param size the size of the buffer in bytes + * @return the Geometry read + * @throws IOException + * @throws ParseException + */ + std::unique_ptr read(const unsigned char* buf, size_t size); + + /** + * \brief Reads a Geometry from an istream in hex format. + * + * @param is the stream to read from + * @return the Geometry read + * @throws IOException + * @throws ParseException + */ + std::unique_ptr readHEX(std::istream& is); + + /** + * \brief Print WKB in HEX form to out stream + * + * @param is is the stream to read from + * @param os is the stream to write to + */ + static std::ostream& printHEX(std::istream& is, std::ostream& os); + +private: + + const geom::GeometryFactory& factory; + + // for now support the WKB standard only - may be generalized later + unsigned int inputDimension; + bool hasZ; + bool hasM; + bool fixStructure; + + ByteOrderDataInStream dis; + + std::array ordValues; + + std::unique_ptr readGeometry(); + + std::unique_ptr readPoint(); + + std::unique_ptr readLineString(); + + std::unique_ptr readLinearRing(); + + std::unique_ptr readCircularString(); + + std::unique_ptr readCompoundCurve(); + + std::unique_ptr readPolygon(); + + std::unique_ptr readCurvePolygon(); + + std::unique_ptr readMultiPoint(); + + std::unique_ptr readMultiLineString(); + + std::unique_ptr readMultiCurve(); + + std::unique_ptr readMultiPolygon(); + + std::unique_ptr readMultiSurface(); + + std::unique_ptr readGeometryCollection(); + + std::unique_ptr readCoordinateSequence(unsigned int); // throws IOException + + void minMemSize(geom::GeometryTypeId geomType, uint64_t size) const; + + void readCoordinate(); // throws IOException + + template + std::unique_ptr readChild() + { + auto g = readGeometry(); + if (dynamic_cast(g.get())) { + return std::unique_ptr(static_cast(g.release())); + } + throw io::ParseException(std::string("Expected ") + geom::GeometryTypeName::name + " but got " + g->getGeometryType()); + } + + // Declare type as noncopyable + WKBReader(const WKBReader& other) = delete; + WKBReader& operator=(const WKBReader& rhs) = delete; +}; + +} // namespace io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/io/WKBStreamReader.h b/Sources/geos/include/geos/io/WKBStreamReader.h new file mode 100644 index 0000000..5402ff9 --- /dev/null +++ b/Sources/geos/include/geos/io/WKBStreamReader.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class PrecisionModel; +} +} + +namespace geos { +namespace io { + +class GEOS_DLL WKBStreamReader { + +public: + WKBStreamReader(std::istream& instr); + ~WKBStreamReader(); + + std::unique_ptr next(); + +private: + + std::istream& instr; + WKBReader rdr; +}; + +} +} + + diff --git a/Sources/geos/include/geos/io/WKBWriter.h b/Sources/geos/include/geos/io/WKBWriter.h new file mode 100644 index 0000000..ebd37b0 --- /dev/null +++ b/Sources/geos/include/geos/io/WKBWriter.h @@ -0,0 +1,260 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/WKBWriter.java rev. 1.1 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for getMachineByteOrder +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + +class CoordinateSequence; +class CompoundCurve; +class CurvePolygon; +class Geometry; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +class MultiPoint; +class MultiLineString; +class MultiPolygon; +class PrecisionModel; +class SimpleCurve; + +} // namespace geom +} // namespace geos + +namespace geos { +namespace io { + +/** + * + * \class WKBWriter + * + * \brief Writes a Geometry into Well-Known Binary format. + * + * The WKB format is specified in the OGC Simple Features for SQL specification. + * This implementation supports the extended WKB standard for representing + * Z and M coordinates. The presence of Z and/or M coordinates is signified + * by setting the high bit of the wkbType word. + * + * Empty Points cannot be represented in WKB; an + * IllegalArgumentException will be thrown if one is + * written. The WKB specification does not support representing LinearRing + * they will be written as LineString + * + * This class is designed to support reuse of a single instance to read multiple + * geometries. This class is not thread-safe; each thread should create its own + * instance. + * + * @see WKBReader + */ +class GEOS_DLL WKBWriter { + +public: + /* + * \brief + * Initializes writer with target coordinate dimension, endianness + * flag and SRID value. + * + * @param dims Supported values are 2, 3 or 4. Note that 4 indicates + * up to 4 dimensions will be written but (e.g.) 2D WKB is still produced + * for 2D geometries. Default since GEOS 3.12 is 4. + * @param bo output byte order - default to native machine byte order. + * Legal values include 0 (big endian/xdr) and 1 (little endian/ndr). + * @param includeSRID true if SRID should be included in WKB (an + * extension). + */ + WKBWriter( + uint8_t dims = 4, + int bo = getMachineByteOrder(), + bool includeSRID = false, + int flv = WKBConstants::wkbExtended); + + /* + * \brief + * Destructor. + */ + ~WKBWriter() = default; + + /* + * \brief + * Returns the output dimension used by the + * WKBWriter. + */ + uint8_t + getOutputDimension() const + { + return defaultOutputDimension; + } + + /* + * Sets the output dimension used by the WKBWriter. + * + * @param newOutputDimension Supported values are 2, 3 or 4. + * Note that 4 indicates up to 4 dimensions will be written but + * (e.g.) 2D WKB is still produced for 2D geometries. + */ + void setOutputDimension(uint8_t newOutputDimension); + + /* + * \brief + * Returns the byte order used by the + * WKBWriter. + */ + int + getByteOrder() const + { + return byteOrder; + } + + /* + * Sets the byte order used by the + * WKBWriter. + */ + void setByteOrder(int newByteOrder); + + /* + * \brief + * Returns whether SRID values are output by the + * WKBWriter. + */ + bool + getIncludeSRID() const + { + return includeSRID; + } + + /* + * Sets whether SRID values should be output by the + * WKBWriter. + */ + void + setIncludeSRID(bool newIncludeSRID) + { + includeSRID = newIncludeSRID; + } + + /* + * \brief + * Returns the WKB flavor the writer will emit. + */ + int + getFlavor() const + { + return flavor; + } + + /* + * \brief + * Set the WKB flavor the writer will emit. + */ + void setFlavor(int newFlavor); + + /** + * \brief Write a Geometry to an ostream. + * + * @param g the geometry to write + * @param os the output stream + * @throws IOException + */ + void write(const geom::Geometry& g, std::ostream& os); + // throws IOException, ParseException + + /** + * \brief Write a Geometry to an ostream in binary hex format. + * + * @param g the geometry to write + * @param os the output stream + * @throws IOException + */ + void writeHEX(const geom::Geometry& g, std::ostream& os); + // throws IOException, ParseException + + static int getWkbType(const geom::Geometry&); + +private: + + // 2, 3, or 4 + uint8_t defaultOutputDimension; + OrdinateSet outputOrdinates; + + // WKBConstants::wkbwkbXDR | WKBConstants::wkbNDR + int byteOrder; + // WKBConstants::wkbIso | WKBConstants::wkbExtended + int flavor; + + bool includeSRID; + + std::ostream* outStream; + + unsigned char buf[8]; + + void writePoint(const geom::Point& p); + void writePointEmpty(const geom::Point& p); + // throws IOException + + void writeSimpleCurve(const geom::SimpleCurve& ls); + // throws IOException + + void writeCompoundCurve(const geom::CompoundCurve& curve); + + void writePolygon(const geom::Polygon& p); + // throws IOException + + void writeCurvePolygon(const geom::CurvePolygon& p); + + void writeGeometryCollection(const geom::GeometryCollection& gc); + // throws IOException, ParseException + + void writeCoordinateSequence(const geom::CoordinateSequence& cs, bool sized); + // throws IOException + + void writeCoordinate(const geom::CoordinateSequence& cs, std::size_t idx); + // throws IOException + + void writeGeometryType(int geometryType, int SRID); + // throws IOException + + void writeSRID(int SRID); + // throws IOException + + void writeByteOrder(); + // throws IOException + + void writeInt(int intValue); + // throws IOException + + OrdinateSet getOutputOrdinates(OrdinateSet ordinates); + +}; + +} // namespace io +} // namespace geos + diff --git a/Sources/geos/include/geos/io/WKTFileReader.h b/Sources/geos/include/geos/io/WKTFileReader.h new file mode 100644 index 0000000..9785d6a --- /dev/null +++ b/Sources/geos/include/geos/io/WKTFileReader.h @@ -0,0 +1,46 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + +class Geometry; +class PrecisionModel; +} +} + +namespace geos { +namespace io { + +class GEOS_DLL WKTFileReader { + +public: + WKTFileReader(); + ~WKTFileReader(); + + std::vector> read(std::string fname); + +private: + std::unique_ptr readGeom(std::ifstream& f, geos::io::WKTReader& rdr); +}; + +} +} diff --git a/Sources/geos/include/geos/io/WKTReader.h b/Sources/geos/include/geos/io/WKTReader.h new file mode 100644 index 0000000..7294d64 --- /dev/null +++ b/Sources/geos/include/geos/io/WKTReader.h @@ -0,0 +1,165 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/WKTReader.java rev. 1.1 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace io { +class StringTokenizer; +} +namespace geom { +class Coordinate; +class CoordinateSequence; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class CircularString; +class CompoundCurve; +class Polygon; +class CurvePolygon; +class MultiPoint; +class MultiLineString; +class MultiCurve; +class MultiPolygon; +class MultiSurface; +class PrecisionModel; +} +} + + +namespace geos { +namespace io { + +/** + * \class WKTReader + * \brief WKT parser class; see also WKTWriter. + */ +class GEOS_DLL WKTReader { +public: + + /** + * \brief Initialize parser with given GeometryFactory. + * + * Note that all Geometry objects created by the + * parser will contain a pointer to the given factory + * so be sure you'll keep the factory alive for the + * whole WKTReader and created Geometry life. + */ + explicit WKTReader(const geom::GeometryFactory& gf) + : geometryFactory(&gf) + , precisionModel(gf.getPrecisionModel()) + , fixStructure(false) + {}; + + /** @deprecated in 3.4.0 */ + explicit WKTReader(const geom::GeometryFactory* gf) + : geometryFactory(gf) + , precisionModel(gf->getPrecisionModel()) + , fixStructure(false) + {}; + + /** + * \brief Initialize parser with default GeometryFactory. + * + */ + WKTReader() + : geometryFactory(geom::GeometryFactory::getDefaultInstance()) + , precisionModel(geometryFactory->getPrecisionModel()) + , fixStructure(false) + {}; + + ~WKTReader() {}; + + void + setFixStructure(bool doFixStructure) { + fixStructure = doFixStructure; + } + + /// Parse a WKT string returning a Geometry + template + std::unique_ptr read(const std::string& wkt) const { + auto g = read(wkt); + auto gt = dynamic_cast(g.get()); + if (!gt) { + // Can improve this message once there's a good way to get a string repr of T + throw io::ParseException("Unexpected WKT type"); + } + return std::unique_ptr(static_cast(g.release())); + } + + std::unique_ptr read(const std::string& wellKnownText) const; + +protected: + std::unique_ptr getCoordinates(io::StringTokenizer* tokenizer, OrdinateSet& ordinates) const; + static double getNextNumber(io::StringTokenizer* tokenizer); + static std::string getNextEmptyOrOpener(io::StringTokenizer* tokenizer, OrdinateSet& dim); + static std::string getNextCloserOrComma(io::StringTokenizer* tokenizer); + static std::string getNextCloser(io::StringTokenizer* tokenizer); + static std::string getNextWord(io::StringTokenizer* tokenizer); + std::unique_ptr readGeometryTaggedText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags, const geom::GeometryTypeId* emptyType = nullptr) const; + + std::unique_ptr readPointText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readLineStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readLinearRingText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiPointText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readPolygonText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiLineStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiPolygonText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readGeometryCollectionText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCircularStringText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCompoundCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readCurvePolygonText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + std::unique_ptr readMultiSurfaceText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + + /// Read the contents of a LINEARRING, LINESTRING, CIRCULARSTRING, or COMPOUNDCURVE + std::unique_ptr readCurveText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; + + /// Read the contents of a POLYGON or a CURVEPOLYGON + std::unique_ptr readSurfaceText(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags) const; +private: + const geom::GeometryFactory* geometryFactory; + const geom::PrecisionModel* precisionModel; + bool fixStructure; + + void getPreciseCoordinate(io::StringTokenizer* tokenizer, OrdinateSet& ordinateFlags, geom::CoordinateXYZM&) const; + + static bool isNumberNext(io::StringTokenizer* tokenizer); + static bool isOpenerNext(io::StringTokenizer* tokenizer); + + static void readOrdinateFlags(const std::string & s, OrdinateSet& ordinateFlags); + static bool isTypeName(const std::string & type, const std::string & typeName); +}; + +} // namespace io +} // namespace geos + + + diff --git a/Sources/geos/include/geos/io/WKTStreamReader.h b/Sources/geos/include/geos/io/WKTStreamReader.h new file mode 100644 index 0000000..e9b7d7c --- /dev/null +++ b/Sources/geos/include/geos/io/WKTStreamReader.h @@ -0,0 +1,48 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + +class Geometry; +class PrecisionModel; +} +} + +namespace geos { +namespace io { + +class GEOS_DLL WKTStreamReader { + +public: + WKTStreamReader(std::istream& instr); + ~WKTStreamReader(); + + std::unique_ptr next(); + +private: + + std::istream& instr; + WKTReader rdr; +}; + +} +} diff --git a/Sources/geos/include/geos/io/WKTWriter.h b/Sources/geos/include/geos/io/WKTWriter.h new file mode 100644 index 0000000..6acaac3 --- /dev/null +++ b/Sources/geos/include/geos/io/WKTWriter.h @@ -0,0 +1,342 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: io/WKTWriter.java rev. 1.34 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateXY; +class CoordinateXYZM; +class CoordinateSequence; +class Curve; +class CompoundCurve; +class Geometry; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +class MultiPoint; +class MultiLineString; +class MultiPolygon; +class PrecisionModel; +class SimpleCurve; +class Surface; +} +namespace io { +class Writer; +} +} + + +namespace geos { +namespace io { + +/** + * \class WKTWriter + * + * \brief Outputs the textual representation of a Geometry. + * See also WKTReader. + * + * The WKTWriter outputs coordinates rounded to the precision + * model. No more than the maximum number of necessary decimal places will be + * output. + * + * The Well-known Text format is defined in the OpenGIS Simple Features + * Specification for SQL. + * + * A non-standard "LINEARRING" tag is used for LinearRings. The WKT spec does + * not define a special tag for LinearRings. The standard tag to use is + * "LINESTRING". + * + * See WKTReader for parsing. + * + */ +class GEOS_DLL WKTWriter { +public: + WKTWriter(); + ~WKTWriter() = default; + + //string(count, ch) can be used for this + //static string stringOfChar(char ch, int count); + + /// Returns WKT string for the given Geometry + std::string write(const geom::Geometry* geometry); + + std::string write(const geom::Geometry& geometry); + + // Send Geometry's WKT to the given Writer + void write(const geom::Geometry* geometry, Writer* writer); + + std::string writeFormatted(const geom::Geometry* geometry); + + void writeFormatted(const geom::Geometry* geometry, Writer* writer); + + /** + * Generates the WKT for a N-point LineString. + * + * @param seq the sequence to output + * + * @return the WKT + */ + static std::string toLineString(const geom::CoordinateSequence& seq); + + /** + * Generates the WKT for a 2-point LineString. + * + * @param p0 the first coordinate + * @param p1 the second coordinate + * + * @return the WKT + */ + static std::string toLineString(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + + /** + * Generates the WKT for a Point. + * + * @param p0 the point coordinate + * + * @return the WKT + */ + static std::string toPoint(const geom::Coordinate& p0); + static std::string toPoint(const geom::CoordinateXY& p0); + + /** + * Sets the rounding precision when writing the WKT + * a precision of -1 disables it + * + * @param p0 the new precision to use + * + */ + void setRoundingPrecision(int p0); + + /** + * Enables/disables trimming of unnecessary decimals + * + * @param p0 the trim boolean + * + */ + void setTrim(bool p0); + + /** + * Enables/disables removal of Z/M dimensions that have + * no non-NaN values in a geometry. + * + * @brief setRemoveEmptyDimensions + * @param remove + */ + void setRemoveEmptyDimensions(bool remove) + { + removeEmptyDimensions = remove; + } + + /** + * Enable old style 3D/4D WKT generation. + * + * By default the WKTWriter produces new style 3D/4D WKT + * (ie. "POINT Z (10 20 30)") but if this method is used + * to turn on old style WKT production then the WKT will + * be formatted in the style "POINT (10 20 30)". + * + * @param useOld3D true or false + */ + void + setOld3D(bool useOld3D) + { + old3D = useOld3D; + } + + /* + * \brief + * Returns the output dimension used by the + * WKTWriter. + */ + int + getOutputDimension() const + { + return defaultOutputDimension; + } + + /* + * Sets the output dimension used by the WKTWriter. + * + * @param newOutputDimension Supported values are 2, 3 or 4. + * Default since GEOS 3.12 is 4. + * Note that 3 indicates up to 3 dimensions will be + * written but 2D WKT is still produced for 2D geometries. + */ + void setOutputDimension(uint8_t newOutputDimension); + + static std::string writeNumber(double d, bool trim, uint32_t precision); + static int writeTrimmedNumber(double d, uint32_t precision, char* buf); + +protected: + + int decimalPlaces; + + void appendGeometryTaggedText( + const geom::Geometry& geometry, + OrdinateSet outputOrdinates, + int level, + Writer& writer) const; + + void appendTag( + const geom::Geometry& geometry, + OrdinateSet outputOrdinates, + Writer& writer) const; + + void appendPointTaggedText( + const geom::Point& point, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendSimpleCurveTaggedText( + const geom::SimpleCurve& lineString, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendCompoundCurveTaggedText( + const geom::CompoundCurve& lineString, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendSurfaceTaggedText( + const geom::Surface& polygon, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendMultiPointTaggedText( + const geom::MultiPoint& multipoint, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendMultiCurveTaggedText( + const geom::GeometryCollection& multiCurve, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendMultiSurfaceTaggedText( + const geom::GeometryCollection& multiSurface, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendGeometryCollectionTaggedText( + const geom::GeometryCollection& geometryCollection, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendOrdinateText(OrdinateSet outputOrdinates, + Writer& writer) const; + + void appendSequenceText(const geom::CoordinateSequence& seq, + OrdinateSet outputOrdinates, + int level, + bool doIntent, + Writer& writer) const; + + void appendCoordinate(const geom::CoordinateXYZM& coordinate, + OrdinateSet outputOrdinates, + Writer& writer) const; + + std::string writeNumber(double d) const; + + void appendCurveText( + const geom::Curve& lineString, + OrdinateSet outputOrdinates, + int level, bool doIndent, Writer& writer) const; + + void appendSimpleCurveText( + const geom::SimpleCurve& lineString, + OrdinateSet outputOrdinates, + int level, bool doIndent, Writer& writer) const; + + void appendSurfaceText( + const geom::Surface& polygon, + OrdinateSet outputOrdinates, + int level, bool indentFirst, Writer& writer) const; + + void appendMultiPointText( + const geom::MultiPoint& multiPoint, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendMultiCurveText( + const geom::GeometryCollection& multiCurve, + OrdinateSet outputOrdinates, + int level, bool indentFirst, Writer& writer) const; + + void appendMultiSurfaceText( + const geom::GeometryCollection& multiSurface, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + + void appendGeometryCollectionText( + const geom::GeometryCollection& geometryCollection, + OrdinateSet outputOrdinates, + int level, Writer& writer) const; + +private: + + enum { + INDENT = 2 + }; + + bool isFormatted; + + int roundingPrecision; + + bool trim; + + bool removeEmptyDimensions = false; + + static constexpr int coordsPerLine = 10; + + uint8_t defaultOutputDimension; + bool old3D; + + void writeFormatted( + const geom::Geometry* geometry, + bool isFormatted, Writer* writer); + + void indent(int level, Writer* writer) const; +}; + +} // namespace geos::io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/io/Writer.h b/Sources/geos/include/geos/io/Writer.h new file mode 100644 index 0000000..fa8bbfe --- /dev/null +++ b/Sources/geos/include/geos/io/Writer.h @@ -0,0 +1,51 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: ORIGINAL WORK to be used like java.io.Writer + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace io { + +class GEOS_DLL Writer { +public: + Writer(); + void reserve(std::size_t capacity); + ~Writer() = default; + void write(const std::string& txt); + const std::string& toString(); +private: + std::string str; +}; + +} // namespace geos::io +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/linearref/ExtractLineByLocation.h b/Sources/geos/include/geos/linearref/ExtractLineByLocation.h new file mode 100644 index 0000000..52dd1c4 --- /dev/null +++ b/Sources/geos/include/geos/linearref/ExtractLineByLocation.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/ExtractLineByLocation.java rev. 1.10 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Extracts the subline of a linear [Geometry](@ref geom::Geometry) between + * two {@link LinearLocation}s on the line. + */ +class ExtractLineByLocation { + +private: + const geom::Geometry* line; + std::unique_ptr reverse(const geom::Geometry* linear); + + /** + * Assumes input is valid (e.g. start <= end) + * + * @param start + * @param end + * @return a linear geometry + */ + std::unique_ptr computeLine(const LinearLocation& start, const LinearLocation& end); + + /** + * Assumes input is valid (e.g. start <= end) + * + * @param start + * @param end + * @return a linear geometry + */ + std::unique_ptr computeLinear(const LinearLocation& start, const LinearLocation& end); + +public: + /** \brief + * Computes the subline of a [LineString](@ref geom::LineString) between + * two {@link LinearLocation}s on the line. + * + * If the start location is after the end location, + * the computed geometry is reversed. + * + * @param line the line to use as the baseline + * @param start the start location + * @param end the end location + * @return the extracted subline + */ + static std::unique_ptr extract(const geom::Geometry* line, const LinearLocation& start, const LinearLocation& end); + + ExtractLineByLocation(const geom::Geometry* line); + + /** \brief + * Extracts a subline of the input. + * + * If `end < start` the linear geometry computed will be reversed. + * + * @param start the start location + * @param end the end location + * @return a linear geometry + */ + std::unique_ptr extract(const LinearLocation& start, const LinearLocation& end); + +}; + +} +} diff --git a/Sources/geos/include/geos/linearref/LengthIndexOfPoint.h b/Sources/geos/include/geos/linearref/LengthIndexOfPoint.h new file mode 100644 index 0000000..a828d55 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LengthIndexOfPoint.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LengthIndexOfPoint.java rev. 1.10 + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + + +namespace geos { +namespace linearref { // geos::linearref + +/** + * \brief + * Computes the length index of the point + * on a linear Geometry nearest a given Coordinate. + * + * The nearest point is not necessarily unique; this class + * always computes the nearest point closest to + * the start of the geometry. + */ +class LengthIndexOfPoint { + +private: + const geom::Geometry* linearGeom; + + double indexOfFromStart(const geom::Coordinate& inputPt, const double minIndex) const; + + double segmentNearestMeasure(const geom::LineSegment* seg, + const geom::Coordinate& inputPt, + double segmentStartMeasure) const; +public: + static double indexOf(const geom::Geometry* linearGeom, const geom::Coordinate& inputPt); + + static double indexOfAfter(const geom::Geometry* linearGeom, const geom::Coordinate& inputPt, double minIndex); + + LengthIndexOfPoint(const geom::Geometry* linearGeom); + + /** + * Find the nearest location along a linear Geometry to a given point. + * + * @param inputPt the coordinate to locate + * @return the location of the nearest point + */ + double indexOf(const geom::Coordinate& inputPt) const; + + /** \brief + * Finds the nearest index along the linear Geometry + * to a given Coordinate after the specified minimum index. + * + * If possible the location returned will be strictly greater than the + * minLocation. + * If this is not possible, the + * value returned will equal minLocation. + * (An example where this is not possible is when + * minLocation = [end of line] ). + * + * @param inputPt the coordinate to locate + * @param minIndex the minimum location for the point location + * @return the location of the nearest point + */ + double indexOfAfter(const geom::Coordinate& inputPt, double minIndex) const; + +}; + +} +} diff --git a/Sources/geos/include/geos/linearref/LengthIndexedLine.h b/Sources/geos/include/geos/linearref/LengthIndexedLine.h new file mode 100644 index 0000000..d5c8237 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LengthIndexedLine.h @@ -0,0 +1,211 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LengthIndexedLine.java r463 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Supports linear referencing along a linear geom::Geometry + * using the length along the line as the index. + * + * Negative length values are taken as measured in the reverse direction + * from the end of the geometry. + * Out-of-range index values are handled by clamping + * them to the valid range of values. + * Non-simple lines (i.e. which loop back to cross or touch + * themselves) are supported. + */ + +class GEOS_DLL LengthIndexedLine { +private: + const geom::Geometry* linearGeom; + LinearLocation locationOf(double index) const; + LinearLocation locationOf(double index, bool resolveLower) const; + double positiveIndex(double index) const; + +public: + + /** \brief + * Constructs an object which allows a linear [Geometry](@ref geom::Geometry) + * to be linearly referenced using length as an index. + * + * @param linearGeom the linear geometry to reference along + */ + + LengthIndexedLine(const geom::Geometry* linearGeom); + + /** \brief + * Computes the [Coordinate](@ref geom::Coordinate) for the point + * on the line at the given index. + * + * If the index is out of range the first or last point on the + * line will be returned. + * The Z-ordinate of the computed point will be interpolated from + * the Z-ordinates of the line segment containing it, if they exist. + * + * @param index the index of the desired point + * @return the Coordinate at the given index + */ + geom::Coordinate extractPoint(double index) const; + + + /** + * \brief + * Computes the [Coordinate](@ref geom::Coordinate) for the point + * on the line at the given index, offset by the given distance. + * + * If the index is out of range the first or last point on the + * line will be returned. + * The computed point is offset to the left of the line if the + * offset distance is positive, to the right if negative. + * + * The Z-ordinate of the computed point will be interpolated from + * the Z-ordinates of the line segment containing it, if they exist. + * + * @param index the index of the desired point + * @param offsetDistance the distance the point is offset from the segment + * (positive is to the left, negative is to the right) + * @return the Coordinate at the given index + */ + geom::Coordinate extractPoint(double index, double offsetDistance) const; + + /** \brief + * Computes the [LineString](@ref geom::LineString) for the interval + * on the line between the given indices. + * + * If the endIndex lies before the startIndex, + * the computed geometry is reversed. + * + * @param startIndex the index of the start of the interval + * @param endIndex the index of the end of the interval + * @return the linear interval between the indices + */ + std::unique_ptr extractLine(double startIndex, double endIndex) const; + + + /** \brief + * Computes the minimum index for a point on the line. + * + * If the line is not simple (i.e. loops back on itself) + * a single point may have more than one possible index. + * In this case, the smallest index is returned. + * + * The supplied point does not *necessarily* have to lie precisely + * on the line, but if it is far from the line the accuracy and + * performance of this function is not guaranteed. + * Use {@link #project} to compute a guaranteed result for points + * which may be far from the line. + * + * @param pt a point on the line + * @return the minimum index of the point + * + * @see project + */ + double indexOf(const geom::Coordinate& pt) const; + + /** \brief + * Finds the index for a point on the line which is + * greater than the given index. + * + * If no such index exists, returns `minIndex`. + * This method can be used to determine all indexes for + * a point which occurs more than once on a non-simple line. + * It can also be used to disambiguate cases where the given point lies + * slightly off the line and is equidistant from two different + * points on the line. + * + * The supplied point does not `*necessarily* have to lie precisely + * on the line, but if it is far from the line the accuracy and + * performance of this function is not guaranteed. + * Use {@link #project} to compute a guaranteed result for points + * which may be far from the line. + * + * @param pt a point on the line + * @param minIndex the value the returned index must be greater than + * @return the index of the point greater than the given minimum index + * + * @see project + */ + double indexOfAfter(const geom::Coordinate& pt, double minIndex) const; + + /** \brief + * Computes the indices for a subline of the line. + * + * (The subline must **conform** to the line; that is, + * all vertices in the subline (except possibly the first and last) + * must be vertices of the line and occur in the same order). + * + * @param subLine a subLine of the line + * @return a pair of indices for the start and end of the subline. + */ + double* indicesOf(const geom::Geometry* subLine) const; + + + /** \brief + * Computes the index for the closest point on the line to the given point. + * + * If more than one point has the closest distance the first one along the line + * is returned. + * (The point does not necessarily have to lie precisely on the line.) + * + * @param pt a point on the line + * @return the index of the point + */ + double project(const geom::Coordinate& pt) const; + + /** \brief + * Returns the index of the start of the line + * @return the start index + */ + double getStartIndex() const; + + /** \brief + * Returns the index of the end of the line + * @return the end index + */ + double getEndIndex() const; + + /** \brief + * Tests whether an index is in the valid index range for the line. + * + * @param index the index to test + * @return `true` if the index is in the valid range + */ + bool isValidIndex(double index) const; + + + /** \brief + * Computes a valid index for this line by clamping the given index + * to the valid range of index values. + * + * @return a valid index value + */ + double clampIndex(double index) const; +}; + +} +} diff --git a/Sources/geos/include/geos/linearref/LengthLocationMap.h b/Sources/geos/include/geos/linearref/LengthLocationMap.h new file mode 100644 index 0000000..0e83062 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LengthLocationMap.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LengthLocationMap.java r463 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Computes the LinearLocation for a given length along a linear + * [Geometry](@ref geom::Geometry). + * + * Negative lengths are measured in reverse from end of the linear geometry. + * Out-of-range values are clamped. + */ +class LengthLocationMap { + + +private: + const geom::Geometry* linearGeom; + + LinearLocation getLocationForward(double length) const; + + LinearLocation resolveHigher(const LinearLocation& loc) const; + +public: + + // TODO: cache computed cumulative length for each vertex + // TODO: support user-defined measures + // TODO: support measure index for fast mapping to a location + + /** + * \brief + * Computes the LinearLocation for a + * given length along a linear [Geometry](@ref geom::Geometry). + * + * @param linearGeom the linear geometry to use + * @param length the length index of the location + * @return the LinearLocation for the length + */ + static LinearLocation + getLocation(const geom::Geometry* linearGeom, double length) + { + LengthLocationMap locater(linearGeom); + return locater.getLocation(length); + } + + /** + * \brief + * Computes the LinearLocation for a + * given length along a linear [Geometry]. + * + * @param linearGeom the linear geometry to use + * @param length the length index of the location + * @param resolveLower if `true` lengths are resolved to the + * lowest possible index + * @return the LinearLocation for the length + */ + static LinearLocation + getLocation(const geom::Geometry* linearGeom, double length, bool resolveLower) + { + LengthLocationMap locater(linearGeom); + return locater.getLocation(length, resolveLower); + } + + /** + * Computes the length for a given LinearLocation + * on a linear [Geometry](@ref geom::Geometry). + * + * @param linearGeom the linear geometry to use + * @param loc the LinearLocation index of the location + * @return the length for the LinearLocation + */ + static double getLength(const geom::Geometry* linearGeom, const LinearLocation& loc); + + LengthLocationMap(const geom::Geometry* linearGeom); + + /** + * \brief + * Compute the LinearLocation corresponding to a length. + * + * Negative lengths are measured in reverse from end of the linear geometry. + * Out-of-range values are clamped. + * Ambiguous indexes are resolved to the lowest possible location value, + * depending on the value of `resolveLower`. + * + * @param length the length index + * @param resolveLower if `true` lengths are resolved to the + * lowest possible index + * @return the corresponding LinearLocation + */ + LinearLocation getLocation(double length, bool resolveLower) const; + + /** + * \brief + * Compute the LinearLocation corresponding to a length. + * + * Negative lengths are measured in reverse from end of the linear geometry. + * Out-of-range values are clamped. + * Ambiguous indexes are resolved to the lowest possible location value. + * + * @param length the length index + * @return the corresponding LinearLocation + */ + LinearLocation getLocation(double length) const; + + double getLength(const LinearLocation& loc) const; + +}; + +} // geos.linearref +} // geos + diff --git a/Sources/geos/include/geos/linearref/LinearGeometryBuilder.h b/Sources/geos/include/geos/linearref/LinearGeometryBuilder.h new file mode 100644 index 0000000..7c20e6b --- /dev/null +++ b/Sources/geos/include/geos/linearref/LinearGeometryBuilder.h @@ -0,0 +1,106 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LinearGeometryBuilder.java rev. 1.1 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Builds a linear geometry ([LineString](@ref geom::LineString) or + * [MultiLineString](@ref geom::MultiLineString)) incrementally (point-by-point). + * + * @version 1.7 + */ +class LinearGeometryBuilder { +private: + const geom::GeometryFactory* geomFact; + + typedef std::vector> GeomPtrVect; + + // Geometry elements owned by this class + GeomPtrVect lines; + + bool ignoreInvalidLines; + bool fixInvalidLines; + std::unique_ptr coordList; + + geom::Coordinate lastPt; + +public: + LinearGeometryBuilder(const geom::GeometryFactory* geomFact); + + ~LinearGeometryBuilder(); + + /** \brief + * Allows invalid lines to be ignored rather than causing Exceptions. + * + * An invalid line is one which has only one unique point. + * + * @param ignoreInvalidLines `true` if short lines are + * to be ignored + */ + void setIgnoreInvalidLines(bool ignoreInvalidLines); + + /** \brief + * Allows invalid lines to be ignored rather than causing Exceptions. + * + * An invalid line is one which has only one unique point. + * + * @param fixInvalidLines `true` if short lines are + * to be ignored + */ + void setFixInvalidLines(bool fixInvalidLines); + + /** \brief + * Adds a point to the current line. + * + * @param pt the Coordinate to add + */ + void add(const geom::Coordinate& pt); + + /** \brief + * Adds a point to the current line. + * + * @param pt the Coordinate to add + * @param allowRepeatedPoints if set to `false`, repeated coordinates are + * collapsed + */ + void add(const geom::Coordinate& pt, bool allowRepeatedPoints); + + // NOTE strk: why return by value ? + geom::Coordinate getLastCoordinate() const; + + /// Terminate the current LineString. + void endLine(); + + std::unique_ptr getGeometry(); +}; + +} // namespace geos.linearref +} // namespace geos + diff --git a/Sources/geos/include/geos/linearref/LinearIterator.h b/Sources/geos/include/geos/linearref/LinearIterator.h new file mode 100644 index 0000000..9cfb297 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LinearIterator.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LinearIterator.java r463 + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace geos { +namespace linearref { + +/** \brief + * An iterator over the components and coordinates of a linear geometry + * (LineString or MultiLineString). + * + * The standard usage pattern for a LinearIterator is: + * + * ~~~~~~ + * for (LinearIterator it = new LinearIterator(...); it.hasNext(); it.next()) { + * ... + * int ci = it.getComponentIndex(); // for example + * int vi = it.getVertexIndex(); // for example + * ... + * } + * ~~~~~~ + * + * @version 1.7 + */ +class LinearIterator { +public: + /** \brief + * Creates an iterator initialized to the start of a linear Geometry + * + * @param linear the linear geometry to iterate over + */ + LinearIterator(const geom::Geometry* linear); + + /** \brief + * Creates an iterator starting at + * a LinearLocation on a linear geom::Geometry + * + * @param linear the linear geometry to iterate over + * @param start the location to start at + */ + LinearIterator(const geom::Geometry* linear, const LinearLocation& start); + + /** \brief + * Creates an iterator starting at a component and vertex in a + * linear geom::Geometry + * + * @param linear the linear geometry to iterate over + * @param componentIndex the component to start at + * @param vertexIndex the vertex to start at + */ + LinearIterator(const geom::Geometry* linear, std::size_t componentIndex, std::size_t vertexIndex); + + /** \brief + * Tests whether there are any vertices left to iterator over. + * @return `true` if there are more vertices to scan + */ + bool hasNext() const; + + + /** \brief + * Moves the iterator ahead to the next vertex and (possibly) linear component. + */ + void next(); + + /** \brief + * Checks whether the iterator cursor is pointing to the + * endpoint of a component geom::LineString. + * + * @return `true` if the iterator is at an endpoint + */ + bool isEndOfLine() const; + + /** \brief + * The component index of the vertex the iterator is currently at. + * @return the current component index + */ + std::size_t getComponentIndex() const; + + /** \brief + * The vertex index of the vertex the iterator is currently at. + * @return the current vertex index + */ + std::size_t getVertexIndex() const; + + /** \brief + * Gets the geom::LineString component the iterator is current at. + * @return a linestring + */ + const geom::LineString* getLine() const; + + /** \brief + * Gets the first geom::Coordinate of the current segment. + * (the coordinate of the current vertex). + * @return a Coordinate + */ + geom::Coordinate getSegmentStart() const; + + /** \brief + * Gets the second geom::Coordinate of the current segment. + * (the coordinate of the next vertex). + * If the iterator is at the end of a line, `null` is returned. + * + * @return a Coordinate or `null` + */ + geom::Coordinate getSegmentEnd() const; + +private: + + static std::size_t segmentEndVertexIndex(const LinearLocation& loc); + + const geom::LineString* currentLine; + std::size_t vertexIndex; + std::size_t componentIndex; + const geom::Geometry* linear; + const std::size_t numLines; + + /** + * Invariant: currentLine <> null if the iterator is pointing + * at a valid coordinate + * + * @throws IllegalArgumentException if linearGeom is not {@link Lineal} + */ + void loadCurrentLine(); + + // Declare type as noncopyable + LinearIterator(const LinearIterator& other) = delete; + LinearIterator& operator=(const LinearIterator& rhs) = delete; +}; + +} +} // namespace geos::linearref + diff --git a/Sources/geos/include/geos/linearref/LinearLocation.h b/Sources/geos/include/geos/linearref/LinearLocation.h new file mode 100644 index 0000000..bbce2b0 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LinearLocation.h @@ -0,0 +1,248 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LinearLocation.java r463 + * + **********************************************************************/ + +#pragma once + +#include +#include // for std::unique_ptr + +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + + +/** \brief + * Represents a location along a [LineString](@ref geom::LineString) + * or [MultiLineString](@ref geom::MultiLineString). + * + * The referenced geometry is not maintained within this location, but + * must be provided for operations which require it. + * Various methods are provided to manipulate the location value and + * query the geometry it references. + */ +class LinearLocation { +private: + std::size_t componentIndex; + std::size_t segmentIndex; + double segmentFraction; + + /** \brief + * Ensures the individual values are locally valid. + * + * Does **not** ensure that the indexes are valid for + * a particular linear geometry. + * + * @see clamp + */ + void normalize(); + +public: + /** \brief + * Gets a location which refers to the end of a linear + * [Geometry](@ref geom::Geometry). + * + * @param linear the linear geometry + * @return a new `LinearLocation` + */ + static LinearLocation getEndLocation(const geom::Geometry* linear); + + /** \brief + * Computes the [Coordinate](@ref geom::Coordinate) of a point a given + * fraction along the line segment `(p0, p1)`. + * + * If the fraction is greater than 1.0 the last + * point of the segment is returned. + * If the fraction is less than or equal to 0.0 the first point + * of the segment is returned. + * The Z ordinate is interpolated from the Z-ordinates of + * the given points, if they are specified. + * + * @param p0 the first point of the line segment + * @param p1 the last point of the line segment + * @param frac the length to the desired point + * @return the Coordinate of the desired point + */ + static geom::Coordinate pointAlongSegmentByFraction(const geom::Coordinate& p0, const geom::Coordinate& p1, + double frac); + + + /** \brief + * Creates a location referring to the start of a linear geometry + */ + LinearLocation(std::size_t segmentIndex = 0, double segmentFraction = 0.0); + + LinearLocation(std::size_t componentIndex, std::size_t segmentIndex, double segmentFraction); + + /** \brief + * Ensures the indexes are valid for a given linear [Geometry](@ref geom::Geometry). + * + * @param linear a linear geometry + */ + void clamp(const geom::Geometry* linear); + + /** \brief + * Snaps the value of this location to the nearest vertex on the given linear + * [Geometry](@ref geom::Geometry), if the vertex is closer than `minDistance`. + * + * @param linearGeom a linear geometry + * @param minDistance the minimum allowable distance to a vertex + */ + void snapToVertex(const geom::Geometry* linearGeom, double minDistance); + + /** \brief + * Gets the length of the segment in the given + * Geometry containing this location. + * + * @param linearGeom a linear geometry + * @return the length of the segment + */ + double getSegmentLength(const geom::Geometry* linearGeom) const; + + /** \brief + * Sets the value of this location to + * refer the end of a linear geometry. + * + * @param linear the linear geometry to set + */ + void setToEnd(const geom::Geometry* linear); + + /** \brief + * Gets the component index for this location. + * + * @return the component index + */ + std::size_t getComponentIndex() const; + + /** \brief + * Gets the segment index for this location. + * + * @return the segment index + */ + std::size_t getSegmentIndex() const; + + /** \brief + * Gets the segment fraction for this location. + * + * @return the segment fraction + */ + double getSegmentFraction() const; + + /** \brief + * Tests whether this location refers to a vertex. + * + * @return true if the location is a vertex + */ + bool isVertex() const; + + /** \brief + * Gets the [Coordinate](@ref geom::Coordinate) along the given linear + * [Geometry](@ref geom::Geometry) which is referenced by this location. + * + * @param linearGeom the linear geometry referenced by this location + * @return the Coordinate at the location + */ + geom::Coordinate getCoordinate(const geom::Geometry* linearGeom) const; + + /** \brief + * Gets a [LineSegment](@ref geom::LineSegment) representing the segment of the + * given linear [Geometry](@ref geom::Geometry) which contains this location. + * + * @param linearGeom a linear geometry + * @return the LineSegment containing the location + */ + std::unique_ptr getSegment(const geom::Geometry* linearGeom) const; + + /** \brief + * Tests whether this location refers to a valid + * location on the given linear [Geometry](@ref geom::Geometry). + * + * @param linearGeom a linear geometry + * @return true if this location is valid + */ + bool isValid(const geom::Geometry* linearGeom) const; + + /** \brief + * Compares this object with the specified object for order. + * + * @param other the LinearLocation with which this Coordinate is being compared + * @return a negative integer, zero, or a positive integer as this + * LinearLocation is less than, equal to, or greater than + * the specified LinearLocation + */ + int compareTo(const LinearLocation& other) const; + + /** \brief + * Compares this object with the specified index values for order. + * + * @param componentIndex1 a component index + * @param segmentIndex1 a segment index + * @param segmentFraction1 a segment fraction + * @return a negative integer, zero, or a positive integer as this LinearLocation + * is less than, equal to, or greater than the specified locationValues + */ + int compareLocationValues(std::size_t componentIndex1, std::size_t segmentIndex1, double segmentFraction1) const; + + /** \brief + * Compares two sets of location values for order. + * + * @param componentIndex0 a component index + * @param segmentIndex0 a segment index + * @param segmentFraction0 a segment fraction + * @param componentIndex1 another component index + * @param segmentIndex1 another segment index + * @param segmentFraction1 another segment fraction + * @return a negative integer, zero, or a positive integer + * as the first set of location values + * is less than, equal to, or greater than the second set of locationValues + */ + static int compareLocationValues( + std::size_t componentIndex0, std::size_t segmentIndex0, double segmentFraction0, + std::size_t componentIndex1, std::size_t segmentIndex1, double segmentFraction1); + + /** \brief + * Tests whether two locations are on the same + * segment in the parent [Geometry](@ref geom::Geometry). + * + * @param loc a location on the same geometry + * @return true if the locations are on the same segment of the parent geometry + */ + bool isOnSameSegment(const LinearLocation& loc) const; + + /** + * \brief + * Tests whether this location is an endpoint of + * the linear component it refers to. + * + * @param linearGeom the linear geometry referenced by this location + * @return true if the location is a component endpoint + */ + bool isEndpoint(const geom::Geometry& linearGeom) const; + + friend std::ostream& operator<< (std::ostream& out, const LinearLocation& obj); + +}; + + +} // namespace geos.linearref +} // namespace geos + diff --git a/Sources/geos/include/geos/linearref/LocationIndexOfLine.h b/Sources/geos/include/geos/linearref/LocationIndexOfLine.h new file mode 100644 index 0000000..978b228 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LocationIndexOfLine.h @@ -0,0 +1,72 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LocationIndexOfLine.java r731 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Determines the location of a subline along a linear [Geometry](@ref geom::Geometry). + * + * The location is reported as a pair of {@link LinearLocation}s. + * + * @note Currently this algorithm is not guaranteed to + * return the correct substring in some situations where + * an endpoint of the test line occurs more than once in the input line. + * (However, the common case of a ring is always handled correctly). + */ +class LocationIndexOfLine { + /** + * MD - this algorithm has been extracted into a class + * because it is intended to validate that the subline truly is a subline, + * and also to use the internal vertex information to unambiguously locate the subline. + */ +private: + const geom::Geometry* linearGeom; + +public: + + /** \brief + * Determines the location of a subline along a linear [Geometry](@ref geom::Geometry). + * + * The location is reported as a pair of [LinearLocations](@ref LinearLocation). + * + * @note Currently this algorithm is not guaranteed to + * return the correct substring in some situations where + * an endpoint of the test line occurs more than once in the input line. + * (However, the common case of a ring is always handled correctly). + * + * @note Caller must take of releasing with delete[] + * + */ + static LinearLocation* indicesOf(const geom::Geometry* linearGeom, const geom::Geometry* subLine); + + LocationIndexOfLine(const geom::Geometry* linearGeom); + + /// Caller must take of releasing with delete[] + LinearLocation* indicesOf(const geom::Geometry* subLine) const; +}; +} +} + diff --git a/Sources/geos/include/geos/linearref/LocationIndexOfPoint.h b/Sources/geos/include/geos/linearref/LocationIndexOfPoint.h new file mode 100644 index 0000000..bbb69ae --- /dev/null +++ b/Sources/geos/include/geos/linearref/LocationIndexOfPoint.h @@ -0,0 +1,77 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LinearGeometryBuilder.java r466 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** \brief + * Computes the LinearLocation of the point on a linear [Geometry](@ref geom::Geometry) + * nearest a given [Coordinate](@ref geom::Coordinate). + * + * The nearest point is not necessarily unique; this class always computes + * the nearest point closest to the start of the geometry. + */ +class LocationIndexOfPoint { + +private: + const geom::Geometry* linearGeom; + + LinearLocation indexOfFromStart(const geom::CoordinateXY& inputPt, const LinearLocation* minIndex) const; + +public: + static LinearLocation indexOf(const geom::Geometry* linearGeom, const geom::CoordinateXY& inputPt); + + static LinearLocation indexOfAfter(const geom::Geometry* linearGeom, const geom::CoordinateXY& inputPt, + const LinearLocation* minIndex); + + LocationIndexOfPoint(const geom::Geometry* linearGeom); + + /** \brief + * Find the nearest location along a linear [Geometry](@ref geom::Geometry) + * to a given point. + * + * @param inputPt the coordinate to locate + * @return the location of the nearest point + */ + LinearLocation indexOf(const geom::CoordinateXY& inputPt) const; + + /** \brief + * Find the nearest LinearLocation along the linear [Geometry](@ref geom::Geometry) + * to a given [Coordinate](@ref geom::Coordinate) after the specified minimum LinearLocation. + * + * If possible the location returned will be strictly greater than the + * `minLocation`. + * If this is not possible, the value returned will equal `minLocation`. + * (An example where this is not possible is when `minLocation = [end of line]`). + * + * @param inputPt the coordinate to locate + * @param minIndex the minimum location for the point location + * @return the location of the nearest point + */ + LinearLocation indexOfAfter(const geom::CoordinateXY& inputPt, const LinearLocation* minIndex) const; +}; +} +} diff --git a/Sources/geos/include/geos/linearref/LocationIndexedLine.h b/Sources/geos/include/geos/linearref/LocationIndexedLine.h new file mode 100644 index 0000000..f603590 --- /dev/null +++ b/Sources/geos/include/geos/linearref/LocationIndexedLine.h @@ -0,0 +1,277 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: linearref/LocationIndexedLine.java r466 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace linearref { // geos::linearref + +/** + * \brief + * Supports linear referencing along a linear + * [Geometry](@ref geom::Geometry) using + * [LinearLocations](@ref LinearLocation) as the index. + */ +class GEOS_DLL LocationIndexedLine { +private: + const geom::Geometry* linearGeom; + + void + checkGeometryType() + { + if(!linearGeom->isLineal()) { + throw util::IllegalArgumentException("Input geometry must be linear"); + } + } + +public: + + /** + * \brief + * Constructs an object which allows linear referencing along + * a given linear [Geometry](@ref geom::Geometry). + * + * @param p_linearGeom the linear geometry to reference along + */ + LocationIndexedLine(const geom::Geometry* p_linearGeom) + : linearGeom(p_linearGeom) + { + checkGeometryType(); + } + + /** + * \brief + * Computes the [Coordinate](@ref geom::Coordinate) for the point + * on the line at the given index. + * + * If the index is out of range the first or last point on the + * line will be returned. + * The Z-ordinate of the computed point will be interpolated from + * the Z-ordinates of the line segment containing it, if they exist. + * + * @param index the index of the desired point + * @return the Coordinate at the given index + */ + geom::Coordinate + extractPoint(const LinearLocation& index) const + { + return index.getCoordinate(linearGeom); + } + + + /** + * \brief + * Computes the [Coordinate](@ref geom::Coordinate) for the point + * on the line at the given index, offset by the given distance. + * + * If the index is out of range the first or last point on the + * line will be returned. + * The computed point is offset to the left of the line if the offset + * distance is positive, to the right if negative. + * + * The Z-ordinate of the computed point will be interpolated from + * the Z-ordinates of the line segment containing it, if they exist. + * + * @param index the index of the desired point + * @param offsetDistance the distance the point is offset from the segment + * (positive is to the left, negative is to the right) + * @return the Coordinate at the given index + */ + geom::Coordinate + extractPoint(const LinearLocation& index, + double offsetDistance) const + { + geom::Coordinate ret; + index.getSegment(linearGeom)->pointAlongOffset( + index.getSegmentFraction(), offsetDistance, ret + ); + return ret; + } + + /** + * \brief + * Computes the [LineString](@ref geom::LineString) for the interval + * on the line between the given indices. + * + * If the endIndex lies before the startIndex, + * the computed geometry is reversed. + * + * @param startIndex the index of the start of the interval + * @param endIndex the index of the end of the interval + * @return the linear interval between the indices + */ + std::unique_ptr + extractLine(const LinearLocation& startIndex, + const LinearLocation& endIndex) const + { + return ExtractLineByLocation::extract(linearGeom, startIndex, endIndex); + } + + + /** + * \brief + * Computes the index for a given point on the line. + * + * The supplied point does not necessarily have to lie precisely + * on the line, but if it is far from the line the accuracy and + * performance of this function is not guaranteed. + * Use {@link #project} to compute a guaranteed result for points + * which may be far from the line. + * + * @param pt a point on the line + * @return the index of the point + * + * @see project + */ + LinearLocation + indexOf(const geom::Coordinate& pt) const + { + return LocationIndexOfPoint::indexOf(linearGeom, pt); + } + + /** + * \brief + * Finds the index for a point on the line + * which is greater than the given index. + * + * If no such index exists, returns minIndex. + * This method can be used to determine all indexes for + * a point which occurs more than once on a non-simple line. + * It can also be used to disambiguate cases where the given point lies + * slightly off the line and is equidistant from two different + * points on the line. + * + * The supplied point does not necessarily have to lie precisely + * on the line, but if it is far from the line the accuracy and + * performance of this function is not guaranteed. + * Use {@link #project} to compute a guaranteed result for points + * which may be far from the line. + * + * @param pt a point on the line + * @param minIndex the value the returned index must be greater than + * @return the index of the point greater than the given minimum index + * + * @see project + */ + LinearLocation + indexOfAfter(const geom::Coordinate& pt, + const LinearLocation& minIndex) const + { + return LocationIndexOfPoint::indexOfAfter(linearGeom, pt, &minIndex); + } + + /** + * \brief + * Computes the indices for a subline of the line. + * + * (The subline must conform to the line; that is, + * all vertices in the subline (except possibly the first and last) + * must be vertices of the line and occur in the same order). + * + * @param subLine a subLine of the line + * @return a pair of indices for the start and end of the subline. + */ + LinearLocation* + indicesOf(const geom::Geometry* subLine) const + { + return LocationIndexOfLine::indicesOf(linearGeom, subLine); + } + + + /** + * \brief + * Computes the index for the closest point on the line to the given point. + * + * If more than one point has the closest distance the first one along + * the line is returned. + * (The point does not necessarily have to lie precisely on the line.) + * + * @param pt a point on the line + * @return the index of the point + */ + LinearLocation + project(const geom::Coordinate& pt) const + { + return LocationIndexOfPoint::indexOf(linearGeom, pt); + } + + /** + * \brief + * Returns the index of the start of the line + * + * @return the start index + */ + LinearLocation + getStartIndex() const + { + return LinearLocation(); + } + + /** + * \brief + * Returns the index of the end of the line + * + * @return the end index + */ + LinearLocation + getEndIndex() const + { + return LinearLocation::getEndLocation(linearGeom); + } + + /** + * \brief + * Tests whether an index is in the valid index range for the line. + * + * @param index the index to test + * @return `true` if the index is in the valid range + */ + bool + isValidIndex(const LinearLocation& index) const + { + return index.isValid(linearGeom); + } + + + /** + * \brief + * Computes a valid index for this line + * by clamping the given index to the valid range of index values + * + * @return a valid index value + */ + LinearLocation + clampIndex(const LinearLocation& index) const + { + LinearLocation loc = index; + loc.clamp(linearGeom); + return loc; + } +}; + +} // geos::linearref +} // geos diff --git a/Sources/geos/include/geos/math/DD.h b/Sources/geos/include/geos/math/DD.h new file mode 100644 index 0000000..61e3a08 --- /dev/null +++ b/Sources/geos/include/geos/math/DD.h @@ -0,0 +1,200 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Crunchy Data + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +/** + * Implements extended-precision floating-point numbers + * which maintain 106 bits (approximately 30 decimal digits) of precision. + *

+ * A DoubleDouble uses a representation containing two double-precision values. + * A number x is represented as a pair of doubles, x.hi and x.lo, + * such that the number represented by x is x.hi + x.lo, where + *

+ *    |x.lo| <= 0.5*ulp(x.hi)
+ * 
+ * and ulp(y) means "unit in the last place of y". + * The basic arithmetic operations are implemented using + * convenient properties of IEEE-754 floating-point arithmetic. + *

+ * The range of values which can be represented is the same as in IEEE-754. + * The precision of the representable numbers + * is twice as great as IEEE-754 double precision. + *

+ * The correctness of the arithmetic algorithms relies on operations + * being performed with standard IEEE-754 double precision and rounding. + * This is the Java standard arithmetic model, but for performance reasons + * Java implementations are not + * constrained to using this standard by default. + * Some processors (notably the Intel Pentium architecture) perform + * floating point operations in (non-IEEE-754-standard) extended-precision. + * A JVM implementation may choose to use the non-standard extended-precision + * as its default arithmetic mode. + * To prevent this from happening, this code uses the + * Java strictfp modifier, + * which forces all operations to take place in the standard IEEE-754 rounding model. + *

+ * The API provides both a set of value-oriented operations + * and a set of mutating operations. + * Value-oriented operations treat DoubleDouble values as + * immutable; operations on them return new objects carrying the result + * of the operation. This provides a simple and safe semantics for + * writing DoubleDouble expressions. However, there is a performance + * penalty for the object allocations required. + * The mutable interface updates object values in-place. + * It provides optimum memory performance, but requires + * care to ensure that aliasing errors are not created + * and constant values are not changed. + *

+ * For example, the following code example constructs three DD instances: + * two to hold the input values and one to hold the result of the addition. + *

+ *     DD a = new DD(2.0);
+ *     DD b = new DD(3.0);
+ *     DD c = a.add(b);
+ * 
+ * In contrast, the following approach uses only one object: + *
+ *     DD a = new DD(2.0);
+ *     a.selfAdd(3.0);
+ * 
+ *

+ * This implementation uses algorithms originally designed variously by + * Knuth, Kahan, Dekker, and Linnainmaa. + * Douglas Priest developed the first C implementation of these techniques. + * Other more recent C++ implementation are due to Keith M. Briggs and David Bailey et al. + * + *

References

+ *
    + *
  • Priest, D., Algorithms for Arbitrary Precision Floating Point Arithmetic, + * in P. Kornerup and D. Matula, Eds., Proc. 10th Symposium on Computer Arithmetic, + * IEEE Computer Society Press, Los Alamitos, Calif., 1991. + *
  • Yozo Hida, Xiaoye S. Li and David H. Bailey, + * Quad-Double Arithmetic: Algorithms, Implementation, and Application, + * manuscript, Oct 2000; Lawrence Berkeley National Laboratory Report BNL-46996. + *
  • David Bailey, High Precision Software Directory; + * http://crd.lbl.gov/~dhbailey/mpdist/index.html + *
+ * + * + * @author Martin Davis + * + */ + +#pragma once + +#include +#include + +namespace geos { +namespace math { // geos.math + +/** + * \class DD + * + * \brief + * Wrapper for DoubleDouble higher precision mathematics + * operations. + */ +class GEOS_DLL DD { + private: + static constexpr double SPLIT = 134217729.0; // 2^27+1, for IEEE double + double hi; + double lo; + + int signum() const; + DD rint() const; + + + public: + DD(double p_hi, double p_lo) : hi(p_hi), lo(p_lo) {}; + DD(double x) : hi(x), lo(0.0) {}; + DD() : hi(0.0), lo(0.0) {}; + + bool operator==(const DD &rhs) const + { + return hi == rhs.hi && lo == rhs.lo; + } + + bool operator!=(const DD &rhs) const + { + return hi != rhs.hi || lo != rhs.lo; + } + + bool operator<(const DD &rhs) const + { + return (hi < rhs.hi) || (hi == rhs.hi && lo < rhs.lo); + } + + bool operator<=(const DD &rhs) const + { + return (hi < rhs.hi) || (hi == rhs.hi && lo <= rhs.lo); + } + + bool operator>(const DD &rhs) const + { + return (hi > rhs.hi) || (hi == rhs.hi && lo > rhs.lo); + } + + bool operator>=(const DD &rhs) const + { + return (hi > rhs.hi) || (hi == rhs.hi && lo >= rhs.lo); + } + + friend GEOS_DLL DD operator+ (const DD &lhs, const DD &rhs); + friend GEOS_DLL DD operator+ (const DD &lhs, double rhs); + friend GEOS_DLL DD operator- (const DD &lhs, const DD &rhs); + friend GEOS_DLL DD operator- (const DD &lhs, double rhs); + friend GEOS_DLL DD operator* (const DD &lhs, const DD &rhs); + friend GEOS_DLL DD operator* (const DD &lhs, double rhs); + friend GEOS_DLL DD operator/ (const DD &lhs, const DD &rhs); + friend GEOS_DLL DD operator/ (const DD &lhs, double rhs); + + static DD determinant(const DD &x1, const DD &y1, const DD &x2, const DD &y2); + static DD determinant(double x1, double y1, double x2, double y2); + static DD abs(const DD &d); + static DD pow(const DD &d, int exp); + static DD trunc(const DD &d); + + bool isNaN() const; + bool isNegative() const; + bool isPositive() const; + bool isZero() const; + double doubleValue() const; + double ToDouble() const { return doubleValue(); } + int intValue() const; + DD negate() const; + DD reciprocal() const; + DD floor() const; + DD ceil() const; + + void selfAdd(const DD &d); + void selfAdd(double p_hi, double p_lo); + void selfAdd(double y); + + void selfSubtract(const DD &d); + void selfSubtract(double p_hi, double p_lo); + void selfSubtract(double y); + + void selfMultiply(double p_hi, double p_lo); + void selfMultiply(const DD &d); + void selfMultiply(double y); + + void selfDivide(double p_hi, double p_lo); + void selfDivide(const DD &d); + void selfDivide(double y); +}; + + +} // namespace geos::math +} // namespace geos + diff --git a/Sources/geos/include/geos/namespaces.h b/Sources/geos/include/geos/namespaces.h new file mode 100644 index 0000000..61132b8 --- /dev/null +++ b/Sources/geos/include/geos/namespaces.h @@ -0,0 +1,317 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2019 Nicklas Larsson + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +namespace geos { + +/// \brief +/// Contains classes and interfaces implementing fundamental computational +/// geometry algorithms. +/// +/// ### Robustness +/// +/// Geometrical algorithms involve a combination of combinatorial and numerical +/// computation. As with all numerical computation using finite-precision +/// numbers, the algorithms chosen are susceptible to problems of robustness. +/// A robustness problem occurs when a numerical calculation produces an +/// incorrect answer for some inputs due to round-off errors. Robustness +/// problems are especially serious in geometric computation, since they can +/// result in errors during topology building. +/// +/// There are many approaches to dealing with the problem of robustness in +/// geometrical computation. Not surprisingly, most robust algorithms are +/// substantially more complex and less performant than the non-robust +/// versions. Fortunately, JTS is sensitive to robustness problems in only a +/// few key functions (such as line intersection and the point-in-polygon +/// test). There are efficient robust algorithms available for these +/// functions, and these algorithms are implemented in JTS. +/// +/// ### Computational Performance +/// +/// Runtime performance is an important consideration for a production-quality +/// implementation of geometric algorithms. The most computationally intensive +/// algorithm used in JTS is intersection detection. JTS methods need to +/// determine both all intersection between the line segments in a single +/// Geometry (self-intersection) and all intersections between the line +/// segments of two different Geometries. +/// +/// The obvious naive algorithm for intersection detection (comparing every +/// segment with every other) has unacceptably slow performance. There is a +/// large literature of faster algorithms for intersection detection. +/// Unfortunately, many of them involve substantial code complexity. JTS tries +/// to balance code simplicity with performance gains. It uses some simple +/// techniques to produce substantial performance gains for common types of +/// input data. +/// +/// ### Package Specification +/// +/// - Java Topology Suite Technical Specifications +/// - +/// OpenGIS Simple Features Specification for SQL + namespace algorithm { // geos::algorithm + + /// Classes to compute distance metrics between geometries. + namespace distance {} + + /// Classes which determine the Location of points in geometries. + namespace locate {} +} // namespace geos::algorithm + +namespace geom { // geos.geom + + /// \brief + /// Contains classes and interfaces implementing algorithms that optimize + /// the performance of repeated calls to specific geometric operations. + namespace prep {} + + /// \brief + /// Provides classes that parse and modify Geometry objects. + namespace util {} +} // namespace geos.geom + +/// \brief +/// Contains classes that implement topology graphs. +/// +/// The Java Topology Suite (JTS) is a Java API that implements a core set of +/// spatial data operations using an explicit precision model and robust +/// geometric algorithms. JTS is int ended to be used in the development of +/// applications that support the validation, cleaning, integration and +/// querying of spatial datasets. +/// +/// JTS attempts to implement the OpenGIS Simple Features Specification (SFS) +/// as accurately as possible. In some cases the SFS is unclear or omits a +/// specification; in this case JTS attempts to choose a reasonable and +/// consistent alternative. Differences from and elaborations of the SFS are +/// documented in this specification. +/// +/// ### Package Specification +/// +/// - Java Topology Suite Technical Specifications +/// - +/// OpenGIS Simple Features Specification for SQL +namespace geomgraph { // geos.geomgraph + + /// \brief + /// Contains classes that implement indexes for performing noding on + /// geometry graph edges. + namespace index {} +} // namespace geos.geomgraph + + +/// Provides classes for various kinds of spatial indexes. +namespace index { // geos.index + + /// Contains classes that implement a Binary Interval Tree index + namespace bintree {} + + /// Contains classes that implement Monotone Chains + namespace chain {} + + /// \brief Contains classes that implement a static index on a set of + /// 1-dimensional intervals, using an R-Tree packed based on the order of + /// the interval midpoints. + namespace intervalrtree {} + + /// Contains classes that implement a Quadtree spatial index + namespace quadtree {} + + /// \brief Contains 2-D and 1-D versions of the Sort-Tile-Recursive (STR) + /// tree, a query-only R-tree. + namespace strtree {} + + /// \brief Contains classes which implement a sweepline algorithm for + /// scanning geometric data structures. + namespace sweepline {} +} // namespace geos.index + +/// \brief +/// Contains the interfaces for converting JTS objects to and from other +/// formats. +/// +/// The Java Topology Suite (JTS) is a Java API that implements a core set of +/// spatial data operations using an explicit precision model and robust +/// geometric algorithms. JTS is intended to be used in the devel opment of +/// applications that support the validation, cleaning, integration and +/// querying of spatial data sets. +/// +/// JTS attempts to implement the OpenGIS Simple Features Specification (SFS) +/// as accurately as possible. In some cases the SFS is unclear or omits a +/// specification; in this case JTS attempts to choose a reasonable and +/// consistent alternative. Differences from and elaborations of the SFS are +/// documented in this specification. +/// +/// ### Package Specification +/// +/// - Java Topology Suite Technical Specifications +/// - +/// OpenGIS Simple Features Specification for SQL +/// +namespace io {} + +/// \brief Contains classes and interfaces implementing linear referencing on +/// linear geometries. +namespace linearref {} + +/// \brief Classes to compute nodings for arrangements of line segments and +/// line segment sequences. +namespace noding { // geos.noding + + /// \brief Contains classes to implement the Snap Rounding algorithm for + /// noding linestrings. + namespace snapround {} +} // namespace geos.noding + +/// Provides classes for implementing operations on geometries. +namespace operation { // geos.operation + + /// Provides classes for computing buffers of geometries + namespace buffer {} + + /// Provides classes for computing the distance between geometries. + namespace distance {} + + /// \brief Provides classes for computing the intersection of a Geometry + /// and a clipping Rectangle. + namespace intersection {} + + /// Line merging package. + namespace linemerge {} + + /// \brief + /// Contains classes that perform a topological overlay to compute boolean + /// spatial functions. + /// + /// The Overlay Algorithm is used in spatial analysis methods for computing + /// set-theoretic operations (boolean combinations) of input + /// [Geometrys](\ref geom::Geometry). + /// The algorithm for computing the overlay uses the intersection operations + /// supported by topology graphs. To compute an overlay it is necessary to + /// explicitly compute the resultant graph formed by the computed + /// intersections. + /// + /// The algorithm to compute a set-theoretic spatial analysis method has the + /// following steps: + /// + /// - Build topology graphs of the two input geometries. For each geometry + /// all self-intersection nodes are computed and added to the graph. + /// - Compute nodes for all intersections between edges and nodes of the + /// graphs. + /// - Compute the labeling for the computed nodes by merging the labels from + /// the input graphs. + /// - Compute new edges between the compute intersection nodes. + /// Label the edges appropriately. + /// - Build the resultant graph from the new nodes and edges. + /// - Compute the labeling for isolated components of the graph. Add the + /// isolated components to the resultant graph. + /// - Compute the result of the boolean combination by selecting the node + /// and edges with the appropriate labels. Polygonize areas and sew linear + /// geometries together. + /// + /// ### Package Specification + /// + /// - Java Topology Suite Technical Specifications + /// - + /// OpenGIS Simple Features Specification for SQL + namespace overlay {} + + /// An API for polygonizing sets of lines. + namespace polygonize {} + + /// \brief Classes which implement topological predicates optimized for + /// particular kinds of geometries. + namespace predicate {} + + /// \brief Contains classes to implement the computation of the spatial + /// relationships of `Geometry`s. + /// + /// The `relate` algorithm computes the `IntersectionMatrix` describing the + /// relationship of two `Geometry`s. The algorithm for computing `relate` + /// uses the intersection operations supported by topology graphs. Although + /// the `relate` result depends on the resultant graph formed by the + /// computed intersections, there is no need to explicitly compute the + /// entire graph. It is sufficient to compute the local structure of the + /// graph at each intersection node. + /// + /// The algorithm to compute `relate` has the following steps: + /// + /// - Build topology graphs of the two input geometries. For each geometry + /// all self-intersection nodes are computed and added to the graph. + /// - Compute nodes for all intersections between edges and nodes of the + /// graphs. + /// - Compute the labeling for the computed nodes by merging the labels + /// from the input graphs. + /// - Compute the labeling for isolated components of the graph (see below) + /// - Compute the `IntersectionMatrix` from the labels on the nodes and + /// edges. + /// + /// ### Labeling isolated components + /// + /// Isolated components are components (edges or nodes) of an input + /// `Geometry` which do not contain any intersections with the other input + /// `Geometry`. The topological relationship of these components to the + /// other input `Geometry` must be computed in order to determine the + /// complete labeling of the component. This can be done by testing whether + /// the component lies in the interior or exterior of the other `Geometry`. + /// If the other `Geometry` is 1-dimensional, the isolated component must + /// lie in the exterior (since otherwise it would have an intersection with + /// an edge of the `Geometry`). If the other `Geometry` is 2-dimensional, + /// a Point-In-Polygon test can be used to determine whether the isolated + /// component is in the interior or exterior. + /// + /// ### Package Specification + /// + /// - Java Topology Suite Technical Specifications + /// - + /// OpenGIS Simple Features Specification for SQL + namespace relate {} + + /// Find shared paths among two linear Geometry objects. + namespace sharedpaths {} + + /// Classes to perform efficient unioning of collections of geometries. + namespace geounion {} + + /// Provides classes for testing the validity of geometries. + namespace valid {} +} // namespace geos.operation + +/// Contains classes to implement a planar graph data structure. +namespace planargraph { // geos::planargraph + + /// Planargraph algorithms. + namespace algorithm {} +} // namespace geos::planargraph + +/// Provides classes for manipulating the precision model of Geometries. +namespace precision {} + +/// Classes which implement algorithms for simplifying or generalizing geometries. +namespace simplify {} + +/// Classes to compute Delaunay triangulations. +namespace triangulate { // geos.triangulate + + /// \brief Classes to implement a topological subdivision of quadeges, to + /// support creating triangulations and Voronoi diagrams. + namespace quadedge {} +} // namespace geos.triangulate + +/// Utility classes for GEOS. +namespace util {} + +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/BasicSegmentString.h b/Sources/geos/include/geos/noding/BasicSegmentString.h new file mode 100644 index 0000000..1d253d6 --- /dev/null +++ b/Sources/geos/include/geos/noding/BasicSegmentString.h @@ -0,0 +1,89 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/BasicSegmentString.java rev. 1.1 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inheritance +#include // for inlines (size()) + + +#include + +// Forward declarations + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Represents a list of contiguous line segments, and supports noding the segments. + * + * The line segments are represented by an array of [Coordinates](@ref geom::Coordinate). + * Intended to optimize the noding of contiguous segments by reducing the number + * of allocated objects. SegmentStrings can carry a context object, which is + * useful for preserving topological or parentage information. + * All noded substrings are initialized with the same context object. + */ +class GEOS_DLL BasicSegmentString : public SegmentString { + +public: + + /// \brief Construct a BasicSegmentString. + /// + /// @param newPts CoordinateSequence representing the string, externally owned + /// @param newContext the context associated to this SegmentString + /// + BasicSegmentString(geom::CoordinateSequence* newPts, + const void* newContext) + : + SegmentString(newContext, newPts) + {} + + ~BasicSegmentString() override + {} + + // see dox in SegmentString.h + std::ostream& print(std::ostream& os) const override; + + /** \brief + * Gets the octant of the segment starting at vertex index. + * + * @param index the index of the vertex starting the segment. + * Must not be the last index in the vertex list + * @return the octant of the segment at the vertex + */ + int getSegmentOctant(std::size_t index) const + { + if(index >= size() - 1) { + return -1; + } + return Octant::octant(getCoordinate(index), getCoordinate(index + 1)); + }; + +private: + + // Declare type as noncopyable + BasicSegmentString(const BasicSegmentString& other) = delete; + BasicSegmentString& operator=(const BasicSegmentString& rhs) = delete; + +}; + +} // namespace geos.noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/BoundaryChainNoder.h b/Sources/geos/include/geos/noding/BoundaryChainNoder.h new file mode 100644 index 0000000..c0a4946 --- /dev/null +++ b/Sources/geos/include/geos/noding/BoundaryChainNoder.h @@ -0,0 +1,202 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) Martin Davis 2022 + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include // for composition +#include // for composition +#include // for composition +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class Coordinate; +} +namespace noding { +class NodedSegmentString; +} +} + +namespace geos { // geos +namespace noding { // geos::noding + +/** + * A noder which extracts chains of boundary segments + * as SegmentStrings from a polygonal coverage. + * Boundary segments are those which are not duplicated in the input polygonal coverage. + * Extracting chains of segments minimize the number of segment strings created, + * which produces a more efficient topological graph structure. + *

+ * This enables fast overlay of polygonal coverages in CoverageUnion. + * Using this noder is faster than SegmentExtractingNoder + * and BoundarySegmentNoder. + *

+ * No precision reduction is carried out. + * If that is required, another noder must be used (such as a snap-rounding noder), + * or the input must be precision-reduced beforehand. + * + * @author Martin Davis + * + */ +class GEOS_DLL BoundaryChainNoder : public Noder { + using Coordinate = geos::geom::Coordinate; + +private: + + class BoundaryChainMap { + + private: + + // Members + SegmentString* segString; + std::vector isBoundary; + + static SegmentString* createChain( + const SegmentString* segString, + std::size_t startIndex, + std::size_t endIndex, + bool constructZ, + bool constructM); + + std::size_t findChainStart(std::size_t index) const; + std::size_t findChainEnd(std::size_t index) const; + + public: + + BoundaryChainMap(SegmentString* ss) + : segString(ss) { + isBoundary.resize(ss->size()-1, false); + }; + + void setBoundarySegment(std::size_t index); + void createChains(std::vector& chains, bool constructZ, bool constructM); + }; + + class Segment { + public: + Segment(const geom::CoordinateSequence& seq, + BoundaryChainMap& segMap, + std::size_t index) + : m_seq(seq) + , m_segMap(segMap) + , m_index(index) + , m_flip(seq.getAt(index).compareTo(seq.getAt(index + 1)) < 0) + {} + + const geom::CoordinateXY& p0() const { + return m_seq.getAt(m_flip ? m_index : m_index + 1); + } + + const geom::CoordinateXY& p1() const { + return m_seq.getAt(m_flip ? m_index + 1 : m_index); + } + + void markBoundary() const { + m_segMap.setBoundarySegment(m_index); + }; + + bool operator==(const Segment& other) const { + return p0().equals2D(other.p0()) && p1().equals2D(other.p1()); + } + + struct HashCode { + std::size_t operator()(const Segment& s) const { + std::size_t h = std::hash{}(s.p0().x); + h ^= (std::hash{}(s.p0().y) << 1); + h ^= (std::hash{}(s.p1().x) << 1); + h ^= (std::hash{}(s.p1().y) << 1); + return h; + } + }; + + private: + const geom::CoordinateSequence& m_seq; + BoundaryChainMap& m_segMap; + std::size_t m_index; + bool m_flip; + }; + + +public: + + using SegmentSet = std::unordered_set; + + BoundaryChainNoder() + : m_chainList(nullptr) + , m_constructZ(false) + , m_constructM(false) + {}; + + // Noder virtual methods + std::vector* getNodedSubstrings() const override; + void computeNodes(std::vector* inputSegStrings) override; + + +private: + + // Members + std::vector* m_chainList; + std::vector> m_substrings; + bool m_constructZ; + bool m_constructM; + + // Methods + void addSegments(std::vector* segStrings, + SegmentSet& segSet, + std::vector& includedSegs); + + static void addSegments(SegmentString* segString, + BoundaryChainMap& segInclude, + SegmentSet& segSet); + + static bool segSetContains( + SegmentSet& segSet, Segment& seg); + + static void markBoundarySegments(SegmentSet& segSet); + + std::vector* extractChains(std::vector& sections) const; + + Coordinate::UnorderedSet findNodePts( + const std::vector* segStrings) const; + + std::vector* nodeChains( + const std::vector* chains, + const Coordinate::UnorderedSet& nodePts); + + void nodeChain( + SegmentString* chain, + const Coordinate::UnorderedSet& nodePts, + std::vector* nodedChains); + + std::size_t findNodeIndex( + const SegmentString* chain, + std::size_t start, + const Coordinate::UnorderedSet& nodePts) const; + + noding::BasicSegmentString* substring( + const SegmentString* segString, + std::size_t start, std::size_t end); + + // Declared as non-copyable + BoundaryChainNoder(const BoundaryChainNoder& other); + BoundaryChainNoder& operator=(const BoundaryChainNoder& rhs); + +}; + +} // namespace geos::noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/FastNodingValidator.h b/Sources/geos/include/geos/noding/FastNodingValidator.h new file mode 100644 index 0000000..6ceadc4 --- /dev/null +++ b/Sources/geos/include/geos/noding/FastNodingValidator.h @@ -0,0 +1,129 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/FastNodingValidator.java rev. ??? (JTS-1.8) + * + **********************************************************************/ + +#pragma once + +#include // for composition +#include // for composition + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Validates that a collection of {@link SegmentString}s is correctly noded. + * + * Indexing is used to improve performance. + * By default validation stops after a single + * non-noded intersection is detected. + * Alternatively, it can be requested to detect all intersections by + * using `setFindAllIntersections(boolean)`. + * + * The validator does not check for topology collapse situations + * (e.g. where two segment strings are fully co-incident). + * + * The validator checks for the following situations which indicated incorrect noding: + * + * - Proper intersections between segments (i.e. the intersection is interior to both segments) + * - Intersections at an interior vertex (i.e. with an endpoint or another interior vertex) + * + * The client may either test the {@link #isValid()} condition, + * or request that a suitable [TopologyException](@ref util::TopologyException) be thrown. + * + */ +class FastNodingValidator { + +public: + + FastNodingValidator(std::vector& newSegStrings) + : + li(), // robust... + segStrings(newSegStrings), + segInt(), + isValidVar(true) + { + } + + /** \brief + * Checks for an intersection and + * reports if one is found. + * + * @return true if the arrangement contains an interior intersection + */ + bool + isValid() + { + execute(); + return isValidVar; + } + + /** \brief + * Returns an error message indicating the segments containing + * the intersection. + * + * @return an error message documenting the intersection location + */ + std::string getErrorMessage() const; + + /** \brief + * Checks for an intersection and throws + * a TopologyException if one is found. + * + * @throws TopologyException if an intersection is found + */ + void checkValid(); + +private: + + geos::algorithm::LineIntersector li; + + std::vector& segStrings; + + std::unique_ptr segInt; + + bool isValidVar; + + void + execute() + { + if(segInt.get() != nullptr) { + return; + } + checkInteriorIntersections(); + } + + void checkInteriorIntersections(); + + // Declare type as noncopyable + FastNodingValidator(const FastNodingValidator& other) = delete; + FastNodingValidator& operator=(const FastNodingValidator& rhs) = delete; +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/FastSegmentSetIntersectionFinder.h b/Sources/geos/include/geos/noding/FastSegmentSetIntersectionFinder.h new file mode 100644 index 0000000..c77ad86 --- /dev/null +++ b/Sources/geos/include/geos/noding/FastSegmentSetIntersectionFinder.h @@ -0,0 +1,80 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: noding/FastSegmentSetIntersectionFinder.java r388 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +//forward declarations +namespace geos { +namespace noding { +class SegmentIntersectionDetector; +class SegmentSetMutualIntersector; +//class MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector; +} +} + + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Finds if two sets of [SegmentStrings](@ref noding::SegmentString) intersect. + * + * Uses indexing for fast performance and to optimize repeated tests + * against a target set of lines. + * Short-circuited to return as soon an intersection is found. + * + * @version 1.7 + */ +class FastSegmentSetIntersectionFinder { +private: + std::unique_ptr segSetMutInt; + std::unique_ptr lineIntersector; + +protected: +public: + FastSegmentSetIntersectionFinder(SegmentString::ConstVect* baseSegStrings); + + ~FastSegmentSetIntersectionFinder() = default; + + /** + * Gets the segment set intersector used by this class. + * This allows other uses of the same underlying indexed structure. + * + * @return the segment set intersector used + */ + const SegmentSetMutualIntersector* + getSegmentSetIntersector() const + { + return segSetMutInt.get(); + } + + bool intersects(SegmentString::ConstVect* segStrings); + bool intersects(SegmentString::ConstVect* segStrings, SegmentIntersectionDetector* intDetector); + +}; + +} // geos::noding +} // geos + diff --git a/Sources/geos/include/geos/noding/GeometryNoder.h b/Sources/geos/include/geos/noding/GeometryNoder.h new file mode 100644 index 0000000..437307f --- /dev/null +++ b/Sources/geos/include/geos/noding/GeometryNoder.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * NOTE: this is not in JTS. JTS has a snapround/GeometryNoder though + * + **********************************************************************/ + +#pragma once + +#include +#include // for NonConstVect + +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +namespace noding { +class Noder; +} +} + +namespace geos { +namespace noding { // geos.noding + +class GEOS_DLL GeometryNoder { +public: + + static std::unique_ptr node(const geom::Geometry& geom); + + GeometryNoder(const geom::Geometry& g); + + std::unique_ptr getNoded(); + +private: + + const geom::Geometry& argGeom; + + SegmentString::NonConstVect lineList; + + static void extractSegmentStrings(const geom::Geometry& g, + SegmentString::NonConstVect& to); + + Noder& getNoder(); + + std::unique_ptr noder; + + std::unique_ptr toGeometry(SegmentString::NonConstVect& noded); + + GeometryNoder(GeometryNoder const&); /*= delete*/ + GeometryNoder& operator=(GeometryNoder const&); /*= delete*/ +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/IntersectionAdder.h b/Sources/geos/include/geos/noding/IntersectionAdder.h new file mode 100644 index 0000000..14fbf00 --- /dev/null +++ b/Sources/geos/include/geos/noding/IntersectionAdder.h @@ -0,0 +1,202 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/IntersectionAdder.java rev. 1.6 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include // for abs() + +#include +#include // for inheritance + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +} +namespace algorithm { +class LineIntersector; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Computes the intersections between two line segments in SegmentString + * and adds them to each string. + * + * The SegmentIntersector is passed to a Noder. + * The NodedSegmentString::addIntersections(algorithm::LineIntersector* li, std::size_t segmentIndex, std::size_t geomIndex) + * method is called whenever the Noder + * detects that two SegmentStrings *might* intersect. + * This class is an example of the *Strategy* pattern. + * + */ +class GEOS_DLL IntersectionAdder: public SegmentIntersector { + +private: + + /** + * These variables keep track of what types of intersections were + * found during ALL edges that have been intersected. + */ + bool hasIntersectionVar; + bool hasProper; + bool hasProperInterior; + bool hasInterior; + + // the proper intersection point found + geom::CoordinateXYZM properIntersectionPoint; + + algorithm::LineIntersector& li; + // bool isSelfIntersection; + // bool intersectionFound; + + /** + * A trivial intersection is an apparent self-intersection which + * in fact is simply the point shared by adjacent line segments. + * Note that closed edges require a special check for the point + * shared by the beginning and end segments. + */ + bool isTrivialIntersection(const SegmentString* e0, std::size_t segIndex0, + const SegmentString* e1, std::size_t segIndex1); + + // Declare type as noncopyable + IntersectionAdder(const IntersectionAdder& other) = delete; + IntersectionAdder& operator=(const IntersectionAdder& rhs) = delete; + +public: + + int numIntersections; + int numInteriorIntersections; + int numProperIntersections; + + // testing only + int numTests; + + IntersectionAdder(algorithm::LineIntersector& newLi) + : + hasIntersectionVar(false), + hasProper(false), + hasProperInterior(false), + hasInterior(false), + properIntersectionPoint(), + li(newLi), + numIntersections(0), + numInteriorIntersections(0), + numProperIntersections(0), + numTests(0) + {} + + algorithm::LineIntersector& + getLineIntersector() + { + return li; + } + + /** + * @return the proper intersection point, or `Coordinate::getNull()` + * if none was found + */ + const geom::CoordinateXYZM& + getProperIntersectionPoint() + { + return properIntersectionPoint; + } + + bool + hasIntersection() + { + return hasIntersectionVar; + } + + /** \brief + * A proper intersection is an intersection which is interior to + * at least two line segments. + * + * Note that a proper intersection is not necessarily in the interior + * of the entire Geometry, since another edge may have an endpoint equal + * to the intersection, which according to SFS semantics can result in + * the point being on the Boundary of the Geometry. + */ + bool + hasProperIntersection() + { + return hasProper; + } + + /** \brief + * A proper interior intersection is a proper intersection which is + * *not* contained in the set of boundary nodes set for this SegmentIntersector. + */ + bool + hasProperInteriorIntersection() + { + return hasProperInterior; + } + + /** \brief + * An interior intersection is an intersection which is + * in the interior of some segment. + */ + bool + hasInteriorIntersection() + { + return hasInterior; + } + + + /** \brief + * This method is called by clients of the SegmentIntersector class to + * process intersections for two segments of the SegmentStrings being intersected. + * + * Note that some clients (such as MonotoneChains) may optimize away + * this call for segment pairs which they have determined do not + * intersect (e.g. by an disjoint envelope test). + */ + void processIntersections( + SegmentString* e0, std::size_t segIndex0, + SegmentString* e1, std::size_t segIndex1) override; + + + static bool + isAdjacentSegments(std::size_t i1, std::size_t i2) + { + return (i1 > i2 ? i1 - i2 : i2 - i1) == 1; + } + + /** \brief + * Always process all intersections. + * + * @return false always + */ + bool + isDone() const override + { + return false; + } +}; + + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/IntersectionFinderAdder.h b/Sources/geos/include/geos/noding/IntersectionFinderAdder.h new file mode 100644 index 0000000..c684dee --- /dev/null +++ b/Sources/geos/include/geos/noding/IntersectionFinderAdder.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/IntersectionFinderAdder.java rev. 1.5 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include // for use in vector +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace noding { +class SegmentString; +} +namespace algorithm { +class LineIntersector; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Finds proper and interior intersections in a set of SegmentStrings, + * and adds them as nodes. + * + */ +class GEOS_DLL IntersectionFinderAdder: public SegmentIntersector { + +public: + + /** \brief + * Creates an intersection finder which finds all proper intersections + * and stores them in the provided Coordinate array + * + * @param newLi the LineIntersector to use + * @param v the Vector to push interior intersections to + */ + IntersectionFinderAdder(algorithm::LineIntersector& newLi, + std::vector& v) + : + li(newLi), + interiorIntersections(v) + {} + + /** \brief + * This method is called by clients of the SegmentIntersector + * class to process intersections for two segments of the + * {@link SegmentString}s being intersected. + * + * Note that some clients (such as `MonotoneChains`) may + * optimize away this call for segment pairs which they have + * determined do not intersect + * (e.g. by an disjoint envelope test). + */ + void processIntersections( + SegmentString* e0, std::size_t segIndex0, + SegmentString* e1, std::size_t segIndex1) override; + + std::vector& + getInteriorIntersections() + { + return interiorIntersections; + } + + /** + * Always process all intersections + * + * @return false always + */ + bool + isDone() const override + { + return false; + } + +private: + algorithm::LineIntersector& li; + std::vector& interiorIntersections; + + // Declare type as noncopyable + IntersectionFinderAdder(const IntersectionFinderAdder& other) = delete; + IntersectionFinderAdder& operator=(const IntersectionFinderAdder& rhs) = delete; +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/IteratedNoder.h b/Sources/geos/include/geos/noding/IteratedNoder.h new file mode 100644 index 0000000..3e82c8a --- /dev/null +++ b/Sources/geos/include/geos/noding/IteratedNoder.h @@ -0,0 +1,120 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/IteratedNoder.java r591 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +#include // due to inlines +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +} +} + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Nodes a set of SegmentStrings completely. + * + * The set of segmentStrings is fully noded; + * i.e. noding is repeated until no further + * intersections are detected. + * + * Iterated noding using a FLOATING precision model is not guaranteed to converge, + * due to roundoff error. This problem is detected and an exception is thrown. + * Clients can choose to rerun the noding using a lower precision model. + * + */ +class GEOS_DLL IteratedNoder : public Noder { // implements Noder + +private: + static const int MAX_ITER = 5; + + + const geom::PrecisionModel* pm; + algorithm::LineIntersector li; + std::vector* nodedSegStrings; + int maxIter; + + /** + * Node the input segment strings once + * and create the split edges between the nodes + */ + void node(std::vector* segStrings, + int& numInteriorIntersections, + geom::CoordinateXY& intersectionPoint); + +public: + + IteratedNoder(const geom::PrecisionModel* newPm) + : + pm(newPm), + li(pm), + maxIter(MAX_ITER) + { + } + + ~IteratedNoder() override {} + + /** \brief + * Sets the maximum number of noding iterations performed before + * the noding is aborted. + * + * Experience suggests that this should rarely need to be changed + * from the default. + * The default is MAX_ITER. + * + * @param n the maximum number of iterations to perform + */ + void + setMaximumIterations(int n) + { + maxIter = n; + } + + std::vector* + getNodedSubstrings() const override + { + return nodedSegStrings; + } + + + /** \brief + * Fully nodes a list of {@link SegmentString}s, i.e. performs noding iteratively + * until no intersections are found between segments. + * + * Maintains labelling of edges correctly through the noding. + * + * @param inputSegmentStrings a collection of SegmentStrings to be noded + * @throws TopologyException if the iterated noding fails to converge. + */ + void computeNodes(std::vector* inputSegmentStrings) override; // throw(GEOSException); +}; + +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/MCIndexNoder.h b/Sources/geos/include/geos/noding/MCIndexNoder.h new file mode 100644 index 0000000..cf810ab --- /dev/null +++ b/Sources/geos/include/geos/noding/MCIndexNoder.h @@ -0,0 +1,141 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/MCIndexNoder.java rev. 1.6 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance +#include +#include // for composition +#include +#include +#include // for inheritance +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineSegment; +class Envelope; +} +namespace noding { +class SegmentString; +class SegmentIntersector; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Nodes a set of SegmentString using a index based + * on [MonotoneChain](@ref index::chain::MonotoneChain) + * and a [SpatialIndex](@ref index::SpatialIndex). + * + * The [SpatialIndex](@ref index::SpatialIndex) used should be something that supports + * envelope (range) queries efficiently (such as a [Quadtree](@ref index::quadtree::Quadtree) + * or [STRtree](@ref index::strtree::STRtree)). + * + * Last port: noding/MCIndexNoder.java rev. 1.4 (JTS-1.7) + */ +class GEOS_DLL MCIndexNoder : public SinglePassNoder { + +private: + std::vector monoChains; + index::strtree::TemplateSTRtree index; + std::vector* nodedSegStrings; + // statistics + int nOverlaps; + double overlapTolerance; + bool indexBuilt; + + void intersectChains(); + + void add(SegmentString* segStr); + +public: + + MCIndexNoder(SegmentIntersector* nSegInt = nullptr, double p_overlapTolerance = 0.0) + : SinglePassNoder(nSegInt) + , nodedSegStrings(nullptr) + , nOverlaps(0) + , overlapTolerance(p_overlapTolerance) + , indexBuilt(false) + {} + + ~MCIndexNoder() override {}; + + + /// \brief Return a reference to this instance's std::vector of MonotoneChains + std::vector& + getMonotoneChains() + { + return monoChains; + } + + index::SpatialIndex& getIndex() + { + return index; + } + + std::vector* getNodedSubstrings() const override + { + assert(nodedSegStrings); // must have called computeNodes before! + return NodedSegmentString::getNodedSubstrings(*nodedSegStrings); + } + + void computeNodes(std::vector* inputSegmentStrings) override; + + class SegmentOverlapAction : public index::chain::MonotoneChainOverlapAction { + public: + SegmentOverlapAction(SegmentIntersector& newSi) + : + index::chain::MonotoneChainOverlapAction(), + si(newSi) + {} + + void overlap(const index::chain::MonotoneChain& mc1, std::size_t start1, + const index::chain::MonotoneChain& mc2, std::size_t start2) override; + private: + SegmentIntersector& si; + + // Declare type as noncopyable + SegmentOverlapAction(const SegmentOverlapAction& other) = delete; + SegmentOverlapAction& operator=(const SegmentOverlapAction& rhs) = delete; + }; + +}; + +} // namespace geos.noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + + diff --git a/Sources/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h b/Sources/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h new file mode 100644 index 0000000..770784b --- /dev/null +++ b/Sources/geos/include/geos/noding/MCIndexSegmentSetMutualIntersector.h @@ -0,0 +1,146 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include // inherited +#include // inherited +#include // inherited +#include // inherited + +namespace geos { +namespace geom { + class Envelope; +} +namespace index { + class SpatialIndex; +} +namespace noding { + class SegmentString; + class SegmentIntersector; +} +} + +//using namespace geos::index::strtree; + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Intersects two sets of [SegmentStrings](@ref SegmentString) using a index based + * on [MonotoneChains](@ref index::chain::MonotoneChain) and a + * [SpatialIndex](@ref index::SpatialIndex). + * + * @version 1.7 + */ +class GEOS_DLL MCIndexSegmentSetMutualIntersector : public SegmentSetMutualIntersector { +public: + + MCIndexSegmentSetMutualIntersector(double p_tolerance) + : monoChains() + , indexCounter(0) + , processCounter(0) + , nOverlaps(0) + , overlapTolerance(p_tolerance) + , indexBuilt(false) + , envelope(nullptr) + {} + + MCIndexSegmentSetMutualIntersector(const geom::Envelope* p_envelope) + : monoChains() + , indexCounter(0) + , processCounter(0) + , nOverlaps(0) + , overlapTolerance(0.0) + , indexBuilt(false) + , envelope(p_envelope) + {} + + MCIndexSegmentSetMutualIntersector() + : MCIndexSegmentSetMutualIntersector(0.0) + {} + + ~MCIndexSegmentSetMutualIntersector() override + {}; + + index::SpatialIndex* + getIndex() + { + return &index; + } + + void setBaseSegments(SegmentString::ConstVect* segStrings) override; + + // NOTE: re-populates the MonotoneChain vector with newly created chains + void process(SegmentString::ConstVect* segStrings) override; + + class SegmentOverlapAction : public index::chain::MonotoneChainOverlapAction { + private: + SegmentIntersector& si; + + // Declare type as noncopyable + SegmentOverlapAction(const SegmentOverlapAction& other) = delete; + SegmentOverlapAction& operator=(const SegmentOverlapAction& rhs) = delete; + + public: + SegmentOverlapAction(SegmentIntersector& p_si) : + index::chain::MonotoneChainOverlapAction(), si(p_si) + {} + + void overlap(const index::chain::MonotoneChain& mc1, std::size_t start1, + const index::chain::MonotoneChain& mc2, std::size_t start2) override; + }; + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + MCIndexSegmentSetMutualIntersector(const MCIndexSegmentSetMutualIntersector&) = delete; + MCIndexSegmentSetMutualIntersector& operator=(const MCIndexSegmentSetMutualIntersector&) = delete; + +private: + + typedef std::vector MonoChains; + MonoChains monoChains; + + /* + * The index::SpatialIndex used should be something that supports + * envelope (range) queries efficiently (such as a index::quadtree::Quadtree + * or index::strtree::STRtree). + */ + index::strtree::TemplateSTRtree index; + int indexCounter; + int processCounter; + // statistics + int nOverlaps; + double overlapTolerance; + + /* memory management helper, holds MonotoneChain objects used + * in the SpatialIndex. It's cleared when the SpatialIndex is + */ + bool indexBuilt; + MonoChains indexChains; + const geom::Envelope* envelope; + + void addToIndex(SegmentString* segStr); + + void intersectChains(); + + void addToMonoChains(SegmentString* segStr); + +}; + +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/NodableSegmentString.h b/Sources/geos/include/geos/noding/NodableSegmentString.h new file mode 100644 index 0000000..8b4e0e1 --- /dev/null +++ b/Sources/geos/include/geos/noding/NodableSegmentString.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * An interface for classes which support adding nodes to + * a segment string. + * + * @author Martin Davis + */ +class GEOS_DLL NodableSegmentString : public SegmentString { +private: +protected: +public: + NodableSegmentString(const void* newContext, geom::CoordinateSequence* newSeq) + : + SegmentString(newContext, newSeq) + { } + + /** + * Adds an intersection node for a given point and segment to this segment string. + * + * @param intPt the location of the intersection + * @param segmentIndex the index of the segment containing the intersection + */ + //virtual void addIntersection( const geom::Coordinate * intPt, int segmentIndex) =0; +}; + +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/NodedSegmentString.h b/Sources/geos/include/geos/noding/NodedSegmentString.h new file mode 100644 index 0000000..18f6f18 --- /dev/null +++ b/Sources/geos/include/geos/noding/NodedSegmentString.h @@ -0,0 +1,218 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: noding/NodedSegmentString.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for inlines +#include +#include // for inheritance +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251 4355) // warning C4355: 'this' : used in base member initializer list +#endif + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Represents a list of contiguous line segments, + * and supports noding the segments. + * + * The line segments are represented by an array of [Coordinates](@ref geom::Coordinate). + * Intended to optimize the noding of contiguous segments by + * reducing the number of allocated objects. + * SegmentStrings can carry a context object, which is useful + * for preserving topological or parentage information. + * All noded substrings are initialized with the same context object. + * + */ +class GEOS_DLL NodedSegmentString : public NodableSegmentString { +public: + + // TODO: provide a templated method using an output iterator + template + static void + getNodedSubstrings(II from, II too_far, + SegmentString::NonConstVect* resultEdgelist) + { + for(II i = from; i != too_far; ++i) { + NodedSegmentString* nss = dynamic_cast(*i); + assert(nss); + nss->getNodeList().addSplitEdges(resultEdgelist); + } + } + + template + static void + getNodedSubstrings(C* segStrings, + SegmentString::NonConstVect* resultEdgelist) + { + getNodedSubstrings(segStrings->begin(), segStrings->end(), resultEdgelist); + } + + static void getNodedSubstrings(const SegmentString::NonConstVect& segStrings, + SegmentString::NonConstVect* resultEdgeList); + + /// Returns allocated object + static SegmentString::NonConstVect* getNodedSubstrings( + const SegmentString::NonConstVect& segStrings); + + std::unique_ptr getNodedCoordinates(); + + bool hasNodes() const + { + return nodeList.size() > 0; + } + + /** \brief + * Creates a new segment string from a list of vertices. + * + * @param newPts CoordinateSequence representing the string, + * ownership transferred. + * @param constructZ should newly-constructed coordinates store Z values? + * @param constructM should newly-constructed coordinates store M values? + * @param newContext the user-defined data of this segment string + * (may be null) + */ + NodedSegmentString(geom::CoordinateSequence* newPts, bool constructZ, bool constructM, const void* newContext) + : NodableSegmentString(newContext, newPts) + , nodeList(*this, constructZ, constructM) + {} + + NodedSegmentString(SegmentString* ss, bool constructZ, bool constructM) + : NodableSegmentString(ss->getData(), ss->getCoordinates()->clone().release()) + , nodeList(*this, constructZ, constructM) + {} + + ~NodedSegmentString() override { + delete seq; + } + + SegmentNodeList& getNodeList(); + + const SegmentNodeList& getNodeList() const; + + std::unique_ptr releaseCoordinates(); + + std::ostream& print(std::ostream& os) const override; + + + /** \brief + * Add {@link SegmentNode}s for one or both + * intersections found for a segment of an edge to the edge + * intersection list. + */ + void addIntersections(algorithm::LineIntersector* li, + std::size_t segmentIndex, std::size_t geomIndex) + { + for (std::size_t i = 0, n = li->getIntersectionNum(); i < n; ++i) { + addIntersection(li, segmentIndex, geomIndex, i); + } + }; + + /** \brief + * Add an SegmentNode for intersection intIndex. + * + * An intersection that falls exactly on a vertex + * of the SegmentString is normalized + * to use the higher of the two possible segmentIndexes + */ + void addIntersection(algorithm::LineIntersector* li, + std::size_t segmentIndex, + std::size_t geomIndex, std::size_t intIndex) + { + ::geos::ignore_unused_variable_warning(geomIndex); + + const auto& intPt = li->getIntersection(intIndex); + addIntersection(intPt, segmentIndex); + }; + + /** \brief + * Add an SegmentNode for intersection intIndex. + * + * An intersection that falls exactly on a vertex of the + * edge is normalized + * to use the higher of the two possible segmentIndexes + */ + template + void addIntersection(const CoordType& intPt, + std::size_t segmentIndex) + { + std::size_t normalizedSegmentIndex = segmentIndex; + + if (segmentIndex > size() - 2) { + throw util::IllegalArgumentException("SegmentString::addIntersection: SegmentIndex out of range"); + } + + // normalize the intersection point location + auto nextSegIndex = normalizedSegmentIndex + 1; + if (nextSegIndex < size()) { + const auto& nextPt = getCoordinate(nextSegIndex); + + // Normalize segment index if intPt falls on vertex + // The check for point equality is 2D only - + // Z values are ignored + if(intPt.equals2D(nextPt)) { + normalizedSegmentIndex = nextSegIndex; + } + } + + /* + * Add the intersection point to edge intersection list + * (unless the node is already known) + */ + nodeList.add(intPt, normalizedSegmentIndex); + } + +private: + + SegmentNodeList nodeList; + +}; + +} // namespace geos::noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + + + + + + + + + + diff --git a/Sources/geos/include/geos/noding/Noder.h b/Sources/geos/include/geos/noding/Noder.h new file mode 100644 index 0000000..768be8b --- /dev/null +++ b/Sources/geos/include/geos/noding/Noder.h @@ -0,0 +1,79 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + + +/** \brief + * Computes all intersections between segments in a set of SegmentString. + * + * Intersections found are represented as [SegmentNodes](@ref SegmentNode) and + * added to the [SegmentStrings](@ref SegmentString) in which they occur. + * As a final step in the noding a new set of segment strings split + * at the nodes may be returned. + * + * Last port: noding/Noder.java rev. 1.8 (JTS-1.7) + * + * TODO: this was really an interface, we should avoid making it a Base class + * + */ +class GEOS_DLL Noder { +public: + /** \brief + * Computes the noding for a collection of [SegmentStrings](@ref SegmentString). + * + * Some Noders may add all these nodes to the input SegmentStrings; + * others may only add some or none at all. + * + * @param segStrings a collection of {@link SegmentString}s to node + * The caller remains responsible for releasing the memory + * associated with the container and its elements. + */ + virtual void computeNodes(std::vector* segStrings) = 0; + + /** \brief + * Returns a collection of fully noded [SegmentStrings](@ref SegmentString). + * The SegmentStrings have the same context as their parent. + * + * @return a newly allocated std::vector of newly allocated + * SegmentStrings (copies of input, if needs be). + * Caller is responsible to delete container and elements. + */ + virtual std::vector* getNodedSubstrings() const = 0; + + virtual + ~Noder() {} + +protected: + Noder() {} +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/NodingIntersectionFinder.h b/Sources/geos/include/geos/noding/NodingIntersectionFinder.h new file mode 100644 index 0000000..b4232d7 --- /dev/null +++ b/Sources/geos/include/geos/noding/NodingIntersectionFinder.h @@ -0,0 +1,262 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include // for inheritance +#include // for composition +#include + +#include + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** + * \brief Finds non-noded intersections in a set of {@link SegmentString}s, if any exist. + * + * Non-noded intersections include: + * + * - intersections which lie in the interior of a segment + * - intersections at a vertex in the interior of a SegmentString + * (e.g. with a segment string endpoint or other interior vertex) + * + * @version 1.7 + */ +class NodingIntersectionFinder: public SegmentIntersector { + +public: + + /** \brief + * Creates an intersection finder which finds an interior intersection + * if one exists + * + * @param newLi the LineIntersector to use + */ + NodingIntersectionFinder(algorithm::LineIntersector& newLi) + : + li(newLi), + interiorIntersection(geom::Coordinate::getNull()), + intersectionCount(0), + isCheckEndSegmentsOnly(false), + findAllIntersections(false) + { + } + + /** \brief + * Tests whether an intersection was found. + * + * @return true if an intersection was found + */ + bool + hasIntersection() const + { + return !interiorIntersection.isNull(); + } + + /** \brief + * Gets the computed location of the intersection. + * Due to round-off, the location may not be exact. + * + * @return the coordinate for the intersection location + */ + const geom::Coordinate& + getInteriorIntersection() const + { + return interiorIntersection; + } + + /** \brief + * Gets the count of intersections found. + * + * @return the intersection count + */ + size_t + count() const + { + return intersectionCount; + } + + /** \brief + * Sets whether only end segments should be tested for interior intersection. + * + * This is a performance optimization that may be used if the segments + * have been previously noded by an appropriate algorithm. + * It may be known that any potential noding failures will occur only in + * end segments. + * + * @param isCESO whether to test only end segments + */ + void + setCheckEndSegmentsOnly(bool isCESO) + { + isCheckEndSegmentsOnly = isCESO; + } + + + /** \brief + * Sets whether all intersections should be computed. + * + * When this is `false` (the default value) the value of + * #isDone() is `true` after the first intersection is found. + * + * Default is `false`. + * + * @param fAI whether all intersections should be computed + */ + void + setFindAllIntersections(bool fAI) + { + findAllIntersections = fAI; + } + + /** \brief + * Gets the endpoints of the intersecting segments. + * + * @return an array of the segment endpoints (p00, p01, p10, p11) + */ + const std::vector& + getIntersectionSegments() const + { + return intSegments; + } + + /** \brief + * This method is called by clients of the SegmentIntersector class to process + * intersections for two segments of the [SegmentStrings](@ref SegmentString) + * being intersected. + * + * @note Some clients (such as `MonotoneChains`) may optimize away + * this call for segment pairs which they have determined do not intersect + * (e.g. by an disjoint envelope test). + */ + void processIntersections( + SegmentString* e0, std::size_t segIndex0, + SegmentString* e1, std::size_t segIndex1) override; + + bool + isDone() const override + { + return !interiorIntersection.isNull(); + } + +private: + algorithm::LineIntersector& li; + geom::Coordinate interiorIntersection; + std::size_t intersectionCount; + bool isCheckEndSegmentsOnly; + bool findAllIntersections; + std::vector intSegments; + + // Declare type as noncopyable + NodingIntersectionFinder(const NodingIntersectionFinder& other) = delete; + NodingIntersectionFinder& operator=(const NodingIntersectionFinder& rhs) = delete; + + /** \brief + * Tests if two vertices with at least one in a segmentString interior + * are equal. + * + * @param p0 a segment vertex + * @param p1 a segment vertex + * @param isEnd0 true if vertex is a segmentString endpoint + * @param isEnd1 true if vertex is a segmentString endpoint + * @return true if an intersection is found + */ + static bool isInteriorVertexIntersection( + const geom::Coordinate& p0, const geom::Coordinate& p1, + bool isEnd0, bool isEnd1) + { + // Intersections between endpoints are valid nodes, so not reported + if (isEnd0 && isEnd1) { + return false; + } + + if (p0.equals2D(p1)) { + return true; + } + + return false; + }; + + /** \brief + * Tests if an intersection occurs between a SegmentString interior vertex and another vertex. + * + * @note intersections between two endpoint vertices are valid noding, + * and are not flagged. + * + * @param p00 a segment vertex + * @param p01 a segment vertex + * @param p10 a segment vertex + * @param p11 a segment vertex + * @param isEnd00 true if vertex is a segmentString endpoint + * @param isEnd01 true if vertex is a segmentString endpoint + * @param isEnd10 true if vertex is a segmentString endpoint + * @param isEnd11 true if vertex is a segmentString endpoint + * @return true if an intersection is found + */ + static bool isInteriorVertexIntersection( + const geom::Coordinate& p00, const geom::Coordinate& p01, + const geom::Coordinate& p10, const geom::Coordinate& p11, + bool isEnd00, bool isEnd01, bool isEnd10, bool isEnd11) + { + if (isInteriorVertexIntersection(p00, p10, isEnd00, isEnd10)) { + return true; + } + if (isInteriorVertexIntersection(p00, p11, isEnd00, isEnd11)) { + return true; + } + if (isInteriorVertexIntersection(p01, p10, isEnd01, isEnd10)) { + return true; + } + if (isInteriorVertexIntersection(p01, p11, isEnd01, isEnd11)) { + return true; + } + return false; + }; + + /** \brief + * Tests whether a segment in a SegmentString is an end segment. + * (either the first or last). + * + * @param segStr a segment string + * @param index the index of a segment in the segment string + * @return true if the segment is an end segment + */ + static bool isEndSegment(const SegmentString* segStr, std::size_t index) + { + if (index == 0) { + return true; + } + if (index >= segStr->size() - 2) { + return true; + } + return false; + }; + +}; + +} // namespace geos.noding +} // namespace geos + + diff --git a/Sources/geos/include/geos/noding/NodingValidator.h b/Sources/geos/include/geos/noding/NodingValidator.h new file mode 100644 index 0000000..31bdd4e --- /dev/null +++ b/Sources/geos/include/geos/noding/NodingValidator.h @@ -0,0 +1,109 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +//#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Validates that a collection of {@link SegmentString}s is correctly noded. + * Throws a TopologyException if a noding error is found. + * + * Last port: noding/NodingValidator.java rev. 1.6 (JTS-1.7) + * + */ +class GEOS_DLL NodingValidator { +private: + algorithm::LineIntersector li; + const std::vector& segStrings; + + /** + * Checks if a segment string contains a segment + * pattern a-b-a (which implies a self-intersection) + */ + void checkCollapses() const; + + void checkCollapses(const SegmentString& ss) const; + + void checkCollapse(const geom::Coordinate& p0, const geom::Coordinate& p1, + const geom::Coordinate& p2) const; + + /** + * Checks all pairs of segments for intersections at an + * interior point of a segment + */ + void checkInteriorIntersections(); + + void checkInteriorIntersections(const SegmentString& ss0, + const SegmentString& ss1); + + void checkInteriorIntersections( + const SegmentString& e0, std::size_t segIndex0, + const SegmentString& e1, std::size_t segIndex1); + + /** + * Checks for intersections between an endpoint of a segment string + * and an interior vertex of another segment string + */ + void checkEndPtVertexIntersections() const; + + void checkEndPtVertexIntersections(const geom::Coordinate& testPt, + const std::vector& segStrings) const; + + /** + * @return true if there is an intersection point which is not an + * endpoint of the segment p0-p1 + */ + bool hasInteriorIntersection(const algorithm::LineIntersector& aLi, + const geom::Coordinate& p0, const geom::Coordinate& p1) const; + + // Declare type as noncopyable + NodingValidator(const NodingValidator& other) = delete; + NodingValidator& operator=(const NodingValidator& rhs) = delete; + +public: + + NodingValidator(const std::vector& newSegStrings): + segStrings(newSegStrings) + {} + + ~NodingValidator() {} + + void checkValid(); + +}; + + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/Octant.h b/Sources/geos/include/geos/noding/Octant.h new file mode 100644 index 0000000..7b96c0a --- /dev/null +++ b/Sources/geos/include/geos/noding/Octant.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Methods for computing and working with octants of the Cartesian plane. + * + * Octants are numbered as follows: + * + * 2|1 + * 3 | 0 + * ---+-- + * 4 | 7 + * 5|6 + * + * If line segments lie along a coordinate axis, the octant is the lower of the two + * possible values. + * + * Last port: noding/Octant.java rev. 1.2 (JTS-1.7) + */ +class GEOS_DLL Octant { +private: + Octant() {} // Can't instantiate it +public: + + /** + * Returns the octant of a directed line segment (specified + * as x and y displacements, which cannot both be 0). + */ + static int octant(double dx, double dy); + + /** + * Returns the octant of a directed line segment from p0 to p1. + */ + static int octant(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + + static int + octant(const geom::CoordinateXY* p0, const geom::CoordinateXY* p1) + { + ::geos::ignore_unused_variable_warning(p0); + return octant(*p0, *p1); + } +}; + + +} // namespace geos.noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/OrientedCoordinateArray.h b/Sources/geos/include/geos/noding/OrientedCoordinateArray.h new file mode 100644 index 0000000..eae9ec6 --- /dev/null +++ b/Sources/geos/include/geos/noding/OrientedCoordinateArray.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/OrientedCoordinateArray.java rev. 1.1 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace noding { +//class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Allows comparing {@link geom::CoordinateSequence}s + * in an orientation-independent way. + */ +class GEOS_DLL OrientedCoordinateArray { +public: + + /** + * Creates a new {@link OrientedCoordinateArray} + * for the given {@link geom::CoordinateSequence}. + * + * @param p_pts the coordinates to orient + */ + OrientedCoordinateArray(const geom::CoordinateSequence& p_pts) + : + pts(&p_pts), + orientationVar(orientation(p_pts)) + { + } + + /** \brief + * Compares two {@link OrientedCoordinateArray}s for their + * relative order + * + * @return -1 this one is smaller + * @return 0 the two objects are equal + * @return 1 this one is greater + * + * In JTS, this is used automatically by ordered lists. + * In C++, operator< would be used instead.... + */ + int compareTo(const OrientedCoordinateArray& o1) const; + + bool operator==(const OrientedCoordinateArray& other) const; + + struct GEOS_DLL HashCode { + std::size_t operator()(const OrientedCoordinateArray & oca) const; + }; + +private: + + static int compareOriented(const geom::CoordinateSequence& pts1, + bool orientation1, + const geom::CoordinateSequence& pts2, + bool orientation2); + + + /** + * Computes the canonical orientation for a coordinate array. + * + * @param pts the array to test + * @return true if the points are oriented forwards + * @return false if the points are oriented in reverse + */ + static bool orientation(const geom::CoordinateSequence& pts); + + /// Externally owned + const geom::CoordinateSequence* pts; + + bool orientationVar; + +}; + +/// Strict weak ordering operator for OrientedCoordinateArray +/// +/// This is the C++ equivalent of JTS's compareTo +inline bool +operator< (const OrientedCoordinateArray& oca1, + const OrientedCoordinateArray& oca2) +{ + return oca1.compareTo(oca2) < 0; +} + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/ScaledNoder.h b/Sources/geos/include/geos/noding/ScaledNoder.h new file mode 100644 index 0000000..6ca44c6 --- /dev/null +++ b/Sources/geos/include/geos/noding/ScaledNoder.h @@ -0,0 +1,132 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/ScaledNoder.java rev. 1.3 (JTS-1.7.1) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include // for inheritance +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +} +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Wraps a {@link Noder} and transforms its input + * into the integer domain. + * + * This is intended for use with Snap-Rounding noders, + * which typically are only intended to work in the integer domain. + * Offsets can be provided to increase the number of digits of + * available precision. + * + */ +class GEOS_DLL ScaledNoder : public Noder { // , public geom::CoordinateFilter { // implements Noder + +public: + + bool + isIntegerPrecision() + { + return (scaleFactor == 1.0); + } + + ScaledNoder(Noder& n, double nScaleFactor, + double nOffsetX = 0.0, double nOffsetY = 0.0) + : + noder(n), + scaleFactor(nScaleFactor), + offsetX(nOffsetX), + offsetY(nOffsetY), + isScaled(nScaleFactor != 1.0) + {} + + ~ScaledNoder() override; + + std::vector* getNodedSubstrings() const override; + + void computeNodes(std::vector* inputSegStr) override; + + //void filter(Coordinate& c); + + void + filter_ro(const geom::Coordinate* c) + { + ::geos::ignore_unused_variable_warning(c); + assert(0); + } + + void filter_rw(geom::Coordinate* c) const; + +private: + + Noder& noder; + + double scaleFactor; + + double offsetX; + + double offsetY; + + bool isScaled; + + void rescale(std::vector& segStrings) const; + + void scale(std::vector& segStrings) const; + + class Scaler; + + class ReScaler; + + friend class ScaledNoder::Scaler; + + friend class ScaledNoder::ReScaler; + + mutable std::vector newCoordSeq; + + // Declare type as noncopyable + ScaledNoder(const ScaledNoder& other) = delete; + ScaledNoder& operator=(const ScaledNoder& rhs) = delete; +}; + +} // namespace geos.noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/noding/SegmentExtractingNoder.h b/Sources/geos/include/geos/noding/SegmentExtractingNoder.h new file mode 100644 index 0000000..6a72771 --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentExtractingNoder.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class PrecisionModel; +} +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos::noding + +/** + * A noder which extracts all line segments + * as SegmentString. + * This enables fast overlay of geometries which are known to be already fully noded. + * In particular, it provides fast union of polygonal and linear coverages. + * Unioning a noded set of lines is an effective way + * to perform line merging and line dissolving. + * + * No precision reduction is carried out. + * If that is required, another noder must be used (such as a snap-rounding noder), + * or the input must be precision-reduced beforehand. + * + */ +class GEOS_DLL SegmentExtractingNoder : public Noder { + +private: + + std::vector* segList; + + void extractSegments( + const std::vector& inputSegs, + std::vector& outputSegs); + + void extractSegments( + const SegmentString* ss, + std::vector& outputSegs); + + +public: + + /** + * Creates a new segment-extracting noder. + */ + SegmentExtractingNoder() : segList(nullptr) + {}; + + void computeNodes(std::vector* segStrings) override; + + /** + * @return a Collection of SegmentString representing the + * substrings. Caller takes ownership over vector and contents. + */ + std::vector* getNodedSubstrings() const override; + + +}; + + +} // namespace geos::noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/SegmentIntersectionDetector.h b/Sources/geos/include/geos/noding/SegmentIntersectionDetector.h new file mode 100644 index 0000000..2165bec --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentIntersectionDetector.h @@ -0,0 +1,177 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Detects and records an intersection between two {@link SegmentString}s, + * if one exists. + * + * This strategy can be configured to search for proper intersections. + * In this case, the presence of any intersection will still be recorded, + * but searching will continue until either a proper intersection has been found + * or no intersections are detected. + * + * Only a single intersection is recorded. + * + * @version 1.7 + */ +class SegmentIntersectionDetector : public SegmentIntersector { +private: + algorithm::LineIntersector* li; + + bool findProper; + bool findAllTypes; + + bool _hasIntersection; + bool _hasProperIntersection; + bool _hasNonProperIntersection; + + const geom::Coordinate* intPt; + geom::CoordinateSequence* intSegments; + +protected: +public: + SegmentIntersectionDetector(algorithm::LineIntersector* p_li) + : + li(p_li), + findProper(false), + findAllTypes(false), + _hasIntersection(false), + _hasProperIntersection(false), + _hasNonProperIntersection(false), + intPt(nullptr), + intSegments(nullptr) + { } + + ~SegmentIntersectionDetector() override + { + //delete intPt; + delete intSegments; + } + + + void + setFindProper(bool p_findProper) + { + this->findProper = p_findProper; + } + + void + setFindAllIntersectionTypes(bool p_findAllTypes) + { + this->findAllTypes = p_findAllTypes; + } + + /** \brief + * Tests whether an intersection was found. + * + * @return true if an intersection was found + */ + bool + hasIntersection() const + { + return _hasIntersection; + } + + /** \brief + * Tests whether a proper intersection was found. + * + * @return `true` if a proper intersection was found + */ + bool + hasProperIntersection() const + { + return _hasProperIntersection; + } + + /** \brief + * Tests whether a non-proper intersection was found. + * + * @return true if a non-proper intersection was found + */ + bool + hasNonProperIntersection() const + { + return _hasNonProperIntersection; + } + + /** \brief + * Gets the computed location of the intersection. + * Due to round-off, the location may not be exact. + * + * @return the coordinate for the intersection location + */ + const geom::Coordinate* + getIntersection() const + { + return intPt; + } + + + /** \brief + * Gets the endpoints of the intersecting segments. + * + * @return an array of the segment endpoints (p00, p01, p10, p11) + */ + const geom::CoordinateSequence* + getIntersectionSegments() const + { + return intSegments; + } + + bool + isDone() const override + { + // If finding all types, we can stop + // when both possible types have been found. + if(findAllTypes) { + return _hasProperIntersection && _hasNonProperIntersection; + } + + // If searching for a proper intersection, only stop if one is found + if(findProper) { + return _hasProperIntersection; + } + + return _hasIntersection; + } + + /** \brief + * This method is called by clients of the SegmentIntersector class to process + * intersections for two segments of the {@link SegmentString}s being intersected. + * + * @note Some clients (such as `MonotoneChains`) may optimize away + * this call for segment pairs which they have determined do not intersect + * (e.g. by an disjoint envelope test). + */ + void processIntersections(noding::SegmentString* e0, std::size_t segIndex0, + noding::SegmentString* e1, std::size_t segIndex1) override; + +}; + +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/SegmentIntersector.h b/Sources/geos/include/geos/noding/SegmentIntersector.h new file mode 100644 index 0000000..28eb2c6 --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentIntersector.h @@ -0,0 +1,89 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** + * \brief + * Processes possible intersections detected by a Noder. + * + * The SegmentIntersector is passed to a Noder. + * The addIntersections method is called whenever the Noder + * detects that two SegmentStrings might intersect. + * This class may be used either to find all intersections, or + * to detect the presence of an intersection. In the latter case, + * Noders may choose to short-circuit their computation by calling the + * isDone method. + * This class is an example of the Strategy pattern. + * + * @version 1.7 + */ +class GEOS_DLL SegmentIntersector { + +public: + + /** + * This method is called by clients + * of the SegmentIntersector interface to process + * intersections for two segments of the SegmentStrings + * being intersected. + */ + virtual void processIntersections( + SegmentString* e0, std::size_t segIndex0, + SegmentString* e1, std::size_t segIndex1) = 0; + + /** + * \brief + * Reports whether the client of this class + * needs to continue testing all intersections in an arrangement. + * + * @return true if there is not need to continue testing segments + * + * The default implementation always return false (process all intersections). + */ + virtual bool + isDone() const + { + return false; + } + + virtual + ~SegmentIntersector() + { } + +protected: + + SegmentIntersector() {} + +}; + +/// Temporary typedef for namespace transition +typedef SegmentIntersector nodingSegmentIntersector; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/SegmentNode.h b/Sources/geos/include/geos/noding/SegmentNode.h new file mode 100644 index 0000000..6a2576e --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentNode.h @@ -0,0 +1,161 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/SegmentNode.java 4667170ea (JTS-1.17) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace geos { +namespace noding { // geos.noding + +/** + * \brief + * Represents an intersection point between two NodedSegmentString. + * + * Final class. + */ +class GEOS_DLL SegmentNode { +private: + int segmentOctant; + + bool isInteriorVar; + +public: + friend std::ostream& operator<< (std::ostream& os, const SegmentNode& n); + + /// the point of intersection (own copy) + geom::CoordinateXYZM coord; + + /// the index of the containing line segment in the parent edge + std::size_t segmentIndex; + + /// Construct a node on the given NodedSegmentString + /// + /// @param ss the parent NodedSegmentString + /// + /// @param nCoord the coordinate of the intersection, will be copied + /// + /// @param nSegmentIndex the index of the segment on parent + /// NodedSegmentString + /// where the Node is located. + /// + /// @param nSegmentOctant + /// + template + SegmentNode(const SegmentString& ss, + const CoordType& nCoord, + std::size_t nSegmentIndex, int nSegmentOctant) + : segmentOctant(nSegmentOctant) + , coord(nCoord) + , segmentIndex(nSegmentIndex) + { + // Number of points in NodedSegmentString is one-more number of segments + assert(segmentIndex < ss.size()); + isInteriorVar = !coord.equals2D(ss.getCoordinate(segmentIndex)); + } + + ~SegmentNode() {} + + /// \brief + /// Return true if this Node is *internal* (not on the boundary) + /// of the corresponding segment. Currently only the *first* + /// segment endpoint is checked, actually. + /// + bool + isInterior() const + { + return isInteriorVar; + } + + bool isEndPoint(unsigned int maxSegmentIndex) const + { + if(segmentIndex == 0 && ! isInteriorVar) { + return true; + } + if(segmentIndex == maxSegmentIndex) { + return true; + } + return false; + }; + + /** + * @return -1 this EdgeIntersection is located before + * the argument location + * @return 0 this EdgeIntersection is at the argument location + * @return 1 this EdgeIntersection is located after the + * argument location + */ + int compareTo(const SegmentNode& other) const + { + if (segmentIndex < other.segmentIndex) { + return -1; + } + if (segmentIndex > other.segmentIndex) { + return 1; + } + + if (coord.equals2D(other.coord)) { + + return 0; + } + + // an exterior node is the segment start point, + // so always sorts first + // this guards against a robustness problem + // where the octants are not reliable + if (!isInteriorVar) return -1; + if (!other.isInteriorVar) return 1; + + return SegmentPointComparator::compare( + segmentOctant, coord, + other.coord); + }; + +}; + +// std::ostream& operator<< (std::ostream& os, const SegmentNode& n); + +struct GEOS_DLL SegmentNodeLT { + bool + operator()(SegmentNode* s1, SegmentNode* s2) const + { + return s1->compareTo(*s2) < 0; + } + + bool + operator()(const SegmentNode& s1, const SegmentNode& s2) const + { + return s1.compareTo(s2) < 0; + } +}; + + +} // namespace geos.noding +} // namespace geos + + + + + + diff --git a/Sources/geos/include/geos/noding/SegmentNodeList.h b/Sources/geos/include/geos/noding/SegmentNodeList.h new file mode 100644 index 0000000..d199fde --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentNodeList.h @@ -0,0 +1,253 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/SegmentNodeList.java rev. 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +} +namespace noding { +class SegmentString; +class NodedSegmentString; +} +} + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * A list of the SegmentNode present along a + * NodedSegmentString. + */ +class GEOS_DLL SegmentNodeList { +private: + // Since we are adding frequently to the SegmentNodeList and + // iterating infrequently, it is faster to store all the + // SegmentNodes in a vector and sort/remove duplicates + // before iteration, rather than storing them in a set + // and continuously maintaining a sorted order. + mutable std::vector nodeMap; + mutable bool ready = false; + + bool constructZ; + bool constructM; + + void prepare() const; + + // the parent edge + const NodedSegmentString& edge; + + /** + * Checks the correctness of the set of split edges corresponding + * to this edge + * + * @param splitEdges the split edges for this edge (in order) + */ + void checkSplitEdgesCorrectness(const std::vector& splitEdges) const; + + /** + * Create a new "split edge" with the section of points between + * (and including) the two intersections. + * The label for the new edge is the same as the label for the + * parent edge. + * + * ownership of return value is transferred + */ + std::unique_ptr createSplitEdge(const SegmentNode* ei0, const SegmentNode* ei1) const; + + /** + * Extracts the points for a split edge running between two nodes. + * The extracted points should contain no duplicate points. + * There should always be at least two points extracted + * (which will be the given nodes). + * + * @param ei0 the start node of the split edge + * @param ei1 the end node of the split edge + * @return the points for the split edge + */ + std::unique_ptr createSplitEdgePts(const SegmentNode* ei0, const SegmentNode* ei1) const; + + /** + * Adds nodes for any collapsed edge pairs. + * Collapsed edge pairs can be caused by inserted nodes, or they + * can be pre-existing in the edge vertex list. + * In order to provide the correct fully noded semantics, + * the vertex at the base of a collapsed pair must also be added + * as a node. + */ + void addCollapsedNodes(); + + /** + / * Adds nodes for any collapsed edge pairs + * which are pre-existing in the vertex list. + */ + void findCollapsesFromExistingVertices( + std::vector& collapsedVertexIndexes) const; + + /** + * Adds nodes for any collapsed edge pairs caused by inserted nodes + * Collapsed edge pairs occur when the same coordinate is inserted + * as a node both before and after an existing edge vertex. + * To provide the correct fully noded semantics, + * the vertex must be added as a node as well. + */ + void findCollapsesFromInsertedNodes( + std::vector& collapsedVertexIndexes) const; + + static bool findCollapseIndex(const SegmentNode& ei0, const SegmentNode& ei1, + size_t& collapsedVertexIndex); + + void addEdgeCoordinates(const SegmentNode* ei0, const SegmentNode* ei1, geom::CoordinateSequence& coordList) const; + +public: + + // Declare type as noncopyable + SegmentNodeList(const SegmentNodeList& other) = delete; + SegmentNodeList& operator=(const SegmentNodeList& rhs) = delete; + + friend std::ostream& operator<< (std::ostream& os, const SegmentNodeList& l); + + using container = decltype(nodeMap); + using iterator = container::iterator; + using const_iterator = container::const_iterator; + + explicit SegmentNodeList(const NodedSegmentString& newEdge, + bool p_constructZ, + bool p_constructM) + : constructZ(p_constructZ) + , constructM(p_constructM) + , edge(newEdge) {} + + ~SegmentNodeList() = default; + + const NodedSegmentString& + getEdge() const + { + return edge; + } + + bool getConstructZ() const { + return constructZ; + } + + bool getConstructM() const { + return constructM; + } + + /** + * Adds an intersection into the list, if it isn't already there. + * The input segmentIndex is expected to be normalized. + * + * @param intPt the intersection Coordinate, will be copied + * @param segmentIndex + */ + template + void add(const CoordType& intPt, std::size_t segmentIndex) { + // Cast edge to SegmentString to avoid circular dependency between NodedSegmentString and SegmentNodeList + nodeMap.emplace_back(edge, intPt, segmentIndex, reinterpret_cast(edge).getSegmentOctant(segmentIndex)); + ready = false; + } + + /// Return the number of nodes in this list + size_t + size() const + { + prepare(); + return nodeMap.size(); + } + + iterator begin() { + prepare(); + return nodeMap.begin(); + } + + const_iterator begin() const { + prepare(); + return nodeMap.begin(); + } + + iterator end() { + prepare(); + return nodeMap.end(); + } + + const_iterator end() const { + prepare(); + return nodeMap.end(); + } + + /** + * Adds entries for the first and last points of the edge to the list + */ + void addEndpoints(); + + /** + * Creates new edges for all the edges that the intersections in this + * list split the parent edge into. + * Adds the edges to the input list (this is so a single list + * can be used to accumulate all split edges for a Geometry). + */ + void addSplitEdges(std::vector& edgeList); + + void + addSplitEdges(std::vector* edgeList) + { + assert(edgeList); + addSplitEdges(*edgeList); + } + + /** + * Gets the list of coordinates for the fully noded segment string, + * including all original segment string vertices and vertices + * introduced by nodes in this list. + * Repeated coordinates are collapsed. + * + * @return an array of Coordinates + * + */ + std::unique_ptr getSplitCoordinates(); + + +}; + +std::ostream& operator<< (std::ostream& os, const SegmentNodeList& l); + +} // namespace geos::noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/noding/SegmentPointComparator.h b/Sources/geos/include/geos/noding/SegmentPointComparator.h new file mode 100644 index 0000000..bd47392 --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentPointComparator.h @@ -0,0 +1,120 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/SegmentPointComparator.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * Implements a robust method of comparing the relative position of two + * points along the same segment. + * + * The coordinates are assumed to lie "near" the segment. + * This means that this algorithm will only return correct results + * if the input coordinates + * have the same precision and correspond to rounded values + * of exact coordinates lying on the segment. + * + */ +class GEOS_DLL SegmentPointComparator { + +public: + + /** \brief + * Compares two Coordinates for their relative position along a + * segment lying in the specified Octant. + * + * @return -1 node0 occurs first + * @return 0 the two nodes are equal + * @return 1 node1 occurs first + */ + static int + compare(int octant, const geom::Coordinate& p0, + const geom::Coordinate& p1) + { + // nodes can only be equal if their coordinates are equal + if(p0.equals2D(p1)) { + return 0; + } + + int xSign = relativeSign(p0.x, p1.x); + int ySign = relativeSign(p0.y, p1.y); + + switch(octant) { + case 0: + return compareValue(xSign, ySign); + case 1: + return compareValue(ySign, xSign); + case 2: + return compareValue(ySign, -xSign); + case 3: + return compareValue(-xSign, ySign); + case 4: + return compareValue(-xSign, -ySign); + case 5: + return compareValue(-ySign, -xSign); + case 6: + return compareValue(-ySign, xSign); + case 7: + return compareValue(xSign, -ySign); + } + assert(0); // invalid octant value + return 0; + + } + + static int + relativeSign(double x0, double x1) + { + if(x0 < x1) { + return -1; + } + if(x0 > x1) { + return 1; + } + return 0; + } + + static int + compareValue(int compareSign0, int compareSign1) + { + if(compareSign0 < 0) { + return -1; + } + if(compareSign0 > 0) { + return 1; + } + if(compareSign1 < 0) { + return -1; + } + if(compareSign1 > 0) { + return 1; + } + return 0; + } + +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/SegmentSetMutualIntersector.h b/Sources/geos/include/geos/noding/SegmentSetMutualIntersector.h new file mode 100644 index 0000000..cdd99b2 --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentSetMutualIntersector.h @@ -0,0 +1,80 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * An intersector for the red-blue intersection problem. + * + * In this class of line arrangement problem, + * two disjoint sets of linestrings are provided. + * It is assumed that within + * each set, no two linestrings intersect except possibly at their endpoints. + * Implementations can take advantage of this fact to optimize processing. + * + * @author Martin Davis + * @version 1.10 + */ +class GEOS_DLL SegmentSetMutualIntersector { +public: + + SegmentSetMutualIntersector() + : segInt(nullptr) + {} + + virtual + ~SegmentSetMutualIntersector() {} + + /** + * Sets the SegmentIntersector to use with this intersector. + * The SegmentIntersector will either record or add intersection nodes + * for the input segment strings. + * + * @param si the segment intersector to use + */ + void + setSegmentIntersector(SegmentIntersector* si) + { + segInt = si; + } + + /** + * + * @param segStrings a collection of [SegmentStrings](@ref SegmentString) to node + */ + virtual void setBaseSegments(SegmentString::ConstVect* segStrings) = 0; + + /** + * Computes the intersections for two collections of [SegmentStrings](@ref SegmentString). + * + * @param segStrings a collection of [SegmentStrings](@ref SegmentString) to node + */ + virtual void process(SegmentString::ConstVect* segStrings) = 0; + +protected: + + SegmentIntersector* segInt; + +}; + +} // geos::noding +} // geos + diff --git a/Sources/geos/include/geos/noding/SegmentString.h b/Sources/geos/include/geos/noding/SegmentString.h new file mode 100644 index 0000000..af7cee2 --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentString.h @@ -0,0 +1,199 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/SegmentString.java r430 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** \brief + * An interface for classes which represent a sequence of contiguous + * line segments. + * + * SegmentStrings can carry a context object, which is useful + * for preserving topological or parentage information. + */ +class GEOS_DLL SegmentString { +public: + typedef std::vector ConstVect; + typedef std::vector NonConstVect; + + friend std::ostream& operator<< (std::ostream& os, + const SegmentString& ss); + + /// \brief Construct a SegmentString. + /// + /// @param newContext the context associated to this SegmentString + /// @param newSeq coordinates of this SegmentString + /// + SegmentString(const void* newContext, geom::CoordinateSequence* newSeq) + : + seq(newSeq), + context(newContext) + {} + + virtual + ~SegmentString() {} + + /** \brief + * Gets the user-defined data for this segment string. + * + * @return the user-defined data + */ + const void* + getData() const + { + return context; + } + + /** \brief + * Sets the user-defined data for this segment string. + * + * @param data an Object containing user-defined data + */ + void + setData(const void* data) + { + context = data; + } + + std::size_t size() const { + return seq->size(); + } + + template + const CoordType& getCoordinate(std::size_t i) const { + return seq->getAt(i); + } + + /// \brief + /// Return a pointer to the CoordinateSequence associated + /// with this SegmentString. + /// + /// @note The CoordinateSequence is owned by this SegmentString! + /// + const geom::CoordinateSequence* getCoordinates() const { + return seq; + } + + geom::CoordinateSequence* getCoordinates() { + return seq; + } + + /** \brief + * Gets the octant of the segment starting at vertex index. + * + * @param index the index of the vertex starting the segment. + * Must not be the last index in the vertex list + * @return the octant of the segment at the vertex + */ + int getSegmentOctant(std::size_t index) const + { + if (index >= size() - 1) { + return -1; + } + return safeOctant(seq->getAt(index), + seq->getAt(index + 1)); + }; + + static int getSegmentOctant(const SegmentString& ss, std::size_t index) { + return ss.getSegmentOctant(index); + } + + /** + * Gets the next vertex in a ring from a vertex index. + * + * @param index the vertex index + * @return the next vertex in the ring + * + * @see isClosed + */ + const geom::CoordinateXY& nextInRing(std::size_t index) const + { + std::size_t nextIndex = index + 1; + if (nextIndex > size() - 1) { + nextIndex = 1; + } + return getCoordinate(nextIndex); + } + + /** + * Gets the previous vertex in a ring from a vertex index. + * + * @param index the vertex index + * @return the previous vertex in the ring + * + * @see isClosed + */ + const geom::CoordinateXY& prevInRing(std::size_t index) const + { + std::size_t prevIndex; + if (index == 0) + prevIndex = size() - 2; + else + prevIndex = index - 1; + return getCoordinate( prevIndex ); + } + + + bool isClosed() const { + return seq->front().equals(seq->back()); + } + + virtual std::ostream& print(std::ostream& os) const; + +protected: + geom::CoordinateSequence* seq; + +private: + const void* context; + + static int safeOctant(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) + { + if(p0.equals2D(p1)) { + return 0; + } + return Octant::octant(p0, p1); + }; + + // Declare type as noncopyable + SegmentString(const SegmentString& other) = delete; + SegmentString& operator=(const SegmentString& rhs) = delete; +}; + +std::ostream& operator<< (std::ostream& os, const SegmentString& ss); + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/SegmentStringUtil.h b/Sources/geos/include/geos/noding/SegmentStringUtil.h new file mode 100644 index 0000000..b780a8f --- /dev/null +++ b/Sources/geos/include/geos/noding/SegmentStringUtil.h @@ -0,0 +1,67 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + * + ********************************************************************** + * + * Last port: noding/SegmentStringUtil.java rev. 1.2 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace noding { // geos::noding + +/** \brief + * Utility methods for processing {@link SegmentString}s. + * + * @author Martin Davis + * + */ +class SegmentStringUtil { +public: + /** \brief + * Extracts all linear components from a given [Geometry](@ref geom::Geometry) + * to [SegmentStrings](@ref SegmentString) + * + * The SegmentString data item is set to be the source Geometry. + * + * @param g the geometry to extract from + * @param segStr a List of SegmentStrings (output parameter). + * Ownership of elements pushed to the vector + * is transferred to caller. + */ + static void + extractSegmentStrings(const geom::Geometry* g, + SegmentString::ConstVect& segStr) + { + geom::LineString::ConstVect lines; + geom::util::LinearComponentExtracter::getLines(*g, lines); + + for(const geom::LineString* line : lines) { + auto pts = line->getCoordinatesRO(); + segStr.push_back(new BasicSegmentString(const_cast(pts), g)); + } + } + +}; + +} // geos::noding +} // geos + diff --git a/Sources/geos/include/geos/noding/SimpleNoder.h b/Sources/geos/include/geos/noding/SimpleNoder.h new file mode 100644 index 0000000..bbe6b54 --- /dev/null +++ b/Sources/geos/include/geos/noding/SimpleNoder.h @@ -0,0 +1,70 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/SimpleNoder.java rev. 1.7 (JTS-1.9) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include +#include // for inlined (FIXME) + +// Forward declarations +namespace geos { +namespace noding { +//class SegmentString; +} +} + +namespace geos { +namespace noding { // geos.noding + + +/** \brief + * Nodes a set of {@link SegmentString}s by + * performing a brute-force comparison of every segment to every other one. + * + * This has n^2 performance, so is too slow for use on large numbers + * of segments. + * + * @version 1.7 + */ +class GEOS_DLL SimpleNoder: public SinglePassNoder { +private: + std::vector* nodedSegStrings; + virtual void computeIntersects(SegmentString* e0, SegmentString* e1); + +public: + SimpleNoder(SegmentIntersector* nSegInt = nullptr) + : + SinglePassNoder(nSegInt) + {} + + void computeNodes(std::vector* inputSegmentStrings) override; + + std::vector* + getNodedSubstrings() const override + { + return NodedSegmentString::getNodedSubstrings(*nodedSegStrings); + } +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/SinglePassNoder.h b/Sources/geos/include/geos/noding/SinglePassNoder.h new file mode 100644 index 0000000..412c19b --- /dev/null +++ b/Sources/geos/include/geos/noding/SinglePassNoder.h @@ -0,0 +1,96 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +class SegmentIntersector; +} +} + +namespace geos { +namespace noding { // geos.noding + + +/** \brief + * Base class for {@link Noder}s which make a single + * pass to find intersections. + * + * This allows using a custom SegmentIntersector + * (which for instance may simply identify intersections, rather than + * insert them). + * + * Last port: noding/SinglePassNoder.java rev. 1.3 (JTS-1.7) + * + * TODO: Noder inheritance (that's just an interface!) + * + */ +class GEOS_DLL SinglePassNoder : public Noder { // implements Noder + +protected: + + /// Externally owned + SegmentIntersector* segInt; + +public: + + SinglePassNoder(SegmentIntersector* nSegInt = nullptr): segInt(nSegInt) {} + + ~SinglePassNoder() override {} + + /** \brief + * Sets the SegmentIntersector to use with this noder. + * + * A SegmentIntersector will normally add intersection nodes + * to the input segment strings, but it may not - it may + * simply record the presence of intersections. + * However, some Noders may require that intersections be added. + * + * @param newSegInt + */ + virtual void + setSegmentIntersector(SegmentIntersector* newSegInt) + { + segInt = newSegInt; + } + + /** \brief + * Computes the noding for a collection of {@link SegmentString}s. + * + * @param segStrings a collection of {@link SegmentString}s to node + */ + void computeNodes(std::vector* segStrings) override = 0; + + /** \brief + * Returns a Collection of fully noded {@link SegmentString}s. + * + * The SegmentStrings have the same context as their parent. + * + * @return a Collection of SegmentStrings + */ + std::vector* getNodedSubstrings() const override = 0; + +}; + +} // namespace geos.noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/ValidatingNoder.h b/Sources/geos/include/geos/noding/ValidatingNoder.h new file mode 100644 index 0000000..ce45c74 --- /dev/null +++ b/Sources/geos/include/geos/noding/ValidatingNoder.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include + +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace noding { // geos.noding + +/** + * A wrapper for {@link noding::Noder}s which validates + * the output arrangement is correctly noded. + * An arrangement of line segments is fully noded if + * there is no line segment + * which has another segment intersecting its interior. + * If the noding is not correct, a {@link util::TopologyException} is thrown + * with details of the first invalid location found. + * + * @author mdavis + * + * @see FastNodingValidator + * + */ +class GEOS_DLL ValidatingNoder : public Noder { + +private: + + std::vector* nodedSS; + noding::Noder& noder; + + +public: + + ValidatingNoder(Noder& noderArg) + : noder(noderArg) + {} + + void computeNodes(std::vector* segStrings) override; + + void validate(); + + std::vector* getNodedSubstrings() const override; + +}; + +} // namespace geos.noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/snap/SnappingIntersectionAdder.h b/Sources/geos/include/geos/noding/snap/SnappingIntersectionAdder.h new file mode 100644 index 0000000..07641c6 --- /dev/null +++ b/Sources/geos/include/geos/noding/snap/SnappingIntersectionAdder.h @@ -0,0 +1,109 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for composition +#include // for use in vector +#include // for inlines (should drop) +#include + + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +} +namespace noding { +class SegmentString; +class NodedSegmentString; +namespace snap { +class SnappingPointIndex; +} +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snap { // geos::noding::snap + +class GEOS_DLL SnappingIntersectionAdder: public SegmentIntersector { // implements SegmentIntersector + +private: + + algorithm::LineIntersector li; + double snapTolerance; + SnappingPointIndex& snapPointIndex; + + /** + * If an endpoint of one segment is near + * the interior of the other segment, add it as an intersection. + * EXCEPT if the endpoint is also close to a segment endpoint + * (since this can introduce "zigs" in the linework). + *

+ * This resolves situations where + * a segment A endpoint is extremely close to another segment B, + * but is not quite crossing. Due to robustness issues + * in orientation detection, this can + * result in the snapped segment A crossing segment B + * without a node being introduced. + */ + void processNearVertex( + SegmentString* srcSS, + std::size_t srcIndex, + const geom::Coordinate& p, + SegmentString* ss, + std::size_t segIndex, + const geom::Coordinate& p0, + const geom::Coordinate& p1); + + /** + * Tests if segments are adjacent on the same SegmentString. + * Closed segStrings require a check for the point shared by the beginning + * and end segments. + */ + static bool isAdjacent(SegmentString* ss0, std::size_t segIndex0, SegmentString* ss1, std::size_t segIndex1); + + +public: + + SnappingIntersectionAdder(double p_snapTolerance, SnappingPointIndex& p_snapPointIndex); + + /** + * This method is called by clients + * of the {@link SegmentIntersector} class to process + * intersections for two segments of the {@link SegmentString}s being intersected. + * Note that some clients (such as MonotoneChains) may optimize away + * this call for segment pairs which they have determined do not intersect + * (e.g. by an disjoint envelope test). + */ + void processIntersections(SegmentString* e0, std::size_t segIndex0, SegmentString* e1, std::size_t segIndex1) override; + + bool isDone() const override { return false; }; + + +}; + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + + + + + diff --git a/Sources/geos/include/geos/noding/snap/SnappingNoder.h b/Sources/geos/include/geos/noding/snap/SnappingNoder.h new file mode 100644 index 0000000..40f057a --- /dev/null +++ b/Sources/geos/include/geos/noding/snap/SnappingNoder.h @@ -0,0 +1,121 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snap/SnappingNoder.java + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class PrecisionModel; +} +namespace noding { +class NodedSegmentString; +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snap { // geos::noding::snap + +/** + * Nodes a set of segment strings + * snapping vertices and intersection points together if + * they lie within the given snap tolerance distance. + * Vertices take priority over intersection points for snapping. + * Input segment strings are generally only split at true node points + * (i.e. the output segment strings are of maximal length in the output arrangement). + * + * The snap tolerance should be chosen to be as small as possible + * while still producing a correct result. + * It probably only needs to be small enough to eliminate + * "nearly-coincident" segments, for which intersection points cannot be computed accurately. + * This implies a factor of about 10e-12 + * smaller than the magnitude of the segment coordinates. + * + * With an appropriate snap tolerance this algorithm appears to be very robust. + * So far no failure cases have been found, + * given a small enough snap tolerance. + * + * The correctness of the output is not verified by this noder. + * If required this can be done by {@link noding::ValidatingNoder}. + */ +class GEOS_DLL SnappingNoder : public Noder { + +private: + + // Members + double snapTolerance; + SnappingPointIndex snapIndex; + std::vector* nodedResult; + + // Methods + + void seedSnapIndex(std::vector& segStrings); + + void snapVertices(std::vector& segStrings, std::vector& nodedStrings); + + SegmentString* snapVertices(SegmentString* ss); + + std::unique_ptr snap(const geom::CoordinateSequence* cs); + + /** + * Computes all interior intersections in the collection of {@link SegmentString}s, + * and returns their {@link Coordinate}s. + * + * Also adds the intersection nodes to the segments. + * + * @return a list of Coordinates for the intersections + */ + std::unique_ptr> snapIntersections(std::vector& inputSS); + + +public: + + /** + * Creates a snapping noder using the given snap distance tolerance. + * @param p_snapTolerance points are snapped if within this distance + */ + SnappingNoder(double p_snapTolerance) + : snapTolerance(p_snapTolerance) + , snapIndex(p_snapTolerance) + {} + + /** + * @return a Collection of NodedSegmentStrings representing the substrings + */ + std::vector* getNodedSubstrings() const override; + + void computeNodes(std::vector* inputSegStrings) override; + + +}; + + +} // namespace geos::noding::snap +} // namespace geos::noding +} // namespace geos diff --git a/Sources/geos/include/geos/noding/snap/SnappingPointIndex.h b/Sources/geos/include/geos/noding/snap/SnappingPointIndex.h new file mode 100644 index 0000000..7a2d444 --- /dev/null +++ b/Sources/geos/include/geos/noding/snap/SnappingPointIndex.h @@ -0,0 +1,62 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snap/SnappingPointIndex.java + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace noding { // geos::noding +namespace snap { // geos::noding::snap + +class GEOS_DLL SnappingPointIndex { + +/** + * An index providing fast creation and lookup of snap points. + * @author mdavis + */ + +private: + + // double snapTolerance; + std::unique_ptr snapPointIndex; + +public: + + SnappingPointIndex(double p_snapTolerance); + + /** + * Snaps a coordinate to an existing snap point, + * if it is within the snap tolerance distance. + * Otherwise adds the coordinate to the snap point index. + * + * @param p the point to snap + * @return the point it snapped to, or the input point + */ + const geom::Coordinate& snap(const geom::Coordinate& p); + +}; + +} // namespace geos::noding::snap +} // namespace geos::noding +} // namespace geos + + + diff --git a/Sources/geos/include/geos/noding/snapround/HotPixel.h b/Sources/geos/include/geos/noding/snapround/HotPixel.h new file mode 100644 index 0000000..a7b52d5 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/HotPixel.h @@ -0,0 +1,171 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snapround/HotPixel.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +} +namespace algorithm { +class LineIntersector; +} +namespace noding { +class NodedSegmentString; +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + +/** \brief + * Implements a "hot pixel" as used in the Snap Rounding algorithm. + * + * A hot pixel contains the interior of the tolerance square and + * the boundary minus the top and right segments. + * + * The hot pixel operations are all computed in the integer domain + * to avoid rounding problems. + */ +class GEOS_DLL HotPixel { + +private: + + static constexpr double TOLERANCE = 0.5; + + static constexpr int UPPER_RIGHT = 0; + static constexpr int UPPER_LEFT = 1; + static constexpr int LOWER_LEFT = 2; + static constexpr int LOWER_RIGHT = 3; + + // Store all ordinates because we may use them in constructing a SegmentNode. + geom::CoordinateXYZM originalPt; + double scaleFactor; + + /* The scaled ordinates of the hot pixel point */ + double hpx; + double hpy; + + /* Indicates if this hot pixel must be a node in the output. */ + bool hpIsNode; + + double scaleRound(double val) const + { + // Use Java-compatible round implementation + return util::round(val * scaleFactor); + }; + + double scale(double val) const + { + return val * scaleFactor; + }; + + bool intersectsPixelClosure(const geom::Coordinate& p0, + const geom::Coordinate& p1) const; + + bool intersectsScaled(double p0x, double p0y, double p1x, double p1y) const; + + // Declare type as noncopyable + HotPixel(const HotPixel& other) = delete; + HotPixel& operator=(const HotPixel& rhs) = delete; + +public: + + /** + * Gets the width of the hot pixel in the original coordinate system. + */ + double getWidth() const { return 1.0 / scaleFactor; }; + + double getScaleFactor() const { return scaleFactor; }; + + /** + * Creates a new hot pixel. + * + * @param pt the coordinate at the centre of the pixel. + * @param scaleFact the scaleFactor determining the pixel size */ + template + HotPixel(const CoordType& pt, double scaleFact) + : originalPt(pt) + , scaleFactor(scaleFact) + , hpx(pt.x) + , hpy(pt.y) + , hpIsNode(false) + { + if(scaleFactor <= 0.0) { + throw util::IllegalArgumentException("Scale factor must be non-zero"); + } + if(scaleFactor != 1.0) { + hpx = scaleRound(pt.x); + hpy = scaleRound(pt.y); + } + } + + /* + * Gets the coordinate this hot pixel is based at. + * + * @return the coordinate of the pixel + */ + const geom::CoordinateXYZM& getCoordinate() const; + + /** + * Tests whether the line segment (p0-p1) intersects this hot pixel. + * + * @param p0 the first coordinate of the line segment to test + * @param p1 the second coordinate of the line segment to test + * @return true if the line segment intersects this hot pixel + */ + bool intersects(const geom::CoordinateXY& p0, + const geom::CoordinateXY& p1) const; + + /** + * Tests whether a coordinate lies in (intersects) this hot pixel. + * + * @param p the coordinate to test + * @return true if the coordinate intersects this hot pixel + */ + bool intersects(const geom::CoordinateXY& p) const; + + bool isNode() const { return hpIsNode; }; + void setToNode() { hpIsNode = true; }; + + std::ostream& operator<< (std::ostream& os); +}; + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/noding/snapround/HotPixelIndex.h b/Sources/geos/include/geos/noding/snapround/HotPixelIndex.h new file mode 100644 index 0000000..690b5f9 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/HotPixelIndex.h @@ -0,0 +1,116 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for unique_ptr +#include // for composition +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace index { +class ItemVisitor; +} +namespace noding { +namespace snapround { +class HotPixel; +} +} +} + + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + + +class GEOS_DLL HotPixelIndex { + +private: + + /* members */ + const geom::PrecisionModel* pm; + double scaleFactor; + std::unique_ptr index; + std::deque hotPixelQue; + + /* methods */ + template + geom::CoordinateXYZM round(const CoordType& pt) { + geom::CoordinateXYZM p2(pt); + pm->makePrecise(p2); + return p2; + } + + HotPixel* find(const geom::Coordinate& pixelPt); + +public: + + HotPixelIndex(const geom::PrecisionModel* p_pm); + HotPixel* addRounded(const geom::CoordinateXYZM& pt); + + template + HotPixel* add(const CoordType& p) { + static_assert(std::is_base_of(), "Only valid for Coordinate types"); + + auto pRound = round(p); + return addRounded(pRound); + } + + void add(const geom::CoordinateSequence* pts); + void add(const std::vector& pts); + void addNodes(const geom::CoordinateSequence* pts); + void addNodes(const std::vector& pts); + + /** + * Visits all the hot pixels which may intersect a segment (p0-p1). + * The visitor must determine whether each hot pixel actually intersects + * the segment. + */ + void query(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, + index::kdtree::KdNodeVisitor& visitor); + +}; + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + diff --git a/Sources/geos/include/geos/noding/snapround/MCIndexPointSnapper.h b/Sources/geos/include/geos/noding/snapround/MCIndexPointSnapper.h new file mode 100644 index 0000000..90bf683 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/MCIndexPointSnapper.h @@ -0,0 +1,98 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snapround/MCIndexPointSnapper.java r486 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +} +namespace index { +class SpatialIndex; +} +namespace noding { +class SegmentString; +namespace snapround { +class HotPixel; +} +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + +/** \brief + * "Snaps" all [SegmentStrings](@ref SegmentString) in a [SpatialIndex](@ref index::SpatialIndex) containing + * [MonotoneChains](@ref index::chain::MonotoneChain) to a given HotPixel. + * + */ +class GEOS_DLL MCIndexPointSnapper { + +public: + + + MCIndexPointSnapper(index::SpatialIndex& nIndex) + : + index(nIndex) + {} + + /** \brief + * Snaps (nodes) all interacting segments to this hot pixel. + * + * The hot pixel may represent a vertex of an edge, in which case this + * routine uses the optimization of not noding the vertex itself. + * + * @param hotPixel the hot pixel to snap to + * @param parentEdge the edge containing the vertex, + * if applicable, or `null` + * @param vertexIndex the index of the vertex, if applicable, or -1 + * @return `true` if a node was added for this pixel + */ + bool snap(HotPixel& hotPixel, SegmentString* parentEdge, + std::size_t vertexIndex); + + bool + snap(HotPixel& hotPixel) + { + return snap(hotPixel, nullptr, 0); + } + + geom::Envelope getSafeEnvelope(const HotPixel& hp) const; + + +private: + + static constexpr double SAFE_ENV_EXPANSION_FACTOR = 0.75; + + index::SpatialIndex& index; + + // Declare type as noncopyable + MCIndexPointSnapper(const MCIndexPointSnapper& other) = delete; + MCIndexPointSnapper& operator=(const MCIndexPointSnapper& rhs) = delete; +}; + + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/snapround/MCIndexSnapRounder.h b/Sources/geos/include/geos/noding/snapround/MCIndexSnapRounder.h new file mode 100644 index 0000000..70305f1 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/MCIndexSnapRounder.h @@ -0,0 +1,157 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snapround/MCIndexSnapRounder.java r486 (JTS-1.12+) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance +#include // for inlines +#include // for inlines +#include // for composition +#include // for use in vector +#include // for inlines + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace noding { +class SegmentString; +class MCIndexNoder; +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + + +/** \brief + * Uses Snap Rounding to compute a rounded, + * fully noded arrangement from a set of SegmentString + * + * Implements the Snap Rounding technique described in Hobby, Guibas & Marimont, + * and Goodrich et al. + * + * Snap Rounding assumes that all vertices lie on a uniform grid + * (hence the precision model of the input must be fixed precision, + * and all the input vertices must be rounded to that precision). + * + * This implementation uses a monotone chains and a spatial index to + * speed up the intersection tests. + * + * This implementation appears to be fully robust using an integer + * precision model. + * + * It will function with non-integer precision models, but the + * results are not 100% guaranteed to be correctly noded. + */ +class GEOS_DLL MCIndexSnapRounder: public Noder { // implements Noder + +public: + + MCIndexSnapRounder(const geom::PrecisionModel& nPm) + : + pm(nPm), + scaleFactor(nPm.getScale()), + pointSnapper(nullptr) + { + li.setPrecisionModel(&pm); + } + + std::vector* + getNodedSubstrings() const override + { + return NodedSegmentString::getNodedSubstrings(*nodedSegStrings); + } + + void computeNodes(std::vector* segStrings) override; + + /** + * Computes nodes introduced as a result of + * snapping segments to vertices of other segments + * + * @param edges the list of segment strings to snap together + * NOTE: they *must* be instances of NodedSegmentString, or + * an assertion will fail. + */ + void computeVertexSnaps(std::vector& edges); + +private: + + /// externally owned + const geom::PrecisionModel& pm; + + algorithm::LineIntersector li; + + double scaleFactor; + + std::vector* nodedSegStrings; + + std::unique_ptr pointSnapper; + + void snapRound(MCIndexNoder& noder, std::vector* segStrings); + + + /** + * Computes all interior intersections in the collection of SegmentStrings, + * and push their Coordinate to the provided vector. + * + * Does NOT node the segStrings. + * + */ + void findInteriorIntersections(MCIndexNoder& noder, + std::vector* segStrings, + std::vector& intersections); + + /** + * Computes nodes introduced as a result of snapping + * segments to snap points (hot pixels) + */ + void computeIntersectionSnaps(std::vector& snapPts); + + /** + * Performs a brute-force comparison of every segment in each {@link SegmentString}. + * This has n^2 performance. + */ + void computeVertexSnaps(NodedSegmentString* e); + + void checkCorrectness(std::vector& inputSegmentStrings); + + // Declare type as noncopyable + MCIndexSnapRounder(const MCIndexSnapRounder& other) = delete; + MCIndexSnapRounder& operator=(const MCIndexSnapRounder& rhs) = delete; +}; + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h b/Sources/geos/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h new file mode 100644 index 0000000..2e82da7 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/SnapRoundingIntersectionAdder.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include // for inheritance +#include // for composition +#include // for use in vector +#include +#include // for inlines (should drop) +#include + + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +} +namespace noding { +class SegmentString; +class NodedSegmentString; +namespace snapround { +class HotPixel; +} +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + +/** + * Finds intersections between line segments which will be snap-rounded, + * and adds them as nodes to the segments. + * + * Intersections are detected and computed using full precision. + * Snapping takes place in a subsequent phase. + * + * The intersection points are recorded, so that HotPixels can be created for them. + * + * To avoid robustness issues with vertices which lie very close to line segments + * a heuristic is used: + * nodes are created if a vertex lies within a tolerance distance + * of the interior of a segment. + * The tolerance distance is chosen to be significantly below the snap-rounding grid size. + * This has empirically proven to eliminate noding failures. + */ +class GEOS_DLL SnapRoundingIntersectionAdder: public SegmentIntersector { // implements SegmentIntersector + +private: + + algorithm::LineIntersector li; + geom::CoordinateSequence intersections; + // const geom::PrecisionModel* pm; + double nearnessTol; + + /** + * If an endpoint of one segment is near + * the interior of the other segment, add it as an intersection. + * EXCEPT if the endpoint is also close to a segment endpoint + * (since this can introduce "zigs" in the linework). + * + * This resolves situations where + * a segment A endpoint is extremely close to another segment B, + * but is not quite crossing. Due to robustness issues + * in orientation detection, this can + * result in the snapped segment A crossing segment B + * without a node being introduced. + */ + void processNearVertex(const geom::CoordinateSequence& seq0, + std::size_t ptIndex, + const geom::CoordinateSequence& seq1, + std::size_t segIndex, + SegmentString* edge); + + bool isNearSegmentInterior(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) const; + +public: + + SnapRoundingIntersectionAdder(double p_nearnessTol) + : SegmentIntersector() + , intersections(geom::CoordinateSequence::XYZM(0)) + , nearnessTol(p_nearnessTol) + {} + + geom::CoordinateSequence getIntersections() { return std::move(intersections); }; + + /** + * This method is called by clients + * of the {@link SegmentIntersector} class to process + * intersections for two segments of the {@link SegmentString}s being intersected. + * Note that some clients (such as MonotoneChains) may optimize away + * this call for segment pairs which they have determined do not intersect + * (e.g. by an disjoint envelope test). + */ + void processIntersections(SegmentString* e0, std::size_t segIndex0, SegmentString* e1, std::size_t segIndex1) override; + + /** + * Always process all intersections + * + */ + bool isDone() const override { return false; } + + +}; + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + diff --git a/Sources/geos/include/geos/noding/snapround/SnapRoundingNoder.h b/Sources/geos/include/geos/noding/snapround/SnapRoundingNoder.h new file mode 100644 index 0000000..78dc8c7 --- /dev/null +++ b/Sources/geos/include/geos/noding/snapround/SnapRoundingNoder.h @@ -0,0 +1,167 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: noding/snapround/SnapRoundingNoder.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class PrecisionModel; +} +namespace noding { +class NodedSegmentString; +} +} + +namespace geos { +namespace noding { // geos::noding +namespace snapround { // geos::noding::snapround + +/** + * Uses Snap Rounding to compute a rounded, + * fully noded arrangement from a set of {@link noding::SegmentString}s, + * in a performant way, and avoiding unnecessary noding. + * + * Implements the Snap Rounding technique described in + * the papers by Hobby, Guibas & Marimont, and Goodrich et al. + * Snap Rounding enforces that all output vertices lie on a uniform grid, + * which is determined by the provided {@link geom::PrecisionModel}. + * + * Input vertices do not have to be rounded to the grid beforehand; + * this is done during the snap-rounding process. + * In fact, rounding cannot be done a priori, + * since rounding vertices by themselves can distort the rounded topology + * of the arrangement (i.e. by moving segments away from hot pixels + * that would otherwise intersect them, or by moving vertices + * across segments). + * + * To minimize the number of introduced nodes, + * the Snap-Rounding Noder avoids creating nodes + * at edge vertices if there is no intersection or snap at that location. + * However, if two different input edges contain identical segments, + * each of the segment vertices will be noded. + * This still provides fully-noded output. + * This is the same behaviour provided by other noders, + * such as {@link noding::MCIndexNoder} and {@link noding::snap::SnappingNoder}. + */ +class GEOS_DLL SnapRoundingNoder : public Noder { + +private: + /** + * The division factor used to determine + * nearness distance tolerance for interior intersection detection. + */ + static constexpr int INTERSECTION_NEARNESS_FACTOR = 100; + + // Members + const geom::PrecisionModel* pm; + noding::snapround::HotPixelIndex pixelIndex; + std::vector snappedResult; + + // Methods + void snapRound(std::vector& inputSegStrings, std::vector& resultNodedSegments); + + /** + * Creates HotPixels for each vertex in the input segStrings. + * The HotPixels are not marked as nodes, since they will + * only be nodes in the final line arrangement + * if they interact with other segments (or they are already + * created as intersection nodes). + */ + void addVertexPixels(std::vector& segStrings); + + /** + * Detects interior intersections in the collection of {@link SegmentString}s, + * and adds nodes for them to the segment strings. + * Also creates HotPixel nodes for the intersection points. + */ + void addIntersectionPixels(std::vector& segStrings); + + /** + * Gets a list of the rounded coordinates. + * Duplicate (collapsed) coordinates are removed. + * + * @param pts the coordinates to round + * @return array of rounded coordinates + */ + std::unique_ptr round(const geom::CoordinateSequence& pts) const; + + /** + * Computes new segment strings which are rounded and contain + * intersections added as a result of snapping segments to snap points (hot pixels). + * + * @param segStrings segments to snap + * @return the snapped segment strings + */ + void computeSnaps(const std::vector& segStrings, std::vector& snapped); + NodedSegmentString* computeSegmentSnaps(NodedSegmentString* ss); + + /** + * Snaps a segment in a segmentString to HotPixels that it intersects. + * + * @param p0 the segment start coordinate + * @param p1 the segment end coordinate + * @param ss the segment string to add intersections to + * @param segIndex the index of the segment + */ + void snapSegment(const geom::CoordinateXY& p0, const geom::CoordinateXY& p1, NodedSegmentString* ss, std::size_t segIndex); + + /** + * Add nodes for any vertices in hot pixels that were + * added as nodes during segment noding. + */ + void addVertexNodeSnaps(NodedSegmentString* ss); + + void snapVertexNode(const geom::CoordinateXY& p0, NodedSegmentString* ss, std::size_t segIndex); + +public: + + SnapRoundingNoder(const geom::PrecisionModel* p_pm) + : pm(p_pm) + , pixelIndex(p_pm) + {} + + /** + * @return a Collection of NodedSegmentStrings representing the substrings + */ + std::vector* getNodedSubstrings() const override; + + /** + * Computes the nodes in the snap-rounding line arrangement. + * The nodes are added to the {@link NodedSegmentString}s provided as the input. + */ + void computeNodes(std::vector* inputSegStrings) override; //override + +}; + + +} // namespace geos::noding::snapround +} // namespace geos::noding +} // namespace geos + + + diff --git a/Sources/geos/include/geos/operation/BoundaryOp.h b/Sources/geos/include/geos/operation/BoundaryOp.h new file mode 100644 index 0000000..4a59027 --- /dev/null +++ b/Sources/geos/include/geos/operation/BoundaryOp.h @@ -0,0 +1,120 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/BoundaryOp.java fd5aebb + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace geom { +class LineString; +class MultiLineString; +} +} + +namespace geos { +namespace operation { + +/** + * Computes the boundary of a Geometry. + * Allows specifying the BoundaryNodeRule to be used. + * This operation will always return a Geometry of the appropriate + * dimension for the boundary (even if the input geometry is empty). + * The boundary of zero-dimensional geometries (Points) is + * always the empty GeometryCollection. + * + * @author Martin Davis + * @version 1.7 + */ +class GEOS_DLL BoundaryOp { + +public: + /** + * Creates a new instance for the given geometry. + * + * @param geom the input geometry + */ + BoundaryOp(const geom::Geometry& geom); + + /** + * Creates a new instance for the given geometry. + * + * @param geom the input geometry + * @param bnRule the Boundary Node Rule to use + */ + BoundaryOp(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& bnRule); + + /** + * Computes a geometry representing the boundary of a geometry. + * + * @param g the input geometry + * @return the computed boundary + */ + static std::unique_ptr getBoundary(const geom::Geometry& g); + + /** + * Computes a geometry representing the boundary of a geometry, + * using an explicit BoundaryNodeRule. + * + * @param g the input geometry + * @param bnRule the Boundary Node Rule to use + * @return the computed boundary + */ + static std::unique_ptr getBoundary(const geom::Geometry& g, const algorithm::BoundaryNodeRule& bnRule); + + /** + * Tests if a geometry has a boundary (it is non-empty). + * The semantics are: + *

    + *
  • Empty geometries do not have boundaries. + *
  • Points do not have boundaries. + *
  • For linear geometries the existence of the boundary + * is determined by the BoundaryNodeRule. + *
  • Non-empty polygons always have a boundary. + *
+ * + * @param geom the geometry providing the boundary + * @param boundaryNodeRule the Boundary Node Rule to use + * @return true if the boundary exists + */ + static bool hasBoundary(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& boundaryNodeRule); + + /** + * Gets the computed boundary. + * + * @return the boundary geometry + */ + std::unique_ptr getBoundary(); + +private: + const geom::Geometry& m_geom; + const geom::GeometryFactory& m_geomFact; + const algorithm::BoundaryNodeRule& m_bnRule; + + std::unique_ptr boundaryMultiLineString(const geom::MultiLineString& mLine); + + std::unique_ptr computeBoundaryCoordinates(const geom::MultiLineString& mLine); + + std::unique_ptr boundaryLineString(const geom::LineString& line); +}; + +} +} + diff --git a/Sources/geos/include/geos/operation/GeometryGraphOperation.h b/Sources/geos/include/geos/operation/GeometryGraphOperation.h new file mode 100644 index 0000000..0886e65 --- /dev/null +++ b/Sources/geos/include/geos/operation/GeometryGraphOperation.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/GeometryGraphOperation.java rev. 1.18 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geom { +class Geometry; +class PrecisionModel; +} +namespace geomgraph { +class GeometryGraph; +} +} + + +namespace geos { +namespace operation { // geos.operation + +/// The base class for operations that require GeometryGraph +class GEOS_DLL GeometryGraphOperation { + +public: + + GeometryGraphOperation(const geom::Geometry* g0, + const geom::Geometry* g1); + + GeometryGraphOperation(const geom::Geometry* g0, + const geom::Geometry* g1, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + GeometryGraphOperation(const geom::Geometry* g0); + + virtual ~GeometryGraphOperation(); + + const geom::Geometry* getArgGeometry(unsigned int i) const; + +protected: + + algorithm::LineIntersector li; + + const geom::PrecisionModel* resultPrecisionModel; + + /** \brief + * The operation args into an array so they can be accessed by index + */ + std::vector> arg; + + void setComputationPrecision(const geom::PrecisionModel* pm); + + // Declare type as noncopyable + GeometryGraphOperation(const GeometryGraphOperation& other) = delete; + GeometryGraphOperation& operator=(const GeometryGraphOperation& rhs) = delete; +}; + +} // namespace geos.operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/BufferBuilder.h b/Sources/geos/include/geos/operation/buffer/BufferBuilder.h new file mode 100644 index 0000000..b3c6cc9 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferBuilder.h @@ -0,0 +1,269 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2011 Sandro Santilli + * Copyright (C) 2008-2010 Safe Software Inc. + * Copyright (C) 2006-2007 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferBuilder.java 149b38907 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for inlines (BufferOp enums) +#include // for inline (OffsetCurveBuilder enums) +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +class GeometryFactory; +} +namespace algorithm { +class LineIntersector; +} +namespace noding { +class Noder; +class SegmentString; +class IntersectionAdder; +} +namespace geomgraph { +class Edge; +class Label; +class PlanarGraph; +} +namespace operation { +namespace buffer { +class BufferSubgraph; +class PolygonBuilder; +} + +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * + * \brief + * Builds the buffer geometry for a given input geometry and precision model. + * + * Allows setting the level of approximation for circular arcs, + * and the precision model in which to carry out the computation. + * + * When computing buffers in floating point double-precision + * it can happen that the process of iterated noding can fail to converge + * (terminate). + * + * In this case a TopologyException will be thrown. + * Retrying the computation in a fixed precision + * can produce more robust results. + * + */ +class GEOS_DLL BufferBuilder { + +public: + /** + * Creates a new BufferBuilder + * + * @param nBufParams buffer parameters, this object will + * keep a reference to the passed parameters + * so caller must make sure the object is + * kept alive for the whole lifetime of + * the buffer builder. + */ + BufferBuilder(const BufferParameters& nBufParams) + : + bufParams(nBufParams), + workingPrecisionModel(nullptr), + li(nullptr), + intersectionAdder(nullptr), + workingNoder(nullptr), + geomFact(nullptr), + edgeList(), + isInvertOrientation(false) + {} + + ~BufferBuilder(); + + + /** + * Sets the precision model to use during the curve computation + * and noding, + * if it is different to the precision model of the Geometry. + * If the precision model is less than the precision of the + * Geometry precision model, + * the Geometry must have previously been rounded to that precision. + * + * @param pm the precision model to use + */ + void + setWorkingPrecisionModel(const geom::PrecisionModel* pm) + { + workingPrecisionModel = pm; + } + + /** + * Sets the {@link noding::Noder} to use during noding. + * This allows choosing fast but non-robust noding, or slower + * but robust noding. + * + * @param newNoder the noder to use + */ + void + setNoder(noding::Noder* newNoder) + { + workingNoder = newNoder; + } + + /** + * Sets whether the offset curve is generated + * using the inverted orientation of input rings. + * This allows generating a buffer(0) polygon from the smaller lobes + * of self-crossing rings. + * + * @param p_isInvertOrientation true if input ring orientation should be inverted + */ + void + setInvertOrientation(bool p_isInvertOrientation) + { + isInvertOrientation = p_isInvertOrientation; + } + + + std::unique_ptr buffer(const geom::Geometry* g, double distance); + + /** + * Generates offset curve for linear geometry. + * + * @param g non-areal geometry object + * @param distance width of offset + * @param leftSide controls on which side of the input geometry + * offset curve is generated. + * + * @note For left-side offset curve, the offset will be at the left side + * of the input line and retain the same direction. + * For right-side offset curve, it'll be at the right side + * and in the opposite direction. + * + * @note BufferParameters::setSingleSided parameter, which is specific to + * areal geometries only, is ignored by this routine. + * + * @note Not in JTS: this is a GEOS extension + */ + std::unique_ptr bufferLineSingleSided( + const geom::Geometry* g, + double distance, bool leftSide); + +private: + + /** + * Compute the change in depth as an edge is crossed from R to L + */ + static int depthDelta(const geomgraph::Label& label); + + const BufferParameters& bufParams; + + const geom::PrecisionModel* workingPrecisionModel; + + algorithm::LineIntersector* li; + + noding::IntersectionAdder* intersectionAdder; + + noding::Noder* workingNoder; + + const geom::GeometryFactory* geomFact; + + geomgraph::EdgeList edgeList; + + std::vector newLabels; + + bool isInvertOrientation; + + void computeNodedEdges(std::vector& bufSegStr, + const geom::PrecisionModel* precisionModel); + // throw(GEOSException); + + /** + * Inserted edges are checked to see if an identical edge already + * exists. + * If so, the edge is not inserted, but its label is merged + * with the existing edge. + * + * The function takes responsibility of releasing the Edge parameter + * memory when appropriate. + */ + void insertUniqueEdge(geomgraph::Edge* e); + + void createSubgraphs(geomgraph::PlanarGraph* graph, + std::vector& list); + + /** + * Completes the building of the input subgraphs by + * depth-labelling them, + * and adds them to the PolygonBuilder. + * The subgraph list must be sorted in rightmost-coordinate order. + * + * @param subgraphList the subgraphs to build + * @param polyBuilder the PolygonBuilder which will build + * the final polygons + */ + void buildSubgraphs(const std::vector& subgraphList, + PolygonBuilder& polyBuilder); + + /// \brief + /// Return the externally-set noding::Noder OR a newly created + /// one using the given precisionModel. + /// + /// NOTE: if an externally-set noding::Noder is available no + /// check is performed to ensure it will use the + /// given PrecisionModel + /// + noding::Noder* getNoder(const geom::PrecisionModel* precisionModel); + + + /** + * Gets the standard result for an empty buffer. + * Since buffer always returns a polygonal result, + * this is chosen to be an empty polygon. + * + * @return the empty result geometry, transferring ownership to caller. + */ + std::unique_ptr createEmptyResultGeometry() const; + + // Declare type as noncopyable + BufferBuilder(const BufferBuilder& other) = delete; + BufferBuilder& operator=(const BufferBuilder& rhs) = delete; +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h b/Sources/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h new file mode 100644 index 0000000..789b8c9 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h @@ -0,0 +1,305 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferCurveSetBuilder.java 4c343e79f (JTS-1.19) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class CoordinateSequence; +class PrecisionModel; +class GeometryCollection; +class Point; +class LineString; +class LinearRing; +class Polygon; +} +namespace geomgraph { +class Label; +} +namespace noding { +class SegmentString; +} +namespace operation { +namespace buffer { +class BufferParameters; +} +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \class BufferCurveSetBuilder + * + * \brief + * Creates all the raw offset curves for a buffer of a Geometry. + * + * Raw curves need to be noded together and polygonized to form the + * final buffer area. + * + */ +class GEOS_DLL BufferCurveSetBuilder { + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + +private: + + static constexpr int MAX_INVERTED_RING_SIZE = 9; + static constexpr int INVERTED_CURVE_VERTEX_FACTOR = 4; + static constexpr double NEARNESS_FACTOR = 0.99; + + // To keep track of newly-created Labels. + // Labels will be released by object dtor + std::vector newLabels; + const geom::Geometry& inputGeom; + double distance; + OffsetCurveBuilder curveBuilder; + + /// The raw offset curves computed. + /// This class holds ownership of std::vector elements. + /// + std::vector curveList; + bool isInvertOrientation = false; + + /** + * Creates a noding::SegmentString for a coordinate list which is a raw + * offset curve, and adds it to the list of buffer curves. + * The noding::SegmentString is tagged with a geomgraph::Label + * giving the topology of the curve. + * The curve may be oriented in either direction. + * If the curve is oriented CW, the locations will be: + * - Left: Location.EXTERIOR + * - Right: Location.INTERIOR + * + * @param coord is raw offset curve, ownership transferred here + */ + void addCurve(geom::CoordinateSequence* coord, geom::Location leftLoc, + geom::Location rightLoc); + + void add(const geom::Geometry& g); + + void addCollection(const geom::GeometryCollection* gc); + + /** + * Add a Point to the graph. + */ + void addPoint(const geom::Point* p); + + void addLineString(const geom::LineString* line); + + void addPolygon(const geom::Polygon* p); + + void addLinearRingSides(const geom::CoordinateSequence* coord, double p_distance); + + /** + * Add an offset curve for a polygon ring. + * The side and left and right topological location arguments + * assume that the ring is oriented CW. + * If the ring is in the opposite orientation, + * the left and right locations must be interchanged and the side + * flipped. + * + * @param coord the coordinates of the ring (must not contain + * repeated points) + * @param offsetDistance the distance at which to create the buffer + * @param side the side of the ring on which to construct the buffer + * line + * @param cwLeftLoc the location on the L side of the ring + * (if it is CW) + * @param cwRightLoc the location on the R side of the ring + * (if it is CW) + */ + void addPolygonRingSide(const geom::CoordinateSequence* coord, + double offsetDistance, int side, geom::Location cwLeftLoc, + geom::Location cwRightLoc); + + void addRingSide(const geom::CoordinateSequence* coord, + double offsetDistance, int side, geom::Location leftLoc, + geom::Location rightLoc); + + /** + * Tests whether the offset curve for a ring is fully inverted. + * An inverted ("inside-out") curve occurs in some specific situations + * involving a buffer distance which should result in a fully-eroded (empty) buffer. + * It can happen that the sides of a small, convex polygon + * produce offset segments which all cross one another to form + * a curve with inverted orientation. + * This happens at buffer distances slightly greater than the distance at + * which the buffer should disappear. + * The inverted curve will produce an incorrect non-empty buffer (for a shell) + * or an incorrect hole (for a hole). + * It must be discarded from the set of offset curves used in the buffer. + * Heuristics are used to reduce the number of cases which area checked, + * for efficiency and correctness. + *

+ * See https://github.com/locationtech/jts/issues/472 + * + * @param inputPts the input ring + * @param distance the buffer distance + * @param curvePts the generated offset curve + * @return true if the offset curve is inverted + */ + static bool isRingCurveInverted( + const geom::CoordinateSequence* inputPts, double dist, + const geom::CoordinateSequence* curvePts); + + /** + * Tests if there are points on the raw offset curve which may + * lie on the final buffer curve + * (i.e. they are (approximately) at the buffer distance from the input ring). + * For efficiency this only tests a limited set of points on the curve. + * + * @param inputRing + * @param distance + * @param curveRing + * @return true if the curve contains points lying at the required buffer distance + */ + static bool hasPointOnBuffer( + const CoordinateSequence* inputRing, double dist, + const CoordinateSequence* curveRing); + + /** + * The ringCoord is assumed to contain no repeated points. + * It may be degenerate (i.e. contain only 1, 2, or 3 points). + * In this case it has no area, and hence has a minimum diameter of 0. + * + * @param ringCoord + * @param bufferDistance + * @return + */ + bool isRingFullyEroded(const geom::LinearRing* ring, bool isHole, + double bufferDistance); + + bool isRingFullyEroded(const CoordinateSequence* ringCoord, const Envelope* env, bool isHole, + double bufferDistance); + + /** + * Tests whether a triangular ring would be eroded completely by + * the given buffer distance. + * This is a precise test. It uses the fact that the inner buffer + * of a triangle converges on the inCentre of the triangle (the + * point equidistant from all sides). If the buffer distance is + * greater than the distance of the inCentre from a side, the + * triangle will be eroded completely. + * + * This test is important, since it removes a problematic case where + * the buffer distance is slightly larger than the inCentre distance. + * In this case the triangle buffer curve "inverts" with incorrect + * topology, producing an incorrect hole in the buffer. + * + * @param triCoord + * @param bufferDistance + * @return + */ + bool isTriangleErodedCompletely(const geom::CoordinateSequence* triCoords, + double bufferDistance); + + // Declare type as noncopyable + BufferCurveSetBuilder(const BufferCurveSetBuilder& other) = delete; + BufferCurveSetBuilder& operator=(const BufferCurveSetBuilder& rhs) = delete; + + /** + * Computes orientation of a ring using a signed-area orientation test. + * For invalid (self-crossing) rings this ensures the largest enclosed area + * is taken to be the interior of the ring. + * This produces a more sensible result when + * used for repairing polygonal geometry via buffer-by-zero. + * For buffer, using the lower robustness of orientation-by-area + * doesn't matter, since narrow or flat rings + * produce an acceptable offset curve for either orientation. + * + * @param coord the ring coordinates + * @return true if the ring is CCW + */ + bool isRingCCW(const geom::CoordinateSequence* coords) const; + +public: + + /// Constructor + BufferCurveSetBuilder( + const geom::Geometry& newInputGeom, + double newDistance, + const geom::PrecisionModel* newPm, + const BufferParameters& newBufParams) + : inputGeom(newInputGeom) + , distance(newDistance) + , curveBuilder(newPm, newBufParams) + , curveList() + , isInvertOrientation(false) + {}; + + /// Destructor + ~BufferCurveSetBuilder(); + + /** \brief + * Computes the set of raw offset curves for the buffer. + * + * Each offset curve has an attached {@link geomgraph::Label} indicating + * its left and right location. + * + * @return a Collection of SegmentStrings representing the raw buffer curves + */ + std::vector& getCurves(); + + /// \brief Add raw curves for a set of CoordinateSequences. + /// + /// @param lineList is a list of CoordinateSequence, ownership + /// of which is transferred here + /// @param leftLoc left location + /// @param rightLoc right location + /// + void addCurves(const std::vector& lineList, + geom::Location leftLoc, geom::Location rightLoc); + + /** + * Sets whether the offset curve is generated + * using the inverted orientation of input rings. + * This allows generating a buffer(0) polygon from the smaller lobes + * of self-crossing rings. + * + * @param p_isInvertOrientation true if input ring orientation should be inverted + */ + void setInvertOrientation(bool p_isInvertOrientation) { + isInvertOrientation = p_isInvertOrientation; + } + +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/buffer/BufferInputLineSimplifier.h b/Sources/geos/include/geos/operation/buffer/BufferInputLineSimplifier.h new file mode 100644 index 0000000..277f296 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferInputLineSimplifier.h @@ -0,0 +1,180 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferInputLineSimplifier.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include // complete type required + +#include +#include // for composition + + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +//class PrecisionModel; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** \brief + * Simplifies a buffer input line to + * remove concavities with shallow depth. + * + * The most important benefit of doing this + * is to reduce the number of points and the complexity of + * shape which will be buffered. + * It also reduces the risk of gores created by + * the quantized fillet arcs (although this issue + * should be eliminated in any case by the + * offset curve generation logic). + * + * A key aspect of the simplification is that it + * affects inside (concave or inward) corners only. + * Convex (outward) corners are preserved, since they + * are required to ensure that the generated buffer curve + * lies at the correct distance from the input geometry. + * + * Another important heuristic used is that the end segments + * of the input are never simplified. This ensures that + * the client buffer code is able to generate end caps faithfully. + * + * No attempt is made to avoid self-intersections in the output. + * This is acceptable for use for generating a buffer offset curve, + * since the buffer algorithm is insensitive to invalid polygonal + * geometry. However, + * this means that this algorithm + * cannot be used as a general-purpose polygon simplification technique. + * + * @author Martin Davis + * + */ +class BufferInputLineSimplifier { + +public: + + /** + * Simplify the input coordinate list. + * + * If the distance tolerance is positive, + * concavities on the LEFT side of the line are simplified. + * If the supplied distance tolerance is negative, + * concavities on the RIGHT side of the line are simplified. + * + * @param inputLine the coordinate sequence to simplify + * @param distanceTol simplification distance tolerance to use + * @return a simplified version of the coordinate sequence + */ + static std::unique_ptr simplify( + const geom::CoordinateSequence& inputLine, double distanceTol); + + BufferInputLineSimplifier(const geom::CoordinateSequence& input); + + /** + * Simplify the input coordinate list. + * If the distance tolerance is positive, + * concavities on the LEFT side of the line are simplified. + * If the supplied distance tolerance is negative, + * concavities on the RIGHT side of the line are simplified. + * + * @param distanceTol simplification distance tolerance to use + * @return the simplified coordinate list + */ + std::unique_ptr simplify(double distanceTol); + +private: + + /** + * Uses a sliding window containing 3 vertices to detect shallow angles + * in which the middle vertex can be deleted, since it does not + * affect the shape of the resulting buffer in a significant way. + * @return + */ + bool deleteShallowConcavities(); + + /** + * Finds the next non-deleted index, + * or the end of the point array if none + * + * @param index + * @return the next non-deleted index, if any + * @return inputLine.size() if there are no more non-deleted indices + */ + std::size_t findNextNonDeletedIndex(std::size_t index) const; + + std::unique_ptr collapseLine() const; + + bool isDeletable(std::size_t i0, std::size_t i1, std::size_t i2, double distanceTol) const; + + bool isShallowConcavity(const geom::Coordinate& p0, + const geom::Coordinate& p1, + const geom::Coordinate& p2, + double distanceTol) const; + + /** + * Checks for shallowness over a sample of points in the given section. + * + * This helps prevents the simplification from incrementally + * "skipping" over points which are in fact non-shallow. + * + * @param p0 start coordinate of section + * @param p2 end coordinate of section + * @param i0 start index of section + * @param i2 end index of section + * @param distanceTol distance tolerance + * @return + */ + bool isShallowSampled(const geom::Coordinate& p0, + const geom::Coordinate& p2, + std::size_t i0, std::size_t i2, double distanceTol) const; + + bool isShallow(const geom::Coordinate& p0, + const geom::Coordinate& p1, + const geom::Coordinate& p2, + double distanceTol) const; + + bool isConcave(const geom::Coordinate& p0, + const geom::Coordinate& p1, + const geom::Coordinate& p2) const; + + static const int NUM_PTS_TO_CHECK = 10; + + static const int INIT = 0; + static const int DELETE = 1; + static const int KEEP = 1; + + const geom::CoordinateSequence& inputLine; + double distanceTol; + std::vector isDeleted; + + int angleOrientation; + + // Declare type as noncopyable + BufferInputLineSimplifier(const BufferInputLineSimplifier& other) = delete; + BufferInputLineSimplifier& operator=(const BufferInputLineSimplifier& rhs) = delete; +}; + + +} // namespace geos.operation.buffer +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/BufferNodeFactory.h b/Sources/geos/include/geos/operation/buffer/BufferNodeFactory.h new file mode 100644 index 0000000..e2e7ab8 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferNodeFactory.h @@ -0,0 +1,52 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class Node; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace buffer { // geos::operation::buffer + +/** \brief + * Creates nodes for use in the geomgraph::PlanarGraph constructed during + * buffer operations. NOTE: also used by operation::valid + */ +class GEOS_DLL BufferNodeFactory: public geomgraph::NodeFactory { +public: + BufferNodeFactory(): geomgraph::NodeFactory() {} + geomgraph::Node* createNode(const geom::Coordinate& coord) const override; + static const geomgraph::NodeFactory& instance(); +}; + + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/BufferOp.h b/Sources/geos/include/geos/operation/buffer/BufferOp.h new file mode 100644 index 0000000..6339ca8 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferOp.h @@ -0,0 +1,307 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2011 Sandro Santilli + * Copyright (C) 2005-2007 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferOp.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include // for unique_ptr +#include // for vector + +#include +#include // for enum values +#include // to materialize Geometry + +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \class BufferOp + * + * \brief + * Computes the buffer of a geometry, for both positive and negative + * buffer distances. + * + * In GIS, the positive (or negative) buffer of a geometry is defined as + * the Minkowski sum (or difference) of the geometry + * with a circle with radius equal to the absolute value of the buffer + * distance. + * In the CAD/CAM world buffers are known as *offset curves*. + * In morphological analysis the operation of positive and negative buffering + * is referred to as *erosion* and *dilation*. + * + * The buffer operation always returns a polygonal result. + * The negative or zero-distance buffer of lines and points is always + * an empty Polygon. + * + * Since true buffer curves may contain circular arcs, + * computed buffer polygons can only be approximations to the true geometry. + * The user can control the accuracy of the curve approximation by specifying + * the number of linear segments with which to approximate a curve. + * + * The end cap style of a linear buffer may be specified. + * The following end cap styles are supported: + * - CAP_ROUND - the usual round end caps + * - CAP_BUTT - end caps are truncated flat at the line ends + * - CAP_SQUARE - end caps are squared off at the buffer distance + * beyond the line ends + * + */ +class GEOS_DLL BufferOp { + + +private: + + /** \brief + * A number of digits of precision which leaves some computational "headroom" + * for floating point operations. + * + * This value should be less than the decimal precision of double-precision values (16). + */ + static constexpr int MAX_PRECISION_DIGITS = 12; + + const geom::Geometry* argGeom; + + util::TopologyException saveException; + + double distance; + + BufferParameters bufParams; + + std::unique_ptr resultGeometry; + + bool isInvertOrientation = false; + + /** + * Compute a reasonable scale factor to limit the precision of + * a given combination of Geometry and buffer distance. + * The scale factor is based on a heuristic. + * + * @param g the Geometry being buffered + * + * @param distance the buffer distance + * + * @param maxPrecisionDigits the mzx # of digits that should be + * allowed by the precision determined by the + * computed scale factor + * + * @return a scale factor that allows a reasonable amount of + * precision for the buffer computation + */ + static double precisionScaleFactor(const geom::Geometry* g, + double distance, int maxPrecisionDigits); + + + void computeGeometry(); + + void bufferOriginalPrecision(); + + void bufferReducedPrecision(int precisionDigits); + + void bufferReducedPrecision(); + + void bufferFixedPrecision(const geom::PrecisionModel& fixedPM); + + static void extractPolygons( + geom::Geometry* poly0, + std::vector>& polys); + +public: + + enum { + /// Specifies a round line buffer end cap style. + /// @deprecated use BufferParameters + CAP_ROUND = BufferParameters::CAP_ROUND, + + /// Specifies a butt (or flat) line buffer end cap style. + /// @deprecated use BufferParameters + CAP_BUTT = BufferParameters::CAP_FLAT, + + /// Specifies a square line buffer end cap style. + /// @deprecated use BufferParameters + CAP_SQUARE = BufferParameters::CAP_SQUARE + }; + + /** \brief + * Computes the buffer for a geometry for a given buffer distance + * and accuracy of approximation. + * + * @param g the geometry to buffer + * @param distance the buffer distance + * @param quadrantSegments the number of segments used to + * approximate a quarter circle + * @param endCapStyle line buffer end cap style (default: BufferParameters::CAP_ROUND) + * @return the buffer of the input geometry + * + */ + static std::unique_ptr bufferOp( + const geom::Geometry* g, + double distance, + int quadrantSegments = BufferParameters::DEFAULT_QUADRANT_SEGMENTS, + int endCapStyle = BufferParameters::CAP_ROUND); + + static std::unique_ptr bufferOp( + const geom::Geometry* g, + double distance, + BufferParameters& bufParms); + + /** \brief + * Initializes a buffer computation for the given geometry. + * + * @param g the geometry to buffer + */ + BufferOp(const geom::Geometry* g) + : + argGeom(g), + bufParams(), + resultGeometry(nullptr), + isInvertOrientation(false) + { + } + + /** \brief + * Initializes a buffer computation for the given geometry + * with the given set of parameters. + * + * @param g the geometry to buffer + * @param params the buffer parameters to use. This class will + * copy it to private memory. + */ + BufferOp(const geom::Geometry* g, const BufferParameters& params) + : + argGeom(g), + bufParams(params), + resultGeometry(nullptr), + isInvertOrientation(false) + { + } + + /** \brief + * Specifies the end cap style of the generated buffer. + * + * The styles supported are CAP_ROUND, CAP_BUTT, and CAP_SQUARE. + * The default is CAP_ROUND. + * + * @param nEndCapStyle the end cap style to specify + */ + inline void setEndCapStyle(int nEndCapStyle); + + /** \brief + * Sets the number of segments used to approximate a angle fillet + * + * @param nQuadrantSegments the number of segments in a fillet for a quadrant + */ + inline void setQuadrantSegments(int nQuadrantSegments); + + /** \brief + * Sets whether the computed buffer should be single-sided. + * + * A single-sided buffer is constructed on only one side + * of each input line. + * + * The side used is determined by the sign of the buffer distance: + * - a positive distance indicates the left-hand side + * - a negative distance indicates the right-hand side + * + * The single-sided buffer of point geometries is + * the same as the regular buffer. + * + * The End Cap Style for single-sided buffers is + * always ignored, + * and forced to the equivalent of `CAP_FLAT`. + * + * @param isSingleSided `true` if a single-sided buffer + * should be constructed + */ + inline void setSingleSided(bool isSingleSided); + + /** \brief + * Returns the buffer computed for a geometry for a given buffer + * distance. + * + * @param nDistance the buffer distance + * @return the buffer of the input geometry + */ + std::unique_ptr getResultGeometry(double nDistance); + + /** + * Buffers a geometry with distance zero. + * The result can be computed using the maximum-signed-area orientation, + * or by combining both orientations. + * + * This can be used to fix an invalid polygonal geometry to be valid + * (i.e. with no self-intersections). + * For some uses (e.g. fixing the result of a simplification) + * a better result is produced by using only the max-area orientation. + * Other uses (e.g. fixing geometry) require both orientations to be used. + * + * This function is for INTERNAL use only. + * + * @param geom the polygonal geometry to buffer by zero + * @param isBothOrientations true if both orientations of input rings should be used + * @return the buffered polygonal geometry + */ + static std::unique_ptr bufferByZero( + const geom::Geometry* geom, + bool isBothOrientations); + +}; + +// BufferOp inlines +void +BufferOp::setQuadrantSegments(int q) +{ + bufParams.setQuadrantSegments(q); +} + +void +BufferOp::setEndCapStyle(int s) +{ + bufParams.setEndCapStyle((BufferParameters::EndCapStyle)s); +} + +void +BufferOp::setSingleSided(bool isSingleSided) +{ + bufParams.setSingleSided(isSingleSided); +} + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/BufferParameters.h b/Sources/geos/include/geos/operation/buffer/BufferParameters.h new file mode 100644 index 0000000..9c83c32 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferParameters.h @@ -0,0 +1,307 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferParameters.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +//#include + +//#include // for composition +//#include // for composition +//#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class PrecisionModel; +} +namespace operation { +namespace buffer { +class OffsetCurveVertexList; +} +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** \brief + * Contains the parameters which + * describe how a buffer should be constructed. + * + */ +class GEOS_DLL BufferParameters { + +public: + + /// End cap styles + enum EndCapStyle { + + /// Specifies a round line buffer end cap style. + CAP_ROUND = 1, + + /// Specifies a flat line buffer end cap style. + CAP_FLAT = 2, + + /// Specifies a square line buffer end cap style. + CAP_SQUARE = 3 + }; + + /// Join styles + enum JoinStyle { + + /// Specifies a round join style. + JOIN_ROUND = 1, + + /// Specifies a mitre join style. + JOIN_MITRE = 2, + + /// Specifies a bevel join style. + JOIN_BEVEL = 3 + }; + + /// \brief + /// The default number of facets into which to divide a fillet + /// of 90 degrees. + /// + /// A value of 8 gives less than 2% max error in the buffer distance. + /// For a max error of < 1%, use QS = 12. + /// For a max error of < 0.1%, use QS = 18. + /// + static const int DEFAULT_QUADRANT_SEGMENTS = 8; + + /// The default mitre limit + /// + /// Allows fairly pointy mitres. + /// + static const double DEFAULT_MITRE_LIMIT; // 5.0 (in .cpp file) + + /// Creates a default set of parameters + BufferParameters(); + + /// Creates a set of parameters with the given quadrantSegments value. + /// + /// @param quadrantSegments the number of quadrant segments to use + /// + BufferParameters(int quadrantSegments); + + /// \brief + /// Creates a set of parameters with the + /// given quadrantSegments and endCapStyle values. + /// + /// @param quadrantSegments the number of quadrant segments to use + /// @param endCapStyle the end cap style to use + /// + BufferParameters(int quadrantSegments, EndCapStyle endCapStyle); + + /// \brief + /// Creates a set of parameters with the + /// given parameter values. + /// + /// @param quadrantSegments the number of quadrant segments to use + /// @param endCapStyle the end cap style to use + /// @param joinStyle the join style to use + /// @param mitreLimit the mitre limit to use + /// + BufferParameters(int quadrantSegments, EndCapStyle endCapStyle, + JoinStyle joinStyle, double mitreLimit); + + /// Gets the number of quadrant segments which will be used + /// + /// @return the number of quadrant segments + /// + int + getQuadrantSegments() const + { + return quadrantSegments; + } + + /// \brief + /// Sets the number of line segments used to approximate + /// an angle fillet in round joins. + /// + /// quadSegs determines the maximum + /// error in the approximation to the true buffer curve. + /// The default value of 8 gives less than 2% max error in the + /// buffer distance. + /// For a max error of < 1%, use QS = 12. + /// For a max error of < 0.1%, use QS = 18. + /// The error is always less than the buffer distance + /// (in other words, the computed buffer curve is always inside + /// the true curve). + /// + /// @param quadSegs the number of segments in a fillet for a quadrant + void setQuadrantSegments(int quadSegs); + + /// \brief + /// Computes the maximum distance error due to a given level + /// of approximation to a true arc. + /// + /// @param quadSegs the number of segments used to approximate + /// a quarter-circle + /// @return the error of approximation + /// + static double bufferDistanceError(int quadSegs); + + /// Gets the end cap style. + /// + /// @return the end cap style + /// + EndCapStyle + getEndCapStyle() const + { + return endCapStyle; + } + + /// Specifies the end cap style of the generated buffer. + /// + /// The styles supported are CAP_ROUND, CAP_BUTT, + /// and CAP_SQUARE. + /// + /// The default is CAP_ROUND. + /// + /// @param style the end cap style to specify + /// + void + setEndCapStyle(EndCapStyle style) + { + endCapStyle = style; + } + + /// Gets the join style. + /// + /// @return the join style + /// + JoinStyle + getJoinStyle() const + { + return joinStyle; + } + + /// \brief + /// Sets the join style for outside (reflex) corners between + /// line segments. + /// + /// Allowable values are JOIN_ROUND (which is the default), + /// JOIN_MITRE and JOIN_BEVEL. + /// + /// @param style the code for the join style + /// + void + setJoinStyle(JoinStyle style) + { + joinStyle = style; + } + + /// Gets the mitre ratio limit. + /// + /// @return the limit value + /// + double + getMitreLimit() const + { + return mitreLimit; + } + + /// Sets the limit on the mitre ratio used for very sharp corners. + /// + /// The mitre ratio is the ratio of the distance from the corner + /// to the end of the mitred offset corner. + /// When two line segments meet at a sharp angle, + /// a miter join will extend far beyond the original geometry. + /// (and in the extreme case will be infinitely far.) + /// To prevent unreasonable geometry, the mitre limit + /// allows controlling the maximum length of the join corner. + /// Corners with a ratio which exceed the limit will be beveled. + /// + /// @param limit the mitre ratio limit + /// + void + setMitreLimit(double limit) + { + mitreLimit = limit; + } + + /** + * Sets whether the computed buffer should be single-sided. + * A single-sided buffer is constructed on only one side of each input line. + * + * The side used is determined by the sign of the buffer distance: + * - a positive distance indicates the left-hand side + * - a negative distance indicates the right-hand side + * + * The single-sided buffer of point geometries is + * the same as the regular buffer. + * + * The End Cap Style for single-sided buffers is + * always ignored, + * and forced to the equivalent of CAP_FLAT. + * + * @param p_isSingleSided true if a single-sided buffer should be constructed + */ + void + setSingleSided(bool p_isSingleSided) + { + _isSingleSided = p_isSingleSided; + } + + /** + * Tests whether the buffer is to be generated on a single side only. + * + * @return true if the generated buffer is to be single-sided + */ + bool + isSingleSided() const + { + return _isSingleSided; + } + + +private: + + /// Defaults to DEFAULT_QUADRANT_SEGMENTS; + int quadrantSegments; + + /// Defaults to CAP_ROUND; + EndCapStyle endCapStyle; + + /// Defaults to JOIN_ROUND; + JoinStyle joinStyle; + + /// Defaults to DEFAULT_MITRE_LIMIT; + double mitreLimit; + + bool _isSingleSided; +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/BufferSubgraph.h b/Sources/geos/include/geos/operation/buffer/BufferSubgraph.h new file mode 100644 index 0000000..8467165 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/BufferSubgraph.h @@ -0,0 +1,202 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/BufferSubgraph.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +} +namespace algorithm { +class CGAlgorithms; +} +namespace geomgraph { +class DirectedEdge; +class Node; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \brief + * A connected subset of the graph of DirectedEdge and geomgraph::Node. + * + * Its edges will generate either + * - a single polygon in the complete buffer, with zero or more holes, or + * - ne or more connected holes + */ +class GEOS_DLL BufferSubgraph { +private: + RightmostEdgeFinder finder; + + std::vector dirEdgeList; + + std::vector nodes; + + geom::Coordinate* rightMostCoord; + + geom::Envelope* env; + + /** \brief + * Adds all nodes and edges reachable from this node to the subgraph. + * + * Uses an explicit stack to avoid a large depth of recursion. + * + * @param startNode a node known to be in the subgraph + */ + void addReachable(geomgraph::Node* startNode); + + /// Adds the argument node and all its out edges to the subgraph + /// + /// @param node the node to add + /// @param nodeStack the current set of nodes being traversed + /// + void add(geomgraph::Node* node, std::vector* nodeStack); + + void clearVisitedEdges(); + + /** \brief + * Compute depths for all dirEdges via breadth-first traversal + * of nodes in graph + * + * @param startEdge edge to start processing with + */ + // MD - use iteration & queue rather than recursion, for speed and robustness + void computeDepths(geomgraph::DirectedEdge* startEdge); + + void computeNodeDepth(geomgraph::Node* n); + + void copySymDepths(geomgraph::DirectedEdge* de); + + bool contains(std::set& nodes, geomgraph::Node* node); + +public: + + friend std::ostream& operator<< (std::ostream& os, const BufferSubgraph& bs); + + BufferSubgraph(); + + ~BufferSubgraph(); + + std::vector* getDirectedEdges(); + + std::vector* getNodes(); + + /** \brief + * Gets the rightmost coordinate in the edges of the subgraph + */ + geom::Coordinate* getRightmostCoordinate(); + + /** \brief + * Creates the subgraph consisting of all edges reachable from + * this node. + * + * Finds the edges in the graph and the rightmost coordinate. + * + * @param node a node to start the graph traversal from + */ + void create(geomgraph::Node* node); + + void computeDepth(int outsideDepth); + + /** \brief + * Find all edges whose depths indicates that they are in the + * result area(s). + * + * Since we want polygon shells to be + * oriented CW, choose dirEdges with the interior of the result + * on the RHS. + * Mark them as being in the result. + * Interior Area edges are the result of dimensional collapses. + * They do not form part of the result area boundary. + */ + void findResultEdges(); + + /** \brief + * BufferSubgraphs are compared on the x-value of their rightmost + * Coordinate. + * + * This defines a partial ordering on the graphs such that: + * + * g1 >= g2 <==> Ring(g2) does not contain Ring(g1) + * + * where Polygon(g) is the buffer polygon that is built from g. + * + * This relationship is used to sort the BufferSubgraphs so + * that shells are guaranteed to + * be built before holes. + */ + int compareTo(BufferSubgraph*); + + /** \brief + * Computes the envelope of the edges in the subgraph. + * The envelope is cached after being computed. + * + * @return the envelope of the graph. + */ + geom::Envelope* getEnvelope(); +}; + +std::ostream& operator<< (std::ostream& os, const BufferSubgraph& bs); + +// INLINES +inline geom::Coordinate* +BufferSubgraph::getRightmostCoordinate() +{ + return rightMostCoord; +} + +inline std::vector* +BufferSubgraph::getNodes() +{ + return &nodes; +} + +inline std::vector* +BufferSubgraph::getDirectedEdges() +{ + return &dirEdgeList; +} + +bool BufferSubgraphGT(BufferSubgraph* first, BufferSubgraph* second); + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/MaximalEdgeRing.h b/Sources/geos/include/geos/operation/buffer/MaximalEdgeRing.h new file mode 100644 index 0000000..4ecd6a0 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/MaximalEdgeRing.h @@ -0,0 +1,104 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/overlay/MaximalEdgeRing.java rev. 1.15 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +} +namespace geomgraph { +class DirectedEdge; +} +namespace operation { +namespace buffer { +class MinimalEdgeRing; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace buffer { // geos::operation::buffer + +/** \brief + * A ring of [DirectedEdges](@ref geomgraph::DirectedEdge) which may contain nodes of degree > 2. + * + * A MaximalEdgeRing may represent two different spatial entities: + * + * - a single polygon possibly containing inversions (if the ring is oriented CW) + * - a single hole possibly containing exversions (if the ring is oriented CCW) + * + * If the MaximalEdgeRing represents a polygon, + * the interior of the polygon is strongly connected. + * + * These are the form of rings used to define polygons under some spatial data models. + * However, under the OGC SFS model, [MinimalEdgeRings](@ref MinimalEdgeRing) are required. + * A MaximalEdgeRing can be converted to a list of MinimalEdgeRings using the + * {@link #buildMinimalRings() } method. + * + * @see com.vividsolutions.jts.operation.overlay.MinimalEdgeRing + */ +class GEOS_DLL MaximalEdgeRing: public geomgraph::EdgeRing { + +public: + + MaximalEdgeRing(geomgraph::DirectedEdge* start, + const geom::GeometryFactory* geometryFactory); + // throw(const TopologyException &) + + ~MaximalEdgeRing() override = default; + + geomgraph::DirectedEdge* getNext(geomgraph::DirectedEdge* de) override; + + void setEdgeRing(geomgraph::DirectedEdge* de, geomgraph::EdgeRing* er) override; + + /// \brief + /// This function returns a newly allocated vector of + /// pointers to newly allocated MinimalEdgeRing objects. + /// + /// @deprecated pass the vector yourself instead + /// + std::vector* buildMinimalRings(); + + /// \brief + /// This function pushes pointers to newly allocated MinimalEdgeRing + /// objects to the provided vector. + /// + void buildMinimalRings(std::vector& minEdgeRings); + void buildMinimalRings(std::vector& minEdgeRings); + + /// \brief + /// For all nodes in this EdgeRing, + /// link the DirectedEdges at the node to form minimalEdgeRings + /// + void linkDirectedEdgesForMinimalEdgeRings(); +}; + + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/MinimalEdgeRing.h b/Sources/geos/include/geos/operation/buffer/MinimalEdgeRing.h new file mode 100644 index 0000000..0b4b5c0 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/MinimalEdgeRing.h @@ -0,0 +1,80 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/overlay/MinimalEdgeRing.java rev. 1.13 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + + +#include + +#include // for inheritance +#include // for inlines + +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +} +namespace geomgraph { +class DirectedEdge; +class EdgeRing; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace buffer { // geos::operation::buffer + +/** \brief + * A ring of [Edges](@ref geomgraph::Edge) with the property that no node + * has degree greater than 2. + * + * These are the form of rings required to represent polygons + * under the OGC SFS spatial data model. + * + * @see operation::buffer::MaximalEdgeRing + * + */ +class GEOS_DLL MinimalEdgeRing: public geomgraph::EdgeRing { + +public: + + MinimalEdgeRing(geomgraph::DirectedEdge* start, + const geom::GeometryFactory* geometryFactory); + + ~MinimalEdgeRing() override {}; + + geomgraph::DirectedEdge* getNext(geomgraph::DirectedEdge* de) override + { + return de->getNextMin(); + }; + + void setEdgeRing(geomgraph::DirectedEdge* de, + geomgraph::EdgeRing* er) override + { + de->setMinEdgeRing(er); + }; + +}; + + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/OffsetCurve.h b/Sources/geos/include/geos/operation/buffer/OffsetCurve.h new file mode 100644 index 0000000..a6fd79c --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/OffsetCurve.h @@ -0,0 +1,329 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class LineString; +class Polygon; +} +namespace operation { +namespace buffer { +class OffsetCurveSection; +class SegmentMCIndex; +} +} +} + +namespace geos { +namespace operation { +namespace buffer { + +/** + * Computes an offset curve from a geometry. + * An offset curve is a linear geometry which is offset a given distance + * from the input. + * If the offset distance is positive the curve lies on the left side of the input; + * if it is negative the curve is on the right side. + * The curve(s) have the same direction as the input line(s). + * + * The offset curve is based on the boundary of the buffer for the geometry + * at the offset distance (see BufferOp). + * The normal mode of operation is to return the sections of the buffer boundary + * which lie on the raw offset curve + * (obtained via rawOffset(LineString, double). + * The offset curve will contain multiple sections + * if the input self-intersects or has close approaches. + * The computed sections are ordered along the raw offset curve. + * Sections are disjoint. They never self-intersect, but may be rings. + * + * * For a LineString the offset curve is a linear geometry + * (LineString or MultiLineString). + * * For a Point or MultiPoint the offset curve is an empty LineString. + * * For a Polygon the offset curve is the boundary of the polygon buffer (which + * may be a MultiLineString. + * * For a collection the output is a MultiLineString containing + * the offset curves of the elements. + * + * In "joined" mode (see setJoined(bool)) + * the sections computed for each input line are joined into a single offset curve line. + * The joined curve may self-intersect. + * At larger offset distances the curve may contain "flat-line" artifacts + * in places where the input self-intersects. + * + * Offset curves support setting the number of quadrant segments, + * the join style, and the mitre limit (if applicable) via + * the BufferParameters. + * + * @author Martin Davis + * + */ +class GEOS_DLL OffsetCurve { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineString = geos::geom::LineString; + using Polygon = geos::geom::Polygon; + +private: + + // Members + const Geometry& inputGeom; + double distance; + bool isJoined = false; + + BufferParameters bufferParams; + double matchDistance; + const GeometryFactory* geomFactory; + + // Methods + + std::unique_ptr computePolygonCurve( + const Polygon& polyGeom, double distance); + + std::unique_ptr computeCurve( + const LineString& lineGeom, double distance); + + std::vector> computeSections( + const LineString& lineGeom, double distance); + + std::unique_ptr offsetSegment( + const CoordinateSequence* pts, double distance); + + static std::unique_ptr getBufferOriented( + const LineString& geom, double distance, + BufferParameters& bufParams); + + /** + * Extracts the largest polygon by area from a geometry. + * Used here to avoid issues with non-robust buffer results + * which have spurious extra polygons. + * + * @param geom a geometry + * @return the polygon element of largest area + */ + static const Polygon* extractMaxAreaPolygon(const Geometry* geom); + + void computeCurveSections( + const CoordinateSequence* bufferRingPts, + const CoordinateSequence& rawCurve, + std::vector>& sections); + + /** + * Matches the segments in a buffer ring to the raw offset curve + * to obtain their match positions (if any). + * + * @param raw0 a raw curve segment start point + * @param raw1 a raw curve segment end point + * @param rawCurveIndex the index of the raw curve segment + * @param bufferSegIndex the spatial index of the buffer ring segments + * @param bufferPts the points of the buffer ring + * @param rawCurvePos the raw curve positions of the buffer ring segments + * @return the index of the minimum matched buffer segment + */ + std::size_t matchSegments( + const Coordinate& raw0, const Coordinate& raw1, + std::size_t rawCurveIndex, + SegmentMCIndex& bufferSegIndex, + const CoordinateSequence* bufferPts, + std::vector& rawCurvePos); + + static double segmentMatchFrac( + const Coordinate& p0, const Coordinate& p1, + const Coordinate& seg0, const Coordinate& seg1, + double matchDistance); + + /** + * This is only called when there is at least one ring segment matched + * (so rawCurvePos has at least one entry != NOT_IN_CURVE). + * The start index of the first section must be provided. + * This is intended to be the section with lowest position + * along the raw curve. + * @param ringPts the points in a buffer ring + * @param rawCurveLoc the position of buffer ring segments along the raw curve + * @param startIndex the index of the start of a section + * @param sections the list of extracted offset curve sections + */ + void extractSections( + const CoordinateSequence* ringPts, + std::vector& rawCurveLoc, + std::size_t startIndex, + std::vector>& sections); + + std::size_t findSectionStart( + const std::vector& loc, + std::size_t end); + + std::size_t findSectionEnd( + const std::vector& loc, + std::size_t start, + std::size_t firstStartIndex); + + static std::size_t nextIndex(std::size_t i, std::size_t size); + static std::size_t prevIndex(std::size_t i, std::size_t size); + + +public: + + // Constants + static constexpr int MATCH_DISTANCE_FACTOR = 10000; + + /** + * A QuadSegs minimum value that will prevent generating + * unwanted offset curve artifacts near end caps. + */ + static constexpr int MIN_QUADRANT_SEGMENTS = 8; + + /** + * Creates a new instance for computing an offset curve for a geometry at a given distance. + * with default quadrant segments (BufferParameters::DEFAULT_QUADRANT_SEGMENTS) + * and join style (BufferParameters::JOIN_STYLE). + * + * @param geom the geometry to offset + * @param dist the offset distance (positive for left, negative for right) + * + * @see BufferParameters + */ + OffsetCurve(const Geometry& geom, double dist) + : inputGeom(geom) + , distance(dist) + , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) + , geomFactory(geom.getFactory()) + { + if (!std::isfinite(dist)) { + throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); + } + }; + + /** + * Creates a new instance for computing an offset curve for a geometry at a given distance. + * setting the quadrant segments and join style and mitre limit + * via {@link BufferParameters}. + * + * @param geom the geometry to offset + * @param dist the offset distance (positive for left, negative for right) + * @param bp the buffer parameters to use + */ + OffsetCurve(const Geometry& geom, double dist, BufferParameters& bp) + : inputGeom(geom) + , distance(dist) + , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) + , geomFactory(geom.getFactory()) + { + if (!std::isfinite(dist)) { + throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); + } + //-- set buffer params, leaving cap style as the default CAP_ROUND + + /** + * Prevent using a very small QuadSegs value, to avoid + * offset curve artifacts near the end caps. + */ + int quadSegs = bp.getQuadrantSegments(); + if (quadSegs < MIN_QUADRANT_SEGMENTS) { + quadSegs = MIN_QUADRANT_SEGMENTS; + } + bufferParams.setQuadrantSegments(quadSegs); + + bufferParams.setJoinStyle( bp.getJoinStyle()); + bufferParams.setMitreLimit( bp.getMitreLimit()); + }; + + /** + * Computes a single curve line for each input linear component, + * by joining curve sections in order along the raw offset curve. + * The default mode is to compute separate curve sections. + * + * @param pIsJoined true if joined mode should be used. + */ + void setJoined(bool pIsJoined); + + static std::unique_ptr getCurve( + const Geometry& geom, + double dist, + int quadSegs, + BufferParameters::JoinStyle joinStyle, + double mitreLimit); + + static std::unique_ptr getCurve( + const Geometry& geom, double dist); + + /** + * Computes the offset curve of a geometry at a given distance, + * joining curve sections into a single line for each input line. + * + * @param geom a geometry + * @param dist the offset distance (positive for left, negative for right) + * @return the joined offset curve + */ + static std::unique_ptr getCurveJoined( + const Geometry& geom, double dist); + + /** + * Gets the computed offset curve lines. + * + * @return the offset curve geometry + */ + std::unique_ptr getCurve(); + + /** + * Gets the raw offset curve for a line at a given distance. + * The quadrant segments, join style and mitre limit can be specified + * via BufferParameters. + * + * The raw offset line may contain loops and other artifacts which are + * not present in the true offset curve. + * + * @param line the line to offset + * @param distance the offset distance (positive for left, negative for right) + * @param bufParams the buffer parameters to use + * @return the raw offset curve points + */ + static std::unique_ptr rawOffsetCurve( + const LineString& line, + double distance, + BufferParameters& bufParams); + + /** + * Gets the raw offset curve for a line at a given distance, + * with default buffer parameters. + * + * @param line the line to offset + * @param distance the offset distance (positive for left, negative for right) + * @return the raw offset curve points + */ + static std::unique_ptr rawOffset( + const LineString& line, + double distance); + +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + + + diff --git a/Sources/geos/include/geos/operation/buffer/OffsetCurveBuilder.h b/Sources/geos/include/geos/operation/buffer/OffsetCurveBuilder.h new file mode 100644 index 0000000..1f6cddf --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/OffsetCurveBuilder.h @@ -0,0 +1,257 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2011 Sandro Santilli + * Copyright (C) 2006-2007 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/OffsetCurveBuilder.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include + +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class PrecisionModel; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \class OffsetCurveBuilder + * + * \brief + * Computes the raw offset curve for a + * single Geometry component (ring, line or point). + * + * A raw offset curve line is not noded - + * it may contain self-intersections (and usually will). + * The final buffer polygon is computed by forming a topological graph + * of all the noded raw curves and tracing outside contours. + * The points in the raw curve are rounded to a given PrecisionModel. + * + * Note: this may not produce correct results if the input + * contains repeated or invalid points. + * Repeated points should be removed before calling. + * See removeRepeatedAndInvalidPoints. + */ +class GEOS_DLL OffsetCurveBuilder { + using CoordinateSequence = geos::geom::CoordinateSequence; + using PrecisionModel = geos::geom::PrecisionModel; + +public: + + /* + * @param nBufParams buffer parameters, this object will + * keep a reference to the passed parameters + * so caller must make sure the object is + * kept alive for the whole lifetime of + * the buffer builder. + */ + OffsetCurveBuilder( + const PrecisionModel* newPrecisionModel, + const BufferParameters& nBufParams) + : distance(0.0) + , precisionModel(newPrecisionModel) + , bufParams(nBufParams) + {} + + /** \brief + * Gets the buffer parameters being used to generate the curve. + * + * @return the buffer parameters being used + */ + const BufferParameters& + getBufferParameters() const + { + return bufParams; + } + + /** + * Tests whether the offset curve for line or point geometries + * at the given offset distance is empty (does not exist). + * This is the case if: + * + * * the distance is zero, + * * the distance is negative, except for the case of singled-sided buffers + * + * @param distance the offset curve distance + * @return true if the offset curve is empty + */ + bool isLineOffsetEmpty(double distance); + + /** \brief + * This method handles single points as well as lines. + * + * Lines are assumed to **not** be closed (the function will not + * fail for closed lines, but will generate superfluous line caps). + * + * @param inputPts input points + * @param distance offset distance + * @param lineList the std::vector to which the newly created + * CoordinateSequences will be pushed_back. + * Caller is responsible to delete these new elements. + */ + void getLineCurve(const CoordinateSequence* inputPts, + double distance, + std::vector& lineList); + + /** + * This method handles single points as well as LineStrings. + * LineStrings are assumed not to be closed (the function will not + * fail for closed lines, but will generate superfluous line caps). + * + * @param inputPts the vertices of the line to offset + * @param pDistance the offset distance + * + * @return a Coordinate array representing the curve + * or null if the curve is empty + */ + std::unique_ptr getLineCurve( + const CoordinateSequence* inputPts, double pDistance); + + /** \brief + * This method handles single points as well as lines. + * + * Lines are assumed to **not** be closed (the function will not + * fail for closed lines, but will generate superfluous line caps). + * + * @param inputPts input points + * @param distance offset distance + * @param lineList the std::vector to which newly created + * CoordinateSequences will be pushed_back. + * Caller will be responsible to delete them. + * @param leftSide indicates that the left side buffer will be + * obtained/skipped + * @param rightSide indicates that the right side buffer will + * be obtained/skipped + * + * @note This is a GEOS extension. + */ + void getSingleSidedLineCurve(const CoordinateSequence* inputPts, + double distance, std::vector& lineList, + bool leftSide, bool rightSide) ; + + /** \brief + * This method handles the degenerate cases of single points and lines, + * as well as rings. + * + * @param inputPts input points + * @param side a [Position](@ref geom::Position) + * @param distance offset distance + * @param lineList the std::vector to which CoordinateSequences will + * be pushed_back + */ + void getRingCurve(const CoordinateSequence* inputPts, int side, + double distance, + std::vector& lineList); + + /** + * This method handles the degenerate cases of single points and lines, + * as well as valid rings. + * + * @param inputPts the coordinates of the ring (must not contain repeated points) + * @param side side the side Position of the ring on which to construct the buffer line + * @param pDistance the positive distance at which to create the offset + * @return a Coordinate array representing the curve, + * or null if the curve is empty + */ + std::unique_ptr getRingCurve( + const CoordinateSequence* inputPts, + int side, double pDistance); + + void getOffsetCurve(const CoordinateSequence* inputPts, + double p_distance, + std::vector& lineList); + + std::unique_ptr getOffsetCurve( + const CoordinateSequence* inputPts, + double pDistance); + + +private: + + double distance; + + const PrecisionModel* precisionModel; + + const BufferParameters& bufParams; + + /** \brief + * Use a value which results in a potential distance error which is + * significantly less than the error due to the quadrant segment discretization. + * + * For QS = 8 a value of 100 is reasonable. + * This should produce a maximum of 1% distance error. + */ + static const double SIMPLIFY_FACTOR; // 100.0; + + /** \brief + * Computes the distance tolerance to use during input + * line simplification. + * + * @param bufDistance the buffer distance + * @return the simplification tolerance + */ + double simplifyTolerance(double bufDistance); + + void computeLineBufferCurve(const CoordinateSequence& inputPts, + OffsetSegmentGenerator& segGen); + + void computeSingleSidedBufferCurve(const CoordinateSequence& inputPts, + bool isRightSide, + OffsetSegmentGenerator& segGen); + + void computeRingBufferCurve(const CoordinateSequence& inputPts, + int side, OffsetSegmentGenerator& segGen); + + void computePointCurve(const geom::Coordinate& pt, + OffsetSegmentGenerator& segGen); + + void computeOffsetCurve( + const CoordinateSequence* inputPts, + bool isRightSide, + OffsetSegmentGenerator& segGen); + + + + // Declare type as noncopyable + OffsetCurveBuilder(const OffsetCurveBuilder& other) = delete; + OffsetCurveBuilder& operator=(const OffsetCurveBuilder& rhs) = delete; +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/OffsetCurveSection.h b/Sources/geos/include/geos/operation/buffer/OffsetCurveSection.h new file mode 100644 index 0000000..2f953b8 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/OffsetCurveSection.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2021 Martin Davis + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // to materialize CoordinateSequence +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class GeometryFactory; +class LineString; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * Models a section of a raw offset curve, + * starting at a given location along the raw curve. + * The location is a decimal number, with the integer part + * containing the segment index and the fractional part + * giving the fractional distance along the segment. + * The location of the last section segment + * is also kept, to allow optimizing joining sections together. + * + * @author mdavis + */ +class GEOS_DLL OffsetCurveSection { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LineString = geos::geom::LineString; + +private: + + std::unique_ptr sectionPts; + double location; + double locLast; + + bool isEndInSameSegment(double nextLoc) const; + + +public: + + OffsetCurveSection(std::unique_ptr && secPts, double pLoc, double pLocLast) + : sectionPts(std::move(secPts)) + , location(pLoc) + , locLast(pLocLast) + {}; + + const CoordinateSequence* getCoordinates() const; + std::unique_ptr releaseCoordinates(); + + double getLocation() const { return location; }; + + /** + * Joins section coordinates into a LineString. + * Join vertices which lie in the same raw curve segment + * are removed, to simplify the result linework. + * + * @param sections the sections to join + * @param geomFactory the geometry factory to use + * @return the simplified linestring for the joined sections + */ + static std::unique_ptr toLine( + std::vector>& sections, + const GeometryFactory* geomFactory); + + static std::unique_ptr toGeometry( + std::vector>& sections, + const GeometryFactory* geomFactory); + + static std::unique_ptr create( + const CoordinateSequence* srcPts, + std::size_t start, std::size_t end, + double loc, double locLast); + + static bool OffsetCurveSectionComparator( + const std::unique_ptr& a, + const std::unique_ptr& b); + +}; + + +} // namespace geos.operation.buffer +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/OffsetSegmentGenerator.h b/Sources/geos/include/geos/operation/buffer/OffsetSegmentGenerator.h new file mode 100644 index 0000000..2f3a369 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/OffsetSegmentGenerator.h @@ -0,0 +1,390 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/OffsetSegmentGenerator.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for composition +#include // for composition +#include // for composition +#include // for composition +#include // for composition + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class PrecisionModel; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * Generates segments which form an offset curve. + * Supports all end cap and join options + * provided for buffering. + * Implements various heuristics to + * produce smoother, simpler curves which are + * still within a reasonable tolerance of the + * true curve. + * + * @author Martin Davis + * + */ +class GEOS_DLL OffsetSegmentGenerator { + +public: + + /* + * @param nBufParams buffer parameters, this object will + * keep a reference to the passed parameters + * so caller must make sure the object is + * kept alive for the whole lifetime of + * the buffer builder. + */ + OffsetSegmentGenerator(const geom::PrecisionModel* newPrecisionModel, + const BufferParameters& bufParams, double distance); + + /** + * Tests whether the input has a narrow concave angle + * (relative to the offset distance). + * In this case the generated offset curve will contain self-intersections + * and heuristic closing segments. + * This is expected behaviour in the case of buffer curves. + * For pure offset curves, + * the output needs to be further treated + * before it can be used. + * + * @return true if the input has a narrow concave angle + */ + bool + hasNarrowConcaveAngle() const + { + return _hasNarrowConcaveAngle; + } + + void initSideSegments(const geom::Coordinate& nS1, + const geom::Coordinate& nS2, int nSide); + + /// Get coordinates by taking ownership of them + /// + /// After this call, the coordinates reference in + /// this object are dropped. Calling twice will + /// segfault... + /// + /// FIXME: refactor memory management of this + /// + void + getCoordinates(std::vector& to) + { + to.push_back(segList.getCoordinates()); + } + + std::unique_ptr + getCoordinates() + { + return std::unique_ptr(segList.getCoordinates()); + } + + void + closeRing() + { + segList.closeRing(); + } + + /// Adds a CW circle around a point + void createCircle(const geom::Coordinate& p, double distance); + + /// Adds a CW square around a point + void createSquare(const geom::Coordinate& p, double distance); + + /// Add first offset point + void + addFirstSegment() + { + segList.addPt(offset1.p0); + } + + /// Add last offset point + void + addLastSegment() + { + segList.addPt(offset1.p1); + } + + void addNextSegment(const geom::Coordinate& p, bool addStartPoint); + + /// \brief + /// Add an end cap around point p1, terminating a line segment + /// coming from p0 + void addLineEndCap(const geom::Coordinate& p0, + const geom::Coordinate& p1); + + void + addSegments(const geom::CoordinateSequence& pts, bool isForward) + { + segList.addPts(pts, isForward); + } + + /** \brief + * Compute an offset segment for an input segment on a given + * side and at a given distance. + * + * The offset points are computed in full double precision, + * for accuracy. + * + * @param seg the segment to offset + * @param side the side of the segment the offset lies on + * @param distance the offset distance + * @param offset the points computed for the offset segment + */ + static void computeOffsetSegment(const geom::LineSegment& seg, + int side, double distance, + geom::LineSegment& offset); + +private: + + /** + * Factor which controls how close offset segments can be to + * skip adding a filler or mitre. + */ + static const double OFFSET_SEGMENT_SEPARATION_FACTOR; // 1.0E-3; + + /** + * Factor which controls how close curve vertices on inside turns + * can be to be snapped + */ + static const double INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR; // 1.0E-3; + + /** + * Factor which controls how close curve vertices can be to be snapped + */ + static const double CURVE_VERTEX_SNAP_DISTANCE_FACTOR; // 1.0E-6; + + /** + * Factor which determines how short closing segs can be for round buffers + */ + static const int MAX_CLOSING_SEG_LEN_FACTOR = 80; + + /** \brief + * the max error of approximation (distance) between a quad segment and + * the true fillet curve + */ + double maxCurveSegmentError; // 0.0 + + /** \brief + * The angle quantum with which to approximate a fillet curve + * (based on the input # of quadrant segments) + */ + double filletAngleQuantum; + + /// The Closing Segment Factor controls how long "closing + /// segments" are. Closing segments are added at the middle of + /// inside corners to ensure a smoother boundary for the buffer + /// offset curve. In some cases (particularly for round joins + /// with default-or-better quantization) the closing segments + /// can be made quite short. This substantially improves + /// performance (due to fewer intersections being created). + /// + /// A closingSegFactor of 0 results in lines to the corner vertex. + /// A closingSegFactor of 1 results in lines halfway + /// to the corner vertex. + /// A closingSegFactor of 80 results in lines 1/81 of the way + /// to the corner vertex (this option is reasonable for the very + /// common default situation of round joins and quadrantSegs >= 8). + /// + /// The default is 1. + /// + int closingSegLengthFactor; // 1; + + /// Owned by this object, destroyed by dtor + /// + /// This actually gets created multiple times + /// and each of the old versions is pushed + /// to the ptLists std::vector to ensure all + /// created CoordinateSequences are properly + /// destroyed. + /// + OffsetSegmentString segList; + + double distance; + + const geom::PrecisionModel* precisionModel; + + const BufferParameters& bufParams; + + algorithm::LineIntersector li; + + geom::Coordinate s0, s1, s2; + + geom::LineSegment seg0; + + geom::LineSegment seg1; + + geom::LineSegment offset0; + + geom::LineSegment offset1; + + int side; + + bool _hasNarrowConcaveAngle; // =false + + void addCollinear(bool addStartPoint); + + /// The mitre will be beveled if it exceeds the mitre ratio limit. + /// + /// @param offset0 the first offset segment + /// @param offset1 the second offset segment + /// @param distance the offset distance + /// + void addMitreJoin(const geom::Coordinate& cornerPt, + const geom::LineSegment& offset0, + const geom::LineSegment& offset1, + double distance); + + + /// Adds a limited mitre join connecting two convex offset segments. + /// A limited mitre join is beveled at the distance + /// determined by the mitre limit factor, + /// or as a standard bevel join, whichever is further. + /// + /// @param offset0 the first offset segment + /// @param offset1 the second offset segment + /// @param distance the offset distance + /// @param mitreLimitDistance the mitre limit ratio + /// + void addLimitedMitreJoin( + const geom::LineSegment& offset0, + const geom::LineSegment& offset1, + double distance, + double mitreLimitDistance); + + /** + * Extends a line segment forwards or backwards a given distance. + * + * @param seg the base line segment + * @param dist the distance to extend by + * @return the extended segment + */ + static geom::LineSegment extend(const geom::LineSegment& seg, double dist); + + /** + * Adds a bevel join connecting the two offset segments + * around a reflex corner. + * Projects a point to a given distance in a given direction angle. + * + * @param pt the point to project + * @param d the projection distance + * @param dir the direction angle (in radians) + * @return the projected point + */ + static geom::Coordinate project(const geom::Coordinate& pt, double d, double dir); + + /// \brief + /// Adds a bevel join connecting the two offset segments + /// around a reflex corner. + /// + /// @param offset0 the first offset segment + /// @param offset1 the second offset segment + /// + void addBevelJoin(const geom::LineSegment& offset0, + const geom::LineSegment& offset1); + + static const double PI; // 3.14159265358979 + + // Not in JTS, used for single-sided buffers + int endCapIndex; + + void init(double newDistance); + + /** + * Use a value which results in a potential distance error which is + * significantly less than the error due to + * the quadrant segment discretization. + * For QS = 8 a value of 100 is reasonable. + * This should produce a maximum of 1% distance error. + */ + static const double SIMPLIFY_FACTOR; // 100.0; + + /// Adds the offset points for an outside (convex) turn + /// + /// @param orientation + /// @param addStartPoint + /// + void addOutsideTurn(int orientation, bool addStartPoint); + + /// Adds the offset points for an inside (concave) turn + /// + /// @param orientation + /// @param addStartPoint + /// + void addInsideTurn(int orientation, bool addStartPoint); + + /** + * Adds points for a circular fillet around a reflex corner. + * + * Adds the start and end points + * + * @param p base point of curve + * @param p0 start point of fillet curve + * @param p1 endpoint of fillet curve + * @param direction the orientation of the fillet + * @param radius the radius of the fillet + */ + void addDirectedFillet(const geom::Coordinate& p, const geom::Coordinate& p0, + const geom::Coordinate& p1, + int direction, double radius); + + /** + * Adds points for a circular fillet arc between two specified angles. + * + * The start and end point for the fillet are not added - + * the caller must add them if required. + * + * @param direction is -1 for a CW angle, 1 for a CCW angle + * @param radius the radius of the fillet + */ + void addDirectedFillet(const geom::Coordinate& p, double startAngle, + double endAngle, int direction, double radius); +private: + // An OffsetSegmentGenerator cannot be copied because of member "const BufferParameters& bufParams" + // Not declaring these functions triggers MSVC warning C4512: "assignment operator could not be generated" + OffsetSegmentGenerator(const OffsetSegmentGenerator&); + void operator=(const OffsetSegmentGenerator&); + +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/OffsetSegmentString.h b/Sources/geos/include/geos/operation/buffer/OffsetSegmentString.h new file mode 100644 index 0000000..a3fdf19 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/OffsetSegmentString.h @@ -0,0 +1,215 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2007 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/OffsetSegmentString.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include // for inlines +#include // for inlines +#include // for inlines + +#include +#include +#include + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/// A dynamic list of the vertices in a constructed offset curve. +/// +/// Automatically removes close vertices +/// which are closer than a given tolerance. +/// +/// @author Martin Davis +/// +class OffsetSegmentString { + +private: + + geom::CoordinateSequence* ptList; + + const geom::PrecisionModel* precisionModel; + + /** \brief + * The distance below which two adjacent points on the curve + * are considered to be coincident. + * + * This is chosen to be a small fraction of the offset distance. + */ + double minimumVertexDistance; + + /** \brief + * Tests whether the given point is redundant relative to the previous + * point in the list (up to tolerance) + * + * @param pt + * @return true if the point is redundant + */ + bool + isRedundant(const geom::Coordinate& pt) const + { + if(ptList->size() < 1) { + return false; + } + const geom::Coordinate& lastPt = ptList->back(); + double ptDist = pt.distance(lastPt); + if(ptDist < minimumVertexDistance) { + return true; + } + return false; + } + + OffsetSegmentString(const OffsetSegmentString&) = delete; + OffsetSegmentString& operator=(const OffsetSegmentString&) = delete; + +public: + + friend std::ostream& operator<< (std::ostream& os, const OffsetSegmentString& node); + + OffsetSegmentString() + : + ptList(new geom::CoordinateSequence()), + precisionModel(nullptr), + minimumVertexDistance(0.0) + { + } + + ~OffsetSegmentString() + { + delete ptList; + } + + void + reset() + { + if(ptList) { + ptList->clear(); + } + else { + ptList = new geom::CoordinateSequence(); + } + + precisionModel = nullptr; + minimumVertexDistance = 0.0; + } + + void + setPrecisionModel(const geom::PrecisionModel* nPrecisionModel) + { + precisionModel = nPrecisionModel; + } + + void + setMinimumVertexDistance(double nMinVertexDistance) + { + minimumVertexDistance = nMinVertexDistance; + } + + void + addPt(const geom::Coordinate& pt) + { + assert(precisionModel); + + geom::Coordinate bufPt = pt; + precisionModel->makePrecise(bufPt); + // don't add duplicate (or near-duplicate) points + if(isRedundant(bufPt)) { + return; + } + // we ask to allow repeated as we checked this ourself + // (JTS uses a vector for ptList, not a CoordinateSequence, + // we should do the same) + ptList->add(bufPt, true); + } + + void + addPts(const geom::CoordinateSequence& pts, bool isForward) + { + if(isForward) { + for(std::size_t i = 0, n = pts.size(); i < n; ++i) { + addPt(pts[i]); + } + } + else { + for(std::size_t i = pts.size(); i > 0; --i) { + addPt(pts[i - 1]); + } + } + } + + /// Check that points are a ring + /// + /// add the startpoint again if they are not + void + closeRing() + { + if(ptList->size() < 1) { + return; + } + const geom::Coordinate& startPt = ptList->front(); + const geom::Coordinate& lastPt = ptList->back(); + if(startPt.equals(lastPt)) { + return; + } + // we ask to allow repeated as we checked this ourself + ptList->add(startPt, true); + } + + /// Get coordinates by taking ownership of them + /// + /// After this call, the coordinates reference in + /// this object are dropped. Calling twice will + /// segfault... + /// + /// FIXME: refactor memory management of this + /// + geom::CoordinateSequence* + getCoordinates() + { + closeRing(); + geom::CoordinateSequence* ret = ptList; + ptList = nullptr; + return ret; + } + + inline size_t + size() const + { + return ptList ? ptList->size() : 0 ; + } + +}; + +inline std::ostream& +operator<< (std::ostream& os, + const OffsetSegmentString& lst) +{ + if(lst.ptList) { + os << *(lst.ptList); + } + else { + os << "empty (consumed?)"; + } + return os; +} + +} // namespace geos.operation.buffer +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/PolygonBuilder.h b/Sources/geos/include/geos/operation/buffer/PolygonBuilder.h new file mode 100644 index 0000000..914a341 --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/PolygonBuilder.h @@ -0,0 +1,202 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/overlay/PolygonBuilder.java rev. 1.20 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +class GeometryFactory; +} +namespace geomgraph { +class EdgeRing; +class Node; +class PlanarGraph; +class DirectedEdge; +} +namespace operation { +namespace buffer { +class MaximalEdgeRing; +class MinimalEdgeRing; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace buffer { // geos::operation::buffer + +/** \brief + * Forms Polygon out of a graph of geomgraph::DirectedEdge. + * + * The edges to use are marked as being in the result Area. + */ +class GEOS_DLL PolygonBuilder { +public: + + PolygonBuilder(const geom::GeometryFactory* newGeometryFactory); + + ~PolygonBuilder(); + + /** + * Add a complete graph. + * The graph is assumed to contain one or more polygons, + * possibly with holes. + */ + void add(geomgraph::PlanarGraph* graph); + // throw(const TopologyException &) + + /** + * Add a set of edges and nodes, which form a graph. + * The graph is assumed to contain one or more polygons, + * possibly with holes. + */ + void add(const std::vector* dirEdges, + const std::vector* nodes); + // throw(const TopologyException &) + + std::vector> getPolygons(); + +private: + + const geom::GeometryFactory* geometryFactory; + + std::vector shellList; + + /** + * For all DirectedEdges in result, form them into MaximalEdgeRings + * + * @param maxEdgeRings + * Formed MaximalEdgeRings will be pushed to this vector. + * Ownership of the elements is transferred to caller. + */ + void buildMaximalEdgeRings( + const std::vector* dirEdges, + std::vector& maxEdgeRings); + // throw(const TopologyException &) + + void buildMinimalEdgeRings( + std::vector& maxEdgeRings, + std::vector& newShellList, + std::vector& freeHoleList, + std::vector& edgeRings); + + /** + * This method takes a list of MinimalEdgeRings derived from a + * MaximalEdgeRing, and tests whether they form a Polygon. + * This is the case if there is a single shell + * in the list. In this case the shell is returned. + * The other possibility is that they are a series of connected + * holes, in which case no shell is returned. + * + * @return the shell geomgraph::EdgeRing, if there is one + * @return NULL, if all the rings are holes + */ + geomgraph::EdgeRing* findShell(std::vector* minEdgeRings); + + /** + * This method assigns the holes for a Polygon (formed from a list of + * MinimalEdgeRings) to its shell. + * Determining the holes for a MinimalEdgeRing polygon serves two + * purposes: + * + * - it is faster than using a point-in-polygon check later on. + * - it ensures correctness, since if the PIP test was used the point + * chosen might lie on the shell, which might return an incorrect + * result from the PIP test + */ + void placePolygonHoles(geomgraph::EdgeRing* shell, + std::vector* minEdgeRings); + + /** + * For all rings in the input list, + * determine whether the ring is a shell or a hole + * and add it to the appropriate list. + * Due to the way the DirectedEdges were linked, + * a ring is a shell if it is oriented CW, a hole otherwise. + */ + void sortShellsAndHoles(std::vector& edgeRings, + std::vector& newShellList, + std::vector& freeHoleList); + + struct FastPIPRing { + geomgraph::EdgeRing* edgeRing; + algorithm::locate::IndexedPointInAreaLocator* pipLocator; + }; + + /** \brief + * This method determines finds a containing shell for all holes + * which have not yet been assigned to a shell. + * + * Holes which do not lie in any shell are (probably) an eroded element, + * so are simply discarded. + */ + void placeFreeHoles(std::vector& newShellList, + std::vector& freeHoleList); + + /** \brief + * Find the innermost enclosing shell geomgraph::EdgeRing containing the + * argument geomgraph::EdgeRing, if any. + * + * The innermost enclosing ring is the smallest enclosing ring. + * The algorithm used depends on the fact that: + * + * ring A contains ring B iff envelope(ring A) + * contains envelope(ring B) + * + * This routine is only safe to use if the chosen point of the hole + * is known to be properly contained in a shell + * (which is guaranteed to be the case if the hole does not touch + * its shell) + * + * @return containing geomgraph::EdgeRing, if there is one + * @return NULL if no containing geomgraph::EdgeRing is found + */ + geomgraph::EdgeRing* findEdgeRingContaining(geomgraph::EdgeRing* testEr, + std::vector& newShellList); + + std::vector> computePolygons( + std::vector& newShellList); + + /** + * Checks the current set of shells (with their associated holes) to + * see if any of them contain the point. + */ + +}; + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/buffer/RightmostEdgeFinder.h b/Sources/geos/include/geos/operation/buffer/RightmostEdgeFinder.h new file mode 100644 index 0000000..cd8d10b --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/RightmostEdgeFinder.h @@ -0,0 +1,105 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/RightmostEdgeFinder.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition + +#include + +// Forward declarations +namespace geos { +namespace geom { +} +namespace geomgraph { +class DirectedEdge; +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \brief + * A RightmostEdgeFinder find the geomgraph::DirectedEdge in a list which has + * the highest coordinate, and which is oriented L to R at that point. + * (I.e. the right side is on the RHS of the edge.) + */ +class GEOS_DLL RightmostEdgeFinder { + +private: + + int minIndex; + + geom::Coordinate minCoord; + + geomgraph::DirectedEdge* minDe; + + geomgraph::DirectedEdge* orientedDe; + + void findRightmostEdgeAtNode(); + + void findRightmostEdgeAtVertex(); + + void checkForRightmostCoordinate(geomgraph::DirectedEdge* de); + + int getRightmostSide(geomgraph::DirectedEdge* de, int index); + + static int getRightmostSideOfSegment(geomgraph::DirectedEdge* de, int i); + +public: + + /** \brief + * A RightmostEdgeFinder finds the geomgraph::DirectedEdge with the + * rightmost coordinate. + * + * The geomgraph::DirectedEdge returned is guaranteed to have the R of + * the world on its RHS. + */ + RightmostEdgeFinder(); + + geomgraph::DirectedEdge* getEdge(); + + geom::Coordinate& getCoordinate(); + + /// Note that only Forward DirectedEdges will be checked + void findEdge(std::vector* dirEdgeList); +}; + +/*public*/ +inline geomgraph::DirectedEdge* +RightmostEdgeFinder::getEdge() +{ + return orientedDe; +} + +/*public*/ +inline geom::Coordinate& +RightmostEdgeFinder::getCoordinate() +{ + return minCoord; +} + + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/buffer/SegmentMCIndex.h b/Sources/geos/include/geos/operation/buffer/SegmentMCIndex.h new file mode 100644 index 0000000..1e23aeb --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/SegmentMCIndex.h @@ -0,0 +1,58 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateSequence; +class Envelope; +} +} + +namespace geos { +namespace operation { +namespace buffer { + +class GEOS_DLL SegmentMCIndex { + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using MonotoneChain = geos::index::chain::MonotoneChain; + using MonotoneChainSelectAction = geos::index::chain::MonotoneChainSelectAction; + template + using TemplateSTRtree = geos::index::strtree::TemplateSTRtree; + +private: + + TemplateSTRtree index; + std::vector segChains; + + void buildIndex(const CoordinateSequence* segs); + +public: + + SegmentMCIndex(const CoordinateSequence* segs); + + void query(const Envelope* env, MonotoneChainSelectAction& action); +}; + + +} // geos.operation.buffer +} // geos.operation +} // geos diff --git a/Sources/geos/include/geos/operation/buffer/SubgraphDepthLocater.h b/Sources/geos/include/geos/operation/buffer/SubgraphDepthLocater.h new file mode 100644 index 0000000..e9766de --- /dev/null +++ b/Sources/geos/include/geos/operation/buffer/SubgraphDepthLocater.h @@ -0,0 +1,120 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/buffer/SubgraphDepthLocater.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class DirectedEdge; +} +namespace operation { +namespace buffer { +class BufferSubgraph; +class DepthSegment; +} +} +} + +namespace geos { +namespace operation { // geos.operation +namespace buffer { // geos.operation.buffer + +/** + * \class SubgraphDepthLocater + * + * \brief + * Locates a subgraph inside a set of subgraphs, + * in order to determine the outside depth of the subgraph. + * + * The input subgraphs are assumed to have had depths + * already calculated for their edges. + * + */ +class GEOS_DLL SubgraphDepthLocater { + +public: + + SubgraphDepthLocater(std::vector* newSubgraphs) + : + subgraphs(newSubgraphs) + {} + + ~SubgraphDepthLocater() {} + + int getDepth(const geom::Coordinate& p); + +private: + + std::vector* subgraphs; + + geom::LineSegment seg; + + /** + * Finds all non-horizontal segments intersecting the stabbing line. + * The stabbing line is the ray to the right of stabbingRayLeftPt. + * + * @param stabbingRayLeftPt the left-hand origin of the stabbing line + * @param stabbedSegments a vector to which DepthSegments intersecting + * the stabbing line will be added. + */ + void findStabbedSegments(const geom::Coordinate& stabbingRayLeftPt, + std::vector& stabbedSegments); + + /** + * Finds all non-horizontal segments intersecting the stabbing line + * in the list of dirEdges. + * The stabbing line is the ray to the right of stabbingRayLeftPt. + * + * @param stabbingRayLeftPt the left-hand origin of the stabbing line + * @param stabbedSegments the current vector of DepthSegments + * intersecting the stabbing line will be added. + */ + void findStabbedSegments(const geom::Coordinate& stabbingRayLeftPt, + std::vector* dirEdges, + std::vector& stabbedSegments); + + /** + * Finds all non-horizontal segments intersecting the stabbing line + * in the input dirEdge. + * The stabbing line is the ray to the right of stabbingRayLeftPt. + * + * @param stabbingRayLeftPt the left-hand origin of the stabbing line + * @param stabbedSegments the current list of DepthSegments intersecting + * the stabbing line + */ + void findStabbedSegments(const geom::Coordinate& stabbingRayLeftPt, + geomgraph::DirectedEdge* dirEdge, + std::vector& stabbedSegments); + +}; + + +} // namespace geos::operation::buffer +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/cluster/AbstractClusterFinder.h b/Sources/geos/include/geos/operation/cluster/AbstractClusterFinder.h new file mode 100644 index 0000000..0b66cd3 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/AbstractClusterFinder.h @@ -0,0 +1,132 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2022 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; + class Geometry; +} +namespace operation { +namespace cluster { + class UnionFind; +} +} +} + +namespace geos { +namespace operation { +namespace cluster { + +/** AbstractClusterFinder defines an interface for bottom-up clustering algorithms, + * where spatial index queries can be used to identify geometries that should be + * clustered together. + */ +class GEOS_DLL AbstractClusterFinder { + +public: + /** + * Cluster the provided geometries, returning an object that provides access + * to the components of each cluster. + * + * @param g A vector of geometries to cluster + */ + Clusters cluster(const std::vector& g); + + /** + * Cluster the components of the provided geometry, returning a vector of clusters. + * This function will take ownership of the provided geometry. Any components that + * are included in a cluster will be returned. Components that are not included in + * any cluster will be destroyed. + * + * @param g A geometry whose components should be clustered. + * @return a Geometry vector, with each entry representing a single cluster. + */ + std::vector> clusterToVector(std::unique_ptr && g); + + /** + * Cluster the components of the provided geometry, returning a vector of clusters. + * The input geometry will not be modified. + * + * @param g A geometry whose components should be clustered. + * @return a Geometry vector, with each entry representing a single cluster. + */ + std::vector> clusterToVector(const geom::Geometry& g); + + /** + * Cluster the components of the provided geometry, returning a GeometryCollection. + * This function will take ownership of the provided geometry. Any components that + * are included in a cluster will be returned. Components that are not included in + * any cluster will be destroyed. + * + * @param g A geometry whose components should be clustered. + * @return a GeometryCollection, with each sub-geometry representing a single cluster. + */ + std::unique_ptr clusterToCollection(std::unique_ptr && g); + + /** + * Cluster the components of the provided geometry, returning a GeometryCollection. + * The input geometry will not be modified. + * + * @param g A geometry whose components should be clustered. + * @return a GeometryCollection, with each sub-geometry representing a single cluster. + */ + std::unique_ptr clusterToCollection(const geom::Geometry & g); + +protected: + /** + * Determine whether two geometries should be considered in the same cluster. + * @param a Geometry + * @param b Geometry + * @return `true` if the clusters associated with `a` and `b` should be merged. + */ + virtual bool shouldJoin(const geom::Geometry* a, const geom::Geometry* b) = 0; + + /** + * Provide an query Envelope that can be used to find all geometries possibly + * in the same cluster as the input. + * @param a Geometry + * @return an Envelope suitable for querying + */ + virtual const geom::Envelope& queryEnvelope(const geom::Geometry* a) = 0; + + /** + * Given a vector and index of components, + * @param components a vector of Geometry components + * @param index a spatial index storing pointers to those components + * @param uf a UnionFind + * @return a vector of with the indices of all components that should be included in a cluster + */ + virtual Clusters process(const std::vector & components, + index::strtree::TemplateSTRtree & index, + UnionFind & uf); + +private: + static std::vector> getComponents(std::unique_ptr&& g); +}; + + + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/Clusters.h b/Sources/geos/include/geos/operation/cluster/Clusters.h new file mode 100644 index 0000000..82f9d46 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/Clusters.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021-2022 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +class UnionFind; + +class GEOS_DLL Clusters { +private: + std::vector m_elemsInCluster; // The IDs of elements that are included in a cluster + std::vector m_starts; // Start position of each cluster in m_elemsInCluster + std::size_t m_numElems; // The number of elements from which clusters were generated + +public: + using const_iterator = decltype(m_elemsInCluster)::const_iterator; + + explicit Clusters(UnionFind & uf, std::vector elemsInCluster, std::size_t numElems); + + // Get the number of clusters available + std::size_t getNumClusters() const { + return m_starts.size(); + } + + // Get the size of a given cluster + std::size_t getSize(std::size_t cluster) const { + return static_cast(std::distance(begin(cluster), end(cluster))); + } + + // Get a vector containing the cluster ID for each item in `elems` + std::vector getClusterIds(std::size_t noClusterValue = std::numeric_limits::max()) const; + + // Get an iterator to the first element in a given cluster + const_iterator begin(std::size_t cluster) const { + return std::next(m_elemsInCluster.begin(), static_cast(m_starts[cluster])); + } + + // Get an iterator beyond the last element in a given cluster + const_iterator end(std::size_t cluster) const { + if (cluster == static_cast(m_starts.size() - 1)) { + return m_elemsInCluster.end(); + } + + return begin(cluster + 1); + } + +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/DBSCANClusterFinder.h b/Sources/geos/include/geos/operation/cluster/DBSCANClusterFinder.h new file mode 100644 index 0000000..be37201 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/DBSCANClusterFinder.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** DBSCANClusterFinder clusters geometries according to the DBSCAN algorithm. + * + */ +class GEOS_DLL DBSCANClusterFinder : public AbstractClusterFinder { +public: + DBSCANClusterFinder(double eps, size_t minPoints) : m_eps(eps), m_minPoints(minPoints) {} + +protected: + + const geom::Envelope& queryEnvelope(const geom::Geometry* a) override { + m_envelope = *a->getEnvelopeInternal(); + m_envelope.expandBy(m_eps); + return m_envelope; + } + + Clusters process(const std::vector & components, + index::strtree::TemplateSTRtree & index, + UnionFind & uf) override; + + bool shouldJoin(const geom::Geometry*, const geom::Geometry*) override { + throw std::runtime_error("Never get here."); + } + +private: + double m_eps; + size_t m_minPoints; + geom::Envelope m_envelope; +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/DisjointOperation.h b/Sources/geos/include/geos/operation/cluster/DisjointOperation.h new file mode 100644 index 0000000..50ae521 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/DisjointOperation.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +class GEOS_DLL DisjointOperation { +public: + DisjointOperation(AbstractClusterFinder& finder) : m_finder(finder), m_split_inputs(false) {} + + /** Splits multipart geometries into their underlying components before identifying + * disjoint subsets. + */ + void setSplitInputs(bool b) { + m_split_inputs = b; + } + + /** Decompose a geometry into disjoint subsets using the provided `ClusterFinder`, + * process each subset using `f`, and combine/flatten the results. It is assumed that + * the processed results of each subset will also be disjoint; therefore, this + * algorithm may not be suitable for operations such as buffering. + * + * @brief process + * @param g a geometry to be processed + * @param f function taking a single argument of `const Geometry&` + */ + template + std::unique_ptr processDisjointSubsets(const geom::Geometry& g, Function&& f) { + if (g.getNumGeometries() == 1) { + return f(g); + } + + auto flattened = m_split_inputs ? operation::cluster::GeometryFlattener::flatten(g.clone()) : g.clone(); + auto clustered = m_finder.clusterToVector(std::move(flattened)); + + for (auto& subset : clustered) { + subset = f(*subset); + } + + auto coll = g.getFactory()->createGeometryCollection(std::move(clustered)); + + return operation::cluster::GeometryFlattener::flatten(std::move(coll)); + } + +private: + AbstractClusterFinder& m_finder; + bool m_split_inputs; +}; + + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/EnvelopeDistanceClusterFinder.h b/Sources/geos/include/geos/operation/cluster/EnvelopeDistanceClusterFinder.h new file mode 100644 index 0000000..eb7dfb2 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/EnvelopeDistanceClusterFinder.h @@ -0,0 +1,50 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** EnvelopeDistanceClusterFinder clusters geometries by the distance between their envelopes. + * Any two geometries whose envelopes are within the specified distance will be included in the same cluster. + */ +class GEOS_DLL EnvelopeDistanceClusterFinder : public AbstractClusterFinder { +public: + explicit EnvelopeDistanceClusterFinder(double d) : m_distance(d), m_distance_squared(d*d) {} + +protected: + const geom::Envelope& queryEnvelope(const geom::Geometry* a) override { + m_envelope = *a->getEnvelopeInternal(); + m_envelope.expandBy(m_distance); + return m_envelope; + } + + bool shouldJoin(const geom::Geometry* a, const geom::Geometry *b) override { + return a->getEnvelopeInternal()->distanceSquared(*b->getEnvelopeInternal()) <= m_distance_squared; + } + +private: + geom::Envelope m_envelope; + double m_distance; + double m_distance_squared; +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/EnvelopeIntersectsClusterFinder.h b/Sources/geos/include/geos/operation/cluster/EnvelopeIntersectsClusterFinder.h new file mode 100644 index 0000000..4f2a7fd --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/EnvelopeIntersectsClusterFinder.h @@ -0,0 +1,41 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** EnvelopeIntersectsClusterFinder clusters geometries by envelope intersection. + * Any two geometries whose envelopes intersect will be included in the same cluster. + */ +class GEOS_DLL EnvelopeIntersectsClusterFinder : public AbstractClusterFinder { +protected: + + const geom::Envelope& queryEnvelope(const geom::Geometry* a) override { + return *(a->getEnvelopeInternal()); + } + + bool shouldJoin(const geom::Geometry* a, const geom::Geometry *b) override { + return a->getEnvelopeInternal()->intersects(b->getEnvelopeInternal()); + } + +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/GeometryDistanceClusterFinder.h b/Sources/geos/include/geos/operation/cluster/GeometryDistanceClusterFinder.h new file mode 100644 index 0000000..802b9b6 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/GeometryDistanceClusterFinder.h @@ -0,0 +1,56 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** GeometryDistanceClusterFinder clusters geometries according to the distance between + * them. Any two geometries that are within the specified threshold distance will be + * included in the same cluster. + */ +class GEOS_DLL GeometryDistanceClusterFinder : public AbstractClusterFinder { +public: + explicit GeometryDistanceClusterFinder(double distance) : m_distance(distance) {} + +protected: + bool shouldJoin(const geom::Geometry* a, const geom::Geometry *b) override { + if (m_prep == nullptr || &(m_prep->getGeometry()) != a) { + m_prep = geom::prep::PreparedGeometryFactory::prepare(a); + } + + return m_prep->isWithinDistance(b, m_distance); + } + + const geom::Envelope& queryEnvelope(const geom::Geometry* a) override { + m_envelope = *a->getEnvelopeInternal(); + m_envelope.expandBy(m_distance); + return m_envelope; + } + +private: + std::unique_ptr m_prep; + double m_distance; + geom::Envelope m_envelope; +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/GeometryFlattener.h b/Sources/geos/include/geos/operation/cluster/GeometryFlattener.h new file mode 100644 index 0000000..6b43212 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/GeometryFlattener.h @@ -0,0 +1,46 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + + +class GEOS_DLL GeometryFlattener { +public: + /** Flatten a geometry such that it contains no nested components (e.g., MultiPolygons within + * GeometryCollections, etc.). The flattened geometry will use the most narrow representation + * possible (Polygon preferred to MultiPolygon, MultiPolygon preferred to GeometryCollection.) + * + * @brief flatten + * @param g a geometry to be flattened; consumed by the function + * @return a geometry with no nested components + */ + static std::unique_ptr flatten(std::unique_ptr&& g); + +private: + static void flatten(std::unique_ptr&& g, std::vector>& components); +}; + + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/GeometryIntersectsClusterFinder.h b/Sources/geos/include/geos/operation/cluster/GeometryIntersectsClusterFinder.h new file mode 100644 index 0000000..8644e84 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/GeometryIntersectsClusterFinder.h @@ -0,0 +1,50 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** GeometryIntersectsClusterFinder clusters geometries by intersection. + * Any two geometries that intersect will be included in the same cluster. + */ +class GEOS_DLL GeometryIntersectsClusterFinder : public AbstractClusterFinder { +protected: + const geom::Envelope& queryEnvelope(const geom::Geometry* a) override { + return *(a->getEnvelopeInternal()); + } + + bool shouldJoin(const geom::Geometry* a, const geom::Geometry *b) override { + if (m_prep == nullptr || &(m_prep->getGeometry()) != a) { + m_prep = geom::prep::PreparedGeometryFactory::prepare(a); + } + + return m_prep->intersects(b); + } + +private: + std::unique_ptr m_prep; +}; + + +} +} +} diff --git a/Sources/geos/include/geos/operation/cluster/UnionFind.h b/Sources/geos/include/geos/operation/cluster/UnionFind.h new file mode 100644 index 0000000..af47151 --- /dev/null +++ b/Sources/geos/include/geos/operation/cluster/UnionFind.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020-2021 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace geos { +namespace operation { +namespace cluster { + +/** UnionFind provides an implementation of a disjoint set data structure that + * is useful in clustering. Elements to be clustered are referred to according + * to a numeric index. + */ +class GEOS_DLL UnionFind { + +public: + /** Create a UnionFind object + * + * @param n the number of elements to be clustered (fixed size) + */ + explicit UnionFind(size_t n) : + clusters(n), + sizes(n), + num_clusters(n) { + std::iota(clusters.begin(), clusters.end(), 0); + std::fill(sizes.begin(), sizes.end(), 1); + } + + // Are two elements in the same cluster? + bool same(size_t i, size_t j) { + return i == j || (find(i) == find(j)); + } + + // Are two elements in a different cluster? + bool different(size_t i, size_t j) { + return !same(i, j); + } + + /** + * Return the ID of the cluster associated with an item + * @param i index of the item to lookup + * @return a numeric cluster ID + */ + size_t find(size_t i) { + size_t root = i; + + while(clusters[root] != root) { + root = clusters[root]; + } + + while (i != root) { + size_t next = clusters[i]; + clusters[i] = root; + i = next; + } + + return i; + } + + /** + * Merge the clusters associated with two items + * @param i ID of an item associated with the first cluster + * @param j ID of an item associated with the second cluster + */ + void join(size_t i, size_t j) { + auto a = find(i); + auto b = find(j); + + if (a == b) { + return; + } + + if ((sizes[b] > sizes[a]) || (sizes[a] == sizes[b] && b <= a)) { + std::swap(a, b); + } + + clusters[a] = clusters[b]; + sizes[b] += sizes[a]; + sizes[a] = 0; + + num_clusters--; + } + + size_t getNumClusters() const { + return num_clusters; + } + + template + void sortByCluster(T begin, T end) { + std::sort(begin, end, [this](size_t a, size_t b) { + return find(a) < find(b); + }); + } + + /** + * Return the clusters associated with all elements + * @return an object that allows iteration over the elements of each cluster + */ + Clusters getClusters(); + + /** + * Return the clusters associated with the given elements + * @param elems a vector of element ids + * @return an object that allows iteration over the elements of each cluster + */ + Clusters getClusters(std::vector elems); + +private: + std::vector clusters; + std::vector sizes; + size_t num_clusters; +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h b/Sources/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h new file mode 100644 index 0000000..82e4261 --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/ConnectedElementLocationFilter.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/ConnectedElementLocationFilter.java b98e8234 + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace distance { // geos::operation::distance + +/** \brief + * A ConnectedElementPointFilter extracts a single point from each connected + * element in a Geometry (e.g. a polygon, linestring or point) and returns + * them in a list. Empty geometries do not provide a location item. + * + * The elements of the list are GeometryLocation. + * + * Empty geometries do not provide a location item. + */ +class GEOS_DLL ConnectedElementLocationFilter: public geom::GeometryFilter { +private: + + std::vector locations; + ConnectedElementLocationFilter() = default; + ConnectedElementLocationFilter(const ConnectedElementLocationFilter&) = delete; + ConnectedElementLocationFilter& operator=(const ConnectedElementLocationFilter&) = delete; +public: + /** \brief + * Returns a list containing a point from each Polygon, LineString, and + * Point found inside the specified geometry. + * + * Thus, if the specified geometry is not a GeometryCollection, + * an empty list will be returned. The elements of the list + * are [GeometryLocations](@ref operation::distance::GeometryLocation). + */ + static std::vector getLocations(const geom::Geometry* geom); + + void filter_ro(const geom::Geometry* geom) override; + void filter_rw(geom::Geometry* geom) override; +}; + + +} // namespace geos::operation::distance +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/distance/ConnectedElementPointFilter.h b/Sources/geos/include/geos/operation/distance/ConnectedElementPointFilter.h new file mode 100644 index 0000000..64e47dc --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/ConnectedElementPointFilter.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/ConnectedElementPointFilter.java rev. 1.7 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +class Geometry; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace distance { // geos::operation::distance + +/** \brief + * Extracts a single point + * from each connected element in a Geometry + * (e.g. a polygon, linestring or point) + * and returns them in a list + */ +class GEOS_DLL ConnectedElementPointFilter: public geom::GeometryFilter { + +private: + std::vector* pts; + +public: + /** + * Returns a list containing a Coordinate from each Polygon, LineString, and Point + * found inside the specified geometry. Thus, if the specified geometry is + * not a GeometryCollection, an empty list will be returned. + */ + static std::vector* getCoordinates(const geom::Geometry* geom); + + ConnectedElementPointFilter(std::vector* newPts) + : + pts(newPts) + {} + + void filter_ro(const geom::Geometry* geom) override; + + //void filter_rw(geom::Geometry * /*geom*/) {}; +}; + + +} // namespace geos::operation::distance +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/distance/DistanceOp.h b/Sources/geos/include/geos/operation/distance/DistanceOp.h new file mode 100644 index 0000000..b4d6fed --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/DistanceOp.h @@ -0,0 +1,228 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/DistanceOp.java r335 (JTS-1.12-) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Polygon; +class LineString; +class Point; +class Geometry; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace distance { // geos::operation::distance + +/** + * \brief + * Find two points on two {@link geom::Geometry}s which lie + * within a given distance, or else are the nearest points + * on the geometries (in which case this also + * provides the distance between the geometries). + * + * The distance computation also finds a pair of points in the + * input geometries which have the minimum distance between them. + * If a point lies in the interior of a line segment, + * the coordinate computed is a close + * approximation to the exact point. + * + * Empty geometry collection components are ignored. + * + * The algorithms used are straightforward O(n^2) + * comparisons. This worst-case performance could be improved on + * by using Voronoi techniques or spatial indexes. + * + */ +class GEOS_DLL DistanceOp { +public: + /** + * \brief + * Compute the distance between the nearest points of two geometries. + * + * @param g0 a {@link geom::Geometry} + * @param g1 another {@link geom::Geometry} + * @return the distance between the geometries + * @return 0 if either input geometry is empty + * @throws IllegalArgumentException if either input geometry is null + */ + static double distance(const geom::Geometry& g0, + const geom::Geometry& g1); + + /// @deprecated, use the version taking references + static double distance(const geom::Geometry* g0, + const geom::Geometry* g1); + + /** + * \brief + * Test whether two geometries lie within a given distance of + * each other. + * + * @param g0 a {@link geom::Geometry} + * @param g1 another {@link geom::Geometry} + * @param distance the distance to test + * @return true if g0.distance(g1) <= distance + */ + static bool isWithinDistance(const geom::Geometry& g0, + const geom::Geometry& g1, + double distance); + + /** + * Compute the the nearest points of two geometries. + * + * The points are presented in the same order as the input Geometries. + * + * @param g0 a {@link geom::Geometry} + * @param g1 another {@link geom::Geometry} + * + * @return the nearest points in the geometries, ownership to caller. + * A NULL return means one of the geometries is empty. + * + */ + static std::unique_ptr nearestPoints( + const geom::Geometry* g0, + const geom::Geometry* g1); + + /// @deprecated use the one taking references + DistanceOp(const geom::Geometry* g0, const geom::Geometry* g1); + + /** + * \brief + * Constructs a DistanceOp that computes the distance and + * nearest points between the two specified geometries. + * + * @param g0 a Geometry + * @param g1 a Geometry + */ + DistanceOp(const geom::Geometry& g0, const geom::Geometry& g1); + + /** + * \brief + * Constructs a DistanceOp that computes the distance and nearest + * points between the two specified geometries. + * + * @param g0 a Geometry + * @param g1 a Geometry + * @param terminateDistance the distance on which to terminate + * the search + */ + DistanceOp(const geom::Geometry& g0, const geom::Geometry& g1, + double terminateDistance); + + ~DistanceOp() = default; + + /** + * Report the distance between the closest points on the input geometries. + * + * @return the distance between the geometries + */ + double distance(); + + /** + * Report the coordinates of the nearest points in the input geometries. + * The points are presented in the same order as the input Geometries. + * + * @return a pair of {@link geom::Coordinate}s of the nearest points + * as a newly allocated object (ownership to caller) + * + */ + std::unique_ptr nearestPoints(); + +private: + + // input + std::array geom; + double terminateDistance; + + // working + algorithm::PointLocator ptLocator; + std::array minDistanceLocation; + double minDistance; + bool computed = false; + + void updateMinDistance(std::array & locGeom, bool flip); + + void computeMinDistance(); + + void computeContainmentDistance(); + + void computeInside(std::vector & locs, + const std::vector& polys, + std::array & locPtPoly); + + + /** + * Computes distance between facets (lines and points) + * of input geometries. + */ + void computeFacetDistance(); + + void computeMinDistanceLines( + const std::vector& lines0, + const std::vector& lines1, + std::array & locGeom); + + void computeMinDistancePoints( + const std::vector& points0, + const std::vector& points1, + std::array & locGeom); + + void computeMinDistanceLinesPoints( + const std::vector& lines0, + const std::vector& points1, + std::array & locGeom); + + void computeMinDistance(const geom::LineString* line0, + const geom::LineString* line1, + std::array & locGeom); + + void computeMinDistance(const geom::LineString* line, + const geom::Point* pt, + std::array & locGeom); +}; + + +} // namespace geos::operation::distance +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/distance/FacetSequence.h b/Sources/geos/include/geos/operation/distance/FacetSequence.h new file mode 100644 index 0000000..fbef50e --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/FacetSequence.h @@ -0,0 +1,84 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/FacetSequence.java (f6187ee2 JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace distance { +class FacetSequence { +private: + const geom::CoordinateSequence* pts; + const std::size_t start; + const std::size_t end; + const geom::Geometry* geom; + /* + * Unlike JTS, we store the envelope in the FacetSequence so + * that it has a clear owner. This is helpful when making a + * tree of FacetSequence objects (FacetSequenceTreeBuilder) + */ + geom::Envelope env; + + double computeDistanceLineLine(const FacetSequence& facetSeq, + std::vector *locs) const; + + double computeDistancePointLine(const geom::Coordinate& pt, + const FacetSequence& facetSeq, + std::vector *locs) const; + + void updateNearestLocationsPointLine(const geom::Coordinate& pt, + const FacetSequence& facetSeq, std::size_t i, + const geom::Coordinate& q0, const geom::Coordinate &q1, + std::vector *locs) const; + + void updateNearestLocationsLineLine(std::size_t i, const geom::Coordinate& p0, const geom::Coordinate& p1, + const FacetSequence& facetSeq, + std::size_t j, const geom::Coordinate& q0, const geom::Coordinate &q1, + std::vector *locs) const; + + void computeEnvelope(); + +public: + const geom::Envelope* getEnvelope() const; + + const geom::Coordinate* getCoordinate(std::size_t index) const; + + std::size_t size() const; + + bool isPoint() const; + + double distance(const FacetSequence& facetSeq) const; + + FacetSequence(const geom::CoordinateSequence* pts, std::size_t start, std::size_t end); + + FacetSequence(const geom::Geometry* geom, const geom::CoordinateSequence* pts, std::size_t start, std::size_t end); + + std::vector nearestLocations(const FacetSequence& facetSeq) const; + +}; + +} +} +} + diff --git a/Sources/geos/include/geos/operation/distance/FacetSequenceTreeBuilder.h b/Sources/geos/include/geos/operation/distance/FacetSequenceTreeBuilder.h new file mode 100644 index 0000000..2a8731f --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/FacetSequenceTreeBuilder.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/FacetSequenceTreeBuilder.java (f6187ee2 JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace distance { +class GEOS_DLL FacetSequenceTreeBuilder { +private: + // 6 seems to be a good facet sequence size + static const std::size_t FACET_SEQUENCE_SIZE = 6; + + // Seems to be better to use a minimum node capacity + static const std::size_t STR_TREE_NODE_CAPACITY = 4; + + static void addFacetSequences(const geom::Geometry* geom, + const geom::CoordinateSequence* pts, + std::vector & sections); + static std::vector computeFacetSequences(const geom::Geometry* g); + + class FacetSequenceTree : public geos::index::strtree::TemplateSTRtree { + public: + // TODO support TemplateSTRtree> and dispense with holding vector. + FacetSequenceTree(std::vector &&seq) : + TemplateSTRtree(STR_TREE_NODE_CAPACITY, seq.size()), sequences(seq) { + for (auto& fs : sequences) { + TemplateSTRtree::insert(fs.getEnvelope(), &fs); + } + } + + private: + std::vector sequences; + }; + +public: + /** \brief + * Return a tree of FacetSequences constructed from the supplied Geometry. + * + * The FacetSequences are owned by the tree and are automatically deleted by + * the tree on destruction. + */ + static std::unique_ptr> build(const geom::Geometry* g); +}; +} +} +} + diff --git a/Sources/geos/include/geos/operation/distance/GeometryLocation.h b/Sources/geos/include/geos/operation/distance/GeometryLocation.h new file mode 100644 index 0000000..1fb4437 --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/GeometryLocation.h @@ -0,0 +1,126 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/GeometryLocation.java rev. 1.7 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace distance { // geos::operation::distance + + +/** \brief + * Represents the location of a point on a Geometry. + * + * Maintains both the actual point location + * (which may not be exact, if the point is not a vertex) + * as well as information about the component + * and segment index where the point occurs. + * Locations inside area Geometrys will not have an associated segment index, + * so in this case the segment index will have the sentinel value of + * INSIDE_AREA. + */ +class GEOS_DLL GeometryLocation { +private: + const geom::Geometry* component; + std::size_t segIndex; + bool inside_area; + geom::CoordinateXY pt; +public: + /** \brief + * A Special value of segmentIndex used for locations + * inside area geometries. + * + * These locations are not located on a segment, + * and thus do not have an associated segment index. + */ + static const int INSIDE_AREA = -1; + + GeometryLocation() : + component(nullptr), + segIndex(0), + inside_area(false), + pt() + {} + + /** \brief + * Constructs a GeometryLocation specifying a point on a geometry, + * as well as the segment that the point is on (or INSIDE_AREA if + * the point is not on a segment). + * + * @param component the component of the geometry containing the point + * @param segIndex the segment index of the location, or INSIDE_AREA + * @param pt the coordinate of the location + */ + GeometryLocation(const geom::Geometry* component, + std::size_t segIndex, const geom::CoordinateXY& pt); + + /** \brief + * Constructs a GeometryLocation specifying a point inside an + * area geometry. + * + * @param component the component of the geometry containing the point + * @param pt the coordinate of the location + */ + GeometryLocation(const geom::Geometry* component, + const geom::CoordinateXY& pt); + + /** + * Returns the geometry component on (or in) which this location occurs. + */ + const geom::Geometry* getGeometryComponent(); + + /** + * Returns the segment index for this location. + * + * If the location is inside an + * area, the index will have the value INSIDE_AREA; + * + * @return the segment index for the location, or INSIDE_AREA + */ + std::size_t getSegmentIndex(); + + /** + * Returns the geom::Coordinate of this location. + */ + geom::CoordinateXY& getCoordinate(); + + /** \brief + * Tests whether this location represents a point + * inside an area geometry. + */ + bool isInsideArea(); + + std::string toString(); +}; + +} // namespace geos::operation::distance +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/distance/IndexedFacetDistance.h b/Sources/geos/include/geos/operation/distance/IndexedFacetDistance.h new file mode 100644 index 0000000..87044c2 --- /dev/null +++ b/Sources/geos/include/geos/operation/distance/IndexedFacetDistance.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/distance/IndexedFacetDistance.java (f6187ee2 JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace operation { +namespace distance { + +/// \brief Computes the distance between the facets (segments and vertices) +/// of two [Geometrys](\ref geom::Geometry) using a Branch-and-Bound algorithm. +/// +/// The Branch-and-Bound algorithm operates over a traversal of R-trees built +/// on the target and the query geometries. +/// +/// This approach provides the following benefits: +/// +/// - Performance is dramatically improved due to the use of the R-tree index +/// and the pruning due to the Branch-and-Bound approach +/// - The spatial index on the target geometry is cached which allow reuse in +/// an repeated query situation. +/// +/// Using this technique is usually much more performant than using the +/// brute-force geom::Geometry::distance() when one +/// or both input geometries are large, or when evaluating many distance +/// computations against a single geometry. +/// +/// \author Martin Davis +class GEOS_DLL IndexedFacetDistance { +public: + + /// \brief Creates a new distance-finding instance for a given target geom::Geometry. + /// + /// Distances will be computed to all facets of the input geometry. + /// The facets of the geometry are the discrete segments and points + /// contained in its components. + /// In the case of lineal and puntal inputs, this is equivalent to computing + /// the conventional distance. + /// In the case of polygonal inputs, this is equivalent to computing the + /// distance to the polygon boundaries. + /// + /// \param g a Geometry, which may be of any type. + IndexedFacetDistance(const geom::Geometry* g) : + cachedTree(FacetSequenceTreeBuilder::build(g)), + baseGeometry(*g) + {} + + /// \brief Computes the distance between facets of two geometries. + /// + /// For geometries with many segments or points, this can be faster than + /// using a simple distance algorithm. + /// + /// \param g1 a geometry + /// \param g2 a geometry + /// \return the distance between facets of the geometries + static double distance(const geom::Geometry* g1, const geom::Geometry* g2); + + /// \brief Computes the nearest points of the facets of two geometries. + /// + /// \param g1 a geometry + /// \param g2 a geometry + /// \return the nearest points on the facets of the geometries + static std::unique_ptr nearestPoints(const geom::Geometry* g1, const geom::Geometry* g2); + + /// \brief Computes the distance from the base geometry to the given geometry. + /// + /// \param g the geometry to compute the distance to + /// + /// \return the computed distance + double distance(const geom::Geometry* g) const; + + /// \brief Tests whether the base geometry lies within a specified distance of the given geometry. + /// + /// \param g the geometry to test + /// \param maxDistance the maximum distance to test + /// + /// \return true of the geometry lies within the specified distance + bool isWithinDistance(const geom::Geometry* g, double maxDistance) const; + + /// \brief Computes the nearest locations on the base geometry and the given geometry. + /// + /// \param g the geometry to compute the nearest location to + /// \return the nearest locations + std::vector nearestLocations(const geom::Geometry* g) const; + + /// \brief Compute the nearest locations on the target geometry and the given geometry. + /// + /// \param g the geometry to compute the nearest point to + /// \return the nearest points + std::unique_ptr nearestPoints(const geom::Geometry* g) const; + +private: + struct FacetDistance { + double operator()(const FacetSequence* a, const FacetSequence* b) const + { + return a->distance(*b); + } + }; + + std::unique_ptr> cachedTree; + const geom::Geometry& baseGeometry; + +}; +} +} +} + diff --git a/Sources/geos/include/geos/operation/grid/Cell.h b/Sources/geos/include/geos/operation/grid/Cell.h new file mode 100644 index 0000000..a58ffc8 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Cell.h @@ -0,0 +1,107 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +namespace geos::operation::grid { + +/** + * @brief The Cell class stores information about the spatial extent of a `Grid` cell and + * any cases where a line crosses that cell (recorded in a `Traversal`). + */ +class Cell +{ + + public: + Cell(double xmin, double ymin, double xmax, double ymax) + : m_box{ xmin, ymin, xmax, ymax } + { + } + + explicit Cell(const geom::Envelope& b) + : m_box{ b } + { + } + + const geom::Envelope& box() const { return m_box; } + + double getWidth() const; + + double getHeight() const; + + double getArea() const; + + /// Force the last Coordinate processed (via `take`) to be considered as an + /// exit point, provided that it lies on the boundary of this Cell. + void forceExit(); + + /// Returns whether the cell can be determined to be wholly or partially + /// covered by a polygon. + bool isDetermined() const; + + /// Return the total length of a linear geometry within this Cell + double getTraversalLength() const; + + /// Return the fraction of this Cell that is covered by a polygon + double getCoveredFraction() const; + + /// Return a newly constructed geometry representing the portion of this Cell + /// that is covered by a polygon + std::unique_ptr getCoveredPolygons(const geom::GeometryFactory&) const; + + /// Return the last (most recent) traversal to which a coordinate has been + /// added. The traversal may or may not be completed. + Traversal& getLastTraversal(); + + /** + * Attempt to take a coordinate and add it to a Traversal in progress, or start a new Traversal + * + * @param c Coordinate to process + * @param prev_original The last *uninterpolated* coordinate preceding `c` in the + * boundary being processed + * + * @return `true` if the Coordinate was inside this cell, `false` otherwise + */ + bool take(const geom::CoordinateXY& c, const geom::CoordinateXY* prev_original = nullptr); + + private: + std::vector*> getCoordLists() const; + + enum class Location + { + INSIDE, + OUTSIDE, + BOUNDARY + }; + + geom::Envelope m_box; + + std::vector m_traversals; + + Side side(const geom::CoordinateXY& c) const; + + Location location(const geom::CoordinateXY& c) const; + + /// If no Traversals have been started or the most recent Traversal has been completed, + /// return a new Traversal. Otherwise, return the most recent Traversal. + Traversal& traversal_in_progress(); +}; + +} diff --git a/Sources/geos/include/geos/operation/grid/Crossing.h b/Sources/geos/include/geos/operation/grid/Crossing.h new file mode 100644 index 0000000..252daa6 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Crossing.h @@ -0,0 +1,52 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos::operation::grid { + +class Crossing +{ + public: + Crossing(Side s, double x, double y) + : m_side{ s } + , m_coord{ x, y } + { + } + + Crossing(Side s, const geom::CoordinateXY& c) + : m_side{ s } + , m_coord{ c } + { + } + + const Side& getSide() const + { + return m_side; + } + + const geom::CoordinateXY& getCoord() const + { + return m_coord; + } + + private: + Side m_side; + geom::CoordinateXY m_coord; +}; + +} diff --git a/Sources/geos/include/geos/operation/grid/FloodFill.h b/Sources/geos/include/geos/operation/grid/FloodFill.h new file mode 100644 index 0000000..4a769cb --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/FloodFill.h @@ -0,0 +1,144 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos::geom { + class Geometry; +} + +namespace geos::algorithm::locate { + class PointOnGeometryLocator; +} + +namespace geos::operation::grid { + +template +struct fill_values +{ + static T EXTERIOR; +}; + +template<> +struct fill_values +{ + static constexpr float EXTERIOR{ 0.0f }; // Cell is known to be entirely outside the polygon + static constexpr float INTERIOR{ 1.0f }; // Cell is known to be entirely within the polygon + static constexpr float FILLABLE{ -1.0f }; // Cell location relative to polygon unknown, but + // can be determined by fill. + static constexpr float UNKNOWN{ -2.0f }; // Cell location relative to polygon unknown + // and cannot be determined from a flood fill + // (must be explicitly tested) +}; + +class FloodFill +{ + + public: + FloodFill(const geom::Geometry& g, const Grid& extent); + + ~FloodFill(); + + template + void flood(Matrix& arr) const; + + bool cellIsInside(size_t i, size_t j) const; + + private: + Grid m_extent; + const geom::Geometry& m_g; + std::unique_ptr m_loc; +}; + +template +void +floodFromPixel(Matrix& arr, size_t i, size_t j, T fill_value) +{ + std::queue> locations; + + locations.emplace(i, j); + + while (!locations.empty()) { + i = locations.front().first; + j = locations.front().second; + locations.pop(); + + if (arr(i, j) == fill_value) { + continue; + } + + // Left + if (j > 0 && arr(i, j - 1) == fill_values::FILLABLE) { + locations.emplace(i, j - 1); + } + + auto j0 = j; + + // Fill along this row until we hit something + for (; j < arr.getNumCols() && arr(i, j) == fill_values::FILLABLE; j++) { + arr(i, j) = fill_value; + } + + auto j1 = j; + + // Initiate scanlines above our current row + if (i > 0) { + for (j = j0; j < j1; j++) { + // Up + if (arr(i - 1, j) == fill_values::FILLABLE) { + locations.emplace(i - 1, j); + } + } + } + + // Initiate scanlines below our current row + if (i < arr.getNumRows() - 1) { + for (j = j0; j < j1; j++) { + // Down + if (arr(i + 1, j) == fill_values::FILLABLE) { + locations.emplace(i + 1, j); + } + } + } + } +} + +template +void +FloodFill::flood(Matrix& arr) const +{ + + for (size_t i = 0; i < arr.getNumRows(); i++) { + for (size_t j = 0; j < arr.getNumCols(); j++) { + if (arr(i, j) == fill_values::UNKNOWN) { + throw std::runtime_error("Cell with unknown position encountered."); + } else if (arr(i, j) == fill_values::FILLABLE) { + // Cell position relative to polygon is unknown but can + // be determined from adjacent cells. + if (cellIsInside(i, j)) { + floodFromPixel(arr, i, j, fill_values::INTERIOR); + } else { + floodFromPixel(arr, i, j, fill_values::EXTERIOR); + } + } + } + } +} + +} diff --git a/Sources/geos/include/geos/operation/grid/Grid.h b/Sources/geos/include/geos/operation/grid/Grid.h new file mode 100644 index 0000000..b18b84b --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Grid.h @@ -0,0 +1,284 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +constexpr double DEFAULT_GRID_COMPAT_TOL = 1e-6; + +namespace geos::operation::grid { +struct infinite_extent +{ + static constexpr size_t padding = 1; +}; + +struct bounded_extent +{ + static constexpr size_t padding = 0; +}; + +template +class Grid +{ + + public: + Grid(const geom::Envelope& extent, double dx, double dy) + : m_extent{ extent } + , m_domain{} + , m_dx{ dx } + , m_dy{ dy } + , m_num_rows{ 2 * extent_tag::padding + (extent.getMaxY() > extent.getMinY() ? static_cast(std::round(extent.getHeight() / dy)) : 0) } + , m_num_cols{ 2 * extent_tag::padding + (extent.getMaxX() > extent.getMinX() ? static_cast(std::round(extent.getWidth() / dx)) : 0) } { + static_assert(std::is_same_v); + } + + Grid(const geom::Envelope& extent, double dx, double dy, const geom::Envelope& domain) + : m_extent{ extent } + , m_domain{ domain } + , m_dx{ dx } + , m_dy{ dy } + , m_num_rows{ 2 * extent_tag::padding + (extent.getMaxY() > extent.getMinY() ? static_cast(std::round(extent.getHeight() / dy)) : 0) } + , m_num_cols{ 2 * extent_tag::padding + (extent.getMaxX() > extent.getMinX() ? static_cast(std::round(extent.getWidth() / dx)) : 0) } + { + static_assert(std::is_same_v); + } + + + static Grid makeEmpty() + { + if constexpr (std::is_same_v) { + return Grid({ 0, 0, 0, 0 }, 0, 0); + } else { + return Grid({ 0, 0, 0, 0 }, 0, 0, geom::Envelope()); + } + } + + size_t getColumn(double x) const + { + if (extent_tag::padding) { + if (x < m_extent.getMinX()) + return 0; + if (x > m_extent.getMaxX()) + return m_num_cols - 1; + if (x == m_extent.getMaxX()) // special case, returning the cell for which xmax is the right + return m_num_cols - 2; + } else { + if (x < m_extent.getMinX() || x > m_extent.getMaxX()) + throw std::out_of_range("x"); + + if (x == m_extent.getMaxX()) + return m_num_cols - 1; + } + + // Since we've already range-checked our x value, make sure that + // the computed column index is no greater than the column index + // associated with xmax. + return std::min( + extent_tag::padding + static_cast(std::floor((x - m_extent.getMinX()) / m_dx)), + getColumn(m_extent.getMaxX())); + } + + size_t getRow(double y) const + { + if (extent_tag::padding) { + if (y > m_extent.getMaxY()) + return 0; + if (y < m_extent.getMinY()) + return m_num_rows - 1; + if (y == m_extent.getMinY()) // special case, returning the cell for which ymin is the bottom + return m_num_rows - 2; + } else { + if (y < m_extent.getMinY() || y > m_extent.getMaxY()) + throw std::out_of_range("y"); + + if (y == m_extent.getMinY()) + return m_num_rows - 1; + } + + // Since we've already range-checked our y value, make sure that + // the computed row index is no greater than the column index + // associated with ymin. + return std::min( + extent_tag::padding + static_cast(std::floor((m_extent.getMaxY() - y) / m_dy)), + getRow(m_extent.getMinY())); + } + + std::size_t getCell(double x, double y) const + { + return getRow(y) * getNumCols() + getColumn(x); + } + + bool isEmpty() const { return m_num_rows <= 2 * extent_tag::padding && m_num_cols <= 2 * extent_tag::padding; } + + size_t getNumRows() const { return m_num_rows; } + + size_t getNumCols() const { return m_num_cols; } + + size_t getSize() const { return getNumRows() * getNumCols(); } + + double xmin() const { return m_extent.getMinX(); } + + double xmax() const { return m_extent.getMaxX(); } + + double ymin() const { return m_extent.getMinY(); } + + double ymax() const { return m_extent.getMaxY(); } + + double dx() const { return m_dx; } + + double dy() const { return m_dy; } + + const geom::Envelope& getExtent() const { return m_extent; } + + size_t getRowOffset(const Grid& other) const { return static_cast(std::round(std::abs(other.m_extent.getMaxY() - m_extent.getMaxY()) / m_dy)); } + + size_t getColOffset(const Grid& other) const { return static_cast(std::round(std::abs(m_extent.getMinX() - other.m_extent.getMinX()) / m_dx)); } + + double getColX(size_t col) const { return m_extent.getMinX() + (static_cast(col - extent_tag::padding) + 0.5) * m_dx; } + + double getRowY(size_t row) const { return m_extent.getMaxY() - (static_cast(row - extent_tag::padding) + 0.5) * m_dy; } + + Grid shrinkToFit(const geom::Envelope& b) const + { + if (b.getArea() == 0) { + return makeEmpty(); + } + + if (b.getMinX() < m_extent.getMinX() || b.getMinY() < m_extent.getMinY() || b.getMaxX() > m_extent.getMaxX() || b.getMaxY() > m_extent.getMaxY()) { + throw std::range_error("Cannot shrink extent to bounds larger than original."); + } + + size_t col0 = getColumn(b.getMinX()); + size_t row1 = getRow(b.getMaxY()); + + // Shrink xmin and ymax to fit the upper-left corner of the supplied extent + double snapped_xmin = m_extent.getMinX() + static_cast(col0 - extent_tag::padding) * m_dx; + double snapped_ymax = m_extent.getMaxY() - static_cast(row1 - extent_tag::padding) * m_dy; + + // Make sure x0 and y1 are within the reduced extent. Because of + // floating point round-off errors, this is not always the case. + if (b.getMinX() < snapped_xmin) { + snapped_xmin -= m_dx; + col0--; + } + + if (b.getMaxY() > snapped_ymax) { + snapped_ymax += m_dy; + row1--; + } + + size_t col1 = getColumn(b.getMaxX()); + size_t row0 = getRow(b.getMinY()); + + size_t num_rows = 1 + (row0 - row1); + size_t num_cols = 1 + (col1 - col0); + + // If xmax or ymin falls cleanly on a cell boundary, we don't + // need as many rows or columns as we otherwise would, because + // we assume that the rightmost cell of the grid is a closed + // interval in x, and the lowermost cell of the grid is a + // closed interval in y. + if (num_rows > 2 && (snapped_ymax - static_cast(num_rows - 1) * m_dy <= b.getMinY())) { + num_rows--; + } + if (num_cols > 2 && (snapped_xmin + static_cast(num_cols - 1) * m_dx >= b.getMaxX())) { + num_cols--; + } + + // Perform offsets relative to the new xmin, ymax origin + // points. If this is not done, then floating point roundoff + // error can cause progressive shrink() calls with the same + // inputs to produce different results. + geom::Envelope reduced_box = { + snapped_xmin, + std::max(snapped_xmin + static_cast(num_cols) * m_dx, b.getMaxX()), + std::min(snapped_ymax - static_cast(num_rows) * m_dy, b.getMinY()), + snapped_ymax + }; + + // Fudge computed xmax and ymin, if needed, to prevent extent + // from growing during a shrink operation. + if (reduced_box.getMaxX() > m_extent.getMaxX()) { + if (std::round(reduced_box.getWidth() / m_dx) == + std::round((m_extent.getMaxX() - reduced_box.getMinX()) / m_dx)) { + reduced_box = geom::Envelope(reduced_box.getMinX(), m_extent.getMaxX(), reduced_box.getMinY(), reduced_box.getMaxY()); + } else { + throw std::runtime_error("Shrink operation failed."); + } + } + if (reduced_box.getMinY() < m_extent.getMinY()) { + if (std::round(reduced_box.getHeight() / m_dy) == + std::round((reduced_box.getMaxY() - m_extent.getMinY()) / m_dy)) { + reduced_box = geom::Envelope(reduced_box.getMinX(), reduced_box.getMaxX(), m_extent.getMinY(), reduced_box.getMaxY()); + } else { + throw std::runtime_error("Shrink operation failed."); + } + } + + if constexpr (std::is_same_v) { + Grid reduced{ reduced_box, m_dx, m_dy }; + + if (b.getMinX() < reduced.xmin() || b.getMinY() < reduced.ymin() || b.getMaxX() > reduced.xmax() || b.getMaxY() > reduced.ymax()) { + throw std::runtime_error("Shrink operation failed."); + } + + return reduced; + } else { + Grid reduced{ reduced_box, m_dx, m_dy , m_domain }; + + if (b.getMinX() < reduced.xmin() || b.getMinY() < reduced.ymin() || b.getMaxX() > reduced.xmax() || b.getMaxY() > reduced.ymax()) { + throw std::runtime_error("Shrink operation failed."); + } + + return reduced; + + } + } + + bool operator==(const Grid& b) const + { + return m_extent == b.m_extent && + m_dx == b.m_dx && + m_dy == b.m_dy; + } + + bool operator!=(const Grid& b) const + { + return !(*this == b); + } + + geom::Envelope getCellEnvelope(size_t row, size_t col) const; + + private: + geom::Envelope m_extent; + geom::Envelope m_domain; + + double m_dx; + double m_dy; + + size_t m_num_rows; + size_t m_num_cols; +}; + +Grid +make_infinite(const Grid& grid, const geom::Envelope& domain); +Grid +make_finite(const Grid& grid); + +} diff --git a/Sources/geos/include/geos/operation/grid/GridIntersection.h b/Sources/geos/include/geos/operation/grid/GridIntersection.h new file mode 100644 index 0000000..e85d401 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/GridIntersection.h @@ -0,0 +1,106 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos::geom { + class Geometry; + class LineString; +} + +namespace geos::operation::grid { + +class Cell; + +/** + * @brief The GridIntersection class computes and stores information about intersection of a Grid and a Geometry. + * For a polygonal geometry, the fraction of each grid cell covered by polygon is stored. + * For a linear geometry, the length of the line in each grid cell is stored. + * + * Intersection information returned as `Raster` with an equivalent resolution to the input `Grid`. The spatial + * extent may be reduced from the input `Grid.` + */ +class GEOS_DLL GridIntersection +{ + + public: + /** + * @brief Compute the fraction of each cell in a rectangular grid that is covered by a Geometry. + * A matrix can be provided to which the fractions will be added. + */ + GridIntersection(const Grid& raster_grid, const geom::Geometry& g, std::shared_ptr> cov = nullptr); + + /** + * @brief Compute the fraction of each cell in a rectangular grid that is covered by an Envelope. + * A matrix can be provided to which the fractions will be added. + */ + GridIntersection(const Grid& raster_grid, const geom::Envelope& box, std::shared_ptr> cov = nullptr); + + /** + * @brief Return the intersection result matrix + */ + std::shared_ptr> getResults() { return m_results; } + + /** + * @brief Partition a polygonal geometry by a grid + */ + static std::unique_ptr subdividePolygon(const Grid& p_grid, const geom::Geometry& g, bool includeExterior); + + static std::shared_ptr> + getIntersectionFractions(const Grid& raster_grid, const geom::Geometry& g); + + static std::shared_ptr> + getIntersectionFractions(const Grid& raster_grid, const geom::Envelope& box); + + /** + * @brief Determines the bounding box of the raster-vector intersection. Considers the bounding boxes + * of individual polygon components separately to avoid unnecessary computation for sparse + * multi-polygons. + * + * @param raster_extent Envelope representing the extent of the vector + * @param geometry Geometry to be processed + * + * @return the portion of `raster_extent` that intersects one or more `component_boxes` + */ + static geom::Envelope + processingRegion(const geom::Envelope& raster_extent, const geom::Geometry& geometry); + + private: + void process(const geom::Geometry& g); + + void processLine(const geom::LineString& ls, bool exterior_ring); + + void processRectangularRing(const geom::Envelope& box, bool exterior_ring); + + void addRingResults(size_t i0, size_t j0, const Matrix& areas, bool exterior_ring); + + void setAreal(bool areal); + + static Matrix collectAreas(const Matrix>& cells, + const Grid& finite_ring_grid, + const geom::Geometry& g); + + Grid m_geometry_grid; + std::shared_ptr> m_results; + bool m_first_geom; + bool m_areal; +}; + +} diff --git a/Sources/geos/include/geos/operation/grid/Matrix.h b/Sources/geos/include/geos/operation/grid/Matrix.h new file mode 100644 index 0000000..67d49d9 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Matrix.h @@ -0,0 +1,185 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos::operation::grid { + +template +class Matrix +{ + + public: + using value_type = T; + + Matrix(size_t rows, size_t cols, std::shared_ptr data) + : m_data{ data } + , m_rows{ rows } + , m_cols{ cols } + {} + + Matrix(size_t rows, size_t cols) + : m_rows{ rows } + , m_cols{ cols } + { + if (m_rows > 0 && m_cols > 0) { + // new T[]() initializes to zero + m_data = std::shared_ptr(new T[m_rows * m_cols]()); + } + } + + Matrix(size_t rows, size_t cols, T value) + : m_rows{ rows } + , m_cols{ cols } + { + if (m_rows > 0 && m_cols > 0) { + // new T[] does not initialize + m_data = std::shared_ptr(new T[m_rows * m_cols]); + } + + std::fill(m_data.get(), m_data.get() + m_rows * m_cols, value); + } + + explicit Matrix(const std::vector>& data) + : m_rows{ data.size() } + , m_cols{ data[0].size() } + { + m_data = std::shared_ptr(new T[m_rows * m_cols]()); + + auto lastpos = m_data.get(); + for (auto& row : data) { + lastpos = std::copy(row.begin(), row.end(), lastpos); + } + } + + Matrix(Matrix&& m) noexcept + : m_rows{ m.getNumRows() } + , m_cols{ m.getNumCols() } + { + m_data = std::move(m.m_data); + } + + T& operator()(size_t row, size_t col) + { + check(row, col); + return m_data.get()[row * m_cols + col]; + } + + const T& operator()(size_t row, size_t col) const + { + check(row, col); + return m_data.get()[row * m_cols + col]; + } + + bool operator!=(const Matrix& other) const { + return !(*this == other); + } + + bool operator==(const Matrix& other) const + { + if (m_rows != other.m_rows) { + return false; + } + if (m_cols != other.m_cols) { + return false; + } + + return 0 == memcmp(m_data.get(), other.m_data.get(), m_rows * m_cols * sizeof(T)); + } + + void increment(size_t row, size_t col, const T& val) + { + check(row, col); + m_data.get()[row * m_cols + col] += val; + } + + size_t getNumRows() const { return m_rows; } + size_t getNumCols() const { return m_cols; } + + T* row(size_t row) + { + return &(m_data[row * m_cols]); + } + + T* data() + { + return m_data.get(); + } + + const T* data() const + { + return m_data.get(); + } + + T* release() + { + return m_data.release(); + } + + const T* begin() const { + return m_data.get(); + } + + const T* end() const { + return m_data.get() + m_rows * m_cols; + } + +#ifdef MATRIX_CHECK_BOUNDS + void check(size_t row, size_t col) const + { + if (row + 1 > m_rows) { + throw std::out_of_range("Row " + std::to_string(row) + " is out of range."); + } + if (col + 1 > m_cols) { + throw std::out_of_range("Col " + std::to_string(col) + " is out of range."); + } + } +#else + void check(size_t, size_t) const + { + } +#endif + + private: + std::shared_ptr m_data; + + size_t m_rows; + size_t m_cols; +}; + +template +std::ostream& +operator<<(std::ostream& os, const Matrix& m) +{ + for (size_t i = 0; i < m.getNumRows(); i++) { + for (size_t j = 0; j < m.getNumCols(); j++) { + if (m(i, j) != 0) { + os << std::right << std::fixed << std::setw(10) << std::setprecision(6) << m(i, j) << " "; + } else { + os << " "; + } + } + os << std::endl; + } + + return os; +} + +} diff --git a/Sources/geos/include/geos/operation/grid/PerimeterDistance.h b/Sources/geos/include/geos/operation/grid/PerimeterDistance.h new file mode 100644 index 0000000..86691d2 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/PerimeterDistance.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos::geom { + class CoordinateXY; + class Envelope; +} + +namespace geos::operation::grid { + +class GEOS_DLL PerimeterDistance { + public: + + /** Calculates the clockwise distance along the edge of an Envelope + * from the lower left corner of the Envelope to a supplied Coordinate + * lying exactly on the boundary of the Envelope. + */ + static double + getPerimeterDistance(const geom::Envelope& env, const geom::CoordinateXY& c); + + static double + getPerimeterDistance(double xmin, double ymin, double xmax, double ymax, double x, double y); + + /** Calculates the counter-clockwise distance between two locations on the perimeter + * of an Envelope. The locations are specified as clockwise distances from the + * lower left corner of the Envelope, consistent with the `perimeter_distance` + * function. + */ + static double + getPerimeterDistanceCCW(double measure1, double measure2, double perimeter); + +}; + +} diff --git a/Sources/geos/include/geos/operation/grid/Side.h b/Sources/geos/include/geos/operation/grid/Side.h new file mode 100644 index 0000000..b9015f7 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Side.h @@ -0,0 +1,33 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos::operation::grid { + +enum class Side +{ + NONE, + LEFT, + RIGHT, + TOP, + BOTTOM +}; + +std::ostream& +operator<<(std::ostream& os, const Side& s); + +} diff --git a/Sources/geos/include/geos/operation/grid/Traversal.h b/Sources/geos/include/geos/operation/grid/Traversal.h new file mode 100644 index 0000000..68bc516 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/Traversal.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +namespace geos::operation::grid { + +/** + * @brief The Traversal class records the coordinates of a line that are + * within a grid cell, as well as the `Side` from which the line + * entered and exited the cell. + */ +class Traversal +{ + public: + Traversal() + : m_entry{ Side::NONE } + , m_exit{ Side::NONE } + { + } + + bool isClosedRing() const; + + bool isEmpty() const; + + bool isEntered() const; + + bool isExited() const; + + bool isTraversed() const; + + bool hasMultipleUniqueCoordinates() const; + + /// Begin a Traversal on the specified `Side` + void enter(const geom::CoordinateXY& c, Side s); + + /// Complete a Traversal on the specified `Side` + void exit(const geom::CoordinateXY& c, Side s); + + Side getEntrySide() const { return m_entry; } + + Side getExitSide() const { return m_exit; } + + const geom::CoordinateXY& getLastCoordinate() const; + + const geom::CoordinateXY& getExitCoordinate() const; + + void add(const geom::CoordinateXY& c); + + void forceExit(Side s) { m_exit = s; } + + const std::vector& getCoordinates() const { return m_coords; } + + private: + std::vector m_coords; + Side m_entry; + Side m_exit; +}; + +} diff --git a/Sources/geos/include/geos/operation/grid/TraversalAreas.h b/Sources/geos/include/geos/operation/grid/TraversalAreas.h new file mode 100644 index 0000000..5d660c9 --- /dev/null +++ b/Sources/geos/include/geos/operation/grid/TraversalAreas.h @@ -0,0 +1,53 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2018-2025 ISciences, LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos::operation::grid { + +class GEOS_DLL TraversalAreas { +public: + /** + * @brief Return the total area of counter-clockwise closed rings formed by this box and the provided Coordinate sequences + * + * @param box boundary of the area to consider (Cell) + * @param coord_lists vector of Coordinate vectors representing points that traverse `box`. Either the first and + * last coordinate of each vector must lie on the boundary of `box`, or the coordinates + * must form a closed ring that does not intersect the boundary of `box.` Clockwise-oriented + * closed rings will be considered holes. + * @return total area + */ + static double + getLeftHandArea(const geom::Envelope& box, const std::vector*>& coord_lists); + + /** + * @brief Return an areal geometry representing the closed rings formed by this box and the provided Coordinate sequences + * + * @param gfact GeometryFactory to create result geometry + * @param box boundary of the area to consider (Cell) + * @param coord_lists vector of Coordinate vectors representing points that traverse `box`. Either the first and + * last coordinate of each vector must lie on the boundary of `box`, or the coordinates + * must form a closed ring that does not intersect the boundary of `box.` Clockwise-oriented + * closed rings will be considered holes. + * @return a Polygon or MultiPolygon geometry + */ + static std::unique_ptr + getLeftHandRings(const geom::GeometryFactory& gfact, const geom::Envelope& box, const std::vector*>& coord_lists); + +}; + +} diff --git a/Sources/geos/include/geos/operation/intersection/Rectangle.h b/Sources/geos/include/geos/operation/intersection/Rectangle.h new file mode 100644 index 0000000..683c051 --- /dev/null +++ b/Sources/geos/include/geos/operation/intersection/Rectangle.h @@ -0,0 +1,236 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2014 Mika Heiskanen + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +class Polygon; +class LinearRing; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace intersection { // geos::operation::intersection + +/** + * \brief Clipping rectangle + * + * A clipping rectangle defines the boundaries of the rectangle + * by defining the limiting x- and y-coordinates. The clipping + * rectangle must be non-empty. In addition, methods are provided + * for specifying the location of a given coordinate with respect + * to the clipping rectangle similarly to the Cohen-Sutherland + * clipping algorithm. + * + */ + +class GEOS_DLL Rectangle { +public: + + /** + * \brief Construct a clipping rectangle + * + * @param x1 x-coordinate of the left edge + * @param y1 y-coordinate of the bottom edge + * @param x2 x-coordinate of the right edge + * @param y2 y-coordinate of the right edge + * @throws IllegalArgumentException if the rectangle is empty + */ + + Rectangle(double x1, double y1, double x2, double y2); + + /** + * @return the minimum x-coordinate of the rectangle + */ + double + xmin() const + { + return xMin; + } + + /** + * @return the minimum y-coordinate of the rectangle + */ + + double + ymin() const + { + return yMin; + } + + + /** + * @return the maximum x-coordinate of the rectangle + */ + + double + xmax() const + { + return xMax; + } + + + /** + * @return the maximum y-coordinate of the rectangle + */ + + double + ymax() const + { + return yMax; + } + + /** + * @return the rectangle as a polygon geometry + * + * @note Ownership transferred to caller + */ + std::unique_ptr toPolygon(const geom::GeometryFactory& f) const; + + std::unique_ptr toLinearRing(const geom::GeometryFactory& f) const; + + /** + * @brief Position with respect to a clipping rectangle + */ + + enum Position { + Inside = 1, + Outside = 2, + + Left = 4, + Top = 8, + Right = 16, + Bottom = 32, + + TopLeft = Top | Left, // 12 + TopRight = Top | Right, // 24 + BottomLeft = Bottom | Left, // 36 + BottomRight = Bottom | Right // 48 + }; + + /** + * @brief Test if the given position is on a Rectangle edge + * @param pos Rectangle::Position + * @return `true`, if the position is on an edge + */ + + static bool + onEdge(Position pos) + { + return (pos > Outside); + } + + /** + * @brief Test if the given positions are on the same Rectangle edge + * @param pos1 [Position](@ref Rectangle::Position) of first coordinate + * @param pos2 [Position](@ref Rectangle::Position) of second coordinate + * @return `true`, if the positions are on the same edge + */ + + static bool + onSameEdge(Position pos1, Position pos2) + { + return onEdge(Position(pos1 & pos2)); + } + + /** + * @brief Establish position of coordinate with respect to a Rectangle + * @param x x-coordinate + * @param y y-coordinate + * @return [Position](@ref Rectangle::Position) of the coordinate + */ + + Position + position(double x, double y) const + { + // We assume the point to be inside and test it first + if(x > xMin && x < xMax && y > yMin && y < yMax) { + return Inside; + } + // Next we assume the point to be outside and test it next + if(x < xMin || x > xMax || y < yMin || y > yMax) { + return Outside; + } + // Slower cases + unsigned int pos = 0; + if(x == xMin) { + pos |= Left; + } + else if(x == xMax) { + pos |= Right; + } + if(y == yMin) { + pos |= Bottom; + } + else if(y == yMax) { + pos |= Top; + } + return Position(pos); + } + + /** + * @brief Next edge in clock-wise direction + * @param pos [Position](@ref Rectangle::Position) + * @return next [Position](@ref Rectangle::Position) in clock-wise direction + */ + + static Position + nextEdge(Position pos) + { + switch(pos) { + case BottomLeft: + case Left: + return Top; + case TopLeft: + case Top: + return Right; + case TopRight: + case Right: + return Bottom; + case BottomRight: + case Bottom: + return Left; + /* silences compiler warnings, Inside & Outside are not handled explicitly */ + default: + return pos; + } + } + +private: + + Rectangle(); + double xMin; + double yMin; + double xMax; + double yMax; + +}; // class RectangleIntersection + +} // namespace geos::operation::intersection +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/intersection/RectangleIntersection.h b/Sources/geos/include/geos/operation/intersection/RectangleIntersection.h new file mode 100644 index 0000000..d3c500a --- /dev/null +++ b/Sources/geos/include/geos/operation/intersection/RectangleIntersection.h @@ -0,0 +1,174 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2014 Mika Heiskanen + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Point; +class MultiPoint; +class Polygon; +class MultiPolygon; +class LineString; +class MultiLineString; +class Geometry; +class GeometryCollection; +class GeometryFactory; +} +namespace operation { +namespace intersection { +class Rectangle; +class RectangleIntersectionBuilder; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace intersection { // geos::operation::intersection + +/** + * \brief + * Speed-optimized clipping of a [Geometry](@ref geom::Geometry) with a rectangle. + * + * Two different methods are provided. The first performs normal + * clipping, the second clips the boundaries of polygons, not + * the polygons themselves. In the first case a polygon will remain + * a polygon or is completely cut out. In the latter case polygons + * will be converted to polylines if any vertex is outside the clipping + * rectangle, or will be cut out completely. + * + * The algorithm works best when the number of intersections is very low. + * For example, if the geometry is completely to the left of the + * clipping rectangle, only the x-coordinate of the geometry is ever + * tested and is only compared with the x-coordinate of the left edge + * of the rectangle. Hence clipping may be faster than calculating + * the envelope of the geometry for trivial overlap tests. + * + * The input geometry must be valid. In particular all [LinearRings](@ref geom::LinearRing) + * must be properly closed, or the algorithm may not terminate. + * + */ +class GEOS_DLL RectangleIntersection { +public: + + /** + * \brief Clip geometry with a rectangle. + * + * @param geom a [Geometry](@ref geom::Geometry) + * @param rect a Rectangle + * @return the clipped geometry + * @return `NULL` if the geometry is outside the Rectangle + */ + static std::unique_ptr clip(const geom::Geometry& geom, + const Rectangle& rect); + + /** + * \brief Clip boundary of a geometry with a rectangle. + * + * Any polygon which intersects the rectangle will be converted to + * a polyline or a multipolyline - including the holes. + * + * @param geom a [Geometry](@ref geom::Geometry) + * @param rect a Rectangle + * @return the clipped geometry + * @return `NULL` if the geometry is outside the Rectangle + */ + static std::unique_ptr clipBoundary(const geom::Geometry& geom, + const Rectangle& rect); + +private: + + RectangleIntersection(const geom::Geometry& geom, const Rectangle& rect); + + std::unique_ptr clipBoundary(); + + std::unique_ptr clip(); + + const geom::Geometry& _geom; + const Rectangle& _rect; + const geom::GeometryFactory* _gf; + + void clip_geom(const geom::Geometry* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect, + bool keep_polygons); + + void clip_point(const geom::Point* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + void clip_multipoint(const geom::MultiPoint* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + void clip_linestring(const geom::LineString* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + void clip_multilinestring(const geom::MultiLineString* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + void clip_polygon(const geom::Polygon* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect, + bool keep_polygons); + + void clip_multipolygon(const geom::MultiPolygon* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect, + bool keep_polygons); + + void clip_geometrycollection( + const geom::GeometryCollection* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect, + bool keep_polygons); + + void clip_polygon_to_linestrings(const geom::Polygon* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + void clip_polygon_to_polygons(const geom::Polygon* g, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + + + /** + * \brief Clip geometry. + * + * Returns true if the geometry was fully inside, and does not output + * anything to RectangleIntersectionBuilder. + */ + bool clip_linestring_parts(const geom::LineString* gi, + RectangleIntersectionBuilder& parts, + const Rectangle& rect); + +}; // class RectangleIntersection + +} // namespace geos::operation::intersection +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/intersection/RectangleIntersectionBuilder.h b/Sources/geos/include/geos/operation/intersection/RectangleIntersectionBuilder.h new file mode 100644 index 0000000..ecdf5d7 --- /dev/null +++ b/Sources/geos/include/geos/operation/intersection/RectangleIntersectionBuilder.h @@ -0,0 +1,159 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2014 Mika Heiskanen + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class GeometryFactory; +class Polygon; +class LineString; +class Point; +} +namespace operation { +namespace intersection { +class Rectangle; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace intersection { // geos::operation::intersection + +/** + * \brief Rebuild geometries from subpaths left by clipping with a rectangle + * + * The RectangleIntersectionBuilder is used to maintain lists of polygons, + * linestrings and points left from clipping a geom::Geometry with a Rectangle. + * Once all clipping has been done, the class builds a valid geom::Geometry from + * the components. + * + * @note This is a utility class needed by RectangleIntersection, and is not + * intended for public use. + */ + +class GEOS_DLL RectangleIntersectionBuilder { + // Regular users are not supposed to use this utility class. + friend class RectangleIntersection; + +public: + + ~RectangleIntersectionBuilder(); + +private: + + /** + * \brief Build the result geometry from partial results and clean up + */ + std::unique_ptr build(); + + /** + * \brief Build polygons from parts left by clipping one + * + * 1. Build exterior ring(s) from lines + * 2. Attach polygons as holes to the exterior ring(s) + */ + void reconnectPolygons(const Rectangle& rect); + + /** + * Reconnect disjointed parts + * + * When we clip a LinearRing we may get multiple linestrings. + * Often the first and last ones can be reconnected to simplify + * output. + * + * Sample clip with a rectangle 0,0 --> 10,10 without reconnecting: + * + * Input: POLYGON ((5 10,0 0,10 0,5 10)) + * Output: MULTILINESTRING ((5 10,0 0),(10 0,5 10)) + * Desired: LINESTRING (10 0,5 10,0 0) + * + * TODO: If there is a very sharp spike from inside the rectangle + * outside, and then back in, it is possible that the + * intersection points at the edge are equal. In this + * case we could reconnect the linestrings. The task is + * the same we're already doing for the 1st/last linestrings, + * we'd just do it for any adjacent pair as well. + */ + void reconnect(); + + void reverseLines(); + + /** + * Export parts to another container + */ + void release(RectangleIntersectionBuilder& parts); + + // Adding Geometry components + void add(geom::Polygon* g); + void add(geom::LineString* g); + void add(geom::Point* g); + + // Trivial methods + bool empty() const; + void clear(); + + // Added components + std::list polygons; + std::list lines; + std::list points; + + /** + * \brief Close a ring clockwise along rectangle edges + * + * Only the 4 corners and x1,y1 need to be considered. The possible + * cases are: + * + * x1,y1 + * corner1 x1,y1 + * corner1 corner2 x1,y1 + * corner1 corner2 corner3 x1,y1 + * corner1 corner2 corner3 corner4 x1,y1 + */ + void close_boundary( + const Rectangle& rect, + geom::CoordinateSequence* ring, + double x1, double y1, + double x2, double y2); + + void close_ring(const Rectangle& rect, geom::CoordinateSequence* ring); + + RectangleIntersectionBuilder(const geom::GeometryFactory& f) + : _gf(f) {} + + const geom::GeometryFactory& _gf; + +}; // class RectangleIntersectionBuilder + +} // namespace geos::operation::intersection +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/linemerge/EdgeString.h b/Sources/geos/include/geos/operation/linemerge/EdgeString.h new file mode 100644 index 0000000..5bacf83 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/EdgeString.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/EdgeString.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class CoordinateSequence; +class LineString; +} +namespace operation { +namespace linemerge { +class LineMergeDirectedEdge; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** + * \brief + * A sequence of LineMergeDirectedEdge forming one of the lines that will + * be output by the line-merging process. + */ +class GEOS_DLL EdgeString { +private: + const geom::GeometryFactory* factory; + std::vector directedEdges; + std::unique_ptr getCoordinates() const; +public: + /** + * \brief + * Constructs an EdgeString with the given factory used to + * convert this EdgeString to a LineString + */ + EdgeString(const geom::GeometryFactory* newFactory); + + ~EdgeString() = default; + + /** + * Adds a directed edge which is known to form part of this line. + */ + void add(LineMergeDirectedEdge* directedEdge); + + /** + * Converts this EdgeString into a LineString. + */ + std::unique_ptr toLineString() const; +}; + +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/linemerge/LineMergeDirectedEdge.h b/Sources/geos/include/geos/operation/linemerge/LineMergeDirectedEdge.h new file mode 100644 index 0000000..4da09e0 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/LineMergeDirectedEdge.h @@ -0,0 +1,78 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/LineMergeDirectedEdge.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +//class LineString; +} +namespace planargraph { +class Node; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** + * \brief + * A [DirectedEdge](@ref planargraph::DirectedEdge) of a LineMergeGraph. + * + */ +class GEOS_DLL LineMergeDirectedEdge: public planargraph::DirectedEdge { +public: + /** \brief + * Constructs a LineMergeDirectedEdge connecting the `from` + * node to the `to` node. + * + * @param from `from` node + * @param to `to` node + * @param directionPt specifies this DirectedEdge's direction (given by an + * imaginary line from the `from` node to + * `directionPt`) + * @param edgeDirection whether this DirectedEdge's direction is the same as or + * opposite to that of the parent Edge (if any) + */ + LineMergeDirectedEdge(planargraph::Node* from, + planargraph::Node* to, + const geom::Coordinate& directionPt, + bool edgeDirection); + + /** \brief + * Returns the directed edge that starts at this directed edge's end point, or null + * if there are zero or multiple directed edges starting there. + * @param checkDirection only consider directed edges with same direction as parent edge + * @return the directed edge + * @return `null` if there are zero or multiple directed edges starting there + */ + LineMergeDirectedEdge* getNext(bool checkDirection = false); +}; + +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/linemerge/LineMergeEdge.h b/Sources/geos/include/geos/operation/linemerge/LineMergeEdge.h new file mode 100644 index 0000000..b4deca4 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/LineMergeEdge.h @@ -0,0 +1,62 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/LineMergeEdge.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** \brief + * An edge of a LineMergeGraph. The marked field indicates + * whether this Edge has been logically deleted from the graph. + */ +class GEOS_DLL LineMergeEdge: public planargraph::Edge { +private: + const geom::LineString* line; +public: + /** + * Constructs a LineMergeEdge with vertices given by the specified + * LineString. + */ + LineMergeEdge(const geom::LineString* newLine); + + /** + * Returns the LineString specifying the vertices of this edge. + */ + const geom::LineString* getLine() const; +}; + + +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/linemerge/LineMergeGraph.h b/Sources/geos/include/geos/operation/linemerge/LineMergeGraph.h new file mode 100644 index 0000000..c711248 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/LineMergeGraph.h @@ -0,0 +1,91 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/LineMergeGraph.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class Coordinate; +} +namespace planargraph { +class Node; +class Edge; +class DirectedEdge; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** \brief + * A planar graph of edges that is analyzed to sew the edges together. + * + * The marked flag on planargraph::Edge + * and planargraph::Node indicates whether they have been + * logically deleted from the graph. + */ +class GEOS_DLL LineMergeGraph: public planargraph::PlanarGraph { + +private: + + planargraph::Node* getNode(const geom::Coordinate& coordinate); + + std::vector newNodes; + + std::vector newEdges; + + std::vector newDirEdges; + +public: + + /** \brief + * Adds an Edge, DirectedEdges, and Nodes for the given + * LineString representation of an edge. + * + * Empty lines or lines with all coordinates equal are not added. + * + * @param lineString the linestring to add to the graph + */ + void addEdge(const geom::LineString* lineString); + + ~LineMergeGraph() override; +}; +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/linemerge/LineMerger.h b/Sources/geos/include/geos/operation/linemerge/LineMerger.h new file mode 100644 index 0000000..041ac39 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/LineMerger.h @@ -0,0 +1,149 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/LineMerger.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // for composition + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +} +namespace planargraph { +class Node; +} +namespace operation { +namespace linemerge { +class EdgeString; +class LineMergeDirectedEdge; +} +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** + * + * \brief + * Sews together a set of fully noded LineStrings. + * + * Sewing stops at nodes of degree 1 or 3 or more. + * The exception is an isolated loop, which only has degree-2 nodes, + * in which case a node is simply chosen as a starting point. + * The direction of each merged LineString will be that of the majority + * of the LineStrings from which it was derived. + * + * Any dimension of Geometry is handled. + * The constituent linework is extracted to form the edges. + * The edges must be correctly noded; that is, they must only meet + * at their endpoints. + * + * The LineMerger will still run on incorrectly noded input + * but will not form polygons from incorrected noded edges. + * + */ +class GEOS_DLL LineMerger { + +private: + + LineMergeGraph graph; + + bool isDirected; + + std::vector> mergedLineStrings; + + std::vector edgeStrings; + + const geom::GeometryFactory* factory; + + void merge(); + + void buildEdgeStringsForObviousStartNodes(); + + void buildEdgeStringsForIsolatedLoops(); + + void buildEdgeStringsForUnprocessedNodes(); + + void buildEdgeStringsForNonDegree2Nodes(); + + void buildEdgeStringsStartingAt(planargraph::Node* node); + + EdgeString* buildEdgeStringStartingWith(LineMergeDirectedEdge* start); + +public: + LineMerger(bool directed = false); + ~LineMerger(); + + /** + * \brief + * Adds a collection of Geometries to be processed. + * May be called multiple times. + * + * Any dimension of Geometry may be added; the constituent + * linework will be extracted. + */ + void add(std::vector* geometries); + + /** + * \brief + * Adds a Geometry to be processed. + * May be called multiple times. + * + * Any dimension of Geometry may be added; the constituent + * linework will be extracted. + */ + void add(const geom::Geometry* geometry); + + /** + * \brief + * Returns the LineStrings built by the merging process. + * + * Ownership of vector _and_ its elements to caller. + */ + std::vector> getMergedLineStrings(); + + void add(const geom::LineString* lineString); + + // Declare type as noncopyable + LineMerger(const LineMerger& other) = delete; + LineMerger& operator=(const LineMerger& rhs) = delete; +}; + +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/linemerge/LineSequencer.h b/Sources/geos/include/geos/operation/linemerge/LineSequencer.h new file mode 100644 index 0000000..bd7a0c8 --- /dev/null +++ b/Sources/geos/include/geos/operation/linemerge/LineSequencer.h @@ -0,0 +1,295 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/linemerge/LineSequencer.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include // for inlines +#include // for inlines + +#include +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +class LineString; +} +namespace planargraph { +class DirectedEdge; +class Subgraph; +class Node; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace linemerge { // geos::operation::linemerge + +/** \brief + * Builds a sequence from a set of LineStrings so that + * they are ordered end to end. + * + * A sequence is a complete non-repeating list of the linear + * components of the input. Each linestring is oriented + * so that identical endpoints are adjacent in the list. + * + * A typical use case is to convert a set of unoriented geometric links + * from a linear network (e.g. such as block faces on a bus route) + * into a continuous oriented path through the network. + * + * The input linestrings may form one or more connected sets. + * The input linestrings should be correctly noded, or the results may + * not be what is expected. + * The computed output is a single MultiLineString containing the ordered + * linestrings in the sequence. + * + * The sequencing employs the classic **Eulerian path** graph algorithm. + * Since Eulerian paths are not uniquely determined, further rules are used + * to make the computed sequence preserve as much as possible of the input + * ordering. Within a connected subset of lines, the ordering rules are: + * + * - If there is degree-1 node which is the start + * node of an linestring, use that node as the start of the sequence + * - If there is a degree-1 node which is the end + * node of an linestring, use that node as the end of the sequence + * - If the sequence has no degree-1 nodes, use any node as the start + * + * @note Not all arrangements of lines can be sequenced. For a connected + * set of edges in a graph, *Euler's Theorem* states that there is a sequence + * containing each edge once **if and only if** there are no more than + * 2 nodes of odd degree. If it is not possible to find a sequence, the + * `isSequenceable` method will return `false`. + * + */ +class GEOS_DLL LineSequencer { + +private: + typedef std::list DirEdgeList; + typedef std::vector< DirEdgeList* > Sequences; + + LineMergeGraph graph; + const geom::GeometryFactory* factory; + unsigned int lineCount; + bool isRun; + std::unique_ptr sequencedGeometry; + bool isSequenceableVar; + + void addLine(const geom::LineString* lineString); + void computeSequence(); + Sequences* findSequences(); + DirEdgeList* findSequence(planargraph::Subgraph& graph); + + void delAll(Sequences&); + + /** + * Builds a geometry ({@link LineString} or {@link MultiLineString} ) + * representing the sequence. + * + * @param sequences + * a vector of vectors of const planarDirectedEdges + * with LineMergeEdges as their parent edges. + * Ownership of container _and_ contents retained by caller. + * + * @return the sequenced geometry, possibly NULL + * if no sequence exists + */ + geom::Geometry* buildSequencedGeometry(const Sequences& sequences); + + static const planargraph::Node* findLowestDegreeNode( + const planargraph::Subgraph& graph); + + void addReverseSubpath(const planargraph::DirectedEdge* de, + DirEdgeList& deList, + DirEdgeList::iterator lit, + bool expectedClosed); + + /** + * Finds an {@link DirectedEdge} for an unvisited edge (if any), + * choosing the dirEdge which preserves orientation, if possible. + * + * @param node the node to examine + * @return the dirEdge found, or null + * if none were unvisited + */ + static const planargraph::DirectedEdge* findUnvisitedBestOrientedDE( + const planargraph::Node* node); + + /** + * Computes a version of the sequence which is optimally + * oriented relative to the underlying geometry. + * + * Heuristics used are: + * + * - If the path has a degree-1 node which is the start + * node of an linestring, use that node as the start of the sequence + * - If the path has a degree-1 node which is the end + * node of an linestring, use that node as the end of the sequence + * - If the sequence has no degree-1 nodes, use any node as the start + * (NOTE: in this case could orient the sequence according to the + * majority of the linestring orientations) + * + * @param seq a List of planarDirectedEdges + * @return the oriented sequence, possibly same as input if already + * oriented + */ + DirEdgeList* orient(DirEdgeList* seq); + + /** + * Reverse the sequence. + * This requires reversing the order of the dirEdges, and flipping + * each dirEdge as well + * + * @param seq a List of DirectedEdges, in sequential order + * @return the reversed sequence + */ + DirEdgeList* reverse(DirEdgeList& seq); + + /** + * Tests whether a complete unique path exists in a graph + * using Euler's Theorem. + * + * @param graph the subgraph containing the edges + * @return true if a sequence exists + */ + bool hasSequence(planargraph::Subgraph& graph); + +public: + + static geom::Geometry* + sequence(const geom::Geometry& geom) + { + LineSequencer sequencer; + sequencer.add(geom); + return sequencer.getSequencedLineStrings(); + } + + LineSequencer() + : + factory(nullptr), + lineCount(0), + isRun(false), + sequencedGeometry(nullptr), + isSequenceableVar(false) + {} + + /** \brief + * Tests whether a [Geometry](@ref geom::Geometry) is sequenced correctly. + * + * [LineStrings](@ref geom::LineString) are trivially sequenced. + * [MultiLineStrings](@ref geom::MultiLineString) are checked for + * correct sequencing. Otherwise, `isSequenced` is defined + * to be `true` for geometries that are not lineal. + * + * @param geom the geometry to test + * @return `true` if the geometry is sequenced or is not lineal + */ + static bool isSequenced(const geom::Geometry* geom); + + /** \brief + * Tests whether the arrangement of linestrings has a valid + * sequence. + * + * @return `true` if a valid sequence exists. + */ + bool + isSequenceable() + { + computeSequence(); + return isSequenceableVar; + } + + /** \brief + * Adds a [Geometry](@ref geom::Geometry) to be sequenced. + * + * May be called multiple times. + * Any dimension of Geometry may be added; the constituent + * linework will be extracted. + * + * @param geometry the geometry to add + */ + void + add(const geom::Geometry& geometry) + { + geometry.applyComponentFilter(*this); + } + + template + void + add(TargetContainer& geoms) + { + for(typename TargetContainer::const_iterator i = geoms.begin(), + e = geoms.end(); i != e; ++i) { + const geom::Geometry* g = *i; + add(*g); + } + } + + /** \brief + * Act as a GeometryComponentFilter so to extract + * the linearworks + */ + void + filter(const geom::Geometry* g) + { + if(const geom::LineString* ls = dynamic_cast(g)) { + addLine(ls); + } + } + + /** \brief + * Returns the LineString or MultiLineString + * built by the sequencing process, if one exists. + * + * @param release release ownership of computed Geometry + * @return the sequenced linestrings, + * or `null` if a valid sequence + * does not exist. + */ + geom::Geometry* + getSequencedLineStrings(bool release = 1) + { + computeSequence(); + if(release) { + return sequencedGeometry.release(); + } + else { + return sequencedGeometry.get(); + } + } +}; + +} // namespace geos::operation::linemerge +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/overlay/snap/GeometrySnapper.h b/Sources/geos/include/geos/operation/overlay/snap/GeometrySnapper.h new file mode 100644 index 0000000..63f092f --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/snap/GeometrySnapper.h @@ -0,0 +1,154 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2010 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/snap/GeometrySnapper.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +//class PrecisionModel; +class Geometry; +class CoordinateSequence; +struct GeomPtrPair; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace snap { // geos::operation::overlay::snap + +/** \brief + * Snaps the vertices and segments of a {@link geom::Geometry} + * to another Geometry's vertices. + * + * Where possible, this operation tries to avoid creating invalid geometries; + * however, it does not guarantee that output geometries will be valid. It is + * the responsibility of the caller to check for and handle invalid geometries. + * + * Because too much snapping can result in invalid + * topology being created, heuristics are used to determine the number and + * location of snapped vertices that are likely safe to snap. These heuristics + * may omit some potential snaps that are otherwise within the tolerance. + * + */ +class GEOS_DLL GeometrySnapper { + +public: + + typedef std::unique_ptr GeomPtr; + + /** + * Snaps two geometries together with a given tolerance. + * + * @param g0 a geometry to snap + * @param g1 a geometry to snap + * @param snapTolerance the tolerance to use + * @param ret the snapped geometries as a pair of smart pointers + * (output parameter) + */ + static void snap(const geom::Geometry& g0, + const geom::Geometry& g1, + double snapTolerance, geom::GeomPtrPair& ret); + + static GeomPtr snapToSelf(const geom::Geometry& g0, + double snapTolerance, bool cleanResult); + + /** + * Creates a new snapper acting on the given geometry + * + * @param g the geometry to snap + */ + GeometrySnapper(const geom::Geometry& g) + : + srcGeom(g) + { + } + + /** \brief + * Snaps the vertices in the component {@link geom::LineString}s + * of the source geometry to the vertices of the given snap geometry + * with a given snap tolerance + * + * @param g a geometry to snap the source to + * @param snapTolerance + * @return a new snapped Geometry + */ + std::unique_ptr snapTo(const geom::Geometry& g, + double snapTolerance); + + /** \brief + * Snaps the vertices in the component {@link geom::LineString}s + * of the source geometry to the vertices of itself + * with a given snap tolerance and optionally cleaning the result. + * + * @param snapTolerance + * @param cleanResult clean the result + * @return a new snapped Geometry + */ + std::unique_ptr snapToSelf(double snapTolerance, + bool cleanResult); + + /** \brief + * Estimates the snap tolerance for a Geometry, taking into account + * its precision model. + * + * @param g a Geometry + * @return the estimated snap tolerance + */ + static double computeOverlaySnapTolerance(const geom::Geometry& g); + + static double computeSizeBasedSnapTolerance(const geom::Geometry& g); + + /** \brief + * Computes the snap tolerance based on input geometries; + */ + static double computeOverlaySnapTolerance(const geom::Geometry& g1, + const geom::Geometry& g2); + + +private: + + // eventually this will be determined from the geometry topology + //static const double snapTol; // = 0.000001; + + static const double snapPrecisionFactor; // = 10e-10 + + const geom::Geometry& srcGeom; + + /// Extract target (unique) coordinates + std::unique_ptr extractTargetCoordinates( + const geom::Geometry& g); + + // Declare type as noncopyable + GeometrySnapper(const GeometrySnapper& other) = delete; + GeometrySnapper& operator=(const GeometrySnapper& rhs) = delete; +}; + + +} // namespace geos::operation::overlay::snap +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlay/snap/LineStringSnapper.h b/Sources/geos/include/geos/operation/overlay/snap/LineStringSnapper.h new file mode 100644 index 0000000..b0c29e6 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/snap/LineStringSnapper.h @@ -0,0 +1,165 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009-2010 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/snap/LineStringSnapper.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +//class PrecisionModel; +//class CoordinateSequence; +class CoordinateList; +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace snap { // geos::operation::overlay::snap + +/** \brief + * Snaps the vertices and segments of a LineString to a set + * of target snap vertices. + * + * A snapping distance tolerance is used to control where snapping is performed. + * + */ +class GEOS_DLL LineStringSnapper { + +public: + + /** + * Creates a new snapper using the given points + * as source points to be snapped. + * + * @param nSrcPts the points to snap + * @param nSnapTol the snap tolerance to use + */ + LineStringSnapper(const geom::CoordinateSequence& nSrcPts, + double nSnapTol) + : + srcPts(nSrcPts), + snapTolerance(nSnapTol), + allowSnappingToSourceVertices(false) + { + std::size_t s = srcPts.size(); + isClosed = s < 2 ? false : srcPts[0].equals2D(srcPts[s - 1]); + } + + // Snap points are assumed to be all distinct points (a set would be better, uh ?) + std::unique_ptr snapTo(const geom::Coordinate::ConstVect& snapPts); + + void + setAllowSnappingToSourceVertices(bool allow) + { + allowSnappingToSourceVertices = allow; + } + +private: + + const geom::CoordinateSequence& srcPts; + + double snapTolerance; + + bool allowSnappingToSourceVertices; + bool isClosed; + + + // Modifies first arg + void snapVertices(geom::CoordinateList& srcCoords, + const geom::Coordinate::ConstVect& snapPts); + + + // Returns snapPts.end() if no snap point is close enough (within snapTol distance) + geom::Coordinate::ConstVect::const_iterator findSnapForVertex(const geom::Coordinate& pt, + const geom::Coordinate::ConstVect& snapPts); + + /** \brief + * Snap segments of the source to nearby snap vertices. + * + * Source segments are "cracked" at a snap vertex. + * A single input segment may be snapped several times + * to different snap vertices. + * + * For each distinct snap vertex, at most one source segment + * is snapped to. This prevents "cracking" multiple segments + * at the same point, which would likely cause + * topology collapse when being used on polygonal linework. + * + * @param srcCoords the coordinates of the source linestring to be snapped + * the object will be modified (coords snapped) + * @param snapPts the target snap vertices */ + void snapSegments(geom::CoordinateList& srcCoords, + const geom::Coordinate::ConstVect& snapPts); + + /// \brief + /// Finds a src segment which snaps to (is close to) the given snap + /// point. + /// + /// Only a single segment is selected for snapping. + /// This prevents multiple segments snapping to the same snap vertex, + /// which would almost certainly cause invalid geometry + /// to be created. + /// (The heuristic approach to snapping used here + /// is really only appropriate when + /// snap pts snap to a unique spot on the src geometry.) + /// + /// Also, if the snap vertex occurs as a vertex in the src + /// coordinate list, no snapping is performed (may be changed + /// using setAllowSnappingToSourceVertices). + /// + /// @param from + /// an iterator to first point of first segment to be checked + /// + /// @param too_far + /// an iterator to last point of last segment to be checked + /// + /// @returns an iterator to the snapped segment or + /// too_far if no segment needs snapping + /// (either none within snapTol distance, + /// or one found on the snapPt) + /// + geom::CoordinateList::iterator findSegmentToSnap( + const geom::Coordinate& snapPt, + geom::CoordinateList::iterator from, + geom::CoordinateList::iterator too_far); + + geom::CoordinateList::iterator findVertexToSnap( + const geom::Coordinate& snapPt, + geom::CoordinateList::iterator from, + geom::CoordinateList::iterator too_far); + + // Declare type as noncopyable + LineStringSnapper(const LineStringSnapper& other) = delete; + LineStringSnapper& operator=(const LineStringSnapper& rhs) = delete; +}; + + +} // namespace geos::operation::overlay::snap +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlay/snap/SnapOverlayOp.h b/Sources/geos/include/geos/operation/overlay/snap/SnapOverlayOp.h new file mode 100644 index 0000000..0c2f1f3 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/snap/SnapOverlayOp.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2009 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/snap/SnapOverlayOp.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include // for dtor visibility by unique_ptr +#include + +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +struct GeomPtrPair; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace snap { // geos::operation::overlay::snap + +/** \brief + * Performs an overlay operation using snapping and enhanced precision + * to improve the robustness of the result. + * + * This class always uses snapping. + * This is less performant than the standard JTS overlay code, + * and may even introduce errors which were not present in the original data. + * For this reason, this class should only be used + * if the standard overlay code fails to produce a correct result. + * + */ +class GEOS_DLL SnapOverlayOp { + +public: + + static std::unique_ptr + overlayOp(const geom::Geometry& g0, const geom::Geometry& g1, int opCode) + { + SnapOverlayOp op(g0, g1); + return op.getResultGeometry(opCode); + } + + static std::unique_ptr + intersection(const geom::Geometry& g0, const geom::Geometry& g1) + { + return overlayOp(g0, g1, overlayng::OverlayNG::INTERSECTION); + } + + static std::unique_ptr + Union(const geom::Geometry& g0, const geom::Geometry& g1) + { + return overlayOp(g0, g1, overlayng::OverlayNG::UNION); + } + + static std::unique_ptr + difference(const geom::Geometry& g0, const geom::Geometry& g1) + { + return overlayOp(g0, g1, overlayng::OverlayNG::DIFFERENCE); + } + + static std::unique_ptr + symDifference(const geom::Geometry& g0, const geom::Geometry& g1) + { + return overlayOp(g0, g1, overlayng::OverlayNG::SYMDIFFERENCE); + } + + SnapOverlayOp(const geom::Geometry& g1, const geom::Geometry& g2) + : + geom0(g1), + geom1(g2) + { + computeSnapTolerance(); + } + + std::unique_ptr getResultGeometry(int opCode); + +private: + + void computeSnapTolerance(); + + void snap(geom::GeomPtrPair& ret); + + void removeCommonBits(const geom::Geometry& geom0, + const geom::Geometry& geom1, + geom::GeomPtrPair& ret); + + // re-adds common bits to the given geom + void prepareResult(geom::Geometry& geom); + + + const geom::Geometry& geom0; + const geom::Geometry& geom1; + + double snapTolerance; + + std::unique_ptr cbr; + + // Declare type as noncopyable + SnapOverlayOp(const SnapOverlayOp& other) = delete; + SnapOverlayOp& operator=(const SnapOverlayOp& rhs) = delete; +}; + +} // namespace geos::operation::overlay::snap +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/overlay/validate/FuzzyPointLocator.h b/Sources/geos/include/geos/operation/overlay/validate/FuzzyPointLocator.h new file mode 100644 index 0000000..253b38c --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/validate/FuzzyPointLocator.h @@ -0,0 +1,97 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/validate/FuzzyPointLocator.java rev. 1.1 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include // for unique_ptr visibility of dtor +#include // for Location::Value enum + +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace validate { // geos::operation::overlay::validate + +/** \brief + * Finds the most likely Location of a point relative to + * the polygonal components of a geometry, using a tolerance value. + * + * If a point is not clearly in the Interior or Exterior, + * it is considered to be on the Boundary. + * In other words, if the point is within the tolerance of the Boundary, + * it is considered to be on the Boundary; otherwise, + * whether it is Interior or Exterior is determined directly. + */ +class GEOS_DLL FuzzyPointLocator { + +public: + + FuzzyPointLocator(const geom::Geometry& geom, double nTolerance); + + geom::Location getLocation(const geom::Coordinate& pt); + +private: + + const geom::Geometry& g; + + double tolerance; + + algorithm::PointLocator ptLocator; + + std::unique_ptr linework; + + // this function has been obsoleted + std::unique_ptr getLineWork(const geom::Geometry& geom); + + /// Extracts linework for polygonal components. + /// + /// @param geom the geometry from which to extract + /// @return a lineal geometry containing the extracted linework + std::unique_ptr extractLineWork(const geom::Geometry& geom); + + // Declare type as noncopyable + FuzzyPointLocator(const FuzzyPointLocator& other) = delete; + FuzzyPointLocator& operator=(const FuzzyPointLocator& rhs) = delete; +}; + +} // namespace geos::operation::overlay::validate +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/overlay/validate/OffsetPointGenerator.h b/Sources/geos/include/geos/operation/overlay/validate/OffsetPointGenerator.h new file mode 100644 index 0000000..58d0c32 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/validate/OffsetPointGenerator.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/validate/OffsetPointGenerator.java rev. 1.1 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include // for unique_ptr visibility of dtor +#include // for unique_ptr visibility of dtor +#include // for use in vector + +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +//class Geometry; +//class MultiPoint; +class LineString; +//class Coordinate; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace validate { // geos::operation::overlay::validate + +/// Generates points offset from both sides of all segments in a geometry +// +class GEOS_DLL OffsetPointGenerator { + +public: + + OffsetPointGenerator(const geom::Geometry& geom, double offset); + + /// Gets the computed offset points. + std::unique_ptr< std::vector > getPoints(); + +private: + + const geom::Geometry& g; + + double offsetDistance; + + std::unique_ptr< std::vector > offsetPts; + + void extractPoints(const geom::LineString* line); + + void computeOffsets(const geom::Coordinate& p0, + const geom::Coordinate& p1); + + // Declare type as noncopyable + OffsetPointGenerator(const OffsetPointGenerator& other) = delete; + OffsetPointGenerator& operator=(const OffsetPointGenerator& rhs) = delete; +}; + +} // namespace geos::operation::overlay::validate +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/overlay/validate/OverlayResultValidator.h b/Sources/geos/include/geos/operation/overlay/validate/OverlayResultValidator.h new file mode 100644 index 0000000..92838d6 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlay/validate/OverlayResultValidator.h @@ -0,0 +1,131 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: operation/overlay/validate/OverlayResultValidator.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // composition +#include // for Location::Value type + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace overlay { // geos::operation::overlay +namespace validate { // geos::operation::overlay::validate + +/** \brief + * Validates that the result of an overlay operation is + * geometrically correct within a determined tolerance. + * + * Uses fuzzy point location to find points which are + * definitely in either the interior or exterior of the result + * geometry, and compares these results with the expected ones. + * + * This algorithm is only useful where the inputs are polygonal. + * + * This is a heuristic test, and may return false positive results + * (I.e. it may fail to detect an invalid result.) + * It should never return a false negative result, however + * (I.e. it should never report a valid result as invalid.) + * + * @see OverlayOp + */ +class GEOS_DLL OverlayResultValidator { + +public: + + static bool isValid( + const geom::Geometry& geom0, + const geom::Geometry& geom1, + int opCode, + const geom::Geometry& result); + + OverlayResultValidator( + const geom::Geometry& geom0, + const geom::Geometry& geom1, + const geom::Geometry& result); + + bool isValid(int opCode); + + geom::Coordinate& + getInvalidLocation() + { + return invalidLocation; + } + +private: + + double boundaryDistanceTolerance; + + const geom::Geometry& g0; + + const geom::Geometry& g1; + + const geom::Geometry& gres; + + FuzzyPointLocator fpl0; + + FuzzyPointLocator fpl1; + + FuzzyPointLocator fplres; + + geom::Coordinate invalidLocation; + + std::vector testCoords; + + void addTestPts(const geom::Geometry& g); + + void addVertices(const geom::Geometry& g); + + bool testValid(int overlayOp); + + bool testValid(int overlayOp, const geom::Coordinate& pt); + + bool isValidResult(int overlayOp, std::vector& location); + + static double computeBoundaryDistanceTolerance( + const geom::Geometry& g0, const geom::Geometry& g1); + + // Declare type as noncopyable + OverlayResultValidator(const OverlayResultValidator& other) = delete; + OverlayResultValidator& operator=(const OverlayResultValidator& rhs) = delete; +}; + +} // namespace geos::operation::overlay::validate +} // namespace geos::operation::overlay +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/overlayng/CoverageUnion.h b/Sources/geos/include/geos/operation/overlayng/CoverageUnion.h new file mode 100644 index 0000000..3bfb38e --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/CoverageUnion.h @@ -0,0 +1,99 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Unions a valid coverage of polygons or lines + * in an efficient way. + * + * A valid polygonal coverage is a collection of geom::Polygon + * which satisfy the following conditions: + * + * * Vector-clean - Line segments within the collection + * must either be identical or intersect only at endpoints. + * * Non-overlapping - No two polygons + * may overlap. Equivalently, polygons must be interior-disjoint. + * + * A valid linear coverage is a collection of geom::LineString + * which satisfies the Vector-clean condition. + * Note that this does not require the LineStrings to be fully noded + * - i.e. they may contain coincident linework. + * Coincident line segments are dissolved by the union. + * Currently linear output is not merged (this may be added in a future release.) + * + * Currently no checking is done to determine whether the input is a valid coverage. + * This is because coverage validation involves segment intersection detection, + * which is much more expensive than the union phase. + * If the input is not a valid coverage + * then in some cases this will be detected during processing + * and a util::TopologyException is thrown. + * Otherwise, the computation will produce output, but it will be invalid. + * + * Unioning a valid coverage implies that no new vertices are created. + * This means that a precision model does not need to be specified. + * The precision of the vertices in the output geometry is not changed. + * + * @author Martin Davis + * + * @see noding::SegmentExtractingNoder + * + */ +class GEOS_DLL CoverageUnion { + using Geometry = geos::geom::Geometry; + +private: + + CoverageUnion() + { + // No instantiation for now + }; + + + +public: + + static constexpr double AREA_PCT_DIFF_TOL = 1e-6; + + /** + * Unions a valid polygonal coverage or linear network. + * + * @param coverage a coverage of polygons or lines + * @return the union of the coverage + * + * @throws util::TopologyException in some cases if the coverage is invalid + */ + static std::unique_ptr geomunion(const Geometry* coverage); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/Edge.h b/Sources/geos/include/geos/operation/overlayng/Edge.h new file mode 100644 index 0000000..b0f670b --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/Edge.h @@ -0,0 +1,346 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Represents the underlying linework for edges in a topology graph, + * and carries the topology information + * derived from the two parent geometries. + * The edge may be the result of the merging of + * two or more edges which have the same underlying linework + * (although possibly different orientations). + * In this case the topology information is + * derived from the merging of the information in the + * source edges. + * Merged edges can occur in the following situations + * + * - Due to topology collapse caused by snapping or rounding + * of polygonal geometries. + * - Due to coincident linework in a linear input + * + * The source edges may have the same parent geometry, + * or different ones, or a mix of the two. + * + * @author mdavis + */ +class GEOS_DLL Edge { + using Location = geos::geom::Location; + +private: + + // Members + int aDim = OverlayLabel::DIM_UNKNOWN; + int aDepthDelta = 0; + bool aIsHole = false; + int bDim = OverlayLabel::DIM_UNKNOWN; + int bDepthDelta = 0; + bool bIsHole = false; + std::unique_ptr pts; + + // Methods + + /** + * Populates the label for an edge resulting from an input geometry. + * - If the edge is not part of the input, the label is left as NOT_PART + * - If input is an Area and the edge is on the boundary + * (which may include some collapses), + * edge is marked as an AREA edge and side locations are assigned + * - If input is an Area and the edge is collapsed + * (depth delta = 0), + * the label is set to COLLAPSE. + * The location will be determined later + * by evaluating the final graph topology. + * - If input is a Line edge is set to a LINE edge. + * For line edges the line location is not significant + * (since there is no parent area for which to determine location). + */ + static void initLabel(OverlayLabel& lbl, uint8_t geomIndex, int dim, int depthDelta, bool p_isHole); + + static int labelDim(int dim, int depthDelta) + { + if (dim == geom::Dimension::False) + return OverlayLabel::DIM_NOT_PART; + + if (dim == geom::Dimension::L) + return OverlayLabel::DIM_LINE; + + // assert: dim is A + bool isCollapse = (depthDelta == 0); + if (isCollapse) + return OverlayLabel::DIM_COLLAPSE; + + return OverlayLabel::DIM_BOUNDARY; + }; + + bool isHole(int index) const + { + if (index == 0) + return aIsHole; + return bIsHole; + }; + + bool isBoundary(int geomIndex) const + { + if (geomIndex == 0) + return aDim == OverlayLabel::DIM_BOUNDARY; + return bDim == OverlayLabel::DIM_BOUNDARY; + }; + + /** + * Tests whether the edge is part of a shell in the given geometry. + * This is only the case if the edge is a boundary. + */ + bool isShell(int geomIndex) const + { + if (geomIndex == 0) { + return (aDim == OverlayLabel::DIM_BOUNDARY && ! aIsHole); + } + return (bDim == OverlayLabel::DIM_BOUNDARY && ! bIsHole); + }; + + static Location locationRight(int depthDelta) + { + int sgn = delSign(depthDelta); + switch (sgn) { + case 0: return Location::NONE; + case 1: return Location::INTERIOR; + case -1: return Location::EXTERIOR; + } + return Location::NONE; + }; + + static Location locationLeft(int depthDelta) + { + // TODO: is it always safe to ignore larger depth deltas? + int sgn = delSign(depthDelta); + switch (sgn) { + case 0: return Location::NONE; + case 1: return Location::EXTERIOR; + case -1: return Location::INTERIOR; + } + return Location::NONE; + }; + + static int delSign(int depthDel) + { + if (depthDel > 0) return 1; + if (depthDel < 0) return -1; + return 0; + }; + + void copyInfo(const EdgeSourceInfo* info) + { + if (info->getIndex() == 0) { + aDim = info->getDimension(); + aIsHole = info->isHole(); + aDepthDelta = info->getDepthDelta(); + } + else { + bDim = info->getDimension(); + bIsHole = info->isHole(); + bDepthDelta = info->getDepthDelta(); + } + }; + + static bool isHoleMerged(int geomIndex, const Edge* edge1, const Edge* edge2) + { + // TOD: this might be clearer with tri-state logic for isHole? + bool isShell1 = edge1->isShell(geomIndex); + bool isShell2 = edge2->isShell(geomIndex); + bool isShellMerged = isShell1 || isShell2; + // flip since isHole is stored + return !isShellMerged; + }; + + +public: + + Edge() + : aDim(OverlayLabel::DIM_UNKNOWN) + , aDepthDelta(0) + , aIsHole(false) + , bDim(OverlayLabel::DIM_UNKNOWN) + , bDepthDelta(0) + , bIsHole(false) + , pts(nullptr) + {}; + + friend std::ostream& operator<<(std::ostream& os, const Edge& e); + + static bool isCollapsed(const geom::CoordinateSequence* pts); + + // takes ownership of pts from caller + Edge(std::unique_ptr&& p_pts, const EdgeSourceInfo* info); + + // return a clone of the underlying points + std::unique_ptr getCoordinates() + { + return pts->clone(); + }; + + // return a read-only pointer to the underlying points + const geom::CoordinateSequence* getCoordinatesRO() const + { + return pts.get(); + }; + + // release the underlying points to the caller + geom::CoordinateSequence* releaseCoordinates() + { + geom::CoordinateSequence* cs = pts.release(); + pts.reset(nullptr); + return cs; + }; + + const geom::Coordinate& getCoordinate(std::size_t index) const + { + return pts->getAt(index); + }; + + std::size_t size() const + { + return pts->size(); + }; + + bool direction() const + { + if (pts->size() < 2) { + throw util::GEOSException("Edge must have >= 2 points"); + } + + const geom::Coordinate& p0 = pts->getAt(0); + const geom::Coordinate& p1 = pts->getAt(1); + const geom::Coordinate& pn0 = pts->getAt(pts->size() - 1); + const geom::Coordinate& pn1 = pts->getAt(pts->size() - 2); + + int cmp = 0; + int cmp0 = p0.compareTo(pn0); + if (cmp0 != 0) cmp = cmp0; + + if (cmp == 0) { + int cmp1 = p1.compareTo(pn1); + if (cmp1 != 0) cmp = cmp1; + } + + if (cmp == 0) { + throw util::GEOSException("Edge direction cannot be determined because endpoints are equal"); + } + + return cmp == -1; + }; + + /** + * Compares two coincident edges to determine + * whether they have the same or opposite direction. + */ + bool relativeDirection(const Edge* edge2) const + { + // assert: the edges match (have the same coordinates up to direction) + if (!getCoordinate(0).equals2D(edge2->getCoordinate(0))) { + return false; + } + if (!getCoordinate(1).equals2D(edge2->getCoordinate(1))) { + return false; + } + return true; + }; + + int dimension(int geomIndex) const + { + if (geomIndex == 0) return aDim; + return bDim; + }; + + /** + * Merges an edge into this edge, + * updating the topology info accordingly. + */ + void merge(const Edge* edge) + { + /** + * Marks this + * as a shell edge if any contributing edge is a shell. + * Update hole status first, since it depends on edge dim + */ + aIsHole = isHoleMerged(0, this, edge); + bIsHole = isHoleMerged(1, this, edge); + + if (edge->aDim > aDim) aDim = edge->aDim; + if (edge->bDim > bDim) bDim = edge->bDim; + + bool relDir = relativeDirection(edge); + int flipFactor = relDir ? 1 : -1; + aDepthDelta += flipFactor * edge->aDepthDelta; + bDepthDelta += flipFactor * edge->bDepthDelta; + }; + + void populateLabel(OverlayLabel &lbl) const + { + initLabel(lbl, 0, aDim, aDepthDelta, aIsHole); + initLabel(lbl, 1, bDim, bDepthDelta, bIsHole); + }; + + bool compareTo(const Edge& e) const + { + const geom::Coordinate& ca = getCoordinate(0); + const geom::Coordinate& cb = e.getCoordinate(0); + if(ca.compareTo(cb) < 0) { + return true; + } + else if (ca.compareTo(cb) > 0) { + return false; + } + else { + const geom::Coordinate& cca = getCoordinate(1); + const geom::Coordinate& ccb = e.getCoordinate(1); + if(cca.compareTo(ccb) < 0) { + return true; + } + else if (cca.compareTo(ccb) > 0) { + return false; + } + else { + return false; + } + } + } + +}; + +bool EdgeComparator(const Edge* a, const Edge* b); + + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/EdgeKey.h b/Sources/geos/include/geos/operation/overlayng/EdgeKey.h new file mode 100644 index 0000000..ef66e46 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/EdgeKey.h @@ -0,0 +1,118 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * A key for sorting and comparing edges in a noded arrangement. + * Relies on the fact that in a correctly noded arrangement + * edges are identical (up to direction) + * iff they have their first segment in common. + * + * @author mdavis + * + */ +class GEOS_DLL EdgeKey { + using Coordinate = geos::geom::Coordinate; + +private: + + // Members + double p0x; + double p0y; + double p1x; + double p1y; + + // Methods + void initPoints(const Edge* edge) + { + bool direction = edge->direction(); + if (direction) { + init(edge->getCoordinate(0), + edge->getCoordinate(1)); + } + else { + std::size_t len = edge->size(); + init(edge->getCoordinate(len - 1), + edge->getCoordinate(len - 2)); + } + } + + void init(const geom::Coordinate& p0, const geom::Coordinate& p1) + { + p0x = p0.x; + p0y = p0.y; + p1x = p1.x; + p1y = p1.y; + } + + +public: + + EdgeKey(const Edge* edge) + { + initPoints(edge); + } + + int compareTo(const EdgeKey* ek) const + { + if (p0x < ek->p0x) return -1; + if (p0x > ek->p0x) return 1; + if (p0y < ek->p0y) return -1; + if (p0y > ek->p0y) return 1; + // first points are equal, compare second + if (p1x < ek->p1x) return -1; + if (p1x > ek->p1x) return 1; + if (p1y < ek->p1y) return -1; + if (p1y > ek->p1y) return 1; + return 0; + } + + bool equals(const EdgeKey* ek) const + { + return p0x == ek->p0x + && p0y == ek->p0y + && p1x == ek->p1x + && p1y == ek->p1y; + } + + friend bool operator<(const EdgeKey& ek1, const EdgeKey& ek2) + { + return ek1.compareTo(&ek2) < 0; + }; + + friend bool operator==(const EdgeKey& ek1, const EdgeKey& ek2) + { + return ek1.equals(&ek2); + }; + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/EdgeMerger.h b/Sources/geos/include/geos/operation/overlayng/EdgeMerger.h new file mode 100644 index 0000000..d1c7c9c --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/EdgeMerger.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace operation { +namespace overlayng { +class Edge; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Performs merging on the noded edges of the input geometries. + * Merging takes place on edges which are coincident + * (i.e. have the same coordinate list, modulo direction). + * The following situations can occur: + * + * - Coincident edges from different input geometries have their labels combined + * - Coincident edges from the same area geometry indicate a topology collapse. + * In this case the topology locations are "summed" to provide a final + * assignment of side location + * - Coincident edges from the same linear geometry can simply be merged + * using the same ON location + * + * One constraint that is maintained is that the direction of linear + * edges should be preserved if possible (which is the case if there is + * no other coincident edge, or if all coincident edges have the same direction). + * This ensures that the overlay output line direction will be as consistent + * as possible with input lines. + * + * The merger also preserves the order of the edges in the input. + * This means that for polygon-line overlay + * the result lines will be in the same order as in the input + * (possibly with multiple result lines for a single input line). + * + * @author mdavis + * + */ +class GEOS_DLL EdgeMerger { + +public: + + static std::vector merge(std::vector& edges); + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h b/Sources/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h new file mode 100644 index 0000000..9d2195b --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/EdgeNodingBuilder.h @@ -0,0 +1,247 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/overlayng/EdgeNodingBuilder.java 6ef89b096 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Builds a set of noded, unique, labelled Edges from + * the edges of the two input geometries. + * + * It performs the following steps: + * + * - Extracts input edges, and attaches topological information + * - if clipping is enabled, handles clipping or limiting input geometry + * - chooses a {@link noding::Noder} based on provided precision model, unless a custom one is supplied + * - calls the chosen noding::Noder, with precision model + * - removes any fully collapsed noded edges + * - builds {@link Edge}s and merges them + * + * @author mdavis + */ +class GEOS_DLL EdgeNodingBuilder { + using PrecisionModel = geos::geom::PrecisionModel; + using Envelope = geos::geom::Envelope; + using GeometryCollection = geos::geom::GeometryCollection; + using Polygon = geos::geom::Polygon; + using CoordinateSequence = geos::geom::CoordinateSequence; + using LinearRing = geos::geom::LinearRing; + using LineString = geos::geom::LineString; + using Geometry = geos::geom::Geometry; + +private: + + // Constants + static constexpr int MIN_LIMIT_PTS = 20; + static constexpr bool IS_NODING_VALIDATED = true; + + // Members + const PrecisionModel* pm; + std::unique_ptr> inputEdges; + noding::Noder* customNoder; + std::array hasEdges; + const Envelope* clipEnv; + std::unique_ptr clipper; + std::unique_ptr limiter; + + // For use in createFloatingPrecisionNoder() + algorithm::LineIntersector lineInt; + noding::IntersectionAdder intAdder; + std::unique_ptr internalNoder; + std::unique_ptr spareInternalNoder; + // EdgeSourceInfo*, Edge* owned by EdgeNodingBuilder, stored in deque + std::deque edgeSourceInfoQue; + std::deque edgeQue; + bool inputHasZ; + bool inputHasM; + + /** + * Gets a noder appropriate for the precision model supplied. + * This is one of: + * + * - Fixed precision: a snap-rounding noder (which should be fully robust) + * - Floating precision: a conventional model (which may be non-robust). + * In this case, a validation step is applied to the output from the noder. + */ + noding::Noder* getNoder(); + static std::unique_ptr createFixedPrecisionNoder(const PrecisionModel* pm); + std::unique_ptr createFloatingPrecisionNoder(bool doValidation); + + + void addCollection(const GeometryCollection* gc, uint8_t geomIndex); + void addGeometryCollection(const GeometryCollection* gc, uint8_t geomIndex, int expectedDim); + void addPolygon(const Polygon* poly, uint8_t geomIndex); + void addPolygonRing(const LinearRing* ring, bool isHole, uint8_t geomIndex); + void addLine(const LineString* line, uint8_t geomIndex); + void addLine(std::unique_ptr& pts, uint8_t geomIndex); + void addEdge(std::unique_ptr& cas, const EdgeSourceInfo* info); + + // Create a EdgeSourceInfo* owned by EdgeNodingBuilder + const EdgeSourceInfo* createEdgeSourceInfo(uint8_t index, int depthDelta, bool isHole); + const EdgeSourceInfo* createEdgeSourceInfo(uint8_t index); + + /** + * Tests whether a geometry (represented by its envelope) + * lies completely outside the clip extent(if any). + */ + bool isClippedCompletely(const Envelope* env) const; + + /** + * Tests whether it is worth limiting a line. + * Lines that have few vertices or are covered + * by the clip extent do not need to be limited. + */ + bool isToBeLimited(const LineString* line) const; + + /** + * If limiter is provided, + * limit the line to the clip envelope. + * + */ + std::vector>& limit(const LineString* line); + + /** + * If a clipper is present, + * clip the line to the clip extent. + * Otherwise, remove duplicate points from the ring. + * + * If clipping is enabled, then every ring MUST + * be clipped, to ensure that holes are clipped to + * be inside the shell. + * This means it is not possible to skip + * clipping for rings with few vertices. + * + * @param ring the line to clip + * @return the points in the clipped line + */ + std::unique_ptr clip(const LinearRing* line); + + /** + * Removes any repeated points from a linear component. + * This is required so that noding can be computed correctly. + * + * @param line the line to process + * @return the points of the line with repeated points removed + */ + static std::unique_ptr removeRepeatedPoints(const LineString* line); + + static int computeDepthDelta(const LinearRing* ring, bool isHole); + + void add(const Geometry* g, uint8_t geomIndex); + + /** + * Nodes a set of segment strings and creates {@link Edge}s from the result. + * The input segment strings each carry a {@link EdgeSourceInfo} object, + * which is used to provide source topology info to the constructed Edges + * (and is then discarded). + */ + std::vector node(std::vector* segStrings); + std::vector createEdges(std::vector* segStrings); + + +public: + + /** + * Creates a new builder, with an optional custom noder. + * If the noder is not provided, a suitable one will + * be used based on the supplied precision model. + */ + EdgeNodingBuilder(const PrecisionModel* p_pm, noding::Noder* p_customNoder) + : pm(p_pm) + , inputEdges(new std::vector) + , customNoder(p_customNoder) + , hasEdges{{false,false}} + , clipEnv(nullptr) + , intAdder(lineInt) + , inputHasZ(false) + , inputHasM(false) + {}; + + ~EdgeNodingBuilder() + { + for (noding::SegmentString* ss: *inputEdges) { + delete ss; + } + } + + void setClipEnvelope(const Envelope* clipEnv); + + // returns newly allocated vector and segmentstrings + // std::vector* node(); + + /** + * Reports whether there are noded edges + * for the given input geometry. + * If there are none, this indicates that either + * the geometry was empty, or has completely collapsed + * (because it is smaller than the noding precision). + * + * @param geomIndex index of input geometry + * @return true if there are edges for the geometry + */ + bool hasEdgesFor(uint8_t geomIndex) const; + + /** + * Creates a set of labelled {Edge}s. + * representing the fully noded edges of the input geometries. + * Coincident edges (from the same or both geometries) + * are merged along with their labels + * into a single unique, fully labelled edge. + * + * @param geom0 the first geometry + * @param geom1 the second geometry + * @return the noded, merged, labelled edges + */ + std::vector build(const Geometry* geom0, const Geometry* geom1); + + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/EdgeSourceInfo.h b/Sources/geos/include/geos/operation/overlayng/EdgeSourceInfo.h new file mode 100644 index 0000000..547be33 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/EdgeSourceInfo.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng +/** + * Records topological information about an + * edge representing a piece of linework (lineString or polygon ring) + * from a single source geometry. + * This information is carried through the noding process + * (which may result in many noded edges sharing the same information object). + * It is then used to populate the topology info fields + * in {@link Edge}s (possibly via merging). + * That information is used to construct the topology graph {@link OverlayLabel}s. + * + * @author mdavis + * + */ + +class GEOS_DLL EdgeSourceInfo { + +private: + + // Members + uint8_t index; + int8_t dim; + bool edgeIsHole; + int depthDelta; + + +public: + + EdgeSourceInfo(uint8_t p_index, int p_depthDelta, bool p_isHole); + EdgeSourceInfo(uint8_t p_index); + + uint8_t getIndex() const { return index; } + int getDimension() const { return dim; } + int getDepthDelta() const { return depthDelta; } + bool isHole() const { return edgeIsHole; } + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/ElevationModel.h b/Sources/geos/include/geos/operation/overlayng/ElevationModel.h new file mode 100644 index 0000000..4a41994 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/ElevationModel.h @@ -0,0 +1,170 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last Port: operation/overlayng/ElevationModel.java 4c88fea52 + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition + +// Forward declarations +namespace geos { + namespace geom { + class Geometry; + } +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + + +/** + * \brief + * A simple elevation model used to populate missing Z values + * in overlay results. + * + * The model divides the extent of the input geometry(s) + * into an NxM grid. + * The default grid size is 3x3. + * If the input has no extent in the X or Y dimension, + * that dimension is given grid size 1. + * The elevation of each grid cell is computed as the average of the Z + * values + * of the input vertices in that cell (if any). + * If a cell has no input vertices within it, it is assigned + * the average elevation over all cells. + * + * If no input vertices have Z values, the model does not assign a Z + * value. + * + * The elevation of an arbitrary location is determined as the + * Z value of the nearest grid cell. + * + * An elevation model can be used to populate missing Z values + * in an overlay result geometry. + * + * @author Martin Davis + * + */ +class GEOS_DLL ElevationModel { + +private: + + class ElevationCell { + private: + + int numZ = 0; + double sumZ = 0.0; + double avgZ; + + public: + + bool isNull() const + { + return numZ == 0; + } + + void add(double z) + { + ++numZ; + sumZ += z; + } + + void compute() + { + avgZ = DoubleNotANumber; + if (numZ > 0) + avgZ = sumZ / numZ; + } + + double getZ() const + { + return avgZ; + } + }; + + + static const int DEFAULT_CELL_NUM; + geom::Envelope extent; + int numCellX; + int numCellY; + double cellSizeX; + double cellSizeY; + std::vector cells; + bool isInitialized = false; + bool hasZValue = false; + double averageZ = DoubleNotANumber; + + void init(); + + ElevationCell& getCell(double x, double y); //, bool isCreateIfMissing); + + int getCellOffset(int ix, int iy) { + return (numCellX * iy + ix); + } + +protected: + + void add(double x, double y, double z); + + +public: + + static std::unique_ptr create(const geom::Geometry& geom1, + const geom::Geometry& geom2); + + static std::unique_ptr create(const geom::Geometry& geom1); + + ElevationModel(const geom::Envelope& extent, int numCellX, int numCellY); + + void add(const geom::Geometry& geom); + + + /** + * Gets the model Z value at a given location. + * If the location lies outside the model grid extent, + * this returns the Z value of the nearest grid cell. + * If the model has no elevation computed (i.e. due + * to empty input), the value is returned as a double NaN. + * + * @param x the x ordinate of the location + * @param y the y ordinate of the location + * @return the computed model Z value + */ + double getZ(double x, double y); + + + /** + * \brief + * Computes Z values for any missing Z values in a geometry, + * using the computed model. + * + * If the model has no Z value, or the geometry coordinate dimension + * does not include Z, no action is taken. + * + * @param geom the geometry to elevate + */ + void populateZ(geom::Geometry& geom); + + +}; + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/IndexedPointOnLineLocator.h b/Sources/geos/include/geos/operation/overlayng/IndexedPointOnLineLocator.h new file mode 100644 index 0000000..dc7874e --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/IndexedPointOnLineLocator.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Coordinate; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Locates points on a linear geometry, + * using a spatial index to provide good performance. + * + * @author mdavis + */ +class IndexedPointOnLineLocator : public algorithm::locate::PointOnGeometryLocator { + +private: + + // Members + const geom::Geometry& inputGeom; + + + +public: + + IndexedPointOnLineLocator(const geom::Geometry& geomLinear) + : inputGeom(geomLinear) + {} + + geom::Location locate(const geom::CoordinateXY* p) override; + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/InputGeometry.h b/Sources/geos/include/geos/operation/overlayng/InputGeometry.h new file mode 100644 index 0000000..e1ff981 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/InputGeometry.h @@ -0,0 +1,106 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + + +/** + * Manages the input geometries for an overlay operation. + * The second geometry is allowed to be null, + * to support for instance precision reduction. + * + * @author Martin Davis + * + */ + +class GEOS_DLL InputGeometry { + using Geometry = geos::geom::Geometry; + using Envelope = geos::geom::Envelope; + using Coordinate = geos::geom::Coordinate; + using Location = geos::geom::Location; + using PointOnGeometryLocator = geos::algorithm::locate::PointOnGeometryLocator; + +private: + + // Members + std::array geom; + std::unique_ptr ptLocatorA; + std::unique_ptr ptLocatorB; + std::array isCollapsed; + + + +public: + + InputGeometry(const Geometry* geomA, const Geometry* geomB); + + bool isSingle() const; + int getDimension(uint8_t index) const; + uint8_t getCoordinateDimension(uint8_t index) const; + const Geometry* getGeometry(uint8_t geomIndex) const; + const Envelope* getEnvelope(uint8_t geomIndex) const; + bool isEmpty(uint8_t geomIndex) const; + bool isArea(uint8_t geomIndex) const; + int getAreaIndex() const; + bool isLine(uint8_t geomIndex) const; + bool isAllPoints() const; + bool hasPoints() const; + + /** + * Tests if an input geometry has edges. + * This indicates that topology needs to be computed for them. + * + * @param geomIndex + * @return true if the input geometry has edges + */ + bool hasEdges(uint8_t geomIndex) const; + + /** + * Determines the location within an area geometry. + * This allows disconnected edges to be fully + * located. + * + * @param geomIndex the index of the geometry + * @param pt the coordinate to locate + * @return the location of the coordinate + * + * @see Location + */ + Location locatePointInArea(uint8_t geomIndex, const Coordinate& pt); + + PointOnGeometryLocator* getLocator(uint8_t geomIndex); + void setCollapsed(uint8_t geomIndex, bool isGeomCollapsed); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/IntersectionPointBuilder.h b/Sources/geos/include/geos/operation/overlayng/IntersectionPointBuilder.h new file mode 100644 index 0000000..90531c7 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/IntersectionPointBuilder.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +} +namespace operation { +namespace overlayng { +class OverlayEdge; +class OverlayGraph; +class OverlayLabel; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Extracts Point resultants from an overlay graph + * created by an Intersection operation + * between non-Point inputs. + * Points may be created during intersection + * if lines or areas touch one another at single points. + * Intersection is the only overlay operation which can + * result in Points from non-Point inputs. + *

+ * Overlay operations where one or more inputs + * are Points are handled via a different code path. + * + * + * @author Martin Davis + * + * @see OverlayPoints + * + */ +class GEOS_DLL IntersectionPointBuilder { + +private: + + // Members + OverlayGraph* graph; + const geom::GeometryFactory* geometryFactory; + std::vector> points; + /** + * Controls whether lines created by area topology collapses + * to participate in the result computation. + * True provides the original JTS semantics. + */ + bool isAllowCollapseLines; + + // Methods + void addResultPoints(); + + /** + * Tests if a node is a result point. + * This is the case if the node is incident on edges from both + * inputs, and none of the edges are themselves in the result. + */ + bool isResultPoint(OverlayEdge* nodeEdge) const; + bool isEdgeOf(const OverlayLabel* label, uint8_t i) const; + + +public: + + + IntersectionPointBuilder(OverlayGraph* p_graph, const geom::GeometryFactory* geomFact) + : graph(p_graph) + , geometryFactory(geomFact) + , isAllowCollapseLines(!OverlayNG::STRICT_MODE_DEFAULT) + {} + + std::vector> getPoints(); + + IntersectionPointBuilder(const IntersectionPointBuilder&) = delete; + IntersectionPointBuilder& operator=(const IntersectionPointBuilder&) = delete; + + void setStrictMode(bool p_isStrictMode) + { + isAllowCollapseLines = ! p_isStrictMode; + } + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/LineBuilder.h b/Sources/geos/include/geos/operation/overlayng/LineBuilder.h new file mode 100644 index 0000000..2abeda3 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/LineBuilder.h @@ -0,0 +1,189 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class LineString; +} +namespace operation { +namespace overlayng { +class OverlayEdge; +class OverlayGraph; +class OverlayLabel; +class InputGeometry; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Finds and builds overlay result lines from the overlay graph. + * Output linework has the following semantics: + * + * - Linework is fully noded + * - Lines are as long as possible between nodes + * + * Various strategies are possible for how to + * merge graph edges into lines. + * This implementation uses the approach + * of having output lines run contiguously from node to node. + * For rings a node point is chosen arbitrarily. + * + * Another possible strategy would be to preserve input linework + * as far as possible (i.e. any sections of input lines which are not + * coincident with other linework would be preserved). + * + * It would also be possible to output LinearRings, + * if the input is a LinearRing and is unchanged. + * This will require additional info from the input linework. + * + * @author Martin Davis + */ +class GEOS_DLL LineBuilder { + +private: + + // Members + OverlayGraph* graph; + int opCode; + const geom::GeometryFactory* geometryFactory; + bool hasResultArea; + int8_t inputAreaIndex; + std::vector> lines; + + /** + * Indicates whether intersections are allowed to produce + * heterogeneous results including proper boundary touches. + * This does not control inclusion of touches along collapses. + * True provides the original JTS semantics. + */ + bool isAllowMixedResult = ! OverlayNG::STRICT_MODE_DEFAULT; + + /** + * Allow lines created by area topology collapses + * to appear in the result. + * True provides the original JTS semantics. + */ + bool isAllowCollapseLines = ! OverlayNG::STRICT_MODE_DEFAULT; + + void markResultLines(); + + /** + * Checks if the topology indicated by an edge label + * determines that this edge should be part of a result line. + * + * Note that the logic here relies on the semantic + * that for intersection lines are only returned if + * there is no result area components. + */ + bool isResultLine(const OverlayLabel* lbl) const; + + /** + * Determines the effective location for a line, + * for the purpose of overlay operation evaluation. + * Line edges and Collapses are reported as INTERIOR + * so they may be included in the result + * if warranted by the effect of the operation + * on the two edges. + * (For instance, the intersection of line edge and a collapsed boundary + * is included in the result). + */ + static geom::Location effectiveLocation(const OverlayLabel* lbl, uint8_t geomIndex); + + void addResultLines(); + void addResultLinesMerged(); + + std::unique_ptr toLine(OverlayEdge* edge) const; + + void addResultLinesForNodes(); + + /** + * Adds lines which form rings (i.e. have only degree-2 vertices). + */ + void addResultLinesRings(); + + /** + * Traverses edges from edgeStart which + * lie in a single line (have degree = 2). + * + * The direction of the linework is preserved as far as possible. + * Specifically, the direction of the line is determined + * by the start edge direction. This implies + * that if all edges are reversed, the created line + * will be reversed to match. + * (Other more complex strategies would be possible. + * E.g. using the direction of the majority of segments, + * or preferring the direction of the A edges.) + */ + std::unique_ptr buildLine(OverlayEdge* node); + + /* + * Finds the next edge around a node which forms + * part of a result line. + */ + static OverlayEdge* nextLineEdgeUnvisited(OverlayEdge* node) ; + + /** + * Computes the degree of the line edges incident on a node + */ + static int degreeOfLines(OverlayEdge* node) ; + + + +public: + + LineBuilder(const InputGeometry* inputGeom, OverlayGraph* p_graph, bool p_hasResultArea, int p_opCode, const geom::GeometryFactory* geomFact) + : graph(p_graph) + , opCode(p_opCode) + , geometryFactory(geomFact) + , hasResultArea(p_hasResultArea) + , inputAreaIndex((int8_t)inputGeom->getAreaIndex()) + , isAllowMixedResult(! OverlayNG::STRICT_MODE_DEFAULT) + , isAllowCollapseLines(! OverlayNG::STRICT_MODE_DEFAULT) + {} + + LineBuilder(const LineBuilder&) = delete; + LineBuilder& operator=(const LineBuilder&) = delete; + + std::vector> getLines(); + + void setStrictMode(bool p_isStrictResultMode) + { + isAllowCollapseLines = ! p_isStrictResultMode; + isAllowMixedResult = ! p_isStrictResultMode; + } + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/LineLimiter.h b/Sources/geos/include/geos/operation/overlayng/LineLimiter.h new file mode 100644 index 0000000..8a9b27f --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/LineLimiter.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class Coordinate; +class CoordinateSequence; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Limits the segments in a list of segments + * to those which intersect an envelope. + * This creates zero or more sections of the input segment sequences, + * containing only line segments which intersect the limit envelope. + * Segments are not clipped, since that can move + * line segments enough to alter topology, + * and it happens in the overlay in any case. + * This can substantially reduce the number of vertices which need to be + * processed during overlay. + * + * This optimization is only applicable to Line geometries, + * since it does not maintain the closed topology of rings. + * Polygonal geometries are optimized using the {@link RingClipper}. + * + * @author Martin Davis + */ +class GEOS_DLL LineLimiter { + using Envelope = geos::geom::Envelope; + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + +private: + + // Members + const Envelope* limitEnv; + std::unique_ptr ptList; + const Coordinate* lastOutside; + std::vector> sections; + + // Methods + void addPoint(const Coordinate* p); + void addOutside(const Coordinate* p); + bool isLastSegmentIntersecting(const Coordinate* p); + bool isSectionOpen(); + void startSection(); + void finishSection(); + + +public: + + LineLimiter(const Envelope* env) + : limitEnv(env) + , ptList(nullptr) + , lastOutside(nullptr) + {}; + + std::vector>& limit(const CoordinateSequence *pts); + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/MaximalEdgeRing.h b/Sources/geos/include/geos/operation/overlayng/MaximalEdgeRing.h new file mode 100644 index 0000000..489625e --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/MaximalEdgeRing.h @@ -0,0 +1,133 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class GeometryFactory; +} +namespace operation { +namespace overlayng { +class OverlayEdge; +class EdgeRing; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +class GEOS_DLL MaximalEdgeRing { + using Coordinate = geos::geom::Coordinate; + using GeometryFactory = geos::geom::GeometryFactory; + +private: + + // Constants + static constexpr int STATE_FIND_INCOMING = 1; + static constexpr int STATE_LINK_OUTGOING = 2; + + // Members + OverlayEdge* startEdge; + + // Methods + void attachEdges(OverlayEdge* startEdge); + void linkMinimalRings(); + + /** + * Links the edges of a {@link MaximalEdgeRing} around this node + * into minimal edge rings ({@link OverlayEdgeRing}s). + * Minimal ring edges are linked in the opposite orientation (CW) + * to the maximal ring. + * This changes self-touching rings into a two or more separate rings, + * as per the OGC SFS polygon topology semantics. + * This relinking must be done to each max ring separately, + * rather than all the node result edges, since there may be + * more than one max ring incident at the node. + * + * @param nodeEdge an edge originating at this node + * @param maxRing the maximal ring to link + */ + static void linkMinRingEdgesAtNode(OverlayEdge* nodeEdge, MaximalEdgeRing* maxRing); + + /** + * Tests if an edge of the maximal edge ring is already linked into + * a minimal {@link OverlayEdgeRing}. If so, this node has already been processed + * earlier in the maximal edgering linking scan. + * + * @param edge an edge of a maximal edgering + * @param maxRing the maximal edgering + * @return true if the edge has already been linked into a minimal edgering. + */ + static bool isAlreadyLinked(OverlayEdge* edge, MaximalEdgeRing* maxRing); + + static OverlayEdge* selectMaxOutEdge(OverlayEdge* currOut, MaximalEdgeRing* maxEdgeRing); + static OverlayEdge* linkMaxInEdge(OverlayEdge* currOut, OverlayEdge* currMaxRingOut, MaximalEdgeRing* maxEdgeRing); + + +public: + + MaximalEdgeRing(OverlayEdge* e) + : startEdge(e) + { + attachEdges(e); + }; + + std::vector> buildMinimalRings(const GeometryFactory* geometryFactory); + + /** + * Traverses the star of edges originating at a node + * and links consecutive result edges together + * into maximal edge rings. + * To link two edges the resultNextMax< pointer + * for an incoming< result edge + * is set to the next outgoing result edge. + * + * Edges are linked when: + * - they belong to an area (i.e. they have sides) + * - they are marked as being in the result + * + * Edges are linked in CCW order + * (which is the order they are linked in the underlying graph). + * This means that rings have their face on the Right + * (in other words, + * the topological location of the face is given by the RHS label of the DirectedEdge). + * This produces rings with CW orientation. + * + * PRECONDITIONS: + * - This edge is in the result + * - This edge is not yet linked + * - The edge and its sym are NOT both marked as being in the result + */ + static void linkResultAreaMaxRingAtNode(OverlayEdge* nodeEdge); + + friend std::ostream& operator<<(std::ostream& os, const MaximalEdgeRing& mer); + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayEdge.h b/Sources/geos/include/geos/operation/overlayng/OverlayEdge.h new file mode 100644 index 0000000..42d3632 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayEdge.h @@ -0,0 +1,278 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +} +namespace operation { +namespace overlayng { +class OverlayEdgeRing; +class MaximalEdgeRing; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** +* Creates a single OverlayEdge. +*/ +class GEOS_DLL OverlayEdge : public edgegraph::HalfEdge { + using Coordinate = geos::geom::Coordinate; + using CoordinateXYZM = geos::geom::CoordinateXYZM; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Location = geos::geom::Location; + +private: + + // Members + const CoordinateSequence* pts; + /** + * 'true' indicates direction is forward along segString + * 'false' is reverse direction + * The label must be interpreted accordingly. + */ + bool direction; + CoordinateXYZM dirPt; + OverlayLabel* label; + bool m_isInResultArea; + bool m_isInResultLine; + bool m_isVisited; + OverlayEdge* nextResultEdge; + const OverlayEdgeRing* edgeRing; + const MaximalEdgeRing* maxEdgeRing; + OverlayEdge* nextResultMaxEdge; + + void markVisited() + { + m_isVisited = true; + }; + + +public: + + OverlayEdge(const CoordinateXYZM& p_orig, const CoordinateXYZM& p_dirPt, + bool p_direction, OverlayLabel* p_label, + const CoordinateSequence* p_pts) + : HalfEdge(p_orig) + , pts(p_pts) + , direction(p_direction) + , dirPt(p_dirPt) + , label(p_label) + , m_isInResultArea(false) + , m_isInResultLine(false) + , m_isVisited(false) + , nextResultEdge(nullptr) + , edgeRing(nullptr) + , maxEdgeRing(nullptr) + , nextResultMaxEdge(nullptr) + {} + + ~OverlayEdge() override {}; + + bool isForward() const + { + return direction; + }; + + const CoordinateXYZM& directionPt() const override + { + return dirPt; + }; + + OverlayLabel* getLabel() const + { + return label; + }; + + Location getLocation(uint8_t index, int position) const + { + return label->getLocation(index, position, direction); + }; + + const CoordinateXYZM& getCoordinate() const + { + return orig(); + }; + + const CoordinateSequence* getCoordinatesRO() const + { + return pts; + }; + + std::unique_ptr getCoordinates() + { + // return a copy of pts + return pts->clone(); + }; + + std::unique_ptr getCoordinatesOriented(); + + /** + * Adds the coordinates of this edge to the given list, + * in the direction of the edge. + * Duplicate coordinates are removed + * (which means that this is safe to use for a path + * of connected edges in the topology graph). + * + * @param coords the coordinate list to add to + */ + void addCoordinates(CoordinateSequence* coords) const; + + OverlayEdge* symOE() const + { + return static_cast(sym()); + }; + + OverlayEdge* oNextOE() const + { + return static_cast(oNext()); + }; + + bool isInResultArea() const + { + return m_isInResultArea; + }; + + bool isInResultAreaBoth() const + { + return m_isInResultArea && symOE()->m_isInResultArea; + }; + + bool isInResultEither() const + { + return isInResult() || symOE()->isInResult(); + }; + + void unmarkFromResultAreaBoth() + { + m_isInResultArea = false; + symOE()->m_isInResultArea = false; + }; + + void markInResultArea() + { + m_isInResultArea = true; + }; + + void markInResultAreaBoth() + { + m_isInResultArea = true; + symOE()->m_isInResultArea = true; + }; + + bool isInResultLine() const + { + return m_isInResultLine; + }; + + void markInResultLine() + { + m_isInResultLine = true; + symOE()->m_isInResultLine = true; + }; + + bool isInResult() const + { + return m_isInResultArea || m_isInResultLine; + }; + + void setNextResult(OverlayEdge* e) + { + // Assert: e.orig() == this.dest(); + nextResultEdge = e; + }; + + OverlayEdge* nextResult() const + { + return nextResultEdge; + }; + + bool isResultLinked() const + { + return nextResultEdge != nullptr; + }; + + void setNextResultMax(OverlayEdge* e) + { + // Assert: e.orig() == this.dest(); + nextResultMaxEdge = e; + }; + + OverlayEdge* nextResultMax() const + { + return nextResultMaxEdge; + }; + + bool isResultMaxLinked() const + { + return nextResultMaxEdge != nullptr; + }; + + bool isVisited() const + { + return m_isVisited; + }; + + void markVisitedBoth() + { + markVisited(); + symOE()->markVisited(); + }; + + void setEdgeRing(const OverlayEdgeRing* p_edgeRing) + { + edgeRing = p_edgeRing; + }; + + const OverlayEdgeRing* getEdgeRing() const + { + return edgeRing; + }; + + const MaximalEdgeRing* getEdgeRingMax() const + { + return maxEdgeRing; + }; + + void setEdgeRingMax(const MaximalEdgeRing* p_maximalEdgeRing) + { + maxEdgeRing = p_maximalEdgeRing; + }; + + friend std::ostream& operator<<(std::ostream& os, const OverlayEdge& oe); + std::string resultSymbol() const; + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayEdgeRing.h b/Sources/geos/include/geos/operation/overlayng/OverlayEdgeRing.h new file mode 100644 index 0000000..b2509f0 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayEdgeRing.h @@ -0,0 +1,156 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace algorithm { +namespace locate { +class PointOnGeometryLocator; +} +} +namespace geom { +class Coordinate; +class CoordinateSequence; +class GeometryFactory; +class LinearRing; +class Polygon; +} +namespace operation { +namespace overlayng { +class OverlayEdge; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +class GEOS_DLL OverlayEdgeRing { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using GeometryFactory = geos::geom::GeometryFactory; + using LinearRing = geos::geom::LinearRing; + using Polygon = geos::geom::Polygon; + using PointOnGeometryLocator = algorithm::locate::PointOnGeometryLocator; + using IndexedPointInAreaLocator = algorithm::locate::IndexedPointInAreaLocator; + +private: + + // Members + OverlayEdge* startEdge; + std::unique_ptr ring; + bool m_isHole; + std::unique_ptr locator; + OverlayEdgeRing* shell; + // a list of EdgeRings which are holes in this EdgeRing + std::vector holes; + + // Methods + void computeRingPts(OverlayEdge* start, CoordinateSequence& pts); + void computeRing(std::unique_ptr && ringPts, const GeometryFactory* geometryFactory); + + /** + * Computes the list of coordinates which are contained in this ring. + * The coordinates are computed once only and cached. + * @return an array of the {@link Coordinate}s in this ring + */ + const CoordinateSequence& getCoordinates(); + PointOnGeometryLocator* getLocator(); + static void closeRing(CoordinateSequence& pts); + + +public: + + OverlayEdgeRing(OverlayEdge* start, const GeometryFactory* geometryFactory); + + std::unique_ptr getRing(); + const LinearRing* getRingPtr() const; + + /** + * Tests whether this ring is a hole. + * @return true if this ring is a hole + */ + bool isHole() const; + + /** + * Sets the containing shell ring of a ring that has been determined to be a hole. + * + * @param shell the shell ring + */ + void setShell(OverlayEdgeRing* p_shell); + + /** + * Tests whether this ring has a shell assigned to it. + * + * @return true if the ring has a shell + */ + bool hasShell() const; + + /** + * Gets the shell for this ring. The shell is the ring itself if it is not a hole, otherwise its parent shell. + * + * @return the shell for this ring + */ + const OverlayEdgeRing* getShell() const; + + void addHole(OverlayEdgeRing* ring); + + bool isInRing(const Coordinate& pt); + + const Coordinate& getCoordinate(); + + /** + * Computes the {@link Polygon} formed by this ring and any contained holes. + * @return the {@link Polygon} formed by this ring and its holes. + */ + std::unique_ptr toPolygon(const GeometryFactory* factory); + + OverlayEdge* getEdge(); + + /** + * Finds the innermost enclosing shell OverlayEdgeRing + * containing this OverlayEdgeRing, if any. + * The innermost enclosing ring is the smallest enclosing ring. + * The algorithm used depends on the fact that: + * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * + * This routine is only safe to use if the chosen point of the hole + * is known to be properly contained in a shell + * (which is guaranteed to be the case if the hole does not touch its shell) + * + * To improve performance of this function the caller should + * make the passed shellList as small as possible (e.g. + * by using a spatial index filter beforehand). + * + * @return containing EdgeRing, if there is one + * or null if no containing EdgeRing is found + */ + OverlayEdgeRing* findEdgeRingContaining(const std::vector& erList); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayGraph.h b/Sources/geos/include/geos/operation/overlayng/OverlayGraph.h new file mode 100644 index 0000000..e44c46b --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayGraph.h @@ -0,0 +1,143 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +} +namespace operation { +namespace overlayng { +class Edge; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * A planar graph of {@link OverlayEdge}, representing + * the topology resulting from an overlay operation. + * Each source {@link Edge} is represented + * by two OverlayEdges, with opposite orientation. + * A single {@link OverlayLabel} is created for each symmetric pair of OverlayEdges. + * + * @author mdavis + * + */ +class GEOS_DLL OverlayGraph { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + +private: + + // Members + std::unordered_map nodeMap; + std::vector edges; + + // Locally store the OverlayEdge and OverlayLabel + std::deque ovEdgeQue; + std::deque ovLabelQue; + + std::vector> csQue; + + // Methods + + /** + * Create and add HalfEdge pairs to map and vector containers, + * using local std::deque storage for objects. + */ + OverlayEdge* createEdgePair(const CoordinateSequence* pts, OverlayLabel* lbl); + + /** + * Create a single OverlayEdge in local std::deque storage, and return the + * pointer. + */ + OverlayEdge* createOverlayEdge(const CoordinateSequence* pts, OverlayLabel* lbl, bool direction); + + void insert(OverlayEdge* e); + + + +public: + + /** + * Creates a new graph for a set of noded, labelled {@link Edge}s. + */ + OverlayGraph(); + + OverlayGraph(const OverlayGraph& g) = delete; + OverlayGraph& operator=(const OverlayGraph& g) = delete; + + /** + * Adds an edge between the coordinates orig and dest + * to this graph. + * Only valid edges can be added (in particular, zero-length segments cannot be added) + * + */ + OverlayEdge* addEdge(Edge* edge); + + /** + * Gets the set of edges in this graph. + * Only one of each symmetric pair of OverlayEdges is included. + * The opposing edge can be found by using {@link OverlayEdge#sym()}. + */ + std::vector& getEdges(); + + /** + * Gets the collection of edges representing the nodes in this graph. + * For each star of edges originating at a node + * a single representative edge is included. + * The other edges around the node can be found by following the next and prev links. + */ + std::vector getNodeEdges(); + + /** + * Gets an edge originating at the given node point. + */ + OverlayEdge* getNodeEdge(const Coordinate& nodePt) const; + + /** + * Gets the representative edges marked as being in the result area. + */ + std::vector getResultAreaEdges(); + + /** + * Create a single OverlayLabel in local std::deque storage + * and return a pointer to the stored object. + */ + OverlayLabel* createOverlayLabel(const Edge* edge); + + friend std::ostream& operator<<(std::ostream& os, const OverlayGraph& og); + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayLabel.h b/Sources/geos/include/geos/operation/overlayng/OverlayLabel.h new file mode 100644 index 0000000..d0c0040 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayLabel.h @@ -0,0 +1,403 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** +* A label for a pair of {@link OverlayEdge}s which records +* the topological information for the edge +* in the {@link OverlayGraph} containing it. +* The label is shared between both OverlayEdges +* of a symmetric pair. +* Accessors for orientation-sensitive information +* require the orientation of the containing OverlayEdge. +* +* A label contains the topological {@link geom::Location}s for +* the two overlay input geometries. +* A labelled input geometry may be either a Line or an Area. +* In both cases, the label locations are populated +* with the locations for the edge {@link geom::Position}s +* once they are computed by topological evaluation. +* The label also records the dimension of each geometry, +* and in the case of area boundary edges, the role +* of the originating ring (which allows +* determination of the edge role in collapse cases). +* +* For each input geometry, the label indicates that an edge +* is in one of the following states (identified by the "dim" field). +* Each state has some additional information about the edge. +* +* * A Boundary edge of an input Area (polygon) +* +* * dim = DIM_BOUNDARY +* * locLeft, locRight : the locations of the edge sides for the input Area +* * isHole : whether the edge was in a shell or a hole +* +* * A Collapsed edge of an input Area +* (which had two or more parent edges) +* +* * dim = DIM_COLLAPSE +* * locLine : the location of the +* edge relative to the input Area +* * isHole : whether some +* contributing edge was in a shell (false), +* or otherwise that all were in holes (true) +* +* * An edge from an input Line +* +* * dim = DIM_LINE +* * locLine : initialized to LOC_UNKNOWN, to simplify logic. +* +* * An edge which is Not Part of an input geometry +* (and thus must be part of the other geometry). +* +* * dim = NOT_PART +* +* Note that: +* +* * an edge cannot be both a Collapse edge and a Line edge in the same input geometry, +* because each input geometry must be homogeneous. +* * an edge may be an Boundary edge in one input geometry +* and a Line or Collapse edge in the other input. +* +* @author Martin Davis +*/ +class GEOS_DLL OverlayLabel { + using Location = geos::geom::Location; + using Position = geos::geom::Position; + +private: + + // Members + int aDim = DIM_NOT_PART; + bool aIsHole = false; + Location aLocLeft = LOC_UNKNOWN; + Location aLocRight = LOC_UNKNOWN; + Location aLocLine = LOC_UNKNOWN; + int bDim = DIM_NOT_PART; + bool bIsHole = false; + Location bLocLeft = LOC_UNKNOWN; + Location bLocRight = LOC_UNKNOWN; + Location bLocLine = LOC_UNKNOWN; + + std::string dimensionSymbol(int dim) const; + void locationString(uint8_t index, bool isForward, std::ostream& os) const; + + +public: + + static constexpr Location LOC_UNKNOWN = Location::NONE; + + enum { + DIM_UNKNOWN = -1, + DIM_NOT_PART = -1, + DIM_LINE = 1, + DIM_BOUNDARY = 2, + DIM_COLLAPSE = 3 + }; + + OverlayLabel() + : aDim(DIM_NOT_PART) + , aIsHole(false) + , aLocLeft(LOC_UNKNOWN) + , aLocRight(LOC_UNKNOWN) + , aLocLine(LOC_UNKNOWN) + , bDim(DIM_NOT_PART) + , bIsHole(false) + , bLocLeft(LOC_UNKNOWN) + , bLocRight(LOC_UNKNOWN) + , bLocLine(LOC_UNKNOWN) {}; + + explicit OverlayLabel(uint8_t p_index) + : OverlayLabel() + { + initLine(p_index); + }; + + OverlayLabel(uint8_t p_index, Location p_locLeft, Location p_locRight, bool p_isHole) + : OverlayLabel() + { + initBoundary(p_index, p_locLeft, p_locRight, p_isHole); + }; + + int dimension(uint8_t index) const { return index == 0 ? aDim : bDim; }; + void initBoundary(uint8_t index, Location locLeft, Location locRight, bool p_isHole); + void initCollapse(uint8_t index, bool p_isHole); + void initLine(uint8_t index); + void initNotPart(uint8_t index); + + /** + * Sets the line location. + * + * This is used to set the locations for linear edges + * encountered during area label propagation. + * + * @param index source to update + * @param loc location to set + */ + void setLocationLine(uint8_t index, Location loc); + void setLocationAll(uint8_t index, Location loc); + void setLocationCollapse(uint8_t index); + + /* + * Tests whether at least one of the sources is a Line. + * + * @return true if at least one source is a line + */ + bool isLine() const + { + return aDim == DIM_LINE || bDim == DIM_LINE; + }; + + bool isLine(uint8_t index) const + { + return index == 0 ? aDim == DIM_LINE : bDim == DIM_LINE; + }; + + bool isLinear(uint8_t index) const + { + if (index == 0) { + return aDim == DIM_LINE || aDim == DIM_COLLAPSE; + } + return bDim == DIM_LINE || bDim == DIM_COLLAPSE; + }; + + bool isKnown(uint8_t index) const + { + if (index == 0) { + return aDim != DIM_UNKNOWN; + } + return bDim != DIM_UNKNOWN; + }; + + bool isNotPart(uint8_t index) const + { + if (index == 0) { + return aDim == DIM_NOT_PART; + } + return bDim == DIM_NOT_PART; + }; + + bool isBoundaryEither() const + { + return aDim == DIM_BOUNDARY || bDim == DIM_BOUNDARY; + }; + + bool isBoundaryBoth() const + { + return aDim == DIM_BOUNDARY && bDim == DIM_BOUNDARY; + }; + + /** + * Tests if the label is for a collapsed + * edge of an area + * which is coincident with the boundary of the other area. + * + * @return true if the label is for a collapse coincident with a boundary + */ + bool isBoundaryCollapse() const + { + if (isLine()) return false; + return ! isBoundaryBoth(); + }; + + /** + * Tests if a label is for an edge where two + * area touch along their boundary. + */ + bool isBoundaryTouch() const + { + return isBoundaryBoth() && + getLocation(0, Position::RIGHT, true) != getLocation(1, Position::RIGHT, true); + }; + + bool isBoundary(uint8_t index) const + { + if (index == 0) { + return aDim == DIM_BOUNDARY; + } + return bDim == DIM_BOUNDARY; + }; + + bool isLineLocationUnknown(int index) const + { + if (index == 0) { + return aLocLine == LOC_UNKNOWN; + } + else { + return bLocLine == LOC_UNKNOWN; + } + }; + + /** + * Tests whether a label is for an edge which is a boundary of one geometry + * and not part of the other. + */ + bool isBoundarySingleton() const + { + if (aDim == DIM_BOUNDARY && bDim == DIM_NOT_PART) { + return true; + } + + if (bDim == DIM_BOUNDARY && aDim == DIM_NOT_PART) { + return true; + } + + return false; + }; + + /** + * Tests if a line edge is inside + * @param index + * @return + */ + bool isLineInArea(int8_t index) const + { + if (index == 0) { + return aLocLine == Location::INTERIOR; + } + return bLocLine == Location::INTERIOR; + }; + + bool isHole(uint8_t index) const + { + if (index == 0) { + return aIsHole; + } + else { + return bIsHole; + } + }; + + bool isCollapse(uint8_t index) const + { + return dimension(index) == DIM_COLLAPSE; + }; + + Location getLineLocation(uint8_t index) const + { + if (index == 0) { + return aLocLine; + } + else { + return bLocLine; + } + }; + + /** + * Tests if a label is a Collapse has location INTERIOR, + * to at least one source geometry. + */ + bool isInteriorCollapse() const + { + if (aDim == DIM_COLLAPSE && aLocLine == Location::INTERIOR) + return true; + if (bDim == DIM_COLLAPSE && bLocLine == Location::INTERIOR) + return true; + + return false; + }; + + /** + * Tests if a label is a Collapse + * and NotPart with location INTERIOR for the other geometry. + */ + bool isCollapseAndNotPartInterior() const; + + /** + * Tests if a line is in the interior of a source geometry. + * + * @param index source geometry + * @return true if the label is a line and is interior + */ + bool isLineInterior(uint8_t index) const + { + if (index == 0) { + return aLocLine == Location::INTERIOR; + } + return bLocLine == Location::INTERIOR; + }; + + /** + * Gets the location for this label for either + * a Boundary or a Line edge. + * This supports a simple determination of + * whether the edge should be included as a result edge. + * + * @param index the source index + * @param position the position for a boundary label + * @param isForward the direction for a boundary label + * @return the location for the specified position + */ + Location getLocationBoundaryOrLine( + uint8_t index, + int position, + bool isForward) const + { + if (isBoundary(index)) { + return getLocation(index, position, isForward); + } + return getLineLocation(index); + }; + + /** + * Gets the linear location for the given source. + * + * @param index the source index + * @return the linear location for the source + */ + Location getLocation(uint8_t index) const { + if (index == 0) { + return aLocLine; + } + return bLocLine; + }; + + Location getLocation(uint8_t index, int position, bool isForward) const; + + bool hasSides(uint8_t index) const { + if (index == 0) { + return aLocLeft != LOC_UNKNOWN + || aLocRight != LOC_UNKNOWN; + } + return bLocLeft != LOC_UNKNOWN + || bLocRight != LOC_UNKNOWN; + }; + + OverlayLabel copy() const + { + OverlayLabel lbl = *this; + return lbl; + }; + + friend std::ostream& operator<<(std::ostream& os, const OverlayLabel& ol); + void toString(bool isForward, std::ostream& os) const; + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayLabeller.h b/Sources/geos/include/geos/operation/overlayng/OverlayLabeller.h new file mode 100644 index 0000000..8a7359b --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayLabeller.h @@ -0,0 +1,207 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace operation { +namespace overlayng { +class OverlayLabel; +class OverlayGraph; +class OverlayEdge; +class InputGeometry; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + + +class GEOS_DLL OverlayLabeller { + +private: + + // Members + OverlayGraph* graph; + InputGeometry* inputGeometry; + std::vector& edges; + + /** + * Finds a boundary edge for this geom, if one exists. + */ + static OverlayEdge* findPropagationStartEdge(OverlayEdge* nodeEdge, uint8_t geomIndex); + + /** + * At this point collapsed edges with unknown location + * must be disconnected from the boundary edges of the parent + * (because otherwise the location would have + * been propagated from them). + * This can occur with a collapsed hole or shell. + * The edges can be labeled based on their parent ring role (shell or hole). + * (This cannot be done earlier, because the location + * based on the boundary edges must take precedence. + * There are situations where a collapsed edge has a location + * which is different to its ring role - + * e.g. a narrow gore in a polygon, which is in + * the interior of the reduced polygon, but whose + * ring role would imply the location EXTERIOR.) + * + * Note that collapsed edges can NOT have location determined via a PIP location check, + * because that is done against the unreduced input geometry, + * which may give an invalid result due to topology collapse. + * + * The labeling is propagated to other connected linear edges, + * since there may be NOT_PART edges which are connected, + * and they can be labeled in the same way. + * (These would get labeled anyway during subsequent disconnected labeling pass, + * but may be more efficient and accurate to do it here.) + */ + void labelCollapsedEdges(); + static void labelCollapsedEdge(OverlayEdge* edge, uint8_t geomIndex); + + /** + * There can be edges which have unknown location + * but are connected to a Line edge with known location. + * In this case line location is propagated to the connected edges. + */ + void labelConnectedLinearEdges(); + void propagateLinearLocations(uint8_t geomIndex); + static void propagateLinearLocationAtNode(OverlayEdge* eNode, uint8_t geomIndex, bool isInputLine, std::deque& edgeStack); + + /** + * Finds all OverlayEdges which are linear + * (i.e. line or collapsed) and have a known location + * for the given input geometry. + */ + static std::vector findLinearEdgesWithLocation(const std::vector &edges, uint8_t geomIndex); + + /** + * At this point there may still be edges which have unknown location + * relative to an input geometry. + * This must be because they are NOT_PART edges for that geometry, + * and are disconnected from any edges of that geometry. + * An example of this is rings of one geometry wholly contained + * in another geometry. + * The location must be fully determined to compute a + * correct result for all overlay operations. + * + * If the input geometry is an Area the edge location can + * be determined via a PIP test. + * If the input is not an Area the location is EXTERIOR. + */ + void labelDisconnectedEdges(); + + /** + * Determines the location of an edge relative to a target input geometry. + * The edge has no location information + * because it is disconnected from other + * edges that would provide that information. + * The location is determined by checking + * if the edge lies inside the target geometry area (if any). + */ + void labelDisconnectedEdge(OverlayEdge* edge, uint8_t geomIndex); + + /** + * Determines the {@link Location} for an edge within an Area geometry + * via point-in-polygon location. + * + * NOTE this is only safe to use for disconnected edges, + * since the test is carried out against the original input geometry, + * and precision reduction may cause incorrect results for edges + * which are close enough to a boundary to become connected. + */ + geom::Location locateEdge(uint8_t geomIndex, OverlayEdge* edge); + + /** + * Determines the {@link Location} for an edge within an Area geometry + * via point-in-polygon location, + * by checking that both endpoints are interior to the target geometry. + * Checking both endpoints ensures correct results in the presence of topology collapse. + *

+ * NOTE this is only safe to use for disconnected edges, + * since the test is carried out against the original input geometry, + * and precision reduction may cause incorrect results for edges + * which are close enough to a boundary to become connected. + */ + geom::Location locateEdgeBothEnds(uint8_t geomIndex, OverlayEdge* edge); + + /** + * Labels edges around nodes based on the arrangement + * of incident area boundary edges. + * Also propagates the labelling to connected linear edges. + */ + void labelAreaNodeEdges(std::vector& nodes); + + + +public: + + OverlayLabeller(OverlayGraph* p_graph, InputGeometry* p_inputGeometry) + : graph(p_graph) + , inputGeometry(p_inputGeometry) + , edges(p_graph->getEdges()) + {} + + /** + * Computes the topological labelling for the edges in the graph-> + */ + void computeLabelling(); + + /** + * Scans around a node CCW, propagating the side labels + * for a given area geometry to all edges (and their sym) + * with unknown locations for that geometry. + */ + void propagateAreaLocations(OverlayEdge* nodeEdge, uint8_t geomIndex); + + void markResultAreaEdges(int overlayOpCode); + + /** + * Marks an edge which forms part of the boundary of the result area. + * This is determined by the overlay operation being executed, + * and the location of the edge. + * The relevant location is either the right side of a boundary edge, + * or the line location of a non-boundary edge. + */ + static void markInResultArea(OverlayEdge* e, int overlayOpCode); + + /** + * Unmarks result area edges where the sym edge + * is also marked as in the result. + * This has the effect of merging edge-adjacent result areas, + * as required by polygon validity rules. + */ + void unmarkDuplicateEdgesFromResultArea(); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayMixedPoints.h b/Sources/geos/include/geos/operation/overlayng/OverlayMixedPoints.h new file mode 100644 index 0000000..c7b6812 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayMixedPoints.h @@ -0,0 +1,149 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include +#include + +#include + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class PrecisionModel; +class Geometry; +class Coordinate; +class CoordinateSequence; +} +namespace algorithm { +namespace locate { +class PointOnGeometryLocator; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Computes an overlay where one input is Point(s) and one is not. + * This class supports overlay being used as an efficient way + * to find points within or outside a polygon. + * + * Input semantics are: + * + * - Duplicates are removed from Point output + * - Non-point output is rounded and noded using the given precision model + * + * Output semantics are: + * + * - An empty result is an empty atomic geometry + * with dimension determined by the inputs and the operation, + * as per overlay semantics + * + * For efficiency the following optimizations are used: + * + * - Input points are not included in the noding of the non-point input geometry + * (in particular, they do not participate in snap-rounding if that is used). + * - If the non-point input geometry is not included in the output + * it is not rounded and noded. This means that points + * are compared to the non-rounded geometry. + * This will be apparent in the result. + * + * @author Martin Davis + */ +class GEOS_DLL OverlayMixedPoints { + using GeometryFactory = geos::geom::GeometryFactory; + using PrecisionModel = geos::geom::PrecisionModel; + using Geometry = geos::geom::Geometry; + using Coordinate = geos::geom::Coordinate; + using CoordinateXY = geos::geom::CoordinateXY; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Point = geos::geom::Point; + using Polygon = geos::geom::Polygon; + using LineString = geos::geom::LineString; + using PointOnGeometryLocator = algorithm::locate::PointOnGeometryLocator; + +private: + + // Members + int opCode; + const PrecisionModel* pm; + const Geometry* geomPoint; + const Geometry* geomNonPointInput; + const GeometryFactory* geometryFactory; + bool isPointRHS; + + std::unique_ptr geomNonPoint; + int geomNonPointDim; + std::unique_ptr locator; + int resultDim; + + // Methods + std::unique_ptr createLocator(const Geometry* geomNonPoint); + + std::unique_ptr prepareNonPoint(const Geometry* geomInput); + + std::unique_ptr computeIntersection(const CoordinateSequence* coords) const; + + std::unique_ptr computeUnion(const CoordinateSequence* coords); + + std::unique_ptr computeDifference(const CoordinateSequence* coords); + + std::unique_ptr createPointResult(std::vector>& points) const; + + std::vector> findPoints(bool isCovered, const CoordinateSequence* coords) const; + + std::vector> createPoints(const CoordinateSequence& coords) const; + + bool hasLocation(bool isCovered, const CoordinateXY& coord) const; + + std::unique_ptr copyNonPoint() const; + + std::unique_ptr extractCoordinates(const Geometry* points, const PrecisionModel* pm) const; + + std::vector> extractPolygons(const Geometry* geom) const; + + std::vector> extractLines(const Geometry* geom) const; + + + +public: + + OverlayMixedPoints(int p_opCode, const Geometry* geom0, const Geometry* geom1, const PrecisionModel* p_pm); + + static std::unique_ptr overlay(int opCode, const Geometry* geom0, const Geometry* geom1, const PrecisionModel* pm); + + std::unique_ptr getResult(); + + + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayNG.h b/Sources/geos/include/geos/operation/overlayng/OverlayNG.h new file mode 100644 index 0000000..9a3ad28 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayNG.h @@ -0,0 +1,413 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Sandro Santilli + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last Port: operation/overlayng/OverlayNG.java 4c88fea52 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class PrecisionModel; +} +namespace noding { +class Noder; +} +namespace operation { +namespace overlayng { +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Computes the geometric overlay of two {@link geom::Geometry}s, + * using an explicit precision model to allow robust computation. + * The overlay can be used to determine any of the + * following set-theoretic operations (boolean combinations) of the geometries: + * + * * INTERSECTION - all points which lie in both geometries + * * UNION - all points which lie in at least one geometry + * * DIFFERENCE - all points which lie in the first geometry but not the second + * * SYMDIFFERENCE - all points which lie in one geometry but not both + * + * The requirements for overlay input are: + * * Input collections must be homogeneous + * (all elements must have the same dimension). + * * Inputs may be simple link GeometryCollections. + * A GeometryCollection is simple if it can be flattened into a valid Multi-geometry; + * i.e. it is homogeneous and does not contain any overlapping Polygons. + * * In general, inputs must be valid geometries. + * However, polygonal inputs may contain the following two kinds of "mild" invalid topology: + * (i) rings which self-touch at discrete points (sometimes called inverted shells and exverted holes). + * (ii) rings which touch along line segments (i.e. topology collapse). + * + * The precision model used for the computation can be supplied + * independent of the precision model of the input geometry. + * The main use for this is to allow using a fixed precision + * for geometry with a floating precision model. + * This does two things: ensures robust computation; + * and forces the output to be validly rounded to the precision model. + * + * For fixed precision models noding is performed using a {@link noding::snapround::SnapRoundingNoder}. + * This provides robust computation (as long as precision is limited to + * around 13 decimal digits). + * + * For floating precision an {@link noding::MCIndexNoder} is used. + * This is not fully robust, so can sometimes result in + * {@link util::TopologyException}s being thrown. + * For robust full-precision overlay see {@link OverlayNGRobust}. + * + * Note: If a {@link noding::snap::SnappingNoder} is used + * it is best to specify a fairly small snap tolerance, + * since the intersection clipping optimization can + * interact with the snapping to alter the result. + * + * Optionally the overlay computation can process using strict mode + * (via setStrictMode(boolean). In strict mode result semantics are: + * + * - Lines and Points resulting from topology collapses are not included + * in the result + * - Result geometry is homogeneous for the + * INTERSECTION and DIFFERENCE operations. + * - Result geometry is homogeneous for the + * UNION and SYMDIFFERENCE operations if + * the inputs have the same dimension. + * + * Strict mode has the following benefits: + * + * - Results are simpler + * - Overlay operations are chainable without needing to remove + * lower-dimension elements + * + * The original JTS overlay semantics correspond to non-strict mode. + * + * If a robustness error occurs, a TopologyException is thrown. + * These are usually caused by numerical rounding causing the noding + * output to not be fully noded. + * For robust computation with full-precision {@link OverlayNGRobust} + * can be used. + * + * @author mdavis + * @see OverlayNGRobust + * + */ +class GEOS_DLL OverlayNG { + using Geometry = geos::geom::Geometry; + using PrecisionModel = geos::geom::PrecisionModel; + using Location = geos::geom::Location; + +private: + + // Members + const geom::PrecisionModel* pm; + InputGeometry inputGeom; + const geom::GeometryFactory* geomFact; + int opCode; + noding::Noder* noder; + bool isStrictMode; + bool isOptimized; + bool isAreaResultOnly; + bool isOutputEdges; + bool isOutputResultEdges; + bool isOutputNodedEdges; + + // Methods + std::unique_ptr computeEdgeOverlay(); + void labelGraph(OverlayGraph* graph); + + /** + * Extracts the result geometry components from the fully labelled topology graph. + * + * This method implements the semantic that the result of an + * intersection operation is homogeneous with highest dimension. + * In other words, + * if an intersection has components of a given dimension + * no lower-dimension components are output. + * For example, if two polygons intersect in an area, + * no linestrings or points are included in the result, + * even if portions of the input do meet in lines or points. + * This semantic choice makes more sense for typical usage, + * in which only the highest dimension components are of interest. + */ + std::unique_ptr extractResult(int opCode, OverlayGraph* graph); + std::unique_ptr createEmptyResult(); + + + +public: + /** + * The default setting for Strict Mode. + * + * The original JTS overlay semantics used non-strict result + * semantics, including; + * - An Intersection result can be mixed-dimension, + * due to inclusion of intersection components of all dimensions + * - Results can include lines caused by Area topology collapse + */ + static constexpr bool STRICT_MODE_DEFAULT = false; + + static constexpr int INTERSECTION = 1; + static constexpr int UNION = 2; + static constexpr int DIFFERENCE = 3; + static constexpr int SYMDIFFERENCE = 4; + + /** + * Creates an overlay operation on the given geometries, + * with a defined precision model. + * The noding strategy is determined by the precision model. + */ + OverlayNG(const geom::Geometry* geom0, const geom::Geometry* geom1, const geom::GeometryFactory* p_geomFact, int p_opCode) + : pm(p_geomFact->getPrecisionModel()) + , inputGeom(geom0, geom1) + , geomFact(p_geomFact) + , opCode(p_opCode) + , noder(nullptr) + , isStrictMode(STRICT_MODE_DEFAULT) + , isOptimized(true) + , isAreaResultOnly(false) + , isOutputEdges(false) + , isOutputResultEdges(false) + , isOutputNodedEdges(false) + {} + + /** + * Creates an overlay operation on the given geometries, + * with a defined precision model. + * The noding strategy is determined by the precision model. + */ + OverlayNG(const geom::Geometry* geom0, const geom::Geometry* geom1, const geom::PrecisionModel* p_pm, int p_opCode) + : pm(p_pm) + , inputGeom(geom0, geom1) + , geomFact(geom0->getFactory()) + , opCode(p_opCode) + , noder(nullptr) + , isStrictMode(STRICT_MODE_DEFAULT) + , isOptimized(true) + , isAreaResultOnly(false) + , isOutputEdges(false) + , isOutputResultEdges(false) + , isOutputNodedEdges(false) + {} + + /** + * Creates an overlay operation on the given geometries + * using the precision model of the geometries. + * + * The noder is chosen according to the precision model specified. + * + * - For {@link PrecisionModel#FIXED} + * a snap-rounding noder is used, and the computation is robust. + * - For {@link PrecisionModel#FLOATING} + * a non-snapping noder is used, + * and this computation may not be robust. + * If errors occur a {@link util::TopologyException} is thrown. + */ + OverlayNG(const geom::Geometry* geom0, const geom::Geometry* geom1, int p_opCode) + : OverlayNG(geom0, geom1, geom0->getFactory()->getPrecisionModel(), p_opCode) + {} + + OverlayNG(const geom::Geometry* geom0, const geom::PrecisionModel* p_pm) + : OverlayNG(geom0, nullptr, p_pm, UNION) + {} + + /** + * Sets whether overlay processing optimizations are enabled. + * It may be useful to disable optimizations + * for testing purposes. + * Default is TRUE (optimization enabled). + * + * @param p_isOptimized whether to optimize processing + */ + void setOptimized(bool p_isOptimized) { isOptimized = p_isOptimized; } + void setStrictMode(bool p_isStrictMode) { isStrictMode = p_isStrictMode; } + void setAreaResultOnly(bool p_areaResultOnly) { isAreaResultOnly = p_areaResultOnly; } + void setOutputEdges(bool p_isOutputEdges) { isOutputEdges = p_isOutputEdges; } + void setOutputResultEdges(bool p_isOutputResultEdges) { isOutputResultEdges = p_isOutputResultEdges; } + void setNoder(noding::Noder* p_noder) { noder = p_noder; } + + void setOutputNodedEdges(bool p_isOutputNodedEdges) + { + isOutputEdges = true; + isOutputNodedEdges = p_isOutputNodedEdges; + } + + /** + * Gets the result of the overlay operation. + * + * @return the result of the overlay operation. + * + * @throws IllegalArgumentException if the input is not supported (e.g. a mixed-dimension geometry) + * @throws TopologyException if a robustness error occurs + */ + std::unique_ptr getResult(); + + /** + * Tests whether a point with a given topological {@link OverlayLabel} + * relative to two geometries is contained in + * the result of overlaying the geometries using + * a given overlay operation. + * + * The method handles arguments of {@link geom::Location::NONE} correctly + */ + static bool isResultOfOpPoint(const OverlayLabel* label, int opCode); + + /** + * Tests whether a point with given {@link geom::Location}s + * relative to two geometries would be contained in + * the result of overlaying the geometries using + * a given overlay operation. + * This is used to determine whether components + * computed during the overlay process should be + * included in the result geometry. + * + * The method handles arguments of {@link geom::Location::NONE} correctly. + */ + static bool isResultOfOp(int overlayOpCode, Location loc0, Location loc1); + + /** + * Computes an overlay operation for + * the given geometry operands, with the + * noding strategy determined by the precision model. + * + * @param geom0 the first geometry argument + * @param geom1 the second geometry argument + * @param opCode the code for the desired overlay operation + * @param pm the precision model to use + * @return the result of the overlay operation + */ + static std::unique_ptr + overlay(const Geometry* geom0, const Geometry* geom1, + int opCode, const PrecisionModel* pm); + + + /** + * Computes an overlay operation on the given geometry operands, + * using a supplied {@link noding::Noder}. + * + * @param geom0 the first geometry argument + * @param geom1 the second geometry argument + * @param opCode the code for the desired overlay operation + * @param pm the precision model to use (which may be null if the noder does not use one) + * @param noder the noder to use + * @return the result of the overlay operation + */ + static std::unique_ptr + overlay(const Geometry* geom0, const Geometry* geom1, + int opCode, const PrecisionModel* pm, noding::Noder* noder); + + + /** + * Computes an overlay operation on the given geometry operands, + * using a supplied {@link noding::Noder}. + * + * @param geom0 the first geometry argument + * @param geom1 the second geometry argument + * @param opCode the code for the desired overlay operation + * @param noder the noder to use + * @return the result of the overlay operation + */ + static std::unique_ptr + overlay(const Geometry* geom0, const Geometry* geom1, + int opCode, noding::Noder* noder); + + /** + * Computes an overlay operation on + * the given geometry operands, + * using the precision model of the geometry. + * and an appropriate noder. + * + * The noder is chosen according to the precision model specified. + * + * - For {@link geom::PrecisionModel#FIXED} + * a snap-rounding noder is used, and the computation is robust. + * - For {@link geom::PrecisionModel#FLOATING} + * a non-snapping noder is used, + * and this computation may not be robust. + * If errors occur a {@link util::TopologyException} is thrown. + * + * @param geom0 the first argument geometry + * @param geom1 the second argument geometry + * @param opCode the code for the desired overlay operation + * @return the result of the overlay operation + */ + static std::unique_ptr + overlay(const Geometry* geom0, const Geometry* geom1, int opCode); + + + /** + * Computes a union operation on + * the given geometry, with the supplied precision model. + * The primary use for this is to perform precision reduction + * (round the geometry to the supplied precision). + * + * The input must be a valid geometry. + * Collections must be homogeneous. + * IMPORTANT: You probably want OverlayNGUnaryUnion, not this. + * + * @param geom the geometry + * @param pm the precision model to use + * @return the result of the union operation + * + * @see OverlayMixedPoints + * @see PrecisionReducer + * @see UnaryUnionNG + * @see CoverageUnion + */ + static std::unique_ptr + geomunion(const Geometry* geom, const PrecisionModel* pm); + + + /** + * Computes a union of a single geometry using a custom noder. + * + * The primary use of this is to support coverage union. + * + * The input must be a valid geometry. + * Collections must be homogeneous. + * IMPORTANT: You probably want OverlayNGUnaryUnion, not this. + * + * @param geom the geometry to union + * @param pm the precision model to use (maybe be null) + * @param noder the noder to use + * @return the result geometry + * + * @see CoverageUnion + */ + static std::unique_ptr + geomunion(const Geometry* geom, const PrecisionModel* pm, noding::Noder* noder); + + + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayNGRobust.h b/Sources/geos/include/geos/operation/overlayng/OverlayNGRobust.h new file mode 100644 index 0000000..c63d7d3 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayNGRobust.h @@ -0,0 +1,175 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/overlayng/OverlayNGRobust.java 6ef89b09 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + + +/** + * Performs an overlay operation, increasing robustness by using a series of + * increasingly aggressive (and slower) noding strategies. + * + * The noding strategies used are: + * + * - A simple, fast noder using FLOATING precision. + * - A {@link noding::snap::SnappingNoder} using an automatically-determined snap tolerance + * - First snapping each geometry to itself, + * and then overlaying them using a SnappingNoder. + * - The above two strategies are repeated with increasing snap tolerance, up to a limit. + * + * If the above heuristics still fail to compute a valid overlay, + * the original {@link util::TopologyException} is thrown. + * + * This algorithm relies on each overlay operation execution + * throwing a {@link util::TopologyException} if it is unable + * to compute the overlay correctly. + * Generally this occurs because the noding phase does + * not produce a valid noding. + * This requires the use of a {@link noding::ValidatingNoder} + * in order to check the results of using a floating noder. + * + * @author Martin Davis + */ +class GEOS_DLL OverlayNGRobust { + using Geometry = geos::geom::Geometry; + +private: + + + // Constants + static constexpr int NUM_SNAP_TRIES = 5; + /** + * A factor for a snapping tolerance distance which + * should allow noding to be computed robustly. + */ + static constexpr double SNAP_TOL_FACTOR = 1e12; + + static std::unique_ptr overlaySnapping( + const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol); + + static std::unique_ptr overlaySnapBoth( + const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol); + + static std::unique_ptr overlaySnapTol( + const Geometry* geom0, const Geometry* geom1, int opCode, double snapTol); + + static double snapTolerance(const Geometry* geom); + + /** + * Computes the largest magnitude of the ordinates of a geometry, + * based on the geometry envelope. + * + * @param geom a geometry + * @return the magnitude of the largest ordinate + */ + static double ordinateMagnitude(const Geometry* geom); + + /** + * Overlay using Snap-Rounding with an automatically-determined + * scale factor. + * + * NOTE: currently this strategy is not used, since all known + * test cases work using one of the Snapping strategies. + */ + static std::unique_ptr + overlaySR(const Geometry* geom0, const Geometry* geom1, int opCode); + + /** + * Self-snaps a geometry by running a union operation with it as the only input. + * This helps to remove narrow spike/gore artifacts to simplify the geometry, + * which improves robustness. + * Collapsed artifacts are removed from the result to allow using + * it in further overlay operations. + * + * @param geom geometry to self-snap + * @param snapTol snap tolerance + * @return the snapped geometry (homogeneous) + */ + static std::unique_ptr + snapSelf(const Geometry* geom, double snapTol); + + +public: + + class SRUnionStrategy : public operation::geounion::UnionStrategy { + + std::unique_ptr Union(const geom::Geometry* g0, const geom::Geometry* g1) override + { + return OverlayNGRobust::Overlay(g0, g1, OverlayNG::UNION); + }; + + bool isFloatingPrecision() const override + { + return true; + }; + + }; + + static std::unique_ptr Intersection( + const Geometry* g0, const Geometry* g1); + + static std::unique_ptr Union( + const Geometry* g0, const Geometry* g1); + + static std::unique_ptr Difference( + const Geometry* g0, const Geometry* g1); + + static std::unique_ptr SymDifference( + const Geometry* g0, const Geometry* g1); + + static std::unique_ptr Union( + const Geometry* a); + + static std::unique_ptr Overlay( + const Geometry* geom0, const Geometry* geom1, int opCode); + + static std::unique_ptr overlaySnapTries( + const Geometry* geom0, const Geometry* geom1, int opCode); + + /** + * Computes a heuristic snap tolerance distance + * for overlaying a pair of geometries using a {@link noding::snap::SnappingNoder}. + */ + static double snapTolerance(const Geometry* geom0, const Geometry* geom1); + + + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayPoints.h b/Sources/geos/include/geos/operation/overlayng/OverlayPoints.h new file mode 100644 index 0000000..0130c30 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayPoints.h @@ -0,0 +1,125 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class GeometryFactory; +class Geometry; +class PrecisionModel; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Performs an overlay operation on inputs which are both point geometries. + * + * Semantics are: + * + * - Points are rounded to the precision model if provided + * - Points with identical XY values are merged to a single point + * - Extended ordinate values are preserved in the output, + * apart from merging + * - An empty result is returned as POINT EMPTY + * + * @author Martin Davis + */ +class GEOS_DLL OverlayPoints { + using Coordinate = geos::geom::Coordinate; + using CoordinateXY = geos::geom::CoordinateXY; + using CoordinateSequence = geos::geom::CoordinateSequence; + using GeometryFactory = geos::geom::GeometryFactory; + using Geometry = geos::geom::Geometry; + using Point = geos::geom::Point; + using PrecisionModel = geos::geom::PrecisionModel; + +private: + + // Members + int opCode; + const Geometry* geom0; + const Geometry* geom1; + const PrecisionModel* pm; + const GeometryFactory* geometryFactory; + std::vector> resultList; + + using PointMap = std::map>; + + // Methods + void + computeIntersection(PointMap& map0, + PointMap& map1, + std::vector>& resultList); + + void + computeDifference(PointMap& map0, + PointMap& map1, + std::vector>& resultList); + + void + computeUnion(PointMap& map0, + PointMap& map1, + std::vector>& resultList); + + PointMap buildPointMap(const Geometry* geom); + +public: + + /** + * Creates an instance of an overlay operation on inputs which are both point geometries. + */ + OverlayPoints(int p_opCode, const Geometry* p_geom0, const Geometry* p_geom1, const PrecisionModel* p_pm) + : opCode(p_opCode) + , geom0(p_geom0) + , geom1(p_geom1) + , pm(p_pm) + , geometryFactory(p_geom0->getFactory()) {} + + OverlayPoints(const OverlayPoints&) = delete; + OverlayPoints& operator=(const OverlayPoints&) = delete; + + /** + * Performs an overlay operation on inputs which are both point geometries. + */ + static std::unique_ptr overlay(int opCode, const Geometry* geom0, const Geometry* geom1, const PrecisionModel* pm); + + /** + * Gets the result of the overlay. + * + * @return the overlay result + */ + std::unique_ptr getResult(); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/overlayng/OverlayUtil.h b/Sources/geos/include/geos/operation/overlayng/OverlayUtil.h new file mode 100644 index 0000000..6bff9c3 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/OverlayUtil.h @@ -0,0 +1,226 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Envelope; +class GeometryFactory; +class PrecisionModel; +} +namespace operation { +namespace overlayng { +class InputGeometry; +class OverlayGraph; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Utility methods for overlay processing. + * + * @author mdavis + * + */ +class GEOS_DLL OverlayUtil { + using Geometry = geos::geom::Geometry; + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using Point = geos::geom::Point; + using LineString = geos::geom::LineString; + using Polygon = geos::geom::Polygon; + using GeometryFactory = geos::geom::GeometryFactory; + using PrecisionModel = geos::geom::PrecisionModel; + +private: + + static constexpr double SAFE_ENV_BUFFER_FACTOR = 0.1; + static constexpr int SAFE_ENV_GRID_FACTOR = 3; + static constexpr double AREA_HEURISTIC_TOLERANCE = 0.1; + + /** + * Computes an envelope which covers the extent of the result of + * a given overlay operation for given inputs. + * The operations which have a result envelope smaller than the extent of the inputs + * are: + * + * - INTERSECTION: result envelope is the intersection of the input envelopes + * - DIFFERENCE: result envelope is the envelope of the A input geometry + * + * Otherwise, null is returned to indicate full extent. + */ + static bool resultEnvelope(int opCode, const InputGeometry* inputGeom, const PrecisionModel* pm, Envelope& rsltEnvelope); + static double safeExpandDistance(const Envelope* env, const PrecisionModel* pm); + static bool safeEnv(const Envelope* env, const PrecisionModel* pm, Envelope& rsltEnvelope); + + static bool isEmpty(const Geometry* geom); + + /** + * Tests for disjoint envelopes adjusting for rounding + * caused by a fixed precision model. + * Assumes envelopes are non-empty. + */ + static bool isDisjoint(const Envelope* envA, const Envelope* envB, const PrecisionModel* pm); + + static bool isLess(double v1, double v2, double tol) { + return v1 <= v2 * (1 + tol); + }; + + static bool isGreater(double v1, double v2, double tol) { + return v1 >= v2 * (1 - tol); + } + + +public: + + static bool isFloating(const PrecisionModel* pm); + + /** + * Computes a clipping envelope for overlay input geometries. + * The clipping envelope encloses all geometry line segments which + * might participate in the overlay, with a buffer to + * account for numerical precision + * (in particular, rounding due to a precision model. + * The clipping envelope is used in both the {@link RingClipper} + * and in the {@link LineLimiter}. + * + * Some overlay operations (i.e. UNION and SYMDIFFERENCE + * cannot use clipping as an optimization, + * since the result envelope is the full extent of the two input geometries. + * In this case the returned + * envelope is null to indicate this. + */ + static bool clippingEnvelope(int opCode, const InputGeometry* inputGeom, const PrecisionModel* pm, Envelope& rsltEnvelope); + + /** + * Tests if the result can be determined to be empty + * based on simple properties of the input geometries + * (such as whether one or both are empty, + * or their envelopes are disjoint). + */ + static bool isEmptyResult(int opCode, const Geometry* a, const Geometry* b, const PrecisionModel* pm); + + /** + * Tests if the geometry envelopes are disjoint, or empty. + * The disjoint test must take into account the precision model + * being used, since geometry coordinates may shift under rounding. + */ + static bool isEnvDisjoint(const Geometry* a, const Geometry* b, const PrecisionModel* pm); + + /** + * Creates an empty result geometry of the appropriate dimension and coordinate dimension, + * based on the given overlay operation and the dimensions of the inputs. + * The created geometry is an atomic geometry, + * not a collection (unless the dimension is -1, + * in which case a GEOMETRYCOLLECTION EMPTY is created.) + */ + static std::unique_ptr createEmptyResult(int dim, size_t coordDim, const GeometryFactory* geomFact); + + /** + * Computes the dimension of the result of + * applying the given operation to inputs + * with the given dimensions. + * This assumes that complete collapse does not occur. + * + * The result dimension is computed according to the following rules: + * - OverlayNG::INTERSECTION - result has the dimension of the lowest input dimension + * - OverlayNG::UNION - result has the dimension of the highest input dimension + * - OverlayNG::DIFFERENCE - result has the dimension of the left-hand input + * - OverlayNG::SYMDIFFERENCE - result has the dimension of the highest input dimension + * (since the Symmetric Difference is the Union of the Differences). + */ + static int resultDimension(int opCode, int dim0, int dim1); + + /** + * Computes the coordinate dimension of overlaying two geometries. + * This is the smallest of the two coordinate dimensions + * (to avoid having to populate Z and M with unknown values). + */ + static uint8_t resultCoordinateDimension(uint8_t coordDim0, uint8_t coordDim1); + + /** + * Creates an overlay result geometry for homogeneous or mixed components. + */ + static std::unique_ptr createResultGeometry( + std::vector>& resultPolyList, + std::vector>& resultLineList, + std::vector>& resultPointList, + const GeometryFactory* geometryFactory); + + static std::unique_ptr toLines(OverlayGraph* graph, bool isOutputEdges, const GeometryFactory* geomFact); + + /** + * A heuristic check for overlay result correctness + * comparing the areas of the input and result. + * The heuristic is necessarily coarse, but it detects some obvious issues. + * (e.g. https://github.com/locationtech/jts/issues/798) + *

+ * Note: - this check is only safe if the precision model is floating. + * It should also be safe for snapping noding if the distance tolerance is reasonably small. + * (Fixed precision models can lead to collapse causing result area to expand.) + * + * @param geom0 input geometry 0 + * @param geom1 input geometry 1 + * @param opCode the overlay opcode + * @param result the overlay result + * @return true if the result area is consistent + */ + static bool isResultAreaConsistent( + const Geometry* geom0, const Geometry* geom1, + int opCode, const Geometry* result); + + /** + * Round the key point if precision model is fixed. + * Note: return value is only copied if rounding is performed. + */ + static bool round(const Point* pt, const PrecisionModel* pm, Coordinate& rsltCoord); + + template + static void moveGeometry(std::vector>& inGeoms, std::vector>& outGeoms) + { + static_assert(std::is_base_of::value, ""); + for (auto& geom: inGeoms) { + Geometry* outGeom = static_cast(geom.release()); + outGeoms.emplace_back(outGeom); + } + return; + } + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/PolygonBuilder.h b/Sources/geos/include/geos/operation/overlayng/PolygonBuilder.h new file mode 100644 index 0000000..9e820fc --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/PolygonBuilder.h @@ -0,0 +1,151 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +class Polygon; +} +namespace operation { +namespace overlayng { +class Edge; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +class GEOS_DLL PolygonBuilder { + +private: + + // Members + const geom::GeometryFactory* geometryFactory; + std::vector shellList; + std::vector freeHoleList; + bool isEnforcePolygonal; + + // Storage + std::vector> vecOER; + + std::vector> computePolygons(const std::vector& shellList) const; + + void buildRings(const std::vector& resultAreaEdges); + + static void linkResultAreaEdgesMax(const std::vector& resultEdges); + + /** + * For all OverlayEdge*s in result, form them into MaximalEdgeRings + */ + static std::vector> + buildMaximalRings(const std::vector &edges); + + /** + * The lifespan of the OverlayEdgeRings is tieds to the lifespan + * of the PolygonBuilder, so we hold them on the PolygonBuilder + * and use bare pointers for managing the relationships + */ + std::vector storeMinimalRings(std::vector>& minRings); + + void buildMinimalRings(const std::vector>& maxRings); + + void assignShellsAndHoles(const std::vector &minRings); + + /** + * Finds the single shell, if any, out of + * a list of minimal rings derived from a maximal ring. + * The other possibility is that they are a set of (connected) holes, + * in which case no shell will be found. + * + * @return the shell ring, if there is one + * or null, if all rings are holes + */ + static OverlayEdgeRing* findSingleShell(const std::vector &edgeRings) ; + + /** + * For the set of minimal rings comprising a maximal ring, + * assigns the holes to the shell known to contain them. + * Assigning the holes directly to the shell serves two purposes: + * + * - it is faster than using a point-in-polygon check later on. + * - it ensures correctness, since if the PIP test was used the point + * chosen might lie on the shell, which might return an incorrect result from the + * PIP test + */ + static void assignHoles(OverlayEdgeRing* shell, const std::vector &edgeRings); + + + /** + * Place holes have not yet been assigned to a shell. + * These "free" holes should + * all be properly contained in their parent shells, so it is safe to use the + * findEdgeRingContaining method. + * (This is the case because any holes which are NOT + * properly contained (i.e. are connected to their + * parent shell) would have formed part of a MaximalEdgeRing + * and been handled in a previous step). + * + * @throws TopologyException if a hole cannot be assigned to a shell + */ + void placeFreeHoles(const std::vector & shellList, const std::vector& freeHoleList) const; + + + +public: + + PolygonBuilder(std::vector& resultAreaEdges, const geom::GeometryFactory* geomFact) + : geometryFactory(geomFact) + , isEnforcePolygonal(true) + { + buildRings(resultAreaEdges); + } + + PolygonBuilder(std::vector& resultAreaEdges, const geom::GeometryFactory* geomFact, bool p_isEnforcePolygonal) + : geometryFactory(geomFact) + , isEnforcePolygonal(p_isEnforcePolygonal) + { + buildRings(resultAreaEdges); + } + + PolygonBuilder(const PolygonBuilder&) = delete; + PolygonBuilder& operator=(const PolygonBuilder&) = delete; + + // Methods + std::vector> getPolygons() const; + std::vector getShellRings() const; + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/PrecisionReducer.h b/Sources/geos/include/geos/operation/overlayng/PrecisionReducer.h new file mode 100644 index 0000000..50f3429 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/PrecisionReducer.h @@ -0,0 +1,75 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +} +namespace operation { +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** +* Reduces the precision of a geometry by rounding it to the +* supplied {@link geom::PrecisionModel}. +* +* The output is always a valid geometry. This implies that input components +* may be merged if they are closer than the grid precision. +* if merging is not desired, then the individual geometry components +* should be processed separately. +* +* The output is fully noded. +* This provides an effective way to node / snap-round a collection of {@link geom::LineString}s. +*/ +class GEOS_DLL PrecisionReducer { + using PrecisionModel = geos::geom::PrecisionModel; + using Geometry = geos::geom::Geometry; + +private: + + // Members + + // Methods + + +public: + + PrecisionReducer() {}; + + static std::unique_ptr reducePrecision(const Geometry* geom, const PrecisionModel* pm, bool replacePrecisionModel = false); + + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/PrecisionUtil.h b/Sources/geos/include/geos/operation/overlayng/PrecisionUtil.h new file mode 100644 index 0000000..b515a19 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/PrecisionUtil.h @@ -0,0 +1,247 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class Envelope; +class PrecisionModel; +} +namespace operation { +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Unions a collection of geometries in an + * efficient way, using {@link OverlayNG} + * to ensure robust computation. + * @author Martin Davis + */ +class GEOS_DLL PrecisionUtil { + using Geometry = geos::geom::Geometry; + using Envelope = geos::geom::Envelope; + using PrecisionModel = geos::geom::PrecisionModel; + using CoordinateFilter = geos::geom::CoordinateFilter; + +private: + + static double robustScale(double inherentScale, double safeScale); + + /** + * Determines the maximum magnitude (absolute value) of the bounds of an + * of an envelope. + * This is equal to the largest ordinate value + * which must be accommodated by a scale factor. + * + */ + static double maxBoundMagnitude(const Envelope* env); + + /** + * Computes the scale factor which will + * produce a given number of digits of precision (significant digits) + * when used to round the given number. + * + * For example: to provide 5 decimal digits of precision + * for the number 123.456 the precision scale factor is 100; + * for 3 digits of precision the scale factor is 1; + * for 2 digits of precision the scale factor is 0.1. + * + * Rounding to the scale factor can be performed with {@link PrecisionModel#round} + * + * @see PrecisionModel.round + */ + static double precisionScale(double value, int precisionDigits); + + + +public: + + static constexpr int MAX_ROBUST_DP_DIGITS = 14; + + PrecisionUtil() {}; + + /** + * Determines a precision model to + * use for robust overlay operations. + * The precision scale factor is chosen to maximize + * output precision while avoiding round-off issues. + * + * NOTE: this is a heuristic determination, so is not guaranteed to + * eliminate precision issues. + * + * WARNING: this is quite slow. + */ + static PrecisionModel robustPM(const Geometry* a, const Geometry* b); + + /** + * Determines a precision model to + * use for robust overlay operations for one geometry. + * The precision scale factor is chosen to maximize + * output precision while avoiding round-off issues. + * + * NOTE: this is a heuristic determination, so is not guaranteed to + * eliminate precision issues. + * + * WARNING: this is quite slow. + */ + static PrecisionModel robustPM(const Geometry* a); + + /** + * Determines a scale factor which maximizes + * the digits of precision and is + * safe to use for overlay operations. + * The robust scale is the minimum of the + * inherent scale and the safe scale factors. + */ + static double robustScale(const Geometry* a, const Geometry* b); + + /** + * Determines a scale factor which maximizes + * the digits of precision and is + * safe to use for overlay operations. + * The robust scale is the minimum of the + * inherent scale and the safe scale factors. + */ + static double robustScale(const Geometry* a); + + /** + * Computes a safe scale factor for a numeric value. + * A safe scale factor ensures that rounded + * number has no more than MAX_PRECISION_DIGITS + * digits of precision. + */ + static double safeScale(double value); + + /** + * Computes a safe scale factor for a geometry. + * A safe scale factor ensures that the rounded + * ordinates have no more than MAX_PRECISION_DIGITS + * digits of precision. + */ + static double safeScale(const Geometry* geom); + + /** + * Computes a safe scale factor for two geometries. + * A safe scale factor ensures that the rounded + * ordinates have no more than MAX_PRECISION_DIGITS + * digits of precision. + */ + static double safeScale(const Geometry* a, const Geometry* b); + + /** + * Computes the inherent scale of a number. + * The inherent scale is the scale factor for rounding + * which preserves all digits of precision + * (significant digits) + * present in the numeric value. + * In other words, it is the scale factor which does not + * change the numeric value when rounded: + * + * num = round( num, inherentScale(num) ) + */ + static double inherentScale(double value); + + /** + * Computes the inherent scale of a geometry. + * The inherent scale is the scale factor for rounding + * which preserves all digits of precision + * (significant digits) + * present in the geometry ordinates. + * + * This is the maximum inherent scale + * of all ordinate values in the geometry. + */ + static double inherentScale(const Geometry* geom); + + /** + * Computes the inherent scale of two geometries. + * The inherent scale is the scale factor for rounding + * which preserves all digits of precision + * (significant digits) + * present in the geometry ordinates. + * + * This is the maximum inherent scale + * of all ordinate values in the geometries. + */ + static double inherentScale(const Geometry* a, const Geometry* b); + + /** + * Determines the + * number of decimal places represented in a double-precision + * number (as determined by Java). + * This uses the Java double-precision print routine + * to determine the number of decimal places, + * This is likely not optimal for performance, + * but should be accurate and portable. + */ + static int numberOfDecimals(double value); + + /** + * Applies the inherent scale calculation + * to every ordinate in a geometry. + */ + class GEOS_DLL InherentScaleFilter: public CoordinateFilter { + + private: + + double scale; + + void updateScaleMax(double value) { + double scaleVal = PrecisionUtil::inherentScale(value); + if (scaleVal > scale) { + scale = scaleVal; + } + } + + public: + + InherentScaleFilter() + : scale(0.0) + {} + + void filter_ro(const geom::Coordinate* coord) override + { + updateScaleMax(coord->x); + updateScaleMax(coord->y); + } + + double getScale() const { + return scale; + } + }; + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/RingClipper.h b/Sources/geos/include/geos/operation/overlayng/RingClipper.h new file mode 100644 index 0000000..d8f6a18 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/RingClipper.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Clips rings of points to a rectangle. + * Uses a variant of Cohen-Sutherland clipping. + * + * In general the output is not topologically valid. + * In particular, the output may contain coincident non-noded line segments + * along the clip rectangle sides. + * However, the output is sufficiently well-structured + * that it can be used as input to the {@link OverlayNG} algorithm + * (which is able to process coincident linework due + * to the need to handle topology collapse under precision reduction). + * + * Because of the likelihood of creating + * extraneous line segments along the clipping rectangle sides, + * this class is not suitable for clipping linestrings. + * + * The clipping envelope should be generated using {@link RobustClipEnvelopeComputer}, + * to ensure that intersecting line segments are not perturbed + * by clipping. + * This is required to ensure that the overlay of the + * clipped geometry is robust and correct (i.e. the same as + * if clipping was not used). + * + * @see LineLimiter + * + * @author Martin Davis + * + */ +class GEOS_DLL RingClipper { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + +private: + + // Constants + static constexpr int BOX_LEFT = 3; + static constexpr int BOX_TOP = 2; + static constexpr int BOX_RIGHT = 1; + static constexpr int BOX_BOTTOM = 0; + + // Members + const Envelope clipEnv; + + // Methods + + /** + * Clips line to the axis-parallel line defined by a single box edge. + */ + std::unique_ptr clipToBoxEdge(const CoordinateSequence* pts, int edgeIndex, bool closeRing) const; + + /** + * Computes the intersection point of a segment + * with an edge of the clip box. + * The segment must be known to intersect the edge. + */ + void intersection(const Coordinate& a, const Coordinate& b, int edgeIndex, Coordinate& rsltPt) const; + double intersectionLineY(const Coordinate& a, const Coordinate& b, double y) const; + double intersectionLineX(const Coordinate& a, const Coordinate& b, double x) const; + bool isInsideEdge(const Coordinate& p, int edgeIndex) const; + + +public: + + RingClipper(const Envelope* env) + : clipEnv(*env) + {}; + + /** + * Clips a list of points to the clipping rectangle box. + */ + std::unique_ptr clip(const CoordinateSequence* cs) const; + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/RobustClipEnvelopeComputer.h b/Sources/geos/include/geos/operation/overlayng/RobustClipEnvelopeComputer.h new file mode 100644 index 0000000..4c43ed7 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/RobustClipEnvelopeComputer.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Geometry; +class GeometryCollection; +class Polygon; +class LinearRing; +} +namespace operation { +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Computes a robust clipping envelope for a pair of polygonal geometries. + * The envelope is computed to be large enough to include the full + * length of all geometry line segments which intersect + * a given target envelope. + * This ensures that line segments which might intersect are + * not perturbed when clipped using {@link RingClipper}. + * @author mdavis + */ +class GEOS_DLL RobustClipEnvelopeComputer { + using Coordinate = geos::geom::Coordinate; + using Geometry = geos::geom::Geometry; + using GeometryCollection = geos::geom::GeometryCollection; + using Polygon = geos::geom::Polygon; + using LinearRing = geos::geom::LinearRing; + using Envelope = geos::geom::Envelope; + +private: + + // Members + const Envelope* targetEnv; + Envelope clipEnv; + + // Methods + void add(const Geometry* g); + void addCollection(const GeometryCollection* gc); + void addPolygon(const Polygon* poly); + void addPolygonRing(const LinearRing* ring); + void addSegment(const Coordinate& p1, const Coordinate& p2); + bool intersectsSegment(const Envelope* env, const Coordinate& p1, const Coordinate& p2); + + + +public: + + RobustClipEnvelopeComputer(const Envelope* p_targetEnv); + + static Envelope getEnvelope(const Geometry* a, const Geometry* b, const Envelope* targetEnv); + + Envelope getEnvelope(); + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/overlayng/UnaryUnionNG.h b/Sources/geos/include/geos/operation/overlayng/UnaryUnionNG.h new file mode 100644 index 0000000..ebc7d75 --- /dev/null +++ b/Sources/geos/include/geos/operation/overlayng/UnaryUnionNG.h @@ -0,0 +1,97 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace overlayng { // geos.operation.overlayng + +/** + * Unions a collection of geometries in an + * efficient way, using {@link OverlayNG} + * to ensure robust computation. + * + * @author Martin Davis + * + */ +class GEOS_DLL UnaryUnionNG { + using Geometry = geos::geom::Geometry; + using PrecisionModel = geos::geom::PrecisionModel; + +private: + + // Members + + + +public: + + /** + * Strategy class for NG unions. + */ + class NGUnionStrategy : public operation::geounion::UnionStrategy { + + public: + + NGUnionStrategy(const PrecisionModel& p_pm) + : pm(p_pm) + {}; + + std::unique_ptr + Union(const geom::Geometry* g0, const geom::Geometry* g1) override + { + return OverlayNG::overlay(g0, g1, OverlayNG::UNION, &pm); + } + + bool + isFloatingPrecision() const override + { + return OverlayUtil::isFloating(&pm); + } + + private: + + const geom::PrecisionModel& pm; + + }; + + + // static methods + static std::unique_ptr Union(const Geometry* geom, const PrecisionModel& pm); + static std::unique_ptr Union(const Geometry* geom); + + +}; + + +} // namespace geos.operation.overlayng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/polygonize/BuildArea.h b/Sources/geos/include/geos/operation/polygonize/BuildArea.h new file mode 100644 index 0000000..679698a --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/BuildArea.h @@ -0,0 +1,75 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright 2011-2014 Sandro Santilli + * Copyright (C) 2019 Even Rouault + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + ********************************************************************** + * + * Ported from rtgeom_geos.c from + * rttopo - topology library + * http://git.osgeo.org/gitea/rttopo/librttopo + * with relicensing from GPL to LGPL with Copyright holder permission. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + +/** \brief Creates an areal geometry formed by the constituent linework of given geometry. + * + * The return type can be a Polygon or MultiPolygon, depending on input. + * If the input lineworks do not form polygons NULL is returned. + * The inputs can be LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS, and GeometryCollections. + * + * This algorithm will assume all inner geometries represent holes + * + * This is the equivalent of the PostGIS ST_BuildArea() function. + */ +class GEOS_DLL BuildArea { + +public: + + /** \brief + * Create a BuildArea object. + */ + BuildArea() = default; + + ~BuildArea() = default; + + /** \brief Return the area built from the constituent linework of the input geometry. */ + std::unique_ptr build(const geom::Geometry* geom); +}; + +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/polygonize/EdgeRing.h b/Sources/geos/include/geos/operation/polygonize/EdgeRing.h new file mode 100644 index 0000000..30f5be2 --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/EdgeRing.h @@ -0,0 +1,350 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/EdgeRing.java 0b3c7e3eb0d3e + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class CoordinateSequence; +class GeometryFactory; +class Coordinate; +} +namespace planargraph { +class DirectedEdge; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + +/** \brief + * Represents a ring of PolygonizeDirectedEdge which form + * a ring of a polygon. The ring may be either an outer shell or a hole. + */ +class GEOS_DLL EdgeRing { +private: + const geom::GeometryFactory* factory; + + typedef std::vector DeList; + DeList deList; + + // cache the following data for efficiency + std::unique_ptr ring; + std::unique_ptr ringPts; + std::unique_ptr ringLocator; + + std::unique_ptr>> holes; + + EdgeRing* shell = nullptr; + bool is_hole; + bool is_valid = false; + bool is_processed = false; + bool is_included_set = false; + bool is_included = false; + bool visitedByUpdateIncludedRecursive = false; + + /** \brief + * Computes the list of coordinates which are contained in this ring. + * The coordinates are computed once only and cached. + * + * @return an array of the Coordinate in this ring + */ + const geom::CoordinateSequence* getCoordinates(); + + static void addEdge(const geom::CoordinateSequence* coords, + bool isForward, + geom::CoordinateSequence* coordList); + + algorithm::locate::PointOnGeometryLocator* getLocator() { + if (ringLocator == nullptr) { + ringLocator.reset(new algorithm::locate::IndexedPointInAreaLocator(*getRingInternal())); + } + return ringLocator.get(); + } + +public: + /** \brief + * Adds a DirectedEdge which is known to form part of this ring. + * + * @param de the DirectedEdge to add. Ownership to the caller. + */ + void add(const PolygonizeDirectedEdge* de); + + /** + * \brief + * Find the innermost enclosing shell EdgeRing + * containing this, if any. + * + * The innermost enclosing ring is the smallest enclosing ring. + * The algorithm used depends on the fact that: + * + * ring A contains ring B iff envelope(ring A) contains envelope(ring B) + * + * This routine is only safe to use if the chosen point of the hole + * is known to be properly contained in a shell + * (which is guaranteed to be the case if the hole does not touch + * its shell) + * + * @return containing EdgeRing, if there is one + * @return null if no containing EdgeRing is found + */ + EdgeRing* findEdgeRingContaining(const std::vector & erList); + + /** + * \brief + * Traverses a ring of DirectedEdges, accumulating them into a list. + * + * This assumes that all dangling directed edges have been removed from + * the graph, so that there is always a next dirEdge. + * + * @param startDE the DirectedEdge to start traversing at + * @return a vector of DirectedEdges that form a ring + */ + static std::vector findDirEdgesInRing(PolygonizeDirectedEdge* startDE); + + /** + * \brief + * Finds a point in a list of points which is not contained in + * another list of points. + * + * @param testPts the CoordinateSequence to test + * @param pts the CoordinateSequence to test the input points against + * @return a Coordinate reference from testPts which is + * not in pts, or Coordinate::nullCoord + */ + static const geom::Coordinate& ptNotInList( + const geom::CoordinateSequence* testPts, + const geom::CoordinateSequence* pts); + + /** \brief + * Tests whether a given point is in an array of points. + * Uses a value-based test. + * + * @param pt a Coordinate for the test point + * @param pts an array of Coordinate to test + * @return true if the point is in the array + */ + static bool isInList(const geom::Coordinate& pt, + const geom::CoordinateSequence* pts); + + explicit EdgeRing(const geom::GeometryFactory* newFactory); + + ~EdgeRing() = default; + + void build(PolygonizeDirectedEdge* startDE); + + void computeHole(); + + const DeList& getEdges() const { + return deList; + } + + /** \brief + * Tests whether this ring is a hole. + * + * Due to the way the edges in the polyongization graph are linked, + * a ring is a hole if it is oriented counter-clockwise. + * @return true if this ring is a hole + */ + bool isHole() const { + return is_hole; + } + + /* Indicates whether we know if the ring should be included in a polygonizer + * output of only polygons. + */ + bool isIncludedSet() const { + return is_included_set; + } + + /* Indicates whether the ring should be included in a polygonizer output of + * only polygons. + */ + bool isIncluded() const { + return is_included; + } + + void setIncluded(bool included) { + is_included = included; + is_included_set = true; + } + + bool isProcessed() const { + return is_processed; + } + + void setProcessed(bool processed) { + is_processed = processed; + } + + /** \brief + * Sets the containing shell ring of a ring that has been determined to be a hole. + * + * @param shellRing the shell ring + */ + void setShell(EdgeRing* shellRing) { + shell = shellRing; + } + + /** \brief + * Tests whether this ring has a shell assigned to it. + * + * @return true if the ring has a shell + */ + bool hasShell() const { + return shell != nullptr; + } + + /** \brief + * Gets the shell for this ring. The shell is the ring itself if it is + * not a hole, otherwise it is the parent shell. + * + * @return the shell for the ring + */ + EdgeRing* getShell() { + return isHole() ? shell : this; + } + + /** \brief + * Tests whether this ring is an outer hole. + * A hole is an outer hole if it is not contained by any shell. + * + * @return true if the ring is an outer hole. + */ + bool isOuterHole() const { + if (!isHole()) { + return false; + } + + return !hasShell(); + } + + /** \brief + * Tests whether this ring is an outer shell. + * + * @return true if the ring is an outer shell. + */ + bool isOuterShell() const { + return getOuterHole() != nullptr; + } + + /** \brief + * Gets the outer hole of a shell, if it has one. + * An outer hole is one that is not contained in any other shell. + * + * Each disjoint connected group of shells is surrounded by + * an outer hole. + * + * @return the outer hole edge ring, or nullptr + */ + EdgeRing* getOuterHole() const; + + /** \brief + * Updates the included status for currently non-included shells + * based on whether they are adjacent to an included shell. + */ + void updateIncludedRecursive(); + + /** \brief + * Adds a hole to the polygon formed by this ring. + * + * @param hole the LinearRing forming the hole. + */ + void addHole(geom::LinearRing* hole); + + void addHole(EdgeRing* holeER); + + /** \brief + * Computes the Polygon formed by this ring and any contained holes. + * + * LinearRings ownership is transferred to returned polygon. + * Subsequent calls to the function will return NULL. + * + * @return the Polygon formed by this ring and its holes. + */ + std::unique_ptr getPolygon(); + + /** \brief + * Tests if the LinearRing ring formed by this edge ring + * is topologically valid. + */ + bool isValid() const; + + void computeValid(); + + /** \brief + * Gets the coordinates for this ring as a LineString. + * + * Used to return the coordinates in this ring + * as a valid geometry, when it has been detected that the ring + * is topologically invalid. + * @return a LineString containing the coordinates in this ring + */ + std::unique_ptr getLineString(); + + /** \brief + * Returns this ring as a LinearRing, or null if an Exception + * occurs while creating it (such as a topology problem). + * + * Ownership of ring is retained by the object. + * Details of problems are written to standard output. + */ + geom::LinearRing* getRingInternal(); + + /** \brief + * Returns this ring as a LinearRing, or null if an Exception + * occurs while creating it (such as a topology problem). + * + * Details of problems are written to standard output. + * Caller gets ownership of ring. + */ + std::unique_ptr getRingOwnership(); + + bool isInRing(const geom::Coordinate & pt) { + return geom::Location::EXTERIOR != getLocator()->locate(&pt); + } +}; + +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/polygonize/HoleAssigner.h b/Sources/geos/include/geos/operation/polygonize/HoleAssigner.h new file mode 100644 index 0000000..c028bfd --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/HoleAssigner.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/HoleAssigner.java 0b3c7e3eb0d3e + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace operation { +namespace polygonize { + +/** \brief + * Assigns hole rings to shell rings during polygonization. + * + * Uses spatial indexing to improve performance of shell lookup. + * + * @author mdavis + */ +class GEOS_DLL HoleAssigner { +public: + /** + * Assigns hole rings to shell rings + * @param holes list of hole rings to assign + * @param shells list of shell rings + */ + static void assignHolesToShells(std::vector & holes, std::vector & shells); + +private: + explicit HoleAssigner(std::vector & shells) : m_shells(shells) { + buildIndex(); + } + + void assignHolesToShells(std::vector & holes); + void assignHoleToShell(EdgeRing* holeER); + std::vector findShells(const geom::Envelope & ringEnv); + + EdgeRing* findEdgeRingContaining(EdgeRing* testER); + + void buildIndex(); + + std::vector& m_shells; + geos::index::strtree::TemplateSTRtree m_shellIndex; +}; +} +} +} + diff --git a/Sources/geos/include/geos/operation/polygonize/PolygonizeDirectedEdge.h b/Sources/geos/include/geos/operation/polygonize/PolygonizeDirectedEdge.h new file mode 100644 index 0000000..61b9bff --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/PolygonizeDirectedEdge.h @@ -0,0 +1,127 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/PolygonizeDirectedEdge.java rev. 1.4 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +//class LineString; +} +namespace planargraph { +class Node; +} +namespace operation { +namespace polygonize { +class EdgeRing; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + +/** \brief + * A DirectedEdge of a PolygonizeGraph, which represents + * an edge of a polygon formed by the graph. + * + * May be logically deleted from the graph by setting the + * marked flag. + */ +class GEOS_DLL PolygonizeDirectedEdge: public planargraph::DirectedEdge { + +private: + + EdgeRing* edgeRing; + + PolygonizeDirectedEdge* next; + + long label; + +public: + + /* + * \brief + * Constructs a directed edge connecting the from node + * to the to node. + * + * @param directionPt + * specifies this DirectedEdge's direction (given by an imaginary + * line from the from node to directionPt) + * + * @param edgeDirection + * whether this DirectedEdge's direction is the same as or + * opposite to that of the parent Edge (if any) + */ + PolygonizeDirectedEdge(planargraph::Node* newFrom, + planargraph::Node* newTo, + const geom::Coordinate& newDirectionPt, + bool nEdgeDirection); + + /* + * Returns the identifier attached to this directed edge. + */ + long getLabel() const; + + /* + * Attaches an identifier to this directed edge. + */ + void setLabel(long newLabel); + + /* + * Returns the next directed edge in the EdgeRing that this + * directed edge is a member of. + */ + PolygonizeDirectedEdge* getNext() const; + + /* + * Gets the EdgeRing this edge is a member of. + */ + EdgeRing* getRing() const { + return edgeRing; + } + + /* + * Sets the next directed edge in the EdgeRing that this + * directed edge is a member of. + */ + void setNext(PolygonizeDirectedEdge* newNext); + + /* + * Returns the ring of directed edges that this directed edge is + * a member of, or null if the ring has not been set. + * @see #setRing(EdgeRing) + */ + bool isInRing() const; + + /* + * Sets the ring of directed edges that this directed edge is + * a member of. + */ + void setRing(EdgeRing* newEdgeRing); +}; +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/polygonize/PolygonizeEdge.h b/Sources/geos/include/geos/operation/polygonize/PolygonizeEdge.h new file mode 100644 index 0000000..718d709 --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/PolygonizeEdge.h @@ -0,0 +1,58 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/PolygonizeEdge.java rev. 1.3 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + +/** \brief + * An edge of a polygonization graph. + * + * @version 1.4 + */ +class GEOS_DLL PolygonizeEdge: public planargraph::Edge { +private: + // Externally owned + const geom::LineString* line; +public: + + // Keep the given pointer (won't do anything to it) + PolygonizeEdge(const geom::LineString* newLine); + + // Just return what it was given initially + const geom::LineString* getLine(); +}; + +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/polygonize/PolygonizeGraph.h b/Sources/geos/include/geos/operation/polygonize/PolygonizeGraph.h new file mode 100644 index 0000000..694f8fe --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/PolygonizeGraph.h @@ -0,0 +1,214 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/PolygonizeGraph.java rev. 974 + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class GeometryFactory; +class Coordinate; +class CoordinateSequence; +} +namespace planargraph { +class Node; +class Edge; +class DirectedEdge; +} +namespace operation { +namespace polygonize { +class EdgeRing; +class PolygonizeDirectedEdge; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + + +/** \brief + * Represents a planar graph of edges that can be used to compute a + * polygonization, and implements the algorithms to compute the + * EdgeRings formed by the graph. + * + * The marked flag on DirectedEdge is used to indicate that a directed edge + * has be logically deleted from the graph. + * + */ +class GEOS_DLL PolygonizeGraph: public planargraph::PlanarGraph { + +public: + + /** + * \brief + * Deletes all edges at a node + */ + static void deleteAllEdges(planargraph::Node* node); + + /** + * \brief + * Create a new polygonization graph. + */ + explicit PolygonizeGraph(const geom::GeometryFactory* newFactory); + + /** + * \brief + * Destroy a polygonization graph. + */ + ~PolygonizeGraph() override; + + /** + * \brief + * Add a LineString forming an edge of the polygon graph. + * @param line the line to add + */ + void addEdge(const geom::LineString* line); + + /** + * \brief + * Computes the EdgeRings formed by the edges in this graph. + * + * @param edgeRingList : the EdgeRing found by the + * polygonization process will be pushed here. + * + */ + void getEdgeRings(std::vector& edgeRingList); + + /** + * \brief + * Finds and removes all cut edges from the graph. + * + * @param cutLines : the list of the LineString forming the removed + * cut edges will be pushed here. + * + * TODO: document ownership of the returned LineStrings + */ + void deleteCutEdges(std::vector& cutLines); + + /** \brief + * Marks all edges from the graph which are "dangles". + * + * Dangles are which are incident on a node with degree 1. + * This process is recursive, since removing a dangling edge + * may result in another edge becoming a dangle. + * In order to handle large recursion depths efficiently, + * an explicit recursion stack is used + * + * @param dangleLines : the LineStrings that formed dangles will + * be push_back'ed here + */ + void deleteDangles(std::vector& dangleLines); + +private: + + static int getDegreeNonDeleted(planargraph::Node* node); + + static int getDegree(planargraph::Node* node, long label); + + const geom::GeometryFactory* factory; + + planargraph::Node* getNode(const geom::Coordinate& pt); + + void computeNextCWEdges(); + + /** + * \brief + * Convert the maximal edge rings found by the initial graph traversal + * into the minimal edge rings required by JTS polygon topology rules. + * + * @param ringEdges + * the list of start edges for the edgeRings to convert. + * + */ + void convertMaximalToMinimalEdgeRings( + std::vector& ringEdges); + + /** + * \brief + * Finds all nodes in a maximal edgering + * which are self-intersection nodes + * + * @param startDE + * @param label + * @param intNodes : intersection nodes found will be pushed here + * the vector won't be cleared before pushing. + */ + static void findIntersectionNodes(PolygonizeDirectedEdge* startDE, + long label, std::vector& intNodes + ); + + /** + * Finds and labels all edgerings in the graph. + * + * The edge rings are labeling with unique integers. + * The labeling allows detecting cut edges. + * + * @param dirEdgesIn a list of the DirectedEdges in the graph + * @param dirEdgesOut each ring found will be pushed here + */ + static void findLabeledEdgeRings( + std::vector& dirEdgesIn, + std::vector& dirEdgesOut); + + static void label(std::vector& dirEdges, long label); + static void label(std::vector& dirEdges, long label); + + static void computeNextCWEdges(planargraph::Node* node); + + /** + * \brief + * Computes the next edge pointers going CCW around the given node, + * for the given edgering label. + * This algorithm has the effect of converting maximal edgerings + * into minimal edgerings + */ + static void computeNextCCWEdges(planargraph::Node* node, long label); + + EdgeRing* findEdgeRing(PolygonizeDirectedEdge* startDE); + + /* These are for memory management */ + std::vector newEdges; + std::vector newDirEdges; + std::vector newNodes; + std::vector newEdgeRings; + std::vector newCoords; +}; + +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/polygonize/Polygonizer.h b/Sources/geos/include/geos/operation/polygonize/Polygonizer.h new file mode 100644 index 0000000..1c21a3a --- /dev/null +++ b/Sources/geos/include/geos/operation/polygonize/Polygonizer.h @@ -0,0 +1,260 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2010 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/polygonize/Polygonizer.java 0b3c7e3eb0d3e + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // for LineStringAdder inheritance +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; + +class LineString; + +class Polygon; +} +namespace operation { +namespace polygonize { +class EdgeRing; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace polygonize { // geos::operation::polygonize + +/** \brief + * Polygonizes a set of Geometrys which contain linework that + * represents the edges of a planar graph. + * + * All types of Geometry are accepted as input; the constituent linework is extracted + * as the edges to be polygonized. + * The edges must be correctly noded; that is, they must only meet + * at their endpoints. Polygonization will accept incorrectly noded input but will + * not form polygons from non-noded edges, and reports them as errors. + * + * The Polygonizer reports the follow kinds of errors: + * + * - Dangles - edges which have one or both ends which are + * not incident on another edge endpoint + * - Cut Edges - edges which are connected at both ends but + * which do not form part of a polygon + * - Invalid Ring Lines - edges which form rings which are invalid + * (e.g. the component lines contain a self-intersection) + * + * The Polygonizer constructor allows extracting only polygons which form a + * valid polygonal result. + * The set of extracted polygons is guaranteed to be edge-disjoint. + * This is useful when it is known that the input lines form a valid + * polygonal geometry (which may include holes or nested polygons). + * + */ +class GEOS_DLL Polygonizer { +private: + /** + * Add every linear element in a geometry into the polygonizer graph. + */ + class GEOS_DLL LineStringAdder: public geom::GeometryComponentFilter { + public: + Polygonizer* pol; + explicit LineStringAdder(Polygonizer* p); + //void filter_rw(geom::Geometry *g); + void filter_ro(const geom::Geometry* g) override; + }; + + // default factory + LineStringAdder lineStringAdder; + + /** + * Add a linestring to the graph of polygon edges. + * + * @param line the {@link LineString} to add + */ + void add(const geom::LineString* line); + + /** + * Perform the polygonization, if it has not already been carried out. + */ + void polygonize(); + + static void findValidRings(const std::vector& edgeRingList, + std::vector& validEdgeRingList, + std::vector& invalidRingList); + + /** + * Extracts unique lines for invalid rings, + * discarding rings which correspond to outer rings and hence contain + * duplicate linework. + */ + std::vector> extractInvalidLines( + std::vector& invalidRings); + + /** + * Tests if a invalid ring should be included in + * the list of reported invalid rings. + * + * Rings are included only if they contain + * linework which is not already in a valid ring, + * or in an already-included ring. + * + * Because the invalid rings list is sorted by extent area, + * this results in outer rings being discarded, + * since all their linework is reported in the rings they contain. + * + * @param invalidRing the ring to test + * @return true if the ring should be included + */ + bool isIncludedInvalid(EdgeRing* invalidRing); + + void findShellsAndHoles(const std::vector& edgeRingList); + + void findDisjointShells(); + + static void findOuterShells(std::vector& shellList); + + static std::vector> extractPolygons(std::vector & shellList, bool includeAll); + + bool extractOnlyPolygonal; + bool computed; + +protected: + + std::unique_ptr graph; + + // initialize with empty collections, in case nothing is computed + std::vector dangles; + std::vector cutEdges; + std::vector> invalidRingLines; + + std::vector holeList; + std::vector shellList; + std::vector> polyList; + +public: + + /** \brief + * Create a Polygonizer with the same GeometryFactory + * as the input Geometrys. + * + * @param onlyPolygonal true if only polygons which form a valid polygonal geometry should be extracted + */ + explicit Polygonizer(bool onlyPolygonal = false); + + ~Polygonizer() = default; + + /** \brief + * Add a collection of geometries to be polygonized. + * May be called multiple times. + * Any dimension of Geometry may be added; + * the constituent linework will be extracted and used + * + * @param geomList a list of Geometry with linework to be polygonized + */ + void add(std::vector* geomList); + + /** \brief + * Add a collection of geometries to be polygonized. + * May be called multiple times. + * Any dimension of Geometry may be added; + * the constituent linework will be extracted and used + * + * @param geomList a list of Geometry with linework to be polygonized + */ + void add(std::vector* geomList); + + /** + * Add a geometry to the linework to be polygonized. + * May be called multiple times. + * Any dimension of Geometry may be added; + * the constituent linework will be extracted and used + * + * @param g a Geometry with linework to be polygonized + */ + void add(const geom::Geometry* g); + + /** \brief + * Gets the list of polygons formed by the polygonization. + * + * Ownership of vector is transferred to caller, subsequent + * calls will return NULL. + * @return a collection of Polygons + */ + std::vector> getPolygons(); + + /** \brief + * Get the list of dangling lines found during polygonization. + * + * @return a (possibly empty) collection of pointers to + * the input LineStrings which are dangles. + * + */ + const std::vector& getDangles(); + + bool hasDangles(); + + /** \brief + * Get the list of cut edges found during polygonization. + * + * @return a (possibly empty) collection of pointers to + * the input LineStrings which are cut edges. + * + */ + const std::vector& getCutEdges(); + + bool hasCutEdges(); + + /** \brief + * Get the list of lines forming invalid rings found during + * polygonization. + * + * @return a (possibly empty) collection of pointers to + * the input LineStrings which form invalid rings + * + */ + const std::vector>& getInvalidRingLines(); + + bool hasInvalidRingLines(); + + bool allInputsFormPolygons(); + +// This seems to be needed by GCC 2.95.4 + friend class Polygonizer::LineStringAdder; +}; + +} // namespace geos::operation::polygonize +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/predicate/RectangleContains.h b/Sources/geos/include/geos/operation/predicate/RectangleContains.h new file mode 100644 index 0000000..2051d7b --- /dev/null +++ b/Sources/geos/include/geos/operation/predicate/RectangleContains.h @@ -0,0 +1,119 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/predicate/RectangleContains.java rev 1.5 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inlines + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class Geometry; +class Point; +class Coordinate; +class LineString; +//class Polygon; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace predicate { // geos::operation::predicate + +/** \brief + * Optimized implementation of spatial predicate "contains" + * for cases where the first Geometry is a rectangle. + * + * As a further optimization, + * this class can be used directly to test many geometries against a single + * rectangle. + * + */ +class GEOS_DLL RectangleContains { + +private: + + const geom::Envelope& rectEnv; + + bool isContainedInBoundary(const geom::Geometry& geom); + + bool isPointContainedInBoundary(const geom::Point& geom); + + /** \brief + * Tests if a point is contained in the boundary of the target + * rectangle. + * + * @param pt the point to test + * @return true if the point is contained in the boundary + */ + bool isPointContainedInBoundary(const geom::CoordinateXY& pt); + + /** \brief + * Tests if a linestring is completely contained in the boundary + * of the target rectangle. + * + * @param line the linestring to test + * @return true if the linestring is contained in the boundary + */ + bool isLineStringContainedInBoundary(const geom::LineString& line); + + /** \brief + * Tests if a line segment is contained in the boundary of the + * target rectangle. + * + * @param p0 an endpoint of the segment + * @param p1 an endpoint of the segment + * @return true if the line segment is contained in the boundary + */ + bool isLineSegmentContainedInBoundary(const geom::Coordinate& p0, + const geom::Coordinate& p1); + +public: + + static bool + contains(const geom::Polygon& rect, const geom::Geometry& b) + { + RectangleContains rc(rect); + return rc.contains(b); + } + + /** + * Create a new contains computer for two geometries. + * + * @param rect a rectangular geometry + */ + RectangleContains(const geom::Polygon& rect) + : + rectEnv(*(rect.getEnvelopeInternal())) + {} + + bool contains(const geom::Geometry& geom); + + // Declare type as noncopyable + RectangleContains(const RectangleContains& other) = delete; + RectangleContains& operator=(const RectangleContains& rhs) = delete; +}; + + + +} // namespace geos::operation::predicate +} // namespace geos::operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/predicate/RectangleIntersects.h b/Sources/geos/include/geos/operation/predicate/RectangleIntersects.h new file mode 100644 index 0000000..912ceeb --- /dev/null +++ b/Sources/geos/include/geos/operation/predicate/RectangleIntersects.h @@ -0,0 +1,98 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/predicate/RectangleIntersects.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inlines + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +//class Polygon; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace predicate { // geos::operation::predicate + +/** \brief + * Optimized implementation of the "intersects" spatial predicate + * for cases where one Geometry is a rectangle. + * + * This class works for all input geometries, including + * [GeometryCollections](@ref geom::GeometryCollection). + * + * As a further optimization, + * this class can be used to test + * many geometries against a single + * rectangle in a slightly more efficient way. + * + */ +class GEOS_DLL RectangleIntersects { + +private: + + const geom::Polygon& rectangle; + + const geom::Envelope& rectEnv; + + // Declare type as noncopyable + RectangleIntersects(const RectangleIntersects& other) = delete; + RectangleIntersects& operator=(const RectangleIntersects& rhs) = delete; + +public: + + /** \brief + * Create a new intersects computer for a rectangle. + * + * @param newRect a rectangular geometry + */ + RectangleIntersects(const geom::Polygon& newRect) + : + rectangle(newRect), + rectEnv(*(newRect.getEnvelopeInternal())) + {} + + bool intersects(const geom::Geometry& geom); + + /** \brief + * Tests whether a rectangle intersects a given geometry. + * + * @param rectangle a rectangular Polygon + * @param b a Geometry of any type + * @return true if the geometries intersect + */ + static bool + intersects(const geom::Polygon& rectangle, + const geom::Geometry& b) + { + RectangleIntersects rp(rectangle); + return rp.intersects(b); + } + +}; + + +} // namespace geos::operation::predicate +} // namespace geos::operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/predicate/SegmentIntersectionTester.h b/Sources/geos/include/geos/operation/predicate/SegmentIntersectionTester.h new file mode 100644 index 0000000..79f8c31 --- /dev/null +++ b/Sources/geos/include/geos/operation/predicate/SegmentIntersectionTester.h @@ -0,0 +1,91 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/predicate/SegmentIntersectionTester.java r378 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class CoordinateSequence; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace predicate { // geos::operation::predicate + +/** \brief + * Tests if any line segments in two sets of CoordinateSequences intersect. + * + * The algorithm is optimized for use when the first input has smaller extent + * than the set of test lines. + * The code is short-circuited to return as soon an intersection is found. + * + */ +class GEOS_DLL SegmentIntersectionTester { + +private: + + /// \brief + /// For purposes of intersection testing, + /// don't need to set precision model + /// + algorithm::LineIntersector li; // Robust + + bool hasIntersectionVar; + +public: + + SegmentIntersectionTester(): hasIntersectionVar(false) {} + + bool hasIntersectionWithLineStrings(const geom::LineString& line, + const std::vector& lines); + + bool hasIntersection(const geom::LineString& line, + const geom::LineString& testLine); + + /** + * Tests the segments of a LineString against the segs in + * another LineString for intersection. + * Uses the envelope of the query LineString + * to filter before testing segments directly. + * This is optimized for the case when the query + * LineString is a rectangle. + * + * Testing shows this is somewhat faster than not checking the envelope. + * + * @param line + * @param testLine + * @return + */ + bool hasIntersectionWithEnvelopeFilter(const geom::LineString& line, + const geom::LineString& testLine); + + +}; + +} // namespace geos::operation::predicate +} // namespace geos::operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relate/EdgeEndBuilder.h b/Sources/geos/include/geos/operation/relate/EdgeEndBuilder.h new file mode 100644 index 0000000..a6c4143 --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/EdgeEndBuilder.h @@ -0,0 +1,70 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/EdgeEndBuilder.java rev. 1.12 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +class Coordinate; +} +namespace geomgraph { +class Edge; +class EdgeIntersection; +class EdgeEnd; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Computes the geomgraph::EdgeEnd objects which arise + * from a noded geomgraph::Edge. + */ +class GEOS_DLL EdgeEndBuilder { +public: + EdgeEndBuilder() {} + + std::vector> computeEdgeEnds(std::vector* edges); + void computeEdgeEnds(geomgraph::Edge* edge, std::vector>& l); + +protected: + + void createEdgeEndForPrev(geomgraph::Edge* edge, + std::vector>& l, + const geomgraph::EdgeIntersection* eiCurr, + const geomgraph::EdgeIntersection* eiPrev); + + void createEdgeEndForNext(geomgraph::Edge* edge, + std::vector>& l, + const geomgraph::EdgeIntersection* eiCurr, + const geomgraph::EdgeIntersection* eiNext); +}; + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relate/EdgeEndBundle.h b/Sources/geos/include/geos/operation/relate/EdgeEndBundle.h new file mode 100644 index 0000000..291d49e --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/EdgeEndBundle.h @@ -0,0 +1,104 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/EdgeEndBundle.java rev. 1.17 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for EdgeEndBundle inheritance + +#include + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geom { +class IntersectionMatrix; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * A collection of geomgraph::EdgeEnd objects which + * originate at the same point and have the same direction. + */ +class GEOS_DLL EdgeEndBundle: public geomgraph::EdgeEnd { +public: + EdgeEndBundle(geomgraph::EdgeEnd* e); + ~EdgeEndBundle() override; + const std::vector& getEdgeEnds(); + void insert(geomgraph::EdgeEnd* e); + + void computeLabel(const algorithm::BoundaryNodeRule& bnr) override; + + /** + * \brief + * Update the IM with the contribution for the computed label for + * the EdgeStubs. + */ + void updateIM(geom::IntersectionMatrix& im); + + std::string print() const override; +protected: + std::vector edgeEnds; + + /** + * Compute the overall ON location for the list of EdgeStubs. + * + * (This is essentially equivalent to computing the self-overlay of + * a single Geometry) + * + * edgeStubs can be either on the boundary (eg Polygon edge) + * OR in the interior (e.g. segment of a LineString) + * of their parent Geometry. + * + * In addition, GeometryCollections use a algorithm::BoundaryNodeRule + * to determine whether a segment is on the boundary or not. + * + * Finally, in GeometryCollections it can occur that an edge + * is both + * on the boundary and in the interior (e.g. a LineString segment + * lying on + * top of a Polygon edge.) In this case the Boundary is + * given precedence. + * + * These observations result in the following rules for computing + * the ON location: + * - if there are an odd number of Bdy edges, the attribute is Bdy + * - if there are an even number >= 2 of Bdy edges, the attribute + * is Int + * - if there are any Int edges, the attribute is Int + * - otherwise, the attribute is NULL. + * + */ + void computeLabelOn(uint8_t geomIndex, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + void computeLabelSides(uint8_t geomIndex); + void computeLabelSide(uint8_t geomIndex, uint32_t side); +}; + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relate/EdgeEndBundleStar.h b/Sources/geos/include/geos/operation/relate/EdgeEndBundleStar.h new file mode 100644 index 0000000..f68d98e --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/EdgeEndBundleStar.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/EdgeEndBundleStar.java rev. 1.13 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for EdgeEndBundleStar inheritance + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +} +namespace geomgraph { +class EdgeEnd; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * An ordered list of EdgeEndBundle objects around a RelateNode. + * + * They are maintained in CCW order (starting with the positive x-axis) + * around the node + * for efficient lookup and topology building. + */ +class GEOS_DLL EdgeEndBundleStar: public geomgraph::EdgeEndStar { +public: + + /// Creates a new empty EdgeEndBundleStar + EdgeEndBundleStar() {} + + ~EdgeEndBundleStar() override; + void insert(geomgraph::EdgeEnd* e) override; + + /** + * Update the IM with the contribution for the EdgeStubs around the node. + */ + void updateIM(geom::IntersectionMatrix& im); +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relate/RelateComputer.h b/Sources/geos/include/geos/operation/relate/RelateComputer.h new file mode 100644 index 0000000..6a9d928 --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/RelateComputer.h @@ -0,0 +1,185 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/RelateComputer.java rev. 1.24 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for RelateComputer composition +#include // for RelateComputer composition +#include // for RelateComputer composition +#include // for RelateComputer composition +#include + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geom { +class Geometry; +} +namespace geomgraph { +class GeometryGraph; +class Edge; +class EdgeEnd; +class Node; +namespace index { +class SegmentIntersector; +} +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Computes the topological relationship between two Geometries. + * + * RelateComputer does not need to build a complete graph structure to compute + * the IntersectionMatrix. The relationship between the geometries can + * be computed by simply examining the labelling of edges incident on each node. + * + * RelateComputer does not currently support arbitrary GeometryCollections. + * This is because GeometryCollections can contain overlapping Polygons. + * In order to correct compute relate on overlapping Polygons, they + * would first need to be noded and merged (if not explicitly, at least + * implicitly). + * + */ +class GEOS_DLL RelateComputer { +public: + RelateComputer(std::vector>& newArg); + ~RelateComputer() = default; + + std::unique_ptr computeIM(); +private: + + algorithm::LineIntersector li; + + algorithm::PointLocator ptLocator; + + /// the arg(s) of the operation + const std::vector>& arg; + + geomgraph::NodeMap nodes; + + /// this intersection matrix will hold the results compute for the relate + std::unique_ptr im; + + std::vector isolatedEdges; + + /// the intersection point found (if any) + geom::Coordinate invalidPoint; + + void insertEdgeEnds(std::vector>& ee); + + void computeProperIntersectionIM( + geomgraph::index::SegmentIntersector* intersector, + geom::IntersectionMatrix* imX); + + void copyNodesAndLabels(uint8_t argIndex); + void computeIntersectionNodes(uint8_t argIndex); + void labelIntersectionNodes(uint8_t argIndex); + + /** + * If the Geometries are disjoint, we need to enter their dimension and + * boundary dimension in the Ext rows in the IM + */ + void computeDisjointIM(geom::IntersectionMatrix* imX, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + void labelNodeEdges(); + + /** + * update the IM with the sum of the IMs for each component + */ + void updateIM(geom::IntersectionMatrix& imX); + + /** + * Compute the IM entry for the intersection of the boundary + * of a geometry with the Exterior. + * This is the nominal dimension of the boundary + * unless the boundary is empty, in which case it is {@link Dimension#FALSE}. + * For linear geometries the Boundary Node Rule determines + * whether the boundary is empty. + * + * @param geom the geometry providing the boundary + * @param boundaryNodeRule the Boundary Node Rule to use + * @return the IM dimension entry + */ + static int getBoundaryDim(const geom::Geometry& geom, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + /** + * Processes isolated edges by computing their labelling and adding them + * to the isolated edges list. + * Isolated edges are guaranteed not to touch the boundary of the target + * (since if they + * did, they would have caused an intersection to be computed and hence would + * not be isolated) + */ + void labelIsolatedEdges(uint8_t thisIndex, uint8_t targetIndex); + + /** + * Label an isolated edge of a graph with its relationship to the target + * geometry. + * If the target has dim 2 or 1, the edge can either be in the interior + * or the exterior. + * If the target has dim 0, the edge must be in the exterior + */ + void labelIsolatedEdge(geomgraph::Edge* e, uint8_t targetIndex, + const geom::Geometry* target); + + /** + * Isolated nodes are nodes whose labels are incomplete + * (e.g. the location for one Geometry is null). + * This is the case because nodes in one graph which don't intersect + * nodes in the other are not completely labelled by the initial process + * of adding nodes to the nodeList. + * To complete the labelling we need to check for nodes that lie in the + * interior of edges, and in the interior of areas. + */ + void labelIsolatedNodes(); + + /** + * Label an isolated node with its relationship to the target geometry. + */ + void labelIsolatedNode(geomgraph::Node* n, uint8_t targetIndex); +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/relate/RelateNode.h b/Sources/geos/include/geos/operation/relate/RelateNode.h new file mode 100644 index 0000000..047533d --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/RelateNode.h @@ -0,0 +1,67 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/RelateNode.java rev. 1.11 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +// Forward declarations +namespace geos { +namespace geom { +class IntersectionMatrix; +class Coordinate; +} +namespace geomgraph { +class EdgeEndStar; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Represents a node in the topological graph used to compute spatial + * relationships. + */ +class GEOS_DLL RelateNode final: public geomgraph::Node { + +public: + + RelateNode(const geom::Coordinate& coord, geomgraph::EdgeEndStar* edges); + + ~RelateNode() override = default; + + /** + * Update the IM with the contribution for the EdgeEnds incident on this node. + */ + void updateIMFromEdges(geom::IntersectionMatrix& im); + +protected: + + void computeIM(geom::IntersectionMatrix& im) override; +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relate/RelateNodeFactory.h b/Sources/geos/include/geos/operation/relate/RelateNodeFactory.h new file mode 100644 index 0000000..ac2a02e --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/RelateNodeFactory.h @@ -0,0 +1,55 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/RelateNodeFactory.java rev. 1.11 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for RelateNodeFactory inheritance + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace geomgraph { +class Node; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Used by the geomgraph::NodeMap in a RelateNodeGraph to create RelateNode objects. + */ +class GEOS_DLL RelateNodeFactory: public geomgraph::NodeFactory { +public: + geomgraph::Node* createNode(const geom::Coordinate& coord) const override; + static const geomgraph::NodeFactory& instance(); +private: + RelateNodeFactory() {} +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relate/RelateNodeGraph.h b/Sources/geos/include/geos/operation/relate/RelateNodeGraph.h new file mode 100644 index 0000000..5a13cc7 --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/RelateNodeGraph.h @@ -0,0 +1,98 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/RelateNodeGraph.java rev. 1.11 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +struct CoordinateLessThan; +} +namespace geomgraph { +//class EdgeEndStar; +class Node; +class GeometryGraph; +class EdgeEnd; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Implements the simple graph of Nodes and geomgraph::EdgeEnd which is all that is + * required to determine topological relationships between Geometries. + * + * Also supports building a topological graph of a single Geometry, to + * allow verification of valid topology. + * + * It is not necessary to create a fully linked + * PlanarGraph to determine relationships, since it is sufficient + * to know how the Geometries interact locally around the nodes. + * In fact, this is not even feasible, since it is not possible to compute + * exact intersection points, and hence the topology around those nodes + * cannot be computed robustly. + * The only Nodes that are created are for improper intersections; + * that is, nodes which occur at existing vertices of the Geometries. + * Proper intersections (e.g. ones which occur between the interior of + * line segments) + * have their topology determined implicitly, without creating a geomgraph::Node object + * to represent them. + * + */ +class GEOS_DLL RelateNodeGraph { + +public: + + RelateNodeGraph(); + + virtual ~RelateNodeGraph(); + + geomgraph::NodeMap::container& getNodeMap(); + + void build(geomgraph::GeometryGraph* geomGraph); + + void computeIntersectionNodes(geomgraph::GeometryGraph* geomGraph, + uint8_t argIndex); + + void copyNodesAndLabels(geomgraph::GeometryGraph* geomGraph, uint8_t argIndex); + + void insertEdgeEnds(std::vector>& ee); + +private: + + geomgraph::NodeMap* nodes; + + RelateNodeGraph(const RelateNodeGraph&) = delete; + RelateNodeGraph& operator=(const RelateNodeGraph&) = delete; + +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relate/RelateOp.h b/Sources/geos/include/geos/operation/relate/RelateOp.h new file mode 100644 index 0000000..c72cafb --- /dev/null +++ b/Sources/geos/include/geos/operation/relate/RelateOp.h @@ -0,0 +1,134 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/relate/RelateOp.java rev. 1.19 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include // for inheritance +#include // for composition + +// Forward declarations +namespace geos { +namespace algorithm { +class BoundaryNodeRule; +} +namespace geom { +class Geometry; +} +} + + +namespace geos { +namespace operation { // geos::operation +namespace relate { // geos::operation::relate + +/** \brief + * Implements the SFS `relate()` operation on two + * geom::Geometry objects. + * + * This class supports specifying a custom algorithm::BoundaryNodeRule + * to be used during the relate computation. + * + * @note Custom Boundary Node Rules do not (currently) + * affect the results of other Geometry methods (such + * as [Geometry::getBoundary()](@ref geom::Geometry::getBoundary() const)). + * The results of these methods may not be consistent with the relationship + * computed by a custom Boundary Node Rule. + * + */ +class GEOS_DLL RelateOp: public GeometryGraphOperation { + +public: + + /** \brief + * Computes the geom::IntersectionMatrix for the spatial relationship + * between two geom::Geometry objects, using the default (OGC SFS) + * Boundary Node Rule + * + * @param a a Geometry to test. Ownership left to caller. + * @param b a Geometry to test. Ownership left to caller. + * + * @return the IntersectionMatrix for the spatial relationship + * between the geometries. Ownership transferred. + */ + static std::unique_ptr relate( + const geom::Geometry* a, + const geom::Geometry* b); + + /** \brief + * Computes the geom::IntersectionMatrix for the spatial relationship + * between two geom::Geometry objects, using a specified + * Boundary Node Rule. + * + * @param a a Geometry to test. Ownership left to caller. + * @param b a Geometry to test. Ownership left to caller. + * @param boundaryNodeRule the Boundary Node Rule to use. + * + * @return the IntersectionMatrix for the spatial relationship + * between the geometries. Ownership transferred. + */ + static std::unique_ptr relate( + const geom::Geometry* a, + const geom::Geometry* b, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + /** \brief + * Creates a new Relate operation, using the default (OGC SFS) + * Boundary Node Rule. + * + * @param g0 a Geometry to relate. Ownership left to caller. + * @param g1 another Geometry to relate. Ownership to caller. + */ + RelateOp(const geom::Geometry* g0, + const geom::Geometry* g1); + + /** \brief + * Creates a new Relate operation with a specified + * Boundary Node Rule. + * + * @param g0 a Geometry to relate. Ownership left to caller. + * @param g1 another Geometry to relate. Ownership to caller. + * @param boundaryNodeRule the Boundary Node Rule to use + */ + RelateOp(const geom::Geometry* g0, + const geom::Geometry* g1, + const algorithm::BoundaryNodeRule& boundaryNodeRule); + + ~RelateOp() override = default; + + /** \brief + * Gets the IntersectionMatrix for the spatial relationship + * between the input geometries. + * + * @return the geom::IntersectionMatrix for the spatial + * relationship between the input geometries. + * Ownership transferred. + */ + std::unique_ptr getIntersectionMatrix(); + +private: + + RelateComputer relateComp; +}; + + +} // namespace geos:operation:relate +} // namespace geos:operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h b/Sources/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h new file mode 100644 index 0000000..3a84a79 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/AdjacentEdgeLocator.h @@ -0,0 +1,120 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSections; + class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; + class LinearRing; + class Polygon; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Determines the location for a point which is known to lie + * on at least one edge of a set of polygons. + * This provides the union-semantics for determining + * point location in a GeometryCollection, which may + * have polygons with adjacent edges which are effectively + * in the interior of the geometry. + * Note that it is also possible to have adjacent edges which + * lie on the boundary of the geometry + * (e.g. a polygon contained within another polygon with adjacent edges). + * + * @author mdavis + * + */ +class GEOS_DLL AdjacentEdgeLocator { + using CoordinateXY = geos::geom::CoordinateXY; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using LinearRing = geos::geom::LinearRing; + using Polygon = geos::geom::Polygon; + using Location = geos::geom::Location; + +public: + + AdjacentEdgeLocator(const Geometry* geom) + { + init(geom); + } + + Location locate(const CoordinateXY* p); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + AdjacentEdgeLocator(const AdjacentEdgeLocator&) = delete; + AdjacentEdgeLocator& operator=(const AdjacentEdgeLocator&) = delete; + + +private: + + // Members + + std::vector ringList; + + /* + * When we have to reorient rings, we end up allocating new + * rings, since we cannot reorient the rings of the input + * geometry, so this is where we store those "local" rings. + */ + std::vector> localRingList; + + + // Methods + + void addSections( + const CoordinateXY* p, + const CoordinateSequence* ring, + NodeSections& sections); + + NodeSection* createSection( + const CoordinateXY* p, + const CoordinateXY* prev, + const CoordinateXY* next); + + void init(const Geometry* geom); + + void addRings(const Geometry* geom); + + void addRing(const LinearRing* ring, bool requireCW); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/BasicPredicate.h b/Sources/geos/include/geos/operation/relateng/BasicPredicate.h new file mode 100644 index 0000000..083973e --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/BasicPredicate.h @@ -0,0 +1,103 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL BasicPredicate : public TopologyPredicate { +protected: + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + +private: + + static constexpr int UNKNOWN = -1; + static constexpr int FALSE = 0; + static constexpr int TRUE = 1; + + int m_value = UNKNOWN; + + static bool isKnown(int val); + + static bool toBoolean(int val); + + static int toValue(bool val); + + +protected: + + /** + * Updates the predicate value to the given state + * if it is currently unknown. + * + * @param val the predicate value to update + */ + void setValue(bool val); + + void setValue(int val); + + void setValueIf(bool val, bool cond); + + void require(bool cond); + + using TopologyPredicate::requireCovers; + void requireCovers(const Envelope& a, const Envelope& b); + + +public: + + /** + * Tests if two geometries intersect + * based on an interaction at given locations. + * + * @param locA the location on geometry A + * @param locB the location on geometry B + * @return true if the geometries intersect + */ + static bool isIntersection(Location locA, Location locB); + + std::string name() const override = 0; + + void finish() override = 0; + + bool isKnown() const override; + + bool value() const override; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/DimensionLocation.h b/Sources/geos/include/geos/operation/relateng/DimensionLocation.h new file mode 100644 index 0000000..22c327a --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/DimensionLocation.h @@ -0,0 +1,57 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL DimensionLocation { + using Location = geos::geom::Location; + +public: + + enum DimensionLocationType { + EXTERIOR = 2, // == Location.EXTERIOR + POINT_INTERIOR = 103, + LINE_INTERIOR = 110, + LINE_BOUNDARY = 111, + AREA_INTERIOR = 120, + AREA_BOUNDARY = 121 + }; + + static int locationArea(Location loc); + + static int locationLine(Location loc); + + static int locationPoint(Location loc); + + static Location location(int dimLoc); + + static int dimension(int dimLoc); + + static int dimension(int dimLoc, int exteriorDim); + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h b/Sources/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h new file mode 100644 index 0000000..2971486 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/EdgeSegmentIntersector.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace noding { + class SegmentString; +} +namespace operation { +namespace relateng { + class RelateSegmentString; + class TopologyComputer; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL EdgeSegmentIntersector : public geos::noding::SegmentIntersector { + using SegmentString = geos::noding::SegmentString; + using LineIntersector = geos::algorithm::LineIntersector; + +private: + + // Members + LineIntersector li; + TopologyComputer& topoComputer; + + // Methods + + + void addIntersections( + RelateSegmentString* ssA, std::size_t segIndexA, + RelateSegmentString* ssB, std::size_t segIndexB); + + +public: + + EdgeSegmentIntersector(TopologyComputer& p_topoComputer) + : topoComputer(p_topoComputer) + {}; + + void processIntersections( + SegmentString* ss0, std::size_t segIndex0, + SegmentString* ss1, std::size_t segIndex1) override; + + bool isDone() const override; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h b/Sources/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h new file mode 100644 index 0000000..3364a4d --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/EdgeSegmentOverlapAction.h @@ -0,0 +1,71 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: index/chain/MonotoneChainOverlapAction.java rev. 1.6 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace index { +namespace chain { + class MonotoneChain; +} +} +namespace noding { + class SegmentIntersector; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace relateng { // geos::operation::relateng + +/** \brief + * The action for the internal iterator for performing + * overlap queries on a MonotoneChain. + */ +class GEOS_DLL EdgeSegmentOverlapAction : public geos::index::chain::MonotoneChainOverlapAction { + using MonotoneChain = geos::index::chain::MonotoneChain; + using SegmentIntersector = geos::noding::SegmentIntersector; + +private: + + SegmentIntersector& si; + + +public: + + EdgeSegmentOverlapAction(SegmentIntersector& p_si) + : si(p_si) + {} + + void overlap( + const MonotoneChain& mc1, std::size_t start1, + const MonotoneChain& mc2, std::size_t start2) override; + + +}; + +} // namespace geos::index::chain +} // namespace geos::index +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/EdgeSetIntersector.h b/Sources/geos/include/geos/operation/relateng/EdgeSetIntersector.h new file mode 100644 index 0000000..302a397 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/EdgeSetIntersector.h @@ -0,0 +1,93 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class Geometry; + class Envelope; +} +namespace noding { + class SegmentString; +} +namespace operation { +namespace relateng { + class RelateSegmentString; + class EdgeSegmentIntersector; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL EdgeSetIntersector { + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + template + using TemplateSTRtree = geos::index::strtree::TemplateSTRtree; + using MonotoneChain = geos::index::chain::MonotoneChain; + using SegmentString = geos::noding::SegmentString; + using EdgeSegmentIntersector = geos::operation::relateng::EdgeSegmentIntersector; + +private: + + // Members + TemplateSTRtree index; + // HPRtree index = new HPRtree(); + const Envelope* envelope = nullptr; + std::deque monoChains; + std::size_t overlapCounter = 0; + + + // Methods + + void addToIndex(const SegmentString* segStr); + + void addEdges(std::vector& segStrings); + + +public: + + EdgeSetIntersector( + std::vector& edgesA, + std::vector& edgesB, + const Envelope* env) + : envelope(env) + { + addEdges(edgesA); + addEdges(edgesB); + // build index to ensure thread-safety + // index.build(); + }; + + void process(EdgeSegmentIntersector& intersector); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/IMPatternMatcher.h b/Sources/geos/include/geos/operation/relateng/IMPatternMatcher.h new file mode 100644 index 0000000..21fc028 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/IMPatternMatcher.h @@ -0,0 +1,83 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IMPatternMatcher : public IMPredicate { + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + using Dimension = geos::geom::Dimension; + using IntersectionMatrix = geos::geom::IntersectionMatrix; + +private: + + std::string imPattern; + IntersectionMatrix patternMatrix; + + static bool requireInteraction(const IntersectionMatrix& im); + + static bool isInteraction(int imDim); + + +public: + + IMPatternMatcher(std::string p_imPattern) + : imPattern(p_imPattern) + , patternMatrix(p_imPattern) + {}; + + std::string name() const override; + + using IMPredicate::init; + void init(const Envelope& envA, const Envelope& envB) override; + + bool requireInteraction() const override; + + bool isDetermined() const override; + + bool valueIM() override; + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const IMPatternMatcher& imp); + +}; + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/IMPredicate.h b/Sources/geos/include/geos/operation/relateng/IMPredicate.h new file mode 100644 index 0000000..afd6543 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/IMPredicate.h @@ -0,0 +1,129 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IMPredicate : public BasicPredicate { +protected: + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + using Dimension = geos::geom::Dimension; + using IntersectionMatrix = geos::geom::IntersectionMatrix; + +private: + + + +protected: + + static constexpr int DIM_UNKNOWN = Dimension::DONTCARE; + + int dimA; + int dimB; + IntersectionMatrix intMatrix; + + /** + * Gets the value of the predicate according to the current + * intersection matrix state. + * + * @return the current predicate value + */ + virtual bool valueIM() = 0; + + /** + * Tests whether predicate evaluation can be short-circuited + * due to the current state of the matrix providing + * enough information to determine the predicate value. + * + * If this value is true then valueIM() + * must provide the correct result of the predicate. + * + * @return true if the predicate value is determined + */ + virtual bool isDetermined() const = 0; + + /** + * Tests whether the exterior of the specified input geometry + * is intersected by any part of the other input. + * + * @param isA the input geometry + * @return true if the input geometry exterior is intersected + */ + bool intersectsExteriorOf(bool isA) const; + + bool isIntersects(Location locA, Location locB) const; + + +public: + + IMPredicate() + { + // intMatrix = new IntersectionMatrix(); + //-- E/E is always dim = 2 + intMatrix.set(Location::EXTERIOR, Location::EXTERIOR, Dimension::A); + } + + static bool isDimsCompatibleWithCovers(int dim0, int dim1); + + void init(int dA, int dB) override; + + void updateDimension(Location locA, Location locB, int dimension) override; + + bool isDimChanged(Location locA, Location locB, int dimension) const; + + using TopologyPredicate::isKnown; + bool isKnown(Location locA, Location locB) const; + + bool isDimension(Location locA, Location locB, int dimension) const; + + int getDimension(Location locA, Location locB) const; + + /** + * Sets the final value based on the state of the IM. + */ + void finish() override; + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const IMPredicate& imp); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h b/Sources/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h new file mode 100644 index 0000000..9e7f2f5 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/IntersectionMatrixPattern.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL IntersectionMatrixPattern { + +private: + + /** + * Cannot be instantiated. + */ + IntersectionMatrixPattern() {}; + + +public: + + /** + * A DE-9IM pattern to detect whether two polygonal geometries are adjacent along + * an edge, but do not overlap. + */ + static constexpr const char* ADJACENT = "F***1****"; + + /** + * A DE-9IM pattern to detect a geometry which properly contains another + * geometry (i.e. which lies entirely in the interior of the first geometry). + */ + static constexpr const char* CONTAINS_PROPERLY = "T**FF*FF*"; + + /** + * A DE-9IM pattern to detect if two geometries intersect in their interiors. + * This can be used to determine if a polygonal coverage contains any overlaps + * (although not whether they are correctly noded). + */ + static constexpr const char* INTERIOR_INTERSECTS = "T********"; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/LineStringExtracter.h b/Sources/geos/include/geos/operation/relateng/LineStringExtracter.h new file mode 100644 index 0000000..993f783 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/LineStringExtracter.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class LineString; + class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL LineStringExtracter : public geos::geom::GeometryFilter { + using LineString = geos::geom::LineString; + using Geometry = geos::geom::Geometry; + +private: + + std::vector& comps; + + +public: + + LineStringExtracter(std::vector& p_comps) + : comps(p_comps) + {} + + void filter_ro(const geom::Geometry* geom) override; + + static void getLines(const Geometry* geom, std::vector& lines); + + static std::vector getLines(const Geometry* geom); + + /** + * Extracts the {@link LineString} elements from a single {@link Geometry} + * and returns them as either a {@link LineString} or {@link MultiLineString}. + * + * @param geom the geometry from which to extract + * @return a linear geometry + */ + // static std::unique_ptr getGeometry(const Geometry* geom); + +}; + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/LinearBoundary.h b/Sources/geos/include/geos/operation/relateng/LinearBoundary.h new file mode 100644 index 0000000..9cd8fe6 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/LinearBoundary.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace algorithm { + class BoundaryNodeRule; +} +namespace geom { + class CoordinateXY; + class LineString; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL LinearBoundary { + using BoundaryNodeRule = geos::algorithm::BoundaryNodeRule; + using Coordinate = geos::geom::Coordinate; + using CoordinateXY = geos::geom::CoordinateXY; + using LineString = geos::geom::LineString; + +private: + + // Members + + Coordinate::ConstIntMap m_vertexDegree; + bool m_hasBoundary; + const BoundaryNodeRule& m_boundaryNodeRule; + + +public: + + // Constructors + + LinearBoundary(std::vector& lines, const BoundaryNodeRule& bnRule); + + bool hasBoundary() const; + + bool isBoundary(const CoordinateXY* pt) const; + + +private: + + // Methods + + bool checkBoundary(Coordinate::ConstIntMap& vertexDegree) const; + + static void computeBoundaryPoints( + std::vector& lines, + Coordinate::ConstIntMap& vertexDegree); + + static void addEndpoint( + const CoordinateXY *p, + Coordinate::ConstIntMap& vertexDegree); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/NodeSection.h b/Sources/geos/include/geos/operation/relateng/NodeSection.h new file mode 100644 index 0000000..16b85e9 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/NodeSection.h @@ -0,0 +1,163 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Represents a computed node along with the incident edges on either side of + * it (if they exist). + * This captures the information about a node in a geometry component + * required to determine the component's contribution to the node topology. + * A node in an area geometry always has edges on both sides of the node. + * A node in a linear geometry may have one or other incident edge missing, if + * the node occurs at an endpoint of the line. + * The edges of an area node are assumed to be provided + * with CW-shell orientation (as per JTS norm). + * This must be enforced by the caller. + * + * @author Martin Davis + * + */ +class GEOS_DLL NodeSection { + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + +private: + + // Members + bool m_isA; + int m_dim; + int m_id; + int m_ringId; + const Geometry* m_poly; + bool m_isNodeAtVertex; + const CoordinateXY* m_v0; + const CoordinateXY m_nodePt; + const CoordinateXY* m_v1; + + // Methods + + static int compareWithNull(const CoordinateXY* v0, const CoordinateXY* v1); + + static int compare(int a, int b); + +public: + + NodeSection( + bool isA, + int dim, + int id, + int ringId, + const Geometry* poly, + bool isNodeAtVertex, + const CoordinateXY* v0, + const CoordinateXY nodePt, + const CoordinateXY* v1) + : m_isA(isA) + , m_dim(dim) + , m_id(id) + , m_ringId(ringId) + , m_poly(poly) + , m_isNodeAtVertex(isNodeAtVertex) + , m_v0(v0) + , m_nodePt(nodePt) + , m_v1(v1) + {}; + + NodeSection(const NodeSection* ns) + : m_isA(ns->isA()) + , m_dim(ns->dimension()) + , m_id(ns->id()) + , m_ringId(ns->ringId()) + , m_poly(ns->getPolygonal()) + , m_isNodeAtVertex(ns->isNodeAtVertex()) + , m_v0(ns->getVertex(0)) + , m_nodePt(ns->nodePt()) + , m_v1(ns->getVertex(1)) + {}; + + const CoordinateXY* getVertex(int i) const; + + const CoordinateXY& nodePt() const; + + int dimension() const; + + int id() const; + + int ringId() const; + + /** + * Gets the polygon this section is part of. + * Will be null if section is not on a polygon boundary. + * + * @return the associated polygon, or null + */ + const Geometry* getPolygonal() const; + + bool isShell() const; + + bool isArea() const; + + static bool isAreaArea(const NodeSection& a, const NodeSection& b); + + bool isA() const; + + bool isSameGeometry(const NodeSection& ns) const; + + bool isSamePolygon(const NodeSection& ns) const; + + bool isNodeAtVertex() const; + + bool isProper() const; + + static bool isProper(const NodeSection& a, const NodeSection& b); + + std::string toString() const; + + static std::string edgeRep(const CoordinateXY* p0, const CoordinateXY* p1); + + friend std::ostream& operator<<(std::ostream& os, const NodeSection& ns); + + /** + * Compare node sections by parent geometry, dimension, element id and ring id, + * and edge vertices. + * Sections are assumed to be at the same node point. + */ + int compareTo(const NodeSection& o) const; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/NodeSections.h b/Sources/geos/include/geos/operation/relateng/NodeSections.h new file mode 100644 index 0000000..ceafa12 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/NodeSections.h @@ -0,0 +1,99 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +class RelateNode; +// class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL NodeSections { + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + +private: + + // Members + const CoordinateXY* nodePt; + std::vector> sections; + + // Methods + + /** + * Sorts the sections so that: + * * lines are before areas + * * edges from the same polygon are contiguous + */ + void prepareSections(); + + static bool hasMultiplePolygonSections( + std::vector>& sections, + std::size_t i); + + static std::vector collectPolygonSections( + std::vector>& sections, + std::size_t i); + + +public: + + NodeSections(const CoordinateXY* pt) + : nodePt(pt) + {}; + + const CoordinateXY* getCoordinate() const; + + void addNodeSection(NodeSection* e); + + bool hasInteractionAB() const; + + const Geometry* getPolygonal(bool isA) const; + + std::unique_ptr createNode(); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + NodeSections(const NodeSections&) = delete; + NodeSections& operator=(const NodeSections&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/PolygonNodeConverter.h b/Sources/geos/include/geos/operation/relateng/PolygonNodeConverter.h new file mode 100644 index 0000000..60d0fac --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/PolygonNodeConverter.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +// class NodeSection; +} +} +} + + +// using geos::geom::CoordinateXY; +// using geos::geom::Geometry; + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Converts the node sections at a polygon node where + * a shell and one or more holes touch, or two or more holes touch. + * This converts the node topological structure from + * the OGC "touching-rings" (AKA "minimal-ring") model to the equivalent "self-touch" + * (AKA "inverted/exverted ring" or "maximal ring") model. + * In the "self-touch" model the converted NodeSection corners enclose areas + * which all lies inside the polygon + * (i.e. they does not enclose hole edges). + * This allows RelateNode to use simple area-additive semantics + * for adding edges and propagating edge locations. + * + * The input node sections are assumed to have canonical orientation + * (CW shells and CCW holes). + * The arrangement of shells and holes must be topologically valid. + * Specifically, the node sections must not cross or be collinear. + * + * This supports multiple shell-shell touches + * (including ones containing holes), and hole-hole touches, + * This generalizes the relate algorithm to support + * both the OGC model and the self-touch model. + * + * @author Martin Davis + * @see RelateNode + */ +class GEOS_DLL PolygonNodeConverter { + using CoordinateXY = geos::geom::CoordinateXY; + +public: + + /** + * Converts a list of sections of valid polygon rings + * to have "self-touching" structure. + * There are the same number of output sections as input ones. + * + * @param polySections the original sections + * @return the converted sections + */ + static std::vector> convert( + std::vector& polySections); + + +private: + + static std::size_t convertShellAndHoles( + std::vector& sections, + std::size_t shellIndex, + std::vector>& convertedSections); + + static std::vector> convertHoles( + std::vector& sections); + + static NodeSection* createSection( + const NodeSection* ns, + const CoordinateXY* v0, + const CoordinateXY* v1); + + static std::vector extractUnique( + std::vector& sections); + + static std::size_t next( + std::vector& ns, std::size_t i); + + static std::size_t findShell( + std::vector& polySections); + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateEdge.h b/Sources/geos/include/geos/operation/relateng/RelateEdge.h new file mode 100644 index 0000000..176a939 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateEdge.h @@ -0,0 +1,173 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { +class RelateNode; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateEdge { + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + using Location = geos::geom::Location; + +private: + + /** + * Indicates that the location is currently unknown + */ + static constexpr Location LOC_UNKNOWN = Location::NONE; + + // Members + const RelateNode* node; + const CoordinateXY* dirPt; + + int aDim = DIM_UNKNOWN; + Location aLocLeft = LOC_UNKNOWN; + Location aLocRight = LOC_UNKNOWN; + Location aLocLine = LOC_UNKNOWN; + + int bDim = DIM_UNKNOWN; + Location bLocLeft = LOC_UNKNOWN; + Location bLocRight = LOC_UNKNOWN; + Location bLocLine = LOC_UNKNOWN; + + +public: + + // Constants + static constexpr bool IS_FORWARD = true; + static constexpr bool IS_REVERSE = false; + static constexpr int DIM_UNKNOWN = -1; + + // Constructors + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA, bool isForward); + + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA); + + RelateEdge( + const RelateNode* node, const CoordinateXY* pt, + bool isA, Location locLeft, Location locRight, Location locLine); + + // Methods + static RelateEdge* create( + const RelateNode* node, + const CoordinateXY* dirPt, + bool isA, int dim, bool isForward); + + static std::size_t findKnownEdgeIndex( + std::vector>& edges, + bool isA); + + static void setAreaInterior( + std::vector>& edges, + bool isA); + + bool isInterior(bool isA, int position) const; + + Location location(bool isA, int position) const; + + int compareToEdge(const CoordinateXY* edgeDirPt) const; + + void setDimLocations(bool isA, int dim, Location loc); + + void setAreaInterior(bool isA); + + void setLocation(bool isA, int pos, Location loc); + + void setAllLocations(bool isA, Location loc); + + void setUnknownLocations(bool isA, Location loc); + + void merge(bool isA, int dim, bool isForward); + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const RelateEdge& re); + + +private: + + // Methods + void mergeSideLocation(bool isA, int pos, Location loc); + + /** + * Area edges override Line edges. + * Merging edges of same dimension is a no-op for + * the dimension and on location. + * But merging an area edge into a line edge + * sets the dimension to A and the location to BOUNDARY. + * + * @param isA + * @param locEdge + */ + void mergeDimEdgeLoc(bool isA, Location locEdge); + + void setDimension(bool isA, int dimension); + + void setLeft(bool isA, Location loc); + + void setRight(bool isA, Location loc); + + void setOn(bool isA, Location loc); + + int dimension(bool isA) const; + + bool isKnown(bool isA) const; + + bool isKnown(bool isA, int pos) const; + + void setLocations(bool isA, Location locLeft, Location locRight, Location locLine); + + void setLocationsLine(bool isA); + + void setLocationsArea(bool isA, bool isForward); + + std::string labelString() const; + + std::string locationString(bool isA) const; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateGeometry.h b/Sources/geos/include/geos/operation/relateng/RelateGeometry.h new file mode 100644 index 0000000..d1282d5 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateGeometry.h @@ -0,0 +1,284 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateSequence; + class Envelope; + class Geometry; + class LinearRing; + class LineString; + class MultiPolygon; + class Point; +} +namespace noding { + class SegmentString; +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +class GEOS_DLL RelateGeometry { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Dimension = geos::geom::Dimension; + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + using Location = geos::geom::Location; + using CoordinateXY = geos::geom::CoordinateXY; + using LinearRing = geos::geom::LinearRing; + using LineString = geos::geom::LineString; + using MultiPolygon = geos::geom::MultiPolygon; + using Point = geos::geom::Point; + using BoundaryNodeRule = geos::algorithm::BoundaryNodeRule; + using SegmentString = geos::noding::SegmentString; + +private: + + // Members + + const Geometry* geom; + bool m_isPrepared = false; + const Envelope* geomEnv; + const BoundaryNodeRule& boundaryNodeRule; + int geomDim = Dimension::False; + bool isLineZeroLen = false; + bool isGeomEmpty = false; + + Coordinate::ConstXYSet uniquePoints; + std::unique_ptr locator; + int elementId = 0; + bool hasPoints = false; + bool hasLines = false; + bool hasAreas = false; + + /* + * Memory contexts for lower level allocations + */ + std::vector> segStringTempStore; + std::vector> segStringPermStore; + std::vector> csStore; + + + // Methods + + void analyzeDimensions(); + + /** + * Tests if all geometry linear elements are zero-length. + * For efficiency the test avoids computing actual length. + * + * @param geom + * @return + */ + static bool isZeroLength(const Geometry* geom); + + static bool isZeroLength(const LineString* line); + + bool isZeroLengthLine(const Geometry* g) const { + // avoid expensive zero-length calculation if not linear + if (getDimension() != Dimension::L) + return false; + return isZeroLength(g); + }; + + RelatePointLocator* getLocator(); + + Coordinate::ConstXYSet createUniquePoints(); + + void extractSegmentStringsFromAtomic(bool isA, + const Geometry* geom, const MultiPolygon* parentPolygonal, + const Envelope* env, + std::vector& segStrings, + std::vector>& segStore); + + void extractRingToSegmentString(bool isA, + const LinearRing* ring, int ringId, const Envelope* env, + const Geometry* parentPoly, + std::vector& segStrings, + std::vector>& segStore); + + void extractSegmentStrings(bool isA, + const Envelope* env, const Geometry* geom, + std::vector& segStrings, + std::vector>& segStore); + + const CoordinateSequence* orientAndRemoveRepeated( + const CoordinateSequence* cs, bool orientCW); + + const CoordinateSequence* removeRepeated( + const CoordinateSequence* cs); + +public: + + static constexpr bool GEOM_A = true; + static constexpr bool GEOM_B = false; + + RelateGeometry(const Geometry* input) + : RelateGeometry(input, false, BoundaryNodeRule::getBoundaryRuleMod2()) + {}; + + RelateGeometry(const Geometry* input, const BoundaryNodeRule& bnRule) + : RelateGeometry(input, false, bnRule) + {}; + + RelateGeometry(const Geometry* input, bool p_isPrepared, const BoundaryNodeRule& bnRule); + + static std::string name(bool isA); + + const Geometry* getGeometry() const { + return geom; + } + + bool isPrepared() const { + return m_isPrepared; + } + + const Envelope* getEnvelope() const { + return geomEnv; + } + + inline int getDimension() const { + return geomDim; + } + + bool hasDimension(int dim) const { + switch (dim) { + case Dimension::P: return hasPoints; + case Dimension::L: return hasLines; + case Dimension::A: return hasAreas; + } + return false; + } + + bool hasAreaAndLine() const { + return hasAreas && hasLines; + } + + /** + * Gets the actual non-empty dimension of the geometry. + * Zero-length LineStrings are treated as Points. + * + * @return the real (non-empty) dimension + */ + int getDimensionReal() const; + + bool hasEdges() const; + + bool isNodeInArea(const CoordinateXY* nodePt, const Geometry* parentPolygonal); + + int locateLineEndWithDim(const CoordinateXY* p); + + /** + * Locates a vertex of a polygon. + * A vertex of a Polygon or MultiPolygon is on + * the {@link Location#BOUNDARY}. + * But a vertex of an overlapped polygon in a GeometryCollection + * may be in the {@link Location#INTERIOR}. + * + * @param pt the polygon vertex + * @return the location of the vertex + */ + Location locateAreaVertex(const CoordinateXY* pt); + + Location locateNode(const CoordinateXY* pt, const Geometry* parentPolygonal); + + int locateWithDim(const CoordinateXY* pt); + + /** + * Indicates whether the geometry requires self-noding + * for correct evaluation of specific spatial predicates. + * Self-noding is required for geometries which may self-cross + * - i.e. lines, and overlapping elements in GeometryCollections. + * Self-noding is not required for polygonal geometries, + * since they can only touch at vertices. + * This ensures that the coordinates of nodes created by + * crossing segments are computed explicitly. + * This ensures that node locations match in situations + * where a self-crossing and mutual crossing occur at the same logical location. + * E.g. a self-crossing line tested against a single segment + * identical to one of the crossed segments. + * + * @return true if self-noding is required for this geometry + */ + bool isSelfNodingRequired() const; + + /** + * Tests whether the geometry has polygonal topology. + * This is not the case if it is a GeometryCollection + * containing more than one polygon (since they may overlap + * or be adjacent). + * The significance is that polygonal topology allows more assumptions + * about the location of boundary vertices. + * + * @return true if the geometry has polygonal topology + */ + bool isPolygonal() const; + + bool isEmpty() const; + + bool hasBoundary(); + + Coordinate::ConstXYSet& getUniquePoints(); + + std::vector getEffectivePoints(); + + /** + * Extract RSegmentStrings from the geometry which + * intersect a given envelope. + * If the envelope is null all edges are extracted. + * @param geomA + * + * @param env the envelope to extract around (may be null) + * @return a list of SegmentStrings + */ + std::vector extractSegmentStrings(bool isA, const Envelope* env); + + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& os, const RelateGeometry& rg); + + /** + * Disable copy construction and assignment. Needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + * Classes with members that are vector<> of unique_ptr<> need this. + */ + RelateGeometry(const RelateGeometry&) = delete; + RelateGeometry& operator=(const RelateGeometry&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateMatrixPredicate.h b/Sources/geos/include/geos/operation/relateng/RelateMatrixPredicate.h new file mode 100644 index 0000000..0acc8e3 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateMatrixPredicate.h @@ -0,0 +1,82 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateMatrixPredicate : public IMPredicate { + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + using Dimension = geos::geom::Dimension; + using IntersectionMatrix = geos::geom::IntersectionMatrix; + +public: + + RelateMatrixPredicate() {}; + + std::string name() const override { + return "relateMatrix"; + }; + + bool requireInteraction() const override { + //-- ensure entire matrix is computed + return false; + }; + + bool isDetermined() const override { + //-- ensure entire matrix is computed + return false; + }; + + bool valueIM() override { + //-- indicates full matrix is being evaluated + return false; + }; + + /** + * Gets the current state of the IM matrix (which may only be partially complete). + * + * @return the IM matrix + */ + std::unique_ptr getIM() { + return std::unique_ptr(new IntersectionMatrix(intMatrix)); + } + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateNG.h b/Sources/geos/include/geos/operation/relateng/RelateNG.h new file mode 100644 index 0000000..66f3839 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateNG.h @@ -0,0 +1,296 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace algorithm { + class BoundaryNodeRule; + +} +namespace geom { + class Geometry; +} +namespace noding { +} +namespace operation { +namespace relateng { + class TopologyPredicate; + class TopologyComputer; + class EdgeSegmentIntersector; +} +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + +/** + * Computes the value of topological predicates between two geometries based on the + * Dimensionally-Extended 9-Intersection Model (DE-9IM). + * Standard and custom topological predicates are provided by RelatePredicate. + * + * The RelateNG algorithm has the following capabilities: + * + * * Efficient short-circuited evaluation of topological predicates + * (including matching custom DE-9IM matrix patterns) + * * Optimized repeated evaluation of predicates against a single geometry + * via cached spatial indexes (AKA "prepared mode") + * * Robust computation (only point-local topology is required, + * so invalid geometry topology does not cause failures) + * * GeometryCollection inputs containing mixed types and overlapping polygons + * are supported, using union semantics. + * * Zero-length LineStrings are treated as being topologically identical to Points. + * * Support for BoundaryNodeRule. + * + * See IntersectionMatrixPattern for a description of DE-9IM patterns. + * + * If not specified, the standard BoundaryNodeRule::MOD2_BOUNDARY_RULE is used. + * + * RelateNG operates in 2D only; it ignores any Z ordinates. + * + * This implementation replaces RelateOp and PreparedGeometry. + * + * FUTURE WORK + * + * * Support for a distance tolerance to provide "approximate" predicate evaluation + * + * @author Martin Davis + * + * @see RelateOp + * @see PreparedGeometry + */ +class GEOS_DLL RelateNG { + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + using BoundaryNodeRule = geos::algorithm::BoundaryNodeRule; + using MCIndexSegmentSetMutualIntersector = geos::noding::MCIndexSegmentSetMutualIntersector; + using LinearRing = geos::geom::LinearRing; + using Envelope = geos::geom::Envelope; + using SegmentString = geos::noding::SegmentString; + using IntersectionMatrix = geos::geom::IntersectionMatrix; + +private: + + // Members + const BoundaryNodeRule& boundaryNodeRule; + RelateGeometry geomA; + std::unique_ptr edgeMutualInt = nullptr; + + // Methods + + RelateNG(const Geometry* inputA, bool isPrepared, const BoundaryNodeRule& bnRule) + : boundaryNodeRule(bnRule) + , geomA(inputA, isPrepared, bnRule) + {} + + RelateNG(const Geometry* inputA, bool isPrepared) + : RelateNG(inputA, isPrepared, BoundaryNodeRule::getBoundaryRuleMod2()) + {} + + bool hasRequiredEnvelopeInteraction(const Geometry* b, TopologyPredicate& predicate); + + bool finishValue(TopologyPredicate& predicate); + + void computePP(RelateGeometry& geomB, TopologyComputer& topoComputer); + + void computeAtPoints(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computePoints(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + void computePoint(bool isA, const CoordinateXY* pt, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeLineEnds(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + + /** + * Compute the topology of a line endpoint. + * Also reports if the line end is in the exterior of the target geometry, + * to optimize testing multiple exterior endpoints. + * + * @param geom + * @param isA + * @param pt + * @param geomTarget + * @param topoComputer + * @return true if the line endpoint is in the exterior of the target + */ + bool computeLineEnd(RelateGeometry& geom, bool isA, const CoordinateXY* pt, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeAreaVertex(RelateGeometry& geom, bool isA, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + bool computeAreaVertex(RelateGeometry& geom, bool isA, const LinearRing* ring, RelateGeometry& geomTarget, TopologyComputer& topoComputer); + + void computeAtEdges(RelateGeometry& geomB, TopologyComputer& topoComputer); + + void computeEdgesAll(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector); + + void computeEdgesMutual(std::vector& edgesB, const Envelope* envInt, EdgeSegmentIntersector& intersector); + + + +public: + + /** + * Tests whether the topological relationship between two geometries + * satisfies a topological predicate. + * + * @param a the A input geometry + * @param b the B input geometry + * @param pred the topological predicate + * @return true if the topological relationship is satisfied + */ + static bool relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred); + + /** + * Tests whether the topological relationship between two geometries + * satisfies a topological predicate, + * using a given BoundaryNodeRule. + * + * @param a the A input geometry + * @param b the B input geometry + * @param pred the topological predicate + * @param bnRule the Boundary Node Rule to use + * @return true if the topological relationship is satisfied + */ + static bool relate(const Geometry* a, const Geometry* b, TopologyPredicate& pred, const BoundaryNodeRule& bnRule); + + /** + * Tests whether the topological relationship to a geometry + * matches a DE-9IM matrix pattern. + * + * @param a the A input geometry + * @param b the B input geometry + * @param imPattern the DE-9IM pattern to match + * @return true if the geometries relationship matches the DE-9IM pattern + * + * @see IntersectionMatrixPattern + */ + static bool relate(const Geometry* a, const Geometry* b, const std::string& imPattern); + + /** + * Computes the DE-9IM matrix + * for the topological relationship between two geometries. + * + * @param a the A input geometry + * @param b the B input geometry + * @return the DE-9IM matrix for the topological relationship + */ + static std::unique_ptr relate(const Geometry* a, const Geometry* b); + + /** + * Computes the DE-9IM matrix + * for the topological relationship between two geometries. + * + * @param a the A input geometry + * @param b the B input geometry + * @param bnRule the Boundary Node Rule to use + * @return the DE-9IM matrix for the relationship + */ + static std::unique_ptr relate(const Geometry* a, const Geometry* b, const BoundaryNodeRule& bnRule); + + /** + * Creates a prepared RelateNG instance to optimize the + * evaluation of relationships against a single geometry. + * + * @param a the A input geometry + * @return a prepared instance + */ + static std::unique_ptr prepare(const Geometry* a); + + /** + * Creates a prepared RelateNG instance to optimize the + * computation of predicates against a single geometry, + * using a given BoundaryNodeRule. + * + * @param a the A input geometry + * @param bnRule the required BoundaryNodeRule + * @return a prepared instance + */ + static std::unique_ptr prepare(const Geometry* a, const BoundaryNodeRule& bnRule); + + + /** + * Computes the DE-9IM matrix for the topological relationship to a geometry. + * + * @param b the B geometry to test against + * @return the DE-9IM matrix + */ + std::unique_ptr evaluate(const Geometry* b); + + + /** + * Tests whether the topological relationship to a geometry + * matches a DE-9IM matrix pattern. + * + * @param b the B geometry to test against + * @param imPattern the DE-9IM pattern to match + * @return true if the geometries' topological relationship matches the DE-9IM pattern + * + * @see IntersectionMatrixPattern + */ + bool evaluate(const Geometry* b, const std::string& imPattern); + + /** + * Tests whether the topological relationship to a geometry + * satisfies a topology predicate. + * + * @param b the B geometry to test against + * @param predicate the topological predicate + * @return true if the predicate is satisfied + */ + bool evaluate(const Geometry* b, TopologyPredicate& predicate); + + static bool intersects(const Geometry* a, const Geometry* b); + static bool crosses(const Geometry* a, const Geometry* b); + static bool disjoint(const Geometry* a, const Geometry* b); + static bool touches(const Geometry* a, const Geometry* b); + static bool within(const Geometry* a, const Geometry* b); + static bool contains(const Geometry* a, const Geometry* b); + static bool overlaps(const Geometry* a, const Geometry* b); + static bool covers(const Geometry* a, const Geometry* b); + static bool coveredBy(const Geometry* a, const Geometry* b); + static bool equalsTopo(const Geometry* a, const Geometry* b); + + bool intersects(const Geometry* a); + bool crosses(const Geometry* a); + bool disjoint(const Geometry* a); + bool touches(const Geometry* a); + bool within(const Geometry* a); + bool contains(const Geometry* a); + bool overlaps(const Geometry* a); + bool covers(const Geometry* a); + bool coveredBy(const Geometry* a); + bool equalsTopo(const Geometry* a); + bool relate(const Geometry* a, const std::string& pat); + std::unique_ptr relate(const Geometry* a); + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateNode.h b/Sources/geos/include/geos/operation/relateng/RelateNode.h new file mode 100644 index 0000000..7c43903 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateNode.h @@ -0,0 +1,143 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +#include + + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSection; +} +} +namespace geom { + class CoordinateXY; + class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelateNode { + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + +private: + + // Members + + /** + * A list of the edges around the node in CCW order, + * ordered by their CCW angle with the positive X-axis. + */ + std::vector> edges; + + const CoordinateXY* nodePt; + + + // Methods + + void updateEdgesInArea(bool isA, std::size_t indexFrom, std::size_t indexTo); + + void updateIfAreaPrev(bool isA, std::size_t index); + + void updateIfAreaNext(bool isA, std::size_t index); + + const RelateEdge* addLineEdge(bool isA, const CoordinateXY* dirPt); + + const RelateEdge* addAreaEdge(bool isA, const CoordinateXY* dirPt, bool isForward); + + /** + * Adds or merges an edge to the node. + * + * @param isA + * @param dirPt + * @param dim dimension of the geometry element containing the edge + * @param isForward the direction of the edge + * + * @return the created or merged edge for this point + */ + const RelateEdge* addEdge(bool isA, const CoordinateXY* dirPt, int dim, bool isForward); + + void finishNode(bool isA, bool isAreaInterior); + + void propagateSideLocations(bool isA, std::size_t startIndex); + + static std::size_t prevIndex(std::vector>& list, std::size_t index); + + static std::size_t nextIndex(std::vector>& list, std::size_t i); + + std::size_t indexOf( + const std::vector>& edges, + const RelateEdge* edge) const; + + +public: + + RelateNode(const CoordinateXY* pt) + : nodePt(pt) + {}; + + const CoordinateXY* getCoordinate() const; + + const std::vector>& getEdges() const; + + void addEdges(std::vector& nss); + void addEdges(std::vector>& nss); + + void addEdges(const NodeSection* ns); + + /** + * Computes the final topology for the edges around this node. + * Although nodes lie on the boundary of areas or the interior of lines, + * in a mixed GC they may also lie in the interior of an area. + * This changes the locations of the sides and line to Interior. + * + * @param isAreaInteriorA true if the node is in the interior of A + * @param isAreaInteriorB true if the node is in the interior of B + */ + void finish(bool isAreaInteriorA, bool isAreaInteriorB); + + std::string toString() const; + + bool hasExteriorEdge(bool isA); + + friend std::ostream& operator<<(std::ostream& os, const RelateNode& ns); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + RelateNode(const RelateNode&) = delete; + RelateNode& operator=(const RelateNode&) = delete; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelatePointLocator.h b/Sources/geos/include/geos/operation/relateng/RelatePointLocator.h new file mode 100644 index 0000000..a2cb3cf --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelatePointLocator.h @@ -0,0 +1,210 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace algorithm { + namespace locate { + // class PointOnGeometryLocator; + } +} +namespace operation { + namespace relateng { + // class LinearBoundary; + // class AdjacentEdgeLocator; + } +} +namespace geom { + class CoordinateXY; + class Geometry; + class LineString; + class Point; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +/** + * Locates a point on a geometry, including mixed-type collections. + * The dimension of the containing geometry element is also determined. + * GeometryCollections are handled with union semantics; + * i.e. the location of a point is that location of that point + * on the union of the elements of the collection. + * + * Union semantics for GeometryCollections has the following behaviours: + * + * * For a mixed-dimension (heterogeneous) collection + * a point may lie on two geometry elements with different dimensions. + * In this case the location on the largest-dimension element is reported. + * * For a collection with overlapping or adjacent polygons, + * points on polygon element boundaries may lie in the effective interior + * of the collection geometry. + * + * Prepared mode is supported via cached spatial indexes. + * + * @author Martin Davis + */ +class GEOS_DLL RelatePointLocator { + using BoundaryNodeRule = geos::algorithm::BoundaryNodeRule; + using PointOnGeometryLocator = geos::algorithm::locate::PointOnGeometryLocator; + using Coordinate = geos::geom::Coordinate; + using CoordinateXY = geos::geom::CoordinateXY; + using Geometry = geos::geom::Geometry; + using LineString = geos::geom::LineString; + using Point = geos::geom::Point; + using Location = geos::geom::Location; + +private: + + // Members + + const Geometry* geom; + bool isPrepared = false; + const BoundaryNodeRule& boundaryRule; + std::unique_ptr adjEdgeLocator; + Coordinate::ConstXYSet points; + std::vector lines; + std::vector polygons; + std::vector> polyLocator; + std::unique_ptr lineBoundary; + bool isEmpty; + + +public: + + // Constructors + + RelatePointLocator(const Geometry* p_geom) + : RelatePointLocator(p_geom, false, BoundaryNodeRule::getBoundaryRuleMod2()) + {}; + + RelatePointLocator(const Geometry* p_geom, bool p_isPrepared, const BoundaryNodeRule& p_bnRule) + : geom(p_geom) + , isPrepared(p_isPrepared) + , boundaryRule(p_bnRule) + { + init(geom); + }; + + void init(const Geometry* p_geom); + + bool hasBoundary() const; + + void extractElements(const Geometry* geom); + + void addPoint(const Point* pt); + + void addLine(const LineString* line); + + void addPolygonal(const Geometry* polygonal); + + Location locate(const CoordinateXY* p); + + int locateLineEndWithDim(const CoordinateXY* p); + + /* + * Locates a point which is known to be a node of the geometry + * (i.e. a vertex or on an edge). + * + * @param p the node point to locate + * @param parentPolygonal the polygon the point is a node of + * @return the location of the node point + */ + Location locateNode(const CoordinateXY* p, const Geometry* parentPolygonal); + + /** + * Locates a point which is known to be a node of the geometry, + * as a DimensionLocation. + * + * @param p the point to locate + * @param parentPolygonal the polygon the point is a node of + * @return the dimension and location of the point + */ + int locateNodeWithDim(const CoordinateXY* p, const Geometry* parentPolygonal); + + /** + * Computes the topological location ( Location) of a single point + * in a Geometry, as well as the dimension of the geometry element the point + * is located in (if not in the Exterior). + * It handles both single-element and multi-element Geometries. + * The algorithm for multi-part Geometries + * takes into account the SFS Boundary Determination Rule. + * + * @param p the point to locate + * @return the Location of the point relative to the input Geometry + */ + int locateWithDim(const CoordinateXY* p); + + +private: + + // Methods + + /** + * Computes the topological location (Location) of a single point + * in a Geometry, as well as the dimension of the geometry element the point + * is located in (if not in the Exterior). + * It handles both single-element and multi-element Geometries. + * The algorithm for multi-part Geometries + * takes into account the SFS Boundary Determination Rule. + * + * @param p the coordinate to locate + * @param isNode whether the coordinate is a node (on an edge) of the geometry + * @param polygon + * @return the Location of the point relative to the input Geometry + */ + int locateWithDim(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + int computeDimLocation(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + Location locateOnPoints(const CoordinateXY* p) const; + + Location locateOnLines(const CoordinateXY* p, bool isNode); + + Location locateOnLine(const CoordinateXY* p, /*bool isNode,*/ const LineString* l); + + Location locateOnPolygons(const CoordinateXY* p, bool isNode, const Geometry* parentPolygonal); + + Location locateOnPolygonal(const CoordinateXY* p, + bool isNode, + const Geometry* parentPolygonal, + std::size_t index); + + PointOnGeometryLocator * getLocator(std::size_t index); + + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelatePredicate.h b/Sources/geos/include/geos/operation/relateng/RelatePredicate.h new file mode 100644 index 0000000..c7950d3 --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelatePredicate.h @@ -0,0 +1,649 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace geos { // geos. +namespace operation { // geos.operation. +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL RelatePredicate { + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + +public: + +/************************************************************************ + * + * Creates a predicate to determine whether two geometries intersect. + * + * The intersects predicate has the following equivalent definitions: + * + * * The two geometries have at least one point in common + * * The DE-9IM Intersection Matrix for the two geometries matches + * at least one of the patterns + * + * [T********] + * [*T*******] + * [***T*****] + * [****T****] + * + * disjoint() = false + * (intersects is the inverse of disjoint) + * + * @return the predicate instance + * + * @see disjoint() + */ +class IntersectsPredicate : public BasicPredicate { + +public: + + std::string name() const override { + return std::string("intersects"); + } + + bool requireSelfNoding() const override { + //-- self-noding is not required to check for a simple interaction + return false; + } + + bool requireExteriorCheck(bool isSourceA) const override { + (void)isSourceA; + //-- intersects only requires testing interaction + return false; + } + + void init(const Envelope& envA, const Envelope& envB) override { + require(envA.intersects(envB)); + } + + void updateDimension(Location locA, Location locB, int dimension) override { + (void)dimension; + setValueIf(true, isIntersection(locA, locB)); + } + + void finish() override { + //-- if no intersecting locations were found + setValue(false); + } + +}; + +static std::unique_ptr intersects(); + +/************************************************************************ + * + * Creates a predicate to determine whether two geometries are disjoint. + * + * The disjoint predicate has the following equivalent definitions: + * + * * The two geometries have no point in common + * * The DE-9IM Intersection Matrix for the two geometries matches + * [FF*FF****] + * * intersects() = false + * (disjoint is the inverse of intersects) + * + * @return the predicate instance + * + * @see intersects() + */ +class DisjointPredicate : public BasicPredicate { + + std::string name() const override { + return std::string("disjoint"); + } + + bool requireSelfNoding() const override { + //-- self-noding is not required to check for a simple interaction + return false; + } + + bool requireInteraction() const override { + //-- ensure entire matrix is computed + return false; + } + + bool requireExteriorCheck(bool isSourceA) const override { + (void)isSourceA; + //-- intersects only requires testing interaction + return false; + } + + void init(const Envelope& envA, const Envelope& envB) override { + setValueIf(true, envA.disjoint(envB)); + } + + void updateDimension(Location locA, Location locB, int dimension) override { + (void)dimension; + setValueIf(false, isIntersection(locA, locB)); + } + + void finish() override { + //-- if no intersecting locations were found + setValue(true); + } +}; + +static std::unique_ptr disjoint(); + +/************************************************************************ + * Creates a predicate to determine whether a geometry contains another geometry. + * + * The contains predicate has the following equivalent definitions: + * + * * Every point of the other geometry is a point of this geometry, + * and the interiors of the two geometries have at least one point in common. + * * The DE-9IM Intersection Matrix for the two geometries matches + * the pattern + * [T*****FF*] + * * within(B, A) = true + * (contains is the converse of within) + * + * An implication of the definition is that "Geometries do not + * contain their boundary". In other words, if a geometry A is a subset of + * the points in the boundary of a geometry B, B.contains(A) = false. + * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) + * For a predicate with similar behavior but avoiding + * this subtle limitation, see covers(). + * + * @return the predicate instance + * + * @see within() + */ +class ContainsPredicate : public IMPredicate { + + std::string name() const override { + return std::string("contains"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_A; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_B; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimA, dimB)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envA, envB); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_A); + } + + bool valueIM() override { + return intMatrix.isContains(); + } +}; + +static std::unique_ptr contains(); + + + +/************************************************************************ + * Creates a predicate to determine whether a geometry is within another geometry. + * + * The within predicate has the following equivalent definitions: + * + * * Every point of this geometry is a point of the other geometry, + * and the interiors of the two geometries have at least one point in common. + * * The DE-9IM Intersection Matrix for the two geometries matches + * [T*F**F***] + * * contains(B, A) = true + * (within is the converse of contains()) + * + * An implication of the definition is that + * "The boundary of a Geometry is not within the Geometry". + * In other words, if a geometry A is a subset of + * the points in the boundary of a geometry B, within(B, A) = false + * (As a concrete example, take A to be a LineString which lies in the boundary of a Polygon B.) + * For a predicate with similar behavior but avoiding + * this subtle limitation, see coveredimBy(). + * + * @return the predicate instance + * + * @see #contains() + */ +class WithinPredicate : public IMPredicate { + + std::string name() const override { + return std::string("within"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_B; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_A; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimB, dimA)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envB, envA); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_B); + } + + bool valueIM() override { + return intMatrix.isWithin(); + } +}; + +static std::unique_ptr within(); + + + +/************************************************************************ + * Creates a predicate to determine whether a geometry covers another geometry. + * + * The covers predicate has the following equivalent definitions: + * + * Every point of the other geometry is a point of this geometry. + * The DE-9IM Intersection Matrix for the two geometries matches + * at least one of the following patterns: + * + * * [T*****FF*] + * * [*T****FF*] + * * [***T**FF*] + * * [****T*FF*] + * + * coveredimBy(b, a) = true + * (covers is the converse of coveredimBy()) + * + * If either geometry is empty, the value of this predicate is false. + * + * This predicate is similar to contains(), + * but is more inclusive (i.e. returns true for more cases). + * In particular, unlike contains it does not distinguish between + * points in the boundary and in the interior of geometries. + * For most cases, covers should be used in preference to contains. + * As an added benefit, covers is more amenable to optimization, + * and hence should be more performant. + * + * @return the predicate instance + * + * @see #coveredimBy() + */ +class CoversPredicate : public IMPredicate { + + std::string name() const override { + return std::string("covers"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_A; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_B; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimA, dimB)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envA, envB); + + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_A); + } + + bool valueIM() override { + return intMatrix.isCovers(); + } +}; + +static std::unique_ptr covers(); + + +/************************************************************************ +* Creates a predicate to determine whether a geometry is covered +* by another geometry. +* +* The coveredimBy predicate has the following equivalent definitions: +* +* Every point of this geometry is a point of the other geometry. +* The DE-9IM Intersection Matrix for the two geometries matches +* at least one of the following patterns: +* +* [T*F**F***] +* [*TF**F***] +* [**FT*F***] +* [**F*TF***] +* +* covers(B, A) = true +* (coveredimBy is the converse of covers()) +* +* If either geometry is empty, the value of this predicate is false. +* +* This predicate is similar to within(), +* but is more inclusive (i.e. returns true for more cases). +* +* @return the predicate instance +* +* @see #covers() +*/ +class CoveredByPredicate : public IMPredicate { + + std::string name() const override { + return std::string("coveredBy"); + } + + bool requireCovers(bool isSourceA) override { + return isSourceA == RelateGeometry::GEOM_B; + } + + bool requireExteriorCheck(bool isSourceA) const override { + //-- only need to check B against Exterior of A + return isSourceA == RelateGeometry::GEOM_A; + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(isDimsCompatibleWithCovers(dimB, dimA)); + } + + void init(const Envelope& envA, const Envelope& envB) override { + BasicPredicate::requireCovers(envB, envA); + } + + bool isDetermined() const override { + return intersectsExteriorOf(RelateGeometry::GEOM_B); + } + + bool valueIM() override { + return intMatrix.isCoveredBy(); + } + +}; + +static std::unique_ptr coveredBy(); + + +/************************************************************************ +* Creates a predicate to determine whether a geometry crosses another geometry. +* +* The crosses predicate has the following equivalent definitions: +* +* The geometries have some but not all interior points in common. +* The DE-9IM Intersection Matrix for the two geometries matches +* one of the following patterns: +* +* [T*T******] (for P/L, P/A, and L/A cases) +* [T*****T**] (for L/P, A/P, and A/L cases) +* [0********] (for L/L cases) +* +* +* For the A/A and P/P cases this predicate returns false. +* +* The SFS defined this predicate only for P/L, P/A, L/L, and L/A cases. +* To make the relation symmetric +* JTS extends the definition to apply to L/P, A/P and A/L cases as well. +* +* @return the predicate instance +*/ + +class CrossesPredicate : public IMPredicate { + + std::string name() const override { + return std::string("crosses"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + bool isBothPointsOrAreas = + (dimA == Dimension::P && dimB == Dimension::P) || + (dimA == Dimension::A && dimB == Dimension::A); + require(!isBothPointsOrAreas); + } + + bool isDetermined() const override { + if (dimA == Dimension::L && dimB == Dimension::L) { + //-- L/L interaction can only be dim = P + if (getDimension(Location::INTERIOR, Location::INTERIOR) > Dimension::P) + return true; + } + else if (dimA < dimB) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::INTERIOR, Location::EXTERIOR)) { + return true; + } + } + else if (dimA > dimB) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) { + return true; + } + } + return false; + } + + bool valueIM() override { + return intMatrix.isCrosses(dimA, dimB); + } +}; + +static std::unique_ptr crosses(); + + +/************************************************************************ +* Creates a predicate to determine whether two geometries are +* topologically equal. +* +* The equals predicate has the following equivalent definitions: +* +* The two geometries have at least one point in common, +* and no point of either geometry lies in the exterior of the other geometry. +* The DE-9IM Intersection Matrix for the two geometries matches +* the pattern T*F**FFF* +* +* @return the predicate instance +*/ +class EqualsTopoPredicate : public IMPredicate { + + std::string name() const override { + return std::string("equals"); + } + + bool requireInteraction() const override { + //-- allow EMPTY = EMPTY + return false; + }; + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + //-- don't require equal dims, because EMPTY = EMPTY for all dims + } + + void init(const Envelope& envA, const Envelope& envB) override { + //-- handle EMPTY = EMPTY cases + setValueIf(true, envA.isNull() && envB.isNull()); + + require(envA.equals(&envB)); + } + + bool isDetermined() const override { + bool isEitherExteriorIntersects = + isIntersects(Location::INTERIOR, Location::EXTERIOR) || + isIntersects(Location::BOUNDARY, Location::EXTERIOR) || + isIntersects(Location::EXTERIOR, Location::INTERIOR) || + isIntersects(Location::EXTERIOR, Location::BOUNDARY); + + return isEitherExteriorIntersects; + } + + bool valueIM() override { + return intMatrix.isEquals(dimA, dimB); + } + +}; + +static std::unique_ptr equalsTopo(); + + +/************************************************************************ + * Creates a predicate to determine whether a geometry overlaps another geometry. + * + * The overlaps predicate has the following equivalent definitions: + * + * The geometries have at least one point each not shared by the other + * (or equivalently neither covers the other), + * they have the same dimension, + * and the intersection of the interiors of the two geometries has + * the same dimension as the geometries themselves. + * The DE-9IM Intersection Matrix for the two geometries matches + * [T*T***T**] (for P/P and A/A cases) + * or [1*T***T**] (for L/L cases) + * + * If the geometries are of different dimension this predicate returns false. + * This predicate is symmetric. + * + * @return the predicate instance + */ +class OverlapsPredicate : public IMPredicate { + + std::string name() const override { + return std::string("overlaps"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + require(dimA == dimB); + } + + bool isDetermined() const override { + if (dimA == Dimension::A || dimA == Dimension::P) { + if (isIntersects(Location::INTERIOR, Location::INTERIOR) && + isIntersects(Location::INTERIOR, Location::EXTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) + return true; + } + if (dimA == Dimension::L) { + if (isDimension(Location::INTERIOR, Location::INTERIOR, Dimension::L) && + isIntersects(Location::INTERIOR, Location::EXTERIOR) && + isIntersects(Location::EXTERIOR, Location::INTERIOR)) + return true; + } + return false; + } + + bool valueIM() override { + return intMatrix.isOverlaps(dimA, dimB); + } +}; + +static std::unique_ptr overlaps(); + + + + + +/************************************************************************ +* Creates a predicate to determine whether a geometry touches another geometry. +* +* The touches predicate has the following equivalent definitions: +* +* The geometries have at least one point in common, +* but their interiors do not intersect. +* The DE-9IM Intersection Matrix for the two geometries matches +* at least one of the following patterns +* +* [FT*******] +* [F**T*****] +* [F***T****] +* +* +* If both geometries have dimension 0, the predicate returns false, +* since points have only interiors. +* This predicate is symmetric. +* +* @return the predicate instance +*/ +class TouchesPredicate : public IMPredicate { + + std::string name() const override { + return std::string("touches"); + } + + void init(int _dimA, int _dimB) override { + IMPredicate::init(_dimA, _dimB); + bool isBothPoints = (dimA == 0 && dimB == 0); + require(! isBothPoints); + } + + bool isDetermined() const override { + bool isInteriorsIntersects = isIntersects(Location::INTERIOR, Location::INTERIOR); + return isInteriorsIntersects; + } + + bool valueIM() override { + return intMatrix.isTouches(dimA, dimB); + } +}; + +static std::unique_ptr touches(); + +/** + * Creates a predicate that matches a DE-9IM matrix pattern. + * + * @param imPattern the pattern to match + * @return a predicate that matches the pattern + * + * @see IntersectionMatrixPattern + */ +static std::unique_ptr matches(const std::string& imPattern) +{ + return std::unique_ptr(new IMPatternMatcher(imPattern)); +} + + + +}; // !RelatePredicate + + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/RelateSegmentString.h b/Sources/geos/include/geos/operation/relateng/RelateSegmentString.h new file mode 100644 index 0000000..dcd486a --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/RelateSegmentString.h @@ -0,0 +1,158 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { + class CoordinateXY; + class CoordinateSequence; + class Geometry; +} +namespace operation { +namespace relateng { + class RelateGeometry; + class NodeSection; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + + +class GEOS_DLL RelateSegmentString : public geos::noding::BasicSegmentString { + using BasicSegmentString = geos::noding::BasicSegmentString; + using Geometry = geos::geom::Geometry; + using CoordinateXY = geos::geom::CoordinateXY; + using CoordinateSequence = geos::geom::CoordinateSequence; + +private: + + // Members + bool m_isA; + int m_dimension; + int m_id; + int m_ringId; + const RelateGeometry* m_inputGeom; + const Geometry* m_parentPolygonal = nullptr; + + // Constructor + RelateSegmentString( + const CoordinateSequence* pts, + bool isA, + int dimension, + int id, + int ringId, + const Geometry* poly, + const RelateGeometry* inputGeom) + : BasicSegmentString(const_cast(pts), nullptr) + , m_isA(isA) + , m_dimension(dimension) + , m_id(id) + , m_ringId(ringId) + , m_inputGeom(inputGeom) + , m_parentPolygonal(poly) + {} + + + // Methods + + static const RelateSegmentString* createSegmentString( + const CoordinateSequence* pts, + bool isA, int dim, int elementId, int ringId, + const Geometry* poly, const RelateGeometry* parent); + + /** + * + * @param ss + * @param segIndex + * @param pt + * @return the previous vertex, or null if none exists + */ + const CoordinateXY* prevVertex( + std::size_t segIndex, + const CoordinateXY* pt) const; + + /** + * @param ss + * @param segIndex + * @param pt + * @return the next vertex, or null if none exists + */ + const CoordinateXY* nextVertex( + std::size_t segIndex, + const CoordinateXY* pt) const; + + +public: + + static const RelateSegmentString* createLine( + const CoordinateSequence* pts, + bool isA, int elementId, + const RelateGeometry* parent); + + static const RelateSegmentString* createRing( + const CoordinateSequence* pts, + bool isA, int elementId, int ringId, + const Geometry* poly, const RelateGeometry* parent); + + inline bool isA() const { + return m_isA; + } + + inline const RelateGeometry* getGeometry() const { + return m_inputGeom; + } + + inline const Geometry* getPolygonal() const { + return m_parentPolygonal; + } + + NodeSection* createNodeSection(std::size_t segIndex, const CoordinateXY intPt) const; + + /** + * Tests if a segment intersection point has that segment as its + * canonical containing segment. + * Segments are half-closed, and contain their start point but not the endpoint, + * except for the final segment in a non-closed segment string, which contains + * its endpoint as well. + * This test ensures that vertices are assigned to a unique segment in a segment string. + * In particular, this avoids double-counting intersections which lie exactly + * at segment endpoints. + * + * @param segIndex the segment the point may lie on + * @param pt the point + * @return true if the segment contains the point + */ + bool isContainingSegment(std::size_t segIndex, const CoordinateXY* pt) const; + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/TopologyComputer.h b/Sources/geos/include/geos/operation/relateng/TopologyComputer.h new file mode 100644 index 0000000..e016d8e --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/TopologyComputer.h @@ -0,0 +1,233 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace relateng { + class NodeSection; + class RelateGeometry; + class RelateNode; + class TopologyPredicate; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL TopologyComputer { + using CoordinateXY = geos::geom::CoordinateXY; + using Location = geos::geom::Location; + using NodeSection = geos::operation::relateng::NodeSection; + using NodeSections = geos::operation::relateng::NodeSections; + using RelateGeometry = geos::operation::relateng::RelateGeometry; + using RelateNode = geos::operation::relateng::RelateNode; + using TopologyPredicate = geos::operation::relateng::TopologyPredicate; + +private: + + // Members + TopologyPredicate& predicate; + RelateGeometry& geomA; + RelateGeometry& geomB; + std::map nodeMap; + std::deque> nodeSectionsStore; + + // Methods + + /** + * Determine a priori partial EXTERIOR topology based on dimensions. + */ + void initExteriorDims(); + + void initExteriorEmpty(bool geomNonEmpty); + + inline RelateGeometry& getGeometry(bool isA) const { + return isA ? geomA : geomB; + }; + + void updateDim(Location locA, Location locB, int dimension); + + void updateDim(bool isAB, Location loc1, Location loc2, int dimension); + + /** + * Update topology for an intersection between A and B. + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateIntersectionAB(const NodeSection* a, const NodeSection* b); + + /** + * Updates topology for an AB Area-Area crossing node. + * Sections cross at a node if (a) the intersection is proper + * (i.e. in the interior of two segments) + * or (b) if non-proper then whether the linework crosses + * is determined by the geometry of the segments on either side of the node. + * In these situations the area geometry interiors intersect (in dimension 2). + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateAreaAreaCross(const NodeSection* a, const NodeSection* b); + + /** + * Updates topology for a node at an AB edge intersection. + * + * @param a the section for geometry A + * @param b the section for geometry B + */ + void updateNodeLocation(const NodeSection* a, const NodeSection* b); + + void addNodeSections(NodeSection* ns0, NodeSection* ns1); + + void addLineEndOnLine(bool isLineA, Location locLineEnd, Location locLine, const CoordinateXY* pt); + + void addLineEndOnArea(bool isLineA, Location locLineEnd, Location locArea, const CoordinateXY* pt); + + /** + * Updates topology for an area vertex (in Interior or on Boundary) + * intersecting a point. + * Note that because the largest dimension of intersecting target is determined, + * the intersecting point is not part of any other target geometry, + * and hence its neighbourhood is in the Exterior of the target. + * + * @param isAreaA whether the area is the A input + * @param locArea the location of the vertex in the area + * @param pt the point at which topology is being updated + */ + void addAreaVertexOnPoint(bool isAreaA, Location locArea, const CoordinateXY* pt); + + void addAreaVertexOnLine(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt); + + void evaluateNode(NodeSections* nodeSections); + + void evaluateNodeEdges(const RelateNode* node); + + NodeSections* getNodeSections(const CoordinateXY& nodePt); + + + +public: + + TopologyComputer( + TopologyPredicate& p_predicate, + RelateGeometry& p_geomA, + RelateGeometry& p_geomB) + : predicate(p_predicate) + , geomA(p_geomA) + , geomB(p_geomB) + { + initExteriorDims(); + }; + + int getDimension(bool isA) const; + + bool isAreaArea() const; + + /** + * Indicates whether the input geometries require self-noding + * for correct evaluation of specific spatial predicates. + * Self-noding is required for geometries which may + * have self-crossing linework. + * This causes the coordinates of nodes created by + * crossing segments to be computed explicitly. + * This ensures that node locations match in situations + * where a self-crossing and mutual crossing occur at the same logical location. + * The canonical example is a self-crossing line tested against a single segment * identical to one of the crossed segments. + * + * @return true if self-noding is required + */ + bool isSelfNodingRequired() const; + + bool isExteriorCheckRequired(bool isA) const; + + bool isResultKnown() const; + + bool getResult() const; + + /** + * Finalize the evaluation. + */ + void finish(); + + void addIntersection(NodeSection* a, NodeSection* b); + + void addPointOnPointInterior(const CoordinateXY* pt); + + void addPointOnPointExterior(bool isGeomA, const CoordinateXY* pt); + + void addPointOnGeometry(bool isA, Location locTarget, int dimTarget, const CoordinateXY* pt); + + /** + * Add topology for a line end. + * The line end point must be "significant"; + * i.e. not contained in an area if the source is a mixed-dimension GC. + * + * @param isLineA the input containing the line end + * @param locLineEnd the location of the line end (Interior or Boundary) + * @param locTarget the location on the target geometry + * @param dimTarget the dimension of the interacting target geometry element, + * (if any), or the dimension of the target + * @param pt the line end coordinate + */ + void addLineEndOnGeometry(bool isLineA, Location locLineEnd, Location locTarget, int dimTarget, const CoordinateXY* pt); + + /** + * Adds topology for an area vertex interaction with a target geometry element. + * Assumes the target geometry element has highest dimension + * (i.e. if the point lies on two elements of different dimension, + * the location on the higher dimension element is provided. + * This is the semantic provided by {@link RelatePointLocator}. + * + * Note that in a GeometryCollection containing overlapping or adjacent polygons, + * the area vertex location may be INTERIOR instead of BOUNDARY. + * + * @param isAreaA the input that is the area + * @param locArea the location on the area + * @param locTarget the location on the target geometry element + * @param dimTarget the dimension of the target geometry element + * @param pt the point of interaction + */ + void addAreaVertex(bool isAreaA, Location locArea, Location locTarget, int dimTarget, const CoordinateXY* pt); + + void addAreaVertexOnArea(bool isAreaA, Location locArea, Location locTarget, const CoordinateXY* pt); + + void evaluateNodes(); + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + TopologyComputer(const TopologyComputer&) = delete; + TopologyComputer& operator=(const TopologyComputer&) = delete; + + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/relateng/TopologyPredicate.h b/Sources/geos/include/geos/operation/relateng/TopologyPredicate.h new file mode 100644 index 0000000..b944d6b --- /dev/null +++ b/Sources/geos/include/geos/operation/relateng/TopologyPredicate.h @@ -0,0 +1,205 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (c) 2024 Martin Davis + * Copyright (C) 2024 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include // for operator<< +#include + +// Forward declarations +namespace geos { +namespace geom { + class Envelope; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace relateng { // geos.operation.relateng + + +class GEOS_DLL TopologyPredicate { + using Envelope = geos::geom::Envelope; + using Location = geos::geom::Location; + +public: + + /* Virtual destructor to ensure proper cleanup of derived classes */ + virtual ~TopologyPredicate() {}; + + /** + * Gets the name of the predicate. + * + * @return the predicate name + */ + virtual std::string name() const = 0; + + /** + * Indicates that the value of the predicate can be finalized + * based on its current state. + */ + virtual void finish() = 0; + + /** + * Tests if the predicate value is known. + * + * @return true if the result is known + */ + virtual bool isKnown() const = 0; + + /** + * Gets the current value of the predicate result. + * The value is only valid if isKnown() is true. + * + * @return the predicate result value + */ + virtual bool value() const = 0; + + /** + * Reports whether this predicate requires self-noding for + * geometries which contain crossing edges + * (for example, LineString, or GeometryCollection + * containing lines or polygons which may self-intersect). + * Self-noding ensures that intersections are computed consistently + * in cases which contain self-crossings and mutual crossings. + * + * Most predicates require this, but it can + * be avoided for simple intersection detection + * (such as in RelatePredicate#intersects() + * and RelatePredicate#disjoint(). + * Avoiding self-noding improves performance for polygonal inputs. + * + * @return true if self-noding is required. + */ + virtual bool requireSelfNoding() const { + return true; + }; + + /** + * Reports whether this predicate requires interaction between + * the input geometries. + * This is the case if + * + * IM[I, I] >= 0 or IM[I, B] >= 0 or IM[B, I] >= 0 or IM[B, B] >= 0 + * + * This allows a fast result if + * the envelopes of the geometries are disjoint. + * + * @return true if the geometries must interact + */ + virtual bool requireInteraction() const { + return true; + }; + + /** + * Reports whether this predicate requires that the source + * cover the target. + * This is the case if + * + * IM[Ext(Src), Int(Tgt)] = F and IM[Ext(Src), Bdy(Tgt)] = F + * + * If true, this allows a fast result if + * the source envelope does not cover the target envelope. + * + * @param isSourceA indicates the source input geometry + * @return true if the predicate requires checking whether the source covers the target + */ + virtual bool requireCovers(bool isSourceA) { + (void)isSourceA; + return false; + } + + /** + * Reports whether this predicate requires checking if the source input intersects + * the Exterior of the target input. + * This is the case if: + * + * IM[Int(Src), Ext(Tgt)] >= 0 or IM[Bdy(Src), Ext(Tgt)] >= 0 + * + * If false, this may permit a faster result in some geometric situations. + * + * @param isSourceA indicates the source input geometry + * @return true if the predicate requires checking whether the source intersects the target exterior + */ + virtual bool requireExteriorCheck(bool isSourceA) const { + (void)isSourceA; + return true; + } + + /** + * Initializes the predicate for a specific geometric case. + * This may allow the predicate result to become known + * if it can be inferred from the dimensions. + * + * @param dimA the dimension of geometry A + * @param dimB the dimension of geometry B + * + * @see Dimension + */ + virtual void init(int dimA, int dimB) { + (void)dimA; + (void)dimB; + }; + + /** + * Initializes the predicate for a specific geometric case. + * This may allow the predicate result to become known + * if it can be inferred from the envelopes. + * + * @param envA the envelope of geometry A + * @param envB the envelope of geometry B + */ + virtual void init(const Envelope& envA, const Envelope& envB) + { + //-- default if envelopes provide no information + (void)envA; + (void)envB; + }; + + /** + * Updates the entry in the DE-9IM intersection matrix + * for given Location in the input geometries. + * + * If this method is called with a {@link Dimension} value + * which is less than the current value for the matrix entry, + * the implementing class should avoid changing the entry + * if this would cause information loss. + * + * @param locA the location on the A axis of the matrix + * @param locB the location on the B axis of the matrix + * @param dimension the dimension value for the entry + * + * @see Dimension + * @see Location + */ + virtual void updateDimension(Location locA, Location locB, int dimension) = 0; + + + friend std::ostream& + operator<<(std::ostream& os, const TopologyPredicate& ns) + { + os << ns.name(); + return os; + } + +}; + +} // namespace geos.operation.relateng +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/sharedpaths/SharedPathsOp.h b/Sources/geos/include/geos/operation/sharedpaths/SharedPathsOp.h new file mode 100644 index 0000000..dfd75f6 --- /dev/null +++ b/Sources/geos/include/geos/operation/sharedpaths/SharedPathsOp.h @@ -0,0 +1,161 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2010 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: original work + * + * Developed by Sandro Santilli (strk@kbt.io) + * for Faunalia (http://www.faunalia.it) + * with funding from Regione Toscana - Settore SISTEMA INFORMATIVO + * TERRITORIALE ED AMBIENTALE - for the project: "Sviluppo strumenti + * software per il trattamento di dati geografici basati su QuantumGIS + * e Postgis (CIG 0494241492)" + * + **********************************************************************/ + +#pragma once + +#include // for GEOS_DLL + +#include + +// Forward declarations +namespace geos { +namespace geom { +class LineString; +class Geometry; +class GeometryFactory; +} +} + + +namespace geos { +namespace operation { // geos.operation + +/// Find shared paths among two linear Geometry objects. +namespace sharedpaths { // geos.operation.sharedpaths + +/** \brief + * Find shared paths among two linear Geometry objects + * + * For each shared path report if it direction is the same + * or opposite. + * + * Paths reported as shared are given in the direction they + * appear in the first geometry. + * + * \remark Developed by Sandro Santilli (strk@kbt.io) + * for Faunalia (http://www.faunalia.it) + * with funding from Regione Toscana - Settore SISTEMA INFORMATIVO + * TERRITORIALE ED AMBIENTALE - for the project: "Sviluppo strumenti + * software per il trattamento di dati geografici basati su QuantumGIS + * e Postgis (CIG 0494241492)" + * + */ +class GEOS_DLL SharedPathsOp { +public: + + /// LineString vector (list of edges) + typedef std::vector PathList; + + /// Find paths shared between two linear geometries + /// + /// @param g1 + /// First geometry. Must be linear. + /// + /// @param g2 + /// Second geometry. Must be linear. + /// + /// @param sameDirection + /// Shared edges having the same direction are pushed + /// onto this vector. They'll be of type LineString. + /// Ownership of the edges is transferred. + /// + /// @param oppositeDirection + /// Shared edges having the opposite direction are pushed + /// onto this vector. They'll be of type geom::LineString. + /// Ownership of the edges is transferred. + /// + static void sharedPathsOp(const geom::Geometry& g1, + const geom::Geometry& g2, + PathList& sameDirection, + PathList& oppositeDirection); + + /// Constructor + /// + /// @param g1 + /// First geometry. Must be linear. + /// + /// @param g2 + /// Second geometry. Must be linear. + /// + SharedPathsOp(const geom::Geometry& g1, const geom::Geometry& g2); + + /// Get shared paths + /// + /// @param sameDirection + /// Shared edges having the same direction are pushed + /// onto this vector. They'll be of type geom::LineString. + /// Ownership of the edges is transferred. + /// + /// @param oppositeDirection + /// Shared edges having the opposite direction are pushed + /// onto this vector. They'll be of type geom::LineString. + /// Ownership of the edges is transferred. + /// + void getSharedPaths(PathList& sameDirection, PathList& oppositeDirection); + + /// Delete all edges in the list + static void clearEdges(PathList& from); + +private: + + /// Get all the linear intersections + /// + /// Ownership of linestring pushed to the given container + /// is transferred to caller. See clearEdges for a deep + /// release if you need one. + /// + void findLinearIntersections(PathList& to); + + /// Check if the given edge goes forward or backward on the given line. + /// + /// PRECONDITION: It is assumed the edge fully lays on the geometry + /// + bool isForward(const geom::LineString& edge, + const geom::Geometry& geom); + + /// Check if the given edge goes in the same direction over + /// the two geometries. + bool + isSameDirection(const geom::LineString& edge) + { + return (isForward(edge, _g1) == isForward(edge, _g2)); + } + + /// Throw an IllegalArgumentException if the geom is not linear + void checkLinealInput(const geom::Geometry& g); + + const geom::Geometry& _g1; + const geom::Geometry& _g2; + const geom::GeometryFactory& _gf; + + // Declare type as noncopyable + SharedPathsOp(const SharedPathsOp& other) = delete; + SharedPathsOp& operator=(const SharedPathsOp& rhs) = delete; + +}; + +} // namespace geos.operation.sharedpaths +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/union/CascadedPolygonUnion.h b/Sources/geos/include/geos/operation/union/CascadedPolygonUnion.h new file mode 100644 index 0000000..2b1e292 --- /dev/null +++ b/Sources/geos/include/geos/operation/union/CascadedPolygonUnion.h @@ -0,0 +1,248 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/union/CascadedPolygonUnion.java r487 (JTS-1.12+) + * Includes custom code to deal with https://trac.osgeo.org/geos/ticket/837 + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +class Polygon; +class MultiPolygon; +class Envelope; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace geounion { // geos::operation::geounion + + +/** +* \brief +* Implementation of UnionStrategy that provides overlay using +* the first generation overlay routines. +*/ +class GEOS_DLL ClassicUnionStrategy : public UnionStrategy { + +public: + + ClassicUnionStrategy() {}; + + /** + * Computes the union of two geometries. + * This method may throw a {@link util::TopologyException} + * if one is encountered + */ + std::unique_ptr Union(const geom::Geometry*, const geom::Geometry*) override; + + /** + * Indicates whether the union function operates using + * a floating (full) precision model. + * If this is the case, then the unary union code + * can make use of the {@link OverlapUnion} performance optimization, + * and perhaps other optimizations as well. + * Otherwise, the union result extent may not be the same as the extent of the inputs, + * which prevents using some optimizations. + */ + bool isFloatingPrecision() const override; + +private: + + /** + * An alternative way of unioning polygonal geometries + * by using buffer(0). + * Only worth using if regular overlay union fails. + */ + std::unique_ptr unionPolygonsByBuffer(const geom::Geometry* g0, const geom::Geometry* g1); + +}; + + + +/** + * \brief + * Provides an efficient method of unioning a collection of polygonal geometries. + * + * This algorithm is faster and likely more robust than the simple iterated + * approach of repeatedly unioning each polygon to a result geometry. + * + * The `buffer(0)` trick is sometimes faster, but can be less robust and + * can sometimes take an exceptionally long time to complete. + * This is particularly the case where there is a high degree of overlap + * between the polygons. In this case, `buffer(0)` is forced to compute + * with *all* line segments from the outset, whereas cascading can eliminate + * many segments at each stage of processing. + * The best case for buffer(0) is the trivial case where there is `no` overlap + * between the input geometries. However, this case is likely rare in practice. + */ +class GEOS_DLL CascadedPolygonUnion { +private: + std::vector* inputPolys; + geom::GeometryFactory const* geomFactory; + + /** + * The effectiveness of the index is somewhat sensitive + * to the node capacity. + * Testing indicates that a smaller capacity is better. + * For an STRtree, 4 is probably a good number (since + * this produces 2x2 "squares"). + */ + static int const STRTREE_NODE_CAPACITY = 4; + + /** \brief + * Computes a [Geometry](@ref geom::Geometry) containing only polygonal components. + * + * Extracts the [Polygons](@ref geom::Polygon) from the input + * and returns them as an appropriate polygonal geometry. + * + * If the input is already `Polygonal`, it is returned unchanged. + * + * A particular use case is to filter out non-polygonal components + * returned from an overlay operation. + * + * @param g the geometry to filter + * @return a Polygonal geometry + */ + static std::unique_ptr restrictToPolygons(std::unique_ptr g); + +public: + CascadedPolygonUnion(); + + /** \brief + * Computes the union of a collection of polygonal [Geometrys](@ref geom::Geometry). + * + * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). + * ownership of elements *and* vector are left to caller. + */ + static std::unique_ptr Union(std::vector* polys); + static std::unique_ptr Union(std::vector* polys, UnionStrategy* unionFun); + + /** \brief + * Computes the union of a set of polygonal [Geometrys](@ref geom::Geometry). + * + * @tparam T an iterator yielding something castable to const Polygon * + * @param start start iterator + * @param end end iterator + * @param unionStrategy strategy to apply + */ + template + static std::unique_ptr + Union(T start, T end, UnionStrategy *unionStrategy) + { + std::vector polys; + for(T i = start; i != end; ++i) { + const geom::Polygon* p = dynamic_cast(*i); + polys.push_back(const_cast(p)); + } + return Union(&polys, unionStrategy); + } + + /** \brief + * Computes the union of a collection of polygonal [Geometrys](@ref geom::Geometry). + * + * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). + * Ownership of elements *and* vector are left to caller. + */ + static std::unique_ptr Union(const geom::MultiPolygon* polys); + + /** \brief + * Creates a new instance to union the given collection of + * [Geometrys](@ref geom::Geometry). + * + * @param polys a collection of polygonal [Geometrys](@ref geom::Geometry). + * Ownership of elements *and* vector are left to caller. + */ + CascadedPolygonUnion(std::vector* polys) + : inputPolys(polys) + , geomFactory(nullptr) + , unionFunction(&defaultUnionFunction) + {} + + CascadedPolygonUnion(std::vector* polys, UnionStrategy* unionFun) + : inputPolys(polys) + , geomFactory(nullptr) + , unionFunction(unionFun) + {} + + /** \brief + * Computes the union of the input geometries. + * + * @return the union of the input geometries + * @return `null` if no input geometries were provided + */ + std::unique_ptr Union(); + +private: + + UnionStrategy* unionFunction; + ClassicUnionStrategy defaultUnionFunction; + + /** + * Unions a section of a list using a recursive binary union on each half + * of the section. + * + * @param geoms the list of geometries containing the section to union + * @param start the start index of the section + * @param end the index after the end of the section + * @return the union of the list section + */ + std::unique_ptr binaryUnion(const std::vector & geoms, std::size_t start, std::size_t end); + + /** + * Computes the union of two geometries, + * either of both of which may be null. + * + * @param g0 a Geometry + * @param g1 a Geometry + * @return the union of the input(s) + * @return null if both inputs are null + */ + std::unique_ptr unionSafe(const geom::Geometry* g0, const geom::Geometry* g1) const; + + std::unique_ptr unionSafe(std::unique_ptr &&, std::unique_ptr &&); + + /** + * Encapsulates the actual unioning of two polygonal geometries. + * + * @param g0 + * @param g1 + * @return + */ + std::unique_ptr unionActual(const geom::Geometry* g0, const geom::Geometry* g1) const; + + std::unique_ptr unionActual(std::unique_ptr &&, std::unique_ptr &&) const; +}; + + + + + +} // namespace geos::operation::union +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/union/CoverageUnion.h b/Sources/geos/include/geos/operation/union/CoverageUnion.h new file mode 100644 index 0000000..9579b4b --- /dev/null +++ b/Sources/geos/include/geos/operation/union/CoverageUnion.h @@ -0,0 +1,65 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { + namespace geom { + class Polygon; + class LineString; + class LinearRing; + class GeometryFactory; + } +} + +namespace geos { +namespace operation { +namespace geounion { + + class GEOS_DLL CoverageUnion { + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Polygon = geos::geom::Polygon; + using LineString = geos::geom::LineString; + using LinearRing = geos::geom::LinearRing; + using LineSegment = geos::geom::LineSegment; + + + public: + static std::unique_ptr Union(const Geometry* geom); + + private: + CoverageUnion() = default; + + void extractRings(const Polygon* geom); + void extractRings(const Geometry* geom); + void extractSegments(const LineString* geom); + void sortRings(); + + std::unique_ptr polygonize(const GeometryFactory* gf); + + std::vector rings; + // std::unordered_set segments; + LineSegment::UnorderedSet segments; + static constexpr double AREA_PCT_DIFF_TOL = 1e-6; + }; + +} +} +} diff --git a/Sources/geos/include/geos/operation/union/DisjointSubsetUnion.h b/Sources/geos/include/geos/operation/union/DisjointSubsetUnion.h new file mode 100644 index 0000000..7a998f3 --- /dev/null +++ b/Sources/geos/include/geos/operation/union/DisjointSubsetUnion.h @@ -0,0 +1,49 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace geos { +namespace operation { +namespace geounion { + +class GEOS_DLL DisjointSubsetUnion { +public: + /** Perform a unary union on a geometry by combining the results of + * unary unions performed on its disjoint subsets. + * + * @brief Union + * @param g the geometry to union + * @return result geometry + */ + static std::unique_ptr Union(const geom::Geometry* g) { + operation::cluster::GeometryIntersectsClusterFinder f; + operation::cluster::DisjointOperation op(f); + op.setSplitInputs(true); + + return op.processDisjointSubsets(*g, [](const geom::Geometry& subset) { + return subset.Union(); + }); + } +}; + +} +} +} diff --git a/Sources/geos/include/geos/operation/union/OverlapUnion.h b/Sources/geos/include/geos/operation/union/OverlapUnion.h new file mode 100644 index 0000000..7537b38 --- /dev/null +++ b/Sources/geos/include/geos/operation/union/OverlapUnion.h @@ -0,0 +1,139 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: ORIGINAL WORK, generalization of CascadedPolygonUnion + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class LineSegment; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace geounion { // geos::operation::geounion + +/** \brief + * Unions MultiPolygons efficiently by using full topological union only + * for polygons which may overlap by virtue of intersecting the common + * area of the inputs. + * + * Other polygons are simply combined with the union result, which is much + * more performant. + * + * This situation is likely to occur during cascaded polygon union, + * since the partitioning of polygons is done heuristically + * and thus may group disjoint polygons which can lie far apart. + * It may also occur in real world data which contains many disjoint polygons + * (e.g. polygons representing parcels on different street blocks). + * + * # Algorithm + * + * The overlap region is determined as the common envelope of intersection. + * The input polygons are partitioned into two sets: + * + * * Overlapping: Polygons which intersect the overlap region, and thus potentially overlap each other + * * Disjoint: Polygons which are disjoint from (lie wholly outside) the overlap region + * + * The Overlapping set is fully unioned, and then combined with the Disjoint set. + * Performing a simple combine works because + * the disjoint polygons do not interact with each + * other (since the inputs are valid MultiPolygons). + * They also do not interact with the Overlapping polygons, + * since they are outside their envelope. + * + * # Verification + * + * In the general case the Overlapping set of polygons will + * extend beyond the overlap envelope. This means that the union result + * will extend beyond the overlap region. + * There is a small chance that the topological + * union of the overlap region will shift the result linework enough + * that the result geometry intersects one of the Disjoint geometries. + * This case is detected and if it occurs + * is remedied by falling back to performing a full union of the original inputs. + * Detection is done by a fairly efficient comparison of edge segments which + * extend beyond the overlap region. If any segments have changed + * then there is a risk of introduced intersections, and full union is performed. + * + * This situation has not been observed in JTS using floating precision, + * but it could happen due to snapping. It has been observed + * in other APIs (e.g. GEOS) due to more aggressive snapping. + * And it will be more likely to happen if a snap-rounding overlay is used. + * + * DEPRECATED: This optimization has been removed, since it impairs performance. + * + * @author mbdavis + * + */ +class GEOS_DLL OverlapUnion { + +public: + + OverlapUnion(const geom::Geometry* p_g0, const geom::Geometry* p_g1, geounion::UnionStrategy* unionFun) + : g0(p_g0) + , g1(p_g1) + , unionFunction(unionFun) + , geomFactory(p_g0->getFactory()) + , isUnionSafe(false) + {}; + + OverlapUnion(const geom::Geometry* p_g0, const geom::Geometry* p_g1) + : OverlapUnion(p_g0, p_g1, &defaultUnionFunction) + {}; + + + std::unique_ptr doUnion(); + +private: + + const geom::Geometry* g0; + const geom::Geometry* g1; + geounion::UnionStrategy* unionFunction; + const geom::GeometryFactory* geomFactory; + bool isUnionSafe; + + geounion::ClassicUnionStrategy defaultUnionFunction; + + static geom::Envelope overlapEnvelope(const geom::Geometry* geom0, const geom::Geometry* geom1); + std::unique_ptr extractByEnvelope(const geom::Envelope& env, const geom::Geometry* geom, std::vector>& disjointGeoms); + static std::unique_ptr combine(std::unique_ptr& unionGeom, std::vector>& disjointPolys); + std::unique_ptr unionFull(const geom::Geometry* geom0, const geom::Geometry* geom1); + static std::unique_ptr unionBuffer(const geom::Geometry* geom0, const geom::Geometry* geom1); + bool isBorderSegmentsSame(const geom::Geometry* result, const geom::Envelope& env); + static bool isEqual(std::vector& segs0, std::vector& segs1); + std::vector extractBorderSegments(const geom::Geometry* geom0, const geom::Geometry* geom1, const geom::Envelope& env); + void extractBorderSegments(const geom::Geometry* geom, const geom::Envelope& penv, std::vector& psegs); + +}; + +} // namespace geos::operation::union +} // namespace geos::operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/union/PointGeometryUnion.h b/Sources/geos/include/geos/operation/union/PointGeometryUnion.h new file mode 100644 index 0000000..35d615e --- /dev/null +++ b/Sources/geos/include/geos/operation/union/PointGeometryUnion.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace geounion { // geos::operation::geounion + +/** + * \brief + * Computes the union of a puntal geometry with another + * arbitrary [Geometry](@ref geom::Geometry). + * + * Does not copy any component geometries. + * + */ +class GEOS_DLL PointGeometryUnion { +public: + + static std::unique_ptr Union( + const geom::Geometry& pointGeom, + const geom::Geometry& otherGeom); + + + PointGeometryUnion(const geom::Geometry& pointGeom, + const geom::Geometry& otherGeom); + + std::unique_ptr Union() const; + +private: + const geom::Geometry& pointGeom; + const geom::Geometry& otherGeom; + const geom::GeometryFactory* geomFact; + + // Declared as non-copyable + PointGeometryUnion(const PointGeometryUnion& other); + PointGeometryUnion& operator=(const PointGeometryUnion& rhs); +}; + +} // namespace geos::operation::union +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/union/UnaryUnionOp.h b/Sources/geos/include/geos/operation/union/UnaryUnionOp.h new file mode 100644 index 0000000..3fed7e3 --- /dev/null +++ b/Sources/geos/include/geos/operation/union/UnaryUnionOp.h @@ -0,0 +1,243 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/union/UnaryUnionOp.java r320 (JTS-1.12) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class GeometryFactory; +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace geounion { // geos::operation::geounion + +/** \brief + * Unions a collection of Geometry or a single Geometry + * (which may be a collection) together. + * + * By using this special-purpose operation over a collection of + * geometries it is possible to take advantage of various optimizations + * to improve performance. + * Heterogeneous [GeometryCollections](@ref geom::GeometryCollection) + * are fully supported. + * + * The result obeys the following contract: + * + * - Unioning a set of overlapping [Polygons](@ref geom::Polygon) has the effect + * of merging the areas (i.e. the same effect as + * iteratively unioning all individual polygons together). + * - Unioning a set of [LineStrings](@ref geom::LineString) has the effect of + * **fully noding** and **dissolving** the input linework. + * In this context "fully noded" means that there will be a node or + * endpoint in the output for every endpoint or line segment crossing + * in the input. + * "Dissolved" means that any duplicate (e.g. coincident) line segments + * or portions of line segments will be reduced to a single line segment + * in the output. + * This is consistent with the semantics of the + * [Geometry::Union(Geometry* )](@ref geom::Geometry::Union()) + * operation. If **merged** linework is required, the + * [LineMerger](@ref operation::linemerge::LineMerger) class + * can be used. + * - Unioning a set of [Points](@ref geom::Point) has the effect of merging + * all identical points (producing a set with no duplicates). + * + * `UnaryUnion` always operates on the individual components of + * MultiGeometries. + * So it is possible to use it to "clean" invalid self-intersecting + * MultiPolygons (although the polygon components must all still be + * individually valid.) + */ +class GEOS_DLL UnaryUnionOp { +public: + + template + static std::unique_ptr + Union(const T& geoms) + { + UnaryUnionOp op(geoms); + return op.Union(); + } + + template + static std::unique_ptr + Union(const T& geoms, + geom::GeometryFactory& geomFact) + { + UnaryUnionOp op(geoms, geomFact); + return op.Union(); + } + + static std::unique_ptr + Union(const geom::Geometry& geom) + { + UnaryUnionOp op(geom); + return op.Union(); + } + + template + UnaryUnionOp(const T& geoms, geom::GeometryFactory& geomFactIn) + : geomFact(&geomFactIn) + , unionFunction(&defaultUnionFunction) + { + extractGeoms(geoms); + } + + template + UnaryUnionOp(const T& geoms) + : geomFact(nullptr) + , unionFunction(&defaultUnionFunction) + { + extractGeoms(geoms); + } + + UnaryUnionOp(const geom::Geometry& geom) + : geomFact(geom.getFactory()) + , unionFunction(&defaultUnionFunction) + { + extract(geom); + } + + void setUnionFunction(UnionStrategy* unionFun) + { + unionFunction = unionFun; + } + + /** + * \brief + * Gets the union of the input geometries. + * + * If no input geometries were provided, an empty GEOMETRYCOLLECTION is returned. + * + * @return a Geometry containing the union + * @return an empty GEOMETRYCOLLECTION if no geometries were provided + * in the input + */ + std::unique_ptr Union(); + +private: + + template + void + extractGeoms(const T& geoms) + { + for(typename T::const_iterator + i = geoms.begin(), + e = geoms.end(); + i != e; + ++i) { + const geom::Geometry* geom = *i; + extract(*geom); + } + } + + void + extract(const geom::Geometry& geom) + { + util::ensureNoCurvedComponents(geom); + + using namespace geom::util; + + if(! geomFact) { + geomFact = geom.getFactory(); + } + + GeometryExtracter::extract(geom, polygons); + GeometryExtracter::extract(geom, lines); + GeometryExtracter::extract(geom, points); + } + + /** + * Computes a unary union with no extra optimization, + * and no short-circuiting. + * Due to the way the overlay operations + * are implemented, this is still efficient in the case of linear + * and puntal geometries. + * Uses robust version of overlay operation + * to ensure identical behaviour to the union(Geometry) operation. + * + * @param g0 a geometry + * @return the union of the input geometry + */ + std::unique_ptr + unionNoOpt(const geom::Geometry& g0) + { + if(! empty.get()) { + empty = geomFact->createEmptyGeometry(); + } + return unionFunction->Union(&g0, empty.get()); + } + + /** + * Computes the union of two geometries, + * either of both of which may be null. + * + * @param g0 a Geometry (ownership transferred) + * @param g1 a Geometry (ownership transferred) + * @return the union of the input(s) + * @return null if both inputs are null + */ + std::unique_ptr unionWithNull( + std::unique_ptr g0, + std::unique_ptr g1 + ); + + // Members + std::vector polygons; + std::vector lines; + std::vector points; + + const geom::GeometryFactory* geomFact; + std::unique_ptr empty; + + UnionStrategy* unionFunction; + ClassicUnionStrategy defaultUnionFunction; + +}; + + +} // namespace geos::operation::union +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/operation/union/UnionStrategy.h b/Sources/geos/include/geos/operation/union/UnionStrategy.h new file mode 100644 index 0000000..5b560d9 --- /dev/null +++ b/Sources/geos/include/geos/operation/union/UnionStrategy.h @@ -0,0 +1,73 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace geounion { // geos::operation::geounion + +/** + * A strategy class that adapts UnaryUnion to different + * kinds of overlay algorithms. + * + * @author Martin Davis + * + */ +class GEOS_DLL UnionStrategy { + +public: + + virtual ~UnionStrategy() {}; + + /** + * Computes the union of two geometries. + * This method may throw a {@link util::TopologyException} + * if one is encountered + */ + virtual std::unique_ptr Union(const geom::Geometry*, const geom::Geometry*) = 0; + + virtual std::unique_ptr Union(std::unique_ptr &&, + std::unique_ptr &&); + + /** + * Indicates whether the union function operates using + * a floating (full) precision model. + * If this is the case, then the unary union code + * can make use of the operation::union::OverlapUnion performance optimization, + * and perhaps other optimizations as well. + * Otherwise, the union result extent may not be the same as the extent of the inputs, + * which prevents using some optimizations. + */ + virtual bool isFloatingPrecision() const = 0; + + +}; + +} // namespace geos::operation::geounion +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/ConsistentAreaTester.h b/Sources/geos/include/geos/operation/valid/ConsistentAreaTester.h new file mode 100644 index 0000000..1addfdf --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/ConsistentAreaTester.h @@ -0,0 +1,137 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/valid/ConsistentAreaTester.java rev. 1.14 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition +#include // for composition +#include // for composition + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace geomgraph { +class GeometryGraph; +} +namespace operation { +namespace relate { +class RelateNodeGraph; +} +} +} + +namespace geos { +namespace operation { // geos::operation +namespace valid { // geos::operation::valid + +/** \brief + * Checks that a geomgraph::GeometryGraph representing an area + * (a geom::Polygon or geom::MultiPolygon) + * has consistent semantics for area geometries. + * This check is required for any reasonable polygonal model + * (including the OGC-SFS model, as well as models which allow ring + * self-intersection at single points) + * + * Checks include: + * + * - test for rings which properly intersect + * (but not for ring self-intersection, or intersections at vertices) + * - test for consistent labelling at all node points + * (this detects vertex intersections with invalid topology, + * i.e. where the exterior side of an edge lies in the interior of the area) + * - test for duplicate rings + * + * If an inconsistency is found the location of the problem + * is recorded and is available to the caller. + * + */ +class GEOS_DLL ConsistentAreaTester { +private: + + algorithm::LineIntersector li; + + /// Not owned + geomgraph::GeometryGraph* geomGraph; + + relate::RelateNodeGraph nodeGraph; + + /// the intersection point found (if any) + geom::Coordinate invalidPoint; + + /** + * Check all nodes to see if their labels are consistent. + * If any are not, return false + */ + bool isNodeEdgeAreaLabelsConsistent(); + +public: + + /** + * Creates a new tester for consistent areas. + * + * @param newGeomGraph the topology graph of the area geometry. + * Caller keeps responsibility for its deletion + */ + ConsistentAreaTester(geomgraph::GeometryGraph* newGeomGraph); + + ~ConsistentAreaTester() = default; + + /** + * @return the intersection point, or null + * if none was found + */ + geom::Coordinate& getInvalidPoint(); + + /** \brief + * Check all nodes to see if their labels are consistent with + * area topology. + * + * @return true if this area has a consistent node + * labelling + */ + bool isNodeConsistentArea(); + + /** + * Checks for two duplicate rings in an area. + * Duplicate rings are rings that are topologically equal + * (that is, which have the same sequence of points up to point order). + * If the area is topologically consistent (determined by calling the + * isNodeConsistentArea, + * duplicate rings can be found by checking for EdgeBundles which contain + * more than one geomgraph::EdgeEnd. + * (This is because topologically consistent areas cannot have two rings sharing + * the same line segment, unless the rings are equal). + * The start point of one of the equal rings will be placed in + * invalidPoint. + * + * @return true if this area Geometry is topologically consistent but has two duplicate rings + */ + bool hasDuplicateRings(); +}; + + + +} // namespace geos::operation::valid +} // namespace geos::operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/IndexedNestedHoleTester.h b/Sources/geos/include/geos/operation/valid/IndexedNestedHoleTester.h new file mode 100644 index 0000000..c52e977 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/IndexedNestedHoleTester.h @@ -0,0 +1,81 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Polygon; +class LinearRing; +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL IndexedNestedHoleTester { + using Polygon = geos::geom::Polygon; + using LinearRing = geos::geom::LinearRing; + using CoordinateXY = geos::geom::CoordinateXY; + +private: + + const Polygon* polygon; + index::strtree::TemplateSTRtree index; + CoordinateXY nestedPt; + + void loadIndex(); + + +public: + + IndexedNestedHoleTester(const Polygon* p_polygon) + : polygon(p_polygon) + { + loadIndex(); + } + + /** + * Gets a point on a nested hole, if one exists. + * + * @return a point on a nested hole, or null if none are nested + */ + const CoordinateXY& getNestedPoint() { return nestedPt; } + + /** + * Tests if any hole is nested (contained) within another hole. + * This is invalid. + * The nested point will be set to reflect this. + * @return true if some hole is nested + */ + bool isNested(); + +}; + + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h b/Sources/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h new file mode 100644 index 0000000..22650be --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/IndexedNestedPolygonTester.h @@ -0,0 +1,111 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Polygon; +class LinearRing; +class MultiPolygon; +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL IndexedNestedPolygonTester { + using Polygon = geos::geom::Polygon; + using MultiPolygon = geos::geom::MultiPolygon; + using LinearRing = geos::geom::LinearRing; + using CoordinateXY = geos::geom::CoordinateXY; + using IndexedPointInAreaLocator = algorithm::locate::IndexedPointInAreaLocator; + template + using TemplateSTRtree = index::strtree::TemplateSTRtree; + +private: + + const MultiPolygon* multiPoly; + TemplateSTRtree index; + // std::vector locators; + std::map locators; + CoordinateXY nestedPt; + + void loadIndex(); + + IndexedPointInAreaLocator& getLocator(const Polygon* poly); + + bool findNestedPoint(const LinearRing* shell, + const Polygon* possibleOuterPoly, + IndexedPointInAreaLocator& locator, + CoordinateXY& coordNested); + + /** + * Finds a point of a shell segment which lies inside a polygon, if any. + * The shell is assume to touch the polyon only at shell vertices, + * and does not cross the polygon. + * + * @param the shell to test + * @param the polygon to test against + * @param coordNested return parameter for found coordinate + * @return an interior segment point, or null if the shell is nested correctly + */ + static bool findIncidentSegmentNestedPoint( + const LinearRing* shell, + const Polygon* poly, + CoordinateXY& coordNested); + + // Declare type as noncopyable + IndexedNestedPolygonTester(const IndexedNestedPolygonTester& other) = delete; + IndexedNestedPolygonTester& operator=(const IndexedNestedPolygonTester& rhs) = delete; + +public: + + IndexedNestedPolygonTester(const MultiPolygon* p_multiPoly); + + /** + * Gets a point on a nested polygon, if one exists. + * + * @return a point on a nested polygon, or null if none are nested + */ + const CoordinateXY& getNestedPoint() const { return nestedPt; } + + /** + * Tests if any polygon is nested (contained) within another polygon. + * This is invalid. + * The nested point will be set to reflect this. + * @return true if some polygon is nested + */ + bool isNested(); + + +}; + + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/valid/IsSimpleOp.h b/Sources/geos/include/geos/operation/valid/IsSimpleOp.h new file mode 100644 index 0000000..1f2abfa --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/IsSimpleOp.h @@ -0,0 +1,299 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2009 Sandro Santilli + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +class BasicSegmentString; +} +// namespace algorithm { +// class BoundaryNodeRule; +// } +namespace geom { +class LineString; +class LinearRing; +class MultiLineString; +class MultiPoint; +class Geometry; +class Polygon; +class GeometryCollection; +} +} + + +namespace geos { // geos +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +/** + * Tests whether a Geometry is simple as defined by the OGC SFS specification. + * + * Simplicity is defined for each geom::Geometry type as follows: + * + * * geom::Point geometries are simple. + * * geom::MultiPoint geometries are simple if every point is unique + * * geom::LineString geometries are simple if they do not self-intersect at interior points + * (i.e. points other than the endpoints). + * * geom::MultiLineString geometries are simple if + * their elements are simple and they intersect only at points + * which are boundary points of both elements. + * (The notion of boundary points can be user-specified - see below). + * * Polygonal geometries have no definition of simplicity. + * The isSimple code checks if all polygon rings are simple. + * (Note: this means that isSimple cannot be used to test + * for ALL self-intersections in Polygons. + * In order to check if a Polygonal geometry has self-intersections, + * use geom::Geometry::isValid()). + * * geom::GeometryCollection geometries are simple if all their elements are simple. + * * Empty geometries are simple + * + * For linear geometries the evaluation of simplicity + * can be customized by supplying a BoundaryNodeRule + * to define how boundary points are determined. + * The default is the SFS-standard. + * + * Note that under the Mod-2 rule, closed LineStrings (rings) + * have no boundary. + * This means that an intersection at their endpoints makes the geometry non-simple. + * If it is required to test whether a set of LineStrings touch + * only at their endpoints, use BoundaryNodeRule::getBoundaryEndPoint(). + * For example, this can be used to validate that a collection of lines + * form a topologically valid linear network. + * + * By default this class finds a single non-simple location. + * To find all non-simple locations, set setFindAllLocations(bool) + * before calling isSimple(), and retrieve the locations + * via getNonSimpleLocations(). + * This can be used to find all intersection points in a linear network. + * + * @see BoundaryNodeRule + */ +class GEOS_DLL IsSimpleOp { + +private: + + const geom::Geometry& inputGeom; + bool isClosedEndpointsInInterior = true; + bool isFindAllLocations = false; + bool isSimpleResult = false; + std::vector nonSimplePts; + bool computed = false; + + void compute(); + + bool computeSimple(const geom::Geometry& geom); + + bool isSimpleMultiPoint(const geom::MultiPoint& mp); + + /** + * Computes simplicity for polygonal geometries. + * Polygonal geometries are simple if and only if + * all of their component rings are simple. + * + * @param geom a Polygonal geometry + * @return true if the geometry is simple + */ + bool isSimplePolygonal(const geom::Geometry& geom); + + /** + * Semantics for GeometryCollection is + * simple iff all components are simple. + * + * @param geom + * @return true if the geometry is simple + */ + bool isSimpleGeometryCollection(const geom::Geometry& geom); + + bool isSimpleLinearGeometry(const geom::Geometry& geom); + + static std::vector> + removeRepeatedPts(const geom::Geometry& geom); + + static std::vector> + createSegmentStrings(std::vector>& seqs); + + class NonSimpleIntersectionFinder : public noding::SegmentIntersector + { + + private: + + bool isClosedEndpointsInInterior; + bool isFindAll = false; + + std::vector& intersectionPts; + algorithm::LineIntersector li; + + // bool hasInteriorInt; + // bool hasInteriorVertexInt; + // bool hasEqualSegments; + // bool hasInteriorEndpointInt; + + bool findIntersection( + noding::SegmentString* ss0, std::size_t segIndex0, + noding::SegmentString* ss1, std::size_t segIndex1, + const geom::CoordinateXY& p00, const geom::CoordinateXY& p01, + const geom::CoordinateXY& p10, const geom::CoordinateXY& p11); + + /** + * Tests whether an intersection vertex is an endpoint of a segment string. + * + * @param ss the segmentString + * @param ssIndex index of segment in segmentString + * @param li the line intersector + * @param liSegmentIndex index of segment in intersector + * @return true if the intersection vertex is an endpoint + */ + bool isIntersectionEndpoint( + const noding::SegmentString* ss, std::size_t ssIndex, + const algorithm::LineIntersector& li, std::size_t liSegmentIndex) const; + + /** + * Finds the vertex index in a segment of an intersection + * which is known to be a vertex. + * + * @param li the line intersector + * @param segmentIndex the intersection segment index + * @return the vertex index (0 or 1) in the segment vertex of the intersection point + */ + std::size_t intersectionVertexIndex( + const algorithm::LineIntersector& li, std::size_t segmentIndex) const; + + public: + + NonSimpleIntersectionFinder( + bool p_isClosedEndpointsInInterior, + bool p_isFindAll, + std::vector& p_intersectionPts) + : isClosedEndpointsInInterior(p_isClosedEndpointsInInterior) + , isFindAll(p_isFindAll) + , intersectionPts(p_intersectionPts) + {}; + + /** + * Tests whether an intersection was found. + * + * @return true if an intersection was found + */ + bool hasIntersection() const; + + void processIntersections( + noding::SegmentString* ss0, std::size_t segIndex0, + noding::SegmentString* ss1, std::size_t segIndex1) override; + + bool isDone() const override; + + }; // NonSimpleIntersectionFinder + + +public: + + IsSimpleOp(const geom::Geometry* geom) + : IsSimpleOp(*geom) + {}; + + /** + * Creates a simplicity checker using the default SFS Mod-2 Boundary Node Rule + * + * @param geom the geometry to test + */ + IsSimpleOp(const geom::Geometry& geom) + : IsSimpleOp(geom, algorithm::BoundaryNodeRule::getBoundaryRuleMod2()) + {}; + + /** + * Creates a simplicity checker using a given {@link algorithm::BoundaryNodeRule} + * + * @param geom the geometry to test + * @param p_boundaryNodeRule the boundary node rule to use. + */ + IsSimpleOp(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& p_boundaryNodeRule) + : inputGeom(geom) + , isClosedEndpointsInInterior(! p_boundaryNodeRule.isInBoundary(2)) + , isFindAllLocations(false) + , computed(false) + {}; + + /** + * Tests whether a geometry is simple. + * + * @param geom the geometry to test + * @return true if the geometry is simple + */ + static bool isSimple(const geom::Geometry& geom); + + static bool isSimple(const geom::Geometry* geom) { + if (!geom) return false; + return isSimple(*geom); + } + + /** + * Gets a non-simple location in a geometry, if any. + * + * @param geom the input geometry + * @return a non-simple location, or null if the geometry is simple + */ + geom::CoordinateXY getNonSimpleLocation(const geom::Geometry& geom); + + /** + * Sets whether all non-simple intersection points + * will be found. + * + * @param isFindAll whether to find all non-simple points + */ + void setFindAllLocations(bool isFindAll); + + /** + * Tests whether the geometry is simple. + * + * @return true if the geometry is simple + */ + bool isSimple(); + + /** + * Gets the coordinate for an location where the geometry + * fails to be simple. + * (i.e. where it has a non-boundary self-intersection). + * + * @return a coordinate for the location of the non-boundary self-intersection + * or null if the geometry is simple + */ + geom::CoordinateXY getNonSimpleLocation(); + + /** + * Gets all non-simple intersection locations. + * + * @return a list of the coordinates of non-simple locations + */ + const std::vector& getNonSimpleLocations(); + + + +}; // IsSimpleOp + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/valid/IsValidOp.h b/Sources/geos/include/geos/operation/valid/IsValidOp.h new file mode 100644 index 0000000..27c6fe4 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/IsValidOp.h @@ -0,0 +1,308 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +class Geometry; +class Point; +class MultiPoint; +class LineString; +class LinearRing; +class Polygon; +class MultiPolygon; +class GeometryCollection; +} +namespace algorithm { +namespace locate { +class IndexedPointInAreaLocator; +} +} +} + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +/** + * Implements the algorithms required to compute the isValid() method + * for Geometry. + * See the documentation for the various geometry types for a specification of validity. + * + * @version 1.7 + */ +class GEOS_DLL IsValidOp { + using CoordinateXY = geos::geom::CoordinateXY; + +private: + + static constexpr int MIN_SIZE_LINESTRING = 2; + static constexpr int MIN_SIZE_RING = 4; + + /** + * The geometry being validated + */ + const geom::Geometry* inputGeometry; + /** + * If the following condition is true GEOS will validate + * inverted shells and exverted holes (the ESRI SDE model) + */ + bool isInvertedRingValid = false; + std::unique_ptr validErr; + + bool hasInvalidError() + { + return validErr != nullptr; + } + + void logInvalid(int code, const geom::CoordinateXY& pt); + + bool isValidGeometry(const geom::Geometry* g); + + /** + * Tests validity of a Point. + */ + bool isValid(const geom::Point* g); + + /** + * Tests validity of a MultiPoint. + */ + bool isValid(const geom::MultiPoint* g); + + /** + * Tests validity of a LineString. + * Almost anything goes for linestrings! + */ + bool isValid(const geom::LineString* g); + + /** + * Tests validity of a LinearRing. + */ + bool isValid(const geom::LinearRing* g); + + /** + * Tests the validity of a polygon. + * Sets the validErr flag. + */ + bool isValid(const geom::Polygon* g); + + /** + * Tests validity of a MultiPolygon. + * + * @param g + * @return + */ + bool isValid(const geom::MultiPolygon* g); + + /** + * Tests validity of a GeometryCollection. + * + * @param gc + * @return + */ + bool isValid(const geom::GeometryCollection* gc); + + void checkCoordinatesValid(const geom::CoordinateSequence* coords); + void checkCoordinatesValid(const geom::Polygon* poly); + void checkRingClosed(const geom::LinearRing* ring); + void checkRingsClosed(const geom::Polygon* poly); + void checkRingsPointSize(const geom::Polygon* poly); + void checkRingPointSize(const geom::LinearRing* ring); + + /** + * Check the number of non-repeated points is at least a given size. + * + * @param line + * @param minSize + */ + void checkTooFewPoints(const geom::LineString* line, std::size_t minSize); + + /** + * Test if the number of non-repeated points in a line + * is at least a given minimum size. + * + * @param line the line to test + * @param minSize the minimum line size + * @return true if the line has the required number of non-repeated points + */ + bool isNonRepeatedSizeAtLeast(const geom::LineString* line, std::size_t minSize); + + void checkAreaIntersections(PolygonTopologyAnalyzer& areaAnalyzer); + + /** + * Check whether a ring self-intersects (except at its endpoints). + * + * @param ring the linear ring to check + */ + void checkRingSimple(const geom::LinearRing* ring); + + + /** + * Tests that each hole is inside the polygon shell. + * This routine assumes that the holes have previously been tested + * to ensure that all vertices lie on the shell or on the same side of it + * (i.e. that the hole rings do not cross the shell ring). + * Given this, a simple point-in-polygon test of a single point in the hole can be used, + * provided the point is chosen such that it does not lie on the shell. + * + * @param poly the polygon to be tested for hole inclusion + */ + void checkHolesInShell(const geom::Polygon* poly); + + /** + * Checks if a polygon hole lies inside its shell + * and if not returns a point indicating this. + * The hole is known to be wholly inside or outside the shell, + * so it suffices to find a single point which is interior or exterior, + * or check the edge topology at a point on the boundary of the shell. + * + * @param hole the hole to test + * @param shell the polygon shell to test against + * @return a hole point outside the shell, or null if it is inside + */ + const CoordinateXY* findHoleOutsideShellPoint( + const geom::LinearRing* hole, + const geom::LinearRing* shell); + + /** + * Checks if any polygon hole is nested inside another. + * Assumes that holes do not cross (overlap), + * This is checked earlier. + * + * @param poly the polygon with holes to test + */ + void checkHolesNotNested(const geom::Polygon* poly); + + /** + * Checks that no element polygon is in the interior of another element polygon. + * + * Preconditions: + * + * * shells do not partially overlap + * * shells do not touch along an edge + * * no duplicate rings exist + * + * These have been confirmed by the PolygonTopologyAnalyzer. + */ + void checkShellsNotNested(const geom::MultiPolygon* mp); + + void checkInteriorConnected(PolygonTopologyAnalyzer& areaAnalyzer); + + +public: + + /** + * Creates a new validator for a geometry. + * + * @param p_inputGeometry the geometry to validate + */ + IsValidOp(const geom::Geometry* p_inputGeometry) + : inputGeometry(p_inputGeometry) + , validErr(nullptr) + {}; + + /** + * Sets whether polygons using Self-Touching Rings to form + * holes are reported as valid. + * If this flag is set, the following Self-Touching conditions + * are treated as being valid: + * + * * the shell ring self-touches to create a hole touching the shell + * * a hole ring self-touches to create two holes touching at a point + * + * The default (following the OGC SFS standard) + * is that this condition is not valid (false). + * + * Self-Touching Rings which disconnect the + * the polygon interior are still considered to be invalid + * (these are invalid under the SFS, and many other + * spatial models as well). + * This includes: + * + * * exverted ("bow-tie") shells which self-touch at a single point + * * inverted shells with the inversion touching the shell at another point + * * exverted holes with exversion touching the hole at another point + * * inverted ("C-shaped") holes which self-touch at a single point causing an island to be formed + * * inverted shells or exverted holes which form part of a chain of touching rings + * (which disconnect the interior) + * + * @param p_isValid states whether geometry with this condition is valid + */ + void setSelfTouchingRingFormingHoleValid(bool p_isValid) + { + isInvertedRingValid = p_isValid; + }; + + /** + * Tests whether a Geometry is valid. + * @param geom the Geometry to test + * @return true if the geometry is valid + */ + static bool isValid(const geom::Geometry* geom) + { + IsValidOp ivo(geom); + return ivo.isValid(); + }; + + static bool isValid(const geom::CoordinateXY& coord) + { + return isValid(&coord); + } + + /** + * Tests the validity of the input geometry. + * + * @return true if the geometry is valid + */ + bool isValid(); + + /** + * Checks whether a coordinate is valid for processing. + * Coordinates are valid if their x and y ordinates are in the + * range of the floating point representation. + * + * @param coord the coordinate to validate + * @return true if the coordinate is valid + */ + static bool isValid(const geom::CoordinateXY* coord); + + /** + * Computes the validity of the geometry, + * and if not valid returns the validation error for the geometry, + * or null if the geometry is valid. + * + * @return the validation error, if the geometry is invalid + * or null if the geometry is valid + */ + const TopologyValidationError* getValidationError(); + + +}; + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/valid/MakeValid.h b/Sources/geos/include/geos/operation/valid/MakeValid.h new file mode 100644 index 0000000..857aab3 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/MakeValid.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright 2009-2010 Sandro Santilli + * Copyright (C) 2019 Even Rouault + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + ********************************************************************** + * + * Ported from rtgeom_geos.c from + * rttopo - topology library + * http://git.osgeo.org/gitea/rttopo/librttopo + * with relicensing from GPL to LGPL with Copyright holder permission. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace valid { // geos::operation::valid + +/** \brief The function attempts to create a valid representation of a given + * invalid geometry without losing any of the input vertices. + * + * Already-valid geometries are returned without further intervention. + * + * Supported inputs are: POINTS, MULTIPOINTS, LINESTRINGS, MULTILINESTRINGS, POLYGONS, MULTIPOLYGONS and GEOMETRYCOLLECTIONS containing any mix of them. + * + * In case of full or partial dimensional collapses, the output geometry may be a collection of lower-to-equal dimension geometries or a geometry of lower dimension. + * + * Single polygons may become multi-geometries in case of self-intersections. + */ +class GEOS_DLL MakeValid { + +public: + + /** \brief + * Create a MakeValid object. + */ + MakeValid() = default; + + ~MakeValid() = default; + + /** \brief Return a valid version of the input geometry. */ + std::unique_ptr build(const geom::Geometry* geom); +}; + +} // namespace geos::operation::valid +} // namespace geos::operation +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/operation/valid/PolygonIntersectionAnalyzer.h b/Sources/geos/include/geos/operation/valid/PolygonIntersectionAnalyzer.h new file mode 100644 index 0000000..4a2eed9 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/PolygonIntersectionAnalyzer.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + + +#include + +#include + +// Forward declarations +namespace geos { +namespace noding { +class SegmentString; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL PolygonIntersectionAnalyzer : public noding::SegmentIntersector { + using CoordinateXY = geos::geom::CoordinateXY; + using SegmentString = geos::noding::SegmentString; + +private: + + algorithm::LineIntersector li; + bool m_hasDoubleTouch = false; + bool isInvertedRingValid = false; + int invalidCode = TopologyValidationError::oNoInvalidIntersection; + CoordinateXY invalidLocation; + CoordinateXY doubleTouchLocation; + + int findInvalidIntersection( + const SegmentString* ss0, std::size_t segIndex0, + const SegmentString* ss1, std::size_t segIndex1); + + bool addDoubleTouch( + const SegmentString* ss0, const SegmentString* ss1, + const CoordinateXY& intPt); + + void addSelfTouch( + const SegmentString* ss, const CoordinateXY& intPt, + const CoordinateXY* e00, const CoordinateXY* e01, + const CoordinateXY* e10, const CoordinateXY* e11); + + const CoordinateXY& prevCoordinateInRing( + const SegmentString* ringSS, std::size_t segIndex) const; + + bool isAdjacentInRing(const SegmentString* ringSS, + std::size_t segIndex0, std::size_t segIndex1) const; + + +public: + + /** + * Creates a new finder, allowing for the mode where inverted rings are valid. + * + * @param isInvertedRingValid true if inverted rings are valid. + */ + PolygonIntersectionAnalyzer(bool p_isInvertedRingValid) + : isInvertedRingValid(p_isInvertedRingValid) + , invalidLocation(CoordinateXY::getNull()) + , doubleTouchLocation(CoordinateXY::getNull()) + {} + + void processIntersections( + SegmentString* ss0, std::size_t segIndex0, + SegmentString* ss1, std::size_t segIndex1) override; + + bool isDone() const override { + return isInvalid() || m_hasDoubleTouch; + }; + + bool isInvalid() const + { + return invalidCode >= 0; + }; + + int getInvalidCode() const + { + return invalidCode; + }; + + const CoordinateXY& getInvalidLocation() const + { + return invalidLocation; + }; + + bool hasDoubleTouch() const + { + return m_hasDoubleTouch; + }; + + const CoordinateXY& getDoubleTouchLocation() const + { + return doubleTouchLocation; + }; + +}; + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/PolygonRing.h b/Sources/geos/include/geos/operation/valid/PolygonRing.h new file mode 100644 index 0000000..b1447c1 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/PolygonRing.h @@ -0,0 +1,229 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class LinearRing; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL PolygonRing { + using CoordinateXY = geos::geom::CoordinateXY; + using LinearRing = geos::geom::LinearRing; + +private: + + int id = -1; + PolygonRing* shell = nullptr; + const LinearRing* ring = nullptr; + + /** + * The root of the touch graph tree containing this ring. + * Serves as the id for the graph partition induced by the touch relation. + */ + PolygonRing* touchSetRoot = nullptr; + + /** + * The set of PolygonRingTouch links + * for this ring. + * The set of all touches in the rings of a polygon + * forms the polygon touch graph. + * This supports detecting touch cycles, which + * reveal the condition of a disconnected interior. + * + * Only a single touch is recorded between any two rings, + * since more than one touch between two rings + * indicates interior disconnection as well. + */ + std::map touches; + + /** + * The set of self-nodes in this ring. + * This supports checking valid ring self-touch topology. + */ + std::vector selfNodes; + + /* METHODS */ + + /** + * Tests if this ring touches a given ring at + * the single point specified. + * + * @param ring the other PolygonRing + * @param pt the touch point + * @return true if the rings touch only at the given point + */ + bool isOnlyTouch(const PolygonRing* polyRing, const CoordinateXY& pt) const; + + /** + * Detects whether the subgraph of holes linked by touch to this ring + * contains a hole cycle. + * If no cycles are detected, the set of touching rings is a tree. + * The set is marked using this ring as the root. + * + * @return a vertex in a hole cycle, or null if no cycle found + */ + const CoordinateXY* findHoleCycleLocation(); + + void init(PolygonRing* root, std::stack& touchStack); + + /** + * Scans for a hole cycle starting at a given touch. + * + * @param currentTouch the touch to investigate + * @param root the root of the touch subgraph + * @param touchStack the stack of touches to scan + * @return a vertex in a hole cycle if found, or null + */ + const CoordinateXY* scanForHoleCycle(PolygonRingTouch* currentTouch, + PolygonRing* root, + std::stack& touchStack); + + + bool isInTouchSet() const + { + return touchSetRoot != nullptr; + }; + + void setTouchSetRoot(PolygonRing* polyRing) + { + touchSetRoot = polyRing; + }; + + PolygonRing* getTouchSetRoot() const + { + return touchSetRoot; + }; + + bool hasTouches() const + { + return ! touches.empty(); + }; + + std::vector getTouches() const; + + void addTouch(PolygonRing* polyRing, const CoordinateXY& pt); + + +public: + + /** + * Creates a ring for a polygon hole. + * @param p_ring the ring geometry + * @param p_index the index of the hole + * @param p_shell the parent polygon shell + */ + PolygonRing(const LinearRing* p_ring, int p_index, PolygonRing* p_shell) + : id(p_index) + , shell(p_shell) + , ring(p_ring) + {}; + + /** + * Creates a ring for a polygon shell. + * @param p_ring + */ + PolygonRing(const LinearRing* p_ring) + : PolygonRing(p_ring, -1, this) + {}; + + /** + * Tests if a polygon ring represents a shell. + * + * @param polyRing the ring to test (may be null) + * @return true if the ring represents a shell + */ + static bool isShell(const PolygonRing* polyRing); + + /** + * Records a touch location between two rings, + * and checks if the rings already touch in a different location. + * + * @param ring0 a polygon ring + * @param ring1 a polygon ring + * @param pt the location where they touch + * @return true if the polygons already touch + */ + static bool addTouch(PolygonRing* ring0, PolygonRing* ring1, const CoordinateXY& pt); + + /** + * Finds a location (if any) where a chain of holes forms a cycle + * in the ring touch graph. + * The shell may form part of the chain as well. + * This indicates that a set of holes disconnects the interior of a polygon. + * + * @param polyRings the list of rings to check + * @return a vertex contained in a ring cycle, or null if none is found + */ + static const CoordinateXY* findHoleCycleLocation(std::vector polyRings); + + /** + * Finds a location of an interior self-touch in a list of rings, + * if one exists. + * This indicates that a self-touch disconnects the interior of a polygon, + * which is invalid. + * + * @param polyRings the list of rings to check + * @return the location of an interior self-touch node, or null if there are none + */ + static const CoordinateXY* findInteriorSelfNode(std::vector polyRings); + + bool isSamePolygon(const PolygonRing* polyRing) const + { + return shell == polyRing->shell; + }; + + bool isShell() const + { + return shell == this; + }; + + void addSelfTouch(const CoordinateXY& origin, + const CoordinateXY* e00, const CoordinateXY* e01, + const CoordinateXY* e10, const CoordinateXY* e11); + + /** + * Finds the location of an invalid interior self-touch in this ring, + * if one exists. + * + * @return the location of an interior self-touch node, or null if there are none + */ + const CoordinateXY* findInteriorSelfNode(); + + +}; + + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/PolygonRingSelfNode.h b/Sources/geos/include/geos/operation/valid/PolygonRingSelfNode.h new file mode 100644 index 0000000..2b95eeb --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/PolygonRingSelfNode.h @@ -0,0 +1,83 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include + + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL PolygonRingSelfNode { + using CoordinateXY = geos::geom::CoordinateXY; + +private: + + CoordinateXY nodePt; + const CoordinateXY* e00; + const CoordinateXY* e01; + const CoordinateXY* e10; + const CoordinateXY* e11; + + +public: + + PolygonRingSelfNode( + const CoordinateXY& p_nodePt, + const CoordinateXY* p_e00, + const CoordinateXY* p_e01, + const CoordinateXY* p_e10, + const CoordinateXY* p_e11) + : nodePt(p_nodePt) + , e00(p_e00) + , e01(p_e01) + , e10(p_e10) + , e11(p_e11) + {} + + /** + * The node point. + * + * @return + */ + const CoordinateXY* getCoordinate() const { + return &nodePt; + } + + /** + * Tests if a self-touch has the segments of each half of the touch + * lying in the exterior of a polygon. + * This is a valid self-touch. + * It applies to both shells and holes. + * Only one of the four possible cases needs to be tested, + * since the situation has full symmetry. + * + * @param isInteriorOnRight whether the interior is to the right of the parent ring + * @return true if the self-touch is in the exterior + */ + bool isExterior(bool isInteriorOnRight) const; + +}; + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/PolygonRingTouch.h b/Sources/geos/include/geos/operation/valid/PolygonRingTouch.h new file mode 100644 index 0000000..b59e64b --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/PolygonRingTouch.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +#include + +// Forward declarations +namespace geos { +namespace operation { +namespace valid { +class PolygonRing; +} +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL PolygonRingTouch { + using CoordinateXY = geos::geom::CoordinateXY; + +private: + + PolygonRing* ring; + CoordinateXY touchPt; + + +public: + + PolygonRingTouch(PolygonRing* p_ring, const CoordinateXY& p_pt) + : ring(p_ring) + , touchPt(p_pt) + {}; + + const CoordinateXY* getCoordinate() const; + + PolygonRing* getRing() const; + + bool isAtLocation(const CoordinateXY& pt) const; + +}; + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/PolygonTopologyAnalyzer.h b/Sources/geos/include/geos/operation/valid/PolygonTopologyAnalyzer.h new file mode 100644 index 0000000..8d72381 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/PolygonTopologyAnalyzer.h @@ -0,0 +1,211 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * Copyright (C) 2021 Martin Davis + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { // geos. +namespace operation { // geos.operation +namespace valid { // geos.operation.valid + +class GEOS_DLL PolygonTopologyAnalyzer { + using CoordinateXY = geos::geom::CoordinateXY; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Geometry = geos::geom::Geometry; + using LinearRing = geos::geom::LinearRing; + using SegmentString = geos::noding::SegmentString; + +private: + + // const Geometry* inputGeom; + bool isInvertedRingValid = false; + PolygonIntersectionAnalyzer segInt; + std::vector polyRings; + geom::CoordinateXY disconnectionPt; + + + // holding area for PolygonRings and SegmentStrings so we + // can pass around pointers with abandon + std::deque polyRingStore; + std::deque segStringStore; + // when building SegmentStrings we sometimes want + // to use deduped CoordinateSequences so we will + // keep the deduped ones here so they get cleaned + // up when processing is complete + std::vector> coordSeqStore; + + PolygonRing* createPolygonRing(const LinearRing* p_ring); + PolygonRing* createPolygonRing(const LinearRing* p_ring, int p_index, PolygonRing* p_shell); + + static const CoordinateXY& + findNonEqualVertex(const LinearRing* ring, const CoordinateXY& p); + + /** + * Tests whether a touching segment is interior to a ring. + * + * Preconditions: + * + * * The segment does not cross the ring + * * The segment vertex p0 lies on the ring + * * The ring is valid + * + * This works for both shells and holes, but the caller must know + * the ring role. + * + * @param p0 the first vertex of the segment + * @param p1 the second vertex of the segment + * @param ringPts the points of the ring + * @return true if the segment is inside the ring. + */ + static bool isIncidentSegmentInRing(const CoordinateXY* p0, const CoordinateXY* p1, + const CoordinateSequence* ringPts); + + static const CoordinateXY& findRingVertexPrev(const CoordinateSequence* ringPts, + std::size_t index, const CoordinateXY& node); + + static const CoordinateXY& findRingVertexNext(const CoordinateSequence* ringPts, + std::size_t index, const CoordinateXY& node); + + static std::size_t ringIndexPrev(const CoordinateSequence* ringPts, std::size_t index); + + static std::size_t ringIndexNext(const CoordinateSequence* ringPts, std::size_t index); + + /** + * Computes the index of the segment which intersects a given point. + * @param ringPts the ring points + * @param pt the intersection point + * @return the intersection segment index, or -1 if no intersection is found + */ + static std::size_t intersectingSegIndex(const CoordinateSequence* ringPts, const CoordinateXY* pt); + + std::vector createSegmentStrings(const Geometry* geom, bool isInvertedRingValid); + + std::vector getPolygonRings(const std::vector& segStrings); + + SegmentString* createSegString(const LinearRing* ring, const PolygonRing* polyRing); + + // Declare type as noncopyable + PolygonTopologyAnalyzer(const PolygonTopologyAnalyzer& other) = delete; + PolygonTopologyAnalyzer& operator=(const PolygonTopologyAnalyzer& rhs) = delete; + +public: + + /* public */ + PolygonTopologyAnalyzer(const Geometry* geom, bool p_isInvertedRingValid); + + /** + * Finds a self-intersection (if any) in a LinearRing. + * + * @param ring the ring to analyze + * @return a self-intersection point if one exists, or null + */ + static CoordinateXY findSelfIntersection(const LinearRing* ring); + + /** + * Tests whether a ring is nested inside another ring. + * + * Preconditions: + * + * * The rings do not cross (i.e. the test is wholly inside or outside the target) + * * The rings may touch at discrete points only + * * The target ring does not self-cross, but it may self-touch + * + * If the test ring start point is properly inside or outside, that provides the result. + * Otherwise the start point is on the target ring, + * and the incident start segment (accounting for repeated points) is + * tested for its topology relative to the target ring. + * + * @param test the ring to test + * @param target the ring to test against + * @return true if the test ring lies inside the target ring + */ + static bool + isRingNested(const LinearRing* test, + const LinearRing* target); + + bool hasInvalidIntersection() { + return segInt.isInvalid(); + } + + int getInvalidCode() { + return segInt.getInvalidCode(); + } + + const CoordinateXY& getInvalidLocation() { + return segInt.getInvalidLocation(); + } + + /** + * Tests whether the interior of the polygonal geometry is + * disconnected. + * If true, the disconnection location is available from + * getDisconnectionLocation(). + * + * @return true if the interior is disconnected + */ + bool isInteriorDisconnected(); + + const CoordinateXY& getDisconnectionLocation() const + { + return disconnectionPt; + }; + + + /** + * Tests whether any polygon with holes has a disconnected interior + * by virtue of the holes (and possibly shell) forming a hole cycle. + * + * This is a global check, which relies on determining + * the touching graph of all holes in a polygon. + * + * If inverted rings disconnect the interior + * via a self-touch, this is checked by the PolygonIntersectionAnalyzer. + * If inverted rings are part of a hole cycle + * this is detected here as well. + */ + void checkInteriorDisconnectedByHoleCycle(); + + /** + * Tests if an area interior is disconnected by a self-touching ring. + * This must be evaluated after other self-intersections have been analyzed + * and determined to not exist, since the logic relies on + * the rings not self-crossing (winding). + *

+ * If self-touching rings are not allowed, + * then the self-touch will previously trigger a self-intersection error. + */ + void checkInteriorDisconnectedBySelfTouch(); + +}; + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos diff --git a/Sources/geos/include/geos/operation/valid/RepeatedPointRemover.h b/Sources/geos/include/geos/operation/valid/RepeatedPointRemover.h new file mode 100644 index 0000000..e6e91c2 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/RepeatedPointRemover.h @@ -0,0 +1,60 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + + +namespace geos { +namespace operation { +namespace valid { + + /** + * Removes repeated, consecutive equal, coordinates from a CoordinateSequence. + */ + class GEOS_DLL RepeatedPointRemover { + + public: + + /** + * Returns a new CoordinateSequence being a copy of the input + * with any consecutive equal Coordinate removed. + * Equality test is 2D based. + * + * \param seq to filter + * \param tolerance to apply + * \return Geometr, ownership of returned object goes to the caller. + */ + static std::unique_ptr + removeRepeatedPoints( + const geom::CoordinateSequence* seq, + double tolerance = 0.0); + + static std::unique_ptr + removeRepeatedAndInvalidPoints( + const geom::CoordinateSequence* seq, + double tolerance = 0.0); + + static std::unique_ptr + removeRepeatedPoints( + const geom::Geometry* geom, + double tolerance = 0.0); + }; + +} +} +} + diff --git a/Sources/geos/include/geos/operation/valid/RepeatedPointTester.h b/Sources/geos/include/geos/operation/valid/RepeatedPointTester.h new file mode 100644 index 0000000..054977c --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/RepeatedPointTester.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/valid/RepeatedPointTester.java rev. 1.8 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include + +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +//class Coordinate; +class CoordinateSequence; +class Geometry; +class Polygon; +class MultiPolygon; +class MultiLineString; +class GeometryCollection; +} +} + +namespace geos { +namespace operation { // geos::operation +namespace valid { // geos::operation::valid + +/** \brief + * Implements the appropriate checks for repeated points + * (consecutive identical coordinates) as defined in the + * JTS spec. + */ +class GEOS_DLL RepeatedPointTester { +public: + RepeatedPointTester() {} + geom::CoordinateXY& getCoordinate(); + bool hasRepeatedPoint(const geom::Geometry* g); + bool hasRepeatedPoint(const geom::CoordinateSequence* coord); +private: + geom::CoordinateXY repeatedCoord; + bool hasRepeatedPoint(const geom::Polygon* p); + bool hasRepeatedPoint(const geom::GeometryCollection* gc); + bool hasRepeatedPoint(const geom::MultiPolygon* gc); + bool hasRepeatedPoint(const geom::MultiLineString* gc); +}; + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/operation/valid/TopologyValidationError.h b/Sources/geos/include/geos/operation/valid/TopologyValidationError.h new file mode 100644 index 0000000..c7ecc46 --- /dev/null +++ b/Sources/geos/include/geos/operation/valid/TopologyValidationError.h @@ -0,0 +1,76 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: operation/valid/TopologyValidationError.java rev. 1.16 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include // for composition + +// Forward declarations +// none required + +namespace geos { +namespace operation { // geos::operation +namespace valid { // geos::operation::valid + +/** \brief + * Contains information about the nature and location of a geom::Geometry + * validation error + * + */ +class GEOS_DLL TopologyValidationError { +public: + + enum errorEnum { + eError, + eRepeatedPoint, + eHoleOutsideShell, + eNestedHoles, + eDisconnectedInterior, + eSelfIntersection, + eRingSelfIntersection, + eNestedShells, + eDuplicatedRings, + eTooFewPoints, + eInvalidCoordinate, + eRingNotClosed, + oNoInvalidIntersection = -1 + }; + + TopologyValidationError(int newErrorType, const geom::CoordinateXY& newPt); + TopologyValidationError(int newErrorType); + const geom::CoordinateXY& getCoordinate() const; + std::string getMessage() const; + int getErrorType() const; + std::string toString() const; + +private: + // Used const char* to reduce dynamic allocations + static const char* errMsg[]; + int errorType; + const geom::CoordinateXY pt; +}; + + +} // namespace geos.operation.valid +} // namespace geos.operation +} // namespace geos + diff --git a/Sources/geos/include/geos/planargraph/DirectedEdge.h b/Sources/geos/include/geos/planargraph/DirectedEdge.h new file mode 100644 index 0000000..5d1d443 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/DirectedEdge.h @@ -0,0 +1,237 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance +#include // for composition + +#include // for typedefs +#include // for typedefs + +// Forward declarations +namespace geos { +namespace planargraph { +class Edge; +class Node; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \brief Represents a directed edge in a PlanarGraph. + * + * A DirectedEdge may or may not have a reference to a parent Edge + * (some applications of planar graphs may not require explicit Edge + * objects to be created). Usually a client using a PlanarGraph + * will subclass DirectedEdge to add its own application-specific + * data and methods. + */ +class GEOS_DLL DirectedEdge: public GraphComponent { + +public: + + friend std::ostream& operator << (std::ostream&, const DirectedEdge&); + + typedef std::list NonConstList; + typedef std::list ConstList; + typedef std::vector NonConstVect; + +protected: + Edge* parentEdge; + Node* from; + Node* to; + geom::Coordinate p0, p1; + DirectedEdge* sym; // optional + bool edgeDirection; + int quadrant; + double angle; +public: + + typedef std::vector ConstVect; + typedef std::vector Vect; + + /** + * \brief + * Returns a List containing the parent Edge (possibly null) + * for each of the given DirectedEdges. + * + * @note Ownership of the returned vector is left to + * the caller, see the equivalent function taking a vector + * reference to avoid this. + */ + static std::vector* toEdges( + std::vector& dirEdges); + + /** + * \brief + * Add parent Edge (possibly null) of each of the given DirectedEdges + * to the given parentEdges vector. + * + * @note Parents are pushed to the parentEdges vector, make sure + * it is empty if index-based correspondence is important. + */ + static void toEdges(std::vector& dirEdges, + std::vector& parentEdges); + + /** + * \brief Constructs a DirectedEdge connecting the `from` + * node to the `to` node. + * + * @param newFrom `from` node + * @param newTo `to` node + * @param directionPt specifies this DirectedEdge's direction + * (given by an imaginary line from the + * `from` node to + * `directionPt`) + * @param newEdgeDirection whether this DirectedEdge's direction + * is the same as or opposite to that of the + * parent Edge (if any) + */ + DirectedEdge(Node* newFrom, Node* newTo, + const geom::Coordinate& directionPt, + bool newEdgeDirection); + + /** + * \brief Returns this DirectedEdge's parent Edge, + * or null if it has none. + */ + Edge* getEdge() const; + + /** + * \brief Associates this DirectedEdge with an Edge + * (possibly null, indicating no associated Edge). + */ + void setEdge(Edge* newParentEdge); + + /** + * \brief Returns 0, 1, 2, or 3, indicating the quadrant in which + * this DirectedEdge's orientation lies. + */ + int getQuadrant() const; + + /** + * \brief Returns a point to which an imaginary line is drawn + * from the from-node to specify this DirectedEdge's orientation. + */ + const geom::Coordinate& getDirectionPt() const; + + /** + * \brief Returns whether the direction of the parent Edge (if any) + * is the same as that of this Directed Edge. + */ + bool getEdgeDirection() const; + + /** + * \brief Returns the node from which this DirectedEdge leaves. + */ + Node* getFromNode() const; + + /** + * \brief Returns the node to which this DirectedEdge goes. + */ + Node* getToNode() const; + + /** + * \brief + * Returns the coordinate of the from-node. + */ + geom::Coordinate& getCoordinate() const; + + /** + * \brief + * Returns the angle that the start of this DirectedEdge makes + * with the positive x-axis, in radians. + */ + double getAngle() const; + + /** + * \brief + * Returns the symmetric DirectedEdge -- the other DirectedEdge + * associated with this DirectedEdge's parent Edge. + */ + DirectedEdge* getSym() const; + + /** + * \brief + * Sets this DirectedEdge's symmetric DirectedEdge, which runs + * in the opposite direction. + */ + void setSym(DirectedEdge* newSym); + + /** + * \brief + * Returns 1 if this DirectedEdge has a greater angle with the + * positive x-axis than b", 0 if the DirectedEdges are collinear, + * and -1 otherwise. + * + * Using the obvious algorithm of simply computing the angle is + * not robust, since the angle calculation is susceptible to roundoff. + * A robust algorithm is: + * + * - first compare the quadrants. + * If the quadrants are different, it it + * trivial to determine which std::vector is "greater". + * - if the vectors lie in the same quadrant, the robust + * Orientation::index(Coordinate, Coordinate, Coordinate) + * function can be used to decide the relative orientation of + * the vectors. + * + */ + int compareTo(const DirectedEdge* obj) const; + + /** + * \brief + * Returns 1 if this DirectedEdge has a greater angle with the + * positive x-axis than b", 0 if the DirectedEdges are collinear, + * and -1 otherwise. + * + * Using the obvious algorithm of simply computing the angle is + * not robust, since the angle calculation is susceptible to roundoff. + * A robust algorithm is: + * + * - first compare the quadrants. + * If the quadrants are different, it it trivial to determine + * which std::vector is "greater". + * - if the vectors lie in the same quadrant, the robust + * Orientation::index(Coordinate, Coordinate, Coordinate) + * function can be used to decide the relative orientation of + * the vectors. + * + */ + int compareDirection(const DirectedEdge* e) const; + + /** + * \brief + * Prints a detailed string representation of this DirectedEdge + * to the given PrintStream. + */ + std::string print() const; + +}; + +/// Strict Weak comparator function for containers +bool pdeLessThan(DirectedEdge* first, DirectedEdge* second); + +/// Output operator +std::ostream& operator << (std::ostream&, const DirectedEdge&); + + +} // namespace geos::planargraph +} // namespace geos + diff --git a/Sources/geos/include/geos/planargraph/DirectedEdgeStar.h b/Sources/geos/include/geos/planargraph/DirectedEdgeStar.h new file mode 100644 index 0000000..4ad9e9c --- /dev/null +++ b/Sources/geos/include/geos/planargraph/DirectedEdgeStar.h @@ -0,0 +1,149 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace planargraph { +class DirectedEdge; +class Edge; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/// A sorted collection of DirectedEdge which leave a Node in a PlanarGraph. +class GEOS_DLL DirectedEdgeStar { +protected: + +private: + /** + * \brief The underlying list of outgoing DirectedEdges + */ + mutable std::vector outEdges; + mutable bool sorted; + void sortEdges() const; + +public: + /** + * \brief Constructs a DirectedEdgeStar with no edges. + */ + DirectedEdgeStar(): sorted(false) {} + + virtual + ~DirectedEdgeStar() {} + + /** + * \brief Adds a new member to this DirectedEdgeStar. + */ + void add(DirectedEdge* de); + + /** + * \brief Drops a member of this DirectedEdgeStar. + */ + void remove(DirectedEdge* de); + + /** + * \brief Returns an Iterator over the DirectedEdges, + * in ascending order by angle with the positive x-axis. + */ + std::vector::iterator + iterator() + { + return begin(); + } + /// Returns an iterator to first DirectedEdge + std::vector::iterator begin(); + + /// Returns an iterator to one-past last DirectedEdge + std::vector::iterator end(); + + /// Returns an const_iterator to first DirectedEdge + std::vector::const_iterator begin() const; + + /// Returns an const_iterator to one-past last DirectedEdge + std::vector::const_iterator end() const; + + /** + * \brief Returns the number of edges around the Node associated + * with this DirectedEdgeStar. + */ + std::size_t + getDegree() const + { + return outEdges.size(); + } + + /** + * \brief Returns the coordinate for the node at which this + * star is based + */ + geom::Coordinate& getCoordinate() const; + + /** + * \brief Returns the DirectedEdges, in ascending order + * by angle with the positive x-axis. + */ + std::vector& getEdges(); + + /** + * \brief Returns the zero-based index of the given Edge, + * after sorting in ascending order by angle with the + * positive x-axis. + */ + int getIndex(const Edge* edge); + + /** + * \brief Returns the zero-based index of the given DirectedEdge, + * after sorting in ascending order + * by angle with the positive x-axis. + */ + int getIndex(const DirectedEdge* dirEdge); + + /** + * \brief Returns the remainder when i is divided by the number of + * edges in this DirectedEdgeStar. + */ + unsigned int getIndex(int i) const; + + /** + * \brief Returns the DirectedEdge on the left-hand side + * of the given DirectedEdge (which must be a member of this + * DirectedEdgeStar). + */ + DirectedEdge* getNextEdge(DirectedEdge* dirEdge); +}; + +} // namespace geos::planargraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/planargraph/Edge.h b/Sources/geos/include/geos/planargraph/Edge.h new file mode 100644 index 0000000..85dc26f --- /dev/null +++ b/Sources/geos/include/geos/planargraph/Edge.h @@ -0,0 +1,141 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance + +#include // for typedefs +#include // for typedefs +#include // ostream +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace planargraph { +class DirectedEdgeStar; +class DirectedEdge; +class Edge; +class Node; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \brief Represents an undirected edge of a PlanarGraph. + * + * An undirected edge in fact simply acts as a central point of reference + * for two opposite DirectedEdge. + * + * Usually a client using a PlanarGraph will subclass Edge + * to add its own application-specific data and methods. + */ +class GEOS_DLL Edge: public GraphComponent { + +public: + + friend std::ostream& operator<< (std::ostream& os, const Node&); + + /// Set of const Edges pointers + typedef std::set ConstSet; + + /// Set of non-const Edges pointers + typedef std::set NonConstSet; + + /// Vector of non-const Edges pointers + typedef std::vector NonConstVect; + + /// Vector of const Edges pointers + typedef std::vector ConstVect; + +protected: + + /** \brief The two DirectedEdges associated with this Edge */ + std::vector dirEdge; + +public: + + /** \brief + * Constructs a Edge whose DirectedEdges are not yet set. + * + * Be sure to call + * {@link #setDirectedEdges(DirectedEdge* de0, DirectedEdge* de1)} + */ + Edge(): dirEdge() {} + + /** + * \brief Constructs an Edge initialized with the given DirectedEdges. + * + * For each DirectedEdge: sets the Edge, sets the symmetric + * DirectedEdge, and adds this Edge to its from-Node. + */ + Edge(DirectedEdge* de0, DirectedEdge* de1) + : + dirEdge() + { + setDirectedEdges(de0, de1); + } + + /** + * \brief Initializes this Edge's two DirectedEdges. + * + * For each DirectedEdge: + * - sets the Edge, sets the symmetric DirectedEdge, and + * - adds this Edge to its from-Node. + */ + void setDirectedEdges(DirectedEdge* de0, DirectedEdge* de1); + + /** + * \brief Returns one of the DirectedEdges associated with this Edge. + * @param i 0 or 1 + */ + DirectedEdge* getDirEdge(int i); + + /** + * \brief Returns the DirectedEdge that starts from the given node, + * or null if the node is not one of the two nodes associated + * with this Edge. + */ + DirectedEdge* getDirEdge(Node* fromNode); + + /** + * \brief If node is one of the two nodes associated + * with this Edge, returns the other node; otherwise returns null. + */ + Node* getOppositeNode(Node* node); +}; + +/// Print a Edge +std::ostream& operator<<(std::ostream& os, const Edge& n); + +/// For backward compatibility +//typedef Edge planarEdge; + +} // namespace geos::planargraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/planargraph/GraphComponent.h b/Sources/geos/include/geos/planargraph/GraphComponent.h new file mode 100644 index 0000000..03935b9 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/GraphComponent.h @@ -0,0 +1,182 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: planargraph/GraphComponent.java rev. 1.7 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \brief The base class for all graph component classes. + * + * Maintains flags of use in generic graph algorithms. + * Provides two flags: + * + * - marked - typically this is used to indicate a state that + * persists for the course of the graph's lifetime. For instance, + * it can be used to indicate that a component has been logically + * deleted from the graph. + * - visited - this is used to indicate that a component has been + * processed or visited by an single graph algorithm. For instance, + * a breadth-first traversal of the graph might use this to indicate + * that a node has already been traversed. + * The visited flag may be set and cleared many times during the + * lifetime of a graph. + * + */ +class GEOS_DLL GraphComponent { + +protected: + + /// Variable holding ''marked'' status + bool isMarkedVar; + + /// Variable holding ''visited'' status + bool isVisitedVar; + +public: + + GraphComponent() + : + isMarkedVar(false), + isVisitedVar(false) + {} + + virtual + ~GraphComponent() {} + + /** \brief + * Tests if a component has been visited during the course + * of a graph algorithm. + * + * @return true if the component has been visited + */ + virtual bool + isVisited() const + { + return isVisitedVar; + } + + /** \brief + * Sets the visited flag for this component. + * @param p_isVisited the desired value of the visited flag + */ + virtual void + setVisited(bool p_isVisited) + { + isVisitedVar = p_isVisited; + } + + /** \brief + * Sets the Visited state for the elements of a container, + * from start to end iterator. + * + * @param start the start element + * @param end one past the last element + * @param visited the state to set the visited flag to + */ + template + static void + setVisited(T start, T end, bool visited) + { + for(T i = start; i != end; ++i) { + (*i)->setVisited(visited); + } + } + + /** \brief + * Sets the Visited state for the values of each map + * container element, from start to end iterator. + * + * @param start the start element + * @param end one past the last element + * @param visited the state to set the visited flag to + */ + template + static void + setVisitedMap(T start, T end, bool visited) + { + for(T i = start; i != end; ++i) { + i->second->setVisited(visited); + } + } + + /** \brief + * Sets the Marked state for the elements of a container, + * from start to end iterator. + * + * @param start the start element + * @param end one past the last element + * @param marked the state to set the marked flag to + */ + template + static void + setMarked(T start, T end, bool marked) + { + for(T i = start; i != end; ++i) { + (*i)->setMarked(marked); + } + } + + + /** \brief + * Sets the Marked state for the values of each map + * container element, from start to end iterator. + * + * @param start the start element + * @param end one past the last element + * @param marked the state to set the visited flag to + */ + template + static void + setMarkedMap(T start, T end, bool marked) + { + for(T i = start; i != end; ++i) { + i->second->setMarked(marked); + } + } + + /** \brief + * Tests if a component has been marked at some point + * during the processing involving this graph. + * @return true if the component has been marked + */ + virtual bool + isMarked() const + { + return isMarkedVar; + } + + /** \brief + * Sets the marked flag for this component. + * @param p_isMarked the desired value of the marked flag + */ + virtual void + setMarked(bool p_isMarked) + { + isMarkedVar = p_isMarked; + } + +}; + +} // namespace geos::planargraph +} // namespace geos + diff --git a/Sources/geos/include/geos/planargraph/Node.h b/Sources/geos/include/geos/planargraph/Node.h new file mode 100644 index 0000000..dccf978 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/Node.h @@ -0,0 +1,158 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include // for inheritance +#include // for inlines +#include // for composition + +// Forward declarations +namespace geos { +namespace planargraph { +//class DirectedEdgeStar; +class DirectedEdge; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \brief A node in a PlanarGraph is a location where 0 or more Edge meet. + * + * A node is connected to each of its incident Edges via an outgoing + * DirectedEdge. Some clients using a PlanarGraph may want to + * subclass Node to add their own application-specific + * data and methods. + * + */ +class GEOS_DLL Node: public GraphComponent { +protected: + + /// The location of this Node + geom::Coordinate pt; + + /// The collection of DirectedEdges that leave this Node + DirectedEdgeStar* deStar; + +public: + + friend std::ostream& operator << (std::ostream& os, const Node&); + + /** \brief + * Returns all Edges that connect the two nodes (which are + * assumed to be different). + * + * Note: returned vector is newly allocated, ownership to + * the caller. + */ + static std::vector* getEdgesBetween(Node* node0, + Node* node1); + + /// Constructs a Node with the given location. + Node(const geom::Coordinate& newPt) + : + pt(newPt) + { + deStar = new DirectedEdgeStar(); + } + + ~Node() override + { + delete deStar; + } + + /** + * \brief + * Constructs a Node with the given location and + * collection of outgoing DirectedEdges. + * Takes ownership of the given DirectedEdgeStar!! + */ + Node(geom::Coordinate& newPt, DirectedEdgeStar* newDeStar) + : + pt(newPt), + deStar(newDeStar) + {} + + /** + * \brief Returns the location of this Node. + */ + geom::Coordinate& + getCoordinate() + { + return pt; + } + + /** + * \brief Adds an outgoing DirectedEdge to this Node. + */ + void + addOutEdge(DirectedEdge* de) + { + deStar->add(de); + } + + /** + * \brief Returns the collection of DirectedEdges that + * leave this Node. + */ + DirectedEdgeStar* + getOutEdges() + { + return deStar; + } + const DirectedEdgeStar* + getOutEdges() const + { + return deStar; + } + + /** + * \brief Returns the number of edges around this Node. + */ + size_t + getDegree() const + { + return deStar->getDegree(); + } + + /** + * \brief Returns the zero-based index of the given Edge, + * after sorting in ascending order by angle with + * the positive x-axis. + */ + int + getIndex(Edge* edge) + { + return deStar->getIndex(edge); + } + +private: + + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + +}; + +/// Print a Node +std::ostream& operator<<(std::ostream& os, const Node& n); + + +} // namespace geos::planargraph +} // namespace geos + diff --git a/Sources/geos/include/geos/planargraph/NodeMap.h b/Sources/geos/include/geos/planargraph/NodeMap.h new file mode 100644 index 0000000..aa39db0 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/NodeMap.h @@ -0,0 +1,135 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for use in container + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace planargraph { +class DirectedEdgeStar; +class DirectedEdge; +class Edge; +class Node; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \brief + * A map of Node, indexed by the coordinate of the node. + * + */ +class GEOS_DLL NodeMap { +public: + typedef std::map container; +private: + container nodeMap; +public: + /** + * \brief Constructs a NodeMap without any Nodes. + */ + NodeMap(); + + container& getNodeMap(); + + virtual ~NodeMap() = default; + + /** + * \brief + * Adds a node to the std::map, replacing any that is already + * at that location. + * @return the added node + */ + Node* add(Node* n); + + /** + * \brief + * Removes the Node at the given location, and returns it + * (or null if no Node was there). + */ + Node* remove(geom::Coordinate& pt); + + /** + * \brief + * Returns the Node at the given location, + * or null if no Node was there. + */ + Node* find(const geom::Coordinate& coord); + + /** + * \brief + * Returns an Iterator over the Nodes in this NodeMap, + * sorted in ascending order + * by angle with the positive x-axis. + */ + container::iterator + iterator() + { + return nodeMap.begin(); + } + + container::iterator + begin() + { + return nodeMap.begin(); + } + container::const_iterator + begin() const + { + return nodeMap.begin(); + } + + container::iterator + end() + { + return nodeMap.end(); + } + container::const_iterator + end() const + { + return nodeMap.end(); + } + + /** + * \brief + * Returns the Nodes in this NodeMap, sorted in ascending order + * by angle with the positive x-axis. + * + * @param nodes : the nodes are push_back'ed here + */ + void getNodes(std::vector& nodes); +}; + + +} // namespace geos::planargraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/planargraph/PlanarGraph.h b/Sources/geos/include/geos/planargraph/PlanarGraph.h new file mode 100644 index 0000000..fd3037f --- /dev/null +++ b/Sources/geos/include/geos/planargraph/PlanarGraph.h @@ -0,0 +1,293 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: planargraph/PlanarGraph.java rev. 107/138 (JTS-1.10) + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition + +#include // for typedefs + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace planargraph { +class DirectedEdge; +class Edge; +class Node; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/** + * \class PlanarGraph + * \brief + * Represents a directed graph which is embeddable in a planar surface. + * + * This class and the other classes in this package serve as a framework for + * building planar graphs for specific algorithms. This class must be + * subclassed to expose appropriate methods to construct the graph. This allows + * controlling the types of graph components (DirectedEdge, Edge and Node) + * which can be added to the graph. An application which uses the graph + * framework will almost always provide subclasses for one or more graph + * components, which hold application-specific data and graph algorithms. + */ +class GEOS_DLL PlanarGraph { + +protected: + + std::vector edges; + std::vector dirEdges; + NodeMap nodeMap; + + /** + * \brief + * Adds a node to the std::map, replacing any that is already at that + * location. + * + * Only subclasses can add Nodes, to ensure Nodes are + * of the right type. + */ + void + add(Node* node) + { + nodeMap.add(node); + } + + /** + * \brief + * Adds the Edge and its DirectedEdges with this PlanarGraph. + * + * Assumes that the Edge has already been created with its associated + * DirectEdges. + * Only subclasses can add Edges, to ensure the edges added are of + * the right class. + */ + void add(Edge* edge); + + /** + * \brief + * Adds the Edge to this PlanarGraph. + * + * Only subclasses can add DirectedEdges, + * to ensure the edges added are of the right class. + */ + void + add(DirectedEdge* dirEdge) + { + dirEdges.push_back(dirEdge); + } + +public: + + typedef std::vector EdgeContainer; + typedef EdgeContainer::iterator EdgeIterator; + + + /** + * \brief + * Constructs a PlanarGraph without any Edges, DirectedEdges, or Nodes. + */ + PlanarGraph() {} + + virtual + ~PlanarGraph() {} + + /** + * \brief + * Returns the Node at the given location, + * or null if no Node was there. + */ + Node* + findNode(const geom::Coordinate& pt) + { + return nodeMap.find(pt); + } + + /** + * \brief + * Returns an Iterator over the Nodes in this PlanarGraph. + */ + NodeMap::container::iterator + nodeIterator() + { + return nodeMap.begin(); + } + + NodeMap::container::iterator + nodeBegin() + { + return nodeMap.begin(); + } + + NodeMap::container::const_iterator + nodeBegin() const + { + return nodeMap.begin(); + } + + NodeMap::container::iterator + nodeEnd() + { + return nodeMap.end(); + } + + NodeMap::container::const_iterator + nodeEnd() const + { + return nodeMap.end(); + } + + /** + * \brief + * Returns the Nodes in this PlanarGraph. + * + * @param nodes : the nodes are push_back'ed here + */ + void + getNodes(std::vector& nodes) + { + nodeMap.getNodes(nodes); + } + + /** + * \brief + * Returns an Iterator over the DirectedEdges in this PlanarGraph, + * in the order in which they were added. + * + * @see add(Edge) + * @see add(DirectedEdge) + */ + std::vector::iterator + dirEdgeIterator() + { + return dirEdges.begin(); + } + + std::vector::iterator + dirEdgeBegin() + { + return dirEdges.begin(); + } + + std::vector::iterator + dirEdgeEnd() + { + return dirEdges.end(); + } + + /// Alias for edgeBegin() + std::vector::iterator + edgeIterator() + { + return edges.begin(); + } + + /// Returns an iterator to first Edge in this graph. + // + /// Edges are stored in the order they were added. + /// @see add(Edge) + /// + std::vector::iterator + edgeBegin() + { + return edges.begin(); + } + + /// Returns an iterator to one-past last Edge in this graph. + // + /// Edges are stored in the order they were added. + /// @see add(Edge) + /// + std::vector::iterator + edgeEnd() + { + return edges.end(); + } + + /** + * Returns the Edges that have been added to this PlanarGraph + * @see PlanarGraph::add(Edge* edge) + */ + std::vector* + getEdges() + { + return &edges; + } + + /** + * \brief + * Removes an Edge and its associated DirectedEdges from their + * from-Nodes and from this PlanarGraph. + * + * Note: This method does not remove the Nodes associated + * with the Edge, even if the removal of the Edge reduces the + * degree of a Node to zero. + */ + void remove(Edge* edge); + + /** + * \brief + * Removes DirectedEdge from its from-Node and from this PlanarGraph. + * + * Note: + * This method does not remove the Nodes associated with the + * DirectedEdge, even if the removal of the DirectedEdge reduces + * the degree of a Node to zero. + */ + void remove(DirectedEdge* de); + + /** + * \brief + * Removes a node from the graph, along with any associated + * DirectedEdges and Edges. + */ + void remove(Node* node); + + /** + * \brief + * Returns all Nodes with the given number of Edges around it. + * The return value is a newly allocated vector of existing nodes + */ + std::vector* findNodesOfDegree(std::size_t degree); + + /** + * \brief + * Get all Nodes with the given number of Edges around it. + * + * Found nodes are pushed to the given vector + */ + void findNodesOfDegree(std::size_t degree, std::vector& to); +}; + +} // namespace geos::planargraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/planargraph/Subgraph.h b/Sources/geos/include/geos/planargraph/Subgraph.h new file mode 100644 index 0000000..18ee0d5 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/Subgraph.h @@ -0,0 +1,181 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace planargraph { +class PlanarGraph; +class DirectedEdge; +class Edge; +} +} + +namespace geos { +namespace planargraph { // geos.planargraph + +/// \brief A subgraph of a PlanarGraph. +/// +/// A subgraph may contain any subset of {@link Edge}s +/// from the parent graph. +/// It will also automatically contain all {@link DirectedEdge}s +/// and {@link Node}s associated with those edges. +/// No new objects are created when edges are added - +/// all associated components must already exist in the parent graph. +/// +/// @note Actually we'll be copying Coordinates in NodeMap. +/// I guess that'll need to be changed soon. +/// +class GEOS_DLL Subgraph { +public: + /** \brief + * Creates a new subgraph of the given PlanarGraph + * + * @param parent the parent graph + */ + Subgraph(PlanarGraph& parent) + : + parentGraph(parent) + {} + + /** \brief + * Gets the PlanarGraph which this subgraph + * is part of. + * + * @return the parent PlanarGraph + */ + PlanarGraph& + getParent() const + { + return parentGraph; + } + + /** \brief + * Adds an Edge to the subgraph. + * + * The associated {@link DirectedEdge}s and {@link Node}s + * are also added. + * + * @param e the edge to add + * + * @return a pair with first element being an iterator + * to the Edge in set and second element + * being a boolean value indicating whether + * the Edge has been inserted now or was + * already in the set. + */ + std::pair::iterator, bool> add(Edge* e); + + /** \brief + * Returns an iterator over the DirectedEdge in this graph, + * in the order in which they were added. + * + * @return an iterator over the directed edges + * + * @see add(Edge) + */ + std::vector::iterator + getDirEdgeBegin() + { + return dirEdges.begin(); + } + + + /** \brief + * Returns an iterator over the {@link Edge}s in this + * graph, in the order in which they were added. + * + * @return an iterator over the edges + * + * @see add(Edge) + */ + std::set::iterator + edgeBegin() + { + return edges.begin(); + } + std::set::iterator + edgeEnd() + { + return edges.end(); + } + + /** \brief + * Returns a iterators over the planar NodeMap::container + * in this graph. + */ + NodeMap::container::iterator + nodeBegin() + { + return nodeMap.begin(); + } + NodeMap::container::const_iterator + nodeEnd() const + { + return nodeMap.end(); + } + NodeMap::container::iterator + nodeEnd() + { + return nodeMap.end(); + } + NodeMap::container::const_iterator + nodeBegin() const + { + return nodeMap.begin(); + } + + /** \brief + * Tests whether an {@link Edge} is contained in this subgraph. + * + * @param e the edge to test + * @return `true` if the edge is contained in this subgraph + */ + bool + contains(Edge* e) + { + return (edges.find(e) != edges.end()); + } + +protected: + + PlanarGraph& parentGraph; + std::set edges; + std::vector dirEdges; + NodeMap nodeMap; + + // Declare type as noncopyable + Subgraph(const Subgraph& other) = delete; + Subgraph& operator=(const Subgraph& rhs) = delete; +}; + +} // namespace geos::planargraph +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/planargraph/algorithm/ConnectedSubgraphFinder.h b/Sources/geos/include/geos/planargraph/algorithm/ConnectedSubgraphFinder.h new file mode 100644 index 0000000..bc07a39 --- /dev/null +++ b/Sources/geos/include/geos/planargraph/algorithm/ConnectedSubgraphFinder.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for inlines + +#include +#include + +// Forward declarations +namespace geos { +namespace planargraph { +class PlanarGraph; +class Subgraph; +class Node; +} +} + +namespace geos { +namespace planargraph { // geos::planargraph +namespace algorithm { // geos::planargraph::algorithm + +/** \brief + * Finds all connected {@link Subgraph}s of a PlanarGraph. + * + * Note: uses the isVisited flag on the nodes. + */ +class GEOS_DLL ConnectedSubgraphFinder { +public: + + ConnectedSubgraphFinder(PlanarGraph& newGraph) + : + graph(newGraph) + {} + + /// \brief + /// Store newly allocated connected Subgraphs into the + /// given std::vector + /// + /// Caller take responsibility in releasing memory associated + /// with the subgraphs themself. + /// + /// + void getConnectedSubgraphs(std::vector& dest); + +private: + + PlanarGraph& graph; + + /// Returns a newly allocated Subgraph + Subgraph* findSubgraph(Node* node); + + + /** + * Adds all nodes and edges reachable from this node to the subgraph. + * Uses an explicit stack to avoid a large depth of recursion. + * + * @param node a node known to be in the subgraph + */ + void addReachable(Node* node, Subgraph* subgraph); + + /** + * Adds the argument node and all its out edges to the subgraph. + * @param node the node to add + * @param nodeStack the current set of nodes being traversed + */ + void addEdges(Node* node, std::stack& nodeStack, + Subgraph* subgraph); + + // Declare type as noncopyable + ConnectedSubgraphFinder(const ConnectedSubgraphFinder& other) = delete; + ConnectedSubgraphFinder& operator=(const ConnectedSubgraphFinder& rhs) = delete; +}; + +} // namespace geos::planargraph::algorithm +} // namespace geos::planargraph +} // namespace geos + diff --git a/Sources/geos/include/geos/precision/CommonBits.h b/Sources/geos/include/geos/precision/CommonBits.h new file mode 100644 index 0000000..64145ed --- /dev/null +++ b/Sources/geos/include/geos/precision/CommonBits.h @@ -0,0 +1,97 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Determines the maximum number of common most-significant + * bits in the mantissa of one or numbers. + * + * Can be used to compute the double-precision number which + * is represented by the common bits. + * If there are no common bits, the number computed is 0.0. + * + */ +class GEOS_DLL CommonBits { + +private: + + bool isFirst; + + int commonMantissaBitsCount; + + int64_t commonBits; + + int64_t commonSignExp; + +public: + + /** \brief + * Computes the bit pattern for the sign and exponent of a + * double-precision number. + * + * @param num + * @return the bit pattern for the sign and exponent + */ + static int64_t signExpBits(int64_t num); + + /** \brief + * This computes the number of common most-significant + * bits in the mantissas of two double-precision numbers. + * + * It does not count the hidden bit, which is always 1. + * It does not determine whether the numbers have the same + * exponent - if they do not, the value computed by this + * function is meaningless. + * @param num1 + * @param num2 + * @return the number of common most-significant mantissa bits + */ + static int numCommonMostSigMantissaBits(int64_t num1, int64_t num2); + + /** \brief + * Zeroes the lower n bits of a bitstring. + * + * @param bits the bitstring to alter + * @param nBits the number of bits to zero + * @return the zeroed bitstring + */ + static int64_t zeroLowerBits(int64_t bits, int nBits); + + /** \brief + * Extracts the i'th bit of a bitstring. + * + * @param bits the bitstring to extract from + * @param i the bit to extract + * @return the value of the extracted bit + */ + static int getBit(int64_t bits, int i); + + CommonBits(); + + void add(double num); + + double getCommon(); + +}; + +} // namespace geos.precision +} // namespace geos + diff --git a/Sources/geos/include/geos/precision/CommonBitsOp.h b/Sources/geos/include/geos/precision/CommonBitsOp.h new file mode 100644 index 0000000..9216411 --- /dev/null +++ b/Sources/geos/include/geos/precision/CommonBitsOp.h @@ -0,0 +1,172 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for unique_ptr composition + +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace geom { +class Geometry; +} +namespace precision { +//class CommonBitsRemover; +} +} + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Provides versions of Geometry spatial functions which use + * common bit removal to reduce the likelihood of robustness problems. + * + * In the current implementation no rounding is performed on the + * reshifted result geometry, which means that it is possible + * that the returned Geometry is invalid. + * Client classes should check the validity of the returned result themselves. + */ +class GEOS_DLL CommonBitsOp { + +private: + + bool returnToOriginalPrecision; + + std::unique_ptr cbr; + + /** \brief + * Computes a copy of the input Geometry with the calculated + * common bits removed from each coordinate. + * + * @param geom0 the Geometry to remove common bits from + * @return a copy of the input Geometry with common bits removed + * (caller takes responsibility of its deletion) + */ + std::unique_ptr removeCommonBits(const geom::Geometry* geom0); + + /** \brief + * + */ + void removeCommonBits( + const geom::Geometry* geom0, + const geom::Geometry* geom1, + std::unique_ptr& rgeom0, + std::unique_ptr& rgeom1); + + +public: + + /** \brief + * Creates a new instance of class, which reshifts result Geometry + */ + CommonBitsOp(); + + /** \brief + * Creates a new instance of class, specifying whether + * the result {@link geom::Geometry}s should be reshifted. + * + * @param nReturnToOriginalPrecision + */ + CommonBitsOp(bool nReturnToOriginalPrecision); + + /** \brief + * Computes the set-theoretic intersection of two Geometry, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic + * intersection of the input Geometries. + */ + std::unique_ptr intersection( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** \brief + * Computes the set-theoretic union of two Geometry, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic union + * of the input Geometries. + */ + std::unique_ptr Union( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** \brief + * Computes the set-theoretic difference of two Geometry, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry, to be subtracted from the first + * @return the Geometry representing the set-theoretic difference + * of the input Geometries. + */ + std::unique_ptr difference( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** \brief + * Computes the set-theoretic symmetric difference of two geometries, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic symmetric + * difference of the input Geometries. + */ + std::unique_ptr symDifference( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** \brief + * Computes the buffer a geometry, + * using enhanced precision. + * @param geom0 the Geometry to buffer + * @param distance the buffer distance + * @return the Geometry representing the buffer of the input Geometry. + */ + std::unique_ptr buffer( + const geom::Geometry* geom0, + double distance); + + /** \brief + * If required, returning the result to the original precision + * if required. + * + * In this current implementation, no rounding is performed on the + * reshifted result geometry, which means that it is possible + * that the returned Geometry is invalid. + * + * @param result the result Geometry to modify + * @return the result Geometry with the required precision + */ + std::unique_ptr computeResultPrecision( + std::unique_ptr result); +}; + +} // namespace geos.precision +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/precision/CommonBitsRemover.h b/Sources/geos/include/geos/precision/CommonBitsRemover.h new file mode 100644 index 0000000..d14a104 --- /dev/null +++ b/Sources/geos/include/geos/precision/CommonBitsRemover.h @@ -0,0 +1,92 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +namespace precision { +class CommonBitsRemover; +class CommonCoordinateFilter; +} +} + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Allow computing and removing common mantissa bits from one or + * more Geometries. + * + */ +class GEOS_DLL CommonBitsRemover { + +private: + + geom::Coordinate commonCoord; + + CommonCoordinateFilter* ccFilter; + + CommonBitsRemover(const CommonBitsRemover&) = delete; + CommonBitsRemover& operator=(const CommonBitsRemover&) = delete; + +public: + + CommonBitsRemover(); + + ~CommonBitsRemover(); + + /** + * Add a geometry to the set of geometries whose common bits are + * being computed. After this method has executed the + * common coordinate reflects the common bits of all added + * geometries. + * + * @param geom a Geometry to test for common bits + */ + void add(const geom::Geometry* geom); + + /** + * The common bits of the Coordinates in the supplied Geometries. + */ + geom::Coordinate& getCommonCoordinate(); + + /** \brief + * Removes the common coordinate bits from a Geometry. + * The coordinates of the Geometry are changed. + * + * @param geom the Geometry from which to remove the common + * coordinate bits + */ + void removeCommonBits(geom::Geometry* geom); + + /** \brief + * Adds the common coordinate bits back into a Geometry. + * The coordinates of the Geometry are changed. + * + * @param geom the Geometry to which to add the common coordinate bits + * @return the shifted Geometry + */ + geom::Geometry* addCommonBits(geom::Geometry* geom); +}; + +} // namespace geos.precision +} // namespace geos + diff --git a/Sources/geos/include/geos/precision/EnhancedPrecisionOp.h b/Sources/geos/include/geos/precision/EnhancedPrecisionOp.h new file mode 100644 index 0000000..412b905 --- /dev/null +++ b/Sources/geos/include/geos/precision/EnhancedPrecisionOp.h @@ -0,0 +1,105 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: precision/EnhancedPrecisionOp.java rev. 1.9 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include +#include // for int64 +#include + +#include + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Provides versions of Geometry spatial functions which use + * enhanced precision techniques to reduce the likelihood of robustness + * problems. + */ +class GEOS_DLL EnhancedPrecisionOp { + +public: + + /** \brief + * Computes the set-theoretic intersection of two + * Geometrys, using enhanced precision. + * + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic + * intersection of the input Geometries. + */ + static std::unique_ptr intersection( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** + * Computes the set-theoretic union of two Geometrys, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic + * union of the input Geometries. + */ + static std::unique_ptr Union( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** + * Computes the set-theoretic difference of two Geometrys, + * using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic + * difference of the input Geometries. + */ + static std::unique_ptr difference( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** + * Computes the set-theoretic symmetric difference of two + * Geometrys, using enhanced precision. + * @param geom0 the first Geometry + * @param geom1 the second Geometry + * @return the Geometry representing the set-theoretic symmetric + * difference of the input Geometries. + */ + static std::unique_ptr symDifference( + const geom::Geometry* geom0, + const geom::Geometry* geom1); + + /** + * Computes the buffer of a Geometry, using enhanced precision. + * This method should no longer be necessary, since the buffer + * algorithm now is highly robust. + * + * @param geom the first Geometry + * @param distance the buffer distance + * @return the Geometry representing the buffer of the input Geometry. + */ + static std::unique_ptr buffer( + const geom::Geometry* geom, + double distance); +}; + + +} // namespace geos.precision +} // namespace geos + diff --git a/Sources/geos/include/geos/precision/GeometryPrecisionReducer.h b/Sources/geos/include/geos/precision/GeometryPrecisionReducer.h new file mode 100644 index 0000000..cb0cb9a --- /dev/null +++ b/Sources/geos/include/geos/precision/GeometryPrecisionReducer.h @@ -0,0 +1,202 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + *********************************************************************** + * + * Last port: precision/GeometryPrecisionReducer.cpp rev. 1.10 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include +#include // for GeometryFactory::Ptr +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class GeometryFactory; +class Geometry; +} +} + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Reduces the precision of a {@link geom::Geometry} + * according to the supplied {@link geom::PrecisionModel}, + * ensuring that the result is valid (unless specified otherwise). + * + * By default the reduced result is topologically valid + * To ensure this a polygonal geometry is reduced in a topologically valid fashion + * (technically, by using snap-rounding). + * It can be forced to be reduced pointwise by using setPointwise(boolean). + * Note that in this case the result geometry may be invalid. + * Linear and point geometry is always reduced pointwise (i.e. without further change to + * its topology or structure), since this does not change validity. + * + * By default the geometry precision model is not changed. + * This can be overridden by usingsetChangePrecisionModel(boolean). + * + * Normally collapsed components (e.g. lines collapsing to a point) + * are not included in the result. + * This behavior can be changed by using setRemoveCollapsedComponents(boolean). + */ +class GEOS_DLL GeometryPrecisionReducer { + +private: + + // Externally owned + const geom::GeometryFactory* newFactory; + + const geom::PrecisionModel& targetPM; + + bool removeCollapsed; + bool changePrecisionModel; + bool useAreaReducer; + bool isPointwise; + + std::unique_ptr fixPolygonalTopology(const geom::Geometry& geom); + + geom::GeometryFactory::Ptr createFactory( + const geom::GeometryFactory& oldGF, + const geom::PrecisionModel& newPM); + + /** + * Duplicates a geometry to one that uses a different PrecisionModel, + * without changing any coordinate values. + * + * @param geom the geometry to duplicate + * @param newPM the precision model to use + * @return the geometry value with a new precision model + */ + std::unique_ptr changePM( + const geom::Geometry* geom, + const geom::PrecisionModel& newPM); + + GeometryPrecisionReducer(GeometryPrecisionReducer const&); /*= delete*/ + GeometryPrecisionReducer& operator=(GeometryPrecisionReducer const&); /*= delete*/ + +public: + + /** + * Convenience method for doing precision reduction + * on a single geometry, + * with collapses removed + * and keeping the geometry precision model the same, + * and preserving polygonal topology. + * + * @param g the geometry to reduce + * @param precModel the precision model to use + * @return the reduced geometry + */ + static std::unique_ptr + reduce(const geom::Geometry& g, const geom::PrecisionModel& precModel); + + static std::unique_ptr + reducePointwise(const geom::Geometry& g, const geom::PrecisionModel& precModel); + + static std::unique_ptr + reduceKeepCollapsed(const geom::Geometry& g, const geom::PrecisionModel& precModel); + + GeometryPrecisionReducer(const geom::PrecisionModel& pm) + : newFactory(nullptr) + , targetPM(pm) + , removeCollapsed(true) + , changePrecisionModel(false) + , useAreaReducer(false) + , isPointwise(false) + {} + + /** + * \brief + * Create a reducer that will change the precision model of the + * new reduced Geometry + * + * @param changeFactory the factory for the created Geometry. + * Its PrecisionModel will be used for the reduction. + * NOTE: ownership left to caller must be kept alive for + * the whole lifetime of the returned Geometry. + */ + GeometryPrecisionReducer(const geom::GeometryFactory& changeFactory) + : newFactory(&changeFactory) + , targetPM(*(changeFactory.getPrecisionModel())) + , removeCollapsed(true) + , changePrecisionModel(false) + , useAreaReducer(false) + , isPointwise(false) + {} + + /** + * Sets whether the reduction will result in collapsed components + * being removed completely, or simply being collapsed to an (invalid) + * Geometry of the same type. + * + * @param remove if true collapsed components will be removed + */ + void + setRemoveCollapsedComponents(bool remove) + { + removeCollapsed = remove; + } + + /** \brief + * Sets whether the {@link geom::PrecisionModel} of the new reduced Geometry + * will be changed to be the {@link geom::PrecisionModel} supplied to + * specify the precision reduction. + * The default is to not change the precision model + * + * @param change if true the precision + * model of the created Geometry will be the + * the precisionModel supplied in the constructor. + */ + void + setChangePrecisionModel(bool change) + { + changePrecisionModel = change; + } + + void + setUseAreaReducer(bool useAR) + { + useAreaReducer = useAR; + } + + /** \brief + * Sets whether the precision reduction will be done + * in pointwise fashion only. + * + * Pointwise precision reduction reduces the precision + * of the individual coordinates only, but does + * not attempt to recreate valid topology. + * This is only relevant for geometries containing polygonal components. + * + * @param pointwise if reduction should be done pointwise only + */ + void + setPointwise(bool pointwise) + { + isPointwise = pointwise; + } + + std::unique_ptr reduce(const geom::Geometry& geom); + + + +}; + +} // namespace geos.precision +} // namespace geos + diff --git a/Sources/geos/include/geos/precision/MinimumClearance.h b/Sources/geos/include/geos/precision/MinimumClearance.h new file mode 100644 index 0000000..6bef22d --- /dev/null +++ b/Sources/geos/include/geos/precision/MinimumClearance.h @@ -0,0 +1,57 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2016 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: precision/MinimumClearance.java (f6187ee2 JTS-1.14) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { +namespace precision { + +/// Computes the Minimum Clearance of a Geometry. +class GEOS_DLL MinimumClearance { +private: + const geom::Geometry* inputGeom; + double minClearance; + std::unique_ptr minClearancePts; + + void compute(); +public: + MinimumClearance(const geom::Geometry* g); + + /** + * Gets the Minimum Clearance distance. + * + * @return the value of the minimum clearance distance + * or DBL_MAX if no Minimum Clearance distance exists + */ + double getDistance(); + + /** + * Gets a LineString containing two points + * which are at the Minimum Clearance distance. + * + * @return the value of the minimum clearance distance + * or LINESTRING EMPTY if no Minimum Clearance distance exists + */ + std::unique_ptr getLine(); +}; +} +} diff --git a/Sources/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h b/Sources/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h new file mode 100644 index 0000000..9ea0671 --- /dev/null +++ b/Sources/geos/include/geos/precision/PointwisePrecisionReducerTransformer.h @@ -0,0 +1,74 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +class CoordinateSequence; +} +} + +namespace geos { +namespace precision { // geos.precision + + +/** + * A transformer to reduce the precision of a geometry pointwise. + * + * @author mdavis + */ +class GEOS_DLL PointwisePrecisionReducerTransformer : public geom::util::GeometryTransformer { + +private: + + const geom::PrecisionModel& targetPM; + + std::unique_ptr reducePointwise( + const geom::CoordinateSequence* coordinates); + +public: + + PointwisePrecisionReducerTransformer( + const geom::PrecisionModel& p_targetPM) + : targetPM(p_targetPM) + {}; + + static std::unique_ptr reduce( + const geom::Geometry& geom, + const geom::PrecisionModel& targetPM); + + + +protected: + + std::unique_ptr transformCoordinates( + const geom::CoordinateSequence* coords, + const geom::Geometry* parent) override; + + +}; + +} // namespace geos.precision +} // namespace geos + + diff --git a/Sources/geos/include/geos/precision/PrecisionReducerTransformer.h b/Sources/geos/include/geos/precision/PrecisionReducerTransformer.h new file mode 100644 index 0000000..49d0949 --- /dev/null +++ b/Sources/geos/include/geos/precision/PrecisionReducerTransformer.h @@ -0,0 +1,95 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +} +} + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Reduces the precision of a {@link geom::Geometry} + * according to the supplied {@link geom::PrecisionModel}, without + * attempting to preserve valid topology. + * + * The topology of the resulting geometry may be invalid if + * topological collapse occurs due to coordinates being shifted. + * It is up to the client to check this and handle it if necessary. + * Collapses may not matter for some uses. An example + * is simplifying the input to the buffer algorithm. + * The buffer algorithm does not depend on the validity of the input geometry. + * + */ +class GEOS_DLL PrecisionReducerTransformer : public geom::util::GeometryTransformer { + +private: + + const geom::PrecisionModel& targetPM; + bool isRemoveCollapsed; + + std::unique_ptr reduceArea(const geom::Geometry* geom); + + void extend( + geom::CoordinateSequence& coords, + std::size_t minLength); + + +public: + + PrecisionReducerTransformer( + const geom::PrecisionModel& p_targetPM, + bool p_isRemoveCollapsed = false) + : targetPM(p_targetPM) + , isRemoveCollapsed(p_isRemoveCollapsed) + {}; + + static std::unique_ptr reduce( + const geom::Geometry& geom, + const geom::PrecisionModel& targetPM, + bool isRemoveCollapsed = false); + + +protected: + + std::unique_ptr transformCoordinates( + const geom::CoordinateSequence* coords, + const geom::Geometry* parent) override; + + std::unique_ptr transformPolygon( + const geom::Polygon* geom, + const geom::Geometry* parent) override; + + std::unique_ptr transformMultiPolygon( + const geom::MultiPolygon* geom, + const geom::Geometry* parent) override; + + +}; + +} // namespace geos.precision +} // namespace geos + + diff --git a/Sources/geos/include/geos/precision/SimpleGeometryPrecisionReducer.h b/Sources/geos/include/geos/precision/SimpleGeometryPrecisionReducer.h new file mode 100644 index 0000000..7bf0b8c --- /dev/null +++ b/Sources/geos/include/geos/precision/SimpleGeometryPrecisionReducer.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2005-2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class PrecisionModel; +class Geometry; +} +} + +namespace geos { +namespace precision { // geos.precision + +/** \brief + * Reduces the precision of a {@link geom::Geometry} + * according to the supplied {@link geom::PrecisionModel}, without + * attempting to preserve valid topology. + * + * The topology of the resulting geometry may be invalid if + * topological collapse occurs due to coordinates being shifted. + * It is up to the client to check this and handle it if necessary. + * Collapses may not matter for some uses. An example + * is simplifying the input to the buffer algorithm. + * The buffer algorithm does not depend on the validity of the input geometry. + * + */ +class GEOS_DLL SimpleGeometryPrecisionReducer { + +private: + + const geom::PrecisionModel* newPrecisionModel; + + bool removeCollapsed; + + //bool changePrecisionModel; + +public: + + SimpleGeometryPrecisionReducer(const geom::PrecisionModel* pm); + + /** + * Sets whether the reduction will result in collapsed components + * being removed completely, or simply being collapsed to an (invalid) + * Geometry of the same type. + * + * @param nRemoveCollapsed if true collapsed + * components will be removed + */ + void setRemoveCollapsedComponents(bool nRemoveCollapsed); + + /* + * Sets whether the {@link PrecisionModel} of the new reduced Geometry + * will be changed to be the {@link PrecisionModel} supplied to + * specify the reduction. The default is to not change the + * precision model + * + * @param changePrecisionModel if true the precision + * model of the created Geometry will be the + * the precisionModel supplied in the constructor. + */ + //void setChangePrecisionModel(bool nChangePrecisionModel); + + const geom::PrecisionModel* getPrecisionModel(); + + bool getRemoveCollapsed(); + std::unique_ptr reduce(const geom::Geometry* geom); +}; + +} // namespace geos.precision +} // namespace geos + diff --git a/Sources/geos/include/geos/profiler.h b/Sources/geos/include/geos/profiler.h new file mode 100644 index 0000000..7707f5a --- /dev/null +++ b/Sources/geos/include/geos/profiler.h @@ -0,0 +1,185 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef PROFILE +#define PROFILE 0 +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace util { + + +/* + * \class Profile utils.h geos.h + * + * \brief Profile statistics + */ +class GEOS_DLL Profile { +public: + using timeunit = std::chrono::microseconds; + + /** \brief Create a named profile */ + Profile(std::string name); + + /** \brief Destructor */ + ~Profile() = default; + + /** \brief start a new timer */ + void + start() + { + starttime = std::chrono::high_resolution_clock::now(); + } + + /** \brief stop current timer */ + void + stop() + { + stoptime = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(stoptime - starttime); + + timings.push_back(elapsed); + + totaltime += elapsed; + if(timings.size() == 1) { + max = min = elapsed; + } + else { + if(elapsed > max) { + max = elapsed; + } + if(elapsed < min) { + min = elapsed; + } + } + + avg = static_cast(totaltime.count()) / static_cast(timings.size()); + } + + /** \brief Return Max stored timing */ + double getMax() const; + + /** \brief Return Min stored timing */ + double getMin() const; + + /** \brief Return total timing */ + double getTot() const; + + /** \brief Return total timing */ + std::string getTotFormatted() const; + + /** \brief Return average timing */ + double getAvg() const; + + /** \brief Return number of timings */ + std::size_t getNumTimings() const; + + /** \brief Profile name */ + std::string name; + + + +private: + /* \brief current start and stop times */ + std::chrono::high_resolution_clock::time_point starttime, stoptime; + + /* \brief actual times */ + std::vector timings; + + /* \brief total time */ + timeunit totaltime; + + /* \brief max time */ + timeunit max; + + /* \brief max time */ + timeunit min; + + /* \brief avg time */ + double avg; +}; + +/* + * \class Profiler utils.h geos.h + * + * \brief Profiling class + * + */ +class GEOS_DLL Profiler { + +public: + + Profiler() = default; + ~Profiler() = default; + + Profiler(const Profiler&) = delete; + Profiler& operator=(const Profiler&) = delete; + + /** + * \brief + * Return the singleton instance of the + * profiler. + */ + static Profiler* instance(void); + + /** + * \brief + * Start timer for named task. The task is + * created if does not exist. + */ + void start(std::string name); + + /** + * \brief + * Stop timer for named task. + * Elapsed time is registered in the given task. + */ + void stop(std::string name); + + /** \brief get Profile of named task */ + Profile* get(std::string name); + + std::map> profs; +}; + + +/** \brief Return a string representing the Profile */ +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Profile&); + +/** \brief Return a string representing the Profiler */ +GEOS_DLL std::ostream& operator<< (std::ostream& os, const Profiler&); + +} // namespace geos::util +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/shape/fractal/HilbertCode.h b/Sources/geos/include/geos/shape/fractal/HilbertCode.h new file mode 100644 index 0000000..d7e8d27 --- /dev/null +++ b/Sources/geos/include/geos/shape/fractal/HilbertCode.h @@ -0,0 +1,128 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace shape { // geos.shape +namespace fractal { // geos.shape.fractal + +/** + * Encodes points as the index along finite planar Hilbert curves. + * + * The planar Hilbert Curve is a continuous space-filling curve. + * In the limit the Hilbert curve has infinitely many vertices and fills + * the space of the unit square. + * A sequence of finite approximations to the infinite Hilbert curve + * is defined by the level number. + * The finite Hilbert curve at level n H(n) contains 2^n+1 points. + * Each finite Hilbert curve defines an ordering of the + * points in the 2-dimensional range square containing the curve. + * Curves fills the range square of side 2^level. + * Curve points have ordinates in the range [0, 2^level - 1]. + * The index of a point along a Hilbert curve is called the Hilbert code. + * The code for a given point is specific to the level chosen. + * + * This implementation represents codes using 32-bit integers. + * This allows levels 0 to 16 to be handled. + * The class supports encoding points in the range of a given level curve + * and decoding the point for a given code value. + * + * The Hilbert order has the property that it tends to preserve locality. + * This means that codes which are near in value will have spatially proximate + * points. The converse is not always true - the delta between + * codes for nearby points is not always small. But the average delta + * is small enough that the Hilbert order is an effective way of linearizing space + * to support range queries. + * + * @author Martin Davis + * + * @see MortonCode + */ +class GEOS_DLL HilbertCode { + +public: + + /** + * The maximum curve level that can be represented. + */ + static constexpr int MAX_LEVEL = 16; + + static geom::Coordinate decode(uint32_t level, uint32_t i); + + static uint32_t encode(uint32_t level, uint32_t x, uint32_t y); + + /** + * The number of points in the curve for the given level. + * The number of points is 2^(2 * level). + * + * @param level the level of the curve + * @return the number of points + */ + static uint32_t levelSize(uint32_t level); + + /** + * The maximum ordinate value for points + * in the curve for the given level. + * The maximum ordinate is 2^level - 1. + * + * @param level the level of the curve + * @return the maximum ordinate value + */ + static uint32_t maxOrdinate(uint32_t level); + + /** + * The level of the finite Hilbert curve which contains at least + * the given number of points. + * + * @param numPoints the number of points required + * @return the level of the curve + */ + static uint32_t level(uint32_t numPoints); + + +private: + + static uint32_t deinterleave(uint32_t x); + + static uint32_t interleave(uint32_t x); + + static uint32_t prefixScan(uint32_t x); + + static uint32_t descan(uint32_t x); + + static void checkLevel(uint32_t level); + + +}; + + +} // namespace geos.shape.fractal +} // namespace geos.shape +} // namespace geos + + + diff --git a/Sources/geos/include/geos/shape/fractal/HilbertEncoder.h b/Sources/geos/include/geos/shape/fractal/HilbertEncoder.h new file mode 100644 index 0000000..9d270f9 --- /dev/null +++ b/Sources/geos/include/geos/shape/fractal/HilbertEncoder.h @@ -0,0 +1,96 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace shape { // geos.shape +namespace fractal { // geos.shape.fractal + + +class GEOS_DLL HilbertEncoder { + +public: + + HilbertEncoder(uint32_t p_level, geom::Envelope& extent); + uint32_t encode(const geom::Envelope* env); + static void sort(std::vector& geoms); + + template + static geom::Envelope getEnvelope(T begin, T end) { + geom::Envelope extent; + for (auto it = begin; it != end; ++it) { + const auto* g = *it; + if (extent.isNull()) + extent = *(g->getEnvelopeInternal()); + else + extent.expandToInclude(*(g->getEnvelopeInternal())); + } + + return extent; + } + + template + static void sort(T begin, T end) { + auto extent = getEnvelope(begin, end); + HilbertEncoder encoder(12, extent); + HilbertComparator hilbertCompare(encoder); + std::sort(begin, end, hilbertCompare); + } + +private: + + uint32_t level; + double minx; + double miny; + double strideX; + double strideY; + + struct HilbertComparator { + + HilbertEncoder& enc; + + HilbertComparator(HilbertEncoder& e) + : enc(e) {}; + + bool + operator()(const geom::Geometry* a, const geom::Geometry* b) + { + return enc.encode(a->getEnvelopeInternal()) > enc.encode(b->getEnvelopeInternal()); + } + }; + +}; + + +} // namespace geos.shape.fractal +} // namespace geos.shape +} // namespace geos + + + diff --git a/Sources/geos/include/geos/shape/fractal/MortonCode.h b/Sources/geos/include/geos/shape/fractal/MortonCode.h new file mode 100644 index 0000000..ad4a42e --- /dev/null +++ b/Sources/geos/include/geos/shape/fractal/MortonCode.h @@ -0,0 +1,140 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + + +#pragma once + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace shape { // geos.shape +namespace fractal { // geos.shape.fractal + + +/** + * Encodes points as the index along the planar Morton (Z-order) curve. + * + * The planar Morton (Z-order) curve is a continuous space-filling curve. + * The Morton curve defines an ordering of the + * points in the positive quadrant of the plane. + * The index of a point along the Morton curve is called the Morton code. + * + * A sequence of subsets of the Morton curve can be defined by a level number. + * Each level subset occupies a square range. + * The curve at level n M(n) contains 2^(n + 1) points. + * It fills the range square of side 2^level. + * Curve points have ordinates in the range [0, 2^level - 1]. + * The code for a given point is identical at all levels. + * The level simply determines the number of points in the curve subset + * and the size of the range square. + * + * This implementation represents codes using 32-bit integers. + * This allows levels 0 to 16 to be handled. + * The class supports encoding points + * and decoding the point for a given code value. + * + * The Morton order has the property that it tends to preserve locality. + * This means that codes which are near in value will have spatially proximate + * points. The converse is not always true - the delta between + * codes for nearby points is not always small. But the average delta + * is small enough that the Morton order is an effective way of linearizing space + * to support range queries. + * + * @author Martin Davis + * + * @see HilbertCode + */ +class GEOS_DLL MortonCode { + +public: + + /** + * The maximum curve level that can be represented. + */ + static constexpr int MAX_LEVEL = 16; + + /** + * Computes the index of the point (x,y) + * in the Morton curve ordering. + * + * @param x the x ordinate of the point + * @param y the y ordinate of the point + * @return the index of the point along the Morton curve + */ + static uint32_t encode(int x, int y); + + /** + * Computes the point on the Morton curve + * for a given index. + * + * @param index the index of the point on the curve + * @return the point on the curve + */ + static geom::Coordinate decode(uint32_t index); + + /** + * The number of points in the curve for the given level. + * The number of points is 22 * level. + * + * @param level the level of the curve + * @return the number of points + */ + static uint32_t levelSize(uint32_t level); + + /** + * The maximum ordinate value for points + * in the curve for the given level. + * The maximum ordinate is 2level - 1. + * + * @param level the level of the curve + * @return the maximum ordinate value + */ + static uint32_t maxOrdinate(uint32_t level); + + /** + * The level of the finite Morton curve which contains at least + * the given number of points. + * + * @param numPoints the number of points required + * @return the level of the curve + */ + static uint32_t level(uint32_t numPoints); + + +private: + + static void checkLevel(uint32_t level) ; + + static uint32_t interleave(uint32_t x); + + static uint32_t deinterleave(uint32_t x); + +}; + + +} // namespace geos.shape.fractal +} // namespace geos.shape +} // namespace geos + + + diff --git a/Sources/geos/include/geos/simplify/ComponentJumpChecker.h b/Sources/geos/include/geos/simplify/ComponentJumpChecker.h new file mode 100644 index 0000000..d25cde5 --- /dev/null +++ b/Sources/geos/include/geos/simplify/ComponentJumpChecker.h @@ -0,0 +1,119 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://libgeos.org + * + * Copyright (C) 2006 Refractions Research Inc. + * Copyright (C) 2023 Martin Davis + * Copyright (C) 2023 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Envelope; +class LineSegment; +} +namespace simplify { +class TaggedLineString; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +class GEOS_DLL ComponentJumpChecker { + using Coordinate = geos::geom::Coordinate; + using Envelope = geos::geom::Envelope; + using LineSegment = geos::geom::LineSegment; + +private: + + const std::vector& components; + + static bool hasJumpAtComponent( + const Coordinate& compPt, + const TaggedLineString* line, + std::size_t start, std::size_t end, + const LineSegment& seg); + + static bool hasJumpAtComponent( + const Coordinate& compPt, + const LineSegment* seg1, const LineSegment* seg2, + const LineSegment& seg); + + static std::size_t crossingCount( + const Coordinate& compPt, + const LineSegment& seg); + + static std::size_t crossingCount( + const Coordinate& compPt, + const LineSegment* seg1, const LineSegment* seg2); + + std::size_t static crossingCount( + const Coordinate& compPt, + const TaggedLineString* line, + std::size_t start, std::size_t end); + + static Envelope computeEnvelope( + const LineSegment* seg1, const LineSegment* seg2); + + static Envelope computeEnvelope( + const TaggedLineString* line, + std::size_t start, std::size_t end); + + +public: + + ComponentJumpChecker(const std::vector& taggedLines) + : components(taggedLines) + {} + + bool hasJump( + const TaggedLineString* line, + std::size_t start, std::size_t end, + const LineSegment& seg) const; + + /** + * Checks if two consecutive segments jumps a component if flattened. + * The segments are assumed to be consecutive. + * (so the seg1.p1 = seg2.p0). + * The flattening segment must be the segment between seg1.p0 and seg2.p1. + * + * @param line the line containing the section being flattened + * @param seg1 the first replaced segment + * @param seg2 the next replaced segment + * @param seg the flattening segment + * @return true if the flattened segment jumps a component + */ + bool hasJump( + const TaggedLineString* line, + const LineSegment* seg1, + const LineSegment* seg2, + const LineSegment& seg) const; + +}; + +} // namespace geos::simplify +} // namespace geos + + + + + diff --git a/Sources/geos/include/geos/simplify/DouglasPeuckerLineSimplifier.h b/Sources/geos/include/geos/simplify/DouglasPeuckerLineSimplifier.h new file mode 100644 index 0000000..c45f16f --- /dev/null +++ b/Sources/geos/include/geos/simplify/DouglasPeuckerLineSimplifier.h @@ -0,0 +1,103 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/DouglasPeuckerLineSimplifier.java rev. 1.4 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace simplify { // geos::simplify + +/** \brief + * Simplifies a linestring (sequence of points) using + * the standard Douglas-Peucker algorithm. + */ +class GEOS_DLL DouglasPeuckerLineSimplifier { + +public: + + /** \brief + * Returns a newly allocated Coordinate vector, wrapped + * into an unique_ptr + */ + static std::unique_ptr simplify( + const geom::CoordinateSequence& nPts, + double distanceTolerance, + bool preserveClosedEndpoint); + + DouglasPeuckerLineSimplifier(const geom::CoordinateSequence& nPts); + + /** \brief + * Sets the distance tolerance for the simplification. + * + * All vertices in the simplified linestring will be within this + * distance of the original linestring. + * + * @param nDistanceTolerance the approximation tolerance to use + */ + void setDistanceTolerance(double nDistanceTolerance); + + /** \brief + * Sets whether the endpoint of a closed LineString should be preserved + * + * @param preserve `true` if the endpoint should be preserved + */ + void setPreserveClosedEndpoint(bool preserve); + + /** \brief + * Returns a newly allocated Coordinate vector, wrapped + * into an unique_ptr + */ + std::unique_ptr simplify(); + +private: + + const geom::CoordinateSequence& pts; + std::vector usePt; + double distanceTolerance; + bool preserveEndpoint; + + void simplifySection(std::size_t i, std::size_t j); + + // Declare type as noncopyable + DouglasPeuckerLineSimplifier(const DouglasPeuckerLineSimplifier& other) = delete; + DouglasPeuckerLineSimplifier& operator=(const DouglasPeuckerLineSimplifier& rhs) = delete; +}; + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/simplify/DouglasPeuckerSimplifier.h b/Sources/geos/include/geos/simplify/DouglasPeuckerSimplifier.h new file mode 100644 index 0000000..97fa32c --- /dev/null +++ b/Sources/geos/include/geos/simplify/DouglasPeuckerSimplifier.h @@ -0,0 +1,84 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/DouglasPeuckerSimplifier.java rev. 1.5 (JTS-1.7) + * + **********************************************************************/ + +#pragma once + +#include +#include // for unique_ptr + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +/** \brief + * Simplifies a Geometry using the standard Douglas-Peucker algorithm. + * + * Ensures that any polygonal geometries returned are valid. + * Simple lines are not guaranteed to remain simple after simplification. + * + * Note that in general D-P does not preserve topology - + * e.g. polygons can be split, collapse to lines or disappear + * holes can be created or disappear, + * and lines can cross. + * To simplify geometry while preserving topology use TopologyPreservingSimplifier. + * (However, using D-P is significantly faster). + * + */ +class GEOS_DLL DouglasPeuckerSimplifier { + +public: + + static std::unique_ptr simplify( + const geom::Geometry* geom, + double tolerance); + + DouglasPeuckerSimplifier(const geom::Geometry* geom); + + /** \brief + * Sets the distance tolerance for the simplification. + * + * All vertices in the simplified geometry will be within this + * distance of the original geometry. + * The tolerance value must be non-negative. A tolerance value + * of zero is effectively a no-op. + * + * @param tolerance the approximation tolerance to use + */ + void setDistanceTolerance(double tolerance); + + std::unique_ptr getResultGeometry(); + + +private: + + const geom::Geometry* inputGeom; + + double distanceTolerance; +}; + + +} // namespace geos::simplify +} // namespace geos + diff --git a/Sources/geos/include/geos/simplify/LineSegmentIndex.h b/Sources/geos/include/geos/simplify/LineSegmentIndex.h new file mode 100644 index 0000000..3a8de12 --- /dev/null +++ b/Sources/geos/include/geos/simplify/LineSegmentIndex.h @@ -0,0 +1,87 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/LineSegmentIndex.java rev. 1.1 (JTS-1.7.1) + * + ********************************************************************** + * + * NOTES + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include // for unique_ptr + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class LineSegment; +} +namespace simplify { +class TaggedLineString; +} +} + +namespace geos { +namespace simplify { // geos::simplify + +class GEOS_DLL LineSegmentIndex { + +public: + + LineSegmentIndex() = default; + + ~LineSegmentIndex() = default; + + void add(const TaggedLineString& line); + + void add(const geom::LineSegment* seg); + + void remove(const geom::LineSegment* seg); + + std::vector + query(const geom::LineSegment* seg); + + +private: + + index::quadtree::Quadtree index; + + std::vector> newEnvelopes; + + /** + * Disable copy construction and assignment. Apparently needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + LineSegmentIndex(const LineSegmentIndex&) = delete; + LineSegmentIndex& operator=(const LineSegmentIndex&) = delete; +}; + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/simplify/LinkedLine.h b/Sources/geos/include/geos/simplify/LinkedLine.h new file mode 100644 index 0000000..606bb7c --- /dev/null +++ b/Sources/geos/include/geos/simplify/LinkedLine.h @@ -0,0 +1,82 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +// #include + +#include + +#include +#include + + +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +} +} + +namespace geos { +namespace simplify { // geos::simplify + +class LinkedLine +{ + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + +public: + + LinkedLine(const CoordinateSequence& pts); + + bool isRing() const; + bool isCorner(std::size_t i) const; + + std::size_t size() const; + std::size_t next(std::size_t i) const; + std::size_t prev(std::size_t i) const; + + const Coordinate& getCoordinate(std::size_t index) const; + const Coordinate& prevCoordinate(std::size_t index) const; + const Coordinate& nextCoordinate(std::size_t index) const; + + bool hasCoordinate(std::size_t index) const; + + void remove(std::size_t index); + + std::unique_ptr getCoordinates() const; + + +private: + + // Members + const CoordinateSequence& m_coord; + bool m_isRing; + std::size_t m_size; + std::vector m_next; + std::vector m_prev; + + void createNextLinks(std::size_t size); + + void createPrevLinks(std::size_t size); + + +}; // LinkedLine + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const LinkedLine& ll); + + +} // geos::simplify +} // geos diff --git a/Sources/geos/include/geos/simplify/LinkedRing.h b/Sources/geos/include/geos/simplify/LinkedRing.h new file mode 100644 index 0000000..85ea607 --- /dev/null +++ b/Sources/geos/include/geos/simplify/LinkedRing.h @@ -0,0 +1,69 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +namespace geos { +namespace simplify { // geos::simplify + + + +class LinkedRing +{ + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + + private: + + const CoordinateSequence& m_coord; + std::size_t m_size; + std::vector m_next; + std::vector m_prev; + + static std::vector createNextLinks(std::size_t size); + static std::vector createPrevLinks(std::size_t size); + + + public: + + LinkedRing(const CoordinateSequence& cs) + : m_coord(cs) + , m_size(cs.size()-1) + , m_next(createNextLinks(m_size)) + , m_prev(createPrevLinks(m_size)) + {}; + + std::size_t size() const; + std::size_t next(std::size_t i) const; + std::size_t prev(std::size_t i) const; + const Coordinate& getCoordinate(std::size_t index) const; + const Coordinate& prevCoordinate(std::size_t index) const; + const Coordinate& nextCoordinate(std::size_t index) const; + bool hasCoordinate(std::size_t index) const; + void remove(std::size_t index); + std::unique_ptr getCoordinates() const; + + +}; // LinkedRing + + +} // geos::simplify +} // geos + diff --git a/Sources/geos/include/geos/simplify/PolygonHullSimplifier.h b/Sources/geos/include/geos/simplify/PolygonHullSimplifier.h new file mode 100644 index 0000000..ac603f2 --- /dev/null +++ b/Sources/geos/include/geos/simplify/PolygonHullSimplifier.h @@ -0,0 +1,229 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +namespace geos { +namespace geom { +class GeometryFactory; +class LinearRing; +class MultiPolygon; +class Polygon; +} +namespace algorithm { +namespace hull { +class RingHullIndex; +} +} +} + +namespace geos { +namespace simplify { // geos::simplify + +/** + * Computes topology-preserving simplified hulls of polygonal geometry. + * Both outer and inner hulls can be computed. + * Outer hulls contain the input geometry and are larger in area. + * Inner hulls are contained by the input geometry and are smaller in area. + * In both the hull vertices are a subset of the input vertices. + * The hull construction attempts to minimize the area difference + * with the input geometry. + * Hulls are generally concave if the input is. + * Computed hulls are topology-preserving: + * they do not contain any self-intersections or overlaps, + * so the result polygonal geometry is valid. + * + * Polygons with holes and MultiPolygons are supported. + * The result has the same geometric type and structure as the input. + * + * The number of vertices in the computed hull is determined by a target parameter. + * Two parameters are supported: + * + * * Vertex Number fraction: the fraction of the input vertices retained in the result. + * Value 1 produces the original geometry. + * Smaller values produce less concave results. + * For outer hulls, value 0 produces the convex hull (with triangles for any holes). + * For inner hulls, value 0 produces a triangle (if no holes are present). + * + * * Area Delta ratio: the ratio of the change in area to the input area. + * Value 0 produces the original geometry. + * Larger values produce less concave results. + * + * The algorithm ensures that the result does not cause the target parameter + * to be exceeded. This allows computing outer or inner hulls + * with a small area delta ratio as an effective way of removing + * narrow gores and spikes. + * + * @author Martin Davis + * + */ +class GEOS_DLL PolygonHullSimplifier +{ + using Envelope = geos::geom::Envelope; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using LinearRing = geos::geom::LinearRing; + using MultiPolygon = geos::geom::MultiPolygon; + using Polygon = geos::geom::Polygon; + +public: + + /** + * Creates a new instance + * to compute a simplified hull of a polygonal geometry. + * An outer or inner hull is computed + * depending on the value of "isOuter". + * + * @param geom the polygonal geometry to process + * @param bOuter indicates whether to compute an outer or inner hull + */ + PolygonHullSimplifier(const Geometry* geom, bool bOuter) + : inputGeom(geom) + , geomFactory(geom->getFactory()) + , isOuter(bOuter) + , vertexNumFraction(-1.0) + , areaDeltaRatio(-1.0) + { + if (!geom->isPolygonal()) { + throw util::IllegalArgumentException("Input geometry must be polygonal"); + } + }; + + /** + * Computes a topology-preserving simplified hull of a polygonal geometry, + * with hull shape determined by a target parameter + * specifying the fraction of the input vertices retained in the result. + * Larger values compute less concave results. + * A value of 1 produces the convex hull; a value of 0 produces the original geometry. + * Either outer or inner hulls can be computed. + * + * @param geom the polygonal geometry to process + * @param isOuter indicates whether to compute an outer or inner hull + * @param vertexNumFraction the target fraction of number of input vertices in result + * @return the hull geometry + */ + static std::unique_ptr hull( + const Geometry* geom, + bool isOuter, + double vertexNumFraction); + + /** + * Computes a topology-preserving simplified hull of a polygonal geometry, + * with hull shape determined by a target parameter + * specifying the ratio of maximum difference in area to original area. + * Larger values compute less concave results. + * A value of 0 produces the original geometry. + * Either outer or inner hulls can be computed.. + * + * @param geom the polygonal geometry to process + * @param isOuter indicates whether to compute an outer or inner hull + * @param areaDeltaRatio the target ratio of area difference to original area + * @return the hull geometry + */ + static std::unique_ptr hullByAreaDelta( + const Geometry* geom, + bool isOuter, + double areaDeltaRatio); + + + /** + * Sets the target fraction of input vertices + * which are retained in the result. + * The value should be in the range [0,1]. + * + * @param p_vertexNumFraction a fraction of the number of input vertices + */ + void setVertexNumFraction(double p_vertexNumFraction); + /** + * Sets the target maximum ratio of the change in area of the result to the input area. + * The value must be 0 or greater. + * + * @param p_areaDeltaRatio a ratio of the change in area of the result + */ + void setAreaDeltaRatio(double p_areaDeltaRatio); + + /** + * Gets the result polygonal hull geometry. + * + * @return the polygonal geometry for the hull + */ + std::unique_ptr getResult(); + + + +private: + + // Members + const Geometry* inputGeom; + const GeometryFactory* geomFactory; + bool isOuter; + double vertexNumFraction; + double areaDeltaRatio; + // Allocate the RingHull* in here so they are cleaned + // up with PolygonHullSimplifier + std::vector> ringStore; + + /** + * Computes hulls for MultiPolygon elements for + * the cases where hulls might overlap. + * + * @param multiPoly the MultiPolygon to process + * @return the hull geometry + */ + std::unique_ptr computeMultiPolygonAll(const MultiPolygon* multiPoly); + std::unique_ptr computeMultiPolygonEach(const MultiPolygon* multiPoly); + std::unique_ptr computePolygon(const Polygon* poly); + + /** + * Create all ring hulls for the rings of a polygon, + * so that all are in the hull index if required. + * + * @param poly the polygon being processed + * @param hullIndex the hull index if present, or null + * @return the list of ring hulls + */ + std::vector initPolygon(const Polygon* poly, + RingHullIndex& hullIndex); + + double ringArea(const Polygon* poly) const; + + RingHull* createRingHull( + const LinearRing* ring, + bool isOuter, + double areaTotal, + RingHullIndex& hullIndex); + + std::unique_ptr polygonHull( + const Polygon* poly, + std::vector& ringHulls, + RingHullIndex& hullIndex) const; + + /** + * Disable copy construction and assignment. Needed to make this + * class compile under MSVC. (See https://stackoverflow.com/q/29565299) + */ + PolygonHullSimplifier(const PolygonHullSimplifier&) = delete; + PolygonHullSimplifier& operator=(const PolygonHullSimplifier&) = delete; + +}; // PolygonHullSimplifier + + +} // geos::simplify +} // geos + diff --git a/Sources/geos/include/geos/simplify/RingHull.h b/Sources/geos/include/geos/simplify/RingHull.h new file mode 100644 index 0000000..d22bb91 --- /dev/null +++ b/Sources/geos/include/geos/simplify/RingHull.h @@ -0,0 +1,208 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace geos { +namespace geom { +class Envelope; +class LinearRing; +class LineString; +class Polygon; +} +namespace simplify { +class RingHullIndex; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +class RingHull +{ + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Envelope = geos::geom::Envelope; + using LinearRing = geos::geom::LinearRing; + using LineString = geos::geom::LineString; + using Polygon = geos::geom::Polygon; + using VertexSequencePackedRtree = geos::index::VertexSequencePackedRtree; + + +public: + + /* + * Creates a new instance. + * + * @param p_ring the ring vertices to process + * @param p_isOuter whether the hull is outer or inner + */ + RingHull(const LinearRing* p_ring, bool p_isOuter); + + void setMinVertexNum(std::size_t minVertexNum); + + void setMaxAreaDelta(double maxAreaDelta); + + const Envelope* getEnvelope() const; + + std::unique_ptr getHull(RingHullIndex& hullIndex); + + static bool isConvex(const LinkedRing& vertexRing, std::size_t index); + + static double area(const LinkedRing& vertexRing, std::size_t index); + + void compute(RingHullIndex& hullIndex); + + std::unique_ptr toGeometry() const; + + +private: + + + class Corner + { + + private: + + std::size_t index; + std::size_t prev; + std::size_t next; + double area; + + public: + + Corner(std::size_t p_idx, std::size_t p_prev, std::size_t p_next, double p_area) + : index(p_idx) + , prev(p_prev) + , next(p_next) + , area(p_area) + {}; + + inline int compareTo(const Corner& rhs) const { + if (area == rhs.getArea()) { + if (index == rhs.getIndex()) + return 0; + else return index < rhs.getIndex() ? -1 : 1; + } + else return area < rhs.getArea() ? -1 : 1; + } + + inline bool operator< (const Corner& rhs) const { + return compareTo(rhs) < 0; + }; + + inline bool operator> (const Corner& rhs) const { + return compareTo(rhs) > 0; + }; + + inline bool operator==(const Corner& rhs) const { + return compareTo(rhs) == 0; + }; + + bool isVertex(std::size_t p_index) const; + std::size_t getIndex() const; + double getArea() const; + void envelope(const LinkedRing& ring, Envelope& env) const; + bool intersects(const Coordinate& v, const LinkedRing& ring) const; + bool isRemoved(const LinkedRing& ring) const; + std::unique_ptr toLineString(const LinkedRing& ring); + + struct Greater { + inline bool operator()(const Corner & a, const Corner & b) const { + return a > b; + } + }; + + using PriorityQueue = std::priority_queue, Corner::Greater>; + + }; // class Corner + + + + const LinearRing* inputRing; + double targetVertexNum = -1.0; + double targetAreaDelta = -1.0; + + /** + * The polygon vertices are provided in CW orientation. + * Thus for convex interior angles + * the vertices forming the angle are in CW orientation. + */ + std::unique_ptr vertex; + std::unique_ptr vertexRing; + double areaDelta = 0; + + /** + * Indexing vertices improves corner intersection testing performance. + * The ring vertices are contiguous, so are suitable for a + * {@link VertexSequencePackedRtree}. + */ + std::unique_ptr vertexIndex; + + Corner::PriorityQueue cornerQueue; + + + void init(CoordinateSequence& ring, bool isOuter); + void addCorner(std::size_t i, Corner::PriorityQueue& cornerQueue); + bool isAtTarget(const Corner& corner); + + /** + * Removes a corner by removing the apex vertex from the ring. + * Two new corners are created with apexes + * at the other vertices of the corner + * (if they are non-convex and thus removable). + * + * @param corner the corner to remove + * @param cornerQueue the corner queue + */ + void removeCorner(const Corner& corner, Corner::PriorityQueue& cornerQueue); + bool isRemovable(const Corner& corner, const RingHullIndex& hullIndex) const; + + /** + * Tests if any vertices in a hull intersect the corner triangle. + * Uses the vertex spatial index for efficiency. + * + * @param corner the corner vertices + * @param cornerEnv the envelope of the corner + * @param hull the hull to test + * @return true if there is an intersecting vertex + */ + bool hasIntersectingVertex( + const Corner& corner, + const Envelope& cornerEnv, + const RingHull* hull) const; + + const Coordinate& getCoordinate(std::size_t index) const; + + void query( + const Envelope& cornerEnv, + std::vector& result) const; + + void queryHull(const Envelope& queryEnv, std::vector& pts); + + + + +}; // RingHull + + +} // geos::simplify +} // geos diff --git a/Sources/geos/include/geos/simplify/RingHullIndex.h b/Sources/geos/include/geos/simplify/RingHullIndex.h new file mode 100644 index 0000000..26f86af --- /dev/null +++ b/Sources/geos/include/geos/simplify/RingHullIndex.h @@ -0,0 +1,55 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + + +namespace geos { +namespace geom { +class Envelope; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +class RingHullIndex +{ + private: + + std::vector hulls; + bool m_enabled; + + public: + + RingHullIndex() + : m_enabled(true) + {}; + + bool enabled(void) const { return m_enabled; }; + void enabled(bool p_enabled) { m_enabled = p_enabled; }; + void add(const RingHull* ringHull); + std::vector query(const geos::geom::Envelope& queryEnv) const; + std::size_t size() const; + + +}; // RingHullIndex + + +} // geos::simplify +} // geos + diff --git a/Sources/geos/include/geos/simplify/TaggedLineSegment.h b/Sources/geos/include/geos/simplify/TaggedLineSegment.h new file mode 100644 index 0000000..98b341c --- /dev/null +++ b/Sources/geos/include/geos/simplify/TaggedLineSegment.h @@ -0,0 +1,83 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/TaggedLineSegment.java rev. 1.1 (JTS-1.7) + * + ********************************************************************** + * + * NOTES: Use of this class by DP simplification algorithms + * makes it useless for a TaggedLineSegment to store copies + * of coordinates. Using pointers would be good enough here. + * We don't do it to avoid having to break inheritance from + * LineSegment, which has copies instead. Whether LineSegment + * itself should be refactored can be discussed. + * --strk 2006-04-12 + * + **********************************************************************/ + +#pragma once + +#include +#include // for inheritance + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Geometry; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +/** \brief + * A geom::LineSegment which is tagged with its location in a geom::Geometry. + * + * Used to index the segments in a geometry and recover the segment locations + * from the index. + */ +class GEOS_DLL TaggedLineSegment: public geom::LineSegment { + +public: + + TaggedLineSegment(const geom::Coordinate& p0, + const geom::Coordinate& p1, + const geom::Geometry* parent, + std::size_t index); + + TaggedLineSegment(const geom::Coordinate& p0, + const geom::Coordinate& p1); + + TaggedLineSegment(const TaggedLineSegment& ls); + + const geom::Geometry* getParent() const; + + std::size_t getIndex() const; + +private: + + const geom::Geometry* parent; + + std::size_t index; + +}; + + + +} // namespace geos::simplify +} // namespace geos + diff --git a/Sources/geos/include/geos/simplify/TaggedLineString.h b/Sources/geos/include/geos/simplify/TaggedLineString.h new file mode 100644 index 0000000..a3f962b --- /dev/null +++ b/Sources/geos/include/geos/simplify/TaggedLineString.h @@ -0,0 +1,144 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/TaggedLineString.java rev. 1.2 (JTS-1.7.1) + * + ********************************************************************** + * + * NOTES: This class can be optimized to work with vector + * rather then with CoordinateSequence. Also, LineSegment should + * be replaced with a class not copying Coordinates. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +class Geometry; +class LineString; +class LinearRing; +} +namespace simplify { +class TaggedLineSegment; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +/** \brief + * Contains and owns a list of TaggedLineSegments. + */ +class GEOS_DLL TaggedLineString { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + +public: + + typedef std::vector CoordVect; + + typedef std::unique_ptr CoordVectPtr; + + typedef CoordinateSequence CoordSeq; + + typedef std::unique_ptr CoordSeqPtr; + + TaggedLineString(const geom::LineString* nParentLine, + std::size_t minimumSize, + bool bIsRing); + + ~TaggedLineString(); + + std::size_t getMinimumSize() const; + + bool isRing() const; + + const geom::LineString* getParent() const; + + const CoordSeq* getParentCoordinates() const; + + CoordSeqPtr getResultCoordinates() const; + + const Coordinate& getCoordinate(std::size_t i) const; + + std::size_t size() const; + + const Coordinate& getComponentPoint() const; + + std::size_t getResultSize() const; + + TaggedLineSegment* getSegment(std::size_t i); + + const TaggedLineSegment* getSegment(std::size_t i) const; + + std::vector& getSegments(); + + const std::vector& getSegments() const; + + const std::vector& getResultSegments() const; + + void addToResult(std::unique_ptr seg); + + const TaggedLineSegment* removeRingEndpoint(); + + std::unique_ptr asLineString() const; + + std::unique_ptr asLinearRing() const; + +private: + + const geom::LineString* parentLine; + + // TaggedLineSegments owned by this object + std::vector segs; + + // TaggedLineSegments owned by this object + std::vector resultSegs; + + std::size_t minimumSize; + + bool m_isRing; + + void init(); + + static std::unique_ptr extractCoordinates( + const std::vector& segs); + + // Copying is turned off + TaggedLineString(const TaggedLineString&); + TaggedLineString& operator= (const TaggedLineString&); + +}; + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/simplify/TaggedLineStringSimplifier.h b/Sources/geos/include/geos/simplify/TaggedLineStringSimplifier.h new file mode 100644 index 0000000..495a126 --- /dev/null +++ b/Sources/geos/include/geos/simplify/TaggedLineStringSimplifier.h @@ -0,0 +1,176 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/TaggedLineStringSimplifier.java r536 (JTS-1.12+) + * + ********************************************************************** + * + * NOTES: This class can be optimized to work with vector + * rather then with CoordinateSequence + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace algorithm { +class LineIntersector; +} +namespace geom { +class CoordinateSequence; +class Coordinate; +class LineSegment; +} +namespace simplify { +class TaggedLineSegment; +class TaggedLineString; +class LineSegmentIndex; +class ComponentJumpChecker; +} +} + +namespace geos { +namespace simplify { // geos::simplify + + +/** \brief + * Simplifies a TaggedLineString, preserving topology + * (in the sense that no new intersections are introduced). + * Uses the recursive Douglas-Peucker algorithm. + * + */ +class GEOS_DLL TaggedLineStringSimplifier { + using CoordinateSequence = geos::geom::CoordinateSequence; + using Coordinate = geos::geom::Coordinate; + using LineSegment = geos::geom::LineSegment; + +public: + + TaggedLineStringSimplifier(LineSegmentIndex* inputIndex, + LineSegmentIndex* outputIndex, + const ComponentJumpChecker* jumpChecker); + + /** + * Simplifies the given {@link TaggedLineString} + * using the distance tolerance specified. + * + * @param line the linestring to simplify + * @param distanceTolerance simplification tolerance + */ + void simplify(TaggedLineString* line, double distanceTolerance); + + +private: + + // externally owned + LineSegmentIndex* inputIndex; + + // externally owned + LineSegmentIndex* outputIndex; + + const ComponentJumpChecker* jumpChecker; + + std::unique_ptr li; + + /// non-const as segments are possibly added to it + TaggedLineString* line; + + const CoordinateSequence* linePts; + + void simplifySection(std::size_t i, std::size_t j, std::size_t depth, double distanceTolerance); + + void simplifyRingEndpoint(double distanceTolerance); + + static std::size_t findFurthestPoint( + const CoordinateSequence* pts, + std::size_t i, std::size_t j, + double& maxDistance); + + bool isTopologyValid( + const TaggedLineString* lineIn, + std::size_t sectionStart, std::size_t sectionEnd, + const LineSegment& flatSeg); + + bool isTopologyValid( + const TaggedLineString* lineIn, + const LineSegment* seg1, const LineSegment* seg2, + const LineSegment& flatSeg); + + bool hasInputIntersection(const LineSegment& flatSeg); + + bool hasInputIntersection( + const TaggedLineString* lineIn, + std::size_t excludeStart, std::size_t excludeEnd, + const LineSegment& flatSeg); + + bool isCollinear(const Coordinate& pt, const LineSegment& seg) const; + + bool hasOutputIntersection(const LineSegment& flatSeg); + + bool hasInvalidIntersection( + const LineSegment& seg0, + const LineSegment& seg1) const; + + + std::unique_ptr flatten( + std::size_t start, std::size_t end); + + /** \brief + * Tests whether a segment is in a section of a TaggedLineString. + * Sections may wrap around the endpoint of the line, + * to support ring endpoint simplification. + * This is indicated by excludedStart > excludedEnd + * + * @param line line to be checked for the presence of `seg` + * @param excludeStart the index of the first segment in the excluded section + * @param excludeEnd the index of the last segment in the excluded section + * @param seg segment to look for in `line` + * @return true if the test segment intersects some segment in the line not in the excluded section + */ + static bool isInLineSection( + const TaggedLineString* line, + const std::size_t excludeStart, const std::size_t excludeEnd, + const TaggedLineSegment* seg); + + /** \brief + * Remove the segs in the section of the line + * + * @param line + * @param start + * @param end + */ + void remove(const TaggedLineString* line, + std::size_t start, + std::size_t end); + +}; + + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/simplify/TaggedLinesSimplifier.h b/Sources/geos/include/geos/simplify/TaggedLinesSimplifier.h new file mode 100644 index 0000000..12e8baa --- /dev/null +++ b/Sources/geos/include/geos/simplify/TaggedLinesSimplifier.h @@ -0,0 +1,88 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/TaggedLinesSimplifier.java rev. 1.4 (JTS-1.7.1) + * + ********************************************************************** + * + * NOTES: changed from JTS design adding a private + * TaggedLineStringSimplifier member and making + * simplify(collection) method become a templated + * function. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include // for templated function body +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace simplify { +class TaggedLineString; +} +} + +namespace geos { +namespace simplify { // geos::simplify + +/** \brief + * Simplifies a collection of TaggedLineStrings, preserving topology + * (in the sense that no new intersections are introduced). + */ +class GEOS_DLL TaggedLinesSimplifier { + +public: + + TaggedLinesSimplifier(); + + /** \brief + * Sets the distance tolerance for the simplification. + * + * All vertices in the simplified geometry will be within this + * distance of the original geometry. + * + * @param tolerance the approximation tolerance to use + */ + void setDistanceTolerance(double tolerance); + + void simplify(std::vector& tlsVector); + +private: + + std::unique_ptr inputIndex; + + std::unique_ptr outputIndex; + + double distanceTolerance; +}; + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/simplify/TopologyPreservingSimplifier.h b/Sources/geos/include/geos/simplify/TopologyPreservingSimplifier.h new file mode 100644 index 0000000..3277789 --- /dev/null +++ b/Sources/geos/include/geos/simplify/TopologyPreservingSimplifier.h @@ -0,0 +1,93 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: simplify/TopologyPreservingSimplifier.java r536 (JTS-1.12+) + * + ********************************************************************** + * + * NOTES: + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include // for unique_ptr +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace simplify { // geos::simplify + +/** \brief + * Simplifies a geometry, ensuring that + * the result is a valid geometry having the + * same dimension and number of components as the input. + * + * The simplification uses a maximum distance difference algorithm + * similar to the one used in the Douglas-Peucker algorithm. + * + * In particular, if the input is an areal geometry + * ( Polygon or MultiPolygon ) + * + * - The result has the same number of shells and holes (rings) as the input, + * in the same order + * - The result rings touch at no more than the number of touching point in the input + * (although they may touch at fewer points) + * + */ +class GEOS_DLL TopologyPreservingSimplifier { + +public: + + static std::unique_ptr simplify( + const geom::Geometry* geom, + double tolerance); + + TopologyPreservingSimplifier(const geom::Geometry* geom); + + /** \brief + * Sets the distance tolerance for the simplification. + * + * All vertices in the simplified geometry will be within this + * distance of the original geometry. + * The tolerance value must be non-negative. A tolerance value + * of zero is effectively a no-op. + * + * @param tolerance the approximation tolerance to use + */ + void setDistanceTolerance(double tolerance); + + std::unique_ptr getResultGeometry(); + +private: + + const geom::Geometry* inputGeom; + + std::unique_ptr lineSimplifier; + +}; + +} // namespace geos::simplify +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/Sources/geos/include/geos/triangulate/DelaunayTriangulationBuilder.h b/Sources/geos/include/geos/triangulate/DelaunayTriangulationBuilder.h new file mode 100644 index 0000000..50f07a2 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/DelaunayTriangulationBuilder.h @@ -0,0 +1,166 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/DelaunayTriangulationBuilder.java r524 + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace geom { +class Geometry; +class MultiLineString; +class GeometryCollection; +class GeometryFactory; +class Envelope; +} +namespace triangulate { +namespace quadedge { +class QuadEdgeSubdivision; +} +} +} + +namespace geos { +namespace triangulate { //geos.triangulate + + +/** \brief + * A utility class which creates Delaunay Triangulations + * from collections of points and extract the resulting + * triangulation edges or triangles as geometries. + * + * @author JTS: Martin Davis + * @author Benjamin Campbell + * + */ +class GEOS_DLL DelaunayTriangulationBuilder { +public: + /** + * Extracts the unique {@link geom::Coordinate}s from the given + * {@link geom::Geometry}. + * + * @param geom the geometry to extract from + * @return a List of the unique Coordinates. Caller takes ownership of the returned object. + */ + static std::unique_ptr extractUniqueCoordinates(const geom::Geometry& geom); + + /** + * Converts all {@link geom::Coordinate}s in a collection to + * {@link quadedge::Vertex}es. + * + * @param coords the coordinates to convert + * @return a List of Vertex objects. + */ + static IncrementalDelaunayTriangulator::VertexList toVertices(const geom::CoordinateSequence& coords); + + /** + * Returns a CoordinateSequence containing only the unique coordinates of its input. + * @param seq a coordinateSequence + * @return a sorted CoordinateSequence with the unique points of seq. + */ + static std::unique_ptr unique(const geom::CoordinateSequence* seq); + +private: + std::unique_ptr siteCoords; + double tolerance; + std::unique_ptr subdiv; + +public: + /** + * Creates a new triangulation builder. + * + */ + DelaunayTriangulationBuilder(); + + ~DelaunayTriangulationBuilder() = default; + + /** + * Sets the sites (vertices) which will be triangulated. + * All vertices of the given geometry will be used as sites. + * + * @param geom the geometry from which the sites will be extracted. + */ + void setSites(const geom::Geometry& geom); + + /** + * Sets the sites (vertices) which will be triangulated + * from a collection of {@link geom::Coordinate}s. + * + * @param coords a CoordinateSequence. + */ + void setSites(const geom::CoordinateSequence& coords); + + /** + * Sets the snapping tolerance which will be used + * to improved the robustness of the triangulation computation. + * A tolerance of 0.0 specifies that no snapping will take place. + * + * @param p_tolerance the tolerance distance to use + */ + inline void + setTolerance(double p_tolerance) + { + this->tolerance = p_tolerance; + } + +private: + void create(); + +public: + /** + * Gets the {@link quadedge::QuadEdgeSubdivision} which models the computed triangulation. + * + * @return the subdivision containing the triangulation + */ + quadedge::QuadEdgeSubdivision& getSubdivision(); + + /** + * Gets the edges of the computed triangulation as a {@link geom::MultiLineString}. + * + * @param geomFact the geometry factory to use to create the output + * @return the edges of the triangulation. The caller takes ownership of the returned object. + */ + std::unique_ptr getEdges(const geom::GeometryFactory& geomFact); + + /** + * Gets the faces of the computed triangulation as a {@link geom::GeometryCollection} + * of {@link geom::Polygon}. + * + * @param geomFact the geometry factory to use to create the output + * @return the faces of the triangulation. The caller takes ownership of the returned object. + */ + std::unique_ptr getTriangles(const geom::GeometryFactory& geomFact); + + /** + * Computes the {@link geom::Envelope} of a collection of + * {@link geom::Coordinate}s. + * + * @param coords a List of Coordinates + * @return the envelope of the set of coordinates + */ + + static geom::Envelope envelope(const geom::CoordinateSequence& coords); + +}; + +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/IncrementalDelaunayTriangulator.h b/Sources/geos/include/geos/triangulate/IncrementalDelaunayTriangulator.h new file mode 100644 index 0000000..0d22412 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/IncrementalDelaunayTriangulator.h @@ -0,0 +1,106 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/IncrementalDelaunayTriangulator.java r524 + * + **********************************************************************/ + +#pragma once + +#include + +#include + + +namespace geos { +namespace triangulate { //geos.triangulate + +namespace quadedge { +class QuadEdge; +class QuadEdgeSubdivision; +} + +/** \brief + * Computes a Delauanay Triangulation of a set of {@link quadedge::Vertex}es, + * using an incrementatal insertion algorithm. + * + * @author JTS: Martin Davis + * @author Benjamin Campbell + */ +class GEOS_DLL IncrementalDelaunayTriangulator { +private: + quadedge::QuadEdgeSubdivision* subdiv; + bool isUsingTolerance; + bool m_isForceConvex; + +public: + /** + * Creates a new triangulator using the given {@link quadedge::QuadEdgeSubdivision}. + * The triangulator uses the tolerance of the supplied subdivision. + * + * @param subdiv + * a subdivision in which to build the TIN + */ + IncrementalDelaunayTriangulator(quadedge::QuadEdgeSubdivision* subdiv); + + typedef std::vector VertexList; + + /** + * Sets whether the triangulation is forced to have a convex boundary. Because + * of the use of a finite-size frame, this condition requires special logic to + * enforce. The default is true, since this is a requirement for some uses of + * Delaunay Triangulations (such as Concave Hull generation). However, forcing + * the triangulation boundary to be convex may cause the overall frame + * triangulation to be non-Delaunay. This can cause a problem for Voronoi + * generation, so the logic can be disabled via this method. + * + * @param isForceConvex true if the triangulation boundary is forced to be convex + */ + void forceConvex(bool isForceConvex); + + /** + * Inserts all sites in a collection. The inserted vertices MUST be + * unique up to the provided tolerance value. (i.e. no two vertices should be + * closer than the provided tolerance value). They do not have to be rounded + * to the tolerance grid, however. + * + * @param vertices a Collection of Vertex + * + * @throws LocateFailureException if the location algorithm + * fails to converge in a reasonable number of iterations + */ + void insertSites(const VertexList& vertices); + + /** + * Inserts a new point into a subdivision representing a Delaunay + * triangulation, and fixes the affected edges so that the result + * is still a Delaunay triangulation. + *

+ * + * @return a quadedge containing the inserted vertex + */ + quadedge::QuadEdge& insertSite(const quadedge::Vertex& v); + +private: + + bool isConcaveBoundary(const quadedge::QuadEdge& e) const; + + bool isConcaveAtOrigin(const quadedge::QuadEdge& e) const; + + bool isBetweenFrameAndInserted(const quadedge::QuadEdge& e, const quadedge::Vertex& vInsert) const; +}; + +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/VoronoiDiagramBuilder.h b/Sources/geos/include/geos/triangulate/VoronoiDiagramBuilder.h new file mode 100644 index 0000000..ac6eb38 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/VoronoiDiagramBuilder.h @@ -0,0 +1,157 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/VoronoiDiagramBuilder.java r524 + * + **********************************************************************/ + +#pragma once + +#include +#include // for composition +#include +#include +#include + +namespace geos { +namespace geom { +class Geometry; +class CoordinateSequence; +class GeometryCollection; +class GeometryFactory; +} +namespace triangulate { //geos.triangulate + +/** \brief + * A utility class which creates Voronoi Diagrams from collections of points. + * + * The diagram is returned as a geom::GeometryCollection of {@link geom::Polygon}s, + * clipped to the larger of a supplied envelope or to an envelope determined + * by the input sites. + * + * @author Martin Davis + * + */ +class GEOS_DLL VoronoiDiagramBuilder { +public: + /** \brief + * Creates a new Voronoi diagram builder. + * + */ + VoronoiDiagramBuilder(); + + ~VoronoiDiagramBuilder() = default; + + /** \brief + * Sets the sites (point or vertices) which will be diagrammed. + * All vertices of the given geometry will be used as sites. + * + * @param geom the geometry from which the sites will be extracted. + */ + void setSites(const geom::Geometry& geom); + + /** \brief + * Sets the sites (point or vertices) which will be diagrammed + * from a collection of {@link geom::Coordinate}s. + * + * @param coords a collection of Coordinates. + */ + void setSites(const geom::CoordinateSequence& coords); + + /** \brief + * Specify whether the geometries in the generated diagram should + * reflect the order of coordinates in the input. If the generated + * diagram cannot be consistent with the input coordinate order + * (e.g., for repeated input points that become a single cell) an + * exception will be thrown. + * + * @param isOrdered should the geometries reflect the input order? + */ + void setOrdered(bool isOrdered); + + /** \brief + * Sets the envelope to clip the diagram to. + * + * The diagram will be clipped to the larger + * of this envelope or an envelope surrounding the sites. + * + * @param clipEnv the clip envelope; must be kept alive by + * caller until done with this instance; + * set to 0 for no clipping. + */ + void setClipEnvelope(const geom::Envelope* clipEnv); + + /** \brief + * Sets the snapping tolerance which will be used + * to improved the robustness of the triangulation computation. + * + * A tolerance of 0.0 specifies that no snapping will take place. + * + * @param tolerance the tolerance distance to use + */ + void setTolerance(double tolerance); + + /** \brief + * Gets the quadedge::QuadEdgeSubdivision which models the computed diagram. + * + * @return the subdivision containing the triangulation + */ + std::unique_ptr getSubdivision(); + + /** \brief + * Gets the faces of the computed diagram as a {@link geom::GeometryCollection} + * of {@link geom::Polygon}s, clipped as specified. + * + * @param geomFact the geometry factory to use to create the output + * @return the faces of the diagram + */ + std::unique_ptr getDiagram(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets the edges of the computed diagram as a {@link geom::MultiLineString}, + * clipped as specified. + * + * @param geomFact the geometry factory to use to create the output + * @return the edges of the diagram + */ + std::unique_ptr getDiagramEdges(const geom::GeometryFactory& geomFact); + + void reorderCellsToInput(std::vector> & polys) const; + +private: + using CoordinateCellMap = std::unordered_map, geom::Coordinate::HashCode>; + + std::unique_ptr siteCoords; + double tolerance; + std::unique_ptr subdiv; + const geom::Envelope* clipEnv; // externally owned + const geom::Geometry* inputGeom; + const geom::CoordinateSequence* inputSeq; + geom::Envelope diagramEnv; + bool isOrdered; + + void create(); + + std::size_t getNumInputPoints() const; + + static std::unique_ptr + clipGeometryCollection(std::vector> & geoms, const geom::Envelope& clipEnv); + + + static void addCellsForCoordinates(CoordinateCellMap& cellMap, const geom::Geometry& g, std::vector> & polys); + static void addCellsForCoordinates(CoordinateCellMap& cellMap, const geom::CoordinateSequence& g, std::vector> & polys); +}; + +} //namespace geos.triangulate +} //namespace geos diff --git a/Sources/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h b/Sources/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h new file mode 100644 index 0000000..f03c97c --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/ConstrainedDelaunayTriangulator.h @@ -0,0 +1,100 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class Polygon; +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + +/** + * Computes the Constrained Delaunay Triangulation of polygons. + * The Constrained Delaunay Triangulation of a polygon is a set of triangles + * covering the polygon, with the maximum total interior angle over all + * possible triangulations. It provides the "best quality" triangulation + * of the polygon. + *

+ * Holes are supported. + */ +class GEOS_DLL ConstrainedDelaunayTriangulator { + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Polygon = geos::geom::Polygon; + template + using TriList = geos::triangulate::tri::TriList; + using Tri = geos::triangulate::tri::Tri; + +private: + + // Members + const Geometry* inputGeom; + const GeometryFactory* geomFact; + + std::unique_ptr compute() const; + + static std::unique_ptr toGeometry( + const geom::GeometryFactory* geomFact, + const std::vector>>& allTriLists); + + +public: + + /** + * Constructs a new triangulator. + * + * @param p_inputGeom the input geometry + */ + ConstrainedDelaunayTriangulator(const Geometry* p_inputGeom) + : inputGeom(p_inputGeom) + , geomFact(p_inputGeom->getFactory()) + {} + + /** + * Computes the Constrained Delaunay Triangulation of each polygon element in a geometry. + * + * @param geom the input geometry + * @return a GeometryCollection of the computed triangle polygons + */ + static std::unique_ptr triangulate(const Geometry* geom); + + /** + * Computes the triangulation of a single polygon + * and returns it as a list of {@link geos::triangulate::tri::Tri}s. + * + * @param poly the input polygon + * @param triList the list to store the triangulation in + */ + static void triangulatePolygon(const Polygon* poly, TriList& triList); + +}; + + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/polygon/PolygonEarClipper.h b/Sources/geos/include/geos/triangulate/polygon/PolygonEarClipper.h new file mode 100644 index 0000000..1eacca8 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/PolygonEarClipper.h @@ -0,0 +1,219 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Polygon; +class Envelope; +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + +/** + * Triangulates a polygon using the Ear-Clipping technique. + * The polygon is provided as a closed list of contiguous vertices + * defining its boundary. + * The vertices must have clockwise orientation. + * + * The polygon boundary must not self-cross, + * but may self-touch at points or along an edge. + * It may contain repeated points, which are treated as a single vertex. + * By default every vertex is triangulated, + * including ones which are "flat" (the adjacent segments are collinear). + * These can be removed by setting setSkipFlatCorners(boolean) + * + * The polygon representation does not allow holes. + * Polygons with holes can be triangulated by preparing them + * with {@link PolygonHoleJoiner}. + * + * @author Martin Davis + * + */ +class GEOS_DLL PolygonEarClipper { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Polygon = geos::geom::Polygon; + using Envelope = geos::geom::Envelope; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + using VertexSequencePackedRtree = geos::index::VertexSequencePackedRtree; + +private: + + // Members + + bool isFlatCornersSkipped = false; + + /** + * The polygon vertices are provided in CW orientation. + * Thus for convex interior angles + * the vertices forming the angle are in CW orientation. + */ + const CoordinateSequence& vertex; + std::vector vertexNext; + std::size_t vertexSize; + + // first available vertex index + std::size_t vertexFirst; + + // indices for current corner + std::array cornerIndex; + + /** + * Indexing vertices improves ear intersection testing performance a lot. + * The polyShell vertices are contiguous, so are suitable for an SPRtree. + */ + VertexSequencePackedRtree vertexCoordIndex; + + // Methods + + std::vector createNextLinks(std::size_t size) const; + + bool isValidEar(std::size_t cornerIndex, const std::array& corner); + + /** + * Finds another vertex intersecting the corner triangle, if any. + * Uses the vertex spatial index for efficiency. + * + * Also finds any vertex which is a duplicate of the corner apex vertex, + * which then requires a full scan of the vertices to confirm ear is valid. + * This is usually a rare situation, so has little impact on performance. + * + * @param cornerIndex the index of the corner apex vertex + * @param corner the corner vertices + * @return the index of an intersecting or duplicate vertex, or NO_COORD_INDEX if none + */ + std::size_t findIntersectingVertex(std::size_t cornerIndex, const std::array& corner) const; + + /** + * Scan all vertices in current ring to check if any are duplicates + * of the corner apex vertex, and if so whether the corner ear + * intersects the adjacent segments and thus is invalid. + * + * @param cornerIndex the index of the corner apex + * @param corner the corner vertices + * @return true if the corner ia a valid ear + */ + bool isValidEarScan(std::size_t cornerIndex, const std::array& corner) const; + + /* private */ + static Envelope envelope(const std::array& corner); + + /** + * Remove the corner apex vertex and update the candidate corner location. + */ + void removeCorner(); + + bool isRemoved(std::size_t vertexIndex) const; + + void initCornerIndex(); + + /** + * Fetch the corner vertices from the indices. + * + * @param corner an array for the corner vertices + */ + void fetchCorner(std::array& cornerVertex) const; + + /** + * Move to next corner. + */ + void nextCorner(std::array& cornerVertex); + + /** + * Get the index of the next available shell coordinate starting from the given + * index. + * + * @param index candidate position + * @return index of the next available shell coordinate + */ + std::size_t nextIndex(std::size_t index) const; + + bool isConvex(const std::array& pts) const; + + bool isFlat(const std::array& pts) const; + + /** + * Detects if a corner has repeated points (AAB or ABB), or is collapsed (ABA). + * @param pts the corner points + * @return true if the corner is flat or collapsed + */ + bool isCornerInvalid(const std::array& pts) const; + + +public: + + /** + * Creates a new ear-clipper instance. + * + * @param polyShell the polygon vertices to process + */ + PolygonEarClipper(const geom::CoordinateSequence& polyShell); + + /** + * Triangulates a polygon via ear-clipping. + * + * @param polyShell the vertices of the polygon + * @param triListResult vector to fill in with the resultant Tri s + */ + static void triangulate(const geom::CoordinateSequence& polyShell, TriList& triListResult); + + /** + * Sets whether flat corners formed by collinear adjacent line segments + * are included in the triangulation. + * Skipping flat corners reduces the number of triangles in the output. + * However, it produces a triangulation which does not include + * all input vertices. This may be undesirable for downstream processes + * (such as computing a Constrained Delaunay Triangulation for + * purposes of computing the medial axis). + * + * The default is to include all vertices in the result triangulation. + * This still produces a valid triangulation, with no zero-area triangles. + * + * Note that repeated vertices are always skipped. + * + * @param p_isFlatCornersSkipped whether to skip collinear vertices + */ + void setSkipFlatCorners(bool p_isFlatCornersSkipped); + + void compute(TriList& triList); + + std::unique_ptr toGeometry() const; + + +}; + + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos diff --git a/Sources/geos/include/geos/triangulate/polygon/PolygonHoleJoiner.h b/Sources/geos/include/geos/triangulate/polygon/PolygonHoleJoiner.h new file mode 100644 index 0000000..870282f --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/PolygonHoleJoiner.h @@ -0,0 +1,266 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Envelope; +class Geometry; +class LinearRing; +class Polygon; +} +namespace noding { +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + + +/** + * Transforms a polygon with holes into a single self-touching (invalid) ring + * by joining holes to the exterior shell or to another hole + * with out-and-back line segments. + * The holes are added in order of their envelopes (leftmost/lowest first). + * As the result shell develops, a hole may be added to what was + * originally another hole. + * + * There is no attempt to optimize the quality of the join lines. + * In particular, holes may be joined by lines longer than is optimal. + * However, holes which touch the shell or other holes are joined at the touch point. + * + * The class does not require the input polygon to have normal + * orientation (shell CW and rings CCW). + * The output ring is always CW. + */ +class GEOS_DLL PolygonHoleJoiner { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using Polygon = geos::geom::Polygon; + using LinearRing = geos::geom::LinearRing; + using BasicSegmentString = geos::noding::BasicSegmentString; + using SegmentSetMutualIntersector = geos::noding::SegmentSetMutualIntersector; + +private: + + // Members + + const Polygon* inputPolygon; + + //-- normalized, sorted and noded polygon rings + std::unique_ptr shellRing; + std::vector> holeRings; + + //-- indicates whether a hole should be testing for touching + std::vector isHoleTouchingHint; + + CoordinateSequence joinedRing; + + // a sorted and searchable version of the joinedRing + std::set joinedPts; + + std::unique_ptr boundaryIntersector; + + // holding place for some BasicSegmentStrings + std::vector> polySegStringStore; + + + // Classes + class InteriorIntersectionDetector; + friend class PolygonHoleJoiner::InteriorIntersectionDetector; + + + void extractOrientedRings(const Polygon* polygon); + static std::unique_ptr extractOrientedRing(const LinearRing* ring, bool isCW); + void nodeRings(); + void joinHoles(); + + void joinHole(std::size_t index, const CoordinateSequence& holeCoords); + + /** + * Joins a hole to the shell only if the hole touches the shell. + * Otherwise, reports the hole is non-touching. + * + * @param holeCoords the hole to join + * @return true if the hole was touching, false if not + */ + bool joinTouchingHole(const CoordinateSequence& holeCoords); + + /** + * Finds the vertex index of a hole where it touches the + * current shell (if it does). + * If a hole does touch, it must touch at a single vertex + * (otherwise, the polygon is invalid). + * + * @param holeCoords the hole + * @return the index of the touching vertex, or -1 if no touch + */ + std::size_t findHoleTouchIndex(const CoordinateSequence& holeCoords); + + /** + * Joins a single non-touching hole to the current joined ring. + * + * @param holeCoords the hole to join + */ + void joinNonTouchingHole( + const CoordinateSequence& holeCoords); + + const Coordinate& findJoinableVertex( + const Coordinate& holeJoinCoord); + + /** + * Gets the join ring vertex index that the hole is joined after. + * A vertex can occur multiple times in the join ring, so it is necessary + * to choose the one which forms a corner having the + * join line in the ring interior. + * + * @param joinCoord the join ring vertex + * @param holeJoinCoord the hole join vertex + * @return the join ring vertex index to join after + */ + std::size_t findJoinIndex( + const Coordinate& joinCoord, + const Coordinate& holeJoinCoord); + + /** + * Tests if a line between a ring corner vertex and a given point + * is interior to the ring corner. + * + * @param ring a ring of points + * @param ringIndex the index of a ring vertex + * @param linePt the point to be joined to the ring + * @return true if the line to the point is interior to the ring corner + */ + static bool isLineInterior( + const CoordinateSequence& ring, + std::size_t ringIndex, + const Coordinate& linePt); + + static std::size_t prev(std::size_t i, std::size_t size); + static std::size_t next(std::size_t i, std::size_t size); + + /** + * Add hole vertices at proper position in shell vertex list. + * This code assumes that if hole touches (shell or other hole), + * it touches at a node. This requires an initial noding step. + * In this case, the code avoids duplicating join vertices. + * + * Also adds hole points to ordered coordinates. + * + * @param joinIndex index of join vertex in shell + * @param holeCoords the vertices of the hole to be inserted + * @param holeJoinIndex index of join vertex in hole + */ + void addJoinedHole( + std::size_t joinIndex, + const CoordinateSequence& holeCoords, + std::size_t holeJoinIndex); + + /** + * Creates the new section of vertices for ad added hole, + * including any required vertices from the shell at the join point, + * and ensuring join vertices are not duplicated. + * + * @param holeCoords the hole vertices + * @param holeJoinIndex the index of the join vertex + * @param joinPt the shell join vertex + * @return a list of new vertices to be added + */ + std::vector createHoleSection( + const CoordinateSequence& holeCoords, + std::size_t holeJoinIndex, + const Coordinate& joinPt); + + /** + * Sort the hole rings by minimum X, minimum Y. + * + * @param poly polygon that contains the holes + * @return a list of sorted hole rings + */ + static std::vector sortHoles( + const Polygon* poly); + + static std::size_t findLowestLeftVertexIndex( + const CoordinateSequence& holeCoords); + + /** + * Tests whether the interior of a line segment intersects the polygon boundary. + * If so, the line is not a valid join line. + * + * @param p0 a segment vertex + * @param p1 the other segment vertex + * @return true if the segment interior intersects a polygon boundary segment + */ + bool intersectsBoundary( + const Coordinate& p0, + const Coordinate& p1); + + std::unique_ptr createBoundaryIntersector(); + + +public: + + PolygonHoleJoiner(const Polygon* p_inputPolygon) + : inputPolygon(p_inputPolygon) + , boundaryIntersector(nullptr) + {}; + + /** + * Joins the shell and holes of a polygon + * and returns the result as an (invalid) Polygon. + * + * @param p_inputPolygon the polygon to join + * @return the result polygon + */ + static std::unique_ptr joinAsPolygon( + const Polygon* p_inputPolygon); + + /** + * Joins the shell and holes of a polygon + * and returns the result as sequence of Coordinates. + * + * @param p_inputPolygon the polygon to join + * @return the result coordinates + */ + static std::unique_ptr join( + const Polygon* p_inputPolygon); + + /** + * Computes the joined ring. + * + * @return the points in the joined ring + */ + std::unique_ptr compute(); + +}; + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos diff --git a/Sources/geos/include/geos/triangulate/polygon/PolygonNoder.h b/Sources/geos/include/geos/triangulate/polygon/PolygonNoder.h new file mode 100644 index 0000000..fde9213 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/PolygonNoder.h @@ -0,0 +1,110 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2023 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +// #include +// #include +//#include + +#include +#include + +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class CoordinateSequence; +} +namespace noding { +class NodedSegmentString; +} +namespace algorithm { +class LineIntersector; +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + +/** + * Adds node vertices to the rings of a polygon + * where holes touch the shell or each other. + * The structure of the polygon is preserved. + * + * This does not fix invalid polygon topology + * (such as self-touching or crossing rings). + * Invalid input remains invalid after noding, + * and does not trigger an error. + */ +class GEOS_DLL PolygonNoder { + using Coordinate = geos::geom::Coordinate; + using CoordinateSequence = geos::geom::CoordinateSequence; + using NodedSegmentString = geos::noding::NodedSegmentString; + using LineIntersector = geos::algorithm::LineIntersector; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + +public: + + PolygonNoder( + std::unique_ptr& shellRing, + std::vector>& holeRings); + + void node(); + bool isShellNoded(); + bool isHoleNoded(std::size_t i); + std::unique_ptr getNodedShell(); + std::unique_ptr getNodedHole(std::size_t i); + std::vector& getHolesTouching(); + + + +private: + + // Members + std::vector isHoleTouching; + std::map nodedRingIndexes; + std::vector> nodedRings; + + // Classes + class NodeAdder; + friend class PolygonNoder::NodeAdder; + + // Methods + NodedSegmentString* + createNodedSegString(std::unique_ptr& ringPts, std::size_t i); + + void createNodedSegmentStrings( + std::unique_ptr& shellRing, + std::vector>& holeRings); + + /* Turn off copy constructors for MSVC */ + PolygonNoder(const PolygonNoder&) = delete; + PolygonNoder& operator=(const PolygonNoder&) = delete; + +}; + + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos diff --git a/Sources/geos/include/geos/triangulate/polygon/PolygonTriangulator.h b/Sources/geos/include/geos/triangulate/polygon/PolygonTriangulator.h new file mode 100644 index 0000000..8c6cd2b --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/PolygonTriangulator.h @@ -0,0 +1,104 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class Polygon; +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + +/** + * Computes a triangulation of each polygon in a {@link geos::geom::Geometry}. + * A polygon triangulation is a non-overlapping set of triangles which + * cover the polygon and have the same vertices as the polygon. + * The priority is on performance rather than triangulation quality, + * so that the output may contain many narrow triangles. + * + * Holes are handled by joining them to the shell to form a + * (self-touching) polygon shell with no holes. + * Although invalid, this can be triangulated effectively. + * + * For better-quality triangulation use geos::triangulate::polygon::ConstrainedDelaunayTriangulator. + * + * @see geos::triangulate::polygon::ConstrainedDelaunayTriangulator + * + * @author Martin Davis + * + */ +class GEOS_DLL PolygonTriangulator { + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + using Polygon = geos::geom::Polygon; + template + using TriList = geos::triangulate::tri::TriList; + using Tri = geos::triangulate::tri::Tri; + +private: + + // Members + + const Geometry* inputGeom; + const GeometryFactory* geomFact; + + std::unique_ptr compute(); + + /** + * Computes the triangulation of a single polygon + * + * @return GeometryCollection of triangular polygons + */ + void triangulatePolygon(const Polygon* poly, TriList& triList); + + +public: + + /** + * Constructs a new triangulator. + * + * @param p_inputGeom the input geometry + */ + PolygonTriangulator(const Geometry* p_inputGeom) + : inputGeom(p_inputGeom) + , geomFact(p_inputGeom->getFactory()) + {} + + /** + * Computes a triangulation of each polygon in a geometry. + * + * @param geom a geometry containing polygons + * @return a GeometryCollection containing the triangle polygons + */ + static std::unique_ptr triangulate(const Geometry* geom); + +}; + + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/polygon/TriDelaunayImprover.h b/Sources/geos/include/geos/triangulate/polygon/TriDelaunayImprover.h new file mode 100644 index 0000000..956c4d8 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/polygon/TriDelaunayImprover.h @@ -0,0 +1,146 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +} + +namespace geos { +namespace triangulate { +namespace polygon { + + +/** + * Improves the quality of a triangulation of Tri via + * iterated Delaunay flipping. + * This produces the Constrained Delaunay Triangulation + * with the constraints being the boundary of the input triangulation. + * + * @author mdavis + * + */ +class GEOS_DLL TriDelaunayImprover { + using Coordinate = geos::geom::Coordinate; + using Tri = geos::triangulate::tri::Tri; + template + using TriList = geos::triangulate::tri::TriList; + +private: + + // Members + static constexpr std::size_t MAX_ITERATION = 200; + TriList& triList; + + /** + * Improves a triangulation by examining pairs of adjacent triangles + * (forming a quadrilateral) and testing if flipping the diagonal of + * the quadrilateral would produce two new triangles with larger minimum + * interior angles. + * + * @return the number of flips that were made + */ + std::size_t improveScan(TriList& triList); + + /** + * Does a flip of the common edge of two Tris if the Delaunay condition is not met. + * + * @param tri0 a Tri + * @param tri1 a Tri + * @return true if the triangles were flipped + */ + bool improveNonDelaunay(Tri* tri, TriIndex index); + + /** + * Tests if the quadrilateral formed by two adjacent triangles is convex. + * opp0-adj0-adj1 and opp1-adj1-adj0 are the triangle corners + * and hence are known to be convex. + * The quadrilateral is convex if the other corners opp0-adj0-opp1 + * and opp1-adj1-opp0 have the same orientation (since at least one must be convex). + * + * @param adj0 adjacent edge vertex 0 + * @param adj1 adjacent edge vertex 1 + * @param opp0 corner vertex of triangle 0 + * @param opp1 corner vertex of triangle 1 + * @return true if the quadrilateral is convex + */ + static bool isConvex(const Coordinate& adj0, const Coordinate& adj1, + const Coordinate& opp0, const Coordinate& opp1); + + /** + * Tests if either of a pair of adjacent triangles satisfy the Delaunay condition. + * The triangles are opp0-adj0-adj1 and opp1-adj1-adj0. + * The Delaunay condition is not met if one opposite vertex + * lies is in the circumcircle of the other triangle. + * + * @param adj0 adjacent edge vertex 0 + * @param adj1 adjacent edge vertex 1 + * @param opp0 corner vertex of triangle 0 + * @param opp1 corner vertex of triangle 1 + * @return true if the triangles are Delaunay + */ + static bool isDelaunay(const Coordinate& adj0, const Coordinate& adj1, + const Coordinate& opp0, const Coordinate& opp1); + + /** + * Tests whether a point p is in the circumcircle of a triangle abc + * (oriented clockwise). + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point + * + * @return true if the point is in the circumcircle + */ + static bool + isInCircle(const Coordinate& a, const Coordinate& b, + const Coordinate& c, const Coordinate& p); + + void improve(); + + +public: + + TriDelaunayImprover(TriList& p_triList) + : triList(p_triList) + {}; + + /** + * Improves the quality of a triangulation of Tri via + * iterated Delaunay flipping. + * The Tris are assumed to be linked into a Triangulation + * (e.g. via TriangulationBuilder). + * + * @param triList the list of Tris to flip. + */ + static void improve(TriList& triList); + + +}; + + + +} // namespace geos.triangulate.polygon +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/quadedge/LastFoundQuadEdgeLocator.h b/Sources/geos/include/geos/triangulate/quadedge/LastFoundQuadEdgeLocator.h new file mode 100644 index 0000000..5253a9f --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/LastFoundQuadEdgeLocator.h @@ -0,0 +1,63 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/LastFoundQuadEdgeLocator.java r524 + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +//fwd declarations +class QuadEdgeSubdivision; + +/** \brief + * Locates {@link QuadEdge}s in a {@link QuadEdgeSubdivision}, optimizing the + * search by starting in the locality of the last edge found. + * + * @author JTS: Martin Davis + * @author Benjamin Campbell + */ +class LastFoundQuadEdgeLocator : public QuadEdgeLocator { +private: + QuadEdgeSubdivision* subdiv; + QuadEdge* lastEdge; + +public: + LastFoundQuadEdgeLocator(QuadEdgeSubdivision* subdiv); + +private: + virtual void init(); + + virtual QuadEdge* findEdge(); + +public: + /** + * Locates an edge e, such that either v is on e, or e is an edge of a triangle containing v. + * The search starts from the last located edge amd proceeds on the general direction of v. + * @return The caller _does not_ take ownership of the returned object. + */ + QuadEdge* locate(const Vertex& v) override; +}; + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/quadedge/LocateFailureException.h b/Sources/geos/include/geos/triangulate/quadedge/LocateFailureException.h new file mode 100644 index 0000000..9f1a2bc --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/LocateFailureException.h @@ -0,0 +1,37 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/LocateFailureException.java r524 + * + **********************************************************************/ + +#pragma once + +#include + +#include + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +class GEOS_DLL LocateFailureException : public geos::util::GEOSException { +public: + LocateFailureException(std::string const& msg); +}; + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/quadedge/QuadEdge.h b/Sources/geos/include/geos/triangulate/quadedge/QuadEdge.h new file mode 100644 index 0000000..30e952a --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/QuadEdge.h @@ -0,0 +1,443 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/QuadEdge.java r524 + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + + +class GEOS_DLL QuadEdgeQuartet; + +/** \brief + * A class that represents the edge data structure which implements the quadedge algebra. + * + * The quadedge algebra was described in a well-known paper by Guibas and Stolfi, + * "Primitives for the manipulation of general subdivisions and the computation of Voronoi diagrams", + * *ACM Transactions on Graphics*, 4(2), 1985, 75-123. + * + * Each edge object is part of a QuadEdgeQuartet of 4 edges, linked via relative memory addresses. + * Quadedges in a subdivision are linked together via their `next` references. + * The linkage between the quadedge quartets determines the topology + * of the subdivision. + * + * The edge class does not contain separate information for vertice or faces; a vertex is implicitly + * defined as a ring of edges (created using the `next` field). + * + * @author JTS: David Skea + * @author JTS: Martin Davis + * @author Benjamin Campbell + * */ +class GEOS_DLL QuadEdge { + friend class QuadEdgeQuartet; +public: + /** \brief + * Creates a new QuadEdge quartet from {@link Vertex} o to {@link Vertex} d. + * + * @param o the origin Vertex + * @param d the destination Vertex + * @param edges a container in which to store the newly created quartet + * @return the new QuadEdge*, + */ + static QuadEdge* makeEdge(const Vertex& o, const Vertex & d, std::deque & edges); + + /** \brief + * Creates a new QuadEdge connecting the destination of a to the origin of + * b, in such a way that all three have the same left face after the + * connection is complete. + * + * Additionally, the data pointers of the new edge are set. + * + * @return the new QuadEdge* + */ + static QuadEdge* connect(QuadEdge& a, QuadEdge& b, std::deque & edges); + + /** \brief + * Splices two edges together or apart. + * + * Splice affects the two edge rings around the origins of a and b, and, independently, the two + * edge rings around the left faces of `a` and `b`. + * In each case, (i) if the two rings are distinct, + * Splice will combine them into one, or (ii) if the two are the same ring, Splice will break it + * into two separate pieces. Thus, Splice can be used both to attach the two edges together, and + * to break them apart. + * + * @param a an edge to splice + * @param b an edge to splice + * + */ + static void splice(QuadEdge& a, QuadEdge& b); + + /** \brief + * Turns an edge counterclockwise inside its enclosing quadrilateral. + * + * @param e the quadedge to turn + */ + static void swap(QuadEdge& e); + +private: + //// the dual of this edge, directed from right to left + Vertex vertex; // The vertex that this edge represents + QuadEdge* next; // A reference to a connected edge + + int8_t num; // the position of the QuadEdge in the quartet (0-3) + + bool isAlive; + bool visited; + + /** + * Quadedges must be made using {@link QuadEdgeQuartet::makeEdge}, + * to ensure proper construction. + */ + explicit QuadEdge(int8_t _num) : + next(nullptr), + num(_num), + isAlive(true), + visited(false) { + } + +public: + /** \brief + * Gets the primary edge of this quadedge and its `sym`. + * + * The primary edge is the one for which the origin + * and destination coordinates are ordered + * according to the standard geom::Coordinate ordering + * + * @return the primary quadedge + */ + const QuadEdge& getPrimary(); + + /** \brief + * Marks this quadedge as being deleted. + * + * This does not free the memory used by + * this quadedge quartet, but indicates + * that this quadedge quartet no longer participates + * in a subdivision. + * + * @note called "delete" in JTS + * + */ + void remove(); + + /** \brief + * Tests whether this edge has been deleted. + * + * @return `true` if this edge has not been deleted. + */ + inline bool + isLive() const + { + return isAlive; + } + + inline bool + isVisited() const + { + return visited; + } + + inline void + setVisited(bool v) { + visited = v; + } + + /** \brief + * Sets the connected edge + * + * @param p_next edge + */ + inline void + setNext(QuadEdge* p_next) + { + this->next = p_next; + } + + /*************************************************************************** + * QuadEdge Algebra + *************************************************************************** + */ + + /** \brief + * Gets the dual of this edge, directed from its right to its left. + * + * @return the rotated edge + */ + inline const QuadEdge& + rot() const + { + return (num < 3) ? *(this + 1) : *(this - 3); + } + + inline QuadEdge& + rot() + { + return (num < 3) ? *(this + 1) : *(this - 3); + } + + /** \brief + * Gets the dual of this edge, directed from its left to its right. + * + * @return the inverse rotated edge. + */ + inline const QuadEdge& + invRot() const + { + return (num > 0) ? *(this - 1) : *(this + 3); + } + + inline QuadEdge& + invRot() + { + return (num > 0) ? *(this - 1) : *(this + 3); + } + + /** \brief + * Gets the edge from the destination to the origin of this edge. + * + * @return the sym of the edge + */ + inline const QuadEdge& + sym() const + { + return (num < 2) ? *(this + 2) : *(this - 2); + } + + inline QuadEdge& + sym() + { + return (num < 2) ? *(this + 2) : *(this - 2); + } + + /** \brief + * Gets the next CCW edge around the origin of this edge. + * + * @return the next linked edge. + */ + inline const QuadEdge& + oNext() const + { + return *next; + } + + inline QuadEdge& + oNext() + { + return *next; + } + + /** \brief + * Gets the next CW edge around (from) the origin of this edge. + * + * @return the previous edge. + */ + inline const QuadEdge& + oPrev() const + { + return rot().oNext().rot(); + } + + inline QuadEdge& + oPrev() + { + return rot().oNext().rot(); + } + + /** \brief + * Gets the next CCW edge around (into) the destination of this edge. + * + * @return the next destination edge. + */ + inline const QuadEdge& + dNext() const + { + return sym().oNext().sym(); + } + + /** \brief + * Gets the next CW edge around (into) the destination of this edge. + * + * @return the previous destination edge. + */ + inline const QuadEdge& + dPrev() const + { + return invRot().oNext().invRot(); + } + + inline QuadEdge& + dPrev() + { + return invRot().oNext().invRot(); + } + + /** \brief + * Gets the CCW edge around the left face following this edge. + * + * @return the next left face edge. + */ + inline const QuadEdge& + lNext() const + { + return invRot().oNext().rot(); + } + + inline QuadEdge& + lNext() + { + return invRot().oNext().rot(); + } + + /** \brief + * Gets the CCW edge around the left face before this edge. + * + * @return the previous left face edge. + */ + inline const QuadEdge& + lPrev() const + { + return oNext().sym(); + } + + inline QuadEdge& + lPrev() + { + return oNext().sym(); + } + + /** \brief + * Gets the edge around the right face ccw following this edge. + * + * @return the next right face edge. + */ + inline const QuadEdge& + rNext() const + { + return rot().oNext().invRot(); + } + + /** \brief + * Gets the edge around the right face ccw before this edge. + * + * @return the previous right face edge. + */ + inline const QuadEdge& + rPrev() const + { + return sym().oNext(); + } + + /*********************************************************************************************** + * Data Access + **********************************************************************************************/ + /** \brief + * Sets the vertex for this edge's origin + * + * @param o the origin vertex + */ + inline void + setOrig(const Vertex& o) + { + vertex = o; + } + + /** \brief + * Sets the vertex for this edge's destination + * + * @param d the destination vertex + */ + inline void + setDest(const Vertex& d) + { + sym().setOrig(d); + } + + /** \brief + * Gets the vertex for the edge's origin + * + * @return the origin vertex + */ + const Vertex& + orig() const + { + return vertex; + } + + /** \brief + * Gets the vertex for the edge's destination + * + * @return the destination vertex + */ + const Vertex& + dest() const + { + return sym().orig(); + } + + /** \brief + * Gets the length of the geometry of this quadedge. + * + * @return the length of the quadedge + */ + inline double + getLength() const + { + return orig().getCoordinate().distance(dest().getCoordinate()); + } + + /** \brief + * Tests if this quadedge and another have the same line segment geometry, + * regardless of orientation. + * + * @param qe a quadege + * @return `true` if the quadedges are based on the same line segment regardless of orientation + */ + bool equalsNonOriented(const QuadEdge& qe) const; + + /** \brief + * Tests if this quadedge and another have the same line segment geometry + * with the same orientation. + * + * @param qe a quadege + * @return `true` if the quadedges are based on the same line segment + */ + bool equalsOriented(const QuadEdge& qe) const; + + /** \brief + * Creates a {@link geom::LineSegment} representing the + * geometry of this edge. + * + * @return a LineSegment + */ + std::unique_ptr toLineSegment() const; +}; + +GEOS_DLL std::ostream& operator<< (std::ostream& os, const QuadEdge* e); + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace geos + diff --git a/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeLocator.h b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeLocator.h new file mode 100644 index 0000000..24f6640 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeLocator.h @@ -0,0 +1,48 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/QuadEdgeLocator.java r524 + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +class Vertex; +class QuadEdge; + +/** \brief + * An interface for classes which locate an edge in a {@link QuadEdgeSubdivision} + * which either contains a given {@link Vertex} V or is an edge of a triangle + * which contains V. + * + * Implementors may utilized different strategies for + * optimizing locating containing edges/triangles. + * + * @author JTS: Martin Davis + * @author Ben Campbell + */ +class QuadEdgeLocator { +public: + virtual ~QuadEdgeLocator() = default; + virtual QuadEdge* locate(const Vertex& v) = 0; //not implemented +}; + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeQuartet.h b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeQuartet.h new file mode 100644 index 0000000..49b4c80 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeQuartet.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + + +namespace geos { +namespace triangulate { +namespace quadedge { + +class GEOS_DLL QuadEdgeQuartet { + +public: + QuadEdgeQuartet() : e{{QuadEdge(0), QuadEdge(1), QuadEdge(2), QuadEdge(3)}} { + e[0].next = &(e[0]); + e[1].next = &(e[3]); + e[2].next = &(e[2]); + e[3].next = &(e[1]); + }; + + static QuadEdge& makeEdge(const Vertex& o, const Vertex & d, std::deque & edges) { + edges.emplace_back(); + auto& qe = edges.back(); + qe.base().setOrig(o); + qe.base().setDest(d); + + return qe.base(); + } + + QuadEdge& base() { + return e[0]; + } + + const QuadEdge& base() const { + return e[0]; + } + + void setVisited(bool status) { + for (auto& edge : e) { + edge.setVisited(status); + } + } + +private: + std::array e; +}; + +} +} +} + diff --git a/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h new file mode 100644 index 0000000..fd4a94f --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/QuadEdgeSubdivision.h @@ -0,0 +1,514 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * Copyright (C) 2019 Daniel Baston + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/QuadEdgeSubdivision.java r524 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace geos { + +namespace geom { + +class CoordinateSequence; +class GeometryCollection; +class MultiLineString; +class GeometryFactory; +class Coordinate; +class Geometry; +class Envelope; +} + +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +class TriangleVisitor; + +/** \brief + * A class that contains the [QuadEdges](@ref QuadEdge) representing a planar + * subdivision that models a triangulation. + * + * The subdivision is constructed using the quadedge algebra defined in the class QuadEdge. + * + * All metric calculations are done in the Vertex class. + * In addition to a triangulation, subdivisions support extraction of Voronoi diagrams. + * This is easily accomplished, since the Voronoi diagram is the dual + * of the Delaunay triangulation. + * + * Subdivisions can be provided with a tolerance value. Inserted vertices which + * are closer than this value to vertices already in the subdivision will be + * ignored. Using a suitable tolerance value can prevent robustness failures + * from happening during Delaunay triangulation. + * + * Subdivisions maintain a **frame** triangle around the client-created + * edges. The frame is used to provide a bounded "container" for all edges + * within a TIN. Normally the frame edges, frame connecting edges, and frame + * triangles are not included in client processing. + * + * @author JTS: David Skea + * @author JTS: Martin Davis + * @author Benjamin Campbell + */ +class GEOS_DLL QuadEdgeSubdivision { +public: + typedef std::vector QuadEdgeList; + + /** \brief + * Gets the edges for the triangle to the left of the given QuadEdge. + * + * @param startQE + * @param triEdge + * + * @throws IllegalArgumentException if the edges do not form a triangle + */ + static void getTriangleEdges(const QuadEdge& startQE, + const QuadEdge* triEdge[3]); + +private: + /** + * Use a deque to ensure QuadEdge pointers are stable. + * Note that it is NOT safe to erase entries from the deque. + */ + std::deque quadEdges; + std::array startingEdges; + double tolerance; + double edgeCoincidenceTolerance; + std::array frameVertex; + geom::Envelope frameEnv; + std::unique_ptr locator; + bool visit_state_clean; + +public: + /** \brief + * Creates a new instance of a quad-edge subdivision based on a frame triangle + * that encloses a supplied bounding box. + * A new super-bounding box that contains the triangle is computed and stored. + * + * @param env the bounding box to surround + * @param tolerance the tolerance value for determining if two sites are equal + */ + QuadEdgeSubdivision(const geom::Envelope& env, double tolerance); + + virtual ~QuadEdgeSubdivision() = default; + +private: + virtual void createFrame(const geom::Envelope& env); + + virtual void initSubdiv(); + +public: + /** \brief + * Gets the vertex-equality tolerance value used in this subdivision + * + * @return the tolerance value + */ + inline double + getTolerance() const + { + return tolerance; + } + + /** \brief + * Gets the envelope of the Subdivision (including the frame). + * + * @return the envelope + */ + inline const geom::Envelope& + getEnvelope() const + { + return frameEnv; + } + + /** \brief + * Gets the collection of base {@link QuadEdge}s (one for every pair of + * vertices which is connected). + * + * @return a QuadEdgeList + */ + inline std::deque& + getEdges() + { + return quadEdges; + } + + /** \brief + * Sets the QuadEdgeLocator to use for locating containing triangles + * in this subdivision. + * + * @param p_locator + * a QuadEdgeLocator + */ + inline void + setLocator(std::unique_ptr p_locator) + { + this->locator = std::move(p_locator); + } + + /** \brief + * Creates a new quadedge, recording it in the edges list. + * + * @param o + * @param d + * @return + */ + virtual QuadEdge& makeEdge(const Vertex& o, const Vertex& d); + + /** \brief + * Creates a new QuadEdge connecting the destination of a to the origin of b, + * in such a way that all three have the same left face after the connection + * is complete. + * The quadedge is recorded in the edges list. + * + * @param a + * @param b + * @return + */ + virtual QuadEdge& connect(QuadEdge& a, QuadEdge& b); + + /** \brief + * Deletes a quadedge from the subdivision. Linked quadedges are updated to + * reflect the deletion. + * + * @param e the quadedge to delete + */ + void remove(QuadEdge& e); + + /** \brief + * Locates an edge of a triangle which contains a location + * specified by a Vertex `v`. + * + * The edge returned has the property that either v is on e, + * or e is an edge of a triangle containing v. + * The search starts from startEdge and proceeds on the general direction of v. + * + * This locate algorithm relies on the subdivision being Delaunay. For + * non-Delaunay subdivisions, this may loop for ever. + * + * @param v the location to search for + * @param startEdge an edge of the subdivision to start searching at + * @return a QuadEdge which contains v, or is on the edge of a triangle containing v + * @throws LocateFailureException if the location algorithm fails to converge in a + * reasonable number of iterations. + * + * @note The returned pointer **should not** be freed be the caller. + */ + QuadEdge* locateFromEdge(const Vertex& v, + const QuadEdge& startEdge) const; + + /** \brief + * Finds a quadedge of a triangle containing a location + * specified by a [Vertex](@ref triangulate::quadedge::Vertex), if one exists. + * + * @param v the vertex to locate + * @return a QuadEdge on the edge of a triangle which touches or contains the location + * @return `null` if no such triangle exists. + * + * @note The returned pointer **should not** be freed be the caller. + */ + inline QuadEdge* + locate(const Vertex& v) const + { + return locator->locate(v); + } + + /** \brief + * Finds a quadedge of a triangle containing a location + * specified by a geom::Coordinate, if one exists. + * + * @param p the Coordinate to locate + * @return a QuadEdge on the edge of a triangle which touches or contains the location + * @return `null` if no such triangle exists. + * + * @note The returned pointer **should not** be freed be the caller. + */ + inline QuadEdge* + locate(const geom::Coordinate& p) + { + return locator->locate(Vertex(p)); + } + + /** \brief + * Locates the edge between the given vertices, if it exists in the + * subdivision. + * + * @param p0 a coordinate + * @param p1 another coordinate + * @return the edge joining the coordinates, if present + * @return `null` if no such edge exists + * + * @note the caller **should not** free the returned pointer + */ + QuadEdge* locate(const geom::Coordinate& p0, const geom::Coordinate& p1); + + /** \brief + * Inserts a new site into the Subdivision, connecting it to the vertices of + * the containing triangle (or quadrilateral, if the split point falls on an + * existing edge). + * + * This method does NOT maintain the Delaunay condition. If desired, this must + * be checked and enforced by the caller. + * + * This method does NOT check if the inserted vertex falls on an edge. This + * must be checked by the caller, since this situation may cause erroneous + * triangulation + * + * @param v the vertex to insert + * @return a new quad edge terminating in v + */ + QuadEdge& insertSite(const Vertex& v); + + /** \brief + * Tests whether a QuadEdge is an edge incident on a frame triangle vertex. + * + * @param e the edge to test + * @return `true` if the edge is connected to the frame triangle + */ + bool isFrameEdge(const QuadEdge& e) const; + + /** \brief + * Tests whether a QuadEdge is an edge on the border of the frame facets and + * the internal facets. E.g. an edge which does not itself touch a frame + * vertex, but which touches an edge which does. + * + * @param e the edge to test + * @return `true` if the edge is on the border of the frame + */ + bool isFrameBorderEdge(const QuadEdge& e) const; + + /** \brief + * Tests whether a vertex is a vertex of the outer triangle. + * + * @param v the vertex to test + * @return `true` if the vertex is an outer triangle vertex + */ + bool isFrameVertex(const Vertex& v) const; + + + /** \brief + * Tests whether a [Coordinate](@ref geom::Coordinate) lies on a QuadEdge, + * up to a tolerance determined by the subdivision tolerance. + * + * @param e a QuadEdge + * @param p a point + * @return `true` if the vertex lies on the edge + */ + bool isOnEdge(const QuadEdge& e, const geom::Coordinate& p) const; + + /** \brief + * Tests whether a Vertex is the start or end vertex of a + * QuadEdge, up to the subdivision tolerance distance. + * + * @param e + * @param v + * @return `true` if the vertex is a endpoint of the edge + */ + bool isVertexOfEdge(const QuadEdge& e, const Vertex& v) const; + + /** \brief + * Gets all primary quadedges in the subdivision. + * + * A primary edge is a QuadEdge which occupies the 0'th position in its + * array of associated quadedges. These provide the unique geometric + * edges of the triangulation. + * + * @param includeFrame `true` if the frame edges are to be included + * @return a List of QuadEdges. The caller takes ownership of the returned QuadEdgeList but not the + * items it contains. + */ + std::unique_ptr getPrimaryEdges(bool includeFrame); + + /***************************************************************************** + * Visitors + ****************************************************************************/ + + void visitTriangles(TriangleVisitor* triVisitor, bool includeFrame); + +private: + typedef std::stack QuadEdgeStack; + typedef std::vector> TriList; + + /** \brief + * The quadedges forming a single triangle. + * + * Only one visitor is allowed to be active at a time, so this is safe. + */ + std::array m_triEdges; + + /** \brief + * Resets the `visited` flag of each `QuadEdge` prior to iteration, if necessary. + */ + void prepareVisit(); + + /** \brief + * Stores the edges for a visited triangle. Also pushes sym (neighbour) edges + * on stack to visit later. + * + * @param edge + * @param edgeStack + * @param includeFrame + * @return the visited triangle edges + * @return `null` if the triangle should not be visited (for instance, if it is + * outer) + */ + std::array* fetchTriangleToVisit(QuadEdge* edge, QuadEdgeStack& edgeStack, bool includeFrame); + + /** \brief + * Gets the coordinates for each triangle in the subdivision as an array. + * + * @param includeFrame true if the frame triangles should be included + * @param triList a list of Coordinate[4] representing each triangle + */ + void getTriangleCoordinates(TriList* triList, bool includeFrame); + +private: + class TriangleCoordinatesVisitor; + class TriangleCircumcentreVisitor; + +public: + /** \brief + * Gets the geometry for the edges in the subdivision as a [MultiLineString](@ref geom::MultiLineString) + * containing 2-point lines. + * + * @param geomFact the GeometryFactory to use + * @return a MultiLineString + * @note The caller takes ownership of the returned object. + */ + std::unique_ptr getEdges(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets the geometry for the triangles in a triangulated subdivision as a + * [GeometryCollection](@ref geos::geom::GeometryCollection) + * of triangular [Polygons](@ref geos::geom::Polygon). + * + * @param geomFact the GeometryFactory to use + * @return a GeometryCollection of triangular polygons. + * + * @note The caller takes ownership of the returned object. + */ + std::unique_ptr getTriangles(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets the cells in the Voronoi diagram for this triangulation. + * The cells are returned as a [GeometryCollection](@ref geom::GeometryCollection) + * of [Polygons](@ref geom::Polygon). + * + * The userData of each polygon is set to be the [Coordinate](@ref geom::Coordinate) + * of the cell site. This allows easily associating external + * data associated with the sites to the cells. + * + * @param geomFact a geometry factory + * @return a GeometryCollection of Polygons + */ + std::unique_ptr getVoronoiDiagram(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets the cells in the Voronoi diagram for this triangulation. + * + * The cells are returned as a [GeometryCollection](@ref geom::GeometryCollection) of + * [LineStrings](@ref geom::LineString). The userData of each polygon is set to be the + * [Coordinate](@ref geom::Coordinate) of the cell site. This allows easily associating external + * data associated with the sites to the cells. + * + * @param geomFact a geometry factory + * @return a MultiLineString + */ + std::unique_ptr getVoronoiDiagramEdges(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets a List of [Polygons](@ref geom::Polygon) for the Voronoi cells + * of this triangulation. + * + * The userData of each polygon is set to be the [Coordinate](@ref geom::Coordinate) + * of the cell site. This allows easily associating external data associated + * with the sites to the cells. + * + * @param geomFact a geometry factory + * @return a List of Polygons + */ + std::vector> getVoronoiCellPolygons(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets a List of [LineStrings](@ref geom::LineString) for the Voronoi cells + * of this triangulation. + * + * The userData of each LineString is set to be the [Coordinate](@ref geom::Coordinate) + * of the cell site. This allows easily associating external + * data associated with the sites to the cells. + * + * @param geomFact a geometry factory + * @return a List of LineString + */ + std::vector> getVoronoiCellEdges(const geom::GeometryFactory& geomFact); + + /** \brief + * Gets a collection of [QuadEdges](@ref QuadEdge) whose origin vertices are a unique set + * which includes all vertices in the subdivision. + * + * The frame vertices can be included if required. This is useful for algorithms which require + * traversing the subdivision starting at all vertices. + * Returning a quadedge for each vertex is more efficient than the alternative + * of finding the actual vertices using `getVertices()` and then locating + * quadedges attached to them. + * + * @param includeFrame `true` if the frame vertices should be included + * @return a collection of QuadEdge with the vertices of the subdivision as their origins + */ + std::unique_ptr getVertexUniqueEdges(bool includeFrame); + + /** \brief + * Gets the Voronoi cell around a site specified by the origin of a QuadEdge. + * + * The userData of the polygon is set to be the [Coordinate](@ref geom::Coordinate) + * of the site. This allows attaching external data associated with the site + * to this cell polygon. + * + * @param qe a quadedge originating at the cell site + * @param geomFact a factory for building the polygon + * @return a polygon indicating the cell extent + */ + std::unique_ptr getVoronoiCellPolygon(const QuadEdge* qe, const geom::GeometryFactory& geomFact); + + /** \brief + * Gets the Voronoi cell edge around a site specified by the origin of a QuadEdge. + * + * The userData of the LineString is set to be the [Coordinate](@ref geom::Coordinate) + * of the site. This allows attaching external data associated with + * the site to this cell polygon. + * + * @param qe a quadedge originating at the cell site + * @param geomFact a factory for building the polygon + * @return a polygon indicating the cell extent + */ + std::unique_ptr getVoronoiCellEdge(const QuadEdge* qe, const geom::GeometryFactory& geomFact); + +}; + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace goes diff --git a/Sources/geos/include/geos/triangulate/quadedge/TrianglePredicate.h b/Sources/geos/include/geos/triangulate/quadedge/TrianglePredicate.h new file mode 100644 index 0000000..c356928 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/TrianglePredicate.h @@ -0,0 +1,124 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/TrianglePredicate.java r524 + * + **********************************************************************/ + +#pragma once + +#include +#include + +namespace geos { +namespace geom { +class CoordinateXY; +} +} + +namespace geos { +namespace triangulate { +namespace quadedge { + +/** \brief + * Algorithms for computing values and predicates + * associated with triangles. + * + * For some algorithms extended-precision + * implementations are provided, which are more robust + * (i.e. they produce correct answers in more cases). + * Also, some more robust formulations of + * some algorithms are provided, which utilize + * normalization to the origin. + * + * @author JTS: Martin Davis + * @author Benjamin Campbell + * + */ +class GEOS_DLL TrianglePredicate { +public: + using CoordinateXY = geos::geom::CoordinateXY; + + /** + * Tests if a point is inside the circle defined by + * the triangle with vertices a, b, c (oriented counter-clockwise). + * This test uses simple + * double-precision arithmetic, and thus may not be robust. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point to test + * @return true if this point is inside the circle defined by the points a, b, c + */ + static geom::Location isInCircleNonRobust( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); + + /** + * Tests if a point is inside the circle defined by + * the triangle with vertices a, b, c (oriented counter-clockwise). + * This test uses simple + * double-precision arithmetic, and thus is not 10% robust. + * However, by using normalization to the origin + * it provides improved robustness and increased performance. + *

+ * Based on code by J.R.Shewchuk. + * + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point to test + * @return true if this point is inside the circle defined by the points a, b, c + */ + static geom::Location isInCircleNormalized( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); + +private: + /** + * Computes twice the area of the oriented triangle (a, b, c), i.e., the area is positive if the + * triangle is oriented counterclockwise. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + */ + static double triArea(const CoordinateXY& a, + const CoordinateXY& b, const CoordinateXY& c); + +public: + /** + * Tests if a point is inside the circle defined by + * the triangle with vertices a, b, c (oriented counter-clockwise). + * This method uses more robust computation. + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @param p the point to test + * @return true if this point is inside the circle defined by the points a, b, c + */ + static geom::Location isInCircleRobust( + const CoordinateXY& a, const CoordinateXY& b, const CoordinateXY& c, + const CoordinateXY& p); +} ; + + +} // namespace geos.triangulate.quadedge +} // namespace geos.triangulate +} // namespace geos + + diff --git a/Sources/geos/include/geos/triangulate/quadedge/TriangleVisitor.h b/Sources/geos/include/geos/triangulate/quadedge/TriangleVisitor.h new file mode 100644 index 0000000..36b4386 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/TriangleVisitor.h @@ -0,0 +1,48 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/TriangleVisitor.java r524 + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +/** \brief + * An interface for algorithms which process the triangles in a {@link QuadEdgeSubdivision}. + * + * @author JTS: Martin Davis + * @author Benjamin Campbell + */ +class GEOS_DLL TriangleVisitor { +public: + /** + * Visits the {@link QuadEdge}s of a triangle. + * + * @param triEdges an array of the 3 quad edges in a triangle (in CCW order) + */ + virtual void visit(std::array & triEdges) = 0; + virtual ~TriangleVisitor() = default; +private: +} ; + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace goes + diff --git a/Sources/geos/include/geos/triangulate/quadedge/Vertex.h b/Sources/geos/include/geos/triangulate/quadedge/Vertex.h new file mode 100644 index 0000000..abb7bdc --- /dev/null +++ b/Sources/geos/include/geos/triangulate/quadedge/Vertex.h @@ -0,0 +1,312 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Excensus LLC. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: triangulate/quadedge/Vertex.java r705 + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include +#include +#include + + +//fwd declarations +namespace geos { +namespace triangulate { +namespace quadedge { +class QuadEdge; +} +} +} + +namespace geos { +namespace triangulate { //geos.triangulate +namespace quadedge { //geos.triangulate.quadedge + +/** \brief + * Models a site (node) in a QuadEdgeSubdivision. + * + * The sites can be points on a line string representing a linear site. + * + * The vertex can be considered as a vector with a norm, length, inner product, cross + * product, etc. Additionally, point relations (e.g., is a point to the left of a line, the circle + * defined by this point and two others, etc.) are also defined in this class. + * + * It is common to want to attach user-defined data to the vertices of a subdivision. + * One way to do this is to subclass `Vertex` to carry any desired information. + * + * @author JTS: David Skea + * @author JTS: Martin Davis + * @author Benjamin Campbell + * */ + +class GEOS_DLL Vertex { +public: + static const int LEFT = 0; + static const int RIGHT = 1; + static const int BEYOND = 2; + static const int BEHIND = 3; + static const int BETWEEN = 4; + static const int ORIGIN = 5; + static const int DESTINATION = 6; +private: + geom::Coordinate p; + +public: + Vertex(double _x, double _y); + + Vertex(double _x, double _y, double _z); + + Vertex(const geom::Coordinate& _p); + + Vertex(); + ~Vertex() {}; + + inline double + getX() const + { + return p.x; + } + + inline double + getY() const + { + return p.y; + } + + inline double + getZ() const + { + return p.z; + } + + inline void + setZ(double _z) + { + p.z = _z; + } + + inline const geom::Coordinate& + getCoordinate() const + { + return p; + } + + inline bool + equals(const Vertex& _x) const + { + return p.equals2D(_x.p); + } + + inline bool + equals(const Vertex& _x, double tolerance) const + { + if(p.distance(_x.getCoordinate()) < tolerance) { + return true; + } + return false; + } + + int classify(const Vertex& p0, const Vertex& p1); + + /** + * Computes the cross product k = u X v. + * + * @param v a vertex + * @return returns the magnitude of u X v + */ + inline double + crossProduct(const Vertex& v) const + { + return (p.x * v.getY() - p.y * v.getX()); + } + + /** + * Computes the inner or dot product + * + * @param v a vertex + * @return returns the dot product u.v + */ + inline double + dot(Vertex v) const + { + return (p.x * v.getX() + p.y * v.getY()); + } + + /** + * Computes the scalar product c(v) + * + * @param c scaling factor + * @return returns the scaled vector + */ + inline std::unique_ptr + times(double c) const + { + return std::unique_ptr(new Vertex(c * p.x, c * p.y)); + } + + /* Vector addition */ + inline std::unique_ptr + sum(Vertex v) const + { + return std::unique_ptr(new Vertex(p.x + v.getX(), p.y + v.getY())); + } + + /* and subtraction */ + inline std::unique_ptr + sub(const Vertex& v) const + { + return std::unique_ptr(new Vertex(p.x - v.getX(), p.y - v.getY())); + } + + /* magnitude of vector */ + inline double + magn() const + { + return (std::sqrt(p.x * p.x + p.y * p.y)); + } + + /* returns k X v (cross product). this is a vector perpendicular to v */ + inline std::unique_ptr + cross() const + { + return std::unique_ptr(new Vertex(p.y, -p.x)); + } + + /** ************************************************************* */ + /*********************************************************************************************** + * Geometric primitives / + **********************************************************************************************/ + + /** + * Tests if the vertex is inside the circle defined by + * the triangle with vertices a, b, c (oriented counter-clockwise). + * + * @param a a vertex of the triangle + * @param b a vertex of the triangle + * @param c a vertex of the triangle + * @return true if this vertex is in the circumcircle of (a,b,c) + */ + bool isInCircle(const Vertex& a, const Vertex& b, const Vertex& c) const { + return triangulate::quadedge::TrianglePredicate::isInCircleRobust(a.p, b.p, c.p, this->p) == geom::Location::INTERIOR; + } + + /** + * Tests whether the triangle formed by this vertex and two + * other vertices is in CCW orientation. + * + * @param b a vertex + * @param c a vertex + * @returns true if the triangle is oriented CCW + */ + inline bool + isCCW(const Vertex& b, const Vertex& c) const + { + // check if signed area is positive + return (b.p.x - p.x) * (c.p.y - p.y) + > (b.p.y - p.y) * (c.p.x - p.x); + } + + bool rightOf(const QuadEdge& e) const; + bool leftOf(const QuadEdge& e) const; + +private: + static std::unique_ptr bisector(const Vertex& a, const Vertex& b); + + inline double + distance(const Vertex& v1, const Vertex& v2) + { + return std::sqrt(pow(v2.getX() - v1.getX(), 2.0) + + pow(v2.getY() - v1.getY(), 2.0)); + } + + /** + * Computes the value of the ratio of the circumradius to shortest edge. If smaller than some + * given tolerance B, the associated triangle is considered skinny. For an equal lateral + * triangle this value is 0.57735. The ratio is related to the minimum triangle angle theta by: + * circumRadius/shortestEdge = 1/(2sin(theta)). + * + * @param b second vertex of the triangle + * @param c third vertex of the triangle + * @return ratio of circumradius to shortest edge. + */ + double circumRadiusRatio(const Vertex& b, const Vertex& c); + + /** + * returns a new vertex that is mid-way between this vertex and another end point. + * + * @param a the other end point. + * @return the point mid-way between this and that. + */ + std::unique_ptr midPoint(const Vertex& a); + + /** + * Computes the centre of the circumcircle of this vertex and two others. + * + * @param b + * @param c + * @return the Coordinate which is the circumcircle of the 3 points. + */ + std::unique_ptr circleCenter(const Vertex& b, const Vertex& c) const; + + /** + * For this vertex enclosed in a triangle defined by three vertices v0, v1 and v2, interpolate + * a z value from the surrounding vertices. + */ + double interpolateZValue(const Vertex& v0, const Vertex& v1, const Vertex& v2) const; + + /** + * Interpolates the Z-value (height) of a point enclosed in a triangle + * whose vertices all have Z values. + * The containing triangle must not be degenerate + * (in other words, the three vertices must enclose a + * non-zero area). + * + * @param p the point to interpolate the Z value of + * @param v0 a vertex of a triangle containing the p + * @param v1 a vertex of a triangle containing the p + * @param v2 a vertex of a triangle containing the p + * @return the interpolated Z-value (height) of the point + */ + static double interpolateZ(const geom::Coordinate& p, const geom::Coordinate& v0, + const geom::Coordinate& v1, const geom::Coordinate& v2); + + /** + * Computes the interpolated Z-value for a point p lying on the segment p0-p1 + * + * @param p + * @param p0 + * @param p1 + * @return the interpolated Z value + */ + static double interpolateZ(const geom::Coordinate& p, const geom::Coordinate& p0, + const geom::Coordinate& p1); +}; + +inline bool +operator<(const Vertex& v1, const Vertex& v2) +{ + return v1.getCoordinate() < v2.getCoordinate(); +} + +} //namespace geos.triangulate.quadedge +} //namespace geos.triangulate +} //namespace geos + diff --git a/Sources/geos/include/geos/triangulate/tri/Tri.h b/Sources/geos/include/geos/triangulate/tri/Tri.h new file mode 100644 index 0000000..06feced --- /dev/null +++ b/Sources/geos/include/geos/triangulate/tri/Tri.h @@ -0,0 +1,186 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +// Forward declarations +namespace geos { +namespace geom { +class Geometry; +class GeometryFactory; +class Polygon; +} +} + +namespace geos { // geos. +namespace triangulate { // geos.triangulate +namespace tri { // geos.triangulate.tri + + +/** + * A memory-efficient representation of a triangle in a triangulation. + * Contains three vertices, and links to adjacent Tris for each edge. + * Tris are constructed independently, and if needed linked + * into a triangulation using {@link TriangulationBuilder}. + * + * @author Martin Davis + * + */ +class GEOS_DLL Tri { + using Coordinate = geos::geom::Coordinate; + using Polygon = geos::geom::Polygon; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + +protected: + + // Members + Coordinate p0; + Coordinate p1; + Coordinate p2; + + /** + * triN is the adjacent triangle across the edge pN - pNN. + * pNN is the next vertex CW from pN. + */ + Tri* tri0; + Tri* tri1; + Tri* tri2; + +private: + + /** + * Replace triOld with triNew + * + * @param triOld + * @param triNew + */ + void replace(Tri* triOld, Tri* triNew); + void remove(TriIndex index); + + /** + * + * Order: 0: opp0-adj0 edge, 1: opp0-adj1 edge, + * 2: opp1-adj0 edge, 3: opp1-adj1 edge + * + * @param tri + * @param index0 + * @param index1 + * @return list of adjactent tris + */ + std::vector getAdjacentTris(Tri* tri, TriIndex index0, TriIndex index1); + + void setCoordinates(const Coordinate& p0, const Coordinate& p1, const Coordinate& p2); + + void flip(Tri* tri, TriIndex index0, TriIndex index1, + const Coordinate& adj0, const Coordinate& adj1, + const Coordinate& opp0, const Coordinate& opp1); + + +public: + + Tri(const Coordinate& c0, const Coordinate& c1, const Coordinate& c2) + : p0(c0) + , p1(c1) + , p2(c2) + , tri0(nullptr) + , tri1(nullptr) + , tri2(nullptr) + {}; + + void setAdjacent(Tri* p_tri0, Tri* p_tri1, Tri* p_tri2); + void setAdjacent(const Coordinate& pt, Tri* tri); + void setTri(TriIndex edgeIndex, Tri* tri); + + /** + * Interchanges the vertices of this triangle and a neighbor + * so that their common edge + * becomes the the other diagonal of the quadrilateral they form. + * Neighbour triangle links are modified accordingly. + * + * @param index the index of the adjacent tri to flip with + */ + void flip(TriIndex index); + + /** + * Removes this triangle from a triangulation. + * All adjacent references and the references to this + * Tri in the adjacent Tris are set to nullptr. + */ + void remove(); + void remove(TriList& triList); + + void validate(); + void validateAdjacent(TriIndex index); + + std::pair getEdge(Tri* neighbor) const; + + const Coordinate& getEdgeStart(TriIndex i) const; + const Coordinate& getEdgeEnd(TriIndex i) const; + + bool hasCoordinate(const Coordinate& v) const; + const Coordinate& getCoordinate(TriIndex i) const; + + TriIndex getIndex(const Coordinate& p) const; + TriIndex getIndex(const Tri* tri) const; + + Tri* getAdjacent(TriIndex i) const; + bool hasAdjacent(TriIndex i) const; + bool hasAdjacent() const; + bool isAdjacent(Tri* tri) const; + + int numAdjacent() const; + + static TriIndex next(TriIndex i); + static TriIndex prev(TriIndex i); + static TriIndex oppVertex(TriIndex edgeIndex); + static TriIndex oppEdge(TriIndex vertexIndex); + + /** + * Tests if a tri vertex is interior. + * A vertex of a triangle is interior if it + * is fully surrounded by other triangles. + * + * @param index the vertex index + * @return true if the vertex is interior + */ + bool isInteriorVertex(TriIndex index) const; + + bool isBorder() const; + bool isBoundary(TriIndex index) const; + Coordinate midpoint(TriIndex edgeIndex) const; + + double getArea() const; + double getLength() const; + double getLength(TriIndex i) const; + + std::unique_ptr toPolygon(const GeometryFactory* gf) const; + static std::unique_ptr toGeometry(std::set& tris, const GeometryFactory* gf); + + friend std::ostream& operator << (std::ostream& os, const Tri&); + +}; + + + + +} // namespace geos.triangulate.tri +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/tri/TriEdge.h b/Sources/geos/include/geos/triangulate/tri/TriEdge.h new file mode 100644 index 0000000..14199b9 --- /dev/null +++ b/Sources/geos/include/geos/triangulate/tri/TriEdge.h @@ -0,0 +1,72 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +namespace geos { // geos. +namespace triangulate { // geos.triangulate +namespace tri { // geos.triangulate.tri + + +/** + * Represents an edge in a {@link Tri}, + * to be used as a key for looking up Tris + * while building a triangulation. + * The edge value is normalized to allow lookup + * of adjacent triangles. + * + * @author mdavis + */ +class GEOS_DLL TriEdge { + using Coordinate = geos::geom::Coordinate; + +private: + + void normalize(); + + +public: + + // Members + Coordinate p0; + Coordinate p1; + + TriEdge(const Coordinate& a, const Coordinate& b) + : p0(a) + , p1(b) + { + normalize(); + } + + struct GEOS_DLL HashCode { + std::size_t operator()(const TriEdge& te) const; + }; + + friend bool operator == (const TriEdge& te0, const TriEdge& te1); + + friend std::ostream& operator << (std::ostream& os, const TriEdge& te); + +}; + + + + +} // namespace geos.triangulate.tri +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/tri/TriList.h b/Sources/geos/include/geos/triangulate/tri/TriList.h new file mode 100644 index 0000000..78a622a --- /dev/null +++ b/Sources/geos/include/geos/triangulate/tri/TriList.h @@ -0,0 +1,181 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Geometry; +} +} + +typedef int TriIndex; + +namespace geos { // geos. +namespace triangulate { // geos.triangulate +namespace tri { // geos.triangulate.tri + + +/** + * A utility class that holds the ownership of + * the Tris being passed between different processing + * operations, and also provides access to those + * Tris as a vector of pointers, to match up + * to the JTS API. + * + */ +template +class TriList { + using Coordinate = geos::geom::Coordinate; + using Geometry = geos::geom::Geometry; + using GeometryFactory = geos::geom::GeometryFactory; + +private: + + // Members + std::deque triStore; + std::vector tris; + + // Methods + TriType* create(const Coordinate& c0, const Coordinate& c1, const Coordinate& c2) + { + triStore.emplace_back(c0, c1, c2); + TriType* newTri = &triStore.back(); + return newTri; + } + + +public: + + TriList() {}; + + std::vector& getTris() + { + return tris; + } + + void remove(TriType* tri) + { + // We can leave triStore untouched, just remove + // the pointer from tris. + for (auto it = tris.begin(); it != tris.end(); ++it) { + if (*it == tri) { + tris.erase(it); + return; + } + } + } + + void add(const Coordinate& c0, const Coordinate& c1, const Coordinate& c2) + { + auto* newTri = create(c0, c1, c2); + tris.push_back(newTri); + }; + + void add(std::array& corner) + { + add(corner[0], corner[1], corner[2]); + }; + + double area() + { + double dArea = 0.0; + for (const auto* tri : tris) { + dArea += tri->getArea(); + } + return dArea; + }; + + double length() + { + double dLength = 0.0; + for (const auto* tri : tris) { + dLength += tri->getLength(); + } + return dLength; + }; + + /* public */ + std::size_t + degree(const TriType* tri, TriIndex index) + { + const Coordinate& v = tri->getCoordinate(index); + std::size_t szDegree = 0; + for (auto* t : *this) { + for (TriIndex i = 0; i < 3; i++) { + if (v.equals2D(t->getCoordinate(i))) + szDegree++; + } + } + return szDegree; + } + + void validate() + { + for (auto* tri : *this) { + tri->validate(); + } + } + + std::unique_ptr toGeometry( + const GeometryFactory* geomFact) const + { + std::vector> geoms; + for (auto* tri: tris) { + std::unique_ptr geom = tri->toPolygon(geomFact); + geoms.emplace_back(geom.release()); + } + return geomFact->createGeometryCollection(std::move(geoms)); + } + + friend std::ostream& operator << (std::ostream& os, TriList& triList) + { + os << "TRILIST "; + os << "[" << triList.size() << "] ("; + for (auto* tri: triList) { + os << " " << *tri << "," << std::endl; + } + os << ")"; + return os; + } + + // Support for iterating on TriList + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + size_t size() const { return tris.size(); } + bool empty() const { return tris.empty(); } + iterator begin() { return tris.begin(); } + iterator end() { return tris.end(); } + const_iterator begin() const { return tris.begin(); } + const_iterator end() const { return tris.end(); } + TriType* operator [] (std::size_t index) { return tris[index]; } + +}; + + +} // namespace geos.triangulate.tri +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/triangulate/tri/TriangulationBuilder.h b/Sources/geos/include/geos/triangulate/tri/TriangulationBuilder.h new file mode 100644 index 0000000..09f5a5e --- /dev/null +++ b/Sources/geos/include/geos/triangulate/tri/TriangulationBuilder.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2020 Paul Ramsey + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +} +namespace triangulate { +namespace tri { +class Tri; +} +} +} + +namespace geos { // geos. +namespace triangulate { // geos.triangulate +namespace tri { // geos.triangulate.tri + + +/** + * Builds a triangulation from a set of {@link Tri}s + * by linking adjacent Tris. + * + * @author mdavis + * + */ +class GEOS_DLL TriangulationBuilder { + using Coordinate = geos::geom::Coordinate; + +private: + + // Members + std::unordered_map triMap; + + // Methods + Tri* find(const Coordinate& p0, const Coordinate& p1) const; + void addAdjacent(Tri* tri, Tri* adj, const Coordinate& p0, const Coordinate& p1); + + +public: + + TriangulationBuilder(TriList& triList); + + TriangulationBuilder() {}; + void add(Tri* tri); + + /** + * Builds the triangulation of a set of {@link Tri}s. + * + * @param triList the list of Tris + */ + static void build(TriList& triList); + +}; + + + + + + + +} // namespace geos.triangulate.tri +} // namespace geos.triangulate +} // namespace geos + diff --git a/Sources/geos/include/geos/util.h b/Sources/geos/include/geos/util.h new file mode 100644 index 0000000..fe0825b --- /dev/null +++ b/Sources/geos/include/geos/util.h @@ -0,0 +1,86 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Utility header to retain a bit of backward compatibility. + * Try to avoid including this header directly. + * + **********************************************************************/ + +#ifndef GEOS_UTIL_H +#define GEOS_UTIL_H + +#include +#include +#include +#include + +// +// Private macros definition +// + +namespace geos { +template +void +ignore_unused_variable_warning(T const &) {} + +namespace detail { +using std::make_unique; + +/** Use detail::down_cast(pointer_to_base) as equivalent of + * static_cast(pointer_to_base) with safe checking in debug + * mode. + * + * Only works if no virtual inheritance is involved. + * + * @param f pointer to a base class + * @return pointer to a derived class + */ +template inline To down_cast(From* f) +{ + static_assert( + (std::is_base_of::type>::value), + "target type not derived from source type"); +#if GEOS_DEBUG + assert(f == nullptr || dynamic_cast(f) != nullptr); +#endif + return static_cast(f); +} + +} // namespace detail + +namespace util { + +template +void ensureNoCurvedComponents(const T& geom) +{ + if (geom.hasCurvedComponents()) { + throw UnsupportedOperationException("Curved geometry types are not supported."); + } +} + +template +void ensureNoCurvedComponents(const T* geom) +{ + ensureNoCurvedComponents(*geom); +} + + +} + + +} // namespace geos + +#endif // GEOS_UTIL_H diff --git a/Sources/geos/include/geos/util/Assert.h b/Sources/geos/include/geos/util/Assert.h new file mode 100644 index 0000000..275de02 --- /dev/null +++ b/Sources/geos/include/geos/util/Assert.h @@ -0,0 +1,66 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +// Forward declarations +namespace geos { +namespace geom { +class CoordinateXY; +} +} + +namespace geos { +namespace util { // geos.util + +class GEOS_DLL Assert { +public: + + static void isTrue(bool assertion, const std::string& message); + + static void + isTrue(bool assertion) + { + isTrue(assertion, std::string()); + } + + + static void equals(const geom::CoordinateXY& expectedValue, + const geom::CoordinateXY& actualValue, + const std::string& message); + + static void + equals(const geom::CoordinateXY& expectedValue, + const geom::CoordinateXY& actualValue) + { + equals(expectedValue, actualValue, std::string()); + } + + + static void shouldNeverReachHere(const std::string& message); + + static void + shouldNeverReachHere() + { + shouldNeverReachHere(std::string()); + } +}; + +} // namespace geos.util +} // namespace geos + diff --git a/Sources/geos/include/geos/util/AssertionFailedException.h b/Sources/geos/include/geos/util/AssertionFailedException.h new file mode 100644 index 0000000..dea2c36 --- /dev/null +++ b/Sources/geos/include/geos/util/AssertionFailedException.h @@ -0,0 +1,48 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace util { // geos.util + +/** \class AssertionFailedException util.h geos.h + * \brief Indicates a bug in GEOS code. + */ +class GEOS_DLL AssertionFailedException: public GEOSException { + +public: + + AssertionFailedException() + : + GEOSException("AssertionFailedException", "") + {} + + AssertionFailedException(const std::string& msg) + : + GEOSException("AssertionFailedException", msg) + {} + + ~AssertionFailedException() noexcept override {} +}; + +} // namespace geos.util +} // namespace geos + diff --git a/Sources/geos/include/geos/util/CoordinateArrayFilter.h b/Sources/geos/include/geos/util/CoordinateArrayFilter.h new file mode 100644 index 0000000..0bed5c4 --- /dev/null +++ b/Sources/geos/include/geos/util/CoordinateArrayFilter.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace util { // geos::util + +/* + * A CoordinateFilter that fills a vector of Coordinate const pointers. + */ +class GEOS_DLL CoordinateArrayFilter : public geom::CoordinateInspector { +public: + + CoordinateArrayFilter(std::vector& target) + : pts(target) + {} + + /** + * Destructor. + * Virtual dctor promises appropriate behaviour when someone will + * delete a derived-class object via a base-class pointer. + * http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7 + */ + ~CoordinateArrayFilter() override {} + + template + void filter(const CoordType* coord) + { + pts.push_back(coord); + } + + void filter(const geom::CoordinateXY*) { + assert(0); // not supported + } + + void filter(const geom::CoordinateXYM*) { + assert(0); // not supported + } + +private: + std::vector& pts; // target set reference + + // Declare type as noncopyable + CoordinateArrayFilter(const CoordinateArrayFilter& other) = delete; + CoordinateArrayFilter& operator=(const CoordinateArrayFilter& rhs) = delete; +}; + + + + + +} // namespace geos::util +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/util/GEOSException.h b/Sources/geos/include/geos/util/GEOSException.h new file mode 100644 index 0000000..0ad0eb7 --- /dev/null +++ b/Sources/geos/include/geos/util/GEOSException.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#pragma warning(disable: 4275) // warning C4275: non-DLL-interface std::exception used as base for DLL-interface GEOSException +#endif + +namespace geos { +namespace util { // geos.util + +/** + * + * \brief Base class for all GEOS exceptions. + * + * Use what() to get a readable message. + */ +class GEOS_DLL GEOSException: public std::runtime_error { + +public: + + GEOSException() + : + std::runtime_error("Unknown error") + {} + + GEOSException(std::string const& msg) + : + std::runtime_error(msg) + {} + + GEOSException(std::string const& name, std::string const& msg) + : + std::runtime_error(name + ": " + msg) + {} + +}; + +} // namespace geos.util +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/util/GeometricShapeFactory.h b/Sources/geos/include/geos/util/GeometricShapeFactory.h new file mode 100644 index 0000000..99146e1 --- /dev/null +++ b/Sources/geos/include/geos/util/GeometricShapeFactory.h @@ -0,0 +1,198 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + ********************************************************************** + * + * Last port: util/GeometricShapeFactory.java rev 1.14 (JTS-1.10+) + * (2009-03-19) + * + **********************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +// Forward declarations +namespace geos { +namespace geom { +class Coordinate; +class Envelope; +class Polygon; +class GeometryFactory; +class PrecisionModel; +class LineString; +} +} + +namespace geos { +namespace util { // geos::util + + +/** \brief + * Computes various kinds of common geometric shapes. + * + * Allows various ways of specifying the location and extent of the shapes, + * as well as number of line segments used to form them. + * + * Example: + *

+ *  GeometricShapeFactory gsf(factory);
+ *  gsf.setSize(100);
+ *  gsf.setNumPoints(100);
+ *  gsf.setBase(Coordinate(0, 0));
+ *  std::unique_ptr rect ( gsf.createRectangle() );
+ * 
+ * + */ +class GEOS_DLL GeometricShapeFactory { +protected: + class Dimensions { + public: + Dimensions(); + geom::CoordinateXY base; + geom::CoordinateXY centre; + double width; + double height; + void setBase(const geom::CoordinateXY& newBase); + void setCentre(const geom::CoordinateXY& newCentre); + void setSize(double size); + void setWidth(double nWidth); + void setHeight(double nHeight); + + // Return newly-allocated object, ownership transferred + std::unique_ptr getEnvelope() const; + }; + const geom::GeometryFactory* geomFact; // externally owned + const geom::PrecisionModel* precModel; // externally owned + Dimensions dim; + uint32_t nPts; + + geom::CoordinateXY coord(double x, double y) const; + +public: + + /** + * \brief + * Create a shape factory which will create shapes using the given + * GeometryFactory. + * + * @param factory the factory to use. You need to keep the factory + * alive for the whole GeometricShapeFactory life time. + */ + GeometricShapeFactory(const geom::GeometryFactory* factory); + + virtual + ~GeometricShapeFactory() {} + + /** + * \brief Creates an elliptical arc, as a LineString. + * + * The arc is always created in a counter-clockwise direction. + * + * @param startAng start angle in radians + * @param angExtent size of angle in radians + * @return an elliptical arc + */ + std::unique_ptr createArc(double startAng, double angExtent); + + /** + * \brief Creates an elliptical arc polygon. + * + * The polygon is formed from the specified arc of an ellipse + * and the two radii connecting the endpoints to the centre of + * the ellipse. + * + * @param startAng start angle in radians + * @param angExt size of angle in radians + * @return an elliptical arc polygon + */ + std::unique_ptr createArcPolygon(double startAng, double angExt); + + /** + * \brief Creates a circular Polygon. + * + * @return a circle + */ + std::unique_ptr createCircle(); + + /** + * \brief Creates a rectangular Polygon. + * + * @return a rectangular Polygon + */ + std::unique_ptr createRectangle(); + + /** + * \brief + * Sets the location of the shape by specifying the base coordinate + * (which in most cases is the * lower left point of the envelope + * containing the shape). + * + * @param base the base coordinate of the shape + */ + void setBase(const geom::CoordinateXY& base); + + /** + * \brief + * Sets the location of the shape by specifying the centre of + * the shape's bounding box + * + * @param centre the centre coordinate of the shape + */ + void setCentre(const geom::CoordinateXY& centre); + + /** + * \brief Sets the height of the shape. + * + * @param height the height of the shape + */ + void setHeight(double height); + + /** + * \brief Sets the total number of points in the created Geometry + */ + void setNumPoints(uint32_t nNPts); + + /** + * \brief + * Sets the size of the extent of the shape in both x and y directions. + * + * @param size the size of the shape's extent + */ + void setSize(double size); + + /** + * \brief Sets the width of the shape. + * + * @param width the width of the shape + */ + void setWidth(double width); + +}; + +} // namespace geos::util +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/util/IllegalArgumentException.h b/Sources/geos/include/geos/util/IllegalArgumentException.h new file mode 100644 index 0000000..5a2bb3c --- /dev/null +++ b/Sources/geos/include/geos/util/IllegalArgumentException.h @@ -0,0 +1,50 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace util { // geos::util + +/** + * \brief Indicates one or more illegal arguments. + * + * This exception is thrown - for example - when + * trying to apply set-theoretic methods to a + * GeometryCollection object. + */ +class GEOS_DLL IllegalArgumentException: public GEOSException { +public: + IllegalArgumentException() + : + GEOSException("IllegalArgumentException", "") + {} + + IllegalArgumentException(const std::string& msg) + : + GEOSException("IllegalArgumentException", msg) + {} + + ~IllegalArgumentException() noexcept override {} +}; + +} // namespace geos::util +} // namespace geos + diff --git a/Sources/geos/include/geos/util/IllegalStateException.h b/Sources/geos/include/geos/util/IllegalStateException.h new file mode 100644 index 0000000..ce5ba83 --- /dev/null +++ b/Sources/geos/include/geos/util/IllegalStateException.h @@ -0,0 +1,42 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2011 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace geos { +namespace util { // geos::util + +/// Indicates an illegal state +class GEOS_DLL IllegalStateException: public GEOSException { +public: + IllegalStateException() + : + GEOSException("IllegalStateException", "") + {} + + IllegalStateException(const std::string& msg) + : + GEOSException("IllegalStateException", msg) + {} + + ~IllegalStateException() noexcept override {} +}; + +} // namespace geos::util +} // namespace geos diff --git a/Sources/geos/include/geos/util/Interrupt.h b/Sources/geos/include/geos/util/Interrupt.h new file mode 100644 index 0000000..5c8da22 --- /dev/null +++ b/Sources/geos/include/geos/util/Interrupt.h @@ -0,0 +1,89 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2012 Sandro Santilli + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace util { // geos::util + +/** \brief Used to manage interruption requests and callbacks. */ +class GEOS_DLL Interrupt { + +public: + + typedef void (Callback)(void); + + /** + * Request interruption of operations + * + * Operations will be terminated by a GEOSInterrupt + * exception at first occasion, by the first thread + * to check for an interrupt request. + */ + static void request(); + + /** Cancel a pending interruption request */ + static void cancel(); + + /** Check if an interruption request is pending */ + static bool check(); + + /** \brief + * Register a callback that will be invoked by all threads + * before checking for interruption requests. + * + * NOTE that interruption request checking may happen + * frequently so the callback should execute quickly. + * + * The callback can be used to call Interrupt::request() + * or Interrupt::requestForCurrentThread(). + */ + static Callback* registerCallback(Callback* cb); + + /** + * Invoke the callback, if any. Process pending interruption, if any. + * + */ + static void process(); + + /* Perform the actual interruption (simply throw an exception) */ + static void interrupt(); + +}; + +class GEOS_DLL CurrentThreadInterrupt { +public: + typedef int (ThreadCallback)(void*); + + /** \brief + * Register a callback that will be invoked by the current thread + * to check if it should be interrupted. If the callback returns + * True, the thread will be interrupted. The previously registered + * callback, if any, will be returned. + */ + static ThreadCallback* registerCallback(ThreadCallback* cb, void* data); + + static void process(); + + static void interrupt(); +}; + + +} // namespace geos::util +} // namespace geos + + +inline void GEOS_CHECK_FOR_INTERRUPTS() { geos::util::Interrupt::process(); geos::util::CurrentThreadInterrupt::process(); } diff --git a/Sources/geos/include/geos/util/Machine.h b/Sources/geos/include/geos/util/Machine.h new file mode 100644 index 0000000..33d2e1c --- /dev/null +++ b/Sources/geos/include/geos/util/Machine.h @@ -0,0 +1,26 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2009 Vivid Solutions Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +/** + * Check endianness of current machine. + * @return 0 for big_endian | xdr; 1 == little_endian | ndr + */ +inline int +getMachineByteOrder() +{ + static int endian_check = 1; // don't modify !! + return *((char*)&endian_check); +} diff --git a/Sources/geos/include/geos/util/TopologyException.h b/Sources/geos/include/geos/util/TopologyException.h new file mode 100644 index 0000000..49b9a81 --- /dev/null +++ b/Sources/geos/include/geos/util/TopologyException.h @@ -0,0 +1,64 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include // to be removed when .inl is available + +#include + +namespace geos { +namespace util { // geos.util + +/** + * \class TopologyException util.h geos.h + * + * \brief + * Indicates an invalid or inconsistent topological situation encountered + * during processing + */ +class GEOS_DLL TopologyException: public GEOSException { +public: + TopologyException() + : + GEOSException("TopologyException", "") + {} + + TopologyException(const std::string& msg) + : + GEOSException("TopologyException", msg) + {} + + TopologyException(const std::string& msg, const geom::Coordinate& newPt) + : + GEOSException("TopologyException", msg + " at " + newPt.toString()), + pt(newPt) + {} + + ~TopologyException() noexcept override {} + geom::Coordinate& + getCoordinate() + { + return pt; + } +private: + geom::Coordinate pt; +}; + +} // namespace geos::util +} // namespace geos + diff --git a/Sources/geos/include/geos/util/UniqueCoordinateArrayFilter.h b/Sources/geos/include/geos/util/UniqueCoordinateArrayFilter.h new file mode 100644 index 0000000..5b0afb0 --- /dev/null +++ b/Sources/geos/include/geos/util/UniqueCoordinateArrayFilter.h @@ -0,0 +1,114 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class +#endif + +namespace geos { +namespace util { // geos::util + +/* + * A CoordinateFilter that fills a vector of Coordinate const pointers. + * The set of coordinates contains no duplicate points. + * + * Last port: util/UniqueCoordinateArrayFilter.java rev. 1.17 + */ +class GEOS_DLL UniqueCoordinateArrayFilter : public geom::CoordinateInspector { +public: + /** + * Constructs a CoordinateArrayFilter. + * + * @param target The destination set. + */ + UniqueCoordinateArrayFilter(std::vector& target) + : pts(target) + , maxUnique(NO_COORD_INDEX) + {} + + UniqueCoordinateArrayFilter(std::vector& target, std::size_t p_maxUnique) + : pts(target) + , maxUnique(p_maxUnique) + {} + + /** + * Destructor. + * Virtual dctor promises appropriate behaviour when someone will + * delete a derived-class object via a base-class pointer. + * http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7 + */ + ~UniqueCoordinateArrayFilter() override {} + + /** + * Performs a filtering operation with or on coord in "read-only" mode. + * @param coord The "read-only" Coordinate to which + * the filter is applied. + */ + template + void filter(const CoordType* coord) + { + if(uniqPts.insert(coord).second) { + // TODO make `pts` a CoordinateSequence rather than coercing the type + pts.push_back(coord); + } + if(maxUnique != NO_COORD_INDEX && uniqPts.size() > maxUnique) { + done = true; + } + } + + void filter(const geom::CoordinateXY*) { + assert(0); // not supported + } + + + void filter(const geom::CoordinateXYM*) { + assert(0); // not supported + } + + bool isDone() const override { + return done; + } + +private: + std::vector& pts; // target set reference + std::set uniqPts; // unique points set + std::size_t maxUnique; // stop visiting when we have this many unique coordinates + bool done = false; + + // Declare type as noncopyable + UniqueCoordinateArrayFilter(const UniqueCoordinateArrayFilter& other) = delete; + UniqueCoordinateArrayFilter& operator=(const UniqueCoordinateArrayFilter& rhs) = delete; +}; + + +} // namespace geos::util +} // namespace geos + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/Sources/geos/include/geos/util/UnsupportedOperationException.h b/Sources/geos/include/geos/util/UnsupportedOperationException.h new file mode 100644 index 0000000..50c020d --- /dev/null +++ b/Sources/geos/include/geos/util/UnsupportedOperationException.h @@ -0,0 +1,52 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +#include + +#include + +namespace geos { +namespace util { // geos::util + +/** + * \class UnsupportedOperationException util.h geos.h + * + * \brief Indicates that the requested operation is unsupported. + * + * This exception is thrown - for example - when requesting the + * X or Y member of an empty Point + */ +class GEOS_DLL UnsupportedOperationException: public GEOSException { +public: + UnsupportedOperationException() + : + GEOSException("UnsupportedOperationException", "") + {} + + UnsupportedOperationException(const std::string& msg) + : + GEOSException("UnsupportedOperationException", msg) + {} + + ~UnsupportedOperationException() noexcept override {} +}; + +} // namespace geos::util +} // namespace geos + diff --git a/Sources/geos/include/geos/util/math.h b/Sources/geos/include/geos/util/math.h new file mode 100644 index 0000000..8c6acb5 --- /dev/null +++ b/Sources/geos/include/geos/util/math.h @@ -0,0 +1,45 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2001-2002 Vivid Solutions Inc. + * Copyright (C) 2006 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +namespace geos { +namespace util { + +/// Symmetric Rounding Algorithm +double sym_round(double val); + +/// Asymmetric Rounding Algorithm +double java_math_round(double val); + +/// Equivalent to Java Math.rint() +double rint_vc(double val); + + +/// Default rounding method for GEOS +/// +/// @note Always use this rounding method, to easy easy switching +/// between different rounding method for the whole codebase. +inline double +round(double val) +{ + return java_math_round(val); +} + +/// Equivalent to std::clamp() in C++17 +double clamp(double x, double min, double max); + +} +} // namespace geos::util diff --git a/Sources/geos/include/geos/util/string.h b/Sources/geos/include/geos/util/string.h new file mode 100644 index 0000000..374dfa0 --- /dev/null +++ b/Sources/geos/include/geos/util/string.h @@ -0,0 +1,31 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2022 ISciences LLC + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ + +#pragma once + +#include + +namespace geos { +namespace util { + +bool endsWith(const std::string & s, const std::string & suffix); +bool endsWith(const std::string & s, char suffix); + +bool startsWith(const std::string & s, const std::string & prefix); +bool startsWith(const std::string & s, char prefix); + +void toUpper(std::string& s); + +} +} diff --git a/Sources/geos/include/geos/vend/include_nlohmann_json.hpp b/Sources/geos/include/geos/vend/include_nlohmann_json.hpp new file mode 100644 index 0000000..494e8e7 --- /dev/null +++ b/Sources/geos/include/geos/vend/include_nlohmann_json.hpp @@ -0,0 +1,24 @@ +/********************************************************************** + * + * GEOS - Geometry Engine Open Source + * http://geos.osgeo.org + * + * Copyright (C) 2021 Jared Erickson + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU Lesser General Public Licence as published + * by the Free Software Foundation. + * See the COPYING file for more information. + * + **********************************************************************/ +#ifndef GEOS_VEND_JSON +#define GEOS_VEND_JSON + +#ifdef nlohmann + #error "GEOS modifies the nlohmann define " +#endif +#define nlohmann geos_nlohmann +#include "geos/vend/json.hpp" +#undef nlohmann + +#endif \ No newline at end of file diff --git a/Sources/geos/include/geos/vend/json.hpp b/Sources/geos/include/geos/vend/json.hpp new file mode 100644 index 0000000..9a2fc4e --- /dev/null +++ b/Sources/geos/include/geos/vend/json.hpp @@ -0,0 +1,24765 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // nullptr_t +#include // exception +#if JSON_DIAGNOSTICS + #include // accumulate +#endif +#include // runtime_error +#include // to_string +#include // vector + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + + +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// #include + + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType, \ + class CustomBaseClass> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept +#endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} + +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != StringType::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +template +inline StringType escape(StringType s) +{ + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +template +static void unescape(StringType& s) +{ + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // size_t + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static JSON_INLINE_VARIABLE constexpr T value{}; +}; + +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif + +template +inline constexpr std::array make_array(Args&& ... args) +{ + return std::array {{static_cast(std::forward(args))...}}; +} + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple +#include // char_traits + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // random_access_iterator_tag + +// #include + +// #include + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); + +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN + +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); + +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector + + // #include + + + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN + + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template + struct adl_serializer; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> + class basic_json; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; + + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; + + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ + +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; + +///////////////// +// char_traits // +///////////////// + +// Primary template of char_traits calls std char_traits +template +struct char_traits : std::char_traits +{}; + +// Explicitly define char traits for unsigned char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = unsigned char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +// Explicitly define char traits for signed char since it is not standard +template<> +struct char_traits : std::char_traits +{ + using char_type = signed char; + using int_type = uint64_t; + + // Redefine to_int_type function + static int_type to_int_type(char_type c) noexcept + { + return static_cast(c); + } + + static char_type to_char_type(int_type i) noexcept + { + return static_cast(i); + } + + static constexpr int_type eof() noexcept + { + return static_cast(EOF); + } +}; + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + + static constexpr auto value = + conjunction < + is_constructible, + is_detected_exact>::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template