pax_global_header00006660000000000000000000000064127171541350014520gustar00rootroot0000000000000052 comment=c69f225276f795c7f35eca1c2b317d7fd11a9d66 .editorconfig000066400000000000000000000005221271715413500135400ustar00rootroot00000000000000# http://editorconfig.org # top-most EditorConfig file root = true # these are the defaults [*] charset = utf-8 end_of_line = lf trim_trailing_whitespace = true insert_final_newline = true # C files want space [*.{c,h}] indent_style = space indent_size = 2 # Makefiles want tab indentation [{Makefile.in,Makefile}] indent_style = tab .gitignore000066400000000000000000000005271271715413500130600ustar00rootroot00000000000000*.zip *.gz *.ine *.ce *.he *.o *.lo *.la .libs/ .deps/ compile depcomp missing src/stamp-h1 aclocal.m4 rttopo.pc m4/ Makefile Makefile.in autom4te.cache/ config.guess config.log config.status config.sub configure install-sh librttopo_geom.h libtool ltmain.sh rtin_wkt_lex.c rtin_wkt_parse.c rtin_wkt_parse.h rttopo_config.h rttopo_config.h.in .gitlab-ci.yml000066400000000000000000000010141271715413500135140ustar00rootroot00000000000000test: script: - apt-get update -qq - apt-get install -y build-essential autoconf libtool - apt-get install -y zip # for make dist which also produces .zip - apt-get install -y git2cl # for make dist which creates ChangeLog - echo deb http://ftp.debian.org/debian sid main >> /etc/apt/sources.list - apt-get update -qq - apt-cache search libgeos - apt-get install -t sid -y libgeos-dev - geos-config --version || true - ./autogen.sh - ./configure - make - make distcheck COPYING000066400000000000000000000432541271715413500121270ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; 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. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. CREDITS000066400000000000000000000007501271715413500121060ustar00rootroot00000000000000Release 1.0.0 of the RT Topology Library was funded by "Regione Toscana - SITA" (CIG: 6445512CC1) The library is based on version 2.2 of PostGIS's liblwgeom, which had contributions by many individual and corporate contributions, see http://postgis.net/docs/manual-2.2/postgis_introduction.html Debian packages were created by Bas Couwenberg Project Steering Committee is formed by: Andrea Peri (Regione Toscana) Sandro Santilli (PostGIS) Alessandro Furieri (Spatialite) ChangeLog000066400000000000000000000661441271715413500126510ustar00rootroot000000000000002016-05-18 Sandro Santilli * configure.ac: Release 1.0.0 2016-05-07 Sandro Santilli * ChangeLog: Add ChangeLog file in repository 2016-05-07 Sandro Santilli * HOWTO_RELEASE, Makefile.am: Rework ChangeLog generation rule Generation target is now "cl" and also works when build dir is not below source dir. ChangeLog is pushed to repo. 2016-05-07 Sandro Santilli * .gitignore: Ignore more generated files 2016-05-07 Sandro Santilli * configure.ac: Set version to 1.0.0-RC3 (and update admin email) 2016-05-07 Sandro Santilli * src/rtutil.c: Release GEOS context on rtgeom_finish Patch by Alessandro Furieri 2016-05-07 Sandro Santilli * headers/librttopo_geom.h.in: Rewrite the introductory section of docs 2016-05-06 Sandro Santilli * src/rtgeom_geos.c: Do not override pre-initialized GEOS handler in rttopo context Fixes leaks of GEOS contexts. Thanks Alessandro Furieri for spotting this. 2016-05-06 Sandro Santilli * .editorconfig, headers/librttopo.h, headers/librttopo_geom.h.in, src/box2d.c, src/bytebuffer.c, src/bytebuffer.h, src/g_box.c, src/g_serialized.c, src/g_util.c, src/librttopo_geom_internal.h, src/measures.c, src/measures.h, src/measures3d.c, src/measures3d.h, src/ptarray.c, src/rtalgorithm.c, src/rtcircstring.c, src/rtcollection.c, src/rtcompound.c, src/rtcurvepoly.c, src/rtgeodetic.c, src/rtgeodetic.h, src/rtgeom.c, src/rtgeom_api.c, src/rtgeom_debug.c, src/rtgeom_geos.c, src/rtgeom_geos_clean.c, src/rtgeom_geos_node.c, src/rtgeom_geos_split.c, src/rtgeom_log.h, src/rtgeom_topo.c, src/rthomogenize.c, src/rtin_geojson.c, src/rtin_twkb.c, src/rtin_wkb.c, src/rtiterator.c, src/rtline.c, src/rtlinearreferencing.c, src/rtmline.c, src/rtmpoint.c, src/rtmpoly.c, src/rtout_encoded_polyline.c, src/rtout_geojson.c, src/rtout_gml.c, src/rtout_kml.c, src/rtout_svg.c, src/rtout_twkb.c, src/rtout_twkb.h, src/rtout_wkb.c, src/rtout_wkt.c, src/rtout_x3d.c, src/rtpoint.c, src/rtpoly.c, src/rtprint.c, src/rtpsurface.c, src/rtspheroid.c, src/rtstroke.c, src/rttin.c, src/rttree.c, src/rttree.h, src/rttriangle.c, src/rtutil.c, src/stringbuffer.c, src/stringbuffer.h, src/varint.c: Style only commit: use 2-spaces instead of tabs, trim ending newlines 2016-05-03 Sandro Santilli * HOWTO_RELEASE: Add upload destination 2016-05-01 Sandro Santilli * .gitlab-ci.yml: Have gitlab-ci install git2cl 2016-05-01 Sandro Santilli * Makefile.am: Include an auto-generated ChangeLog file in distribution Closes #9 2016-04-30 Sandro Santilli * CREDITS: Add PSC info 2016-04-30 Sandro Santilli * CREDITS: Add Bas credits 2016-04-30 Sandro Santilli * README.md: Add link to development mailing list 2016-04-26 Sandro Santilli * README.md: Use markdown link for windows build reference 2016-04-26 Sandro Santilli * Makefile.am, README.md, doc/BUILDING-ON-WINDOWS.md: Add windows build instructions 2016-04-26 Sandro Santilli * .gitlab-ci.yml: Install zip tool for dist 2016-04-26 Sandro Santilli * HOWTO_RELEASE: Tweak instruction to push annotated tag 2016-04-26 Sandro Santilli * HOWTO_RELEASE: Add HOWTO_RELEASE file This is not distributed as part of the tarball 2016-04-26 Sandro Santilli * .gitlab-ci.yml: Have gitlab-ci run distcheck The 'distcheck' rule will test for install/uninstall targets 2016-04-26 Sandro Santilli * README.md: Add license info 2016-04-26 Sandro Santilli * CREDITS, Makefile.am, doc/g_serialized.txt, g_serialized.txt: Add credits, move g_serialized under doc 2016-04-26 Sandro Santilli * configure.ac: Set version to 1.0.0-RC2 2016-04-26 Sandro Santilli * configure.ac: Drop trailing newlines, as per .editorconfig 2016-04-26 Sandro Santilli * Makefile.am: Distribute DATASTORES.md 2016-04-25 Sandro Santilli * README.md, doc/DATASTORES.md: Add list of known data stores 2016-04-25 Sandro Santilli * README.md: Reword about section 2016-04-25 Sandro Santilli * src/rtprint.c: Do not use dynamic arrays 2016-04-25 Sandro Santilli * README.md: Reword the About part 2016-04-25 Sandro Santilli * Makefile.am, README.md, README.topo: Merge the two readmes, add info about building 2016-04-25 Sandro Santilli * src/Makefile.am: Remove unexistent file from noinst_HEADERS 2016-04-25 Sandro Santilli * makefile.vc, nmake.opt: Add build scripts for MSVC Files provided by Alessandro Furieri 2016-04-25 Sandro Santilli * src/Makefile.am, src/rtgeodetic_tree.c, src/rtgeodetic_tree.h: Drop unused geodetic tree files 2016-04-25 Alessandro Furieri * .gitignore, Makefile, Makefile.am, Version.config, aclocal.m4, autogen.sh, configure.ac, headers/Makefile.am, headers/librttopo.h, headers/librttopo_geom.h.in, macros/intltool.m4, macros/lib-ld.m4, macros/lib-link.m4, macros/lib-prefix.m4, macros/libtool.m4, macros/ltoptions.m4, macros/ltsugar.m4, macros/ltversion.m4, macros/lt~obsolete.m4, macros/po.m4, macros/progtest.m4, rttopo.pc.in, src/Makefile.am, src/Makefile.in, src/librttopo.h, src/librttopo_geom.h.in, src/librttopo_geom_internal.h, src/rttopo_config.h.in: Switch to automake based builds 2016-04-21 Sandro Santilli * src/librttopo_geom.h.in: Update copyright info 2016-04-21 Sandro Santilli * src/librttopo_geom.h.in: Remove trailing spaces 2016-04-21 Sandro Santilli * src/rtgeom_topo.c: Update copyright notice 2016-04-21 Sandro Santilli * src/rtgeom_topo.c: Remove trailing spaces 2016-04-21 Sandro Santilli * .editorconfig: Add an EditorConfig configuration See http://editorconfig.org/ 2016-04-21 Sandro Santilli * src/rtgeom_topo.c: Add last port info 2016-04-21 Sandro Santilli * src/rtgeom_topo.c: Fix debug print 2016-04-21 Sandro Santilli * src/rtgeom_topo.c: rtt_AddEdge*: avoid db access for edges known to be dangling 2016-04-20 Sandro Santilli * src/rtgeom_topo.c: Fix bogus port of side-location-conflict check Rectifies 891d7bb0b5f25bf73ccd3a304febd96b59279527 2016-04-20 Sandro Santilli * src/rtgeom_topo.c: Improves snapping robustness See http://trac.osgeo.org/postgis/ticket/3402 2016-04-20 Sandro Santilli * src/rtgeom_topo.c: Fixes geometry-intersects-edge exception when snapping twice to the same pointset. See http://trac.osgeo.org/postgis/ticket/3412 2016-04-19 Sandro Santilli * README.md: Add a mention of the gilab.com mirror 2016-04-19 Sandro Santilli * README.md: Add link to osgeo/gogs as official code repo 2016-04-19 Sandro Santilli * configure.ac, src/Makefile.in, src/box2d.c, src/bytebuffer.c, src/bytebuffer.h, src/g_box.c, src/g_serialized.c, src/g_util.c, src/librttopo.h, src/librttopo_geom.h.in, src/librttopo_geom_internal.h, src/librttopo_internal.h, src/measures.c, src/measures.h, src/measures3d.c, src/measures3d.h, src/ptarray.c, src/rtalgorithm.c, src/rtcircstring.c, src/rtcollection.c, src/rtcompound.c, src/rtcurvepoly.c, src/rtgeodetic.c, src/rtgeodetic.h, src/rtgeodetic_tree.c, src/rtgeodetic_tree.h, src/rtgeom.c, src/rtgeom_api.c, src/rtgeom_debug.c, src/rtgeom_geos.c, src/rtgeom_geos.h, src/rtgeom_geos_clean.c, src/rtgeom_geos_node.c, src/rtgeom_geos_split.c, src/rtgeom_log.h, src/rtgeom_topo.c, src/rthomogenize.c, src/rtin_geojson.c, src/rtin_twkb.c, src/rtin_wkb.c, src/rtiterator.c, src/rtline.c, src/rtlinearreferencing.c, src/rtmcurve.c, src/rtmline.c, src/rtmpoint.c, src/rtmpoly.c, src/rtmsurface.c, src/rtout_encoded_polyline.c, src/rtout_geojson.c, src/rtout_gml.c, src/rtout_kml.c, src/rtout_svg.c, src/rtout_twkb.c, src/rtout_twkb.h, src/rtout_wkb.c, src/rtout_wkt.c, src/rtout_x3d.c, src/rtpoint.c, src/rtpoly.c, src/rtprint.c, src/rtpsurface.c, src/rtspheroid.c, src/rtstroke.c, src/rttin.c, src/rttopo_config.h.in, src/rttree.c, src/rttree.h, src/rttriangle.c, src/rtutil.c, src/stringbuffer.c, src/stringbuffer.h, src/varint.c, src/varint.h: Update homepage to use osgeo/gogs 2016-04-19 Sandro Santilli * src/box2d.c, src/rtgeodetic_tree.c, src/rtgeodetic_tree.h, src/rtprint.c, src/rttopo_config.h.in, src/rttree.c, src/rttree.h, src/rtutil.c: Add missing copyright notices 2016-04-19 Sandro Santilli * src/librttopo_geom.h.in, src/rtutil.c: Add rtgeom_finish function, and improve dox for rtgeom_init 2016-04-19 Sandro Santilli * src/rtutil.c: Allow calling rtgeom_init with all null pointers 2016-04-18 Sandro Santilli * src/rtgeom_topo.c: Fix crash on face-collapsing edge change See https://trac.osgeo.org/postgis/ticket/3463 2016-04-18 Sandro Santilli * src/rtgeom_topo.c: Check for side-location conflicts when adding a new edge It is possible to trigger such failure adding an edge to a corrupted topology. Test added. See https://trac.osgeo.org/postgis/ticket/3464 2016-04-18 Sandro Santilli * src/rtgeom_topo.c: PRId64 to RTTFMT_ELEMID 2016-04-18 Sandro Santilli * src/rtgeom_topo.c: Give more decimal digits in edge-motion collision detection point 2016-04-18 Sandro Santilli * .gitignore, README.topo, configure.ac, src/Makefile.in, src/box2d.c, src/bytebuffer.c, src/g_box.c, src/g_serialized.c, src/g_util.c, src/librtgeom.h.in, src/librtgeom_internal.h, src/librtgeom_topo.h, src/librtgeom_topo_internal.h, src/librttopo.h, src/librttopo_geom.h.in, src/librttopo_geom_internal.h, src/librttopo_internal.h, src/measures.h, src/ptarray.c, src/rtalgorithm.c, src/rtcircstring.c, src/rtcollection.c, src/rtcompound.c, src/rtcurvepoly.c, src/rtgeodetic.c, src/rtgeodetic_tree.c, src/rtgeom.c, src/rtgeom_api.c, src/rtgeom_debug.c, src/rtgeom_geos.c, src/rtgeom_geos.h, src/rtgeom_geos_clean.c, src/rtgeom_geos_node.c, src/rtgeom_geos_split.c, src/rtgeom_log.h, src/rtgeom_topo.c, src/rthomogenize.c, src/rtin_geojson.c, src/rtin_twkb.c, src/rtin_wkb.c, src/rtiterator.c, src/rtline.c, src/rtlinearreferencing.c, src/rtmcurve.c, src/rtmline.c, src/rtmpoint.c, src/rtmpoly.c, src/rtmsurface.c, src/rtout_encoded_polyline.c, src/rtout_geojson.c, src/rtout_gml.c, src/rtout_kml.c, src/rtout_svg.c, src/rtout_twkb.h, src/rtout_wkb.c, src/rtout_wkt.c, src/rtout_x3d.c, src/rtpoint.c, src/rtpoly.c, src/rtprint.c, src/rtpsurface.c, src/rtspheroid.c, src/rtstroke.c, src/rttin.c, src/rttree.c, src/rttriangle.c, src/rtutil.c, src/stringbuffer.c, src/stringbuffer.h, src/varint.c, src/varint.h: Rename librtgeom to librttopo Renames: librtgeom.h to librttopo_geom.h librtgeom_internal.h to librttopo_geom_internal.h librtgeom_topo*.h to librttopo*.h librtgeom.{so,a} to librttopo.{so,a} See #1 2016-04-18 Sandro Santilli * configure.ac: Require GEOS-3.5.0 as the minimum version 2016-04-18 Sandro Santilli * .gitlab-ci.yml: Install geos-3.5.0 2016-04-18 Sandro Santilli * README.md: Update build badge 2016-04-18 Sandro Santilli * Makefile: Do not make "clean" and "uninstall" dependent on running configure Fixes #2 2016-04-18 Sandro Santilli * src/Makefile.in: Include varint module in the build 2016-04-18 Sandro Santilli * configure.ac: Drop references to PostGIS/liblwgeom 2015-12-15 Sandro Santilli * Makefile: Add missing Makefile 2015-12-15 Sandro Santilli * .gitlab-ci.yml: See which version of GEOS are available 2015-12-15 Sandro Santilli * src/Makefile.in: Fix clean rule 2015-12-15 Sandro Santilli * src/librtgeom.h.in, src/rtutil.c: Remove the possibility to use a pre-initialized GEOS context This is to allow for intercepting geos messages from the library, which would otherwise require changing the passed context. 2015-12-14 Sandro Santilli * .gitignore: Ignore the right Makefile, not the root one 2015-12-14 Sandro Santilli * src/box2d.c, src/bytebuffer.c, src/bytebuffer.h, src/g_box.c, src/g_serialized.c, src/g_util.c, src/librtgeom.h.in, src/librtgeom_internal.h, src/librtgeom_topo.h, src/librtgeom_topo_internal.h, src/measures.c, src/measures.h, src/measures3d.c, src/measures3d.h, src/ptarray.c, src/rtalgorithm.c, src/rtcircstring.c, src/rtcollection.c, src/rtcompound.c, src/rtcurvepoly.c, src/rtgeodetic.c, src/rtgeodetic.h, src/rtgeodetic_tree.c, src/rtgeodetic_tree.h, src/rtgeom.c, src/rtgeom_api.c, src/rtgeom_debug.c, src/rtgeom_geos.c, src/rtgeom_geos.h, src/rtgeom_geos_clean.c, src/rtgeom_geos_node.c, src/rtgeom_geos_split.c, src/rtgeom_log.h, src/rtgeom_topo.c, src/rthomogenize.c, src/rtin_geojson.c, src/rtin_twkb.c, src/rtin_wkb.c, src/rtiterator.c, src/rtline.c, src/rtlinearreferencing.c, src/rtmcurve.c, src/rtmline.c, src/rtmpoint.c, src/rtmpoly.c, src/rtmsurface.c, src/rtout_encoded_polyline.c, src/rtout_geojson.c, src/rtout_gml.c, src/rtout_kml.c, src/rtout_svg.c, src/rtout_twkb.c, src/rtout_twkb.h, src/rtout_wkb.c, src/rtout_wkt.c, src/rtout_x3d.c, src/rtpoint.c, src/rtpoly.c, src/rtprint.c, src/rtpsurface.c, src/rtspheroid.c, src/rtstroke.c, src/rttin.c, src/rttopo_config.h.in, src/rttree.c, src/rttree.h, src/rttriangle.c, src/rtutil.c, src/snprintf.c, src/stringbuffer.c, src/stringbuffer.h, src/varint.c, src/varint.h: Update copyright headers 2015-12-14 Sandro Santilli * COPYING: Add COPYING file 2015-12-12 Sandro Santilli * README.md: Add build status badge 2015-12-12 Sandro Santilli * TODO: Remove done item 2015-12-12 Sandro Santilli * src/librtgeom.h.in, src/librtgeom_internal.h, src/rtutil.c: Fix re-entrancy of custom allocators and loggers 2015-12-12 Sandro Santilli * TODO: Updated 2015-12-12 Sandro Santilli * Makefile.in, box2d.c, bytebuffer.c, bytebuffer.h, configure.ac, g_box.c, g_serialized.c, g_util.c, librtgeom.h.in, librtgeom_internal.h, librtgeom_topo.h, librtgeom_topo_internal.h, measures.c, measures.h, measures3d.c, measures3d.h, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtcurvepoly.c, rtgeodetic.c, rtgeodetic.h, rtgeodetic_tree.c, rtgeodetic_tree.h, rtgeom.c, rtgeom_api.c, rtgeom_debug.c, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rtgeom_log.h, rtgeom_topo.c, rthomogenize.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmcurve.c, rtmline.c, rtmpoint.c, rtmpoly.c, rtmsurface.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_twkb.h, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtprint.c, rtpsurface.c, rtspheroid.c, rtstroke.c, rttin.c, rttopo_config.h.in, rttree.c, rttree.h, rttriangle.c, rtutil.c, snprintf.c, src/Makefile.in, src/box2d.c, src/bytebuffer.c, src/bytebuffer.h, src/g_box.c, src/g_serialized.c, src/g_util.c, src/librtgeom.h.in, src/librtgeom_internal.h, src/librtgeom_topo.h, src/librtgeom_topo_internal.h, src/measures.c, src/measures.h, src/measures3d.c, src/measures3d.h, src/ptarray.c, src/rtalgorithm.c, src/rtcircstring.c, src/rtcollection.c, src/rtcompound.c, src/rtcurvepoly.c, src/rtgeodetic.c, src/rtgeodetic.h, src/rtgeodetic_tree.c, src/rtgeodetic_tree.h, src/rtgeom.c, src/rtgeom_api.c, src/rtgeom_debug.c, src/rtgeom_geos.c, src/rtgeom_geos.h, src/rtgeom_geos_clean.c, src/rtgeom_geos_node.c, src/rtgeom_geos_split.c, src/rtgeom_log.h, src/rtgeom_topo.c, src/rthomogenize.c, src/rtin_geojson.c, src/rtin_twkb.c, src/rtin_wkb.c, src/rtiterator.c, src/rtline.c, src/rtlinearreferencing.c, src/rtmcurve.c, src/rtmline.c, src/rtmpoint.c, src/rtmpoly.c, src/rtmsurface.c, src/rtout_encoded_polyline.c, src/rtout_geojson.c, src/rtout_gml.c, src/rtout_kml.c, src/rtout_svg.c, src/rtout_twkb.c, src/rtout_twkb.h, src/rtout_wkb.c, src/rtout_wkt.c, src/rtout_x3d.c, src/rtpoint.c, src/rtpoly.c, src/rtprint.c, src/rtpsurface.c, src/rtspheroid.c, src/rtstroke.c, src/rttin.c, src/rttopo_config.h.in, src/rttree.c, src/rttree.h, src/rttriangle.c, src/rtutil.c, src/snprintf.c, src/stringbuffer.c, src/stringbuffer.h, src/varint.c, src/varint.h, stringbuffer.c, stringbuffer.h, varint.c, varint.h: Move sources under a src/ dir 2015-12-12 Sandro Santilli * librtgeom.h.in, librtgeom_internal.h, ptarray.c, rtgeom.c: Prefix AFFINE 2015-12-12 Sandro Santilli * g_box.c, g_serialized.c, librtgeom.h.in, measures.c, measures3d.c, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcompound.c, rtgeodetic.c, rtgeodetic_tree.c, rtgeom.c, rtgeom_api.c, rtgeom_geos.c, rtgeom_geos_clean.c, rtgeom_geos_split.c, rtgeom_topo.c, rtin_twkb.c, rtin_wkb.c, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmpoint.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtprint.c, rtpsurface.c, rtspheroid.c, rtstroke.c, rttin.c, rttree.c, rttriangle.c: Prefix getPoint* with rt_ 2015-12-11 Sandro Santilli * Makefile.in, librtgeom.h.in, rtgeodetic.c, rtgeodetic.h, rtgeodetic_tree.c, rtgeodetic_tree.h, rtspheroid.c: Revert "Remove geodetic and spheroid functions" This reverts commit cb34f54ca9c15ad2d65eac67dfdd5e94eae475f3. Conflicts: Makefile.in Also adapted all signatures to re-entrancy mode. Closes #1 2015-12-11 Sandro Santilli * TODO: Add more missing symbols from spatialite 2015-12-11 Sandro Santilli * TODO: Add list of function to expose to public API 2015-12-11 Sandro Santilli * macros/codeset.m4, macros/gettext.m4, macros/gtk-2.0.m4, macros/iconv.m4, macros/intl.m4, macros/intldir.m4, macros/intlmacosx.m4, macros/nls.m4: Remove some unused macros 2015-12-11 Sandro Santilli * Makefile.in, aclocal.m4, configure.ac, librtgeom_internal.h, macros/ac_proj4_version.m4, rtgeom_transform.c: Drop PROJ4 dependency, and reprojection support 2015-12-11 Sandro Santilli * configure.ac: Relax GEOS requirement to accept 3.4.0 (mainly for gitlab testing) 2015-12-11 Sandro Santilli * configure.ac, librtgeom.h.in, librtgeom_internal.h, rtgeom_geos.h, rtgeom_geos_split.c: Ensure only the re-entrant API is used Raises GEOS requirement to 3.5.0 2015-12-11 Sandro Santilli * .gitlab-ci.yml: Stop using lsb_release on gitlab-ci 2015-12-11 Sandro Santilli * librtgeom_internal.h, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rtgeom_topo.c: Make handling of GEOS errors re-entrant 2015-12-10 Sandro Santilli * librtgeom_topo.h: Add info about how to create RTCTX 2015-12-10 Sandro Santilli * librtgeom.h.in, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_node.c, rtgeom_geos_split.c: Switch to reentrant GEOS interface in rtgeom 2015-12-10 Sandro Santilli * librtgeom.h.in, rtgeom_api.c, rtutil.c: Add RTCTX initializing functions 2015-12-10 Sandro Santilli * librtgeom_internal.h, librtgeom_topo_internal.h, rtgeom_geos.c, rtgeom_geos.h, rtgeom_topo.c: Switch to reentrant GEOS interface in rtgeom_topo 2015-12-10 Sandro Santilli * librtgeom_topo.h, rtgeom_topo.c: Take RTCTX at rtt_CreateBackendIface time 2015-12-10 Sandro Santilli * box2d.c, bytebuffer.c, bytebuffer.h, g_box.c, g_serialized.c, g_util.c, librtgeom.h.in, librtgeom_internal.h, librtgeom_topo.h, measures.c, measures.h, measures3d.c, measures3d.h, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtcurvepoly.c, rtgeom.c, rtgeom_api.c, rtgeom_debug.c, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rtgeom_log.h, rtgeom_topo.c, rtgeom_transform.c, rthomogenize.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmline.c, rtmpoint.c, rtmpoly.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_twkb.h, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtprint.c, rtpsurface.c, rtstroke.c, rttin.c, rttree.c, rttree.h, rttriangle.c, rtutil.c, stringbuffer.c, stringbuffer.h, varint.c, varint.h: Made all RTCTX params const 2015-12-10 Sandro Santilli * rtgeom_log.h, rtgeom_topo.c, rtutil.c: Const correct rtnotice/rtdebug 2015-12-10 Sandro Santilli * g_box.c: do not redefine isfinite 2015-12-10 Sandro Santilli * Makefile.in, librtgeom.h.in, rtgeom_geos.h, rtgeom_geos_cluster.c, rtin_encoded_polyline.c, rtunionfind.c, rtunionfind.h: Remove polyline encoding and clustering functions 2015-12-10 Sandro Santilli * box2d.c, bytebuffer.c, bytebuffer.h, g_box.c, g_serialized.c, g_util.c, librtgeom.h.in, librtgeom_internal.h, librtgeom_topo.h, librtgeom_topo_internal.h, measures.c, measures.h, measures3d.c, measures3d.h, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtcurvepoly.c, rtgeom.c, rtgeom_api.c, rtgeom_debug.c, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_cluster.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rtgeom_log.h, rtgeom_topo.c, rtgeom_transform.c, rthomogenize.c, rtin_encoded_polyline.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmline.c, rtmpoint.c, rtmpoly.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_twkb.h, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtprint.c, rtpsurface.c, rtstroke.c, rttin.c, rttree.c, rttree.h, rttriangle.c, rtunionfind.c, rtunionfind.h, rtutil.c, stringbuffer.c, stringbuffer.h, varint.c, varint.h: Add RTCTX argument to all exposed functions 2015-12-10 Sandro Santilli * Makefile.in, effectivearea.c, effectivearea.h: Drop effective area 2015-12-10 Sandro Santilli * rtgeom_geos.c: Use custom geos error reporter 2015-12-10 Sandro Santilli * rtgeom_log.h, stringbuffer.h: Include internal header 2015-12-10 Sandro Santilli * Makefile.in, librtgeom.h.in, rtgeodetic.c, rtgeodetic.h, rtgeodetic_tree.c, rtgeodetic_tree.h, rtspheroid.c: Remove geodetic and spheroid functions 2015-12-10 Sandro Santilli * Makefile.in, librtgeom.h.in, rtin_wkt.c, rtin_wkt.h, rtin_wkt_lex.l, rtin_wkt_parse.y: Remove WKT parser 2015-12-09 Sandro Santilli * librtgeom.h.in, librtgeom_internal.h: Add RTCTX typedef 2015-12-09 Sandro Santilli * Makefile.in: Fix librtgeom.h build rule 2015-12-09 Sandro Santilli * effectivearea.c, g_box.c, g_serialized.c, g_util.c, librtgeom.h.in, ptarray.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtgeodetic.c, rtgeom.c, rtgeom_api.c, rtgeom_debug.c, rtgeom_geos.c, rtgeom_geos_clean.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rthomogenize.c, rtin_geojson.c, rtin_wkt.c, rtline.c, rtlinearreferencing.c, rtmline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_twkb.c, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtpsurface.c, rtstroke.c, rttin.c, rttriangle.c: Prefix TYPMOD and FLAG macros 2015-12-09 Sandro Santilli * effectivearea.c, effectivearea.h, g_box.c, g_serialized.c, librtgeom.h.in, librtgeom_internal.h, measures.c, measures.h, measures3d.c, measures3d.h, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtgeodetic.c, rtgeodetic.h, rtgeodetic_tree.c, rtgeodetic_tree.h, rtgeom.c, rtgeom_api.c, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_clean.c, rtgeom_geos_split.c, rtgeom_topo.c, rtgeom_transform.c, rtin_encoded_polyline.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtin_wkt.c, rtin_wkt.h, rtin_wkt_parse.y, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmpoint.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtspheroid.c, rtstroke.c, rttree.c, rttree.h, rttriangle.c: Prefix POINTARRAY 2015-12-09 Sandro Santilli * box2d.c, effectivearea.c, g_box.c, g_serialized.c, librtgeom.h.in, librtgeom_internal.h, librtgeom_topo.h, measures.c, measures.h, measures3d.c, measures3d.h, ptarray.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtgeodetic.c, rtgeodetic.h, rtgeodetic_tree.c, rtgeodetic_tree.h, rtgeom.c, rtgeom_api.c, rtgeom_geos.c, rtgeom_geos.h, rtgeom_geos_cluster.c, rtgeom_geos_split.c, rtgeom_topo.c, rtgeom_transform.c, rtin_encoded_polyline.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtin_wkt.c, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmpoint.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtprint.c, rtpsurface.c, rtspheroid.c, rtstroke.c, rttin.c, rttree.c, rttree.h, rttriangle.c: Prefix GBOX and POINT* 2015-12-09 Sandro Santilli * librtgeom.h.in: Remove unused macros 2015-12-09 Sandro Santilli * librtgeom.h.in, librtgeom_internal.h, rtgeom.c, rthomogenize.c: Prefix NUMTYPES 2015-12-09 Sandro Santilli * bytebuffer.c, g_serialized.c, g_serialized.txt, librtgeom.h.in, librtgeom_internal.h, rtgeom.c, rtgeom_topo.c, rtin_twkb.c, rtin_wkb.c, rtin_wkt.c, rtin_wkt.h, rtin_wkt_lex.l, rtin_wkt_parse.y, rtout_wkb.c, rtout_wkt.c: Prefix WKT/WKB macros with "RT" 2015-12-09 Sandro Santilli * effectivearea.c, g_box.c, g_serialized.c, g_util.c, librtgeom.h.in, librtgeom_internal.h, measures.c, measures3d.c, rtalgorithm.c, rtcircstring.c, rtcollection.c, rtcompound.c, rtcurvepoly.c, rtgeodetic.c, rtgeodetic_tree.c, rtgeom.c, rtgeom_debug.c, rtgeom_geos.c, rtgeom_geos_clean.c, rtgeom_geos_cluster.c, rtgeom_geos_node.c, rtgeom_geos_split.c, rtgeom_topo.c, rtgeom_transform.c, rthomogenize.c, rtin_geojson.c, rtin_twkb.c, rtin_wkb.c, rtin_wkt.c, rtin_wkt_parse.y, rtiterator.c, rtline.c, rtlinearreferencing.c, rtmline.c, rtmpoint.c, rtmpoly.c, rtout_encoded_polyline.c, rtout_geojson.c, rtout_gml.c, rtout_kml.c, rtout_svg.c, rtout_twkb.c, rtout_twkb.h, rtout_wkb.c, rtout_wkt.c, rtout_x3d.c, rtpoint.c, rtpoly.c, rtpsurface.c, rtspheroid.c, rtstroke.c, rttin.c, rttriangle.c: Prefix geometry type macros with RT 2015-12-09 Sandro Santilli * Makefile.in, configure.ac, rtgeom_sfcgal.c, rtgeom_sfcgal.h: Remove SFCGAL support 2015-12-01 Sandro Santilli * Initial import HOWTO_RELEASE000066400000000000000000000007461271715413500130560ustar00rootroot00000000000000- Set version in configure.ac - Run make distcheck, fix if needed - Commit all of the above - Regenerate ChangeLog (make cl) and commit again - Commit and push to repository, confirm bots are all happy - Add annotated tag: git tag -sa librttopo- - Push annotated tag to repository: git push librttopo- - Create release artifacts: make dist - Upload release artifacts to upload.osgeo.org - Update websites (to be defined) - Announce on discuss@lists.osgeo.org Makefile.am000066400000000000000000000005361271715413500131240ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src headers EXTRA_DIST = \ ChangeLog \ CREDITS \ doc/BUILDING-ON-WINDOWS.md \ doc/DATASTORES.md \ doc/g_serialized.txt \ makefile.vc \ nmake.opt \ README.md AUTOMAKE_OPTIONS = dist-zip foreign pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = rttopo.pc cl: cd $(srcdir) && git2cl > ChangeLog README.md000066400000000000000000000036351271715413500123520ustar00rootroot00000000000000RT Topology Library =================== ## Build status [![Gitlab-CI](https://gitlab.com/rttopo/rttopo/badges/master/build.svg)] (https://gitlab.com/rttopo/rttopo/commits/master) ## About The RT Topology Library exposes an API to create and manage standard (ISO 13249 aka SQL/MM) topologies using user-provided [data stores] (doc/DATASTORES.md) and released under the GNU GPL license (version 2 or later). The code is derived from [PostGIS](http://postgis.net) liblwgeom library enhanced to provide thread-safety, have less dependencies and be independent from PostGIS release cycles. The RT Topology Library was funded by "Regione Toscana - SITA" (CIG: 6445512CC1), which also funded many improvements in the originating liblwgeom. Official code repository is https://git.osgeo.org/gogs/rttopo/librttopo. A mirror exists at https://gitlab.com/rttopo/rttopo, automatically updated on every push to the official repository. Development mailing list: https://lists.osgeo.org/mailman/listinfo/librttopo-dev ## Building, testing, installing ### Unix Using Autotools: ./autogen.sh # in ${srcdir}, if obtained from GIT ${srcdir}/configure # in build dir make # build make check # test make install # or install-strip ### Microsoft Windows See [separate document](doc/BUILDING-ON-WINDOWS.md) ## Using The public header for topology support is `librttopo.h`. The caller has to setup a backend interface (`RTT_BE_IFACE`) implementing all the required callbacks and will then be able to use the provided editing functions. The contract for each callback is fully specified in the header. The callbacks are as simple as possible while still allowing for backend-specific optimizations. The backend interface is an opaque object and callabcks are registered into it using free functions. This is to allow for modifying the required set of callbacks between versions of the library without breaking backward compatibility. TODO000066400000000000000000000000401271715413500115460ustar00rootroot00000000000000- Expose GEOSGeometry-based API autogen.sh000077500000000000000000000043031271715413500130650ustar00rootroot00000000000000#!/bin/sh # # rttopo bootstrapping script # giveup() { echo echo " Something went wrong, giving up!" echo exit 1 } OSTYPE=`uname -s` AUTOCONF=`which autoconf 2>/dev/null` if [ ! ${AUTOCONF} ]; then echo "Missing autoconf!" exit fi AUTOCONF_VER=`${AUTOCONF} --version | grep -E "^.*[0-9]$" | sed 's/^.* //'` AUTOHEADER=`which autoheader 2>/dev/null` if [ ! ${AUTOHEADER} ]; then echo "Missing autoheader!" exit fi for aclocal in aclocal aclocal-1.10 aclocal-1.9; do ACLOCAL=`which $aclocal 2>/dev/null` if test -x "${ACLOCAL}"; then break; fi done if [ ! ${ACLOCAL} ]; then echo "Missing aclocal!" exit fi ACLOCAL_VER=`${ACLOCAL} --version | grep -E "^.*[0-9]$" | sed 's/^.* //'` for automake in automake automake-1.10 automake-1.9; do AUTOMAKE=`which $automake 2>/dev/null` if test -x "${AUTOMAKE}"; then break; fi done if [ ! ${AUTOMAKE} ]; then echo "Missing automake!" exit fi AUTOMAKE_VER=`${AUTOMAKE} --version | grep -E "^.*[0-9]$" | sed 's/^.* //'` for libtoolize in libtoolize glibtoolize; do LIBTOOLIZE=`which $libtoolize 2>/dev/null` if test -x "${LIBTOOLIZE}"; then break; fi done if [ ! ${LIBTOOLIZE} ]; then echo "Missing libtoolize!" exit fi LIBTOOLIZE_VER=`${LIBTOOLIZE} --version | grep -E "^.*[0-9]\.[0-9]" | sed 's/^.* //'` AMOPTS="--add-missing --copy -Woverride" if test "$OSTYPE" = "IRIX" -o "$OSTYPE" = "IRIX64"; then AMOPTS=$AMOPTS" --include-deps"; fi LTOPTS="--force --copy" echo "* Running ${LIBTOOLIZE} (${LIBTOOLIZE_VER})" echo " OPTIONS = ${LTOPTS}" ${LIBTOOLIZE} ${LTOPTS} || giveup echo "* Running $ACLOCAL (${ACLOCAL_VER})" ${ACLOCAL} -I m4 || giveup echo "* Running ${AUTOHEADER} (${AUTOCONF_VER})" ${AUTOHEADER} || giveup echo "* Running ${AUTOMAKE} (${AUTOMAKE_VER})" echo " OPTIONS = ${AMOPTS}" ${AUTOMAKE} ${AMOPTS} || giveup echo "* Running ${AUTOCONF} (${AUTOCONF_VER})" ${AUTOCONF} || giveup if test -f "${PWD}/configure"; then echo "======================================" echo "Now you are ready to run './configure'" echo "======================================" else echo " Failed to generate ./configure script!" giveup fi configure.ac000066400000000000000000000113611271715413500133540ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) AC_INIT(librttopo, 1.0.0, strk@kbt.io) AC_LANG(C) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([foreign]) AM_MAINTAINER_MODE AM_CONFIG_HEADER(src/rttopo_config.h) # disabling debug support AH_TEMPLATE([NDEBUG], [Must be defined in order to disable debug mode.]) AC_DEFINE(NDEBUG) AH_TEMPLATE([LIBRTGEOM_VERSION], [RTTOPO library version.]) AC_DEFINE_UNQUOTED([LIBRTGEOM_VERSION], ["$VERSION"], [rtgeom version]) AC_SUBST([LIBRTGEOM_VERSION]) AH_TEMPLATE([RTGEOM_GEOS_VERSION], [RTTOPO GEOS version.]) AH_TEMPLATE([RTGEOM_DEBUG_LEVEL], [RTGEOM Debug Level.]) AC_DEFINE(RTGEOM_DEBUG_LEVEL) AC_DEFINE_UNQUOTED([RTGEOM_DEBUG_LEVEL], [0], [debug level]) # Checks for header files. AC_CHECK_HEADERS(stdlib.h,, [AC_MSG_ERROR([cannot find stdlib.h, bailing out])]) AC_CHECK_HEADERS(stdio.h,, [AC_MSG_ERROR([cannot find stdio.h, bailing out])]) AC_CHECK_HEADERS(string.h,, [AC_MSG_ERROR([cannot find string.h, bailing out])]) AC_CHECK_HEADERS(math.h,, [AC_MSG_ERROR([cannot find math.h, bailing out])]) AC_CHECK_HEADERS(float.h,, [AC_MSG_ERROR([cannot find float.h, bailing out])]) AC_CHECK_HEADERS(inttypes.h,, [AC_MSG_ERROR([cannot find inttypes.h, bailing out])]) AC_CHECK_HEADERS(stdint.h,, [AC_MSG_ERROR([cannot find stdint.h, bailing out])]) AC_CHECK_HEADERS(ctype.h,, [AC_MSG_ERROR([cannot find ctype.h, bailing out])]) AC_CHECK_HEADERS(errno.h,, [AC_MSG_ERROR([cannot find errno.h, bailing out])]) AC_CHECK_HEADERS(assert.h,, [AC_MSG_ERROR([cannot find assert.h, bailing out])]) AC_CHECK_HEADERS(stdarg.h,, [AC_MSG_ERROR([cannot find stdarg.h, bailing out])]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_C_VOLATILE # Checks for library functions. AC_FUNC_LSTAT AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MEMCMP AC_FUNC_STAT AC_FUNC_STRFTIME AC_CHECK_FUNCS([memset sqrt strcasecmp strerror strncasecmp strstr fdatasync ftruncate getcwd gettimeofday localtime_r memmove strerror]) AC_CONFIG_FILES([Makefile \ src/Makefile \ headers/Makefile \ headers/librttopo_geom.h \ rttopo.pc]) #----------------------------------------------------------------------- # --with-geosconfig # AC_ARG_WITH([geosconfig], [AS_HELP_STRING([--with-geosconfig=FILE], [specify an alternative geos-config file])], [GEOSCONFIG="$withval"], [GEOSCONFIG=""]) if test "x$GEOSCONFIG" = "x"; then # GEOSCONFIG was not specified, so search within the current path AC_PATH_PROG([GEOSCONFIG], [geos-config]) # If we couldn't find geos-config, display an error if test "x$GEOSCONFIG" = "x"; then AC_MSG_ERROR([could not find geos-config within the current path. You may need to try re-running configure with a --with-geosconfig parameter.]) fi else # GEOSCONFIG was specified; display a message to the user if test "x$GEOSCONFIG" = "xyes"; then AC_MSG_ERROR([you must specify a parameter to --with-geosconfig, e.g. --with-geosconfig=/path/to/geos-config]) else if test -f $GEOSCONFIG; then AC_MSG_RESULT([Using user-specified geos-config file: $GEOSCONFIG]) else AC_MSG_ERROR([the user-specified geos-config file $GEOSCONFIG does not exist]) fi fi fi # Extract the linker and include flags GEOS_LDFLAGS=`$GEOSCONFIG --ldflags` GEOS_CFLAGS=-I`$GEOSCONFIG --includes` AC_SUBST([GEOS_LDFLAGS]) AC_SUBST([GEOS_CFLAGS]) # Ensure that we can parse geos_c.h CPPFLAGS_SAVE="$CPPFLAGS" CPPFLAGS="$GEOS_CFLAGS" AC_CHECK_HEADERS([geos_c.h],, [AC_MSG_ERROR([could not find geos_c.h - you may need to specify the directory of a geos-config file using --with-geosconfig])]) CPPFLAGS="$CPPFLAGS_SAVE" # Ensure we can link against libgeos_c LIBS_SAVE="$LIBS" LIBS="$GEOS_LDFLAGS" AC_SEARCH_LIBS(GEOSContext_setErrorMessageHandler_r,geos_c,,AC_MSG_ERROR([could not find libgeos_c (or obsolete 'libgeos_c' < v.3.5.0 found) - you may need to specify the directory of a geos-config file using --with-geosconfig])) LIBS="$LIBS_SAVE" LIBS="$LIBS $GEOS_LDFLAGS -lgeos_c" GEOS_MAJOR_VERSION=`$GEOSCONFIG --version | cut -d. -f1 | sed 's/[[^0-9]]//g'` GEOS_MINOR_VERSION=`$GEOSCONFIG --version | cut -d. -f2 | sed 's/[[^0-9]]//g'` RTGEOM_GEOS_VERSION="$GEOS_MAJOR_VERSION$GEOS_MINOR_VERSION" AC_DEFINE_UNQUOTED([RTGEOM_GEOS_VERSION], [$RTGEOM_GEOS_VERSION], [GEOS library version]) AC_SUBST([RTGEOM_GEOS_VERSION]) # SRID stuff SRID_MAX=999999 SRID_USR_MAX=998999 AC_SUBST([SRID_MAX]) AC_SUBST([SRID_USR_MAX]) AC_OUTPUT doc/000077500000000000000000000000001271715413500116315ustar00rootroot00000000000000doc/BUILDING-ON-WINDOWS.md000066400000000000000000000025601271715413500150150ustar00rootroot00000000000000Building on Windows =================== On Windows systems you can choose between two different compilers: - MinGW / MSYS This represents a smart porting of a minimalistic Linux-like development toolkit - Microsoft Visual Studio .NET (aka MSVC) This is the standard platform development toolkit from Microsoft. # Using MinGW / MSYS We assume that you have already installed the MinGW compiler and the MSYS shell. Building 'librttopo' under Windows is then more or less like building on any other UNIX-like system. If you have unpacked the sources as C:\librttpopo-, then the required steps are: cd c:/librttopo- export "CFLAGS=-I/usr/local/include" export "LDFLAGS=-L/usr/local/lib" ./configure make make install # or (in order to save some disk space) make install-strip # Using Microsoft Visual Studio .NET We assume that you have already installed Visual Studio enabling the command line tools. Note that you are expected to the Visual Studio command prompt shell rather than the GUI build environment. If you have unpacked the sources as C:\librttpopo-, then the required steps are: cd c:\librttopo- nmake /f makefile.vc nmake /f makefile.vc install Please note: the MSVC build configuration is based on the OSGeo4W distribution. Any depending library (e.g. GEOS) is expected to be already installed on C:\OSGeo4W doc/DATASTORES.md000066400000000000000000000001441271715413500136230ustar00rootroot00000000000000List of known data stores for librttopo ======================================= * spatialite 4.4.0 doc/g_serialized.txt000066400000000000000000000063661271715413500150460ustar00rootroot00000000000000 GSERIALIZED FORM ================= The new serialized form, used by GEOGRAPHY, attempts to learn from the lessons of the SERIALIZED_RTGEOM, making slightly different trade-offs between alignment and overall compactness. * It is understood that GSERIALIZED is be used primarily (only) by PostGIS. Other users of the geometry library (for example, the loaders and dumpers) will serialize to RTWKB or EWKB or various text representations. Therefore, GSERIALIZED includes the uint32 "size" field at the top used by PostgreSQL for varlena types. * Like SERIALIZED_RTGEOM, GSERIALIZED is built to be read and written recursively, so that nested collections of collections (of ...) are possible without restrictions on depth. * GSERIALIZED preserves double alignment of ordinate arrays. This will allow coordinate access without memcpy. * GSERIALIZED includes a mandatory SRID, in recognition of the fact that most production use of PostGIS does actually use an SRID. In SERIALIZED_RTGEOM the SRID is optional. * GSERIALIZED places the dimensionality information, the SRID information and the bounding boxes at the front of the structure, and all sub-components inherit from those parent values. * GSERIALIZED retains the idea of optional bounding boxes, so that small features do not carry the extra storage overhead of a largely redundant bounding box. STRUCTURE --------- Most of the internals of GSERIALIZED are anonymous. One thing that differs from SERIALIZED_RTGEOM is that the geometry type is no longer directly accessible from the structure (it was inside the one byte "type" at the top of the SERIALIZED_RTGEOM). To access the type in GSERIALIZED, you first have to figure out whether there is a an bounding box, how many dimensions that bounding box has, and move the data pointer appropriately before dereferencing. The gserialized_get_type(GSERIALIZED *s) function carries out this operation. typedef struct { uint32 size; /* For PgSQL use, use VAR* macros to manipulate. */ uchar srid[3]; /* 21 bits of SRID (and 3 spare bits) */ uchar flags; /* HasZ, HasM, HasBBox, IsGeodetic */ uchar data[1]; /* See gserialized.txt */ } GSERIALIZED; The standard header is as follows (using <> to denote 4-byte fields and [] to denote 8-byte fields): size /* Used by PgSQL */ /* 1 byte */ /* bounding box is optional */ /* number of dimensions is variable and indicated in the flags */ After the header comes the recursively searchable geometry representations. Each type is double aligned, so any combination is double aligned, and we start after a double aligned standard header, so we are golden: /* 0 if empty, 1 otherwise */ [double] [double] [double] /* 0 if empty, N otherwise */ [double] ... [double] /* 0 if empty, N otherwise */ /* pad if nrings % 2 > 0 */ [double] ... [double] /* 0 if empty, N otherwise */ [geom] ... [geom] /* 0 if empty, N otherwise */ [double] ... [double] /* 0 if empty, N otherwise */ [geom] ... [geom] headers/000077500000000000000000000000001271715413500124775ustar00rootroot00000000000000headers/Makefile.am000066400000000000000000000000671271715413500145360ustar00rootroot00000000000000 nobase_include_HEADERS = librttopo_geom.h librttopo.h headers/librttopo.h000066400000000000000000001271031271715413500146720ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2015 Sandro Santilli * **********************************************************************/ #ifndef LIBRTGEOM_TOPO_H #define LIBRTGEOM_TOPO_H 1 #include "librttopo_geom.h" /* INT64 */ typedef int64_t RTT_INT64; /** Identifier of topology element */ typedef RTT_INT64 RTT_ELEMID; /* * ISO primitive elements */ /** NODE */ typedef struct { RTT_ELEMID node_id; RTT_ELEMID containing_face; /* -1 if not isolated */ RTPOINT *geom; } RTT_ISO_NODE; void rtt_iso_node_release(RTT_ISO_NODE* node); /** Node fields */ #define RTT_COL_NODE_NODE_ID 1<<0 #define RTT_COL_NODE_CONTAINING_FACE 1<<1 #define RTT_COL_NODE_GEOM 1<<2 #define RTT_COL_NODE_ALL (1<<3)-1 /** EDGE */ typedef struct { RTT_ELEMID edge_id; RTT_ELEMID start_node; RTT_ELEMID end_node; RTT_ELEMID face_left; RTT_ELEMID face_right; RTT_ELEMID next_left; RTT_ELEMID next_right; RTLINE *geom; } RTT_ISO_EDGE; /** Edge fields */ #define RTT_COL_EDGE_EDGE_ID 1<<0 #define RTT_COL_EDGE_START_NODE 1<<1 #define RTT_COL_EDGE_END_NODE 1<<2 #define RTT_COL_EDGE_FACE_LEFT 1<<3 #define RTT_COL_EDGE_FACE_RIGHT 1<<4 #define RTT_COL_EDGE_NEXT_LEFT 1<<5 #define RTT_COL_EDGE_NEXT_RIGHT 1<<6 #define RTT_COL_EDGE_GEOM 1<<7 #define RTT_COL_EDGE_ALL (1<<8)-1 /** FACE */ typedef struct { RTT_ELEMID face_id; RTGBOX *mbr; } RTT_ISO_FACE; /** Face fields */ #define RTT_COL_FACE_FACE_ID 1<<0 #define RTT_COL_FACE_MBR 1<<1 #define RTT_COL_FACE_ALL (1<<2)-1 typedef enum RTT_SPATIALTYPE_T { RTT_PUNTAL = 0, RTT_LINEAL = 1, RTT_AREAL = 2, RTT_COLLECTION = 3 } RTT_SPATIALTYPE; /* * Backend handling functions */ /* opaque pointers referencing native backend objects */ /** * Backend private data pointer * * Only the backend handler needs to know what it really is. * It will be passed to all registered callback functions. */ typedef struct RTT_BE_DATA_T RTT_BE_DATA; /** * Backend interface handler * * Embeds all registered backend callbacks and private data pointer. * Will need to be passed (directly or indirectly) to al public facing * APIs of this library. */ typedef struct RTT_BE_IFACE_T RTT_BE_IFACE; /** * Topology handler. * * Embeds backend interface handler. * Will need to be passed to all topology manipulation APIs * of this library. */ typedef struct RTT_BE_TOPOLOGY_T RTT_BE_TOPOLOGY; /** * Structure containing base backend callbacks * * Used for registering into the backend iface */ typedef struct RTT_BE_CALLBACKS_T { /** * Read last error message from backend * * @return NULL-terminated error string */ const char* (*lastErrorMessage) (const RTT_BE_DATA* be); /** * Create a new topology in the backend * * @param name the topology name * @param srid the topology SRID * @param precision the topology precision/tolerance * @param hasZ non-zero if topology primitives should have a Z ordinate * @return a topology handler, which embeds the backend data/params * or NULL on error (@see lastErrorMessage) */ RTT_BE_TOPOLOGY* (*createTopology) ( const RTT_BE_DATA* be, const char* name, int srid, double precision, int hasZ ); /** * Load a topology from the backend * * @param name the topology name * @return a topology handler, which embeds the backend data/params * or NULL on error (@see lastErrorMessage) */ RTT_BE_TOPOLOGY* (*loadTopologyByName) ( const RTT_BE_DATA* be, const char* name ); /** * Release memory associated to a backend topology * * @param topo the backend topology handler * @return 1 on success, 0 on error (@see lastErrorMessage) */ int (*freeTopology) (RTT_BE_TOPOLOGY* topo); /** * Get nodes by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of node identifiers * in the input array, gets number of node in output array. * @param fields fields to be filled in the returned structure, see * RTT_COL_NODE_* macros * * @return an array of nodes * or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) * (@see lastErrorMessage) * */ RTT_ISO_NODE* (*getNodeById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields ); /** * Get nodes within distance by point * * @param topo the topology to act upon * @param pt the query point * @param dist the distance * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_NODE_* macros * @param limit max number of nodes to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of nodes or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ RTT_ISO_NODE* (*getNodeWithinDistance2D) ( const RTT_BE_TOPOLOGY* topo, const RTPOINT* pt, double dist, int* numelems, int fields, int limit ); /** * Insert nodes * * Insert node primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param nodes the nodes to insert. Those with a node_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the nodes array * * @return 1 on success, 0 on error (@see lastErrorMessage) */ int (*insertNodes) ( const RTT_BE_TOPOLOGY* topo, RTT_ISO_NODE* nodes, int numelems ); /** * Get edge by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of edge identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_EDGE_* macros * * @return an array of edges or NULL in the following cases: * - none found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ RTT_ISO_EDGE* (*getEdgeById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields ); /** * Get edges within distance by point * * @param topo the topology to act upon * @param pt the query point * @param dist the distance * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_EDGE_* macros * @param limit max number of edges to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of edges or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ RTT_ISO_EDGE* (*getEdgeWithinDistance2D) ( const RTT_BE_TOPOLOGY* topo, const RTPOINT* pt, double dist, int* numelems, int fields, int limit ); /** * Get next available edge identifier * * Identifiers returned by this function should not be considered * available anymore. * * @param topo the topology to act upon * * @return next available edge identifier or -1 on error */ RTT_ELEMID (*getNextEdgeId) ( const RTT_BE_TOPOLOGY* topo ); /** * Insert edges * * Insert edge primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param edges the edges to insert. Those with a edge_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the edges array * * @return number of inserted edges, or -1 (@see lastErrorMessage) */ int (*insertEdges) ( const RTT_BE_TOPOLOGY* topo, RTT_ISO_EDGE* edges, int numelems ); /** * Update edges selected by fields match/mismatch * * @param topo the topology to act upon * @param sel_edge an RTT_ISO_EDGE object with selecting fields set. * @param sel_fields fields used to select edges to be updated, * see RTT_COL_EDGE_* macros * @param upd_edge an RTT_ISO_EDGE object with updated fields set. * @param upd_fields fields to be updated for the selected edges, * see RTT_COL_EDGE_* macros * @param exc_edge an RTT_ISO_EDGE object with exclusion fields set, * can be NULL if no exlusion condition exists. * @param exc_fields fields used for excluding edges from the update, * see RTT_COL_EDGE_* macros * * @return number of edges being updated or -1 on error * (@see lastErroMessage) */ int (*updateEdges) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields, const RTT_ISO_EDGE* upd_edge, int upd_fields, const RTT_ISO_EDGE* exc_edge, int exc_fields ); /** * Get faces by id * * @param topo the topology to act upon * @param ids an array of element identifiers * @param numelems input/output parameter, pass number of edge identifiers * in the input array, gets number of node in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_FACE_* macros * * @return an array of faces or NULL in the following cases: * - none found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ RTT_ISO_FACE* (*getFaceById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields ); /** * Get face containing point * * @param topo the topology to act upon * @param pt the query point * * @return a face identifier, -1 if no face contains the point * (could be in universe face or on an edge) * or -2 on error (@see lastErrorMessage) */ RTT_ELEMID (*getFaceContainingPoint) ( const RTT_BE_TOPOLOGY* topo, const RTPOINT* pt ); /** * Update TopoGeometry objects after an edge split event * * @param topo the topology to act upon * @param split_edge identifier of the edge that was split. * @param new_edge1 identifier of the first new edge that was created * as a result of edge splitting. * @param new_edge2 identifier of the second new edge that was created * as a result of edge splitting, or -1 if the old edge was * modified rather than replaced. * * @return 1 on success, 0 on error * * @note on splitting an edge, the new edges both have the * same direction as the original one. If a second new edge was * created, its start node will be equal to the first new edge * end node. */ int (*updateTopoGeomEdgeSplit) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID split_edge, RTT_ELEMID new_edge1, RTT_ELEMID new_edge2 ); /** * Delete edges * * @param topo the topology to act upon * @param sel_edge an RTT_ISO_EDGE object with selecting fields set. * @param sel_fields fields used to select edges to be deleted, * see RTT_COL_EDGE_* macros * * @return number of edges being deleted or -1 on error * (@see lastErroMessage) */ int (*deleteEdges) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields ); /** * Get edges whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_NODE_* macros * @param limit max number of nodes to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of nodes or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ RTT_ISO_NODE* (*getNodeWithinBox2D) ( const RTT_BE_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ); /** * Get edges whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_EDGE_* macros * @param limit max number of edges to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of edges or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ RTT_ISO_EDGE* (*getEdgeWithinBox2D) ( const RTT_BE_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ); /** * Get edges that start or end on any of the given node identifiers * * @param topo the topology to act upon * @param ids an array of node identifiers * @param numelems input/output parameter, pass number of node identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_EDGE_* macros * * @return an array of edges that are incident to a node * or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) * (@see lastErrorMessage) */ RTT_ISO_EDGE* (*getEdgeByNode) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields ); /** * Update nodes selected by fields match/mismatch * * @param topo the topology to act upon * @param sel_node an RTT_ISO_NODE object with selecting fields set. * @param sel_fields fields used to select nodes to be updated, * see RTT_COL_NODE_* macros * @param upd_node an RTT_ISO_NODE object with updated fields set. * @param upd_fields fields to be updated for the selected nodes, * see RTT_COL_NODE_* macros * @param exc_node an RTT_ISO_NODE object with exclusion fields set, * can be NULL if no exlusion condition exists. * @param exc_fields fields used for excluding nodes from the update, * see RTT_COL_NODE_* macros * * @return number of nodes being updated or -1 on error * (@see lastErroMessage) */ int (*updateNodes) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_NODE* sel_node, int sel_fields, const RTT_ISO_NODE* upd_node, int upd_fields, const RTT_ISO_NODE* exc_node, int exc_fields ); /** * Update TopoGeometry objects after a face split event * * @param topo the topology to act upon * @param split_face identifier of the face that was split. * @param new_face1 identifier of the first new face that was created * as a result of face splitting. * @param new_face2 identifier of the second new face that was created * as a result of face splitting, or -1 if the old face was * modified rather than replaced. * * @return 1 on success, 0 on error (@see lastErroMessage) * */ int (*updateTopoGeomFaceSplit) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID split_face, RTT_ELEMID new_face1, RTT_ELEMID new_face2 ); /** * Insert faces * * Insert face primitives in the topology, performing no * consistency checks. * * @param topo the topology to act upon * @param faces the faces to insert. Those with a node_id set to -1 * it will be replaced to an automatically assigned identifier * @param nelems number of elements in the faces array * * @return number of inserted faces, or -1 (@see lastErrorMessage) */ int (*insertFaces) ( const RTT_BE_TOPOLOGY* topo, RTT_ISO_FACE* faces, int numelems ); /** * Update faces by id * * @param topo the topology to act upon * @param faces an array of RTT_ISO_FACE object with selecting id * and setting mbr. * @param numfaces number of faces in the "faces" array * * @return number of faces being updated or -1 on error * (@see lastErroMessage) */ int (*updateFacesById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_FACE* faces, int numfaces ); /* * Get the ordered list edge visited by a side walk * * The walk starts from the side of an edge and stops when * either the max number of visited edges OR the starting * position is reached. The output list never includes a * duplicated signed edge identifier. * * It is expected that the walk uses the "next_left" and "next_right" * attributes of ISO edges to perform the walk (rather than recomputing * the turns at each node). * * @param topo the topology to operate on * @param edge walk start position and direction: * abs value identifies the edge, sign expresses * side (left if positive, right if negative) * and direction (forward if positive, backward if negative). * @param numedges output parameter, gets the number of edges visited * @param limit max edges to return (to avoid an infinite loop in case * of a corrupted topology). 0 is for unlimited. * The function is expected to error out if the limit is hit. * * @return an array of signed edge identifiers (positive edges being * walked in their direction, negative ones in opposite) or * NULL on error (@see lastErroMessage) */ RTT_ELEMID* (*getRingEdges) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID edge, int *numedges, int limit ); /** * Update edges by id * * @param topo the topology to act upon * @param edges an array of RTT_ISO_EDGE object with selecting id * and updating fields. * @param numedges number of edges in the "edges" array * @param upd_fields fields to be updated for the selected edges, * see RTT_COL_EDGE_* macros * * @return number of edges being updated or -1 on error * (@see lastErroMessage) */ int (*updateEdgesById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_EDGE* edges, int numedges, int upd_fields ); /** * \brief * Get edges that have any of the given faces on the left or right side * and optionally whose bounding box overlaps the given one. * * @param topo the topology to act upon * @param ids an array of face identifiers * @param numelems input/output parameter, pass number of face identifiers * in the input array, gets number of edges in output array * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_EDGE_* macros * @param box optional bounding box to further restrict matches, use * NULL for no further restriction. * * @return an array of edges identifiers or NULL in the following cases: * - no edge found ("numelems" is set to 0) * - error ("numelems" is set to -1) */ RTT_ISO_EDGE* (*getEdgeByFace) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields, const RTGBOX *box ); /** * Get isolated nodes contained in any of the given faces * * @param topo the topology to act upon * @param faces an array of face identifiers * @param numelems input/output parameter, pass number of face * identifiers in the input array, gets number of * nodes in output array if the return is not null, * otherwise see @return section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_NODE_* macros * @param box optional bounding box to further restrict matches, use * NULL for no further restriction. * * @return an array of nodes or NULL in the following cases: * - no nod found ("numelems" is set to 0) * - error ("numelems" is set to -1, @see lastErrorMessage) */ RTT_ISO_NODE* (*getNodeByFace) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* faces, int* numelems, int fields, const RTGBOX *box ); /** * Update nodes by id * * @param topo the topology to act upon * @param nodes an array of RTT_ISO_EDGE objects with selecting id * and updating fields. * @param numnodes number of nodes in the "nodes" array * @param upd_fields fields to be updated for the selected edges, * see RTT_COL_NODE_* macros * * @return number of nodes being updated or -1 on error * (@see lastErroMessage) */ int (*updateNodesById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ISO_NODE* nodes, int numnodes, int upd_fields ); /** * Delete faces by id * * @param topo the topology to act upon * @param ids an array of face identifiers * @param numelems number of face identifiers in the ids array * * @return number of faces being deleted or -1 on error * (@see lastErroMessage) */ int (*deleteFacesById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int numelems ); /** * Get topology SRID * @return 0 for unknown */ int (*topoGetSRID) ( const RTT_BE_TOPOLOGY* topo ); /** * Get topology precision */ double (*topoGetPrecision) ( const RTT_BE_TOPOLOGY* topo ); /** * Get topology Z flag * @return 1 if topology elements do have Z value, 0 otherwise */ int (*topoHasZ) ( const RTT_BE_TOPOLOGY* topo ); /** * Delete nodes by id * * @param topo the topology to act upon * @param ids an array of node identifiers * @param numelems number of node identifiers in the ids array * * @return number of nodes being deleted or -1 on error * (@see lastErroMessage) */ int (*deleteNodesById) ( const RTT_BE_TOPOLOGY* topo, const RTT_ELEMID* ids, int numelems ); /** * Check TopoGeometry objects before an edge removal event * * @param topo the topology to act upon * @param rem_edge identifier of the edge that's been removed * @param face_left identifier of the face on the edge's left side * @param face_right identifier of the face on the edge's right side * * @return 1 to allow, 0 to forbid the operation * (reporting reason via lastErrorMessage) * */ int (*checkTopoGeomRemEdge) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID rem_edge, RTT_ELEMID face_left, RTT_ELEMID face_right ); /** * Update TopoGeometry objects after healing two faces * * @param topo the topology to act upon * @param face1 identifier of the first face * @param face2 identifier of the second face * @param newface identifier of the new face * * @note that newface may or may not be equal to face1 or face2, * while face1 should never be the same as face2. * * @return 1 on success, 0 on error (@see lastErrorMessage) * */ int (*updateTopoGeomFaceHeal) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID face1, RTT_ELEMID face2, RTT_ELEMID newface ); /** * Check TopoGeometry objects before a node removal event * * @param topo the topology to act upon * @param rem_node identifier of the node that's been removed * @param e1 identifier of the first connected edge * @param e2 identifier of the second connected edge * * The operation should be forbidden if any TopoGeometry object * exists which contains only one of the two healed edges. * * The operation should also be forbidden if the removed node * takes part in the definition of a TopoGeometry, although * this wasn't the case yet as of PostGIS version 2.1.8: * https://trac.osgeo.org/postgis/ticket/3239 * * @return 1 to allow, 0 to forbid the operation * (reporting reason via lastErrorMessage) * */ int (*checkTopoGeomRemNode) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID rem_node, RTT_ELEMID e1, RTT_ELEMID e2 ); /** * Update TopoGeometry objects after healing two edges * * @param topo the topology to act upon * @param edge1 identifier of the first edge * @param edge2 identifier of the second edge * @param newedge identifier of the new edge, taking the space * previously occupied by both original edges * * @note that newedge may or may not be equal to edge1 or edge2, * while edge1 should never be the same as edge2. * * @return 1 on success, 0 on error (@see lastErrorMessage) * */ int (*updateTopoGeomEdgeHeal) ( const RTT_BE_TOPOLOGY* topo, RTT_ELEMID edge1, RTT_ELEMID edge2, RTT_ELEMID newedge ); /** * Get faces whose bounding box overlaps a given 2D bounding box * * @param topo the topology to act upon * @param box the query box * @param numelems output parameter, gets number of elements found * if the return is not null, otherwise see @return * section for semantic. * @param fields fields to be filled in the returned structure, see * RTT_COL_FACE_* macros * @param limit max number of faces to return, 0 for no limit, -1 * to only check for existance if a matching row. * * @return an array of faces or null in the following cases: * - limit=-1 ("numelems" is set to 1 if found, 0 otherwise) * - limit>0 and no records found ("numelems" is set to 0) * - error ("numelems" is set to -1) * */ RTT_ISO_FACE* (*getFaceWithinBox2D) ( const RTT_BE_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ); } RTT_BE_CALLBACKS; /** * Create a new backend interface * * Ownership to caller delete with rtt_FreeBackendIface * * @param ctx librtgeom context, create with rtgeom_init * @param data Backend data, passed as first parameter to all callback functions */ RTT_BE_IFACE* rtt_CreateBackendIface(const RTCTX* ctx, const RTT_BE_DATA* data); /** * Register backend callbacks into the opaque iface handler * * @param iface the backend interface handler (see rtt_CreateBackendIface) * @param cb a pointer to the callbacks structure; ownership left to caller. */ void rtt_BackendIfaceRegisterCallbacks(RTT_BE_IFACE* iface, const RTT_BE_CALLBACKS* cb); /** Release memory associated with an RTT_BE_IFACE */ void rtt_FreeBackendIface(RTT_BE_IFACE* iface); /******************************************************************** * * End of BE interface * *******************************************************************/ /** * Topology errors type */ typedef enum RTT_TOPOERR_TYPE_T { RTT_TOPOERR_EDGE_CROSSES_NODE, RTT_TOPOERR_EDGE_INVALID, RTT_TOPOERR_EDGE_NOT_SIMPLE, RTT_TOPOERR_EDGE_CROSSES_EDGE, RTT_TOPOERR_EDGE_STARTNODE_MISMATCH, RTT_TOPOERR_EDGE_ENDNODE_MISMATCH, RTT_TOPOERR_FACE_WITHOUT_EDGES, RTT_TOPOERR_FACE_HAS_NO_RINGS, RTT_TOPOERR_FACE_OVERLAPS_FACE, RTT_TOPOERR_FACE_WITHIN_FACE } RTT_TOPOERR_TYPE; /** Topology error */ typedef struct RTT_TOPOERR_T { /** Type of error */ RTT_TOPOERR_TYPE err; /** Identifier of first affected element */ RTT_ELEMID elem1; /** Identifier of second affected element (0 if inapplicable) */ RTT_ELEMID elem2; } RTT_TOPOERR; /* * Topology functions */ /** Opaque topology structure * * Embeds backend interface and topology */ typedef struct RTT_TOPOLOGY_T RTT_TOPOLOGY; /******************************************************************* * * Non-ISO signatures here * *******************************************************************/ /** * Initializes a new topology * * @param iface the backend interface handler (see rtt_CreateBackendIface) * @param name name of the new topology * @param srid the topology SRID * @param prec the topology precision/tolerance * @param hasz non-zero if topology primitives should have a Z ordinate * * @return the handler of the topology, or NULL on error * (librtgeom error handler will be invoked with error message) */ RTT_TOPOLOGY *rtt_CreateTopology(RTT_BE_IFACE *iface, const char *name, int srid, double prec, int hasz); /** * Loads an existing topology by name from the database * * @param iface the backend interface handler (see rtt_CreateBackendIface) * @param name name of the topology to load * * @return the handler of the topology, or NULL on error * (librtgeom error handler will be invoked with error message) */ RTT_TOPOLOGY *rtt_LoadTopology(RTT_BE_IFACE *iface, const char *name); /** * Drop a topology and all its associated objects from the database * * @param topo the topology to drop */ void rtt_DropTopology(RTT_TOPOLOGY* topo); /** Release memory associated with an RTT_TOPOLOGY * * @param topo the topology to release (it's not removed from db) */ void rtt_FreeTopology(RTT_TOPOLOGY* topo); /** * Retrieve the id of a node at a point location * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for a node * @return a node identifier if one is found, 0 if none is found, -1 * on error (multiple nodes within distance). * The librtgeom error handler will be invoked in case of error. */ RTT_ELEMID rtt_GetNodeByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol); /** * Find the edge-id of an edge that intersects a given point * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for an * intersecting edge * @return an edge identifier if one is found, 0 if none is found, -1 * on error (multiple edges within distance). * The librtgeom error handler will be invoked in case of error. */ RTT_ELEMID rtt_GetEdgeByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol); /** * Find the face-id of a face containing a given point * * @param topo the topology to operate on * @param point the point to use for query * @param tol max distance around the given point to look for a * containing face * @return a face identifier if one is found (0 if universe), -1 * on error (multiple faces within distance or point on node * or edge). * The librtgeom error handler will be invoked in case of error. */ RTT_ELEMID rtt_GetFaceByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol); /******************************************************************* * * Topology population (non-ISO) * *******************************************************************/ /** * Adds a point to the topology * * The given point will snap to existing nodes or edges within given * tolerance. An existing edge may be split by the point. * * @param topo the topology to operate on * @param point the point to add * @param tol snap tolerance, the topology tolerance will be used if 0 * * @return identifier of added (or pre-existing) node or -1 on error * (librtgeom error handler will be invoked with error message) */ RTT_ELEMID rtt_AddPoint(RTT_TOPOLOGY* topo, RTPOINT* point, double tol); /** * Adds a linestring to the topology * * The given line will snap to existing nodes or edges within given * tolerance. Existing edges or faces may be split by the line. * * @param topo the topology to operate on * @param line the line to add * @param tol snap tolerance, the topology tolerance will be used if 0 * @param nedges output parameter, will be set to number of edges the * line was split into, or -1 on error * (librtgeom error handler will be invoked with error message) * * @return an array of edge identifiers that sewed togheter * will build up the input linestring (after snapping). Caller * will need to free the array using rtfree(const RTCTX *ctx), if not null. */ RTT_ELEMID* rtt_AddLine(RTT_TOPOLOGY* topo, RTLINE* line, double tol, int* nedges); /** * Adds a polygon to the topology * * The boundary of the given polygon will snap to existing nodes or * edges within given tolerance. * Existing edges or faces may be split by the boundary of the polygon. * * @param topo the topology to operate on * @param poly the polygon to add * @param tol snap tolerance, the topology tolerance will be used if 0 * @param nfaces output parameter, will be set to number of faces the * polygon was split into, or -1 on error * (librtgeom error handler will be invoked with error message) * * @return an array of face identifiers that sewed togheter * will build up the input polygon (after snapping). Caller * will need to free the array using rtfree(const RTCTX *ctx), if not null. */ RTT_ELEMID* rtt_AddPolygon(RTT_TOPOLOGY* topo, RTPOLY* poly, double tol, int* nfaces); /******************************************************************* * * ISO signatures here * *******************************************************************/ /** * Populate an empty topology with data from a simple geometry * * For ST_CreateTopoGeo * * @param topo the topology to operate on * @param geom the geometry to import * */ void rtt_CreateTopoGeo(RTT_TOPOLOGY* topo, RTGEOM *geom); /** * Add an isolated node * * For ST_AddIsoNode * * @param topo the topology to operate on * @param face the identifier of containing face or -1 for "unknown" * @param pt the node position * @param skipChecks if non-zero skips consistency checks * (coincident nodes, crossing edges, * actual face containement) * * @return ID of the newly added node, or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_AddIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID face, RTPOINT* pt, int skipChecks); /** * Move an isolated node * * For ST_MoveIsoNode * * @param topo the topology to operate on * @param node the identifier of the nod to be moved * @param pt the new node position * @return 0 on success, -1 on error * (librtgeom error handler will be invoked with error message) * */ int rtt_MoveIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID node, RTPOINT* pt); /** * Remove an isolated node * * For ST_RemoveIsoNode * * @param topo the topology to operate on * @param node the identifier of the node to be moved * @return 0 on success, -1 on error * (librtgeom error handler will be invoked with error message) * */ int rtt_RemoveIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID node); /** * Remove an isolated edge * * For ST_RemIsoEdge * * @param topo the topology to operate on * @param edge the identifier of the edge to be moved * @return 0 on success, -1 on error * (librtgeom error handler will be invoked with error message) * */ int rtt_RemIsoEdge(RTT_TOPOLOGY* topo, RTT_ELEMID edge); /** * Add an isolated edge connecting two existing isolated nodes * * For ST_AddIsoEdge * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @return ID of the newly added edge, or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_AddIsoEdge(RTT_TOPOLOGY* topo, RTT_ELEMID startNode, RTT_ELEMID endNode, const RTLINE *geom); /** * Add a new edge possibly splitting a face (modifying it) * * For ST_AddEdgeModFace * * If the new edge splits a face, the face is shrinked and a new one * is created. Unless the face being split is the Universal Face, the * new face will be on the right side of the newly added edge. * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @param skipChecks if non-zero skips consistency checks * (curve being simple and valid, start/end nodes * consistency actual face containement) * * @return ID of the newly added edge or null on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_AddEdgeModFace(RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, RTLINE *geom, int skipChecks); /** * Add a new edge possibly splitting a face (replacing with two new faces) * * For ST_AddEdgeNewFaces * * If the new edge splits a face, the face is replaced by two new faces. * * @param topo the topology to operate on * @param start_node identifier of the starting node * @param end_node identifier of the ending node * @param geom the edge geometry * @param skipChecks if non-zero skips consistency checks * (curve being simple and valid, start/end nodes * consistency actual face containement) * @return ID of the newly added edge * */ RTT_ELEMID rtt_AddEdgeNewFaces(RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, RTLINE *geom, int skipChecks); /** * Remove an edge, possibly merging two faces (replacing both with a new one) * * For ST_RemEdgeNewFace * * @param topo the topology to operate on * @param edge identifier of the edge to be removed * @return the id of newly created face, 0 if no new face was created * or -1 on error * */ RTT_ELEMID rtt_RemEdgeNewFace(RTT_TOPOLOGY* topo, RTT_ELEMID edge); /** * Remove an edge, possibly merging two faces (replacing one with the other) * * For ST_RemEdgeModFace * * Preferentially keep the face on the right, to be symmetric with * rtt_AddEdgeModFace. * * @param topo the topology to operate on * @param edge identifier of the edge to be removed * @return the id of the face that takes the space previously occupied * by the removed edge, or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_RemEdgeModFace(RTT_TOPOLOGY* topo, RTT_ELEMID edge); /** * Changes the shape of an edge without affecting the topology structure. * * For ST_ChangeEdgeGeom * * @param topo the topology to operate on * @param curve the edge geometry * @return 0 on success, -1 on error * (librtgeom error handler will be invoked with error message) * */ int rtt_ChangeEdgeGeom(RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTLINE* curve); /** * Split an edge by a node, modifying the original edge and adding a new one. * * For ST_ModEdgeSplit * * @param topo the topology to operate on * @param edge identifier of the edge to be split * @param pt geometry of the new node * @param skipChecks if non-zero skips consistency checks * (coincident node, point not on edge...) * @return the id of newly created node, or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_ModEdgeSplit(RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTPOINT* pt, int skipChecks); /** * Split an edge by a node, replacing it with two new edges * * For ST_NewEdgesSplit * * @param topo the topology to operate on * @param edge identifier of the edge to be split * @param pt geometry of the new node * @param skipChecks if non-zero skips consistency checks * (coincident node, point not on edge...) * @return the id of newly created node * */ RTT_ELEMID rtt_NewEdgesSplit(RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTPOINT* pt, int skipChecks); /** * Merge two edges, modifying the first and deleting the second * * For ST_ModEdgeHeal * * @param topo the topology to operate on * @param e1 identifier of first edge * @param e2 identifier of second edge * @return the id of the removed node or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_ModEdgeHeal(RTT_TOPOLOGY* topo, RTT_ELEMID e1, RTT_ELEMID e2); /** * Merge two edges, replacing both with a new one * * For ST_NewEdgeHeal * * @param topo the topology to operate on * @param e1 identifier of first edge * @param e2 identifier of second edge * @return the id of the new edge or -1 on error * (librtgeom error handler will be invoked with error message) * */ RTT_ELEMID rtt_NewEdgeHeal(RTT_TOPOLOGY* topo, RTT_ELEMID e1, RTT_ELEMID e2); /** * Return the list of directed edges bounding a face * * For ST_GetFaceEdges * * @param topo the topology to operate on * @param face identifier of the face * @param edges will be set to an array of signed edge identifiers, will * need to be released with rtfree * @return the number of edges in the edges array, or -1 on error * (librtgeom error handler will be invoked with error message) * */ int rtt_GetFaceEdges(RTT_TOPOLOGY* topo, RTT_ELEMID face, RTT_ELEMID **edges); /** * Return the geometry of a face * * For ST_GetFaceGeometry * * @param topo the topology to operate on * @param face identifier of the face * @return a polygon geometry representing the face, ownership to caller, * to be released with rtgeom_release, or NULL on error * (librtgeom error handler will be invoked with error message) */ RTGEOM* rtt_GetFaceGeometry(RTT_TOPOLOGY* topo, RTT_ELEMID face); #endif /* LIBRTGEOM_TOPO_H */ headers/librttopo_geom.h.in000066400000000000000000002331431271715413500163100ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011-2016 Sandro Santilli * Copyright 2011 Paul Ramsey * Copyright 2007-2008 Mark Cave-Ayland * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef _LIBRTGEOM_H #define _LIBRTGEOM_H 1 #define GEOS_USE_ONLY_R_API 1 #include #include #include #include /** * @file librttopo_geom.h * * This library is the generic geometry handling section of librttopo. * The geometry objects, constructors, destructors, and a set of spatial * processing functions, are implemented here. * * Programs using this library can install their custom memory managers * passing them to the rtgeom_init function, and their custom message * handlers using the appropriate rtgeom_set_*_logger functions. */ /** * Return types for functions with status returns. */ #define RT_TRUE 1 #define RT_FALSE 0 #define RT_UNKNOWN 2 #define RT_FAILURE 0 #define RT_SUCCESS 1 /** * RTRTTYPE numbers */ #define RTPOINTTYPE 1 #define RTLINETYPE 2 #define RTPOLYGONTYPE 3 #define RTMULTIPOINTTYPE 4 #define RTMULTILINETYPE 5 #define RTMULTIPOLYGONTYPE 6 #define RTCOLLECTIONTYPE 7 #define RTCIRCSTRINGTYPE 8 #define RTCOMPOUNDTYPE 9 #define RTCURVEPOLYTYPE 10 #define RTMULTICURVETYPE 11 #define RTMULTISURFACETYPE 12 #define RTPOLYHEDRALSURFACETYPE 13 #define RTTRIANGLETYPE 14 #define RTTINTYPE 15 #define RTNUMTYPES 16 /** * Flags applied in EWKB to indicate Z/M dimensions and * presence/absence of SRID and bounding boxes */ #define RTWKBZOFFSET 0x80000000 #define RTWKBMOFFSET 0x40000000 #define RTWKBSRIDFLAG 0x20000000 #define RTWKBBBOXFLAG 0x10000000 /** Ordinate names */ typedef enum RTORD_T { RTORD_X = 0, RTORD_Y = 1, RTORD_Z = 2, RTORD_M = 3 } RTORD; /** * Macros for manipulating the 'flags' byte. A uint8_t used as follows: * ---RGBMZ * Three unused bits, followed by ReadOnly, Geodetic, HasBBox, HasM and HasZ flags. */ #define RTFLAGS_GET_Z(flags) ((flags) & 0x01) #define RTFLAGS_GET_M(flags) (((flags) & 0x02)>>1) #define RTFLAGS_GET_BBOX(flags) (((flags) & 0x04)>>2) #define RTFLAGS_GET_GEODETIC(flags) (((flags) & 0x08)>>3) #define RTFLAGS_GET_READONLY(flags) (((flags) & 0x10)>>4) #define RTFLAGS_GET_SOLID(flags) (((flags) & 0x20)>>5) #define RTFLAGS_SET_Z(flags, value) ((flags) = (value) ? ((flags) | 0x01) : ((flags) & 0xFE)) #define RTFLAGS_SET_M(flags, value) ((flags) = (value) ? ((flags) | 0x02) : ((flags) & 0xFD)) #define RTFLAGS_SET_BBOX(flags, value) ((flags) = (value) ? ((flags) | 0x04) : ((flags) & 0xFB)) #define RTFLAGS_SET_GEODETIC(flags, value) ((flags) = (value) ? ((flags) | 0x08) : ((flags) & 0xF7)) #define RTFLAGS_SET_READONLY(flags, value) ((flags) = (value) ? ((flags) | 0x10) : ((flags) & 0xEF)) #define RTFLAGS_SET_SOLID(flags, value) ((flags) = (value) ? ((flags) | 0x20) : ((flags) & 0xDF)) #define RTFLAGS_NDIMS(flags) (2 + RTFLAGS_GET_Z(flags) + RTFLAGS_GET_M(flags)) #define RTFLAGS_GET_ZM(flags) (RTFLAGS_GET_M(flags) + RTFLAGS_GET_Z(flags) * 2) #define RTFLAGS_NDIMS_BOX(flags) (RTFLAGS_GET_GEODETIC(flags) ? 3 : RTFLAGS_NDIMS(flags)) /** * Macros for manipulating the 'typemod' int. An int32_t used as follows: * Plus/minus = Top bit. * Spare bits = Next 2 bits. * SRID = Next 21 bits. * RTTYPE = Next 6 bits. * ZM Flags = Bottom 2 bits. */ #define RTTYPMOD_GET_SRID(typmod) ((((typmod) & 0x1FFFFF00)<<3)>>11) #define RTTYPMOD_SET_SRID(typmod, srid) ((typmod) = (((typmod) & 0xE00000FF) | ((srid & 0x001FFFFF)<<8))) #define RTTYPMOD_GET_TYPE(typmod) ((typmod & 0x000000FC)>>2) #define RTTYPMOD_SET_TYPE(typmod, type) ((typmod) = (typmod & 0xFFFFFF03) | ((type & 0x0000003F)<<2)) #define RTTYPMOD_GET_Z(typmod) ((typmod & 0x00000002)>>1) #define RTTYPMOD_SET_Z(typmod) ((typmod) = typmod | 0x00000002) #define RTTYPMOD_GET_M(typmod) (typmod & 0x00000001) #define RTTYPMOD_SET_M(typmod) ((typmod) = typmod | 0x00000001) #define RTTYPMOD_GET_NDIMS(typmod) (2+RTTYPMOD_GET_Z(typmod)+RTTYPMOD_GET_M(typmod)) /** * Maximum allowed SRID value in serialized geometry. * Currently we are using 21 bits (2097152) of storage for SRID. */ #define SRID_MAXIMUM @SRID_MAX@ /** * Maximum valid SRID value for the user * We reserve 1000 values for internal use */ #define SRID_USER_MAXIMUM @SRID_USR_MAX@ /** Unknown SRID value */ #define SRID_UNKNOWN 0 #define SRID_IS_UNKNOWN(x) ((int)x<=0) /* ** EPSG WGS84 geographics, OGC standard default SRS, better be in ** the SPATIAL_REF_SYS table! */ #define SRID_DEFAULT 4326 #ifndef __GNUC__ # define __attribute__(x) #endif /** * RT library context */ typedef struct RTCTX_T RTCTX; /** * Global functions for memory/logging handlers. */ typedef void* (*rtallocator)(size_t size); typedef void* (*rtreallocator)(void *mem, size_t size); typedef void (*rtfreeor)(void* mem); typedef void (*rtreporter)(const char* fmt, va_list ap, void *arg) __attribute__ (( format(printf, 1, 0) )); typedef void (*rtdebuglogger)(int level, const char* fmt, va_list ap, void *arg) __attribute__ (( format(printf, 2,0) )); /** * Initialize the library with custom memory management functions * you want your application to use. * @param allocator function for allocating memory, * or NULL to use the default * @param reallocator function for reallocating memory, * or NULL to use the default * @param freeor function for release memory, * or NULL to use the default * @return a context object to use in subsequent calls * to the library * @see rtgeom_finish to destroy the created context * @ingroup system */ RTCTX *rtgeom_init(rtallocator allocator, rtreallocator reallocator, rtfreeor freeor); /** * Deinitialize the library, releasing all context memory * * @param ctx a context returned by rtgeom_init * */ void rtgeom_finish(RTCTX *ctx); /** Return rtgeom version string (not to be freed) */ const char* rtgeom_version(void); /** * Return a valid SRID from an arbitrary integer * Raises a notice if what comes out is different from * what went in. * Raises an error if SRID value is out of bounds. */ extern int clamp_srid(const RTCTX *ctx, int srid); /* Raise an rterror if srids do not match */ void error_if_srid_mismatch(const RTCTX *ctx, int srid1, int srid2); /** * This functions are called by programs which want to set up * custom handling for error reporting */ extern void rtgeom_set_error_logger(RTCTX *ctx, rtreporter logger, void *arg); extern void rtgeom_set_notice_logger(RTCTX *ctx, rtreporter logger, void *arg); extern void rtgeom_set_debug_logger(RTCTX *ctx, rtdebuglogger logger, void *arg); /** * Request interruption of any running code * * Safe for use from signal handlers * * Interrupted code will (as soon as it finds out * to be interrupted) cleanup and return as soon as possible. * * The return value from interrupted code is undefined, * it is the caller responsibility to not take it in consideration. * */ extern void rtgeom_request_interrupt(const RTCTX *ctx); /** * Cancel any interruption request */ extern void rtgeom_cancel_interrupt(const RTCTX *ctx); /** * Install a callback to be called periodically during * algorithm execution. Mostly only needed on WIN32 to * dispatch queued signals. * * The callback is invoked before checking for interrupt * being requested, so you can request interruption from * the callback, if you want (see rtgeom_request_interrupt). * */ typedef void (rtinterrupt_callback)(); extern rtinterrupt_callback *rtgeom_register_interrupt_callback(const RTCTX *ctx, rtinterrupt_callback *); /******************************************************************/ typedef struct { double afac, bfac, cfac, dfac, efac, ffac, gfac, hfac, ifac, xoff, yoff, zoff; } RTAFFINE; /******************************************************************/ typedef struct { double xmin, ymin, zmin; double xmax, ymax, zmax; int32_t srid; } BOX3D; /****************************************************************** * RTGBOX structure. * We include the flags (information about dimensinality), * so we don't have to constantly pass them * into functions that use the RTGBOX. */ typedef struct { uint8_t flags; double xmin; double xmax; double ymin; double ymax; double zmin; double zmax; double mmin; double mmax; } RTGBOX; /****************************************************************** * SPHEROID * * Standard definition of an ellipsoid (what wkt calls a spheroid) * f = (a-b)/a * e_sq = (a*a - b*b)/(a*a) * b = a - fa */ typedef struct { double a; /* semimajor axis */ double b; /* semiminor axis b = (a - fa) */ double f; /* flattening f = (a-b)/a */ double e; /* eccentricity (first) */ double e_sq; /* eccentricity squared (first) e_sq = (a*a-b*b)/(a*a) */ double radius; /* spherical average radius = (2*a+b)/3 */ char name[20]; /* name of ellipse */ } SPHEROID; /****************************************************************** * RTPOINT2D, POINT3D, RTPOINT3DM, RTPOINT4D */ typedef struct { double x, y; } RTPOINT2D; typedef struct { double x, y, z; } RTPOINT3DZ; typedef struct { double x, y, z; } POINT3D; typedef struct { double x, y, m; } RTPOINT3DM; typedef struct { double x, y, z, m; } RTPOINT4D; /****************************************************************** * RTPOINTARRAY * Point array abstracts a lot of the complexity of points and point lists. * It handles 2d/3d translation * (2d points converted to 3d will have z=0 or NaN) * DO NOT MIX 2D and 3D POINTS! EVERYTHING* is either one or the other */ typedef struct { /* Array of POINT 2D, 3D or 4D, possibly missaligned. */ uint8_t *serialized_pointlist; /* Use RTFLAGS_* macros to handle */ uint8_t flags; int npoints; /* how many points we are currently storing */ int maxpoints; /* how many points we have space for in serialized_pointlist */ } RTPOINTARRAY; /****************************************************************** * GSERIALIZED */ typedef struct { uint32_t size; /* For PgSQL use only, use VAR* macros to manipulate. */ uint8_t srid[3]; /* 24 bits of SRID */ uint8_t flags; /* HasZ, HasM, HasBBox, IsGeodetic, IsReadOnly */ uint8_t data[1]; /* See gserialized.txt */ } GSERIALIZED; /****************************************************************** * RTGEOM (any geometry type) * * Abstract type, note that 'type', 'bbox' and 'srid' are available in * all geometry variants. */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; void *data; } RTGEOM; /* RTPOINTYPE */ typedef struct { uint8_t type; /* RTPOINTTYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; RTPOINTARRAY *point; /* hide 2d/3d (this will be an array of 1 point) */ } RTPOINT; /* "light-weight point" */ /* RTLINETYPE */ typedef struct { uint8_t type; /* RTLINETYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; RTPOINTARRAY *points; /* array of POINT3D */ } RTLINE; /* "light-weight line" */ /* TRIANGLE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; RTPOINTARRAY *points; } RTTRIANGLE; /* RTCIRCSTRINGTYPE */ typedef struct { uint8_t type; /* RTCIRCSTRINGTYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; RTPOINTARRAY *points; /* array of POINT(3D/3DM) */ } RTCIRCSTRING; /* "light-weight circularstring" */ /* RTPOLYGONTYPE */ typedef struct { uint8_t type; /* RTPOLYGONTYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; int nrings; /* how many rings we are currently storing */ int maxrings; /* how many rings we have space for in **rings */ RTPOINTARRAY **rings; /* list of rings (list of points) */ } RTPOLY; /* "light-weight polygon" */ /* RTMULTIPOINTTYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTPOINT **geoms; } RTMPOINT; /* RTMULTILINETYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTLINE **geoms; } RTMLINE; /* RTMULTIPOLYGONTYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTPOLY **geoms; } RTMPOLY; /* RTCOLLECTIONTYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTGEOM **geoms; } RTCOLLECTION; /* RTCOMPOUNDTYPE */ typedef struct { uint8_t type; /* RTCOMPOUNDTYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTGEOM **geoms; } RTCOMPOUND; /* "light-weight compound line" */ /* RTCURVEPOLYTYPE */ typedef struct { uint8_t type; /* RTCURVEPOLYTYPE */ uint8_t flags; RTGBOX *bbox; int32_t srid; int nrings; /* how many rings we are currently storing */ int maxrings; /* how many rings we have space for in **rings */ RTGEOM **rings; /* list of rings (list of points) */ } RTCURVEPOLY; /* "light-weight polygon" */ /* MULTICURVE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTGEOM **geoms; } RTMCURVE; /* RTMULTISURFACETYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTGEOM **geoms; } RTMSURFACE; /* RTPOLYHEDRALSURFACETYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTPOLY **geoms; } RTPSURFACE; /* RTTINTYPE */ typedef struct { uint8_t type; uint8_t flags; RTGBOX *bbox; int32_t srid; int ngeoms; /* how many geometries we are currently storing */ int maxgeoms; /* how many geometries we have space for in **geoms */ RTTRIANGLE **geoms; } RTTIN; /* Casts RTGEOM->RT* (return NULL if cast is illegal) */ extern RTMPOLY *rtgeom_as_rtmpoly(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTMLINE *rtgeom_as_rtmline(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTMPOINT *rtgeom_as_rtmpoint(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTCOLLECTION *rtgeom_as_rtcollection(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTPOLY *rtgeom_as_rtpoly(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTLINE *rtgeom_as_rtline(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTPOINT *rtgeom_as_rtpoint(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTCIRCSTRING *rtgeom_as_rtcircstring(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTCURVEPOLY *rtgeom_as_rtcurvepoly(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTCOMPOUND *rtgeom_as_rtcompound(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTPSURFACE *rtgeom_as_rtpsurface(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTTRIANGLE *rtgeom_as_rttriangle(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTTIN *rtgeom_as_rttin(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTGEOM *rtgeom_as_multi(const RTCTX *ctx, const RTGEOM *rtgeom); extern RTGEOM *rtgeom_as_curve(const RTCTX *ctx, const RTGEOM *rtgeom); /* Casts RT*->RTGEOM (artays cast) */ extern RTGEOM *rttin_as_rtgeom(const RTCTX *ctx, const RTTIN *obj); extern RTGEOM *rttriangle_as_rtgeom(const RTCTX *ctx, const RTTRIANGLE *obj); extern RTGEOM *rtpsurface_as_rtgeom(const RTCTX *ctx, const RTPSURFACE *obj); extern RTGEOM *rtmpoly_as_rtgeom(const RTCTX *ctx, const RTMPOLY *obj); extern RTGEOM *rtmline_as_rtgeom(const RTCTX *ctx, const RTMLINE *obj); extern RTGEOM *rtmpoint_as_rtgeom(const RTCTX *ctx, const RTMPOINT *obj); extern RTGEOM *rtcollection_as_rtgeom(const RTCTX *ctx, const RTCOLLECTION *obj); extern RTGEOM *rtcircstring_as_rtgeom(const RTCTX *ctx, const RTCIRCSTRING *obj); extern RTGEOM *rtcompound_as_rtgeom(const RTCTX *ctx, const RTCOMPOUND *obj); extern RTGEOM *rtcurvepoly_as_rtgeom(const RTCTX *ctx, const RTCURVEPOLY *obj); extern RTGEOM *rtpoly_as_rtgeom(const RTCTX *ctx, const RTPOLY *obj); extern RTGEOM *rtline_as_rtgeom(const RTCTX *ctx, const RTLINE *obj); extern RTGEOM *rtpoint_as_rtgeom(const RTCTX *ctx, const RTPOINT *obj); extern RTCOLLECTION* rtcollection_add_rtgeom(const RTCTX *ctx, RTCOLLECTION *col, const RTGEOM *geom); extern RTMPOINT* rtmpoint_add_rtpoint(const RTCTX *ctx, RTMPOINT *mobj, const RTPOINT *obj); extern RTMLINE* rtmline_add_rtline(const RTCTX *ctx, RTMLINE *mobj, const RTLINE *obj); extern RTMPOLY* rtmpoly_add_rtpoly(const RTCTX *ctx, RTMPOLY *mobj, const RTPOLY *obj); extern RTPSURFACE* rtpsurface_add_rtpoly(const RTCTX *ctx, RTPSURFACE *mobj, const RTPOLY *obj); extern RTTIN* rttin_add_rttriangle(const RTCTX *ctx, RTTIN *mobj, const RTTRIANGLE *obj); /*********************************************************************** ** Utility functions for flag byte and srid_flag integer. */ /** * Construct a new flags char. */ extern uint8_t gflags(const RTCTX *ctx, int hasz, int hasm, int geodetic); /** * Extract the geometry type from the serialized form (it hides in * the anonymous data area, so this is a handy function). */ extern uint32_t gserialized_get_type(const RTCTX *ctx, const GSERIALIZED *g); /** * Returns the size in bytes to read from toast to get the basic * information from a geometry: GSERIALIZED struct, bbox and type */ extern uint32_t gserialized_max_header_size(const RTCTX *ctx); /** * Extract the SRID from the serialized form (it is packed into * three bytes so this is a handy function). */ extern int32_t gserialized_get_srid(const RTCTX *ctx, const GSERIALIZED *g); /** * Write the SRID into the serialized form (it is packed into * three bytes so this is a handy function). */ extern void gserialized_set_srid(const RTCTX *ctx, GSERIALIZED *g, int32_t srid); /** * Check if a #GSERIALIZED is empty without deserializing first. * Only checks if the number of elements of the parent geometry * is zero, will not catch collections of empty, eg: * GEOMETRYCOLLECTION(POINT EMPTY) */ extern int gserialized_is_empty(const RTCTX *ctx, const GSERIALIZED *g); /** * Check if a #GSERIALIZED has a bounding box without deserializing first. */ extern int gserialized_has_bbox(const RTCTX *ctx, const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has a Z ordinate. */ extern int gserialized_has_z(const RTCTX *ctx, const GSERIALIZED *gser); /** * Check if a #GSERIALIZED has an M ordinate. */ extern int gserialized_has_m(const RTCTX *ctx, const GSERIALIZED *gser); /** * Check if a #GSERIALIZED is a geography. */ extern int gserialized_is_geodetic(const RTCTX *ctx, const GSERIALIZED *gser); /** * Return a number indicating presence of Z and M coordinates. * 0 = None, 1 = M, 2 = Z, 3 = ZM */ extern int gserialized_get_zm(const RTCTX *ctx, const GSERIALIZED *gser); /** * Return the number of dimensions (2, 3, 4) in a geometry */ extern int gserialized_ndims(const RTCTX *ctx, const GSERIALIZED *gser); /** * Call this function to drop BBOX and SRID * from RTGEOM. If RTGEOM type is *not* flagged * with the HASBBOX flag and has a bbox, it * will be released. */ extern void rtgeom_drop_bbox(const RTCTX *ctx, RTGEOM *rtgeom); extern void rtgeom_drop_srid(const RTCTX *ctx, RTGEOM *rtgeom); /** * Compute a bbox if not already computed * * After calling this function rtgeom->bbox is only * NULL if the geometry is empty. */ extern void rtgeom_add_bbox(const RTCTX *ctx, RTGEOM *rtgeom); /** * Compute a box for geom and all sub-geometries, if not already computed */ extern void rtgeom_add_bbox_deep(const RTCTX *ctx, RTGEOM *rtgeom, RTGBOX *gbox); /** * Get a non-empty geometry bounding box, computing and * caching it if not already there * * NOTE: empty geometries don't have a bounding box so * you'd still get a NULL for them. */ extern const RTGBOX *rtgeom_get_bbox(const RTCTX *ctx, const RTGEOM *rtgeom); /** * Determine whether a RTGEOM can contain sub-geometries or not */ extern int rtgeom_is_collection(const RTCTX *ctx, const RTGEOM *rtgeom); /******************************************************************/ /* Functions that work on type numbers */ /** * Determine whether a type number is a collection or not */ extern int rttype_is_collection(const RTCTX *ctx, uint8_t type); /** * Given an rttype number, what homogeneous collection can hold it? */ extern int rttype_get_collectiontype(const RTCTX *ctx, uint8_t type); /** * Return the type name string associated with a type number * (e.g. Point, LineString, Polygon) */ extern const char *rttype_name(const RTCTX *ctx, uint8_t type); /******************************************************************/ /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * will set point's m=0 (or NaN) if pa is 3d or 2d * NOTE: point is a real POINT3D *not* a pointer */ extern RTPOINT4D rt_getPoint4d(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * will set point's m=0 (or NaN) if pa is 3d or 2d * NOTE: this will modify the point4d pointed to by 'point'. */ extern int rt_getPoint4d_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT4D *point); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * NOTE: point is a real POINT3D *not* a pointer */ extern RTPOINT3DZ rt_getPoint3dz(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); extern RTPOINT3DM rt_getPoint3dm(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /* * copies a point from the point array into the parameter point * will set point's z=0 (or NaN) if pa is 2d * NOTE: this will modify the point3d pointed to by 'point'. */ extern int rt_getPoint3dz_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT3DZ *point); extern int rt_getPoint3dm_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT3DM *point); /* * copies a point from the point array into the parameter point * z value (if present is not returned) * NOTE: point is a real POINT3D *not* a pointer */ extern RTPOINT2D rt_getPoint2d(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /* * copies a point from the point array into the parameter point * z value (if present is not returned) * NOTE: this will modify the point2d pointed to by 'point'. */ extern int rt_getPoint2d_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT2D *point); /** * Returns a RTPOINT2D pointer into the RTPOINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ extern const RTPOINT2D* rt_getPoint2d_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /** * Returns a RTPOINT3DZ pointer into the RTPOINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ extern const RTPOINT3DZ* rt_getPoint3dz_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /** * Returns a RTPOINT4D pointer into the RTPOINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ extern const RTPOINT4D* rt_getPoint4d_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /* * set point N to the given value * NOTE that the pointarray can be of any * dimension, the appropriate ordinate values * will be extracted from it * * N must be a valid point index */ extern void ptarray_set_point4d(const RTCTX *ctx, RTPOINTARRAY *pa, int n, const RTPOINT4D *p4d); /* * get a pointer to nth point of a RTPOINTARRAY * You'll need to cast it to appropriate dimensioned point. * Note that if you cast to a higher dimensional point you'll * possibly corrupt the RTPOINTARRAY. * * WARNING: Don't cast this to a POINT ! * it would not be reliable due to memory alignment constraints */ extern uint8_t *rt_getPoint_internal(const RTCTX *ctx, const RTPOINTARRAY *pa, int n); /* * size of point represeneted in the RTPOINTARRAY * 16 for 2d, 24 for 3d, 32 for 4d */ extern int ptarray_point_size(const RTCTX *ctx, const RTPOINTARRAY *pa); /** * Construct an empty pointarray, allocating storage and setting * the npoints, but not filling in any information. Should be used in conjunction * with ptarray_set_point4d to fill in the information in the array. */ extern RTPOINTARRAY* ptarray_construct(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints); /** * Construct a new #RTPOINTARRAY, copying in the data from ptlist */ extern RTPOINTARRAY* ptarray_construct_copy_data(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist); /** * Construct a new #RTPOINTARRAY, referencing to the data from ptlist */ extern RTPOINTARRAY* ptarray_construct_reference_data(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints, uint8_t *ptlist); /** * Create a new #RTPOINTARRAY with no points. Allocate enough storage * to hold maxpoints vertices before having to reallocate the storage * area. */ extern RTPOINTARRAY* ptarray_construct_empty(const RTCTX *ctx, char hasz, char hasm, uint32_t maxpoints); /** * Append a point to the end of an existing #RTPOINTARRAY * If allow_duplicate is RT_TRUE, then a duplicate point will * not be added. */ extern int ptarray_append_point(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *pt, int allow_duplicates); /** * Append a #RTPOINTARRAY, pa2 to the end of an existing #RTPOINTARRAY, pa1. * * If gap_tolerance is >= 0 then the end point of pa1 will be checked for * being within gap_tolerance 2d distance from start point of pa2 or an * error will be raised and RT_FAILURE returned. * A gap_tolerance < 0 disables the check. * * If end point of pa1 and start point of pa2 are 2d-equal, then pa2 first * point will not be appended. */ extern int ptarray_append_ptarray(const RTCTX *ctx, RTPOINTARRAY *pa1, RTPOINTARRAY *pa2, double gap_tolerance); /** * Insert a point into an existing #RTPOINTARRAY. Zero * is the index of the start of the array. */ extern int ptarray_insert_point(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *p, int where); /** * Remove a point from an existing #RTPOINTARRAY. Zero * is the index of the start of the array. */ extern int ptarray_remove_point(const RTCTX *ctx, RTPOINTARRAY *pa, int where); /** * @brief Add a point in a pointarray. * * @param pa the source RTPOINTARRAY * @param p the point to add * @param pdims number of ordinates in p (2..4) * @param where to insert the point. 0 prepends, pa->npoints appends * * @returns a newly constructed RTPOINTARRAY using a newly allocated buffer * for the actual points, or NULL on error. */ extern RTPOINTARRAY *ptarray_addPoint(const RTCTX *ctx, const RTPOINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where); /** * @brief Remove a point from a pointarray. * @param which - is the offset (starting at 0) * @return #RTPOINTARRAY is newly allocated */ extern RTPOINTARRAY *ptarray_removePoint(const RTCTX *ctx, RTPOINTARRAY *pa, uint32_t where); /** * @brief Merge two given RTPOINTARRAY and returns a pointer * on the new aggregate one. * Warning: this function free the two inputs RTPOINTARRAY * @return #RTPOINTARRAY is newly allocated */ extern RTPOINTARRAY *ptarray_merge(const RTCTX *ctx, RTPOINTARRAY *pa1, RTPOINTARRAY *pa2); extern int ptarray_is_closed(const RTCTX *ctx, const RTPOINTARRAY *pa); extern int ptarray_is_closed_2d(const RTCTX *ctx, const RTPOINTARRAY *pa); extern int ptarray_is_closed_3d(const RTCTX *ctx, const RTPOINTARRAY *pa); extern int ptarray_is_closed_z(const RTCTX *ctx, const RTPOINTARRAY *pa); extern void ptarray_longitude_shift(const RTCTX *ctx, RTPOINTARRAY *pa); extern int ptarray_isccw(const RTCTX *ctx, const RTPOINTARRAY *pa); extern void ptarray_reverse(const RTCTX *ctx, RTPOINTARRAY *pa); extern RTPOINTARRAY* ptarray_flip_coordinates(const RTCTX *ctx, RTPOINTARRAY *pa); /** * @d1 start location (distance from start / total distance) * @d2 end location (distance from start / total distance) * @param tolerance snap to vertices at locations < tolerance away from given ones */ extern RTPOINTARRAY *ptarray_substring(const RTCTX *ctx, RTPOINTARRAY *pa, double d1, double d2, double tolerance); /** * Strip out the Z/M components of an #RTGEOM */ extern RTGEOM* rtgeom_force_2d(const RTCTX *ctx, const RTGEOM *geom); extern RTGEOM* rtgeom_force_3dz(const RTCTX *ctx, const RTGEOM *geom); extern RTGEOM* rtgeom_force_3dm(const RTCTX *ctx, const RTGEOM *geom); extern RTGEOM* rtgeom_force_4d(const RTCTX *ctx, const RTGEOM *geom); extern RTGEOM* rtgeom_simplify(const RTCTX *ctx, const RTGEOM *igeom, double dist, int preserve_collapsed); extern RTGEOM* rtgeom_set_effective_area(const RTCTX *ctx, const RTGEOM *igeom, int set_area, double area); /* * Force to use SFS 1.1 geometry type * (rather than SFS 1.2 and/or SQL/MM) */ extern RTGEOM* rtgeom_force_sfs(const RTCTX *ctx, RTGEOM *geom, int version); /*-------------------------------------------------------- * all the base types (point/line/polygon) will have a * basic constructor, basic de-serializer, basic serializer, * bounding box finder and (TODO) serialized form size finder. *--------------------------------------------------------*/ /* * convenience functions to hide the RTPOINTARRAY */ extern int rtpoint_getPoint2d_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT2D *out); extern int rtpoint_getPoint3dz_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT3DZ *out); extern int rtpoint_getPoint3dm_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT3DM *out); extern int rtpoint_getPoint4d_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT4D *out); /****************************************************************** * RTLINE functions ******************************************************************/ /** * Add a RTPOINT to an RTLINE */ extern int rtline_add_rtpoint(const RTCTX *ctx, RTLINE *line, RTPOINT *point, int where); /****************************************************************** * RTPOLY functions ******************************************************************/ /** * Add a ring, allocating extra space if necessary. The polygon takes * ownership of the passed point array. */ extern int rtpoly_add_ring(const RTCTX *ctx, RTPOLY *poly, RTPOINTARRAY *pa); /** * Add a ring, allocating extra space if necessary. The curvepolygon takes * ownership of the passed point array. */ extern int rtcurvepoly_add_ring(const RTCTX *ctx, RTCURVEPOLY *poly, RTGEOM *ring); /** * Add a component, allocating extra space if necessary. The compoundcurve * takes owership of the passed geometry. */ extern int rtcompound_add_rtgeom(const RTCTX *ctx, RTCOMPOUND *comp, RTGEOM *geom); /** * Construct an equivalent compound curve from a linestring. * Compound curves can have linear components, so this works fine */ extern RTCOMPOUND* rtcompound_construct_from_rtline(const RTCTX *ctx, const RTLINE *rtpoly); /** * Construct an equivalent curve polygon from a polygon. Curve polygons * can have linear rings as their rings, so this works fine (in theory?) */ extern RTCURVEPOLY* rtcurvepoly_construct_from_rtpoly(const RTCTX *ctx, RTPOLY *rtpoly); /****************************************************************** * RTGEOM functions ******************************************************************/ extern int rtcollection_ngeoms(const RTCTX *ctx, const RTCOLLECTION *col); /* Given a generic geometry/collection, return the "simplest" form. */ extern RTGEOM *rtgeom_homogenize(const RTCTX *ctx, const RTGEOM *geom); /****************************************************************** * RTMULTIx and RTCOLLECTION functions ******************************************************************/ RTGEOM *rtcollection_getsubgeom(const RTCTX *ctx, RTCOLLECTION *col, int gnum); RTCOLLECTION* rtcollection_extract(const RTCTX *ctx, RTCOLLECTION *col, int type); /****************************************************************** * SERIALIZED FORM functions ******************************************************************/ /** * Set the SRID on an RTGEOM * For collections, only the parent gets an SRID, all * the children get SRID_UNKNOWN. */ extern void rtgeom_set_srid(const RTCTX *ctx, RTGEOM *geom, int srid); /*------------------------------------------------------ * other stuff * * handle the double-to-float conversion. The results of this * will usually be a slightly bigger box because of the difference * between float8 and float4 representations. */ extern BOX3D* box3d_from_gbox(const RTCTX *ctx, const RTGBOX *gbox); extern RTGBOX* box3d_to_gbox(const RTCTX *ctx, const BOX3D *b3d); void expand_box3d(BOX3D *box, double d); /**************************************************************** * MEMORY MANAGEMENT ****************************************************************/ /* * The *_free family of functions frees *all* memory associated * with the pointer. When the recursion gets to the level of the * RTPOINTARRAY, the RTPOINTARRAY is only freed if it is not flagged * as "read only". RTGEOMs constructed on top of GSERIALIZED * from PgSQL use read only point arrays. */ extern void ptarray_free(const RTCTX *ctx, RTPOINTARRAY *pa); extern void rtpoint_free(const RTCTX *ctx, RTPOINT *pt); extern void rtline_free(const RTCTX *ctx, RTLINE *line); extern void rtpoly_free(const RTCTX *ctx, RTPOLY *poly); extern void rttriangle_free(const RTCTX *ctx, RTTRIANGLE *triangle); extern void rtmpoint_free(const RTCTX *ctx, RTMPOINT *mpt); extern void rtmline_free(const RTCTX *ctx, RTMLINE *mline); extern void rtmpoly_free(const RTCTX *ctx, RTMPOLY *mpoly); extern void rtpsurface_free(const RTCTX *ctx, RTPSURFACE *psurf); extern void rttin_free(const RTCTX *ctx, RTTIN *tin); extern void rtcollection_free(const RTCTX *ctx, RTCOLLECTION *col); extern void rtcircstring_free(const RTCTX *ctx, RTCIRCSTRING *curve); extern void rtgeom_free(const RTCTX *ctx, RTGEOM *geom); /* * The *_release family of functions frees the RTGEOM structures * surrounding the RTPOINTARRAYs but leaves the RTPOINTARRAYs * intact. Useful when re-shaping geometries between types, * or splicing geometries together. */ extern void rtpoint_release(const RTCTX *ctx, RTPOINT *rtpoint); extern void rtline_release(const RTCTX *ctx, RTLINE *rtline); extern void rtpoly_release(const RTCTX *ctx, RTPOLY *rtpoly); extern void rttriangle_release(const RTCTX *ctx, RTTRIANGLE *rttriangle); extern void rtcircstring_release(const RTCTX *ctx, RTCIRCSTRING *rtcirc); extern void rtmpoint_release(const RTCTX *ctx, RTMPOINT *rtpoint); extern void rtmline_release(const RTCTX *ctx, RTMLINE *rtline); extern void rtmpoly_release(const RTCTX *ctx, RTMPOLY *rtpoly); extern void rtpsurface_release(RTPSURFACE *rtpsurface); extern void rttin_release(RTTIN *rttin); extern void rtcollection_release(const RTCTX *ctx, RTCOLLECTION *rtcollection); extern void rtgeom_release(const RTCTX *ctx, RTGEOM *rtgeom); /**************************************************************** * Utility ****************************************************************/ extern void printBOX3D(const RTCTX *ctx, BOX3D *b); extern void printPA(const RTCTX *ctx, RTPOINTARRAY *pa); extern void printRTPOINT(const RTCTX *ctx, RTPOINT *point); extern void printRTLINE(const RTCTX *ctx, RTLINE *line); extern void printRTPOLY(const RTCTX *ctx, RTPOLY *poly); extern void printRTTRIANGLE(const RTCTX *ctx, RTTRIANGLE *triangle); extern void printRTPSURFACE(const RTCTX *ctx, RTPSURFACE *psurf); extern void printRTTIN(const RTCTX *ctx, RTTIN *tin); extern float next_float_down(const RTCTX *ctx, double d); extern float next_float_up(const RTCTX *ctx, double d); extern double next_double_down(const RTCTX *ctx, float d); extern double next_double_up(const RTCTX *ctx, float d); /* general utilities 2D */ extern double distance2d_pt_pt(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2); extern double distance2d_sqr_pt_pt(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2); extern double distance2d_pt_seg(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINT2D *A, const RTPOINT2D *B); extern double distance2d_sqr_pt_seg(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINT2D *A, const RTPOINT2D *B); extern RTGEOM* rtgeom_closest_line(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern RTGEOM* rtgeom_furthest_line(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern RTGEOM* rtgeom_closest_point(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern RTGEOM* rtgeom_furthest_point(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_mindistance2d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_mindistance2d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance); extern double rtgeom_maxdistance2d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_maxdistance2d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance); /* 3D */ extern double distance3d_pt_pt(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2); extern double distance3d_pt_seg(const POINT3D *p, const POINT3D *A, const POINT3D *B); extern RTGEOM* rtgeom_furthest_line_3d(const RTCTX *ctx, RTGEOM *rt1, RTGEOM *rt2); extern RTGEOM* rtgeom_closest_line_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern RTGEOM* rtgeom_closest_point_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_mindistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_mindistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance); extern double rtgeom_maxdistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2); extern double rtgeom_maxdistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance); extern double rtgeom_area(const RTCTX *ctx, const RTGEOM *geom); extern double rtgeom_length(const RTCTX *ctx, const RTGEOM *geom); extern double rtgeom_length_2d(const RTCTX *ctx, const RTGEOM *geom); extern double rtgeom_perimeter(const RTCTX *ctx, const RTGEOM *geom); extern double rtgeom_perimeter_2d(const RTCTX *ctx, const RTGEOM *geom); extern void rtgeom_affine(const RTCTX *ctx, RTGEOM *geom, const RTAFFINE *affine); extern void rtgeom_scale(const RTCTX *ctx, RTGEOM *geom, const RTPOINT4D *factors); extern int rtgeom_dimension(const RTCTX *ctx, const RTGEOM *geom); extern RTPOINT* rtline_get_rtpoint(const RTCTX *ctx, const RTLINE *line, int where); extern RTPOINT* rtcircstring_get_rtpoint(const RTCTX *ctx, const RTCIRCSTRING *circ, int where); extern RTPOINT* rtcompound_get_startpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp); extern RTPOINT* rtcompound_get_endpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp); extern RTPOINT* rtcompound_get_rtpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp, int where); extern double ptarray_length_2d(const RTCTX *ctx, const RTPOINTARRAY *pts); extern double ptarray_length(const RTCTX *ctx, const RTPOINTARRAY *pts); extern double ptarray_arc_length_2d(const RTCTX *ctx, const RTPOINTARRAY *pts); extern int pt_in_ring_2d(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *ring); extern int azimuth_pt_pt(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, double *ret); extern int rtpoint_inside_circle(const RTCTX *ctx, const RTPOINT *p, double cx, double cy, double rad); extern void rtgeom_reverse(const RTCTX *ctx, RTGEOM *rtgeom); extern void rtline_reverse(const RTCTX *ctx, RTLINE *line); extern void rtpoly_reverse(const RTCTX *ctx, RTPOLY *poly); extern void rttriangle_reverse(const RTCTX *ctx, RTTRIANGLE *triangle); extern char* rtgeom_summary(const RTCTX *ctx, const RTGEOM *rtgeom, int offset); extern char* rtpoint_to_latlon(const RTCTX *ctx, const RTPOINT *p, const char *format); extern int rtgeom_startpoint(const RTCTX *ctx, const RTGEOM* rtgeom, RTPOINT4D* pt); /** * Ensure the outer ring is clockwise oriented and all inner rings * are counter-clockwise. */ extern void rtgeom_force_clockwise(const RTCTX *ctx, RTGEOM *rtgeom); extern void rtpoly_force_clockwise(const RTCTX *ctx, RTPOLY *poly); extern void rttriangle_force_clockwise(const RTCTX *ctx, RTTRIANGLE *triangle); extern void interpolate_point4d(const RTCTX *ctx, RTPOINT4D *A, RTPOINT4D *B, RTPOINT4D *I, double F); void rtgeom_longitude_shift(const RTCTX *ctx, RTGEOM *rtgeom); /** * @brief Check whether or not a rtgeom is big enough to warrant a bounding box. * * Check whether or not a rtgeom is big enough to warrant a bounding box * when stored in the serialized form on disk. Currently only points are * considered small enough to not require a bounding box, because the * index operations can generate a large number of box-retrieval operations * when scanning keys. */ extern int rtgeom_needs_bbox(const RTCTX *ctx, const RTGEOM *geom); /** * Count the total number of vertices in any #RTGEOM. */ extern int rtgeom_count_vertices(const RTCTX *ctx, const RTGEOM *geom); /** * Count the total number of rings in any #RTGEOM. Multipolygons * and other collections get counted, not the same as OGC st_numrings. */ extern int rtgeom_count_rings(const RTCTX *ctx, const RTGEOM *geom); /** * Return true or false depending on whether a geometry has * a valid SRID set. */ extern int rtgeom_has_srid(const RTCTX *ctx, const RTGEOM *geom); /** * Return true or false depending on whether a geometry is an "empty" * geometry (no vertices members) */ extern int rtgeom_is_empty(const RTCTX *ctx, const RTGEOM *geom); /** * Return true or false depending on whether a geometry is a linear * feature that closes on itself. */ extern int rtgeom_is_closed(const RTCTX *ctx, const RTGEOM *geom); /** * Return the dimensionality (relating to point/line/poly) of an rtgeom */ extern int rtgeom_dimensionality(const RTCTX *ctx, RTGEOM *geom); /* Is rtgeom1 geometrically equal to rtgeom2 ? */ char rtgeom_same(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2); char ptarray_same(const RTCTX *ctx, const RTPOINTARRAY *pa1, const RTPOINTARRAY *pa2); char rtpoint_same(const RTCTX *ctx, const RTPOINT *p1, const RTPOINT *p2); char rtline_same(const RTCTX *ctx, const RTLINE *p1, const RTLINE *p2); char rtpoly_same(const RTCTX *ctx, const RTPOLY *p1, const RTPOLY *p2); char rttriangle_same(const RTCTX *ctx, const RTTRIANGLE *p1, const RTTRIANGLE *p2); char rtcollection_same(const RTCTX *ctx, const RTCOLLECTION *p1, const RTCOLLECTION *p2); char rtcircstring_same(const RTCTX *ctx, const RTCIRCSTRING *p1, const RTCIRCSTRING *p2); /** * @brief Clone RTGEOM object. Serialized point lists are not copied. * * #RTGBOX are copied * * @see ptarray_clone */ extern RTGEOM *rtgeom_clone(const RTCTX *ctx, const RTGEOM *rtgeom); /** * Deep clone an RTGEOM, everything is copied */ extern RTGEOM *rtgeom_clone_deep(const RTCTX *ctx, const RTGEOM *rtgeom); /* TODO Move to Internal */ RTPOINT *rtpoint_clone(const RTCTX *ctx, const RTPOINT *rtgeom); RTPOINTARRAY *ptarray_clone_deep(const RTCTX *ctx, const RTPOINTARRAY *ptarray); /* * Geometry constructors. These constructors to not copy the point arrays * passed to them, they just take references, so do not free them out * from underneath the geometries. */ extern RTPOINT* rtpoint_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *point); extern RTMPOINT *rtmpoint_construct(const RTCTX *ctx, int srid, const RTPOINTARRAY *pa); extern RTLINE* rtline_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points); extern RTCIRCSTRING* rtcircstring_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points); extern RTPOLY* rtpoly_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, uint32_t nrings, RTPOINTARRAY **points); extern RTCURVEPOLY* rtcurvepoly_construct(int srid, RTGBOX *bbox, uint32_t nrings, RTGEOM **geoms); extern RTTRIANGLE* rttriangle_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points); extern RTCOLLECTION* rtcollection_construct(const RTCTX *ctx, uint8_t type, int srid, RTGBOX *bbox, uint32_t ngeoms, RTGEOM **geoms); /* * Empty geometry constructors. */ extern RTGEOM* rtgeom_construct_empty(const RTCTX *ctx, uint8_t type, int srid, char hasz, char hasm); extern RTPOINT* rtpoint_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTLINE* rtline_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTPOLY* rtpoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTCURVEPOLY* rtcurvepoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTCIRCSTRING* rtcircstring_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTCOMPOUND* rtcompound_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTTRIANGLE* rttriangle_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTMPOINT* rtmpoint_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTMLINE* rtmline_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTMPOLY* rtmpoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm); extern RTCOLLECTION* rtcollection_construct_empty(const RTCTX *ctx, uint8_t type, int srid, char hasz, char hasm); /* Other constructors */ extern RTPOINT *rtpoint_make2d(const RTCTX *ctx, int srid, double x, double y); extern RTPOINT *rtpoint_make3dz(const RTCTX *ctx, int srid, double x, double y, double z); extern RTPOINT *rtpoint_make3dm(const RTCTX *ctx, int srid, double x, double y, double m); extern RTPOINT *rtpoint_make4d(const RTCTX *ctx, int srid, double x, double y, double z, double m); extern RTPOINT *rtpoint_make(const RTCTX *ctx, int srid, int hasz, int hasm, const RTPOINT4D *p); extern RTLINE *rtline_from_rtgeom_array(const RTCTX *ctx, int srid, uint32_t ngeoms, RTGEOM **geoms); extern RTLINE *rtline_from_ptarray(const RTCTX *ctx, int srid, uint32_t npoints, RTPOINT **points); /* TODO: deprecate */ extern RTLINE *rtline_from_rtmpoint(const RTCTX *ctx, int srid, const RTMPOINT *mpoint); extern RTLINE *rtline_addpoint(RTLINE *line, RTPOINT *point, uint32_t where); extern RTLINE *rtline_removepoint(const RTCTX *ctx, RTLINE *line, uint32_t which); extern void rtline_setPoint4d(const RTCTX *ctx, RTLINE *line, uint32_t which, RTPOINT4D *newpoint); extern RTPOLY *rtpoly_from_rtlines(const RTCTX *ctx, const RTLINE *shell, uint32_t nholes, const RTLINE **holes); extern RTTRIANGLE *rttriangle_from_rtline(const RTCTX *ctx, const RTLINE *shell); /* Some point accessors */ extern double rtpoint_get_x(const RTCTX *ctx, const RTPOINT *point); extern double rtpoint_get_y(const RTCTX *ctx, const RTPOINT *point); extern double rtpoint_get_z(const RTCTX *ctx, const RTPOINT *point); extern double rtpoint_get_m(const RTCTX *ctx, const RTPOINT *point); /** * Return SRID number */ extern int32_t rtgeom_get_srid(const RTCTX *ctx, const RTGEOM *geom); /** * Return RTRTTYPE number */ extern uint32_t rtgeom_get_type(const RTCTX *ctx, const RTGEOM *geom); /** * Return #RT_TRUE if geometry has Z ordinates */ extern int rtgeom_has_z(const RTCTX *ctx, const RTGEOM *geom); /** * Return #RT_TRUE if geometry has M ordinates. */ extern int rtgeom_has_m(const RTCTX *ctx, const RTGEOM *geom); /** * Return the number of dimensions (2, 3, 4) in a geometry */ extern int rtgeom_ndims(const RTCTX *ctx, const RTGEOM *geom); /* * Given a point, returns the location of closest point on pointarray * as a fraction of total length (0: first point -- 1: last point). * * If not-null, the third argument will be set to the actual distance * of the point from the pointarray. */ extern double ptarray_locate_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT4D *pt, double *dist, RTPOINT4D *p_located); /** * Add a measure dimension to a line, interpolating linearly from the start * to the end value. */ extern RTLINE *rtline_measured_from_rtline(const RTCTX *ctx, const RTLINE *rtline, double m_start, double m_end); extern RTMLINE* rtmline_measured_from_rtmline(const RTCTX *ctx, const RTMLINE *rtmline, double m_start, double m_end); /** * Determine the location(s) along a measured line where m occurs and * return as a multipoint. Offset to left (positive) or right (negative). */ extern RTGEOM* rtgeom_locate_along(const RTCTX *ctx, const RTGEOM *rtin, double m, double offset); /** * Determine the segments along a measured line that fall within the m-range * given. Return as a multiline or geometrycollection. * Offset to left (positive) or right (negative). */ extern RTCOLLECTION* rtgeom_locate_between(const RTCTX *ctx, const RTGEOM *rtin, double from, double to, double offset); /** * Find the measure value at the location on the line closest to the point. */ extern double rtgeom_interpolate_point(const RTCTX *ctx, const RTGEOM *rtin, const RTPOINT *rtpt); /** * Find the time of closest point of approach * * @param mindist if not null will be set to the minimum distance between * the trajectories at the closest point of approach. * * @return the time value in which the minimum distance was reached, -1 * if inputs are invalid (rterror is called in that case), * -2 if the trajectories do not share any point in time. */ extern double rtgeom_tcpa(const RTCTX *ctx, const RTGEOM *g1, const RTGEOM *g2, double *mindist); /** * Is the closest point of approach within a distance ? * * @return RT_TRUE or RT_FALSE */ extern int rtgeom_cpa_within(const RTCTX *ctx, const RTGEOM *g1, const RTGEOM *g2, double maxdist); /** * Return RT_TRUE or RT_FALSE depending on whether or not a geometry is * a linestring with measure value growing from start to end vertex */ extern int rtgeom_is_trajectory(const RTCTX *ctx, const RTGEOM *geom); extern int rtline_is_trajectory(const RTCTX *ctx, const RTLINE *geom); /* * Ensure every segment is at most 'dist' long. * Returned RTGEOM might is unchanged if a POINT. */ extern RTGEOM *rtgeom_segmentize2d(const RTCTX *ctx, RTGEOM *line, double dist); extern RTPOINTARRAY *ptarray_segmentize2d(const RTCTX *ctx, const RTPOINTARRAY *ipa, double dist); extern RTLINE *rtline_segmentize2d(const RTCTX *ctx, RTLINE *line, double dist); extern RTPOLY *rtpoly_segmentize2d(const RTCTX *ctx, RTPOLY *line, double dist); extern RTCOLLECTION *rtcollection_segmentize2d(const RTCTX *ctx, RTCOLLECTION *coll, double dist); /** * Calculate the GeoHash (http://geohash.org) string for a geometry. Caller must free. */ char *rtgeom_geohash(const RTCTX *ctx, const RTGEOM *rtgeom, int precision); unsigned int geohash_point_as_int(const RTCTX *ctx, RTPOINT2D *pt); /** * The return values of rtline_crossing_direction(const RTCTX *ctx) */ enum RTCG_LINE_CROSS_TYPE { LINE_NO_CROSS = 0, LINE_CROSS_LEFT = -1, LINE_CROSS_RIGHT = 1, LINE_MULTICROSS_END_LEFT = -2, LINE_MULTICROSS_END_RIGHT = 2, LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3, LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 }; /** * Given two lines, characterize how (and if) they cross each other */ int rtline_crossing_direction(const RTCTX *ctx, const RTLINE *l1, const RTLINE *l2); /** * Given a geometry clip based on the from/to range of one of its ordinates (x, y, z, m). Use for m- and z- clipping. */ RTCOLLECTION* rtgeom_clip_to_ordinate_range(const RTCTX *ctx, const RTGEOM *rtin, char ordinate, double from, double to, double offset); /** * Macros for specifying GML options. * @{ */ /** For GML3 only, include srsDimension attribute in output */ #define RT_GML_IS_DIMS (1<<0) /** For GML3 only, declare that datas are lat/lon. Swaps axis order */ #define RT_GML_IS_DEGREE (1<<1) /** For GML3, use rather than for lines */ #define RT_GML_SHORTLINE (1<<2) /** For GML2 and GML3, output only extent of geometry */ #define RT_GML_EXTENT (1<<4) #define IS_DIMS(x) ((x) & RT_GML_IS_DIMS) #define IS_DEGREE(x) ((x) & RT_GML_IS_DEGREE) /** @} */ /** * Macros for specifying X3D options. * @{ */ /** For flip X/Y coordinates to Y/X */ #define RT_X3D_FLIP_XY (1<<0) #define RT_X3D_USE_GEOCOORDS (1<<1) #define X3D_USE_GEOCOORDS(x) ((x) & RT_X3D_USE_GEOCOORDS) extern char* rtgeom_to_gml2(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, const char *prefix); extern char* rtgeom_extent_to_gml2(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, const char *prefix); /** * @param opts output options bitfield, see RT_GML macros for meaning */ extern char* rtgeom_extent_to_gml3(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, int opts, const char *prefix); extern char* rtgeom_to_gml3(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, int opts, const char *prefix, const char *id); extern char* rtgeom_to_kml2(const RTCTX *ctx, const RTGEOM *geom, int precision, const char *prefix); extern char* rtgeom_to_geojson(const RTCTX *ctx, const RTGEOM *geo, char *srs, int precision, int has_bbox); extern char* rtgeom_to_svg(const RTCTX *ctx, const RTGEOM *geom, int precision, int relative); extern char* rtgeom_to_x3d3(const RTCTX *ctx, const RTGEOM *geom, char *srs, int precision, int opts, const char *defid); /** * Create an RTGEOM object from a GeoJSON representation * * @param geojson the GeoJSON input * @param srs output parameter. Will be set to a newly allocated * string holding the spatial reference string, or NULL * if no such parameter is found in input. * If not null, the pointer must be freed with rtfree. */ extern RTGEOM* rtgeom_from_geojson(const RTCTX *ctx, const char *geojson, char **srs); /** * Initialize a spheroid object for use in geodetic functions. */ extern void spheroid_init(const RTCTX *ctx, SPHEROID *s, double a, double b); /** * Calculate the geodetic distance from rtgeom1 to rtgeom2 on the spheroid. * A spheroid with major axis == minor axis will be treated as a sphere. * Pass in a tolerance in spheroid units. */ extern double rtgeom_distance_spheroid(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2, const SPHEROID *spheroid, double tolerance); /** * Calculate the location of a point on a spheroid, give a start point, bearing and distance. */ extern RTPOINT* rtgeom_project_spheroid(const RTCTX *ctx, const RTPOINT *r, const SPHEROID *spheroid, double distance, double azimuth); /** * Derive a new geometry with vertices added to ensure no vertex is more * than max_seg_length (in radians) from any other vertex. */ extern RTGEOM* rtgeom_segmentize_sphere(const RTCTX *ctx, const RTGEOM *rtg_in, double max_seg_length); /** * Calculate the bearing between two points on a spheroid. */ extern double rtgeom_azumith_spheroid(const RTCTX *ctx, const RTPOINT *r, const RTPOINT *s, const SPHEROID *spheroid); /** * Calculate the geodetic area of a rtgeom on the sphere. The result * will be multiplied by the average radius of the supplied spheroid. */ extern double rtgeom_area_sphere(const RTCTX *ctx, const RTGEOM *rtgeom, const SPHEROID *spheroid); /** * Calculate the geodetic area of a rtgeom on the spheroid. The result * will have the squared units of the spheroid axes. */ extern double rtgeom_area_spheroid(const RTCTX *ctx, const RTGEOM *rtgeom, const SPHEROID *spheroid); /** * Calculate the geodetic length of a rtgeom on the unit sphere. The result * will have to by multiplied by the real radius to get the real length. */ extern double rtgeom_length_spheroid(const RTCTX *ctx, const RTGEOM *geom, const SPHEROID *s); /** * Calculate covers predicate for two rtgeoms on the sphere. Currently * only handles point-in-polygon. */ extern int rtgeom_covers_rtgeom_sphere(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2); /** * Remove repeated points! */ extern RTGEOM* rtgeom_remove_repeated_points(const RTCTX *ctx, const RTGEOM *in, double tolerance); extern char rttriangle_is_repeated_points(const RTCTX *ctx, RTTRIANGLE *triangle); /** * Swap ordinate values in every vertex of the geometry. * * Ordinates to swap are specified using an index with meaning: * 0=x, 1=y, 2=z, 3=m * * Swapping an existing ordinate with an unexisting one results * in undefined value being written in the existing ordinate. * Caller should verify and prevent such calls. * * Availability: 2.2.0 */ extern void rtgeom_swap_ordinates(const RTCTX *ctx, RTGEOM *in, RTORD o1, RTORD o2); /** * Reverse the X and Y coordinate order. Useful for geometries in lat/lon order * than need to be converted to lon/lat order. * * NOTE: uses rtgeom_swap_ordinates internally, * kept for backward compatibility */ extern RTGEOM* rtgeom_flip_coordinates(const RTCTX *ctx, RTGEOM *in); struct RTPOINTITERATOR; typedef struct RTPOINTITERATOR RTPOINTITERATOR; /** * Create a new RTPOINTITERATOR over supplied RTGEOM* */ extern RTPOINTITERATOR* rtpointiterator_create(const RTCTX *ctx, const RTGEOM* g); /** * Create a new RTPOINTITERATOR over supplied RTGEOM* * Supports modification of coordinates during iteration. */ extern RTPOINTITERATOR* rtpointiterator_create_rw(const RTCTX *ctx, RTGEOM* g); /** * Free all memory associated with the iterator */ extern void rtpointiterator_destroy(const RTCTX *ctx, RTPOINTITERATOR* s); /** * Returns RT_TRUE if there is another point available in the iterator. */ extern int rtpointiterator_has_next(const RTCTX *ctx, RTPOINTITERATOR* s); /** * Attempts to replace the next point int the iterator with p, and advances * the iterator to the next point. * Returns RT_SUCCESS if the assignment was successful, RT_FAILURE otherwise. * */ extern int rtpointiterator_modify_next(const RTCTX *ctx, RTPOINTITERATOR* s, const RTPOINT4D* p); /** * Attempts to assign the next point in the iterator to p, and advances * the iterator to the next point. If p is NULL, the iterator will be * advanced without reading a point. * Returns RT_SUCCESS if the assignment was successful, RT_FAILURE otherwise. * */ extern int rtpointiterator_next(const RTCTX *ctx, RTPOINTITERATOR* s, RTPOINT4D* p); /** * Attempts to assigns the next point in the iterator to p. Does not advance. * Returns RT_SUCCESS if the assignment was successful, RT_FAILURE otherwise. */ extern int rtpointiterator_peek(const RTCTX *ctx, RTPOINTITERATOR* s, RTPOINT4D* p); /** * Convert a single hex digit into the corresponding char */ extern uint8_t parse_hex(const RTCTX *ctx, char *str); /** * Convert a char into a human readable hex digit */ extern void deparse_hex(const RTCTX *ctx, uint8_t str, char *result); /*********************************************************************** ** Functions for managing serialized forms and bounding boxes. */ /** * Calculate the geocentric bounding box directly from the serialized * form of the geodetic coordinates. Only accepts serialized geographies * flagged as geodetic. Caller is responsible for disposing of the RTGBOX. */ extern RTGBOX* gserialized_calculate_gbox_geocentric(const GSERIALIZED *g); /** * Calculate the geocentric bounding box directly from the serialized * form of the geodetic coordinates. Only accepts serialized geographies * flagged as geodetic. */ int gserialized_calculate_gbox_geocentric_p(const GSERIALIZED *g, RTGBOX *g_box); /** * Return a RTWKT representation of the gserialized geometry. * Caller is responsible for disposing of the char*. */ extern char* gserialized_to_string(const RTCTX *ctx, const GSERIALIZED *g); /** * Return a copy of the input serialized geometry. */ extern GSERIALIZED* gserialized_copy(const RTCTX *ctx, const GSERIALIZED *g); /** * Check that coordinates of RTGEOM are all within the geodetic range (-180, -90, 180, 90) */ extern int rtgeom_check_geodetic(const RTCTX *ctx, const RTGEOM *geom); /** * Gently move coordinates of RTGEOM if they are close enough into geodetic range. */ extern int rtgeom_nudge_geodetic(const RTCTX *ctx, RTGEOM *geom); /** * Force coordinates of RTGEOM into geodetic range (-180, -90, 180, 90) */ extern int rtgeom_force_geodetic(const RTCTX *ctx, RTGEOM *geom); /** * Set the RTFLAGS geodetic bit on geometry an all sub-geometries and pointlists */ extern void rtgeom_set_geodetic(const RTCTX *ctx, RTGEOM *geom, int value); /** * Calculate the geodetic bounding box for an RTGEOM. Z/M coordinates are * ignored for this calculation. Pass in non-null, geodetic bounding box for function * to fill out. RTGEOM must have been built from a GSERIALIZED to provide * double aligned point arrays. */ extern int rtgeom_calculate_gbox_geodetic(const RTCTX *ctx, const RTGEOM *geom, RTGBOX *gbox); /** * Calculate the 2-4D bounding box of a geometry. Z/M coordinates are honored * for this calculation, though for curves they are not included in calculations * of curvature. */ extern int rtgeom_calculate_gbox_cartesian(const RTCTX *ctx, const RTGEOM *rtgeom, RTGBOX *gbox); /** * Calculate bounding box of a geometry, automatically taking into account * whether it is cartesian or geodetic. */ extern int rtgeom_calculate_gbox(const RTCTX *ctx, const RTGEOM *rtgeom, RTGBOX *gbox); /** * New function to read doubles directly from the double* coordinate array * of an aligned rtgeom #RTPOINTARRAY (built by de-serializing a #GSERIALIZED). */ extern int rt_getPoint2d_p_ro(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT2D **point); /** * Calculate geodetic (x/y/z) box and add values to gbox. Return #RT_SUCCESS on success. */ extern int ptarray_calculate_gbox_geodetic(const RTCTX *ctx, const RTPOINTARRAY *pa, RTGBOX *gbox); /** * Calculate box (x/y) and add values to gbox. Return #RT_SUCCESS on success. */ extern int ptarray_calculate_gbox_cartesian(const RTCTX *ctx, const RTPOINTARRAY *pa, RTGBOX *gbox ); /** * Calculate a spherical point that falls outside the geocentric gbox */ void gbox_pt_outside(const RTCTX *ctx, const RTGBOX *gbox, RTPOINT2D *pt_outside); /** * Create a new gbox with the dimensionality indicated by the flags. Caller * is responsible for freeing. */ extern RTGBOX* gbox_new(const RTCTX *ctx, uint8_t flags); /** * Zero out all the entries in the #RTGBOX. Useful for cleaning * statically allocated gboxes. */ extern void gbox_init(const RTCTX *ctx, RTGBOX *gbox); /** * Update the merged #RTGBOX to be large enough to include itself and the new box. */ extern int gbox_merge(const RTCTX *ctx, const RTGBOX *new_box, RTGBOX *merged_box); /** * Update the output #RTGBOX to be large enough to include both inputs. */ extern int gbox_union(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2, RTGBOX *gout); /** * Move the box minimums down and the maximums up by the distance provided. */ extern void gbox_expand(const RTCTX *ctx, RTGBOX *g, double d); /** * Initialize a #RTGBOX using the values of the point. */ extern int gbox_init_point3d(const RTCTX *ctx, const POINT3D *p, RTGBOX *gbox); /** * Update the #RTGBOX to be large enough to include itself and the new point. */ extern int gbox_merge_point3d(const RTCTX *ctx, const POINT3D *p, RTGBOX *gbox); /** * Return true if the point is inside the gbox */ extern int gbox_contains_point3d(const RTCTX *ctx, const RTGBOX *gbox, const POINT3D *pt); /** * Allocate a string representation of the #RTGBOX, based on dimensionality of flags. */ extern char* gbox_to_string(const RTCTX *ctx, const RTGBOX *gbox); /** * Return a copy of the #RTGBOX, based on dimensionality of flags. */ extern RTGBOX* gbox_copy(const RTCTX *ctx, const RTGBOX *gbox); /** * Warning, do not use this function, it is very particular about inputs. */ extern RTGBOX* gbox_from_string(const RTCTX *ctx, const char *str); /** * Return #RT_TRUE if the #RTGBOX overlaps, #RT_FALSE otherwise. */ extern int gbox_overlaps(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Return #RT_TRUE if the #RTGBOX overlaps on the 2d plane, #RT_FALSE otherwise. */ extern int gbox_overlaps_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Return #RT_TRUE if the first #RTGBOX contains the second on the 2d plane, #RT_FALSE otherwise. */ extern int gbox_contains_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Copy the values of original #RTGBOX into duplicate. */ extern void gbox_duplicate(const RTCTX *ctx, const RTGBOX *original, RTGBOX *duplicate); /** * Return the number of bytes necessary to hold a #RTGBOX of this dimension in * serialized form. */ extern size_t gbox_serialized_size(const RTCTX *ctx, uint8_t flags); /** * Check if 2 given Gbox are the same */ extern int gbox_same(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Check if 2 given RTGBOX are the same in x and y */ extern int gbox_same_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Check if two given RTGBOX are the same in x and y, or would round to the same * RTGBOX in x and if serialized in GSERIALIZED */ extern int gbox_same_2d_float(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2); /** * Round given RTGBOX to float boundaries * * This turns a RTGBOX into the version it would become * after a serialize/deserialize round trip. */ extern void gbox_float_round(const RTCTX *ctx, RTGBOX *gbox); /** * Return false if any of the dimensions is NaN or infinite */ extern int gbox_is_valid(const RTCTX *ctx, const RTGBOX *gbox); /** * Utility function to get type number from string. For example, a string 'POINTZ' * would return type of 1 and z of 1 and m of 0. Valid */ extern int geometry_type_from_string(const RTCTX *ctx, const char *str, uint8_t *type, int *z, int *m); /** * Calculate required memory segment to contain a serialized form of the RTGEOM. * Primarily used internally by the serialization code. Exposed to allow the cunit * tests to exercise it. */ extern size_t gserialized_from_rtgeom_size(const RTCTX *ctx, const RTGEOM *geom); /** * Allocate a new #GSERIALIZED from an #RTGEOM. For all non-point types, a bounding * box will be calculated and embedded in the serialization. The geodetic flag is used * to control the box calculation (cartesian or geocentric). If set, the size pointer * will contain the size of the final output, which is useful for setting the PgSQL * VARSIZE information. */ extern GSERIALIZED* gserialized_from_rtgeom(const RTCTX *ctx, RTGEOM *geom, int is_geodetic, size_t *size); /** * Allocate a new #RTGEOM from a #GSERIALIZED. The resulting #RTGEOM will have coordinates * that are double aligned and suitable for direct reading using rt_getPoint2d_p_ro */ extern RTGEOM* rtgeom_from_gserialized(const RTCTX *ctx, const GSERIALIZED *g); /** * Pull a #RTGBOX from the header of a #GSERIALIZED, if one is available. If * it is not, calculate it from the geometry. If that doesn't work (null * or empty) return RT_FAILURE. */ extern int gserialized_get_gbox_p(const RTCTX *ctx, const GSERIALIZED *g, RTGBOX *gbox); /** * Parser check flags * * @see rtgeom_from_wkb * @see rtgeom_from_hexwkb */ #define RT_PARSER_CHECK_MINPOINTS 1 #define RT_PARSER_CHECK_ODD 2 #define RT_PARSER_CHECK_CLOSURE 4 #define RT_PARSER_CHECK_ZCLOSURE 8 #define RT_PARSER_CHECK_NONE 0 #define RT_PARSER_CHECK_ALL (RT_PARSER_CHECK_MINPOINTS | RT_PARSER_CHECK_ODD | RT_PARSER_CHECK_CLOSURE) /** * Parser result structure: returns the result of attempting to convert * (E)RTWKT/(E)RTWKB to RTGEOM */ typedef struct struct_rtgeom_parser_result { const char *wkinput; /* Copy of pointer to input RTWKT/RTWKB */ uint8_t *serialized_rtgeom; /* Pointer to serialized RTGEOM */ int size; /* Size of serialized RTGEOM in bytes */ RTGEOM *geom; /* Pointer to RTGEOM struct */ const char *message; /* Error/warning message */ int errcode; /* Error/warning number */ int errlocation; /* Location of error */ int parser_check_flags; /* Bitmask of validity checks run during this parse */ } RTGEOM_PARSER_RESULT; /* * Parser error messages (these must match the message array in rtgparse.c) */ #define PARSER_ERROR_MOREPOINTS 1 #define PARSER_ERROR_ODDPOINTS 2 #define PARSER_ERROR_UNCLOSED 3 #define PARSER_ERROR_MIXDIMS 4 #define PARSER_ERROR_INVALIDGEOM 5 #define RTPARSER_ERROR_INVALIDWKBTYPE 6 #define PARSER_ERROR_INCONTINUOUS 7 #define PARSER_ERROR_TRIANGLEPOINTS 8 #define PARSER_ERROR_LESSPOINTS 9 #define PARSER_ERROR_OTHER 10 /* * Unparser result structure: returns the result of attempting to convert RTGEOM to (E)RTWKT/(E)RTWKB */ typedef struct struct_rtgeom_unparser_result { uint8_t *serialized_rtgeom; /* Copy of pointer to input serialized RTGEOM */ char *wkoutput; /* Pointer to RTWKT or RTWKB output */ int size; /* Size of serialized RTGEOM in bytes */ const char *message; /* Error/warning message */ int errlocation; /* Location of error */ } RTGEOM_UNPARSER_RESULT; /* * Unparser error messages (these must match the message array in rtgunparse.c) */ #define UNPARSER_ERROR_MOREPOINTS 1 #define UNPARSER_ERROR_ODDPOINTS 2 #define UNPARSER_ERROR_UNCLOSED 3 /* ** Variants available for RTWKB and RTWKT output types */ #define RTWKB_ISO 0x01 #define RTWKB_SFSQL 0x02 #define RTWKB_EXTENDED 0x04 #define RTWKB_NDR 0x08 #define RTWKB_XDR 0x10 #define RTWKB_HEX 0x20 #define RTWKB_NO_NPOINTS 0x40 /* Internal use only */ #define RTWKB_NO_SRID 0x80 /* Internal use only */ #define RTWKT_ISO 0x01 #define RTWKT_SFSQL 0x02 #define RTWKT_EXTENDED 0x04 /* ** Variants available for TWKB */ #define TWKB_BBOX 0x01 /* User wants bboxes */ #define TWKB_SIZE 0x02 /* User wants sizes */ #define TWKB_ID 0x04 /* User wants id */ #define RTTWKB_NO_TYPE 0x10 /* No type because it is a sub geoemtry */ #define TWKB_NO_ID 0x20 /* No ID because it is a subgeoemtry */ #define TWKB_DEFAULT_PRECISION 0 /* Aim for 1m (or ft) rounding by default */ /* ** New parsing and unparsing functions. */ /** * @param rtgeom geometry to convert to RTWKT * @param variant output format to use (RTWKT_ISO, RTWKT_SFSQL, RTWKT_EXTENDED) */ extern char* rtgeom_to_wkt(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, int precision, size_t *size_out); /** * @param rtgeom geometry to convert to RTWKT * @param variant output format to use * (RTWKB_ISO, RTWKB_SFSQL, RTWKB_EXTENDED, RTWKB_NDR, RTWKB_XDR) */ extern uint8_t* rtgeom_to_wkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, size_t *size_out); /** * @param rtgeom geometry to convert to HEXWKB * @param variant output format to use * (RTWKB_ISO, RTWKB_SFSQL, RTWKB_EXTENDED, RTWKB_NDR, RTWKB_XDR) */ extern char* rtgeom_to_hexwkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, size_t *size_out); /** * @param rtgeom geometry to convert to EWKT */ extern char *rtgeom_to_ewkt(const RTCTX *ctx, const RTGEOM *rtgeom); /** * @param check parser check flags, see RT_PARSER_CHECK_* macros * @param size length of RTWKB byte buffer * @param wkb RTWKB byte buffer */ extern RTGEOM* rtgeom_from_wkb(const RTCTX *ctx, const uint8_t *wkb, const size_t wkb_size, const char check); /** * @param check parser check flags, see RT_PARSER_CHECK_* macros */ extern RTGEOM* rtgeom_from_hexwkb(const RTCTX *ctx, const char *hexwkb, const char check); extern uint8_t* bytes_from_hexbytes(const RTCTX *ctx, const char *hexbuf, size_t hexsize); extern char* hexbytes_from_bytes(const RTCTX *ctx, uint8_t *bytes, size_t size); /* Memory management */ extern void *rtalloc(const RTCTX *ctx, size_t size); extern void *rtrealloc(const RTCTX *ctx, void *mem, size_t size); extern void rtfree(const RTCTX *ctx, void *mem); /* Utilities */ extern char *rtmessage_truncate(const RTCTX *ctx, char *str, int startpos, int endpos, int maxlength, int truncdirection); /* * TWKB functions */ /** * @param check parser check flags, see RT_PARSER_CHECK_* macros * @param size parser check flags, see RT_PARSER_CHECK_* macros */ extern RTGEOM* rtgeom_from_twkb(const RTCTX *ctx, uint8_t *twkb, size_t twkb_size, char check); /** * @param geom input geometry * @param variant what variations on TWKB are requested? * @param twkb_size returns the length of the output TWKB in bytes if set */ extern uint8_t* rtgeom_to_twkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size); extern uint8_t* rtgeom_to_twkb_with_idlist(const RTCTX *ctx, const RTGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size); /******************************************************************************* * SQLMM internal functions - TODO: Move into separate header files ******************************************************************************/ int rtgeom_has_arc(const RTCTX *ctx, const RTGEOM *geom); RTGEOM *rtgeom_stroke(const RTCTX *ctx, const RTGEOM *geom, uint32_t perQuad); RTGEOM *rtgeom_unstroke(const RTCTX *ctx, const RTGEOM *geom); /******************************************************************************* * GEOS proxy functions on RTGEOM ******************************************************************************/ /** Return GEOS version string (not to be freed) */ const char* rtgeom_geos_version(void); /** Convert an RTGEOM to a GEOS Geometry and convert back -- for debug only */ RTGEOM* rtgeom_geos_noop(const RTCTX *ctx, const RTGEOM *geom) ; RTGEOM *rtgeom_normalize(const RTCTX *ctx, const RTGEOM *geom); RTGEOM *rtgeom_intersection(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2); RTGEOM *rtgeom_difference(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2); RTGEOM *rtgeom_symdifference(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2); RTGEOM *rtgeom_union(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2); RTGEOM *rtgeom_linemerge(const RTCTX *ctx, const RTGEOM *geom1); RTGEOM *rtgeom_unaryunion(const RTCTX *ctx, const RTGEOM *geom1); RTGEOM *rtgeom_clip_by_rect(const RTCTX *ctx, const RTGEOM *geom1, double x0, double y0, double x1, double y1); RTCOLLECTION *rtgeom_subdivide(const RTCTX *ctx, const RTGEOM *geom, int maxvertices); /** * Snap vertices and segments of a geometry to another using a given tolerance. * * @param geom1 the geometry to snap * @param geom2 the geometry to snap to * @param tolerance the distance under which vertices and segments are snapped * * Requires GEOS-3.3.0+ */ RTGEOM* rtgeom_snap(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2, double tolerance); /* * Return the set of paths shared between two linear geometries, * and their direction (same or opposite). * * @param geom1 a lineal geometry * @param geom2 another lineal geometry * * Requires GEOS-3.3.0+ */ RTGEOM* rtgeom_sharedpaths(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2); /* * An offset curve against the input line. * * @param rtline a lineal geometry * @param size offset distance. Offset left if negative and right if positive * @param quadsegs number of quadrature segments in curves (try 8) * @param joinStyle (1 = round, 2 = mitre, 3 = bevel) * @param mitreLimit (try 5.0) * @return derived geometry (linestring or multilinestring) * * Requires GEOS-3.2.0+ */ RTGEOM* rtgeom_offsetcurve(const RTCTX *ctx, const RTLINE *rtline, double size, int quadsegs, int joinStyle, double mitreLimit); /* * Return true if the input geometry is "simple" as per OGC defn. * * @return 1 if simple, 0 if non-simple, -1 on exception (rterror is called * in that case) */ int rtgeom_is_simple(const RTCTX *ctx, const RTGEOM *rtgeom); /******************************************************************************* * GEOS-dependent extra functions on RTGEOM ******************************************************************************/ /** * Take a geometry and return an areal geometry * (Polygon or MultiPolygon). * Actually a wrapper around GEOSpolygonize, * transforming the resulting collection into * a valid polygon Geometry. */ RTGEOM* rtgeom_buildarea(const RTCTX *ctx, const RTGEOM *geom) ; /** * Attempts to make an invalid geometries valid w/out losing points. * * NOTE: this is only available when librtgeom is built against * GEOS 3.3.0 or higher */ RTGEOM* rtgeom_make_valid(const RTCTX *ctx, RTGEOM* geom); /* * Split (multi)polygon by line; (multi)line by (multi)line, * (multi)point or (multi)polygon boundary. * * Collections are accepted as first argument. * Returns all obtained pieces as a collection. */ RTGEOM* rtgeom_split(const RTCTX *ctx, const RTGEOM* rtgeom_in, const RTGEOM* blade_in); /* * Fully node a set of linestrings, using the least nodes preserving * all the input ones. * * Requires GEOS-3.3.0 or higher */ RTGEOM* rtgeom_node(const RTCTX *ctx, const RTGEOM* rtgeom_in); /** * Take vertices of a geometry and build a delaunay * triangulation on them. * * @param geom the input geometry * @param tolerance an optional snapping tolerance for improved tolerance * @param edgeOnly if non-zero the result will be a MULTILINESTRING, * otherwise it'll be a COLLECTION of polygons. */ RTGEOM* rtgeom_delaunay_triangulation(const RTCTX *ctx, const RTGEOM *geom, double tolerance, int edgeOnly); #endif /* !defined _LIBRTGEOM_H */ makefile.vc000066400000000000000000000040311271715413500131710ustar00rootroot00000000000000# $Id: makefile.vc 2016/04/24 Sandro Furieri $ # # NMAKE Makefile to build librttopo on Windows # !INCLUDE nmake.opt LIBOBJ = src\box2d.obj src\bytebuffer.obj src\g_box.obj \ src\g_serialized.obj src\g_util.obj src\measures3d.obj src\measures.obj \ src\ptarray.obj src\rtalgorithm.obj src\rtcircstring.obj src\rtcollection.obj \ src\rtcompound.obj src\rtcurvepoly.obj src\rtgeodetic.obj \ src\rtgeom_api.obj src\rtgeom.obj src\rtgeom_debug.obj src\rtgeom_geos.obj \ src\rtgeom_geos_clean.obj src\rtgeom_geos_node.obj src\rtgeom_geos_split.obj \ src\rtgeom_topo.obj src\rthomogenize.obj src\rtin_geojson.obj src\rtin_twkb.obj \ src\rtin_wkb.obj src\rtiterator.obj src\rtlinearreferencing.obj src\rtline.obj \ src\rtmcurve.obj src\rtmline.obj src\rtmpoint.obj src\rtmpoly.obj src\rtmsurface.obj \ src\rtout_encoded_polyline.obj src\rtout_geojson.obj src\rtout_gml.obj \ src\rtout_kml.obj src\rtout_svg.obj src\rtout_twkb.obj src\rtout_wkb.obj \ src\rtout_wkt.obj src\rtout_x3d.obj src\rtpoint.obj src\rtpoly.obj src\rtprint.obj \ src\rtpsurface.obj src\rtspheroid.obj src\rtstroke.obj src\rttin.obj src\rttree.obj \ src\rttriangle.obj src\rtutil.obj src\stringbuffer.obj src\varint.obj LIBRTTOPO_DLL = librttopo$(VERSION).dll CFLAGS = /nologo -IC:\OSGeo4W\include -I. -Iheaders $(OPTFLAGS) default: all all: librttopo.lib librttopo_i.lib librttopo.lib: $(LIBOBJ) if exist librttopo.lib del librttopo.lib lib /out:librttopo.lib $(LIBOBJ) $(LIBRTTOPO_DLL): librttopo_i.lib librttopo_i.lib: $(LIBOBJ) link /debug /dll /out:$(LIBRTTOPO_DLL) \ /implib:librrttopo_i.lib $(LIBOBJ) \ C:\OSGeo4W\lib\geos_c.lib if exist $(LIBRTTOPO_DLL).manifest mt -manifest \ $(LIBRTTOPO_DLL).manifest -outputresource:$(LIBRTTOPO_DLL);2 .c.obj: $(CC) $(CFLAGS) /c $*.c /Fo$@ clean: del *.dll del *.exp del *.manifest del *.lib del src\*.obj del *.pdb install: all -mkdir $(INSTDIR) -mkdir $(INSTDIR)\bin -mkdir $(INSTDIR)\lib -mkdir $(INSTDIR)\include copy *.dll $(INSTDIR)\bin copy *.lib $(INSTDIR)\lib copy headers\*.h $(INSTDIR)\include nmake.opt000066400000000000000000000007221271715413500127040ustar00rootroot00000000000000# Directory tree where RTTOPO will be installed. INSTDIR=C:\OSGeo4W # Uncomment the first for an optimized build, or the second for debug. OPTFLAGS= /nologo /Ox /fp:precise /W4 /MD /D_CRT_SECURE_NO_WARNINGS \ /DDLL_EXPORT #OPTFLAGS= /nologo /Zi /MD /Fdlibrttopo.pdb /DDLL_EXPORT # Set the version number for the DLL. Normally we leave this blank since # we want software that is dynamically loading the DLL to have no problem # with version numbers. VERSION= rttopo.pc.in000066400000000000000000000003661271715413500133510ustar00rootroot00000000000000# Package Information for pkg-config prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: rttopo Description: the RT Topology library Version: @VERSION@ Libs: -L${libdir} -lrttopo -lm Cflags: -I${includedir} src/000077500000000000000000000000001271715413500116535ustar00rootroot00000000000000src/Makefile.am000066400000000000000000000022251271715413500137100ustar00rootroot00000000000000 AM_CPPFLAGS = -I$(top_srcdir)/src AM_CPPFLAGS += -I$(top_srcdir)/headers lib_LTLIBRARIES = librttopo.la librttopo_la_SOURCES = box2d.c bytebuffer.c g_box.c \ g_serialized.c g_util.c measures3d.c measures.c \ ptarray.c rtalgorithm.c rtcircstring.c rtcollection.c \ rtcompound.c rtcurvepoly.c rtgeodetic.c \ rtgeom_api.c rtgeom.c rtgeom_debug.c rtgeom_geos.c \ rtgeom_geos_clean.c rtgeom_geos_node.c rtgeom_geos_split.c \ rtgeom_topo.c rthomogenize.c rtin_geojson.c rtin_twkb.c \ rtin_wkb.c rtiterator.c rtlinearreferencing.c rtline.c \ rtmcurve.c rtmline.c rtmpoint.c rtmpoly.c rtmsurface.c \ rtout_encoded_polyline.c rtout_geojson.c rtout_gml.c \ rtout_kml.c rtout_svg.c rtout_twkb.c rtout_wkb.c \ rtout_wkt.c rtout_x3d.c rtpoint.c rtpoly.c rtprint.c \ rtpsurface.c rtspheroid.c rtstroke.c rttin.c rttree.c \ rttriangle.c rtutil.c stringbuffer.c varint.c librttopo_la_LDFLAGS = -version-info 1:0:0 -no-undefined librttopo_la_LIBADD = -lm noinst_HEADERS = bytebuffer.h librttopo_geom_internal.h \ librttopo_internal.h measures3d.h measures.h \ rtgeodetic.h rtgeom_geos.h \ rtgeom_log.h rtout_twkb.h rttopo_config.h \ rttree.h stringbuffer.h varint.h src/box2d.c000066400000000000000000000027601271715413500130420ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2004-2015 Sandro Santilli * Copyright (C) 2008-2011 Paul Ramsey * Copyright (C) 2008 Mark Cave-Ayland * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #ifndef EPSILON #define EPSILON 1.0E-06 #endif #ifndef FPeq #define FPeq(A,B) (fabs((A) - (B)) <= EPSILON) #endif RTGBOX * box2d_clone(const RTCTX *ctx, const RTGBOX *in) { RTGBOX *ret = rtalloc(ctx, sizeof(RTGBOX)); memcpy(ret, in, sizeof(RTGBOX)); return ret; } src/bytebuffer.c000066400000000000000000000220651271715413500141610ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2015 Nicklas Avén * **********************************************************************/ #include "librttopo_geom_internal.h" #include "bytebuffer.h" /** * Allocate a new bytebuffer_t. Use bytebuffer_destroy to free. */ bytebuffer_t* bytebuffer_create(const RTCTX *ctx) { RTDEBUG(2,"Entered bytebuffer_create"); return bytebuffer_create_with_size(ctx, BYTEBUFFER_STARTSIZE); } /** * Allocate a new bytebuffer_t. Use bytebuffer_destroy to free. */ bytebuffer_t* bytebuffer_create_with_size(const RTCTX *ctx, size_t size) { RTDEBUGF(2,"Entered bytebuffer_create_with_size %d", size); bytebuffer_t *s; s = rtalloc(ctx, sizeof(bytebuffer_t)); s->buf_start = rtalloc(ctx, size); s->readcursor = s->writecursor = s->buf_start; s->capacity = size; memset(s->buf_start,0,size); RTDEBUGF(4,"We create a buffer on %p of %d bytes", s->buf_start, size); return s; } /** * Allocate just the internal buffer of an existing bytebuffer_t * struct. Useful for allocating short-lived bytebuffers off the stack. */ void bytebuffer_init_with_size(const RTCTX *ctx, bytebuffer_t *b, size_t size) { b->buf_start = rtalloc(ctx, size); b->readcursor = b->writecursor = b->buf_start; b->capacity = size; memset(b->buf_start, 0, size); } /** * Free the bytebuffer_t and all memory managed within it. */ void bytebuffer_destroy(const RTCTX *ctx, bytebuffer_t *s) { RTDEBUG(2,"Entered bytebuffer_destroy"); RTDEBUGF(4,"The buffer has used %d bytes",bytebuffer_getlength(ctx, s)); if ( s->buf_start ) { RTDEBUGF(4,"let's free buf_start %p",s->buf_start); rtfree(ctx, s->buf_start); RTDEBUG(4,"buf_start is freed"); } if ( s ) { rtfree(ctx, s); RTDEBUG(4,"bytebuffer_t is freed"); } return; } /** * Set the read cursor to the beginning */ void bytebuffer_reset_reading(const RTCTX *ctx, bytebuffer_t *s) { s->readcursor = s->buf_start; } /** * Reset the bytebuffer_t. Useful for starting a fresh string * without the expense of freeing and re-allocating a new * bytebuffer_t. */ void bytebuffer_clear(const RTCTX *ctx, bytebuffer_t *s) { s->readcursor = s->writecursor = s->buf_start; } /** * If necessary, expand the bytebuffer_t internal buffer to accomodate the * specified additional size. */ static inline void bytebuffer_makeroom(const RTCTX *ctx, bytebuffer_t *s, size_t size_to_add) { RTDEBUGF(2,"Entered bytebuffer_makeroom with space need of %d", size_to_add); size_t current_write_size = (s->writecursor - s->buf_start); size_t capacity = s->capacity; size_t required_size = current_write_size + size_to_add; RTDEBUGF(2,"capacity = %d and required size = %d",capacity ,required_size); while (capacity < required_size) capacity *= 2; if ( capacity > s->capacity ) { RTDEBUGF(4,"We need to realloc more memory. New capacity is %d", capacity); s->buf_start = rtrealloc(ctx, s->buf_start, capacity); s->capacity = capacity; s->writecursor = s->buf_start + current_write_size; s->readcursor = s->buf_start + (s->readcursor - s->buf_start); } return; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_byte(const RTCTX *ctx, bytebuffer_t *s, const uint8_t val) { RTDEBUGF(2,"Entered bytebuffer_append_byte with value %d", val); bytebuffer_makeroom(ctx, s, 1); *(s->writecursor)=val; s->writecursor += 1; return; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_bulk(const RTCTX *ctx, bytebuffer_t *s, void * start, size_t size) { RTDEBUGF(2,"bytebuffer_append_bulk with size %d",size); bytebuffer_makeroom(ctx, s, size); memcpy(s->writecursor, start, size); s->writecursor += size; return; } /** * Writes a uint8_t value to the buffer */ void bytebuffer_append_bytebuffer(const RTCTX *ctx, bytebuffer_t *write_to,bytebuffer_t *write_from ) { RTDEBUG(2,"bytebuffer_append_bytebuffer"); size_t size = bytebuffer_getlength(ctx, write_from); bytebuffer_makeroom(ctx, write_to, size); memcpy(write_to->writecursor, write_from->buf_start, size); write_to->writecursor += size; return; } /** * Writes a signed varInt to the buffer */ void bytebuffer_append_varint(const RTCTX *ctx, bytebuffer_t *b, const int64_t val) { size_t size; bytebuffer_makeroom(ctx, b, 8); size = varint_s64_encode_buf(ctx, val, b->writecursor); b->writecursor += size; return; } /** * Writes a unsigned varInt to the buffer */ void bytebuffer_append_uvarint(const RTCTX *ctx, bytebuffer_t *b, const uint64_t val) { size_t size; bytebuffer_makeroom(ctx, b, 8); size = varint_u64_encode_buf(ctx, val, b->writecursor); b->writecursor += size; return; } /* * Writes Integer to the buffer */ void bytebuffer_append_int(const RTCTX *ctx, bytebuffer_t *buf, const int val, int swap) { RTDEBUGF(2,"Entered bytebuffer_append_int with value %d, swap = %d", val, swap); RTDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); char *iptr = (char*)(&val); int i = 0; if ( sizeof(int) != RTWKB_INT_SIZE ) { rterror(ctx, "Machine int size is not %d bytes!", RTWKB_INT_SIZE); } bytebuffer_makeroom(ctx, buf, RTWKB_INT_SIZE); /* Machine/request arch mismatch, so flip byte order */ if ( swap) { RTDEBUG(4,"Ok, let's do the swaping thing"); for ( i = 0; i < RTWKB_INT_SIZE; i++ ) { *(buf->writecursor) = iptr[RTWKB_INT_SIZE - 1 - i]; buf->writecursor += 1; } } /* If machine arch and requested arch match, don't flip byte order */ else { RTDEBUG(4,"Ok, let's do the memcopying thing"); memcpy(buf->writecursor, iptr, RTWKB_INT_SIZE); buf->writecursor += RTWKB_INT_SIZE; } RTDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); return; } /** * Writes a float64 to the buffer */ void bytebuffer_append_double(const RTCTX *ctx, bytebuffer_t *buf, const double val, int swap) { RTDEBUGF(2,"Entered bytebuffer_append_double with value %lf swap = %d", val, swap); RTDEBUGF(4,"buf_start = %p and write_cursor=%p", buf->buf_start,buf->writecursor); char *dptr = (char*)(&val); int i = 0; if ( sizeof(double) != RTWKB_DOUBLE_SIZE ) { rterror(ctx, "Machine double size is not %d bytes!", RTWKB_DOUBLE_SIZE); } bytebuffer_makeroom(ctx, buf, RTWKB_DOUBLE_SIZE); /* Machine/request arch mismatch, so flip byte order */ if ( swap ) { RTDEBUG(4,"Ok, let's do the swapping thing"); for ( i = 0; i < RTWKB_DOUBLE_SIZE; i++ ) { *(buf->writecursor) = dptr[RTWKB_DOUBLE_SIZE - 1 - i]; buf->writecursor += 1; } } /* If machine arch and requested arch match, don't flip byte order */ else { RTDEBUG(4,"Ok, let's do the memcopying thing"); memcpy(buf->writecursor, dptr, RTWKB_DOUBLE_SIZE); buf->writecursor += RTWKB_DOUBLE_SIZE; } RTDEBUG(4,"Return from bytebuffer_append_double"); return; } /** * Reads a signed varInt from the buffer */ int64_t bytebuffer_read_varint(const RTCTX *ctx, bytebuffer_t *b) { size_t size; int64_t val = varint_s64_decode(ctx, b->readcursor, b->buf_start + b->capacity, &size); b->readcursor += size; return val; } /** * Reads a unsigned varInt from the buffer */ uint64_t bytebuffer_read_uvarint(const RTCTX *ctx, bytebuffer_t *b) { size_t size; uint64_t val = varint_u64_decode(ctx, b->readcursor, b->buf_start + b->capacity, &size); b->readcursor += size; return val; } /** * Returns the length of the current buffer */ size_t bytebuffer_getlength(const RTCTX *ctx, bytebuffer_t *s) { return (size_t) (s->writecursor - s->buf_start); } /** * Returns a new bytebuffer were both ingoing bytebuffers is merged. * Caller is responsible for freeing both incoming bytefyffers and resulting bytebuffer */ bytebuffer_t* bytebuffer_merge(const RTCTX *ctx, bytebuffer_t **buff_array, int nbuffers) { size_t total_size = 0, current_size, acc_size = 0; int i; for ( i = 0; i < nbuffers; i++ ) { total_size += bytebuffer_getlength(ctx, buff_array[i]); } bytebuffer_t *res = bytebuffer_create_with_size(ctx, total_size); for ( i = 0; i < nbuffers; i++) { current_size = bytebuffer_getlength(ctx, buff_array[i]); memcpy(res->buf_start+acc_size, buff_array[i]->buf_start, current_size); acc_size += current_size; } res->writecursor = res->buf_start + total_size; res->readcursor = res->buf_start; return res; } src/bytebuffer.h000066400000000000000000000051311271715413500141610ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2015 Nicklas Avén * **********************************************************************/ #ifndef _BYTEBUFFER_H #define _BYTEBUFFER_H 1 #include #include #include #include "varint.h" #include "rtgeom_log.h" #define BYTEBUFFER_STARTSIZE 128 typedef struct { size_t capacity; uint8_t *buf_start; uint8_t *writecursor; uint8_t *readcursor; } bytebuffer_t; void bytebuffer_init_with_size(const RTCTX *ctx, bytebuffer_t *b, size_t size); bytebuffer_t *bytebuffer_create_with_size(const RTCTX *ctx, size_t size); bytebuffer_t *bytebuffer_create(const RTCTX *ctx); void bytebuffer_destroy(const RTCTX *ctx, bytebuffer_t *s); void bytebuffer_clear(const RTCTX *ctx, bytebuffer_t *s); void bytebuffer_append_byte(const RTCTX *ctx, bytebuffer_t *s, const uint8_t val); void bytebuffer_append_varint(const RTCTX *ctx, bytebuffer_t *s, const int64_t val); void bytebuffer_append_uvarint(const RTCTX *ctx, bytebuffer_t *s, const uint64_t val); uint64_t bytebuffer_read_uvarint(const RTCTX *ctx, bytebuffer_t *s); int64_t bytebuffer_read_varint(const RTCTX *ctx, bytebuffer_t *s); size_t bytebuffer_getlength(const RTCTX *ctx, bytebuffer_t *s); bytebuffer_t* bytebuffer_merge(const RTCTX *ctx, bytebuffer_t **buff_array, int nbuffers); void bytebuffer_reset_reading(const RTCTX *ctx, bytebuffer_t *s); void bytebuffer_append_bytebuffer(const RTCTX *ctx, bytebuffer_t *write_to,bytebuffer_t *write_from); void bytebuffer_append_bulk(const RTCTX *ctx, bytebuffer_t *s, void * start, size_t size); void bytebuffer_append_int(const RTCTX *ctx, bytebuffer_t *buf, const int val, int swap); void bytebuffer_append_double(const RTCTX *ctx, bytebuffer_t *buf, const double val, int swap); #endif /* _BYTEBUFFER_H */ src/g_box.c000066400000000000000000000466711271715413500131330ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #if !HAVE_ISFINITE #endif #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include #include RTGBOX* gbox_new(const RTCTX *ctx, uint8_t flags) { RTGBOX *g = (RTGBOX*)rtalloc(ctx, sizeof(RTGBOX)); gbox_init(ctx, g); g->flags = flags; return g; } void gbox_init(const RTCTX *ctx, RTGBOX *gbox) { memset(gbox, 0, sizeof(RTGBOX)); } RTGBOX* gbox_clone(const RTCTX *ctx, const RTGBOX *gbox) { RTGBOX *g = rtalloc(ctx, sizeof(RTGBOX)); memcpy(g, gbox, sizeof(RTGBOX)); return g; } /* TODO to be removed */ BOX3D* box3d_from_gbox(const RTCTX *ctx, const RTGBOX *gbox) { BOX3D *b; assert(gbox); b = rtalloc(ctx, sizeof(BOX3D)); b->xmin = gbox->xmin; b->xmax = gbox->xmax; b->ymin = gbox->ymin; b->ymax = gbox->ymax; if ( RTFLAGS_GET_Z(gbox->flags) ) { b->zmin = gbox->zmin; b->zmax = gbox->zmax; } else { b->zmin = b->zmax = 0.0; } b->srid = SRID_UNKNOWN; return b; } /* TODO to be removed */ RTGBOX* box3d_to_gbox(const RTCTX *ctx, const BOX3D *b3d) { RTGBOX *b; assert(b3d); b = rtalloc(ctx, sizeof(RTGBOX)); b->xmin = b3d->xmin; b->xmax = b3d->xmax; b->ymin = b3d->ymin; b->ymax = b3d->ymax; b->zmin = b3d->zmin; b->zmax = b3d->zmax; return b; } void gbox_expand(const RTCTX *ctx, RTGBOX *g, double d) { g->xmin -= d; g->xmax += d; g->ymin -= d; g->ymax += d; if ( RTFLAGS_GET_Z(g->flags) ) { g->zmin -= d; g->zmax += d; } if ( RTFLAGS_GET_M(g->flags) ) { g->mmin -= d; g->mmax += d; } } int gbox_union(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2, RTGBOX *gout) { if ( ( ! g1 ) && ( ! g2 ) ) return RT_FALSE; if ( ! g1 ) { memcpy(gout, g2, sizeof(RTGBOX)); return RT_TRUE; } if ( ! g2 ) { memcpy(gout, g1, sizeof(RTGBOX)); return RT_TRUE; } gout->flags = g1->flags; gout->xmin = FP_MIN(g1->xmin, g2->xmin); gout->xmax = FP_MAX(g1->xmax, g2->xmax); gout->ymin = FP_MIN(g1->ymin, g2->ymin); gout->ymax = FP_MAX(g1->ymax, g2->ymax); gout->zmin = FP_MIN(g1->zmin, g2->zmin); gout->zmax = FP_MAX(g1->zmax, g2->zmax); return RT_TRUE; } int gbox_same(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { if (RTFLAGS_GET_ZM(g1->flags) != RTFLAGS_GET_ZM(g2->flags)) return RT_FALSE; if (!gbox_same_2d(ctx, g1, g2)) return RT_FALSE; if (RTFLAGS_GET_Z(g1->flags) && (g1->zmin != g2->zmin || g1->zmax != g2->zmax)) return RT_FALSE; if (RTFLAGS_GET_M(g1->flags) && (g1->mmin != g2->mmin || g1->mmax != g2->mmax)) return RT_FALSE; return RT_TRUE; } int gbox_same_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { if (g1->xmin == g2->xmin && g1->ymin == g2->ymin && g1->xmax == g2->xmax && g1->ymax == g2->ymax) return RT_TRUE; return RT_FALSE; } int gbox_same_2d_float(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { if ((g1->xmax == g2->xmax || next_float_up(ctx, g1->xmax) == next_float_up(ctx, g2->xmax)) && (g1->ymax == g2->ymax || next_float_up(ctx, g1->ymax) == next_float_up(ctx, g2->ymax)) && (g1->xmin == g2->xmin || next_float_down(ctx, g1->xmin) == next_float_down(ctx, g1->xmin)) && (g1->ymin == g2->ymin || next_float_down(ctx, g2->ymin) == next_float_down(ctx, g2->ymin))) return RT_TRUE; return RT_FALSE; } int gbox_is_valid(const RTCTX *ctx, const RTGBOX *gbox) { /* X */ if ( ! isfinite(gbox->xmin) || isnan(gbox->xmin) || ! isfinite(gbox->xmax) || isnan(gbox->xmax) ) return RT_FALSE; /* Y */ if ( ! isfinite(gbox->ymin) || isnan(gbox->ymin) || ! isfinite(gbox->ymax) || isnan(gbox->ymax) ) return RT_FALSE; /* Z */ if ( RTFLAGS_GET_GEODETIC(gbox->flags) || RTFLAGS_GET_Z(gbox->flags) ) { if ( ! isfinite(gbox->zmin) || isnan(gbox->zmin) || ! isfinite(gbox->zmax) || isnan(gbox->zmax) ) return RT_FALSE; } /* M */ if ( RTFLAGS_GET_M(gbox->flags) ) { if ( ! isfinite(gbox->mmin) || isnan(gbox->mmin) || ! isfinite(gbox->mmax) || isnan(gbox->mmax) ) return RT_FALSE; } return RT_TRUE; } int gbox_merge_point3d(const RTCTX *ctx, const POINT3D *p, RTGBOX *gbox) { if ( gbox->xmin > p->x ) gbox->xmin = p->x; if ( gbox->ymin > p->y ) gbox->ymin = p->y; if ( gbox->zmin > p->z ) gbox->zmin = p->z; if ( gbox->xmax < p->x ) gbox->xmax = p->x; if ( gbox->ymax < p->y ) gbox->ymax = p->y; if ( gbox->zmax < p->z ) gbox->zmax = p->z; return RT_SUCCESS; } int gbox_init_point3d(const RTCTX *ctx, const POINT3D *p, RTGBOX *gbox) { gbox->xmin = gbox->xmax = p->x; gbox->ymin = gbox->ymax = p->y; gbox->zmin = gbox->zmax = p->z; return RT_SUCCESS; } int gbox_contains_point3d(const RTCTX *ctx, const RTGBOX *gbox, const POINT3D *pt) { if ( gbox->xmin > pt->x || gbox->ymin > pt->y || gbox->zmin > pt->z || gbox->xmax < pt->x || gbox->ymax < pt->y || gbox->zmax < pt->z ) { return RT_FALSE; } return RT_TRUE; } int gbox_merge(const RTCTX *ctx, const RTGBOX *new_box, RTGBOX *merge_box) { assert(merge_box); if ( RTFLAGS_GET_ZM(merge_box->flags) != RTFLAGS_GET_ZM(new_box->flags) ) return RT_FAILURE; if ( new_box->xmin < merge_box->xmin) merge_box->xmin = new_box->xmin; if ( new_box->ymin < merge_box->ymin) merge_box->ymin = new_box->ymin; if ( new_box->xmax > merge_box->xmax) merge_box->xmax = new_box->xmax; if ( new_box->ymax > merge_box->ymax) merge_box->ymax = new_box->ymax; if ( RTFLAGS_GET_Z(merge_box->flags) || RTFLAGS_GET_GEODETIC(merge_box->flags) ) { if ( new_box->zmin < merge_box->zmin) merge_box->zmin = new_box->zmin; if ( new_box->zmax > merge_box->zmax) merge_box->zmax = new_box->zmax; } if ( RTFLAGS_GET_M(merge_box->flags) ) { if ( new_box->mmin < merge_box->mmin) merge_box->mmin = new_box->mmin; if ( new_box->mmax > merge_box->mmax) merge_box->mmax = new_box->mmax; } return RT_SUCCESS; } int gbox_overlaps(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { /* Make sure our boxes are consistent */ if ( RTFLAGS_GET_GEODETIC(g1->flags) != RTFLAGS_GET_GEODETIC(g2->flags) ) rterror(ctx, "gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); /* Check X/Y first */ if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || g1->xmin > g2->xmax || g1->ymin > g2->ymax ) return RT_FALSE; /* Deal with the geodetic case special: we only compare the geodetic boxes (x/y/z) */ /* Never the M dimension */ if ( RTFLAGS_GET_GEODETIC(g1->flags) && RTFLAGS_GET_GEODETIC(g2->flags) ) { if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) return RT_FALSE; else return RT_TRUE; } /* If both geodetic or both have Z, check Z */ if ( RTFLAGS_GET_Z(g1->flags) && RTFLAGS_GET_Z(g2->flags) ) { if ( g1->zmax < g2->zmin || g1->zmin > g2->zmax ) return RT_FALSE; } /* If both have M, check M */ if ( RTFLAGS_GET_M(g1->flags) && RTFLAGS_GET_M(g2->flags) ) { if ( g1->mmax < g2->mmin || g1->mmin > g2->mmax ) return RT_FALSE; } return RT_TRUE; } int gbox_overlaps_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { /* Make sure our boxes are consistent */ if ( RTFLAGS_GET_GEODETIC(g1->flags) != RTFLAGS_GET_GEODETIC(g2->flags) ) rterror(ctx, "gbox_overlaps: cannot compare geodetic and non-geodetic boxes"); /* Check X/Y first */ if ( g1->xmax < g2->xmin || g1->ymax < g2->ymin || g1->xmin > g2->xmax || g1->ymin > g2->ymax ) return RT_FALSE; return RT_TRUE; } int gbox_contains_2d(const RTCTX *ctx, const RTGBOX *g1, const RTGBOX *g2) { if ( ( g2->xmin < g1->xmin ) || ( g2->xmax > g1->xmax ) || ( g2->ymin < g1->ymin ) || ( g2->ymax > g1->ymax ) ) { return RT_FALSE; } return RT_TRUE; } int gbox_contains_point2d(const RTCTX *ctx, const RTGBOX *g, const RTPOINT2D *p) { if ( ( g->xmin <= p->x ) && ( g->xmax >= p->x ) && ( g->ymin <= p->y ) && ( g->ymax >= p->y ) ) { return RT_TRUE; } return RT_FALSE; } /** * Warning, this function is only good for x/y/z boxes, used * in unit testing of geodetic box generation. */ RTGBOX* gbox_from_string(const RTCTX *ctx, const char *str) { const char *ptr = str; char *nextptr; char *gbox_start = strstr(str, "RTGBOX(("); RTGBOX *gbox = gbox_new(ctx, gflags(ctx, 0,0,1)); if ( ! gbox_start ) return NULL; /* No header found */ ptr += 6; gbox->xmin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->ymin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->zmin = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 3; gbox->xmax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->ymax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ ptr = nextptr + 1; gbox->zmax = strtod(ptr, &nextptr); if ( ptr == nextptr ) return NULL; /* No double found */ return gbox; } char* gbox_to_string(const RTCTX *ctx, const RTGBOX *gbox) { static int sz = 128; char *str = NULL; if ( ! gbox ) return strdup("NULL POINTER"); str = (char*)rtalloc(ctx, sz); if ( RTFLAGS_GET_GEODETIC(gbox->flags) ) { snprintf(str, sz, "RTGBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); return str; } if ( RTFLAGS_GET_Z(gbox->flags) && RTFLAGS_GET_M(gbox->flags) ) { snprintf(str, sz, "RTGBOX((%.8g,%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->zmax, gbox->mmax); return str; } if ( RTFLAGS_GET_Z(gbox->flags) ) { snprintf(str, sz, "RTGBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->zmin, gbox->xmax, gbox->ymax, gbox->zmax); return str; } if ( RTFLAGS_GET_M(gbox->flags) ) { snprintf(str, sz, "RTGBOX((%.8g,%.8g,%.8g),(%.8g,%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->mmin, gbox->xmax, gbox->ymax, gbox->mmax); return str; } snprintf(str, sz, "RTGBOX((%.8g,%.8g),(%.8g,%.8g))", gbox->xmin, gbox->ymin, gbox->xmax, gbox->ymax); return str; } RTGBOX* gbox_copy(const RTCTX *ctx, const RTGBOX *box) { RTGBOX *copy = (RTGBOX*)rtalloc(ctx, sizeof(RTGBOX)); memcpy(copy, box, sizeof(RTGBOX)); return copy; } void gbox_duplicate(const RTCTX *ctx, const RTGBOX *original, RTGBOX *duplicate) { assert(duplicate); memcpy(duplicate, original, sizeof(RTGBOX)); } size_t gbox_serialized_size(const RTCTX *ctx, uint8_t flags) { if ( RTFLAGS_GET_GEODETIC(flags) ) return 6 * sizeof(float); else return 2 * RTFLAGS_NDIMS(flags) * sizeof(float); } /* ******************************************************************************** ** Compute cartesian bounding RTGBOX boxes from RTGEOM. */ int rt_arc_calculate_gbox_cartesian_2d(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, RTGBOX *gbox) { RTPOINT2D xmin, ymin, xmax, ymax; RTPOINT2D C; int A2_side; double radius_A; RTDEBUG(2, "rt_arc_calculate_gbox_cartesian_2d called."); radius_A = rt_arc_center(ctx, A1, A2, A3, &C); /* Negative radius signals straight line, p1/p2/p3 are colinear */ if (radius_A < 0.0) { gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); return RT_SUCCESS; } /* Matched start/end points imply circle */ if ( A1->x == A3->x && A1->y == A3->y ) { gbox->xmin = C.x - radius_A; gbox->ymin = C.y - radius_A; gbox->xmax = C.x + radius_A; gbox->ymax = C.y + radius_A; return RT_SUCCESS; } /* First approximation, bounds of start/end points */ gbox->xmin = FP_MIN(A1->x, A3->x); gbox->ymin = FP_MIN(A1->y, A3->y); gbox->xmax = FP_MAX(A1->x, A3->x); gbox->ymax = FP_MAX(A1->y, A3->y); /* Create points for the possible extrema */ xmin.x = C.x - radius_A; xmin.y = C.y; ymin.x = C.x; ymin.y = C.y - radius_A; xmax.x = C.x + radius_A; xmax.y = C.y; ymax.x = C.x; ymax.y = C.y + radius_A; /* Divide the circle into two parts, one on each side of a line joining p1 and p3. The circle extrema on the same side of that line as p2 is on, are also the extrema of the bbox. */ A2_side = rt_segment_side(ctx, A1, A3, A2); if ( A2_side == rt_segment_side(ctx, A1, A3, &xmin) ) gbox->xmin = xmin.x; if ( A2_side == rt_segment_side(ctx, A1, A3, &ymin) ) gbox->ymin = ymin.y; if ( A2_side == rt_segment_side(ctx, A1, A3, &xmax) ) gbox->xmax = xmax.x; if ( A2_side == rt_segment_side(ctx, A1, A3, &ymax) ) gbox->ymax = ymax.y; return RT_SUCCESS; } static int rt_arc_calculate_gbox_cartesian(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2, const RTPOINT4D *p3, RTGBOX *gbox) { int rv; RTDEBUG(2, "rt_arc_calculate_gbox_cartesian called."); rv = rt_arc_calculate_gbox_cartesian_2d(ctx, (RTPOINT2D*)p1, (RTPOINT2D*)p2, (RTPOINT2D*)p3, gbox); gbox->zmin = FP_MIN(p1->z, p3->z); gbox->mmin = FP_MIN(p1->m, p3->m); gbox->zmax = FP_MAX(p1->z, p3->z); gbox->mmax = FP_MAX(p1->m, p3->m); return rv; } int ptarray_calculate_gbox_cartesian(const RTCTX *ctx, const RTPOINTARRAY *pa, RTGBOX *gbox ) { int i; RTPOINT4D p; int has_z, has_m; if ( ! pa ) return RT_FAILURE; if ( ! gbox ) return RT_FAILURE; if ( pa->npoints < 1 ) return RT_FAILURE; has_z = RTFLAGS_GET_Z(pa->flags); has_m = RTFLAGS_GET_M(pa->flags); gbox->flags = gflags(ctx, has_z, has_m, 0); RTDEBUGF(4, "ptarray_calculate_gbox Z: %d M: %d", has_z, has_m); rt_getPoint4d_p(ctx, pa, 0, &p); gbox->xmin = gbox->xmax = p.x; gbox->ymin = gbox->ymax = p.y; if ( has_z ) gbox->zmin = gbox->zmax = p.z; if ( has_m ) gbox->mmin = gbox->mmax = p.m; for ( i = 1 ; i < pa->npoints; i++ ) { rt_getPoint4d_p(ctx, pa, i, &p); gbox->xmin = FP_MIN(gbox->xmin, p.x); gbox->xmax = FP_MAX(gbox->xmax, p.x); gbox->ymin = FP_MIN(gbox->ymin, p.y); gbox->ymax = FP_MAX(gbox->ymax, p.y); if ( has_z ) { gbox->zmin = FP_MIN(gbox->zmin, p.z); gbox->zmax = FP_MAX(gbox->zmax, p.z); } if ( has_m ) { gbox->mmin = FP_MIN(gbox->mmin, p.m); gbox->mmax = FP_MAX(gbox->mmax, p.m); } } return RT_SUCCESS; } static int rtcircstring_calculate_gbox_cartesian(const RTCTX *ctx, RTCIRCSTRING *curve, RTGBOX *gbox) { uint8_t flags = gflags(ctx, RTFLAGS_GET_Z(curve->flags), RTFLAGS_GET_M(curve->flags), 0); RTGBOX tmp; RTPOINT4D p1, p2, p3; int i; if ( ! curve ) return RT_FAILURE; if ( curve->points->npoints < 3 ) return RT_FAILURE; tmp.flags = flags; /* Initialize */ gbox->xmin = gbox->ymin = gbox->zmin = gbox->mmin = FLT_MAX; gbox->xmax = gbox->ymax = gbox->zmax = gbox->mmax = -1*FLT_MAX; for ( i = 2; i < curve->points->npoints; i += 2 ) { rt_getPoint4d_p(ctx, curve->points, i-2, &p1); rt_getPoint4d_p(ctx, curve->points, i-1, &p2); rt_getPoint4d_p(ctx, curve->points, i, &p3); if (rt_arc_calculate_gbox_cartesian(ctx, &p1, &p2, &p3, &tmp) == RT_FAILURE) continue; gbox_merge(ctx, &tmp, gbox); } return RT_SUCCESS; } static int rtpoint_calculate_gbox_cartesian(const RTCTX *ctx, RTPOINT *point, RTGBOX *gbox) { if ( ! point ) return RT_FAILURE; return ptarray_calculate_gbox_cartesian(ctx, point->point, gbox ); } static int rtline_calculate_gbox_cartesian(const RTCTX *ctx, RTLINE *line, RTGBOX *gbox) { if ( ! line ) return RT_FAILURE; return ptarray_calculate_gbox_cartesian(ctx, line->points, gbox ); } static int rttriangle_calculate_gbox_cartesian(const RTCTX *ctx, RTTRIANGLE *triangle, RTGBOX *gbox) { if ( ! triangle ) return RT_FAILURE; return ptarray_calculate_gbox_cartesian(ctx, triangle->points, gbox ); } static int rtpoly_calculate_gbox_cartesian(const RTCTX *ctx, RTPOLY *poly, RTGBOX *gbox) { if ( ! poly ) return RT_FAILURE; if ( poly->nrings == 0 ) return RT_FAILURE; /* Just need to check outer ring */ return ptarray_calculate_gbox_cartesian(ctx, poly->rings[0], gbox ); } static int rtcollection_calculate_gbox_cartesian(const RTCTX *ctx, RTCOLLECTION *coll, RTGBOX *gbox) { RTGBOX subbox; int i; int result = RT_FAILURE; int first = RT_TRUE; assert(coll); if ( (coll->ngeoms == 0) || !gbox) return RT_FAILURE; subbox.flags = coll->flags; for ( i = 0; i < coll->ngeoms; i++ ) { if ( rtgeom_calculate_gbox_cartesian(ctx, (RTGEOM*)(coll->geoms[i]), &subbox) == RT_SUCCESS ) { /* Keep a copy of the sub-bounding box for later if ( coll->geoms[i]->bbox ) rtfree(ctx, coll->geoms[i]->bbox); coll->geoms[i]->bbox = gbox_copy(ctx, &subbox); */ if ( first ) { gbox_duplicate(ctx, &subbox, gbox); first = RT_FALSE; } else { gbox_merge(ctx, &subbox, gbox); } result = RT_SUCCESS; } } return result; } int rtgeom_calculate_gbox_cartesian(const RTCTX *ctx, const RTGEOM *rtgeom, RTGBOX *gbox) { if ( ! rtgeom ) return RT_FAILURE; RTDEBUGF(4, "rtgeom_calculate_gbox got type (%d) - %s", rtgeom->type, rttype_name(ctx, rtgeom->type)); switch (rtgeom->type) { case RTPOINTTYPE: return rtpoint_calculate_gbox_cartesian(ctx, (RTPOINT *)rtgeom, gbox); case RTLINETYPE: return rtline_calculate_gbox_cartesian(ctx, (RTLINE *)rtgeom, gbox); case RTCIRCSTRINGTYPE: return rtcircstring_calculate_gbox_cartesian(ctx, (RTCIRCSTRING *)rtgeom, gbox); case RTPOLYGONTYPE: return rtpoly_calculate_gbox_cartesian(ctx, (RTPOLY *)rtgeom, gbox); case RTTRIANGLETYPE: return rttriangle_calculate_gbox_cartesian(ctx, (RTTRIANGLE *)rtgeom, gbox); case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTICURVETYPE: case RTMULTIPOLYGONTYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return rtcollection_calculate_gbox_cartesian(ctx, (RTCOLLECTION *)rtgeom, gbox); } /* Never get here, please. */ rterror(ctx, "unsupported type (%d) - %s", rtgeom->type, rttype_name(ctx, rtgeom->type)); return RT_FAILURE; } void gbox_float_round(const RTCTX *ctx, RTGBOX *gbox) { gbox->xmin = next_float_down(ctx, gbox->xmin); gbox->xmax = next_float_up(ctx, gbox->xmax); gbox->ymin = next_float_down(ctx, gbox->ymin); gbox->ymax = next_float_up(ctx, gbox->ymax); if ( RTFLAGS_GET_M(gbox->flags) ) { gbox->mmin = next_float_down(ctx, gbox->mmin); gbox->mmax = next_float_up(ctx, gbox->mmax); } if ( RTFLAGS_GET_Z(gbox->flags) ) { gbox->zmin = next_float_down(ctx, gbox->zmin); gbox->zmax = next_float_up(ctx, gbox->zmax); } } src/g_serialized.c000066400000000000000000001060171271715413500144650ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /*********************************************************************** * GSERIALIZED metadata utility functions. */ int gserialized_has_bbox(const RTCTX *ctx, const GSERIALIZED *gser) { return RTFLAGS_GET_BBOX(gser->flags); } int gserialized_has_z(const RTCTX *ctx, const GSERIALIZED *gser) { return RTFLAGS_GET_Z(gser->flags); } int gserialized_has_m(const RTCTX *ctx, const GSERIALIZED *gser) { return RTFLAGS_GET_M(gser->flags); } int gserialized_get_zm(const RTCTX *ctx, const GSERIALIZED *gser) { return 2 * RTFLAGS_GET_Z(gser->flags) + RTFLAGS_GET_M(gser->flags); } int gserialized_ndims(const RTCTX *ctx, const GSERIALIZED *gser) { return RTFLAGS_NDIMS(gser->flags); } int gserialized_is_geodetic(const RTCTX *ctx, const GSERIALIZED *gser) { return RTFLAGS_GET_GEODETIC(gser->flags); } uint32_t gserialized_max_header_size(const RTCTX *ctx) { /* read GSERIALIZED size + max bbox according gbox_serialized_size(ctx, 2 + Z + M) + 1 int for type */ return sizeof(GSERIALIZED) + 8 * sizeof(float) + sizeof(int); } uint32_t gserialized_get_type(const RTCTX *ctx, const GSERIALIZED *s) { uint32_t *ptr; assert(s); ptr = (uint32_t*)(s->data); RTDEBUG(4,"entered"); if ( RTFLAGS_GET_BBOX(s->flags) ) { RTDEBUGF(4,"skipping forward past bbox (%d bytes)",gbox_serialized_size(ctx, s->flags)); ptr += (gbox_serialized_size(ctx, s->flags) / sizeof(uint32_t)); } return *ptr; } int32_t gserialized_get_srid(const RTCTX *ctx, const GSERIALIZED *s) { int32_t srid = 0; srid = srid | (s->srid[0] << 16); srid = srid | (s->srid[1] << 8); srid = srid | s->srid[2]; /* Only the first 21 bits are set. Slide up and back to pull the negative bits down, if we need them. */ srid = (srid<<11)>>11; /* 0 is our internal unknown value. We'll map back and forth here for now */ if ( srid == 0 ) return SRID_UNKNOWN; else return clamp_srid(ctx, srid); } void gserialized_set_srid(const RTCTX *ctx, GSERIALIZED *s, int32_t srid) { RTDEBUGF(3, "Called with srid = %d", srid); srid = clamp_srid(ctx, srid); /* 0 is our internal unknown value. * We'll map back and forth here for now */ if ( srid == SRID_UNKNOWN ) srid = 0; s->srid[0] = (srid & 0x001F0000) >> 16; s->srid[1] = (srid & 0x0000FF00) >> 8; s->srid[2] = (srid & 0x000000FF); } GSERIALIZED* gserialized_copy(const RTCTX *ctx, const GSERIALIZED *g) { GSERIALIZED *g_out = NULL; assert(g); g_out = (GSERIALIZED*)rtalloc(ctx, SIZE_GET(g->size)); memcpy((uint8_t*)g_out,(uint8_t*)g,SIZE_GET(g->size)); return g_out; } static size_t gserialized_is_empty_recurse(const RTCTX *ctx, const uint8_t *p, int *isempty); static size_t gserialized_is_empty_recurse(const RTCTX *ctx, const uint8_t *p, int *isempty) { int i; int32_t type, num; memcpy(&type, p, 4); memcpy(&num, p+4, 4); if ( rttype_is_collection(ctx, type) ) { size_t lz = 8; for ( i = 0; i < num; i++ ) { lz += gserialized_is_empty_recurse(ctx, p+lz, isempty); if ( ! *isempty ) return lz; } *isempty = RT_TRUE; return lz; } else { *isempty = (num == 0 ? RT_TRUE : RT_FALSE); return 8; } } int gserialized_is_empty(const RTCTX *ctx, const GSERIALIZED *g) { uint8_t *p = (uint8_t*)g; int isempty = 0; assert(g); p += 8; /* Skip varhdr and srid/flags */ if( RTFLAGS_GET_BBOX(g->flags) ) p += gbox_serialized_size(ctx, g->flags); /* Skip the box */ gserialized_is_empty_recurse(ctx, p, &isempty); return isempty; } char* gserialized_to_string(const RTCTX *ctx, const GSERIALIZED *g) { return rtgeom_to_wkt(ctx, rtgeom_from_gserialized(ctx, g), RTWKT_ISO, 12, 0); } int gserialized_read_gbox_p(const RTCTX *ctx, const GSERIALIZED *g, RTGBOX *gbox) { /* Null input! */ if ( ! ( g && gbox ) ) return RT_FAILURE; /* Initialize the flags on the box */ gbox->flags = g->flags; /* Has pre-calculated box */ if ( RTFLAGS_GET_BBOX(g->flags) ) { int i = 0; float *fbox = (float*)(g->data); gbox->xmin = fbox[i++]; gbox->xmax = fbox[i++]; gbox->ymin = fbox[i++]; gbox->ymax = fbox[i++]; /* Geodetic? Read next dimension (geocentric Z) and return */ if ( RTFLAGS_GET_GEODETIC(g->flags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; return RT_SUCCESS; } /* Cartesian? Read extra dimensions (if there) and return */ if ( RTFLAGS_GET_Z(g->flags) ) { gbox->zmin = fbox[i++]; gbox->zmax = fbox[i++]; } if ( RTFLAGS_GET_M(g->flags) ) { gbox->mmin = fbox[i++]; gbox->mmax = fbox[i++]; } return RT_SUCCESS; } return RT_FAILURE; } /* * Populate a bounding box *without* allocating an RTGEOM. Useful * for some performance purposes. */ static int gserialized_peek_gbox_p(const RTCTX *ctx, const GSERIALIZED *g, RTGBOX *gbox) { uint32_t type = gserialized_get_type(ctx, g); /* Peeking doesn't help if you already have a box or are geodetic */ if ( RTFLAGS_GET_GEODETIC(g->flags) || RTFLAGS_GET_BBOX(g->flags) ) { return RT_FAILURE; } /* Boxes of points are easy peasy */ if ( type == RTPOINTTYPE ) { int i = 1; /* Start past */ double *dptr = (double*)(g->data); /* Read the empty flag */ int *iptr = (int*)(g->data); int isempty = (iptr[1] == 0); /* EMPTY point has no box */ if ( isempty ) return RT_FAILURE; gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; if ( RTFLAGS_GET_Z(g->flags) ) { gbox->zmin = gbox->zmax = dptr[i++]; } if ( RTFLAGS_GET_M(g->flags) ) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(ctx, gbox); return RT_SUCCESS; } /* We can calculate the box of a two-point cartesian line trivially */ else if ( type == RTLINETYPE ) { int ndims = RTFLAGS_NDIMS(g->flags); int i = 0; /* Start at */ double *dptr = (double*)(g->data); int *iptr = (int*)(g->data); int npoints = iptr[1]; /* Read the npoints */ /* This only works with 2-point lines */ if ( npoints != 2 ) return RT_FAILURE; /* Advance to X */ /* Past */ i++; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); if ( RTFLAGS_GET_Z(g->flags) ) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if ( RTFLAGS_GET_M(g->flags) ) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(ctx, gbox); return RT_SUCCESS; } /* We can also do single-entry multi-points */ else if ( type == RTMULTIPOINTTYPE ) { int i = 0; /* Start at */ double *dptr = (double*)(g->data); int *iptr = (int*)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ /* This only works with single-entry multipoints */ if ( ngeoms != 1 ) return RT_FAILURE; /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; /* Read the doubles from the one point */ gbox->xmin = gbox->xmax = dptr[i++]; gbox->ymin = gbox->ymax = dptr[i++]; if ( RTFLAGS_GET_Z(g->flags) ) { gbox->zmin = gbox->zmax = dptr[i++]; } if ( RTFLAGS_GET_M(g->flags) ) { gbox->mmin = gbox->mmax = dptr[i++]; } gbox_float_round(ctx, gbox); return RT_SUCCESS; } /* And we can do single-entry multi-lines with two vertices (!!!) */ else if ( type == RTMULTILINETYPE ) { int ndims = RTFLAGS_NDIMS(g->flags); int i = 0; /* Start at */ double *dptr = (double*)(g->data); int *iptr = (int*)(g->data); int ngeoms = iptr[1]; /* Read the ngeoms */ int npoints; /* This only works with 1-line multilines */ if ( ngeoms != 1 ) return RT_FAILURE; /* Npoints is at */ npoints = iptr[3]; if ( npoints != 2 ) return RT_FAILURE; /* Advance to X */ /* Move forward two doubles (four ints) */ /* Past */ /* Past */ i += 2; gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]); /* Advance to Y */ i++; gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]); if ( RTFLAGS_GET_Z(g->flags) ) { /* Advance to Z */ i++; gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]); } if ( RTFLAGS_GET_M(g->flags) ) { /* Advance to M */ i++; gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]); gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]); } gbox_float_round(ctx, gbox); return RT_SUCCESS; } return RT_FAILURE; } /** * Read the bounding box off a serialization and calculate one if * it is not already there. */ int gserialized_get_gbox_p(const RTCTX *ctx, const GSERIALIZED *g, RTGBOX *box) { /* Try to just read the serialized box. */ if ( gserialized_read_gbox_p(ctx, g, box) == RT_SUCCESS ) { return RT_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an rtgeom */ else if ( gserialized_peek_gbox_p(ctx, g, box) == RT_SUCCESS ) { return RT_SUCCESS; } /* Damn! Nothing for it but to create an rtgeom... */ /* See http://trac.osgeo.org/postgis/ticket/1023 */ else { RTGEOM *rtgeom = rtgeom_from_gserialized(ctx, g); int ret = rtgeom_calculate_gbox(ctx, rtgeom, box); gbox_float_round(ctx, box); rtgeom_free(ctx, rtgeom); return ret; } } /*********************************************************************** * Calculate the GSERIALIZED size for an RTGEOM. */ /* Private functions */ static size_t gserialized_from_any_size(const RTCTX *ctx, const RTGEOM *geom); /* Local prototype */ static size_t gserialized_from_rtpoint_size(const RTCTX *ctx, const RTPOINT *point) { size_t size = 4; /* Type number. */ assert(point); size += 4; /* Number of points (one or zero (empty)). */ size += point->point->npoints * RTFLAGS_NDIMS(point->flags) * sizeof(double); RTDEBUGF(3, "point size = %d", size); return size; } static size_t gserialized_from_rtline_size(const RTCTX *ctx, const RTLINE *line) { size_t size = 4; /* Type number. */ assert(line); size += 4; /* Number of points (zero => empty). */ size += line->points->npoints * RTFLAGS_NDIMS(line->flags) * sizeof(double); RTDEBUGF(3, "linestring size = %d", size); return size; } static size_t gserialized_from_rttriangle_size(const RTCTX *ctx, const RTTRIANGLE *triangle) { size_t size = 4; /* Type number. */ assert(triangle); size += 4; /* Number of points (zero => empty). */ size += triangle->points->npoints * RTFLAGS_NDIMS(triangle->flags) * sizeof(double); RTDEBUGF(3, "triangle size = %d", size); return size; } static size_t gserialized_from_rtpoly_size(const RTCTX *ctx, const RTPOLY *poly) { size_t size = 4; /* Type number. */ int i = 0; assert(poly); size += 4; /* Number of rings (zero => empty). */ if ( poly->nrings % 2 ) size += 4; /* Padding to double alignment. */ for ( i = 0; i < poly->nrings; i++ ) { size += 4; /* Number of points in ring. */ size += poly->rings[i]->npoints * RTFLAGS_NDIMS(poly->flags) * sizeof(double); } RTDEBUGF(3, "polygon size = %d", size); return size; } static size_t gserialized_from_rtcircstring_size(const RTCTX *ctx, const RTCIRCSTRING *curve) { size_t size = 4; /* Type number. */ assert(curve); size += 4; /* Number of points (zero => empty). */ size += curve->points->npoints * RTFLAGS_NDIMS(curve->flags) * sizeof(double); RTDEBUGF(3, "circstring size = %d", size); return size; } static size_t gserialized_from_rtcollection_size(const RTCTX *ctx, const RTCOLLECTION *col) { size_t size = 4; /* Type number. */ int i = 0; assert(col); size += 4; /* Number of sub-geometries (zero => empty). */ for ( i = 0; i < col->ngeoms; i++ ) { size_t subsize = gserialized_from_any_size(ctx, col->geoms[i]); size += subsize; RTDEBUGF(3, "rtcollection subgeom(%d) size = %d", i, subsize); } RTDEBUGF(3, "rtcollection size = %d", size); return size; } static size_t gserialized_from_any_size(const RTCTX *ctx, const RTGEOM *geom) { RTDEBUGF(2, "Input type: %s", rttype_name(ctx, geom->type)); switch (geom->type) { case RTPOINTTYPE: return gserialized_from_rtpoint_size(ctx, (RTPOINT *)geom); case RTLINETYPE: return gserialized_from_rtline_size(ctx, (RTLINE *)geom); case RTPOLYGONTYPE: return gserialized_from_rtpoly_size(ctx, (RTPOLY *)geom); case RTTRIANGLETYPE: return gserialized_from_rttriangle_size(ctx, (RTTRIANGLE *)geom); case RTCIRCSTRINGTYPE: return gserialized_from_rtcircstring_size(ctx, (RTCIRCSTRING *)geom); case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTICURVETYPE: case RTMULTIPOLYGONTYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return gserialized_from_rtcollection_size(ctx, (RTCOLLECTION *)geom); default: rterror(ctx, "Unknown geometry type: %d - %s", geom->type, rttype_name(ctx, geom->type)); return 0; } } /* Public function */ size_t gserialized_from_rtgeom_size(const RTCTX *ctx, const RTGEOM *geom) { size_t size = 8; /* Header overhead. */ assert(geom); if ( geom->bbox ) size += gbox_serialized_size(ctx, geom->flags); size += gserialized_from_any_size(ctx, geom); RTDEBUGF(3, "g_serialize size = %d", size); return size; } /*********************************************************************** * Serialize an RTGEOM into GSERIALIZED. */ /* Private functions */ static size_t gserialized_from_rtgeom_any(const RTCTX *ctx, const RTGEOM *geom, uint8_t *buf); static size_t gserialized_from_rtpoint(const RTCTX *ctx, const RTPOINT *point, uint8_t *buf) { uint8_t *loc; int ptsize = ptarray_point_size(ctx, point->point); int type = RTPOINTTYPE; assert(point); assert(buf); if ( RTFLAGS_GET_ZM(point->flags) != RTFLAGS_GET_ZM(point->point->flags) ) rterror(ctx, "Dimensions mismatch in rtpoint"); RTDEBUGF(2, "rtpoint_to_gserialized(%p, %p) called", point, buf); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of points (0 => empty). */ memcpy(loc, &(point->point->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if ( point->point->npoints > 0 ) { memcpy(loc, rt_getPoint_internal(ctx, point->point, 0), ptsize); loc += ptsize; } return (size_t)(loc - buf); } static size_t gserialized_from_rtline(const RTCTX *ctx, const RTLINE *line, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = RTLINETYPE; assert(line); assert(buf); RTDEBUGF(2, "rtline_to_gserialized(%p, %p) called", line, buf); if ( RTFLAGS_GET_Z(line->flags) != RTFLAGS_GET_Z(line->points->flags) ) rterror(ctx, "Dimensions mismatch in rtline"); ptsize = ptarray_point_size(ctx, line->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(line->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); RTDEBUGF(3, "rtline_to_gserialized added npoints (%d)", line->points->npoints); /* Copy in the ordinates. */ if ( line->points->npoints > 0 ) { size = line->points->npoints * ptsize; memcpy(loc, rt_getPoint_internal(ctx, line->points, 0), size); loc += size; } RTDEBUGF(3, "rtline_to_gserialized copied serialized_pointlist (%d bytes)", ptsize * line->points->npoints); return (size_t)(loc - buf); } static size_t gserialized_from_rtpoly(const RTCTX *ctx, const RTPOLY *poly, uint8_t *buf) { int i; uint8_t *loc; int ptsize; int type = RTPOLYGONTYPE; assert(poly); assert(buf); RTDEBUG(2, "rtpoly_to_gserialized called"); ptsize = sizeof(double) * RTFLAGS_NDIMS(poly->flags); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the nrings. */ memcpy(loc, &(poly->nrings), sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints per ring. */ for ( i = 0; i < poly->nrings; i++ ) { memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Add in padding if necessary to remain double aligned. */ if ( poly->nrings % 2 ) { memset(loc, 0, sizeof(uint32_t)); loc += sizeof(uint32_t); } /* Copy in the ordinates. */ for ( i = 0; i < poly->nrings; i++ ) { RTPOINTARRAY *pa = poly->rings[i]; size_t pasize; if ( RTFLAGS_GET_ZM(poly->flags) != RTFLAGS_GET_ZM(pa->flags) ) rterror(ctx, "Dimensions mismatch in rtpoly"); pasize = pa->npoints * ptsize; memcpy(loc, rt_getPoint_internal(ctx, pa, 0), pasize); loc += pasize; } return (size_t)(loc - buf); } static size_t gserialized_from_rttriangle(const RTCTX *ctx, const RTTRIANGLE *triangle, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = RTTRIANGLETYPE; assert(triangle); assert(buf); RTDEBUGF(2, "rttriangle_to_gserialized(%p, %p) called", triangle, buf); if ( RTFLAGS_GET_ZM(triangle->flags) != RTFLAGS_GET_ZM(triangle->points->flags) ) rterror(ctx, "Dimensions mismatch in rttriangle"); ptsize = ptarray_point_size(ctx, triangle->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t)); loc += sizeof(uint32_t); RTDEBUGF(3, "rttriangle_to_gserialized added npoints (%d)", triangle->points->npoints); /* Copy in the ordinates. */ if ( triangle->points->npoints > 0 ) { size = triangle->points->npoints * ptsize; memcpy(loc, rt_getPoint_internal(ctx, triangle->points, 0), size); loc += size; } RTDEBUGF(3, "rttriangle_to_gserialized copied serialized_pointlist (%d bytes)", ptsize * triangle->points->npoints); return (size_t)(loc - buf); } static size_t gserialized_from_rtcircstring(const RTCTX *ctx, const RTCIRCSTRING *curve, uint8_t *buf) { uint8_t *loc; int ptsize; size_t size; int type = RTCIRCSTRINGTYPE; assert(curve); assert(buf); if (RTFLAGS_GET_ZM(curve->flags) != RTFLAGS_GET_ZM(curve->points->flags)) rterror(ctx, "Dimensions mismatch in rtcircstring"); ptsize = ptarray_point_size(ctx, curve->points); loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the npoints. */ memcpy(loc, &curve->points->npoints, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Copy in the ordinates. */ if ( curve->points->npoints > 0 ) { size = curve->points->npoints * ptsize; memcpy(loc, rt_getPoint_internal(ctx, curve->points, 0), size); loc += size; } return (size_t)(loc - buf); } static size_t gserialized_from_rtcollection(const RTCTX *ctx, const RTCOLLECTION *coll, uint8_t *buf) { size_t subsize = 0; uint8_t *loc; int i; int type; assert(coll); assert(buf); type = coll->type; loc = buf; /* Write in the type. */ memcpy(loc, &type, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Write in the number of subgeoms. */ memcpy(loc, &coll->ngeoms, sizeof(uint32_t)); loc += sizeof(uint32_t); /* Serialize subgeoms. */ for ( i=0; ingeoms; i++ ) { if (RTFLAGS_GET_ZM(coll->flags) != RTFLAGS_GET_ZM(coll->geoms[i]->flags)) rterror(ctx, "Dimensions mismatch in rtcollection"); subsize = gserialized_from_rtgeom_any(ctx, coll->geoms[i], loc); loc += subsize; } return (size_t)(loc - buf); } static size_t gserialized_from_rtgeom_any(const RTCTX *ctx, const RTGEOM *geom, uint8_t *buf) { assert(geom); assert(buf); RTDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d", geom->type, rttype_name(ctx, geom->type), RTFLAGS_GET_Z(geom->flags), RTFLAGS_GET_M(geom->flags)); RTDEBUGF(2, "RTGEOM(%p) uint8_t(%p)", geom, buf); switch (geom->type) { case RTPOINTTYPE: return gserialized_from_rtpoint(ctx, (RTPOINT *)geom, buf); case RTLINETYPE: return gserialized_from_rtline(ctx, (RTLINE *)geom, buf); case RTPOLYGONTYPE: return gserialized_from_rtpoly(ctx, (RTPOLY *)geom, buf); case RTTRIANGLETYPE: return gserialized_from_rttriangle(ctx, (RTTRIANGLE *)geom, buf); case RTCIRCSTRINGTYPE: return gserialized_from_rtcircstring(ctx, (RTCIRCSTRING *)geom, buf); case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTICURVETYPE: case RTMULTIPOLYGONTYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return gserialized_from_rtcollection(ctx, (RTCOLLECTION *)geom, buf); default: rterror(ctx, "Unknown geometry type: %d - %s", geom->type, rttype_name(ctx, geom->type)); return 0; } return 0; } static size_t gserialized_from_gbox(const RTCTX *ctx, const RTGBOX *gbox, uint8_t *buf) { uint8_t *loc = buf; float f; size_t return_size; assert(buf); f = next_float_down(ctx, gbox->xmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(ctx, gbox->xmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_down(ctx, gbox->ymin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(ctx, gbox->ymax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); if ( RTFLAGS_GET_GEODETIC(gbox->flags) ) { f = next_float_down(ctx, gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(ctx, gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); return_size = (size_t)(loc - buf); RTDEBUGF(4, "returning size %d", return_size); return return_size; } if ( RTFLAGS_GET_Z(gbox->flags) ) { f = next_float_down(ctx, gbox->zmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(ctx, gbox->zmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } if ( RTFLAGS_GET_M(gbox->flags) ) { f = next_float_down(ctx, gbox->mmin); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); f = next_float_up(ctx, gbox->mmax); memcpy(loc, &f, sizeof(float)); loc += sizeof(float); } return_size = (size_t)(loc - buf); RTDEBUGF(4, "returning size %d", return_size); return return_size; } /* Public function */ GSERIALIZED* gserialized_from_rtgeom(const RTCTX *ctx, RTGEOM *geom, int is_geodetic, size_t *size) { size_t expected_size = 0; size_t return_size = 0; uint8_t *serialized = NULL; uint8_t *ptr = NULL; GSERIALIZED *g = NULL; assert(geom); /* ** See if we need a bounding box, add one if we don't have one. */ if ( (! geom->bbox) && rtgeom_needs_bbox(ctx, geom) && (!rtgeom_is_empty(ctx, geom)) ) { rtgeom_add_bbox(ctx, geom); } /* ** Harmonize the flags to the state of the rtgeom */ if ( geom->bbox ) RTFLAGS_SET_BBOX(geom->flags, 1); /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */ expected_size = gserialized_from_rtgeom_size(ctx, geom); serialized = rtalloc(ctx, expected_size); ptr = serialized; /* Move past size, srid and flags. */ ptr += 8; /* Write in the serialized form of the gbox, if necessary. */ if ( geom->bbox ) ptr += gserialized_from_gbox(ctx, geom->bbox, ptr); /* Write in the serialized form of the geometry. */ ptr += gserialized_from_rtgeom_any(ctx, geom, ptr); /* Calculate size as returned by data processing functions. */ return_size = ptr - serialized; if ( expected_size != return_size ) /* Uh oh! */ { rterror(ctx, "Return size (%d) not equal to expected size (%d)!", return_size, expected_size); return NULL; } if ( size ) /* Return the output size to the caller if necessary. */ *size = return_size; g = (GSERIALIZED*)serialized; /* ** We are aping PgSQL code here, PostGIS code should use ** VARSIZE to set this for real. */ g->size = return_size << 2; /* Set the SRID! */ gserialized_set_srid(ctx, g, geom->srid); g->flags = geom->flags; return g; } /*********************************************************************** * De-serialize GSERIALIZED into an RTGEOM. */ static RTGEOM* rtgeom_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size); static RTPOINT* rtpoint_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; RTPOINT *point; uint32_t npoints = 0; assert(data_ptr); point = (RTPOINT*)rtalloc(ctx, sizeof(RTPOINT)); point->srid = SRID_UNKNOWN; /* Default */ point->bbox = NULL; point->type = RTPOINTTYPE; point->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = rt_get_uint32_t(ctx, data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) point->point = ptarray_construct_reference_data(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), 1, data_ptr); else point->point = ptarray_construct(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), 0); /* Empty point */ data_ptr += npoints * RTFLAGS_NDIMS(g_flags) * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return point; } static RTLINE* rtline_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; RTLINE *line; uint32_t npoints = 0; assert(data_ptr); line = (RTLINE*)rtalloc(ctx, sizeof(RTLINE)); line->srid = SRID_UNKNOWN; /* Default */ line->bbox = NULL; line->type = RTLINETYPE; line->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = rt_get_uint32_t(ctx, data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) line->points = ptarray_construct_reference_data(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), npoints, data_ptr); else line->points = ptarray_construct(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), 0); /* Empty linestring */ data_ptr += RTFLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return line; } static RTPOLY* rtpoly_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; RTPOLY *poly; uint8_t *ordinate_ptr; uint32_t nrings = 0; int i = 0; assert(data_ptr); poly = (RTPOLY*)rtalloc(ctx, sizeof(RTPOLY)); poly->srid = SRID_UNKNOWN; /* Default */ poly->bbox = NULL; poly->type = RTPOLYGONTYPE; poly->flags = g_flags; data_ptr += 4; /* Skip past the polygontype. */ nrings = rt_get_uint32_t(ctx, data_ptr); /* Zero => empty geometry */ poly->nrings = nrings; RTDEBUGF(4, "nrings = %d", nrings); data_ptr += 4; /* Skip past the nrings. */ ordinate_ptr = data_ptr; /* Start the ordinate pointer. */ if ( nrings > 0) { poly->rings = (RTPOINTARRAY**)rtalloc(ctx, sizeof(RTPOINTARRAY*) * nrings ); ordinate_ptr += nrings * 4; /* Move past all the npoints values. */ if ( nrings % 2 ) /* If there is padding, move past that too. */ ordinate_ptr += 4; } else /* Empty polygon */ { poly->rings = NULL; } for ( i = 0; i < nrings; i++ ) { uint32_t npoints = 0; /* Read in the number of points. */ npoints = rt_get_uint32_t(ctx, data_ptr); data_ptr += 4; /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */ poly->rings[i] = ptarray_construct_reference_data(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), npoints, ordinate_ptr); ordinate_ptr += sizeof(double) * RTFLAGS_NDIMS(g_flags) * npoints; } if ( g_size ) *g_size = ordinate_ptr - start_ptr; return poly; } static RTTRIANGLE* rttriangle_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; RTTRIANGLE *triangle; uint32_t npoints = 0; assert(data_ptr); triangle = (RTTRIANGLE*)rtalloc(ctx, sizeof(RTTRIANGLE)); triangle->srid = SRID_UNKNOWN; /* Default */ triangle->bbox = NULL; triangle->type = RTTRIANGLETYPE; triangle->flags = g_flags; data_ptr += 4; /* Skip past the type. */ npoints = rt_get_uint32_t(ctx, data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) triangle->points = ptarray_construct_reference_data(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), npoints, data_ptr); else triangle->points = ptarray_construct(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), 0); /* Empty triangle */ data_ptr += RTFLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return triangle; } static RTCIRCSTRING* rtcircstring_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint8_t *start_ptr = data_ptr; RTCIRCSTRING *circstring; uint32_t npoints = 0; assert(data_ptr); circstring = (RTCIRCSTRING*)rtalloc(ctx, sizeof(RTCIRCSTRING)); circstring->srid = SRID_UNKNOWN; /* Default */ circstring->bbox = NULL; circstring->type = RTCIRCSTRINGTYPE; circstring->flags = g_flags; data_ptr += 4; /* Skip past the circstringtype. */ npoints = rt_get_uint32_t(ctx, data_ptr); /* Zero => empty geometry */ data_ptr += 4; /* Skip past the npoints. */ if ( npoints > 0 ) circstring->points = ptarray_construct_reference_data(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), npoints, data_ptr); else circstring->points = ptarray_construct(ctx, RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), 0); /* Empty circularstring */ data_ptr += RTFLAGS_NDIMS(g_flags) * npoints * sizeof(double); if ( g_size ) *g_size = data_ptr - start_ptr; return circstring; } static RTCOLLECTION* rtcollection_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint32_t type; uint8_t *start_ptr = data_ptr; RTCOLLECTION *collection; uint32_t ngeoms = 0; int i = 0; assert(data_ptr); type = rt_get_uint32_t(ctx, data_ptr); data_ptr += 4; /* Skip past the type. */ collection = (RTCOLLECTION*)rtalloc(ctx, sizeof(RTCOLLECTION)); collection->srid = SRID_UNKNOWN; /* Default */ collection->bbox = NULL; collection->type = type; collection->flags = g_flags; ngeoms = rt_get_uint32_t(ctx, data_ptr); collection->ngeoms = ngeoms; /* Zero => empty geometry */ data_ptr += 4; /* Skip past the ngeoms. */ if ( ngeoms > 0 ) collection->geoms = rtalloc(ctx, sizeof(RTGEOM*) * ngeoms); else collection->geoms = NULL; /* Sub-geometries are never de-serialized with boxes (#1254) */ RTFLAGS_SET_BBOX(g_flags, 0); for ( i = 0; i < ngeoms; i++ ) { uint32_t subtype = rt_get_uint32_t(ctx, data_ptr); size_t subsize = 0; if ( ! rtcollection_allows_subtype(ctx, type, subtype) ) { rterror(ctx, "Invalid subtype (%s) for collection type (%s)", rttype_name(ctx, subtype), rttype_name(ctx, type)); rtfree(ctx, collection); return NULL; } collection->geoms[i] = rtgeom_from_gserialized_buffer(ctx, data_ptr, g_flags, &subsize); data_ptr += subsize; } if ( g_size ) *g_size = data_ptr - start_ptr; return collection; } RTGEOM* rtgeom_from_gserialized_buffer(const RTCTX *ctx, uint8_t *data_ptr, uint8_t g_flags, size_t *g_size) { uint32_t type; assert(data_ptr); type = rt_get_uint32_t(ctx, data_ptr); RTDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, rttype_name(ctx, type), RTFLAGS_GET_Z(g_flags), RTFLAGS_GET_M(g_flags), RTFLAGS_GET_GEODETIC(g_flags), RTFLAGS_GET_BBOX(g_flags)); switch (type) { case RTPOINTTYPE: return (RTGEOM *)rtpoint_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); case RTLINETYPE: return (RTGEOM *)rtline_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); case RTCIRCSTRINGTYPE: return (RTGEOM *)rtcircstring_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); case RTPOLYGONTYPE: return (RTGEOM *)rtpoly_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); case RTTRIANGLETYPE: return (RTGEOM *)rttriangle_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return (RTGEOM *)rtcollection_from_gserialized_buffer(ctx, data_ptr, g_flags, g_size); default: rterror(ctx, "Unknown geometry type: %d - %s", type, rttype_name(ctx, type)); return NULL; } } RTGEOM* rtgeom_from_gserialized(const RTCTX *ctx, const GSERIALIZED *g) { uint8_t g_flags = 0; int32_t g_srid = 0; uint32_t g_type = 0; uint8_t *data_ptr = NULL; RTGEOM *rtgeom = NULL; RTGBOX bbox; size_t g_size = 0; assert(g); g_srid = gserialized_get_srid(ctx, g); g_flags = g->flags; g_type = gserialized_get_type(ctx, g); RTDEBUGF(4, "Got type %d (%s), srid=%d", g_type, rttype_name(ctx, g_type), g_srid); data_ptr = (uint8_t*)g->data; if ( RTFLAGS_GET_BBOX(g_flags) ) data_ptr += gbox_serialized_size(ctx, g_flags); rtgeom = rtgeom_from_gserialized_buffer(ctx, data_ptr, g_flags, &g_size); if ( ! rtgeom ) rterror(ctx, "rtgeom_from_gserialized: unable create geometry"); /* Ooops! */ rtgeom->type = g_type; rtgeom->flags = g_flags; if ( gserialized_read_gbox_p(ctx, g, &bbox) == RT_SUCCESS ) { rtgeom->bbox = gbox_copy(ctx, &bbox); } else if ( rtgeom_needs_bbox(ctx, rtgeom) && (rtgeom_calculate_gbox(ctx, rtgeom, &bbox) == RT_SUCCESS) ) { rtgeom->bbox = gbox_copy(ctx, &bbox); } else { rtgeom->bbox = NULL; } rtgeom_set_srid(ctx, rtgeom, g_srid); return rtgeom; } src/g_util.c000066400000000000000000000153161271715413500133100ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #include #include "librttopo_geom_internal.h" /* Structure for the type array */ struct geomtype_struct { char *typename; int type; int z; int m; }; /* Type array. Note that the order of this array is important in that any typename in the list must *NOT* occur within an entry before it. Otherwise if we search for "POINT" at the top of the list we would also match MULTIPOINT, for example. */ struct geomtype_struct geomtype_struct_array[] = { { "GEOMETRYCOLLECTIONZM", RTCOLLECTIONTYPE, 1, 1 }, { "GEOMETRYCOLLECTIONZ", RTCOLLECTIONTYPE, 1, 0 }, { "GEOMETRYCOLLECTIONM", RTCOLLECTIONTYPE, 0, 1 }, { "GEOMETRYCOLLECTION", RTCOLLECTIONTYPE, 0, 0 }, { "GEOMETRYZM", 0, 1, 1 }, { "GEOMETRYZ", 0, 1, 0 }, { "GEOMETRYM", 0, 0, 1 }, { "GEOMETRY", 0, 0, 0 }, { "POLYHEDRALSURFACEZM", RTPOLYHEDRALSURFACETYPE, 1, 1 }, { "POLYHEDRALSURFACEZ", RTPOLYHEDRALSURFACETYPE, 1, 0 }, { "POLYHEDRALSURFACEM", RTPOLYHEDRALSURFACETYPE, 0, 1 }, { "POLYHEDRALSURFACE", RTPOLYHEDRALSURFACETYPE, 0, 0 }, { "TINZM", RTTINTYPE, 1, 1 }, { "TINZ", RTTINTYPE, 1, 0 }, { "TINM", RTTINTYPE, 0, 1 }, { "TIN", RTTINTYPE, 0, 0 }, { "CIRCULARSTRINGZM", RTCIRCSTRINGTYPE, 1, 1 }, { "CIRCULARSTRINGZ", RTCIRCSTRINGTYPE, 1, 0 }, { "CIRCULARSTRINGM", RTCIRCSTRINGTYPE, 0, 1 }, { "CIRCULARSTRING", RTCIRCSTRINGTYPE, 0, 0 }, { "COMPOUNDCURVEZM", RTCOMPOUNDTYPE, 1, 1 }, { "COMPOUNDCURVEZ", RTCOMPOUNDTYPE, 1, 0 }, { "COMPOUNDCURVEM", RTCOMPOUNDTYPE, 0, 1 }, { "COMPOUNDCURVE", RTCOMPOUNDTYPE, 0, 0 }, { "CURVEPOLYGONZM", RTCURVEPOLYTYPE, 1, 1 }, { "CURVEPOLYGONZ", RTCURVEPOLYTYPE, 1, 0 }, { "CURVEPOLYGONM", RTCURVEPOLYTYPE, 0, 1 }, { "CURVEPOLYGON", RTCURVEPOLYTYPE, 0, 0 }, { "MULTICURVEZM", RTMULTICURVETYPE, 1, 1 }, { "MULTICURVEZ", RTMULTICURVETYPE, 1, 0 }, { "MULTICURVEM", RTMULTICURVETYPE, 0, 1 }, { "MULTICURVE", RTMULTICURVETYPE, 0, 0 }, { "MULTISURFACEZM", RTMULTISURFACETYPE, 1, 1 }, { "MULTISURFACEZ", RTMULTISURFACETYPE, 1, 0 }, { "MULTISURFACEM", RTMULTISURFACETYPE, 0, 1 }, { "MULTISURFACE", RTMULTISURFACETYPE, 0, 0 }, { "MULTILINESTRINGZM", RTMULTILINETYPE, 1, 1 }, { "MULTILINESTRINGZ", RTMULTILINETYPE, 1, 0 }, { "MULTILINESTRINGM", RTMULTILINETYPE, 0, 1 }, { "MULTILINESTRING", RTMULTILINETYPE, 0, 0 }, { "MULTIPOLYGONZM", RTMULTIPOLYGONTYPE, 1, 1 }, { "MULTIPOLYGONZ", RTMULTIPOLYGONTYPE, 1, 0 }, { "MULTIPOLYGONM", RTMULTIPOLYGONTYPE, 0, 1 }, { "MULTIPOLYGON", RTMULTIPOLYGONTYPE, 0, 0 }, { "MULTIPOINTZM", RTMULTIPOINTTYPE, 1, 1 }, { "MULTIPOINTZ", RTMULTIPOINTTYPE, 1, 0 }, { "MULTIPOINTM", RTMULTIPOINTTYPE, 0, 1 }, { "MULTIPOINT", RTMULTIPOINTTYPE, 0, 0 }, { "LINESTRINGZM", RTLINETYPE, 1, 1 }, { "LINESTRINGZ", RTLINETYPE, 1, 0 }, { "LINESTRINGM", RTLINETYPE, 0, 1 }, { "LINESTRING", RTLINETYPE, 0, 0 }, { "TRIANGLEZM", RTTRIANGLETYPE, 1, 1 }, { "TRIANGLEZ", RTTRIANGLETYPE, 1, 0 }, { "TRIANGLEM", RTTRIANGLETYPE, 0, 1 }, { "TRIANGLE", RTTRIANGLETYPE, 0, 0 }, { "POLYGONZM", RTPOLYGONTYPE, 1, 1 }, { "POLYGONZ", RTPOLYGONTYPE, 1, 0 }, { "POLYGONM", RTPOLYGONTYPE, 0, 1 }, { "POLYGON", RTPOLYGONTYPE, 0, 0 }, { "POINTZM", RTPOINTTYPE, 1, 1 }, { "POINTZ", RTPOINTTYPE, 1, 0 }, { "POINTM", RTPOINTTYPE, 0, 1 }, { "POINT", RTPOINTTYPE, 0, 0 } }; #define GEOMTYPE_STRUCT_ARRAY_LEN (sizeof geomtype_struct_array/sizeof(struct geomtype_struct)) /* * We use a very simple upper case mapper here, because the system toupper() function * is locale dependent and may have trouble mapping lower case strings to the upper * case ones we expect (see, the "Turkisk I", http://www.i18nguy.com/unicode/turkish-i18n.html) * We could also count on PgSQL sending us *lower* case inputs, as it seems to do that * regardless of the case the user provides for the type arguments. */ const char dumb_upper_map[128] = "................................................0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......ABCDEFGHIJKLMNOPQRSTUVWXYZ....."; static char dump_toupper(const RTCTX *ctx, int in) { if ( in < 0 || in > 127 ) return '.'; return dumb_upper_map[in]; } uint8_t gflags(const RTCTX *ctx, int hasz, int hasm, int geodetic) { uint8_t flags = 0; if ( hasz ) RTFLAGS_SET_Z(flags, 1); if ( hasm ) RTFLAGS_SET_M(flags, 1); if ( geodetic ) RTFLAGS_SET_GEODETIC(flags, 1); return flags; } /** * Calculate type integer and dimensional flags from string input. * Case insensitive, and insensitive to spaces at front and back. * Type == 0 in the case of the string "GEOMETRY" or "GEOGRAPHY". * Return RT_SUCCESS for success. */ int geometry_type_from_string(const RTCTX *ctx, const char *str, uint8_t *type, int *z, int *m) { char *tmpstr; int tmpstartpos, tmpendpos; int i; assert(str); assert(type); assert(z); assert(m); /* Initialize. */ *type = 0; *z = 0; *m = 0; /* Locate any leading/trailing spaces */ tmpstartpos = 0; for (i = 0; i < strlen(str); i++) { if (str[i] != ' ') { tmpstartpos = i; break; } } tmpendpos = strlen(str) - 1; for (i = strlen(str) - 1; i >= 0; i--) { if (str[i] != ' ') { tmpendpos = i; break; } } /* Copy and convert to upper case for comparison */ tmpstr = rtalloc(ctx, tmpendpos - tmpstartpos + 2); for (i = tmpstartpos; i <= tmpendpos; i++) tmpstr[i - tmpstartpos] = dump_toupper(ctx, str[i]); /* Add NULL to terminate */ tmpstr[i - tmpstartpos] = '\0'; /* Now check for the type */ for (i = 0; i < GEOMTYPE_STRUCT_ARRAY_LEN; i++) { if (!strcmp(tmpstr, geomtype_struct_array[i].typename)) { *type = geomtype_struct_array[i].type; *z = geomtype_struct_array[i].z; *m = geomtype_struct_array[i].m; rtfree(ctx, tmpstr); return RT_SUCCESS; } } rtfree(ctx, tmpstr); return RT_FAILURE; } src/librttopo_geom_internal.h000066400000000000000000000444751271715413500167630ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2011-2012 Sandro Santilli * Copyright (C) 2011 Paul Ramsey * Copyright (C) 2007-2008 Mark Cave-Ayland * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef _LIBRTGEOM_INTERNAL_H #define _LIBRTGEOM_INTERNAL_H 1 #include "rttopo_config.h" #include "librttopo_geom.h" #include "rtgeom_log.h" #include #include #include #include #include #include #if defined(_WIN32) && !defined(__MINGW32__) #define _USE_MATH_DEFINES #endif #include #if HAVE_IEEEFP_H #include #endif #if defined(PJ_VERSION) && PJ_VERSION >= 490 /* Enable new geodesic functions */ #define PROJ_GEODESIC 1 #else /* Use the old (pre-2.2) geodesic functions */ #define PROJ_GEODESIC 0 #endif #include #include "librttopo_geom.h" /** * Floating point comparators. */ #define FP_TOLERANCE 1e-12 #define FP_IS_ZERO(A) (fabs(A) <= FP_TOLERANCE) #define FP_MAX(A, B) (((A) > (B)) ? (A) : (B)) #define FP_MIN(A, B) (((A) < (B)) ? (A) : (B)) #define FP_ABS(a) ((a) < (0) ? -(a) : (a)) #define FP_EQUALS(A, B) (fabs((A)-(B)) <= FP_TOLERANCE) #define FP_NEQUALS(A, B) (fabs((A)-(B)) > FP_TOLERANCE) #define FP_LT(A, B) (((A) + FP_TOLERANCE) < (B)) #define FP_LTEQ(A, B) (((A) - FP_TOLERANCE) <= (B)) #define FP_GT(A, B) (((A) - FP_TOLERANCE) > (B)) #define FP_GTEQ(A, B) (((A) + FP_TOLERANCE) >= (B)) #define FP_CONTAINS_TOP(A, X, B) (FP_LT(A, X) && FP_LTEQ(X, B)) #define FP_CONTAINS_BOTTOM(A, X, B) (FP_LTEQ(A, X) && FP_LT(X, B)) #define FP_CONTAINS_INCL(A, X, B) (FP_LTEQ(A, X) && FP_LTEQ(X, B)) #define FP_CONTAINS_EXCL(A, X, B) (FP_LT(A, X) && FP_LT(X, B)) #define FP_CONTAINS(A, X, B) FP_CONTAINS_EXCL(A, X, B) /* * this will change to NaN when I figure out how to * get NaN in a platform-independent way */ #define NO_VALUE 0.0 #define NO_Z_VALUE NO_VALUE #define NO_M_VALUE NO_VALUE /** * Well-Known Text (RTWKT) Output Variant Types */ #define RTWKT_NO_TYPE 0x08 /* Internal use only */ #define RTWKT_NO_PARENS 0x10 /* Internal use only */ #define RTWKT_IS_CHILD 0x20 /* Internal use only */ /** * Well-Known Binary (RTWKB) Output Variant Types */ #define RTWKB_DOUBLE_SIZE 8 /* Internal use only */ #define RTWKB_INT_SIZE 4 /* Internal use only */ #define RTWKB_BYTE_SIZE 1 /* Internal use only */ /** * Well-Known Binary (RTWKB) Geometry Types */ #define RTWKB_POINT_TYPE 1 #define RTWKB_LINESTRING_TYPE 2 #define RTWKB_POLYGON_TYPE 3 #define RTWKB_MULTIPOINT_TYPE 4 #define RTWKB_MULTILINESTRING_TYPE 5 #define RTWKB_MULTIPOLYGON_TYPE 6 #define RTWKB_GEOMETRYCOLLECTION_TYPE 7 #define RTWKB_CIRCULARSTRING_TYPE 8 #define RTWKB_COMPOUNDCURVE_TYPE 9 #define RTWKB_CURVEPOLYGON_TYPE 10 #define RTWKB_MULTICURVE_TYPE 11 #define RTWKB_MULTISURFACE_TYPE 12 #define RTWKB_CURVE_TYPE 13 /* from ISO draft, not sure is real */ #define RTWKB_SURFACE_TYPE 14 /* from ISO draft, not sure is real */ #define RTWKB_POLYHEDRALSURFACE_TYPE 15 #define RTWKB_TIN_TYPE 16 #define RTWKB_TRIANGLE_TYPE 17 /** * Macro for reading the size from the GSERIALIZED size attribute. * Cribbed from PgSQL, top 30 bits are size. Use VARSIZE() when working * internally with PgSQL. */ #define SIZE_GET(varsize) (((varsize) >> 2) & 0x3FFFFFFF) #define SIZE_SET(varsize, size) (((varsize) & 0x00000003)|(((size) & 0x3FFFFFFF) << 2 )) /** * Tolerance used to determine equality. */ #define EPSILON_SQLMM 1e-8 /* * Export functions */ #define OUT_MAX_DOUBLE 1E15 #define OUT_SHOW_DIGS_DOUBLE 20 #define OUT_MAX_DOUBLE_PRECISION 15 #define OUT_MAX_DIGS_DOUBLE (OUT_SHOW_DIGS_DOUBLE + 2) /* +2 mean add dot and sign */ /** * Constants for point-in-polygon return values */ #define RT_INSIDE 1 #define RT_BOUNDARY 0 #define RT_OUTSIDE -1 #define RTGEOM_GEOS_ERRMSG_MAXSIZE 256 struct RTCTX_T { GEOSContextHandle_t gctx; char rtgeom_geos_errmsg[RTGEOM_GEOS_ERRMSG_MAXSIZE]; rtallocator rtalloc_var; rtreallocator rtrealloc_var; rtfreeor rtfree_var; rtreporter error_logger; void * error_logger_arg; rtreporter notice_logger; void * notice_logger_arg; rtdebuglogger debug_logger; void * debug_logger_arg; }; /* * Internal prototypes */ /* Machine endianness */ #define XDR 0 /* big endian */ #define NDR 1 /* little endian */ extern char getMachineEndian(const RTCTX *ctx); /* * Force dims */ RTGEOM* rtgeom_force_dims(const RTCTX *ctx, const RTGEOM *rtgeom, int hasz, int hasm); RTPOINT* rtpoint_force_dims(const RTCTX *ctx, const RTPOINT *rtpoint, int hasz, int hasm); RTLINE* rtline_force_dims(const RTCTX *ctx, const RTLINE *rtline, int hasz, int hasm); RTPOLY* rtpoly_force_dims(const RTCTX *ctx, const RTPOLY *rtpoly, int hasz, int hasm); RTCOLLECTION* rtcollection_force_dims(const RTCTX *ctx, const RTCOLLECTION *rtcol, int hasz, int hasm); RTPOINTARRAY* ptarray_force_dims(const RTCTX *ctx, const RTPOINTARRAY *pa, int hasz, int hasm); /** * Swap ordinate values o1 and o2 on a given RTPOINTARRAY * * Ordinates semantic is: 0=x 1=y 2=z 3=m */ void ptarray_swap_ordinates(const RTCTX *ctx, RTPOINTARRAY *pa, RTORD o1, RTORD o2); /* * Is Empty? */ int rtpoly_is_empty(const RTCTX *ctx, const RTPOLY *poly); int rtcollection_is_empty(const RTCTX *ctx, const RTCOLLECTION *col); int rtcircstring_is_empty(const RTCTX *ctx, const RTCIRCSTRING *circ); int rttriangle_is_empty(const RTCTX *ctx, const RTTRIANGLE *triangle); int rtline_is_empty(const RTCTX *ctx, const RTLINE *line); int rtpoint_is_empty(const RTCTX *ctx, const RTPOINT *point); /* * Number of vertices? */ int rtline_count_vertices(const RTCTX *ctx, RTLINE *line); int rtpoly_count_vertices(const RTCTX *ctx, RTPOLY *poly); int rtcollection_count_vertices(const RTCTX *ctx, RTCOLLECTION *col); /* * Read from byte buffer */ extern uint32_t rt_get_uint32_t(const RTCTX *ctx, const uint8_t *loc); extern int32_t rt_get_int32_t(const RTCTX *ctx, const uint8_t *loc); /* * DP simplification */ /** * @param minpts minimun number of points to retain, if possible. */ RTPOINTARRAY* ptarray_simplify(const RTCTX *ctx, RTPOINTARRAY *inpts, double epsilon, unsigned int minpts); RTLINE* rtline_simplify(const RTCTX *ctx, const RTLINE *iline, double dist, int preserve_collapsed); RTPOLY* rtpoly_simplify(const RTCTX *ctx, const RTPOLY *ipoly, double dist, int preserve_collapsed); RTCOLLECTION* rtcollection_simplify(const RTCTX *ctx, const RTCOLLECTION *igeom, double dist, int preserve_collapsed); /* * Computational geometry */ int signum(const RTCTX *ctx, double n); /* * The possible ways a pair of segments can interact. Returned by rt_segment_intersects */ enum RTCG_SEGMENT_INTERSECTION_TYPE { SEG_ERROR = -1, SEG_NO_INTERSECTION = 0, SEG_COLINEAR = 1, SEG_CROSS_LEFT = 2, SEG_CROSS_RIGHT = 3, SEG_TOUCH_LEFT = 4, SEG_TOUCH_RIGHT = 5 }; /* * Do the segments intersect? How? */ int rt_segment_intersects(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q1, const RTPOINT2D *q2); /* * Get/Set an enumeratoed ordinate. (x,y,z,m) */ double rtpoint_get_ordinate(const RTCTX *ctx, const RTPOINT4D *p, char ordinate); void rtpoint_set_ordinate(const RTCTX *ctx, RTPOINT4D *p, char ordinate, double value); /* * Generate an interpolated coordinate p given an interpolation value and ordinate to apply it to */ int point_interpolate(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2, RTPOINT4D *p, int hasz, int hasm, char ordinate, double interpolation_value); /** * Clip a line based on the from/to range of one of its ordinates. Use for m- and z- clipping */ RTCOLLECTION *rtline_clip_to_ordinate_range(const RTCTX *ctx, const RTLINE *line, char ordinate, double from, double to); /** * Clip a multi-line based on the from/to range of one of its ordinates. Use for m- and z- clipping */ RTCOLLECTION *rtmline_clip_to_ordinate_range(const RTCTX *ctx, const RTMLINE *mline, char ordinate, double from, double to); /** * Clip a multi-point based on the from/to range of one of its ordinates. Use for m- and z- clipping */ RTCOLLECTION *rtmpoint_clip_to_ordinate_range(const RTCTX *ctx, const RTMPOINT *mpoint, char ordinate, double from, double to); /** * Clip a point based on the from/to range of one of its ordinates. Use for m- and z- clipping */ RTCOLLECTION *rtpoint_clip_to_ordinate_range(const RTCTX *ctx, const RTPOINT *mpoint, char ordinate, double from, double to); /* * Geohash */ int rtgeom_geohash_precision(const RTCTX *ctx, RTGBOX bbox, RTGBOX *bounds); char *geohash_point(const RTCTX *ctx, double longitude, double latitude, int precision); void decode_geohash_bbox(const RTCTX *ctx, char *geohash, double *lat, double *lon, int precision); /* * Point comparisons */ int p4d_same(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2); int p3d_same(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2); int p2d_same(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2); /* * Area calculations */ double rtpoly_area(const RTCTX *ctx, const RTPOLY *poly); double rtcurvepoly_area(const RTCTX *ctx, const RTCURVEPOLY *curvepoly); double rttriangle_area(const RTCTX *ctx, const RTTRIANGLE *triangle); /** * Pull a #RTGBOX from the header of a #GSERIALIZED, if one is available. If * it is not, return RT_FAILURE. */ extern int gserialized_read_gbox_p(const RTCTX *ctx, const GSERIALIZED *g, RTGBOX *gbox); /* * Length calculations */ double rtcompound_length(const RTCTX *ctx, const RTCOMPOUND *comp); double rtcompound_length_2d(const RTCTX *ctx, const RTCOMPOUND *comp); double rtline_length(const RTCTX *ctx, const RTLINE *line); double rtline_length_2d(const RTCTX *ctx, const RTLINE *line); double rtcircstring_length(const RTCTX *ctx, const RTCIRCSTRING *circ); double rtcircstring_length_2d(const RTCTX *ctx, const RTCIRCSTRING *circ); double rtpoly_perimeter(const RTCTX *ctx, const RTPOLY *poly); double rtpoly_perimeter_2d(const RTCTX *ctx, const RTPOLY *poly); double rtcurvepoly_perimeter(const RTCTX *ctx, const RTCURVEPOLY *poly); double rtcurvepoly_perimeter_2d(const RTCTX *ctx, const RTCURVEPOLY *poly); double rttriangle_perimeter(const RTCTX *ctx, const RTTRIANGLE *triangle); double rttriangle_perimeter_2d(const RTCTX *ctx, const RTTRIANGLE *triangle); /* * Segmentization */ RTLINE *rtcircstring_stroke(const RTCTX *ctx, const RTCIRCSTRING *icurve, uint32_t perQuad); RTLINE *rtcompound_stroke(const RTCTX *ctx, const RTCOMPOUND *icompound, uint32_t perQuad); RTPOLY *rtcurvepoly_stroke(const RTCTX *ctx, const RTCURVEPOLY *curvepoly, uint32_t perQuad); /* * Affine */ void ptarray_affine(const RTCTX *ctx, RTPOINTARRAY *pa, const RTAFFINE *affine); /* * Scale */ void ptarray_scale(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *factor); /* * PointArray */ int ptarray_has_z(const RTCTX *ctx, const RTPOINTARRAY *pa); int ptarray_has_m(const RTCTX *ctx, const RTPOINTARRAY *pa); double ptarray_signed_area(const RTCTX *ctx, const RTPOINTARRAY *pa); /* * Clone support */ RTLINE *rtline_clone(const RTCTX *ctx, const RTLINE *rtgeom); RTPOLY *rtpoly_clone(const RTCTX *ctx, const RTPOLY *rtgeom); RTTRIANGLE *rttriangle_clone(const RTCTX *ctx, const RTTRIANGLE *rtgeom); RTCOLLECTION *rtcollection_clone(const RTCTX *ctx, const RTCOLLECTION *rtgeom); RTCIRCSTRING *rtcircstring_clone(const RTCTX *ctx, const RTCIRCSTRING *curve); RTPOINTARRAY *ptarray_clone(const RTCTX *ctx, const RTPOINTARRAY *ptarray); RTGBOX *box2d_clone(const RTCTX *ctx, const RTGBOX *rtgeom); RTLINE *rtline_clone_deep(const RTCTX *ctx, const RTLINE *rtgeom); RTPOLY *rtpoly_clone_deep(const RTCTX *ctx, const RTPOLY *rtgeom); RTCOLLECTION *rtcollection_clone_deep(const RTCTX *ctx, const RTCOLLECTION *rtgeom); RTGBOX *gbox_clone(const RTCTX *ctx, const RTGBOX *gbox); /* * Startpoint */ int rtpoly_startpoint(const RTCTX *ctx, const RTPOLY* rtpoly, RTPOINT4D* pt); int ptarray_startpoint(const RTCTX *ctx, const RTPOINTARRAY* pa, RTPOINT4D* pt); int rtcollection_startpoint(const RTCTX *ctx, const RTCOLLECTION* col, RTPOINT4D* pt); /* * Write into *ret the coordinates of the closest point on * segment A-B to the reference input point R */ void closest_point_on_segment(const RTCTX *ctx, const RTPOINT4D *R, const RTPOINT4D *A, const RTPOINT4D *B, RTPOINT4D *ret); /* * Repeated points */ RTPOINTARRAY *ptarray_remove_repeated_points_minpoints(const RTCTX *ctx, const RTPOINTARRAY *in, double tolerance, int minpoints); RTPOINTARRAY *ptarray_remove_repeated_points(const RTCTX *ctx, const RTPOINTARRAY *in, double tolerance); RTGEOM* rtmpoint_remove_repeated_points(const RTCTX *ctx, const RTMPOINT *in, double tolerance); RTGEOM* rtline_remove_repeated_points(const RTCTX *ctx, const RTLINE *in, double tolerance); RTGEOM* rtcollection_remove_repeated_points(const RTCTX *ctx, const RTCOLLECTION *in, double tolerance); RTGEOM* rtpoly_remove_repeated_points(const RTCTX *ctx, const RTPOLY *in, double tolerance); /* * Closure test */ int rtline_is_closed(const RTCTX *ctx, const RTLINE *line); int rtpoly_is_closed(const RTCTX *ctx, const RTPOLY *poly); int rtcircstring_is_closed(const RTCTX *ctx, const RTCIRCSTRING *curve); int rtcompound_is_closed(const RTCTX *ctx, const RTCOMPOUND *curve); int rtpsurface_is_closed(const RTCTX *ctx, const RTPSURFACE *psurface); int rttin_is_closed(const RTCTX *ctx, const RTTIN *tin); /** * Snap to grid */ /** * Snap-to-grid Support */ typedef struct gridspec_t { double ipx; double ipy; double ipz; double ipm; double xsize; double ysize; double zsize; double msize; } gridspec; RTGEOM* rtgeom_grid(const RTCTX *ctx, const RTGEOM *rtgeom, const gridspec *grid); RTCOLLECTION* rtcollection_grid(const RTCTX *ctx, const RTCOLLECTION *coll, const gridspec *grid); RTPOINT* rtpoint_grid(const RTCTX *ctx, const RTPOINT *point, const gridspec *grid); RTPOLY* rtpoly_grid(const RTCTX *ctx, const RTPOLY *poly, const gridspec *grid); RTLINE* rtline_grid(const RTCTX *ctx, const RTLINE *line, const gridspec *grid); RTCIRCSTRING* rtcircstring_grid(const RTCTX *ctx, const RTCIRCSTRING *line, const gridspec *grid); RTPOINTARRAY* ptarray_grid(const RTCTX *ctx, const RTPOINTARRAY *pa, const gridspec *grid); /* * What side of the line formed by p1 and p2 does q fall? * Returns -1 for left and 1 for right and 0 for co-linearity */ int rt_segment_side(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q); int rt_arc_side(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, const RTPOINT2D *Q); int rt_arc_calculate_gbox_cartesian_2d(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, RTGBOX *gbox); double rt_arc_center(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *p3, RTPOINT2D *result); int rt_pt_in_seg(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2); int rt_pt_in_arc(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3); int rt_arc_is_pt(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3); double rt_seg_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2); double rt_arc_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3); int pt_in_ring_2d(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *ring); int ptarray_contains_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt); int ptarrayarc_contains_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt); int ptarray_contains_point_partial(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt, int check_closed, int *winding_number); int ptarrayarc_contains_point_partial(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt, int check_closed, int *winding_number); int rtcompound_contains_point(const RTCTX *ctx, const RTCOMPOUND *comp, const RTPOINT2D *pt); int rtgeom_contains_point(const RTCTX *ctx, const RTGEOM *geom, const RTPOINT2D *pt); /** * Split a line by a point and push components to the provided multiline. * If the point doesn't split the line, push nothing to the container. * Returns 0 if the point is off the line. * Returns 1 if the point is on the line boundary (endpoints). * Return 2 if the point is on the interior of the line (only case in which * a split happens). * * NOTE: the components pushed to the output vector have their SRID stripped */ int rtline_split_by_point_to(const RTCTX *ctx, const RTLINE* ln, const RTPOINT* pt, RTMLINE* to); /** Ensure the collection can hold at least up to ngeoms geometries */ void rtcollection_reserve(const RTCTX *ctx, RTCOLLECTION *col, int ngeoms); /** Check if subtype is allowed in collectiontype */ extern int rtcollection_allows_subtype(const RTCTX *ctx, int collectiontype, int subtype); /** RTGBOX utility functions to figure out coverage/location on the globe */ double gbox_angular_height(const RTCTX *ctx, const RTGBOX* gbox); double gbox_angular_width(const RTCTX *ctx, const RTGBOX* gbox); int gbox_centroid(const RTCTX *ctx, const RTGBOX* gbox, RTPOINT2D* out); /* Utilities */ extern void trim_trailing_zeros(const RTCTX *ctx, char *num); extern uint8_t RTMULTITYPE[RTNUMTYPES]; extern rtinterrupt_callback *_rtgeom_interrupt_callback; extern int _rtgeom_interrupt_requested; #define RT_ON_INTERRUPT(x) { \ if ( _rtgeom_interrupt_callback ) { \ (*_rtgeom_interrupt_callback)(); \ } \ if ( _rtgeom_interrupt_requested ) { \ _rtgeom_interrupt_requested = 0; \ rtnotice(ctx, "librtgeom code interrupted"); \ x; \ } \ } int ptarray_npoints_in_rect(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTGBOX *gbox); int gbox_contains_point2d(const RTCTX *ctx, const RTGBOX *g, const RTPOINT2D *p); int rtpoly_contains_point(const RTCTX *ctx, const RTPOLY *poly, const RTPOINT2D *pt); #endif /* _LIBRTGEOM_INTERNAL_H */ src/librttopo_internal.h000066400000000000000000000064551271715413500157500ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2015 Sandro Santilli * **********************************************************************/ #ifndef LIBRTGEOM_TOPO_INTERNAL_H #define LIBRTGEOM_TOPO_INTERNAL_H 1 #include "rttopo_config.h" #include "geos_c.h" #include "librttopo_geom.h" #include "librttopo.h" /************************************************************************ * * Generic SQL handler * ************************************************************************/ struct RTT_BE_IFACE_T { const RTT_BE_DATA *data; const RTT_BE_CALLBACKS *cb; const RTCTX *ctx; }; const char* rtt_be_lastErrorMessage(const RTT_BE_IFACE* be); RTT_BE_TOPOLOGY * rtt_be_loadTopologyByName(RTT_BE_IFACE *be, const char *name); int rtt_be_freeTopology(RTT_TOPOLOGY *topo); RTT_ISO_NODE* rtt_be_getNodeWithinDistance2D(RTT_TOPOLOGY* topo, RTPOINT* pt, double dist, int* numelems, int fields, int limit); RTT_ISO_NODE* rtt_be_getNodeById(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields); int rtt_be_ExistsCoincidentNode(RTT_TOPOLOGY* topo, RTPOINT* pt); int rtt_be_insertNodes(RTT_TOPOLOGY* topo, RTT_ISO_NODE* node, int numelems); int rtt_be_ExistsEdgeIntersectingPoint(RTT_TOPOLOGY* topo, RTPOINT* pt); RTT_ELEMID rtt_be_getNextEdgeId(RTT_TOPOLOGY* topo); RTT_ISO_EDGE* rtt_be_getEdgeById(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields); RTT_ISO_EDGE* rtt_be_getEdgeWithinDistance2D(RTT_TOPOLOGY* topo, RTPOINT* pt, double dist, int* numelems, int fields, int limit); int rtt_be_insertEdges(RTT_TOPOLOGY* topo, RTT_ISO_EDGE* edge, int numelems); int rtt_be_updateEdges(RTT_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields, const RTT_ISO_EDGE* upd_edge, int upd_fields, const RTT_ISO_EDGE* exc_edge, int exc_fields); int rtt_be_deleteEdges(RTT_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields); RTT_ELEMID rtt_be_getFaceContainingPoint(RTT_TOPOLOGY* topo, RTPOINT* pt); int rtt_be_updateTopoGeomEdgeSplit(RTT_TOPOLOGY* topo, RTT_ELEMID split_edge, RTT_ELEMID new_edge1, RTT_ELEMID new_edge2); /************************************************************************ * * Internal objects * ************************************************************************/ struct RTT_TOPOLOGY_T { const RTT_BE_IFACE *be_iface; RTT_BE_TOPOLOGY *be_topo; int srid; double precision; int hasZ; }; #endif /* LIBRTGEOM_TOPO_INTERNAL_H */ src/measures.c000066400000000000000000002115451271715413500136530ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * Copyright 2010 Nicklas Avén * Copyright 2012 Paul Ramsey * **********************************************************************/ #include #include #include "measures.h" #include "rtgeom_log.h" /*------------------------------------------------------------------------------------------------------------ Initializing functions The functions starting the distance-calculation processses --------------------------------------------------------------------------------------------------------------*/ RTGEOM * rtgeom_closest_line(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist2d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MIN); } RTGEOM * rtgeom_furthest_line(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist2d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MAX); } RTGEOM * rtgeom_closest_point(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist2d_distancepoint(ctx, rt1, rt2, rt1->srid, DIST_MIN); } RTGEOM * rtgeom_furthest_point(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist2d_distancepoint(ctx, rt1, rt2, rt1->srid, DIST_MAX); } void rt_dist2d_distpts_init(const RTCTX *ctx, DISTPTS *dl, int mode) { dl->twisted = -1; dl->p1.x = dl->p1.y = 0.0; dl->p2.x = dl->p2.y = 0.0; dl->mode = mode; dl->tolerance = 0.0; if ( mode == DIST_MIN ) dl->distance = FLT_MAX; else dl->distance = -1 * FLT_MAX; } /** Function initializing shortestline and longestline calculations. */ RTGEOM * rt_dist2d_distanceline(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode) { double x1,x2,y1,y2; double initdistance = ( mode == DIST_MIN ? FLT_MAX : -1.0); DISTPTS thedl; RTPOINT *rtpoints[2]; RTGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0.0; RTDEBUG(2, "rt_dist2d_distanceline is called"); if (!rt_dist2d_comp(ctx, rt1,rt2,&thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } /*if thedl.distance is unchanged there where only empty geometries input*/ if (thedl.distance == initdistance) { RTDEBUG(3, "didn't find geometries to measure between, returning null"); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } else { x1=thedl.p1.x; y1=thedl.p1.y; x2=thedl.p2.x; y2=thedl.p2.y; rtpoints[0] = rtpoint_make2d(ctx, srid, x1, y1); rtpoints[1] = rtpoint_make2d(ctx, srid, x2, y2); result = (RTGEOM *)rtline_from_ptarray(ctx, srid, 2, rtpoints); } return result; } /** Function initializing closestpoint calculations. */ RTGEOM * rt_dist2d_distancepoint(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2,int srid,int mode) { double x,y; DISTPTS thedl; double initdistance = FLT_MAX; RTGEOM *result; thedl.mode = mode; thedl.distance= initdistance; thedl.tolerance = 0; RTDEBUG(2, "rt_dist2d_distancepoint is called"); if (!rt_dist2d_comp(ctx, rt1,rt2,&thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } if (thedl.distance == initdistance) { RTDEBUG(3, "didn't find geometries to measure between, returning null"); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } else { x=thedl.p1.x; y=thedl.p1.y; result = (RTGEOM *)rtpoint_make2d(ctx, srid, x, y); } return result; } /** Function initialazing max distance calculation */ double rtgeom_maxdistance2d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { RTDEBUG(2, "rtgeom_maxdistance2d is called"); return rtgeom_maxdistance2d_tolerance(ctx, rt1, rt2, 0.0 ); } /** Function handling max distance calculations and dfyllywithin calculations. The difference is just the tolerance. */ double rtgeom_maxdistance2d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance) { /*double thedist;*/ DISTPTS thedl; RTDEBUG(2, "rtgeom_maxdistance2d_tolerance is called"); thedl.mode = DIST_MAX; thedl.distance= -1; thedl.tolerance = tolerance; if (rt_dist2d_comp(ctx, rt1,rt2,&thedl)) { return thedl.distance; } /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); return -1; } /** Function initialazing min distance calculation */ double rtgeom_mindistance2d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { RTDEBUG(2, "rtgeom_mindistance2d is called"); return rtgeom_mindistance2d_tolerance(ctx, rt1, rt2, 0.0 ); } /** Function handling min distance calculations and dwithin calculations. The difference is just the tolerance. */ double rtgeom_mindistance2d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance) { DISTPTS thedl; RTDEBUG(2, "rtgeom_mindistance2d_tolerance is called"); thedl.mode = DIST_MIN; thedl.distance= FLT_MAX; thedl.tolerance = tolerance; if (rt_dist2d_comp(ctx, rt1,rt2,&thedl)) { return thedl.distance; } /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); return FLT_MAX; } /*------------------------------------------------------------------------------------------------------------ End of Initializing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Preprocessing functions Functions preparing geometries for distance-calculations --------------------------------------------------------------------------------------------------------------*/ /** This function just deserializes geometries Bboxes is not checked here since it is the subgeometries bboxes we will use anyway. */ int rt_dist2d_comp(const RTCTX *ctx, const RTGEOM *rt1,const RTGEOM *rt2, DISTPTS *dl) { RTDEBUG(2, "rt_dist2d_comp is called"); return rt_dist2d_recursive(ctx, rt1, rt2, dl); } static int rt_dist2d_is_collection(const RTCTX *ctx, const RTGEOM *g) { switch (g->type) { case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTCOMPOUNDTYPE: case RTPOLYHEDRALSURFACETYPE: return RT_TRUE; break; default: return RT_FALSE; } } /** This is a recursive function delivering every possible combinatin of subgeometries */ int rt_dist2d_recursive(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS *dl) { int i, j; int n1=1; int n2=1; RTGEOM *g1 = NULL; RTGEOM *g2 = NULL; RTCOLLECTION *c1 = NULL; RTCOLLECTION *c2 = NULL; RTDEBUGF(2, "rt_dist2d_comp is called with type1=%d, type2=%d", rtg1->type, rtg2->type); if (rt_dist2d_is_collection(ctx, rtg1)) { RTDEBUG(3, "First geometry is collection"); c1 = rtgeom_as_rtcollection(ctx, rtg1); n1 = c1->ngeoms; } if (rt_dist2d_is_collection(ctx, rtg2)) { RTDEBUG(3, "Second geometry is collection"); c2 = rtgeom_as_rtcollection(ctx, rtg2); n2 = c2->ngeoms; } for ( i = 0; i < n1; i++ ) { if (rt_dist2d_is_collection(ctx, rtg1)) { g1 = c1->geoms[i]; } else { g1 = (RTGEOM*)rtg1; } if (rtgeom_is_empty(ctx, g1)) return RT_TRUE; if (rt_dist2d_is_collection(ctx, g1)) { RTDEBUG(3, "Found collection inside first geometry collection, recursing"); if (!rt_dist2d_recursive(ctx, g1, rtg2, dl)) return RT_FALSE; continue; } for ( j = 0; j < n2; j++ ) { if (rt_dist2d_is_collection(ctx, rtg2)) { g2 = c2->geoms[j]; } else { g2 = (RTGEOM*)rtg2; } if (rt_dist2d_is_collection(ctx, g2)) { RTDEBUG(3, "Found collection inside second geometry collection, recursing"); if (!rt_dist2d_recursive(ctx, g1, g2, dl)) return RT_FALSE; continue; } if ( ! g1->bbox ) { rtgeom_add_bbox(ctx, g1); } if ( ! g2->bbox ) { rtgeom_add_bbox(ctx, g2); } /*If one of geometries is empty, return. True here only means continue searching. False would have stoped the process*/ if (rtgeom_is_empty(ctx, g1)||rtgeom_is_empty(ctx, g2)) return RT_TRUE; if ( (dl->mode != DIST_MAX) && (! rt_dist2d_check_overlap(ctx, g1, g2)) && (g1->type == RTLINETYPE || g1->type == RTPOLYGONTYPE) && (g2->type == RTLINETYPE || g2->type == RTPOLYGONTYPE) ) { if (!rt_dist2d_distribute_fast(ctx, g1, g2, dl)) return RT_FALSE; } else { if (!rt_dist2d_distribute_bruteforce(ctx, g1, g2, dl)) return RT_FALSE; if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ } } } return RT_TRUE; } int rt_dist2d_distribute_bruteforce(const RTCTX *ctx, const RTGEOM *rtg1,const RTGEOM *rtg2, DISTPTS *dl) { int t1 = rtg1->type; int t2 = rtg2->type; switch ( t1 ) { case RTPOINTTYPE: { dl->twisted = 1; switch ( t2 ) { case RTPOINTTYPE: return rt_dist2d_point_point(ctx, (RTPOINT *)rtg1, (RTPOINT *)rtg2, dl); case RTLINETYPE: return rt_dist2d_point_line(ctx, (RTPOINT *)rtg1, (RTLINE *)rtg2, dl); case RTPOLYGONTYPE: return rt_dist2d_point_poly(ctx, (RTPOINT *)rtg1, (RTPOLY *)rtg2, dl); case RTCIRCSTRINGTYPE: return rt_dist2d_point_circstring(ctx, (RTPOINT *)rtg1, (RTCIRCSTRING *)rtg2, dl); case RTCURVEPOLYTYPE: return rt_dist2d_point_curvepoly(ctx, (RTPOINT *)rtg1, (RTCURVEPOLY *)rtg2, dl); default: rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); } } case RTLINETYPE: { dl->twisted = 1; switch ( t2 ) { case RTPOINTTYPE: dl->twisted=(-1); return rt_dist2d_point_line(ctx, (RTPOINT *)rtg2, (RTLINE *)rtg1, dl); case RTLINETYPE: return rt_dist2d_line_line(ctx, (RTLINE *)rtg1, (RTLINE *)rtg2, dl); case RTPOLYGONTYPE: return rt_dist2d_line_poly(ctx, (RTLINE *)rtg1, (RTPOLY *)rtg2, dl); case RTCIRCSTRINGTYPE: return rt_dist2d_line_circstring(ctx, (RTLINE *)rtg1, (RTCIRCSTRING *)rtg2, dl); case RTCURVEPOLYTYPE: return rt_dist2d_line_curvepoly(ctx, (RTLINE *)rtg1, (RTCURVEPOLY *)rtg2, dl); default: rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); } } case RTCIRCSTRINGTYPE: { dl->twisted = 1; switch ( t2 ) { case RTPOINTTYPE: dl->twisted = -1; return rt_dist2d_point_circstring(ctx, (RTPOINT *)rtg2, (RTCIRCSTRING *)rtg1, dl); case RTLINETYPE: dl->twisted = -1; return rt_dist2d_line_circstring(ctx, (RTLINE *)rtg2, (RTCIRCSTRING *)rtg1, dl); case RTPOLYGONTYPE: return rt_dist2d_circstring_poly(ctx, (RTCIRCSTRING *)rtg1, (RTPOLY *)rtg2, dl); case RTCIRCSTRINGTYPE: return rt_dist2d_circstring_circstring(ctx, (RTCIRCSTRING *)rtg1, (RTCIRCSTRING *)rtg2, dl); case RTCURVEPOLYTYPE: return rt_dist2d_circstring_curvepoly(ctx, (RTCIRCSTRING *)rtg1, (RTCURVEPOLY *)rtg2, dl); default: rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); } } case RTPOLYGONTYPE: { dl->twisted = -1; switch ( t2 ) { case RTPOINTTYPE: return rt_dist2d_point_poly(ctx, (RTPOINT *)rtg2, (RTPOLY *)rtg1, dl); case RTLINETYPE: return rt_dist2d_line_poly(ctx, (RTLINE *)rtg2, (RTPOLY *)rtg1, dl); case RTCIRCSTRINGTYPE: return rt_dist2d_circstring_poly(ctx, (RTCIRCSTRING *)rtg2, (RTPOLY *)rtg1, dl); case RTPOLYGONTYPE: dl->twisted = 1; return rt_dist2d_poly_poly(ctx, (RTPOLY *)rtg1, (RTPOLY *)rtg2, dl); case RTCURVEPOLYTYPE: dl->twisted = 1; return rt_dist2d_poly_curvepoly(ctx, (RTPOLY *)rtg1, (RTCURVEPOLY *)rtg2, dl); default: rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); } } case RTCURVEPOLYTYPE: { dl->twisted = (-1); switch ( t2 ) { case RTPOINTTYPE: return rt_dist2d_point_curvepoly(ctx, (RTPOINT *)rtg2, (RTCURVEPOLY *)rtg1, dl); case RTLINETYPE: return rt_dist2d_line_curvepoly(ctx, (RTLINE *)rtg2, (RTCURVEPOLY *)rtg1, dl); case RTPOLYGONTYPE: return rt_dist2d_poly_curvepoly(ctx, (RTPOLY *)rtg2, (RTCURVEPOLY *)rtg1, dl); case RTCIRCSTRINGTYPE: return rt_dist2d_circstring_curvepoly(ctx, (RTCIRCSTRING *)rtg2, (RTCURVEPOLY *)rtg1, dl); case RTCURVEPOLYTYPE: dl->twisted = 1; return rt_dist2d_curvepoly_curvepoly(ctx, (RTCURVEPOLY *)rtg1, (RTCURVEPOLY *)rtg2, dl); default: rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); } } default: { rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t1)); } } /*You shouldn't being able to get here*/ rterror(ctx, "unspecified error in function rt_dist2d_distribute_bruteforce"); return RT_FALSE; } /** We have to check for overlapping bboxes */ int rt_dist2d_check_overlap(const RTCTX *ctx, RTGEOM *rtg1,RTGEOM *rtg2) { RTDEBUG(2, "rt_dist2d_check_overlap is called"); if ( ! rtg1->bbox ) rtgeom_calculate_gbox(ctx, rtg1, rtg1->bbox); if ( ! rtg2->bbox ) rtgeom_calculate_gbox(ctx, rtg2, rtg2->bbox); /*Check if the geometries intersect. */ if ((rtg1->bbox->xmaxbbox->xmin||rtg1->bbox->xmin>rtg2->bbox->xmax||rtg1->bbox->ymaxbbox->ymin||rtg1->bbox->ymin>rtg2->bbox->ymax)) { RTDEBUG(3, "geometries bboxes did not overlap"); return RT_FALSE; } RTDEBUG(3, "geometries bboxes overlap"); return RT_TRUE; } /** Here the geometries are distributed for the new faster distance-calculations */ int rt_dist2d_distribute_fast(const RTCTX *ctx, RTGEOM *rtg1, RTGEOM *rtg2, DISTPTS *dl) { RTPOINTARRAY *pa1, *pa2; int type1 = rtg1->type; int type2 = rtg2->type; RTDEBUGF(2, "rt_dist2d_distribute_fast is called with typ1=%d, type2=%d", rtg1->type, rtg2->type); switch (type1) { case RTLINETYPE: pa1 = ((RTLINE *)rtg1)->points; break; case RTPOLYGONTYPE: pa1 = ((RTPOLY *)rtg1)->rings[0]; break; default: rterror(ctx, "Unsupported geometry1 type: %s", rttype_name(ctx, type1)); return RT_FALSE; } switch (type2) { case RTLINETYPE: pa2 = ((RTLINE *)rtg2)->points; break; case RTPOLYGONTYPE: pa2 = ((RTPOLY *)rtg2)->rings[0]; break; default: rterror(ctx, "Unsupported geometry2 type: %s", rttype_name(ctx, type1)); return RT_FALSE; } dl->twisted=1; return rt_dist2d_fast_ptarray_ptarray(ctx, pa1, pa2, dl, rtg1->bbox, rtg2->bbox); } /*------------------------------------------------------------------------------------------------------------ End of Preprocessing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Brute force functions The old way of calculating distances, now used for: 1) distances to points (because there shouldn't be anything to gain by the new way of doing it) 2) distances when subgeometries geometries bboxes overlaps --------------------------------------------------------------------------------------------------------------*/ /** point to point calculation */ int rt_dist2d_point_point(const RTCTX *ctx, RTPOINT *point1, RTPOINT *point2, DISTPTS *dl) { const RTPOINT2D *p1, *p2; p1 = rt_getPoint2d_cp(ctx, point1->point, 0); p2 = rt_getPoint2d_cp(ctx, point2->point, 0); return rt_dist2d_pt_pt(ctx, p1, p2, dl); } /** point to line calculation */ int rt_dist2d_point_line(const RTCTX *ctx, RTPOINT *point, RTLINE *line, DISTPTS *dl) { const RTPOINT2D *p; RTDEBUG(2, "rt_dist2d_point_line is called"); p = rt_getPoint2d_cp(ctx, point->point, 0); return rt_dist2d_pt_ptarray(ctx, p, line->points, dl); } int rt_dist2d_point_circstring(const RTCTX *ctx, RTPOINT *point, RTCIRCSTRING *circ, DISTPTS *dl) { const RTPOINT2D *p; p = rt_getPoint2d_cp(ctx, point->point, 0); return rt_dist2d_pt_ptarrayarc(ctx, p, circ->points, dl); } /** * 1. see if pt in outer boundary. if no, then treat the outer ring like a line * 2. if in the boundary, test to see if its in a hole. * if so, then return dist to hole, else return 0 (point in polygon) */ int rt_dist2d_point_poly(const RTCTX *ctx, RTPOINT *point, RTPOLY *poly, DISTPTS *dl) { const RTPOINT2D *p; int i; RTDEBUG(2, "rt_dist2d_point_poly called"); p = rt_getPoint2d_cp(ctx, point->point, 0); if (dl->mode == DIST_MAX) { RTDEBUG(3, "looking for maxdistance"); return rt_dist2d_pt_ptarray(ctx, p, poly->rings[0], dl); } /* Return distance to outer ring if not inside it */ if ( ptarray_contains_point(ctx, poly->rings[0], p) == RT_OUTSIDE ) { RTDEBUG(3, "first point not inside outer-ring"); return rt_dist2d_pt_ptarray(ctx, p, poly->rings[0], dl); } /* * Inside the outer ring. * Scan though each of the inner rings looking to * see if its inside. If not, distance==0. * Otherwise, distance = pt to ring distance */ for ( i = 1; i < poly->nrings; i++) { /* Inside a hole. Distance = pt -> ring */ if ( ptarray_contains_point(ctx, poly->rings[i], p) != RT_OUTSIDE ) { RTDEBUG(3, " inside an hole"); return rt_dist2d_pt_ptarray(ctx, p, poly->rings[i], dl); } } RTDEBUG(3, " inside the polygon"); if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = p->x; dl->p1.y = dl->p2.y = p->y; } return RT_TRUE; /* Is inside the polygon */ } int rt_dist2d_point_curvepoly(const RTCTX *ctx, RTPOINT *point, RTCURVEPOLY *poly, DISTPTS *dl) { const RTPOINT2D *p; int i; p = rt_getPoint2d_cp(ctx, point->point, 0); if (dl->mode == DIST_MAX) rterror(ctx, "rt_dist2d_point_curvepoly cannot calculate max distance"); /* Return distance to outer ring if not inside it */ if ( rtgeom_contains_point(ctx, poly->rings[0], p) == RT_OUTSIDE ) { return rt_dist2d_recursive(ctx, (RTGEOM*)point, poly->rings[0], dl); } /* * Inside the outer ring. * Scan though each of the inner rings looking to * see if its inside. If not, distance==0. * Otherwise, distance = pt to ring distance */ for ( i = 1; i < poly->nrings; i++) { /* Inside a hole. Distance = pt -> ring */ if ( rtgeom_contains_point(ctx, poly->rings[i], p) != RT_OUTSIDE ) { RTDEBUG(3, " inside a hole"); return rt_dist2d_recursive(ctx, (RTGEOM*)point, poly->rings[i], dl); } } RTDEBUG(3, " inside the polygon"); if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = p->x; dl->p1.y = dl->p2.y = p->y; } return RT_TRUE; /* Is inside the polygon */ } /** line to line calculation */ int rt_dist2d_line_line(const RTCTX *ctx, RTLINE *line1, RTLINE *line2, DISTPTS *dl) { RTPOINTARRAY *pa1 = line1->points; RTPOINTARRAY *pa2 = line2->points; RTDEBUG(2, "rt_dist2d_line_line is called"); return rt_dist2d_ptarray_ptarray(ctx, pa1, pa2, dl); } int rt_dist2d_line_circstring(const RTCTX *ctx, RTLINE *line1, RTCIRCSTRING *line2, DISTPTS *dl) { return rt_dist2d_ptarray_ptarrayarc(ctx, line1->points, line2->points, dl); } /** * line to polygon calculation * Brute force. * Test line-ring distance against each ring. * If there's an intersection (distance==0) then return 0 (crosses boundary). * Otherwise, test to see if any point is inside outer rings of polygon, * but not in inner rings. * If so, return 0 (line inside polygon), * otherwise return min distance to a ring (could be outside * polygon or inside a hole) */ int rt_dist2d_line_poly(const RTCTX *ctx, RTLINE *line, RTPOLY *poly, DISTPTS *dl) { const RTPOINT2D *pt; int i; RTDEBUGF(2, "rt_dist2d_line_poly called (%d rings)", poly->nrings); pt = rt_getPoint2d_cp(ctx, line->points, 0); if ( ptarray_contains_point(ctx, poly->rings[0], pt) == RT_OUTSIDE ) { return rt_dist2d_ptarray_ptarray(ctx, line->points, poly->rings[0], dl); } for (i=1; inrings; i++) { if (!rt_dist2d_ptarray_ptarray(ctx, line->points, poly->rings[i], dl)) return RT_FALSE; RTDEBUGF(3, " distance from ring %d: %f, mindist: %f", i, dl->distance, dl->tolerance); /* just a check if the answer is already given */ if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; } /* * No intersection, have to check if a point is * inside polygon */ pt = rt_getPoint2d_cp(ctx, line->points, 0); /* * Outside outer ring, so min distance to a ring * is the actual min distance if ( ! pt_in_ring_2d(ctx, &pt, poly->rings[0]) ) { return ; } */ /* * Its in the outer ring. * Have to check if its inside a hole */ for (i=1; inrings; i++) { if ( ptarray_contains_point(ctx, poly->rings[i], pt) != RT_OUTSIDE ) { /* * Its inside a hole, then the actual * distance is the min ring distance */ return RT_TRUE; } } if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; } return RT_TRUE; /* Not in hole, so inside polygon */ } int rt_dist2d_line_curvepoly(const RTCTX *ctx, RTLINE *line, RTCURVEPOLY *poly, DISTPTS *dl) { const RTPOINT2D *pt = rt_getPoint2d_cp(ctx, line->points, 0); int i; if ( rtgeom_contains_point(ctx, poly->rings[0], pt) == RT_OUTSIDE ) { return rt_dist2d_recursive(ctx, (RTGEOM*)line, poly->rings[0], dl); } for ( i = 1; i < poly->nrings; i++ ) { if ( ! rt_dist2d_recursive(ctx, (RTGEOM*)line, poly->rings[i], dl) ) return RT_FALSE; if ( dl->distance<=dl->tolerance && dl->mode == DIST_MIN ) return RT_TRUE; } for ( i=1; i < poly->nrings; i++ ) { if ( rtgeom_contains_point(ctx, poly->rings[i],pt) != RT_OUTSIDE ) { /* Its inside a hole, then the actual */ return RT_TRUE; } } if (dl->mode == DIST_MIN) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; } return RT_TRUE; /* Not in hole, so inside polygon */ } /** Function handling polygon to polygon calculation 1 if we are looking for maxdistance, just check the outer rings. 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings 3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole of poly1 4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole of poly2 5 If we have come all the way here we know that the first point of one of them is inside the other ones outer ring and not in holes so we check wich one is inside. */ int rt_dist2d_poly_poly(const RTCTX *ctx, RTPOLY *poly1, RTPOLY *poly2, DISTPTS *dl) { const RTPOINT2D *pt; int i; RTDEBUG(2, "rt_dist2d_poly_poly called"); /*1 if we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) { return rt_dist2d_ptarray_ptarray(ctx, poly1->rings[0], poly2->rings[0], dl); } /* 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings here it would be possible to handle the information about wich one is inside wich one and only search for the smaller ones in the bigger ones holes.*/ pt = rt_getPoint2d_cp(ctx, poly1->rings[0], 0); if ( ptarray_contains_point(ctx, poly2->rings[0], pt) == RT_OUTSIDE ) { pt = rt_getPoint2d_cp(ctx, poly2->rings[0], 0); if ( ptarray_contains_point(ctx, poly1->rings[0], pt) == RT_OUTSIDE ) { return rt_dist2d_ptarray_ptarray(ctx, poly1->rings[0], poly2->rings[0], dl); } } /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole of poly1*/ pt = rt_getPoint2d_cp(ctx, poly2->rings[0], 0); for (i=1; inrings; i++) { /* Inside a hole */ if ( ptarray_contains_point(ctx, poly1->rings[i], pt) != RT_OUTSIDE ) { return rt_dist2d_ptarray_ptarray(ctx, poly1->rings[i], poly2->rings[0], dl); } } /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole of poly2*/ pt = rt_getPoint2d_cp(ctx, poly1->rings[0], 0); for (i=1; inrings; i++) { /* Inside a hole */ if ( ptarray_contains_point(ctx, poly2->rings[i], pt) != RT_OUTSIDE ) { return rt_dist2d_ptarray_ptarray(ctx, poly1->rings[0], poly2->rings[i], dl); } } /*5 If we have come all the way here we know that the first point of one of them is inside the other ones outer ring and not in holes so we check wich one is inside.*/ pt = rt_getPoint2d_cp(ctx, poly1->rings[0], 0); if ( ptarray_contains_point(ctx, poly2->rings[0], pt) != RT_OUTSIDE ) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return RT_TRUE; } pt = rt_getPoint2d_cp(ctx, poly2->rings[0], 0); if ( ptarray_contains_point(ctx, poly1->rings[0], pt) != RT_OUTSIDE ) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return RT_TRUE; } rterror(ctx, "Unspecified error in function rt_dist2d_poly_poly"); return RT_FALSE; } int rt_dist2d_poly_curvepoly(const RTCTX *ctx, RTPOLY *poly1, RTCURVEPOLY *curvepoly2, DISTPTS *dl) { RTCURVEPOLY *curvepoly1 = rtcurvepoly_construct_from_rtpoly(ctx, poly1); int rv = rt_dist2d_curvepoly_curvepoly(ctx, curvepoly1, curvepoly2, dl); rtgeom_free(ctx, (RTGEOM*)curvepoly1); return rv; } int rt_dist2d_circstring_poly(const RTCTX *ctx, RTCIRCSTRING *circ, RTPOLY *poly, DISTPTS *dl) { RTCURVEPOLY *curvepoly = rtcurvepoly_construct_from_rtpoly(ctx, poly); int rv = rt_dist2d_line_curvepoly(ctx, (RTLINE*)circ, curvepoly, dl); rtgeom_free(ctx, (RTGEOM*)curvepoly); return rv; } int rt_dist2d_circstring_curvepoly(const RTCTX *ctx, RTCIRCSTRING *circ, RTCURVEPOLY *poly, DISTPTS *dl) { return rt_dist2d_line_curvepoly(ctx, (RTLINE*)circ, poly, dl); } int rt_dist2d_circstring_circstring(const RTCTX *ctx, RTCIRCSTRING *line1, RTCIRCSTRING *line2, DISTPTS *dl) { return rt_dist2d_ptarrayarc_ptarrayarc(ctx, line1->points, line2->points, dl); } static const RTPOINT2D * rt_curvering_getfirstpoint2d_cp(const RTCTX *ctx, RTGEOM *geom) { switch( geom->type ) { case RTLINETYPE: return rt_getPoint2d_cp(ctx, ((RTLINE*)geom)->points, 0); case RTCIRCSTRINGTYPE: return rt_getPoint2d_cp(ctx, ((RTCIRCSTRING*)geom)->points, 0); case RTCOMPOUNDTYPE: { RTCOMPOUND *comp = (RTCOMPOUND*)geom; RTLINE *line = (RTLINE*)(comp->geoms[0]); return rt_getPoint2d_cp(ctx, line->points, 0); } default: rterror(ctx, "rt_curvering_getfirstpoint2d_cp: unknown type"); } return NULL; } int rt_dist2d_curvepoly_curvepoly(const RTCTX *ctx, RTCURVEPOLY *poly1, RTCURVEPOLY *poly2, DISTPTS *dl) { const RTPOINT2D *pt; int i; RTDEBUG(2, "rt_dist2d_curvepoly_curvepoly called"); /*1 if we are looking for maxdistance, just check the outer rings.*/ if (dl->mode == DIST_MAX) { return rt_dist2d_recursive(ctx, poly1->rings[0], poly2->rings[0], dl); } /* 2 check if poly1 has first point outside poly2 and vice versa, if so, just check outer rings here it would be possible to handle the information about wich one is inside wich one and only search for the smaller ones in the bigger ones holes.*/ pt = rt_curvering_getfirstpoint2d_cp(ctx, poly1->rings[0]); if ( rtgeom_contains_point(ctx, poly2->rings[0], pt) == RT_OUTSIDE ) { pt = rt_curvering_getfirstpoint2d_cp(ctx, poly2->rings[0]); if ( rtgeom_contains_point(ctx, poly1->rings[0], pt) == RT_OUTSIDE ) { return rt_dist2d_recursive(ctx, poly1->rings[0], poly2->rings[0], dl); } } /*3 check if first point of poly2 is in a hole of poly1. If so check outer ring of poly2 against that hole of poly1*/ pt = rt_curvering_getfirstpoint2d_cp(ctx, poly2->rings[0]); for (i = 1; i < poly1->nrings; i++) { /* Inside a hole */ if ( rtgeom_contains_point(ctx, poly1->rings[i], pt) != RT_OUTSIDE ) { return rt_dist2d_recursive(ctx, poly1->rings[i], poly2->rings[0], dl); } } /*4 check if first point of poly1 is in a hole of poly2. If so check outer ring of poly1 against that hole of poly2*/ pt = rt_curvering_getfirstpoint2d_cp(ctx, poly1->rings[0]); for (i = 1; i < poly2->nrings; i++) { /* Inside a hole */ if ( rtgeom_contains_point(ctx, poly2->rings[i], pt) != RT_OUTSIDE ) { return rt_dist2d_recursive(ctx, poly1->rings[0], poly2->rings[i], dl); } } /*5 If we have come all the way here we know that the first point of one of them is inside the other ones outer ring and not in holes so we check wich one is inside.*/ pt = rt_curvering_getfirstpoint2d_cp(ctx, poly1->rings[0]); if ( rtgeom_contains_point(ctx, poly2->rings[0], pt) != RT_OUTSIDE ) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return RT_TRUE; } pt = rt_curvering_getfirstpoint2d_cp(ctx, poly2->rings[0]); if ( rtgeom_contains_point(ctx, poly1->rings[0], pt) != RT_OUTSIDE ) { dl->distance = 0.0; dl->p1.x = dl->p2.x = pt->x; dl->p1.y = dl->p2.y = pt->y; return RT_TRUE; } rterror(ctx, "Unspecified error in function rt_dist2d_curvepoly_curvepoly"); return RT_FALSE; } /** * search all the segments of pointarray to see which one is closest to p1 * Returns minimum distance between point and pointarray */ int rt_dist2d_pt_ptarray(const RTCTX *ctx, const RTPOINT2D *p, RTPOINTARRAY *pa,DISTPTS *dl) { int t; const RTPOINT2D *start, *end; int twist = dl->twisted; RTDEBUG(2, "rt_dist2d_pt_ptarray is called"); start = rt_getPoint2d_cp(ctx, pa, 0); if ( !rt_dist2d_pt_pt(ctx, p, start, dl) ) return RT_FALSE; for (t=1; tnpoints; t++) { dl->twisted=twist; end = rt_getPoint2d_cp(ctx, pa, t); if (!rt_dist2d_pt_seg(ctx, p, start, end, dl)) return RT_FALSE; if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ start = end; } return RT_TRUE; } /** * Search all the arcs of pointarray to see which one is closest to p1 * Returns minimum distance between point and arc pointarray. */ int rt_dist2d_pt_ptarrayarc(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *pa, DISTPTS *dl) { int t; const RTPOINT2D *A1; const RTPOINT2D *A2; const RTPOINT2D *A3; int twist = dl->twisted; RTDEBUG(2, "rt_dist2d_pt_ptarrayarc is called"); if ( pa->npoints % 2 == 0 || pa->npoints < 3 ) { rterror(ctx, "rt_dist2d_pt_ptarrayarc called with non-arc input"); return RT_FALSE; } if (dl->mode == DIST_MAX) { rterror(ctx, "rt_dist2d_pt_ptarrayarc does not currently support DIST_MAX mode"); return RT_FALSE; } A1 = rt_getPoint2d_cp(ctx, pa, 0); if ( ! rt_dist2d_pt_pt(ctx, p, A1, dl) ) return RT_FALSE; for ( t=1; tnpoints; t += 2 ) { dl->twisted = twist; A2 = rt_getPoint2d_cp(ctx, pa, t); A3 = rt_getPoint2d_cp(ctx, pa, t+1); if ( rt_dist2d_pt_arc(ctx, p, A1, A2, A3, dl) == RT_FALSE ) return RT_FALSE; if ( dl->distance <= dl->tolerance && dl->mode == DIST_MIN ) return RT_TRUE; /*just a check if the answer is already given*/ A1 = A3; } return RT_TRUE; } /** * test each segment of l1 against each segment of l2. */ int rt_dist2d_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,DISTPTS *dl) { int t,u; const RTPOINT2D *start, *end; const RTPOINT2D *start2, *end2; int twist = dl->twisted; RTDEBUGF(2, "rt_dist2d_ptarray_ptarray called (points: %d-%d)",l1->npoints, l2->npoints); if (dl->mode == DIST_MAX)/*If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have to be between two vertexes*/ { for (t=0; tnpoints; t++) /*for each segment in L1 */ { start = rt_getPoint2d_cp(ctx, l1, t); for (u=0; unpoints; u++) /*for each segment in L2 */ { start2 = rt_getPoint2d_cp(ctx, l2, u); rt_dist2d_pt_pt(ctx, start, start2, dl); RTDEBUGF(4, "maxdist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance); RTDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); } } } else { start = rt_getPoint2d_cp(ctx, l1, 0); for (t=1; tnpoints; t++) /*for each segment in L1 */ { end = rt_getPoint2d_cp(ctx, l1, t); start2 = rt_getPoint2d_cp(ctx, l2, 0); for (u=1; unpoints; u++) /*for each segment in L2 */ { end2 = rt_getPoint2d_cp(ctx, l2, u); dl->twisted=twist; rt_dist2d_seg_seg(ctx, start, end, start2, end2, dl); RTDEBUGF(4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance); RTDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ start2 = end2; } start = end; } } return RT_TRUE; } /** * Test each segment of pa against each arc of pb for distance. */ int rt_dist2d_ptarray_ptarrayarc(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINTARRAY *pb, DISTPTS *dl) { int t, u; const RTPOINT2D *A1; const RTPOINT2D *A2; const RTPOINT2D *B1; const RTPOINT2D *B2; const RTPOINT2D *B3; int twist = dl->twisted; RTDEBUGF(2, "rt_dist2d_ptarray_ptarrayarc called (points: %d-%d)",pa->npoints, pb->npoints); if ( pb->npoints % 2 == 0 || pb->npoints < 3 ) { rterror(ctx, "rt_dist2d_ptarray_ptarrayarc called with non-arc input"); return RT_FALSE; } if ( dl->mode == DIST_MAX ) { rterror(ctx, "rt_dist2d_ptarray_ptarrayarc does not currently support DIST_MAX mode"); return RT_FALSE; } else { A1 = rt_getPoint2d_cp(ctx, pa, 0); for ( t=1; t < pa->npoints; t++ ) /* For each segment in pa */ { A2 = rt_getPoint2d_cp(ctx, pa, t); B1 = rt_getPoint2d_cp(ctx, pb, 0); for ( u=1; u < pb->npoints; u += 2 ) /* For each arc in pb */ { B2 = rt_getPoint2d_cp(ctx, pb, u); B3 = rt_getPoint2d_cp(ctx, pb, u+1); dl->twisted = twist; rt_dist2d_seg_arc(ctx, A1, A2, B1, B2, B3, dl); /* If we've found a distance within tolerance, we're done */ if ( dl->distance <= dl->tolerance && dl->mode == DIST_MIN ) return RT_TRUE; B1 = B3; } A1 = A2; } } return RT_TRUE; } /** * Test each arc of pa against each arc of pb for distance. */ int rt_dist2d_ptarrayarc_ptarrayarc(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINTARRAY *pb, DISTPTS *dl) { int t, u; const RTPOINT2D *A1; const RTPOINT2D *A2; const RTPOINT2D *A3; const RTPOINT2D *B1; const RTPOINT2D *B2; const RTPOINT2D *B3; int twist = dl->twisted; RTDEBUGF(2, "rt_dist2d_ptarrayarc_ptarrayarc called (points: %d-%d)",pa->npoints, pb->npoints); if (dl->mode == DIST_MAX) { rterror(ctx, "rt_dist2d_ptarrayarc_ptarrayarc does not currently support DIST_MAX mode"); return RT_FALSE; } else { A1 = rt_getPoint2d_cp(ctx, pa, 0); for ( t=1; t < pa->npoints; t += 2 ) /* For each segment in pa */ { A2 = rt_getPoint2d_cp(ctx, pa, t); A3 = rt_getPoint2d_cp(ctx, pa, t+1); B1 = rt_getPoint2d_cp(ctx, pb, 0); for ( u=1; u < pb->npoints; u += 2 ) /* For each arc in pb */ { B2 = rt_getPoint2d_cp(ctx, pb, u); B3 = rt_getPoint2d_cp(ctx, pb, u+1); dl->twisted = twist; rt_dist2d_arc_arc(ctx, A1, A2, A3, B1, B2, B3, dl); /* If we've found a distance within tolerance, we're done */ if ( dl->distance <= dl->tolerance && dl->mode == DIST_MIN ) return RT_TRUE; B1 = B3; } A1 = A3; } } return RT_TRUE; } /** * Calculate the shortest distance between an arc and an edge. * Line/circle approach from http://stackoverflow.com/questions/1073336/circle-line-collision-detection */ int rt_dist2d_seg_arc(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *B1, const RTPOINT2D *B2, const RTPOINT2D *B3, DISTPTS *dl) { RTPOINT2D C; /* center of arc circle */ double radius_C; /* radius of arc circle */ RTPOINT2D D; /* point on A closest to C */ double dist_C_D; /* distance from C to D */ int pt_in_arc, pt_in_seg; DISTPTS dltmp; /* Bail out on crazy modes */ if ( dl->mode < 0 ) rterror(ctx, "rt_dist2d_seg_arc does not support maxdistance mode"); /* What if the "arc" is a point? */ if ( rt_arc_is_pt(ctx, B1, B2, B3) ) return rt_dist2d_pt_seg(ctx, B1, A1, A2, dl); /* Calculate center and radius of the circle. */ radius_C = rt_arc_center(ctx, B1, B2, B3, &C); /* This "arc" is actually a line (B2 is colinear with B1,B3) */ if ( radius_C < 0.0 ) return rt_dist2d_seg_seg(ctx, A1, A2, B1, B3, dl); /* Calculate distance between the line and circle center */ rt_dist2d_distpts_init(ctx, &dltmp, DIST_MIN); if ( rt_dist2d_pt_seg(ctx, &C, A1, A2, &dltmp) == RT_FALSE ) rterror(ctx, "rt_dist2d_pt_seg failed in rt_dist2d_seg_arc"); D = dltmp.p1; dist_C_D = dltmp.distance; /* Line intersects circle, maybe arc intersects edge? */ /* If so, that's the closest point. */ /* If not, the closest point is one of the end points of A */ if ( dist_C_D < radius_C ) { double length_A; /* length of the segment A */ RTPOINT2D E, F; /* points of interection of edge A and circle(B) */ double dist_D_EF; /* distance from D to E or F (same distance both ways) */ dist_D_EF = sqrt(radius_C*radius_C - dist_C_D*dist_C_D); length_A = sqrt((A2->x-A1->x)*(A2->x-A1->x)+(A2->y-A1->y)*(A2->y-A1->y)); /* Point of intersection E */ E.x = D.x - (A2->x-A1->x) * dist_D_EF / length_A; E.y = D.y - (A2->y-A1->y) * dist_D_EF / length_A; /* Point of intersection F */ F.x = D.x + (A2->x-A1->x) * dist_D_EF / length_A; F.y = D.y + (A2->y-A1->y) * dist_D_EF / length_A; /* If E is within A and within B then it's an interesction point */ pt_in_arc = rt_pt_in_arc(ctx, &E, B1, B2, B3); pt_in_seg = rt_pt_in_seg(ctx, &E, A1, A2); if ( pt_in_arc && pt_in_seg ) { dl->distance = 0.0; dl->p1 = E; dl->p2 = E; return RT_TRUE; } /* If F is within A and within B then it's an interesction point */ pt_in_arc = rt_pt_in_arc(ctx, &F, B1, B2, B3); pt_in_seg = rt_pt_in_seg(ctx, &F, A1, A2); if ( pt_in_arc && pt_in_seg ) { dl->distance = 0.0; dl->p1 = F; dl->p2 = F; return RT_TRUE; } } /* Line grazes circle, maybe arc intersects edge? */ /* If so, grazing point is the closest point. */ /* If not, the closest point is one of the end points of A */ else if ( dist_C_D == radius_C ) { /* Closest point D is also the point of grazing */ pt_in_arc = rt_pt_in_arc(ctx, &D, B1, B2, B3); pt_in_seg = rt_pt_in_seg(ctx, &D, A1, A2); /* Is D contained in both A and B? */ if ( pt_in_arc && pt_in_seg ) { dl->distance = 0.0; dl->p1 = D; dl->p2 = D; return RT_TRUE; } } /* Line misses circle. */ /* If closest point to A on circle is within B, then that's the closest */ /* Otherwise, the closest point will be an end point of A */ else { RTPOINT2D G; /* Point on circle closest to A */ G.x = C.x + (D.x-C.x) * radius_C / dist_C_D; G.y = C.y + (D.y-C.y) * radius_C / dist_C_D; pt_in_arc = rt_pt_in_arc(ctx, &G, B1, B2, B3); pt_in_seg = rt_pt_in_seg(ctx, &D, A1, A2); /* Closest point is on the interior of A and B */ if ( pt_in_arc && pt_in_seg ) return rt_dist2d_pt_pt(ctx, &D, &G, dl); } /* Now we test the many combinations of end points with either */ /* arcs or edges. Each previous check determined if the closest */ /* potential point was within the arc/segment inscribed on the */ /* line/circle holding the arc/segment. */ /* Closest point is in the arc, but not in the segment, so */ /* one of the segment end points must be the closest. */ if ( pt_in_arc & ! pt_in_seg ) { rt_dist2d_pt_arc(ctx, A1, B1, B2, B3, dl); rt_dist2d_pt_arc(ctx, A2, B1, B2, B3, dl); return RT_TRUE; } /* or, one of the arc end points is the closest */ else if ( pt_in_seg && ! pt_in_arc ) { rt_dist2d_pt_seg(ctx, B1, A1, A2, dl); rt_dist2d_pt_seg(ctx, B3, A1, A2, dl); return RT_TRUE; } /* Finally, one of the end-point to end-point combos is the closest. */ else { rt_dist2d_pt_pt(ctx, A1, B1, dl); rt_dist2d_pt_pt(ctx, A1, B3, dl); rt_dist2d_pt_pt(ctx, A2, B1, dl); rt_dist2d_pt_pt(ctx, A2, B3, dl); return RT_TRUE; } return RT_FALSE; } int rt_dist2d_pt_arc(const RTCTX *ctx, const RTPOINT2D* P, const RTPOINT2D* A1, const RTPOINT2D* A2, const RTPOINT2D* A3, DISTPTS* dl) { double radius_A, d; RTPOINT2D C; /* center of circle defined by arc A */ RTPOINT2D X; /* point circle(A) where line from C to P crosses */ if ( dl->mode < 0 ) rterror(ctx, "rt_dist2d_pt_arc does not support maxdistance mode"); /* What if the arc is a point? */ if ( rt_arc_is_pt(ctx, A1, A2, A3) ) return rt_dist2d_pt_pt(ctx, P, A1, dl); /* Calculate centers and radii of circles. */ radius_A = rt_arc_center(ctx, A1, A2, A3, &C); /* This "arc" is actually a line (A2 is colinear with A1,A3) */ if ( radius_A < 0.0 ) return rt_dist2d_pt_seg(ctx, P, A1, A3, dl); /* Distance from point to center */ d = distance2d_pt_pt(ctx, &C, P); /* X is the point on the circle where the line from P to C crosses */ X.x = C.x + (P->x - C.x) * radius_A / d; X.y = C.y + (P->y - C.y) * radius_A / d; /* Is crossing point inside the arc? Or arc is actually circle? */ if ( p2d_same(ctx, A1, A3) || rt_pt_in_arc(ctx, &X, A1, A2, A3) ) { rt_dist2d_pt_pt(ctx, P, &X, dl); } else { /* Distance is the minimum of the distances to the arc end points */ rt_dist2d_pt_pt(ctx, A1, P, dl); rt_dist2d_pt_pt(ctx, A3, P, dl); } return RT_TRUE; } int rt_dist2d_arc_arc(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, const RTPOINT2D *B1, const RTPOINT2D *B2, const RTPOINT2D *B3, DISTPTS *dl) { RTPOINT2D CA, CB; /* Center points of arcs A and B */ double radius_A, radius_B, d; /* Radii of arcs A and B */ RTPOINT2D P; /* Temporary point P */ RTPOINT2D D; /* Mid-point between the centers CA and CB */ int pt_in_arc_A, pt_in_arc_B; /* Test whether potential intersection point is within the arc */ if ( dl->mode != DIST_MIN ) rterror(ctx, "rt_dist2d_arc_arc only supports mindistance"); /* TODO: Handle case where arc is closed circle (A1 = A3) */ /* What if one or both of our "arcs" is actually a point? */ if ( rt_arc_is_pt(ctx, B1, B2, B3) && rt_arc_is_pt(ctx, A1, A2, A3) ) return rt_dist2d_pt_pt(ctx, B1, A1, dl); else if ( rt_arc_is_pt(ctx, B1, B2, B3) ) return rt_dist2d_pt_arc(ctx, B1, A1, A2, A3, dl); else if ( rt_arc_is_pt(ctx, A1, A2, A3) ) return rt_dist2d_pt_arc(ctx, A1, B1, B2, B3, dl); /* Calculate centers and radii of circles. */ radius_A = rt_arc_center(ctx, A1, A2, A3, &CA); radius_B = rt_arc_center(ctx, B1, B2, B3, &CB); /* Two co-linear arcs?!? That's two segments. */ if ( radius_A < 0 && radius_B < 0 ) return rt_dist2d_seg_seg(ctx, A1, A3, B1, B3, dl); /* A is co-linear, delegate to rt_dist_seg_arc here. */ if ( radius_A < 0 ) return rt_dist2d_seg_arc(ctx, A1, A3, B1, B2, B3, dl); /* B is co-linear, delegate to rt_dist_seg_arc here. */ if ( radius_B < 0 ) return rt_dist2d_seg_arc(ctx, B1, B3, A1, A2, A3, dl); /* Make sure that arc "A" has the bigger radius */ if ( radius_B > radius_A ) { const RTPOINT2D *tmp; tmp = B1; B1 = A1; A1 = tmp; tmp = B2; B2 = A2; A2 = tmp; tmp = B3; B3 = A3; A3 = tmp; P = CB; CB = CA; CA = P; d = radius_B; radius_B = radius_A; radius_A = d; } /* Center-center distance */ d = distance2d_pt_pt(ctx, &CA, &CB); /* Equal circles. Arcs may intersect at multiple points, or at none! */ if ( FP_EQUALS(d, 0.0) && FP_EQUALS(radius_A, radius_B) ) { rterror(ctx, "rt_dist2d_arc_arc can't handle cojoint circles, uh oh"); } /* Circles touch at a point. Is that point within the arcs? */ if ( d == (radius_A + radius_B) ) { D.x = CA.x + (CB.x - CA.x) * radius_A / d; D.y = CA.y + (CB.y - CA.y) * radius_A / d; pt_in_arc_A = rt_pt_in_arc(ctx, &D, A1, A2, A3); pt_in_arc_B = rt_pt_in_arc(ctx, &D, B1, B2, B3); /* Arcs do touch at D, return it */ if ( pt_in_arc_A && pt_in_arc_B ) { dl->distance = 0.0; dl->p1 = D; dl->p2 = D; return RT_TRUE; } } /* Disjoint or contained circles don't intersect. Closest point may be on */ /* the line joining CA to CB. */ else if ( d > (radius_A + radius_B) /* Disjoint */ || d < (radius_A - radius_B) /* Contained */ ) { RTPOINT2D XA, XB; /* Points where the line from CA to CB cross their circle bounds */ /* Calculate hypothetical nearest points, the places on the */ /* two circles where the center-center line crosses. If both */ /* arcs contain their hypothetical points, that's the crossing distance */ XA.x = CA.x + (CB.x - CA.x) * radius_A / d; XA.y = CA.y + (CB.y - CA.y) * radius_A / d; XB.x = CB.x + (CA.x - CB.x) * radius_B / d; XB.y = CB.y + (CA.y - CB.y) * radius_B / d; pt_in_arc_A = rt_pt_in_arc(ctx, &XA, A1, A2, A3); pt_in_arc_B = rt_pt_in_arc(ctx, &XB, B1, B2, B3); /* If the nearest points are both within the arcs, that's our answer */ /* the shortest distance is at the nearest points */ if ( pt_in_arc_A && pt_in_arc_B ) { return rt_dist2d_pt_pt(ctx, &XA, &XB, dl); } } /* Circles cross at two points, are either of those points in both arcs? */ /* http://paulbourke.net/geometry/2circle/ */ else if ( d < (radius_A + radius_B) ) { RTPOINT2D E, F; /* Points where circle(A) and circle(B) cross */ /* Distance from CA to D */ double a = (radius_A*radius_A - radius_B*radius_B + d*d) / (2*d); /* Distance from D to E or F */ double h = sqrt(radius_A*radius_A - a*a); /* Location of D */ D.x = CA.x + (CB.x - CA.x) * a / d; D.y = CA.y + (CB.y - CA.y) * a / d; /* Start from D and project h units perpendicular to CA-D to get E */ E.x = D.x + (D.y - CA.y) * h / a; E.y = D.y + (D.x - CA.x) * h / a; /* Crossing point E contained in arcs? */ pt_in_arc_A = rt_pt_in_arc(ctx, &E, A1, A2, A3); pt_in_arc_B = rt_pt_in_arc(ctx, &E, B1, B2, B3); if ( pt_in_arc_A && pt_in_arc_B ) { dl->p1 = dl->p2 = E; dl->distance = 0.0; return RT_TRUE; } /* Start from D and project h units perpendicular to CA-D to get F */ F.x = D.x - (D.y - CA.y) * h / a; F.y = D.y - (D.x - CA.x) * h / a; /* Crossing point F contained in arcs? */ pt_in_arc_A = rt_pt_in_arc(ctx, &F, A1, A2, A3); pt_in_arc_B = rt_pt_in_arc(ctx, &F, B1, B2, B3); if ( pt_in_arc_A && pt_in_arc_B ) { dl->p1 = dl->p2 = F; dl->distance = 0.0; return RT_TRUE; } } else { rterror(ctx, "rt_dist2d_arc_arc: arcs neither touch, intersect nor are disjoint! INCONCEIVABLE!"); return RT_FALSE; } /* Closest point is in the arc A, but not in the arc B, so */ /* one of the B end points must be the closest. */ if ( pt_in_arc_A & ! pt_in_arc_B ) { rt_dist2d_pt_arc(ctx, B1, A1, A2, A3, dl); rt_dist2d_pt_arc(ctx, B3, A1, A2, A3, dl); return RT_TRUE; } /* Closest point is in the arc B, but not in the arc A, so */ /* one of the A end points must be the closest. */ else if ( pt_in_arc_B && ! pt_in_arc_A ) { rt_dist2d_pt_arc(ctx, A1, B1, B2, B3, dl); rt_dist2d_pt_arc(ctx, A3, B1, B2, B3, dl); return RT_TRUE; } /* Finally, one of the end-point to end-point combos is the closest. */ else { rt_dist2d_pt_pt(ctx, A1, B1, dl); rt_dist2d_pt_pt(ctx, A1, B3, dl); rt_dist2d_pt_pt(ctx, A2, B1, dl); rt_dist2d_pt_pt(ctx, A2, B3, dl); return RT_TRUE; } return RT_TRUE; } /** Finds the shortest distance between two segments. This function is changed so it is not doing any comparasion of distance but just sending every possible combination further to rt_dist2d_pt_seg */ int rt_dist2d_seg_seg(const RTCTX *ctx, const RTPOINT2D *A, const RTPOINT2D *B, const RTPOINT2D *C, const RTPOINT2D *D, DISTPTS *dl) { double s_top, s_bot,s; double r_top, r_bot,r; RTDEBUGF(2, "rt_dist2d_seg_seg [%g,%g]->[%g,%g] by [%g,%g]->[%g,%g]", A->x,A->y,B->x,B->y, C->x,C->y, D->x, D->y); /*A and B are the same point */ if ( ( A->x == B->x) && (A->y == B->y) ) { return rt_dist2d_pt_seg(ctx, A,C,D,dl); } /*U and V are the same point */ if ( ( C->x == D->x) && (C->y == D->y) ) { dl->twisted= ((dl->twisted) * (-1)); return rt_dist2d_pt_seg(ctx, D,A,B,dl); } /* AB and CD are line segments */ /* from comp.graphics.algo Solving the above for r and s yields (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) r = ----------------------------- (eqn 1) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- (eqn 2) (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) Let P be the position vector of the intersection point, then P=A+r(B-A) or Px=Ax+r(Bx-Ax) Py=Ay+r(By-Ay) By examining the values of r & s, you can also determine some other limiting conditions: If 0<=r<=1 & 0<=s<=1, intersection exists r<0 or r>1 or s<0 or s>1 line segments do not intersect If the denominator in eqn 1 is zero, AB & CD are parallel If the numerator in eqn 1 is also zero, AB & CD are collinear. */ r_top = (A->y-C->y)*(D->x-C->x) - (A->x-C->x)*(D->y-C->y); r_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x); s_top = (A->y-C->y)*(B->x-A->x) - (A->x-C->x)*(B->y-A->y); s_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x); if ( (r_bot==0) || (s_bot == 0) ) { if ((rt_dist2d_pt_seg(ctx, A,C,D,dl)) && (rt_dist2d_pt_seg(ctx, B,C,D,dl))) { dl->twisted= ((dl->twisted) * (-1)); /*here we change the order of inputted geometrys and that we notice by changing sign on dl->twisted*/ return ((rt_dist2d_pt_seg(ctx, C,A,B,dl)) && (rt_dist2d_pt_seg(ctx, D,A,B,dl))); /*if all is successful we return true*/ } else { return RT_FALSE; /* if any of the calls to rt_dist2d_pt_seg goes wrong we return false*/ } } s = s_top/s_bot; r= r_top/r_bot; if (((r<0) || (r>1) || (s<0) || (s>1)) || (dl->mode == DIST_MAX)) { if ((rt_dist2d_pt_seg(ctx, A,C,D,dl)) && (rt_dist2d_pt_seg(ctx, B,C,D,dl))) { dl->twisted= ((dl->twisted) * (-1)); /*here we change the order of inputted geometrys and that we notice by changing sign on dl->twisted*/ return ((rt_dist2d_pt_seg(ctx, C,A,B,dl)) && (rt_dist2d_pt_seg(ctx, D,A,B,dl))); /*if all is successful we return true*/ } else { return RT_FALSE; /* if any of the calls to rt_dist2d_pt_seg goes wrong we return false*/ } } else { if (dl->mode == DIST_MIN) /*If there is intersection we identify the intersection point and return it but only if we are looking for mindistance*/ { RTPOINT2D theP; if (((A->x==C->x)&&(A->y==C->y))||((A->x==D->x)&&(A->y==D->y))) { theP.x = A->x; theP.y = A->y; } else if (((B->x==C->x)&&(B->y==C->y))||((B->x==D->x)&&(B->y==D->y))) { theP.x = B->x; theP.y = B->y; } else { theP.x = A->x+r*(B->x-A->x); theP.y = A->y+r*(B->y-A->y); } dl->distance=0.0; dl->p1=theP; dl->p2=theP; } return RT_TRUE; } rterror(ctx, "unspecified error in function rt_dist2d_seg_seg"); return RT_FALSE; /*If we have come here something is wrong*/ } /*------------------------------------------------------------------------------------------------------------ End of Brute force functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ New faster distance calculations --------------------------------------------------------------------------------------------------------------*/ /** The new faster calculation comparing pointarray to another pointarray the arrays can come from both polygons and linestrings. The naming is not good but comes from that it compares a chosen selection of the points not all of them */ int rt_dist2d_fast_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,DISTPTS *dl, RTGBOX *box1, RTGBOX *box2) { /*here we define two lists to hold our calculated "z"-values and the order number in the geometry*/ double k, thevalue; float deltaX, deltaY, c1m, c2m; RTPOINT2D c1, c2; const RTPOINT2D *theP; float min1X, max1X, max1Y, min1Y,min2X, max2X, max2Y, min2Y; int t; int n1 = l1->npoints; int n2 = l2->npoints; LISTSTRUCT *list1, *list2; list1 = (LISTSTRUCT*)rtalloc(ctx, sizeof(LISTSTRUCT)*n1); list2 = (LISTSTRUCT*)rtalloc(ctx, sizeof(LISTSTRUCT)*n2); RTDEBUG(2, "rt_dist2d_fast_ptarray_ptarray is called"); max1X = box1->xmax; min1X = box1->xmin; max1Y = box1->ymax; min1Y = box1->ymin; max2X = box2->xmax; min2X = box2->xmin; max2Y = box2->ymax; min2Y = box2->ymin; /*we want the center of the bboxes, and calculate the slope between the centerpoints*/ c1.x = min1X + (max1X-min1X)/2; c1.y = min1Y + (max1Y-min1Y)/2; c2.x = min2X + (max2X-min2X)/2; c2.y = min2Y + (max2Y-min2Y)/2; deltaX=(c2.x-c1.x); deltaY=(c2.y-c1.y); /*Here we calculate where the line perpendicular to the center-center line crosses the axes for each vertex if the center-center line is vertical the perpendicular line will be horizontal and we find it's crossing the Y-axes with z = y-kx */ if ((deltaX*deltaX)<(deltaY*deltaY)) /*North or South*/ { k = -deltaX/deltaY; for (t=0; ty - (k * theP->x); list1[t].themeasure=thevalue; list1[t].pnr=t; } for (t=0; ty - (k * theP->x); list2[t].themeasure=thevalue; list2[t].pnr=t; } c1m = c1.y-(k*c1.x); c2m = c2.y-(k*c2.x); } /*if the center-center line is horizontal the perpendicular line will be vertical. To eliminate problems with deviding by zero we are here mirroring the coordinate-system and we find it's crossing the X-axes with z = x-(1/k)y */ else /*West or East*/ { k = -deltaY/deltaX; for (t=0; tx - (k * theP->y); list1[t].themeasure=thevalue; list1[t].pnr=t; /* rtnotice(ctx, "l1 %d, measure=%f",t,thevalue ); */ } for (t=0; tx - (k * theP->y); list2[t].themeasure=thevalue; list2[t].pnr=t; /* rtnotice(ctx, "l2 %d, measure=%f",t,thevalue ); */ } c1m = c1.x-(k*c1.y); c2m = c2.x-(k*c2.y); } /*we sort our lists by the calculated values*/ qsort(list1, n1, sizeof(LISTSTRUCT), struct_cmp_by_measure); qsort(list2, n2, sizeof(LISTSTRUCT), struct_cmp_by_measure); if (c1m < c2m) { if (!rt_dist2d_pre_seg_seg(ctx, l1,l2,list1,list2,k,dl)) { rtfree(ctx, list1); rtfree(ctx, list2); return RT_FALSE; } } else { dl->twisted= ((dl->twisted) * (-1)); if (!rt_dist2d_pre_seg_seg(ctx, l2,l1,list2,list1,k,dl)) { rtfree(ctx, list1); rtfree(ctx, list2); return RT_FALSE; } } rtfree(ctx, list1); rtfree(ctx, list2); return RT_TRUE; } int struct_cmp_by_measure(const void *a, const void *b) { LISTSTRUCT *ia = (LISTSTRUCT*)a; LISTSTRUCT *ib = (LISTSTRUCT*)b; return ( ia->themeasure>ib->themeasure ) ? 1 : -1; } /** preparation before rt_dist2d_seg_seg. */ int rt_dist2d_pre_seg_seg(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,LISTSTRUCT *list1, LISTSTRUCT *list2,double k, DISTPTS *dl) { const RTPOINT2D *p1, *p2, *p3, *p4, *p01, *p02; int pnr1,pnr2,pnr3,pnr4, n1, n2, i, u, r, twist; double maxmeasure; n1= l1->npoints; n2 = l2->npoints; RTDEBUG(2, "rt_dist2d_pre_seg_seg is called"); p1 = rt_getPoint2d_cp(ctx, l1, list1[0].pnr); p3 = rt_getPoint2d_cp(ctx, l2, list2[0].pnr); rt_dist2d_pt_pt(ctx, p1, p3, dl); maxmeasure = sqrt(dl->distance*dl->distance + (dl->distance*dl->distance*k*k)); twist = dl->twisted; /*to keep the incomming order between iterations*/ for (i =(n1-1); i>=0; --i) { /*we break this iteration when we have checked every point closer to our perpendicular "checkline" than our shortest found distance*/ if (((list2[0].themeasure-list1[i].themeasure)) > maxmeasure) break; for (r=-1; r<=1; r +=2) /*because we are not iterating in the original pointorder we have to check the segment before and after every point*/ { pnr1 = list1[i].pnr; p1 = rt_getPoint2d_cp(ctx, l1, pnr1); if (pnr1+r<0) { p01 = rt_getPoint2d_cp(ctx, l1, (n1-1)); if (( p1->x == p01->x) && (p1->y == p01->y)) pnr2 = (n1-1); else pnr2 = pnr1; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else if (pnr1+r>(n1-1)) { p01 = rt_getPoint2d_cp(ctx, l1, 0); if (( p1->x == p01->x) && (p1->y == p01->y)) pnr2 = 0; else pnr2 = pnr1; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr2 = pnr1+r; p2 = rt_getPoint2d_cp(ctx, l1, pnr2); for (u=0; u= maxmeasure) break; pnr3 = list2[u].pnr; p3 = rt_getPoint2d_cp(ctx, l2, pnr3); if (pnr3==0) { p02 = rt_getPoint2d_cp(ctx, l2, (n2-1)); if (( p3->x == p02->x) && (p3->y == p02->y)) pnr4 = (n2-1); else pnr4 = pnr3; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr4 = pnr3-1; p4 = rt_getPoint2d_cp(ctx, l2, pnr4); dl->twisted=twist; if (!rt_dist2d_selected_seg_seg(ctx, p1, p2, p3, p4, dl)) return RT_FALSE; if (pnr3>=(n2-1)) { p02 = rt_getPoint2d_cp(ctx, l2, 0); if (( p3->x == p02->x) && (p3->y == p02->y)) pnr4 = 0; else pnr4 = pnr3; /* if it is a line and the last and first point is not the same we avoid the edge between start and end this way*/ } else pnr4 = pnr3+1; p4 = rt_getPoint2d_cp(ctx, l2, pnr4); dl->twisted=twist; /*we reset the "twist" for each iteration*/ if (!rt_dist2d_selected_seg_seg(ctx, p1, p2, p3, p4, dl)) return RT_FALSE; maxmeasure = sqrt(dl->distance*dl->distance + (dl->distance*dl->distance*k*k));/*here we "translate" the found mindistance so it can be compared to our "z"-values*/ } } } return RT_TRUE; } /** This is the same function as rt_dist2d_seg_seg but without any calculations to determine intersection since we already know they do not intersect */ int rt_dist2d_selected_seg_seg(const RTCTX *ctx, const RTPOINT2D *A, const RTPOINT2D *B, const RTPOINT2D *C, const RTPOINT2D *D, DISTPTS *dl) { RTDEBUGF(2, "rt_dist2d_selected_seg_seg [%g,%g]->[%g,%g] by [%g,%g]->[%g,%g]", A->x,A->y,B->x,B->y, C->x,C->y, D->x, D->y); /*A and B are the same point */ if ( ( A->x == B->x) && (A->y == B->y) ) { return rt_dist2d_pt_seg(ctx, A,C,D,dl); } /*U and V are the same point */ if ( ( C->x == D->x) && (C->y == D->y) ) { dl->twisted= ((dl->twisted) * (-1)); return rt_dist2d_pt_seg(ctx, D,A,B,dl); } if ((rt_dist2d_pt_seg(ctx, A,C,D,dl)) && (rt_dist2d_pt_seg(ctx, B,C,D,dl))) { dl->twisted= ((dl->twisted) * (-1)); /*here we change the order of inputted geometrys and that we notice by changing sign on dl->twisted*/ return ((rt_dist2d_pt_seg(ctx, C,A,B,dl)) && (rt_dist2d_pt_seg(ctx, D,A,B,dl))); /*if all is successful we return true*/ } else { return RT_FALSE; /* if any of the calls to rt_dist2d_pt_seg goes wrong we return false*/ } } /*------------------------------------------------------------------------------------------------------------ End of New faster distance calculations --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Functions in common for Brute force and new calculation --------------------------------------------------------------------------------------------------------------*/ /** rt_dist2d_comp from p to line A->B This one is now sending every occation to rt_dist2d_pt_pt Before it was handling occations where r was between 0 and 1 internally and just returning the distance without identifying the points. To get this points it was nessecary to change and it also showed to be about 10%faster. */ int rt_dist2d_pt_seg(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINT2D *A, const RTPOINT2D *B, DISTPTS *dl) { RTPOINT2D c; double r; /*if start==end, then use pt distance */ if ( ( A->x == B->x) && (A->y == B->y) ) { return rt_dist2d_pt_pt(ctx, p,A,dl); } /* * otherwise, we use comp.graphics.algorithms * Frequently Asked Questions method * * (1) AC dot AB * r = --------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); /*This is for finding the maxdistance. the maxdistance have to be between two vertexes, compared to mindistance which can be between tvo vertexes vertex.*/ if (dl->mode == DIST_MAX) { if (r>=0.5) { return rt_dist2d_pt_pt(ctx, p,A,dl); } if (r<0.5) { return rt_dist2d_pt_pt(ctx, p,B,dl); } } if (r<0) /*If p projected on the line is outside point A*/ { return rt_dist2d_pt_pt(ctx, p,A,dl); } if (r>=1) /*If p projected on the line is outside point B or on point B*/ { return rt_dist2d_pt_pt(ctx, p,B,dl); } /*If the point p is on the segment this is a more robust way to find out that*/ if (( ((A->y-p->y)*(B->x-A->x)==(A->x-p->x)*(B->y-A->y) ) ) && (dl->mode == DIST_MIN)) { dl->distance = 0.0; dl->p1 = *p; dl->p2 = *p; } /*If the projection of point p on the segment is between A and B then we find that "point on segment" and send it to rt_dist2d_pt_pt*/ c.x=A->x + r * (B->x-A->x); c.y=A->y + r * (B->y-A->y); return rt_dist2d_pt_pt(ctx, p,&c,dl); } /** Compares incomming points and stores the points closest to each other or most far away from each other depending on dl->mode (max or min) */ int rt_dist2d_pt_pt(const RTCTX *ctx, const RTPOINT2D *thep1, const RTPOINT2D *thep2, DISTPTS *dl) { double hside = thep2->x - thep1->x; double vside = thep2->y - thep1->y; double dist = sqrt ( hside*hside + vside*vside ); if (((dl->distance - dist)*(dl->mode))>0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/ { dl->distance = dist; if (dl->twisted>0) /*To get the points in right order. twisted is updated between 1 and (-1) every time the order is changed earlier in the chain*/ { dl->p1 = *thep1; dl->p2 = *thep2; } else { dl->p1 = *thep2; dl->p2 = *thep1; } } return RT_TRUE; } /*------------------------------------------------------------------------------------------------------------ End of Functions in common for Brute force and new calculation --------------------------------------------------------------------------------------------------------------*/ /** The old function nessecary for ptarray_segmentize2d in ptarray.c */ double distance2d_pt_pt(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2) { double hside = p2->x - p1->x; double vside = p2->y - p1->y; return sqrt ( hside*hside + vside*vside ); } double distance2d_sqr_pt_pt(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2) { double hside = p2->x - p1->x; double vside = p2->y - p1->y; return hside*hside + vside*vside; } /** The old function nessecary for ptarray_segmentize2d in ptarray.c */ double distance2d_pt_seg(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINT2D *A, const RTPOINT2D *B) { double r,s; /*if start==end, then use pt distance */ if ( ( A->x == B->x) && (A->y == B->y) ) return distance2d_pt_pt(ctx, p,A); /* * otherwise, we use comp.graphics.algorithms * Frequently Asked Questions method * * (1) AC dot AB * r = --------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); if (r<0) return distance2d_pt_pt(ctx, p,A); if (r>1) return distance2d_pt_pt(ctx, p,B); /* * (2) * (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) * s = ----------------------------- * L^2 * * Then the distance from C to P = |s|*L. * */ s = ( (A->y-p->y)*(B->x-A->x)- (A->x-p->x)*(B->y-A->y) ) / ( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); return FP_ABS(s) * sqrt( (B->x-A->x)*(B->x-A->x) + (B->y-A->y)*(B->y-A->y) ); } /* return distance squared, useful to avoid sqrt calculations */ double distance2d_sqr_pt_seg(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINT2D *A, const RTPOINT2D *B) { double r,s; if ( ( A->x == B->x) && (A->y == B->y) ) return distance2d_sqr_pt_pt(ctx, p,A); r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); if (r<0) return distance2d_sqr_pt_pt(ctx, p,A); if (r>1) return distance2d_sqr_pt_pt(ctx, p,B); /* * (2) * (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) * s = ----------------------------- * L^2 * * Then the distance from C to P = |s|*L. * */ s = ( (A->y-p->y)*(B->x-A->x)- (A->x-p->x)*(B->y-A->y) ) / ( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); return s * s * ( (B->x-A->x)*(B->x-A->x) + (B->y-A->y)*(B->y-A->y) ); } /** * Compute the azimuth of segment AB in radians. * Return 0 on exception (same point), 1 otherwise. */ int azimuth_pt_pt(const RTCTX *ctx, const RTPOINT2D *A, const RTPOINT2D *B, double *d) { if ( A->x == B->x ) { if ( A->y < B->y ) *d=0.0; else if ( A->y > B->y ) *d=M_PI; else return 0; return 1; } if ( A->y == B->y ) { if ( A->x < B->x ) *d=M_PI/2; else if ( A->x > B->x ) *d=M_PI+(M_PI/2); else return 0; return 1; } if ( A->x < B->x ) { if ( A->y < B->y ) { *d=atan(fabs(A->x - B->x) / fabs(A->y - B->y) ); } else /* ( A->y > B->y ) - equality case handled above */ { *d=atan(fabs(A->y - B->y) / fabs(A->x - B->x) ) + (M_PI/2); } } else /* ( A->x > B->x ) - equality case handled above */ { if ( A->y > B->y ) { *d=atan(fabs(A->x - B->x) / fabs(A->y - B->y) ) + M_PI; } else /* ( A->y < B->y ) - equality case handled above */ { *d=atan(fabs(A->y - B->y) / fabs(A->x - B->x) ) + (M_PI+(M_PI/2)); } } return 1; } src/measures.h000066400000000000000000000146241271715413500136570ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2010 Nicklas Avén * Copyright 2010 Nicklas Avén * **********************************************************************/ /********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * Copyright 2010 Nicklas Avén * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include "librttopo_geom_internal.h" /* for the measure functions*/ #define DIST_MAX -1 #define DIST_MIN 1 /** * Structure used in distance-calculations */ typedef struct { double distance; /*the distance between p1 and p2*/ RTPOINT2D p1; RTPOINT2D p2; int mode; /*the direction of looking, if thedir = -1 then we look for maxdistance and if it is 1 then we look for mindistance*/ int twisted; /*To preserve the order of incoming points to match the first and secon point in shortest and longest line*/ double tolerance; /*the tolerance for dwithin and dfullywithin*/ } DISTPTS; typedef struct { double themeasure; /*a value calculated to compare distances*/ int pnr; /*pointnumber. the ordernumber of the point*/ } LISTSTRUCT; /* * Preprocessing functions */ int rt_dist2d_comp(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, DISTPTS *dl); int rt_dist2d_distribute_bruteforce(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS *dl); int rt_dist2d_recursive(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS *dl); int rt_dist2d_check_overlap(const RTCTX *ctx, RTGEOM *rtg1, RTGEOM *rtg2); int rt_dist2d_distribute_fast(const RTCTX *ctx, RTGEOM *rtg1, RTGEOM *rtg2, DISTPTS *dl); /* * Brute force functions */ int rt_dist2d_pt_ptarray(const RTCTX *ctx, const RTPOINT2D *p, RTPOINTARRAY *pa, DISTPTS *dl); int rt_dist2d_pt_ptarrayarc(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *pa, DISTPTS *dl); int rt_dist2d_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2, DISTPTS *dl); int rt_dist2d_ptarray_ptarrayarc(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINTARRAY *pb, DISTPTS *dl); int rt_dist2d_ptarrayarc_ptarrayarc(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINTARRAY *pb, DISTPTS *dl); int rt_dist2d_ptarray_poly(RTPOINTARRAY *pa, RTPOLY *poly, DISTPTS *dl); int rt_dist2d_point_point(const RTCTX *ctx, RTPOINT *point1, RTPOINT *point2, DISTPTS *dl); int rt_dist2d_point_line(const RTCTX *ctx, RTPOINT *point, RTLINE *line, DISTPTS *dl); int rt_dist2d_point_circstring(const RTCTX *ctx, RTPOINT *point, RTCIRCSTRING *circ, DISTPTS *dl); int rt_dist2d_point_poly(const RTCTX *ctx, RTPOINT *point, RTPOLY *poly, DISTPTS *dl); int rt_dist2d_point_curvepoly(const RTCTX *ctx, RTPOINT *point, RTCURVEPOLY *poly, DISTPTS *dl); int rt_dist2d_line_line(const RTCTX *ctx, RTLINE *line1, RTLINE *line2, DISTPTS *dl); int rt_dist2d_line_circstring(const RTCTX *ctx, RTLINE *line1, RTCIRCSTRING *line2, DISTPTS *dl); int rt_dist2d_line_poly(const RTCTX *ctx, RTLINE *line, RTPOLY *poly, DISTPTS *dl); int rt_dist2d_line_curvepoly(const RTCTX *ctx, RTLINE *line, RTCURVEPOLY *poly, DISTPTS *dl); int rt_dist2d_circstring_circstring(const RTCTX *ctx, RTCIRCSTRING *line1, RTCIRCSTRING *line2, DISTPTS *dl); int rt_dist2d_circstring_poly(const RTCTX *ctx, RTCIRCSTRING *circ, RTPOLY *poly, DISTPTS *dl); int rt_dist2d_circstring_curvepoly(const RTCTX *ctx, RTCIRCSTRING *circ, RTCURVEPOLY *poly, DISTPTS *dl); int rt_dist2d_poly_poly(const RTCTX *ctx, RTPOLY *poly1, RTPOLY *poly2, DISTPTS *dl); int rt_dist2d_poly_curvepoly(const RTCTX *ctx, RTPOLY *poly1, RTCURVEPOLY *curvepoly2, DISTPTS *dl); int rt_dist2d_curvepoly_curvepoly(const RTCTX *ctx, RTCURVEPOLY *poly1, RTCURVEPOLY *poly2, DISTPTS *dl); /* * New faster distance calculations */ int rt_dist2d_pre_seg_seg(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,LISTSTRUCT *list1, LISTSTRUCT *list2,double k, DISTPTS *dl); int rt_dist2d_selected_seg_seg(const RTCTX *ctx, const RTPOINT2D *A, const RTPOINT2D *B, const RTPOINT2D *C, const RTPOINT2D *D, DISTPTS *dl); int struct_cmp_by_measure(const void *a, const void *b); int rt_dist2d_fast_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1,RTPOINTARRAY *l2, DISTPTS *dl, RTGBOX *box1, RTGBOX *box2); /* * Distance calculation primitives. */ int rt_dist2d_pt_pt(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *Q, DISTPTS *dl); int rt_dist2d_pt_seg(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2, DISTPTS *dl); int rt_dist2d_pt_arc(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, DISTPTS *dl); int rt_dist2d_seg_seg(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *B1, const RTPOINT2D *B2, DISTPTS *dl); int rt_dist2d_seg_arc(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *B1, const RTPOINT2D *B2, const RTPOINT2D *B3, DISTPTS *dl); int rt_dist2d_arc_arc(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, const RTPOINT2D *B1, const RTPOINT2D *B2, const RTPOINT2D* B3, DISTPTS *dl); void rt_dist2d_distpts_init(const RTCTX *ctx, DISTPTS *dl, int mode); /* * Length primitives */ double rt_arc_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3); /* * Geometry returning functions */ RTGEOM* rt_dist2d_distancepoint(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode); RTGEOM* rt_dist2d_distanceline(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode); src/measures3d.c000066400000000000000000001143311271715413500140750ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011 Nicklas Avén * Copyright 2011 Nicklas Avén * **********************************************************************/ /********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * Copyright 2011 Nicklas Avén * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include #include #include "measures3d.h" #include "rtgeom_log.h" static inline int get_3dvector_from_points(const RTCTX *ctx, RTPOINT3DZ *p1,RTPOINT3DZ *p2, VECTOR3D *v) { v->x=p2->x-p1->x; v->y=p2->y-p1->y; v->z=p2->z-p1->z; return RT_TRUE; } static inline int get_3dcross_product(const RTCTX *ctx, VECTOR3D *v1,VECTOR3D *v2, VECTOR3D *v) { v->x=(v1->y*v2->z)-(v1->z*v2->y); v->y=(v1->z*v2->x)-(v1->x*v2->z); v->z=(v1->x*v2->y)-(v1->y*v2->x); return RT_TRUE; } /** This function is used to create a vertical line used for cases where one if the geometries lacks z-values. The vertical line crosses the 2d point that is closest and the z-range is from maxz to minz in the geoemtrie that has z values. */ static RTGEOM* create_v_line(const RTCTX *ctx, const RTGEOM *rtgeom,double x, double y, int srid) { RTPOINT *rtpoints[2]; RTGBOX gbox; int rv = rtgeom_calculate_gbox(ctx, rtgeom, &gbox); if ( rv == RT_FAILURE ) return NULL; rtpoints[0] = rtpoint_make3dz(ctx, srid, x, y, gbox.zmin); rtpoints[1] = rtpoint_make3dz(ctx, srid, x, y, gbox.zmax); return (RTGEOM *)rtline_from_ptarray(ctx, srid, 2, rtpoints); } RTGEOM * rtgeom_closest_line_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist3d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MIN); } RTGEOM * rtgeom_furthest_line_3d(const RTCTX *ctx, RTGEOM *rt1, RTGEOM *rt2) { return rt_dist3d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MAX); } RTGEOM * rtgeom_closest_point_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { return rt_dist3d_distancepoint(ctx, rt1, rt2, rt1->srid, DIST_MIN); } /** Function initializing 3dshortestline and 3dlongestline calculations. */ RTGEOM * rt_dist3d_distanceline(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode) { RTDEBUG(2, "rt_dist3d_distanceline is called"); double x1,x2,y1,y2, z1, z2, x, y; double initdistance = ( mode == DIST_MIN ? FLT_MAX : -1.0); DISTPTS3D thedl; RTPOINT *rtpoints[2]; RTGEOM *result; thedl.mode = mode; thedl.distance = initdistance; thedl.tolerance = 0.0; /*Check if we really have 3D geoemtries*/ /*If not, send it to 2D-calculations which will give the same result*/ /*as an infinite z-value at one or two of the geometries*/ if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2)) { rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); if(!rtgeom_has_z(ctx, rt1) && !rtgeom_has_z(ctx, rt2)) return rt_dist2d_distanceline(ctx, rt1, rt2, srid, mode); DISTPTS thedl2d; thedl2d.mode = mode; thedl2d.distance = initdistance; thedl2d.tolerance = 0.0; if (!rt_dist2d_comp(ctx, rt1,rt2,&thedl2d)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } RTGEOM *vertical_line; if(!rtgeom_has_z(ctx, rt1)) { x=thedl2d.p1.x; y=thedl2d.p1.y; vertical_line = create_v_line(ctx, rt2,x,y,srid); if (!rt_dist3d_recursive(ctx, vertical_line, rt2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rtfree(ctx, vertical_line); rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } rtfree(ctx, vertical_line); } if(!rtgeom_has_z(ctx, rt2)) { x=thedl2d.p2.x; y=thedl2d.p2.y; vertical_line = create_v_line(ctx, rt1,x,y,srid); if (!rt_dist3d_recursive(ctx, rt1, vertical_line, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rtfree(ctx, vertical_line); rterror(ctx, "Some unspecified error."); return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } rtfree(ctx, vertical_line); } } else { if (!rt_dist3d_recursive(ctx, rt1, rt2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } } /*if thedl.distance is unchanged there where only empty geometries input*/ if (thedl.distance == initdistance) { RTDEBUG(3, "didn't find geometries to measure between, returning null"); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } else { x1=thedl.p1.x; y1=thedl.p1.y; z1=thedl.p1.z; x2=thedl.p2.x; y2=thedl.p2.y; z2=thedl.p2.z; rtpoints[0] = rtpoint_make3dz(ctx, srid, x1, y1, z1); rtpoints[1] = rtpoint_make3dz(ctx, srid, x2, y2, z2); result = (RTGEOM *)rtline_from_ptarray(ctx, srid, 2, rtpoints); } return result; } /** Function initializing 3dclosestpoint calculations. */ RTGEOM * rt_dist3d_distancepoint(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode) { double x,y,z; DISTPTS3D thedl; double initdistance = FLT_MAX; RTGEOM *result; thedl.mode = mode; thedl.distance= initdistance; thedl.tolerance = 0; RTDEBUG(2, "rt_dist3d_distancepoint is called"); /*Check if we really have 3D geoemtries*/ /*If not, send it to 2D-calculations which will give the same result*/ /*as an infinite z-value at one or two of the geometries*/ if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2)) { rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); if(!rtgeom_has_z(ctx, rt1) && !rtgeom_has_z(ctx, rt2)) return rt_dist2d_distancepoint(ctx, rt1, rt2, srid, mode); DISTPTS thedl2d; thedl2d.mode = mode; thedl2d.distance = initdistance; thedl2d.tolerance = 0.0; if (!rt_dist2d_comp(ctx, rt1,rt2,&thedl2d)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } RTGEOM *vertical_line; if(!rtgeom_has_z(ctx, rt1)) { x=thedl2d.p1.x; y=thedl2d.p1.y; vertical_line = create_v_line(ctx, rt2,x,y,srid); if (!rt_dist3d_recursive(ctx, vertical_line, rt2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rtfree(ctx, vertical_line); rterror(ctx, "Some unspecified error."); return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } rtfree(ctx, vertical_line); } if(!rtgeom_has_z(ctx, rt2)) { x=thedl2d.p2.x; y=thedl2d.p2.y; vertical_line = create_v_line(ctx, rt1,x,y,srid); if (!rt_dist3d_recursive(ctx, rt1, vertical_line, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rtfree(ctx, vertical_line); rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } rtfree(ctx, vertical_line); } } else { if (!rt_dist3d_recursive(ctx, rt1, rt2, &thedl)) { /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } } if (thedl.distance == initdistance) { RTDEBUG(3, "didn't find geometries to measure between, returning null"); result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0); } else { x=thedl.p1.x; y=thedl.p1.y; z=thedl.p1.z; result = (RTGEOM *)rtpoint_make3dz(ctx, srid, x, y, z); } return result; } /** Function initializing 3d max distance calculation */ double rtgeom_maxdistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { RTDEBUG(2, "rtgeom_maxdistance3d is called"); return rtgeom_maxdistance3d_tolerance(ctx, rt1, rt2, 0.0 ); } /** Function handling 3d max distance calculations and dfullywithin calculations. The difference is just the tolerance. */ double rtgeom_maxdistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance) { if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2)) { rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); return rtgeom_maxdistance2d_tolerance(ctx, rt1, rt2, tolerance); } /*double thedist;*/ DISTPTS3D thedl; RTDEBUG(2, "rtgeom_maxdistance3d_tolerance is called"); thedl.mode = DIST_MAX; thedl.distance= -1; thedl.tolerance = tolerance; if (rt_dist3d_recursive(ctx, rt1, rt2, &thedl)) { return thedl.distance; } /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); return -1; } /** Function initializing 3d min distance calculation */ double rtgeom_mindistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2) { RTDEBUG(2, "rtgeom_mindistance3d is called"); return rtgeom_mindistance3d_tolerance(ctx, rt1, rt2, 0.0 ); } /** Function handling 3d min distance calculations and dwithin calculations. The difference is just the tolerance. */ double rtgeom_mindistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance) { if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2)) { rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); return rtgeom_mindistance2d_tolerance(ctx, rt1, rt2, tolerance); } DISTPTS3D thedl; RTDEBUG(2, "rtgeom_mindistance3d_tolerance is called"); thedl.mode = DIST_MIN; thedl.distance= FLT_MAX; thedl.tolerance = tolerance; if (rt_dist3d_recursive(ctx, rt1, rt2, &thedl)) { return thedl.distance; } /*should never get here. all cases ought to be error handled earlier*/ rterror(ctx, "Some unspecified error."); return FLT_MAX; } /*------------------------------------------------------------------------------------------------------------ End of Initializing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Preprocessing functions Functions preparing geometries for distance-calculations --------------------------------------------------------------------------------------------------------------*/ /** This is a recursive function delivering every possible combination of subgeometries */ int rt_dist3d_recursive(const RTCTX *ctx, const RTGEOM *rtg1,const RTGEOM *rtg2, DISTPTS3D *dl) { int i, j; int n1=1; int n2=1; RTGEOM *g1 = NULL; RTGEOM *g2 = NULL; RTCOLLECTION *c1 = NULL; RTCOLLECTION *c2 = NULL; RTDEBUGF(2, "rt_dist3d_recursive is called with type1=%d, type2=%d", rtg1->type, rtg2->type); if (rtgeom_is_collection(ctx, rtg1)) { RTDEBUG(3, "First geometry is collection"); c1 = rtgeom_as_rtcollection(ctx, rtg1); n1 = c1->ngeoms; } if (rtgeom_is_collection(ctx, rtg2)) { RTDEBUG(3, "Second geometry is collection"); c2 = rtgeom_as_rtcollection(ctx, rtg2); n2 = c2->ngeoms; } for ( i = 0; i < n1; i++ ) { if (rtgeom_is_collection(ctx, rtg1)) { g1 = c1->geoms[i]; } else { g1 = (RTGEOM*)rtg1; } if (rtgeom_is_empty(ctx, g1)) return RT_TRUE; if (rtgeom_is_collection(ctx, g1)) { RTDEBUG(3, "Found collection inside first geometry collection, recursing"); if (!rt_dist3d_recursive(ctx, g1, rtg2, dl)) return RT_FALSE; continue; } for ( j = 0; j < n2; j++ ) { if (rtgeom_is_collection(ctx, rtg2)) { g2 = c2->geoms[j]; } else { g2 = (RTGEOM*)rtg2; } if (rtgeom_is_collection(ctx, g2)) { RTDEBUG(3, "Found collection inside second geometry collection, recursing"); if (!rt_dist3d_recursive(ctx, g1, g2, dl)) return RT_FALSE; continue; } /*If one of geometries is empty, return. True here only means continue searching. False would have stoped the process*/ if (rtgeom_is_empty(ctx, g1)||rtgeom_is_empty(ctx, g2)) return RT_TRUE; if (!rt_dist3d_distribute_bruteforce(ctx, g1, g2, dl)) return RT_FALSE; if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ } } return RT_TRUE; } /** This function distributes the brute-force for 3D so far the only type, tasks depending on type */ int rt_dist3d_distribute_bruteforce(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS3D *dl) { int t1 = rtg1->type; int t2 = rtg2->type; RTDEBUGF(2, "rt_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", rtg1->type, rtg2->type); if ( t1 == RTPOINTTYPE ) { if ( t2 == RTPOINTTYPE ) { dl->twisted=1; return rt_dist3d_point_point(ctx, (RTPOINT *)rtg1, (RTPOINT *)rtg2, dl); } else if ( t2 == RTLINETYPE ) { dl->twisted=1; return rt_dist3d_point_line(ctx, (RTPOINT *)rtg1, (RTLINE *)rtg2, dl); } else if ( t2 == RTPOLYGONTYPE ) { dl->twisted=1; return rt_dist3d_point_poly(ctx, (RTPOINT *)rtg1, (RTPOLY *)rtg2,dl); } else { rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); return RT_FALSE; } } else if ( t1 == RTLINETYPE ) { if ( t2 == RTPOINTTYPE ) { dl->twisted=(-1); return rt_dist3d_point_line(ctx, (RTPOINT *)rtg2,(RTLINE *)rtg1,dl); } else if ( t2 == RTLINETYPE ) { dl->twisted=1; return rt_dist3d_line_line(ctx, (RTLINE *)rtg1,(RTLINE *)rtg2,dl); } else if ( t2 == RTPOLYGONTYPE ) { dl->twisted=1; return rt_dist3d_line_poly(ctx, (RTLINE *)rtg1,(RTPOLY *)rtg2,dl); } else { rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); return RT_FALSE; } } else if ( t1 == RTPOLYGONTYPE ) { if ( t2 == RTPOLYGONTYPE ) { dl->twisted=1; return rt_dist3d_poly_poly(ctx, (RTPOLY *)rtg1, (RTPOLY *)rtg2,dl); } else if ( t2 == RTPOINTTYPE ) { dl->twisted=-1; return rt_dist3d_point_poly(ctx, (RTPOINT *)rtg2, (RTPOLY *)rtg1,dl); } else if ( t2 == RTLINETYPE ) { dl->twisted=-1; return rt_dist3d_line_poly(ctx, (RTLINE *)rtg2,(RTPOLY *)rtg1,dl); } else { rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2)); return RT_FALSE; } } else { rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t1)); return RT_FALSE; } /*You shouldn't being able to get here*/ rterror(ctx, "unspecified error in function rt_dist3d_distribute_bruteforce"); return RT_FALSE; } /*------------------------------------------------------------------------------------------------------------ End of Preprocessing functions --------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------------------------ Brute force functions So far the only way to do 3D-calculations --------------------------------------------------------------------------------------------------------------*/ /** point to point calculation */ int rt_dist3d_point_point(const RTCTX *ctx, RTPOINT *point1, RTPOINT *point2, DISTPTS3D *dl) { RTPOINT3DZ p1; RTPOINT3DZ p2; RTDEBUG(2, "rt_dist3d_point_point is called"); rt_getPoint3dz_p(ctx, point1->point, 0, &p1); rt_getPoint3dz_p(ctx, point2->point, 0, &p2); return rt_dist3d_pt_pt(ctx, &p1, &p2,dl); } /** point to line calculation */ int rt_dist3d_point_line(const RTCTX *ctx, RTPOINT *point, RTLINE *line, DISTPTS3D *dl) { RTPOINT3DZ p; RTPOINTARRAY *pa = line->points; RTDEBUG(2, "rt_dist3d_point_line is called"); rt_getPoint3dz_p(ctx, point->point, 0, &p); return rt_dist3d_pt_ptarray(ctx, &p, pa, dl); } /** Computes point to polygon distance For mindistance that means: 1)find the plane of the polygon 2)projecting the point to the plane of the polygon 3)finding if that projected point is inside the polygon, if so the distance is measured to that projected point 4) if not in polygon above, check the distance against the boundary of the polygon for max distance it is artays point against boundary */ int rt_dist3d_point_poly(const RTCTX *ctx, RTPOINT *point, RTPOLY *poly, DISTPTS3D *dl) { RTPOINT3DZ p, projp;/*projp is "point projected on plane"*/ PLANE3D plane; RTDEBUG(2, "rt_dist3d_point_poly is called"); rt_getPoint3dz_p(ctx, point->point, 0, &p); /*If we are lookig for max distance, longestline or dfullywithin*/ if (dl->mode == DIST_MAX) { RTDEBUG(3, "looking for maxdistance"); return rt_dist3d_pt_ptarray(ctx, &p, poly->rings[0], dl); } /*Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boudary*/ if(!define_plane(ctx, poly->rings[0], &plane)) return RT_FALSE; /*get our point projected on the plane of the polygon*/ project_point_on_plane(ctx, &p, &plane, &projp); return rt_dist3d_pt_poly(ctx, &p, poly,&plane, &projp, dl); } /** line to line calculation */ int rt_dist3d_line_line(const RTCTX *ctx, RTLINE *line1, RTLINE *line2, DISTPTS3D *dl) { RTPOINTARRAY *pa1 = line1->points; RTPOINTARRAY *pa2 = line2->points; RTDEBUG(2, "rt_dist3d_line_line is called"); return rt_dist3d_ptarray_ptarray(ctx, pa1, pa2, dl); } /** line to polygon calculation */ int rt_dist3d_line_poly(const RTCTX *ctx, RTLINE *line, RTPOLY *poly, DISTPTS3D *dl) { PLANE3D plane; RTDEBUG(2, "rt_dist3d_line_poly is called"); if (dl->mode == DIST_MAX) { return rt_dist3d_ptarray_ptarray(ctx, line->points, poly->rings[0], dl); } if(!define_plane(ctx, poly->rings[0], &plane)) return RT_FALSE; return rt_dist3d_ptarray_poly(ctx, line->points, poly,&plane, dl); } /** polygon to polygon calculation */ int rt_dist3d_poly_poly(const RTCTX *ctx, RTPOLY *poly1, RTPOLY *poly2, DISTPTS3D *dl) { PLANE3D plane; RTDEBUG(2, "rt_dist3d_poly_poly is called"); if (dl->mode == DIST_MAX) { return rt_dist3d_ptarray_ptarray(ctx, poly1->rings[0], poly2->rings[0], dl); } if(!define_plane(ctx, poly2->rings[0], &plane)) return RT_FALSE; /*What we do here is to compare the bondary of one polygon with the other polygon and then take the second boudary comparing with the first polygon*/ dl->twisted=1; if(!rt_dist3d_ptarray_poly(ctx, poly1->rings[0], poly2,&plane, dl)) return RT_FALSE; if(dl->distance==0.0) /*Just check if the answer already is given*/ return RT_TRUE; if(!define_plane(ctx, poly1->rings[0], &plane)) return RT_FALSE; dl->twisted=-1; /*because we swithc the order of geometries we swithch "twisted" to -1 which will give the right order of points in shortest line.*/ return rt_dist3d_ptarray_poly(ctx, poly2->rings[0], poly1,&plane, dl); } /** * search all the segments of pointarray to see which one is closest to p * Returns distance between point and pointarray */ int rt_dist3d_pt_ptarray(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINTARRAY *pa,DISTPTS3D *dl) { int t; RTPOINT3DZ start, end; int twist = dl->twisted; RTDEBUG(2, "rt_dist3d_pt_ptarray is called"); rt_getPoint3dz_p(ctx, pa, 0, &start); for (t=1; tnpoints; t++) { dl->twisted=twist; rt_getPoint3dz_p(ctx, pa, t, &end); if (!rt_dist3d_pt_seg(ctx, p, &start, &end,dl)) return RT_FALSE; if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ start = end; } return RT_TRUE; } /** If searching for min distance, this one finds the closest point on segment A-B from p. if searching for max distance it just sends p-A and p-B to pt-pt calculation */ int rt_dist3d_pt_seg(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINT3DZ *A, RTPOINT3DZ *B, DISTPTS3D *dl) { RTPOINT3DZ c; double r; /*if start==end, then use pt distance */ if ( ( A->x == B->x) && (A->y == B->y) && (A->z == B->z) ) { return rt_dist3d_pt_pt(ctx, p,A,dl); } r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) + ( p->z-A->z) * (B->z-A->z) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y)+(B->z-A->z)*(B->z-A->z) ); /*This is for finding the 3Dmaxdistance. the maxdistance have to be between two vertexes, compared to mindistance which can be between tvo vertexes vertex.*/ if (dl->mode == DIST_MAX) { if (r>=0.5) { return rt_dist3d_pt_pt(ctx, p,A,dl); } if (r<0.5) { return rt_dist3d_pt_pt(ctx, p,B,dl); } } if (r<0) /*If the first vertex A is closest to the point p*/ { return rt_dist3d_pt_pt(ctx, p,A,dl); } if (r>1) /*If the second vertex B is closest to the point p*/ { return rt_dist3d_pt_pt(ctx, p,B,dl); } /*else if the point p is closer to some point between a and b then we find that point and send it to rt_dist3d_pt_pt*/ c.x=A->x + r * (B->x-A->x); c.y=A->y + r * (B->y-A->y); c.z=A->z + r * (B->z-A->z); return rt_dist3d_pt_pt(ctx, p,&c,dl); } double distance3d_pt_pt(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2) { double dx = p2->x - p1->x; double dy = p2->y - p1->y; double dz = p2->z - p1->z; return sqrt ( dx*dx + dy*dy + dz*dz); } /** Compares incomming points and stores the points closest to each other or most far away from each other depending on dl->mode (max or min) */ int rt_dist3d_pt_pt(const RTCTX *ctx, RTPOINT3DZ *thep1, RTPOINT3DZ *thep2,DISTPTS3D *dl) { double dx = thep2->x - thep1->x; double dy = thep2->y - thep1->y; double dz = thep2->z - thep1->z; double dist = sqrt ( dx*dx + dy*dy + dz*dz); RTDEBUGF(2, "rt_dist3d_pt_pt called (with points: p1.x=%f, p1.y=%f,p1.z=%f,p2.x=%f, p2.y=%f,p2.z=%f)",thep1->x,thep1->y,thep1->z,thep2->x,thep2->y,thep2->z ); if (((dl->distance - dist)*(dl->mode))>0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/ { dl->distance = dist; if (dl->twisted>0) /*To get the points in right order. twisted is updated between 1 and (-1) every time the order is changed earlier in the chain*/ { dl->p1 = *thep1; dl->p2 = *thep2; } else { dl->p1 = *thep2; dl->p2 = *thep1; } } return RT_TRUE; } /** Finds all combinationes of segments between two pointarrays */ int rt_dist3d_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,DISTPTS3D *dl) { int t,u; RTPOINT3DZ start, end; RTPOINT3DZ start2, end2; int twist = dl->twisted; RTDEBUGF(2, "rt_dist3d_ptarray_ptarray called (points: %d-%d)",l1->npoints, l2->npoints); if (dl->mode == DIST_MAX)/*If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have to be between two vertexes*/ { for (t=0; tnpoints; t++) /*for each segment in L1 */ { rt_getPoint3dz_p(ctx, l1, t, &start); for (u=0; unpoints; u++) /*for each segment in L2 */ { rt_getPoint3dz_p(ctx, l2, u, &start2); rt_dist3d_pt_pt(ctx, &start,&start2,dl); RTDEBUGF(4, "maxdist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance); RTDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); } } } else { rt_getPoint3dz_p(ctx, l1, 0, &start); for (t=1; tnpoints; t++) /*for each segment in L1 */ { rt_getPoint3dz_p(ctx, l1, t, &end); rt_getPoint3dz_p(ctx, l2, 0, &start2); for (u=1; unpoints; u++) /*for each segment in L2 */ { rt_getPoint3dz_p(ctx, l2, u, &end2); dl->twisted=twist; rt_dist3d_seg_seg(ctx, &start, &end, &start2, &end2,dl); RTDEBUGF(4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance); RTDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if the answer is already given*/ start2 = end2; } start = end; } } return RT_TRUE; } /** Finds the two closest points on two linesegments */ int rt_dist3d_seg_seg(const RTCTX *ctx, RTPOINT3DZ *s1p1, RTPOINT3DZ *s1p2, RTPOINT3DZ *s2p1, RTPOINT3DZ *s2p2, DISTPTS3D *dl) { VECTOR3D v1, v2, vl; double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line between the two lines is perpendicular to both lines*/ RTPOINT3DZ p1, p2; double a, b, c, d, e, D; /*s1p1 and s1p2 are the same point */ if ( ( s1p1->x == s1p2->x) && (s1p1->y == s1p2->y) && (s1p1->z == s1p2->z) ) { return rt_dist3d_pt_seg(ctx, s1p1,s2p1,s2p2,dl); } /*s2p1 and s2p2 are the same point */ if ( ( s2p1->x == s2p2->x) && (s2p1->y == s2p2->y) && (s2p1->z == s2p2->z) ) { dl->twisted= ((dl->twisted) * (-1)); return rt_dist3d_pt_seg(ctx, s2p1,s1p1,s1p2,dl); } /* Here we use algorithm from softsurfer.com that can be found here http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm */ if (!get_3dvector_from_points(ctx, s1p1, s1p2, &v1)) return RT_FALSE; if (!get_3dvector_from_points(ctx, s2p1, s2p2, &v2)) return RT_FALSE; if (!get_3dvector_from_points(ctx, s2p1, s1p1, &vl)) return RT_FALSE; a = DOT(v1,v1); b = DOT(v1,v2); c = DOT(v2,v2); d = DOT(v1,vl); e = DOT(v2,vl); D = a*c - b*b; if (D <0.000000001) { /* the lines are almost parallel*/ s1k = 0.0; /*If the lines are paralell we try by using the startpoint of first segment. If that gives a projected point on the second line outside segment 2 it wil be found that s2k is >1 or <0.*/ if(b>c) /* use the largest denominator*/ { s2k=d/b; } else { s2k =e/c; } } else { s1k = (b*e - c*d) / D; s2k = (a*e - b*d) / D; } /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the combinations with start and end points will be tested*/ if(s1k<0.0||s1k>1.0||s2k<0.0||s2k>1.0) { if(s1k<0.0) { if (!rt_dist3d_pt_seg(ctx, s1p1, s2p1, s2p2, dl)) { return RT_FALSE; } } if(s1k>1.0) { if (!rt_dist3d_pt_seg(ctx, s1p2, s2p1, s2p2, dl)) { return RT_FALSE; } } if(s2k<0.0) { dl->twisted= ((dl->twisted) * (-1)); if (!rt_dist3d_pt_seg(ctx, s2p1, s1p1, s1p2, dl)) { return RT_FALSE; } } if(s2k>1.0) { dl->twisted= ((dl->twisted) * (-1)); if (!rt_dist3d_pt_seg(ctx, s2p2, s1p1, s1p2, dl)) { return RT_FALSE; } } } else {/*Find the closest point on the edges of both segments*/ p1.x=s1p1->x+s1k*(s1p2->x-s1p1->x); p1.y=s1p1->y+s1k*(s1p2->y-s1p1->y); p1.z=s1p1->z+s1k*(s1p2->z-s1p1->z); p2.x=s2p1->x+s2k*(s2p2->x-s2p1->x); p2.y=s2p1->y+s2k*(s2p2->y-s2p1->y); p2.z=s2p1->z+s2k*(s2p2->z-s2p1->z); if (!rt_dist3d_pt_pt(ctx, &p1,&p2,dl))/* Send the closest points to point-point calculation*/ { return RT_FALSE; } } return RT_TRUE; } /** Checking if the point projected on the plane of the polygon actually is inside that polygon. If so the mindistance is between that projected point and our original point. If not we check from original point to the bounadary. If the projected point is inside a hole of the polygon we check the distance to the boudary of that hole. */ int rt_dist3d_pt_poly(const RTCTX *ctx, RTPOINT3DZ *p, RTPOLY *poly, PLANE3D *plane,RTPOINT3DZ *projp, DISTPTS3D *dl) { int i; RTDEBUG(2, "rt_dist3d_point_poly called"); if(pt_in_ring_3d(ctx, projp, poly->rings[0], plane)) { for (i=1; inrings; i++) { /* Inside a hole. Distance = pt -> ring */ if ( pt_in_ring_3d(ctx, projp, poly->rings[i], plane )) { RTDEBUG(3, " inside an hole"); return rt_dist3d_pt_ptarray(ctx, p, poly->rings[i], dl); } } return rt_dist3d_pt_pt(ctx, p,projp,dl);/* If the projected point is inside the polygon the shortest distance is between that point and the inputed point*/ } else { return rt_dist3d_pt_ptarray(ctx, p, poly->rings[0], dl); /*If the projected point is outside the polygon we search for the closest distance against the boundarry instead*/ } return RT_TRUE; } /** Computes pointarray to polygon distance */ int rt_dist3d_ptarray_poly(const RTCTX *ctx, RTPOINTARRAY *pa, RTPOLY *poly,PLANE3D *plane, DISTPTS3D *dl) { int i,j,k; double f, s1, s2; VECTOR3D projp1_projp2; RTPOINT3DZ p1, p2,projp1, projp2, intersectionp; rt_getPoint3dz_p(ctx, pa, 0, &p1); s1=project_point_on_plane(ctx, &p1, plane, &projp1); /*the sign of s1 tells us on which side of the plane the point is. */ rt_dist3d_pt_poly(ctx, &p1, poly, plane,&projp1, dl); for (i=1;inpoints;i++) { int intersects; rt_getPoint3dz_p(ctx, pa, i, &p2); s2=project_point_on_plane(ctx, &p2, plane, &projp2); rt_dist3d_pt_poly(ctx, &p2, poly, plane,&projp2, dl); /*If s1and s2 has different signs that means they are on different sides of the plane of the polygon. That means that the edge between the points crosses the plane and might intersect with the polygon*/ if((s1*s2)<=0) { f=fabs(s1)/(fabs(s1)+fabs(s2)); /*The size of s1 and s2 is the distance from the point to the plane.*/ get_3dvector_from_points(ctx, &projp1, &projp2,&projp1_projp2); /*get the point where the line segment crosses the plane*/ intersectionp.x=projp1.x+f*projp1_projp2.x; intersectionp.y=projp1.y+f*projp1_projp2.y; intersectionp.z=projp1.z+f*projp1_projp2.z; intersects = RT_TRUE; /*We set intersects to true until the opposite is proved*/ if(pt_in_ring_3d(ctx, &intersectionp, poly->rings[0], plane)) /*Inside outer ring*/ { for (k=1;knrings; k++) { /* Inside a hole, so no intersection with the polygon*/ if ( pt_in_ring_3d(ctx, &intersectionp, poly->rings[k], plane )) { intersects=RT_FALSE; break; } } if(intersects) { dl->distance=0.0; dl->p1.x=intersectionp.x; dl->p1.y=intersectionp.y; dl->p1.z=intersectionp.z; dl->p2.x=intersectionp.x; dl->p2.y=intersectionp.y; dl->p2.z=intersectionp.z; return RT_TRUE; } } } projp1=projp2; s1=s2; p1=p2; } /*check or pointarray against boundary and inner boundaries of the polygon*/ for (j=0;jnrings;j++) { rt_dist3d_ptarray_ptarray(ctx, pa, poly->rings[j], dl); } return RT_TRUE; } /** Here we define the plane of a polygon (boundary pointarray of a polygon) the plane is stored as a pont in plane (plane.pop) and a normal vector (plane.pv) */ int define_plane(const RTCTX *ctx, RTPOINTARRAY *pa, PLANE3D *pl) { int i,j, numberofvectors, pointsinslice; RTPOINT3DZ p, p1, p2; double sumx=0; double sumy=0; double sumz=0; double vl; /*vector length*/ VECTOR3D v1, v2, v; if((pa->npoints-1)==3) /*Triangle is special case*/ { pointsinslice=1; } else { pointsinslice=(int) floor((pa->npoints-1)/4); /*divide the pointarray into 4 slices*/ } /*find the avg point*/ for (i=0;i<(pa->npoints-1);i++) { rt_getPoint3dz_p(ctx, pa, i, &p); sumx+=p.x; sumy+=p.y; sumz+=p.z; } pl->pop.x=(sumx/(pa->npoints-1)); pl->pop.y=(sumy/(pa->npoints-1)); pl->pop.z=(sumz/(pa->npoints-1)); sumx=0; sumy=0; sumz=0; numberofvectors= floor((pa->npoints-1)/pointsinslice); /*the number of vectors we try can be 3, 4 or 5*/ rt_getPoint3dz_p(ctx, pa, 0, &p1); for (j=pointsinslice;jnpoints;j+=pointsinslice) { rt_getPoint3dz_p(ctx, pa, j, &p2); if (!get_3dvector_from_points(ctx, &(pl->pop), &p1, &v1) || !get_3dvector_from_points(ctx, &(pl->pop), &p2, &v2)) return RT_FALSE; /*perpendicular vector is cross product of v1 and v2*/ if (!get_3dcross_product(ctx, &v1,&v2, &v)) return RT_FALSE; vl=VECTORLENGTH(v); sumx+=(v.x/vl); sumy+=(v.y/vl); sumz+=(v.z/vl); p1=p2; } pl->pv.x=(sumx/numberofvectors); pl->pv.y=(sumy/numberofvectors); pl->pv.z=(sumz/numberofvectors); return 1; } /** Finds a point on a plane from where the original point is perpendicular to the plane */ double project_point_on_plane(const RTCTX *ctx, RTPOINT3DZ *p, PLANE3D *pl, RTPOINT3DZ *p0) { /*In our plane definition we have a point on the plane and a normal vektor (pl.pv), perpendicular to the plane this vector will be paralell to the line between our inputted point above the plane and the point we are searching for on the plane. So, we already have a direction from p to find p0, but we don't know the distance. */ VECTOR3D v1; double f; if (!get_3dvector_from_points(ctx, &(pl->pop), p, &v1)) return RT_FALSE; f=-(DOT(pl->pv,v1)/DOT(pl->pv,pl->pv)); p0->x=p->x+pl->pv.x*f; p0->y=p->y+pl->pv.y*f; p0->z=p->z+pl->pv.z*f; return f; } /** * pt_in_ring_3d(ctx): crossing number test for a point in a polygon * input: p = a point, * pa = vertex points of a ring V[n+1] with V[n]=V[0] * plane=the plane that the vertex points are lying on * returns: 0 = outside, 1 = inside * * Our polygons have first and last point the same, * * The difference in 3D variant is that we exclude the dimension that faces the plane least. * That is the dimension with the highest number in pv */ int pt_in_ring_3d(const RTCTX *ctx, const RTPOINT3DZ *p, const RTPOINTARRAY *ring,PLANE3D *plane) { int cn = 0; /* the crossing number counter */ int i; RTPOINT3DZ v1, v2; RTPOINT3DZ first, last; rt_getPoint3dz_p(ctx, ring, 0, &first); rt_getPoint3dz_p(ctx, ring, ring->npoints-1, &last); if ( memcmp(&first, &last, sizeof(RTPOINT3DZ)) ) { rterror(ctx, "pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)", first.x, first.y, first.z, last.x, last.y, last.z); return RT_FALSE; } RTDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z); /* printPA(ctx, ring); */ /* loop through all edges of the polygon */ rt_getPoint3dz_p(ctx, ring, 0, &v1); if(fabs(plane->pv.z)>=fabs(plane->pv.x)&&fabs(plane->pv.z)>=fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x and y vector we project the ring to the xy-plane*/ { for (i=0; inpoints-1; i++) { double vt; rt_getPoint3dz_p(ctx, ring, i+1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.y <= p->y) && (v2.y > p->y)) /* a downward crossing */ || ((v1.y > p->y) && (v2.y <= p->y)) ) { vt = (double)(p->y - v1.y) / (v2.y - v1.y); /* P.x x < v1.x + vt * (v2.x - v1.x)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } else if(fabs(plane->pv.y)>=fabs(plane->pv.x)&&fabs(plane->pv.y)>=fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger than x and z vector we project the ring to the xz-plane*/ { for (i=0; inpoints-1; i++) { double vt; rt_getPoint3dz_p(ctx, ring, i+1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z)) ) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* P.x x < v1.x + vt * (v2.x - v1.x)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } else /*Hopefully we only have the cases where x part of the normal vector is largest left*/ { for (i=0; inpoints-1; i++) { double vt; rt_getPoint3dz_p(ctx, ring, i+1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z)) ) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* P.x y < v1.y + vt * (v2.y - v1.y)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } RTDEBUGF(3, "pt_in_ring_3d returning %d", cn&1); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ } /*------------------------------------------------------------------------------------------------------------ End of Brute force functions --------------------------------------------------------------------------------------------------------------*/ src/measures3d.h000066400000000000000000000077421271715413500141110ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011 Nicklas Avén * **********************************************************************/ #ifndef _MEASURES3D_H #define _MEASURES3D_H 1 #include #include "measures.h" #define DOT(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) #define VECTORLENGTH(v) sqrt(((v).x * (v).x) + ((v).y * (v).y) + ((v).z * (v).z)) /** Structure used in distance-calculations */ typedef struct { double distance; /*the distance between p1 and p2*/ RTPOINT3DZ p1; RTPOINT3DZ p2; int mode; /*the direction of looking, if thedir = -1 then we look for 3dmaxdistance and if it is 1 then we look for 3dmindistance*/ int twisted; /*To preserve the order of incoming points to match the first and second point in 3dshortest and 3dlongest line*/ double tolerance; /*the tolerance for 3ddwithin and 3ddfullywithin*/ } DISTPTS3D; typedef struct { double x,y,z; } VECTOR3D; typedef struct { RTPOINT3DZ pop; /*Point On Plane*/ VECTOR3D pv; /*Perpendicular normal vector*/ } PLANE3D; /* Geometry returning functions */ RTGEOM * rt_dist3d_distancepoint(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2,int srid,int mode); RTGEOM * rt_dist3d_distanceline(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2,int srid,int mode); /* Preprocessing functions */ int rt_dist3d_distribute_bruteforce(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS3D *dl); int rt_dist3d_recursive(const RTCTX *ctx, const RTGEOM *rtg1,const RTGEOM *rtg2, DISTPTS3D *dl); int rt_dist3d_distribute_fast(const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS3D *dl); /* Brute force functions */ int rt_dist3d_pt_ptarray(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINTARRAY *pa, DISTPTS3D *dl); int rt_dist3d_point_point(const RTCTX *ctx, RTPOINT *point1, RTPOINT *point2, DISTPTS3D *dl); int rt_dist3d_point_line(const RTCTX *ctx, RTPOINT *point, RTLINE *line, DISTPTS3D *dl); int rt_dist3d_line_line(const RTCTX *ctx, RTLINE *line1,RTLINE *line2 , DISTPTS3D *dl); int rt_dist3d_point_poly(const RTCTX *ctx, RTPOINT *point, RTPOLY *poly, DISTPTS3D *dl); int rt_dist3d_line_poly(const RTCTX *ctx, RTLINE *line, RTPOLY *poly, DISTPTS3D *dl); int rt_dist3d_poly_poly(const RTCTX *ctx, RTPOLY *poly1, RTPOLY *poly2, DISTPTS3D *dl); int rt_dist3d_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,DISTPTS3D *dl); int rt_dist3d_seg_seg(const RTCTX *ctx, RTPOINT3DZ *A, RTPOINT3DZ *B, RTPOINT3DZ *C, RTPOINT3DZ *D, DISTPTS3D *dl); int rt_dist3d_pt_pt(const RTCTX *ctx, RTPOINT3DZ *p1, RTPOINT3DZ *p2, DISTPTS3D *dl); int rt_dist3d_pt_seg(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINT3DZ *A, RTPOINT3DZ *B, DISTPTS3D *dl); int rt_dist3d_pt_poly(const RTCTX *ctx, RTPOINT3DZ *p, RTPOLY *poly, PLANE3D *plane,RTPOINT3DZ *projp, DISTPTS3D *dl); int rt_dist3d_ptarray_poly(const RTCTX *ctx, RTPOINTARRAY *pa, RTPOLY *poly, PLANE3D *plane, DISTPTS3D *dl); double project_point_on_plane(const RTCTX *ctx, RTPOINT3DZ *p, PLANE3D *pl, RTPOINT3DZ *p0); int define_plane(const RTCTX *ctx, RTPOINTARRAY *pa, PLANE3D *pl); int pt_in_ring_3d(const RTCTX *ctx, const RTPOINT3DZ *p, const RTPOINTARRAY *ring,PLANE3D *plane); /* Helper functions */ #endif /* !defined _MEASURES3D_H */ src/ptarray.c000066400000000000000000001327501271715413500135110ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include "rttopo_config.h" /*#define RTGEOM_DEBUG_LEVEL 4*/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" int ptarray_has_z(const RTCTX *ctx, const RTPOINTARRAY *pa) { if ( ! pa ) return RT_FALSE; return RTFLAGS_GET_Z(pa->flags); } int ptarray_has_m(const RTCTX *ctx, const RTPOINTARRAY *pa) { if ( ! pa ) return RT_FALSE; return RTFLAGS_GET_M(pa->flags); } /* * Size of point represeneted in the RTPOINTARRAY * 16 for 2d, 24 for 3d, 32 for 4d */ int inline ptarray_point_size(const RTCTX *ctx, const RTPOINTARRAY *pa) { RTDEBUGF(5, "ptarray_point_size: RTFLAGS_NDIMS(pa->flags)=%x",RTFLAGS_NDIMS(pa->flags)); return sizeof(double)*RTFLAGS_NDIMS(pa->flags); } RTPOINTARRAY* ptarray_construct(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints) { RTPOINTARRAY *pa = ptarray_construct_empty(ctx, hasz, hasm, npoints); pa->npoints = npoints; return pa; } RTPOINTARRAY* ptarray_construct_empty(const RTCTX *ctx, char hasz, char hasm, uint32_t maxpoints) { RTPOINTARRAY *pa = rtalloc(ctx, sizeof(RTPOINTARRAY)); pa->serialized_pointlist = NULL; /* Set our dimsionality info on the bitmap */ pa->flags = gflags(ctx, hasz, hasm, 0); /* We will be allocating a bit of room */ pa->npoints = 0; pa->maxpoints = maxpoints; /* Allocate the coordinate array */ if ( maxpoints > 0 ) pa->serialized_pointlist = rtalloc(ctx, maxpoints * ptarray_point_size(ctx, pa)); else pa->serialized_pointlist = NULL; return pa; } /* * Add a point into a pointarray. Only adds as many dimensions as the * pointarray supports. */ int ptarray_insert_point(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *p, int where) { size_t point_size = ptarray_point_size(ctx, pa); RTDEBUGF(5,"pa = %p; p = %p; where = %d", pa, p, where); RTDEBUGF(5,"pa->npoints = %d; pa->maxpoints = %d", pa->npoints, pa->maxpoints); if ( RTFLAGS_GET_READONLY(pa->flags) ) { rterror(ctx, "ptarray_insert_point: called on read-only point array"); return RT_FAILURE; } /* Error on invalid offset value */ if ( where > pa->npoints || where < 0) { rterror(ctx, "ptarray_insert_point: offset out of range (%d)", where); return RT_FAILURE; } /* If we have no storage, let's allocate some */ if( pa->maxpoints == 0 || ! pa->serialized_pointlist ) { pa->maxpoints = 32; pa->npoints = 0; pa->serialized_pointlist = rtalloc(ctx, ptarray_point_size(ctx, pa) * pa->maxpoints); } /* Error out if we have a bad situation */ if ( pa->npoints > pa->maxpoints ) { rterror(ctx, "npoints (%d) is greated than maxpoints (%d)", pa->npoints, pa->maxpoints); return RT_FAILURE; } /* Check if we have enough storage, add more if necessary */ if( pa->npoints == pa->maxpoints ) { pa->maxpoints *= 2; pa->serialized_pointlist = rtrealloc(ctx, pa->serialized_pointlist, ptarray_point_size(ctx, pa) * pa->maxpoints); } /* Make space to insert the new point */ if( where < pa->npoints ) { size_t copy_size = point_size * (pa->npoints - where); memmove(rt_getPoint_internal(ctx, pa, where+1), rt_getPoint_internal(ctx, pa, where), copy_size); RTDEBUGF(5,"copying %d bytes to start vertex %d from start vertex %d", copy_size, where+1, where); } /* We have one more point */ ++pa->npoints; /* Copy the new point into the gap */ ptarray_set_point4d(ctx, pa, where, p); RTDEBUGF(5,"copying new point to start vertex %d", point_size, where); return RT_SUCCESS; } int ptarray_append_point(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *pt, int repeated_points) { /* Check for pathology */ if( ! pa || ! pt ) { rterror(ctx, "ptarray_append_point: null input"); return RT_FAILURE; } /* Check for duplicate end point */ if ( repeated_points == RT_FALSE && pa->npoints > 0 ) { RTPOINT4D tmp; rt_getPoint4d_p(ctx, pa, pa->npoints-1, &tmp); RTDEBUGF(4,"checking for duplicate end point (pt = POINT(%g %g) pa->npoints-q = POINT(%g %g))",pt->x,pt->y,tmp.x,tmp.y); /* Return RT_SUCCESS and do nothing else if previous point in list is equal to this one */ if ( (pt->x == tmp.x) && (pt->y == tmp.y) && (RTFLAGS_GET_Z(pa->flags) ? pt->z == tmp.z : 1) && (RTFLAGS_GET_M(pa->flags) ? pt->m == tmp.m : 1) ) { return RT_SUCCESS; } } /* Append is just a special case of insert */ return ptarray_insert_point(ctx, pa, pt, pa->npoints); } int ptarray_append_ptarray(const RTCTX *ctx, RTPOINTARRAY *pa1, RTPOINTARRAY *pa2, double gap_tolerance) { unsigned int poff = 0; unsigned int npoints; unsigned int ncap; unsigned int ptsize; /* Check for pathology */ if( ! pa1 || ! pa2 ) { rterror(ctx, "ptarray_append_ptarray: null input"); return RT_FAILURE; } npoints = pa2->npoints; if ( ! npoints ) return RT_SUCCESS; /* nothing more to do */ if( RTFLAGS_GET_READONLY(pa1->flags) ) { rterror(ctx, "ptarray_append_ptarray: target pointarray is read-only"); return RT_FAILURE; } if( RTFLAGS_GET_ZM(pa1->flags) != RTFLAGS_GET_ZM(pa2->flags) ) { rterror(ctx, "ptarray_append_ptarray: appending mixed dimensionality is not allowed"); return RT_FAILURE; } ptsize = ptarray_point_size(ctx, pa1); /* Check for duplicate end point */ if ( pa1->npoints ) { RTPOINT2D tmp1, tmp2; rt_getPoint2d_p(ctx, pa1, pa1->npoints-1, &tmp1); rt_getPoint2d_p(ctx, pa2, 0, &tmp2); /* If the end point and start point are the same, then don't copy start point */ if (p2d_same(ctx, &tmp1, &tmp2)) { poff = 1; --npoints; } else if ( gap_tolerance == 0 || ( gap_tolerance > 0 && distance2d_pt_pt(ctx, &tmp1, &tmp2) > gap_tolerance ) ) { rterror(ctx, "Second line start point too far from first line end point"); return RT_FAILURE; } } /* Check if we need extra space */ ncap = pa1->npoints + npoints; if ( pa1->maxpoints < ncap ) { pa1->maxpoints = ncap > pa1->maxpoints*2 ? ncap : pa1->maxpoints*2; pa1->serialized_pointlist = rtrealloc(ctx, pa1->serialized_pointlist, ptsize * pa1->maxpoints); } memcpy(rt_getPoint_internal(ctx, pa1, pa1->npoints), rt_getPoint_internal(ctx, pa2, poff), ptsize * npoints); pa1->npoints = ncap; return RT_SUCCESS; } /* * Add a point into a pointarray. Only adds as many dimensions as the * pointarray supports. */ int ptarray_remove_point(const RTCTX *ctx, RTPOINTARRAY *pa, int where) { size_t ptsize = ptarray_point_size(ctx, pa); /* Check for pathology */ if( ! pa ) { rterror(ctx, "ptarray_remove_point: null input"); return RT_FAILURE; } /* Error on invalid offset value */ if ( where >= pa->npoints || where < 0) { rterror(ctx, "ptarray_remove_point: offset out of range (%d)", where); return RT_FAILURE; } /* If the point is any but the last, we need to copy the data back one point */ if( where < pa->npoints - 1 ) { memmove(rt_getPoint_internal(ctx, pa, where), rt_getPoint_internal(ctx, pa, where+1), ptsize * (pa->npoints - where - 1)); } /* We have one less point */ pa->npoints--; return RT_SUCCESS; } /** * Build a new #RTPOINTARRAY, but on top of someone else's ordinate array. * Flag as read-only, so that ptarray_free(ctx) does not free the serialized_ptlist */ RTPOINTARRAY* ptarray_construct_reference_data(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints, uint8_t *ptlist) { RTPOINTARRAY *pa = rtalloc(ctx, sizeof(RTPOINTARRAY)); RTDEBUGF(5, "hasz = %d, hasm = %d, npoints = %d, ptlist = %p", hasz, hasm, npoints, ptlist); pa->flags = gflags(ctx, hasz, hasm, 0); RTFLAGS_SET_READONLY(pa->flags, 1); /* We don't own this memory, so we can't alter or free it. */ pa->npoints = npoints; pa->maxpoints = npoints; pa->serialized_pointlist = ptlist; return pa; } RTPOINTARRAY* ptarray_construct_copy_data(const RTCTX *ctx, char hasz, char hasm, uint32_t npoints, const uint8_t *ptlist) { RTPOINTARRAY *pa = rtalloc(ctx, sizeof(RTPOINTARRAY)); pa->flags = gflags(ctx, hasz, hasm, 0); pa->npoints = npoints; pa->maxpoints = npoints; if ( npoints > 0 ) { pa->serialized_pointlist = rtalloc(ctx, ptarray_point_size(ctx, pa) * npoints); memcpy(pa->serialized_pointlist, ptlist, ptarray_point_size(ctx, pa) * npoints); } else { pa->serialized_pointlist = NULL; } return pa; } void ptarray_free(const RTCTX *ctx, RTPOINTARRAY *pa) { if(pa) { if(pa->serialized_pointlist && ( ! RTFLAGS_GET_READONLY(pa->flags) ) ) rtfree(ctx, pa->serialized_pointlist); rtfree(ctx, pa); RTDEBUG(5,"Freeing a PointArray"); } } void ptarray_reverse(const RTCTX *ctx, RTPOINTARRAY *pa) { /* TODO change this to double array operations once point array is double aligned */ RTPOINT4D pbuf; uint32_t i; int ptsize = ptarray_point_size(ctx, pa); int last = pa->npoints-1; int mid = pa->npoints/2; for (i=0; inpoints ; i++) { rt_getPoint4d_p(ctx, pa, i, &p); d = p.y; p.y = p.x; p.x = d; ptarray_set_point4d(ctx, pa, i, &p); } return pa; } void ptarray_swap_ordinates(const RTCTX *ctx, RTPOINTARRAY *pa, RTORD o1, RTORD o2) { int i; double d, *dp1, *dp2; RTPOINT4D p; #if PARANOIA_LEVEL > 0 assert(o1 < 4); assert(o2 < 4); #endif dp1 = ((double*)&p)+(unsigned)o1; dp2 = ((double*)&p)+(unsigned)o2; for (i=0 ; i < pa->npoints ; i++) { rt_getPoint4d_p(ctx, pa, i, &p); d = *dp2; *dp2 = *dp1; *dp1 = d; ptarray_set_point4d(ctx, pa, i, &p); } } /** * @brief Returns a modified #RTPOINTARRAY so that no segment is * longer than the given distance (computed using 2d). * * Every input point is kept. * Z and M values for added points (if needed) are set to 0. */ RTPOINTARRAY * ptarray_segmentize2d(const RTCTX *ctx, const RTPOINTARRAY *ipa, double dist) { double segdist; RTPOINT4D p1, p2; RTPOINT4D pbuf; RTPOINTARRAY *opa; int ipoff=0; /* input point offset */ int hasz = RTFLAGS_GET_Z(ipa->flags); int hasm = RTFLAGS_GET_M(ipa->flags); pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0; /* Initial storage */ opa = ptarray_construct_empty(ctx, hasz, hasm, ipa->npoints); /* Add first point */ rt_getPoint4d_p(ctx, ipa, ipoff, &p1); ptarray_append_point(ctx, opa, &p1, RT_FALSE); ipoff++; while (ipoffnpoints) { /* * We use these pointers to avoid * "strict-aliasing rules break" warning raised * by gcc (3.3 and up). * * It looks that casting a variable address (also * referred to as "type-punned pointer") * breaks those "strict" rules. * */ RTPOINT4D *p1ptr=&p1, *p2ptr=&p2; rt_getPoint4d_p(ctx, ipa, ipoff, &p2); segdist = distance2d_pt_pt(ctx, (RTPOINT2D *)p1ptr, (RTPOINT2D *)p2ptr); if (segdist > dist) /* add an intermediate point */ { pbuf.x = p1.x + (p2.x-p1.x)/segdist * dist; pbuf.y = p1.y + (p2.y-p1.y)/segdist * dist; if( hasz ) pbuf.z = p1.z + (p2.z-p1.z)/segdist * dist; if( hasm ) pbuf.m = p1.m + (p2.m-p1.m)/segdist * dist; ptarray_append_point(ctx, opa, &pbuf, RT_FALSE); p1 = pbuf; } else /* copy second point */ { ptarray_append_point(ctx, opa, &p2, (ipa->npoints==2)?RT_TRUE:RT_FALSE); p1 = p2; ipoff++; } RT_ON_INTERRUPT(ptarray_free(ctx, opa); return NULL); } return opa; } char ptarray_same(const RTCTX *ctx, const RTPOINTARRAY *pa1, const RTPOINTARRAY *pa2) { uint32_t i; size_t ptsize; if ( RTFLAGS_GET_ZM(pa1->flags) != RTFLAGS_GET_ZM(pa2->flags) ) return RT_FALSE; RTDEBUG(5,"dimensions are the same"); if ( pa1->npoints != pa2->npoints ) return RT_FALSE; RTDEBUG(5,"npoints are the same"); ptsize = ptarray_point_size(ctx, pa1); RTDEBUGF(5, "ptsize = %d", ptsize); for (i=0; inpoints; i++) { if ( memcmp(rt_getPoint_internal(ctx, pa1, i), rt_getPoint_internal(ctx, pa2, i), ptsize) ) return RT_FALSE; RTDEBUGF(5,"point #%d is the same",i); } return RT_TRUE; } RTPOINTARRAY * ptarray_addPoint(const RTCTX *ctx, const RTPOINTARRAY *pa, uint8_t *p, size_t pdims, uint32_t where) { RTPOINTARRAY *ret; RTPOINT4D pbuf; size_t ptsize = ptarray_point_size(ctx, pa); RTDEBUGF(3, "pa %x p %x size %d where %d", pa, p, pdims, where); if ( pdims < 2 || pdims > 4 ) { rterror(ctx, "ptarray_addPoint: point dimension out of range (%d)", pdims); return NULL; } if ( where > pa->npoints ) { rterror(ctx, "ptarray_addPoint: offset out of range (%d)", where); return NULL; } RTDEBUG(3, "called with a %dD point"); pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0.0; memcpy((uint8_t *)&pbuf, p, pdims*sizeof(double)); RTDEBUG(3, "initialized point buffer"); ret = ptarray_construct(ctx, RTFLAGS_GET_Z(pa->flags), RTFLAGS_GET_M(pa->flags), pa->npoints+1); if ( where == -1 ) where = pa->npoints; if ( where ) { memcpy(rt_getPoint_internal(ctx, ret, 0), rt_getPoint_internal(ctx, pa, 0), ptsize*where); } memcpy(rt_getPoint_internal(ctx, ret, where), (uint8_t *)&pbuf, ptsize); if ( where+1 != ret->npoints ) { memcpy(rt_getPoint_internal(ctx, ret, where+1), rt_getPoint_internal(ctx, pa, where), ptsize*(pa->npoints-where)); } return ret; } RTPOINTARRAY * ptarray_removePoint(const RTCTX *ctx, RTPOINTARRAY *pa, uint32_t which) { RTPOINTARRAY *ret; size_t ptsize = ptarray_point_size(ctx, pa); RTDEBUGF(3, "pa %x which %d", pa, which); #if PARANOIA_LEVEL > 0 if ( which > pa->npoints-1 ) { rterror(ctx, "ptarray_removePoint: offset (%d) out of range (%d..%d)", which, 0, pa->npoints-1); return NULL; } if ( pa->npoints < 3 ) { rterror(ctx, "ptarray_removePointe: can't remove a point from a 2-vertex RTPOINTARRAY"); } #endif ret = ptarray_construct(ctx, RTFLAGS_GET_Z(pa->flags), RTFLAGS_GET_M(pa->flags), pa->npoints-1); /* copy initial part */ if ( which ) { memcpy(rt_getPoint_internal(ctx, ret, 0), rt_getPoint_internal(ctx, pa, 0), ptsize*which); } /* copy final part */ if ( which < pa->npoints-1 ) { memcpy(rt_getPoint_internal(ctx, ret, which), rt_getPoint_internal(ctx, pa, which+1), ptsize*(pa->npoints-which-1)); } return ret; } RTPOINTARRAY * ptarray_merge(const RTCTX *ctx, RTPOINTARRAY *pa1, RTPOINTARRAY *pa2) { RTPOINTARRAY *pa; size_t ptsize = ptarray_point_size(ctx, pa1); if (RTFLAGS_GET_ZM(pa1->flags) != RTFLAGS_GET_ZM(pa2->flags)) rterror(ctx, "ptarray_cat: Mixed dimension"); pa = ptarray_construct(ctx, RTFLAGS_GET_Z(pa1->flags), RTFLAGS_GET_M(pa1->flags), pa1->npoints + pa2->npoints); memcpy( rt_getPoint_internal(ctx, pa, 0), rt_getPoint_internal(ctx, pa1, 0), ptsize*(pa1->npoints)); memcpy( rt_getPoint_internal(ctx, pa, pa1->npoints), rt_getPoint_internal(ctx, pa2, 0), ptsize*(pa2->npoints)); ptarray_free(ctx, pa1); ptarray_free(ctx, pa2); return pa; } /** * @brief Deep clone a pointarray (also clones serialized pointlist) */ RTPOINTARRAY * ptarray_clone_deep(const RTCTX *ctx, const RTPOINTARRAY *in) { RTPOINTARRAY *out = rtalloc(ctx, sizeof(RTPOINTARRAY)); size_t size; RTDEBUG(3, "ptarray_clone_deep called."); out->flags = in->flags; out->npoints = in->npoints; out->maxpoints = in->maxpoints; RTFLAGS_SET_READONLY(out->flags, 0); size = in->npoints * ptarray_point_size(ctx, in); out->serialized_pointlist = rtalloc(ctx, size); memcpy(out->serialized_pointlist, in->serialized_pointlist, size); return out; } /** * @brief Clone a RTPOINTARRAY object. Serialized pointlist is not copied. */ RTPOINTARRAY * ptarray_clone(const RTCTX *ctx, const RTPOINTARRAY *in) { RTPOINTARRAY *out = rtalloc(ctx, sizeof(RTPOINTARRAY)); RTDEBUG(3, "ptarray_clone_deep called."); out->flags = in->flags; out->npoints = in->npoints; out->maxpoints = in->maxpoints; RTFLAGS_SET_READONLY(out->flags, 1); out->serialized_pointlist = in->serialized_pointlist; return out; } /** * Check for ring closure using whatever dimensionality is declared on the * pointarray. */ int ptarray_is_closed(const RTCTX *ctx, const RTPOINTARRAY *in) { return 0 == memcmp(rt_getPoint_internal(ctx, in, 0), rt_getPoint_internal(ctx, in, in->npoints-1), ptarray_point_size(ctx, in)); } int ptarray_is_closed_2d(const RTCTX *ctx, const RTPOINTARRAY *in) { return 0 == memcmp(rt_getPoint_internal(ctx, in, 0), rt_getPoint_internal(ctx, in, in->npoints-1), sizeof(RTPOINT2D)); } int ptarray_is_closed_3d(const RTCTX *ctx, const RTPOINTARRAY *in) { return 0 == memcmp(rt_getPoint_internal(ctx, in, 0), rt_getPoint_internal(ctx, in, in->npoints-1), sizeof(POINT3D)); } int ptarray_is_closed_z(const RTCTX *ctx, const RTPOINTARRAY *in) { if ( RTFLAGS_GET_Z(in->flags) ) return ptarray_is_closed_3d(ctx, in); else return ptarray_is_closed_2d(ctx, in); } /** * Return 1 if the point is inside the RTPOINTARRAY, -1 if it is outside, * and 0 if it is on the boundary. */ int ptarray_contains_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt) { return ptarray_contains_point_partial(ctx, pa, pt, RT_TRUE, NULL); } int ptarray_contains_point_partial(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt, int check_closed, int *winding_number) { int wn = 0; int i; double side; const RTPOINT2D *seg1; const RTPOINT2D *seg2; double ymin, ymax; seg1 = rt_getPoint2d_cp(ctx, pa, 0); seg2 = rt_getPoint2d_cp(ctx, pa, pa->npoints-1); if ( check_closed && ! p2d_same(ctx, seg1, seg2) ) rterror(ctx, "ptarray_contains_point called on unclosed ring"); for ( i=1; i < pa->npoints; i++ ) { seg2 = rt_getPoint2d_cp(ctx, pa, i); /* Zero length segments are ignored. */ if ( seg1->x == seg2->x && seg1->y == seg2->y ) { seg1 = seg2; continue; } ymin = FP_MIN(seg1->y, seg2->y); ymax = FP_MAX(seg1->y, seg2->y); /* Only test segments in our vertical range */ if ( pt->y > ymax || pt->y < ymin ) { seg1 = seg2; continue; } side = rt_segment_side(ctx, seg1, seg2, pt); /* * A point on the boundary of a ring is not contained. * WAS: if (fabs(side) < 1e-12), see #852 */ if ( (side == 0) && rt_pt_in_seg(ctx, pt, seg1, seg2) ) { return RT_BOUNDARY; } /* * If the point is to the left of the line, and it's rising, * then the line is to the right of the point and * circling counter-clockwise, so incremement. */ if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) ) { wn++; } /* * If the point is to the right of the line, and it's falling, * then the line is to the right of the point and circling * clockwise, so decrement. */ else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } seg1 = seg2; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return RT_OUTSIDE; } /* Inside */ return RT_INSIDE; } /** * For RTPOINTARRAYs representing CIRCULARSTRINGS. That is, linked triples * with each triple being control points of a circular arc. Such * RTPOINTARRAYs have an odd number of vertices. * * Return 1 if the point is inside the RTPOINTARRAY, -1 if it is outside, * and 0 if it is on the boundary. */ int ptarrayarc_contains_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt) { return ptarrayarc_contains_point_partial(ctx, pa, pt, RT_TRUE /* Check closed*/, NULL); } int ptarrayarc_contains_point_partial(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt, int check_closed, int *winding_number) { int wn = 0; int i, side; const RTPOINT2D *seg1; const RTPOINT2D *seg2; const RTPOINT2D *seg3; RTGBOX gbox; /* Check for not an arc ring (artays have odd # of points) */ if ( (pa->npoints % 2) == 0 ) { rterror(ctx, "ptarrayarc_contains_point called with even number of points"); return RT_OUTSIDE; } /* Check for not an arc ring (artays have >= 3 points) */ if ( pa->npoints < 3 ) { rterror(ctx, "ptarrayarc_contains_point called too-short pointarray"); return RT_OUTSIDE; } /* Check for unclosed case */ seg1 = rt_getPoint2d_cp(ctx, pa, 0); seg3 = rt_getPoint2d_cp(ctx, pa, pa->npoints-1); if ( check_closed && ! p2d_same(ctx, seg1, seg3) ) { rterror(ctx, "ptarrayarc_contains_point called on unclosed ring"); return RT_OUTSIDE; } /* OK, it's closed. Is it just one circle? */ else if ( p2d_same(ctx, seg1, seg3) && pa->npoints == 3 ) { double radius, d; RTPOINT2D c; seg2 = rt_getPoint2d_cp(ctx, pa, 1); /* Wait, it's just a point, so it can't contain anything */ if ( rt_arc_is_pt(ctx, seg1, seg2, seg3) ) return RT_OUTSIDE; /* See if the point is within the circle radius */ radius = rt_arc_center(ctx, seg1, seg2, seg3, &c); d = distance2d_pt_pt(ctx, pt, &c); if ( FP_EQUALS(d, radius) ) return RT_BOUNDARY; /* Boundary of circle */ else if ( d < radius ) return RT_INSIDE; /* Inside circle */ else return RT_OUTSIDE; /* Outside circle */ } else if ( p2d_same(ctx, seg1, pt) || p2d_same(ctx, seg3, pt) ) { return RT_BOUNDARY; /* Boundary case */ } /* Start on the ring */ seg1 = rt_getPoint2d_cp(ctx, pa, 0); for ( i=1; i < pa->npoints; i += 2 ) { seg2 = rt_getPoint2d_cp(ctx, pa, i); seg3 = rt_getPoint2d_cp(ctx, pa, i+1); /* Catch an easy boundary case */ if( p2d_same(ctx, seg3, pt) ) return RT_BOUNDARY; /* Skip arcs that have no size */ if ( rt_arc_is_pt(ctx, seg1, seg2, seg3) ) { seg1 = seg3; continue; } /* Only test segments in our vertical range */ rt_arc_calculate_gbox_cartesian_2d(ctx, seg1, seg2, seg3, &gbox); if ( pt->y > gbox.ymax || pt->y < gbox.ymin ) { seg1 = seg3; continue; } /* Outside of horizontal range, and not between end points we also skip */ if ( (pt->x > gbox.xmax || pt->x < gbox.xmin) && (pt->y > FP_MAX(seg1->y, seg3->y) || pt->y < FP_MIN(seg1->y, seg3->y)) ) { seg1 = seg3; continue; } side = rt_arc_side(ctx, seg1, seg2, seg3, pt); /* On the boundary */ if ( (side == 0) && rt_pt_in_arc(ctx, pt, seg1, seg2, seg3) ) { return RT_BOUNDARY; } /* Going "up"! Point to left of arc. */ if ( side < 0 && (seg1->y <= pt->y) && (pt->y < seg3->y) ) { wn++; } /* Going "down"! */ if ( side > 0 && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } /* Inside the arc! */ if ( pt->x <= gbox.xmax && pt->x >= gbox.xmin ) { RTPOINT2D C; double radius = rt_arc_center(ctx, seg1, seg2, seg3, &C); double d = distance2d_pt_pt(ctx, pt, &C); /* On the boundary! */ if ( d == radius ) return RT_BOUNDARY; /* Within the arc! */ if ( d < radius ) { /* Left side, increment winding number */ if ( side < 0 ) wn++; /* Right side, decrement winding number */ if ( side > 0 ) wn--; } } seg1 = seg3; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return RT_OUTSIDE; } /* Inside */ return RT_INSIDE; } /** * Returns the area in cartesian units. Area is negative if ring is oriented CCW, * positive if it is oriented CW and zero if the ring is degenerate or flat. * http://en.wikipedia.org/wiki/Shoelace_formula */ double ptarray_signed_area(const RTCTX *ctx, const RTPOINTARRAY *pa) { const RTPOINT2D *P1; const RTPOINT2D *P2; const RTPOINT2D *P3; double sum = 0.0; double x0, x, y1, y2; int i; if (! pa || pa->npoints < 3 ) return 0.0; P1 = rt_getPoint2d_cp(ctx, pa, 0); P2 = rt_getPoint2d_cp(ctx, pa, 1); x0 = P1->x; for ( i = 1; i < pa->npoints - 1; i++ ) { P3 = rt_getPoint2d_cp(ctx, pa, i+1); x = P2->x - x0; y1 = P3->y; y2 = P1->y; sum += x * (y2-y1); /* Move forwards! */ P1 = P2; P2 = P3; } return sum / 2.0; } int ptarray_isccw(const RTCTX *ctx, const RTPOINTARRAY *pa) { double area = 0; area = ptarray_signed_area(ctx, pa); if ( area > 0 ) return RT_FALSE; else return RT_TRUE; } RTPOINTARRAY* ptarray_force_dims(const RTCTX *ctx, const RTPOINTARRAY *pa, int hasz, int hasm) { /* TODO handle zero-length point arrays */ int i; int in_hasz = RTFLAGS_GET_Z(pa->flags); int in_hasm = RTFLAGS_GET_M(pa->flags); RTPOINT4D pt; RTPOINTARRAY *pa_out = ptarray_construct_empty(ctx, hasz, hasm, pa->npoints); for( i = 0; i < pa->npoints; i++ ) { rt_getPoint4d_p(ctx, pa, i, &pt); if( hasz && ! in_hasz ) pt.z = 0.0; if( hasm && ! in_hasm ) pt.m = 0.0; ptarray_append_point(ctx, pa_out, &pt, RT_TRUE); } return pa_out; } RTPOINTARRAY * ptarray_substring(const RTCTX *ctx, RTPOINTARRAY *ipa, double from, double to, double tolerance) { RTPOINTARRAY *dpa; RTPOINT4D pt; RTPOINT4D p1, p2; RTPOINT4D *p1ptr=&p1; /* don't break strict-aliasing rule */ RTPOINT4D *p2ptr=&p2; int nsegs, i; double length, slength, tlength; int state = 0; /* 0=before, 1=inside */ /* * Create a dynamic pointarray with an initial capacity * equal to full copy of input points */ dpa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(ipa->flags), RTFLAGS_GET_M(ipa->flags), ipa->npoints); /* Compute total line length */ length = ptarray_length_2d(ctx, ipa); RTDEBUGF(3, "Total length: %g", length); /* Get 'from' and 'to' lengths */ from = length*from; to = length*to; RTDEBUGF(3, "From/To: %g/%g", from, to); tlength = 0; rt_getPoint4d_p(ctx, ipa, 0, &p1); nsegs = ipa->npoints - 1; for ( i = 0; i < nsegs; i++ ) { double dseg; rt_getPoint4d_p(ctx, ipa, i+1, &p2); RTDEBUGF(3 ,"Segment %d: (%g,%g,%g,%g)-(%g,%g,%g,%g)", i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m); /* Find the length of this segment */ slength = distance2d_pt_pt(ctx, (RTPOINT2D *)p1ptr, (RTPOINT2D *)p2ptr); /* * We are before requested start. */ if ( state == 0 ) /* before */ { RTDEBUG(3, " Before start"); if ( fabs ( from - ( tlength + slength ) ) <= tolerance ) { RTDEBUG(3, " Second point is our start"); /* * Second point is our start */ ptarray_append_point(ctx, dpa, &p2, RT_FALSE); state=1; /* we're inside now */ goto END; } else if ( fabs(from - tlength) <= tolerance ) { RTDEBUG(3, " First point is our start"); /* * First point is our start */ ptarray_append_point(ctx, dpa, &p1, RT_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } /* * Didn't reach the 'from' point, * nothing to do */ else if ( from > tlength + slength ) goto END; else /* tlength < from < tlength+slength */ { RTDEBUG(3, " Seg contains first point"); /* * Our start is between first and * second point */ dseg = (from - tlength) / slength; interpolate_point4d(ctx, &p1, &p2, &pt, dseg); ptarray_append_point(ctx, dpa, &pt, RT_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } } if ( state == 1 ) /* inside */ { RTDEBUG(3, " Inside"); /* * 'to' point is our second point. */ if ( fabs(to - ( tlength + slength ) ) <= tolerance ) { RTDEBUG(3, " Second point is our end"); ptarray_append_point(ctx, dpa, &p2, RT_FALSE); break; /* substring complete */ } /* * 'to' point is our first point. * (should only happen if 'to' is 0) */ else if ( fabs(to - tlength) <= tolerance ) { RTDEBUG(3, " First point is our end"); ptarray_append_point(ctx, dpa, &p1, RT_FALSE); break; /* substring complete */ } /* * Didn't reach the 'end' point, * just copy second point */ else if ( to > tlength + slength ) { ptarray_append_point(ctx, dpa, &p2, RT_FALSE); goto END; } /* * 'to' point falls on this segment * Interpolate and break. */ else if ( to < tlength + slength ) { RTDEBUG(3, " Seg contains our end"); dseg = (to - tlength) / slength; interpolate_point4d(ctx, &p1, &p2, &pt, dseg); ptarray_append_point(ctx, dpa, &pt, RT_FALSE); break; } else { RTDEBUG(3, "Unhandled case"); } } END: tlength += slength; memcpy(&p1, &p2, sizeof(RTPOINT4D)); } RTDEBUGF(3, "Out of loop, ptarray has %d points", dpa->npoints); return dpa; } /* * Write into the *ret argument coordinates of the closes point on * the given segment to the reference input point. */ void closest_point_on_segment(const RTCTX *ctx, const RTPOINT4D *p, const RTPOINT4D *A, const RTPOINT4D *B, RTPOINT4D *ret) { double r; if ( FP_EQUALS(A->x, B->x) && FP_EQUALS(A->y, B->y) ) { *ret = *A; return; } /* * We use comp.graphics.algorithms Frequently Asked Questions method * * (1) AC dot AB * r = ---------- * ||AB||^2 * r has the following meaning: * r=0 P = A * r=1 P = B * r<0 P is on the backward extension of AB * r>1 P is on the forward extension of AB * 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); if (r<0) { *ret = *A; return; } if (r>1) { *ret = *B; return; } ret->x = A->x + ( (B->x - A->x) * r ); ret->y = A->y + ( (B->y - A->y) * r ); ret->z = A->z + ( (B->z - A->z) * r ); ret->m = A->m + ( (B->m - A->m) * r ); } /* * Given a point, returns the location of closest point on pointarray * and, optionally, it's actual distance from the point array. */ double ptarray_locate_point(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT4D *p4d, double *mindistout, RTPOINT4D *proj4d) { double mindist=-1; double tlen, plen; int t, seg=-1; RTPOINT4D start4d, end4d, projtmp; RTPOINT2D proj, p; const RTPOINT2D *start = NULL, *end = NULL; /* Initialize our 2D copy of the input parameter */ p.x = p4d->x; p.y = p4d->y; if ( ! proj4d ) proj4d = &projtmp; start = rt_getPoint2d_cp(ctx, pa, 0); /* If the pointarray has only one point, the nearest point is */ /* just that point */ if ( pa->npoints == 1 ) { rt_getPoint4d_p(ctx, pa, 0, proj4d); if ( mindistout ) *mindistout = distance2d_pt_pt(ctx, &p, start); return 0.0; } /* Loop through pointarray looking for nearest segment */ for (t=1; tnpoints; t++) { double dist; end = rt_getPoint2d_cp(ctx, pa, t); dist = distance2d_pt_seg(ctx, &p, start, end); if (t==1 || dist < mindist ) { mindist = dist; seg=t-1; } if ( mindist == 0 ) { RTDEBUG(3, "Breaking on mindist=0"); break; } start = end; } if ( mindistout ) *mindistout = mindist; RTDEBUGF(3, "Closest segment: %d", seg); RTDEBUGF(3, "mindist: %g", mindist); /* * We need to project the * point on the closest segment. */ rt_getPoint4d_p(ctx, pa, seg, &start4d); rt_getPoint4d_p(ctx, pa, seg+1, &end4d); closest_point_on_segment(ctx, p4d, &start4d, &end4d, proj4d); /* Copy 4D values into 2D holder */ proj.x = proj4d->x; proj.y = proj4d->y; RTDEBUGF(3, "Closest segment:%d, npoints:%d", seg, pa->npoints); /* For robustness, force 1 when closest point == endpoint */ if ( (seg >= (pa->npoints-2)) && p2d_same(ctx, &proj, end) ) { return 1.0; } RTDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y); tlen = ptarray_length_2d(ctx, pa); RTDEBUGF(3, "tlen %g", tlen); /* Location of any point on a zero-length line is 0 */ /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */ if ( tlen == 0 ) return 0; plen=0; start = rt_getPoint2d_cp(ctx, pa, 0); for (t=0; t 180 becomes X - 360 */ void ptarray_longitude_shift(const RTCTX *ctx, RTPOINTARRAY *pa) { int i; double x; for (i=0; inpoints; i++) { memcpy(&x, rt_getPoint_internal(ctx, pa, i), sizeof(double)); if ( x < 0 ) x+= 360; else if ( x > 180 ) x -= 360; memcpy(rt_getPoint_internal(ctx, pa, i), &x, sizeof(double)); } } /* * Returns a RTPOINTARRAY with consecutive equal points * removed. Equality test on all dimensions of input. * * Artays returns a newly allocated object. * */ RTPOINTARRAY * ptarray_remove_repeated_points_minpoints(const RTCTX *ctx, const RTPOINTARRAY *in, double tolerance, int minpoints) { RTPOINTARRAY* out; size_t ptsize; size_t ipn, opn; const RTPOINT2D *last_point, *this_point; double tolsq = tolerance * tolerance; if ( minpoints < 1 ) minpoints = 1; RTDEBUGF(3, "%s called", __func__); /* Single or zero point arrays can't have duplicates */ if ( in->npoints < 3 ) return ptarray_clone_deep(ctx, in); ptsize = ptarray_point_size(ctx, in); RTDEBUGF(3, " ptsize: %d", ptsize); /* Allocate enough space for all points */ out = ptarray_construct(ctx, RTFLAGS_GET_Z(in->flags), RTFLAGS_GET_M(in->flags), in->npoints); /* Now fill up the actual points (NOTE: could be optimized) */ opn=1; memcpy(rt_getPoint_internal(ctx, out, 0), rt_getPoint_internal(ctx, in, 0), ptsize); last_point = rt_getPoint2d_cp(ctx, in, 0); RTDEBUGF(3, " first point copied, out points: %d", opn); for ( ipn = 1; ipn < in->npoints; ++ipn) { this_point = rt_getPoint2d_cp(ctx, in, ipn); if ( (ipn >= in->npoints-minpoints+1 && opn < minpoints) || (tolerance == 0 && memcmp(rt_getPoint_internal(ctx, in, ipn-1), rt_getPoint_internal(ctx, in, ipn), ptsize) != 0) || (tolerance > 0.0 && distance2d_sqr_pt_pt(ctx, last_point, this_point) > tolsq) ) { /* The point is different from the previous, * we add it to output */ memcpy(rt_getPoint_internal(ctx, out, opn++), rt_getPoint_internal(ctx, in, ipn), ptsize); last_point = this_point; RTDEBUGF(3, " Point %d differs from point %d. Out points: %d", ipn, ipn-1, opn); } } RTDEBUGF(3, " in:%d out:%d", out->npoints, opn); out->npoints = opn; return out; } RTPOINTARRAY * ptarray_remove_repeated_points(const RTCTX *ctx, const RTPOINTARRAY *in, double tolerance) { return ptarray_remove_repeated_points_minpoints(ctx, in, tolerance, 2); } static void ptarray_dp_findsplit(const RTCTX *ctx, RTPOINTARRAY *pts, int p1, int p2, int *split, double *dist) { int k; const RTPOINT2D *pk, *pa, *pb; double tmp, d; RTDEBUG(4, "function called"); *split = p1; d = -1; if (p1 + 1 < p2) { pa = rt_getPoint2d_cp(ctx, pts, p1); pb = rt_getPoint2d_cp(ctx, pts, p2); RTDEBUGF(4, "P%d(%f,%f) to P%d(%f,%f)", p1, pa->x, pa->y, p2, pb->x, pb->y); for (k=p1+1; kx, pk->y); /* distance computation */ tmp = distance2d_sqr_pt_seg(ctx, pk, pa, pb); if (tmp > d) { d = tmp; /* record the maximum */ *split = k; RTDEBUGF(4, "P%d is farthest (%g)", k, d); } } *dist = d; } /* length---should be redone if can == 0 */ else { RTDEBUG(3, "segment too short, no split/no dist"); *dist = -1; } } RTPOINTARRAY * ptarray_simplify(const RTCTX *ctx, RTPOINTARRAY *inpts, double epsilon, unsigned int minpts) { int *stack; /* recursion stack */ int sp=-1; /* recursion stack pointer */ int p1, split; double dist; RTPOINTARRAY *outpts; RTPOINT4D pt; double eps_sqr = epsilon * epsilon; /* Allocate recursion stack */ stack = rtalloc(ctx, sizeof(int)*inpts->npoints); p1 = 0; stack[++sp] = inpts->npoints-1; RTDEBUGF(2, "Input has %d pts and %d dims", inpts->npoints, RTFLAGS_NDIMS(inpts->flags)); /* Allocate output RTPOINTARRAY, and add first point. */ outpts = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(inpts->flags), RTFLAGS_GET_M(inpts->flags), inpts->npoints); rt_getPoint4d_p(ctx, inpts, 0, &pt); ptarray_append_point(ctx, outpts, &pt, RT_FALSE); RTDEBUG(3, "Added P0 to simplified point array (size 1)"); do { ptarray_dp_findsplit(ctx, inpts, p1, stack[sp], &split, &dist); RTDEBUGF(3, "Farthest point from P%d-P%d is P%d (dist. %g)", p1, stack[sp], split, dist); if (dist > eps_sqr || ( outpts->npoints+sp+1 < minpts && dist >= 0 ) ) { RTDEBUGF(4, "Added P%d to stack (outpts:%d)", split, sp); stack[++sp] = split; } else { rt_getPoint4d_p(ctx, inpts, stack[sp], &pt); RTDEBUGF(4, "npoints , minpoints %d %d", outpts->npoints, minpts); ptarray_append_point(ctx, outpts, &pt, RT_FALSE); RTDEBUGF(4, "Added P%d to simplified point array (size: %d)", stack[sp], outpts->npoints); p1 = stack[sp--]; } RTDEBUGF(4, "stack pointer = %d", sp); } while (! (sp<0) ); rtfree(ctx, stack); return outpts; } /** * Find the 2d length of the given #RTPOINTARRAY, using circular * arc interpolation between each coordinate triple. * Length(A1, A2, A3, A4, A5) = Length(A1, A2, A3)+Length(A3, A4, A5) */ double ptarray_arc_length_2d(const RTCTX *ctx, const RTPOINTARRAY *pts) { double dist = 0.0; int i; const RTPOINT2D *a1; const RTPOINT2D *a2; const RTPOINT2D *a3; if ( pts->npoints % 2 != 1 ) rterror(ctx, "arc point array with even number of points"); a1 = rt_getPoint2d_cp(ctx, pts, 0); for ( i=2; i < pts->npoints; i += 2 ) { a2 = rt_getPoint2d_cp(ctx, pts, i-1); a3 = rt_getPoint2d_cp(ctx, pts, i); dist += rt_arc_length(ctx, a1, a2, a3); a1 = a3; } return dist; } /** * Find the 2d length of the given #RTPOINTARRAY (even if it's 3d) */ double ptarray_length_2d(const RTCTX *ctx, const RTPOINTARRAY *pts) { double dist = 0.0; int i; const RTPOINT2D *frm; const RTPOINT2D *to; if ( pts->npoints < 2 ) return 0.0; frm = rt_getPoint2d_cp(ctx, pts, 0); for ( i=1; i < pts->npoints; i++ ) { to = rt_getPoint2d_cp(ctx, pts, i); dist += sqrt( ((frm->x - to->x)*(frm->x - to->x)) + ((frm->y - to->y)*(frm->y - to->y)) ); frm = to; } return dist; } /** * Find the 3d/2d length of the given #RTPOINTARRAY * (depending on its dimensionality) */ double ptarray_length(const RTCTX *ctx, const RTPOINTARRAY *pts) { double dist = 0.0; int i; RTPOINT3DZ frm; RTPOINT3DZ to; if ( pts->npoints < 2 ) return 0.0; /* compute 2d length if 3d is not available */ if ( ! RTFLAGS_GET_Z(pts->flags) ) return ptarray_length_2d(ctx, pts); rt_getPoint3dz_p(ctx, pts, 0, &frm); for ( i=1; i < pts->npoints; i++ ) { rt_getPoint3dz_p(ctx, pts, i, &to); dist += sqrt( ((frm.x - to.x)*(frm.x - to.x)) + ((frm.y - to.y)*(frm.y - to.y)) + ((frm.z - to.z)*(frm.z - to.z)) ); frm = to; } return dist; } /* * Get a pointer to nth point of a RTPOINTARRAY. * * Casting to returned pointer to RTPOINT2D* should be safe, * as gserialized format artays keeps the RTPOINTARRAY pointer * aligned to double boundary. */ uint8_t * rt_getPoint_internal(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { size_t size; uint8_t *ptr; #if PARANOIA_LEVEL > 0 if ( pa == NULL ) { rterror(ctx, "rt_getPoint got NULL pointarray"); return NULL; } RTDEBUGF(5, "(n=%d, pa.npoints=%d, pa.maxpoints=%d)",n,pa->npoints,pa->maxpoints); if ( ( n < 0 ) || ( n > pa->npoints ) || ( n >= pa->maxpoints ) ) { rterror(ctx, "rt_getPoint_internal called outside of ptarray range (n=%d, pa.npoints=%d, pa.maxpoints=%d)",n,pa->npoints,pa->maxpoints); return NULL; /*error */ } #endif size = ptarray_point_size(ctx, pa); ptr = pa->serialized_pointlist + size * n; if ( RTFLAGS_NDIMS(pa->flags) == 2) { RTDEBUGF(5, "point = %g %g", *((double*)(ptr)), *((double*)(ptr+8))); } else if ( RTFLAGS_NDIMS(pa->flags) == 3) { RTDEBUGF(5, "point = %g %g %g", *((double*)(ptr)), *((double*)(ptr+8)), *((double*)(ptr+16))); } else if ( RTFLAGS_NDIMS(pa->flags) == 4) { RTDEBUGF(5, "point = %g %g %g %g", *((double*)(ptr)), *((double*)(ptr+8)), *((double*)(ptr+16)), *((double*)(ptr+24))); } return ptr; } /** * Affine transform a pointarray. */ void ptarray_affine(const RTCTX *ctx, RTPOINTARRAY *pa, const RTAFFINE *a) { int i; double x,y,z; RTPOINT4D p4d; RTDEBUG(2, "rtgeom_affine_ptarray start"); if ( RTFLAGS_GET_Z(pa->flags) ) { RTDEBUG(3, " has z"); for (i=0; inpoints; i++) { rt_getPoint4d_p(ctx, pa, i, &p4d); x = p4d.x; y = p4d.y; z = p4d.z; p4d.x = a->afac * x + a->bfac * y + a->cfac * z + a->xoff; p4d.y = a->dfac * x + a->efac * y + a->ffac * z + a->yoff; p4d.z = a->gfac * x + a->hfac * y + a->ifac * z + a->zoff; ptarray_set_point4d(ctx, pa, i, &p4d); RTDEBUGF(3, " POINT %g %g %g => %g %g %g", x, y, x, p4d.x, p4d.y, p4d.z); } } else { RTDEBUG(3, " doesn't have z"); for (i=0; inpoints; i++) { rt_getPoint4d_p(ctx, pa, i, &p4d); x = p4d.x; y = p4d.y; p4d.x = a->afac * x + a->bfac * y + a->xoff; p4d.y = a->dfac * x + a->efac * y + a->yoff; ptarray_set_point4d(ctx, pa, i, &p4d); RTDEBUGF(3, " POINT %g %g %g => %g %g %g", x, y, x, p4d.x, p4d.y, p4d.z); } } RTDEBUG(3, "rtgeom_affine_ptarray end"); } /** * Scale a pointarray. */ void ptarray_scale(const RTCTX *ctx, RTPOINTARRAY *pa, const RTPOINT4D *fact) { int i; RTPOINT4D p4d; RTDEBUG(3, "ptarray_scale start"); for (i=0; inpoints; ++i) { rt_getPoint4d_p(ctx, pa, i, &p4d); p4d.x *= fact->x; p4d.y *= fact->y; p4d.z *= fact->z; p4d.m *= fact->m; ptarray_set_point4d(ctx, pa, i, &p4d); } RTDEBUG(3, "ptarray_scale end"); } int ptarray_startpoint(const RTCTX *ctx, const RTPOINTARRAY* pa, RTPOINT4D* pt) { return rt_getPoint4d_p(ctx, pa, 0, pt); } /* * Stick an array of points to the given gridspec. * Return "gridded" points in *outpts and their number in *outptsn. * * Two consecutive points falling on the same grid cell are collapsed * into one single point. * */ RTPOINTARRAY * ptarray_grid(const RTCTX *ctx, const RTPOINTARRAY *pa, const gridspec *grid) { RTPOINT4D pt; int ipn; /* input point numbers */ RTPOINTARRAY *dpa; RTDEBUGF(2, "ptarray_grid called on %p", pa); dpa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(pa->flags),RTFLAGS_GET_M(pa->flags), pa->npoints); for (ipn=0; ipnnpoints; ++ipn) { rt_getPoint4d_p(ctx, pa, ipn, &pt); if ( grid->xsize ) pt.x = rint((pt.x - grid->ipx)/grid->xsize) * grid->xsize + grid->ipx; if ( grid->ysize ) pt.y = rint((pt.y - grid->ipy)/grid->ysize) * grid->ysize + grid->ipy; if ( RTFLAGS_GET_Z(pa->flags) && grid->zsize ) pt.z = rint((pt.z - grid->ipz)/grid->zsize) * grid->zsize + grid->ipz; if ( RTFLAGS_GET_M(pa->flags) && grid->msize ) pt.m = rint((pt.m - grid->ipm)/grid->msize) * grid->msize + grid->ipm; ptarray_append_point(ctx, dpa, &pt, RT_FALSE); } return dpa; } int ptarray_npoints_in_rect(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTGBOX *gbox) { const RTPOINT2D *pt; int n = 0; int i; for ( i = 0; i < pa->npoints; i++ ) { pt = rt_getPoint2d_cp(ctx, pa, i); if ( gbox_contains_point2d(ctx, gbox, pt) ) n++; } return n; } src/rtalgorithm.c000066400000000000000000000533471271715413500143670ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2008 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include /* for tolower */ /** * Returns -1 if n < 0.0 and 1 if n > 0.0 */ int signum(const RTCTX *ctx, double n) { if( n < 0 ) return -1; if( n > 0 ) return 1; return 0; } int p4d_same(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) && FP_EQUALS(p1->m,p2->m) ) return RT_TRUE; else return RT_FALSE; } int p3d_same(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z) ) return RT_TRUE; else return RT_FALSE; } int p2d_same(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2) { if( FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) ) return RT_TRUE; else return RT_FALSE; } /** * rt_segment_side(ctx) * * Return -1 if point Q is left of segment P * Return 1 if point Q is right of segment P * Return 0 if point Q in on segment P */ int rt_segment_side(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q) { double side = ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) ); if ( side == 0.0 ) return 0; else return signum(ctx, side); } /** * Returns the length of a linear segment */ double rt_seg_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2) { return sqrt((A1->x-A2->x)*(A1->x-A2->x)+(A1->y-A2->y)*(A1->y-A2->y)); } /** * Returns true if P is on the same side of the plane partition * defined by A1/A3 as A2 is. Only makes sense if P has already been * determined to be on the circle defined by A1/A2/A3. */ int rt_pt_in_arc(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3) { return rt_segment_side(ctx, A1, A3, A2) == rt_segment_side(ctx, A1, A3, P); } /** * Returns true if P is between A1/A2. Only makes sense if P has already been * deterined to be on the line defined by A1/A2. */ int rt_pt_in_seg(const RTCTX *ctx, const RTPOINT2D *P, const RTPOINT2D *A1, const RTPOINT2D *A2) { return ((A1->x <= P->x && P->x < A2->x) || (A1->x >= P->x && P->x > A2->x)) || ((A1->y <= P->y && P->y < A2->y) || (A1->y >= P->y && P->y > A2->y)); } /** * Returns true if arc A is actually a point (all vertices are the same) . */ int rt_arc_is_pt(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3) { if ( A1->x == A2->x && A2->x == A3->x && A1->y == A2->y && A2->y == A3->y ) return RT_TRUE; else return RT_FALSE; } /** * Returns the length of a circular arc segment */ double rt_arc_length(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3) { RTPOINT2D C; double radius_A, circumference_A; int a2_side, clockwise; double a1, a3; double angle; if ( rt_arc_is_pt(ctx, A1, A2, A3) ) return 0.0; radius_A = rt_arc_center(ctx, A1, A2, A3, &C); /* Co-linear! Return linear distance! */ if ( radius_A < 0 ) { double dx = A1->x - A3->x; double dy = A1->y - A3->y; return sqrt(dx*dx + dy*dy); } /* Closed circle! Return the circumference! */ circumference_A = M_PI * 2 * radius_A; if ( p2d_same(ctx, A1, A3) ) return circumference_A; /* Determine the orientation of the arc */ a2_side = rt_segment_side(ctx, A1, A3, A2); /* The side of the A1/A3 line that A2 falls on dictates the sweep direction from A1 to A3. */ if ( a2_side == -1 ) clockwise = RT_TRUE; else clockwise = RT_FALSE; /* Angles of each point that defines the arc section */ a1 = atan2(A1->y - C.y, A1->x - C.x); a3 = atan2(A3->y - C.y, A3->x - C.x); /* What's the sweep from A1 to A3? */ if ( clockwise ) { if ( a1 > a3 ) angle = a1 - a3; else angle = 2*M_PI + a1 - a3; } else { if ( a3 > a1 ) angle = a3 - a1; else angle = 2*M_PI + a3 - a1; } /* Length as proportion of circumference */ return circumference_A * (angle / (2*M_PI)); } int rt_arc_side(const RTCTX *ctx, const RTPOINT2D *A1, const RTPOINT2D *A2, const RTPOINT2D *A3, const RTPOINT2D *Q) { RTPOINT2D C; double radius_A; double side_Q, side_A2; double d; side_Q = rt_segment_side(ctx, A1, A3, Q); radius_A = rt_arc_center(ctx, A1, A2, A3, &C); side_A2 = rt_segment_side(ctx, A1, A3, A2); /* Linear case */ if ( radius_A < 0 ) return side_Q; d = distance2d_pt_pt(ctx, Q, &C); /* Q is on the arc boundary */ if ( d == radius_A && side_Q == side_A2 ) { return 0; } /* Q on A1-A3 line, so its on opposite side to A2 */ if ( side_Q == 0 ) { return -1 * side_A2; } /* * Q is inside the arc boundary, so it's not on the side we * might think from examining only the end points */ if ( d < radius_A && side_Q == side_A2 ) { side_Q *= -1; } return side_Q; } /** * Determines the center of the circle defined by the three given points. * In the event the circle is complete, the midpoint of the segment defined * by the first and second points is returned. If the points are colinear, * as determined by equal slopes, then NULL is returned. If the interior * point is coincident with either end point, they are taken as colinear. */ double rt_arc_center(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *p3, RTPOINT2D *result) { RTPOINT2D c; double cx, cy, cr; double dx21, dy21, dx31, dy31, h21, h31, d; c.x = c.y = 0.0; RTDEBUGF(2, "rt_arc_center called (%.16f,%.16f), (%.16f,%.16f), (%.16f,%.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y); /* Closed circle */ if (fabs(p1->x - p3->x) < EPSILON_SQLMM && fabs(p1->y - p3->y) < EPSILON_SQLMM) { cx = p1->x + (p2->x - p1->x) / 2.0; cy = p1->y + (p2->y - p1->y) / 2.0; c.x = cx; c.y = cy; *result = c; cr = sqrt(pow(cx - p1->x, 2.0) + pow(cy - p1->y, 2.0)); return cr; } /* Using cartesian eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle */ dx21 = p2->x - p1->x; dy21 = p2->y - p1->y; dx31 = p3->x - p1->x; dy31 = p3->y - p1->y; h21 = pow(dx21, 2.0) + pow(dy21, 2.0); h31 = pow(dx31, 2.0) + pow(dy31, 2.0); /* 2 * |Cross product|, d<0 means clockwise and d>0 counterclockwise sweeping angle */ d = 2 * (dx21 * dy31 - dx31 * dy21); /* Check colinearity, |Cross product| = 0 */ if (fabs(d) < EPSILON_SQLMM) return -1.0; /* Calculate centroid coordinates and radius */ cx = p1->x + (h21 * dy31 - h31 * dy21) / d; cy = p1->y - (h21 * dx31 - h31 * dx21) / d; c.x = cx; c.y = cy; *result = c; cr = sqrt(pow(cx - p1->x, 2) + pow(cy - p1->y, 2)); RTDEBUGF(2, "rt_arc_center center is (%.16f,%.16f)", result->x, result->y); return cr; } int pt_in_ring_2d(const RTCTX *ctx, const RTPOINT2D *p, const RTPOINTARRAY *ring) { int cn = 0; /* the crossing number counter */ int i; const RTPOINT2D *v1, *v2; const RTPOINT2D *first, *last; first = rt_getPoint2d_cp(ctx, ring, 0); last = rt_getPoint2d_cp(ctx, ring, ring->npoints-1); if ( memcmp(first, last, sizeof(RTPOINT2D)) ) { rterror(ctx, "pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)", first->x, first->y, last->x, last->y); return RT_FALSE; } RTDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y); /* printPA(ctx, ring); */ /* loop through all edges of the polygon */ v1 = rt_getPoint2d_cp(ctx, ring, 0); for (i=0; inpoints-1; i++) { double vt; v2 = rt_getPoint2d_cp(ctx, ring, i+1); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1->y <= p->y) && (v2->y > p->y)) /* a downward crossing */ || ((v1->y > p->y) && (v2->y <= p->y)) ) { vt = (double)(p->y - v1->y) / (v2->y - v1->y); /* P->x x < v1->x + vt * (v2->x - v1->x)) { /* a valid crossing of y=p->y right of p->x */ ++cn; } } v1 = v2; } RTDEBUGF(3, "pt_in_ring_2d returning %d", cn&1); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ } static int rt_seg_interact(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q1, const RTPOINT2D *q2) { double minq=FP_MIN(q1->x,q2->x); double maxq=FP_MAX(q1->x,q2->x); double minp=FP_MIN(p1->x,p2->x); double maxp=FP_MAX(p1->x,p2->x); if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) return RT_FALSE; minq=FP_MIN(q1->y,q2->y); maxq=FP_MAX(q1->y,q2->y); minp=FP_MIN(p1->y,p2->y); maxp=FP_MAX(p1->y,p2->y); if (FP_GT(minp,maxq) || FP_LT(maxp,minq)) return RT_FALSE; return RT_TRUE; } /** ** @brief returns the kind of #RTCG_SEGMENT_INTERSECTION_TYPE behavior of lineseg 1 (constructed from p1 and p2) and lineseg 2 (constructed from q1 and q2) ** @param p1 start point of first straight linesegment ** @param p2 end point of first straight linesegment ** @param q1 start point of second line segment ** @param q2 end point of second line segment ** @return a #RTCG_SEGMENT_INTERSECTION_TYPE ** Returns one of ** SEG_ERROR = -1, ** SEG_NO_INTERSECTION = 0, ** SEG_COLINEAR = 1, ** SEG_CROSS_LEFT = 2, ** SEG_CROSS_RIGHT = 3, */ int rt_segment_intersects(const RTCTX *ctx, const RTPOINT2D *p1, const RTPOINT2D *p2, const RTPOINT2D *q1, const RTPOINT2D *q2) { int pq1, pq2, qp1, qp2; /* No envelope interaction => we are done. */ if (!rt_seg_interact(ctx, p1, p2, q1, p2)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of q on the same side of p? */ pq1=rt_segment_side(ctx, p1,p2,q1); pq2=rt_segment_side(ctx, p1,p2,q2); if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0)) { return SEG_NO_INTERSECTION; } /* Are the start and end points of p on the same side of q? */ qp1=rt_segment_side(ctx, q1,q2,p1); qp2=rt_segment_side(ctx, q1,q2,p2); if ( (qp1 > 0.0 && qp2 > 0.0) || (qp1 < 0.0 && qp2 < 0.0) ) { return SEG_NO_INTERSECTION; } /* Nobody is on one side or another? Must be colinear. */ if ( pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0 ) { return SEG_COLINEAR; } /* ** When one end-point touches, the sidedness is determined by the ** location of the other end-point. Only touches by the first point ** will be considered "real" to avoid double counting. */ RTDEBUGF(4, "pq1=%.15g pq2=%.15g", pq1, pq2); RTDEBUGF(4, "qp1=%.15g qp2=%.15g", qp1, qp2); /* Second point of p or q touches, it's not a crossing. */ if ( pq2 == 0 || qp2 == 0 ) { return SEG_NO_INTERSECTION; } /* First point of p touches, it's a "crossing". */ if ( pq1 == 0 ) { if ( pq2 > 0 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* First point of q touches, it's a crossing. */ if ( qp1 == 0 ) { if ( pq1 < pq2 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; } /* The segments cross, what direction is the crossing? */ if ( pq1 < pq2 ) return SEG_CROSS_RIGHT; else return SEG_CROSS_LEFT; /* This should never happen! */ return SEG_ERROR; } /** ** @brief rtline_crossing_direction: returns the kind of #RTCG_LINE_CROSS_TYPE behavior of 2 linestrings ** @param l1 first line string ** @param l2 second line string ** @return a #RTCG_LINE_CROSS_TYPE ** LINE_NO_CROSS = 0 ** LINE_CROSS_LEFT = -1 ** LINE_CROSS_RIGHT = 1 ** LINE_MULTICROSS_END_LEFT = -2 ** LINE_MULTICROSS_END_RIGHT = 2 ** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3 ** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 ** */ int rtline_crossing_direction(const RTCTX *ctx, const RTLINE *l1, const RTLINE *l2) { int i = 0, j = 0; const RTPOINT2D *p1, *p2, *q1, *q2; RTPOINTARRAY *pa1 = NULL, *pa2 = NULL; int cross_left = 0; int cross_right = 0; int first_cross = 0; int this_cross = 0; pa1 = (RTPOINTARRAY*)l1->points; pa2 = (RTPOINTARRAY*)l2->points; /* One-point lines can't intersect (and shouldn't exist). */ if ( pa1->npoints < 2 || pa2->npoints < 2 ) return LINE_NO_CROSS; RTDEBUGF(4, "l1 = %s", rtgeom_to_ewkt(ctx, (RTGEOM*)l1)); RTDEBUGF(4, "l2 = %s", rtgeom_to_ewkt(ctx, (RTGEOM*)l2)); /* Initialize first point of q */ q1 = rt_getPoint2d_cp(ctx, pa2, 0); for ( i = 1; i < pa2->npoints; i++ ) { /* Update second point of q to next value */ q2 = rt_getPoint2d_cp(ctx, pa2, i); /* Initialize first point of p */ p1 = rt_getPoint2d_cp(ctx, pa1, 0); for ( j = 1; j < pa1->npoints; j++ ) { /* Update second point of p to next value */ p2 = rt_getPoint2d_cp(ctx, pa1, j); this_cross = rt_segment_intersects(ctx, p1, p2, q1, q2); RTDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1->x, p1->y, p2->x, p2->y); if ( this_cross == SEG_CROSS_LEFT ) { RTDEBUG(4,"this_cross == SEG_CROSS_LEFT"); cross_left++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } if ( this_cross == SEG_CROSS_RIGHT ) { RTDEBUG(4,"this_cross == SEG_CROSS_RIGHT"); cross_right++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } /* ** Crossing at a co-linearity can be turned handled by extending ** segment to next vertext and seeing if the end points straddle ** the co-linear segment. */ if ( this_cross == SEG_COLINEAR ) { RTDEBUG(4,"this_cross == SEG_COLINEAR"); /* TODO: Add logic here and in segment_intersects() continue; */ } RTDEBUG(4,"this_cross == SEG_NO_INTERSECTION"); /* Turn second point of p into first point */ p1 = p2; } /* Turn second point of q into first point */ q1 = q2; } RTDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right); if ( !cross_left && !cross_right ) return LINE_NO_CROSS; if ( !cross_left && cross_right == 1 ) return LINE_CROSS_RIGHT; if ( !cross_right && cross_left == 1 ) return LINE_CROSS_LEFT; if ( cross_left - cross_right == 1 ) return LINE_MULTICROSS_END_LEFT; if ( cross_left - cross_right == -1 ) return LINE_MULTICROSS_END_RIGHT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT ) return LINE_MULTICROSS_END_SAME_FIRST_LEFT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT ) return LINE_MULTICROSS_END_SAME_FIRST_RIGHT; return LINE_NO_CROSS; } static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz"; /* ** Calculate the geohash, iterating downwards and gaining precision. ** From geohash-native.c, (c) 2008 David Troy ** Released under the MIT License. */ char * geohash_point(const RTCTX *ctx, double longitude, double latitude, int precision) { int is_even=1, i=0; double lat[2], lon[2], mid; char bits[] = {16,8,4,2,1}; int bit=0, ch=0; char *geohash = NULL; geohash = rtalloc(ctx, precision + 1); lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; while (i < precision) { if (is_even) { mid = (lon[0] + lon[1]) / 2; if (longitude >= mid) { ch |= bits[bit]; lon[0] = mid; } else { lon[1] = mid; } } else { mid = (lat[0] + lat[1]) / 2; if (latitude >= mid) { ch |= bits[bit]; lat[0] = mid; } else { lat[1] = mid; } } is_even = !is_even; if (bit < 4) { bit++; } else { geohash[i++] = base32[ch]; bit = 0; ch = 0; } } geohash[i] = 0; return geohash; } /* ** Calculate the geohash, iterating downwards and gaining precision. ** From geohash-native.c, (c) 2008 David Troy ** Released under the MIT License. */ unsigned int geohash_point_as_int(const RTCTX *ctx, RTPOINT2D *pt) { int is_even=1; double lat[2], lon[2], mid; int bit=32; unsigned int ch = 0; double longitude = pt->x; double latitude = pt->y; lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; while (--bit >= 0) { if (is_even) { mid = (lon[0] + lon[1]) / 2; if (longitude > mid) { ch |= 0x0001 << bit; lon[0] = mid; } else { lon[1] = mid; } } else { mid = (lat[0] + lat[1]) / 2; if (latitude > mid) { ch |= 0x0001 << bit; lat[0] = mid; } else { lat[1] = mid; } } is_even = !is_even; } return ch; } /* ** Decode a GeoHash into a bounding box. The lat and lon arguments should ** both be passed as double arrays of length 2 at a minimum where the values ** set in them will be the southwest and northeast coordinates of the bounding ** box accordingly. A precision less than 0 indicates that the entire length ** of the GeoHash should be used. */ void decode_geohash_bbox(const RTCTX *ctx, char *geohash, double *lat, double *lon, int precision) { int i, j, hashlen; char c, cd, mask, is_even = 1; static char bits[] = {16, 8, 4, 2, 1}; lat[0] = -90.0; lat[1] = 90.0; lon[0] = -180.0; lon[1] = 180.0; hashlen = strlen(geohash); if (precision < 0 || precision > hashlen) { precision = hashlen; } for (i = 0; i < precision; i++) { c = tolower(geohash[i]); cd = strchr(base32, c) - base32; for (j = 0; j < 5; j++) { mask = bits[j]; if (is_even) { lon[!(cd & mask)] = (lon[0] + lon[1]) / 2; } else { lat[!(cd & mask)] = (lat[0] + lat[1]) / 2; } is_even = !is_even; } } } int rtgeom_geohash_precision(const RTCTX *ctx, RTGBOX bbox, RTGBOX *bounds) { double minx, miny, maxx, maxy; double latmax, latmin, lonmax, lonmin; double lonwidth, latwidth; double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust; int precision = 0; /* Get the bounding box, return error if things don't work out. */ minx = bbox.xmin; miny = bbox.ymin; maxx = bbox.xmax; maxy = bbox.ymax; if ( minx == maxx && miny == maxy ) { /* It's a point. Doubles have 51 bits of precision. ** 2 * 51 / 5 == 20 */ return 20; } lonmin = -180.0; latmin = -90.0; lonmax = 180.0; latmax = 90.0; /* Shrink a world bounding box until one of the edges interferes with the ** bounds of our rectangle. */ while ( 1 ) { lonwidth = lonmax - lonmin; latwidth = latmax - latmin; latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0; if ( minx > lonmin + lonwidth / 2.0 ) { lonminadjust = lonwidth / 2.0; } else if ( maxx < lonmax - lonwidth / 2.0 ) { lonmaxadjust = -1 * lonwidth / 2.0; } if ( miny > latmin + latwidth / 2.0 ) { latminadjust = latwidth / 2.0; } else if (maxy < latmax - latwidth / 2.0 ) { latmaxadjust = -1 * latwidth / 2.0; } /* Only adjust if adjustments are legal (we haven't crossed any edges). */ if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) ) { latmin += latminadjust; lonmin += lonminadjust; latmax += latmaxadjust; lonmax += lonmaxadjust; /* Each adjustment cycle corresponds to 2 bits of storage in the ** geohash. */ precision += 2; } else { break; } } /* Save the edges of our bounds, in case someone cares later. */ bounds->xmin = lonmin; bounds->xmax = lonmax; bounds->ymin = latmin; bounds->ymax = latmax; /* Each geohash character (base32) can contain 5 bits of information. ** We are returning the precision in characters, so here we divide. */ return precision / 5; } /* ** Return a geohash string for the geometry. ** Where the precision is non-positive, calculate a precision based on the ** bounds of the feature. Big features have loose precision. ** Small features have tight precision. */ char * rtgeom_geohash(const RTCTX *ctx, const RTGEOM *rtgeom, int precision) { RTGBOX gbox; RTGBOX gbox_bounds; double lat, lon; int result; gbox_init(ctx, &gbox); gbox_init(ctx, &gbox_bounds); result = rtgeom_calculate_gbox_cartesian(ctx, rtgeom, &gbox); if ( result == RT_FAILURE ) return NULL; /* Return error if we are being fed something outside our working bounds */ if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 ) { rterror(ctx, "Geohash requires inputs in decimal degrees, got (%g %g, %g %g).", gbox.xmin, gbox.ymin, gbox.xmax, gbox.ymax); return NULL; } /* What is the center of our geometry bounds? We'll use that to ** approximate location. */ lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2; lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2; if ( precision <= 0 ) { precision = rtgeom_geohash_precision(ctx, gbox, &gbox_bounds); } /* ** Return the geohash of the center, with a precision determined by the ** extent of the bounds. ** Possible change: return the point at the center of the precision bounds? */ return geohash_point(ctx, lon, lat, precision); } src/rtcircstring.c000066400000000000000000000224101271715413500145330ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic RTCIRCSTRING functions */ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" void printRTCIRCSTRING(const RTCTX *ctx, RTCIRCSTRING *curve); void rtcircstring_reverse(const RTCTX *ctx, RTCIRCSTRING *curve); void rtcircstring_release(const RTCTX *ctx, RTCIRCSTRING *rtcirc); char rtcircstring_same(const RTCTX *ctx, const RTCIRCSTRING *me, const RTCIRCSTRING *you); RTCIRCSTRING * rtcircstring_from_rtpointarray(const RTCTX *ctx, int srid, uint32_t npoints, RTPOINT **points); RTCIRCSTRING * rtcircstring_from_rtmpoint(const RTCTX *ctx, int srid, RTMPOINT *mpoint); RTCIRCSTRING * rtcircstring_addpoint(const RTCTX *ctx, RTCIRCSTRING *curve, RTPOINT *point, uint32_t where); RTCIRCSTRING * rtcircstring_removepoint(const RTCTX *ctx, RTCIRCSTRING *curve, uint32_t index); void rtcircstring_setPoint4d(const RTCTX *ctx, RTCIRCSTRING *curve, uint32_t index, RTPOINT4D *newpoint); /* * Construct a new RTCIRCSTRING. points will *NOT* be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ RTCIRCSTRING * rtcircstring_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points) { RTCIRCSTRING *result; /* * The first arc requires three points. Each additional * arc requires two more points. Thus the minimum point count * is three, and the count must be odd. */ if (points->npoints % 2 != 1 || points->npoints < 3) { rtnotice(ctx, "rtcircstring_construct: invalid point count %d", points->npoints); } result = (RTCIRCSTRING*) rtalloc(ctx, sizeof(RTCIRCSTRING)); result->type = RTCIRCSTRINGTYPE; result->flags = points->flags; RTFLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->points = points; result->bbox = bbox; return result; } RTCIRCSTRING * rtcircstring_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTCIRCSTRING *result = rtalloc(ctx, sizeof(RTCIRCSTRING)); result->type = RTCIRCSTRINGTYPE; result->flags = gflags(ctx, hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(ctx, hasz, hasm, 1); result->bbox = NULL; return result; } void rtcircstring_release(const RTCTX *ctx, RTCIRCSTRING *rtcirc) { rtgeom_release(ctx, rtcircstring_as_rtgeom(ctx, rtcirc)); } void rtcircstring_free(const RTCTX *ctx, RTCIRCSTRING *curve) { if ( ! curve ) return; if ( curve->bbox ) rtfree(ctx, curve->bbox); if ( curve->points ) ptarray_free(ctx, curve->points); rtfree(ctx, curve); } void printRTCIRCSTRING(const RTCTX *ctx, RTCIRCSTRING *curve) { rtnotice(ctx, "RTCIRCSTRING {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(curve->flags)); rtnotice(ctx, " srid = %i", (int)curve->srid); printPA(ctx, curve->points); rtnotice(ctx, "}"); } /* @brief Clone RTCIRCSTRING object. Serialized point lists are not copied. * * @see ptarray_clone */ RTCIRCSTRING * rtcircstring_clone(const RTCTX *ctx, const RTCIRCSTRING *g) { return (RTCIRCSTRING *)rtline_clone(ctx, (RTLINE *)g); } void rtcircstring_reverse(const RTCTX *ctx, RTCIRCSTRING *curve) { ptarray_reverse(ctx, curve->points); } /* check coordinate equality */ char rtcircstring_same(const RTCTX *ctx, const RTCIRCSTRING *me, const RTCIRCSTRING *you) { return ptarray_same(ctx, me->points, you->points); } /* * Construct a RTCIRCSTRING from an array of RTPOINTs * RTCIRCSTRING dimensions are large enough to host all input dimensions. */ RTCIRCSTRING * rtcircstring_from_rtpointarray(const RTCTX *ctx, int srid, uint32_t npoints, RTPOINT **points) { int zmflag=0; uint32_t i; RTPOINTARRAY *pa; uint8_t *newpoints, *ptr; size_t ptsize, size; /* * Find output dimensions, check integrity */ for (i = 0; i < npoints; i++) { if (points[i]->type != RTPOINTTYPE) { rterror(ctx, "rtcurve_from_rtpointarray: invalid input type: %s", rttype_name(ctx, points[i]->type)); return NULL; } if (RTFLAGS_GET_Z(points[i]->flags)) zmflag |= 2; if (RTFLAGS_GET_M(points[i]->flags)) zmflag |= 1; if (zmflag == 3) break; } if (zmflag == 0) ptsize = 2 * sizeof(double); else if (zmflag == 3) ptsize = 4 * sizeof(double); else ptsize = 3 * sizeof(double); /* * Allocate output points array */ size = ptsize * npoints; newpoints = rtalloc(ctx, size); memset(newpoints, 0, size); ptr = newpoints; for (i = 0; i < npoints; i++) { size = ptarray_point_size(ctx, points[i]->point); memcpy(ptr, rt_getPoint_internal(ctx, points[i]->point, 0), size); ptr += ptsize; } pa = ptarray_construct_reference_data(ctx, zmflag&2, zmflag&1, npoints, newpoints); return rtcircstring_construct(ctx, srid, NULL, pa); } /* * Construct a RTCIRCSTRING from a RTMPOINT */ RTCIRCSTRING * rtcircstring_from_rtmpoint(const RTCTX *ctx, int srid, RTMPOINT *mpoint) { uint32_t i; RTPOINTARRAY *pa; char zmflag = RTFLAGS_GET_ZM(mpoint->flags); size_t ptsize, size; uint8_t *newpoints, *ptr; if (zmflag == 0) ptsize = 2 * sizeof(double); else if (zmflag == 3) ptsize = 4 * sizeof(double); else ptsize = 3 * sizeof(double); /* Allocate space for output points */ size = ptsize * mpoint->ngeoms; newpoints = rtalloc(ctx, size); memset(newpoints, 0, size); ptr = newpoints; for (i = 0; i < mpoint->ngeoms; i++) { memcpy(ptr, rt_getPoint_internal(ctx, mpoint->geoms[i]->point, 0), ptsize); ptr += ptsize; } pa = ptarray_construct_reference_data(ctx, zmflag&2, zmflag&1, mpoint->ngeoms, newpoints); RTDEBUGF(3, "rtcurve_from_rtmpoint: constructed pointarray for %d points, %d zmflag", mpoint->ngeoms, zmflag); return rtcircstring_construct(ctx, srid, NULL, pa); } RTCIRCSTRING * rtcircstring_addpoint(const RTCTX *ctx, RTCIRCSTRING *curve, RTPOINT *point, uint32_t where) { RTPOINTARRAY *newpa; RTCIRCSTRING *ret; newpa = ptarray_addPoint(ctx, curve->points, rt_getPoint_internal(ctx, point->point, 0), RTFLAGS_NDIMS(point->flags), where); ret = rtcircstring_construct(ctx, curve->srid, NULL, newpa); return ret; } RTCIRCSTRING * rtcircstring_removepoint(const RTCTX *ctx, RTCIRCSTRING *curve, uint32_t index) { RTPOINTARRAY *newpa; RTCIRCSTRING *ret; newpa = ptarray_removePoint(ctx, curve->points, index); ret = rtcircstring_construct(ctx, curve->srid, NULL, newpa); return ret; } /* * Note: input will be changed, make sure you have permissions for this. * */ void rtcircstring_setPoint4d(const RTCTX *ctx, RTCIRCSTRING *curve, uint32_t index, RTPOINT4D *newpoint) { ptarray_set_point4d(ctx, curve->points, index, newpoint); } int rtcircstring_is_closed(const RTCTX *ctx, const RTCIRCSTRING *curve) { if (RTFLAGS_GET_Z(curve->flags)) return ptarray_is_closed_3d(ctx, curve->points); return ptarray_is_closed_2d(ctx, curve->points); } int rtcircstring_is_empty(const RTCTX *ctx, const RTCIRCSTRING *circ) { if ( !circ->points || circ->points->npoints < 1 ) return RT_TRUE; return RT_FALSE; } double rtcircstring_length(const RTCTX *ctx, const RTCIRCSTRING *circ) { return rtcircstring_length_2d(ctx, circ); } double rtcircstring_length_2d(const RTCTX *ctx, const RTCIRCSTRING *circ) { if ( rtcircstring_is_empty(ctx, circ) ) return 0.0; return ptarray_arc_length_2d(ctx, circ->points); } /* * Returns freshly allocated #RTPOINT that corresponds to the index where. * Returns NULL if the geometry is empty or the index invalid. */ RTPOINT* rtcircstring_get_rtpoint(const RTCTX *ctx, const RTCIRCSTRING *circ, int where) { RTPOINT4D pt; RTPOINT *rtpoint; RTPOINTARRAY *pa; if ( rtcircstring_is_empty(ctx, circ) || where < 0 || where >= circ->points->npoints ) return NULL; pa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(circ->flags), RTFLAGS_GET_M(circ->flags), 1); pt = rt_getPoint4d(ctx, circ->points, where); ptarray_append_point(ctx, pa, &pt, RT_TRUE); rtpoint = rtpoint_construct(ctx, circ->srid, NULL, pa); return rtpoint; } /* * Snap to grid */ RTCIRCSTRING* rtcircstring_grid(const RTCTX *ctx, const RTCIRCSTRING *line, const gridspec *grid) { RTCIRCSTRING *oline; RTPOINTARRAY *opa; opa = ptarray_grid(ctx, line->points, grid); /* Skip line3d with less then 2 points */ if ( opa->npoints < 2 ) return NULL; /* TODO: grid bounding box... */ oline = rtcircstring_construct(ctx, line->srid, NULL, opa); return oline; } src/rtcollection.c000066400000000000000000000366341271715413500145340ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #define CHECK_RTGEOM_ZM 1 void rtcollection_release(const RTCTX *ctx, RTCOLLECTION *rtcollection) { rtgeom_release(ctx, rtcollection_as_rtgeom(ctx, rtcollection)); } RTCOLLECTION * rtcollection_construct(const RTCTX *ctx, uint8_t type, int srid, RTGBOX *bbox, uint32_t ngeoms, RTGEOM **geoms) { RTCOLLECTION *ret; int hasz, hasm; #ifdef CHECK_RTGEOM_ZM char zm; uint32_t i; #endif RTDEBUGF(2, "rtcollection_construct called with %d, %d, %p, %d, %p.", type, srid, bbox, ngeoms, geoms); if( ! rttype_is_collection(ctx, type) ) rterror(ctx, "Non-collection type specified in collection constructor!"); hasz = 0; hasm = 0; if ( ngeoms > 0 ) { hasz = RTFLAGS_GET_Z(geoms[0]->flags); hasm = RTFLAGS_GET_M(geoms[0]->flags); #ifdef CHECK_RTGEOM_ZM zm = RTFLAGS_GET_ZM(geoms[0]->flags); RTDEBUGF(3, "rtcollection_construct type[0]=%d", geoms[0]->type); for (i=1; itype); if ( zm != RTFLAGS_GET_ZM(geoms[i]->flags) ) rterror(ctx, "rtcollection_construct: mixed dimension geometries: %d/%d", zm, RTFLAGS_GET_ZM(geoms[i]->flags)); } #endif } ret = rtalloc(ctx, sizeof(RTCOLLECTION)); ret->type = type; ret->flags = gflags(ctx, hasz,hasm,0); RTFLAGS_SET_BBOX(ret->flags, bbox?1:0); ret->srid = srid; ret->ngeoms = ngeoms; ret->maxgeoms = ngeoms; ret->geoms = geoms; ret->bbox = bbox; return ret; } RTCOLLECTION * rtcollection_construct_empty(const RTCTX *ctx, uint8_t type, int srid, char hasz, char hasm) { RTCOLLECTION *ret; if( ! rttype_is_collection(ctx, type) ) rterror(ctx, "Non-collection type specified in collection constructor!"); ret = rtalloc(ctx, sizeof(RTCOLLECTION)); ret->type = type; ret->flags = gflags(ctx, hasz,hasm,0); ret->srid = srid; ret->ngeoms = 0; ret->maxgeoms = 1; /* Allocate room for sub-members, just in case. */ ret->geoms = rtalloc(ctx, ret->maxgeoms * sizeof(RTGEOM*)); ret->bbox = NULL; return ret; } RTGEOM * rtcollection_getsubgeom(const RTCTX *ctx, RTCOLLECTION *col, int gnum) { return (RTGEOM *)col->geoms[gnum]; } /** * @brief Clone #RTCOLLECTION object. #RTPOINTARRAY are not copied. * Bbox is cloned if present in input. */ RTCOLLECTION * rtcollection_clone(const RTCTX *ctx, const RTCOLLECTION *g) { uint32_t i; RTCOLLECTION *ret = rtalloc(ctx, sizeof(RTCOLLECTION)); memcpy(ret, g, sizeof(RTCOLLECTION)); if ( g->ngeoms > 0 ) { ret->geoms = rtalloc(ctx, sizeof(RTGEOM *)*g->ngeoms); for (i=0; ingeoms; i++) { ret->geoms[i] = rtgeom_clone(ctx, g->geoms[i]); } if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); } else { ret->bbox = NULL; /* empty collection */ ret->geoms = NULL; } return ret; } /** * @brief Deep clone #RTCOLLECTION object. #RTPOINTARRAY are copied. */ RTCOLLECTION * rtcollection_clone_deep(const RTCTX *ctx, const RTCOLLECTION *g) { uint32_t i; RTCOLLECTION *ret = rtalloc(ctx, sizeof(RTCOLLECTION)); memcpy(ret, g, sizeof(RTCOLLECTION)); if ( g->ngeoms > 0 ) { ret->geoms = rtalloc(ctx, sizeof(RTGEOM *)*g->ngeoms); for (i=0; ingeoms; i++) { ret->geoms[i] = rtgeom_clone_deep(ctx, g->geoms[i]); } if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); } else { ret->bbox = NULL; /* empty collection */ ret->geoms = NULL; } return ret; } /** * Ensure the collection can hold up at least ngeoms */ void rtcollection_reserve(const RTCTX *ctx, RTCOLLECTION *col, int ngeoms) { if ( ngeoms <= col->maxgeoms ) return; /* Allocate more space if we need it */ do { col->maxgeoms *= 2; } while ( col->maxgeoms < ngeoms ); col->geoms = rtrealloc(ctx, col->geoms, sizeof(RTGEOM*) * col->maxgeoms); } /** * Appends geom to the collection managed by col. Does not copy or * clone, simply takes a reference on the passed geom. */ RTCOLLECTION* rtcollection_add_rtgeom(const RTCTX *ctx, RTCOLLECTION *col, const RTGEOM *geom) { if ( col == NULL || geom == NULL ) return NULL; if ( col->geoms == NULL && (col->ngeoms || col->maxgeoms) ) { rterror(ctx, "Collection is in inconsistent state. Null memory but non-zero collection counts."); return NULL; } /* Check type compatibility */ if ( ! rtcollection_allows_subtype(ctx, col->type, geom->type) ) { rterror(ctx, "%s cannot contain %s element", rttype_name(ctx, col->type), rttype_name(ctx, geom->type)); return NULL; } /* In case this is a truly empty, make some initial space */ if ( col->geoms == NULL ) { col->maxgeoms = 2; col->ngeoms = 0; col->geoms = rtalloc(ctx, col->maxgeoms * sizeof(RTGEOM*)); } /* Allocate more space if we need it */ rtcollection_reserve(ctx, col, col->ngeoms + 1); #if PARANOIA_LEVEL > 1 /* See http://trac.osgeo.org/postgis/ticket/2933 */ /* Make sure we don't already have a reference to this geom */ { int i = 0; for ( i = 0; i < col->ngeoms; i++ ) { if ( col->geoms[i] == geom ) { RTDEBUGF(4, "Found duplicate geometry in collection %p == %p", col->geoms[i], geom); return col; } } } #endif col->geoms[col->ngeoms] = (RTGEOM*)geom; col->ngeoms++; return col; } RTCOLLECTION * rtcollection_segmentize2d(const RTCTX *ctx, RTCOLLECTION *col, double dist) { uint32_t i; RTGEOM **newgeoms; if ( ! col->ngeoms ) return rtcollection_clone(ctx, col); newgeoms = rtalloc(ctx, sizeof(RTGEOM *)*col->ngeoms); for (i=0; ingeoms; i++) { newgeoms[i] = rtgeom_segmentize2d(ctx, col->geoms[i], dist); if ( ! newgeoms[i] ) { while (i--) rtgeom_free(ctx, newgeoms[i]); rtfree(ctx, newgeoms); return NULL; } } return rtcollection_construct(ctx, col->type, col->srid, NULL, col->ngeoms, newgeoms); } /** @brief check for same geometry composition * */ char rtcollection_same(const RTCTX *ctx, const RTCOLLECTION *c1, const RTCOLLECTION *c2) { uint32_t i; RTDEBUG(2, "rtcollection_same called"); if ( c1->type != c2->type ) return RT_FALSE; if ( c1->ngeoms != c2->ngeoms ) return RT_FALSE; for ( i = 0; i < c1->ngeoms; i++ ) { if ( ! rtgeom_same(ctx, c1->geoms[i], c2->geoms[i]) ) return RT_FALSE; } /* Former method allowed out-of-order equality between collections hit = rtalloc(ctx, sizeof(uint32_t)*c1->ngeoms); memset(hit, 0, sizeof(uint32_t)*c1->ngeoms); for (i=0; ingeoms; i++) { char found=0; for (j=0; jngeoms; j++) { if ( hit[j] ) continue; if ( rtgeom_same(ctx, c1->geoms[i], c2->geoms[j]) ) { hit[j] = 1; found=1; break; } } if ( ! found ) return RT_FALSE; } */ return RT_TRUE; } int rtcollection_ngeoms(const RTCTX *ctx, const RTCOLLECTION *col) { int i; int ngeoms = 0; if ( ! col ) { rterror(ctx, "Null input geometry."); return 0; } for ( i = 0; i < col->ngeoms; i++ ) { if ( col->geoms[i]) { switch (col->geoms[i]->type) { case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTPOLYGONTYPE: ngeoms += 1; break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTICURVETYPE: case RTMULTIPOLYGONTYPE: ngeoms += col->ngeoms; break; case RTCOLLECTIONTYPE: ngeoms += rtcollection_ngeoms(ctx, (RTCOLLECTION*)col->geoms[i]); break; } } } return ngeoms; } void rtcollection_free(const RTCTX *ctx, RTCOLLECTION *col) { int i; if ( ! col ) return; if ( col->bbox ) { rtfree(ctx, col->bbox); } for ( i = 0; i < col->ngeoms; i++ ) { RTDEBUGF(4,"freeing geom[%d]", i); if ( col->geoms && col->geoms[i] ) rtgeom_free(ctx, col->geoms[i]); } if ( col->geoms ) { rtfree(ctx, col->geoms); } rtfree(ctx, col); } /** * Takes a potentially heterogeneous collection and returns a homogeneous * collection consisting only of the specified type. */ RTCOLLECTION* rtcollection_extract(const RTCTX *ctx, RTCOLLECTION *col, int type) { int i = 0; RTGEOM **geomlist; RTCOLLECTION *outcol; int geomlistsize = 16; int geomlistlen = 0; uint8_t outtype; if ( ! col ) return NULL; switch (type) { case RTPOINTTYPE: outtype = RTMULTIPOINTTYPE; break; case RTLINETYPE: outtype = RTMULTILINETYPE; break; case RTPOLYGONTYPE: outtype = RTMULTIPOLYGONTYPE; break; default: rterror(ctx, "Only POLYGON, LINESTRING and POINT are supported by rtcollection_extract. %s requested.", rttype_name(ctx, type)); return NULL; } geomlist = rtalloc(ctx, sizeof(RTGEOM*) * geomlistsize); /* Process each sub-geometry */ for ( i = 0; i < col->ngeoms; i++ ) { int subtype = col->geoms[i]->type; /* Don't bother adding empty sub-geometries */ if ( rtgeom_is_empty(ctx, col->geoms[i]) ) { continue; } /* Copy our sub-types into the output list */ if ( subtype == type ) { /* We've over-run our buffer, double the memory segment */ if ( geomlistlen == geomlistsize ) { geomlistsize *= 2; geomlist = rtrealloc(ctx, geomlist, sizeof(RTGEOM*) * geomlistsize); } geomlist[geomlistlen] = rtgeom_clone(ctx, col->geoms[i]); geomlistlen++; } /* Recurse into sub-collections */ if ( rttype_is_collection(ctx, subtype ) ) { int j = 0; RTCOLLECTION *tmpcol = rtcollection_extract(ctx, (RTCOLLECTION*)col->geoms[i], type); for ( j = 0; j < tmpcol->ngeoms; j++ ) { /* We've over-run our buffer, double the memory segment */ if ( geomlistlen == geomlistsize ) { geomlistsize *= 2; geomlist = rtrealloc(ctx, geomlist, sizeof(RTGEOM*) * geomlistsize); } geomlist[geomlistlen] = tmpcol->geoms[j]; geomlistlen++; } rtfree(ctx, tmpcol); } } if ( geomlistlen > 0 ) { RTGBOX gbox; outcol = rtcollection_construct(ctx, outtype, col->srid, NULL, geomlistlen, geomlist); rtgeom_calculate_gbox(ctx, (RTGEOM *) outcol, &gbox); outcol->bbox = gbox_copy(ctx, &gbox); } else { rtfree(ctx, geomlist); outcol = rtcollection_construct_empty(ctx, outtype, col->srid, RTFLAGS_GET_Z(col->flags), RTFLAGS_GET_M(col->flags)); } return outcol; } RTGEOM* rtcollection_remove_repeated_points(const RTCTX *ctx, const RTCOLLECTION *coll, double tolerance) { uint32_t i; RTGEOM **newgeoms; newgeoms = rtalloc(ctx, sizeof(RTGEOM *)*coll->ngeoms); for (i=0; ingeoms; i++) { newgeoms[i] = rtgeom_remove_repeated_points(ctx, coll->geoms[i], tolerance); } return (RTGEOM*)rtcollection_construct(ctx, coll->type, coll->srid, coll->bbox ? gbox_copy(ctx, coll->bbox) : NULL, coll->ngeoms, newgeoms); } RTCOLLECTION* rtcollection_force_dims(const RTCTX *ctx, const RTCOLLECTION *col, int hasz, int hasm) { RTCOLLECTION *colout; /* Return 2D empty */ if( rtcollection_is_empty(ctx, col) ) { colout = rtcollection_construct_empty(ctx, col->type, col->srid, hasz, hasm); } else { int i; RTGEOM **geoms = NULL; geoms = rtalloc(ctx, sizeof(RTGEOM*) * col->ngeoms); for( i = 0; i < col->ngeoms; i++ ) { geoms[i] = rtgeom_force_dims(ctx, col->geoms[i], hasz, hasm); } colout = rtcollection_construct(ctx, col->type, col->srid, NULL, col->ngeoms, geoms); } return colout; } int rtcollection_is_empty(const RTCTX *ctx, const RTCOLLECTION *col) { int i; if ( (col->ngeoms == 0) || (!col->geoms) ) return RT_TRUE; for( i = 0; i < col->ngeoms; i++ ) { if ( ! rtgeom_is_empty(ctx, col->geoms[i]) ) return RT_FALSE; } return RT_TRUE; } int rtcollection_count_vertices(const RTCTX *ctx, RTCOLLECTION *col) { int i = 0; int v = 0; /* vertices */ assert(col); for ( i = 0; i < col->ngeoms; i++ ) { v += rtgeom_count_vertices(ctx, col->geoms[i]); } return v; } RTCOLLECTION* rtcollection_simplify(const RTCTX *ctx, const RTCOLLECTION *igeom, double dist, int preserve_collapsed) { int i; RTCOLLECTION *out = rtcollection_construct_empty(ctx, igeom->type, igeom->srid, RTFLAGS_GET_Z(igeom->flags), RTFLAGS_GET_M(igeom->flags)); if( rtcollection_is_empty(ctx, igeom) ) return out; /* should we return NULL instead ? */ for( i = 0; i < igeom->ngeoms; i++ ) { RTGEOM *ngeom = rtgeom_simplify(ctx, igeom->geoms[i], dist, preserve_collapsed); if ( ngeom ) out = rtcollection_add_rtgeom(ctx, out, ngeom); } return out; } int rtcollection_allows_subtype(const RTCTX *ctx, int collectiontype, int subtype) { if ( collectiontype == RTCOLLECTIONTYPE ) return RT_TRUE; if ( collectiontype == RTMULTIPOINTTYPE && subtype == RTPOINTTYPE ) return RT_TRUE; if ( collectiontype == RTMULTILINETYPE && subtype == RTLINETYPE ) return RT_TRUE; if ( collectiontype == RTMULTIPOLYGONTYPE && subtype == RTPOLYGONTYPE ) return RT_TRUE; if ( collectiontype == RTCOMPOUNDTYPE && (subtype == RTLINETYPE || subtype == RTCIRCSTRINGTYPE) ) return RT_TRUE; if ( collectiontype == RTCURVEPOLYTYPE && (subtype == RTCIRCSTRINGTYPE || subtype == RTLINETYPE || subtype == RTCOMPOUNDTYPE) ) return RT_TRUE; if ( collectiontype == RTMULTICURVETYPE && (subtype == RTCIRCSTRINGTYPE || subtype == RTLINETYPE || subtype == RTCOMPOUNDTYPE) ) return RT_TRUE; if ( collectiontype == RTMULTISURFACETYPE && (subtype == RTPOLYGONTYPE || subtype == RTCURVEPOLYTYPE) ) return RT_TRUE; if ( collectiontype == RTPOLYHEDRALSURFACETYPE && subtype == RTPOLYGONTYPE ) return RT_TRUE; if ( collectiontype == RTTINTYPE && subtype == RTTRIANGLETYPE ) return RT_TRUE; /* Must be a bad combination! */ return RT_FALSE; } int rtcollection_startpoint(const RTCTX *ctx, const RTCOLLECTION* col, RTPOINT4D* pt) { if ( col->ngeoms < 1 ) return RT_FAILURE; return rtgeom_startpoint(ctx, col->geoms[0], pt); } RTCOLLECTION* rtcollection_grid(const RTCTX *ctx, const RTCOLLECTION *coll, const gridspec *grid) { uint32_t i; RTCOLLECTION *newcoll; newcoll = rtcollection_construct_empty(ctx, coll->type, coll->srid, rtgeom_has_z(ctx, (RTGEOM*)coll), rtgeom_has_m(ctx, (RTGEOM*)coll)); for (i=0; ingeoms; i++) { RTGEOM *g = rtgeom_grid(ctx, coll->geoms[i], grid); if ( g ) rtcollection_add_rtgeom(ctx, newcoll, g); } return newcoll; } src/rtcompound.c000066400000000000000000000163771271715413500142270ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" int rtcompound_is_closed(const RTCTX *ctx, const RTCOMPOUND *compound) { size_t size; int npoints=0; if ( rtgeom_has_z(ctx, (RTGEOM*)compound) ) { size = sizeof(POINT3D); } else { size = sizeof(RTPOINT2D); } if ( compound->geoms[compound->ngeoms - 1]->type == RTCIRCSTRINGTYPE ) { npoints = ((RTCIRCSTRING *)compound->geoms[compound->ngeoms - 1])->points->npoints; } else if (compound->geoms[compound->ngeoms - 1]->type == RTLINETYPE) { npoints = ((RTLINE *)compound->geoms[compound->ngeoms - 1])->points->npoints; } if ( memcmp(rt_getPoint_internal(ctx, (RTPOINTARRAY *)compound->geoms[0]->data, 0), rt_getPoint_internal(ctx, (RTPOINTARRAY *)compound->geoms[compound->ngeoms - 1]->data, npoints - 1), size) ) { return RT_FALSE; } return RT_TRUE; } double rtcompound_length(const RTCTX *ctx, const RTCOMPOUND *comp) { double length = 0.0; RTLINE *line; if ( rtgeom_is_empty(ctx, (RTGEOM*)comp) ) return 0.0; line = rtcompound_stroke(ctx, comp, 32); length = rtline_length(ctx, line); rtline_free(ctx, line); return length; } double rtcompound_length_2d(const RTCTX *ctx, const RTCOMPOUND *comp) { double length = 0.0; RTLINE *line; if ( rtgeom_is_empty(ctx, (RTGEOM*)comp) ) return 0.0; line = rtcompound_stroke(ctx, comp, 32); length = rtline_length_2d(ctx, line); rtline_free(ctx, line); return length; } int rtcompound_add_rtgeom(const RTCTX *ctx, RTCOMPOUND *comp, RTGEOM *geom) { RTCOLLECTION *col = (RTCOLLECTION*)comp; /* Empty things can't continuously join up with other things */ if ( rtgeom_is_empty(ctx, geom) ) { RTDEBUG(4, "Got an empty component for a compound curve!"); return RT_FAILURE; } if( col->ngeoms > 0 ) { RTPOINT4D last, first; /* First point of the component we are adding */ RTLINE *newline = (RTLINE*)geom; /* Last point of the previous component */ RTLINE *prevline = (RTLINE*)(col->geoms[col->ngeoms-1]); rt_getPoint4d_p(ctx, newline->points, 0, &first); rt_getPoint4d_p(ctx, prevline->points, prevline->points->npoints-1, &last); if ( !(FP_EQUALS(first.x,last.x) && FP_EQUALS(first.y,last.y)) ) { RTDEBUG(4, "Components don't join up end-to-end!"); RTDEBUGF(4, "first pt (%g %g %g %g) last pt (%g %g %g %g)", first.x, first.y, first.z, first.m, last.x, last.y, last.z, last.m); return RT_FAILURE; } } col = rtcollection_add_rtgeom(ctx, col, geom); return RT_SUCCESS; } RTCOMPOUND * rtcompound_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTCOMPOUND *ret = (RTCOMPOUND*)rtcollection_construct_empty(ctx, RTCOMPOUNDTYPE, srid, hasz, hasm); return ret; } int rtgeom_contains_point(const RTCTX *ctx, const RTGEOM *geom, const RTPOINT2D *pt) { switch( geom->type ) { case RTLINETYPE: return ptarray_contains_point(ctx, ((RTLINE*)geom)->points, pt); case RTCIRCSTRINGTYPE: return ptarrayarc_contains_point(ctx, ((RTCIRCSTRING*)geom)->points, pt); case RTCOMPOUNDTYPE: return rtcompound_contains_point(ctx, (RTCOMPOUND*)geom, pt); } rterror(ctx, "rtgeom_contains_point failed"); return RT_FAILURE; } int rtcompound_contains_point(const RTCTX *ctx, const RTCOMPOUND *comp, const RTPOINT2D *pt) { int i; RTLINE *rtline; RTCIRCSTRING *rtcirc; int wn = 0; int winding_number = 0; int result; for ( i = 0; i < comp->ngeoms; i++ ) { RTGEOM *rtgeom = comp->geoms[i]; if ( rtgeom->type == RTLINETYPE ) { rtline = rtgeom_as_rtline(ctx, rtgeom); if ( comp->ngeoms == 1 ) { return ptarray_contains_point(ctx, rtline->points, pt); } else { /* Don't check closure while doing p-i-p test */ result = ptarray_contains_point_partial(ctx, rtline->points, pt, RT_FALSE, &winding_number); } } else { rtcirc = rtgeom_as_rtcircstring(ctx, rtgeom); if ( ! rtcirc ) { rterror(ctx, "Unexpected component of type %s in compound curve", rttype_name(ctx, rtgeom->type)); return 0; } if ( comp->ngeoms == 1 ) { return ptarrayarc_contains_point(ctx, rtcirc->points, pt); } else { /* Don't check closure while doing p-i-p test */ result = ptarrayarc_contains_point_partial(ctx, rtcirc->points, pt, RT_FALSE, &winding_number); } } /* Propogate boundary condition */ if ( result == RT_BOUNDARY ) return RT_BOUNDARY; wn += winding_number; } /* Outside */ if (wn == 0) return RT_OUTSIDE; /* Inside */ return RT_INSIDE; } RTCOMPOUND * rtcompound_construct_from_rtline(const RTCTX *ctx, const RTLINE *rtline) { RTCOMPOUND* ogeom = rtcompound_construct_empty(ctx, rtline->srid, RTFLAGS_GET_Z(rtline->flags), RTFLAGS_GET_M(rtline->flags)); rtcompound_add_rtgeom(ctx, ogeom, rtgeom_clone(ctx, (RTGEOM*)rtline)); /* ogeom->bbox = rtline->bbox; */ return ogeom; } RTPOINT* rtcompound_get_rtpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp, int where) { int i; int count = 0; int npoints = 0; if ( rtgeom_is_empty(ctx, (RTGEOM*)rtcmp) ) return NULL; npoints = rtgeom_count_vertices(ctx, (RTGEOM*)rtcmp); if ( where < 0 || where >= npoints ) { rterror(ctx, "%s: index %d is not in range of number of vertices (%d) in input", __func__, where, npoints); return NULL; } for ( i = 0; i < rtcmp->ngeoms; i++ ) { RTGEOM* part = rtcmp->geoms[i]; int npoints_part = rtgeom_count_vertices(ctx, part); if ( where >= count && where < count + npoints_part ) { return rtline_get_rtpoint(ctx, (RTLINE*)part, where - count); } else { count += npoints_part; } } return NULL; } RTPOINT * rtcompound_get_startpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp) { return rtcompound_get_rtpoint(ctx, rtcmp, 0); } RTPOINT * rtcompound_get_endpoint(const RTCTX *ctx, const RTCOMPOUND *rtcmp) { RTLINE *rtline; if ( rtcmp->ngeoms < 1 ) { return NULL; } rtline = (RTLINE*)(rtcmp->geoms[rtcmp->ngeoms-1]); if ( (!rtline) || (!rtline->points) || (rtline->points->npoints < 1) ) { return NULL; } return rtline_get_rtpoint(ctx, rtline, rtline->points->npoints-1); } src/rtcurvepoly.c000066400000000000000000000110621271715413500144150ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic RTCURVEPOLY manipulation */ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" RTCURVEPOLY * rtcurvepoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTCURVEPOLY *ret; ret = rtalloc(ctx, sizeof(RTCURVEPOLY)); ret->type = RTCURVEPOLYTYPE; ret->flags = gflags(ctx, hasz, hasm, 0); ret->srid = srid; ret->nrings = 0; ret->maxrings = 1; /* Allocate room for sub-members, just in case. */ ret->rings = rtalloc(ctx, ret->maxrings * sizeof(RTGEOM*)); ret->bbox = NULL; return ret; } RTCURVEPOLY * rtcurvepoly_construct_from_rtpoly(const RTCTX *ctx, RTPOLY *rtpoly) { RTCURVEPOLY *ret; int i; ret = rtalloc(ctx, sizeof(RTCURVEPOLY)); ret->type = RTCURVEPOLYTYPE; ret->flags = rtpoly->flags; ret->srid = rtpoly->srid; ret->nrings = rtpoly->nrings; ret->maxrings = rtpoly->nrings; /* Allocate room for sub-members, just in case. */ ret->rings = rtalloc(ctx, ret->maxrings * sizeof(RTGEOM*)); ret->bbox = rtpoly->bbox ? gbox_clone(ctx, rtpoly->bbox) : NULL; for ( i = 0; i < ret->nrings; i++ ) { ret->rings[i] = rtline_as_rtgeom(ctx, rtline_construct(ctx, ret->srid, NULL, ptarray_clone_deep(ctx, rtpoly->rings[i]))); } return ret; } int rtcurvepoly_add_ring(const RTCTX *ctx, RTCURVEPOLY *poly, RTGEOM *ring) { int i; /* Can't do anything with NULLs */ if( ! poly || ! ring ) { RTDEBUG(4,"NULL inputs!!! quitting"); return RT_FAILURE; } /* Check that we're not working with garbage */ if ( poly->rings == NULL && (poly->nrings || poly->maxrings) ) { RTDEBUG(4,"mismatched nrings/maxrings"); rterror(ctx, "Curvepolygon is in inconsistent state. Null memory but non-zero collection counts."); } /* Check that we're adding an allowed ring type */ if ( ! ( ring->type == RTLINETYPE || ring->type == RTCIRCSTRINGTYPE || ring->type == RTCOMPOUNDTYPE ) ) { RTDEBUGF(4,"got incorrect ring type: %s",rttype_name(ctx, ring->type)); return RT_FAILURE; } /* In case this is a truly empty, make some initial space */ if ( poly->rings == NULL ) { poly->maxrings = 2; poly->nrings = 0; poly->rings = rtalloc(ctx, poly->maxrings * sizeof(RTGEOM*)); } /* Allocate more space if we need it */ if ( poly->nrings == poly->maxrings ) { poly->maxrings *= 2; poly->rings = rtrealloc(ctx, poly->rings, sizeof(RTGEOM*) * poly->maxrings); } /* Make sure we don't already have a reference to this geom */ for ( i = 0; i < poly->nrings; i++ ) { if ( poly->rings[i] == ring ) { RTDEBUGF(4, "Found duplicate geometry in collection %p == %p", poly->rings[i], ring); return RT_SUCCESS; } } /* Add the ring and increment the ring count */ poly->rings[poly->nrings] = (RTGEOM*)ring; poly->nrings++; return RT_SUCCESS; } /** * This should be rewritten to make use of the curve itself. */ double rtcurvepoly_area(const RTCTX *ctx, const RTCURVEPOLY *curvepoly) { double area = 0.0; RTPOLY *poly; if( rtgeom_is_empty(ctx, (RTGEOM*)curvepoly) ) return 0.0; poly = rtcurvepoly_stroke(ctx, curvepoly, 32); area = rtpoly_area(ctx, poly); rtpoly_free(ctx, poly); return area; } double rtcurvepoly_perimeter(const RTCTX *ctx, const RTCURVEPOLY *poly) { double result=0.0; int i; for (i=0; inrings; i++) result += rtgeom_length(ctx, poly->rings[i]); return result; } double rtcurvepoly_perimeter_2d(const RTCTX *ctx, const RTCURVEPOLY *poly) { double result=0.0; int i; for (i=0; inrings; i++) result += rtgeom_length_2d(ctx, poly->rings[i]); return result; } src/rtgeodetic.c000066400000000000000000002567101271715413500141630ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * Copyright 2009 David Skea * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeodetic.h" #include "rtgeom_log.h" /** * For testing geodetic bounding box, we have a magic global variable. * When this is true (when the cunit tests set it), use the slow, but * guaranteed correct, algorithm. Otherwise use the regular one. */ int gbox_geocentric_slow = RT_FALSE; /** * Convert a longitude to the range of -PI,PI */ double longitude_radians_normalize(const RTCTX *ctx, double lon) { if ( lon == -1.0 * M_PI ) return M_PI; if ( lon == -2.0 * M_PI ) return 0.0; if ( lon > 2.0 * M_PI ) lon = remainder(lon, 2.0 * M_PI); if ( lon < -2.0 * M_PI ) lon = remainder(lon, -2.0 * M_PI); if ( lon > M_PI ) lon = -2.0 * M_PI + lon; if ( lon < -1.0 * M_PI ) lon = 2.0 * M_PI + lon; if ( lon == -2.0 * M_PI ) lon *= -1.0; return lon; } /** * Convert a latitude to the range of -PI/2,PI/2 */ double latitude_radians_normalize(const RTCTX *ctx, double lat) { if ( lat > 2.0 * M_PI ) lat = remainder(lat, 2.0 * M_PI); if ( lat < -2.0 * M_PI ) lat = remainder(lat, -2.0 * M_PI); if ( lat > M_PI ) lat = M_PI - lat; if ( lat < -1.0 * M_PI ) lat = -1.0 * M_PI - lat; if ( lat > M_PI_2 ) lat = M_PI - lat; if ( lat < -1.0 * M_PI_2 ) lat = -1.0 * M_PI - lat; return lat; } /** * Convert a longitude to the range of -180,180 * @param lon longitude in degrees */ double longitude_degrees_normalize(const RTCTX *ctx, double lon) { if ( lon > 360.0 ) lon = remainder(lon, 360.0); if ( lon < -360.0 ) lon = remainder(lon, -360.0); if ( lon > 180.0 ) lon = -360.0 + lon; if ( lon < -180.0 ) lon = 360 + lon; if ( lon == -180.0 ) return 180.0; if ( lon == -360.0 ) return 0.0; return lon; } /** * Convert a latitude to the range of -90,90 * @param lat latitude in degrees */ double latitude_degrees_normalize(const RTCTX *ctx, double lat) { if ( lat > 360.0 ) lat = remainder(lat, 360.0); if ( lat < -360.0 ) lat = remainder(lat, -360.0); if ( lat > 180.0 ) lat = 180.0 - lat; if ( lat < -180.0 ) lat = -180.0 - lat; if ( lat > 90.0 ) lat = 180.0 - lat; if ( lat < -90.0 ) lat = -180.0 - lat; return lat; } /** * Shift a point around by a number of radians */ void point_shift(const RTCTX *ctx, GEOGRAPHIC_POINT *p, double shift) { double lon = p->lon + shift; if ( lon > M_PI ) p->lon = -1.0 * M_PI + (lon - M_PI); else p->lon = lon; return; } int geographic_point_equals(const RTCTX *ctx, const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2) { return FP_EQUALS(g1->lat, g2->lat) && FP_EQUALS(g1->lon, g2->lon); } /** * Initialize a geographic point * @param lon longitude in degrees * @param lat latitude in degrees */ void geographic_point_init(const RTCTX *ctx, double lon, double lat, GEOGRAPHIC_POINT *g) { g->lat = latitude_radians_normalize(ctx, deg2rad(lat)); g->lon = longitude_radians_normalize(ctx, deg2rad(lon)); } /** Returns the angular height (latitudinal span) of the box in radians */ double gbox_angular_height(const RTCTX *ctx, const RTGBOX* gbox) { double d[6]; int i; double zmin = FLT_MAX; double zmax = -1 * FLT_MAX; POINT3D pt; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Generate all 8 corner vectors of the box */ for ( i = 0; i < 8; i++ ) { pt.x = d[i / 4]; pt.y = d[2 + (i % 4) / 2]; pt.z = d[4 + (i % 2)]; normalize(ctx, &pt); if ( pt.z < zmin ) zmin = pt.z; if ( pt.z > zmax ) zmax = pt.z; } return asin(zmax) - asin(zmin); } /** Returns the angular width (longitudinal span) of the box in radians */ double gbox_angular_width(const RTCTX *ctx, const RTGBOX* gbox) { double d[6]; int i, j; POINT3D pt[3]; double maxangle; double magnitude; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Start with the bottom corner */ pt[0].x = gbox->xmin; pt[0].y = gbox->ymin; magnitude = sqrt(pt[0].x*pt[0].x + pt[0].y*pt[0].y); pt[0].x /= magnitude; pt[0].y /= magnitude; /* Generate all 8 corner vectors of the box */ /* Find the vector furthest from our seed vector */ for ( j = 0; j < 2; j++ ) { maxangle = -1 * FLT_MAX; for ( i = 0; i < 4; i++ ) { double angle, dotprod; POINT3D pt_n; pt_n.x = d[i / 2]; pt_n.y = d[2 + (i % 2)]; magnitude = sqrt(pt_n.x*pt_n.x + pt_n.y*pt_n.y); pt_n.x /= magnitude; pt_n.y /= magnitude; pt_n.z = 0.0; dotprod = pt_n.x*pt[j].x + pt_n.y*pt[j].y; angle = acos(dotprod > 1.0 ? 1.0 : dotprod); if ( angle > maxangle ) { pt[j+1] = pt_n; maxangle = angle; } } } /* Return the distance between the two furthest vectors */ return maxangle; } /** Computes the average(ish) center of the box and returns success. */ int gbox_centroid(const RTCTX *ctx, const RTGBOX* gbox, RTPOINT2D* out) { double d[6]; GEOGRAPHIC_POINT g; POINT3D pt; int i; /* Take a copy of the box corners so we can treat them as a list */ /* Elements are xmin, xmax, ymin, ymax, zmin, zmax */ memcpy(d, &(gbox->xmin), 6*sizeof(double)); /* Zero out our return vector */ pt.x = pt.y = pt.z = 0.0; for ( i = 0; i < 8; i++ ) { POINT3D pt_n; pt_n.x = d[i / 4]; pt_n.y = d[2 + ((i % 4) / 2)]; pt_n.z = d[4 + (i % 2)]; normalize(ctx, &pt_n); pt.x += pt_n.x; pt.y += pt_n.y; pt.z += pt_n.z; } pt.x /= 8.0; pt.y /= 8.0; pt.z /= 8.0; normalize(ctx, &pt); cart2geog(ctx, &pt, &g); out->x = longitude_degrees_normalize(ctx, rad2deg(g.lon)); out->y = latitude_degrees_normalize(ctx, rad2deg(g.lat)); return RT_SUCCESS; } /** * Check to see if this geocentric gbox is wrapped around a pole. * Only makes sense if this gbox originated from a polygon, as it's assuming * the box is generated from external edges and there's an "interior" which * contains the pole. * * This function is overdetermined, for very large polygons it might add an * unwarranted pole. STILL NEEDS WORK! */ static int gbox_check_poles(const RTCTX *ctx, RTGBOX *gbox) { int rv = RT_FALSE; RTDEBUG(4, "checking poles"); RTDEBUGF(4, "gbox %s", gbox_to_string(ctx, gbox)); /* Z axis */ if ( gbox->xmin < 0.0 && gbox->xmax > 0.0 && gbox->ymin < 0.0 && gbox->ymax > 0.0 ) { if ( (gbox->zmin + gbox->zmax) > 0.0 ) { RTDEBUG(4, "enclosed positive z axis"); gbox->zmax = 1.0; } else { RTDEBUG(4, "enclosed negative z axis"); gbox->zmin = -1.0; } rv = RT_TRUE; } /* Y axis */ if ( gbox->xmin < 0.0 && gbox->xmax > 0.0 && gbox->zmin < 0.0 && gbox->zmax > 0.0 ) { if ( gbox->ymin + gbox->ymax > 0.0 ) { RTDEBUG(4, "enclosed positive y axis"); gbox->ymax = 1.0; } else { RTDEBUG(4, "enclosed negative y axis"); gbox->ymin = -1.0; } rv = RT_TRUE; } /* X axis */ if ( gbox->ymin < 0.0 && gbox->ymax > 0.0 && gbox->zmin < 0.0 && gbox->zmax > 0.0 ) { if ( gbox->xmin + gbox->xmax > 0.0 ) { RTDEBUG(4, "enclosed positive x axis"); gbox->xmax = 1.0; } else { RTDEBUG(4, "enclosed negative x axis"); gbox->xmin = -1.0; } rv = RT_TRUE; } return rv; } /** * Convert spherical coordinates to cartesion coordinates on unit sphere */ void geog2cart(const RTCTX *ctx, const GEOGRAPHIC_POINT *g, POINT3D *p) { p->x = cos(g->lat) * cos(g->lon); p->y = cos(g->lat) * sin(g->lon); p->z = sin(g->lat); } /** * Convert cartesion coordinates on unit sphere to spherical coordinates */ void cart2geog(const RTCTX *ctx, const POINT3D *p, GEOGRAPHIC_POINT *g) { g->lon = atan2(p->y, p->x); g->lat = asin(p->z); } /** * Convert lon/lat coordinates to cartesion coordinates on unit sphere */ void ll2cart(const RTCTX *ctx, const RTPOINT2D *g, POINT3D *p) { double x_rad = M_PI * g->x / 180.0; double y_rad = M_PI * g->y / 180.0; double cos_y_rad = cos(y_rad); p->x = cos_y_rad * cos(x_rad); p->y = cos_y_rad * sin(x_rad); p->z = sin(y_rad); } /** * Convert cartesion coordinates on unit sphere to lon/lat coordinates static void cart2ll(const POINT3D *p, RTPOINT2D *g) { g->x = longitude_degrees_normalize(ctx, 180.0 * atan2(p->y, p->x) / M_PI); g->y = latitude_degrees_normalize(ctx, 180.0 * asin(p->z) / M_PI); } */ /** * Calculate the dot product of two unit vectors * (-1 == opposite, 0 == orthogonal, 1 == identical) */ static double dot_product(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2) { return (p1->x*p2->x) + (p1->y*p2->y) + (p1->z*p2->z); } /** * Calculate the cross product of two vectors */ static void cross_product(const RTCTX *ctx, const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->y * b->z - a->z * b->y; n->y = a->z * b->x - a->x * b->z; n->z = a->x * b->y - a->y * b->x; return; } /** * Calculate the sum of two vectors */ void vector_sum(const RTCTX *ctx, const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->x + b->x; n->y = a->y + b->y; n->z = a->z + b->z; return; } /** * Calculate the difference of two vectors */ static void vector_difference(const RTCTX *ctx, const POINT3D *a, const POINT3D *b, POINT3D *n) { n->x = a->x - b->x; n->y = a->y - b->y; n->z = a->z - b->z; return; } /** * Scale a vector out by a factor */ static void vector_scale(const RTCTX *ctx, POINT3D *n, double scale) { n->x *= scale; n->y *= scale; n->z *= scale; return; } /* * static inline double vector_magnitude(const POINT3D* v) * { * return sqrt(v->x*v->x + v->y*v->y + v->z*v->z); * } */ /** * Angle between two unit vectors */ double vector_angle(const RTCTX *ctx, const POINT3D* v1, const POINT3D* v2) { POINT3D v3, normal; double angle, x, y; cross_product(ctx, v1, v2, &normal); normalize(ctx, &normal); cross_product(ctx, &normal, v1, &v3); x = dot_product(ctx, v1, v2); y = dot_product(ctx, v2, &v3); angle = atan2(y, x); return angle; } /** * Normalize to a unit vector. */ static void normalize2d(const RTCTX *ctx, RTPOINT2D *p) { double d = sqrt(p->x*p->x + p->y*p->y); if (FP_IS_ZERO(d)) { p->x = p->y = 0.0; return; } p->x = p->x / d; p->y = p->y / d; return; } /** * Calculates the unit normal to two vectors, trying to avoid * problems with over-narrow or over-wide cases. */ void unit_normal(const RTCTX *ctx, const POINT3D *P1, const POINT3D *P2, POINT3D *normal) { double p_dot = dot_product(ctx, P1, P2); POINT3D P3; /* If edge is really large, calculate a narrower equivalent angle A1/A3. */ if ( p_dot < 0 ) { vector_sum(ctx, P1, P2, &P3); normalize(ctx, &P3); } /* If edge is narrow, calculate a wider equivalent angle A1/A3. */ else if ( p_dot > 0.95 ) { vector_difference(ctx, P2, P1, &P3); normalize(ctx, &P3); } /* Just keep the current angle in A1/A3. */ else { P3 = *P2; } /* Normals to the A-plane and B-plane */ cross_product(ctx, P1, &P3, normal); normalize(ctx, normal); } /** * Rotates v1 through an angle (in radians) within the plane defined by v1/v2, returns * the rotated vector in n. */ void vector_rotate(const RTCTX *ctx, const POINT3D* v1, const POINT3D* v2, double angle, POINT3D* n) { POINT3D u; double cos_a = cos(angle); double sin_a = sin(angle); double uxuy, uyuz, uxuz; double ux2, uy2, uz2; double rxx, rxy, rxz, ryx, ryy, ryz, rzx, rzy, rzz; /* Need a unit vector normal to rotate around */ unit_normal(ctx, v1, v2, &u); uxuy = u.x * u.y; uxuz = u.x * u.z; uyuz = u.y * u.z; ux2 = u.x * u.x; uy2 = u.y * u.y; uz2 = u.z * u.z; rxx = cos_a + ux2 * (1 - cos_a); rxy = uxuy * (1 - cos_a) - u.z * sin_a; rxz = uxuz * (1 - cos_a) + u.y * sin_a; ryx = uxuy * (1 - cos_a) + u.z * sin_a; ryy = cos_a + uy2 * (1 - cos_a); ryz = uyuz * (1 - cos_a) - u.x * sin_a; rzx = uxuz * (1 - cos_a) - u.y * sin_a; rzy = uyuz * (1 - cos_a) + u.x * sin_a; rzz = cos_a + uz2 * (1 - cos_a); n->x = rxx * v1->x + rxy * v1->y + rxz * v1->z; n->y = ryx * v1->x + ryy * v1->y + ryz * v1->z; n->z = rzx * v1->x + rzy * v1->y + rzz * v1->z; normalize(ctx, n); } /** * Normalize to a unit vector. */ void normalize(const RTCTX *ctx, POINT3D *p) { double d = sqrt(p->x*p->x + p->y*p->y + p->z*p->z); if (FP_IS_ZERO(d)) { p->x = p->y = p->z = 0.0; return; } p->x = p->x / d; p->y = p->y / d; p->z = p->z / d; return; } /** * Computes the cross product of two vectors using their lat, lng representations. * Good even for small distances between p and q. */ void robust_cross_product(const RTCTX *ctx, const GEOGRAPHIC_POINT *p, const GEOGRAPHIC_POINT *q, POINT3D *a) { double lon_qpp = (q->lon + p->lon) / -2.0; double lon_qmp = (q->lon - p->lon) / 2.0; double sin_p_lat_minus_q_lat = sin(p->lat-q->lat); double sin_p_lat_plus_q_lat = sin(p->lat+q->lat); double sin_lon_qpp = sin(lon_qpp); double sin_lon_qmp = sin(lon_qmp); double cos_lon_qpp = cos(lon_qpp); double cos_lon_qmp = cos(lon_qmp); a->x = sin_p_lat_minus_q_lat * sin_lon_qpp * cos_lon_qmp - sin_p_lat_plus_q_lat * cos_lon_qpp * sin_lon_qmp; a->y = sin_p_lat_minus_q_lat * cos_lon_qpp * cos_lon_qmp + sin_p_lat_plus_q_lat * sin_lon_qpp * sin_lon_qmp; a->z = cos(p->lat) * cos(q->lat) * sin(q->lon-p->lon); } void x_to_z(const RTCTX *ctx, POINT3D *p) { double tmp = p->z; p->z = p->x; p->x = tmp; } void y_to_z(const RTCTX *ctx, POINT3D *p) { double tmp = p->z; p->z = p->y; p->y = tmp; } int crosses_dateline(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e) { double sign_s = signum(s->lon); double sign_e = signum(e->lon); double ss = fabs(s->lon); double ee = fabs(e->lon); if ( sign_s == sign_e ) { return RT_FALSE; } else { double dl = ss + ee; if ( dl < M_PI ) return RT_FALSE; else if ( FP_EQUALS(dl, M_PI) ) return RT_FALSE; else return RT_TRUE; } } /** * Returns -1 if the point is to the left of the plane formed * by the edge, 1 if the point is to the right, and 0 if the * point is on the plane. */ static int edge_point_side(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { POINT3D normal, pt; double w; /* Normal to the plane defined by e */ robust_cross_product(ctx, &(e->start), &(e->end), &normal); normalize(ctx, &normal); geog2cart(ctx, p, &pt); /* We expect the dot product of with normal with any vector in the plane to be zero */ w = dot_product(ctx, &normal, &pt); RTDEBUGF(4,"dot product %.9g",w); if ( FP_IS_ZERO(w) ) { RTDEBUG(4, "point is on plane (dot product is zero)"); return 0; } if ( w < 0 ) return -1; else return 1; } /** * Returns the angle in radians at point B of the triangle formed by A-B-C */ static double sphere_angle(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { POINT3D normal1, normal2; robust_cross_product(ctx, b, a, &normal1); robust_cross_product(ctx, b, c, &normal2); normalize(ctx, &normal1); normalize(ctx, &normal2); return sphere_distance_cartesian(ctx, &normal1, &normal2); } /** * Computes the spherical area of a triangle. If C is to the left of A/B, * the area is negative. If C is to the right of A/B, the area is positive. * * @param a The first triangle vertex. * @param b The second triangle vertex. * @param c The last triangle vertex. * @return the signed area in radians. */ static double sphere_signed_area(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { double angle_a, angle_b, angle_c; double area_radians = 0.0; int side; GEOGRAPHIC_EDGE e; angle_a = sphere_angle(ctx, b,a,c); angle_b = sphere_angle(ctx, a,b,c); angle_c = sphere_angle(ctx, b,c,a); area_radians = angle_a + angle_b + angle_c - M_PI; /* What's the direction of the B/C edge? */ e.start = *a; e.end = *b; side = edge_point_side(ctx, &e, c); /* Co-linear points implies no area */ if ( side == 0 ) return 0.0; /* Add the sign to the area */ return side * area_radians; } /** * Returns true if the point p is on the great circle plane. * Forms the scalar triple product of A,B,p and if the volume of the * resulting parallelepiped is near zero the point p is on the * great circle plane. */ int edge_point_on_plane(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { int side = edge_point_side(ctx, e, p); if ( side == 0 ) return RT_TRUE; return RT_FALSE; } /** * Returns true if the point p is inside the cone defined by the * two ends of the edge e. */ int edge_point_in_cone(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { POINT3D vcp, vs, ve, vp; double vs_dot_vcp, vp_dot_vcp; geog2cart(ctx, &(e->start), &vs); geog2cart(ctx, &(e->end), &ve); /* Antipodal case, everything is inside. */ if ( vs.x == -1.0 * ve.x && vs.y == -1.0 * ve.y && vs.z == -1.0 * ve.z ) return RT_TRUE; geog2cart(ctx, p, &vp); /* The normalized sum bisects the angle between start and end. */ vector_sum(ctx, &vs, &ve, &vcp); normalize(ctx, &vcp); /* The projection of start onto the center defines the minimum similarity */ vs_dot_vcp = dot_product(ctx, &vs, &vcp); RTDEBUGF(4,"vs_dot_vcp %.19g",vs_dot_vcp); /* The projection of candidate p onto the center */ vp_dot_vcp = dot_product(ctx, &vp, &vcp); RTDEBUGF(4,"vp_dot_vcp %.19g",vp_dot_vcp); /* If p is more similar than start then p is inside the cone */ RTDEBUGF(4,"fabs(vp_dot_vcp - vs_dot_vcp) %.39g",fabs(vp_dot_vcp - vs_dot_vcp)); /* ** We want to test that vp_dot_vcp is >= vs_dot_vcp but there are ** numerical stability issues for values that are very very nearly ** equal. Unfortunately there are also values of vp_dot_vcp that are legitimately ** very close to but still less than vs_dot_vcp which we also need to catch. ** The tolerance of 10-17 seems to do the trick on 32-bit and 64-bit architectures, ** for the test cases here. ** However, tuning the tolerance value feels like a dangerous hack. ** Fundamentally, the problem is that this test is so sensitive. */ /* 1.1102230246251565404236316680908203125e-16 */ if ( vp_dot_vcp > vs_dot_vcp || fabs(vp_dot_vcp - vs_dot_vcp) < 2e-16 ) { RTDEBUG(4, "point is in cone"); return RT_TRUE; } RTDEBUG(4, "point is not in cone"); return RT_FALSE; } /** * True if the longitude of p is within the range of the longitude of the ends of e */ int edge_contains_coplanar_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { GEOGRAPHIC_EDGE g; GEOGRAPHIC_POINT q; double slon = fabs((e->start).lon) + fabs((e->end).lon); double dlon = fabs(fabs((e->start).lon) - fabs((e->end).lon)); double slat = (e->start).lat + (e->end).lat; RTDEBUGF(4, "e.start == GPOINT(%.6g %.6g) ", (e->start).lat, (e->start).lon); RTDEBUGF(4, "e.end == GPOINT(%.6g %.6g) ", (e->end).lat, (e->end).lon); RTDEBUGF(4, "p == GPOINT(%.6g %.6g) ", p->lat, p->lon); /* Copy values into working registers */ g = *e; q = *p; /* Vertical plane, we need to do this calculation in latitude */ if ( FP_EQUALS( g.start.lon, g.end.lon ) ) { RTDEBUG(4, "vertical plane, we need to do this calculation in latitude"); /* Supposed to be co-planar... */ if ( ! FP_EQUALS( q.lon, g.start.lon ) ) return RT_FALSE; if ( ( g.start.lat <= q.lat && q.lat <= g.end.lat ) || ( g.end.lat <= q.lat && q.lat <= g.start.lat ) ) { return RT_TRUE; } else { return RT_FALSE; } } /* Over the pole, we need normalize latitude and do this calculation in latitude */ if ( FP_EQUALS( slon, M_PI ) && ( signum(g.start.lon) != signum(g.end.lon) || FP_EQUALS(dlon, M_PI) ) ) { RTDEBUG(4, "over the pole..."); /* Antipodal, everything (or nothing?) is inside */ if ( FP_EQUALS( slat, 0.0 ) ) return RT_TRUE; /* Point *is* the north pole */ if ( slat > 0.0 && FP_EQUALS(q.lat, M_PI_2 ) ) return RT_TRUE; /* Point *is* the south pole */ if ( slat < 0.0 && FP_EQUALS(q.lat, -1.0 * M_PI_2) ) return RT_TRUE; RTDEBUG(4, "coplanar?..."); /* Supposed to be co-planar... */ if ( ! FP_EQUALS( q.lon, g.start.lon ) ) return RT_FALSE; RTDEBUG(4, "north or south?..."); /* Over north pole, test based on south pole */ if ( slat > 0.0 ) { RTDEBUG(4, "over the north pole..."); if ( q.lat > FP_MIN(g.start.lat, g.end.lat) ) return RT_TRUE; else return RT_FALSE; } else /* Over south pole, test based on north pole */ { RTDEBUG(4, "over the south pole..."); if ( q.lat < FP_MAX(g.start.lat, g.end.lat) ) return RT_TRUE; else return RT_FALSE; } } /* Dateline crossing, flip everything to the opposite hemisphere */ else if ( slon > M_PI && ( signum(g.start.lon) != signum(g.end.lon) ) ) { RTDEBUG(4, "crosses dateline, flip longitudes..."); if ( g.start.lon > 0.0 ) g.start.lon -= M_PI; else g.start.lon += M_PI; if ( g.end.lon > 0.0 ) g.end.lon -= M_PI; else g.end.lon += M_PI; if ( q.lon > 0.0 ) q.lon -= M_PI; else q.lon += M_PI; } if ( ( g.start.lon <= q.lon && q.lon <= g.end.lon ) || ( g.end.lon <= q.lon && q.lon <= g.start.lon ) ) { RTDEBUG(4, "true, this edge contains point"); return RT_TRUE; } RTDEBUG(4, "false, this edge does not contain point"); return RT_FALSE; } /** * Given two points on a unit sphere, calculate their distance apart in radians. */ double sphere_distance(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e) { double d_lon = e->lon - s->lon; double cos_d_lon = cos(d_lon); double cos_lat_e = cos(e->lat); double sin_lat_e = sin(e->lat); double cos_lat_s = cos(s->lat); double sin_lat_s = sin(s->lat); double a1 = POW2(cos_lat_e * sin(d_lon)); double a2 = POW2(cos_lat_s * sin_lat_e - sin_lat_s * cos_lat_e * cos_d_lon); double a = sqrt(a1 + a2); double b = sin_lat_s * sin_lat_e + cos_lat_s * cos_lat_e * cos_d_lon; return atan2(a, b); } /** * Given two unit vectors, calculate their distance apart in radians. */ double sphere_distance_cartesian(const RTCTX *ctx, const POINT3D *s, const POINT3D *e) { return acos(dot_product(ctx, s, e)); } /** * Given two points on a unit sphere, calculate the direction from s to e. */ double sphere_direction(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d) { double heading = 0.0; double f; /* Starting from the poles? Special case. */ if ( FP_IS_ZERO(cos(s->lat)) ) return (s->lat > 0.0) ? M_PI : 0.0; f = (sin(e->lat) - sin(s->lat) * cos(d)) / (sin(d) * cos(s->lat)); if ( FP_EQUALS(f, 1.0) ) heading = 0.0; else if ( fabs(f) > 1.0 ) { RTDEBUGF(4, "f = %g", f); heading = acos(f); } else heading = acos(f); if ( sin(e->lon - s->lon) < 0.0 ) heading = -1 * heading; return heading; } #if 0 /* unused */ /** * Computes the spherical excess of a spherical triangle defined by * the three vectices A, B, C. Computes on the unit sphere (i.e., divides * edge lengths by the radius, even if the radius is 1.0). The excess is * signed based on the sign of the delta longitude of A and B. * * @param a The first triangle vertex. * @param b The second triangle vertex. * @param c The last triangle vertex. * @return the signed spherical excess. */ static double sphere_excess(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c) { double a_dist = sphere_distance(ctx, b, c); double b_dist = sphere_distance(ctx, c, a); double c_dist = sphere_distance(ctx, a, b); double hca = sphere_direction(ctx, c, a, b_dist); double hcb = sphere_direction(ctx, c, b, a_dist); double sign = signum(hcb-hca); double ss = (a_dist + b_dist + c_dist) / 2.0; double E = tan(ss/2.0)*tan((ss-a_dist)/2.0)*tan((ss-b_dist)/2.0)*tan((ss-c_dist)/2.0); return 4.0 * atan(sqrt(fabs(E))) * sign; } #endif /** * Returns true if the point p is on the minor edge defined by the * end points of e. */ int edge_contains_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p) { if ( edge_point_in_cone(ctx, e, p) && edge_point_on_plane(ctx, e, p) ) /* if ( edge_contains_coplanar_point(ctx, e, p) && edge_point_on_plane(ctx, e, p) ) */ { RTDEBUG(4, "point is on edge"); return RT_TRUE; } RTDEBUG(4, "point is not on edge"); return RT_FALSE; } /** * Used in great circle to compute the pole of the great circle. */ double z_to_latitude(const RTCTX *ctx, double z, int top) { double sign = signum(z); double tlat = acos(z); RTDEBUGF(4, "inputs: z(%.8g) sign(%.8g) tlat(%.8g)", z, sign, tlat); if (FP_IS_ZERO(z)) { if (top) return M_PI_2; else return -1.0 * M_PI_2; } if (fabs(tlat) > M_PI_2 ) { tlat = sign * (M_PI - fabs(tlat)); } else { tlat = sign * tlat; } RTDEBUGF(4, "output: tlat(%.8g)", tlat); return tlat; } /** * Computes the pole of the great circle disk which is the intersection of * the great circle with the line of maximum/minimum gradiant that lies on * the great circle plane. */ int clairaut_cartesian(const RTCTX *ctx, const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom) { POINT3D t1, t2; GEOGRAPHIC_POINT vN1, vN2; RTDEBUG(4,"entering function"); unit_normal(ctx, start, end, &t1); unit_normal(ctx, end, start, &t2); RTDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z); RTDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z); cart2geog(ctx, &t1, &vN1); cart2geog(ctx, &t2, &vN2); g_top->lat = z_to_latitude(ctx, t1.z,RT_TRUE); g_top->lon = vN2.lon; g_bottom->lat = z_to_latitude(ctx, t2.z,RT_FALSE); g_bottom->lon = vN1.lon; RTDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon); RTDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon); return RT_SUCCESS; } /** * Computes the pole of the great circle disk which is the intersection of * the great circle with the line of maximum/minimum gradiant that lies on * the great circle plane. */ int clairaut_geographic(const RTCTX *ctx, const GEOGRAPHIC_POINT *start, const GEOGRAPHIC_POINT *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom) { POINT3D t1, t2; GEOGRAPHIC_POINT vN1, vN2; RTDEBUG(4,"entering function"); robust_cross_product(ctx, start, end, &t1); normalize(ctx, &t1); robust_cross_product(ctx, end, start, &t2); normalize(ctx, &t2); RTDEBUGF(4, "unit normal t1 == POINT(%.8g %.8g %.8g)", t1.x, t1.y, t1.z); RTDEBUGF(4, "unit normal t2 == POINT(%.8g %.8g %.8g)", t2.x, t2.y, t2.z); cart2geog(ctx, &t1, &vN1); cart2geog(ctx, &t2, &vN2); g_top->lat = z_to_latitude(ctx, t1.z,RT_TRUE); g_top->lon = vN2.lon; g_bottom->lat = z_to_latitude(ctx, t2.z,RT_FALSE); g_bottom->lon = vN1.lon; RTDEBUGF(4, "clairaut top == GPOINT(%.6g %.6g)", g_top->lat, g_top->lon); RTDEBUGF(4, "clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->lat, g_bottom->lon); return RT_SUCCESS; } /** * Returns true if an intersection can be calculated, and places it in *g. * Returns false otherwise. */ int edge_intersection(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g) { POINT3D ea, eb, v; RTDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", e1->start.lat, e1->start.lon, e1->end.lat, e1->end.lon); RTDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", e2->start.lat, e2->start.lon, e2->end.lat, e2->end.lon); RTDEBUGF(4, "e1 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e1->start.lon), rad2deg(e1->start.lat), rad2deg(e1->end.lon), rad2deg(e1->end.lat)); RTDEBUGF(4, "e2 start(%.20g %.20g) end(%.20g %.20g)", rad2deg(e2->start.lon), rad2deg(e2->start.lat), rad2deg(e2->end.lon), rad2deg(e2->end.lat)); if ( geographic_point_equals(ctx, &(e1->start), &(e2->start)) ) { *g = e1->start; return RT_TRUE; } if ( geographic_point_equals(ctx, &(e1->end), &(e2->end)) ) { *g = e1->end; return RT_TRUE; } if ( geographic_point_equals(ctx, &(e1->end), &(e2->start)) ) { *g = e1->end; return RT_TRUE; } if ( geographic_point_equals(ctx, &(e1->start), &(e2->end)) ) { *g = e1->start; return RT_TRUE; } robust_cross_product(ctx, &(e1->start), &(e1->end), &ea); normalize(ctx, &ea); robust_cross_product(ctx, &(e2->start), &(e2->end), &eb); normalize(ctx, &eb); RTDEBUGF(4, "e1 cross product == POINT(%.12g %.12g %.12g)", ea.x, ea.y, ea.z); RTDEBUGF(4, "e2 cross product == POINT(%.12g %.12g %.12g)", eb.x, eb.y, eb.z); RTDEBUGF(4, "fabs(dot_product(ctx, ea, eb)) == %.14g", fabs(dot_product(ctx, &ea, &eb))); if ( FP_EQUALS(fabs(dot_product(ctx, &ea, &eb)), 1.0) ) { RTDEBUGF(4, "parallel edges found! dot_product = %.12g", dot_product(ctx, &ea, &eb)); /* Parallel (maybe equal) edges! */ /* Hack alert, only returning ONE end of the edge right now, most do better later. */ /* Hack alart #2, returning a value of 2 to indicate a co-linear crossing event. */ if ( edge_contains_point(ctx, e1, &(e2->start)) ) { *g = e2->start; return 2; } if ( edge_contains_point(ctx, e1, &(e2->end)) ) { *g = e2->end; return 2; } if ( edge_contains_point(ctx, e2, &(e1->start)) ) { *g = e1->start; return 2; } if ( edge_contains_point(ctx, e2, &(e1->end)) ) { *g = e1->end; return 2; } } unit_normal(ctx, &ea, &eb, &v); RTDEBUGF(4, "v == POINT(%.12g %.12g %.12g)", v.x, v.y, v.z); g->lat = atan2(v.z, sqrt(v.x * v.x + v.y * v.y)); g->lon = atan2(v.y, v.x); RTDEBUGF(4, "g == GPOINT(%.12g %.12g)", g->lat, g->lon); RTDEBUGF(4, "g == POINT(%.12g %.12g)", rad2deg(g->lon), rad2deg(g->lat)); if ( edge_contains_point(ctx, e1, g) && edge_contains_point(ctx, e2, g) ) { return RT_TRUE; } else { RTDEBUG(4, "flipping point to other side of sphere"); g->lat = -1.0 * g->lat; g->lon = g->lon + M_PI; if ( g->lon > M_PI ) { g->lon = -1.0 * (2.0 * M_PI - g->lon); } if ( edge_contains_point(ctx, e1, g) && edge_contains_point(ctx, e2, g) ) { return RT_TRUE; } } return RT_FALSE; } double edge_distance_to_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest) { double d1 = 1000000000.0, d2, d3, d_nearest; POINT3D n, p, k; GEOGRAPHIC_POINT gk, g_nearest; /* Zero length edge, */ if ( geographic_point_equals(ctx, &(e->start), &(e->end)) ) { *closest = e->start; return sphere_distance(ctx, &(e->start), gp); } robust_cross_product(ctx, &(e->start), &(e->end), &n); normalize(ctx, &n); geog2cart(ctx, gp, &p); vector_scale(ctx, &n, dot_product(ctx, &p, &n)); vector_difference(ctx, &p, &n, &k); normalize(ctx, &k); cart2geog(ctx, &k, &gk); if ( edge_contains_point(ctx, e, &gk) ) { d1 = sphere_distance(ctx, gp, &gk); } d2 = sphere_distance(ctx, gp, &(e->start)); d3 = sphere_distance(ctx, gp, &(e->end)); d_nearest = d1; g_nearest = gk; if ( d2 < d_nearest ) { d_nearest = d2; g_nearest = e->start; } if ( d3 < d_nearest ) { d_nearest = d3; g_nearest = e->end; } if (closest) *closest = g_nearest; return d_nearest; } /** * Calculate the distance between two edges. * IMPORTANT: this test does not check for edge intersection!!! (distance == 0) * You have to check for intersection before calling this function. */ double edge_distance_to_edge(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2) { double d; GEOGRAPHIC_POINT gcp1s, gcp1e, gcp2s, gcp2e, c1, c2; double d1s = edge_distance_to_point(ctx, e1, &(e2->start), &gcp1s); double d1e = edge_distance_to_point(ctx, e1, &(e2->end), &gcp1e); double d2s = edge_distance_to_point(ctx, e2, &(e1->start), &gcp2s); double d2e = edge_distance_to_point(ctx, e2, &(e1->end), &gcp2e); d = d1s; c1 = gcp1s; c2 = e2->start; if ( d1e < d ) { d = d1e; c1 = gcp1e; c2 = e2->end; } if ( d2s < d ) { d = d2s; c1 = e1->start; c2 = gcp2s; } if ( d2e < d ) { d = d2e; c1 = e1->end; c2 = gcp2e; } if ( closest1 ) *closest1 = c1; if ( closest2 ) *closest2 = c2; return d; } /** * Given a starting location r, a distance and an azimuth * to the new point, compute the location of the projected point on the unit sphere. */ int sphere_project(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n) { double d = distance; double lat1 = r->lat; double lon1 = r->lon; double lat2, lon2; lat2 = asin(sin(lat1)*cos(d) + cos(lat1)*sin(d)*cos(azimuth)); /* If we're going straight up or straight down, we don't need to calculate the longitude */ /* TODO: this isn't quite true, what if we're going over the pole? */ if ( FP_EQUALS(azimuth, M_PI) || FP_EQUALS(azimuth, 0.0) ) { lon2 = r->lon; } else { lon2 = lon1 + atan2(sin(azimuth)*sin(d)*cos(lat1), cos(d)-sin(lat1)*sin(lat2)); } if ( isnan(lat2) || isnan(lon2) ) return RT_FAILURE; n->lat = lat2; n->lon = lon2; return RT_SUCCESS; } int edge_calculate_gbox_slow(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, RTGBOX *gbox) { int steps = 1000000; int i; double dx, dy, dz; double distance = sphere_distance(ctx, &(e->start), &(e->end)); POINT3D pn, p, start, end; /* Edge is zero length, just return the naive box */ if ( FP_IS_ZERO(distance) ) { RTDEBUG(4, "edge is zero length. returning"); geog2cart(ctx, &(e->start), &start); geog2cart(ctx, &(e->end), &end); gbox_init_point3d(ctx, &start, gbox); gbox_merge_point3d(ctx, &end, gbox); return RT_SUCCESS; } /* Edge is antipodal (one point on each side of the globe), set the box to contain the whole world and return */ if ( FP_EQUALS(distance, M_PI) ) { RTDEBUG(4, "edge is antipodal. setting to maximum size box, and returning"); gbox->xmin = gbox->ymin = gbox->zmin = -1.0; gbox->xmax = gbox->ymax = gbox->zmax = 1.0; return RT_SUCCESS; } /* Walk along the chord between start and end incrementally, normalizing at each step. */ geog2cart(ctx, &(e->start), &start); geog2cart(ctx, &(e->end), &end); dx = (end.x - start.x)/steps; dy = (end.y - start.y)/steps; dz = (end.z - start.z)/steps; p = start; gbox->xmin = gbox->xmax = p.x; gbox->ymin = gbox->ymax = p.y; gbox->zmin = gbox->zmax = p.z; for ( i = 0; i < steps; i++ ) { p.x += dx; p.y += dy; p.z += dz; pn = p; normalize(ctx, &pn); gbox_merge_point3d(ctx, &pn, gbox); } return RT_SUCCESS; } /** * The magic function, given an edge in spherical coordinates, calculate a * 3D bounding box that fully contains it, taking into account the curvature * of the sphere on which it is inscribed. * * Any arc on the sphere defines a plane that bisects the sphere. In this plane, * the arc is a portion of a unit circle. * Projecting the end points of the axes (1,0,0), (-1,0,0) etc, into the plane * and normalizing yields potential extrema points. Those points on the * side of the plane-dividing line formed by the end points that is opposite * the origin of the plane are extrema and should be added to the bounding box. */ int edge_calculate_gbox(const RTCTX *ctx, const POINT3D *A1, const POINT3D *A2, RTGBOX *gbox) { RTPOINT2D R1, R2, RX, O; POINT3D AN, A3; POINT3D X[6]; int i, o_side; /* Initialize the box with the edge end points */ gbox_init_point3d(ctx, A1, gbox); gbox_merge_point3d(ctx, A2, gbox); /* Zero length edge, just return! */ if ( p3d_same(ctx, A1, A2) ) return RT_SUCCESS; /* Error out on antipodal edge */ if ( FP_EQUALS(A1->x, -1*A2->x) && FP_EQUALS(A1->y, -1*A2->y) && FP_EQUALS(A1->z, -1*A2->z) ) { rterror(ctx, "Antipodal (180 degrees long) edge detected!"); return RT_FAILURE; } /* Create A3, a vector in the plane of A1/A2, orthogonal to A1 */ unit_normal(ctx, A1, A2, &AN); unit_normal(ctx, &AN, A1, &A3); /* Project A1 and A2 into the 2-space formed by the plane A1/A3 */ R1.x = 1.0; R1.y = 0.0; R2.x = dot_product(ctx, A2, A1); R2.y = dot_product(ctx, A2, &A3); /* Initialize our 3-space axis points (x+, x-, y+, y-, z+, z-) */ memset(X, 0, sizeof(POINT3D) * 6); X[0].x = X[2].y = X[4].z = 1.0; X[1].x = X[3].y = X[5].z = -1.0; /* Initialize a 2-space origin point. */ O.x = O.y = 0.0; /* What side of the line joining R1/R2 is O? */ o_side = rt_segment_side(ctx, &R1, &R2, &O); /* Add any extrema! */ for ( i = 0; i < 6; i++ ) { /* Convert 3-space axis points to 2-space unit vectors */ RX.x = dot_product(ctx, &(X[i]), A1); RX.y = dot_product(ctx, &(X[i]), &A3); normalize2d(ctx, &RX); /* Any axis end on the side of R1/R2 opposite the origin */ /* is an extreme point in the arc, so we add the 3-space */ /* version of the point on R1/R2 to the gbox */ if ( rt_segment_side(ctx, &R1, &R2, &RX) != o_side ) { POINT3D Xn; Xn.x = RX.x * A1->x + RX.y * A3.x; Xn.y = RX.x * A1->y + RX.y * A3.y; Xn.z = RX.x * A1->z + RX.y * A3.z; gbox_merge_point3d(ctx, &Xn, gbox); } } return RT_SUCCESS; } void rtpoly_pt_outside(const RTCTX *ctx, const RTPOLY *poly, RTPOINT2D *pt_outside) { /* Make sure we have boxes */ if ( poly->bbox ) { gbox_pt_outside(ctx, poly->bbox, pt_outside); return; } else { RTGBOX gbox; rtgeom_calculate_gbox_geodetic(ctx, (RTGEOM*)poly, &gbox); gbox_pt_outside(ctx, &gbox, pt_outside); return; } } /** * Given a unit geocentric gbox, return a lon/lat (degrees) coordinate point point that is * guaranteed to be outside the box (and therefore anything it contains). */ void gbox_pt_outside(const RTCTX *ctx, const RTGBOX *gbox, RTPOINT2D *pt_outside) { double grow = M_PI / 180.0 / 60.0; /* one arc-minute */ int i; RTGBOX ge; POINT3D corners[8]; POINT3D pt; GEOGRAPHIC_POINT g; while ( grow < M_PI ) { /* Assign our box and expand it slightly. */ ge = *gbox; if ( ge.xmin > -1 ) ge.xmin -= grow; if ( ge.ymin > -1 ) ge.ymin -= grow; if ( ge.zmin > -1 ) ge.zmin -= grow; if ( ge.xmax < 1 ) ge.xmax += grow; if ( ge.ymax < 1 ) ge.ymax += grow; if ( ge.zmax < 1 ) ge.zmax += grow; /* Build our eight corner points */ corners[0].x = ge.xmin; corners[0].y = ge.ymin; corners[0].z = ge.zmin; corners[1].x = ge.xmin; corners[1].y = ge.ymax; corners[1].z = ge.zmin; corners[2].x = ge.xmin; corners[2].y = ge.ymin; corners[2].z = ge.zmax; corners[3].x = ge.xmax; corners[3].y = ge.ymin; corners[3].z = ge.zmin; corners[4].x = ge.xmax; corners[4].y = ge.ymax; corners[4].z = ge.zmin; corners[5].x = ge.xmax; corners[5].y = ge.ymin; corners[5].z = ge.zmax; corners[6].x = ge.xmin; corners[6].y = ge.ymax; corners[6].z = ge.zmax; corners[7].x = ge.xmax; corners[7].y = ge.ymax; corners[7].z = ge.zmax; RTDEBUG(4, "trying to use a box corner point..."); for ( i = 0; i < 8; i++ ) { normalize(ctx, &(corners[i])); RTDEBUGF(4, "testing corner %d: POINT(%.8g %.8g %.8g)", i, corners[i].x, corners[i].y, corners[i].z); if ( ! gbox_contains_point3d(ctx, gbox, &(corners[i])) ) { RTDEBUGF(4, "corner %d is outside our gbox", i); pt = corners[i]; normalize(ctx, &pt); cart2geog(ctx, &pt, &g); pt_outside->x = rad2deg(g.lon); pt_outside->y = rad2deg(g.lat); RTDEBUGF(4, "returning POINT(%.8g %.8g) as outside point", pt_outside->x, pt_outside->y); return; } } /* Try a wider growth to push the corners outside the original box. */ grow *= 2.0; } /* This should never happen! */ rterror(ctx, "BOOM! Could not generate outside point!"); return; } /** * Create a new point array with no segment longer than the input segment length (expressed in radians!) * @param pa_in - input point array pointer * @param max_seg_length - maximum output segment length in radians */ static RTPOINTARRAY* ptarray_segmentize_sphere(const RTCTX *ctx, const RTPOINTARRAY *pa_in, double max_seg_length) { RTPOINTARRAY *pa_out; int hasz = ptarray_has_z(ctx, pa_in); int hasm = ptarray_has_m(ctx, pa_in); int pa_in_offset = 0; /* input point offset */ RTPOINT4D p1, p2, p; POINT3D q1, q2, q, qn; GEOGRAPHIC_POINT g1, g2, g; double d; /* Just crap out on crazy input */ if ( ! pa_in ) rterror(ctx, "ptarray_segmentize_sphere: null input pointarray"); if ( max_seg_length <= 0.0 ) rterror(ctx, "ptarray_segmentize_sphere: maximum segment length must be positive"); /* Empty starting array */ pa_out = ptarray_construct_empty(ctx, hasz, hasm, pa_in->npoints); /* Add first point */ rt_getPoint4d_p(ctx, pa_in, pa_in_offset, &p1); ptarray_append_point(ctx, pa_out, &p1, RT_FALSE); geographic_point_init(ctx, p1.x, p1.y, &g1); pa_in_offset++; while ( pa_in_offset < pa_in->npoints ) { rt_getPoint4d_p(ctx, pa_in, pa_in_offset, &p2); geographic_point_init(ctx, p2.x, p2.y, &g2); /* Skip duplicate points (except in case of 2-point lines!) */ if ( (pa_in->npoints > 2) && p4d_same(ctx, &p1, &p2) ) { /* Move one offset forward */ p1 = p2; g1 = g2; pa_in_offset++; continue; } /* How long is this edge? */ d = sphere_distance(ctx, &g1, &g2); /* We need to segmentize this edge */ if ( d > max_seg_length ) { int nsegs = 1 + d / max_seg_length; int i; double dx, dy, dz, dzz = 0, dmm = 0; geog2cart(ctx, &g1, &q1); geog2cart(ctx, &g2, &q2); dx = (q2.x - q1.x) / nsegs; dy = (q2.y - q1.y) / nsegs; dz = (q2.z - q1.z) / nsegs; /* The independent Z/M values on the ptarray */ if ( hasz ) dzz = (p2.z - p1.z) / nsegs; if ( hasm ) dmm = (p2.m - p1.m) / nsegs; q = q1; p = p1; for ( i = 0; i < nsegs - 1; i++ ) { /* Move one increment forwards */ q.x += dx; q.y += dy; q.z += dz; qn = q; normalize(ctx, &qn); /* Back to spherical coordinates */ cart2geog(ctx, &qn, &g); /* Back to lon/lat */ p.x = rad2deg(g.lon); p.y = rad2deg(g.lat); if ( hasz ) p.z += dzz; if ( hasm ) p.m += dmm; ptarray_append_point(ctx, pa_out, &p, RT_FALSE); } ptarray_append_point(ctx, pa_out, &p2, RT_FALSE); } /* This edge is already short enough */ else { ptarray_append_point(ctx, pa_out, &p2, (pa_in->npoints==2)?RT_TRUE:RT_FALSE); } /* Move one offset forward */ p1 = p2; g1 = g2; pa_in_offset++; } return pa_out; } /** * Create a new, densified geometry where no segment is longer than max_seg_length. * Input geometry is not altered, output geometry must be freed by caller. * @param rtg_in = input geometry * @param max_seg_length = maximum segment length in radians */ RTGEOM* rtgeom_segmentize_sphere(const RTCTX *ctx, const RTGEOM *rtg_in, double max_seg_length) { RTPOINTARRAY *pa_out; RTLINE *rtline; RTPOLY *rtpoly_in, *rtpoly_out; RTCOLLECTION *rtcol_in, *rtcol_out; int i; /* Reflect NULL */ if ( ! rtg_in ) return NULL; /* Clone empty */ if ( rtgeom_is_empty(ctx, rtg_in) ) return rtgeom_clone(ctx, rtg_in); switch (rtg_in->type) { case RTMULTIPOINTTYPE: case RTPOINTTYPE: return rtgeom_clone_deep(ctx, rtg_in); break; case RTLINETYPE: rtline = rtgeom_as_rtline(ctx, rtg_in); pa_out = ptarray_segmentize_sphere(ctx, rtline->points, max_seg_length); return rtline_as_rtgeom(ctx, rtline_construct(ctx, rtg_in->srid, NULL, pa_out)); break; case RTPOLYGONTYPE: rtpoly_in = rtgeom_as_rtpoly(ctx, rtg_in); rtpoly_out = rtpoly_construct_empty(ctx, rtg_in->srid, rtgeom_has_z(ctx, rtg_in), rtgeom_has_m(ctx, rtg_in)); for ( i = 0; i < rtpoly_in->nrings; i++ ) { pa_out = ptarray_segmentize_sphere(ctx, rtpoly_in->rings[i], max_seg_length); rtpoly_add_ring(ctx, rtpoly_out, pa_out); } return rtpoly_as_rtgeom(ctx, rtpoly_out); break; case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: rtcol_in = rtgeom_as_rtcollection(ctx, rtg_in); rtcol_out = rtcollection_construct_empty(ctx, rtg_in->type, rtg_in->srid, rtgeom_has_z(ctx, rtg_in), rtgeom_has_m(ctx, rtg_in)); for ( i = 0; i < rtcol_in->ngeoms; i++ ) { rtcollection_add_rtgeom(ctx, rtcol_out, rtgeom_segmentize_sphere(ctx, rtcol_in->geoms[i], max_seg_length)); } return rtcollection_as_rtgeom(ctx, rtcol_out); break; default: rterror(ctx, "rtgeom_segmentize_sphere: unsupported input geometry type: %d - %s", rtg_in->type, rttype_name(ctx, rtg_in->type)); break; } rterror(ctx, "rtgeom_segmentize_sphere got to the end of the function, should not happen"); return NULL; } /** * Returns the area of the ring (ring must be closed) in square radians (surface of * the sphere is 4*PI). */ double ptarray_area_sphere(const RTCTX *ctx, const RTPOINTARRAY *pa) { int i; const RTPOINT2D *p; GEOGRAPHIC_POINT a, b, c; double area = 0.0; /* Return zero on nonsensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; p = rt_getPoint2d_cp(ctx, pa, 0); geographic_point_init(ctx, p->x, p->y, &a); p = rt_getPoint2d_cp(ctx, pa, 1); geographic_point_init(ctx, p->x, p->y, &b); for ( i = 2; i < pa->npoints-1; i++ ) { p = rt_getPoint2d_cp(ctx, pa, i); geographic_point_init(ctx, p->x, p->y, &c); area += sphere_signed_area(ctx, &a, &b, &c); b = c; } return fabs(area); } static double ptarray_distance_spheroid(const RTCTX *ctx, const RTPOINTARRAY *pa1, const RTPOINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection) { GEOGRAPHIC_EDGE e1, e2; GEOGRAPHIC_POINT g1, g2; GEOGRAPHIC_POINT nearest1, nearest2; POINT3D A1, A2, B1, B2; const RTPOINT2D *p; double distance; int i, j; int use_sphere = (s->a == s->b ? 1 : 0); /* Make result really big, so that everything will be smaller than it */ distance = FLT_MAX; /* Empty point arrays? Return negative */ if ( pa1->npoints == 0 || pa2->npoints == 0 ) return -1.0; /* Handle point/point case here */ if ( pa1->npoints == 1 && pa2->npoints == 1 ) { p = rt_getPoint2d_cp(ctx, pa1, 0); geographic_point_init(ctx, p->x, p->y, &g1); p = rt_getPoint2d_cp(ctx, pa2, 0); geographic_point_init(ctx, p->x, p->y, &g2); /* Sphere special case, axes equal */ distance = s->radius * sphere_distance(ctx, &g1, &g2); if ( use_sphere ) return distance; /* Below tolerance, actual distance isn't of interest */ else if ( distance < 0.95 * tolerance ) return distance; /* Close or greater than tolerance, get the real answer to be sure */ else return spheroid_distance(ctx, &g1, &g2, s); } /* Handle point/line case here */ if ( pa1->npoints == 1 || pa2->npoints == 1 ) { /* Handle one/many case here */ int i; const RTPOINTARRAY *pa_one; const RTPOINTARRAY *pa_many; if ( pa1->npoints == 1 ) { pa_one = pa1; pa_many = pa2; } else { pa_one = pa2; pa_many = pa1; } /* Initialize our point */ p = rt_getPoint2d_cp(ctx, pa_one, 0); geographic_point_init(ctx, p->x, p->y, &g1); /* Initialize start of line */ p = rt_getPoint2d_cp(ctx, pa_many, 0); geographic_point_init(ctx, p->x, p->y, &(e1.start)); /* Iterate through the edges in our line */ for ( i = 1; i < pa_many->npoints; i++ ) { double d; p = rt_getPoint2d_cp(ctx, pa_many, i); geographic_point_init(ctx, p->x, p->y, &(e1.end)); /* Get the spherical distance between point and edge */ d = s->radius * edge_distance_to_point(ctx, &e1, &g1, &g2); /* New shortest distance! Record this distance / location */ if ( d < distance ) { distance = d; nearest2 = g2; } /* We've gotten closer than the tolerance... */ if ( d < tolerance ) { /* Working on a sphere? The answer is correct, return */ if ( use_sphere ) { return d; } /* Far enough past the tolerance that the spheroid calculation won't change things */ else if ( d < tolerance * 0.95 ) { return d; } /* On a spheroid and near the tolerance? Confirm that we are *actually* closer than tolerance */ else { d = spheroid_distance(ctx, &g1, &nearest2, s); /* Yes, closer than tolerance, return! */ if ( d < tolerance ) return d; } } e1.start = e1.end; } /* On sphere, return answer */ if ( use_sphere ) return distance; /* On spheroid, calculate final answer based on closest approach */ else return spheroid_distance(ctx, &g1, &nearest2, s); } /* Initialize start of line 1 */ p = rt_getPoint2d_cp(ctx, pa1, 0); geographic_point_init(ctx, p->x, p->y, &(e1.start)); geog2cart(ctx, &(e1.start), &A1); /* Handle line/line case */ for ( i = 1; i < pa1->npoints; i++ ) { p = rt_getPoint2d_cp(ctx, pa1, i); geographic_point_init(ctx, p->x, p->y, &(e1.end)); geog2cart(ctx, &(e1.end), &A2); /* Initialize start of line 2 */ p = rt_getPoint2d_cp(ctx, pa2, 0); geographic_point_init(ctx, p->x, p->y, &(e2.start)); geog2cart(ctx, &(e2.start), &B1); for ( j = 1; j < pa2->npoints; j++ ) { double d; p = rt_getPoint2d_cp(ctx, pa2, j); geographic_point_init(ctx, p->x, p->y, &(e2.end)); geog2cart(ctx, &(e2.end), &B2); RTDEBUGF(4, "e1.start == GPOINT(%.6g %.6g) ", e1.start.lat, e1.start.lon); RTDEBUGF(4, "e1.end == GPOINT(%.6g %.6g) ", e1.end.lat, e1.end.lon); RTDEBUGF(4, "e2.start == GPOINT(%.6g %.6g) ", e2.start.lat, e2.start.lon); RTDEBUGF(4, "e2.end == GPOINT(%.6g %.6g) ", e2.end.lat, e2.end.lon); if ( check_intersection && edge_intersects(ctx, &A1, &A2, &B1, &B2) ) { RTDEBUG(4,"edge intersection! returning 0.0"); return 0.0; } d = s->radius * edge_distance_to_edge(ctx, &e1, &e2, &g1, &g2); RTDEBUGF(4,"got edge_distance_to_edge %.8g", d); if ( d < distance ) { distance = d; nearest1 = g1; nearest2 = g2; } if ( d < tolerance ) { if ( use_sphere ) { return d; } else { d = spheroid_distance(ctx, &nearest1, &nearest2, s); if ( d < tolerance ) return d; } } /* Copy end to start to allow a new end value in next iteration */ e2.start = e2.end; B1 = B2; } /* Copy end to start to allow a new end value in next iteration */ e1.start = e1.end; A1 = A2; } RTDEBUGF(4,"finished all loops, returning %.8g", distance); if ( use_sphere ) return distance; else return spheroid_distance(ctx, &nearest1, &nearest2, s); } /** * Calculate the area of an RTGEOM. Anything except POLYGON, MULTIPOLYGON * and GEOMETRYCOLLECTION return zero immediately. Multi's recurse, polygons * calculate external ring area and subtract internal ring area. A RTGBOX is * required to calculate an outside point. */ double rtgeom_area_sphere(const RTCTX *ctx, const RTGEOM *rtgeom, const SPHEROID *spheroid) { int type; double radius2 = spheroid->radius * spheroid->radius; assert(rtgeom); /* No area in nothing */ if ( rtgeom_is_empty(ctx, rtgeom) ) return 0.0; /* Read the geometry type number */ type = rtgeom->type; /* Anything but polygons and collections returns zero */ if ( ! ( type == RTPOLYGONTYPE || type == RTMULTIPOLYGONTYPE || type == RTCOLLECTIONTYPE ) ) return 0.0; /* Actually calculate area */ if ( type == RTPOLYGONTYPE ) { RTPOLY *poly = (RTPOLY*)rtgeom; int i; double area = 0.0; /* Just in case there's no rings */ if ( poly->nrings < 1 ) return 0.0; /* First, the area of the outer ring */ area += radius2 * ptarray_area_sphere(ctx, poly->rings[0]); /* Subtract areas of inner rings */ for ( i = 1; i < poly->nrings; i++ ) { area -= radius2 * ptarray_area_sphere(ctx, poly->rings[i]); } return area; } /* Recurse into sub-geometries to get area */ if ( type == RTMULTIPOLYGONTYPE || type == RTCOLLECTIONTYPE ) { RTCOLLECTION *col = (RTCOLLECTION*)rtgeom; int i; double area = 0.0; for ( i = 0; i < col->ngeoms; i++ ) { area += rtgeom_area_sphere(ctx, col->geoms[i], spheroid); } return area; } /* Shouldn't get here. */ return 0.0; } /** * Calculate a projected point given a source point, a distance and a bearing. * @param r - location of first point. * @param spheroid - spheroid definition. * @param distance - distance, in units of the spheroid def'n. * @param azimuth - azimuth in radians. * @return s - location of projected point. * */ RTPOINT* rtgeom_project_spheroid(const RTCTX *ctx, const RTPOINT *r, const SPHEROID *spheroid, double distance, double azimuth) { GEOGRAPHIC_POINT geo_source, geo_dest; RTPOINT4D pt_dest; double x, y; RTPOINTARRAY *pa; RTPOINT *rtp; /* Check the azimuth validity, convert to radians */ if ( azimuth < -2.0 * M_PI || azimuth > 2.0 * M_PI ) { rterror(ctx, "Azimuth must be between -2PI and 2PI"); return NULL; } /* Check the distance validity */ if ( distance < 0.0 || distance > (M_PI * spheroid->radius) ) { rterror(ctx, "Distance must be between 0 and %g", M_PI * spheroid->radius); return NULL; } /* Convert to ta geodetic point */ x = rtpoint_get_x(ctx, r); y = rtpoint_get_y(ctx, r); geographic_point_init(ctx, x, y, &geo_source); /* Try the projection */ if( spheroid_project(ctx, &geo_source, spheroid, distance, azimuth, &geo_dest) == RT_FAILURE ) { RTDEBUGF(3, "Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance); rterror(ctx, "Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance); return NULL; } /* Build the output RTPOINT */ pa = ptarray_construct(ctx, 0, 0, 1); pt_dest.x = rad2deg(longitude_radians_normalize(ctx, geo_dest.lon)); pt_dest.y = rad2deg(latitude_radians_normalize(ctx, geo_dest.lat)); pt_dest.z = pt_dest.m = 0.0; ptarray_set_point4d(ctx, pa, 0, &pt_dest); rtp = rtpoint_construct(ctx, r->srid, NULL, pa); rtgeom_set_geodetic(ctx, rtpoint_as_rtgeom(ctx, rtp), RT_TRUE); return rtp; } /** * Calculate a bearing (azimuth) given a source and destination point. * @param r - location of first point. * @param s - location of second point. * @param spheroid - spheroid definition. * @return azimuth - azimuth in radians. * */ double rtgeom_azumith_spheroid(const RTCTX *ctx, const RTPOINT *r, const RTPOINT *s, const SPHEROID *spheroid) { GEOGRAPHIC_POINT g1, g2; double x1, y1, x2, y2; /* Convert r to a geodetic point */ x1 = rtpoint_get_x(ctx, r); y1 = rtpoint_get_y(ctx, r); geographic_point_init(ctx, x1, y1, &g1); /* Convert s to a geodetic point */ x2 = rtpoint_get_x(ctx, s); y2 = rtpoint_get_y(ctx, s); geographic_point_init(ctx, x2, y2, &g2); /* Same point, return NaN */ if ( FP_EQUALS(x1, x2) && FP_EQUALS(y1, y2) ) { return NAN; } /* Do the direction calculation */ return spheroid_direction(ctx, &g1, &g2, spheroid); } /** * Calculate the distance between two RTGEOMs, using the coordinates are * longitude and latitude. Return immediately when the calulated distance drops * below the tolerance (useful for dwithin calculations). * Return a negative distance for incalculable cases. */ double rtgeom_distance_spheroid(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2, const SPHEROID *spheroid, double tolerance) { uint8_t type1, type2; int check_intersection = RT_FALSE; RTGBOX gbox1, gbox2; gbox_init(ctx, &gbox1); gbox_init(ctx, &gbox2); assert(rtgeom1); assert(rtgeom2); RTDEBUGF(4, "entered function, tolerance %.8g", tolerance); /* What's the distance to an empty geometry? We don't know. Return a negative number so the caller can catch this case. */ if ( rtgeom_is_empty(ctx, rtgeom1) || rtgeom_is_empty(ctx, rtgeom2) ) { return -1.0; } type1 = rtgeom1->type; type2 = rtgeom2->type; /* Make sure we have boxes */ if ( rtgeom1->bbox ) gbox1 = *(rtgeom1->bbox); else rtgeom_calculate_gbox_geodetic(ctx, rtgeom1, &gbox1); /* Make sure we have boxes */ if ( rtgeom2->bbox ) gbox2 = *(rtgeom2->bbox); else rtgeom_calculate_gbox_geodetic(ctx, rtgeom2, &gbox2); /* If the boxes aren't disjoint, we have to check for edge intersections */ if ( gbox_overlaps(ctx, &gbox1, &gbox2) ) check_intersection = RT_TRUE; /* Point/line combinations can all be handled with simple point array iterations */ if ( ( type1 == RTPOINTTYPE || type1 == RTLINETYPE ) && ( type2 == RTPOINTTYPE || type2 == RTLINETYPE ) ) { RTPOINTARRAY *pa1, *pa2; if ( type1 == RTPOINTTYPE ) pa1 = ((RTPOINT*)rtgeom1)->point; else pa1 = ((RTLINE*)rtgeom1)->points; if ( type2 == RTPOINTTYPE ) pa2 = ((RTPOINT*)rtgeom2)->point; else pa2 = ((RTLINE*)rtgeom2)->points; return ptarray_distance_spheroid(ctx, pa1, pa2, spheroid, tolerance, check_intersection); } /* Point/Polygon cases, if point-in-poly, return zero, else return distance. */ if ( ( type1 == RTPOLYGONTYPE && type2 == RTPOINTTYPE ) || ( type2 == RTPOLYGONTYPE && type1 == RTPOINTTYPE ) ) { const RTPOINT2D *p; RTPOLY *rtpoly; RTPOINT *rtpt; double distance = FLT_MAX; int i; if ( type1 == RTPOINTTYPE ) { rtpt = (RTPOINT*)rtgeom1; rtpoly = (RTPOLY*)rtgeom2; } else { rtpt = (RTPOINT*)rtgeom2; rtpoly = (RTPOLY*)rtgeom1; } p = rt_getPoint2d_cp(ctx, rtpt->point, 0); /* Point in polygon implies zero distance */ if ( rtpoly_covers_point2d(ctx, rtpoly, p) ) { return 0.0; } /* Not inside, so what's the actual distance? */ for ( i = 0; i < rtpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(ctx, rtpoly->rings[i], rtpt->point, spheroid, tolerance, check_intersection); if ( ring_distance < distance ) distance = ring_distance; if ( distance < tolerance ) return distance; } return distance; } /* Line/polygon case, if start point-in-poly, return zero, else return distance. */ if ( ( type1 == RTPOLYGONTYPE && type2 == RTLINETYPE ) || ( type2 == RTPOLYGONTYPE && type1 == RTLINETYPE ) ) { const RTPOINT2D *p; RTPOLY *rtpoly; RTLINE *rtline; double distance = FLT_MAX; int i; if ( type1 == RTLINETYPE ) { rtline = (RTLINE*)rtgeom1; rtpoly = (RTPOLY*)rtgeom2; } else { rtline = (RTLINE*)rtgeom2; rtpoly = (RTPOLY*)rtgeom1; } p = rt_getPoint2d_cp(ctx, rtline->points, 0); RTDEBUG(4, "checking if a point of line is in polygon"); /* Point in polygon implies zero distance */ if ( rtpoly_covers_point2d(ctx, rtpoly, p) ) return 0.0; RTDEBUG(4, "checking ring distances"); /* Not contained, so what's the actual distance? */ for ( i = 0; i < rtpoly->nrings; i++ ) { double ring_distance = ptarray_distance_spheroid(ctx, rtpoly->rings[i], rtline->points, spheroid, tolerance, check_intersection); RTDEBUGF(4, "ring[%d] ring_distance = %.8g", i, ring_distance); if ( ring_distance < distance ) distance = ring_distance; if ( distance < tolerance ) return distance; } RTDEBUGF(4, "all rings checked, returning distance = %.8g", distance); return distance; } /* Polygon/polygon case, if start point-in-poly, return zero, else return distance. */ if ( ( type1 == RTPOLYGONTYPE && type2 == RTPOLYGONTYPE ) || ( type2 == RTPOLYGONTYPE && type1 == RTPOLYGONTYPE ) ) { const RTPOINT2D *p; RTPOLY *rtpoly1 = (RTPOLY*)rtgeom1; RTPOLY *rtpoly2 = (RTPOLY*)rtgeom2; double distance = FLT_MAX; int i, j; /* Point of 2 in polygon 1 implies zero distance */ p = rt_getPoint2d_cp(ctx, rtpoly1->rings[0], 0); if ( rtpoly_covers_point2d(ctx, rtpoly2, p) ) return 0.0; /* Point of 1 in polygon 2 implies zero distance */ p = rt_getPoint2d_cp(ctx, rtpoly2->rings[0], 0); if ( rtpoly_covers_point2d(ctx, rtpoly1, p) ) return 0.0; /* Not contained, so what's the actual distance? */ for ( i = 0; i < rtpoly1->nrings; i++ ) { for ( j = 0; j < rtpoly2->nrings; j++ ) { double ring_distance = ptarray_distance_spheroid(ctx, rtpoly1->rings[i], rtpoly2->rings[j], spheroid, tolerance, check_intersection); if ( ring_distance < distance ) distance = ring_distance; if ( distance < tolerance ) return distance; } } return distance; } /* Recurse into collections */ if ( rttype_is_collection(ctx, type1) ) { int i; double distance = FLT_MAX; RTCOLLECTION *col = (RTCOLLECTION*)rtgeom1; for ( i = 0; i < col->ngeoms; i++ ) { double geom_distance = rtgeom_distance_spheroid(ctx, col->geoms[i], rtgeom2, spheroid, tolerance); if ( geom_distance < distance ) distance = geom_distance; if ( distance < tolerance ) return distance; } return distance; } /* Recurse into collections */ if ( rttype_is_collection(ctx, type2) ) { int i; double distance = FLT_MAX; RTCOLLECTION *col = (RTCOLLECTION*)rtgeom2; for ( i = 0; i < col->ngeoms; i++ ) { double geom_distance = rtgeom_distance_spheroid(ctx, rtgeom1, col->geoms[i], spheroid, tolerance); if ( geom_distance < distance ) distance = geom_distance; if ( distance < tolerance ) return distance; } return distance; } rterror(ctx, "arguments include unsupported geometry type (%s, %s)", rttype_name(ctx, type1), rttype_name(ctx, type1)); return -1.0; } int rtgeom_covers_rtgeom_sphere(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2) { int type1, type2; RTGBOX gbox1, gbox2; gbox1.flags = gbox2.flags = 0; assert(rtgeom1); assert(rtgeom2); type1 = rtgeom1->type; type2 = rtgeom2->type; /* Currently a restricted implementation */ if ( ! ( (type1 == RTPOLYGONTYPE || type1 == RTMULTIPOLYGONTYPE || type1 == RTCOLLECTIONTYPE) && (type2 == RTPOINTTYPE || type2 == RTMULTIPOINTTYPE || type2 == RTCOLLECTIONTYPE) ) ) { rterror(ctx, "rtgeom_covers_rtgeom_sphere: only POLYGON covers POINT tests are currently supported"); return RT_FALSE; } /* Make sure we have boxes */ if ( rtgeom1->bbox ) gbox1 = *(rtgeom1->bbox); else rtgeom_calculate_gbox_geodetic(ctx, rtgeom1, &gbox1); /* Make sure we have boxes */ if ( rtgeom2->bbox ) gbox2 = *(rtgeom2->bbox); else rtgeom_calculate_gbox_geodetic(ctx, rtgeom2, &gbox2); /* Handle the polygon/point case */ if ( type1 == RTPOLYGONTYPE && type2 == RTPOINTTYPE ) { RTPOINT2D pt_to_test; rt_getPoint2d_p(ctx, ((RTPOINT*)rtgeom2)->point, 0, &pt_to_test); return rtpoly_covers_point2d(ctx, (RTPOLY*)rtgeom1, &pt_to_test); } /* If any of the first argument parts covers the second argument, it's true */ if ( rttype_is_collection(ctx, type1 ) ) { int i; RTCOLLECTION *col = (RTCOLLECTION*)rtgeom1; for ( i = 0; i < col->ngeoms; i++ ) { if ( rtgeom_covers_rtgeom_sphere(ctx, col->geoms[i], rtgeom2) ) { return RT_TRUE; } } return RT_FALSE; } /* Only if all of the second arguments are covered by the first argument is the condition true */ if ( rttype_is_collection(ctx, type2 ) ) { int i; RTCOLLECTION *col = (RTCOLLECTION*)rtgeom2; for ( i = 0; i < col->ngeoms; i++ ) { if ( ! rtgeom_covers_rtgeom_sphere(ctx, rtgeom1, col->geoms[i]) ) { return RT_FALSE; } } return RT_TRUE; } /* Don't get here */ rterror(ctx, "rtgeom_covers_rtgeom_sphere: reached end of function without resolution"); return RT_FALSE; } /** * Given a polygon (lon/lat decimal degrees) and point (lon/lat decimal degrees) and * a guaranteed outside point (lon/lat decimal degrees) (calculate with gbox_pt_outside(ctx)) * return RT_TRUE if point is inside or on edge of polygon. */ int rtpoly_covers_point2d(const RTCTX *ctx, const RTPOLY *poly, const RTPOINT2D *pt_to_test) { int i; int in_hole_count = 0; POINT3D p; GEOGRAPHIC_POINT gpt_to_test; RTPOINT2D pt_outside; RTGBOX gbox; gbox.flags = 0; /* Nulls and empties don't contain anything! */ if ( ! poly || rtgeom_is_empty(ctx, (RTGEOM*)poly) ) { RTDEBUG(4,"returning false, geometry is empty or null"); return RT_FALSE; } /* Make sure we have boxes */ if ( poly->bbox ) gbox = *(poly->bbox); else rtgeom_calculate_gbox_geodetic(ctx, (RTGEOM*)poly, &gbox); /* Point not in box? Done! */ geographic_point_init(ctx, pt_to_test->x, pt_to_test->y, &gpt_to_test); geog2cart(ctx, &gpt_to_test, &p); if ( ! gbox_contains_point3d(ctx, &gbox, &p) ) { RTDEBUG(4, "the point is not in the box!"); return RT_FALSE; } /* Calculate our outside point from the gbox */ gbox_pt_outside(ctx, &gbox, &pt_outside); RTDEBUGF(4, "pt_outside POINT(%.18g %.18g)", pt_outside.x, pt_outside.y); RTDEBUGF(4, "pt_to_test POINT(%.18g %.18g)", pt_to_test->x, pt_to_test->y); RTDEBUGF(4, "polygon %s", rtgeom_to_ewkt(ctx, (RTGEOM*)poly)); RTDEBUGF(4, "gbox %s", gbox_to_string(ctx, &gbox)); /* Not in outer ring? We're done! */ if ( ! ptarray_contains_point_sphere(ctx, poly->rings[0], &pt_outside, pt_to_test) ) { RTDEBUG(4,"returning false, point is outside ring"); return RT_FALSE; } RTDEBUGF(4, "testing %d rings", poly->nrings); /* But maybe point is in a hole... */ for ( i = 1; i < poly->nrings; i++ ) { RTDEBUGF(4, "ring test loop %d", i); /* Count up hole containment. Odd => outside boundary. */ if ( ptarray_contains_point_sphere(ctx, poly->rings[i], &pt_outside, pt_to_test) ) in_hole_count++; } RTDEBUGF(4, "in_hole_count == %d", in_hole_count); if ( in_hole_count % 2 ) { RTDEBUG(4,"returning false, inner ring containment count is odd"); return RT_FALSE; } RTDEBUG(4,"returning true, inner ring containment count is even"); return RT_TRUE; } /** * This function can only be used on RTGEOM that is built on top of * GSERIALIZED, otherwise alignment errors will ensue. */ int rt_getPoint2d_p_ro(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT2D **point) { uint8_t *pa_ptr = NULL; assert(pa); assert(n >= 0); assert(n < pa->npoints); pa_ptr = rt_getPoint_internal(ctx, pa, n); /* printf( "pa_ptr[0]: %g\n", *((double*)pa_ptr)); */ *point = (RTPOINT2D*)pa_ptr; return RT_SUCCESS; } int ptarray_calculate_gbox_geodetic(const RTCTX *ctx, const RTPOINTARRAY *pa, RTGBOX *gbox) { int i; int first = RT_TRUE; const RTPOINT2D *p; POINT3D A1, A2; RTGBOX edge_gbox; assert(gbox); assert(pa); gbox_init(ctx, &edge_gbox); edge_gbox.flags = gbox->flags; if ( pa->npoints == 0 ) return RT_FAILURE; if ( pa->npoints == 1 ) { p = rt_getPoint2d_cp(ctx, pa, 0); ll2cart(ctx, p, &A1); gbox->xmin = gbox->xmax = A1.x; gbox->ymin = gbox->ymax = A1.y; gbox->zmin = gbox->zmax = A1.z; return RT_SUCCESS; } p = rt_getPoint2d_cp(ctx, pa, 0); ll2cart(ctx, p, &A1); for ( i = 1; i < pa->npoints; i++ ) { p = rt_getPoint2d_cp(ctx, pa, i); ll2cart(ctx, p, &A2); edge_calculate_gbox(ctx, &A1, &A2, &edge_gbox); /* Initialize the box */ if ( first ) { gbox_duplicate(ctx, &edge_gbox, gbox); first = RT_FALSE; } /* Expand the box where necessary */ else { gbox_merge(ctx, &edge_gbox, gbox); } A1 = A2; } return RT_SUCCESS; } static int rtpoint_calculate_gbox_geodetic(const RTCTX *ctx, const RTPOINT *point, RTGBOX *gbox) { assert(point); return ptarray_calculate_gbox_geodetic(ctx, point->point, gbox); } static int rtline_calculate_gbox_geodetic(const RTCTX *ctx, const RTLINE *line, RTGBOX *gbox) { assert(line); return ptarray_calculate_gbox_geodetic(ctx, line->points, gbox); } static int rtpolygon_calculate_gbox_geodetic(const RTCTX *ctx, const RTPOLY *poly, RTGBOX *gbox) { RTGBOX ringbox; int i; int first = RT_TRUE; assert(poly); if ( poly->nrings == 0 ) return RT_FAILURE; ringbox.flags = gbox->flags; for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_calculate_gbox_geodetic(ctx, poly->rings[i], &ringbox) == RT_FAILURE ) return RT_FAILURE; if ( first ) { gbox_duplicate(ctx, &ringbox, gbox); first = RT_FALSE; } else { gbox_merge(ctx, &ringbox, gbox); } } /* If the box wraps a poly, push that axis to the absolute min/max as appropriate */ gbox_check_poles(ctx, gbox); return RT_SUCCESS; } static int rttriangle_calculate_gbox_geodetic(const RTCTX *ctx, const RTTRIANGLE *triangle, RTGBOX *gbox) { assert(triangle); return ptarray_calculate_gbox_geodetic(ctx, triangle->points, gbox); } static int rtcollection_calculate_gbox_geodetic(const RTCTX *ctx, const RTCOLLECTION *coll, RTGBOX *gbox) { RTGBOX subbox; int i; int result = RT_FAILURE; int first = RT_TRUE; assert(coll); if ( coll->ngeoms == 0 ) return RT_FAILURE; subbox.flags = gbox->flags; for ( i = 0; i < coll->ngeoms; i++ ) { if ( rtgeom_calculate_gbox_geodetic(ctx, (RTGEOM*)(coll->geoms[i]), &subbox) == RT_SUCCESS ) { /* Keep a copy of the sub-bounding box for later */ if ( coll->geoms[i]->bbox ) rtfree(ctx, coll->geoms[i]->bbox); coll->geoms[i]->bbox = gbox_copy(ctx, &subbox); if ( first ) { gbox_duplicate(ctx, &subbox, gbox); first = RT_FALSE; } else { gbox_merge(ctx, &subbox, gbox); } result = RT_SUCCESS; } } return result; } int rtgeom_calculate_gbox_geodetic(const RTCTX *ctx, const RTGEOM *geom, RTGBOX *gbox) { int result = RT_FAILURE; RTDEBUGF(4, "got type %d", geom->type); /* Add a geodetic flag to the incoming gbox */ gbox->flags = gflags(ctx, RTFLAGS_GET_Z(geom->flags),RTFLAGS_GET_M(geom->flags),1); switch (geom->type) { case RTPOINTTYPE: result = rtpoint_calculate_gbox_geodetic(ctx, (RTPOINT*)geom, gbox); break; case RTLINETYPE: result = rtline_calculate_gbox_geodetic(ctx, (RTLINE *)geom, gbox); break; case RTPOLYGONTYPE: result = rtpolygon_calculate_gbox_geodetic(ctx, (RTPOLY *)geom, gbox); break; case RTTRIANGLETYPE: result = rttriangle_calculate_gbox_geodetic(ctx, (RTTRIANGLE *)geom, gbox); break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: result = rtcollection_calculate_gbox_geodetic(ctx, (RTCOLLECTION *)geom, gbox); break; default: rterror(ctx, "rtgeom_calculate_gbox_geodetic: unsupported input geometry type: %d - %s", geom->type, rttype_name(ctx, geom->type)); break; } return result; } static int ptarray_check_geodetic(const RTCTX *ctx, const RTPOINTARRAY *pa) { int t; RTPOINT2D pt; assert(pa); for (t=0; tnpoints; t++) { rt_getPoint2d_p(ctx, pa, t, &pt); /* printf( "%d (%g, %g)\n", t, pt.x, pt.y); */ if ( pt.x < -180.0 || pt.y < -90.0 || pt.x > 180.0 || pt.y > 90.0 ) return RT_FALSE; } return RT_TRUE; } static int rtpoint_check_geodetic(const RTCTX *ctx, const RTPOINT *point) { assert(point); return ptarray_check_geodetic(ctx, point->point); } static int rtline_check_geodetic(const RTCTX *ctx, const RTLINE *line) { assert(line); return ptarray_check_geodetic(ctx, line->points); } static int rtpoly_check_geodetic(const RTCTX *ctx, const RTPOLY *poly) { int i = 0; assert(poly); for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_check_geodetic(ctx, poly->rings[i]) == RT_FALSE ) return RT_FALSE; } return RT_TRUE; } static int rttriangle_check_geodetic(const RTCTX *ctx, const RTTRIANGLE *triangle) { assert(triangle); return ptarray_check_geodetic(ctx, triangle->points); } static int rtcollection_check_geodetic(const RTCTX *ctx, const RTCOLLECTION *col) { int i = 0; assert(col); for ( i = 0; i < col->ngeoms; i++ ) { if ( rtgeom_check_geodetic(ctx, col->geoms[i]) == RT_FALSE ) return RT_FALSE; } return RT_TRUE; } int rtgeom_check_geodetic(const RTCTX *ctx, const RTGEOM *geom) { if ( rtgeom_is_empty(ctx, geom) ) return RT_TRUE; switch (geom->type) { case RTPOINTTYPE: return rtpoint_check_geodetic(ctx, (RTPOINT *)geom); case RTLINETYPE: return rtline_check_geodetic(ctx, (RTLINE *)geom); case RTPOLYGONTYPE: return rtpoly_check_geodetic(ctx, (RTPOLY *)geom); case RTTRIANGLETYPE: return rttriangle_check_geodetic(ctx, (RTTRIANGLE *)geom); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return rtcollection_check_geodetic(ctx, (RTCOLLECTION *)geom); default: rterror(ctx, "rtgeom_check_geodetic: unsupported input geometry type: %d - %s", geom->type, rttype_name(ctx, geom->type)); } return RT_FALSE; } static int ptarray_force_geodetic(const RTCTX *ctx, RTPOINTARRAY *pa) { int t; int changed = RT_FALSE; RTPOINT4D pt; assert(pa); for ( t=0; t < pa->npoints; t++ ) { rt_getPoint4d_p(ctx, pa, t, &pt); if ( pt.x < -180.0 || pt.x > 180.0 || pt.y < -90.0 || pt.y > 90.0 ) { pt.x = longitude_degrees_normalize(ctx, pt.x); pt.y = latitude_degrees_normalize(ctx, pt.y); ptarray_set_point4d(ctx, pa, t, &pt); changed = RT_TRUE; } } return changed; } static int rtpoint_force_geodetic(const RTCTX *ctx, RTPOINT *point) { assert(point); return ptarray_force_geodetic(ctx, point->point); } static int rtline_force_geodetic(const RTCTX *ctx, RTLINE *line) { assert(line); return ptarray_force_geodetic(ctx, line->points); } static int rtpoly_force_geodetic(const RTCTX *ctx, RTPOLY *poly) { int i = 0; int changed = RT_FALSE; assert(poly); for ( i = 0; i < poly->nrings; i++ ) { if ( ptarray_force_geodetic(ctx, poly->rings[i]) == RT_TRUE ) changed = RT_TRUE; } return changed; } static int rtcollection_force_geodetic(const RTCTX *ctx, RTCOLLECTION *col) { int i = 0; int changed = RT_FALSE; assert(col); for ( i = 0; i < col->ngeoms; i++ ) { if ( rtgeom_force_geodetic(ctx, col->geoms[i]) == RT_TRUE ) changed = RT_TRUE; } return changed; } int rtgeom_force_geodetic(const RTCTX *ctx, RTGEOM *geom) { switch ( rtgeom_get_type(ctx, geom) ) { case RTPOINTTYPE: return rtpoint_force_geodetic(ctx, (RTPOINT *)geom); case RTLINETYPE: return rtline_force_geodetic(ctx, (RTLINE *)geom); case RTPOLYGONTYPE: return rtpoly_force_geodetic(ctx, (RTPOLY *)geom); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return rtcollection_force_geodetic(ctx, (RTCOLLECTION *)geom); default: rterror(ctx, "unsupported input geometry type: %d", rtgeom_get_type(ctx, geom)); } return RT_FALSE; } double ptarray_length_spheroid(const RTCTX *ctx, const RTPOINTARRAY *pa, const SPHEROID *s) { GEOGRAPHIC_POINT a, b; double za = 0.0, zb = 0.0; RTPOINT4D p; int i; int hasz = RT_FALSE; double length = 0.0; double seglength = 0.0; /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 2 ) return 0.0; /* See if we have a third dimension */ hasz = RTFLAGS_GET_Z(pa->flags); /* Initialize first point */ rt_getPoint4d_p(ctx, pa, 0, &p); geographic_point_init(ctx, p.x, p.y, &a); if ( hasz ) za = p.z; /* Loop and sum the length for each segment */ for ( i = 1; i < pa->npoints; i++ ) { seglength = 0.0; rt_getPoint4d_p(ctx, pa, i, &p); geographic_point_init(ctx, p.x, p.y, &b); if ( hasz ) zb = p.z; /* Special sphere case */ if ( s->a == s->b ) seglength = s->radius * sphere_distance(ctx, &a, &b); /* Spheroid case */ else seglength = spheroid_distance(ctx, &a, &b, s); /* Add in the vertical displacement if we're in 3D */ if ( hasz ) seglength = sqrt( (zb-za)*(zb-za) + seglength*seglength ); /* Add this segment length to the total */ length += seglength; /* B gets incremented in the next loop, so we save the value here */ a = b; za = zb; } return length; } double rtgeom_length_spheroid(const RTCTX *ctx, const RTGEOM *geom, const SPHEROID *s) { int type; int i = 0; double length = 0.0; assert(geom); /* No area in nothing */ if ( rtgeom_is_empty(ctx, geom) ) return 0.0; type = geom->type; if ( type == RTPOINTTYPE || type == RTMULTIPOINTTYPE ) return 0.0; if ( type == RTLINETYPE ) return ptarray_length_spheroid(ctx, ((RTLINE*)geom)->points, s); if ( type == RTPOLYGONTYPE ) { RTPOLY *poly = (RTPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { length += ptarray_length_spheroid(ctx, poly->rings[i], s); } return length; } if ( type == RTTRIANGLETYPE ) return ptarray_length_spheroid(ctx, ((RTTRIANGLE*)geom)->points, s); if ( rttype_is_collection(ctx, type ) ) { RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) { length += rtgeom_length_spheroid(ctx, col->geoms[i], s); } return length; } rterror(ctx, "unsupported type passed to rtgeom_length_sphere"); return 0.0; } /** * When features are snapped or sometimes they are just this way, they are very close to * the geodetic bounds but slightly over. This routine nudges those points, and only * those points, back over to the bounds. * http://trac.osgeo.org/postgis/ticket/1292 */ static int ptarray_nudge_geodetic(const RTCTX *ctx, RTPOINTARRAY *pa) { int i; RTPOINT4D p; int altered = RT_FALSE; int rv = RT_FALSE; static double tolerance = 1e-10; if ( ! pa ) rterror(ctx, "ptarray_nudge_geodetic called with null input"); for(i = 0; i < pa->npoints; i++ ) { rt_getPoint4d_p(ctx, pa, i, &p); if ( p.x < -180.0 && (-180.0 - p.x < tolerance) ) { p.x = -180.0; altered = RT_TRUE; } if ( p.x > 180.0 && (p.x - 180.0 < tolerance) ) { p.x = 180.0; altered = RT_TRUE; } if ( p.y < -90.0 && (-90.0 - p.y < tolerance) ) { p.y = -90.0; altered = RT_TRUE; } if ( p.y > 90.0 && (p.y - 90.0 < tolerance) ) { p.y = 90.0; altered = RT_TRUE; } if ( altered == RT_TRUE ) { ptarray_set_point4d(ctx, pa, i, &p); altered = RT_FALSE; rv = RT_TRUE; } } return rv; } /** * When features are snapped or sometimes they are just this way, they are very close to * the geodetic bounds but slightly over. This routine nudges those points, and only * those points, back over to the bounds. * http://trac.osgeo.org/postgis/ticket/1292 */ int rtgeom_nudge_geodetic(const RTCTX *ctx, RTGEOM *geom) { int type; int i = 0; int rv = RT_FALSE; assert(geom); /* No points in nothing */ if ( rtgeom_is_empty(ctx, geom) ) return RT_FALSE; type = geom->type; if ( type == RTPOINTTYPE ) return ptarray_nudge_geodetic(ctx, ((RTPOINT*)geom)->point); if ( type == RTLINETYPE ) return ptarray_nudge_geodetic(ctx, ((RTLINE*)geom)->points); if ( type == RTPOLYGONTYPE ) { RTPOLY *poly = (RTPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { int n = ptarray_nudge_geodetic(ctx, poly->rings[i]); rv = (rv == RT_TRUE ? rv : n); } return rv; } if ( type == RTTRIANGLETYPE ) return ptarray_nudge_geodetic(ctx, ((RTTRIANGLE*)geom)->points); if ( rttype_is_collection(ctx, type ) ) { RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) { int n = rtgeom_nudge_geodetic(ctx, col->geoms[i]); rv = (rv == RT_TRUE ? rv : n); } return rv; } rterror(ctx, "unsupported type (%s) passed to rtgeom_nudge_geodetic", rttype_name(ctx, type)); return rv; } /** * Utility function for checking if P is within the cone defined by A1/A2. */ static int point_in_cone(const RTCTX *ctx, const POINT3D *A1, const POINT3D *A2, const POINT3D *P) { POINT3D AC; /* Center point of A1/A2 */ double min_similarity, similarity; /* The normalized sum bisects the angle between start and end. */ vector_sum(ctx, A1, A2, &AC); normalize(ctx, &AC); /* The projection of start onto the center defines the minimum similarity */ min_similarity = dot_product(ctx, A1, &AC); /* The projection of candidate p onto the center */ similarity = dot_product(ctx, P, &AC); /* If the point is more similar than the end, the point is in the cone */ if ( similarity > min_similarity || fabs(similarity - min_similarity) < 2e-16 ) { return RT_TRUE; } return RT_FALSE; } /** * Utility function for ptarray_contains_point_sphere(ctx) */ static int point3d_equals(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2) { return FP_EQUALS(p1->x, p2->x) && FP_EQUALS(p1->y, p2->y) && FP_EQUALS(p1->z, p2->z); } /** * Utility function for edge_intersects(ctx), signum with a tolerance * in determining if the value is zero. */ static int dot_product_side(const RTCTX *ctx, const POINT3D *p, const POINT3D *q) { double dp = dot_product(ctx, p, q); if ( FP_IS_ZERO(dp) ) return 0; return dp < 0.0 ? -1 : 1; } /** * Returns non-zero if edges A and B interact. The type of interaction is given in the * return value with the bitmask elements defined above. */ int edge_intersects(const RTCTX *ctx, const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2) { POINT3D AN, BN, VN; /* Normals to plane A and plane B */ double ab_dot; int a1_side, a2_side, b1_side, b2_side; int rv = PIR_NO_INTERACT; /* Normals to the A-plane and B-plane */ unit_normal(ctx, A1, A2, &AN); unit_normal(ctx, B1, B2, &BN); /* Are A-plane and B-plane basically the same? */ ab_dot = dot_product(ctx, &AN, &BN); if ( FP_EQUALS(fabs(ab_dot), 1.0) ) { /* Co-linear case */ if ( point_in_cone(ctx, A1, A2, B1) || point_in_cone(ctx, A1, A2, B2) || point_in_cone(ctx, B1, B2, A1) || point_in_cone(ctx, B1, B2, A2) ) { rv |= PIR_INTERSECTS; rv |= PIR_COLINEAR; } return rv; } /* What side of plane-A and plane-B do the end points */ /* of A and B fall? */ a1_side = dot_product_side(ctx, &BN, A1); a2_side = dot_product_side(ctx, &BN, A2); b1_side = dot_product_side(ctx, &AN, B1); b2_side = dot_product_side(ctx, &AN, B2); /* Both ends of A on the same side of plane B. */ if ( a1_side == a2_side && a1_side != 0 ) { /* No intersection. */ return PIR_NO_INTERACT; } /* Both ends of B on the same side of plane A. */ if ( b1_side == b2_side && b1_side != 0 ) { /* No intersection. */ return PIR_NO_INTERACT; } /* A straddles B and B straddles A, so... */ if ( a1_side != a2_side && (a1_side + a2_side) == 0 && b1_side != b2_side && (b1_side + b2_side) == 0 ) { /* Have to check if intersection point is inside both arcs */ unit_normal(ctx, &AN, &BN, &VN); if ( point_in_cone(ctx, A1, A2, &VN) && point_in_cone(ctx, B1, B2, &VN) ) { return PIR_INTERSECTS; } /* Have to check if intersection point is inside both arcs */ vector_scale(ctx, &VN, -1); if ( point_in_cone(ctx, A1, A2, &VN) && point_in_cone(ctx, B1, B2, &VN) ) { return PIR_INTERSECTS; } return PIR_NO_INTERACT; } /* The rest are all intersects variants... */ rv |= PIR_INTERSECTS; /* A touches B */ if ( a1_side == 0 ) { /* Touches at A1, A2 is on what side? */ rv |= (a2_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT); } else if ( a2_side == 0 ) { /* Touches at A2, A1 is on what side? */ rv |= (a1_side < 0 ? PIR_A_TOUCH_RIGHT : PIR_A_TOUCH_LEFT); } /* B touches A */ if ( b1_side == 0 ) { /* Touches at B1, B2 is on what side? */ rv |= (b2_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT); } else if ( b2_side == 0 ) { /* Touches at B2, B1 is on what side? */ rv |= (b1_side < 0 ? PIR_B_TOUCH_RIGHT : PIR_B_TOUCH_LEFT); } return rv; } /** * This routine returns RT_TRUE if the stabline joining the pt_outside and pt_to_test * crosses the ring an odd number of times, or if the pt_to_test is on the ring boundary itself, * returning RT_FALSE otherwise. * The pt_outside *must* be guaranteed to be outside the ring (use the geography_pt_outside() function * to derive one in postgis, or the gbox_pt_outside(ctx) function if you don't mind burning CPU cycles * building a gbox first). */ int ptarray_contains_point_sphere(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt_outside, const RTPOINT2D *pt_to_test) { POINT3D S1, S2; /* Stab line end points */ POINT3D E1, E2; /* Edge end points (3-space) */ RTPOINT2D p; /* Edge end points (lon/lat) */ int count = 0, i, inter; /* Null input, not enough points for a ring? You ain't closed! */ if ( ! pa || pa->npoints < 4 ) return RT_FALSE; /* Set up our stab line */ ll2cart(ctx, pt_to_test, &S1); ll2cart(ctx, pt_outside, &S2); /* Initialize first point */ rt_getPoint2d_p(ctx, pa, 0, &p); ll2cart(ctx, &p, &E1); /* Walk every edge and see if the stab line hits it */ for ( i = 1; i < pa->npoints; i++ ) { RTDEBUGF(4, "testing edge (%d)", i); RTDEBUGF(4, " start point == POINT(%.12g %.12g)", p.x, p.y); /* Read next point. */ rt_getPoint2d_p(ctx, pa, i, &p); ll2cart(ctx, &p, &E2); /* Skip over too-short edges. */ if ( point3d_equals(ctx, &E1, &E2) ) { continue; } /* Our test point is on an edge end! Point is "in ring" by our definition */ if ( point3d_equals(ctx, &S1, &E1) ) { return RT_TRUE; } /* Calculate relationship between stab line and edge */ inter = edge_intersects(ctx, &S1, &S2, &E1, &E2); /* We have some kind of interaction... */ if ( inter & PIR_INTERSECTS ) { /* If the stabline is touching the edge, that implies the test point */ /* is on the edge, so we're done, the point is in (on) the ring. */ if ( (inter & PIR_A_TOUCH_RIGHT) || (inter & PIR_A_TOUCH_LEFT) ) { return RT_TRUE; } /* It's a touching interaction, disregard all the left-side ones. */ /* It's a co-linear intersection, ignore those. */ if ( inter & PIR_B_TOUCH_RIGHT || inter & PIR_COLINEAR ) { /* Do nothing, to avoid double counts. */ RTDEBUGF(4," edge (%d) crossed, disregarding to avoid double count", i, count); } else { /* Increment crossingn count. */ count++; RTDEBUGF(4," edge (%d) crossed, count == %d", i, count); } } else { RTDEBUGF(4," edge (%d) did not cross", i); } /* Increment to next edge */ E1 = E2; } RTDEBUGF(4,"final count == %d", count); /* An odd number of crossings implies containment! */ if ( count % 2 ) { return RT_TRUE; } return RT_FALSE; } src/rtgeodetic.h000066400000000000000000000152531271715413500141630ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009 Paul Ramsey * **********************************************************************/ #ifndef _RTGEODETIC_H #define _RTGEODETIC_H 1 /* For NAN */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #ifndef NAN #define NAN 0.0/0.0 #endif extern int gbox_geocentric_slow; #define POW2(x) ((x)*(x)) /** * Point in spherical coordinates on the world. Units of radians. */ typedef struct { double lon; double lat; } GEOGRAPHIC_POINT; /** * Two-point great circle segment from a to b. */ typedef struct { GEOGRAPHIC_POINT start; GEOGRAPHIC_POINT end; } GEOGRAPHIC_EDGE; /** * Holder for sorting points in distance algorithm */ typedef struct { double measure; uint32_t index; } DISTANCE_ORDER; /** * Conversion functions */ #define deg2rad(d) (M_PI * (d) / 180.0) #define rad2deg(r) (180.0 * (r) / M_PI) /** * Ape a java function */ #define signum(a) ((a) < 0 ? -1 : ((a) > 0 ? 1 : (a))) /** * Bitmask elements for edge_intersects(const RTCTX *ctx) return value. */ #define PIR_NO_INTERACT 0x00 #define PIR_INTERSECTS 0x01 #define PIR_COLINEAR 0x02 #define PIR_A_TOUCH_RIGHT 0x04 #define PIR_A_TOUCH_LEFT 0x08 #define PIR_B_TOUCH_RIGHT 0x10 #define PIR_B_TOUCH_LEFT 0x20 /* * Geodetic calculations */ void geog2cart(const RTCTX *ctx, const GEOGRAPHIC_POINT *g, POINT3D *p); void cart2geog(const RTCTX *ctx, const POINT3D *p, GEOGRAPHIC_POINT *g); void robust_cross_product(const RTCTX *ctx, const GEOGRAPHIC_POINT *p, const GEOGRAPHIC_POINT *q, POINT3D *a); void x_to_z(const RTCTX *ctx, POINT3D *p); void y_to_z(const RTCTX *ctx, POINT3D *p); int edge_point_on_plane(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_point_in_cone(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_contains_coplanar_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); int edge_contains_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *p); double z_to_latitude(const RTCTX *ctx, double z, int top); int clairaut_cartesian(const RTCTX *ctx, const POINT3D *start, const POINT3D *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom); int clairaut_geographic(const RTCTX *ctx, const GEOGRAPHIC_POINT *start, const GEOGRAPHIC_POINT *end, GEOGRAPHIC_POINT *g_top, GEOGRAPHIC_POINT *g_bottom); double sphere_distance(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e); double sphere_distance_cartesian(const RTCTX *ctx, const POINT3D *s, const POINT3D *e); int sphere_project(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, double distance, double azimuth, GEOGRAPHIC_POINT *n); int edge_calculate_gbox_slow(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, RTGBOX *gbox); int edge_calculate_gbox(const RTCTX *ctx, const POINT3D *A1, const POINT3D *A2, RTGBOX *gbox); int edge_intersection(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *g); int edge_intersects(const RTCTX *ctx, const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2); double edge_distance_to_point(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest); double edge_distance_to_edge(const RTCTX *ctx, const GEOGRAPHIC_EDGE *e1, const GEOGRAPHIC_EDGE *e2, GEOGRAPHIC_POINT *closest1, GEOGRAPHIC_POINT *closest2); void geographic_point_init(const RTCTX *ctx, double lon, double lat, GEOGRAPHIC_POINT *g); int ptarray_contains_point_sphere(const RTCTX *ctx, const RTPOINTARRAY *pa, const RTPOINT2D *pt_outside, const RTPOINT2D *pt_to_test); int rtpoly_covers_point2d(const RTCTX *ctx, const RTPOLY *poly, const RTPOINT2D *pt_to_test); void rtpoly_pt_outside(const RTCTX *ctx, const RTPOLY *poly, RTPOINT2D *pt_outside); int ptarray_point_in_ring(const RTPOINTARRAY *pa, const RTPOINT2D *pt_outside, const RTPOINT2D *pt_to_test); double ptarray_area_sphere(const RTCTX *ctx, const RTPOINTARRAY *pa); double latitude_degrees_normalize(const RTCTX *ctx, double lat); double longitude_degrees_normalize(const RTCTX *ctx, double lon); double ptarray_length_spheroid(const RTCTX *ctx, const RTPOINTARRAY *pa, const SPHEROID *s); int geographic_point_equals(const RTCTX *ctx, const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2); int crosses_dateline(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e); void point_shift(const RTCTX *ctx, GEOGRAPHIC_POINT *p, double shift); double longitude_radians_normalize(const RTCTX *ctx, double lon); double latitude_radians_normalize(const RTCTX *ctx, double lat); void vector_sum(const RTCTX *ctx, const POINT3D *a, const POINT3D *b, POINT3D *n); double vector_angle(const RTCTX *ctx, const POINT3D* v1, const POINT3D* v2); void vector_rotate(const RTCTX *ctx, const POINT3D* v1, const POINT3D* v2, double angle, POINT3D* n); void normalize(const RTCTX *ctx, POINT3D *p); void unit_normal(const RTCTX *ctx, const POINT3D *P1, const POINT3D *P2, POINT3D *normal); double sphere_direction(const RTCTX *ctx, const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e, double d); void ll2cart(const RTCTX *ctx, const RTPOINT2D *g, POINT3D *p); /* ** Prototypes for spheroid functions. */ double spheroid_distance(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid); double spheroid_direction(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid); int spheroid_project(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g); #endif /* _RTGEODETIC_H */ /** * Notes for rewrite * * Define separate POINT types for 2-d-points-in-radiands and 3-d-points-in-geocentric * Maintain consistent units (radians?) throughout all calculations * Put an index pointer onto RTGEOM itself, and cache the indexed RTGEOM instead of a bare tree * only primitive objects should get a tree */ src/rtgeom.c000066400000000000000000001432221271715413500133200ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /** Force Right-hand-rule on RTGEOM polygons **/ void rtgeom_force_clockwise(const RTCTX *ctx, RTGEOM *rtgeom) { RTCOLLECTION *coll; int i; switch (rtgeom->type) { case RTPOLYGONTYPE: rtpoly_force_clockwise(ctx, (RTPOLY *)rtgeom); return; case RTTRIANGLETYPE: rttriangle_force_clockwise(ctx, (RTTRIANGLE *)rtgeom); return; /* Not handle POLYHEDRALSURFACE and TIN as they are supposed to be well oriented */ case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: coll = (RTCOLLECTION *)rtgeom; for (i=0; ingeoms; i++) rtgeom_force_clockwise(ctx, coll->geoms[i]); return; } } /** Reverse vertex order of RTGEOM **/ void rtgeom_reverse(const RTCTX *ctx, RTGEOM *rtgeom) { int i; RTCOLLECTION *col; switch (rtgeom->type) { case RTLINETYPE: rtline_reverse(ctx, (RTLINE *)rtgeom); return; case RTPOLYGONTYPE: rtpoly_reverse(ctx, (RTPOLY *)rtgeom); return; case RTTRIANGLETYPE: rttriangle_reverse(ctx, (RTTRIANGLE *)rtgeom); return; case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: col = (RTCOLLECTION *)rtgeom; for (i=0; ingeoms; i++) rtgeom_reverse(ctx, col->geoms[i]); return; } } RTPOINT * rtgeom_as_rtpoint(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTPOINTTYPE ) return (RTPOINT *)rtgeom; else return NULL; } RTLINE * rtgeom_as_rtline(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTLINETYPE ) return (RTLINE *)rtgeom; else return NULL; } RTCIRCSTRING * rtgeom_as_rtcircstring(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTCIRCSTRINGTYPE ) return (RTCIRCSTRING *)rtgeom; else return NULL; } RTCOMPOUND * rtgeom_as_rtcompound(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTCOMPOUNDTYPE ) return (RTCOMPOUND *)rtgeom; else return NULL; } RTCURVEPOLY * rtgeom_as_rtcurvepoly(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTCURVEPOLYTYPE ) return (RTCURVEPOLY *)rtgeom; else return NULL; } RTPOLY * rtgeom_as_rtpoly(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTPOLYGONTYPE ) return (RTPOLY *)rtgeom; else return NULL; } RTTRIANGLE * rtgeom_as_rttriangle(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTTRIANGLETYPE ) return (RTTRIANGLE *)rtgeom; else return NULL; } RTCOLLECTION * rtgeom_as_rtcollection(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom_is_collection(ctx, rtgeom) ) return (RTCOLLECTION*)rtgeom; else return NULL; } RTMPOINT * rtgeom_as_rtmpoint(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTMULTIPOINTTYPE ) return (RTMPOINT *)rtgeom; else return NULL; } RTMLINE * rtgeom_as_rtmline(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTMULTILINETYPE ) return (RTMLINE *)rtgeom; else return NULL; } RTMPOLY * rtgeom_as_rtmpoly(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom == NULL ) return NULL; if ( rtgeom->type == RTMULTIPOLYGONTYPE ) return (RTMPOLY *)rtgeom; else return NULL; } RTPSURFACE * rtgeom_as_rtpsurface(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom->type == RTPOLYHEDRALSURFACETYPE ) return (RTPSURFACE *)rtgeom; else return NULL; } RTTIN * rtgeom_as_rttin(const RTCTX *ctx, const RTGEOM *rtgeom) { if ( rtgeom->type == RTTINTYPE ) return (RTTIN *)rtgeom; else return NULL; } RTGEOM * rttin_as_rtgeom(const RTCTX *ctx, const RTTIN *obj) { return (RTGEOM *)obj; } RTGEOM * rtpsurface_as_rtgeom(const RTCTX *ctx, const RTPSURFACE *obj) { return (RTGEOM *)obj; } RTGEOM * rtmpoly_as_rtgeom(const RTCTX *ctx, const RTMPOLY *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtmline_as_rtgeom(const RTCTX *ctx, const RTMLINE *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtmpoint_as_rtgeom(const RTCTX *ctx, const RTMPOINT *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtcollection_as_rtgeom(const RTCTX *ctx, const RTCOLLECTION *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtcircstring_as_rtgeom(const RTCTX *ctx, const RTCIRCSTRING *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtcurvepoly_as_rtgeom(const RTCTX *ctx, const RTCURVEPOLY *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtcompound_as_rtgeom(const RTCTX *ctx, const RTCOMPOUND *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtpoly_as_rtgeom(const RTCTX *ctx, const RTPOLY *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rttriangle_as_rtgeom(const RTCTX *ctx, const RTTRIANGLE *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtline_as_rtgeom(const RTCTX *ctx, const RTLINE *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } RTGEOM * rtpoint_as_rtgeom(const RTCTX *ctx, const RTPOINT *obj) { if ( obj == NULL ) return NULL; return (RTGEOM *)obj; } /** ** Look-up for the correct MULTI* type promotion for singleton types. */ uint8_t RTMULTITYPE[RTNUMTYPES] = { 0, RTMULTIPOINTTYPE, /* 1 */ RTMULTILINETYPE, /* 2 */ RTMULTIPOLYGONTYPE, /* 3 */ 0,0,0,0, RTMULTICURVETYPE, /* 8 */ RTMULTICURVETYPE, /* 9 */ RTMULTISURFACETYPE, /* 10 */ RTPOLYHEDRALSURFACETYPE, /* 11 */ 0, 0, RTTINTYPE, /* 14 */ 0 }; /** * Create a new RTGEOM of the appropriate MULTI* type. */ RTGEOM * rtgeom_as_multi(const RTCTX *ctx, const RTGEOM *rtgeom) { RTGEOM **ogeoms; RTGEOM *ogeom = NULL; RTGBOX *box = NULL; int type; type = rtgeom->type; if ( ! RTMULTITYPE[type] ) return rtgeom_clone(ctx, rtgeom); if( rtgeom_is_empty(ctx, rtgeom) ) { ogeom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTITYPE[type], rtgeom->srid, RTFLAGS_GET_Z(rtgeom->flags), RTFLAGS_GET_M(rtgeom->flags) ); } else { ogeoms = rtalloc(ctx, sizeof(RTGEOM*)); ogeoms[0] = rtgeom_clone(ctx, rtgeom); /* Sub-geometries are not allowed to have bboxes or SRIDs, move the bbox to the collection */ box = ogeoms[0]->bbox; ogeoms[0]->bbox = NULL; ogeoms[0]->srid = SRID_UNKNOWN; ogeom = (RTGEOM *)rtcollection_construct(ctx, RTMULTITYPE[type], rtgeom->srid, box, 1, ogeoms); } return ogeom; } /** * Create a new RTGEOM of the appropriate CURVE* type. */ RTGEOM * rtgeom_as_curve(const RTCTX *ctx, const RTGEOM *rtgeom) { RTGEOM *ogeom; int type = rtgeom->type; /* int hasz = RTFLAGS_GET_Z(rtgeom->flags); int hasm = RTFLAGS_GET_M(rtgeom->flags); int srid = rtgeom->srid; */ switch(type) { case RTLINETYPE: /* turn to COMPOUNDCURVE */ ogeom = (RTGEOM*)rtcompound_construct_from_rtline(ctx, (RTLINE*)rtgeom); break; case RTPOLYGONTYPE: ogeom = (RTGEOM*)rtcurvepoly_construct_from_rtpoly(ctx, rtgeom_as_rtpoly(ctx, rtgeom)); break; case RTMULTILINETYPE: /* turn to MULTICURVE */ ogeom = rtgeom_clone(ctx, rtgeom); ogeom->type = RTMULTICURVETYPE; break; case RTMULTIPOLYGONTYPE: /* turn to MULTISURFACE */ ogeom = rtgeom_clone(ctx, rtgeom); ogeom->type = RTMULTISURFACETYPE; break; case RTCOLLECTIONTYPE: default: ogeom = rtgeom_clone(ctx, rtgeom); break; } /* TODO: copy bbox from input geom ? */ return ogeom; } /** * Free the containing RTGEOM and the associated BOX. Leave the underlying * geoms/points/point objects intact. Useful for functions that are stripping * out subcomponents of complex objects, or building up new temporary objects * on top of subcomponents. */ void rtgeom_release(const RTCTX *ctx, RTGEOM *rtgeom) { if ( ! rtgeom ) rterror(ctx, "rtgeom_release: someone called on 0x0"); RTDEBUGF(3, "releasing type %s", rttype_name(ctx, rtgeom->type)); /* Drop bounding box (artays a copy) */ if ( rtgeom->bbox ) { RTDEBUGF(3, "rtgeom_release: releasing bbox. %p", rtgeom->bbox); rtfree(ctx, rtgeom->bbox); } rtfree(ctx, rtgeom); } /* @brief Clone RTGEOM object. Serialized point lists are not copied. * * @see ptarray_clone */ RTGEOM * rtgeom_clone(const RTCTX *ctx, const RTGEOM *rtgeom) { RTDEBUGF(2, "rtgeom_clone called with %p, %s", rtgeom, rttype_name(ctx, rtgeom->type)); switch (rtgeom->type) { case RTPOINTTYPE: return (RTGEOM *)rtpoint_clone(ctx, (RTPOINT *)rtgeom); case RTLINETYPE: return (RTGEOM *)rtline_clone(ctx, (RTLINE *)rtgeom); case RTCIRCSTRINGTYPE: return (RTGEOM *)rtcircstring_clone(ctx, (RTCIRCSTRING *)rtgeom); case RTPOLYGONTYPE: return (RTGEOM *)rtpoly_clone(ctx, (RTPOLY *)rtgeom); case RTTRIANGLETYPE: return (RTGEOM *)rttriangle_clone(ctx, (RTTRIANGLE *)rtgeom); case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return (RTGEOM *)rtcollection_clone(ctx, (RTCOLLECTION *)rtgeom); default: rterror(ctx, "rtgeom_clone: Unknown geometry type: %s", rttype_name(ctx, rtgeom->type)); return NULL; } } /** * Deep-clone an #RTGEOM object. #RTPOINTARRAY are copied. */ RTGEOM * rtgeom_clone_deep(const RTCTX *ctx, const RTGEOM *rtgeom) { RTDEBUGF(2, "rtgeom_clone called with %p, %s", rtgeom, rttype_name(ctx, rtgeom->type)); switch (rtgeom->type) { case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTTRIANGLETYPE: return (RTGEOM *)rtline_clone_deep(ctx, (RTLINE *)rtgeom); case RTPOLYGONTYPE: return (RTGEOM *)rtpoly_clone_deep(ctx, (RTPOLY *)rtgeom); case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return (RTGEOM *)rtcollection_clone_deep(ctx, (RTCOLLECTION *)rtgeom); default: rterror(ctx, "rtgeom_clone_deep: Unknown geometry type: %s", rttype_name(ctx, rtgeom->type)); return NULL; } } /** * Return an alloced string */ char* rtgeom_to_ewkt(const RTCTX *ctx, const RTGEOM *rtgeom) { char* wkt = NULL; size_t wkt_size = 0; wkt = rtgeom_to_wkt(ctx, rtgeom, RTWKT_EXTENDED, 12, &wkt_size); if ( ! wkt ) { rterror(ctx, "Error writing geom %p to RTWKT", rtgeom); } return wkt; } /** * @brief geom1 same as geom2 * iff * + have same type * + have same # objects * + have same bvol * + each object in geom1 has a corresponding object in geom2 (see above) * @param rtgeom1 * @param rtgeom2 */ char rtgeom_same(const RTCTX *ctx, const RTGEOM *rtgeom1, const RTGEOM *rtgeom2) { RTDEBUGF(2, "rtgeom_same(ctx, %s, %s) called", rttype_name(ctx, rtgeom1->type), rttype_name(ctx, rtgeom2->type)); if ( rtgeom1->type != rtgeom2->type ) { RTDEBUG(3, " type differ"); return RT_FALSE; } if ( RTFLAGS_GET_ZM(rtgeom1->flags) != RTFLAGS_GET_ZM(rtgeom2->flags) ) { RTDEBUG(3, " ZM flags differ"); return RT_FALSE; } /* Check boxes if both already computed */ if ( rtgeom1->bbox && rtgeom2->bbox ) { /*rtnotice(ctx, "bbox1:%p, bbox2:%p", rtgeom1->bbox, rtgeom2->bbox);*/ if ( ! gbox_same(ctx, rtgeom1->bbox, rtgeom2->bbox) ) { RTDEBUG(3, " bounding boxes differ"); return RT_FALSE; } } /* geoms have same type, invoke type-specific function */ switch (rtgeom1->type) { case RTPOINTTYPE: return rtpoint_same(ctx, (RTPOINT *)rtgeom1, (RTPOINT *)rtgeom2); case RTLINETYPE: return rtline_same(ctx, (RTLINE *)rtgeom1, (RTLINE *)rtgeom2); case RTPOLYGONTYPE: return rtpoly_same(ctx, (RTPOLY *)rtgeom1, (RTPOLY *)rtgeom2); case RTTRIANGLETYPE: return rttriangle_same(ctx, (RTTRIANGLE *)rtgeom1, (RTTRIANGLE *)rtgeom2); case RTCIRCSTRINGTYPE: return rtcircstring_same(ctx, (RTCIRCSTRING *)rtgeom1, (RTCIRCSTRING *)rtgeom2); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return rtcollection_same(ctx, (RTCOLLECTION *)rtgeom1, (RTCOLLECTION *)rtgeom2); default: rterror(ctx, "rtgeom_same: unsupported geometry type: %s", rttype_name(ctx, rtgeom1->type)); return RT_FALSE; } } int rtpoint_inside_circle(const RTCTX *ctx, const RTPOINT *p, double cx, double cy, double rad) { const RTPOINT2D *pt; RTPOINT2D center; if ( ! p || ! p->point ) return RT_FALSE; pt = rt_getPoint2d_cp(ctx, p->point, 0); center.x = cx; center.y = cy; if ( distance2d_pt_pt(ctx, pt, ¢er) < rad ) return RT_TRUE; return RT_FALSE; } void rtgeom_drop_bbox(const RTCTX *ctx, RTGEOM *rtgeom) { if ( rtgeom->bbox ) rtfree(ctx, rtgeom->bbox); rtgeom->bbox = NULL; RTFLAGS_SET_BBOX(rtgeom->flags, 0); } /** * Ensure there's a box in the RTGEOM. * If the box is already there just return, * else compute it. */ void rtgeom_add_bbox(const RTCTX *ctx, RTGEOM *rtgeom) { /* an empty RTGEOM has no bbox */ if ( rtgeom_is_empty(ctx, rtgeom) ) return; if ( rtgeom->bbox ) return; RTFLAGS_SET_BBOX(rtgeom->flags, 1); rtgeom->bbox = gbox_new(ctx, rtgeom->flags); rtgeom_calculate_gbox(ctx, rtgeom, rtgeom->bbox); } void rtgeom_add_bbox_deep(const RTCTX *ctx, RTGEOM *rtgeom, RTGBOX *gbox) { if ( rtgeom_is_empty(ctx, rtgeom) ) return; RTFLAGS_SET_BBOX(rtgeom->flags, 1); if ( ! ( gbox || rtgeom->bbox ) ) { rtgeom->bbox = gbox_new(ctx, rtgeom->flags); rtgeom_calculate_gbox(ctx, rtgeom, rtgeom->bbox); } else if ( gbox && ! rtgeom->bbox ) { rtgeom->bbox = gbox_clone(ctx, gbox); } if ( rtgeom_is_collection(ctx, rtgeom) ) { int i; RTCOLLECTION *rtcol = (RTCOLLECTION*)rtgeom; for ( i = 0; i < rtcol->ngeoms; i++ ) { rtgeom_add_bbox_deep(ctx, rtcol->geoms[i], rtgeom->bbox); } } } const RTGBOX * rtgeom_get_bbox(const RTCTX *ctx, const RTGEOM *rtg) { /* add it if not already there */ rtgeom_add_bbox(ctx, (RTGEOM *)rtg); return rtg->bbox; } /** * Calculate the gbox for this goemetry, a cartesian box or * geodetic box, depending on how it is flagged. */ int rtgeom_calculate_gbox(const RTCTX *ctx, const RTGEOM *rtgeom, RTGBOX *gbox) { gbox->flags = rtgeom->flags; if( RTFLAGS_GET_GEODETIC(rtgeom->flags) ) return rtgeom_calculate_gbox_geodetic(ctx, rtgeom, gbox); else return rtgeom_calculate_gbox_cartesian(ctx, rtgeom, gbox); } void rtgeom_drop_srid(const RTCTX *ctx, RTGEOM *rtgeom) { rtgeom->srid = SRID_UNKNOWN; /* TODO: To be changed to SRID_UNKNOWN */ } RTGEOM * rtgeom_segmentize2d(const RTCTX *ctx, RTGEOM *rtgeom, double dist) { switch (rtgeom->type) { case RTLINETYPE: return (RTGEOM *)rtline_segmentize2d(ctx, (RTLINE *)rtgeom, dist); case RTPOLYGONTYPE: return (RTGEOM *)rtpoly_segmentize2d(ctx, (RTPOLY *)rtgeom, dist); case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return (RTGEOM *)rtcollection_segmentize2d(ctx, (RTCOLLECTION *)rtgeom, dist); default: return rtgeom_clone(ctx, rtgeom); } } RTGEOM* rtgeom_force_2d(const RTCTX *ctx, const RTGEOM *geom) { return rtgeom_force_dims(ctx, geom, 0, 0); } RTGEOM* rtgeom_force_3dz(const RTCTX *ctx, const RTGEOM *geom) { return rtgeom_force_dims(ctx, geom, 1, 0); } RTGEOM* rtgeom_force_3dm(const RTCTX *ctx, const RTGEOM *geom) { return rtgeom_force_dims(ctx, geom, 0, 1); } RTGEOM* rtgeom_force_4d(const RTCTX *ctx, const RTGEOM *geom) { return rtgeom_force_dims(ctx, geom, 1, 1); } RTGEOM* rtgeom_force_dims(const RTCTX *ctx, const RTGEOM *geom, int hasz, int hasm) { switch(geom->type) { case RTPOINTTYPE: return rtpoint_as_rtgeom(ctx, rtpoint_force_dims(ctx, (RTPOINT*)geom, hasz, hasm)); case RTCIRCSTRINGTYPE: case RTLINETYPE: case RTTRIANGLETYPE: return rtline_as_rtgeom(ctx, rtline_force_dims(ctx, (RTLINE*)geom, hasz, hasm)); case RTPOLYGONTYPE: return rtpoly_as_rtgeom(ctx, rtpoly_force_dims(ctx, (RTPOLY*)geom, hasz, hasm)); case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return rtcollection_as_rtgeom(ctx, rtcollection_force_dims(ctx, (RTCOLLECTION*)geom, hasz, hasm)); default: rterror(ctx, "rtgeom_force_2d: unsupported geom type: %s", rttype_name(ctx, geom->type)); return NULL; } } RTGEOM* rtgeom_force_sfs(const RTCTX *ctx, RTGEOM *geom, int version) { RTCOLLECTION *col; int i; RTGEOM *g; /* SFS 1.2 version */ if (version == 120) { switch(geom->type) { /* SQL/MM types */ case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: return rtgeom_stroke(ctx, geom, 32); case RTCOLLECTIONTYPE: col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) col->geoms[i] = rtgeom_force_sfs(ctx, (RTGEOM*)col->geoms[i], version); return rtcollection_as_rtgeom(ctx, (RTCOLLECTION*)geom); default: return (RTGEOM *)geom; } } /* SFS 1.1 version */ switch(geom->type) { /* SQL/MM types */ case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: return rtgeom_stroke(ctx, geom, 32); /* SFS 1.2 types */ case RTTRIANGLETYPE: g = rtpoly_as_rtgeom(ctx, rtpoly_from_rtlines(ctx, (RTLINE*)geom, 0, NULL)); rtgeom_free(ctx, geom); return g; case RTTINTYPE: col = (RTCOLLECTION*) geom; for ( i = 0; i < col->ngeoms; i++ ) { g = rtpoly_as_rtgeom(ctx, rtpoly_from_rtlines(ctx, (RTLINE*)col->geoms[i], 0, NULL)); rtgeom_free(ctx, col->geoms[i]); col->geoms[i] = g; } col->type = RTCOLLECTIONTYPE; return rtmpoly_as_rtgeom(ctx, (RTMPOLY*)geom); case RTPOLYHEDRALSURFACETYPE: geom->type = RTCOLLECTIONTYPE; return (RTGEOM *)geom; /* Collection */ case RTCOLLECTIONTYPE: col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) col->geoms[i] = rtgeom_force_sfs(ctx, (RTGEOM*)col->geoms[i], version); return rtcollection_as_rtgeom(ctx, (RTCOLLECTION*)geom); default: return (RTGEOM *)geom; } } int32_t rtgeom_get_srid(const RTCTX *ctx, const RTGEOM *geom) { if ( ! geom ) return SRID_UNKNOWN; return geom->srid; } uint32_t rtgeom_get_type(const RTCTX *ctx, const RTGEOM *geom) { if ( ! geom ) return 0; return geom->type; } int rtgeom_has_z(const RTCTX *ctx, const RTGEOM *geom) { if ( ! geom ) return RT_FALSE; return RTFLAGS_GET_Z(geom->flags); } int rtgeom_has_m(const RTCTX *ctx, const RTGEOM *geom) { if ( ! geom ) return RT_FALSE; return RTFLAGS_GET_M(geom->flags); } int rtgeom_ndims(const RTCTX *ctx, const RTGEOM *geom) { if ( ! geom ) return 0; return RTFLAGS_NDIMS(geom->flags); } void rtgeom_set_geodetic(const RTCTX *ctx, RTGEOM *geom, int value) { RTPOINT *pt; RTLINE *ln; RTPOLY *ply; RTCOLLECTION *col; int i; RTFLAGS_SET_GEODETIC(geom->flags, value); if ( geom->bbox ) RTFLAGS_SET_GEODETIC(geom->bbox->flags, value); switch(geom->type) { case RTPOINTTYPE: pt = (RTPOINT*)geom; if ( pt->point ) RTFLAGS_SET_GEODETIC(pt->point->flags, value); break; case RTLINETYPE: ln = (RTLINE*)geom; if ( ln->points ) RTFLAGS_SET_GEODETIC(ln->points->flags, value); break; case RTPOLYGONTYPE: ply = (RTPOLY*)geom; for ( i = 0; i < ply->nrings; i++ ) RTFLAGS_SET_GEODETIC(ply->rings[i]->flags, value); break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) rtgeom_set_geodetic(ctx, col->geoms[i], value); break; default: rterror(ctx, "rtgeom_set_geodetic: unsupported geom type: %s", rttype_name(ctx, geom->type)); return; } } void rtgeom_longitude_shift(const RTCTX *ctx, RTGEOM *rtgeom) { int i; switch (rtgeom->type) { RTPOINT *point; RTLINE *line; RTPOLY *poly; RTTRIANGLE *triangle; RTCOLLECTION *coll; case RTPOINTTYPE: point = (RTPOINT *)rtgeom; ptarray_longitude_shift(ctx, point->point); return; case RTLINETYPE: line = (RTLINE *)rtgeom; ptarray_longitude_shift(ctx, line->points); return; case RTPOLYGONTYPE: poly = (RTPOLY *)rtgeom; for (i=0; inrings; i++) ptarray_longitude_shift(ctx, poly->rings[i]); return; case RTTRIANGLETYPE: triangle = (RTTRIANGLE *)rtgeom; ptarray_longitude_shift(ctx, triangle->points); return; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: coll = (RTCOLLECTION *)rtgeom; for (i=0; ingeoms; i++) rtgeom_longitude_shift(ctx, coll->geoms[i]); return; default: rterror(ctx, "rtgeom_longitude_shift: unsupported geom type: %s", rttype_name(ctx, rtgeom->type)); } } int rtgeom_is_closed(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if( rtgeom_is_empty(ctx, geom) ) return RT_FALSE; /* Test linear types for closure */ switch (type) { case RTLINETYPE: return rtline_is_closed(ctx, (RTLINE*)geom); case RTPOLYGONTYPE: return rtpoly_is_closed(ctx, (RTPOLY*)geom); case RTCIRCSTRINGTYPE: return rtcircstring_is_closed(ctx, (RTCIRCSTRING*)geom); case RTCOMPOUNDTYPE: return rtcompound_is_closed(ctx, (RTCOMPOUND*)geom); case RTTINTYPE: return rttin_is_closed(ctx, (RTTIN*)geom); case RTPOLYHEDRALSURFACETYPE: return rtpsurface_is_closed(ctx, (RTPSURFACE*)geom); } /* Recurse into collections and see if anything is not closed */ if ( rtgeom_is_collection(ctx, geom) ) { RTCOLLECTION *col = rtgeom_as_rtcollection(ctx, geom); int i; int closed; for ( i = 0; i < col->ngeoms; i++ ) { closed = rtgeom_is_closed(ctx, col->geoms[i]); if ( ! closed ) return RT_FALSE; } return RT_TRUE; } /* All non-linear non-collection types we will call closed */ return RT_TRUE; } int rtgeom_is_collection(const RTCTX *ctx, const RTGEOM *geom) { if( ! geom ) return RT_FALSE; return rttype_is_collection(ctx, geom->type); } /** Return TRUE if the geometry may contain sub-geometries, i.e. it is a MULTI* or COMPOUNDCURVE */ int rttype_is_collection(const RTCTX *ctx, uint8_t type) { switch (type) { case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: return RT_TRUE; break; default: return RT_FALSE; } } /** * Given an rttype number, what homogeneous collection can hold it? */ int rttype_get_collectiontype(const RTCTX *ctx, uint8_t type) { switch (type) { case RTPOINTTYPE: return RTMULTIPOINTTYPE; case RTLINETYPE: return RTMULTILINETYPE; case RTPOLYGONTYPE: return RTMULTIPOLYGONTYPE; case RTCIRCSTRINGTYPE: return RTMULTICURVETYPE; case RTCOMPOUNDTYPE: return RTMULTICURVETYPE; case RTCURVEPOLYTYPE: return RTMULTISURFACETYPE; case RTTRIANGLETYPE: return RTTINTYPE; default: return RTCOLLECTIONTYPE; } } void rtgeom_free(const RTCTX *ctx, RTGEOM *rtgeom) { /* There's nothing here to free... */ if( ! rtgeom ) return; RTDEBUGF(5,"freeing a %s",rttype_name(ctx, rtgeom->type)); switch (rtgeom->type) { case RTPOINTTYPE: rtpoint_free(ctx, (RTPOINT *)rtgeom); break; case RTLINETYPE: rtline_free(ctx, (RTLINE *)rtgeom); break; case RTPOLYGONTYPE: rtpoly_free(ctx, (RTPOLY *)rtgeom); break; case RTCIRCSTRINGTYPE: rtcircstring_free(ctx, (RTCIRCSTRING *)rtgeom); break; case RTTRIANGLETYPE: rttriangle_free(ctx, (RTTRIANGLE *)rtgeom); break; case RTMULTIPOINTTYPE: rtmpoint_free(ctx, (RTMPOINT *)rtgeom); break; case RTMULTILINETYPE: rtmline_free(ctx, (RTMLINE *)rtgeom); break; case RTMULTIPOLYGONTYPE: rtmpoly_free(ctx, (RTMPOLY *)rtgeom); break; case RTPOLYHEDRALSURFACETYPE: rtpsurface_free(ctx, (RTPSURFACE *)rtgeom); break; case RTTINTYPE: rttin_free(ctx, (RTTIN *)rtgeom); break; case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTCOLLECTIONTYPE: rtcollection_free(ctx, (RTCOLLECTION *)rtgeom); break; default: rterror(ctx, "rtgeom_free called with unknown type (%d) %s", rtgeom->type, rttype_name(ctx, rtgeom->type)); } return; } int rtgeom_needs_bbox(const RTCTX *ctx, const RTGEOM *geom) { assert(geom); if ( geom->type == RTPOINTTYPE ) { return RT_FALSE; } else if ( geom->type == RTLINETYPE ) { if ( rtgeom_count_vertices(ctx, geom) <= 2 ) return RT_FALSE; else return RT_TRUE; } else if ( geom->type == RTMULTIPOINTTYPE ) { if ( ((RTCOLLECTION*)geom)->ngeoms == 1 ) return RT_FALSE; else return RT_TRUE; } else if ( geom->type == RTMULTILINETYPE ) { if ( ((RTCOLLECTION*)geom)->ngeoms == 1 && rtgeom_count_vertices(ctx, geom) <= 2 ) return RT_FALSE; else return RT_TRUE; } else { return RT_TRUE; } } /** * Count points in an #RTGEOM. */ int rtgeom_count_vertices(const RTCTX *ctx, const RTGEOM *geom) { int result = 0; /* Null? Zero. */ if( ! geom ) return 0; RTDEBUGF(4, "rtgeom_count_vertices got type %s", rttype_name(ctx, geom->type)); /* Empty? Zero. */ if( rtgeom_is_empty(ctx, geom) ) return 0; switch (geom->type) { case RTPOINTTYPE: result = 1; break; case RTTRIANGLETYPE: case RTCIRCSTRINGTYPE: case RTLINETYPE: result = rtline_count_vertices(ctx, (RTLINE *)geom); break; case RTPOLYGONTYPE: result = rtpoly_count_vertices(ctx, (RTPOLY *)geom); break; case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: result = rtcollection_count_vertices(ctx, (RTCOLLECTION *)geom); break; default: rterror(ctx, "%s: unsupported input geometry type: %s", __func__, rttype_name(ctx, geom->type)); break; } RTDEBUGF(3, "counted %d vertices", result); return result; } /** * For an #RTGEOM, returns 0 for points, 1 for lines, * 2 for polygons, 3 for volume, and the max dimension * of a collection. */ int rtgeom_dimension(const RTCTX *ctx, const RTGEOM *geom) { /* Null? Zero. */ if( ! geom ) return -1; RTDEBUGF(4, "rtgeom_dimension got type %s", rttype_name(ctx, geom->type)); /* Empty? Zero. */ /* if( rtgeom_is_empty(ctx, geom) ) return 0; */ switch (geom->type) { case RTPOINTTYPE: case RTMULTIPOINTTYPE: return 0; case RTCIRCSTRINGTYPE: case RTLINETYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTMULTILINETYPE: return 1; case RTTRIANGLETYPE: case RTPOLYGONTYPE: case RTCURVEPOLYTYPE: case RTMULTISURFACETYPE: case RTMULTIPOLYGONTYPE: case RTTINTYPE: return 2; case RTPOLYHEDRALSURFACETYPE: { /* A closed polyhedral surface contains a volume. */ int closed = rtpsurface_is_closed(ctx, (RTPSURFACE*)geom); return ( closed ? 3 : 2 ); } case RTCOLLECTIONTYPE: { int maxdim = 0, i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for( i = 0; i < col->ngeoms; i++ ) { int dim = rtgeom_dimension(ctx, col->geoms[i]); maxdim = ( dim > maxdim ? dim : maxdim ); } return maxdim; } default: rterror(ctx, "%s: unsupported input geometry type: %s", __func__, rttype_name(ctx, geom->type)); } return -1; } /** * Count rings in an #RTGEOM. */ int rtgeom_count_rings(const RTCTX *ctx, const RTGEOM *geom) { int result = 0; /* Null? Empty? Zero. */ if( ! geom || rtgeom_is_empty(ctx, geom) ) return 0; switch (geom->type) { case RTPOINTTYPE: case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTLINETYPE: result = 0; break; case RTTRIANGLETYPE: result = 1; break; case RTPOLYGONTYPE: result = ((RTPOLY *)geom)->nrings; break; case RTCURVEPOLYTYPE: result = ((RTCURVEPOLY *)geom)->nrings; break; case RTMULTISURFACETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: { RTCOLLECTION *col = (RTCOLLECTION*)geom; int i = 0; for( i = 0; i < col->ngeoms; i++ ) result += rtgeom_count_rings(ctx, col->geoms[i]); break; } default: rterror(ctx, "rtgeom_count_rings: unsupported input geometry type: %s", rttype_name(ctx, geom->type)); break; } RTDEBUGF(3, "counted %d rings", result); return result; } int rtgeom_is_empty(const RTCTX *ctx, const RTGEOM *geom) { int result = RT_FALSE; RTDEBUGF(4, "rtgeom_is_empty: got type %s", rttype_name(ctx, geom->type)); switch (geom->type) { case RTPOINTTYPE: return rtpoint_is_empty(ctx, (RTPOINT*)geom); break; case RTLINETYPE: return rtline_is_empty(ctx, (RTLINE*)geom); break; case RTCIRCSTRINGTYPE: return rtcircstring_is_empty(ctx, (RTCIRCSTRING*)geom); break; case RTPOLYGONTYPE: return rtpoly_is_empty(ctx, (RTPOLY*)geom); break; case RTTRIANGLETYPE: return rttriangle_is_empty(ctx, (RTTRIANGLE*)geom); break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return rtcollection_is_empty(ctx, (RTCOLLECTION *)geom); break; default: rterror(ctx, "rtgeom_is_empty: unsupported input geometry type: %s", rttype_name(ctx, geom->type)); break; } return result; } int rtgeom_has_srid(const RTCTX *ctx, const RTGEOM *geom) { if ( geom->srid != SRID_UNKNOWN ) return RT_TRUE; return RT_FALSE; } static int rtcollection_dimensionality(const RTCTX *ctx, RTCOLLECTION *col) { int i; int dimensionality = 0; for ( i = 0; i < col->ngeoms; i++ ) { int d = rtgeom_dimensionality(ctx, col->geoms[i]); if ( d > dimensionality ) dimensionality = d; } return dimensionality; } extern int rtgeom_dimensionality(const RTCTX *ctx, RTGEOM *geom) { int dim; RTDEBUGF(3, "rtgeom_dimensionality got type %s", rttype_name(ctx, geom->type)); switch (geom->type) { case RTPOINTTYPE: case RTMULTIPOINTTYPE: return 0; break; case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTMULTILINETYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: return 1; break; case RTPOLYGONTYPE: case RTTRIANGLETYPE: case RTCURVEPOLYTYPE: case RTMULTIPOLYGONTYPE: case RTMULTISURFACETYPE: return 2; break; case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: dim = rtgeom_is_closed(ctx, geom)?3:2; return dim; break; case RTCOLLECTIONTYPE: return rtcollection_dimensionality(ctx, (RTCOLLECTION *)geom); break; default: rterror(ctx, "rtgeom_dimensionality: unsupported input geometry type: %s", rttype_name(ctx, geom->type)); break; } return 0; } extern RTGEOM* rtgeom_remove_repeated_points(const RTCTX *ctx, const RTGEOM *in, double tolerance) { RTDEBUGF(4, "rtgeom_remove_repeated_points got type %s", rttype_name(ctx, in->type)); if(rtgeom_is_empty(ctx, in)) { return rtgeom_clone_deep(ctx, in); } switch (in->type) { case RTMULTIPOINTTYPE: return rtmpoint_remove_repeated_points(ctx, (RTMPOINT*)in, tolerance); break; case RTLINETYPE: return rtline_remove_repeated_points(ctx, (RTLINE*)in, tolerance); case RTMULTILINETYPE: case RTCOLLECTIONTYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: return rtcollection_remove_repeated_points(ctx, (RTCOLLECTION *)in, tolerance); case RTPOLYGONTYPE: return rtpoly_remove_repeated_points(ctx, (RTPOLY *)in, tolerance); break; case RTPOINTTYPE: case RTTRIANGLETYPE: case RTTINTYPE: /* No point is repeated for a single point, or for Triangle or TIN */ return rtgeom_clone_deep(ctx, in); case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTCURVEPOLYTYPE: case RTMULTISURFACETYPE: /* Dunno how to handle these, will return untouched */ return rtgeom_clone_deep(ctx, in); default: rtnotice(ctx, "%s: unsupported geometry type: %s", __func__, rttype_name(ctx, in->type)); return rtgeom_clone_deep(ctx, in); break; } return 0; } RTGEOM* rtgeom_flip_coordinates(const RTCTX *ctx, RTGEOM *in) { rtgeom_swap_ordinates(ctx, in,RTORD_X,RTORD_Y); return in; } void rtgeom_swap_ordinates(const RTCTX *ctx, RTGEOM *in, RTORD o1, RTORD o2) { RTCOLLECTION *col; RTPOLY *poly; int i; #if PARANOIA_LEVEL > 0 assert(o1 < 4); assert(o2 < 4); #endif if ( (!in) || rtgeom_is_empty(ctx, in) ) return; /* TODO: check for rtgeom NOT having the specified dimension ? */ RTDEBUGF(4, "rtgeom_flip_coordinates, got type: %s", rttype_name(ctx, in->type)); switch (in->type) { case RTPOINTTYPE: ptarray_swap_ordinates(ctx, rtgeom_as_rtpoint(ctx, in)->point, o1, o2); break; case RTLINETYPE: ptarray_swap_ordinates(ctx, rtgeom_as_rtline(ctx, in)->points, o1, o2); break; case RTCIRCSTRINGTYPE: ptarray_swap_ordinates(ctx, rtgeom_as_rtcircstring(ctx, in)->points, o1, o2); break; case RTPOLYGONTYPE: poly = (RTPOLY *) in; for (i=0; inrings; i++) { ptarray_swap_ordinates(ctx, poly->rings[i], o1, o2); } break; case RTTRIANGLETYPE: ptarray_swap_ordinates(ctx, rtgeom_as_rttriangle(ctx, in)->points, o1, o2); break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTISURFACETYPE: case RTMULTICURVETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: col = (RTCOLLECTION *) in; for (i=0; ingeoms; i++) { rtgeom_swap_ordinates(ctx, col->geoms[i], o1, o2); } break; default: rterror(ctx, "rtgeom_swap_ordinates: unsupported geometry type: %s", rttype_name(ctx, in->type)); return; } /* only refresh bbox if X or Y changed */ if ( in->bbox && (o1 < 2 || o2 < 2) ) { rtgeom_drop_bbox(ctx, in); rtgeom_add_bbox(ctx, in); } } void rtgeom_set_srid(const RTCTX *ctx, RTGEOM *geom, int32_t srid) { int i; RTDEBUGF(4,"entered with srid=%d",srid); geom->srid = srid; if ( rtgeom_is_collection(ctx, geom) ) { /* All the children are set to the same SRID value */ RTCOLLECTION *col = rtgeom_as_rtcollection(ctx, geom); for ( i = 0; i < col->ngeoms; i++ ) { rtgeom_set_srid(ctx, col->geoms[i], srid); } } } RTGEOM* rtgeom_simplify(const RTCTX *ctx, const RTGEOM *igeom, double dist, int preserve_collapsed) { switch (igeom->type) { case RTPOINTTYPE: case RTMULTIPOINTTYPE: return rtgeom_clone(ctx, igeom); case RTLINETYPE: return (RTGEOM*)rtline_simplify(ctx, (RTLINE*)igeom, dist, preserve_collapsed); case RTPOLYGONTYPE: return (RTGEOM*)rtpoly_simplify(ctx, (RTPOLY*)igeom, dist, preserve_collapsed); case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return (RTGEOM*)rtcollection_simplify(ctx, (RTCOLLECTION *)igeom, dist, preserve_collapsed); default: rterror(ctx, "%s: unsupported geometry type: %s", __func__, rttype_name(ctx, igeom->type)); } return NULL; } double rtgeom_area(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if ( type == RTPOLYGONTYPE ) return rtpoly_area(ctx, (RTPOLY*)geom); else if ( type == RTCURVEPOLYTYPE ) return rtcurvepoly_area(ctx, (RTCURVEPOLY*)geom); else if (type == RTTRIANGLETYPE ) return rttriangle_area(ctx, (RTTRIANGLE*)geom); else if ( rtgeom_is_collection(ctx, geom) ) { double area = 0.0; int i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) area += rtgeom_area(ctx, col->geoms[i]); return area; } else return 0.0; } double rtgeom_perimeter(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if ( type == RTPOLYGONTYPE ) return rtpoly_perimeter(ctx, (RTPOLY*)geom); else if ( type == RTCURVEPOLYTYPE ) return rtcurvepoly_perimeter(ctx, (RTCURVEPOLY*)geom); else if ( type == RTTRIANGLETYPE ) return rttriangle_perimeter(ctx, (RTTRIANGLE*)geom); else if ( rtgeom_is_collection(ctx, geom) ) { double perimeter = 0.0; int i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) perimeter += rtgeom_perimeter(ctx, col->geoms[i]); return perimeter; } else return 0.0; } double rtgeom_perimeter_2d(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if ( type == RTPOLYGONTYPE ) return rtpoly_perimeter_2d(ctx, (RTPOLY*)geom); else if ( type == RTCURVEPOLYTYPE ) return rtcurvepoly_perimeter_2d(ctx, (RTCURVEPOLY*)geom); else if ( type == RTTRIANGLETYPE ) return rttriangle_perimeter_2d(ctx, (RTTRIANGLE*)geom); else if ( rtgeom_is_collection(ctx, geom) ) { double perimeter = 0.0; int i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) perimeter += rtgeom_perimeter_2d(ctx, col->geoms[i]); return perimeter; } else return 0.0; } double rtgeom_length(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if ( type == RTLINETYPE ) return rtline_length(ctx, (RTLINE*)geom); else if ( type == RTCIRCSTRINGTYPE ) return rtcircstring_length(ctx, (RTCIRCSTRING*)geom); else if ( type == RTCOMPOUNDTYPE ) return rtcompound_length(ctx, (RTCOMPOUND*)geom); else if ( rtgeom_is_collection(ctx, geom) ) { double length = 0.0; int i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) length += rtgeom_length(ctx, col->geoms[i]); return length; } else return 0.0; } double rtgeom_length_2d(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if ( type == RTLINETYPE ) return rtline_length_2d(ctx, (RTLINE*)geom); else if ( type == RTCIRCSTRINGTYPE ) return rtcircstring_length_2d(ctx, (RTCIRCSTRING*)geom); else if ( type == RTCOMPOUNDTYPE ) return rtcompound_length_2d(ctx, (RTCOMPOUND*)geom); else if ( rtgeom_is_collection(ctx, geom) ) { double length = 0.0; int i; RTCOLLECTION *col = (RTCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) length += rtgeom_length_2d(ctx, col->geoms[i]); return length; } else return 0.0; } void rtgeom_affine(const RTCTX *ctx, RTGEOM *geom, const RTAFFINE *affine) { int type = geom->type; int i; switch(type) { /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */ case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTTRIANGLETYPE: { RTLINE *l = (RTLINE*)geom; ptarray_affine(ctx, l->points, affine); break; } case RTPOLYGONTYPE: { RTPOLY *p = (RTPOLY*)geom; for( i = 0; i < p->nrings; i++ ) ptarray_affine(ctx, p->rings[i], affine); break; } case RTCURVEPOLYTYPE: { RTCURVEPOLY *c = (RTCURVEPOLY*)geom; for( i = 0; i < c->nrings; i++ ) rtgeom_affine(ctx, c->rings[i], affine); break; } default: { if( rtgeom_is_collection(ctx, geom) ) { RTCOLLECTION *c = (RTCOLLECTION*)geom; for( i = 0; i < c->ngeoms; i++ ) { rtgeom_affine(ctx, c->geoms[i], affine); } } else { rterror(ctx, "rtgeom_affine: unable to handle type '%s'", rttype_name(ctx, type)); } } } } void rtgeom_scale(const RTCTX *ctx, RTGEOM *geom, const RTPOINT4D *factor) { int type = geom->type; int i; switch(type) { /* Take advantage of fact tht pt/ln/circ/tri have same memory structure */ case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTTRIANGLETYPE: { RTLINE *l = (RTLINE*)geom; ptarray_scale(ctx, l->points, factor); break; } case RTPOLYGONTYPE: { RTPOLY *p = (RTPOLY*)geom; for( i = 0; i < p->nrings; i++ ) ptarray_scale(ctx, p->rings[i], factor); break; } case RTCURVEPOLYTYPE: { RTCURVEPOLY *c = (RTCURVEPOLY*)geom; for( i = 0; i < c->nrings; i++ ) rtgeom_scale(ctx, c->rings[i], factor); break; } default: { if( rtgeom_is_collection(ctx, geom) ) { RTCOLLECTION *c = (RTCOLLECTION*)geom; for( i = 0; i < c->ngeoms; i++ ) { rtgeom_scale(ctx, c->geoms[i], factor); } } else { rterror(ctx, "rtgeom_scale: unable to handle type '%s'", rttype_name(ctx, type)); } } } /* Recompute bbox if needed */ if ( geom->bbox ) { /* TODO: expose a gbox_scale function */ geom->bbox->xmin *= factor->x; geom->bbox->xmax *= factor->x; geom->bbox->ymin *= factor->y; geom->bbox->ymax *= factor->y; geom->bbox->zmin *= factor->z; geom->bbox->zmax *= factor->z; geom->bbox->mmin *= factor->m; geom->bbox->mmax *= factor->m; } } RTGEOM* rtgeom_construct_empty(const RTCTX *ctx, uint8_t type, int srid, char hasz, char hasm) { switch(type) { case RTPOINTTYPE: return rtpoint_as_rtgeom(ctx, rtpoint_construct_empty(ctx, srid, hasz, hasm)); case RTLINETYPE: return rtline_as_rtgeom(ctx, rtline_construct_empty(ctx, srid, hasz, hasm)); case RTPOLYGONTYPE: return rtpoly_as_rtgeom(ctx, rtpoly_construct_empty(ctx, srid, hasz, hasm)); case RTCURVEPOLYTYPE: return rtcurvepoly_as_rtgeom(ctx, rtcurvepoly_construct_empty(ctx, srid, hasz, hasm)); case RTCIRCSTRINGTYPE: return rtcircstring_as_rtgeom(ctx, rtcircstring_construct_empty(ctx, srid, hasz, hasm)); case RTTRIANGLETYPE: return rttriangle_as_rtgeom(ctx, rttriangle_construct_empty(ctx, srid, hasz, hasm)); case RTCOMPOUNDTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return rtcollection_as_rtgeom(ctx, rtcollection_construct_empty(ctx, type, srid, hasz, hasm)); default: rterror(ctx, "rtgeom_construct_empty: unsupported geometry type: %s", rttype_name(ctx, type)); return NULL; } } int rtgeom_startpoint(const RTCTX *ctx, const RTGEOM* rtgeom, RTPOINT4D* pt) { if ( ! rtgeom ) return RT_FAILURE; switch( rtgeom->type ) { case RTPOINTTYPE: return ptarray_startpoint(ctx, ((RTPOINT*)rtgeom)->point, pt); case RTTRIANGLETYPE: case RTCIRCSTRINGTYPE: case RTLINETYPE: return ptarray_startpoint(ctx, ((RTLINE*)rtgeom)->points, pt); case RTPOLYGONTYPE: return rtpoly_startpoint(ctx, (RTPOLY*)rtgeom, pt); case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return rtcollection_startpoint(ctx, (RTCOLLECTION*)rtgeom, pt); default: rterror(ctx, "int: unsupported geometry type: %s", rttype_name(ctx, rtgeom->type)); return RT_FAILURE; } } RTGEOM * rtgeom_grid(const RTCTX *ctx, const RTGEOM *rtgeom, const gridspec *grid) { switch ( rtgeom->type ) { case RTPOINTTYPE: return (RTGEOM *)rtpoint_grid(ctx, (RTPOINT *)rtgeom, grid); case RTLINETYPE: return (RTGEOM *)rtline_grid(ctx, (RTLINE *)rtgeom, grid); case RTPOLYGONTYPE: return (RTGEOM *)rtpoly_grid(ctx, (RTPOLY *)rtgeom, grid); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: case RTCOMPOUNDTYPE: return (RTGEOM *)rtcollection_grid(ctx, (RTCOLLECTION *)rtgeom, grid); case RTCIRCSTRINGTYPE: return (RTGEOM *)rtcircstring_grid(ctx, (RTCIRCSTRING *)rtgeom, grid); default: rterror(ctx, "rtgeom_grid: Unsupported geometry type: %s", rttype_name(ctx, rtgeom->type)); return NULL; } } /* Prototype for recursion */ static int rtgeom_subdivide_recursive(const RTCTX *ctx, const RTGEOM *geom, int maxvertices, int depth, RTCOLLECTION *col, const RTGBOX *clip); static int rtgeom_subdivide_recursive(const RTCTX *ctx, const RTGEOM *geom, int maxvertices, int depth, RTCOLLECTION *col, const RTGBOX *clip) { const int maxdepth = 50; int nvertices = 0; int i, n = 0; double width = clip->xmax - clip->xmin; double height = clip->ymax - clip->ymin; RTGBOX subbox1, subbox2; RTGEOM *clipped1, *clipped2; if ( geom->type == RTPOLYHEDRALSURFACETYPE || geom->type == RTTINTYPE ) { rterror(ctx, "%s: unsupported geometry type '%s'", __func__, rttype_name(ctx, geom->type)); } if ( width == 0.0 && height == 0.0 ) return 0; /* Artays just recurse into collections */ if ( rtgeom_is_collection(ctx, geom) ) { RTCOLLECTION *incol = (RTCOLLECTION*)geom; int n = 0; for ( i = 0; i < incol->ngeoms; i++ ) { /* Don't increment depth yet, since we aren't actually subdividing geomtries yet */ n += rtgeom_subdivide_recursive(ctx, incol->geoms[i], maxvertices, depth, col, clip); } return n; } /* But don't go too far. 2^25 = 33M, that's enough subdivision */ /* Signal callers above that we depth'ed out with a negative */ /* return value */ if ( depth > maxdepth ) { return 0; } nvertices = rtgeom_count_vertices(ctx, geom); /* Skip empties entirely */ if ( nvertices == 0 ) { return 0; } /* If it is under the vertex tolerance, just add it, we're done */ if ( nvertices < maxvertices ) { rtcollection_add_rtgeom(ctx, col, rtgeom_clone_deep(ctx, geom)); return 1; } subbox1 = subbox2 = *clip; if ( width > height ) { subbox1.xmax = subbox2.xmin = (clip->xmin + clip->xmax)/2; } else { subbox1.ymax = subbox2.ymin = (clip->ymin + clip->ymax)/2; } if ( height == 0 ) { subbox1.ymax += FP_TOLERANCE; subbox2.ymax += FP_TOLERANCE; subbox1.ymin -= FP_TOLERANCE; subbox2.ymin -= FP_TOLERANCE; } if ( width == 0 ) { subbox1.xmax += FP_TOLERANCE; subbox2.xmax += FP_TOLERANCE; subbox1.xmin -= FP_TOLERANCE; subbox2.xmin -= FP_TOLERANCE; } clipped1 = rtgeom_clip_by_rect(ctx, geom, subbox1.xmin, subbox1.ymin, subbox1.xmax, subbox1.ymax); clipped2 = rtgeom_clip_by_rect(ctx, geom, subbox2.xmin, subbox2.ymin, subbox2.xmax, subbox2.ymax); if ( clipped1 ) { n += rtgeom_subdivide_recursive(ctx, clipped1, maxvertices, ++depth, col, &subbox1); rtgeom_free(ctx, clipped1); } if ( clipped2 ) { n += rtgeom_subdivide_recursive(ctx, clipped2, maxvertices, ++depth, col, &subbox2); rtgeom_free(ctx, clipped2); } return n; } RTCOLLECTION * rtgeom_subdivide(const RTCTX *ctx, const RTGEOM *geom, int maxvertices) { static int startdepth = 0; static int minmaxvertices = 8; RTCOLLECTION *col; RTGBOX clip; col = rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, geom->srid, rtgeom_has_z(ctx, geom), rtgeom_has_m(ctx, geom)); if ( rtgeom_is_empty(ctx, geom) ) return col; if ( maxvertices < minmaxvertices ) { rtcollection_free(ctx, col); rterror(ctx, "%s: cannot subdivide to fewer than %d vertices per output", __func__, minmaxvertices); } clip = *(rtgeom_get_bbox(ctx, geom)); rtgeom_subdivide_recursive(ctx, geom, maxvertices, startdepth, col, &clip); rtgeom_set_srid(ctx, (RTGEOM*)col, geom->srid); return col; } int rtgeom_is_trajectory(const RTCTX *ctx, const RTGEOM *geom) { int type = geom->type; if( type != RTLINETYPE ) { rtnotice(ctx, "Geometry is not a LINESTRING"); return RT_FALSE; } return rtline_is_trajectory(ctx, (RTLINE*)geom); } src/rtgeom_api.c000066400000000000000000000425271271715413500141570ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include #include #include /* * Lower this to reduce integrity checks */ #define PARANOIA_LEVEL 1 const char * rtgeom_version() { static char *ptr = NULL; static char buf[256]; if ( ! ptr ) { ptr = buf; snprintf(ptr, 256, LIBRTGEOM_VERSION); } return ptr; } /********************************************************************** * BOX routines * * returns the float thats very close to the input, but <= * handles the funny differences in float4 and float8 reps. **********************************************************************/ typedef union { float value; uint32_t word; } ieee_float_shape_type; #define GET_FLOAT_WORD(i,d) \ do { \ ieee_float_shape_type gf_u; \ gf_u.value = (d); \ (i) = gf_u.word; \ } while (0) #define SET_FLOAT_WORD(d,i) \ do { \ ieee_float_shape_type sf_u; \ sf_u.word = (i); \ (d) = sf_u.value; \ } while (0) /* * Returns the next smaller or next larger float * from x (in direction of y). */ static float nextafterf_custom(const RTCTX *ctx, float x, float y) { int hx,hy,ix,iy; GET_FLOAT_WORD(hx,x); GET_FLOAT_WORD(hy,y); ix = hx&0x7fffffff; /* |x| */ iy = hy&0x7fffffff; /* |y| */ if ((ix>0x7f800000) || /* x is nan */ (iy>0x7f800000)) /* y is nan */ return x+y; if (x==y) return y; /* x=y, return y */ if (ix==0) { /* x == 0 */ SET_FLOAT_WORD(x,(hy&0x80000000)|1);/* return +-minsubnormal */ y = x*x; if (y==x) return y; else return x; /* raise underflow flag */ } if (hx>=0) { /* x > 0 */ if (hx>hy) { /* x > y, x -= ulp */ hx -= 1; } else { /* x < y, x += ulp */ hx += 1; } } else { /* x < 0 */ if (hy>=0||hx>hy) { /* x < y, x -= ulp */ hx -= 1; } else { /* x > y, x += ulp */ hx += 1; } } hy = hx&0x7f800000; if (hy>=0x7f800000) return x+x; /* overflow */ if (hy<0x00800000) { /* underflow */ y = x*x; if (y!=x) { /* raise underflow flag */ SET_FLOAT_WORD(y,hx); return y; } } SET_FLOAT_WORD(x,hx); return x; } float next_float_down(const RTCTX *ctx, double d) { float result = d; if ( ((double) result) <=d) return result; return nextafterf_custom(ctx, result, result - 1000000); } /* * Returns the float thats very close to the input, but >=. * handles the funny differences in float4 and float8 reps. */ float next_float_up(const RTCTX *ctx, double d) { float result = d; if ( ((double) result) >=d) return result; return nextafterf_custom(ctx, result, result + 1000000); } /* * Returns the double thats very close to the input, but <. * handles the funny differences in float4 and float8 reps. */ double next_double_down(const RTCTX *ctx, float d) { double result = d; if ( result < d) return result; return nextafterf_custom(ctx, result, result - 1000000); } /* * Returns the double thats very close to the input, but > * handles the funny differences in float4 and float8 reps. */ double next_double_up(const RTCTX *ctx, float d) { double result = d; if ( result > d) return result; return nextafterf_custom(ctx, result, result + 1000000); } /************************************************************************ * RTPOINTARRAY support functions * * TODO: should be moved to ptarray.c probably * ************************************************************************/ /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: point is a real POINT3D *not* a pointer */ RTPOINT4D rt_getPoint4d(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { RTPOINT4D result; rt_getPoint4d_p(ctx, pa, n, &result); return result; } /* * Copies a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * will set point's m=NO_M_VALUE if pa is 3d or 2d * * NOTE: this will modify the point4d pointed to by 'point'. */ int rt_getPoint4d_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT4D *op) { uint8_t *ptr; int zmflag; #if PARANOIA_LEVEL > 0 if ( ! pa ) rterror(ctx, "rt_getPoint4d_p: NULL pointarray"); if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "rt_getPoint4d_p: point offset out of range"); } #endif RTDEBUG(4, "rt_getPoint4d_p called."); /* Get a pointer to nth point offset and zmflag */ ptr=rt_getPoint_internal(ctx, pa, n); zmflag=RTFLAGS_GET_ZM(pa->flags); RTDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag); switch (zmflag) { case 0: /* 2d */ memcpy(op, ptr, sizeof(RTPOINT2D)); op->m=NO_M_VALUE; op->z=NO_Z_VALUE; break; case 3: /* ZM */ memcpy(op, ptr, sizeof(RTPOINT4D)); break; case 2: /* Z */ memcpy(op, ptr, sizeof(RTPOINT3DZ)); op->m=NO_M_VALUE; break; case 1: /* M */ memcpy(op, ptr, sizeof(RTPOINT3DM)); op->m=op->z; /* we use Z as temporary storage */ op->z=NO_Z_VALUE; break; default: rterror(ctx, "Unknown ZM flag ??"); } return 1; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * NOTE: point is a real RTPOINT3DZ *not* a pointer */ RTPOINT3DZ rt_getPoint3dz(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { RTPOINT3DZ result; rt_getPoint3dz_p(ctx, pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: point is a real RTPOINT3DZ *not* a pointer */ RTPOINT3DM rt_getPoint3dm(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { RTPOINT3DM result; rt_getPoint3dm_p(ctx, pa, n, &result); return result; } /* * Copy a point from the point array into the parameter point * will set point's z=NO_Z_VALUE if pa is 2d * * NOTE: this will modify the point3dz pointed to by 'point'. */ int rt_getPoint3dz_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT3DZ *op) { uint8_t *ptr; #if PARANOIA_LEVEL > 0 if ( ! pa ) return 0; if ( (n<0) || (n>=pa->npoints)) { RTDEBUGF(4, "%d out of numpoint range (%d)", n, pa->npoints); return 0; /*error */ } #endif RTDEBUGF(2, "rt_getPoint3dz_p called on array of %d-dimensions / %u pts", RTFLAGS_NDIMS(pa->flags), pa->npoints); /* Get a pointer to nth point offset */ ptr=rt_getPoint_internal(ctx, pa, n); /* * if input RTPOINTARRAY has the Z, it is artays * at third position so make a single copy */ if ( RTFLAGS_GET_Z(pa->flags) ) { memcpy(op, ptr, sizeof(RTPOINT3DZ)); } /* * Otherwise copy the 2d part and initialize * Z to NO_Z_VALUE */ else { memcpy(op, ptr, sizeof(RTPOINT2D)); op->z=NO_Z_VALUE; } return 1; } /* * Copy a point from the point array into the parameter point * will set point's m=NO_Z_VALUE if pa has no M * * NOTE: this will modify the point3dm pointed to by 'point'. */ int rt_getPoint3dm_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT3DM *op) { uint8_t *ptr; int zmflag; #if PARANOIA_LEVEL > 0 if ( ! pa ) return 0; if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "%d out of numpoint range (%d)", n, pa->npoints); return 0; /*error */ } #endif RTDEBUGF(2, "rt_getPoint3dm_p(ctx, %d) called on array of %d-dimensions / %u pts", n, RTFLAGS_NDIMS(pa->flags), pa->npoints); /* Get a pointer to nth point offset and zmflag */ ptr=rt_getPoint_internal(ctx, pa, n); zmflag=RTFLAGS_GET_ZM(pa->flags); /* * if input RTPOINTARRAY has the M and NO Z, * we can issue a single memcpy */ if ( zmflag == 1 ) { memcpy(op, ptr, sizeof(RTPOINT3DM)); return 1; } /* * Otherwise copy the 2d part and * initialize M to NO_M_VALUE */ memcpy(op, ptr, sizeof(RTPOINT2D)); /* * Then, if input has Z skip it and * copy next double, otherwise initialize * M to NO_M_VALUE */ if ( zmflag == 3 ) { ptr+=sizeof(RTPOINT3DZ); memcpy(&(op->m), ptr, sizeof(double)); } else { op->m=NO_M_VALUE; } return 1; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: point is a real RTPOINT2D *not* a pointer */ RTPOINT2D rt_getPoint2d(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { const RTPOINT2D *result; result = rt_getPoint2d_cp(ctx, pa, n); return *result; } /* * Copy a point from the point array into the parameter point * z value (if present) is not returned. * * NOTE: this will modify the point2d pointed to by 'point'. */ int rt_getPoint2d_p(const RTCTX *ctx, const RTPOINTARRAY *pa, int n, RTPOINT2D *point) { #if PARANOIA_LEVEL > 0 if ( ! pa ) return 0; if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "rt_getPoint2d_p: point offset out of range"); return 0; /*error */ } #endif /* this does x,y */ memcpy(point, rt_getPoint_internal(ctx, pa, n), sizeof(RTPOINT2D)); return 1; } /** * Returns a pointer into the RTPOINTARRAY serialized_ptlist, * suitable for reading from. This is very high performance * and declared const because you aren't allowed to muck with the * values, only read them. */ const RTPOINT2D* rt_getPoint2d_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { if ( ! pa ) return 0; if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "rt_getPoint2D_const_p: point offset out of range"); return 0; /*error */ } return (const RTPOINT2D*)rt_getPoint_internal(ctx, pa, n); } const RTPOINT3DZ* rt_getPoint3dz_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { if ( ! pa ) return 0; if ( ! RTFLAGS_GET_Z(pa->flags) ) { rterror(ctx, "rt_getPoint3dz_cp: no Z coordinates in point array"); return 0; /*error */ } if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "rt_getPoint3dz_cp: point offset out of range"); return 0; /*error */ } return (const RTPOINT3DZ*)rt_getPoint_internal(ctx, pa, n); } const RTPOINT4D* rt_getPoint4d_cp(const RTCTX *ctx, const RTPOINTARRAY *pa, int n) { if ( ! pa ) return 0; if ( ! (RTFLAGS_GET_Z(pa->flags) && RTFLAGS_GET_Z(pa->flags)) ) { rterror(ctx, "rt_getPoint3dz_cp: no Z and M coordinates in point array"); return 0; /*error */ } if ( (n<0) || (n>=pa->npoints)) { rterror(ctx, "rt_getPoint3dz_cp: point offset out of range"); return 0; /*error */ } return (const RTPOINT4D*)rt_getPoint_internal(ctx, pa, n); } /* * set point N to the given value * NOTE that the pointarray can be of any * dimension, the appropriate ordinate values * will be extracted from it * */ void ptarray_set_point4d(const RTCTX *ctx, RTPOINTARRAY *pa, int n, const RTPOINT4D *p4d) { uint8_t *ptr; assert(n >= 0 && n < pa->npoints); ptr=rt_getPoint_internal(ctx, pa, n); switch ( RTFLAGS_GET_ZM(pa->flags) ) { case 3: memcpy(ptr, p4d, sizeof(RTPOINT4D)); break; case 2: memcpy(ptr, p4d, sizeof(RTPOINT3DZ)); break; case 1: memcpy(ptr, p4d, sizeof(RTPOINT2D)); ptr+=sizeof(RTPOINT2D); memcpy(ptr, &(p4d->m), sizeof(double)); break; case 0: memcpy(ptr, p4d, sizeof(RTPOINT2D)); break; } } /***************************************************************************** * Basic sub-geometry types *****************************************************************************/ /* handle missaligned uint32_t32 data */ uint32_t rt_get_uint32_t(const RTCTX *ctx, const uint8_t *loc) { uint32_t result; memcpy(&result, loc, sizeof(uint32_t)); return result; } /* handle missaligned signed int32_t data */ int32_t rt_get_int32_t(const RTCTX *ctx, const uint8_t *loc) { int32_t result; memcpy(&result,loc, sizeof(int32_t)); return result; } /************************************************ * debugging routines ************************************************/ void printBOX3D(const RTCTX *ctx, BOX3D *box) { rtnotice(ctx, "BOX3D: %g %g, %g %g", box->xmin, box->ymin, box->xmax, box->ymax); } void printPA(const RTCTX *ctx, RTPOINTARRAY *pa) { int t; RTPOINT4D pt; char *mflag; if ( RTFLAGS_GET_M(pa->flags) ) mflag = "M"; else mflag = ""; rtnotice(ctx, " RTPOINTARRAY%s{", mflag); rtnotice(ctx, " ndims=%i, ptsize=%i", RTFLAGS_NDIMS(pa->flags), ptarray_point_size(ctx, pa)); rtnotice(ctx, " npoints = %i", pa->npoints); for (t =0; tnpoints; t++) { rt_getPoint4d_p(ctx, pa, t, &pt); if (RTFLAGS_NDIMS(pa->flags) == 2) { rtnotice(ctx, " %i : %lf,%lf",t,pt.x,pt.y); } if (RTFLAGS_NDIMS(pa->flags) == 3) { rtnotice(ctx, " %i : %lf,%lf,%lf",t,pt.x,pt.y,pt.z); } if (RTFLAGS_NDIMS(pa->flags) == 4) { rtnotice(ctx, " %i : %lf,%lf,%lf,%lf",t,pt.x,pt.y,pt.z,pt.m); } } rtnotice(ctx, " }"); } /** * Given a string with at least 2 chars in it, convert them to * a byte value. No error checking done! */ uint8_t parse_hex(const RTCTX *ctx, char *str) { /* do this a little brute force to make it faster */ uint8_t result_high = 0; uint8_t result_low = 0; switch (str[0]) { case '0' : result_high = 0; break; case '1' : result_high = 1; break; case '2' : result_high = 2; break; case '3' : result_high = 3; break; case '4' : result_high = 4; break; case '5' : result_high = 5; break; case '6' : result_high = 6; break; case '7' : result_high = 7; break; case '8' : result_high = 8; break; case '9' : result_high = 9; break; case 'A' : case 'a' : result_high = 10; break; case 'B' : case 'b' : result_high = 11; break; case 'C' : case 'c' : result_high = 12; break; case 'D' : case 'd' : result_high = 13; break; case 'E' : case 'e' : result_high = 14; break; case 'F' : case 'f' : result_high = 15; break; } switch (str[1]) { case '0' : result_low = 0; break; case '1' : result_low = 1; break; case '2' : result_low = 2; break; case '3' : result_low = 3; break; case '4' : result_low = 4; break; case '5' : result_low = 5; break; case '6' : result_low = 6; break; case '7' : result_low = 7; break; case '8' : result_low = 8; break; case '9' : result_low = 9; break; case 'A' : case 'a' : result_low = 10; break; case 'B' : case 'b' : result_low = 11; break; case 'C' : case 'c' : result_low = 12; break; case 'D' : case 'd' : result_low = 13; break; case 'E' : case 'e' : result_low = 14; break; case 'F' : case 'f' : result_low = 15; break; } return (uint8_t) ((result_high<<4) + result_low); } /** * Given one byte, populate result with two byte representing * the hex number. * * Ie. deparse_hex(ctx, 255, mystr) * -> mystr[0] = 'F' and mystr[1] = 'F' * * No error checking done */ void deparse_hex(const RTCTX *ctx, uint8_t str, char *result) { int input_high; int input_low; static char outchr[]= { "0123456789ABCDEF" }; input_high = (str>>4); input_low = (str & 0x0F); result[0] = outchr[input_high]; result[1] = outchr[input_low]; } /** * Find interpolation point I * between point A and point B * so that the len(AI) == len(AB)*F * and I falls on AB segment. * * Example: * * F=0.5 : A----I----B * F=1 : A---------B==I * F=0 : A==I---------B * F=.2 : A-I-------B */ void interpolate_point4d(const RTCTX *ctx, RTPOINT4D *A, RTPOINT4D *B, RTPOINT4D *I, double F) { #if PARANOIA_LEVEL > 0 double absF=fabs(F); if ( absF < 0 || absF > 1 ) { rterror(ctx, "interpolate_point4d: invalid F (%g)", F); } #endif I->x=A->x+((B->x-A->x)*F); I->y=A->y+((B->y-A->y)*F); I->z=A->z+((B->z-A->z)*F); I->m=A->m+((B->m-A->m)*F); } int _rtgeom_interrupt_requested = 0; void rtgeom_request_interrupt(const RTCTX *ctx) { _rtgeom_interrupt_requested = 1; } void rtgeom_cancel_interrupt(const RTCTX *ctx) { _rtgeom_interrupt_requested = 0; } rtinterrupt_callback *_rtgeom_interrupt_callback = 0; rtinterrupt_callback * rtgeom_register_interrupt_callback(const RTCTX *ctx, rtinterrupt_callback *cb) { rtinterrupt_callback *old = _rtgeom_interrupt_callback; _rtgeom_interrupt_callback = cb; return old; } src/rtgeom_debug.c000066400000000000000000000114731271715413500144700ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2004 Refractions Research Inc. * **********************************************************************/ #include "rtgeom_log.h" #include "librttopo_geom.h" #include #include /* Place to hold the ZM string used in other summaries */ static char tflags[6]; static char * rtgeom_flagchars(const RTCTX *ctx, RTGEOM *rtg) { int flagno = 0; if ( RTFLAGS_GET_Z(rtg->flags) ) tflags[flagno++] = 'Z'; if ( RTFLAGS_GET_M(rtg->flags) ) tflags[flagno++] = 'M'; if ( RTFLAGS_GET_BBOX(rtg->flags) ) tflags[flagno++] = 'B'; if ( RTFLAGS_GET_GEODETIC(rtg->flags) ) tflags[flagno++] = 'G'; if ( rtg->srid != SRID_UNKNOWN ) tflags[flagno++] = 'S'; tflags[flagno] = '\0'; RTDEBUGF(4, "Flags: %s - returning %p", rtg->flags, tflags); return tflags; } /* * Returns an alloced string containing summary for the RTGEOM object */ static char * rtpoint_summary(const RTCTX *ctx, RTPOINT *point, int offset) { char *result; char *pad=""; char *zmflags = rtgeom_flagchars(ctx, (RTGEOM*)point); result = (char *)rtalloc(ctx, 128+offset); sprintf(result, "%*.s%s[%s]", offset, pad, rttype_name(ctx, point->type), zmflags); return result; } static char * rtline_summary(const RTCTX *ctx, RTLINE *line, int offset) { char *result; char *pad=""; char *zmflags = rtgeom_flagchars(ctx, (RTGEOM*)line); result = (char *)rtalloc(ctx, 128+offset); sprintf(result, "%*.s%s[%s] with %d points", offset, pad, rttype_name(ctx, line->type), zmflags, line->points->npoints); return result; } static char * rtcollection_summary(const RTCTX *ctx, RTCOLLECTION *col, int offset) { size_t size = 128; char *result; char *tmp; int i; static char *nl = "\n"; char *pad=""; char *zmflags = rtgeom_flagchars(ctx, (RTGEOM*)col); RTDEBUG(2, "rtcollection_summary called"); result = (char *)rtalloc(ctx, size); sprintf(result, "%*.s%s[%s] with %d elements\n", offset, pad, rttype_name(ctx, col->type), zmflags, col->ngeoms); for (i=0; ingeoms; i++) { tmp = rtgeom_summary(ctx, col->geoms[i], offset+2); size += strlen(tmp)+1; result = rtrealloc(ctx, result, size); RTDEBUGF(4, "Reallocated %d bytes for result", size); if ( i > 0 ) strcat(result,nl); strcat(result, tmp); rtfree(ctx, tmp); } RTDEBUG(3, "rtcollection_summary returning"); return result; } static char * rtpoly_summary(const RTCTX *ctx, RTPOLY *poly, int offset) { char tmp[256]; size_t size = 64*(poly->nrings+1)+128; char *result; int i; char *pad=""; static char *nl = "\n"; char *zmflags = rtgeom_flagchars(ctx, (RTGEOM*)poly); RTDEBUG(2, "rtpoly_summary called"); result = (char *)rtalloc(ctx, size); sprintf(result, "%*.s%s[%s] with %i rings\n", offset, pad, rttype_name(ctx, poly->type), zmflags, poly->nrings); for (i=0; inrings; i++) { sprintf(tmp,"%s ring %i has %i points", pad, i, poly->rings[i]->npoints); if ( i > 0 ) strcat(result,nl); strcat(result,tmp); } RTDEBUG(3, "rtpoly_summary returning"); return result; } char * rtgeom_summary(const RTCTX *ctx, const RTGEOM *rtgeom, int offset) { char *result; switch (rtgeom->type) { case RTPOINTTYPE: return rtpoint_summary(ctx, (RTPOINT *)rtgeom, offset); case RTCIRCSTRINGTYPE: case RTTRIANGLETYPE: case RTLINETYPE: return rtline_summary(ctx, (RTLINE *)rtgeom, offset); case RTPOLYGONTYPE: return rtpoly_summary(ctx, (RTPOLY *)rtgeom, offset); case RTTINTYPE: case RTMULTISURFACETYPE: case RTMULTICURVETYPE: case RTCURVEPOLYTYPE: case RTCOMPOUNDTYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return rtcollection_summary(ctx, (RTCOLLECTION *)rtgeom, offset); default: result = (char *)rtalloc(ctx, 256); sprintf(result, "Object is of unknown type: %d", rtgeom->type); return result; } return NULL; } src/rtgeom_geos.c000066400000000000000000001347161271715413500143450ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011-2014 Sandro Santilli * **********************************************************************/ #include "rtgeom_geos.h" #include "librttopo_geom.h" #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include RTTIN * rttin_from_geos(const RTCTX *ctx, const GEOSGeometry *geom, int want3d); #undef RTGEOM_PROFILE_BUILDAREA const char * rtgeom_get_last_geos_error(const RTCTX *ctx) { return ctx->rtgeom_geos_errmsg; } static void rtgeom_geos_notice(const char *msg, void *ctx) { rtnotice(ctx, "%s\n", msg); } extern void rtgeom_geos_error(const char *msg, void *ptr) { RTCTX *ctx = (RTCTX *)ptr; /* TODO: write in the context, not in the global ! */ /* Call the supplied function */ if ( RTGEOM_GEOS_ERRMSG_MAXSIZE-1 < snprintf(ctx->rtgeom_geos_errmsg, RTGEOM_GEOS_ERRMSG_MAXSIZE-1, "%s", msg) ) { ctx->rtgeom_geos_errmsg[RTGEOM_GEOS_ERRMSG_MAXSIZE-1] = '\0'; } } void rtgeom_geos_ensure_init(const RTCTX *ctx) { if ( ctx->gctx != NULL ) return; GEOSContextHandle_t h = GEOS_init_r(); ((RTCTX*)ctx)->gctx = h; GEOSContext_setNoticeMessageHandler_r(h, rtgeom_geos_notice, (void*)ctx); GEOSContext_setErrorMessageHandler_r(h, rtgeom_geos_error, (void*)ctx); } /* ** GEOS <==> PostGIS conversion functions ** ** Default conversion creates a GEOS point array, then iterates through the ** PostGIS points, setting each value in the GEOS array one at a time. ** */ /* Return a RTPOINTARRAY from a GEOSCoordSeq */ RTPOINTARRAY * ptarray_from_GEOSCoordSeq(const RTCTX *ctx, const GEOSCoordSequence *cs, char want3d) { uint32_t dims=2; uint32_t size, i; RTPOINTARRAY *pa; RTPOINT4D point; RTDEBUG(2, "ptarray_fromGEOSCoordSeq called"); if ( ! GEOSCoordSeq_getSize_r(ctx->gctx, cs, &size) ) rterror(ctx, "Exception thrown"); RTDEBUGF(4, " GEOSCoordSeq size: %d", size); if ( want3d ) { if ( ! GEOSCoordSeq_getDimensions_r(ctx->gctx, cs, &dims) ) rterror(ctx, "Exception thrown"); RTDEBUGF(4, " GEOSCoordSeq dimensions: %d", dims); /* forget higher dimensions (if any) */ if ( dims > 3 ) dims = 3; } RTDEBUGF(4, " output dimensions: %d", dims); pa = ptarray_construct(ctx, (dims==3), 0, size); for (i=0; igctx, cs, i, &(point.x)); GEOSCoordSeq_getY_r(ctx->gctx, cs, i, &(point.y)); if ( dims >= 3 ) GEOSCoordSeq_getZ_r(ctx->gctx, cs, i, &(point.z)); ptarray_set_point4d(ctx, pa,i,&point); } return pa; } /* Return an RTGEOM from a Geometry */ RTGEOM * GEOS2RTGEOM(const RTCTX *ctx, const GEOSGeometry *geom, char want3d) { int type = GEOSGeomTypeId_r(ctx->gctx, geom) ; int hasZ; int SRID = GEOSGetSRID_r(ctx->gctx, geom); /* GEOS's 0 is equivalent to our unknown as for SRID values */ if ( SRID == 0 ) SRID = SRID_UNKNOWN; if ( want3d ) { hasZ = GEOSHasZ_r(ctx->gctx, geom); if ( ! hasZ ) { RTDEBUG(3, "Geometry has no Z, won't provide one"); want3d = 0; } } /* if ( GEOSisEmpty_r(ctx->gctx, geom) ) { return (RTGEOM*)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, SRID, want3d, 0); } */ switch (type) { const GEOSCoordSequence *cs; RTPOINTARRAY *pa, **ppaa; const GEOSGeometry *g; RTGEOM **geoms; uint32_t i, ngeoms; case GEOS_POINT: RTDEBUG(4, "rtgeom_from_geometry: it's a Point"); cs = GEOSGeom_getCoordSeq_r(ctx->gctx, geom); if ( GEOSisEmpty_r(ctx->gctx, geom) ) return (RTGEOM*)rtpoint_construct_empty(ctx, SRID, want3d, 0); pa = ptarray_from_GEOSCoordSeq(ctx, cs, want3d); return (RTGEOM *)rtpoint_construct(ctx, SRID, NULL, pa); case GEOS_LINESTRING: case GEOS_LINEARRING: RTDEBUG(4, "rtgeom_from_geometry: it's a LineString or LinearRing"); if ( GEOSisEmpty_r(ctx->gctx, geom) ) return (RTGEOM*)rtline_construct_empty(ctx, SRID, want3d, 0); cs = GEOSGeom_getCoordSeq_r(ctx->gctx, geom); pa = ptarray_from_GEOSCoordSeq(ctx, cs, want3d); return (RTGEOM *)rtline_construct(ctx, SRID, NULL, pa); case GEOS_POLYGON: RTDEBUG(4, "rtgeom_from_geometry: it's a Polygon"); if ( GEOSisEmpty_r(ctx->gctx, geom) ) return (RTGEOM*)rtpoly_construct_empty(ctx, SRID, want3d, 0); ngeoms = GEOSGetNumInteriorRings_r(ctx->gctx, geom); ppaa = rtalloc(ctx, sizeof(RTPOINTARRAY *)*(ngeoms+1)); g = GEOSGetExteriorRing_r(ctx->gctx, geom); cs = GEOSGeom_getCoordSeq_r(ctx->gctx, g); ppaa[0] = ptarray_from_GEOSCoordSeq(ctx, cs, want3d); for (i=0; igctx, geom, i); cs = GEOSGeom_getCoordSeq_r(ctx->gctx, g); ppaa[i+1] = ptarray_from_GEOSCoordSeq(ctx, cs, want3d); } return (RTGEOM *)rtpoly_construct(ctx, SRID, NULL, ngeoms+1, ppaa); case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_GEOMETRYCOLLECTION: RTDEBUG(4, "rtgeom_from_geometry: it's a Collection or Multi"); ngeoms = GEOSGetNumGeometries_r(ctx->gctx, geom); geoms = NULL; if ( ngeoms ) { geoms = rtalloc(ctx, sizeof(RTGEOM *)*ngeoms); for (i=0; igctx, geom, i); geoms[i] = GEOS2RTGEOM(ctx, g, want3d); } } return (RTGEOM *)rtcollection_construct(ctx, type, SRID, NULL, ngeoms, geoms); default: rterror(ctx, "GEOS2RTGEOM: unknown geometry type: %d", type); return NULL; } } static GEOSCoordSeq ptarray_to_GEOSCoordSeq(const RTCTX *ctx, const RTPOINTARRAY *pa) { uint32_t dims = 2; uint32_t i; const RTPOINT3DZ *p3d; const RTPOINT2D *p2d; GEOSCoordSeq sq; if ( RTFLAGS_GET_Z(pa->flags) ) dims = 3; if ( ! (sq = GEOSCoordSeq_create_r(ctx->gctx, pa->npoints, dims)) ) rterror(ctx, "Error creating GEOS Coordinate Sequence"); for ( i=0; i < pa->npoints; i++ ) { if ( dims == 3 ) { p3d = rt_getPoint3dz_cp(ctx, pa, i); p2d = (const RTPOINT2D *)p3d; RTDEBUGF(4, "Point: %g,%g,%g", p3d->x, p3d->y, p3d->z); } else { p2d = rt_getPoint2d_cp(ctx, pa, i); RTDEBUGF(4, "Point: %g,%g", p2d->x, p2d->y); } #if RTGEOM_GEOS_VERSION < 33 /* Make sure we don't pass any infinite values down into GEOS */ /* GEOS 3.3+ is supposed to handle this stuff OK */ if ( isinf(p2d->x) || isinf(p2d->y) || (dims == 3 && isinf(p3d->z)) ) rterror(ctx, "Infinite coordinate value found in geometry."); if ( isnan(p2d->x) || isnan(p2d->y) || (dims == 3 && isnan(p3d->z)) ) rterror(ctx, "NaN coordinate value found in geometry."); #endif GEOSCoordSeq_setX_r(ctx->gctx, sq, i, p2d->x); GEOSCoordSeq_setY_r(ctx->gctx, sq, i, p2d->y); if ( dims == 3 ) GEOSCoordSeq_setZ_r(ctx->gctx, sq, i, p3d->z); } return sq; } static GEOSGeometry * ptarray_to_GEOSLinearRing(const RTCTX *ctx, const RTPOINTARRAY *pa, int autofix) { GEOSCoordSeq sq; GEOSGeom g; RTPOINTARRAY *npa = 0; if ( autofix ) { /* check ring for being closed and fix if not */ if ( ! ptarray_is_closed_2d(ctx, pa) ) { npa = ptarray_addPoint(ctx, pa, rt_getPoint_internal(ctx, pa, 0), RTFLAGS_NDIMS(pa->flags), pa->npoints); pa = npa; } /* TODO: check ring for having at least 4 vertices */ #if 0 while ( pa->npoints < 4 ) { npa = ptarray_addPoint(ctx, npa, rt_getPoint_internal(ctx, pa, 0), RTFLAGS_NDIMS(pa->flags), pa->npoints); } #endif } sq = ptarray_to_GEOSCoordSeq(ctx, pa); if ( npa ) ptarray_free(ctx, npa); g = GEOSGeom_createLinearRing_r(ctx->gctx, sq); return g; } GEOSGeometry * GBOX2GEOS(const RTCTX *ctx, const RTGBOX *box) { GEOSGeometry* envelope; GEOSGeometry* ring; GEOSCoordSequence* seq = GEOSCoordSeq_create_r(ctx->gctx, 5, 2); if (!seq) { return NULL; } GEOSCoordSeq_setX_r(ctx->gctx, seq, 0, box->xmin); GEOSCoordSeq_setY_r(ctx->gctx, seq, 0, box->ymin); GEOSCoordSeq_setX_r(ctx->gctx, seq, 1, box->xmax); GEOSCoordSeq_setY_r(ctx->gctx, seq, 1, box->ymin); GEOSCoordSeq_setX_r(ctx->gctx, seq, 2, box->xmax); GEOSCoordSeq_setY_r(ctx->gctx, seq, 2, box->ymax); GEOSCoordSeq_setX_r(ctx->gctx, seq, 3, box->xmin); GEOSCoordSeq_setY_r(ctx->gctx, seq, 3, box->ymax); GEOSCoordSeq_setX_r(ctx->gctx, seq, 4, box->xmin); GEOSCoordSeq_setY_r(ctx->gctx, seq, 4, box->ymin); ring = GEOSGeom_createLinearRing_r(ctx->gctx, seq); if (!ring) { GEOSCoordSeq_destroy_r(ctx->gctx, seq); return NULL; } envelope = GEOSGeom_createPolygon_r(ctx->gctx, ring, NULL, 0); if (!envelope) { GEOSGeom_destroy_r(ctx->gctx, ring); return NULL; } return envelope; } GEOSGeometry * RTGEOM2GEOS(const RTCTX *ctx, const RTGEOM *rtgeom, int autofix) { GEOSCoordSeq sq; GEOSGeom g, shell; GEOSGeom *geoms = NULL; /* RTGEOM *tmp; */ uint32_t ngeoms, i; int geostype; #if RTDEBUG_LEVEL >= 4 char *wkt; #endif RTDEBUGF(4, "RTGEOM2GEOS got a %s", rttype_name(ctx, rtgeom->type)); if (rtgeom_has_arc(ctx, rtgeom)) { RTGEOM *rtgeom_stroked = rtgeom_stroke(ctx, rtgeom, 32); GEOSGeometry *g = RTGEOM2GEOS(ctx, rtgeom_stroked, autofix); rtgeom_free(ctx, rtgeom_stroked); return g; } switch (rtgeom->type) { RTPOINT *rtp = NULL; RTPOLY *rtpoly = NULL; RTLINE *rtl = NULL; RTCOLLECTION *rtc = NULL; #if RTGEOM_GEOS_VERSION < 33 RTPOINTARRAY *pa = NULL; #endif case RTPOINTTYPE: rtp = (RTPOINT *)rtgeom; if ( rtgeom_is_empty(ctx, rtgeom) ) { #if RTGEOM_GEOS_VERSION < 33 pa = ptarray_construct_empty(ctx, rtgeom_has_z(ctx, rtgeom), rtgeom_has_m(ctx, rtgeom), 2); sq = ptarray_to_GEOSCoordSeq(ctx, pa); shell = GEOSGeom_createLinearRing_r(ctx->gctx, sq); g = GEOSGeom_createPolygon_r(ctx->gctx, shell, NULL, 0); #else g = GEOSGeom_createEmptyPolygon_r(ctx->gctx); #endif } else { sq = ptarray_to_GEOSCoordSeq(ctx, rtp->point); g = GEOSGeom_createPoint_r(ctx->gctx, sq); } if ( ! g ) { /* rtnotice(ctx, "Exception in RTGEOM2GEOS"); */ return NULL; } break; case RTLINETYPE: rtl = (RTLINE *)rtgeom; /* TODO: if (autofix) */ if ( rtl->points->npoints == 1 ) { /* Duplicate point, to make geos-friendly */ rtl->points = ptarray_addPoint(ctx, rtl->points, rt_getPoint_internal(ctx, rtl->points, 0), RTFLAGS_NDIMS(rtl->points->flags), rtl->points->npoints); } sq = ptarray_to_GEOSCoordSeq(ctx, rtl->points); g = GEOSGeom_createLineString_r(ctx->gctx, sq); if ( ! g ) { /* rtnotice(ctx, "Exception in RTGEOM2GEOS"); */ return NULL; } break; case RTPOLYGONTYPE: rtpoly = (RTPOLY *)rtgeom; if ( rtgeom_is_empty(ctx, rtgeom) ) { #if RTGEOM_GEOS_VERSION < 33 RTPOINTARRAY *pa = ptarray_construct_empty(ctx, rtgeom_has_z(ctx, rtgeom), rtgeom_has_m(ctx, rtgeom), 2); sq = ptarray_to_GEOSCoordSeq(ctx, pa); shell = GEOSGeom_createLinearRing_r(ctx->gctx, sq); g = GEOSGeom_createPolygon_r(ctx->gctx, shell, NULL, 0); #else g = GEOSGeom_createEmptyPolygon_r(ctx->gctx); #endif } else { shell = ptarray_to_GEOSLinearRing(ctx, rtpoly->rings[0], autofix); if ( ! shell ) return NULL; /*rterror(ctx, "RTGEOM2GEOS: exception during polygon shell conversion"); */ ngeoms = rtpoly->nrings-1; if ( ngeoms > 0 ) geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=1; inrings; ++i) { geoms[i-1] = ptarray_to_GEOSLinearRing(ctx, rtpoly->rings[i], autofix); if ( ! geoms[i-1] ) { --i; while (i) GEOSGeom_destroy_r(ctx->gctx, geoms[--i]); free(geoms); GEOSGeom_destroy_r(ctx->gctx, shell); return NULL; } /*rterror(ctx, "RTGEOM2GEOS: exception during polygon hole conversion"); */ } g = GEOSGeom_createPolygon_r(ctx->gctx, shell, geoms, ngeoms); if (geoms) free(geoms); } if ( ! g ) return NULL; break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: if ( rtgeom->type == RTMULTIPOINTTYPE ) geostype = GEOS_MULTIPOINT; else if ( rtgeom->type == RTMULTILINETYPE ) geostype = GEOS_MULTILINESTRING; else if ( rtgeom->type == RTMULTIPOLYGONTYPE ) geostype = GEOS_MULTIPOLYGON; else geostype = GEOS_GEOMETRYCOLLECTION; rtc = (RTCOLLECTION *)rtgeom; ngeoms = rtc->ngeoms; if ( ngeoms > 0 ) geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=0; igeoms[i], 0); if ( ! g ) { while (i) GEOSGeom_destroy_r(ctx->gctx, geoms[--i]); free(geoms); return NULL; } geoms[i] = g; } g = GEOSGeom_createCollection_r(ctx->gctx, geostype, geoms, ngeoms); if ( geoms ) free(geoms); if ( ! g ) return NULL; break; default: rterror(ctx, "Unknown geometry type: %d - %s", rtgeom->type, rttype_name(ctx, rtgeom->type)); return NULL; } GEOSSetSRID_r(ctx->gctx, g, rtgeom->srid); #if RTDEBUG_LEVEL >= 4 wkt = GEOSGeomToWKT_r(ctx->gctx, g); RTDEBUGF(4, "RTGEOM2GEOS: GEOSGeom: %s", wkt); free(wkt); #endif return g; } const char* rtgeom_geos_version() { const char *ver = GEOSversion(); return ver; } RTGEOM * rtgeom_normalize(const RTCTX *ctx, const RTGEOM *geom1) { RTGEOM *result ; GEOSGeometry *g1; int is3d ; int srid ; srid = (int)(geom1->srid); is3d = RTFLAGS_GET_Z(geom1->flags); rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; } if ( -1 == GEOSNormalize_r(ctx->gctx, g1) ) { rterror(ctx, "Error in GEOSNormalize: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } GEOSSetSRID_r(ctx->gctx, g1, srid); /* needed ? */ result = GEOS2RTGEOM(ctx, g1, is3d); GEOSGeom_destroy_r(ctx->gctx, g1); if (result == NULL) { rterror(ctx, "Error performing intersection: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } return result ; } RTGEOM * rtgeom_intersection(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2) { RTGEOM *result ; GEOSGeometry *g1, *g2, *g3 ; int is3d ; int srid ; /* A.Intersection(Empty) == Empty */ if ( rtgeom_is_empty(ctx, geom2) ) return rtgeom_clone_deep(ctx, geom2); /* Empty.Intersection(A) == Empty */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); RTDEBUG(3, "intersection() START"); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; } g2 = RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { rterror(ctx, "Second argument geometry could not be converted to GEOS."); GEOSGeom_destroy_r(ctx->gctx, g1); return NULL ; } RTDEBUG(3, " constructed geometrys - calling geos"); RTDEBUGF(3, " g1 = %s", GEOSGeomToWKT_r(ctx->gctx, g1)); RTDEBUGF(3, " g2 = %s", GEOSGeomToWKT_r(ctx->gctx, g2)); /*RTDEBUGF(3, "g2 is valid = %i",GEOSisvalid_r(ctx->gctx, g2)); */ /*RTDEBUGF(3, "g1 is valid = %i",GEOSisvalid_r(ctx->gctx, g1)); */ g3 = GEOSIntersection_r(ctx->gctx, g1,g2); RTDEBUG(3, " intersection finished"); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); rterror(ctx, "Error performing intersection: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3) ) ; GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); if (result == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "Error performing intersection: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); return result ; } RTGEOM * rtgeom_linemerge(const RTCTX *ctx, const RTGEOM *geom1) { RTGEOM *result ; GEOSGeometry *g1, *g3 ; int is3d = RTFLAGS_GET_Z(geom1->flags); int srid = geom1->srid; /* Empty.Linemerge() == Empty */ if ( rtgeom_is_empty(ctx, geom1) ) return (RTGEOM*)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, is3d, rtgeom_has_m(ctx, geom1) ); rtgeom_geos_ensure_init(ctx); RTDEBUG(3, "linemerge() START"); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; } RTDEBUG(3, " constructed geometrys - calling geos"); RTDEBUGF(3, " g1 = %s", GEOSGeomToWKT_r(ctx->gctx, g1)); /*RTDEBUGF(3, "g1 is valid = %i",GEOSisvalid_r(ctx->gctx, g1)); */ g3 = GEOSLineMerge_r(ctx->gctx, g1); RTDEBUG(3, " linemerge finished"); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "Error performing linemerge: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3) ) ; GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); if (result == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "Error performing linemerge: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g3); return result ; } RTGEOM * rtgeom_unaryunion(const RTCTX *ctx, const RTGEOM *geom1) { RTGEOM *result ; GEOSGeometry *g1, *g3 ; int is3d = RTFLAGS_GET_Z(geom1->flags); int srid = geom1->srid; /* Empty.UnaryUnion() == Empty */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom1); rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; } g3 = GEOSUnaryUnion_r(ctx->gctx, g1); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "Error performing unaryunion: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3) ) ; GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); if (result == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "Error performing unaryunion: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g3); return result ; } RTGEOM * rtgeom_difference(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2) { GEOSGeometry *g1, *g2, *g3; RTGEOM *result; int is3d; int srid; /* A.Difference(Empty) == A */ if ( rtgeom_is_empty(ctx, geom2) ) return rtgeom_clone_deep(ctx, geom1); /* Empty.Intersection(A) == Empty */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "Second argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g3 = GEOSDifference_r(ctx->gctx, g1,g2); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); rterror(ctx, "GEOSDifference: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3) ) ; GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); if (result == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "Error performing difference: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); /* compressType(result); */ return result; } RTGEOM * rtgeom_symdifference(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2) { GEOSGeometry *g1, *g2, *g3; RTGEOM *result; int is3d; int srid; /* A.SymDifference(Empty) == A */ if ( rtgeom_is_empty(ctx, geom2) ) return rtgeom_clone_deep(ctx, geom1); /* Empty.DymDifference(B) == B */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom2); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { rterror(ctx, "Second argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, g1); return NULL; } g3 = GEOSSymDifference_r(ctx->gctx, g1,g2); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); rterror(ctx, "GEOSSymDifference: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /*never get here */ } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3)); GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); if (result == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "GEOS symdifference_r(ctx->gctx) threw an error (result postgis geometry formation)!"); return NULL ; /*never get here */ } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g3); return result; } RTGEOM* rtgeom_union(const RTCTX *ctx, const RTGEOM *geom1, const RTGEOM *geom2) { int is3d; int srid; GEOSGeometry *g1, *g2, *g3; RTGEOM *result; RTDEBUG(2, "in geomunion"); /* A.Union(empty) == A */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom2); /* B.Union(empty) == B */ if ( rtgeom_is_empty(ctx, geom2) ) return rtgeom_clone_deep(ctx, geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "Second argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "g1=%s", GEOSGeomToWKT_r(ctx->gctx, g1)); RTDEBUGF(3, "g2=%s", GEOSGeomToWKT_r(ctx->gctx, g2)); g3 = GEOSUnion_r(ctx->gctx, g1,g2); RTDEBUGF(3, "g3=%s", GEOSGeomToWKT_r(ctx->gctx, g3)); GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); if (g3 == NULL) { rterror(ctx, "GEOSUnion: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /* never get here */ } GEOSSetSRID_r(ctx->gctx, g3, srid); result = GEOS2RTGEOM(ctx, g3, is3d); GEOSGeom_destroy_r(ctx->gctx, g3); if (result == NULL) { rterror(ctx, "Error performing union: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL; /*never get here */ } return result; } RTGEOM * rtgeom_clip_by_rect(const RTCTX *ctx, const RTGEOM *geom1, double x0, double y0, double x1, double y1) { #if RTGEOM_GEOS_VERSION < 35 rterror(ctx, "The GEOS version this postgis binary " "was compiled against (%d) doesn't support " "'GEOSClipByRect' function _r(ctx->gctx, 3.3.5+ required)", RTGEOM_GEOS_VERSION); return NULL; #else /* RTGEOM_GEOS_VERSION >= 35 */ RTGEOM *result ; GEOSGeometry *g1, *g3 ; int is3d ; /* A.Intersection(Empty) == Empty */ if ( rtgeom_is_empty(ctx, geom1) ) return rtgeom_clone_deep(ctx, geom1); is3d = RTFLAGS_GET_Z(geom1->flags); rtgeom_geos_ensure_init(ctx); RTDEBUG(3, "clip_by_rect() START"); g1 = RTGEOM2GEOS(ctx, geom1, 1); /* auto-fix structure */ if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; } RTDEBUG(3, " constructed geometrys - calling geos"); RTDEBUGF(3, " g1 = %s", GEOSGeomToWKT_r(ctx->gctx, g1)); /*RTDEBUGF(3, "g1 is valid = %i",GEOSisvalid_r(ctx->gctx, g1)); */ g3 = GEOSClipByRect_r(ctx->gctx, g1,x0,y0,x1,y1); GEOSGeom_destroy_r(ctx->gctx, g1); RTDEBUG(3, " clip_by_rect finished"); if (g3 == NULL) { rtnotice(ctx, "Error performing rectangular clipping: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3) ) ; result = GEOS2RTGEOM(ctx, g3, is3d); GEOSGeom_destroy_r(ctx->gctx, g3); if (result == NULL) { rterror(ctx, "Error performing intersection: GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL ; /* never get here */ } result->srid = geom1->srid; return result ; #endif /* RTGEOM_GEOS_VERSION >= 35 */ } /* ------------ BuildArea stuff ---------------------------------------------------------------------{ */ typedef struct Face_t { const GEOSGeometry* geom; GEOSGeometry* env; double envarea; struct Face_t* parent; /* if this face is an hole of another one, or NULL */ } Face; static Face* newFace(const RTCTX *ctx, const GEOSGeometry* g); static void delFace(const RTCTX *ctx, Face* f); static unsigned int countParens(const RTCTX *ctx, const Face* f); static void findFaceHoles(const RTCTX *ctx, Face** faces, int nfaces); static Face* newFace(const RTCTX *ctx, const GEOSGeometry* g) { Face* f = rtalloc(ctx, sizeof(Face)); f->geom = g; f->env = GEOSEnvelope_r(ctx->gctx, f->geom); GEOSArea_r(ctx->gctx, f->env, &f->envarea); f->parent = NULL; /* rtnotice(ctx, "Built Face with area %g and %d holes", f->envarea, GEOSGetNumInteriorRings_r(ctx->gctx, f->geom)); */ return f; } static unsigned int countParens(const RTCTX *ctx, const Face* f) { unsigned int pcount = 0; while ( f->parent ) { ++pcount; f = f->parent; } return pcount; } /* Destroy the face and release memory associated with it */ static void delFace(const RTCTX *ctx, Face* f) { GEOSGeom_destroy_r(ctx->gctx, f->env); rtfree(ctx, f); } static int compare_by_envarea(const void* g1, const void* g2) { Face* f1 = *(Face**)g1; Face* f2 = *(Face**)g2; double n1 = f1->envarea; double n2 = f2->envarea; if ( n1 < n2 ) return 1; if ( n1 > n2 ) return -1; return 0; } /* Find holes of each face */ static void findFaceHoles(const RTCTX *ctx, Face** faces, int nfaces) { int i, j, h; /* We sort by envelope area so that we know holes are only * after their shells */ qsort(faces, nfaces, sizeof(Face*), compare_by_envarea); for (i=0; igctx, f->geom); RTDEBUGF(2, "Scanning face %d with env area %g and %d holes", i, f->envarea, nholes); for (h=0; hgctx, f->geom, h); RTDEBUGF(2, "Looking for hole %d/%d of face %d among %d other faces", h+1, nholes, i, nfaces-i-1); for (j=i+1; jparent ) continue; /* hole already assigned */ f2er = GEOSGetExteriorRing_r(ctx->gctx, f2->geom); /* TODO: can be optimized as the ring would have the * same vertices, possibly in different order. * maybe comparing number of points could already be * useful. */ if ( GEOSEquals_r(ctx->gctx, f2er, hole) ) { RTDEBUGF(2, "Hole %d/%d of face %d is face %d", h+1, nholes, i, j); f2->parent = f; break; } } } } } static GEOSGeometry* collectFacesWithEvenAncestors(const RTCTX *ctx, Face** faces, int nfaces) { GEOSGeometry **geoms = rtalloc(ctx, sizeof(GEOSGeometry*)*nfaces); GEOSGeometry *ret; unsigned int ngeoms = 0; int i; for (i=0; igctx, f->geom); } ret = GEOSGeom_createCollection_r(ctx->gctx, GEOS_MULTIPOLYGON, geoms, ngeoms); rtfree(ctx, geoms); return ret; } GEOSGeometry* RTGEOM_GEOS_buildArea(const RTCTX *ctx, const GEOSGeometry* geom_in) { GEOSGeometry *tmp; GEOSGeometry *geos_result, *shp; GEOSGeometry const *vgeoms[1]; uint32_t i, ngeoms; int srid = GEOSGetSRID_r(ctx->gctx, geom_in); Face ** geoms; vgeoms[0] = geom_in; #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Polygonizing"); #endif geos_result = GEOSPolygonize_r(ctx->gctx, vgeoms, 1); RTDEBUGF(3, "GEOSpolygonize returned @ %p", geos_result); /* Null return from GEOSpolygonize _r(ctx->gctx, an exception) */ if ( ! geos_result ) return 0; /* * We should now have a collection */ #if PARANOIA_LEVEL > 0 if ( GEOSGeometryTypeId_r(ctx->gctx, geos_result) != RTCOLLECTIONTYPE ) { GEOSGeom_destroy_r(ctx->gctx, geos_result); rterror(ctx, "Unexpected return from GEOSpolygonize"); return 0; } #endif ngeoms = GEOSGetNumGeometries_r(ctx->gctx, geos_result); #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Num geometries from polygonizer: %d", ngeoms); #endif RTDEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms); RTDEBUGF(3, "GEOSpolygonize: polygonized:%s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, geos_result, 0))); /* * No geometries in collection, early out */ if ( ngeoms == 0 ) { GEOSSetSRID_r(ctx->gctx, geos_result, srid); return geos_result; } /* * Return first geometry if we only have one in collection, * to avoid the unnecessary Geometry clone below. */ if ( ngeoms == 1 ) { tmp = (GEOSGeometry *)GEOSGetGeometryN_r(ctx->gctx, geos_result, 0); if ( ! tmp ) { GEOSGeom_destroy_r(ctx->gctx, geos_result); return 0; /* exception */ } shp = GEOSGeom_clone_r(ctx->gctx, tmp); GEOSGeom_destroy_r(ctx->gctx, geos_result); /* only safe after the clone above */ GEOSSetSRID_r(ctx->gctx, shp, srid); return shp; } RTDEBUGF(2, "Polygonize returned %d geoms", ngeoms); /* * Polygonizer returns a polygon for each face in the built topology. * * This means that for any face with holes we'll have other faces * representing each hole. We can imagine a parent-child relationship * between these faces. * * In order to maximize the number of visible rings in output we * only use those faces which have an even number of parents. * * Example: * * +---------------+ * | L0 | L0 has no parents * | +---------+ | * | | L1 | | L1 is an hole of L0 * | | +---+ | | * | | |L2 | | | L2 is an hole of L1 (which is an hole of L0) * | | | | | | * | | +---+ | | * | +---------+ | * | | * +---------------+ * * See http://trac.osgeo.org/postgis/ticket/1806 * */ #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Preparing face structures"); #endif /* Prepare face structures for later analysis */ geoms = rtalloc(ctx, sizeof(Face**)*ngeoms); for (i=0; igctx, geos_result, i)); #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Finding face holes"); #endif /* Find faces representing other faces holes */ findFaceHoles(ctx, geoms, ngeoms); #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Colletting even ancestor faces"); #endif /* Build a MultiPolygon composed only by faces with an * even number of ancestors */ tmp = collectFacesWithEvenAncestors(ctx, geoms, ngeoms); #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Cleaning up"); #endif /* Cleanup face structures */ for (i=0; igctx, geos_result); #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Self-unioning"); #endif /* Run a single overlay operation to dissolve shared edges */ shp = GEOSUnionCascaded_r(ctx->gctx, tmp); if ( ! shp ) { GEOSGeom_destroy_r(ctx->gctx, tmp); return 0; /* exception */ } #ifdef RTGEOM_PROFILE_BUILDAREA rtnotice(ctx, "Final cleanup"); #endif GEOSGeom_destroy_r(ctx->gctx, tmp); GEOSSetSRID_r(ctx->gctx, shp, srid); return shp; } RTGEOM* rtgeom_buildarea(const RTCTX *ctx, const RTGEOM *geom) { GEOSGeometry* geos_in; GEOSGeometry* geos_out; RTGEOM* geom_out; int SRID = (int)(geom->srid); int is3d = RTFLAGS_GET_Z(geom->flags); /* Can't build an area from an empty! */ if ( rtgeom_is_empty(ctx, geom) ) { return (RTGEOM*)rtpoly_construct_empty(ctx, SRID, is3d, 0); } RTDEBUG(3, "buildarea called"); RTDEBUGF(3, "ST_BuildArea got geom @ %p", geom); rtgeom_geos_ensure_init(ctx); geos_in = RTGEOM2GEOS(ctx, geom, 0); if ( 0 == geos_in ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } geos_out = RTGEOM_GEOS_buildArea(ctx, geos_in); GEOSGeom_destroy_r(ctx->gctx, geos_in); if ( ! geos_out ) /* exception thrown.. */ { rterror(ctx, "RTGEOM_GEOS_buildArea: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* If no geometries are in result collection, return NULL */ if ( GEOSGetNumGeometries_r(ctx->gctx, geos_out) == 0 ) { GEOSGeom_destroy_r(ctx->gctx, geos_out); return NULL; } geom_out = GEOS2RTGEOM(ctx, geos_out, is3d); GEOSGeom_destroy_r(ctx->gctx, geos_out); #if PARANOIA_LEVEL > 0 if ( geom_out == NULL ) { rterror(ctx, "serialization error"); return NULL; } #endif return geom_out; } int rtgeom_is_simple(const RTCTX *ctx, const RTGEOM *geom) { GEOSGeometry* geos_in; int simple; /* Empty is artays simple */ if ( rtgeom_is_empty(ctx, geom) ) { return 1; } rtgeom_geos_ensure_init(ctx); geos_in = RTGEOM2GEOS(ctx, geom, 0); if ( 0 == geos_in ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return -1; } simple = GEOSisSimple_r(ctx->gctx, geos_in); GEOSGeom_destroy_r(ctx->gctx, geos_in); if ( simple == 2 ) /* exception thrown */ { rterror(ctx, "rtgeom_is_simple: %s", rtgeom_get_last_geos_error(ctx)); return -1; } return simple ? 1 : 0; } /* ------------ end of BuildArea stuff ---------------------------------------------------------------------} */ RTGEOM* rtgeom_geos_noop(const RTCTX *ctx, const RTGEOM* geom_in) { GEOSGeometry *geosgeom; RTGEOM* geom_out; int is3d = RTFLAGS_GET_Z(geom_in->flags); rtgeom_geos_ensure_init(ctx); geosgeom = RTGEOM2GEOS(ctx, geom_in, 0); if ( ! geosgeom ) { rterror(ctx, "Geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } geom_out = GEOS2RTGEOM(ctx, geosgeom, is3d); GEOSGeom_destroy_r(ctx->gctx, geosgeom); if ( ! geom_out ) { rterror(ctx, "GEOS Geometry could not be converted to RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); } return geom_out; } RTGEOM* rtgeom_snap(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2, double tolerance) { #if RTGEOM_GEOS_VERSION < 33 rterror(ctx, "The GEOS version this rtgeom library " "was compiled against (%d) doesn't support " "'Snap' function (3.3.0+ required)", RTGEOM_GEOS_VERSION); return NULL; #else /* RTGEOM_GEOS_VERSION >= 33 */ int srid, is3d; GEOSGeometry *g1, *g2, *g3; RTGEOM* out; srid = geom1->srid; error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); g1 = (GEOSGeometry *)RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = (GEOSGeometry *)RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { rterror(ctx, "Second argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, g1); return NULL; } g3 = GEOSSnap_r(ctx->gctx, g1, g2, tolerance); if (g3 == NULL) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); rterror(ctx, "GEOSSnap: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSSetSRID_r(ctx->gctx, g3, srid); out = GEOS2RTGEOM(ctx, g3, is3d); if (out == NULL) { GEOSGeom_destroy_r(ctx->gctx, g3); rterror(ctx, "GEOSSnap_r(ctx->gctx) threw an error (result RTGEOM geometry formation)!"); return NULL; } GEOSGeom_destroy_r(ctx->gctx, g3); return out; #endif /* RTGEOM_GEOS_VERSION >= 33 */ } RTGEOM* rtgeom_sharedpaths(const RTCTX *ctx, const RTGEOM* geom1, const RTGEOM* geom2) { #if RTGEOM_GEOS_VERSION < 33 rterror(ctx, "The GEOS version this postgis binary " "was compiled against (%d) doesn't support " "'SharedPaths' function (3.3.0+ required)", RTGEOM_GEOS_VERSION); return NULL; #else /* RTGEOM_GEOS_VERSION >= 33 */ GEOSGeometry *g1, *g2, *g3; RTGEOM *out; int is3d, srid; srid = geom1->srid; error_if_srid_mismatch(ctx, srid, (int)(geom2->srid)); is3d = (RTFLAGS_GET_Z(geom1->flags) || RTFLAGS_GET_Z(geom2->flags)) ; rtgeom_geos_ensure_init(ctx); g1 = (GEOSGeometry *)RTGEOM2GEOS(ctx, geom1, 0); if ( 0 == g1 ) /* exception thrown at construction */ { rterror(ctx, "First argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = (GEOSGeometry *)RTGEOM2GEOS(ctx, geom2, 0); if ( 0 == g2 ) /* exception thrown at construction */ { rterror(ctx, "Second argument geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, g1); return NULL; } g3 = GEOSSharedPaths_r(ctx->gctx, g1,g2); GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); if (g3 == NULL) { rterror(ctx, "GEOSSharedPaths: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSSetSRID_r(ctx->gctx, g3, srid); out = GEOS2RTGEOM(ctx, g3, is3d); GEOSGeom_destroy_r(ctx->gctx, g3); if (out == NULL) { rterror(ctx, "GEOS2RTGEOM threw an error"); return NULL; } return out; #endif /* RTGEOM_GEOS_VERSION >= 33 */ } RTGEOM* rtgeom_offsetcurve(const RTCTX *ctx, const RTLINE *rtline, double size, int quadsegs, int joinStyle, double mitreLimit) { #if RTGEOM_GEOS_VERSION < 32 rterror(ctx, "rtgeom_offsetcurve: GEOS 3.2 or higher required"); #else GEOSGeometry *g1, *g3; RTGEOM *rtgeom_result; RTGEOM *rtgeom_in = rtline_as_rtgeom(ctx, rtline); rtgeom_geos_ensure_init(ctx); g1 = (GEOSGeometry *)RTGEOM2GEOS(ctx, rtgeom_in, 0); if ( ! g1 ) { rterror(ctx, "rtgeom_offsetcurve: Geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } #if RTGEOM_GEOS_VERSION < 33 /* Size is artays positive for GEOSSingleSidedBuffer, and a flag determines left/right */ g3 = GEOSSingleSidedBuffer_r(ctx->gctx, g1, size < 0 ? -size : size, quadsegs, joinStyle, mitreLimit, size < 0 ? 0 : 1); #else g3 = GEOSOffsetCurve_r(ctx->gctx, g1, size, quadsegs, joinStyle, mitreLimit); #endif /* Don't need input geometry anymore */ GEOSGeom_destroy_r(ctx->gctx, g1); if (g3 == NULL) { rterror(ctx, "GEOSOffsetCurve: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3)); GEOSSetSRID_r(ctx->gctx, g3, rtgeom_get_srid(ctx, rtgeom_in)); rtgeom_result = GEOS2RTGEOM(ctx, g3, rtgeom_has_z(ctx, rtgeom_in)); GEOSGeom_destroy_r(ctx->gctx, g3); if (rtgeom_result == NULL) { rterror(ctx, "rtgeom_offsetcurve: GEOS2RTGEOM returned null"); return NULL; } return rtgeom_result; #endif /* RTGEOM_GEOS_VERSION < 32 */ } RTTIN * rttin_from_geos(const RTCTX *ctx, const GEOSGeometry *geom, int want3d) { int type = GEOSGeomTypeId_r(ctx->gctx, geom); int hasZ; int SRID = GEOSGetSRID_r(ctx->gctx, geom); /* GEOS's 0 is equivalent to our unknown as for SRID values */ if ( SRID == 0 ) SRID = SRID_UNKNOWN; if ( want3d ) { hasZ = GEOSHasZ_r(ctx->gctx, geom); if ( ! hasZ ) { RTDEBUG(3, "Geometry has no Z, won't provide one"); want3d = 0; } } switch (type) { RTTRIANGLE **geoms; uint32_t i, ngeoms; case GEOS_GEOMETRYCOLLECTION: RTDEBUG(4, "rtgeom_from_geometry: it's a Collection or Multi"); ngeoms = GEOSGetNumGeometries_r(ctx->gctx, geom); geoms = NULL; if ( ngeoms ) { geoms = rtalloc(ctx, ngeoms * sizeof *geoms); if (!geoms) { rterror(ctx, "rttin_from_geos: can't allocate geoms"); return NULL; } for (i=0; igctx, geom, i); ring = GEOSGetExteriorRing_r(ctx->gctx, poly); cs = GEOSGeom_getCoordSeq_r(ctx->gctx, ring); pa = ptarray_from_GEOSCoordSeq(ctx, cs, want3d); geoms[i] = rttriangle_construct(ctx, SRID, NULL, pa); } } return (RTTIN *)rtcollection_construct(ctx, RTTINTYPE, SRID, NULL, ngeoms, (RTGEOM **)geoms); case GEOS_POLYGON: case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_LINESTRING: case GEOS_LINEARRING: case GEOS_POINT: rterror(ctx, "rttin_from_geos: invalid geometry type for tin: %d", type); break; default: rterror(ctx, "GEOS2RTGEOM: unknown geometry type: %d", type); return NULL; } /* shouldn't get here */ return NULL; } /* * output = 1 for edges, 2 for TIN, 0 for polygons */ RTGEOM* rtgeom_delaunay_triangulation(const RTCTX *ctx, const RTGEOM *rtgeom_in, double tolerance, int output) { #if RTGEOM_GEOS_VERSION < 34 rterror(ctx, "rtgeom_delaunay_triangulation: GEOS 3.4 or higher required"); return NULL; #else GEOSGeometry *g1, *g3; RTGEOM *rtgeom_result; if (output < 0 || output > 2) { rterror(ctx, "rtgeom_delaunay_triangulation: invalid output type specified %d", output); return NULL; } rtgeom_geos_ensure_init(ctx); g1 = (GEOSGeometry *)RTGEOM2GEOS(ctx, rtgeom_in, 0); if ( ! g1 ) { rterror(ctx, "rtgeom_delaunay_triangulation: Geometry could not be converted to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* if output != 1 we want polys */ g3 = GEOSDelaunayTriangulation_r(ctx->gctx, g1, tolerance, output == 1); /* Don't need input geometry anymore */ GEOSGeom_destroy_r(ctx->gctx, g1); if (g3 == NULL) { rterror(ctx, "GEOSDelaunayTriangulation: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* RTDEBUGF(3, "result: %s", GEOSGeomToWKT_r(ctx->gctx, g3)); */ GEOSSetSRID_r(ctx->gctx, g3, rtgeom_get_srid(ctx, rtgeom_in)); if (output == 2) { rtgeom_result = (RTGEOM *)rttin_from_geos(ctx, g3, rtgeom_has_z(ctx, rtgeom_in)); } else { rtgeom_result = GEOS2RTGEOM(ctx, g3, rtgeom_has_z(ctx, rtgeom_in)); } GEOSGeom_destroy_r(ctx->gctx, g3); if (rtgeom_result == NULL) { if (output != 2) { rterror(ctx, "rtgeom_delaunay_triangulation: GEOS2RTGEOM returned null"); } else { rterror(ctx, "rtgeom_delaunay_triangulation: rttin_from_geos returned null"); } return NULL; } return rtgeom_result; #endif /* RTGEOM_GEOS_VERSION < 34 */ } src/rtgeom_geos.h000066400000000000000000000032451271715413500143420ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * **********************************************************************/ #include "librttopo_geom.h" /* ** Public prototypes for GEOS utility functions. */ RTGEOM *GEOS2RTGEOM(const RTCTX *ctx, const GEOSGeometry *geom, char want3d); GEOSGeometry * RTGEOM2GEOS(const RTCTX *ctx, const RTGEOM *g, int autofix); GEOSGeometry * GBOX2GEOS(const RTCTX *ctx, const RTGBOX *g); GEOSGeometry * RTGEOM_GEOS_buildArea(const RTCTX *ctx, const GEOSGeometry* geom_in); RTPOINTARRAY *ptarray_from_GEOSCoordSeq(const RTCTX *ctx, const GEOSCoordSequence *cs, char want3d); /* Return (read-only) last geos error message */ const char *rtgeom_get_last_geos_error(const RTCTX *ctx); extern void rtgeom_geos_error(const char *msg, void *ctx); extern void rtgeom_geos_ensure_init(const RTCTX *ctx); src/rtgeom_geos_clean.c000066400000000000000000000702411271715413500154770ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2009-2010 Sandro Santilli * **********************************************************************/ #include "librttopo_geom.h" #include "rtgeom_geos.h" #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include #include #include /* #define RTGEOM_DEBUG_LEVEL 4 */ /* #define PARANOIA_LEVEL 2 */ #undef RTGEOM_PROFILE_MAKEVALID /* * Return Nth vertex in GEOSGeometry as a POINT. * May return NULL if the geometry has NO vertexex. */ static GEOSGeometry* RTGEOM_GEOS_getPointN(const RTCTX *ctx, const GEOSGeometry* g_in, uint32_t n) { uint32_t dims; const GEOSCoordSequence* seq_in; GEOSCoordSeq seq_out; double val; uint32_t sz; int gn; GEOSGeometry* ret; switch ( GEOSGeomTypeId_r(ctx->gctx, g_in) ) { case GEOS_MULTIPOINT: case GEOS_MULTILINESTRING: case GEOS_MULTIPOLYGON: case GEOS_GEOMETRYCOLLECTION: { for (gn=0; gngctx, g_in); ++gn) { const GEOSGeometry* g = GEOSGetGeometryN_r(ctx->gctx, g_in, gn); ret = RTGEOM_GEOS_getPointN(ctx, g,n); if ( ret ) return ret; } break; } case GEOS_POLYGON: { ret = RTGEOM_GEOS_getPointN(ctx, GEOSGetExteriorRing_r(ctx->gctx, g_in), n); if ( ret ) return ret; for (gn=0; gngctx, g_in); ++gn) { const GEOSGeometry* g = GEOSGetInteriorRingN_r(ctx->gctx, g_in, gn); ret = RTGEOM_GEOS_getPointN(ctx, g, n); if ( ret ) return ret; } break; } case GEOS_POINT: case GEOS_LINESTRING: case GEOS_LINEARRING: break; } seq_in = GEOSGeom_getCoordSeq_r(ctx->gctx, g_in); if ( ! seq_in ) return NULL; if ( ! GEOSCoordSeq_getSize_r(ctx->gctx, seq_in, &sz) ) return NULL; if ( ! sz ) return NULL; if ( ! GEOSCoordSeq_getDimensions_r(ctx->gctx, seq_in, &dims) ) return NULL; seq_out = GEOSCoordSeq_create_r(ctx->gctx, 1, dims); if ( ! seq_out ) return NULL; if ( ! GEOSCoordSeq_getX_r(ctx->gctx, seq_in, n, &val) ) return NULL; if ( ! GEOSCoordSeq_setX_r(ctx->gctx, seq_out, n, val) ) return NULL; if ( ! GEOSCoordSeq_getY_r(ctx->gctx, seq_in, n, &val) ) return NULL; if ( ! GEOSCoordSeq_setY_r(ctx->gctx, seq_out, n, val) ) return NULL; if ( dims > 2 ) { if ( ! GEOSCoordSeq_getZ_r(ctx->gctx, seq_in, n, &val) ) return NULL; if ( ! GEOSCoordSeq_setZ_r(ctx->gctx, seq_out, n, val) ) return NULL; } return GEOSGeom_createPoint_r(ctx->gctx, seq_out); } RTGEOM * rtcollection_make_geos_friendly(const RTCTX *ctx, RTCOLLECTION *g); RTGEOM * rtline_make_geos_friendly(const RTCTX *ctx, RTLINE *line); RTGEOM * rtpoly_make_geos_friendly(const RTCTX *ctx, RTPOLY *poly); RTPOINTARRAY* ring_make_geos_friendly(const RTCTX *ctx, RTPOINTARRAY* ring); /* * Ensure the geometry is "structurally" valid * (enough for GEOS to accept it) * May return the input untouched (if already valid). * May return geometries of lower dimension (on collapses) */ static RTGEOM * rtgeom_make_geos_friendly(const RTCTX *ctx, RTGEOM *geom) { RTDEBUGF(2, "rtgeom_make_geos_friendly enter (type %d)", geom->type); switch (geom->type) { case RTPOINTTYPE: case RTMULTIPOINTTYPE: /* a point is artays valid */ return geom; break; case RTLINETYPE: /* lines need at least 2 points */ return rtline_make_geos_friendly(ctx, (RTLINE *)geom); break; case RTPOLYGONTYPE: /* polygons need all rings closed and with npoints > 3 */ return rtpoly_make_geos_friendly(ctx, (RTPOLY *)geom); break; case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOLLECTIONTYPE: return rtcollection_make_geos_friendly(ctx, (RTCOLLECTION *)geom); break; case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTISURFACETYPE: case RTMULTICURVETYPE: default: rterror(ctx, "rtgeom_make_geos_friendly: unsupported input geometry type: %s (%d)", rttype_name(ctx, geom->type), geom->type); break; } return 0; } /* * Close the point array, if not already closed in 2d. * Returns the input if already closed in 2d, or a newly * constructed RTPOINTARRAY. * TODO: move in ptarray.c */ RTPOINTARRAY* ptarray_close2d(const RTCTX *ctx, RTPOINTARRAY* ring); RTPOINTARRAY* ptarray_close2d(const RTCTX *ctx, RTPOINTARRAY* ring) { RTPOINTARRAY* newring; /* close the ring if not already closed (2d only) */ if ( ! ptarray_is_closed_2d(ctx, ring) ) { /* close it up */ newring = ptarray_addPoint(ctx, ring, rt_getPoint_internal(ctx, ring, 0), RTFLAGS_NDIMS(ring->flags), ring->npoints); ring = newring; } return ring; } /* May return the same input or a new one (never zero) */ RTPOINTARRAY* ring_make_geos_friendly(const RTCTX *ctx, RTPOINTARRAY* ring) { RTPOINTARRAY* closedring; RTPOINTARRAY* ring_in = ring; /* close the ring if not already closed (2d only) */ closedring = ptarray_close2d(ctx, ring); if (closedring != ring ) { ring = closedring; } /* return 0 for collapsed ring (after closeup) */ while ( ring->npoints < 4 ) { RTPOINTARRAY *oring = ring; RTDEBUGF(4, "ring has %d points, adding another", ring->npoints); /* let's add another... */ ring = ptarray_addPoint(ctx, ring, rt_getPoint_internal(ctx, ring, 0), RTFLAGS_NDIMS(ring->flags), ring->npoints); if ( oring != ring_in ) ptarray_free(ctx, oring); } return ring; } /* Make sure all rings are closed and have > 3 points. * May return the input untouched. */ RTGEOM * rtpoly_make_geos_friendly(const RTCTX *ctx, RTPOLY *poly) { RTGEOM* ret; RTPOINTARRAY **new_rings; int i; /* If the polygon has no rings there's nothing to do */ if ( ! poly->nrings ) return (RTGEOM*)poly; /* Allocate enough pointers for all rings */ new_rings = rtalloc(ctx, sizeof(RTPOINTARRAY*)*poly->nrings); /* All rings must be closed and have > 3 points */ for (i=0; inrings; i++) { RTPOINTARRAY* ring_in = poly->rings[i]; RTPOINTARRAY* ring_out = ring_make_geos_friendly(ctx, ring_in); if ( ring_in != ring_out ) { RTDEBUGF(3, "rtpoly_make_geos_friendly: ring %d cleaned, now has %d points", i, ring_out->npoints); ptarray_free(ctx, ring_in); } else { RTDEBUGF(3, "rtpoly_make_geos_friendly: ring %d untouched", i); } assert ( ring_out ); new_rings[i] = ring_out; } rtfree(ctx, poly->rings); poly->rings = new_rings; ret = (RTGEOM*)poly; return ret; } /* Need NO or >1 points. Duplicate first if only one. */ RTGEOM * rtline_make_geos_friendly(const RTCTX *ctx, RTLINE *line) { RTGEOM *ret; if (line->points->npoints == 1) /* 0 is fine, 2 is fine */ { #if 1 /* Duplicate point */ line->points = ptarray_addPoint(ctx, line->points, rt_getPoint_internal(ctx, line->points, 0), RTFLAGS_NDIMS(line->points->flags), line->points->npoints); ret = (RTGEOM*)line; #else /* Turn into a point */ ret = (RTGEOM*)rtpoint_construct(ctx, line->srid, 0, line->points); #endif return ret; } else { return (RTGEOM*)line; /* return rtline_clone(ctx, line); */ } } RTGEOM * rtcollection_make_geos_friendly(const RTCTX *ctx, RTCOLLECTION *g) { RTGEOM **new_geoms; uint32_t i, new_ngeoms=0; RTCOLLECTION *ret; /* enough space for all components */ new_geoms = rtalloc(ctx, sizeof(RTGEOM *)*g->ngeoms); ret = rtalloc(ctx, sizeof(RTCOLLECTION)); memcpy(ret, g, sizeof(RTCOLLECTION)); ret->maxgeoms = g->ngeoms; for (i=0; ingeoms; i++) { RTGEOM* newg = rtgeom_make_geos_friendly(ctx, g->geoms[i]); if ( newg ) new_geoms[new_ngeoms++] = newg; } ret->bbox = NULL; /* recompute later... */ ret->ngeoms = new_ngeoms; if ( new_ngeoms ) { ret->geoms = new_geoms; } else { free(new_geoms); ret->geoms = NULL; ret->maxgeoms = 0; } return (RTGEOM*)ret; } /* * Fully node given linework */ static GEOSGeometry* RTGEOM_GEOS_nodeLines(const RTCTX *ctx, const GEOSGeometry* lines) { GEOSGeometry* noded; GEOSGeometry* point; /* * Union with first geometry point, obtaining full noding * and dissolving of duplicated repeated points * * TODO: substitute this with UnaryUnion? */ point = RTGEOM_GEOS_getPointN(ctx, lines, 0); if ( ! point ) return NULL; RTDEBUGF(3, "Boundary point: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, point, 0))); noded = GEOSUnion_r(ctx->gctx, lines, point); if ( NULL == noded ) { GEOSGeom_destroy_r(ctx->gctx, point); return NULL; } GEOSGeom_destroy_r(ctx->gctx, point); RTDEBUGF(3, "RTGEOM_GEOS_nodeLines: in[%s] out[%s]", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, lines, 0)), rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, noded, 0))); return noded; } #if RTGEOM_GEOS_VERSION >= 33 /* * We expect rtgeom_geos_ensure_init(ctx); * Will return NULL on error (expect error handler being called by then) * */ static GEOSGeometry* RTGEOM_GEOS_makeValidPolygon(const RTCTX *ctx, const GEOSGeometry* gin) { GEOSGeom gout; GEOSGeom geos_bound; GEOSGeom geos_cut_edges, geos_area, collapse_points; GEOSGeometry *vgeoms[3]; /* One for area, one for cut-edges */ unsigned int nvgeoms=0; assert (GEOSGeomTypeId_r(ctx->gctx, gin) == GEOS_POLYGON || GEOSGeomTypeId_r(ctx->gctx, gin) == GEOS_MULTIPOLYGON); geos_bound = GEOSBoundary_r(ctx->gctx, gin); if ( NULL == geos_bound ) { return NULL; } RTDEBUGF(3, "Boundaries: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, geos_bound, 0))); /* Use noded boundaries as initial "cut" edges */ #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: noding lines"); #endif geos_cut_edges = RTGEOM_GEOS_nodeLines(ctx, geos_bound); if ( NULL == geos_cut_edges ) { GEOSGeom_destroy_r(ctx->gctx, geos_bound); rtnotice(ctx, "RTGEOM_GEOS_nodeLines(ctx): %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* NOTE: the noding process may drop lines collapsing to points. * We want to retrive any of those */ { GEOSGeometry* pi; GEOSGeometry* po; #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: extracting unique points from bounds"); #endif pi = GEOSGeom_extractUniquePoints_r(ctx->gctx, geos_bound); if ( NULL == pi ) { GEOSGeom_destroy_r(ctx->gctx, geos_bound); rtnotice(ctx, "GEOSGeom_extractUniquePoints_r(ctx->gctx): %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "Boundaries input points %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, pi, 0))); #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: extracting unique points from cut_edges"); #endif po = GEOSGeom_extractUniquePoints_r(ctx->gctx, geos_cut_edges); if ( NULL == po ) { GEOSGeom_destroy_r(ctx->gctx, geos_bound); GEOSGeom_destroy_r(ctx->gctx, pi); rtnotice(ctx, "GEOSGeom_extractUniquePoints_r(ctx->gctx): %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "Boundaries output points %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, po, 0))); #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: find collapse points"); #endif collapse_points = GEOSDifference_r(ctx->gctx, pi, po); if ( NULL == collapse_points ) { GEOSGeom_destroy_r(ctx->gctx, geos_bound); GEOSGeom_destroy_r(ctx->gctx, pi); GEOSGeom_destroy_r(ctx->gctx, po); rtnotice(ctx, "GEOSDifference_r(ctx->gctx): %s", rtgeom_get_last_geos_error(ctx)); return NULL; } RTDEBUGF(3, "Collapse points: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, collapse_points, 0))); #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: cleanup(1)"); #endif GEOSGeom_destroy_r(ctx->gctx, pi); GEOSGeom_destroy_r(ctx->gctx, po); } GEOSGeom_destroy_r(ctx->gctx, geos_bound); RTDEBUGF(3, "Noded Boundaries: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, geos_cut_edges, 0))); /* And use an empty geometry as initial "area" */ geos_area = GEOSGeom_createEmptyPolygon_r(ctx->gctx); if ( ! geos_area ) { rtnotice(ctx, "GEOSGeom_createEmptyPolygon_r(ctx->gctx): %s", rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); return NULL; } /* * See if an area can be build with the remaining edges * and if it can, symdifference with the original area. * Iterate this until no more polygons can be created * with left-over edges. */ while (GEOSGetNumGeometries_r(ctx->gctx, geos_cut_edges)) { GEOSGeometry* new_area=0; GEOSGeometry* new_area_bound=0; GEOSGeometry* symdif=0; GEOSGeometry* new_cut_edges=0; #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: building area from %d edges", GEOSGetNumGeometries_r(ctx->gctx, geos_cut_edges)); #endif /* * ASSUMPTION: cut_edges should already be fully noded */ new_area = RTGEOM_GEOS_buildArea(ctx, geos_cut_edges); if ( ! new_area ) /* must be an exception */ { GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); GEOSGeom_destroy_r(ctx->gctx, geos_area); rtnotice(ctx, "RTGEOM_GEOS_buildArea(ctx) threw an error: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } if ( GEOSisEmpty_r(ctx->gctx, new_area) ) { /* no more rings can be build with thes edges */ GEOSGeom_destroy_r(ctx->gctx, new_area); break; } /* * We succeeded in building a ring ! */ #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: ring built with %d cut edges, saving boundaries", GEOSGetNumGeometries_r(ctx->gctx, geos_cut_edges)); #endif /* * Save the new ring boundaries first (to compute * further cut edges later) */ new_area_bound = GEOSBoundary_r(ctx->gctx, new_area); if ( ! new_area_bound ) { /* We did check for empty area already so * this must be some other error */ rtnotice(ctx, "GEOSBoundary_r(ctx->gctx, '%s') threw an error: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, new_area, 0)), rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, new_area); GEOSGeom_destroy_r(ctx->gctx, geos_area); return NULL; } #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: running SymDifference with new area"); #endif /* * Now symdif new and old area */ symdif = GEOSSymDifference_r(ctx->gctx, geos_area, new_area); if ( ! symdif ) /* must be an exception */ { GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); GEOSGeom_destroy_r(ctx->gctx, new_area); GEOSGeom_destroy_r(ctx->gctx, new_area_bound); GEOSGeom_destroy_r(ctx->gctx, geos_area); rtnotice(ctx, "GEOSSymDifference_r(ctx->gctx) threw an error: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSGeom_destroy_r(ctx->gctx, geos_area); GEOSGeom_destroy_r(ctx->gctx, new_area); geos_area = symdif; symdif = 0; /* * Now let's re-set geos_cut_edges with what's left * from the original boundary. * ASSUMPTION: only the previous cut-edges can be * left, so we don't need to reconsider * the whole original boundaries * * NOTE: this is an expensive operation. * */ #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: computing new cut_edges (GEOSDifference)"); #endif new_cut_edges = GEOSDifference_r(ctx->gctx, geos_cut_edges, new_area_bound); GEOSGeom_destroy_r(ctx->gctx, new_area_bound); if ( ! new_cut_edges ) /* an exception ? */ { /* cleanup and throw */ GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); GEOSGeom_destroy_r(ctx->gctx, geos_area); /* TODO: Shouldn't this be an rterror ? */ rtnotice(ctx, "GEOSDifference_r(ctx->gctx) threw an error: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); geos_cut_edges = new_cut_edges; } #ifdef RTGEOM_PROFILE_MAKEVALID rtnotice(ctx, "ST_MakeValid: final checks"); #endif if ( ! GEOSisEmpty_r(ctx->gctx, geos_area) ) { vgeoms[nvgeoms++] = geos_area; } else { GEOSGeom_destroy_r(ctx->gctx, geos_area); } if ( ! GEOSisEmpty_r(ctx->gctx, geos_cut_edges) ) { vgeoms[nvgeoms++] = geos_cut_edges; } else { GEOSGeom_destroy_r(ctx->gctx, geos_cut_edges); } if ( ! GEOSisEmpty_r(ctx->gctx, collapse_points) ) { vgeoms[nvgeoms++] = collapse_points; } else { GEOSGeom_destroy_r(ctx->gctx, collapse_points); } if ( 1 == nvgeoms ) { /* Return cut edges */ gout = vgeoms[0]; } else { /* Collect areas and lines (if any line) */ gout = GEOSGeom_createCollection_r(ctx->gctx, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms); if ( ! gout ) /* an exception again */ { /* cleanup and throw */ /* TODO: Shouldn't this be an rterror ? */ rtnotice(ctx, "GEOSGeom_createCollection_r(ctx->gctx) threw an error: %s", rtgeom_get_last_geos_error(ctx)); /* TODO: cleanup! */ return NULL; } } return gout; } static GEOSGeometry* RTGEOM_GEOS_makeValidLine(const RTCTX *ctx, const GEOSGeometry* gin) { GEOSGeometry* noded; noded = RTGEOM_GEOS_nodeLines(ctx, gin); return noded; } static GEOSGeometry* RTGEOM_GEOS_makeValidMultiLine(const RTCTX *ctx, const GEOSGeometry* gin) { GEOSGeometry** lines; GEOSGeometry** points; GEOSGeometry* mline_out=0; GEOSGeometry* mpoint_out=0; GEOSGeometry* gout=0; uint32_t nlines=0, nlines_alloc; uint32_t npoints=0; uint32_t ngeoms=0, nsubgeoms; uint32_t i, j; ngeoms = GEOSGetNumGeometries_r(ctx->gctx, gin); nlines_alloc = ngeoms; lines = rtalloc(ctx, sizeof(GEOSGeometry*)*nlines_alloc); points = rtalloc(ctx, sizeof(GEOSGeometry*)*ngeoms); for (i=0; igctx, gin, i); GEOSGeometry* vg; vg = RTGEOM_GEOS_makeValidLine(ctx, g); if ( GEOSisEmpty_r(ctx->gctx, vg) ) { /* we don't care about this one */ GEOSGeom_destroy_r(ctx->gctx, vg); } if ( GEOSGeomTypeId_r(ctx->gctx, vg) == GEOS_POINT ) { points[npoints++] = vg; } else if ( GEOSGeomTypeId_r(ctx->gctx, vg) == GEOS_LINESTRING ) { lines[nlines++] = vg; } else if ( GEOSGeomTypeId_r(ctx->gctx, vg) == GEOS_MULTILINESTRING ) { nsubgeoms=GEOSGetNumGeometries_r(ctx->gctx, vg); nlines_alloc += nsubgeoms; lines = rtrealloc(ctx, lines, sizeof(GEOSGeometry*)*nlines_alloc); for (j=0; jgctx, vg, j); /* NOTE: ownership of the cloned geoms will be * taken by final collection */ lines[nlines++] = GEOSGeom_clone_r(ctx->gctx, gc); } } else { /* NOTE: return from GEOSGeomType will leak * but we really don't expect this to happen */ rterror(ctx, "unexpected geom type returned " "by RTGEOM_GEOS_makeValid: %s", GEOSGeomType_r(ctx->gctx, vg)); } } if ( npoints ) { if ( npoints > 1 ) { mpoint_out = GEOSGeom_createCollection_r(ctx->gctx, GEOS_MULTIPOINT, points, npoints); } else { mpoint_out = points[0]; } } if ( nlines ) { if ( nlines > 1 ) { mline_out = GEOSGeom_createCollection_r(ctx->gctx, GEOS_MULTILINESTRING, lines, nlines); } else { mline_out = lines[0]; } } rtfree(ctx, lines); if ( mline_out && mpoint_out ) { points[0] = mline_out; points[1] = mpoint_out; gout = GEOSGeom_createCollection_r(ctx->gctx, GEOS_GEOMETRYCOLLECTION, points, 2); } else if ( mline_out ) { gout = mline_out; } else if ( mpoint_out ) { gout = mpoint_out; } rtfree(ctx, points); return gout; } static GEOSGeometry* RTGEOM_GEOS_makeValid(const RTCTX *ctx, const GEOSGeometry*); /* * We expect rtgeom_geos_ensure_init(ctx); * Will return NULL on error (expect error handler being called by then) */ static GEOSGeometry* RTGEOM_GEOS_makeValidCollection(const RTCTX *ctx, const GEOSGeometry* gin) { int nvgeoms; GEOSGeometry **vgeoms; GEOSGeom gout; unsigned int i; nvgeoms = GEOSGetNumGeometries_r(ctx->gctx, gin); if ( nvgeoms == -1 ) { rterror(ctx, "GEOSGetNumGeometries: %s", rtgeom_get_last_geos_error(ctx)); return 0; } vgeoms = rtalloc(ctx, sizeof(GEOSGeometry*) * nvgeoms ); if ( ! vgeoms ) { rterror(ctx, "RTGEOM_GEOS_makeValidCollection: out of memory"); return 0; } for ( i=0; igctx, gin, i) ); if ( ! vgeoms[i] ) { while (i--) GEOSGeom_destroy_r(ctx->gctx, vgeoms[i]); rtfree(ctx, vgeoms); /* we expect rterror being called already by makeValid */ return NULL; } } /* Collect areas and lines (if any line) */ gout = GEOSGeom_createCollection_r(ctx->gctx, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms); if ( ! gout ) /* an exception again */ { /* cleanup and throw */ for ( i=0; igctx, vgeoms[i]); rtfree(ctx, vgeoms); rterror(ctx, "GEOSGeom_createCollection_r(ctx->gctx) threw an error: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } rtfree(ctx, vgeoms); return gout; } static GEOSGeometry* RTGEOM_GEOS_makeValid(const RTCTX *ctx, const GEOSGeometry* gin) { GEOSGeometry* gout; char ret_char; /* * Step 2: return what we got so far if already valid */ ret_char = GEOSisValid_r(ctx->gctx, gin); if ( ret_char == 2 ) { /* I don't think should ever happen */ rterror(ctx, "GEOSisValid_r(ctx->gctx): %s", rtgeom_get_last_geos_error(ctx)); return NULL; } else if ( ret_char ) { RTDEBUGF(3, "Geometry [%s] is valid. ", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, gin, 0))); /* It's valid at this step, return what we have */ return GEOSGeom_clone_r(ctx->gctx, gin); } RTDEBUGF(3, "Geometry [%s] is still not valid: %s. " "Will try to clean up further.", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, gin, 0)), rtgeom_get_last_geos_error(ctx)); /* * Step 3 : make what we got valid */ switch (GEOSGeomTypeId_r(ctx->gctx, gin)) { case GEOS_MULTIPOINT: case GEOS_POINT: /* points are artays valid, but we might have invalid ordinate values */ rtnotice(ctx, "PUNTUAL geometry resulted invalid to GEOS -- dunno how to clean that up"); return NULL; break; case GEOS_LINESTRING: gout = RTGEOM_GEOS_makeValidLine(ctx, gin); if ( ! gout ) /* an exception or something */ { /* cleanup and throw */ rterror(ctx, "%s", rtgeom_get_last_geos_error(ctx)); return NULL; } break; /* we've done */ case GEOS_MULTILINESTRING: gout = RTGEOM_GEOS_makeValidMultiLine(ctx, gin); if ( ! gout ) /* an exception or something */ { /* cleanup and throw */ rterror(ctx, "%s", rtgeom_get_last_geos_error(ctx)); return NULL; } break; /* we've done */ case GEOS_POLYGON: case GEOS_MULTIPOLYGON: { gout = RTGEOM_GEOS_makeValidPolygon(ctx, gin); if ( ! gout ) /* an exception or something */ { /* cleanup and throw */ rterror(ctx, "%s", rtgeom_get_last_geos_error(ctx)); return NULL; } break; /* we've done */ } case GEOS_GEOMETRYCOLLECTION: { gout = RTGEOM_GEOS_makeValidCollection(ctx, gin); if ( ! gout ) /* an exception or something */ { /* cleanup and throw */ rterror(ctx, "%s", rtgeom_get_last_geos_error(ctx)); return NULL; } break; /* we've done */ } default: { char* typname = GEOSGeomType_r(ctx->gctx, gin); rtnotice(ctx, "ST_MakeValid: doesn't support geometry type: %s", typname); GEOSFree_r(ctx->gctx, typname); return NULL; break; } } #if PARANOIA_LEVEL > 1 /* * Now check if every point of input is also found * in output, or abort by returning NULL * * Input geometry was rtgeom_in */ { int loss; GEOSGeometry *pi, *po, *pd; /* TODO: handle some errors here... * Lack of exceptions is annoying indeed, * I'm getting old --strk; */ pi = GEOSGeom_extractUniquePoints_r(ctx->gctx, gin); po = GEOSGeom_extractUniquePoints_r(ctx->gctx, gout); pd = GEOSDifference_r(ctx->gctx, pi, po); /* input points - output points */ GEOSGeom_destroy_r(ctx->gctx, pi); GEOSGeom_destroy_r(ctx->gctx, po); loss = !GEOSisEmpty_r(ctx->gctx, pd); GEOSGeom_destroy_r(ctx->gctx, pd); if ( loss ) { rtnotice(ctx, "Vertices lost in RTGEOM_GEOS_makeValid"); /* return NULL */ } } #endif /* PARANOIA_LEVEL > 1 */ return gout; } /* Exported. Uses GEOS internally */ RTGEOM* rtgeom_make_valid(const RTCTX *ctx, RTGEOM* rtgeom_in) { int is3d; GEOSGeom geosgeom; GEOSGeometry* geosout; RTGEOM *rtgeom_out; is3d = RTFLAGS_GET_Z(rtgeom_in->flags); /* * Step 1 : try to convert to GEOS, if impossible, clean that up first * otherwise (adding only duplicates of existing points) */ rtgeom_geos_ensure_init(ctx); rtgeom_out = rtgeom_in; geosgeom = RTGEOM2GEOS(ctx, rtgeom_out, 0); if ( ! geosgeom ) { RTDEBUGF(4, "Original geom can't be converted to GEOS _r(ctx->gctx, %s)" " - will try cleaning that up first", rtgeom_get_last_geos_error(ctx)); rtgeom_out = rtgeom_make_geos_friendly(ctx, rtgeom_out); if ( ! rtgeom_out ) { rterror(ctx, "Could not make a valid geometry out of input"); } /* try again as we did cleanup now */ /* TODO: invoke RTGEOM2GEOS directly with autoclean ? */ geosgeom = RTGEOM2GEOS(ctx, rtgeom_out, 0); if ( ! geosgeom ) { rterror(ctx, "Couldn't convert RTGEOM geom to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } } else { RTDEBUG(4, "original geom converted to GEOS"); rtgeom_out = rtgeom_in; } geosout = RTGEOM_GEOS_makeValid(ctx, geosgeom); GEOSGeom_destroy_r(ctx->gctx, geosgeom); if ( ! geosout ) { return NULL; } rtgeom_out = GEOS2RTGEOM(ctx, geosout, is3d); GEOSGeom_destroy_r(ctx->gctx, geosout); if ( rtgeom_is_collection(ctx, rtgeom_in) && ! rtgeom_is_collection(ctx, rtgeom_out) ) {{ RTGEOM **ogeoms = rtalloc(ctx, sizeof(RTGEOM*)); RTGEOM *ogeom; RTDEBUG(3, "rtgeom_make_valid: forcing multi"); /* NOTE: this is safe because rtgeom_out is surely not rtgeom_in or * otherwise we couldn't have a collection and a non-collection */ assert(rtgeom_in != rtgeom_out); ogeoms[0] = rtgeom_out; ogeom = (RTGEOM *)rtcollection_construct(ctx, RTMULTITYPE[rtgeom_out->type], rtgeom_out->srid, rtgeom_out->bbox, 1, ogeoms); rtgeom_out->bbox = NULL; rtgeom_out = ogeom; }} rtgeom_out->srid = rtgeom_in->srid; return rtgeom_out; } #endif /* RTGEOM_GEOS_VERSION >= 33 */ src/rtgeom_geos_node.c000066400000000000000000000172121271715413500153410ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2011 Sandro Santilli * **********************************************************************/ #include "rtgeom_geos.h" #include "librttopo_geom_internal.h" #include #include static int rtgeom_ngeoms(const RTCTX *ctx, const RTGEOM* n) { const RTCOLLECTION* c = rtgeom_as_rtcollection(ctx, n); if ( c ) return c->ngeoms; else return 1; } static const RTGEOM* rtgeom_subgeom(const RTCTX *ctx, const RTGEOM* g, int n) { const RTCOLLECTION* c = rtgeom_as_rtcollection(ctx, g); if ( c ) return rtcollection_getsubgeom(ctx, (RTCOLLECTION*)c, n); else return g; } static void rtgeom_collect_endpoints(const RTCTX *ctx, const RTGEOM* rtg, RTMPOINT* col) { int i, n; RTLINE* l; switch (rtg->type) { case RTMULTILINETYPE: for ( i = 0, n = rtgeom_ngeoms(ctx, rtg); i < n; ++i ) { rtgeom_collect_endpoints(ctx, rtgeom_subgeom(ctx, rtg, i), col); } break; case RTLINETYPE: l = (RTLINE*)rtg; col = rtmpoint_add_rtpoint(ctx, col, rtline_get_rtpoint(ctx, l, 0)); col = rtmpoint_add_rtpoint(ctx, col, rtline_get_rtpoint(ctx, l, l->points->npoints-1)); break; default: rterror(ctx, "rtgeom_collect_endpoints: invalid type %s", rttype_name(ctx, rtg->type)); break; } } static RTMPOINT* rtgeom_extract_endpoints(const RTCTX *ctx, const RTGEOM* rtg) { RTMPOINT* col = rtmpoint_construct_empty(ctx, SRID_UNKNOWN, RTFLAGS_GET_Z(rtg->flags), RTFLAGS_GET_M(rtg->flags)); rtgeom_collect_endpoints(ctx, rtg, col); return col; } /* Assumes rtgeom_geos_ensure_init(ctx) was called */ /* May return RTPOINT or RTMPOINT */ static RTGEOM* rtgeom_extract_unique_endpoints(const RTCTX *ctx, const RTGEOM* rtg) { #if RTGEOM_GEOS_VERSION < 33 rterror(ctx, "The GEOS version this postgis binary " "was compiled against (%d) doesn't support " "'GEOSUnaryUnion' function _r(ctx->gctx, 3.3.0+ required)", RTGEOM_GEOS_VERSION); return NULL; #else /* RTGEOM_GEOS_VERSION >= 33 */ RTGEOM* ret; GEOSGeometry *gepu; RTMPOINT *epall = rtgeom_extract_endpoints(ctx, rtg); GEOSGeometry *gepall = RTGEOM2GEOS(ctx, (RTGEOM*)epall, 1); rtmpoint_free(ctx, epall); if ( ! gepall ) { rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* UnaryUnion to remove duplicates */ /* TODO: do it all within pgis using indices */ gepu = GEOSUnaryUnion_r(ctx->gctx, gepall); if ( ! gepu ) { GEOSGeom_destroy_r(ctx->gctx, gepall); rterror(ctx, "GEOSUnaryUnion: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSGeom_destroy_r(ctx->gctx, gepall); ret = GEOS2RTGEOM(ctx, gepu, RTFLAGS_GET_Z(rtg->flags)); GEOSGeom_destroy_r(ctx->gctx, gepu); if ( ! ret ) { rterror(ctx, "Error during GEOS2RTGEOM"); return NULL; } return ret; #endif /* RTGEOM_GEOS_VERSION >= 33 */ } /* exported */ extern RTGEOM* rtgeom_node(const RTCTX *ctx, const RTGEOM* rtgeom_in); RTGEOM* rtgeom_node(const RTCTX *ctx, const RTGEOM* rtgeom_in) { #if RTGEOM_GEOS_VERSION < 33 rterror(ctx, "The GEOS version this postgis binary " "was compiled against (%d) doesn't support " "'GEOSUnaryUnion' function _r(ctx->gctx, 3.3.0+ required)", RTGEOM_GEOS_VERSION); return NULL; #else /* RTGEOM_GEOS_VERSION >= 33 */ GEOSGeometry *g1, *gu, *gm; RTGEOM *ep, *lines; RTCOLLECTION *col, *tc; int pn, ln, np, nl; if ( rtgeom_dimension(ctx, rtgeom_in) != 1 ) { rterror(ctx, "Noding geometries of dimension != 1 is unsupported"); return NULL; } rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, rtgeom_in, 1); if ( ! g1 ) { rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } ep = rtgeom_extract_unique_endpoints(ctx, rtgeom_in); if ( ! ep ) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "Error extracting unique endpoints from input"); return NULL; } /* Unary union input to fully node */ gu = GEOSUnaryUnion_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g1); if ( ! gu ) { rtgeom_free(ctx, ep); rterror(ctx, "GEOSUnaryUnion: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* Linemerge (in case of overlaps) */ gm = GEOSLineMerge_r(ctx->gctx, gu); GEOSGeom_destroy_r(ctx->gctx, gu); if ( ! gm ) { rtgeom_free(ctx, ep); rterror(ctx, "GEOSLineMerge: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } lines = GEOS2RTGEOM(ctx, gm, RTFLAGS_GET_Z(rtgeom_in->flags)); GEOSGeom_destroy_r(ctx->gctx, gm); if ( ! lines ) { rtgeom_free(ctx, ep); rterror(ctx, "Error during GEOS2RTGEOM"); return NULL; } /* * Reintroduce endpoints from input, using split-line-by-point. * Note that by now we can be sure that each point splits at * most _one_ segment as any point shared by multiple segments * would already be a node. Also we can be sure that any of * the segments endpoints won't split any other segment. * We can use the above 2 assertions to early exit the loop. */ col = rtcollection_construct_empty(ctx, RTMULTILINETYPE, rtgeom_in->srid, RTFLAGS_GET_Z(rtgeom_in->flags), RTFLAGS_GET_M(rtgeom_in->flags)); np = rtgeom_ngeoms(ctx, ep); for (pn=0; pn ln+1) { tc->geoms[nl] = tc->geoms[nl-1]; --nl; } rtgeom_free(ctx, tc->geoms[ln]); tc->geoms[ln] = col->geoms[0]; tc->geoms[ln+1] = col->geoms[1]; tc->ngeoms++; } else { rtgeom_free(ctx, lines); /* transfer ownership rather than cloning */ lines = (RTGEOM*)rtcollection_clone_deep(ctx, col); assert(col->ngeoms == 2); rtgeom_free(ctx, col->geoms[0]); rtgeom_free(ctx, col->geoms[1]); } /* reset the vector */ assert(col->ngeoms == 2); col->ngeoms = 0; break; } } rtgeom_free(ctx, ep); rtcollection_free(ctx, col); lines->srid = rtgeom_in->srid; return (RTGEOM*)lines; #endif /* RTGEOM_GEOS_VERSION >= 33 */ } src/rtgeom_geos_split.c000066400000000000000000000373061271715413500155550ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011-2015 Sandro Santilli * **********************************************************************/ #include "rtgeom_geos.h" #include "librttopo_geom_internal.h" #include #include static RTGEOM* rtline_split_by_line(const RTCTX *ctx, const RTLINE* rtgeom_in, const RTGEOM* blade_in); static RTGEOM* rtline_split_by_point(const RTCTX *ctx, const RTLINE* rtgeom_in, const RTPOINT* blade_in); static RTGEOM* rtline_split_by_mpoint(const RTCTX *ctx, const RTLINE* rtgeom_in, const RTMPOINT* blade_in); static RTGEOM* rtline_split(const RTCTX *ctx, const RTLINE* rtgeom_in, const RTGEOM* blade_in); static RTGEOM* rtpoly_split_by_line(const RTCTX *ctx, const RTPOLY* rtgeom_in, const RTLINE* blade_in); static RTGEOM* rtcollection_split(const RTCTX *ctx, const RTCOLLECTION* rtcoll_in, const RTGEOM* blade_in); static RTGEOM* rtpoly_split(const RTCTX *ctx, const RTPOLY* rtpoly_in, const RTGEOM* blade_in); /* Initializes and uses GEOS internally */ static RTGEOM* rtline_split_by_line(const RTCTX *ctx, const RTLINE* rtline_in, const RTGEOM* blade_in) { RTGEOM** components; RTGEOM* diff; RTCOLLECTION* out; GEOSGeometry* gdiff; /* difference */ GEOSGeometry* g1; GEOSGeometry* g2; int ret; /* ASSERT blade_in is LINE or MULTILINE */ assert (blade_in->type == RTLINETYPE || blade_in->type == RTMULTILINETYPE || blade_in->type == RTPOLYGONTYPE || blade_in->type == RTMULTIPOLYGONTYPE ); /* Possible outcomes: * * 1. The lines do not cross or overlap * -> Return a collection with single element * 2. The lines cross * -> Return a collection of all elements resulting from the split */ rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, (RTGEOM*)rtline_in, 0); if ( ! g1 ) { rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = RTGEOM2GEOS(ctx, blade_in, 0); if ( ! g2 ) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* If blade is a polygon, pick its boundary */ if ( blade_in->type == RTPOLYGONTYPE || blade_in->type == RTMULTIPOLYGONTYPE ) { gdiff = GEOSBoundary_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g2); if ( ! gdiff ) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "GEOSBoundary: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = gdiff; gdiff = NULL; } /* If interior intersecton is linear we can't split */ ret = GEOSRelatePattern_r(ctx->gctx, g1, g2, "1********"); if ( 2 == ret ) { rterror(ctx, "GEOSRelatePattern: %s", rtgeom_get_last_geos_error(ctx)); GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); return NULL; } if ( ret ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); rterror(ctx, "Splitter line has linear intersection with input"); return NULL; } gdiff = GEOSDifference_r(ctx->gctx, g1,g2); GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); if (gdiff == NULL) { rterror(ctx, "GEOSDifference: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } diff = GEOS2RTGEOM(ctx, gdiff, RTFLAGS_GET_Z(rtline_in->flags)); GEOSGeom_destroy_r(ctx->gctx, gdiff); if (NULL == diff) { rterror(ctx, "GEOS2RTGEOM: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } out = rtgeom_as_rtcollection(ctx, diff); if ( ! out ) { components = rtalloc(ctx, sizeof(RTGEOM*)*1); components[0] = diff; out = rtcollection_construct(ctx, RTCOLLECTIONTYPE, rtline_in->srid, NULL, 1, components); } else { /* Set SRID */ rtgeom_set_srid(ctx, (RTGEOM*)out, rtline_in->srid); /* Force collection type */ out->type = RTCOLLECTIONTYPE; } return (RTGEOM*)out; } static RTGEOM* rtline_split_by_point(const RTCTX *ctx, const RTLINE* rtline_in, const RTPOINT* blade_in) { RTMLINE* out; out = rtmline_construct_empty(ctx, rtline_in->srid, RTFLAGS_GET_Z(rtline_in->flags), RTFLAGS_GET_M(rtline_in->flags)); if ( rtline_split_by_point_to(ctx, rtline_in, blade_in, out) < 2 ) { rtmline_add_rtline(ctx, out, rtline_clone_deep(ctx, rtline_in)); } /* Turn multiline into collection */ out->type = RTCOLLECTIONTYPE; return (RTGEOM*)out; } static RTGEOM* rtline_split_by_mpoint(const RTCTX *ctx, const RTLINE* rtline_in, const RTMPOINT* mp) { RTMLINE* out; int i, j; out = rtmline_construct_empty(ctx, rtline_in->srid, RTFLAGS_GET_Z(rtline_in->flags), RTFLAGS_GET_M(rtline_in->flags)); rtmline_add_rtline(ctx, out, rtline_clone_deep(ctx, rtline_in)); for (i=0; ingeoms; ++i) { for (j=0; jngeoms; ++j) { rtline_in = out->geoms[j]; RTPOINT *blade_in = mp->geoms[i]; int ret = rtline_split_by_point_to(ctx, rtline_in, blade_in, out); if ( 2 == ret ) { /* the point splits this line, * 2 splits were added to collection. * We'll move the latest added into * the slot of the current one. */ rtline_free(ctx, out->geoms[j]); out->geoms[j] = out->geoms[--out->ngeoms]; } } } /* Turn multiline into collection */ out->type = RTCOLLECTIONTYPE; return (RTGEOM*)out; } int rtline_split_by_point_to(const RTCTX *ctx, const RTLINE* rtline_in, const RTPOINT* blade_in, RTMLINE* v) { double loc, dist; RTPOINT4D pt, pt_projected; RTPOINTARRAY* pa1; RTPOINTARRAY* pa2; double vstol; /* vertex snap tolerance */ /* Possible outcomes: * * 1. The point is not on the line or on the boundary * -> Leave collection untouched, return 0 * 2. The point is on the boundary * -> Leave collection untouched, return 1 * 3. The point is in the line * -> Push 2 elements on the collection: * o start_point - cut_point * o cut_point - last_point * -> Return 2 */ rt_getPoint4d_p(ctx, blade_in->point, 0, &pt); loc = ptarray_locate_point(ctx, rtline_in->points, &pt, &dist, &pt_projected); /* rtnotice(ctx, "Location: %g -- Distance: %g", loc, dist); */ if ( dist > 0 ) /* TODO: accept a tolerance ? */ { /* No intersection */ return 0; } if ( loc == 0 || loc == 1 ) { /* Intersection is on the boundary */ return 1; } /* There is a real intersection, let's get two substrings */ /* Compute vertex snap tolerance based on line length * TODO: take as parameter ? */ vstol = ptarray_length_2d(ctx, rtline_in->points) / 1e14; pa1 = ptarray_substring(ctx, rtline_in->points, 0, loc, vstol); pa2 = ptarray_substring(ctx, rtline_in->points, loc, 1, vstol); /* NOTE: I've seen empty pointarrays with loc != 0 and loc != 1 */ if ( pa1->npoints == 0 || pa2->npoints == 0 ) { ptarray_free(ctx, pa1); ptarray_free(ctx, pa2); /* Intersection is on the boundary */ return 1; } rtmline_add_rtline(ctx, v, rtline_construct(ctx, SRID_UNKNOWN, NULL, pa1)); rtmline_add_rtline(ctx, v, rtline_construct(ctx, SRID_UNKNOWN, NULL, pa2)); return 2; } static RTGEOM* rtline_split(const RTCTX *ctx, const RTLINE* rtline_in, const RTGEOM* blade_in) { switch (blade_in->type) { case RTPOINTTYPE: return rtline_split_by_point(ctx, rtline_in, (RTPOINT*)blade_in); case RTMULTIPOINTTYPE: return rtline_split_by_mpoint(ctx, rtline_in, (RTMPOINT*)blade_in); case RTLINETYPE: case RTMULTILINETYPE: case RTPOLYGONTYPE: case RTMULTIPOLYGONTYPE: return rtline_split_by_line(ctx, rtline_in, blade_in); default: rterror(ctx, "Splitting a Line by a %s is unsupported", rttype_name(ctx, blade_in->type)); return NULL; } return NULL; } /* Initializes and uses GEOS internally */ static RTGEOM* rtpoly_split_by_line(const RTCTX *ctx, const RTPOLY* rtpoly_in, const RTLINE* blade_in) { RTCOLLECTION* out; GEOSGeometry* g1; GEOSGeometry* g2; GEOSGeometry* g1_bounds; GEOSGeometry* polygons; const GEOSGeometry *vgeoms[1]; int i,n; int hasZ = RTFLAGS_GET_Z(rtpoly_in->flags); /* Possible outcomes: * * 1. The line does not split the polygon * -> Return a collection with single element * 2. The line does split the polygon * -> Return a collection of all elements resulting from the split */ rtgeom_geos_ensure_init(ctx); g1 = RTGEOM2GEOS(ctx, (RTGEOM*)rtpoly_in, 0); if ( NULL == g1 ) { rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g1_bounds = GEOSBoundary_r(ctx->gctx, g1); if ( NULL == g1_bounds ) { GEOSGeom_destroy_r(ctx->gctx, g1); rterror(ctx, "GEOSBoundary: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } g2 = RTGEOM2GEOS(ctx, (RTGEOM*)blade_in, 0); if ( NULL == g2 ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); rterror(ctx, "RTGEOM2GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } vgeoms[0] = GEOSUnion_r(ctx->gctx, g1_bounds, g2); if ( NULL == vgeoms[0] ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); rterror(ctx, "GEOSUnion: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } /* debugging.. rtnotice(ctx, "Bounds poly: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, g1_bounds, hasZ))); rtnotice(ctx, "Line: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, g2, hasZ))); rtnotice(ctx, "Noded bounds: %s", rtgeom_to_ewkt(ctx, GEOS2RTGEOM(ctx, vgeoms[0], hasZ))); */ polygons = GEOSPolygonize_r(ctx->gctx, vgeoms, 1); if ( NULL == polygons ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); GEOSGeom_destroy_r(ctx->gctx, (GEOSGeometry*)vgeoms[0]); rterror(ctx, "GEOSPolygonize: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } #if PARANOIA_LEVEL > 0 if ( GEOSGeometryTypeId_r(ctx->gctx, polygons) != RTCOLLECTIONTYPE ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); GEOSGeom_destroy_r(ctx->gctx, (GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy_r(ctx->gctx, polygons); rterror(ctx, "Unexpected return from GEOSpolygonize"); return 0; } #endif /* We should now have all polygons, just skip * the ones which are in holes of the original * geometries and return the rest in a collection */ n = GEOSGetNumGeometries_r(ctx->gctx, polygons); out = rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, rtpoly_in->srid, hasZ, 0); /* Allocate space for all polys */ out->geoms = rtrealloc(ctx, out->geoms, sizeof(RTGEOM*)*n); assert(0 == out->ngeoms); for (i=0; igctx, polygons, i); int contains; pos = GEOSPointOnSurface_r(ctx->gctx, p); if ( ! pos ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); GEOSGeom_destroy_r(ctx->gctx, (GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy_r(ctx->gctx, polygons); rterror(ctx, "GEOSPointOnSurface: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } contains = GEOSContains_r(ctx->gctx, g1, pos); if ( 2 == contains ) { GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); GEOSGeom_destroy_r(ctx->gctx, (GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy_r(ctx->gctx, polygons); GEOSGeom_destroy_r(ctx->gctx, pos); rterror(ctx, "GEOSContains: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } GEOSGeom_destroy_r(ctx->gctx, pos); if ( 0 == contains ) { /* Original geometry doesn't contain * a point in this ring, must be an hole */ continue; } out->geoms[out->ngeoms++] = GEOS2RTGEOM(ctx, p, hasZ); } GEOSGeom_destroy_r(ctx->gctx, g1); GEOSGeom_destroy_r(ctx->gctx, g2); GEOSGeom_destroy_r(ctx->gctx, g1_bounds); GEOSGeom_destroy_r(ctx->gctx, (GEOSGeometry*)vgeoms[0]); GEOSGeom_destroy_r(ctx->gctx, polygons); return (RTGEOM*)out; } static RTGEOM* rtcollection_split(const RTCTX *ctx, const RTCOLLECTION* rtcoll_in, const RTGEOM* blade_in) { RTGEOM** split_vector=NULL; RTCOLLECTION* out; size_t split_vector_capacity; size_t split_vector_size=0; size_t i,j; split_vector_capacity=8; split_vector = rtalloc(ctx, split_vector_capacity * sizeof(RTGEOM*)); if ( ! split_vector ) { rterror(ctx, "Out of virtual memory"); return NULL; } for (i=0; ingeoms; ++i) { RTCOLLECTION* col; RTGEOM* split = rtgeom_split(ctx, rtcoll_in->geoms[i], blade_in); /* an exception should prevent this from ever returning NULL */ if ( ! split ) return NULL; col = rtgeom_as_rtcollection(ctx, split); /* Output, if any, will artays be a collection */ assert(col); /* Reallocate split_vector if needed */ if ( split_vector_size + col->ngeoms > split_vector_capacity ) { /* NOTE: we could be smarter on reallocations here */ split_vector_capacity += col->ngeoms; split_vector = rtrealloc(ctx, split_vector, split_vector_capacity * sizeof(RTGEOM*)); if ( ! split_vector ) { rterror(ctx, "Out of virtual memory"); return NULL; } } for (j=0; jngeoms; ++j) { col->geoms[j]->srid = SRID_UNKNOWN; /* strip srid */ split_vector[split_vector_size++] = col->geoms[j]; } rtfree(ctx, col->geoms); rtfree(ctx, col); } /* Now split_vector has split_vector_size geometries */ out = rtcollection_construct(ctx, RTCOLLECTIONTYPE, rtcoll_in->srid, NULL, split_vector_size, split_vector); return (RTGEOM*)out; } static RTGEOM* rtpoly_split(const RTCTX *ctx, const RTPOLY* rtpoly_in, const RTGEOM* blade_in) { switch (blade_in->type) { case RTLINETYPE: return rtpoly_split_by_line(ctx, rtpoly_in, (RTLINE*)blade_in); default: rterror(ctx, "Splitting a Polygon by a %s is unsupported", rttype_name(ctx, blade_in->type)); return NULL; } return NULL; } /* exported */ RTGEOM* rtgeom_split(const RTCTX *ctx, const RTGEOM* rtgeom_in, const RTGEOM* blade_in) { switch (rtgeom_in->type) { case RTLINETYPE: return rtline_split(ctx, (const RTLINE*)rtgeom_in, blade_in); case RTPOLYGONTYPE: return rtpoly_split(ctx, (const RTPOLY*)rtgeom_in, blade_in); case RTMULTIPOLYGONTYPE: case RTMULTILINETYPE: case RTCOLLECTIONTYPE: return rtcollection_split(ctx, (const RTCOLLECTION*)rtgeom_in, blade_in); default: rterror(ctx, "Splitting of %s geometries is unsupported", rttype_name(ctx, rtgeom_in->type)); return NULL; } } src/rtgeom_log.h000066400000000000000000000057361271715413500141750ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2008 Paul Ramsey * Copyright 2007-2008 Mark Cave-Ayland * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #ifndef RTGEOM_LOG_H #define RTGEOM_LOG_H 1 #include "librttopo_geom_internal.h" #include /* * Debug macros */ #if RTGEOM_DEBUG_LEVEL > 0 /* Display a notice at the given debug level */ #define RTDEBUG(level, msg) \ do { \ if (RTGEOM_DEBUG_LEVEL >= level) \ rtdebug(const RTCTX *ctx, level, "[%s:%s:%d] " msg, __FILE__, __func__, __LINE__); \ } while (0); /* Display a formatted notice at the given debug level * (like printf, with variadic arguments) */ #define RTDEBUGF(level, msg, ...) \ do { \ if (RTGEOM_DEBUG_LEVEL >= level) \ rtdebug(const RTCTX *ctx, level, "[%s:%s:%d] " msg, \ __FILE__, __func__, __LINE__, __VA_ARGS__); \ } while (0); #else /* RTGEOM_DEBUG_LEVEL <= 0 */ /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define RTDEBUG(level, msg) \ ((void) 0) /* Empty prototype that can be optimised away by the compiler * for non-debug builds */ #define RTDEBUGF(level, msg, ...) \ ((void) 0) #endif /* RTGEOM_DEBUG_LEVEL <= 0 */ /** * Write a notice out to the notice handler. * * Uses standard printf() substitutions. * Use for messages you artays want output. * For debugging, use RTDEBUG() or RTDEBUGF(). * @ingroup logging */ void rtnotice(const RTCTX *ctx, const char *fmt, ...); /** * Write a notice out to the error handler. * * Uses standard printf() substitutions. * Use for errors you artays want output. * For debugging, use RTDEBUG() or RTDEBUGF(). * @ingroup logging */ void rterror(const RTCTX *ctx, const char *fmt, ...); /** * Write a debug message out. * Don't call this function directly, use the * macros, RTDEBUG() or RTDEBUGF(), for * efficiency. * @ingroup logging */ void rtdebug(const RTCTX *ctx, int level, const char *fmt, ...); #endif /* RTGEOM_LOG_H */ src/rtgeom_topo.c000066400000000000000000005716231271715413500143730ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2015-2016 Sandro Santilli * ********************************************************************** * * Last port: lwgeom_topo.c r14853 * **********************************************************************/ #include "rttopo_config.h" /*#define RTGEOM_DEBUG_LEVEL 1*/ #include "rtgeom_log.h" #include "librttopo_geom_internal.h" #include "librttopo_internal.h" #include "rtgeom_geos.h" #include #include /* for PRId64 */ #include #include #ifdef WIN32 # define RTTFMT_ELEMID "lld" #else # define RTTFMT_ELEMID PRId64 #endif /* TODO: move this to rtgeom_log.h */ #define RTDEBUGG(level, geom, msg) \ if (RTGEOM_DEBUG_LEVEL >= level) \ do { \ size_t sz; \ char *wkt1 = rtgeom_to_wkt(iface->ctx, geom, RTWKT_EXTENDED, 15, &sz); \ /* char *wkt1 = rtgeom_to_hexwkb(iface->ctx, geom, RTWKT_EXTENDED, &sz); */ \ RTDEBUGF(level, msg ": %s", wkt1); \ rtfree(iface->ctx, wkt1); \ } while (0); /********************************************************************* * * Backend iface * ********************************************************************/ RTT_BE_IFACE* rtt_CreateBackendIface(const RTCTX *ctx, const RTT_BE_DATA *data) { RTT_BE_IFACE *iface = rtalloc(ctx, sizeof(RTT_BE_IFACE)); iface->data = data; iface->cb = NULL; iface->ctx = ctx; return iface; } void rtt_BackendIfaceRegisterCallbacks(RTT_BE_IFACE *iface, const RTT_BE_CALLBACKS* cb) { iface->cb = cb; } void rtt_FreeBackendIface(RTT_BE_IFACE* iface) { rtfree(iface->ctx, iface); } static void _rtt_EnsureGeos(const RTCTX *ctx) { rtgeom_geos_ensure_init(ctx); } /********************************************************************* * * Backend wrappers * ********************************************************************/ #define CHECKCB(be, method) do { \ if ( ! (be)->cb || ! (be)->cb->method ) \ rterror((be)->ctx, "Callback " # method " not registered by backend"); \ } while (0) #define CB0(be, method) \ CHECKCB(be, method);\ return (be)->cb->method((be)->data) #define CB1(be, method, a1) \ CHECKCB(be, method);\ return (be)->cb->method((be)->data, a1) #define CBT0(to, method) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo) #define CBT1(to, method, a1) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1) #define CBT2(to, method, a1, a2) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2) #define CBT3(to, method, a1, a2, a3) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3) #define CBT4(to, method, a1, a2, a3, a4) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4) #define CBT5(to, method, a1, a2, a3, a4, a5) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5) #define CBT6(to, method, a1, a2, a3, a4, a5, a6) \ CHECKCB((to)->be_iface, method);\ return (to)->be_iface->cb->method((to)->be_topo, a1, a2, a3, a4, a5, a6) const char * rtt_be_lastErrorMessage(const RTT_BE_IFACE* be) { CB0(be, lastErrorMessage); } RTT_BE_TOPOLOGY * rtt_be_loadTopologyByName(RTT_BE_IFACE *be, const char *name) { CB1(be, loadTopologyByName, name); } static int rtt_be_topoGetSRID(RTT_TOPOLOGY *topo) { CBT0(topo, topoGetSRID); } static double rtt_be_topoGetPrecision(RTT_TOPOLOGY *topo) { CBT0(topo, topoGetPrecision); } static int rtt_be_topoHasZ(RTT_TOPOLOGY *topo) { CBT0(topo, topoHasZ); } int rtt_be_freeTopology(RTT_TOPOLOGY *topo) { CBT0(topo, freeTopology); } RTT_ISO_NODE* rtt_be_getNodeById(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields) { CBT3(topo, getNodeById, ids, numelems, fields); } RTT_ISO_NODE* rtt_be_getNodeWithinDistance2D(RTT_TOPOLOGY* topo, RTPOINT* pt, double dist, int* numelems, int fields, int limit) { CBT5(topo, getNodeWithinDistance2D, pt, dist, numelems, fields, limit); } static RTT_ISO_NODE* rtt_be_getNodeWithinBox2D( const RTT_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ) { CBT4(topo, getNodeWithinBox2D, box, numelems, fields, limit); } static RTT_ISO_EDGE* rtt_be_getEdgeWithinBox2D( const RTT_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ) { CBT4(topo, getEdgeWithinBox2D, box, numelems, fields, limit); } static RTT_ISO_FACE* rtt_be_getFaceWithinBox2D( const RTT_TOPOLOGY* topo, const RTGBOX* box, int* numelems, int fields, int limit ) { CBT4(topo, getFaceWithinBox2D, box, numelems, fields, limit); } int rtt_be_insertNodes(RTT_TOPOLOGY* topo, RTT_ISO_NODE* node, int numelems) { CBT2(topo, insertNodes, node, numelems); } static int rtt_be_insertFaces(RTT_TOPOLOGY* topo, RTT_ISO_FACE* face, int numelems) { CBT2(topo, insertFaces, face, numelems); } static int rtt_be_deleteFacesById(const RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int numelems) { CBT2(topo, deleteFacesById, ids, numelems); } static int rtt_be_deleteNodesById(const RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int numelems) { CBT2(topo, deleteNodesById, ids, numelems); } RTT_ELEMID rtt_be_getNextEdgeId(RTT_TOPOLOGY* topo) { CBT0(topo, getNextEdgeId); } RTT_ISO_EDGE* rtt_be_getEdgeById(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields) { CBT3(topo, getEdgeById, ids, numelems, fields); } static RTT_ISO_FACE* rtt_be_getFaceById(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields) { CBT3(topo, getFaceById, ids, numelems, fields); } static RTT_ISO_EDGE* rtt_be_getEdgeByNode(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields) { CBT3(topo, getEdgeByNode, ids, numelems, fields); } static RTT_ISO_EDGE* rtt_be_getEdgeByFace(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields, const RTGBOX *box) { CBT4(topo, getEdgeByFace, ids, numelems, fields, box); } static RTT_ISO_NODE* rtt_be_getNodeByFace(RTT_TOPOLOGY* topo, const RTT_ELEMID* ids, int* numelems, int fields, const RTGBOX *box) { CBT4(topo, getNodeByFace, ids, numelems, fields, box); } RTT_ISO_EDGE* rtt_be_getEdgeWithinDistance2D(RTT_TOPOLOGY* topo, RTPOINT* pt, double dist, int* numelems, int fields, int limit) { CBT5(topo, getEdgeWithinDistance2D, pt, dist, numelems, fields, limit); } int rtt_be_insertEdges(RTT_TOPOLOGY* topo, RTT_ISO_EDGE* edge, int numelems) { CBT2(topo, insertEdges, edge, numelems); } int rtt_be_updateEdges(RTT_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields, const RTT_ISO_EDGE* upd_edge, int upd_fields, const RTT_ISO_EDGE* exc_edge, int exc_fields ) { CBT6(topo, updateEdges, sel_edge, sel_fields, upd_edge, upd_fields, exc_edge, exc_fields); } static int rtt_be_updateNodes(RTT_TOPOLOGY* topo, const RTT_ISO_NODE* sel_node, int sel_fields, const RTT_ISO_NODE* upd_node, int upd_fields, const RTT_ISO_NODE* exc_node, int exc_fields ) { CBT6(topo, updateNodes, sel_node, sel_fields, upd_node, upd_fields, exc_node, exc_fields); } static int rtt_be_updateFacesById(RTT_TOPOLOGY* topo, const RTT_ISO_FACE* faces, int numfaces ) { CBT2(topo, updateFacesById, faces, numfaces); } static int rtt_be_updateEdgesById(RTT_TOPOLOGY* topo, const RTT_ISO_EDGE* edges, int numedges, int upd_fields ) { CBT3(topo, updateEdgesById, edges, numedges, upd_fields); } static int rtt_be_updateNodesById(RTT_TOPOLOGY* topo, const RTT_ISO_NODE* nodes, int numnodes, int upd_fields ) { CBT3(topo, updateNodesById, nodes, numnodes, upd_fields); } int rtt_be_deleteEdges(RTT_TOPOLOGY* topo, const RTT_ISO_EDGE* sel_edge, int sel_fields ) { CBT2(topo, deleteEdges, sel_edge, sel_fields); } RTT_ELEMID rtt_be_getFaceContainingPoint(RTT_TOPOLOGY* topo, RTPOINT* pt) { CBT1(topo, getFaceContainingPoint, pt); } int rtt_be_updateTopoGeomEdgeSplit(RTT_TOPOLOGY* topo, RTT_ELEMID split_edge, RTT_ELEMID new_edge1, RTT_ELEMID new_edge2) { CBT3(topo, updateTopoGeomEdgeSplit, split_edge, new_edge1, new_edge2); } static int rtt_be_updateTopoGeomFaceSplit(RTT_TOPOLOGY* topo, RTT_ELEMID split_face, RTT_ELEMID new_face1, RTT_ELEMID new_face2) { CBT3(topo, updateTopoGeomFaceSplit, split_face, new_face1, new_face2); } static int rtt_be_checkTopoGeomRemEdge(RTT_TOPOLOGY* topo, RTT_ELEMID edge_id, RTT_ELEMID face_left, RTT_ELEMID face_right) { CBT3(topo, checkTopoGeomRemEdge, edge_id, face_left, face_right); } static int rtt_be_checkTopoGeomRemNode(RTT_TOPOLOGY* topo, RTT_ELEMID node_id, RTT_ELEMID eid1, RTT_ELEMID eid2) { CBT3(topo, checkTopoGeomRemNode, node_id, eid1, eid2); } static int rtt_be_updateTopoGeomFaceHeal(RTT_TOPOLOGY* topo, RTT_ELEMID face1, RTT_ELEMID face2, RTT_ELEMID newface) { CBT3(topo, updateTopoGeomFaceHeal, face1, face2, newface); } static int rtt_be_updateTopoGeomEdgeHeal(RTT_TOPOLOGY* topo, RTT_ELEMID edge1, RTT_ELEMID edge2, RTT_ELEMID newedge) { CBT3(topo, updateTopoGeomEdgeHeal, edge1, edge2, newedge); } static RTT_ELEMID* rtt_be_getRingEdges( RTT_TOPOLOGY* topo, RTT_ELEMID edge, int *numedges, int limit ) { CBT3(topo, getRingEdges, edge, numedges, limit); } /* wrappers of backend wrappers... */ int rtt_be_ExistsCoincidentNode(RTT_TOPOLOGY* topo, RTPOINT* pt) { int exists = 0; rtt_be_getNodeWithinDistance2D(topo, pt, 0, &exists, 0, -1); if ( exists == -1 ) { rterror(topo->be_iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return 0; } return exists; } int rtt_be_ExistsEdgeIntersectingPoint(RTT_TOPOLOGY* topo, RTPOINT* pt) { int exists = 0; rtt_be_getEdgeWithinDistance2D(topo, pt, 0, &exists, 0, -1); if ( exists == -1 ) { rterror(topo->be_iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return 0; } return exists; } /************************************************************************ * * Utility functions * ************************************************************************/ static RTGEOM * _rtt_toposnap(const RTCTX *ctx, RTGEOM *src, RTGEOM *tgt, double tol) { RTGEOM *tmp = src; RTGEOM *tmp2; int changed; int iterations = 0; int maxiterations = rtgeom_count_vertices(ctx, tgt); /* GEOS snapping can be unstable */ /* See https://trac.osgeo.org/geos/ticket/760 */ do { RTGEOM *tmp3; tmp2 = rtgeom_snap(ctx, tmp, tgt, tol); ++iterations; changed = ( rtgeom_count_vertices(ctx, tmp2) != rtgeom_count_vertices(ctx, tmp) ); #if GEOS_NUMERIC_VERSION < 30309 /* Up to GEOS-3.3.8, snapping could duplicate points */ if ( changed ) { tmp3 = rtgeom_remove_repeated_points(ctx, tmp2, 0 ); rtgeom_free(ctx, tmp2); tmp2 = tmp3; changed = ( rtgeom_count_vertices(ctx, tmp2) != rtgeom_count_vertices(ctx, tmp) ); } #endif /* GEOS_NUMERIC_VERSION < 30309 */ RTDEBUGF(2, "After iteration %d, geometry changed ? %d (%d vs %d vertices)", iterations, changed, rtgeom_count_vertices(ctx, tmp2), rtgeom_count_vertices(ctx, tmp)); if ( tmp != src ) rtgeom_free(ctx, tmp); tmp = tmp2; } while ( changed && iterations <= maxiterations ); RTDEBUGF(1, "It took %d/%d iterations to properly snap", iterations, maxiterations); return tmp; } static void _rtt_release_faces(const RTCTX *ctx, RTT_ISO_FACE *faces, int num_faces) { int i; for ( i=0; ictx, "Could not load topology from backend: %s", rterror(iface->ctx, "%s", rtt_be_lastErrorMessage(iface)); return NULL; } topo = rtalloc(iface->ctx, sizeof(RTT_TOPOLOGY)); topo->be_iface = iface; topo->be_topo = be_topo; topo->srid = rtt_be_topoGetSRID(topo); topo->hasZ = rtt_be_topoHasZ(topo); topo->precision = rtt_be_topoGetPrecision(topo); return topo; } void rtt_FreeTopology( RTT_TOPOLOGY* topo ) { const RTT_BE_IFACE *iface = topo->be_iface; if ( ! rtt_be_freeTopology(topo) ) { rtnotice(topo->be_iface->ctx, "Could not release backend topology memory: %s", rtt_be_lastErrorMessage(topo->be_iface)); } rtfree(iface->ctx, topo); } RTT_ELEMID rtt_AddIsoNode( RTT_TOPOLOGY* topo, RTT_ELEMID face, RTPOINT* pt, int skipISOChecks ) { RTT_ELEMID foundInFace = -1; const RTT_BE_IFACE *iface = topo->be_iface; if ( ! skipISOChecks ) { if ( rtt_be_ExistsCoincidentNode(topo, pt) ) /*x*/ { rterror(iface->ctx, "SQL/MM Spatial exception - coincident node"); return -1; } if ( rtt_be_ExistsEdgeIntersectingPoint(topo, pt) ) /*x*/ { rterror(iface->ctx, "SQL/MM Spatial exception - edge crosses node."); return -1; } } if ( face == -1 || ! skipISOChecks ) { foundInFace = rtt_be_getFaceContainingPoint(topo, pt); /*x*/ if ( foundInFace == -2 ) { rterror(topo->be_iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( foundInFace == -1 ) foundInFace = 0; } if ( face == -1 ) { face = foundInFace; } else if ( ! skipISOChecks && foundInFace != face ) { #if 0 rterror(iface->ctx, "SQL/MM Spatial exception - within face %d (not %d)", foundInFace, face); #else rterror(topo->be_iface->ctx, "SQL/MM Spatial exception - not within face"); #endif return -1; } RTT_ISO_NODE node; node.node_id = -1; node.containing_face = face; node.geom = pt; if ( ! rtt_be_insertNodes(topo, &node, 1) ) { rterror(topo->be_iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } return node.node_id; } /* Check that an edge does not cross an existing node or edge * * @param myself the id of an edge to skip, if any * (for ChangeEdgeGeom). Can use 0 for none. * * Return -1 on cross or error, 0 if everything is fine. * Note that before returning -1, rterror is invoked... */ static int _rtt_CheckEdgeCrossing( RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, const RTLINE *geom, RTT_ELEMID myself ) { int i, num_nodes, num_edges; RTT_ISO_EDGE *edges; RTT_ISO_NODE *nodes; const RTGBOX *edgebox; GEOSGeometry *edgegg; const GEOSPreparedGeometry* prepared_edge; const RTT_BE_IFACE *iface = topo->be_iface; _rtt_EnsureGeos(iface->ctx); edgegg = RTGEOM2GEOS(iface->ctx, rtline_as_rtgeom(iface->ctx, geom), 0); if ( ! edgegg ) { rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } prepared_edge = GEOSPrepare_r(iface->ctx->gctx, edgegg ); if ( ! prepared_edge ) { rterror(iface->ctx, "Could not prepare edge geometry: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } edgebox = rtgeom_get_bbox(iface->ctx, rtline_as_rtgeom(iface->ctx, geom) ); /* loop over each node within the edge's gbox */ nodes = rtt_be_getNodeWithinBox2D( topo, edgebox, &num_nodes, RTT_COL_NODE_ALL, 0 ); RTDEBUGF(1, "rtt_be_getNodeWithinBox2D returned %d nodes", num_nodes); if ( num_nodes == -1 ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; inode_id == start_node ) continue; if ( node->node_id == end_node ) continue; /* check if the edge contains this node (not on boundary) */ nodegg = RTGEOM2GEOS(iface->ctx, rtpoint_as_rtgeom(iface->ctx, node->geom) , 0); /* ST_RelateMatch(rec.relate, 'T********') */ contains = GEOSPreparedContains_r(iface->ctx->gctx, prepared_edge, nodegg ); GEOSGeom_destroy_r(iface->ctx->gctx, nodegg); if (contains == 2) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); _rtt_release_nodes(iface->ctx, nodes, num_nodes); rterror(iface->ctx, "GEOS exception on PreparedContains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } if ( contains ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); _rtt_release_nodes(iface->ctx, nodes, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - geometry crosses a node"); return -1; } } if ( nodes ) _rtt_release_nodes(iface->ctx, nodes, num_nodes); /* may be NULL if num_nodes == 0 */ /* loop over each edge within the edge's gbox */ edges = rtt_be_getEdgeWithinBox2D( topo, edgebox, &num_edges, RTT_COL_EDGE_ALL, 0 ); RTDEBUGF(1, "rtt_be_getEdgeWithinBox2D returned %d edges", num_edges); if ( num_edges == -1 ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; iedge_id; GEOSGeometry *eegg; char *relate; int match; if ( edge_id == myself ) continue; if ( ! edge->geom ) { _rtt_release_edges(iface->ctx, edges, num_edges); rterror(iface->ctx, "Edge %d has NULL geometry!", edge_id); return -1; } eegg = RTGEOM2GEOS(iface->ctx, rtline_as_rtgeom(iface->ctx, edge->geom), 0 ); if ( ! eegg ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); _rtt_release_edges(iface->ctx, edges, num_edges); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } RTDEBUGF(2, "Edge %d converted to GEOS", edge_id); /* check if the edge crosses our edge (not boundary-boundary) */ relate = GEOSRelateBoundaryNodeRule_r(iface->ctx->gctx, eegg, edgegg, 2); if ( ! relate ) { GEOSGeom_destroy_r(iface->ctx->gctx, eegg); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); _rtt_release_edges(iface->ctx, edges, num_edges); rterror(iface->ctx, "GEOSRelateBoundaryNodeRule error: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } RTDEBUGF(2, "Edge %d relate pattern is %s", edge_id, relate); match = GEOSRelatePatternMatch_r(iface->ctx->gctx, relate, "F********"); if ( match ) { /* error or no interior intersection */ GEOSGeom_destroy_r(iface->ctx->gctx, eegg); GEOSFree_r(iface->ctx->gctx, relate); if ( match == 2 ) { _rtt_release_edges(iface->ctx, edges, num_edges); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); rterror(iface->ctx, "GEOSRelatePatternMatch error: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } else continue; /* no interior intersection */ } match = GEOSRelatePatternMatch_r(iface->ctx->gctx, relate, "1FFF*FFF2"); if ( match ) { _rtt_release_edges(iface->ctx, edges, num_edges); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); GEOSGeom_destroy_r(iface->ctx->gctx, eegg); GEOSFree_r(iface->ctx->gctx, relate); if ( match == 2 ) { rterror(iface->ctx, "GEOSRelatePatternMatch error: %s", rtgeom_get_last_geos_error(iface->ctx)); } else { rterror(iface->ctx, "SQL/MM Spatial exception - coincident edge %" RTTFMT_ELEMID, edge_id); } return -1; } match = GEOSRelatePatternMatch_r(iface->ctx->gctx, relate, "1********"); if ( match ) { _rtt_release_edges(iface->ctx, edges, num_edges); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); GEOSGeom_destroy_r(iface->ctx->gctx, eegg); GEOSFree_r(iface->ctx->gctx, relate); if ( match == 2 ) { rterror(iface->ctx, "GEOSRelatePatternMatch error: %s", rtgeom_get_last_geos_error(iface->ctx)); } else { rterror(iface->ctx, "Spatial exception - geometry intersects edge %" RTTFMT_ELEMID, edge_id); } return -1; } match = GEOSRelatePatternMatch_r(iface->ctx->gctx, relate, "T********"); if ( match ) { _rtt_release_edges(iface->ctx, edges, num_edges); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); GEOSGeom_destroy_r(iface->ctx->gctx, eegg); GEOSFree_r(iface->ctx->gctx, relate); if ( match == 2 ) { rterror(iface->ctx, "GEOSRelatePatternMatch error: %s", rtgeom_get_last_geos_error(iface->ctx)); } else { rterror(iface->ctx, "SQL/MM Spatial exception - geometry crosses edge %" RTTFMT_ELEMID, edge_id); } return -1; } RTDEBUGF(2, "Edge %d analisys completed, it does no harm", edge_id); GEOSFree_r(iface->ctx->gctx, relate); GEOSGeom_destroy_r(iface->ctx->gctx, eegg); } if ( edges ) _rtt_release_edges(iface->ctx, edges, num_edges); /* would be NULL if num_edges was 0 */ GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepared_edge); GEOSGeom_destroy_r(iface->ctx->gctx, edgegg); return 0; } RTT_ELEMID rtt_AddIsoEdge( RTT_TOPOLOGY* topo, RTT_ELEMID startNode, RTT_ELEMID endNode, const RTLINE* geom ) { int num_nodes; int i; RTT_ISO_EDGE newedge; RTT_ISO_NODE *endpoints; RTT_ELEMID containing_face = -1; RTT_ELEMID node_ids[2]; RTT_ISO_NODE updated_nodes[2]; int skipISOChecks = 0; RTPOINT2D p1, p2; const RTT_BE_IFACE *iface = topo->be_iface; /* NOT IN THE SPECS: * A closed edge is never isolated (as it forms a face) */ if ( startNode == endNode ) { rterror(iface->ctx, "Closed edges would not be isolated, try rtt_AddEdgeNewFaces"); return -1; } if ( ! skipISOChecks ) { /* Acurve must be simple */ if ( ! rtgeom_is_simple(iface->ctx, rtline_as_rtgeom(iface->ctx, geom)) ) { rterror(iface->ctx, "SQL/MM Spatial exception - curve not simple"); return -1; } } /* * Check for: * existence of nodes * nodes faces match * Extract: * nodes face id * nodes geoms */ num_nodes = 2; node_ids[0] = startNode; node_ids[1] = endNode; endpoints = rtt_be_getNodeById( topo, node_ids, &num_nodes, RTT_COL_NODE_ALL ); if ( num_nodes < 0 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( num_nodes < 2 ) { if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - non-existent node"); return -1; } for ( i=0; icontaining_face == -1 ) { _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - not isolated node"); return -1; } if ( containing_face == -1 ) containing_face = n->containing_face; else if ( containing_face != n->containing_face ) { _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - nodes in different faces"); return -1; } if ( ! skipISOChecks ) { if ( n->node_id == startNode ) { /* l) Check that start point of acurve match start node geoms. */ rt_getPoint2d_p(iface->ctx, geom->points, 0, &p1); rt_getPoint2d_p(iface->ctx, n->geom->point, 0, &p2); if ( ! p2d_same(iface->ctx, &p1, &p2) ) { _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - " "start node not geometry start point."); return -1; } } else { /* m) Check that end point of acurve match end node geoms. */ rt_getPoint2d_p(iface->ctx, geom->points, geom->points->npoints-1, &p1); rt_getPoint2d_p(iface->ctx, n->geom->point, 0, &p2); if ( ! p2d_same(iface->ctx, &p1, &p2) ) { _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - " "end node not geometry end point."); return -1; } } } } if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); if ( ! skipISOChecks ) { if ( _rtt_CheckEdgeCrossing( topo, startNode, endNode, geom, 0 ) ) { /* would have called rterror already, leaking :( */ return -1; } } /* * All checks passed, time to prepare the new edge */ newedge.edge_id = rtt_be_getNextEdgeId( topo ); if ( newedge.edge_id == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* TODO: this should likely be an exception instead ! */ if ( containing_face == -1 ) containing_face = 0; newedge.start_node = startNode; newedge.end_node = endNode; newedge.face_left = newedge.face_right = containing_face; newedge.next_left = -newedge.edge_id; newedge.next_right = newedge.edge_id; newedge.geom = (RTLINE *)geom; /* const cast.. */ int ret = rtt_be_insertEdges(topo, &newedge, 1); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { rterror(iface->ctx, "Insertion of split edge failed (no reason)"); return -1; } /* * Update Node containing_face values * * the nodes anode and anothernode are no more isolated * because now there is an edge connecting them */ updated_nodes[0].node_id = startNode; updated_nodes[0].containing_face = -1; updated_nodes[1].node_id = endNode; updated_nodes[1].containing_face = -1; ret = rtt_be_updateNodesById(topo, updated_nodes, 2, RTT_COL_NODE_CONTAINING_FACE); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } return newedge.edge_id; } static RTCOLLECTION * _rtt_EdgeSplit( RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTPOINT* pt, int skipISOChecks, RTT_ISO_EDGE** oldedge ) { RTGEOM *split; RTCOLLECTION *split_col; int i; const RTT_BE_IFACE *iface = topo->be_iface; /* Get edge */ i = 1; RTDEBUG(1, "calling rtt_be_getEdgeById"); *oldedge = rtt_be_getEdgeById(topo, &edge, &i, RTT_COL_EDGE_ALL); RTDEBUGF(1, "rtt_be_getEdgeById returned %p", *oldedge); if ( ! *oldedge ) { RTDEBUGF(1, "rtt_be_getEdgeById returned NULL and set i=%d", i); if ( i == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } else if ( i == 0 ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge"); return NULL; } else { rterror(iface->ctx, "Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return NULL; } } /* * - check if a coincident node already exists */ if ( ! skipISOChecks ) { RTDEBUG(1, "calling rtt_be_ExistsCoincidentNode"); if ( rtt_be_ExistsCoincidentNode(topo, pt) ) /*x*/ { RTDEBUG(1, "rtt_be_ExistsCoincidentNode returned"); _rtt_release_edges(iface->ctx, *oldedge, 1); rterror(iface->ctx, "SQL/MM Spatial exception - coincident node"); return NULL; } RTDEBUG(1, "rtt_be_ExistsCoincidentNode returned"); } /* Split edge */ split = rtgeom_split(iface->ctx, (RTGEOM*)(*oldedge)->geom, (RTGEOM*)pt); if ( ! split ) { _rtt_release_edges(iface->ctx, *oldedge, 1); rterror(iface->ctx, "could not split edge by point ?"); return NULL; } split_col = rtgeom_as_rtcollection(iface->ctx, split); if ( ! split_col ) { _rtt_release_edges(iface->ctx, *oldedge, 1); rtgeom_free(iface->ctx, split); rterror(iface->ctx, "rtgeom_as_rtcollection returned NULL"); return NULL; } if (split_col->ngeoms < 2) { _rtt_release_edges(iface->ctx, *oldedge, 1); rtgeom_free(iface->ctx, split); rterror(iface->ctx, "SQL/MM Spatial exception - point not on edge"); return NULL; } #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, (RTGEOM*)split_col, RTWKT_EXTENDED, 2, &sz); RTDEBUGF(1, "returning split col: %s", wkt); rtfree(iface->ctx, wkt); } #endif return split_col; } RTT_ELEMID rtt_ModEdgeSplit( RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTPOINT* pt, int skipISOChecks ) { RTT_ISO_NODE node; RTT_ISO_EDGE* oldedge = NULL; RTCOLLECTION *split_col; const RTGEOM *oldedge_geom; const RTGEOM *newedge_geom; RTT_ISO_EDGE newedge1; RTT_ISO_EDGE seledge, updedge, excedge; int ret; const RTT_BE_IFACE *iface = topo->be_iface; split_col = _rtt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge ); if ( ! split_col ) return -1; /* should have raised an exception */ oldedge_geom = split_col->geoms[0]; newedge_geom = split_col->geoms[1]; /* Make sure the SRID is set on the subgeom */ ((RTGEOM*)oldedge_geom)->srid = split_col->srid; ((RTGEOM*)newedge_geom)->srid = split_col->srid; /* Add new node, getting new id back */ node.node_id = -1; node.containing_face = -1; /* means not-isolated */ node.geom = pt; if ( ! rtt_be_insertNodes(topo, &node, 1) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if (node.node_id == -1) { /* should have been set by backend */ _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend coding error: " "insertNodes callback did not return node_id"); return -1; } /* Insert the new edge */ newedge1.edge_id = rtt_be_getNextEdgeId(topo); if ( newedge1.edge_id == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } newedge1.start_node = node.node_id; newedge1.end_node = oldedge->end_node; newedge1.face_left = oldedge->face_left; newedge1.face_right = oldedge->face_right; newedge1.next_left = oldedge->next_left == -oldedge->edge_id ? -newedge1.edge_id : oldedge->next_left; newedge1.next_right = -oldedge->edge_id; newedge1.geom = rtgeom_as_rtline(iface->ctx, newedge_geom); /* rtgeom_split of a line should only return lines ... */ if ( ! newedge1.geom ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "first geometry in rtgeom_split output is not a line"); return -1; } ret = rtt_be_insertEdges(topo, &newedge1, 1); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Insertion of split edge failed (no reason)"); return -1; } /* Update the old edge */ updedge.geom = rtgeom_as_rtline(iface->ctx, oldedge_geom); /* rtgeom_split of a line should only return lines ... */ if ( ! updedge.geom ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "second geometry in rtgeom_split output is not a line"); return -1; } updedge.next_left = newedge1.edge_id; updedge.end_node = node.node_id; ret = rtt_be_updateEdges(topo, oldedge, RTT_COL_EDGE_EDGE_ID, &updedge, RTT_COL_EDGE_GEOM|RTT_COL_EDGE_NEXT_LEFT|RTT_COL_EDGE_END_NODE, NULL, 0); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Edge being split (%d) disappeared during operations?", oldedge->edge_id); return -1; } else if ( ret > 1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "More than a single edge found with id %d !", oldedge->edge_id); return -1; } /* Update all next edge references to match new layout (ST_ModEdgeSplit) */ updedge.next_right = -newedge1.edge_id; excedge.edge_id = newedge1.edge_id; seledge.next_right = -oldedge->edge_id; seledge.start_node = oldedge->end_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_RIGHT|RTT_COL_EDGE_START_NODE, &updedge, RTT_COL_EDGE_NEXT_RIGHT, &excedge, RTT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = -newedge1.edge_id; excedge.edge_id = newedge1.edge_id; seledge.next_left = -oldedge->edge_id; seledge.end_node = oldedge->end_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_LEFT|RTT_COL_EDGE_END_NODE, &updedge, RTT_COL_EDGE_NEXT_LEFT, &excedge, RTT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Update TopoGeometries composition */ ret = rtt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedge1.edge_id, -1); if ( ! ret ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); /* return new node id */ return node.node_id; } RTT_ELEMID rtt_NewEdgesSplit( RTT_TOPOLOGY* topo, RTT_ELEMID edge, RTPOINT* pt, int skipISOChecks ) { RTT_ISO_NODE node; RTT_ISO_EDGE* oldedge = NULL; RTCOLLECTION *split_col; const RTGEOM *oldedge_geom; const RTGEOM *newedge_geom; RTT_ISO_EDGE newedges[2]; RTT_ISO_EDGE seledge, updedge; int ret; const RTT_BE_IFACE *iface = topo->be_iface; split_col = _rtt_EdgeSplit( topo, edge, pt, skipISOChecks, &oldedge ); if ( ! split_col ) return -1; /* should have raised an exception */ oldedge_geom = split_col->geoms[0]; newedge_geom = split_col->geoms[1]; /* Make sure the SRID is set on the subgeom */ ((RTGEOM*)oldedge_geom)->srid = split_col->srid; ((RTGEOM*)newedge_geom)->srid = split_col->srid; /* Add new node, getting new id back */ node.node_id = -1; node.containing_face = -1; /* means not-isolated */ node.geom = pt; if ( ! rtt_be_insertNodes(topo, &node, 1) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if (node.node_id == -1) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); /* should have been set by backend */ rterror(iface->ctx, "Backend coding error: " "insertNodes callback did not return node_id"); return -1; } /* Delete the old edge */ seledge.edge_id = edge; ret = rtt_be_deleteEdges(topo, &seledge, RTT_COL_EDGE_EDGE_ID); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Get new edges identifiers */ newedges[0].edge_id = rtt_be_getNextEdgeId(topo); if ( newedges[0].edge_id == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } newedges[1].edge_id = rtt_be_getNextEdgeId(topo); if ( newedges[1].edge_id == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Define the first new edge (to new node) */ newedges[0].start_node = oldedge->start_node; newedges[0].end_node = node.node_id; newedges[0].face_left = oldedge->face_left; newedges[0].face_right = oldedge->face_right; newedges[0].next_left = newedges[1].edge_id; if ( oldedge->next_right == edge ) newedges[0].next_right = newedges[0].edge_id; else if ( oldedge->next_right == -edge ) newedges[0].next_right = -newedges[1].edge_id; else newedges[0].next_right = oldedge->next_right; newedges[0].geom = rtgeom_as_rtline(iface->ctx, oldedge_geom); /* rtgeom_split of a line should only return lines ... */ if ( ! newedges[0].geom ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "first geometry in rtgeom_split output is not a line"); return -1; } /* Define the second new edge (from new node) */ newedges[1].start_node = node.node_id; newedges[1].end_node = oldedge->end_node; newedges[1].face_left = oldedge->face_left; newedges[1].face_right = oldedge->face_right; newedges[1].next_right = -newedges[0].edge_id; if ( oldedge->next_left == -edge ) newedges[1].next_left = -newedges[1].edge_id; else if ( oldedge->next_left == edge ) newedges[1].next_left = newedges[0].edge_id; else newedges[1].next_left = oldedge->next_left; newedges[1].geom = rtgeom_as_rtline(iface->ctx, newedge_geom); /* rtgeom_split of a line should only return lines ... */ if ( ! newedges[1].geom ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "second geometry in rtgeom_split output is not a line"); return -1; } /* Insert both new edges */ ret = rtt_be_insertEdges(topo, newedges, 2); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Insertion of split edge failed (no reason)"); return -1; } /* Update all next edge references pointing to old edge id */ updedge.next_right = newedges[1].edge_id; seledge.next_right = edge; seledge.start_node = oldedge->start_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_RIGHT|RTT_COL_EDGE_START_NODE, &updedge, RTT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_right = -newedges[0].edge_id; seledge.next_right = -edge; seledge.start_node = oldedge->end_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_RIGHT|RTT_COL_EDGE_START_NODE, &updedge, RTT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = newedges[0].edge_id; seledge.next_left = edge; seledge.end_node = oldedge->start_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_LEFT|RTT_COL_EDGE_END_NODE, &updedge, RTT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } updedge.next_left = -newedges[1].edge_id; seledge.next_left = -edge; seledge.end_node = oldedge->end_node; ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_LEFT|RTT_COL_EDGE_END_NODE, &updedge, RTT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( ret == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_release(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Update TopoGeometries composition */ ret = rtt_be_updateTopoGeomEdgeSplit(topo, oldedge->edge_id, newedges[0].edge_id, newedges[1].edge_id); if ( ! ret ) { _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } _rtt_release_edges(iface->ctx, oldedge, 1); rtcollection_free(iface->ctx, split_col); /* return new node id */ return node.node_id; } /* Data structure used by AddEdgeX functions */ typedef struct edgeend_t { /* Signed identifier of next clockwise edge (+outgoing,-incoming) */ RTT_ELEMID nextCW; /* Identifier of face between myaz and next CW edge */ RTT_ELEMID cwFace; /* Signed identifier of next counterclockwise edge (+outgoing,-incoming) */ RTT_ELEMID nextCCW; /* Identifier of face between myaz and next CCW edge */ RTT_ELEMID ccwFace; int was_isolated; double myaz; /* azimuth of edgeend geometry */ } edgeend; /* * Get first distinct vertex from endpoint * @param pa the pointarray to seek points in * @param ref the point we want to search a distinct one * @param from vertex index to start from * @param dir 1 to go forward * -1 to go backward * @return 0 if edge is collapsed (no distinct points) */ static int _rtt_FirstDistinctVertex2D(const RTCTX *ctx, const RTPOINTARRAY* pa, RTPOINT2D *ref, int from, int dir, RTPOINT2D *op) { int i, toofar, inc; RTPOINT2D fp; if ( dir > 0 ) { toofar = pa->npoints; inc = 1; } else { toofar = -1; inc = -1; } RTDEBUGF(1, "first point is index %d", from); fp = *ref; /* rt_getPoint2d_p(ctx, pa, from, &fp); */ for ( i = from+inc; i != toofar; i += inc ) { RTDEBUGF(1, "testing point %d", i); rt_getPoint2d_p(ctx, pa, i, op); /* pick next point */ if ( p2d_same(ctx, op, &fp) ) continue; /* equal to startpoint */ /* this is a good one, neither same of start nor of end point */ return 1; /* found */ } /* no distinct vertices found */ return 0; } /* * Return non-zero on failure (rterror is invoked in that case) * Possible failures: * -1 no two distinct vertices exist * -2 azimuth computation failed for first edge end */ static int _rtt_InitEdgeEndByLine(const RTCTX *ctx, edgeend *fee, edgeend *lee, RTLINE *edge, RTPOINT2D *fp, RTPOINT2D *lp) { RTPOINTARRAY *pa = edge->points; RTPOINT2D pt; fee->nextCW = fee->nextCCW = lee->nextCW = lee->nextCCW = 0; fee->cwFace = fee->ccwFace = lee->cwFace = lee->ccwFace = -1; /* Compute azimuth of first edge end */ RTDEBUG(1, "computing azimuth of first edge end"); if ( ! _rtt_FirstDistinctVertex2D(ctx, pa, fp, 0, 1, &pt) ) { rterror(ctx, "Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(ctx, fp, &pt, &(fee->myaz)) ) { rterror(ctx, "error computing azimuth of first edgeend [%g %g,%g %g]", fp->x, fp->y, pt.x, pt.y); return -2; } RTDEBUGF(1, "azimuth of first edge end [%g %g,%g %g] is %g", fp->x, fp->y, pt.x, pt.y, fee->myaz); /* Compute azimuth of second edge end */ RTDEBUG(1, "computing azimuth of second edge end"); if ( ! _rtt_FirstDistinctVertex2D(ctx, pa, lp, pa->npoints-1, -1, &pt) ) { rterror(ctx, "Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(ctx, lp, &pt, &(lee->myaz)) ) { rterror(ctx, "error computing azimuth of last edgeend [%g %g,%g %g]", lp->x, lp->y, pt.x, pt.y); return -2; } RTDEBUGF(1, "azimuth of last edge end [%g %g,%g %g] is %g", lp->x, lp->y, pt.x, pt.y, lee->myaz); return 0; } /* * Find the first edges encountered going clockwise and counterclockwise * around a node, starting from the given azimuth, and take * note of the face on the both sides. * * @param topo the topology to act upon * @param node the identifier of the node to analyze * @param data input (myaz) / output (nextCW, nextCCW) parameter * @param other edgeend, if also incident to given node (closed edge). * @param myedge_id identifier of the edge id that data->myaz belongs to * @return number of incident edges found * */ static int _rtt_FindAdjacentEdges( RTT_TOPOLOGY* topo, RTT_ELEMID node, edgeend *data, edgeend *other, int myedge_id ) { RTT_ISO_EDGE *edges; int numedges = 1; int i; double minaz, maxaz; double az, azdif; const RTT_BE_IFACE *iface = topo->be_iface; data->nextCW = data->nextCCW = 0; data->cwFace = data->ccwFace = -1; if ( other ) { azdif = other->myaz - data->myaz; if ( azdif < 0 ) azdif += 2 * M_PI; minaz = maxaz = azdif; /* TODO: set nextCW/nextCCW/cwFace/ccwFace to other->something ? */ RTDEBUGF(1, "Other edge end has cwFace=%d and ccwFace=%d", other->cwFace, other->ccwFace); } else { minaz = maxaz = -1; } RTDEBUGF(1, "Looking for edges incident to node %" RTTFMT_ELEMID " and adjacent to azimuth %g", node, data->myaz); /* Get incident edges */ edges = rtt_be_getEdgeByNode( topo, &node, &numedges, RTT_COL_EDGE_ALL ); if ( numedges == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return 0; } RTDEBUGF(1, "getEdgeByNode returned %d edges, minaz=%g, maxaz=%g", numedges, minaz, maxaz); /* For each incident edge-end (1 or 2): */ for ( i = 0; i < numedges; ++i ) { RTT_ISO_EDGE *edge; RTGEOM *g; RTGEOM *cleangeom; RTPOINT2D p1, p2; RTPOINTARRAY *pa; edge = &(edges[i]); if ( edge->edge_id == myedge_id ) continue; g = rtline_as_rtgeom(iface->ctx, edge->geom); /* NOTE: remove_repeated_points call could be replaced by * some other mean to pick two distinct points for endpoints */ cleangeom = rtgeom_remove_repeated_points(iface->ctx, g, 0 ); pa = rtgeom_as_rtline(iface->ctx, cleangeom)->points; if ( pa->npoints < 2 ) {{ RTT_ELEMID id = edge->edge_id; _rtt_release_edges(iface->ctx, edges, numedges); rtgeom_free(iface->ctx, cleangeom); rterror(iface->ctx, "corrupted topology: edge %" RTTFMT_ELEMID " does not have two distinct points", id); return -1; }} if ( edge->start_node == node ) { rt_getPoint2d_p(iface->ctx, pa, 0, &p1); rt_getPoint2d_p(iface->ctx, pa, 1, &p2); RTDEBUGF(1, "edge %" RTTFMT_ELEMID " starts on node %" RTTFMT_ELEMID ", edgeend is %g,%g-%g,%g", edge->edge_id, node, p1.x, p1.y, p2.x, p2.y); if ( ! azimuth_pt_pt(iface->ctx, &p1, &p2, &az) ) {{ RTT_ELEMID id = edge->edge_id; _rtt_release_edges(iface->ctx, edges, numedges); rtgeom_free(iface->ctx, cleangeom); rterror(iface->ctx, "error computing azimuth of edge %d first edgeend [%g,%g-%g,%g]", id, p1.x, p1.y, p2.x, p2.y); return -1; }} azdif = az - data->myaz; RTDEBUGF(1, "azimuth of edge %" RTTFMT_ELEMID ": %g (diff: %g)", edge->edge_id, az, azdif); if ( azdif < 0 ) azdif += 2 * M_PI; if ( minaz == -1 ) { minaz = maxaz = azdif; data->nextCW = data->nextCCW = edge->edge_id; /* outgoing */ data->cwFace = edge->face_left; data->ccwFace = edge->face_right; RTDEBUGF(1, "new nextCW and nextCCW edge is %" RTTFMT_ELEMID ", outgoing, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (face_right is new ccwFace, face_left is new cwFace)", edge->edge_id, edge->face_left, edge->face_right); } else { if ( azdif < minaz ) { data->nextCW = edge->edge_id; /* outgoing */ data->cwFace = edge->face_left; RTDEBUGF(1, "new nextCW edge is %" RTTFMT_ELEMID ", outgoing, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (previous had minaz=%g, face_left is new cwFace)", edge->edge_id, edge->face_left, edge->face_right, minaz); minaz = azdif; } else if ( azdif > maxaz ) { data->nextCCW = edge->edge_id; /* outgoing */ data->ccwFace = edge->face_right; RTDEBUGF(1, "new nextCCW edge is %" RTTFMT_ELEMID ", outgoing, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (previous had maxaz=%g, face_right is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right, maxaz); maxaz = azdif; } } } if ( edge->end_node == node ) { rt_getPoint2d_p(iface->ctx, pa, pa->npoints-1, &p1); rt_getPoint2d_p(iface->ctx, pa, pa->npoints-2, &p2); RTDEBUGF(1, "edge %" RTTFMT_ELEMID " ends on node %" RTTFMT_ELEMID ", edgeend is %g,%g-%g,%g", edge->edge_id, node, p1.x, p1.y, p2.x, p2.y); if ( ! azimuth_pt_pt(iface->ctx, &p1, &p2, &az) ) {{ RTT_ELEMID id = edge->edge_id; _rtt_release_edges(iface->ctx, edges, numedges); rtgeom_free(iface->ctx, cleangeom); rterror(iface->ctx, "error computing azimuth of edge %d last edgeend [%g,%g-%g,%g]", id, p1.x, p1.y, p2.x, p2.y); return -1; }} azdif = az - data->myaz; RTDEBUGF(1, "azimuth of edge %" RTTFMT_ELEMID ": %g (diff: %g)", edge->edge_id, az, azdif); if ( azdif < 0 ) azdif += 2 * M_PI; if ( minaz == -1 ) { minaz = maxaz = azdif; data->nextCW = data->nextCCW = -edge->edge_id; /* incoming */ data->cwFace = edge->face_right; data->ccwFace = edge->face_left; RTDEBUGF(1, "new nextCW and nextCCW edge is %" RTTFMT_ELEMID ", incoming, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (face_right is new cwFace, face_left is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right); } else { if ( azdif < minaz ) { data->nextCW = -edge->edge_id; /* incoming */ data->cwFace = edge->face_right; RTDEBUGF(1, "new nextCW edge is %" RTTFMT_ELEMID ", incoming, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (previous had minaz=%g, face_right is new cwFace)", edge->edge_id, edge->face_left, edge->face_right, minaz); minaz = azdif; } else if ( azdif > maxaz ) { data->nextCCW = -edge->edge_id; /* incoming */ data->ccwFace = edge->face_left; RTDEBUGF(1, "new nextCCW edge is %" RTTFMT_ELEMID ", outgoing, from start point, " "with face_left %" RTTFMT_ELEMID " and face_right %" RTTFMT_ELEMID " (previous had maxaz=%g, face_left is new ccwFace)", edge->edge_id, edge->face_left, edge->face_right, maxaz); maxaz = azdif; } } } rtgeom_free(iface->ctx, cleangeom); } if ( numedges ) _rtt_release_edges(iface->ctx, edges, numedges); RTDEBUGF(1, "edges adjacent to azimuth %g" " (incident to node %" RTTFMT_ELEMID ")" ": CW:%" RTTFMT_ELEMID "(%g) CCW:%" RTTFMT_ELEMID "(%g)", data->myaz, node, data->nextCW, minaz, data->nextCCW, maxaz); if ( myedge_id < 1 && numedges && data->cwFace != data->ccwFace ) { if ( data->cwFace != -1 && data->ccwFace != -1 ) { rterror(iface->ctx, "Corrupted topology: adjacent edges %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID " bind different face (%" RTTFMT_ELEMID " and %" RTTFMT_ELEMID ")", data->nextCW, data->nextCCW, data->cwFace, data->ccwFace); return -1; } } /* Return number of incident edges found */ return numedges; } /* * Get a point internal to the line and write it into the "ip" * parameter * * return 0 on failure (line is empty or collapsed), 1 otherwise */ static int _rtt_GetInteriorEdgePoint(const RTCTX *ctx, const RTLINE* edge, RTPOINT2D* ip) { int i; RTPOINT2D fp, lp, tp; RTPOINTARRAY *pa = edge->points; if ( pa->npoints < 2 ) return 0; /* empty or structurally collapsed */ rt_getPoint2d_p(ctx, pa, 0, &fp); /* save first point */ rt_getPoint2d_p(ctx, pa, pa->npoints-1, &lp); /* save last point */ for (i=1; inpoints-1; ++i) { rt_getPoint2d_p(ctx, pa, i, &tp); /* pick next point */ if ( p2d_same(ctx, &tp, &fp) ) continue; /* equal to startpoint */ if ( p2d_same(ctx, &tp, &lp) ) continue; /* equal to endpoint */ /* this is a good one, neither same of start nor of end point */ *ip = tp; return 1; /* found */ } /* no distinct vertex found */ /* interpolate if start point != end point */ if ( p2d_same(ctx, &fp, &lp) ) return 0; /* no distinct points in edge */ ip->x = fp.x + ( (lp.x - fp.x) * 0.5 ); ip->y = fp.y + ( (lp.y - fp.y) * 0.5 ); return 1; } /* * Add a split face by walking on the edge side. * * @param topo the topology to act upon * @param sedge edge id and walking side and direction * (forward,left:positive backward,right:negative) * @param face the face in which the edge identifier is known to be * @param mbr_only do not create a new face but update MBR of the current * * @return: * -1: if mbr_only was requested * 0: if the edge does not form a ring * -1: if it is impossible to create a face on the requested side * ( new face on the side is the universe ) * -2: error * >0 : id of newly added face */ static RTT_ELEMID _rtt_AddFaceSplit( RTT_TOPOLOGY* topo, RTT_ELEMID sedge, RTT_ELEMID face, int mbr_only ) { int numedges, numfaceedges, i, j; int newface_outside; int num_signed_edge_ids; RTT_ELEMID *signed_edge_ids; RTT_ELEMID *edge_ids; RTT_ISO_EDGE *edges; RTT_ISO_EDGE *ring_edges; RTT_ISO_EDGE *forward_edges = NULL; int forward_edges_count = 0; RTT_ISO_EDGE *backward_edges = NULL; int backward_edges_count = 0; const RTT_BE_IFACE *iface = topo->be_iface; signed_edge_ids = rtt_be_getRingEdges(topo, sedge, &num_signed_edge_ids, 0); if ( ! signed_edge_ids ) { rterror(iface->ctx, "Backend error (no ring edges for edge %" RTTFMT_ELEMID "): %s", sedge, rtt_be_lastErrorMessage(topo->be_iface)); return -2; } RTDEBUGF(1, "getRingEdges returned %d edges", num_signed_edge_ids); /* You can't get to the other side of an edge forming a ring */ for (i=0; ictx, signed_edge_ids ); return 0; } } RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " split face %" RTTFMT_ELEMID " (mbr_only:%d)", sedge, face, mbr_only); /* Construct a polygon using edges of the ring */ numedges = 0; edge_ids = rtalloc(iface->ctx, sizeof(RTT_ELEMID)*num_signed_edge_ids); for (i=0; ictx, edge_ids ); if ( i == -1 ) { rtfree(iface->ctx, signed_edge_ids ); /* ring_edges should be NULL */ rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } else if ( i != numedges ) { rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Unexpected error: %d edges found when expecting %d", i, numedges); return -2; } /* Should now build a polygon with those edges, in the order * given by GetRingEdges. */ RTPOINTARRAY *pa = NULL; for ( i=0; ictx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "missing edge that was found in ring edges loop"); return -2; } if ( pa == NULL ) { pa = ptarray_clone_deep(iface->ctx, edge->geom->points); if ( eid < 0 ) ptarray_reverse(iface->ctx, pa); } else { if ( eid < 0 ) { epa = ptarray_clone_deep(iface->ctx, edge->geom->points); ptarray_reverse(iface->ctx, epa); ptarray_append_ptarray(iface->ctx, pa, epa, 0); ptarray_free(iface->ctx, epa); } else { /* avoid a clone here */ ptarray_append_ptarray(iface->ctx, pa, edge->geom->points, 0); } } } RTPOINTARRAY **points = rtalloc(iface->ctx, sizeof(RTPOINTARRAY*)); points[0] = pa; /* NOTE: the ring may very well have collapsed components, * which would make it topologically invalid */ RTPOLY* shell = rtpoly_construct(iface->ctx, 0, 0, 1, points); int isccw = ptarray_isccw(iface->ctx, pa); RTDEBUGF(1, "Ring of edge %" RTTFMT_ELEMID " is %sclockwise", sedge, isccw ? "counter" : ""); const RTGBOX* shellbox = rtgeom_get_bbox(iface->ctx, rtpoly_as_rtgeom(iface->ctx, shell)); if ( face == 0 ) { /* Edge split the universe face */ if ( ! isccw ) { rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); /* Face on the left side of this ring is the universe face. * Next call (for the other side) should create the split face */ RTDEBUG(1, "The left face of this clockwise ring is the universe, " "won't create a new face there"); return -1; } } if ( mbr_only && face != 0 ) { if ( isccw ) {{ RTT_ISO_FACE updface; updface.face_id = face; updface.mbr = (RTGBOX *)shellbox; /* const cast, we won't free it, later */ int ret = rtt_be_updateFacesById( topo, &updface, 1 ); if ( ret == -1 ) { rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox above */ rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != 1 ) { rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox above */ rterror(iface->ctx, "Unexpected error: %d faces found when expecting 1", ret); return -2; } }} rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox above */ return -1; /* mbr only was requested */ } RTT_ISO_FACE *oldface = NULL; RTT_ISO_FACE newface; newface.face_id = -1; if ( face != 0 && ! isccw) {{ /* Face created an hole in an outer face */ int nfaces = 1; oldface = rtt_be_getFaceById(topo, &face, &nfaces, RTT_COL_FACE_ALL); if ( nfaces == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox */ _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( nfaces != 1 ) { rtfree(iface->ctx, signed_edge_ids ); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox */ _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Unexpected error: %d faces found when expecting 1", nfaces); return -2; } newface.mbr = oldface->mbr; }} else { newface.mbr = (RTGBOX *)shellbox; /* const cast, we won't free it, later */ } /* Insert the new face */ int ret = rtt_be_insertFaces( topo, &newface, 1 ); if ( ret == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox */ _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != 1 ) { rtfree(iface->ctx, signed_edge_ids ); rtpoly_free(iface->ctx, shell); /* NOTE: owns shellbox */ _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Unexpected error: %d faces inserted when expecting 1", ret); return -2; } if ( oldface ) { newface.mbr = NULL; /* it is a reference to oldface mbr... */ _rtt_release_faces(iface->ctx, oldface, 1); } /* Update side location of new face edges */ /* We want the new face to be on the left, if possible */ if ( face != 0 && ! isccw ) { /* ring is clockwise in a real face */ /* face shrinked, must update all non-contained edges and nodes */ RTDEBUG(1, "New face is on the outside of the ring, updating rings in former shell"); newface_outside = 1; /* newface is outside */ } else { RTDEBUG(1, "New face is on the inside of the ring, updating forward edges in new ring"); newface_outside = 0; /* newface is inside */ } /* Update edges bounding the old face */ /* (1) fetch all edges where left_face or right_face is = oldface */ int fields = RTT_COL_EDGE_EDGE_ID | RTT_COL_EDGE_FACE_LEFT | RTT_COL_EDGE_FACE_RIGHT | RTT_COL_EDGE_GEOM ; numfaceedges = 1; edges = rtt_be_getEdgeByFace( topo, &face, &numfaceedges, fields, newface.mbr ); if ( numfaceedges == -1 ) { rtfree(iface->ctx, signed_edge_ids ); _rtt_release_edges(iface->ctx, ring_edges, numedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } RTDEBUGF(1, "rtt_be_getEdgeByFace returned %d edges", numfaceedges); GEOSGeometry *shellgg = 0; const GEOSPreparedGeometry* prepshell = 0; shellgg = RTGEOM2GEOS(iface->ctx, rtpoly_as_rtgeom(iface->ctx, shell), 0); if ( ! shellgg ) { rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, signed_edge_ids); _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); rterror(iface->ctx, "Could not convert shell geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } prepshell = GEOSPrepare_r(iface->ctx->gctx, shellgg ); if ( ! prepshell ) { GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, signed_edge_ids); _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); rterror(iface->ctx, "Could not prepare shell geometry: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } if ( numfaceedges ) { forward_edges = rtalloc(iface->ctx, sizeof(RTT_ISO_EDGE)*numfaceedges); forward_edges_count = 0; backward_edges = rtalloc(iface->ctx, sizeof(RTT_ISO_EDGE)*numfaceedges); backward_edges_count = 0; /* (2) loop over the results and: */ for ( i=0; iedge_id ) { /* IDEA: remove entry from signed_edge_ids ? */ RTDEBUGF(1, "Edge %d is a forward edge of the new ring", e->edge_id); forward_edges[forward_edges_count].edge_id = e->edge_id; forward_edges[forward_edges_count++].face_left = newface.face_id; found++; if ( found == 2 ) break; } else if ( -seid == e->edge_id ) { /* IDEA: remove entry from signed_edge_ids ? */ RTDEBUGF(1, "Edge %d is a backward edge of the new ring", e->edge_id); backward_edges[backward_edges_count].edge_id = e->edge_id; backward_edges[backward_edges_count++].face_right = newface.face_id; found++; if ( found == 2 ) break; } } if ( found ) continue; /* We need to check only a single point * (to avoid collapsed elements of the shell polygon * giving false positive). * The point but must not be an endpoint. */ if ( ! _rtt_GetInteriorEdgePoint(iface->ctx, e->geom, &ep) ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, forward_edges); /* contents owned by ring_edges */ rtfree(iface->ctx, backward_edges); /* contents owned by ring_edges */ _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); rterror(iface->ctx, "Could not find interior point for edge %d: %s", e->edge_id, rtgeom_get_last_geos_error(iface->ctx)); return -2; } epgeom = rtpoint_make2d(iface->ctx, 0, ep.x, ep.y); egg = RTGEOM2GEOS(iface->ctx, rtpoint_as_rtgeom(iface->ctx, epgeom) , 0); rtpoint_free(iface->ctx, epgeom); if ( ! egg ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, forward_edges); /* contents owned by ring_edges */ rtfree(iface->ctx, backward_edges); /* contents owned by ring_edges */ _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } /* IDEA: can be optimized by computing this on our side rather * than on GEOS _r(iface->ctx->gctx, saves conversion of big edges) */ /* IDEA: check that bounding box shortcut is taken, or use * shellbox to do it here */ contains = GEOSPreparedContains_r(iface->ctx->gctx, prepshell, egg ); GEOSGeom_destroy_r(iface->ctx->gctx, egg); if ( contains == 2 ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); rtfree(iface->ctx, forward_edges); /* contents owned by ring_edges */ rtfree(iface->ctx, backward_edges); /* contents owned by ring_edges */ _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); rterror(iface->ctx, "GEOS exception on PreparedContains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } RTDEBUGF(1, "Edge %d %scontained in new ring", e->edge_id, (contains?"":"not ")); /* (2.2) skip edges (NOT, if newface_outside) contained in shell */ if ( newface_outside ) { if ( contains ) { RTDEBUGF(1, "Edge %d contained in an hole of the new face", e->edge_id); continue; } } else { if ( ! contains ) { RTDEBUGF(1, "Edge %d not contained in the face shell", e->edge_id); continue; } } /* (2.3) push to forward_edges if left_face = oface */ if ( e->face_left == face ) { RTDEBUGF(1, "Edge %d has new face on the left side", e->edge_id); forward_edges[forward_edges_count].edge_id = e->edge_id; forward_edges[forward_edges_count++].face_left = newface.face_id; } /* (2.4) push to backward_edges if right_face = oface */ if ( e->face_right == face ) { RTDEBUGF(1, "Edge %d has new face on the right side", e->edge_id); backward_edges[backward_edges_count].edge_id = e->edge_id; backward_edges[backward_edges_count++].face_right = newface.face_id; } } /* Update forward edges */ if ( forward_edges_count ) { ret = rtt_be_updateEdgesById(topo, forward_edges, forward_edges_count, RTT_COL_EDGE_FACE_LEFT); if ( ret == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != forward_edges_count ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Unexpected error: %d edges updated when expecting %d", ret, forward_edges_count); return -2; } } /* Update backward edges */ if ( backward_edges_count ) { ret = rtt_be_updateEdgesById(topo, backward_edges, backward_edges_count, RTT_COL_EDGE_FACE_RIGHT); if ( ret == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( ret != backward_edges_count ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Unexpected error: %d edges updated when expecting %d", ret, backward_edges_count); return -2; } } rtfree(iface->ctx, forward_edges); rtfree(iface->ctx, backward_edges); } _rtt_release_edges(iface->ctx, ring_edges, numedges); _rtt_release_edges(iface->ctx, edges, numfaceedges); /* Update isolated nodes which are now in new face */ int numisonodes = 1; fields = RTT_COL_NODE_NODE_ID | RTT_COL_NODE_GEOM; RTT_ISO_NODE *nodes = rtt_be_getNodeByFace(topo, &face, &numisonodes, fields, newface.mbr); if ( numisonodes == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } if ( numisonodes ) { RTT_ISO_NODE *updated_nodes = rtalloc(iface->ctx, sizeof(RTT_ISO_NODE)*numisonodes); int nodes_to_update = 0; for (i=0; ictx, rtpoint_as_rtgeom(iface->ctx, n->geom), 0 ); int contains; if ( ! ngg ) { _rtt_release_nodes(iface->ctx, nodes, numisonodes); if ( prepshell ) GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); if ( shellgg ) GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); rterror(iface->ctx, "Could not convert node geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } contains = GEOSPreparedContains_r(iface->ctx->gctx, prepshell, ngg ); GEOSGeom_destroy_r(iface->ctx->gctx, ngg); if ( contains == 2 ) { _rtt_release_nodes(iface->ctx, nodes, numisonodes); if ( prepshell ) GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); if ( shellgg ) GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); rterror(iface->ctx, "GEOS exception on PreparedContains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -2; } RTDEBUGF(1, "Node %d is %scontained in new ring, newface is %s", n->node_id, contains ? "" : "not ", newface_outside ? "outside" : "inside" ); if ( newface_outside ) { if ( contains ) { RTDEBUGF(1, "Node %d contained in an hole of the new face", n->node_id); continue; } } else { if ( ! contains ) { RTDEBUGF(1, "Node %d not contained in the face shell", n->node_id); continue; } } updated_nodes[nodes_to_update].node_id = n->node_id; updated_nodes[nodes_to_update++].containing_face = newface.face_id; RTDEBUGF(1, "Node %d will be updated", n->node_id); } _rtt_release_nodes(iface->ctx, nodes, numisonodes); if ( nodes_to_update ) { int ret = rtt_be_updateNodesById(topo, updated_nodes, nodes_to_update, RTT_COL_NODE_CONTAINING_FACE); if ( ret == -1 ) { rtfree(iface->ctx, signed_edge_ids ); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -2; } } rtfree(iface->ctx, updated_nodes); } GEOSPreparedGeom_destroy_r(iface->ctx->gctx, prepshell); GEOSGeom_destroy_r(iface->ctx->gctx, shellgg); rtfree(iface->ctx, signed_edge_ids); rtpoly_free(iface->ctx, shell); return newface.face_id; } static RTT_ELEMID _rtt_AddEdge( RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, RTLINE *geom, int skipChecks, int modFace ) { RTT_ISO_EDGE newedge; RTGEOM *cleangeom; edgeend span; /* start point analisys */ edgeend epan; /* end point analisys */ RTPOINT2D p1, pn, p2; RTPOINTARRAY *pa; RTT_ELEMID node_ids[2]; const RTPOINT *start_node_geom = NULL; const RTPOINT *end_node_geom = NULL; int num_nodes; RTT_ISO_NODE *endpoints; int i; int prev_left; int prev_right; RTT_ISO_EDGE seledge; RTT_ISO_EDGE updedge; const RTT_BE_IFACE *iface = topo->be_iface; if ( ! skipChecks ) { /* curve must be simple */ if ( ! rtgeom_is_simple(iface->ctx, rtline_as_rtgeom(iface->ctx, geom)) ) { rterror(iface->ctx, "SQL/MM Spatial exception - curve not simple"); return -1; } } newedge.start_node = start_node; newedge.end_node = end_node; newedge.geom = geom; newedge.face_left = -1; newedge.face_right = -1; cleangeom = rtgeom_remove_repeated_points(iface->ctx, rtline_as_rtgeom(iface->ctx, geom), 0 ); pa = rtgeom_as_rtline(iface->ctx, cleangeom)->points; if ( pa->npoints < 2 ) { rtgeom_free(iface->ctx, cleangeom); rterror(iface->ctx, "Invalid edge (no two distinct vertices exist)"); return -1; } /* Initialize endpoint info (some of that ) */ span.cwFace = span.ccwFace = epan.cwFace = epan.ccwFace = -1; /* Compute azimut of first edge end on start node */ rt_getPoint2d_p(iface->ctx, pa, 0, &p1); rt_getPoint2d_p(iface->ctx, pa, 1, &pn); if ( p2d_same(iface->ctx, &p1, &pn) ) { rtgeom_free(iface->ctx, cleangeom); /* Can still happen, for 2-point lines */ rterror(iface->ctx, "Invalid edge (no two distinct vertices exist)"); return -1; } if ( ! azimuth_pt_pt(iface->ctx, &p1, &pn, &span.myaz) ) { rtgeom_free(iface->ctx, cleangeom); rterror(iface->ctx, "error computing azimuth of first edgeend [%g,%g-%g,%g]", p1.x, p1.y, pn.x, pn.y); return -1; } RTDEBUGF(1, "edge's start node is %g,%g", p1.x, p1.y); /* Compute azimuth of last edge end on end node */ rt_getPoint2d_p(iface->ctx, pa, pa->npoints-1, &p2); rt_getPoint2d_p(iface->ctx, pa, pa->npoints-2, &pn); rtgeom_free(iface->ctx, cleangeom); if ( ! azimuth_pt_pt(iface->ctx, &p2, &pn, &epan.myaz) ) { rterror(iface->ctx, "error computing azimuth of last edgeend [%g,%g-%g,%g]", p2.x, p2.y, pn.x, pn.y); return -1; } RTDEBUGF(1, "edge's end node is %g,%g", p2.x, p2.y); /* * Check endpoints existance, match with Curve geometry * and get face information (if any) */ if ( start_node != end_node ) { num_nodes = 2; node_ids[0] = start_node; node_ids[1] = end_node; } else { num_nodes = 1; node_ids[0] = start_node; } endpoints = rtt_be_getNodeById( topo, node_ids, &num_nodes, RTT_COL_NODE_ALL ); if ( num_nodes < 0 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; icontaining_face != -1 ) { if ( newedge.face_left == -1 ) { newedge.face_left = newedge.face_right = node->containing_face; } else if ( newedge.face_left != node->containing_face ) { _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - geometry crosses an edge" " (endnodes in faces %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID ")", newedge.face_left, node->containing_face); } } RTDEBUGF(1, "Node %d, with geom %p (looking for %d and %d)", node->node_id, node->geom, start_node, end_node); if ( node->node_id == start_node ) { start_node_geom = node->geom; } if ( node->node_id == end_node ) { end_node_geom = node->geom; } } if ( ! skipChecks ) { if ( ! start_node_geom ) { if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - non-existent node"); return -1; } else { pa = start_node_geom->point; rt_getPoint2d_p(iface->ctx, pa, 0, &pn); if ( ! p2d_same(iface->ctx, &pn, &p1) ) { if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception" " - start node not geometry start point." //" - start node not geometry start point (%g,%g != %g,%g).", pn.x, pn.y, p1.x, p1.y ); return -1; } } if ( ! end_node_geom ) { if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception - non-existent node"); return -1; } else { pa = end_node_geom->point; rt_getPoint2d_p(iface->ctx, pa, 0, &pn); if ( ! p2d_same(iface->ctx, &pn, &p2) ) { if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); rterror(iface->ctx, "SQL/MM Spatial exception" " - end node not geometry end point." //" - end node not geometry end point (%g,%g != %g,%g).", pn.x, pn.y, p2.x, p2.y ); return -1; } } if ( num_nodes ) _rtt_release_nodes(iface->ctx, endpoints, num_nodes); if ( _rtt_CheckEdgeCrossing( topo, start_node, end_node, geom, 0 ) ) return -1; } /* ! skipChecks */ /* * All checks passed, time to prepare the new edge */ newedge.edge_id = rtt_be_getNextEdgeId( topo ); if ( newedge.edge_id == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Find adjacent edges to each endpoint */ int isclosed = start_node == end_node; int found; found = _rtt_FindAdjacentEdges( topo, start_node, &span, isclosed ? &epan : NULL, -1 ); if ( found ) { span.was_isolated = 0; newedge.next_right = span.nextCW ? span.nextCW : -newedge.edge_id; prev_left = span.nextCCW ? -span.nextCCW : newedge.edge_id; RTDEBUGF(1, "New edge %d is connected on start node, " "next_right is %d, prev_left is %d", newedge.edge_id, newedge.next_right, prev_left); if ( newedge.face_right == -1 ) { newedge.face_right = span.cwFace; } if ( newedge.face_left == -1 ) { newedge.face_left = span.ccwFace; } } else { span.was_isolated = 1; newedge.next_right = isclosed ? -newedge.edge_id : newedge.edge_id; prev_left = isclosed ? newedge.edge_id : -newedge.edge_id; RTDEBUGF(1, "New edge %d is isolated on start node, " "next_right is %d, prev_left is %d", newedge.edge_id, newedge.next_right, prev_left); } found = _rtt_FindAdjacentEdges( topo, end_node, &epan, isclosed ? &span : NULL, -1 ); if ( found ) { epan.was_isolated = 0; newedge.next_left = epan.nextCW ? epan.nextCW : newedge.edge_id; prev_right = epan.nextCCW ? -epan.nextCCW : -newedge.edge_id; RTDEBUGF(1, "New edge %d is connected on end node, " "next_left is %d, prev_right is %d", newedge.edge_id, newedge.next_left, prev_right); if ( newedge.face_right == -1 ) { newedge.face_right = span.ccwFace; } else if ( newedge.face_right != epan.ccwFace ) { /* side-location conflict */ rterror(iface->ctx, "Side-location conflict: " "new edge starts in face" " %" RTTFMT_ELEMID " and ends in face" " %" RTTFMT_ELEMID, newedge.face_right, epan.ccwFace ); return -1; } if ( newedge.face_left == -1 ) { newedge.face_left = span.cwFace; } else if ( newedge.face_left != epan.cwFace ) { /* side-location conflict */ rterror(iface->ctx, "Side-location conflict: " "new edge starts in face" " %" RTTFMT_ELEMID " and ends in face" " %" RTTFMT_ELEMID, newedge.face_left, epan.cwFace ); return -1; } } else { epan.was_isolated = 1; newedge.next_left = isclosed ? newedge.edge_id : -newedge.edge_id; prev_right = isclosed ? -newedge.edge_id : newedge.edge_id; RTDEBUGF(1, "New edge %d is isolated on end node, " "next_left is %d, prev_right is %d", newedge.edge_id, newedge.next_left, prev_right); } /* * If we don't have faces setup by now we must have encountered * a malformed topology (no containing_face on isolated nodes, no * left/right faces on adjacent edges or mismatching values) */ if ( newedge.face_left != newedge.face_right ) { rterror(iface->ctx, "Left(%" RTTFMT_ELEMID ")/right(%" RTTFMT_ELEMID ")" "faces mismatch: invalid topology ?", newedge.face_left, newedge.face_right); return -1; } else if ( newedge.face_left == -1 ) { rterror(iface->ctx, "Could not derive edge face from linked primitives:" " invalid topology ?"); return -1; } /* * Insert the new edge, and update all linking */ int ret = rtt_be_insertEdges(topo, &newedge, 1); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( ret == 0 ) { rterror(iface->ctx, "Insertion of split edge failed (no reason)"); return -1; } int updfields; /* Link prev_left to us * (if it's not us already) */ if ( llabs(prev_left) != newedge.edge_id ) { if ( prev_left > 0 ) { /* its next_left_edge is us */ updfields = RTT_COL_EDGE_NEXT_LEFT; updedge.next_left = newedge.edge_id; seledge.edge_id = prev_left; } else { /* its next_right_edge is us */ updfields = RTT_COL_EDGE_NEXT_RIGHT; updedge.next_right = newedge.edge_id; seledge.edge_id = -prev_left; } ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_EDGE_ID, &updedge, updfields, NULL, 0); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Link prev_right to us * (if it's not us already) */ if ( llabs(prev_right) != newedge.edge_id ) { if ( prev_right > 0 ) { /* its next_left_edge is -us */ updfields = RTT_COL_EDGE_NEXT_LEFT; updedge.next_left = -newedge.edge_id; seledge.edge_id = prev_right; } else { /* its next_right_edge is -us */ updfields = RTT_COL_EDGE_NEXT_RIGHT; updedge.next_right = -newedge.edge_id; seledge.edge_id = -prev_right; } ret = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_EDGE_ID, &updedge, updfields, NULL, 0); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* NOT IN THE SPECS... * set containing_face = null for start_node and end_node * if they where isolated * */ RTT_ISO_NODE updnode, selnode; updnode.containing_face = -1; if ( span.was_isolated ) { selnode.node_id = start_node; ret = rtt_be_updateNodes(topo, &selnode, RTT_COL_NODE_NODE_ID, &updnode, RTT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( epan.was_isolated ) { selnode.node_id = end_node; ret = rtt_be_updateNodes(topo, &selnode, RTT_COL_NODE_NODE_ID, &updnode, RTT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Check face splitting */ if ( ! isclosed && ( epan.was_isolated || span.was_isolated ) ) { RTDEBUG(1, "New edge is dangling, so it cannot split any face"); return newedge.edge_id; /* no split */ } int newface1 = -1; /* IDEA: avoid building edge ring if input is closed, which means we * know in advance it splits a face */ if ( ! modFace ) { newface1 = _rtt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 ); if ( newface1 == 0 ) { RTDEBUG(1, "New edge does not split any face"); return newedge.edge_id; /* no split */ } } int newface = _rtt_AddFaceSplit( topo, newedge.edge_id, newedge.face_left, 0 ); if ( modFace ) { if ( newface == 0 ) { RTDEBUG(1, "New edge does not split any face"); return newedge.edge_id; /* no split */ } if ( newface < 0 ) { /* face on the left is the universe face */ /* must be forming a maximal ring in universal face */ newface = _rtt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 0 ); if ( newface < 0 ) return newedge.edge_id; /* no split */ } else { _rtt_AddFaceSplit( topo, -newedge.edge_id, newedge.face_left, 1 ); } } /* * Update topogeometries, if needed */ if ( newedge.face_left != 0 ) { ret = rtt_be_updateTopoGeomFaceSplit(topo, newedge.face_left, newface, newface1); if ( ret == 0 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modFace ) { /* drop old face from the face table */ ret = rtt_be_deleteFacesById(topo, &(newedge.face_left), 1); if ( ret == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } } return newedge.edge_id; } RTT_ELEMID rtt_AddEdgeModFace( RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, RTLINE *geom, int skipChecks ) { return _rtt_AddEdge( topo, start_node, end_node, geom, skipChecks, 1 ); } RTT_ELEMID rtt_AddEdgeNewFaces( RTT_TOPOLOGY* topo, RTT_ELEMID start_node, RTT_ELEMID end_node, RTLINE *geom, int skipChecks ) { return _rtt_AddEdge( topo, start_node, end_node, geom, skipChecks, 0 ); } static RTGEOM * _rtt_FaceByEdges(RTT_TOPOLOGY *topo, RTT_ISO_EDGE *edges, int numfaceedges) { RTGEOM *outg; RTCOLLECTION *bounds; const RTT_BE_IFACE *iface = topo->be_iface; RTGEOM **geoms = rtalloc(iface->ctx, sizeof(RTGEOM*) * numfaceedges ); int i, validedges = 0; for ( i=0; ictx, edges[i].geom); } if ( ! validedges ) { /* Face has no valid boundary edges, we'll return EMPTY, see * https://trac.osgeo.org/postgis/ticket/3221 */ if ( numfaceedges ) rtfree(iface->ctx, geoms); RTDEBUG(1, "_rtt_FaceByEdges returning empty polygon"); return rtpoly_as_rtgeom(iface->ctx, rtpoly_construct_empty(iface->ctx, topo->srid, topo->hasZ, 0) ); } bounds = rtcollection_construct(iface->ctx, RTMULTILINETYPE, topo->srid, NULL, /* gbox */ validedges, geoms); outg = rtgeom_buildarea(iface->ctx, rtcollection_as_rtgeom(iface->ctx, bounds) ); rtcollection_release(iface->ctx, bounds); rtfree(iface->ctx, geoms); #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, outg, RTWKT_EXTENDED, 2, &sz); RTDEBUGF(1, "_rtt_FaceByEdges returning area: %s", wkt); rtfree(iface->ctx, wkt); } #endif return outg; } RTGEOM* rtt_GetFaceGeometry(RTT_TOPOLOGY* topo, RTT_ELEMID faceid) { int numfaceedges; RTT_ISO_EDGE *edges; RTT_ISO_FACE *face; RTPOLY *out; RTGEOM *outg; int i; int fields; const RTT_BE_IFACE *iface = topo->be_iface; if ( faceid == 0 ) { rterror(iface->ctx, "SQL/MM Spatial exception - universal face has no geometry"); return NULL; } /* Construct the face geometry */ numfaceedges = 1; fields = RTT_COL_EDGE_GEOM | RTT_COL_EDGE_FACE_LEFT | RTT_COL_EDGE_FACE_RIGHT ; edges = rtt_be_getEdgeByFace( topo, &faceid, &numfaceedges, fields, NULL ); if ( numfaceedges == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } if ( numfaceedges == 0 ) { i = 1; face = rtt_be_getFaceById(topo, &faceid, &i, RTT_COL_FACE_FACE_ID); if ( i == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } if ( i == 0 ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent face."); return NULL; } rtfree(iface->ctx, face ); if ( i > 1 ) { rterror(iface->ctx, "Corrupted topology: multiple face records have face_id=%" RTTFMT_ELEMID, faceid); return NULL; } /* Face has no boundary edges, we'll return EMPTY, see * https://trac.osgeo.org/postgis/ticket/3221 */ out = rtpoly_construct_empty(iface->ctx, topo->srid, topo->hasZ, 0); return rtpoly_as_rtgeom(iface->ctx, out); } outg = _rtt_FaceByEdges( topo, edges, numfaceedges ); _rtt_release_edges(iface->ctx, edges, numfaceedges); return outg; } /* Find which edge from the "edges" set defines the next * portion of the given "ring". * * The edge might be either forward or backward. * * @param ring The ring to find definition of. * It is assumed it does not contain duplicated vertices. * @param from offset of the ring point to start looking from * @param edges array of edges to search into * @param numedges number of edges in the edges array * * @return index of the edge defining the next ring portion or * -1 if no edge was found to be part of the ring */ static int _rtt_FindNextRingEdge(const RTCTX *ctx, const RTPOINTARRAY *ring, int from, const RTT_ISO_EDGE *edges, int numedges) { int i; RTPOINT2D p1; /* Get starting ring point */ rt_getPoint2d_p(ctx, ring, from, &p1); RTDEBUGF(1, "Ring's 'from' point (%d) is %g,%g", from, p1.x, p1.y); /* find the edges defining the next portion of ring starting from * vertex "from" */ for ( i=0; igeom; RTPOINTARRAY *epa = edge->points; RTPOINT2D p2, pt; int match = 0; int j; /* Skip if the edge is a dangling one */ if ( isoe->face_left == isoe->face_right ) { RTDEBUGF(3, "_rtt_FindNextRingEdge: edge %" RTTFMT_ELEMID " has same face (%" RTTFMT_ELEMID ") on both sides, skipping", isoe->edge_id, isoe->face_left); continue; } #if 0 size_t sz; RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " is %s", isoe->edge_id, rtgeom_to_wkt(ctx, rtline_as_rtgeom(ctx, edge), RTWKT_EXTENDED, 2, &sz)); #endif /* ptarray_remove_repeated_points ? */ rt_getPoint2d_p(ctx, epa, 0, &p2); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " 'first' point is %g,%g", isoe->edge_id, p2.x, p2.y); RTDEBUGF(1, "Rings's 'from' point is still %g,%g", p1.x, p1.y); if ( p2d_same(ctx, &p1, &p2) ) { RTDEBUG(1, "p2d_same(ctx, p1,p2) returned true"); RTDEBUGF(1, "First point of edge %" RTTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from); /* first point matches, let's check next non-equal one */ for ( j=1; jnpoints; ++j ) { rt_getPoint2d_p(ctx, epa, j, &p2); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " 'next' point %d is %g,%g", isoe->edge_id, j, p2.x, p2.y); /* we won't check duplicated edge points */ if ( p2d_same(ctx, &p1, &p2) ) continue; /* we assume there are no duplicated points in ring */ rt_getPoint2d_p(ctx, ring, from+1, &pt); RTDEBUGF(1, "Ring's point %d is %g,%g", from+1, pt.x, pt.y); match = p2d_same(ctx, &pt, &p2); break; /* we want to check a single non-equal next vertex */ } #if RTGEOM_DEBUG_LEVEL > 0 if ( match ) { RTDEBUGF(1, "Prev point of edge %" RTTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from+1); } else { RTDEBUGF(1, "Prev point of edge %" RTTFMT_ELEMID " does not match ring vertex %d", isoe->edge_id, from+1); } #endif } if ( ! match ) { RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " did not match as forward", isoe->edge_id); rt_getPoint2d_p(ctx, epa, epa->npoints-1, &p2); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " 'last' point is %g,%g", isoe->edge_id, p2.x, p2.y); if ( p2d_same(ctx, &p1, &p2) ) { RTDEBUGF(1, "Last point of edge %" RTTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from); /* last point matches, let's check next non-equal one */ for ( j=epa->npoints-2; j>=0; --j ) { rt_getPoint2d_p(ctx, epa, j, &p2); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " 'prev' point %d is %g,%g", isoe->edge_id, j, p2.x, p2.y); /* we won't check duplicated edge points */ if ( p2d_same(ctx, &p1, &p2) ) continue; /* we assume there are no duplicated points in ring */ rt_getPoint2d_p(ctx, ring, from+1, &pt); RTDEBUGF(1, "Ring's point %d is %g,%g", from+1, pt.x, pt.y); match = p2d_same(ctx, &pt, &p2); break; /* we want to check a single non-equal next vertex */ } } #if RTGEOM_DEBUG_LEVEL > 0 if ( match ) { RTDEBUGF(1, "Prev point of edge %" RTTFMT_ELEMID " matches ring vertex %d", isoe->edge_id, from+1); } else { RTDEBUGF(1, "Prev point of edge %" RTTFMT_ELEMID " does not match ring vertex %d", isoe->edge_id, from+1); } #endif } if ( match ) return i; } return -1; } /* Reverse values in array between "from" (inclusive) * and "to" (exclusive) indexes */ static void _rtt_ReverseElemidArray(RTT_ELEMID *ary, int from, int to) { RTT_ELEMID t; while (from < to) { t = ary[from]; ary[from++] = ary[to]; ary[to--] = t; } } /* Rotate values in array between "from" (inclusive) * and "to" (exclusive) indexes, so that "rotidx" is * the new value at "from" */ static void _rtt_RotateElemidArray(RTT_ELEMID *ary, int from, int to, int rotidx) { _rtt_ReverseElemidArray(ary, from, rotidx-1); _rtt_ReverseElemidArray(ary, rotidx, to-1); _rtt_ReverseElemidArray(ary, from, to-1); } int rtt_GetFaceEdges(RTT_TOPOLOGY* topo, RTT_ELEMID face_id, RTT_ELEMID **out ) { RTGEOM *face; RTPOLY *facepoly; RTT_ISO_EDGE *edges; int numfaceedges; int fields, i; int nseid = 0; /* number of signed edge ids */ int prevseid; RTT_ELEMID *seid; /* signed edge ids */ const RTT_BE_IFACE *iface = topo->be_iface; /* Get list of face edges */ numfaceedges = 1; fields = RTT_COL_EDGE_EDGE_ID | RTT_COL_EDGE_GEOM | RTT_COL_EDGE_FACE_LEFT | RTT_COL_EDGE_FACE_RIGHT ; edges = rtt_be_getEdgeByFace( topo, &face_id, &numfaceedges, fields, NULL ); if ( numfaceedges == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! numfaceedges ) return 0; /* no edges in output */ /* order edges by occurrence in face */ face = _rtt_FaceByEdges(topo, edges, numfaceedges); if ( ! face ) { /* _rtt_FaceByEdges should have already invoked rterror in this case */ _rtt_release_edges(iface->ctx, edges, numfaceedges); return -1; } if ( rtgeom_is_empty(iface->ctx, face) ) { /* no edges in output */ _rtt_release_edges(iface->ctx, edges, numfaceedges); rtgeom_free(iface->ctx, face); return 0; } /* force_lhr, if the face is not the universe */ /* _rtt_FaceByEdges seems to guaranteed RHR */ /* rtgeom_force_clockwise(iface->ctx, face); */ if ( face_id ) rtgeom_reverse(iface->ctx, face); #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, face, RTWKT_EXTENDED, 6, &sz); RTDEBUGF(1, "Geometry of face %" RTTFMT_ELEMID " is: %s", face_id, wkt); rtfree(iface->ctx, wkt); } #endif facepoly = rtgeom_as_rtpoly(iface->ctx, face); if ( ! facepoly ) { _rtt_release_edges(iface->ctx, edges, numfaceedges); rtgeom_free(iface->ctx, face); rterror(iface->ctx, "Geometry of face %" RTTFMT_ELEMID " is not a polygon", face_id); return -1; } nseid = prevseid = 0; seid = rtalloc(iface->ctx, sizeof(RTT_ELEMID) * numfaceedges ); /* for each ring of the face polygon... */ for ( i=0; inrings; ++i ) { const RTPOINTARRAY *ring = facepoly->rings[i]; int j = 0; RTT_ISO_EDGE *nextedge; RTLINE *nextline; RTDEBUGF(1, "Ring %d has %d points", i, ring->npoints); while ( j < ring->npoints-1 ) { RTDEBUGF(1, "Looking for edge covering ring %d from vertex %d", i, j); int edgeno = _rtt_FindNextRingEdge(iface->ctx, ring, j, edges, numfaceedges); if ( edgeno == -1 ) { /* should never happen */ _rtt_release_edges(iface->ctx, edges, numfaceedges); rtgeom_free(iface->ctx, face); rtfree(iface->ctx, seid); rterror(iface->ctx, "No edge (among %d) found to be defining geometry of face %" RTTFMT_ELEMID, numfaceedges, face_id); return -1; } nextedge = &(edges[edgeno]); nextline = nextedge->geom; RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " covers ring %d from vertex %d to %d", nextedge->edge_id, i, j, j + nextline->points->npoints - 1); #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, rtline_as_rtgeom(iface->ctx, nextline), RTWKT_EXTENDED, 6, &sz); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " is %s", nextedge->edge_id, wkt); rtfree(iface->ctx, wkt); } #endif j += nextline->points->npoints - 1; /* Add next edge to the output array */ seid[nseid++] = nextedge->face_left == face_id ? nextedge->edge_id : -nextedge->edge_id; /* avoid checking again on next time turn */ nextedge->face_left = nextedge->face_right = -1; } /* now "scroll" the list of edges so that the one * with smaller absolute edge_id is first */ /* Range is: [prevseid, nseid) -- [inclusive, exclusive) */ if ( (nseid - prevseid) > 1 ) {{ RTT_ELEMID minid = 0; int minidx = 0; RTDEBUGF(1, "Looking for smallest id among the %d edges " "composing ring %d", (nseid-prevseid), i); for ( j=prevseid; jctx, face); _rtt_release_edges(iface->ctx, edges, numfaceedges); *out = seid; return nseid; } static GEOSGeometry * _rtt_EdgeMotionArea(const RTCTX *ctx, RTLINE *geom, int isclosed) { GEOSGeometry *gg; RTPOINT4D p4d; RTPOINTARRAY *pa; RTPOINTARRAY **pas; RTPOLY *poly; RTGEOM *g; pas = rtalloc(ctx, sizeof(RTPOINTARRAY*)); _rtt_EnsureGeos(ctx); if ( isclosed ) { pas[0] = ptarray_clone_deep(ctx, geom->points ); poly = rtpoly_construct(ctx, 0, 0, 1, pas); gg = RTGEOM2GEOS(ctx, rtpoly_as_rtgeom(ctx, poly), 0 ); rtpoly_free(ctx, poly); /* should also delete the pointarrays */ } else { pa = geom->points; rt_getPoint4d_p(ctx, pa, 0, &p4d); pas[0] = ptarray_clone_deep(ctx, pa ); /* don't bother dup check */ if ( RT_FAILURE == ptarray_append_point(ctx, pas[0], &p4d, RT_TRUE) ) { ptarray_free(ctx, pas[0]); rtfree(ctx, pas); rterror(ctx, "Could not append point to pointarray"); return NULL; } poly = rtpoly_construct(ctx, 0, NULL, 1, pas); /* make valid, in case the edge self-intersects on its first-last * vertex segment */ g = rtgeom_make_valid(ctx, rtpoly_as_rtgeom(ctx, poly)); rtpoly_free(ctx, poly); /* should also delete the pointarrays */ if ( ! g ) { rterror(ctx, "Could not make edge motion area valid"); return NULL; } gg = RTGEOM2GEOS(ctx, g, 0); rtgeom_free(ctx, g); } if ( ! gg ) { rterror(ctx, "Could not convert old edge area geometry to GEOS: %s", rtgeom_get_last_geos_error(ctx)); return NULL; } return gg; } int rtt_ChangeEdgeGeom(RTT_TOPOLOGY* topo, RTT_ELEMID edge_id, RTLINE *geom) { RTT_ISO_EDGE *oldedge; RTT_ISO_EDGE newedge; RTPOINT2D p1, p2, pt; int i; int isclosed = 0; const RTT_BE_IFACE *iface = topo->be_iface; /* curve must be simple */ if ( ! rtgeom_is_simple(iface->ctx, rtline_as_rtgeom(iface->ctx, geom)) ) { rterror(iface->ctx, "SQL/MM Spatial exception - curve not simple"); return -1; } i = 1; oldedge = rtt_be_getEdgeById(topo, &edge_id, &i, RTT_COL_EDGE_ALL); if ( ! oldedge ) { RTDEBUGF(1, "rtt_ChangeEdgeGeom: " "rtt_be_getEdgeById returned NULL and set i=%d", i); if ( i == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( i == 0 ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge %" RTTFMT_ELEMID, edge_id); return -1; } else { rterror(iface->ctx, "Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return -1; } } RTDEBUGF(1, "rtt_ChangeEdgeGeom: " "old edge has %d points, new edge has %d points", oldedge->geom->points->npoints, geom->points->npoints); /* * e) Check StartPoint consistency */ rt_getPoint2d_p(iface->ctx, oldedge->geom->points, 0, &p1); rt_getPoint2d_p(iface->ctx, geom->points, 0, &pt); if ( ! p2d_same(iface->ctx, &p1, &pt) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "SQL/MM Spatial exception - " "start node not geometry start point."); return -1; } /* * f) Check EndPoint consistency */ if ( oldedge->geom->points->npoints < 2 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Corrupted topology: edge %" RTTFMT_ELEMID " has less than 2 vertices", oldedge->edge_id); return -1; } rt_getPoint2d_p(iface->ctx, oldedge->geom->points, oldedge->geom->points->npoints-1, &p2); if ( geom->points->npoints < 2 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Invalid edge: less than 2 vertices"); return -1; } rt_getPoint2d_p(iface->ctx, geom->points, geom->points->npoints-1, &pt); if ( ! p2d_same(iface->ctx, &pt, &p2) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "SQL/MM Spatial exception - " "end node not geometry end point."); return -1; } /* Not in the specs: * if the edge is closed, check we didn't change winding ! * (should be part of isomorphism checking) */ if ( oldedge->start_node == oldedge->end_node ) { isclosed = 1; #if 1 /* TODO: this is actually bogus as a test */ /* check for valid edge (distinct vertices must exist) */ if ( ! _rtt_GetInteriorEdgePoint(iface->ctx, geom, &pt) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Invalid edge (no two distinct vertices exist)"); return -1; } #endif if ( ptarray_isccw(iface->ctx, oldedge->geom->points) != ptarray_isccw(iface->ctx, geom->points) ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Edge twist at node POINT(%g %g)", p1.x, p1.y); return -1; } } if ( _rtt_CheckEdgeCrossing(topo, oldedge->start_node, oldedge->end_node, geom, edge_id ) ) { /* would have called rterror already, leaking :( */ _rtt_release_edges(iface->ctx, oldedge, 1); return -1; } RTDEBUG(1, "rtt_ChangeEdgeGeom: " "edge crossing check passed "); /* * Not in the specs: * Check topological isomorphism */ /* Check that the "motion range" doesn't include any node */ // 1. compute combined bbox of old and new edge RTGBOX mbox; /* motion box */ rtgeom_add_bbox(iface->ctx, (RTGEOM*)oldedge->geom); /* just in case */ rtgeom_add_bbox(iface->ctx, (RTGEOM*)geom); /* just in case */ gbox_union(iface->ctx, oldedge->geom->bbox, geom->bbox, &mbox); // 2. fetch all nodes in the combined box RTT_ISO_NODE *nodes; int numnodes; nodes = rtt_be_getNodeWithinBox2D(topo, &mbox, &numnodes, RTT_COL_NODE_ALL, 0); RTDEBUGF(1, "rtt_be_getNodeWithinBox2D returned %d nodes", numnodes); if ( numnodes == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } // 3. if any node beside endnodes are found: if ( numnodes > ( 1 + isclosed ? 0 : 1 ) ) {{ GEOSGeometry *oarea, *narea; const GEOSPreparedGeometry *oareap, *nareap; _rtt_EnsureGeos(iface->ctx); oarea = _rtt_EdgeMotionArea(iface->ctx, oldedge->geom, isclosed); if ( ! oarea ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Could not compute edge motion area for old edge"); return -1; } narea = _rtt_EdgeMotionArea(iface->ctx, geom, isclosed); if ( ! narea ) { GEOSGeom_destroy_r(iface->ctx->gctx, oarea); _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Could not compute edge motion area for new edge"); return -1; } // 3.2. bail out if any node is in one and not the other oareap = GEOSPrepare_r(iface->ctx->gctx, oarea ); nareap = GEOSPrepare_r(iface->ctx->gctx, narea ); for (i=0; inode_id == oldedge->start_node ) continue; if ( n->node_id == oldedge->end_node ) continue; ngg = RTGEOM2GEOS(iface->ctx, rtpoint_as_rtgeom(iface->ctx, n->geom) , 0); ocont = GEOSPreparedContains_r(iface->ctx->gctx, oareap, ngg ); ncont = GEOSPreparedContains_r(iface->ctx->gctx, nareap, ngg ); GEOSGeom_destroy_r(iface->ctx->gctx, ngg); if (ocont == 2 || ncont == 2) { _rtt_release_nodes(iface->ctx, nodes, numnodes); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, oareap); GEOSGeom_destroy_r(iface->ctx->gctx, oarea); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, nareap); GEOSGeom_destroy_r(iface->ctx->gctx, narea); rterror(iface->ctx, "GEOS exception on PreparedContains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } if (ocont != ncont) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, oareap); GEOSGeom_destroy_r(iface->ctx->gctx, oarea); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, nareap); GEOSGeom_destroy_r(iface->ctx->gctx, narea); wkt = rtgeom_to_wkt(iface->ctx, rtpoint_as_rtgeom(iface->ctx, n->geom), RTWKT_ISO, 15, &sz); _rtt_release_nodes(iface->ctx, nodes, numnodes); rterror(iface->ctx, "Edge motion collision at %s", wkt); rtfree(iface->ctx, wkt); /* would not necessarely reach this point */ return -1; } } GEOSPreparedGeom_destroy_r(iface->ctx->gctx, oareap); GEOSGeom_destroy_r(iface->ctx->gctx, oarea); GEOSPreparedGeom_destroy_r(iface->ctx->gctx, nareap); GEOSGeom_destroy_r(iface->ctx->gctx, narea); }} if ( numnodes ) _rtt_release_nodes(iface->ctx, nodes, numnodes); RTDEBUG(1, "nodes containment check passed"); /* * Check edge adjacency before * TODO: can be optimized to gather azimuths of all edge ends once */ edgeend span_pre, epan_pre; /* initialize span_pre.myaz and epan_pre.myaz with existing edge */ i = _rtt_InitEdgeEndByLine(iface->ctx, &span_pre, &epan_pre, oldedge->geom, &p1, &p2); if ( i ) return -1; /* rterror should have been raised */ _rtt_FindAdjacentEdges( topo, oldedge->start_node, &span_pre, isclosed ? &epan_pre : NULL, edge_id ); _rtt_FindAdjacentEdges( topo, oldedge->end_node, &epan_pre, isclosed ? &span_pre : NULL, edge_id ); RTDEBUGF(1, "edges adjacent to old edge are %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID " (first point), %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID " (last point)" RTTFMT_ELEMID, span_pre.nextCW, span_pre.nextCCW, epan_pre.nextCW, epan_pre.nextCCW); /* update edge geometry */ newedge.edge_id = edge_id; newedge.geom = geom; i = rtt_be_updateEdgesById(topo, &newedge, 1, RTT_COL_EDGE_GEOM); if ( i == -1 ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! i ) { _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Unexpected error: %d edges updated when expecting 1", i); return -1; } /* * Check edge adjacency after */ edgeend span_post, epan_post; i = _rtt_InitEdgeEndByLine(iface->ctx, &span_post, &epan_post, geom, &p1, &p2); if ( i ) return -1; /* rterror should have been raised */ /* initialize epan_post.myaz and epan_post.myaz */ i = _rtt_InitEdgeEndByLine(iface->ctx, &span_post, &epan_post, geom, &p1, &p2); if ( i ) return -1; /* rterror should have been raised */ _rtt_FindAdjacentEdges( topo, oldedge->start_node, &span_post, isclosed ? &epan_post : NULL, edge_id ); _rtt_FindAdjacentEdges( topo, oldedge->end_node, &epan_post, isclosed ? &span_post : NULL, edge_id ); RTDEBUGF(1, "edges adjacent to new edge are %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID " (first point), %" RTTFMT_ELEMID " and %" RTTFMT_ELEMID " (last point)" RTTFMT_ELEMID, span_pre.nextCW, span_pre.nextCCW, epan_pre.nextCW, epan_pre.nextCCW); /* Bail out if next CW or CCW edge on start node changed */ if ( span_pre.nextCW != span_post.nextCW || span_pre.nextCCW != span_post.nextCCW ) {{ RTT_ELEMID nid = oldedge->start_node; _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Edge changed disposition around start node %" RTTFMT_ELEMID, nid); return -1; }} /* Bail out if next CW or CCW edge on end node changed */ if ( epan_pre.nextCW != epan_post.nextCW || epan_pre.nextCCW != epan_post.nextCCW ) {{ RTT_ELEMID nid = oldedge->end_node; _rtt_release_edges(iface->ctx, oldedge, 1); rterror(iface->ctx, "Edge changed disposition around end node %" RTTFMT_ELEMID, nid); return -1; }} /* -- Update faces MBR of left and right faces -- TODO: think about ways to optimize this part, like see if -- the old edge geometry partecipated in the definition -- of the current MBR (for shrinking) or the new edge MBR -- would be larger than the old face MBR... -- */ int facestoupdate = 0; RTT_ISO_FACE faces[2]; RTGEOM *nface1 = NULL; RTGEOM *nface2 = NULL; if ( oldedge->face_left != 0 ) { nface1 = rtt_GetFaceGeometry(topo, oldedge->face_left); if ( ! nface1 ) { rterror(iface->ctx, "lwt_ChangeEdgeGeom could not construct face %" RTTFMT_ELEMID ", on the left of edge %" RTTFMT_ELEMID, oldedge->face_left, edge_id); return -1; } #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, nface1, RTWKT_EXTENDED, 2, &sz); RTDEBUGF(1, "new geometry of face left (%d): %s", (int)oldedge->face_left, wkt); rtfree(iface->ctx, wkt); } #endif rtgeom_add_bbox(iface->ctx, nface1); faces[facestoupdate].face_id = oldedge->face_left; /* ownership left to nface */ faces[facestoupdate++].mbr = nface1->bbox; } if ( oldedge->face_right != 0 /* no need to update twice the same face.. */ && oldedge->face_right != oldedge->face_left ) { nface2 = rtt_GetFaceGeometry(topo, oldedge->face_right); if ( ! nface2 ) { rterror(iface->ctx, "lwt_ChangeEdgeGeom could not construct face %" RTTFMT_ELEMID ", on the right of edge %" RTTFMT_ELEMID, oldedge->face_right, edge_id); return -1; } #if 0 { size_t sz; char *wkt = rtgeom_to_wkt(iface->ctx, nface2, RTWKT_EXTENDED, 2, &sz); RTDEBUGF(1, "new geometry of face right (%d): %s", (int)oldedge->face_right, wkt); rtfree(iface->ctx, wkt); } #endif rtgeom_add_bbox(iface->ctx, nface2); faces[facestoupdate].face_id = oldedge->face_right; faces[facestoupdate++].mbr = nface2->bbox; /* ownership left to nface */ } RTDEBUGF(1, "%d faces to update", facestoupdate); if ( facestoupdate ) { i = rtt_be_updateFacesById( topo, &(faces[0]), facestoupdate ); if ( i != facestoupdate ) { if ( nface1 ) rtgeom_free(iface->ctx, nface1); if ( nface2 ) rtgeom_free(iface->ctx, nface2); _rtt_release_edges(iface->ctx, oldedge, 1); if ( i == -1 ) rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); else rterror(iface->ctx, "Unexpected error: %d faces found when expecting 1", i); return -1; } } if ( nface1 ) rtgeom_free(iface->ctx, nface1); if ( nface2 ) rtgeom_free(iface->ctx, nface2); RTDEBUG(1, "all done, cleaning up edges"); _rtt_release_edges(iface->ctx, oldedge, 1); return 0; /* success */ } /* Only return CONTAINING_FACE in the node object */ static RTT_ISO_NODE * _rtt_GetIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID nid) { RTT_ISO_NODE *node; int n = 1; const RTT_BE_IFACE *iface = topo->be_iface; node = rtt_be_getNodeById( topo, &nid, &n, RTT_COL_NODE_CONTAINING_FACE ); if ( n < 0 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return 0; } if ( n < 1 ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent node"); return 0; } if ( node->containing_face == -1 ) { rtfree(iface->ctx, node); rterror(iface->ctx, "SQL/MM Spatial exception - not isolated node"); return 0; } return node; } int rtt_MoveIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID nid, RTPOINT *pt) { RTT_ISO_NODE *node; int ret; const RTT_BE_IFACE *iface = topo->be_iface; node = _rtt_GetIsoNode( topo, nid ); if ( ! node ) return -1; if ( rtt_be_ExistsCoincidentNode(topo, pt) ) { rtfree(iface->ctx, node); rterror(iface->ctx, "SQL/MM Spatial exception - coincident node"); return -1; } if ( rtt_be_ExistsEdgeIntersectingPoint(topo, pt) ) { rtfree(iface->ctx, node); rterror(iface->ctx, "SQL/MM Spatial exception - edge crosses node."); return -1; } /* TODO: check that the new point is in the same containing face ! * See https://trac.osgeo.org/postgis/ticket/3232 */ node->node_id = nid; node->geom = pt; ret = rtt_be_updateNodesById(topo, node, 1, RTT_COL_NODE_GEOM); if ( ret == -1 ) { rtfree(iface->ctx, node); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } rtfree(iface->ctx, node); return 0; } int rtt_RemoveIsoNode(RTT_TOPOLOGY* topo, RTT_ELEMID nid) { RTT_ISO_NODE *node; int n = 1; const RTT_BE_IFACE *iface = topo->be_iface; node = _rtt_GetIsoNode( topo, nid ); if ( ! node ) return -1; n = rtt_be_deleteNodesById( topo, &nid, n ); if ( n == -1 ) { rtfree(iface->ctx, node); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( n != 1 ) { rtfree(iface->ctx, node); rterror(iface->ctx, "Unexpected error: %d nodes deleted when expecting 1", n); return -1; } /* TODO: notify to caller about node being removed ? * See https://trac.osgeo.org/postgis/ticket/3231 */ rtfree(iface->ctx, node); return 0; /* success */ } int rtt_RemIsoEdge(RTT_TOPOLOGY* topo, RTT_ELEMID id) { RTT_ISO_EDGE deledge; RTT_ISO_EDGE *edge; RTT_ELEMID nid[2]; RTT_ISO_NODE upd_node[2]; RTT_ELEMID containing_face; int n = 1; int i; const RTT_BE_IFACE *iface = topo->be_iface; edge = rtt_be_getEdgeById( topo, &id, &n, RTT_COL_EDGE_START_NODE| RTT_COL_EDGE_END_NODE | RTT_COL_EDGE_FACE_LEFT | RTT_COL_EDGE_FACE_RIGHT ); if ( ! edge ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! n ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge"); return -1; } if ( n > 1 ) { rtfree(iface->ctx, edge); rterror(iface->ctx, "Corrupted topology: more than a single edge have id %" RTTFMT_ELEMID, id); return -1; } if ( edge[0].face_left != edge[0].face_right ) { rtfree(iface->ctx, edge); rterror(iface->ctx, "SQL/MM Spatial exception - not isolated edge"); return -1; } containing_face = edge[0].face_left; nid[0] = edge[0].start_node; nid[1] = edge[0].end_node; rtfree(iface->ctx, edge); n = 2; edge = rtt_be_getEdgeByNode( topo, nid, &n, RTT_COL_EDGE_EDGE_ID ); if ( n == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; ictx, edge); rterror(iface->ctx, "SQL/MM Spatial exception - not isolated edge"); return -1; } if ( edge ) rtfree(iface->ctx, edge); deledge.edge_id = id; n = rtt_be_deleteEdges( topo, &deledge, RTT_COL_EDGE_EDGE_ID ); if ( n == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( n != 1 ) { rterror(iface->ctx, "Unexpected error: %d edges deleted when expecting 1", n); return -1; } upd_node[0].node_id = nid[0]; upd_node[0].containing_face = containing_face; n = 1; if ( nid[1] != nid[0] ) { upd_node[1].node_id = nid[1]; upd_node[1].containing_face = containing_face; ++n; } n = rtt_be_updateNodesById(topo, upd_node, n, RTT_COL_NODE_CONTAINING_FACE); if ( n == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* TODO: notify to caller about edge being removed ? * See https://trac.osgeo.org/postgis/ticket/3248 */ return 0; /* success */ } /* Used by _rtt_RemEdge to update edge face ref on healing * * @param of old face id (never 0 as you cannot remove face 0) * @param nf new face id * @return 0 on success, -1 on backend error */ static int _rtt_UpdateEdgeFaceRef( RTT_TOPOLOGY *topo, RTT_ELEMID of, RTT_ELEMID nf) { RTT_ISO_EDGE sel_edge, upd_edge; int ret; assert( of != 0 ); /* Update face_left for all edges still referencing old face */ sel_edge.face_left = of; upd_edge.face_left = nf; ret = rtt_be_updateEdges(topo, &sel_edge, RTT_COL_EDGE_FACE_LEFT, &upd_edge, RTT_COL_EDGE_FACE_LEFT, NULL, 0); if ( ret == -1 ) return -1; /* Update face_right for all edges still referencing old face */ sel_edge.face_right = of; upd_edge.face_right = nf; ret = rtt_be_updateEdges(topo, &sel_edge, RTT_COL_EDGE_FACE_RIGHT, &upd_edge, RTT_COL_EDGE_FACE_RIGHT, NULL, 0); if ( ret == -1 ) return -1; return 0; } /* Used by _rtt_RemEdge to update node face ref on healing * * @param of old face id (never 0 as you cannot remove face 0) * @param nf new face id * @return 0 on success, -1 on backend error */ static int _rtt_UpdateNodeFaceRef( RTT_TOPOLOGY *topo, RTT_ELEMID of, RTT_ELEMID nf) { RTT_ISO_NODE sel, upd; int ret; assert( of != 0 ); /* Update face_left for all edges still referencing old face */ sel.containing_face = of; upd.containing_face = nf; ret = rtt_be_updateNodes(topo, &sel, RTT_COL_NODE_CONTAINING_FACE, &upd, RTT_COL_NODE_CONTAINING_FACE, NULL, 0); if ( ret == -1 ) return -1; return 0; } /* Used by rtt_RemEdgeModFace and rtt_RemEdgeNewFaces * * Returns -1 on error, identifier of the face that takes up the space * previously occupied by the removed edge if modFace is 1, identifier of * the created face (0 if none) if modFace is 0. */ static RTT_ELEMID _rtt_RemEdge(RTT_TOPOLOGY* topo, RTT_ELEMID edge_id, int modFace ) { const RTT_BE_IFACE *iface = topo->be_iface; int i, nedges, nfaces, fields; RTT_ISO_EDGE *edge = NULL; RTT_ISO_EDGE *upd_edge = NULL; RTT_ISO_EDGE upd_edge_left[2]; int nedge_left = 0; RTT_ISO_EDGE upd_edge_right[2]; int nedge_right = 0; RTT_ISO_NODE upd_node[2]; int nnode = 0; RTT_ISO_FACE *faces = NULL; RTT_ISO_FACE newface; RTT_ELEMID node_ids[2]; RTT_ELEMID face_ids[2]; int fnode_edges = 0; /* number of edges on the first node (excluded * the one being removed ) */ int lnode_edges = 0; /* number of edges on the last node (excluded * the one being removed ) */ newface.face_id = 0; i = 1; edge = rtt_be_getEdgeById(topo, &edge_id, &i, RTT_COL_EDGE_ALL); if ( ! edge ) { RTDEBUGF(1, "rtt_be_getEdgeById returned NULL and set i=%d", i); if ( i == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( i == 0 ) { rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge %" RTTFMT_ELEMID, edge_id); return -1; } else { rterror(iface->ctx, "Backend coding error: getEdgeById callback returned NULL " "but numelements output parameter has value %d " "(expected 0 or 1)", i); return -1; } } if ( ! rtt_be_checkTopoGeomRemEdge(topo, edge_id, edge->face_left, edge->face_right) ) { rterror(iface->ctx, "%s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } RTDEBUG(1, "Updating next_{right,left}_face of ring edges..."); /* Update edge linking */ nedges = 0; node_ids[nedges++] = edge->start_node; if ( edge->end_node != edge->start_node ) { node_ids[nedges++] = edge->end_node; } fields = RTT_COL_EDGE_EDGE_ID | RTT_COL_EDGE_START_NODE | RTT_COL_EDGE_END_NODE | RTT_COL_EDGE_NEXT_LEFT | RTT_COL_EDGE_NEXT_RIGHT; upd_edge = rtt_be_getEdgeByNode( topo, &(node_ids[0]), &nedges, fields ); if ( nedges == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } nedge_left = nedge_right = 0; for ( i=0; iedge_id == edge_id ) continue; if ( e->start_node == edge->start_node || e->end_node == edge->start_node ) { ++fnode_edges; } if ( e->start_node == edge->end_node || e->end_node == edge->end_node ) { ++lnode_edges; } if ( e->next_left == -edge_id ) { upd_edge_left[nedge_left].edge_id = e->edge_id; upd_edge_left[nedge_left++].next_left = edge->next_left != edge_id ? edge->next_left : edge->next_right; } else if ( e->next_left == edge_id ) { upd_edge_left[nedge_left].edge_id = e->edge_id; upd_edge_left[nedge_left++].next_left = edge->next_right != -edge_id ? edge->next_right : edge->next_left; } if ( e->next_right == -edge_id ) { upd_edge_right[nedge_right].edge_id = e->edge_id; upd_edge_right[nedge_right++].next_right = edge->next_left != edge_id ? edge->next_left : edge->next_right; } else if ( e->next_right == edge_id ) { upd_edge_right[nedge_right].edge_id = e->edge_id; upd_edge_right[nedge_right++].next_right = edge->next_right != -edge_id ? edge->next_right : edge->next_left; } } if ( nedge_left ) { RTDEBUGF(1, "updating %d 'next_left' edges", nedge_left); /* update edges in upd_edge_left set next_left */ i = rtt_be_updateEdgesById(topo, &(upd_edge_left[0]), nedge_left, RTT_COL_EDGE_NEXT_LEFT); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rtfree(iface->ctx, upd_edge); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( nedge_right ) { RTDEBUGF(1, "updating %d 'next_right' edges", nedge_right); /* update edges in upd_edge_right set next_right */ i = rtt_be_updateEdgesById(topo, &(upd_edge_right[0]), nedge_right, RTT_COL_EDGE_NEXT_RIGHT); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rtfree(iface->ctx, upd_edge); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } RTDEBUGF(1, "releasing %d updateable edges in %p", nedges, upd_edge); rtfree(iface->ctx, upd_edge); /* Id of face that will take up all the space previously * taken by left and right faces of the edge */ RTT_ELEMID floodface; /* Find floodface, and update its mbr if != 0 */ if ( edge->face_left == edge->face_right ) { floodface = edge->face_right; } else { /* Two faces healed */ if ( edge->face_left == 0 || edge->face_right == 0 ) { floodface = 0; RTDEBUG(1, "floodface is universe"); } else { /* we choose right face as the face that will remain * to be symmetric with ST_AddEdgeModFace */ floodface = edge->face_right; RTDEBUGF(1, "floodface is %" RTTFMT_ELEMID, floodface); /* update mbr of floodface as union of mbr of both faces */ face_ids[0] = edge->face_left; face_ids[1] = edge->face_right; nfaces = 2; fields = RTT_COL_FACE_ALL; faces = rtt_be_getFaceById(topo, face_ids, &nfaces, fields); if ( nfaces == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } RTGBOX *box1=NULL; RTGBOX *box2=NULL; for ( i=0; iface_left ) { if ( ! box1 ) box1 = faces[i].mbr; else { i = edge->face_left; _rtt_release_edges(iface->ctx, edge, 1); _rtt_release_faces(iface->ctx, faces, nfaces); rterror(iface->ctx, "corrupted topology: more than 1 face have face_id=%" RTTFMT_ELEMID, i); return -1; } } else if ( faces[i].face_id == edge->face_right ) { if ( ! box2 ) box2 = faces[i].mbr; else { i = edge->face_right; _rtt_release_edges(iface->ctx, edge, 1); _rtt_release_faces(iface->ctx, faces, nfaces); rterror(iface->ctx, "corrupted topology: more than 1 face have face_id=%" RTTFMT_ELEMID, i); return -1; } } else { i = faces[i].face_id; _rtt_release_edges(iface->ctx, edge, 1); _rtt_release_faces(iface->ctx, faces, nfaces); rterror(iface->ctx, "Backend coding error: getFaceById returned face " "with non-requested id %" RTTFMT_ELEMID, i); return -1; } } if ( ! box1 ) { i = edge->face_left; _rtt_release_edges(iface->ctx, edge, 1); _rtt_release_faces(iface->ctx, faces, nfaces); rterror(iface->ctx, "corrupted topology: no face have face_id=%" RTTFMT_ELEMID " (left face for edge %" RTTFMT_ELEMID ")", i, edge_id); return -1; } if ( ! box2 ) { i = edge->face_right; _rtt_release_edges(iface->ctx, edge, 1); _rtt_release_faces(iface->ctx, faces, nfaces); rterror(iface->ctx, "corrupted topology: no face have face_id=%" RTTFMT_ELEMID " (right face for edge %" RTTFMT_ELEMID ")", i, edge_id); return -1; } gbox_merge(iface->ctx, box2, box1); /* box1 is now the union of the two */ newface.mbr = box1; if ( modFace ) { newface.face_id = floodface; i = rtt_be_updateFacesById( topo, &newface, 1 ); _rtt_release_faces(iface->ctx, faces, 2); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( i != 1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Unexpected error: %d faces updated when expecting 1", i); return -1; } } else { /* New face replaces the old two faces */ newface.face_id = -1; i = rtt_be_insertFaces( topo, &newface, 1 ); _rtt_release_faces(iface->ctx, faces, 2); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( i != 1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Unexpected error: %d faces inserted when expecting 1", i); return -1; } floodface = newface.face_id; } } /* Update face references for edges and nodes still referencing * the removed face(s) */ if ( edge->face_left != floodface ) { if ( -1 == _rtt_UpdateEdgeFaceRef(topo, edge->face_left, floodface) ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( -1 == _rtt_UpdateNodeFaceRef(topo, edge->face_left, floodface) ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( edge->face_right != floodface ) { if ( -1 == _rtt_UpdateEdgeFaceRef(topo, edge->face_right, floodface) ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( -1 == _rtt_UpdateNodeFaceRef(topo, edge->face_right, floodface) ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* Update topogeoms on heal */ if ( ! rtt_be_updateTopoGeomFaceHeal(topo, edge->face_right, edge->face_left, floodface) ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "%s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* two faces healed */ /* Delete the edge */ i = rtt_be_deleteEdges(topo, edge, RTT_COL_EDGE_EDGE_ID); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* If any of the edge nodes remained isolated, set * containing_face = floodface */ if ( ! fnode_edges ) { upd_node[nnode].node_id = edge->start_node; upd_node[nnode].containing_face = floodface; ++nnode; } if ( edge->end_node != edge->start_node && ! lnode_edges ) { upd_node[nnode].node_id = edge->end_node; upd_node[nnode].containing_face = floodface; ++nnode; } if ( nnode ) { i = rtt_be_updateNodesById(topo, upd_node, nnode, RTT_COL_NODE_CONTAINING_FACE); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } if ( edge->face_left != edge->face_right ) /* or there'd be no face to remove */ { RTT_ELEMID ids[2]; int nids = 0; if ( edge->face_right != floodface ) ids[nids++] = edge->face_right; if ( edge->face_left != floodface ) ids[nids++] = edge->face_left; i = rtt_be_deleteFacesById(topo, ids, nids); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edge, 1); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } _rtt_release_edges(iface->ctx, edge, 1); return modFace ? floodface : newface.face_id; } RTT_ELEMID rtt_RemEdgeModFace( RTT_TOPOLOGY* topo, RTT_ELEMID edge_id ) { return _rtt_RemEdge( topo, edge_id, 1 ); } RTT_ELEMID rtt_RemEdgeNewFace( RTT_TOPOLOGY* topo, RTT_ELEMID edge_id ) { return _rtt_RemEdge( topo, edge_id, 0 ); } static RTT_ELEMID _rtt_HealEdges( RTT_TOPOLOGY* topo, RTT_ELEMID eid1, RTT_ELEMID eid2, int modEdge ) { const RTT_BE_IFACE *iface = topo->be_iface; RTT_ELEMID ids[2]; RTT_ELEMID commonnode = -1; int caseno = 0; RTT_ISO_EDGE *node_edges; int num_node_edges; RTT_ISO_EDGE *edges; RTT_ISO_EDGE *e1 = NULL; RTT_ISO_EDGE *e2 = NULL; RTT_ISO_EDGE newedge, updedge, seledge; int nedges, i; int e1freenode; int e2sign, e2freenode; RTPOINTARRAY *pa; char buf[256]; char *ptr; size_t bufleft = 256; ptr = buf; /* NOT IN THE SPECS: see if the same edge is given twice.. */ if ( eid1 == eid2 ) { rterror(iface->ctx, "Cannot heal edge %" RTTFMT_ELEMID " with itself, try with another", eid1); return -1; } ids[0] = eid1; ids[1] = eid2; nedges = 2; edges = rtt_be_getEdgeById(topo, ids, &nedges, RTT_COL_EDGE_ALL); if ( nedges == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for ( i=0; ictx, edges, nedges); rterror(iface->ctx, "Corrupted topology: multiple edges have id %" RTTFMT_ELEMID, eid1); return -1; } e1 = &(edges[i]); } else if ( edges[i].edge_id == eid2 ) { if ( e2 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Corrupted topology: multiple edges have id %" RTTFMT_ELEMID, eid2); return -1; } e2 = &(edges[i]); } } if ( ! e1 ) { if ( edges ) _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge %" RTTFMT_ELEMID, eid1); return -1; } if ( ! e2 ) { if ( edges ) _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "SQL/MM Spatial exception - non-existent edge %" RTTFMT_ELEMID, eid2); return -1; } /* NOT IN THE SPECS: See if any of the two edges are closed. */ if ( e1->start_node == e1->end_node ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Edge %" RTTFMT_ELEMID " is closed, cannot heal to edge %" RTTFMT_ELEMID, eid1, eid2); return -1; } if ( e2->start_node == e2->end_node ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Edge %" RTTFMT_ELEMID " is closed, cannot heal to edge %" RTTFMT_ELEMID, eid2, eid1); return -1; } /* Find common node */ if ( e1->end_node == e2->start_node ) { commonnode = e1->end_node; caseno = 1; } else if ( e1->end_node == e2->end_node ) { commonnode = e1->end_node; caseno = 2; } /* Check if any other edge is connected to the common node, if found */ if ( commonnode != -1 ) { num_node_edges = 1; node_edges = rtt_be_getEdgeByNode( topo, &commonnode, &num_node_edges, RTT_COL_EDGE_EDGE_ID ); if ( num_node_edges == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; i= bufleft ) { bufleft = 0; buf[252] = '.'; buf[253] = '.'; buf[254] = '.'; buf[255] = '\0'; } else { bufleft -= r; ptr += r; } } } rtfree(iface->ctx, node_edges); } if ( commonnode == -1 ) { if ( e1->start_node == e2->start_node ) { commonnode = e1->start_node; caseno = 3; } else if ( e1->start_node == e2->end_node ) { commonnode = e1->start_node; caseno = 4; } /* Check if any other edge is connected to the common node, if found */ if ( commonnode != -1 ) { num_node_edges = 1; node_edges = rtt_be_getEdgeByNode( topo, &commonnode, &num_node_edges, RTT_COL_EDGE_EDGE_ID ); if ( num_node_edges == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; i= bufleft ) { bufleft = 0; buf[252] = '.'; buf[253] = '.'; buf[254] = '.'; buf[255] = '\0'; } else { bufleft -= r; ptr += r; } } } if ( num_node_edges ) rtfree(iface->ctx, node_edges); } } if ( commonnode == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); if ( ptr != buf ) { rterror(iface->ctx, "SQL/MM Spatial exception - other edges connected (%s)", buf); } else { rterror(iface->ctx, "SQL/MM Spatial exception - non-connected edges"); } return -1; } if ( ! rtt_be_checkTopoGeomRemNode(topo, commonnode, eid1, eid2 ) ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "%s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* Construct the geometry of the new edge */ switch (caseno) { case 1: /* e1.end = e2.start */ pa = ptarray_clone_deep(iface->ctx, e1->geom->points); //pa = ptarray_merge(iface->ctx, pa, e2->geom->points); ptarray_append_ptarray(iface->ctx, pa, e2->geom->points, 0); newedge.start_node = e1->start_node; newedge.end_node = e2->end_node; newedge.next_left = e2->next_left; newedge.next_right = e1->next_right; e1freenode = 1; e2freenode = -1; e2sign = 1; break; case 2: /* e1.end = e2.end */ { RTPOINTARRAY *pa2; pa2 = ptarray_clone_deep(iface->ctx, e2->geom->points); ptarray_reverse(iface->ctx, pa2); pa = ptarray_clone_deep(iface->ctx, e1->geom->points); //pa = ptarray_merge(iface->ctx, e1->geom->points, pa); ptarray_append_ptarray(iface->ctx, pa, pa2, 0); ptarray_free(iface->ctx, pa2); newedge.start_node = e1->start_node; newedge.end_node = e2->start_node; newedge.next_left = e2->next_right; newedge.next_right = e1->next_right; e1freenode = 1; e2freenode = 1; e2sign = -1; break; } case 3: /* e1.start = e2.start */ pa = ptarray_clone_deep(iface->ctx, e2->geom->points); ptarray_reverse(iface->ctx, pa); //pa = ptarray_merge(iface->ctx, pa, e1->geom->points); ptarray_append_ptarray(iface->ctx, pa, e1->geom->points, 0); newedge.end_node = e1->end_node; newedge.start_node = e2->end_node; newedge.next_left = e1->next_left; newedge.next_right = e2->next_left; e1freenode = -1; e2freenode = -1; e2sign = -1; break; case 4: /* e1.start = e2.end */ pa = ptarray_clone_deep(iface->ctx, e2->geom->points); //pa = ptarray_merge(iface->ctx, pa, e1->geom->points); ptarray_append_ptarray(iface->ctx, pa, e1->geom->points, 0); newedge.end_node = e1->end_node; newedge.start_node = e2->start_node; newedge.next_left = e1->next_left; newedge.next_right = e2->next_right; e1freenode = -1; e2freenode = 1; e2sign = 1; break; default: pa = NULL; e1freenode = 0; e2freenode = 0; e2sign = 0; _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Coding error: caseno=%d should never happen", caseno); break; } newedge.geom = rtline_construct(iface->ctx, topo->srid, NULL, pa); if ( modEdge ) { /* Update data of the first edge */ newedge.edge_id = eid1; i = rtt_be_updateEdgesById(topo, &newedge, 1, RTT_COL_EDGE_NEXT_LEFT| RTT_COL_EDGE_NEXT_RIGHT | RTT_COL_EDGE_START_NODE | RTT_COL_EDGE_END_NODE | RTT_COL_EDGE_GEOM); if ( i == -1 ) { rtline_free(iface->ctx, newedge.geom); _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( i != 1 ) { rtline_free(iface->ctx, newedge.geom); if ( edges ) _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Unexpected error: %d edges updated when expecting 1", i); return -1; } } else { /* Add new edge */ newedge.edge_id = -1; newedge.face_left = e1->face_left; newedge.face_right = e1->face_right; i = rtt_be_insertEdges(topo, &newedge, 1); if ( i == -1 ) { rtline_free(iface->ctx, newedge.geom); _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( i == 0 ) { rtline_free(iface->ctx, newedge.geom); _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Insertion of split edge failed (no reason)"); return -1; } } rtline_free(iface->ctx, newedge.geom); /* -- Update next_left_edge/next_right_edge for -- any edge having them still pointing at the edge being removed -- (eid2 only when modEdge, or both otherwise) -- -- NOTE: -- e#freenode is 1 when edge# end node was the common node -- and -1 otherwise. This gives the sign of possibly found references -- to its "free" (non connected to other edge) endnode. -- e2sign is -1 if edge1 direction is opposite to edge2 direction, -- or 1 otherwise. -- */ /* update edges connected to e2's boundary from their end node */ seledge.next_left = e2freenode * eid2; updedge.next_left = e2freenode * newedge.edge_id * e2sign; i = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_LEFT, &updedge, RTT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* update edges connected to e2's boundary from their start node */ seledge.next_right = e2freenode * eid2; updedge.next_right = e2freenode * newedge.edge_id * e2sign; i = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_RIGHT, &updedge, RTT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modEdge ) { /* update edges connected to e1's boundary from their end node */ seledge.next_left = e1freenode * eid1; updedge.next_left = e1freenode * newedge.edge_id; i = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_LEFT, &updedge, RTT_COL_EDGE_NEXT_LEFT, NULL, 0); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* update edges connected to e1's boundary from their start node */ seledge.next_right = e1freenode * eid1; updedge.next_right = e1freenode * newedge.edge_id; i = rtt_be_updateEdges(topo, &seledge, RTT_COL_EDGE_NEXT_RIGHT, &updedge, RTT_COL_EDGE_NEXT_RIGHT, NULL, 0); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } /* delete the edges (only second on modEdge or both) */ i = rtt_be_deleteEdges(topo, e2, RTT_COL_EDGE_EDGE_ID); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( ! modEdge ) { i = rtt_be_deleteEdges(topo, e1, RTT_COL_EDGE_EDGE_ID); if ( i == -1 ) { _rtt_release_edges(iface->ctx, edges, nedges); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } } _rtt_release_edges(iface->ctx, edges, nedges); /* delete the common node */ i = rtt_be_deleteNodesById( topo, &commonnode, 1 ); if ( i == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } /* -- -- NOT IN THE SPECS: -- Drop composition rows involving second -- edge, as the first edge took its space, -- and all affected TopoGeom have been previously checked -- for being composed by both edges. */ if ( ! rtt_be_updateTopoGeomEdgeHeal(topo, eid1, eid2, newedge.edge_id) ) { rterror(iface->ctx, "%s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } return modEdge ? commonnode : newedge.edge_id; } RTT_ELEMID rtt_ModEdgeHeal( RTT_TOPOLOGY* topo, RTT_ELEMID e1, RTT_ELEMID e2 ) { return _rtt_HealEdges( topo, e1, e2, 1 ); } RTT_ELEMID rtt_NewEdgeHeal( RTT_TOPOLOGY* topo, RTT_ELEMID e1, RTT_ELEMID e2 ) { return _rtt_HealEdges( topo, e1, e2, 0 ); } RTT_ELEMID rtt_GetNodeByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol) { RTT_ISO_NODE *elem; int num; int flds = RTT_COL_NODE_NODE_ID|RTT_COL_NODE_GEOM; /* geom not needed */ RTT_ELEMID id = 0; RTPOINT2D qp; /* query point */ const RTT_BE_IFACE *iface = topo->be_iface; if ( ! rt_getPoint2d_p(iface->ctx, pt->point, 0, &qp) ) { rterror(iface->ctx, "Empty query point"); return -1; } elem = rtt_be_getNodeWithinDistance2D(topo, pt, tol, &num, flds, 0); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } else if ( num ) { if ( num > 1 ) { _rtt_release_nodes(iface->ctx, elem, num); rterror(iface->ctx, "Two or more nodes found"); return -1; } id = elem[0].node_id; _rtt_release_nodes(iface->ctx, elem, num); } return id; } RTT_ELEMID rtt_GetEdgeByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol) { RTT_ISO_EDGE *elem; int num, i; int flds = RTT_COL_EDGE_EDGE_ID|RTT_COL_EDGE_GEOM; /* GEOM is not needed */ RTT_ELEMID id = 0; const RTT_BE_IFACE *iface = topo->be_iface; RTGEOM *qp = rtpoint_as_rtgeom(iface->ctx, pt); /* query point */ if ( rtgeom_is_empty(iface->ctx, qp) ) { rterror(iface->ctx, "Empty query point"); return -1; } elem = rtt_be_getEdgeWithinDistance2D(topo, pt, tol, &num, flds, 0); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; igeom ) { _rtt_release_edges(iface->ctx, elem, num); rtnotice(iface->ctx, "Corrupted topology: edge %" RTTFMT_ELEMID " has null geometry", e->edge_id); continue; } /* Should we check for intersection not being on an endpoint * as documented ? */ geom = rtline_as_rtgeom(iface->ctx, e->geom); dist = rtgeom_mindistance2d_tolerance(iface->ctx, geom, qp, tol); if ( dist > tol ) continue; #endif if ( id ) { _rtt_release_edges(iface->ctx, elem, num); rterror(iface->ctx, "Two or more edges found"); return -1; } else id = e->edge_id; } if ( num ) _rtt_release_edges(iface->ctx, elem, num); return id; } RTT_ELEMID rtt_GetFaceByPoint(RTT_TOPOLOGY *topo, RTPOINT *pt, double tol) { RTT_ELEMID id = 0; RTT_ISO_EDGE *elem; int num, i; int flds = RTT_COL_EDGE_EDGE_ID | RTT_COL_EDGE_GEOM | RTT_COL_EDGE_FACE_LEFT | RTT_COL_EDGE_FACE_RIGHT; const RTT_BE_IFACE *iface = topo->be_iface; RTGEOM *qp = rtpoint_as_rtgeom(iface->ctx, pt); id = rtt_be_getFaceContainingPoint(topo, pt); if ( id == -2 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( id > 0 ) { return id; } id = 0; /* or it'll be -1 for not found */ RTDEBUG(1, "No face properly contains query point," " looking for edges"); /* Not in a face, may be in universe or on edge, let's check * for distance */ /* NOTE: we never pass a tolerance of 0 to avoid ever using * ST_Within, which doesn't include endpoints matches */ elem = rtt_be_getEdgeWithinDistance2D(topo, pt, tol?tol:1e-5, &num, flds, 0); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } for (i=0; igeom ) { _rtt_release_edges(iface->ctx, elem, num); rtnotice(iface->ctx, "Corrupted topology: edge %" RTTFMT_ELEMID " has null geometry", e->edge_id); continue; } /* don't consider dangling edges */ if ( e->face_left == e->face_right ) { RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " is dangling, won't consider it", e->edge_id); continue; } geom = rtline_as_rtgeom(iface->ctx, e->geom); dist = rtgeom_mindistance2d_tolerance(iface->ctx, geom, qp, tol); RTDEBUGF(1, "Distance from edge %" RTTFMT_ELEMID " is %g (tol=%g)", e->edge_id, dist, tol); /* we won't consider edges too far */ if ( dist > tol ) continue; if ( e->face_left == 0 ) { eface = e->face_right; } else if ( e->face_right == 0 ) { eface = e->face_left; } else { _rtt_release_edges(iface->ctx, elem, num); rterror(iface->ctx, "Two or more faces found"); return -1; } if ( id && id != eface ) { _rtt_release_edges(iface->ctx, elem, num); rterror(iface->ctx, "Two or more faces found" #if 0 /* debugging */ " (%" RTTFMT_ELEMID " and %" RTTFMT_ELEMID ")", id, eface #endif ); return -1; } else id = eface; } if ( num ) _rtt_release_edges(iface->ctx, elem, num); return id; } /* Return the smallest delta that can perturbate * the maximum absolute value of a geometry ordinate */ static double _rtt_minTolerance(const RTCTX *ctx, RTGEOM *g ) { const RTGBOX* gbox; double max; double ret; gbox = rtgeom_get_bbox(ctx, g); if ( ! gbox ) return 0; /* empty */ max = FP_ABS(gbox->xmin); if ( max < FP_ABS(gbox->xmax) ) max = FP_ABS(gbox->xmax); if ( max < FP_ABS(gbox->ymin) ) max = FP_ABS(gbox->ymin); if ( max < FP_ABS(gbox->ymax) ) max = FP_ABS(gbox->ymax); ret = 3.6 * pow(10, - ( 15 - log10(max?max:1.0) ) ); return ret; } #define _RTT_MINTOLERANCE( topo, geom ) ( \ topo->precision ? topo->precision : _rtt_minTolerance(topo->be_iface->ctx, geom) ) typedef struct scored_pointer_t { void *ptr; double score; } scored_pointer; static int compare_scored_pointer(const void *si1, const void *si2) { double a = ((scored_pointer *)si1)->score; double b = ((scored_pointer *)si2)->score; if ( a < b ) return -1; else if ( a > b ) return 1; else return 0; } RTT_ELEMID rtt_AddPoint(RTT_TOPOLOGY* topo, RTPOINT* point, double tol) { int num, i; double mindist = FLT_MAX; RTT_ISO_NODE *nodes, *nodes2; RTT_ISO_EDGE *edges, *edges2; const RTT_BE_IFACE *iface = topo->be_iface; RTGEOM *pt = rtpoint_as_rtgeom(iface->ctx, point); int flds; RTT_ELEMID id = 0; scored_pointer *sorted; /* Get tolerance, if 0 was given */ if ( ! tol ) tol = _RTT_MINTOLERANCE( topo, pt ); RTDEBUGG(1, pt, "Adding point"); /* -- 1. Check if any existing node is closer than the given precision -- and if so pick the closest TODO: use WithinBox2D */ flds = RTT_COL_NODE_NODE_ID|RTT_COL_NODE_GEOM; nodes = rtt_be_getNodeWithinDistance2D(topo, point, tol, &num, flds, 0); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { RTDEBUGF(1, "New point is within %.15g units of %d nodes", tol, num); /* Order by distance if there are more than a single return */ if ( num > 1 ) {{ sorted= rtalloc(iface->ctx, sizeof(scored_pointer)*num); for (i=0; ictx, rtpoint_as_rtgeom(iface->ctx, nodes[i].geom), pt); RTDEBUGF(1, "Node %" RTTFMT_ELEMID " distance: %.15g", ((RTT_ISO_NODE*)(sorted[i].ptr))->node_id, sorted[i].score); } qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer); nodes2 = rtalloc(iface->ctx, sizeof(RTT_ISO_NODE)*num); for (i=0; ictx, sorted); rtfree(iface->ctx, nodes); nodes = nodes2; }} for ( i=0; ictx, n->geom); double dist = rtgeom_mindistance2d(iface->ctx, g, pt); /* TODO: move this check in the previous sort scan ... */ if ( dist >= tol ) continue; /* must be closer than tolerated */ if ( ! id || dist < mindist ) { id = n->node_id; mindist = dist; } } if ( id ) { /* found an existing node */ if ( nodes ) _rtt_release_nodes(iface->ctx, nodes, num); return id; } } _rtt_EnsureGeos(iface->ctx); /* -- 2. Check if any existing edge falls within tolerance -- and if so split it by a point projected on it TODO: use WithinBox2D */ flds = RTT_COL_EDGE_EDGE_ID|RTT_COL_EDGE_GEOM; edges = rtt_be_getEdgeWithinDistance2D(topo, point, tol, &num, flds, 0); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { RTDEBUGF(1, "New point is within %.15g units of %d edges", tol, num); /* Order by distance if there are more than a single return */ if ( num > 1 ) {{ int j; sorted = rtalloc(iface->ctx, sizeof(scored_pointer)*num); for (i=0; ictx, rtline_as_rtgeom(iface->ctx, edges[i].geom), pt); RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " distance: %.15g", ((RTT_ISO_EDGE*)(sorted[i].ptr))->edge_id, sorted[i].score); } qsort(sorted, num, sizeof(scored_pointer), compare_scored_pointer); edges2 = rtalloc(iface->ctx, sizeof(RTT_ISO_EDGE)*num); for (j=0, i=0; ictx, ((RTT_ISO_EDGE*)sorted[i].ptr)->geom); } } num = j; rtfree(iface->ctx, sorted); rtfree(iface->ctx, edges); edges = edges2; }} for (i=0; ictx, e->geom); RTGEOM *prj; int contains; GEOSGeometry *prjg, *gg; RTT_ELEMID edge_id = e->edge_id; RTDEBUGF(1, "Splitting edge %" RTTFMT_ELEMID, edge_id); /* project point to line, split edge by point */ prj = rtgeom_closest_point(iface->ctx, g, pt); if ( rtgeom_has_z(iface->ctx, pt) ) {{ /* -- This is a workaround for ClosestPoint lack of Z support: -- http://trac.osgeo.org/postgis/ticket/2033 */ RTGEOM *tmp; double z; RTPOINT4D p4d; RTPOINT *prjpt; /* add Z to "prj" */ tmp = rtgeom_force_3dz(iface->ctx, prj); prjpt = rtgeom_as_rtpoint(iface->ctx, tmp); rt_getPoint4d_p(iface->ctx, point->point, 0, &p4d); z = p4d.z; rt_getPoint4d_p(iface->ctx, prjpt->point, 0, &p4d); p4d.z = z; ptarray_set_point4d(iface->ctx, prjpt->point, 0, &p4d); rtgeom_free(iface->ctx, prj); prj = tmp; }} prjg = RTGEOM2GEOS(iface->ctx, prj, 0); if ( ! prjg ) { rtgeom_free(iface->ctx, prj); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } gg = RTGEOM2GEOS(iface->ctx, g, 0); if ( ! gg ) { rtgeom_free(iface->ctx, prj); _rtt_release_edges(iface->ctx, edges, num); GEOSGeom_destroy_r(iface->ctx->gctx, prjg); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } contains = GEOSContains_r(iface->ctx->gctx, gg, prjg); GEOSGeom_destroy_r(iface->ctx->gctx, prjg); GEOSGeom_destroy_r(iface->ctx->gctx, gg); if ( contains == 2 ) { rtgeom_free(iface->ctx, prj); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "GEOS exception on Contains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } if ( ! contains ) {{ double snaptol; RTGEOM *snapedge; RTLINE *snapline; RTPOINT4D p1, p2; RTDEBUGF(1, "Edge %" RTTFMT_ELEMID " does not contain projected point to it", edge_id); /* In order to reduce the robustness issues, we'll pick * an edge that contains the projected point, if possible */ if ( i+1 < num ) { RTDEBUG(1, "But there's another to check"); rtgeom_free(iface->ctx, prj); continue; } /* -- The tolerance must be big enough for snapping to happen -- and small enough to snap only to the projected point. -- Unfortunately ST_Distance returns 0 because it also uses -- a projected point internally, so we need another way. */ snaptol = _rtt_minTolerance(iface->ctx, prj); snapedge = _rtt_toposnap(iface->ctx, g, prj, snaptol); snapline = rtgeom_as_rtline(iface->ctx, snapedge); RTDEBUGF(1, "Edge snapped with tolerance %g", snaptol); /* TODO: check if snapping did anything ? */ #if RTGEOM_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = rtgeom_to_wkt(iface->ctx, g, RTWKT_EXTENDED, 15, &sz); char *wkt2 = rtgeom_to_wkt(iface->ctx, snapedge, RTWKT_EXTENDED, 15, &sz); RTDEBUGF(1, "Edge %s snapped became %s", wkt1, wkt2); rtfree(iface->ctx, wkt1); rtfree(iface->ctx, wkt2); } #endif /* -- Snapping currently snaps the first point below tolerance -- so may possibly move first point. See ticket #1631 */ rt_getPoint4d_p(iface->ctx, e->geom->points, 0, &p1); rt_getPoint4d_p(iface->ctx, snapline->points, 0, &p2); RTDEBUGF(1, "Edge first point is %g %g, " "snapline first point is %g %g", p1.x, p1.y, p2.x, p2.y); if ( p1.x != p2.x || p1.y != p2.y ) { RTDEBUG(1, "Snapping moved first point, re-adding it"); if ( RT_SUCCESS != ptarray_insert_point(iface->ctx, snapline->points, &p1, 0) ) { rtgeom_free(iface->ctx, prj); rtgeom_free(iface->ctx, snapedge); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "GEOS exception on Contains: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } #if RTGEOM_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = rtgeom_to_wkt(iface->ctx, g, RTWKT_EXTENDED, 15, &sz); RTDEBUGF(1, "Tweaked snapline became %s", wkt1); rtfree(iface->ctx, wkt1); } #endif } #if RTGEOM_DEBUG_LEVEL > 0 else { RTDEBUG(1, "Snapping did not move first point"); } #endif if ( -1 == rtt_ChangeEdgeGeom( topo, edge_id, snapline ) ) { /* TODO: should have invoked rterror already, leaking memory */ rtgeom_free(iface->ctx, prj); rtgeom_free(iface->ctx, snapedge); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "rtt_ChangeEdgeGeom failed"); return -1; } rtgeom_free(iface->ctx, snapedge); }} #if RTGEOM_DEBUG_LEVEL > 0 else {{ size_t sz; char *wkt1 = rtgeom_to_wkt(iface->ctx, g, RTWKT_EXTENDED, 15, &sz); char *wkt2 = rtgeom_to_wkt(iface->ctx, prj, RTWKT_EXTENDED, 15, &sz); RTDEBUGF(1, "Edge %s contains projected point %s", wkt1, wkt2); rtfree(iface->ctx, wkt1); rtfree(iface->ctx, wkt2); }} #endif /* TODO: pass 1 as last argument (skipChecks) ? */ id = rtt_ModEdgeSplit( topo, edge_id, rtgeom_as_rtpoint(iface->ctx, prj), 0 ); if ( -1 == id ) { /* TODO: should have invoked rterror already, leaking memory */ rtgeom_free(iface->ctx, prj); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "rtt_ModEdgeSplit failed"); return -1; } rtgeom_free(iface->ctx, prj); /* * TODO: decimate the two new edges with the given tolerance ? * * the edge identifiers to decimate would be: edge_id and "id" * The problem here is that decimation of existing edges * may introduce intersections or topological inconsistencies, * for example: * * - A node may end up falling on the other side of the edge * - The decimated edge might intersect another existing edge * */ break; /* we only want to snap a single edge */ } _rtt_release_edges(iface->ctx, edges, num); } else { /* The point is isolated, add it as such */ /* TODO: pass 1 as last argument (skipChecks) ? */ id = rtt_AddIsoNode(topo, -1, point, 0); if ( -1 == id ) { /* should have invoked rterror already, leaking memory */ rterror(iface->ctx, "rtt_AddIsoNode failed"); return -1; } } return id; } /* Return identifier of an equal edge, 0 if none or -1 on error * (and rterror gets called on error) */ static RTT_ELEMID _rtt_GetEqualEdge( RTT_TOPOLOGY *topo, RTLINE *edge ) { const RTT_BE_IFACE *iface = topo->be_iface; RTT_ELEMID id; RTT_ISO_EDGE *edges; int num, i; const RTGBOX *qbox = rtgeom_get_bbox(iface->ctx, rtline_as_rtgeom(iface->ctx, edge) ); GEOSGeometry *edgeg; const int flds = RTT_COL_EDGE_EDGE_ID|RTT_COL_EDGE_GEOM; edges = rtt_be_getEdgeWithinBox2D( topo, qbox, &num, flds, 0 ); if ( num == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } if ( num ) { _rtt_EnsureGeos(iface->ctx); edgeg = RTGEOM2GEOS(iface->ctx, rtline_as_rtgeom(iface->ctx, edge), 0 ); if ( ! edgeg ) { _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } for (i=0; ictx, e->geom); GEOSGeometry *gg; int equals; gg = RTGEOM2GEOS(iface->ctx, g, 0 ); if ( ! gg ) { GEOSGeom_destroy_r(iface->ctx->gctx, edgeg); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } equals = GEOSEquals_r(iface->ctx->gctx, gg, edgeg); GEOSGeom_destroy_r(iface->ctx->gctx, gg); if ( equals == 2 ) { GEOSGeom_destroy_r(iface->ctx->gctx, edgeg); _rtt_release_edges(iface->ctx, edges, num); rterror(iface->ctx, "GEOSEquals exception: %s", rtgeom_get_last_geos_error(iface->ctx)); return -1; } if ( equals ) { id = e->edge_id; GEOSGeom_destroy_r(iface->ctx->gctx, edgeg); _rtt_release_edges(iface->ctx, edges, num); return id; } } GEOSGeom_destroy_r(iface->ctx->gctx, edgeg); _rtt_release_edges(iface->ctx, edges, num); } return 0; } /* * Add a pre-noded pre-split line edge. Used by rtt_AddLine * Return edge id, 0 if none added (empty edge), -1 on error */ static RTT_ELEMID _rtt_AddLineEdge( RTT_TOPOLOGY* topo, RTLINE* edge, double tol ) { const RTT_BE_IFACE *iface = topo->be_iface; RTCOLLECTION *col; RTPOINT *start_point, *end_point; RTGEOM *tmp, *tmp2; RTT_ISO_NODE *node; RTT_ELEMID nid[2]; /* start_node, end_node */ RTT_ELEMID id; /* edge id */ RTPOINT4D p4d; int nn, i; RTDEBUGG(1, rtline_as_rtgeom(iface->ctx, edge), "_lwtAddLineEdge"); RTDEBUGF(1, "_lwtAddLineEdge with tolerance %g", tol); start_point = rtline_get_rtpoint(iface->ctx, edge, 0); if ( ! start_point ) { rtnotice(iface->ctx, "Empty component of noded line"); return 0; /* must be empty */ } nid[0] = rtt_AddPoint( topo, start_point, tol ); rtpoint_free(iface->ctx, start_point); /* too late if rtt_AddPoint calls rterror */ if ( nid[0] == -1 ) return -1; /* rterror should have been called */ end_point = rtline_get_rtpoint(iface->ctx, edge, edge->points->npoints-1); if ( ! end_point ) { rterror(iface->ctx, "could not get last point of line " "after successfully getting first point !?"); return -1; } nid[1] = rtt_AddPoint( topo, end_point, tol ); rtpoint_free(iface->ctx, end_point); /* too late if rtt_AddPoint calls rterror */ if ( nid[1] == -1 ) return -1; /* rterror should have been called */ /* -- Added endpoints may have drifted due to tolerance, so -- we need to re-snap the edge to the new nodes before adding it */ nn = nid[0] == nid[1] ? 1 : 2; node = rtt_be_getNodeById( topo, nid, &nn, RTT_COL_NODE_NODE_ID|RTT_COL_NODE_GEOM ); if ( nn == -1 ) { rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return -1; } start_point = NULL; end_point = NULL; for (i=0; ictx, node, nn); rterror(iface->ctx, "Could not find just-added nodes % " RTTFMT_ELEMID " and %" RTTFMT_ELEMID, nid[0], nid[1]); return -1; } /* snap */ rt_getPoint4d_p(iface->ctx, start_point->point, 0, &p4d ); rtline_setPoint4d(iface->ctx, edge, 0, &p4d); rt_getPoint4d_p(iface->ctx, end_point->point, 0, &p4d ); rtline_setPoint4d(iface->ctx, edge, edge->points->npoints-1, &p4d); if ( nn ) _rtt_release_nodes(iface->ctx, node, nn); /* make valid, after snap (to handle collapses) */ tmp = rtgeom_make_valid(iface->ctx, rtline_as_rtgeom(iface->ctx, edge)); col = rtgeom_as_rtcollection(iface->ctx, tmp); if ( col ) {{ col = rtcollection_extract(iface->ctx, col, RTLINETYPE); /* Check if the so-snapped edge collapsed (see #1650) */ if ( col->ngeoms == 0 ) { rtcollection_free(iface->ctx, col); rtgeom_free(iface->ctx, tmp); RTDEBUG(1, "Made-valid snapped edge collapsed"); return 0; } tmp2 = rtgeom_clone_deep(iface->ctx, col->geoms[0] ); rtgeom_free(iface->ctx, tmp); tmp = tmp2; edge = rtgeom_as_rtline(iface->ctx, tmp); rtcollection_free(iface->ctx, col); if ( ! edge ) { /* should never happen */ rterror(iface->ctx, "rtcollection_extract(iface->ctx, RTLINETYPE) returned a non-line?"); return -1; } }} else { edge = rtgeom_as_rtline(iface->ctx, tmp); if ( ! edge ) { RTDEBUGF(1, "Made-valid snapped edge collapsed to %s", rttype_name(iface->ctx, rtgeom_get_type(iface->ctx, tmp))); rtgeom_free(iface->ctx, tmp); return 0; } } /* check if the so-snapped edge _now_ exists */ id = _rtt_GetEqualEdge ( topo, edge ); RTDEBUGF(1, "_rtt_GetEqualEdge returned %" RTTFMT_ELEMID, id); if ( id == -1 ) { rtgeom_free(iface->ctx, tmp); /* probably too late, due to internal rterror */ return -1; } if ( id ) { rtgeom_free(iface->ctx, tmp); /* possibly takes "edge" down with it */ return id; } /* No previously existing edge was found, we'll add one */ /* Remove consecutive vertices below given tolerance * on edge addition */ if ( tol ) {{ tmp2 = rtline_remove_repeated_points(iface->ctx, edge, tol); RTDEBUGG(1, tmp2, "Repeated-point removed"); edge = rtgeom_as_rtline(iface->ctx, tmp2); rtgeom_free(iface->ctx, tmp); tmp = tmp2; /* check if the so-decimated edge _now_ exists */ id = _rtt_GetEqualEdge ( topo, edge ); RTDEBUGF(1, "_rtt_GetEqualEdge returned %" RTTFMT_ELEMID, id); if ( id == -1 ) { rtgeom_free(iface->ctx, tmp); /* probably too late, due to internal lwerror */ return -1; } if ( id ) { rtgeom_free(iface->ctx, tmp); /* takes "edge" down with it */ return id; } }} /* TODO: skip checks ? */ id = rtt_AddEdgeModFace( topo, nid[0], nid[1], edge, 0 ); RTDEBUGF(1, "rtt_AddEdgeModFace returned %" RTTFMT_ELEMID, id); if ( id == -1 ) { rtgeom_free(iface->ctx, tmp); /* probably too late, due to internal rterror */ return -1; } rtgeom_free(iface->ctx, tmp); /* possibly takes "edge" down with it */ return id; } /* Simulate split-loop as it was implemented in pl/pgsql version * of TopoGeo_addLinestring */ static RTGEOM * _rtt_split_by_nodes(const RTCTX *ctx, const RTGEOM *g, const RTGEOM *nodes) { RTCOLLECTION *col = rtgeom_as_rtcollection(ctx, nodes); int i; RTGEOM *bg; bg = rtgeom_clone_deep(ctx, g); if ( ! col->ngeoms ) return bg; for (i=0; ingeoms; ++i) { RTGEOM *g2; g2 = rtgeom_split(ctx, bg, col->geoms[i]); rtgeom_free(ctx, bg); bg = g2; } bg->srid = nodes->srid; return bg; } RTT_ELEMID* rtt_AddLine(RTT_TOPOLOGY* topo, RTLINE* line, double tol, int* nedges) { const RTT_BE_IFACE *iface = topo->be_iface; RTGEOM *geomsbuf[1]; RTGEOM **geoms; int ngeoms; RTGEOM *noded, *tmp; RTCOLLECTION *col; RTT_ELEMID *ids; RTT_ISO_EDGE *edges; RTT_ISO_NODE *nodes; int num; int i; RTGBOX qbox; *nedges = -1; /* error condition, by default */ /* Get tolerance, if 0 was given */ if ( ! tol ) tol = _RTT_MINTOLERANCE( topo, (RTGEOM*)line ); RTDEBUGF(1, "Working tolerance:%.15g", tol); RTDEBUGF(1, "Input line has srid=%d", line->srid); /* Remove consecutive vertices below given tolerance upfront */ if ( tol ) {{ RTLINE *clean = rtgeom_as_rtline(iface->ctx, rtline_remove_repeated_points(iface->ctx, line, tol)); tmp = rtline_as_rtgeom(iface->ctx, clean); /* NOTE: might collapse to non-simple */ RTDEBUGG(1, tmp, "Repeated-point removed"); }} else tmp=(RTGEOM*)line; /* 1. Self-node */ noded = rtgeom_node(iface->ctx, (RTGEOM*)tmp); if ( tmp != (RTGEOM*)line ) rtgeom_free(iface->ctx, tmp); if ( ! noded ) return NULL; /* should have called rterror already */ RTDEBUGG(1, noded, "Noded"); qbox = *rtgeom_get_bbox(iface->ctx, rtline_as_rtgeom(iface->ctx, line) ); RTDEBUGF(1, "Line BOX is %.15g %.15g, %.15g %.15g", qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax); gbox_expand(iface->ctx, &qbox, tol); RTDEBUGF(1, "BOX expanded by %g is %.15g %.15g, %.15g %.15g", tol, qbox.xmin, qbox.ymin, qbox.xmax, qbox.ymax); /* 2. Node to edges falling within tol distance */ edges = rtt_be_getEdgeWithinBox2D( topo, &qbox, &num, RTT_COL_EDGE_ALL, 0 ); if ( num == -1 ) { rtgeom_free(iface->ctx, noded); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } RTDEBUGF(1, "Line bbox intersects %d edges bboxes", num); if ( num ) {{ /* collect those whose distance from us is < tol */ RTGEOM **nearby = rtalloc(iface->ctx, sizeof(RTGEOM *)*num); int nn=0; for (i=0; ictx, e->geom); double dist = rtgeom_mindistance2d(iface->ctx, g, noded); if ( dist >= tol ) continue; /* must be closer than tolerated */ nearby[nn++] = g; } if ( nn ) {{ RTCOLLECTION *col; RTGEOM *iedges; /* just an alias for col */ RTGEOM *snapped; RTGEOM *set1, *set2; RTDEBUGF(1, "Line intersects %d edges", nn); col = rtcollection_construct(iface->ctx, RTCOLLECTIONTYPE, topo->srid, NULL, nn, nearby); iedges = rtcollection_as_rtgeom(iface->ctx, col); RTDEBUGG(1, iedges, "Collected edges"); RTDEBUGF(1, "Snapping noded, with srid=%d " "to interesecting edges, with srid=%d", noded->srid, iedges->srid); snapped = _rtt_toposnap(iface->ctx, noded, iedges, tol); rtgeom_free(iface->ctx, noded); RTDEBUGG(1, snapped, "Snapped"); RTDEBUGF(1, "Diffing snapped, with srid=%d " "and interesecting edges, with srid=%d", snapped->srid, iedges->srid); noded = rtgeom_difference(iface->ctx, snapped, iedges); RTDEBUGG(1, noded, "Differenced"); RTDEBUGF(1, "Intersecting snapped, with srid=%d " "and interesecting edges, with srid=%d", snapped->srid, iedges->srid); set1 = rtgeom_intersection(iface->ctx, snapped, iedges); RTDEBUGG(1, set1, "Intersected"); rtgeom_free(iface->ctx, snapped); RTDEBUGF(1, "Linemerging set1, with srid=%d", set1->srid); set2 = rtgeom_linemerge(iface->ctx, set1); RTDEBUGG(1, set2, "Linemerged"); RTDEBUGG(1, noded, "Noded"); rtgeom_free(iface->ctx, set1); RTDEBUGF(1, "Unioning noded, with srid=%d " "and set2, with srid=%d", noded->srid, set2->srid); set1 = rtgeom_union(iface->ctx, noded, set2); rtgeom_free(iface->ctx, set2); rtgeom_free(iface->ctx, noded); noded = set1; RTDEBUGG(1, set1, "Unioned"); /* will not release the geoms array */ rtcollection_release(iface->ctx, col); }} rtfree(iface->ctx, nearby); _rtt_release_edges(iface->ctx, edges, num); }} /* 2.1. Node with existing nodes within tol * TODO: check if we should be only considering _isolated_ nodes! */ nodes = rtt_be_getNodeWithinBox2D( topo, &qbox, &num, RTT_COL_NODE_ALL, 0 ); if ( num == -1 ) { rtgeom_free(iface->ctx, noded); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } RTDEBUGF(1, "Line bbox intersects %d nodes bboxes", num); if ( num ) {{ /* collect those whose distance from us is < tol */ RTGEOM **nearby = rtalloc(iface->ctx, sizeof(RTGEOM *)*num); int nn=0; for (i=0; ictx, n->geom); double dist = rtgeom_mindistance2d(iface->ctx, g, noded); if ( dist >= tol ) continue; /* must be closer than tolerated */ nearby[nn++] = g; } if ( nn ) {{ RTCOLLECTION *col; RTGEOM *inodes; /* just an alias for col */ RTGEOM *tmp; RTDEBUGF(1, "Line intersects %d nodes", nn); col = rtcollection_construct(iface->ctx, RTMULTIPOINTTYPE, topo->srid, NULL, nn, nearby); inodes = rtcollection_as_rtgeom(iface->ctx, col); RTDEBUGG(1, inodes, "Collected nodes"); /* TODO: consider snapping once against all elements * (rather than once with edges and once with nodes) */ tmp = _rtt_toposnap(iface->ctx, noded, inodes, tol); rtgeom_free(iface->ctx, noded); noded = tmp; RTDEBUGG(1, noded, "Node-snapped"); tmp = _rtt_split_by_nodes(iface->ctx, noded, inodes); /* rtgeom_split(iface->ctx, noded, inodes); */ rtgeom_free(iface->ctx, noded); noded = tmp; RTDEBUGG(1, noded, "Node-split"); /* will not release the geoms array */ rtcollection_release(iface->ctx, col); /* -- re-node to account for ST_Snap introduced self-intersections -- See http://trac.osgeo.org/postgis/ticket/1714 -- TODO: consider running UnaryUnion once after all noding */ tmp = rtgeom_unaryunion(iface->ctx, noded); rtgeom_free(iface->ctx, noded); noded = tmp; RTDEBUGG(1, noded, "Unary-unioned"); }} rtfree(iface->ctx, nearby); _rtt_release_nodes(iface->ctx, nodes, num); }} RTDEBUGG(1, noded, "Finally-noded"); /* 3. For each (now-noded) segment, insert an edge */ col = rtgeom_as_rtcollection(iface->ctx, noded); if ( col ) { RTDEBUG(1, "Noded line was a collection"); geoms = col->geoms; ngeoms = col->ngeoms; } else { RTDEBUG(1, "Noded line was a single geom"); geomsbuf[0] = noded; geoms = geomsbuf; ngeoms = 1; } RTDEBUGF(1, "Line was split into %d edges", ngeoms); /* TODO: refactor to first add all nodes (re-snapping edges if * needed) and then check all edges for existing already * ( so to save a DB scan for each edge to be added ) */ ids = rtalloc(iface->ctx, sizeof(RTT_ELEMID)*ngeoms); num = 0; for ( i=0; isrid = noded->srid; #if RTGEOM_DEBUG_LEVEL > 0 { size_t sz; char *wkt1 = rtgeom_to_wkt(iface->ctx, g, RTWKT_EXTENDED, 15, &sz); RTDEBUGF(1, "Component %d of split line is: %s", i, wkt1); rtfree(iface->ctx, wkt1); } #endif id = _rtt_AddLineEdge( topo, rtgeom_as_rtline(iface->ctx, g), tol ); RTDEBUGF(1, "_rtt_AddLineEdge returned %" RTTFMT_ELEMID, id); if ( id < 0 ) { rtgeom_free(iface->ctx, noded); rtfree(iface->ctx, ids); return NULL; } if ( ! id ) { RTDEBUGF(1, "Component %d of split line collapsed", i); continue; } RTDEBUGF(1, "Component %d of split line is edge %" RTTFMT_ELEMID, i, id); ids[num++] = id; /* TODO: skip duplicates */ } RTDEBUGG(1, noded, "Noded before free"); rtgeom_free(iface->ctx, noded); /* TODO: XXX remove duplicated ids if not done before */ *nedges = num; return ids; } RTT_ELEMID* rtt_AddPolygon(RTT_TOPOLOGY* topo, RTPOLY* poly, double tol, int* nfaces) { const RTT_BE_IFACE *iface = topo->be_iface; int i; *nfaces = -1; /* error condition, by default */ int num; RTT_ISO_FACE *faces; int nfacesinbox; RTT_ELEMID *ids = NULL; RTGBOX qbox; const GEOSPreparedGeometry *ppoly; GEOSGeometry *polyg; /* Get tolerance, if 0 was given */ if ( ! tol ) tol = _RTT_MINTOLERANCE( topo, (RTGEOM*)poly ); RTDEBUGF(1, "Working tolerance:%.15g", tol); /* Add each ring as an edge */ for ( i=0; inrings; ++i ) { RTLINE *line; RTPOINTARRAY *pa; RTT_ELEMID *eids; int nedges; pa = ptarray_clone(iface->ctx, poly->rings[i]); line = rtline_construct(iface->ctx, topo->srid, NULL, pa); eids = rtt_AddLine( topo, line, tol, &nedges ); if ( nedges < 0 ) { /* probably too late as rtt_AddLine invoked rterror */ rtline_free(iface->ctx, line); rterror(iface->ctx, "Error adding ring %d of polygon", i); return NULL; } rtline_free(iface->ctx, line); rtfree(iface->ctx, eids); } /* -- Find faces covered by input polygon -- NOTE: potential snapping changed polygon edges */ qbox = *rtgeom_get_bbox(iface->ctx, rtpoly_as_rtgeom(iface->ctx, poly) ); gbox_expand(iface->ctx, &qbox, tol); faces = rtt_be_getFaceWithinBox2D( topo, &qbox, &nfacesinbox, RTT_COL_FACE_ALL, 0 ); if ( nfacesinbox == -1 ) { rtfree(iface->ctx, ids); rterror(iface->ctx, "Backend error: %s", rtt_be_lastErrorMessage(topo->be_iface)); return NULL; } num = 0; if ( nfacesinbox ) { polyg = RTGEOM2GEOS(iface->ctx, rtpoly_as_rtgeom(iface->ctx, poly), 0); if ( ! polyg ) { _rtt_release_faces(iface->ctx, faces, nfacesinbox); rterror(iface->ctx, "Could not convert poly geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return NULL; } ppoly = GEOSPrepare_r(iface->ctx->gctx, polyg); ids = rtalloc(iface->ctx, sizeof(RTT_ELEMID)*nfacesinbox); for ( i=0; iface_id ); if ( ! fg ) { i = f->face_id; /* so we can destroy faces */ GEOSPreparedGeom_destroy_r(iface->ctx->gctx, ppoly); GEOSGeom_destroy_r(iface->ctx->gctx, polyg); rtfree(iface->ctx, ids); _rtt_release_faces(iface->ctx, faces, nfacesinbox); rterror(iface->ctx, "Could not get geometry of face %" RTTFMT_ELEMID, i); return NULL; } /* check if a point on this face's surface is covered by our polygon */ fgg = RTGEOM2GEOS(iface->ctx, fg, 0); rtgeom_free(iface->ctx, fg); if ( ! fgg ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, ppoly); GEOSGeom_destroy_r(iface->ctx->gctx, polyg); _rtt_release_faces(iface->ctx, faces, nfacesinbox); rterror(iface->ctx, "Could not convert edge geometry to GEOS: %s", rtgeom_get_last_geos_error(iface->ctx)); return NULL; } sp = GEOSPointOnSurface_r(iface->ctx->gctx, fgg); GEOSGeom_destroy_r(iface->ctx->gctx, fgg); if ( ! sp ) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, ppoly); GEOSGeom_destroy_r(iface->ctx->gctx, polyg); _rtt_release_faces(iface->ctx, faces, nfacesinbox); rterror(iface->ctx, "Could not find point on face surface: %s", rtgeom_get_last_geos_error(iface->ctx)); return NULL; } covers = GEOSPreparedCovers_r(iface->ctx->gctx, ppoly, sp ); GEOSGeom_destroy_r(iface->ctx->gctx, sp); if (covers == 2) { GEOSPreparedGeom_destroy_r(iface->ctx->gctx, ppoly); GEOSGeom_destroy_r(iface->ctx->gctx, polyg); _rtt_release_faces(iface->ctx, faces, nfacesinbox); rterror(iface->ctx, "PreparedCovers error: %s", rtgeom_get_last_geos_error(iface->ctx)); return NULL; } if ( ! covers ) { continue; /* we're not composed by this face */ } /* TODO: avoid duplicates ? */ ids[num++] = f->face_id; } GEOSPreparedGeom_destroy_r(iface->ctx->gctx, ppoly); GEOSGeom_destroy_r(iface->ctx->gctx, polyg); _rtt_release_faces(iface->ctx, faces, nfacesinbox); } /* possibly 0 if non face's surface point was found * to be covered by input polygon */ *nfaces = num; return ids; } src/rthomogenize.c000066400000000000000000000161001271715413500145270ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2010 Olivier Courtin * **********************************************************************/ #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" typedef struct { int cnt[RTNUMTYPES]; RTCOLLECTION* buf[RTNUMTYPES]; } HomogenizeBuffer; static void init_homogenizebuffer(const RTCTX *ctx, HomogenizeBuffer *buffer) { int i; for ( i = 0; i < RTNUMTYPES; i++ ) { buffer->cnt[i] = 0; buffer->buf[i] = NULL; } } /* static void free_homogenizebuffer(HomogenizeBuffer *buffer) { int i; for ( i = 0; i < RTNUMTYPES; i++ ) { if ( buffer->buf[i] ) { rtcollection_free(ctx, buffer->buf[i]); } } } */ /* ** Given a generic collection, return the "simplest" form. ** ** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) ** ** In general, if the subcomponents are homogeneous, return a properly ** typed collection. ** Otherwise, return a generic collection, with the subtypes in minimal ** typed collections. */ static void rtcollection_build_buffer(const RTCTX *ctx, const RTCOLLECTION *col, HomogenizeBuffer *buffer) { int i; if ( ! col ) return; if ( rtgeom_is_empty(ctx, rtcollection_as_rtgeom(ctx, col)) ) return; for ( i = 0; i < col->ngeoms; i++ ) { RTGEOM *geom = col->geoms[i]; switch(geom->type) { case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTTRIANGLETYPE: case RTCURVEPOLYTYPE: case RTPOLYGONTYPE: { /* Init if necessary */ if ( ! buffer->buf[geom->type] ) { RTCOLLECTION *bufcol = rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, col->srid, RTFLAGS_GET_Z(col->flags), RTFLAGS_GET_M(col->flags)); bufcol->type = rttype_get_collectiontype(ctx, geom->type); buffer->buf[geom->type] = bufcol; } /* Add sub-geom to buffer */ rtcollection_add_rtgeom(ctx, buffer->buf[geom->type], rtgeom_clone(ctx, geom)); /* Increment count for this singleton type */ buffer->cnt[geom->type] = buffer->cnt[geom->type] + 1; } default: { rtcollection_build_buffer(ctx, rtgeom_as_rtcollection(ctx, geom), buffer); } } } return; } static RTGEOM* rtcollection_homogenize(const RTCTX *ctx, const RTCOLLECTION *col) { int i; int ntypes = 0; int type = 0; RTGEOM *outgeom = NULL; HomogenizeBuffer buffer; /* Sort all the parts into a buffer */ init_homogenizebuffer(ctx, &buffer); rtcollection_build_buffer(ctx, col, &buffer); /* Check for homogeneity */ for ( i = 0; i < RTNUMTYPES; i++ ) { if ( buffer.cnt[i] > 0 ) { ntypes++; type = i; } } /* No types? Huh. Return empty. */ if ( ntypes == 0 ) { RTCOLLECTION *outcol; outcol = rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, col->srid, RTFLAGS_GET_Z(col->flags), RTFLAGS_GET_M(col->flags)); outgeom = rtcollection_as_rtgeom(ctx, outcol); } /* One type, return homogeneous collection */ else if ( ntypes == 1 ) { RTCOLLECTION *outcol; outcol = buffer.buf[type]; if ( outcol->ngeoms == 1 ) { outgeom = outcol->geoms[0]; outcol->ngeoms=0; rtcollection_free(ctx, outcol); } else { outgeom = rtcollection_as_rtgeom(ctx, outcol); } outgeom->srid = col->srid; } /* Bah, more than out type, return anonymous collection */ else if ( ntypes > 1 ) { int j; RTCOLLECTION *outcol; outcol = rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, col->srid, RTFLAGS_GET_Z(col->flags), RTFLAGS_GET_M(col->flags)); for ( j = 0; j < RTNUMTYPES; j++ ) { if ( buffer.buf[j] ) { RTCOLLECTION *bcol = buffer.buf[j]; if ( bcol->ngeoms == 1 ) { rtcollection_add_rtgeom(ctx, outcol, bcol->geoms[0]); bcol->ngeoms=0; rtcollection_free(ctx, bcol); } else { rtcollection_add_rtgeom(ctx, outcol, rtcollection_as_rtgeom(ctx, bcol)); } } } outgeom = rtcollection_as_rtgeom(ctx, outcol); } return outgeom; } /* ** Given a generic geometry, return the "simplest" form. ** ** eg: ** LINESTRING() => LINESTRING() ** ** MULTILINESTRING(with a single line) => LINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) */ RTGEOM * rtgeom_homogenize(const RTCTX *ctx, const RTGEOM *geom) { RTGEOM *hgeom; /* EMPTY Geometry */ if (rtgeom_is_empty(ctx, geom)) { if( rtgeom_is_collection(ctx, geom) ) { return rtcollection_as_rtgeom(ctx, rtcollection_construct_empty(ctx, geom->type, geom->srid, rtgeom_has_z(ctx, geom), rtgeom_has_m(ctx, geom))); } return rtgeom_clone(ctx, geom); } switch (geom->type) { /* Return simple geometries untouched */ case RTPOINTTYPE: case RTLINETYPE: case RTCIRCSTRINGTYPE: case RTCOMPOUNDTYPE: case RTTRIANGLETYPE: case RTCURVEPOLYTYPE: case RTPOLYGONTYPE: return rtgeom_clone(ctx, geom); /* Process homogeneous geometries lightly */ case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: { RTCOLLECTION *col = (RTCOLLECTION*)geom; /* Strip single-entry multi-geometries down to singletons */ if ( col->ngeoms == 1 ) { hgeom = rtgeom_clone(ctx, (RTGEOM*)(col->geoms[0])); hgeom->srid = geom->srid; if (geom->bbox) hgeom->bbox = gbox_copy(ctx, geom->bbox); return hgeom; } /* Return proper multigeometry untouched */ return rtgeom_clone(ctx, geom); } /* Work on anonymous collections separately */ case RTCOLLECTIONTYPE: return rtcollection_homogenize(ctx, (RTCOLLECTION *) geom); } /* Unknown type */ rterror(ctx, "rtgeom_homogenize: Geometry Type not supported (%i)", rttype_name(ctx, geom->type)); return NULL; /* Never get here! */ } src/rtin_geojson.c000066400000000000000000000371471271715413500145330ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2013 Sandro Santilli * Copyright 2011 Kashif Rasul * **********************************************************************/ #include #include "librttopo_geom.h" #include "rtgeom_log.h" #include "rttopo_config.h" #if defined(HAVE_LIBJSON) || defined(HAVE_LIBJSON_C) /* --{ */ #ifdef HAVE_LIBJSON_C #include #include #else #include #include #endif #ifndef JSON_C_VERSION /* Adds support for libjson < 0.10 */ # define json_tokener_error_desc(x) json_tokener_errors[(x)] #endif #include static void geojson_rterror(char *msg, int error_code) { RTDEBUGF(3, "rtgeom_from_geojson ERROR %i", error_code); rterror(ctx, "%s", msg); } /* Prototype */ static RTGEOM* parse_geojson(json_object *geojson, int *hasz, int root_srid); static json_object* findMemberByName(json_object* poObj, const char* pszName ) { json_object* poTmp; json_object_iter it; poTmp = poObj; if( NULL == pszName || NULL == poObj) return NULL; it.key = NULL; it.val = NULL; it.entry = NULL; if( NULL != json_object_get_object(poTmp) ) { if( NULL == json_object_get_object(poTmp)->head ) { geojson_rterror("invalid GeoJSON representation", 2); return NULL; } for( it.entry = json_object_get_object(poTmp)->head; ( it.entry ? ( it.key = (char*)it.entry->k, it.val = (json_object*)it.entry->v, it.entry) : 0); it.entry = it.entry->next) { if( strcasecmp((char *)it.key, pszName )==0 ) return it.val; } } return NULL; } static int parse_geojson_coord(json_object *poObj, int *hasz, RTPOINTARRAY *pa) { RTPOINT4D pt; RTDEBUGF(3, "parse_geojson_coord called for object %s.", json_object_to_json_string( poObj ) ); if( json_type_array == json_object_get_type( poObj ) ) { json_object* poObjCoord = NULL; const int nSize = json_object_array_length( poObj ); RTDEBUGF(3, "parse_geojson_coord called for array size %d.", nSize ); if ( nSize < 2 ) { geojson_rterror("Too few ordinates in GeoJSON", 4); return RT_FAILURE; } /* Read X coordinate */ poObjCoord = json_object_array_get_idx( poObj, 0 ); pt.x = json_object_get_double( poObjCoord ); RTDEBUGF(3, "parse_geojson_coord pt.x = %f.", pt.x ); /* Read Y coordinate */ poObjCoord = json_object_array_get_idx( poObj, 1 ); pt.y = json_object_get_double( poObjCoord ); RTDEBUGF(3, "parse_geojson_coord pt.y = %f.", pt.y ); if( nSize > 2 ) /* should this be >= 3 ? */ { /* Read Z coordinate */ poObjCoord = json_object_array_get_idx( poObj, 2 ); pt.z = json_object_get_double( poObjCoord ); RTDEBUGF(3, "parse_geojson_coord pt.z = %f.", pt.z ); *hasz = RT_TRUE; } else if ( nSize == 2 ) { *hasz = RT_FALSE; /* Initialize Z coordinate, if required */ if ( RTFLAGS_GET_Z(pa->flags) ) pt.z = 0.0; } else { /* TODO: should we account for nSize > 3 ? */ /* more than 3 coordinates, we're just dropping dimensions here... */ } /* Initialize M coordinate, if required */ if ( RTFLAGS_GET_M(pa->flags) ) pt.m = 0.0; } else { /* If it's not an array, just don't handle it */ return RT_FAILURE; } return ptarray_append_point(ctx, pa, &pt, RT_TRUE); } static RTGEOM* parse_geojson_point(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom; RTPOINTARRAY *pa; json_object* coords = NULL; RTDEBUGF(3, "parse_geojson_point called with root_srid = %d.", root_srid ); coords = findMemberByName( geojson, "coordinates" ); if ( ! coords ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } pa = ptarray_construct_empty(ctx, 1, 0, 1); parse_geojson_coord(coords, hasz, pa); geom = (RTGEOM *) rtpoint_construct(ctx, root_srid, NULL, pa); RTDEBUG(2, "parse_geojson_point finished."); return geom; } static RTGEOM* parse_geojson_linestring(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom; RTPOINTARRAY *pa; json_object* points = NULL; int i = 0; RTDEBUG(2, "parse_geojson_linestring called."); points = findMemberByName( geojson, "coordinates" ); if ( ! points ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } pa = ptarray_construct_empty(ctx, 1, 0, 1); if( json_type_array == json_object_get_type( points ) ) { const int nPoints = json_object_array_length( points ); for(i = 0; i < nPoints; ++i) { json_object* coords = NULL; coords = json_object_array_get_idx( points, i ); parse_geojson_coord(coords, hasz, pa); } } geom = (RTGEOM *) rtline_construct(ctx, root_srid, NULL, pa); RTDEBUG(2, "parse_geojson_linestring finished."); return geom; } static RTGEOM* parse_geojson_polygon(json_object *geojson, int *hasz, int root_srid) { RTPOINTARRAY **ppa = NULL; json_object* rings = NULL; json_object* points = NULL; int i = 0, j = 0; int nRings = 0, nPoints = 0; rings = findMemberByName( geojson, "coordinates" ); if ( ! rings ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } if ( json_type_array != json_object_get_type(rings) ) { geojson_rterror("The 'coordinates' in GeoJSON are not an array", 4); return NULL; } nRings = json_object_array_length( rings ); /* No rings => POLYGON EMPTY */ if ( ! nRings ) { return (RTGEOM *)rtpoly_construct_empty(ctx, root_srid, 0, 0); } for ( i = 0; i < nRings; i++ ) { points = json_object_array_get_idx(rings, i); if ( ! points || json_object_get_type(points) != json_type_array ) { geojson_rterror("The 'coordinates' in GeoJSON ring are not an array", 4); return NULL; } nPoints = json_object_array_length(points); /* Skip empty rings */ if ( nPoints == 0 ) continue; if ( ! ppa ) ppa = (RTPOINTARRAY**)rtalloc(ctx, sizeof(RTPOINTARRAY*) * nRings); ppa[i] = ptarray_construct_empty(ctx, 1, 0, 1); for ( j = 0; j < nPoints; j++ ) { json_object* coords = NULL; coords = json_object_array_get_idx( points, j ); parse_geojson_coord(coords, hasz, ppa[i]); } } /* All the rings were empty! */ if ( ! ppa ) return (RTGEOM *)rtpoly_construct_empty(ctx, root_srid, 0, 0); return (RTGEOM *) rtpoly_construct(ctx, root_srid, NULL, nRings, ppa); } static RTGEOM* parse_geojson_multipoint(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom; int i = 0; json_object* poObjPoints = NULL; if (!root_srid) { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, root_srid, 1, 0); } else { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, -1, 1, 0); } poObjPoints = findMemberByName( geojson, "coordinates" ); if ( ! poObjPoints ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } if( json_type_array == json_object_get_type( poObjPoints ) ) { const int nPoints = json_object_array_length( poObjPoints ); for( i = 0; i < nPoints; ++i) { RTPOINTARRAY *pa; json_object* poObjCoords = NULL; poObjCoords = json_object_array_get_idx( poObjPoints, i ); pa = ptarray_construct_empty(ctx, 1, 0, 1); parse_geojson_coord(poObjCoords, hasz, pa); geom = (RTGEOM*)rtmpoint_add_rtpoint(ctx, (RTMPOINT*)geom, (RTPOINT*)rtpoint_construct(ctx, root_srid, NULL, pa)); } } return geom; } static RTGEOM* parse_geojson_multilinestring(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom = NULL; int i, j; json_object* poObjLines = NULL; if (!root_srid) { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTILINETYPE, root_srid, 1, 0); } else { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTILINETYPE, -1, 1, 0); } poObjLines = findMemberByName( geojson, "coordinates" ); if ( ! poObjLines ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } if( json_type_array == json_object_get_type( poObjLines ) ) { const int nLines = json_object_array_length( poObjLines ); for( i = 0; i < nLines; ++i) { RTPOINTARRAY *pa = NULL; json_object* poObjLine = NULL; poObjLine = json_object_array_get_idx( poObjLines, i ); pa = ptarray_construct_empty(ctx, 1, 0, 1); if( json_type_array == json_object_get_type( poObjLine ) ) { const int nPoints = json_object_array_length( poObjLine ); for(j = 0; j < nPoints; ++j) { json_object* coords = NULL; coords = json_object_array_get_idx( poObjLine, j ); parse_geojson_coord(coords, hasz, pa); } geom = (RTGEOM*)rtmline_add_rtline(ctx, (RTMLINE*)geom, (RTLINE*)rtline_construct(ctx, root_srid, NULL, pa)); } } } return geom; } static RTGEOM* parse_geojson_multipolygon(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom = NULL; int i, j, k; json_object* poObjPolys = NULL; if (!root_srid) { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTIPOLYGONTYPE, root_srid, 1, 0); } else { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTMULTIPOLYGONTYPE, -1, 1, 0); } poObjPolys = findMemberByName( geojson, "coordinates" ); if ( ! poObjPolys ) { geojson_rterror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } if( json_type_array == json_object_get_type( poObjPolys ) ) { const int nPolys = json_object_array_length( poObjPolys ); for(i = 0; i < nPolys; ++i) { json_object* poObjPoly = json_object_array_get_idx( poObjPolys, i ); if( json_type_array == json_object_get_type( poObjPoly ) ) { RTPOLY *rtpoly = rtpoly_construct_empty(ctx, geom->srid, rtgeom_has_z(ctx, geom), rtgeom_has_m(ctx, geom)); int nRings = json_object_array_length( poObjPoly ); for(j = 0; j < nRings; ++j) { json_object* points = json_object_array_get_idx( poObjPoly, j ); if( json_type_array == json_object_get_type( points ) ) { RTPOINTARRAY *pa = ptarray_construct_empty(ctx, 1, 0, 1); int nPoints = json_object_array_length( points ); for ( k=0; k < nPoints; k++ ) { json_object* coords = json_object_array_get_idx( points, k ); parse_geojson_coord(coords, hasz, pa); } rtpoly_add_ring(ctx, rtpoly, pa); } } geom = (RTGEOM*)rtmpoly_add_rtpoly(ctx, (RTMPOLY*)geom, rtpoly); } } } return geom; } static RTGEOM* parse_geojson_geometrycollection(json_object *geojson, int *hasz, int root_srid) { RTGEOM *geom = NULL; int i; json_object* poObjGeoms = NULL; if (!root_srid) { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, root_srid, 1, 0); } else { geom = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, -1, 1, 0); } poObjGeoms = findMemberByName( geojson, "geometries" ); if ( ! poObjGeoms ) { geojson_rterror("Unable to find 'geometries' in GeoJSON string", 4); return NULL; } if( json_type_array == json_object_get_type( poObjGeoms ) ) { const int nGeoms = json_object_array_length( poObjGeoms ); json_object* poObjGeom = NULL; for(i = 0; i < nGeoms; ++i ) { poObjGeom = json_object_array_get_idx( poObjGeoms, i ); geom = (RTGEOM*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION *)geom, parse_geojson(poObjGeom, hasz, root_srid)); } } return geom; } static RTGEOM* parse_geojson(json_object *geojson, int *hasz, int root_srid) { json_object* type = NULL; const char* name; if( NULL == geojson ) { geojson_rterror("invalid GeoJSON representation", 2); return NULL; } type = findMemberByName( geojson, "type" ); if( NULL == type ) { geojson_rterror("unknown GeoJSON type", 3); return NULL; } name = json_object_get_string( type ); if( strcasecmp( name, "Point" )==0 ) return parse_geojson_point(geojson, hasz, root_srid); if( strcasecmp( name, "LineString" )==0 ) return parse_geojson_linestring(geojson, hasz, root_srid); if( strcasecmp( name, "Polygon" )==0 ) return parse_geojson_polygon(geojson, hasz, root_srid); if( strcasecmp( name, "MultiPoint" )==0 ) return parse_geojson_multipoint(geojson, hasz, root_srid); if( strcasecmp( name, "MultiLineString" )==0 ) return parse_geojson_multilinestring(geojson, hasz, root_srid); if( strcasecmp( name, "MultiPolygon" )==0 ) return parse_geojson_multipolygon(geojson, hasz, root_srid); if( strcasecmp( name, "GeometryCollection" )==0 ) return parse_geojson_geometrycollection(geojson, hasz, root_srid); rterror(ctx, "invalid GeoJson representation"); return NULL; /* Never reach */ } #endif /* HAVE_LIBJSON or HAVE_LIBJSON_C --} */ RTGEOM* rtgeom_from_geojson(const RTCTX *ctx, const char *geojson, char **srs) { #ifndef HAVE_LIBJSON *srs = NULL; rterror(ctx, "You need JSON-C for rtgeom_from_geojson"); return NULL; #else /* HAVE_LIBJSON */ /* size_t geojson_size = strlen(geojson); */ RTGEOM *rtgeom; int hasz=RT_TRUE; json_tokener* jstok = NULL; json_object* poObj = NULL; json_object* poObjSrs = NULL; *srs = NULL; /* Begin to Parse json */ jstok = json_tokener_new(); poObj = json_tokener_parse_ex(jstok, geojson, -1); if( jstok->err != json_tokener_success) { char err[256]; snprintf(err, 256, "%s (at offset %d)", json_tokener_error_desc(jstok->err), jstok->char_offset); json_tokener_free(jstok); json_object_put(poObj); geojson_rterror(err, 1); return NULL; } json_tokener_free(jstok); poObjSrs = findMemberByName( poObj, "crs" ); if (poObjSrs != NULL) { json_object* poObjSrsType = findMemberByName( poObjSrs, "type" ); if (poObjSrsType != NULL) { json_object* poObjSrsProps = findMemberByName( poObjSrs, "properties" ); if ( poObjSrsProps ) { json_object* poNameURL = findMemberByName( poObjSrsProps, "name" ); if ( poNameURL ) { const char* pszName = json_object_get_string( poNameURL ); if ( pszName ) { *srs = rtalloc(ctx, strlen(pszName) + 1); strcpy(*srs, pszName); } } } } } rtgeom = parse_geojson(poObj, &hasz, 0); json_object_put(poObj); rtgeom_add_bbox(ctx, rtgeom); if (!hasz) { RTGEOM *tmp = rtgeom_force_2d(ctx, rtgeom); rtgeom_free(ctx, rtgeom); rtgeom = tmp; RTDEBUG(2, "geom_from_geojson called."); } return rtgeom; #endif /* HAVE_LIBJSON } */ } src/rtin_twkb.c000066400000000000000000000425361271715413500140340ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2014 Nicklas Avén * **********************************************************************/ #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include "varint.h" #define TWKB_IN_MAXCOORDS 4 /** * Used for passing the parse state between the parsing functions. */ typedef struct { /* Pointers to the bytes */ uint8_t *twkb; /* Points to start of TWKB */ uint8_t *twkb_end; /* Points to end of TWKB */ uint8_t *pos; /* Current read position */ uint32_t check; /* Simple validity checks on geometries */ uint32_t rttype; /* Current type we are handling */ uint8_t has_bbox; uint8_t has_size; uint8_t has_idlist; uint8_t has_z; uint8_t has_m; uint8_t is_empty; /* Precision factors to convert ints to double */ double factor; double factor_z; double factor_m; uint64_t size; /* Info about current geometry */ uint8_t magic_byte; /* the magic byte contain info about if twkb contain id, size info, bboxes and precision */ int ndims; /* Number of dimensions */ int64_t *coords; /* An array to keep delta values from 4 dimensions */ } twkb_parse_state; /** * Internal function declarations. */ RTGEOM* rtgeom_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s); /**********************************************************************/ /** * Check that we are not about to read off the end of the RTWKB * array. */ static inline void twkb_parse_state_advance(const RTCTX *ctx, twkb_parse_state *s, size_t next) { if( (s->pos + next) > s->twkb_end) { rterror(ctx, "%s: TWKB structure does not match expected size!", __func__); // rtnotice(ctx, "TWKB structure does not match expected size!"); } s->pos += next; } static inline int64_t twkb_parse_state_varint(const RTCTX *ctx, twkb_parse_state *s) { size_t size; int64_t val = varint_s64_decode(ctx, s->pos, s->twkb_end, &size); twkb_parse_state_advance(ctx, s, size); return val; } static inline uint64_t twkb_parse_state_uvarint(const RTCTX *ctx, twkb_parse_state *s) { size_t size; uint64_t val = varint_u64_decode(ctx, s->pos, s->twkb_end, &size); twkb_parse_state_advance(ctx, s, size); return val; } static inline double twkb_parse_state_double(const RTCTX *ctx, twkb_parse_state *s, double factor) { size_t size; int64_t val = varint_s64_decode(ctx, s->pos, s->twkb_end, &size); twkb_parse_state_advance(ctx, s, size); return val / factor; } static inline void twkb_parse_state_varint_skip(const RTCTX *ctx, twkb_parse_state *s) { size_t size = varint_size(ctx, s->pos, s->twkb_end); if ( ! size ) rterror(ctx, "%s: no varint to skip", __func__); twkb_parse_state_advance(ctx, s, size); return; } static uint32_t rttype_from_twkb_type(const RTCTX *ctx, uint8_t twkb_type) { switch (twkb_type) { case 1: return RTPOINTTYPE; case 2: return RTLINETYPE; case 3: return RTPOLYGONTYPE; case 4: return RTMULTIPOINTTYPE; case 5: return RTMULTILINETYPE; case 6: return RTMULTIPOLYGONTYPE; case 7: return RTCOLLECTIONTYPE; default: /* Error! */ rterror(ctx, "Unknown RTWKB type"); return 0; } return 0; } /** * Byte * Read a byte and advance the parse state forward. */ static uint8_t byte_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { uint8_t val = *(s->pos); twkb_parse_state_advance(ctx, s, RTWKB_BYTE_SIZE); return val; } /** * RTPOINTARRAY * Read a dynamically sized point array and advance the parse state forward. */ static RTPOINTARRAY* ptarray_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s, uint32_t npoints) { RTPOINTARRAY *pa = NULL; uint32_t ndims = s->ndims; int i; double *dlist; RTDEBUG(2,"Entering ptarray_from_twkb_state"); RTDEBUGF(4,"Pointarray has %d points", npoints); /* Empty! */ if( npoints == 0 ) return ptarray_construct_empty(ctx, s->has_z, s->has_m, 0); pa = ptarray_construct(ctx, s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints; i++ ) { int j = 0; /* X */ s->coords[j] += twkb_parse_state_varint(ctx, s); dlist[ndims*i + j] = s->coords[j] / s->factor; j++; /* Y */ s->coords[j] += twkb_parse_state_varint(ctx, s); dlist[ndims*i + j] = s->coords[j] / s->factor; j++; /* Z */ if ( s->has_z ) { s->coords[j] += twkb_parse_state_varint(ctx, s); dlist[ndims*i + j] = s->coords[j] / s->factor_z; j++; } /* M */ if ( s->has_m ) { s->coords[j] += twkb_parse_state_varint(ctx, s); dlist[ndims*i + j] = s->coords[j] / s->factor_m; j++; } } return pa; } /** * POINT */ static RTPOINT* rtpoint_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { static uint32_t npoints = 1; RTPOINTARRAY *pa; RTDEBUG(2,"Entering rtpoint_from_twkb_state"); if ( s->is_empty ) return rtpoint_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); pa = ptarray_from_twkb_state(ctx, s, npoints); return rtpoint_construct(ctx, SRID_UNKNOWN, NULL, pa); } /** * LINESTRING */ static RTLINE* rtline_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { uint32_t npoints; RTPOINTARRAY *pa; RTDEBUG(2,"Entering rtline_from_twkb_state"); if ( s->is_empty ) return rtline_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); /* Read number of points */ npoints = twkb_parse_state_uvarint(ctx, s); if ( npoints == 0 ) return rtline_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); /* Read coordinates */ pa = ptarray_from_twkb_state(ctx, s, npoints); if( pa == NULL ) return rtline_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { rterror(ctx, "%s must have at least two points", rttype_name(ctx, s->rttype)); return NULL; } return rtline_construct(ctx, SRID_UNKNOWN, NULL, pa); } /** * POLYGON */ static RTPOLY* rtpoly_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { uint32_t nrings; int i; RTPOLY *poly; RTDEBUG(2,"Entering rtpoly_from_twkb_state"); if ( s->is_empty ) return rtpoly_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); /* Read number of rings */ nrings = twkb_parse_state_uvarint(ctx, s); /* Start w/ empty polygon */ poly = rtpoly_construct_empty(ctx, SRID_UNKNOWN, s->has_z, s->has_m); RTDEBUGF(4,"Polygon has %d rings", nrings); /* Empty polygon? */ if( nrings == 0 ) return poly; for( i = 0; i < nrings; i++ ) { /* Ret number of points */ uint32_t npoints = twkb_parse_state_uvarint(ctx, s); RTPOINTARRAY *pa = ptarray_from_twkb_state(ctx, s, npoints); /* Skip empty rings */ if( pa == NULL ) continue; /* Force first and last points to be the same. */ if( ! ptarray_is_closed_2d(ctx, pa) ) { RTPOINT4D pt; rt_getPoint4d_p(ctx, pa, 0, &pt); ptarray_append_point(ctx, pa, &pt, RT_FALSE); } /* Check for at least four points. */ if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { RTDEBUGF(2, "%s must have at least four points in each ring", rttype_name(ctx, s->rttype)); rterror(ctx, "%s must have at least four points in each ring", rttype_name(ctx, s->rttype)); return NULL; } /* Add ring to polygon */ if ( rtpoly_add_ring(ctx, poly, pa) == RT_FAILURE ) { RTDEBUG(2, "Unable to add ring to polygon"); rterror(ctx, "Unable to add ring to polygon"); } } return poly; } /** * MULTIPOINT */ static RTCOLLECTION* rtmultipoint_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { int ngeoms, i; RTGEOM *geom = NULL; RTCOLLECTION *col = rtcollection_construct_empty(ctx, s->rttype, SRID_UNKNOWN, s->has_z, s->has_m); RTDEBUG(2,"Entering rtmultipoint_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(ctx, s); RTDEBUGF(4,"Number of geometries %d", ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(ctx, s); } for ( i = 0; i < ngeoms; i++ ) { geom = rtpoint_as_rtgeom(ctx, rtpoint_from_twkb_state(ctx, s)); if ( rtcollection_add_rtgeom(ctx, col, geom) == NULL ) { rterror(ctx, "Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * MULTILINESTRING */ static RTCOLLECTION* rtmultiline_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { int ngeoms, i; RTGEOM *geom = NULL; RTCOLLECTION *col = rtcollection_construct_empty(ctx, s->rttype, SRID_UNKNOWN, s->has_z, s->has_m); RTDEBUG(2,"Entering rtmultilinestring_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(ctx, s); RTDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(ctx, s); } for ( i = 0; i < ngeoms; i++ ) { geom = rtline_as_rtgeom(ctx, rtline_from_twkb_state(ctx, s)); if ( rtcollection_add_rtgeom(ctx, col, geom) == NULL ) { rterror(ctx, "Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * MULTIPOLYGON */ static RTCOLLECTION* rtmultipoly_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { int ngeoms, i; RTGEOM *geom = NULL; RTCOLLECTION *col = rtcollection_construct_empty(ctx, s->rttype, SRID_UNKNOWN, s->has_z, s->has_m); RTDEBUG(2,"Entering rtmultipolygon_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(ctx, s); RTDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(ctx, s); } for ( i = 0; i < ngeoms; i++ ) { geom = rtpoly_as_rtgeom(ctx, rtpoly_from_twkb_state(ctx, s)); if ( rtcollection_add_rtgeom(ctx, col, geom) == NULL ) { rterror(ctx, "Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * COLLECTION, RTMULTIPOINTTYPE, RTMULTILINETYPE, RTMULTIPOLYGONTYPE **/ static RTCOLLECTION* rtcollection_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { int ngeoms, i; RTGEOM *geom = NULL; RTCOLLECTION *col = rtcollection_construct_empty(ctx, s->rttype, SRID_UNKNOWN, s->has_z, s->has_m); RTDEBUG(2,"Entering rtcollection_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(ctx, s); RTDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(ctx, s); } for ( i = 0; i < ngeoms; i++ ) { geom = rtgeom_from_twkb_state(ctx, s); if ( rtcollection_add_rtgeom(ctx, col, geom) == NULL ) { rterror(ctx, "Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } static void header_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { RTDEBUG(2,"Entering magicbyte_from_twkb_state"); uint8_t extended_dims; /* Read the first two bytes */ uint8_t type_precision = byte_from_twkb_state(ctx, s); uint8_t metadata = byte_from_twkb_state(ctx, s); /* Strip type and precision out of first byte */ uint8_t type = type_precision & 0x0F; int8_t precision = unzigzag8(ctx, (type_precision & 0xF0) >> 4); /* Convert TWKB type to internal type */ s->rttype = rttype_from_twkb_type(ctx, type); /* Convert the precision into factor */ s->factor = pow(10, (double)precision); /* Strip metadata flags out of second byte */ s->has_bbox = metadata & 0x01; s->has_size = (metadata & 0x02) >> 1; s->has_idlist = (metadata & 0x04) >> 2; extended_dims = (metadata & 0x08) >> 3; s->is_empty = (metadata & 0x10) >> 4; /* Flag for higher dims means read a third byte */ if ( extended_dims ) { int8_t precision_z, precision_m; extended_dims = byte_from_twkb_state(ctx, s); /* Strip Z/M presence and precision from ext byte */ s->has_z = (extended_dims & 0x01); s->has_m = (extended_dims & 0x02) >> 1; precision_z = (extended_dims & 0x1C) >> 2; precision_m = (extended_dims & 0xE0) >> 5; /* Convert the precision into factor */ s->factor_z = pow(10, (double)precision_z); s->factor_m = pow(10, (double)precision_m); } else { s->has_z = 0; s->has_m = 0; s->factor_z = 0; s->factor_m = 0; } /* Read the size, if there is one */ if ( s->has_size ) { s->size = twkb_parse_state_uvarint(ctx, s); } /* Calculate the number of dimensions */ s->ndims = 2 + s->has_z + s->has_m; return; } /** * Generic handling for TWKB geometries. The front of every TWKB geometry * (including those embedded in collections) is a type byte and metadata byte, * then optional size, bbox, etc. Read those, then switch to particular type * handling code. */ RTGEOM* rtgeom_from_twkb_state(const RTCTX *ctx, twkb_parse_state *s) { RTGBOX bbox; RTGEOM *geom = NULL; uint32_t has_bbox = RT_FALSE; int i; /* Read the first two bytes, and optional */ /* extended precision info and optional size info */ header_from_twkb_state(ctx, s); /* Just experienced a geometry header, so now we */ /* need to reset our coordinate deltas */ for ( i = 0; i < TWKB_IN_MAXCOORDS; i++ ) { s->coords[i] = 0.0; } /* Read the bounding box, is there is one */ if ( s->has_bbox ) { /* Initialize */ has_bbox = s->has_bbox; memset(&bbox, 0, sizeof(RTGBOX)); bbox.flags = gflags(ctx, s->has_z, s->has_m, 0); /* X */ bbox.xmin = twkb_parse_state_double(ctx, s, s->factor); bbox.xmax = bbox.xmin + twkb_parse_state_double(ctx, s, s->factor); /* Y */ bbox.ymin = twkb_parse_state_double(ctx, s, s->factor); bbox.ymax = bbox.ymin + twkb_parse_state_double(ctx, s, s->factor); /* Z */ if ( s->has_z ) { bbox.zmin = twkb_parse_state_double(ctx, s, s->factor_z); bbox.zmax = bbox.zmin + twkb_parse_state_double(ctx, s, s->factor_z); } /* M */ if ( s->has_z ) { bbox.mmin = twkb_parse_state_double(ctx, s, s->factor_m); bbox.mmax = bbox.mmin + twkb_parse_state_double(ctx, s, s->factor_m); } } /* Switch to code for the particular type we're dealing with */ switch( s->rttype ) { case RTPOINTTYPE: geom = rtpoint_as_rtgeom(ctx, rtpoint_from_twkb_state(ctx, s)); break; case RTLINETYPE: geom = rtline_as_rtgeom(ctx, rtline_from_twkb_state(ctx, s)); break; case RTPOLYGONTYPE: geom = rtpoly_as_rtgeom(ctx, rtpoly_from_twkb_state(ctx, s)); break; case RTMULTIPOINTTYPE: geom = rtcollection_as_rtgeom(ctx, rtmultipoint_from_twkb_state(ctx, s)); break; case RTMULTILINETYPE: geom = rtcollection_as_rtgeom(ctx, rtmultiline_from_twkb_state(ctx, s)); break; case RTMULTIPOLYGONTYPE: geom = rtcollection_as_rtgeom(ctx, rtmultipoly_from_twkb_state(ctx, s)); break; case RTCOLLECTIONTYPE: geom = rtcollection_as_rtgeom(ctx, rtcollection_from_twkb_state(ctx, s)); break; /* Unknown type! */ default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, s->rttype), s->rttype); break; } if ( has_bbox ) { geom->bbox = gbox_clone(ctx, &bbox); } return geom; } /** * RTWKB inputs *must* have a declared size, to prevent malformed RTWKB from reading * off the end of the memory segment (this stops a malevolent user from declaring * a one-ring polygon to have 10 rings, causing the RTWKB reader to walk off the * end of the memory). * * Check is a bitmask of: RT_PARSER_CHECK_MINPOINTS, RT_PARSER_CHECK_ODD, * RT_PARSER_CHECK_CLOSURE, RT_PARSER_CHECK_NONE, RT_PARSER_CHECK_ALL */ RTGEOM* rtgeom_from_twkb(const RTCTX *ctx, uint8_t *twkb, size_t twkb_size, char check) { int64_t coords[TWKB_IN_MAXCOORDS] = {0, 0, 0, 0}; twkb_parse_state s; RTDEBUG(2,"Entering rtgeom_from_twkb"); RTDEBUGF(4,"twkb_size: %d",(int) twkb_size); /* Zero out the state */ memset(&s, 0, sizeof(twkb_parse_state)); /* Initialize the state appropriately */ s.twkb = s.pos = twkb; s.twkb_end = twkb + twkb_size; s.check = check; s.coords = coords; /* Handle the check catch-all values */ if ( check & RT_PARSER_CHECK_NONE ) s.check = 0; else s.check = check; /* Read the rest of the geometry */ return rtgeom_from_twkb_state(ctx, &s); } src/rtin_wkb.c000066400000000000000000000541121271715413500136410ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include "rttopo_config.h" /*#define RTGEOM_DEBUG_LEVEL 4*/ #include "librttopo_geom_internal.h" /* NOTE: includes rtgeom_log.h */ #include "rtgeom_log.h" #include /** * Used for passing the parse state between the parsing functions. */ typedef struct { const uint8_t *wkb; /* Points to start of RTWKB */ size_t wkb_size; /* Expected size of RTWKB */ int swap_bytes; /* Do an endian flip? */ int check; /* Simple validity checks on geometries */ uint32_t rttype; /* Current type we are handling */ uint32_t srid; /* Current SRID we are handling */ int has_z; /* Z? */ int has_m; /* M? */ int has_srid; /* SRID? */ const uint8_t *pos; /* Current parse position */ } wkb_parse_state; /** * Internal function declarations. */ RTGEOM* rtgeom_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s); /**********************************************************************/ /* Our static character->number map. Anything > 15 is invalid */ static uint8_t hex2char[256] = { /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* 0-9 */ 0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20, /* A-F */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, /* not Hex characters */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* a-f */ 20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, /* not Hex characters (upper 128 characters) */ 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20 }; uint8_t* bytes_from_hexbytes(const RTCTX *ctx, const char *hexbuf, size_t hexsize) { uint8_t *buf = NULL; register uint8_t h1, h2; int i; if( hexsize % 2 ) rterror(ctx, "Invalid hex string, length (%d) has to be a multiple of two!", hexsize); buf = rtalloc(ctx, hexsize/2); if( ! buf ) rterror(ctx, "Unable to allocate memory buffer."); for( i = 0; i < hexsize/2; i++ ) { h1 = hex2char[(int)hexbuf[2*i]]; h2 = hex2char[(int)hexbuf[2*i+1]]; if( h1 > 15 ) rterror(ctx, "Invalid hex character (%c) encountered", hexbuf[2*i]); if( h2 > 15 ) rterror(ctx, "Invalid hex character (%c) encountered", hexbuf[2*i+1]); /* First character is high bits, second is low bits */ buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F); } return buf; } /**********************************************************************/ /** * Check that we are not about to read off the end of the RTWKB * array. */ static inline void wkb_parse_state_check(const RTCTX *ctx, wkb_parse_state *s, size_t next) { if( (s->pos + next) > (s->wkb + s->wkb_size) ) rterror(ctx, "RTWKB structure does not match expected size!"); } /** * Take in an unknown kind of wkb type number and ensure it comes out * as an extended RTWKB type number (with Z/M/SRID flags masked onto the * high bits). */ static void rttype_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s, uint32_t wkb_type) { uint32_t wkb_simple_type; RTDEBUG(4, "Entered function"); s->has_z = RT_FALSE; s->has_m = RT_FALSE; s->has_srid = RT_FALSE; /* If any of the higher bits are set, this is probably an extended type. */ if( wkb_type & 0xF0000000 ) { if( wkb_type & RTWKBZOFFSET ) s->has_z = RT_TRUE; if( wkb_type & RTWKBMOFFSET ) s->has_m = RT_TRUE; if( wkb_type & RTWKBSRIDFLAG ) s->has_srid = RT_TRUE; RTDEBUGF(4, "Extended type: has_z=%d has_m=%d has_srid=%d", s->has_z, s->has_m, s->has_srid); } /* Mask off the flags */ wkb_type = wkb_type & 0x0FFFFFFF; /* Strip out just the type number (1-12) from the ISO number (eg 3001-3012) */ wkb_simple_type = wkb_type % 1000; /* Extract the Z/M information from ISO style numbers */ if( wkb_type >= 3000 && wkb_type < 4000 ) { s->has_z = RT_TRUE; s->has_m = RT_TRUE; } else if ( wkb_type >= 2000 && wkb_type < 3000 ) { s->has_m = RT_TRUE; } else if ( wkb_type >= 1000 && wkb_type < 2000 ) { s->has_z = RT_TRUE; } switch (wkb_simple_type) { case RTWKB_POINT_TYPE: s->rttype = RTPOINTTYPE; break; case RTWKB_LINESTRING_TYPE: s->rttype = RTLINETYPE; break; case RTWKB_POLYGON_TYPE: s->rttype = RTPOLYGONTYPE; break; case RTWKB_MULTIPOINT_TYPE: s->rttype = RTMULTIPOINTTYPE; break; case RTWKB_MULTILINESTRING_TYPE: s->rttype = RTMULTILINETYPE; break; case RTWKB_MULTIPOLYGON_TYPE: s->rttype = RTMULTIPOLYGONTYPE; break; case RTWKB_GEOMETRYCOLLECTION_TYPE: s->rttype = RTCOLLECTIONTYPE; break; case RTWKB_CIRCULARSTRING_TYPE: s->rttype = RTCIRCSTRINGTYPE; break; case RTWKB_COMPOUNDCURVE_TYPE: s->rttype = RTCOMPOUNDTYPE; break; case RTWKB_CURVEPOLYGON_TYPE: s->rttype = RTCURVEPOLYTYPE; break; case RTWKB_MULTICURVE_TYPE: s->rttype = RTMULTICURVETYPE; break; case RTWKB_MULTISURFACE_TYPE: s->rttype = RTMULTISURFACETYPE; break; case RTWKB_POLYHEDRALSURFACE_TYPE: s->rttype = RTPOLYHEDRALSURFACETYPE; break; case RTWKB_TIN_TYPE: s->rttype = RTTINTYPE; break; case RTWKB_TRIANGLE_TYPE: s->rttype = RTTRIANGLETYPE; break; /* PostGIS 1.5 emits 13, 14 for CurvePolygon, MultiCurve */ /* These numbers aren't SQL/MM (numbers currently only */ /* go up to 12. We can handle the old data here (for now??) */ /* converting them into the rttypes that are intended. */ case RTWKB_CURVE_TYPE: s->rttype = RTCURVEPOLYTYPE; break; case RTWKB_SURFACE_TYPE: s->rttype = RTMULTICURVETYPE; break; default: /* Error! */ rterror(ctx, "Unknown RTWKB type (%d)! Full RTWKB type number was (%d).", wkb_simple_type, wkb_type); break; } RTDEBUGF(4,"Got rttype %s (%u)", rttype_name(ctx, s->rttype), s->rttype); return; } /** * Byte * Read a byte and advance the parse state forward. */ static char byte_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { char char_value = 0; RTDEBUG(4, "Entered function"); wkb_parse_state_check(ctx, s, RTWKB_BYTE_SIZE); RTDEBUG(4, "Passed state check"); char_value = s->pos[0]; RTDEBUGF(4, "Read byte value: %x", char_value); s->pos += RTWKB_BYTE_SIZE; return char_value; } /** * Int32 * Read 4-byte integer and advance the parse state forward. */ static uint32_t integer_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { uint32_t i = 0; wkb_parse_state_check(ctx, s, RTWKB_INT_SIZE); memcpy(&i, s->pos, RTWKB_INT_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int j = 0; uint8_t tmp; for( j = 0; j < RTWKB_INT_SIZE/2; j++ ) { tmp = ((uint8_t*)(&i))[j]; ((uint8_t*)(&i))[j] = ((uint8_t*)(&i))[RTWKB_INT_SIZE - j - 1]; ((uint8_t*)(&i))[RTWKB_INT_SIZE - j - 1] = tmp; } } s->pos += RTWKB_INT_SIZE; return i; } /** * Double * Read an 8-byte double and advance the parse state forward. */ static double double_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { double d = 0; wkb_parse_state_check(ctx, s, RTWKB_DOUBLE_SIZE); memcpy(&d, s->pos, RTWKB_DOUBLE_SIZE); /* Swap? Copy into a stack-allocated integer. */ if( s->swap_bytes ) { int i = 0; uint8_t tmp; for( i = 0; i < RTWKB_DOUBLE_SIZE/2; i++ ) { tmp = ((uint8_t*)(&d))[i]; ((uint8_t*)(&d))[i] = ((uint8_t*)(&d))[RTWKB_DOUBLE_SIZE - i - 1]; ((uint8_t*)(&d))[RTWKB_DOUBLE_SIZE - i - 1] = tmp; } } s->pos += RTWKB_DOUBLE_SIZE; return d; } /** * RTPOINTARRAY * Read a dynamically sized point array and advance the parse state forward. * First read the number of points, then read the points. */ static RTPOINTARRAY* ptarray_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { RTPOINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; uint32_t npoints = 0; /* Calculate the size of this point array. */ npoints = integer_from_wkb_state(ctx, s); RTDEBUGF(4,"Pointarray has %d points", npoints); if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = npoints * ndims * RTWKB_DOUBLE_SIZE; /* Empty! */ if( npoints == 0 ) return ptarray_construct(ctx, s->has_z, s->has_m, npoints); /* Does the data we want to read exist? */ wkb_parse_state_check(ctx, s, pa_size); /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(ctx, s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately. */ else { int i = 0; double *dlist; pa = ptarray_construct(ctx, s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < npoints * ndims; i++ ) { dlist[i] = double_from_wkb_state(ctx, s); } } return pa; } /** * POINT * Read a RTWKB point, starting just after the endian byte, * type number and optional srid number. * Advance the parse state forward appropriately. * RTWKB point has just a set of doubles, with the quantity depending on the * dimension of the point, so this looks like a special case of the above * with only one point. */ static RTPOINT* rtpoint_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { static uint32_t npoints = 1; RTPOINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; const RTPOINT2D *pt; /* Count the dimensions. */ if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = ndims * RTWKB_DOUBLE_SIZE; /* Does the data we want to read exist? */ wkb_parse_state_check(ctx, s, pa_size); /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(ctx, s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately */ else { int i = 0; double *dlist; pa = ptarray_construct(ctx, s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < ndims; i++ ) { dlist[i] = double_from_wkb_state(ctx, s); } } /* Check for POINT(NaN NaN) ==> POINT EMPTY */ pt = rt_getPoint2d_cp(ctx, pa, 0); if ( isnan(pt->x) && isnan(pt->y) ) { ptarray_free(ctx, pa); return rtpoint_construct_empty(ctx, s->srid, s->has_z, s->has_m); } else { return rtpoint_construct(ctx, s->srid, NULL, pa); } } /** * LINESTRING * Read a RTWKB linestring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (two point minimum). */ static RTLINE* rtline_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { RTPOINTARRAY *pa = ptarray_from_wkb_state(ctx, s); if( pa == NULL || pa->npoints == 0 ) return rtline_construct_empty(ctx, s->srid, s->has_z, s->has_m); if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 2 ) { rterror(ctx, "%s must have at least two points", rttype_name(ctx, s->rttype)); return NULL; } return rtline_construct(ctx, s->srid, NULL, pa); } /** * CIRCULARSTRING * Read a RTWKB circularstring, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * There is only one pointarray in a linestring. Optionally * check for minimal following of rules (three point minimum, * odd number of points). */ static RTCIRCSTRING* rtcircstring_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { RTPOINTARRAY *pa = ptarray_from_wkb_state(ctx, s); if( pa == NULL || pa->npoints == 0 ) return rtcircstring_construct_empty(ctx, s->srid, s->has_z, s->has_m); if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 3 ) { rterror(ctx, "%s must have at least three points", rttype_name(ctx, s->rttype)); return NULL; } if( s->check & RT_PARSER_CHECK_ODD && ! (pa->npoints % 2) ) { rterror(ctx, "%s must have an odd number of points", rttype_name(ctx, s->rttype)); return NULL; } return rtcircstring_construct(ctx, s->srid, NULL, pa); } /** * POLYGON * Read a RTWKB polygon, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * First read the number of rings, then read each ring * (which are structured as point arrays) */ static RTPOLY* rtpoly_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(ctx, s); int i = 0; RTPOLY *poly = rtpoly_construct_empty(ctx, s->srid, s->has_z, s->has_m); RTDEBUGF(4,"Polygon has %d rings", nrings); /* Empty polygon? */ if( nrings == 0 ) return poly; for( i = 0; i < nrings; i++ ) { RTPOINTARRAY *pa = ptarray_from_wkb_state(ctx, s); if( pa == NULL ) continue; /* Check for at least four points. */ if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { RTDEBUGF(2, "%s must have at least four points in each ring", rttype_name(ctx, s->rttype)); rterror(ctx, "%s must have at least four points in each ring", rttype_name(ctx, s->rttype)); return NULL; } /* Check that first and last points are the same. */ if( s->check & RT_PARSER_CHECK_CLOSURE && ! ptarray_is_closed_2d(ctx, pa) ) { RTDEBUGF(2, "%s must have closed rings", rttype_name(ctx, s->rttype)); rterror(ctx, "%s must have closed rings", rttype_name(ctx, s->rttype)); return NULL; } /* Add ring to polygon */ if ( rtpoly_add_ring(ctx, poly, pa) == RT_FAILURE ) { RTDEBUG(2, "Unable to add ring to polygon"); rterror(ctx, "Unable to add ring to polygon"); } } return poly; } /** * TRIANGLE * Read a RTWKB triangle, starting just after the endian byte, * type number and optional srid number. Advance the parse state * forward appropriately. * Triangles are encoded like polygons in RTWKB, but more like linestrings * as rtgeometries. */ static RTTRIANGLE* rttriangle_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { uint32_t nrings = integer_from_wkb_state(ctx, s); RTTRIANGLE *tri = rttriangle_construct_empty(ctx, s->srid, s->has_z, s->has_m); RTPOINTARRAY *pa = NULL; /* Empty triangle? */ if( nrings == 0 ) return tri; /* Should be only one ring. */ if ( nrings != 1 ) rterror(ctx, "Triangle has wrong number of rings: %d", nrings); /* There's only one ring, we hope? */ pa = ptarray_from_wkb_state(ctx, s); /* If there's no points, return an empty triangle. */ if( pa == NULL ) return tri; /* Check for at least four points. */ if( s->check & RT_PARSER_CHECK_MINPOINTS && pa->npoints < 4 ) { RTDEBUGF(2, "%s must have at least four points", rttype_name(ctx, s->rttype)); rterror(ctx, "%s must have at least four points", rttype_name(ctx, s->rttype)); return NULL; } if( s->check & RT_PARSER_CHECK_CLOSURE && ! ptarray_is_closed(ctx, pa) ) { rterror(ctx, "%s must have closed rings", rttype_name(ctx, s->rttype)); return NULL; } if( s->check & RT_PARSER_CHECK_ZCLOSURE && ! ptarray_is_closed_z(ctx, pa) ) { rterror(ctx, "%s must have closed rings", rttype_name(ctx, s->rttype)); return NULL; } /* Empty TRIANGLE starts w/ empty RTPOINTARRAY, free it first */ if (tri->points) ptarray_free(ctx, tri->points); tri->points = pa; return tri; } /** * RTCURVEPOLYTYPE */ static RTCURVEPOLY* rtcurvepoly_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(ctx, s); RTCURVEPOLY *cp = rtcurvepoly_construct_empty(ctx, s->srid, s->has_z, s->has_m); RTGEOM *geom = NULL; int i; /* Empty collection? */ if ( ngeoms == 0 ) return cp; for ( i = 0; i < ngeoms; i++ ) { geom = rtgeom_from_wkb_state(ctx, s); if ( rtcurvepoly_add_ring(ctx, cp, geom) == RT_FAILURE ) rterror(ctx, "Unable to add geometry (%p) to curvepoly (%p)", geom, cp); } return cp; } /** * RTPOLYHEDRALSURFACETYPE */ /** * COLLECTION, RTMULTIPOINTTYPE, RTMULTILINETYPE, RTMULTIPOLYGONTYPE, RTCOMPOUNDTYPE, * RTMULTICURVETYPE, RTMULTISURFACETYPE, * RTTINTYPE */ static RTCOLLECTION* rtcollection_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { uint32_t ngeoms = integer_from_wkb_state(ctx, s); RTCOLLECTION *col = rtcollection_construct_empty(ctx, s->rttype, s->srid, s->has_z, s->has_m); RTGEOM *geom = NULL; int i; RTDEBUGF(4,"Collection has %d components", ngeoms); /* Empty collection? */ if ( ngeoms == 0 ) return col; /* Be strict in polyhedral surface closures */ if ( s->rttype == RTPOLYHEDRALSURFACETYPE ) s->check |= RT_PARSER_CHECK_ZCLOSURE; for ( i = 0; i < ngeoms; i++ ) { geom = rtgeom_from_wkb_state(ctx, s); if ( rtcollection_add_rtgeom(ctx, col, geom) == NULL ) { rterror(ctx, "Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; } /** * GEOMETRY * Generic handling for RTWKB geometries. The front of every RTWKB geometry * (including those embedded in collections) is an endian byte, a type * number and an optional srid number. We handle all those here, then pass * to the appropriate handler for the specific type. */ RTGEOM* rtgeom_from_wkb_state(const RTCTX *ctx, wkb_parse_state *s) { char wkb_little_endian; uint32_t wkb_type; RTDEBUG(4,"Entered function"); /* Fail when handed incorrect starting byte */ wkb_little_endian = byte_from_wkb_state(ctx, s); if( wkb_little_endian != 1 && wkb_little_endian != 0 ) { RTDEBUG(4,"Leaving due to bad first byte!"); rterror(ctx, "Invalid endian flag value encountered."); return NULL; } /* Check the endianness of our input */ s->swap_bytes = RT_FALSE; if( getMachineEndian(ctx) == NDR ) /* Machine arch is little */ { if ( ! wkb_little_endian ) /* Data is big! */ s->swap_bytes = RT_TRUE; } else /* Machine arch is big */ { if ( wkb_little_endian ) /* Data is little! */ s->swap_bytes = RT_TRUE; } /* Read the type number */ wkb_type = integer_from_wkb_state(ctx, s); RTDEBUGF(4,"Got RTWKB type number: 0x%X", wkb_type); rttype_from_wkb_state(ctx, s, wkb_type); /* Read the SRID, if necessary */ if( s->has_srid ) { s->srid = clamp_srid(ctx, integer_from_wkb_state(ctx, s)); /* TODO: warn on explicit UNKNOWN srid ? */ RTDEBUGF(4,"Got SRID: %u", s->srid); } /* Do the right thing */ switch( s->rttype ) { case RTPOINTTYPE: return (RTGEOM*)rtpoint_from_wkb_state(ctx, s); break; case RTLINETYPE: return (RTGEOM*)rtline_from_wkb_state(ctx, s); break; case RTCIRCSTRINGTYPE: return (RTGEOM*)rtcircstring_from_wkb_state(ctx, s); break; case RTPOLYGONTYPE: return (RTGEOM*)rtpoly_from_wkb_state(ctx, s); break; case RTTRIANGLETYPE: return (RTGEOM*)rttriangle_from_wkb_state(ctx, s); break; case RTCURVEPOLYTYPE: return (RTGEOM*)rtcurvepoly_from_wkb_state(ctx, s); break; case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOMPOUNDTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: case RTCOLLECTIONTYPE: return (RTGEOM*)rtcollection_from_wkb_state(ctx, s); break; /* Unknown type! */ default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, s->rttype), s->rttype); } /* Return value to keep compiler happy. */ return NULL; } /* TODO add check for SRID consistency */ /** * RTWKB inputs *must* have a declared size, to prevent malformed RTWKB from reading * off the end of the memory segment (this stops a malevolent user from declaring * a one-ring polygon to have 10 rings, causing the RTWKB reader to walk off the * end of the memory). * * Check is a bitmask of: RT_PARSER_CHECK_MINPOINTS, RT_PARSER_CHECK_ODD, * RT_PARSER_CHECK_CLOSURE, RT_PARSER_CHECK_NONE, RT_PARSER_CHECK_ALL */ RTGEOM* rtgeom_from_wkb(const RTCTX *ctx, const uint8_t *wkb, const size_t wkb_size, const char check) { wkb_parse_state s; /* Initialize the state appropriately */ s.wkb = wkb; s.wkb_size = wkb_size; s.swap_bytes = RT_FALSE; s.check = check; s.rttype = 0; s.srid = SRID_UNKNOWN; s.has_z = RT_FALSE; s.has_m = RT_FALSE; s.has_srid = RT_FALSE; s.pos = wkb; /* Hand the check catch-all values */ if ( check & RT_PARSER_CHECK_NONE ) s.check = 0; else s.check = check; return rtgeom_from_wkb_state(ctx, &s); } RTGEOM* rtgeom_from_hexwkb(const RTCTX *ctx, const char *hexwkb, const char check) { int hexwkb_len; uint8_t *wkb; RTGEOM *rtgeom; if ( ! hexwkb ) { rterror(ctx, "rtgeom_from_hexwkb: null input"); return NULL; } hexwkb_len = strlen(hexwkb); wkb = bytes_from_hexbytes(ctx, hexwkb, hexwkb_len); rtgeom = rtgeom_from_wkb(ctx, wkb, hexwkb_len/2, check); rtfree(ctx, wkb); return rtgeom; } src/rtiterator.c000066400000000000000000000147761271715413500142350ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2015 Daniel Baston * **********************************************************************/ #include "librttopo_geom.h" #include "rtgeom_log.h" struct LISTNODE { struct LISTNODE* next; void* item; }; typedef struct LISTNODE LISTNODE; /* The RTPOINTITERATOR consists of two stacks of items to process: a stack * of geometries, and a stack of RTPOINTARRAYs extracted from those geometries. * The index "i" refers to the "next" point, which is found at the top of the * pointarrays stack. * * When the pointarrays stack is depleted, we pull a geometry from the geometry * stack to replenish it. */ struct RTPOINTITERATOR { LISTNODE* geoms; LISTNODE* pointarrays; uint32_t i; char allow_modification; }; static LISTNODE* prepend_node(const RTCTX *ctx, void* g, LISTNODE* front) { LISTNODE* n = rtalloc(ctx, sizeof(LISTNODE)); n->item = g; n->next = front; return n; } static LISTNODE* pop_node(const RTCTX *ctx, LISTNODE* i) { LISTNODE* next = i->next; rtfree(ctx, i); return next; } static int add_rtgeom_to_stack(const RTCTX *ctx, RTPOINTITERATOR* s, RTGEOM* g) { if (rtgeom_is_empty(ctx, g)) return RT_FAILURE; s->geoms = prepend_node(ctx, g, s->geoms); return RT_SUCCESS; } /** Return a pointer to the first of one or more LISTNODEs holding the RTPOINTARRAYs * of a geometry. Will not handle GeometryCollections. */ static LISTNODE* extract_pointarrays_from_rtgeom(const RTCTX *ctx, RTGEOM* g) { switch(rtgeom_get_type(ctx, g)) { case RTPOINTTYPE: return prepend_node(ctx, rtgeom_as_rtpoint(ctx, g)->point, NULL); case RTLINETYPE: return prepend_node(ctx, rtgeom_as_rtline(ctx, g)->points, NULL); case RTTRIANGLETYPE: return prepend_node(ctx, rtgeom_as_rttriangle(ctx, g)->points, NULL); case RTCIRCSTRINGTYPE: return prepend_node(ctx, rtgeom_as_rtcircstring(ctx, g)->points, NULL); case RTPOLYGONTYPE: { LISTNODE* n = NULL; RTPOLY* p = rtgeom_as_rtpoly(ctx, g); int i; for (i = p->nrings - 1; i >= 0; i--) { n = prepend_node(ctx, p->rings[i], n); } return n; } default: rterror(ctx, "Unsupported geometry type for rtpointiterator"); } return NULL; } /** Remove an RTCOLLECTION from the iterator stack, and add the components of the * RTCOLLECTIONs to the stack. */ static void unroll_collection(const RTCTX *ctx, RTPOINTITERATOR* s) { int i; RTCOLLECTION* c; if (!s->geoms) { return; } c = (RTCOLLECTION*) s->geoms->item; s->geoms = pop_node(ctx, s->geoms); for (i = c->ngeoms - 1; i >= 0; i--) { RTGEOM* g = rtcollection_getsubgeom(ctx, c, i); add_rtgeom_to_stack(ctx, s, g); } } /** Unroll RTCOLLECTIONs from the top of the stack, as necessary, until the element at the * top of the stack is not a RTCOLLECTION. */ static void unroll_collections(const RTCTX *ctx, RTPOINTITERATOR* s) { while(s->geoms && rtgeom_is_collection(ctx, s->geoms->item)) { unroll_collection(ctx, s); } } static int rtpointiterator_advance(const RTCTX *ctx, RTPOINTITERATOR* s) { s->i += 1; /* We've reached the end of our current RTPOINTARRAY. Try to see if there * are any more RTPOINTARRAYS on the stack. */ if (s->pointarrays && s->i >= ((RTPOINTARRAY*) s->pointarrays->item)->npoints) { s->pointarrays = pop_node(ctx, s->pointarrays); s->i = 0; } /* We don't have a current RTPOINTARRAY. Pull a geometry from the stack, and * decompose it into its POINTARRARYs. */ if (!s->pointarrays) { RTGEOM* g; unroll_collections(ctx, s); if (!s->geoms) { return RT_FAILURE; } s->i = 0; g = s->geoms->item; s->pointarrays = extract_pointarrays_from_rtgeom(ctx, g); s->geoms = pop_node(ctx, s->geoms); } if (!s->pointarrays) { return RT_FAILURE; } return RT_SUCCESS; } /* Public API implementation */ int rtpointiterator_peek(const RTCTX *ctx, RTPOINTITERATOR* s, RTPOINT4D* p) { if (!rtpointiterator_has_next(ctx, s)) return RT_FAILURE; return rt_getPoint4d_p(ctx, s->pointarrays->item, s->i, p); } int rtpointiterator_has_next(const RTCTX *ctx, RTPOINTITERATOR* s) { if (s->pointarrays && s->i < ((RTPOINTARRAY*) s->pointarrays->item)->npoints) return RT_TRUE; return RT_FALSE; } int rtpointiterator_next(const RTCTX *ctx, RTPOINTITERATOR* s, RTPOINT4D* p) { if (!rtpointiterator_has_next(ctx, s)) return RT_FAILURE; /* If p is NULL, just advance without reading */ if (p && !rtpointiterator_peek(ctx, s, p)) return RT_FAILURE; rtpointiterator_advance(ctx, s); return RT_SUCCESS; } int rtpointiterator_modify_next(const RTCTX *ctx, RTPOINTITERATOR* s, const RTPOINT4D* p) { if (!rtpointiterator_has_next(ctx, s)) return RT_FAILURE; if (!s->allow_modification) { rterror(ctx, "Cannot write to read-only iterator"); return RT_FAILURE; } ptarray_set_point4d(ctx, s->pointarrays->item, s->i, p); rtpointiterator_advance(ctx, s); return RT_SUCCESS; } RTPOINTITERATOR* rtpointiterator_create(const RTCTX *ctx, const RTGEOM* g) { RTPOINTITERATOR* it = rtpointiterator_create_rw(ctx, (RTGEOM*) g); it->allow_modification = RT_FALSE; return it; } RTPOINTITERATOR* rtpointiterator_create_rw(const RTCTX *ctx, RTGEOM* g) { RTPOINTITERATOR* it = rtalloc(ctx, sizeof(RTPOINTITERATOR)); it->geoms = NULL; it->pointarrays = NULL; it->i = 0; it->allow_modification = RT_TRUE; add_rtgeom_to_stack(ctx, it, g); rtpointiterator_advance(ctx, it); return it; } void rtpointiterator_destroy(const RTCTX *ctx, RTPOINTITERATOR* s) { while (s->geoms != NULL) { s->geoms = pop_node(ctx, s->geoms); } while (s->pointarrays != NULL) { s->pointarrays = pop_node(ctx, s->pointarrays); } rtfree(ctx, s); } src/rtline.c000066400000000000000000000345051271715413500133230ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic RTLINE functions */ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /* * Construct a new RTLINE. points will *NOT* be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ RTLINE * rtline_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points) { RTLINE *result; result = (RTLINE*) rtalloc(ctx, sizeof(RTLINE)); RTDEBUG(2, "rtline_construct called."); result->type = RTLINETYPE; result->flags = points->flags; RTFLAGS_SET_BBOX(result->flags, bbox?1:0); RTDEBUGF(3, "rtline_construct type=%d", result->type); result->srid = srid; result->points = points; result->bbox = bbox; return result; } RTLINE * rtline_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTLINE *result = rtalloc(ctx, sizeof(RTLINE)); result->type = RTLINETYPE; result->flags = gflags(ctx, hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(ctx, hasz, hasm, 1); result->bbox = NULL; return result; } void rtline_free(const RTCTX *ctx, RTLINE *line) { if ( ! line ) return; if ( line->bbox ) rtfree(ctx, line->bbox); if ( line->points ) ptarray_free(ctx, line->points); rtfree(ctx, line); } void printRTLINE(const RTCTX *ctx, RTLINE *line) { rtnotice(ctx, "RTLINE {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(line->flags)); rtnotice(ctx, " srid = %i", (int)line->srid); printPA(ctx, line->points); rtnotice(ctx, "}"); } /* @brief Clone RTLINE object. Serialized point lists are not copied. * * @see ptarray_clone */ RTLINE * rtline_clone(const RTCTX *ctx, const RTLINE *g) { RTLINE *ret = rtalloc(ctx, sizeof(RTLINE)); RTDEBUGF(2, "rtline_clone called with %p", g); memcpy(ret, g, sizeof(RTLINE)); ret->points = ptarray_clone(ctx, g->points); if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); return ret; } /* Deep clone RTLINE object. RTPOINTARRAY *is* copied. */ RTLINE * rtline_clone_deep(const RTCTX *ctx, const RTLINE *g) { RTLINE *ret = rtalloc(ctx, sizeof(RTLINE)); RTDEBUGF(2, "rtline_clone_deep called with %p", g); memcpy(ret, g, sizeof(RTLINE)); if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); if ( g->points ) ret->points = ptarray_clone_deep(ctx, g->points); RTFLAGS_SET_READONLY(ret->flags,0); return ret; } void rtline_release(const RTCTX *ctx, RTLINE *rtline) { rtgeom_release(ctx, rtline_as_rtgeom(ctx, rtline)); } void rtline_reverse(const RTCTX *ctx, RTLINE *line) { if ( rtline_is_empty(ctx, line) ) return; ptarray_reverse(ctx, line->points); } RTLINE * rtline_segmentize2d(const RTCTX *ctx, RTLINE *line, double dist) { RTPOINTARRAY *segmentized = ptarray_segmentize2d(ctx, line->points, dist); if ( ! segmentized ) return NULL; return rtline_construct(ctx, line->srid, NULL, segmentized); } /* check coordinate equality */ char rtline_same(const RTCTX *ctx, const RTLINE *l1, const RTLINE *l2) { return ptarray_same(ctx, l1->points, l2->points); } /* * Construct a RTLINE from an array of point and line geometries * RTLINE dimensions are large enough to host all input dimensions. */ RTLINE * rtline_from_rtgeom_array(const RTCTX *ctx, int srid, uint32_t ngeoms, RTGEOM **geoms) { int i; int hasz = RT_FALSE; int hasm = RT_FALSE; RTPOINTARRAY *pa; RTLINE *line; RTPOINT4D pt; /* * Find output dimensions, check integrity */ for (i=0; iflags) ) hasz = RT_TRUE; if ( RTFLAGS_GET_M(geoms[i]->flags) ) hasm = RT_TRUE; if ( hasz && hasm ) break; /* Nothing more to learn! */ } /* ngeoms should be a guess about how many points we have in input */ pa = ptarray_construct_empty(ctx, hasz, hasm, ngeoms); for ( i=0; i < ngeoms; i++ ) { RTGEOM *g = geoms[i]; if ( rtgeom_is_empty(ctx, g) ) continue; if ( g->type == RTPOINTTYPE ) { rtpoint_getPoint4d_p(ctx, (RTPOINT*)g, &pt); ptarray_append_point(ctx, pa, &pt, RT_TRUE); } else if ( g->type == RTLINETYPE ) { ptarray_append_ptarray(ctx, pa, ((RTLINE*)g)->points, -1); } else { ptarray_free(ctx, pa); rterror(ctx, "rtline_from_ptarray: invalid input type: %s", rttype_name(ctx, g->type)); return NULL; } } if ( pa->npoints > 0 ) line = rtline_construct(ctx, srid, NULL, pa); else { /* Is this really any different from the above ? */ ptarray_free(ctx, pa); line = rtline_construct_empty(ctx, srid, hasz, hasm); } return line; } /* * Construct a RTLINE from an array of RTPOINTs * RTLINE dimensions are large enough to host all input dimensions. */ RTLINE * rtline_from_ptarray(const RTCTX *ctx, int srid, uint32_t npoints, RTPOINT **points) { int i; int hasz = RT_FALSE; int hasm = RT_FALSE; RTPOINTARRAY *pa; RTLINE *line; RTPOINT4D pt; /* * Find output dimensions, check integrity */ for (i=0; itype != RTPOINTTYPE ) { rterror(ctx, "rtline_from_ptarray: invalid input type: %s", rttype_name(ctx, points[i]->type)); return NULL; } if ( RTFLAGS_GET_Z(points[i]->flags) ) hasz = RT_TRUE; if ( RTFLAGS_GET_M(points[i]->flags) ) hasm = RT_TRUE; if ( hasz && hasm ) break; /* Nothing more to learn! */ } pa = ptarray_construct_empty(ctx, hasz, hasm, npoints); for ( i=0; i < npoints; i++ ) { if ( ! rtpoint_is_empty(ctx, points[i]) ) { rtpoint_getPoint4d_p(ctx, points[i], &pt); ptarray_append_point(ctx, pa, &pt, RT_TRUE); } } if ( pa->npoints > 0 ) line = rtline_construct(ctx, srid, NULL, pa); else line = rtline_construct_empty(ctx, srid, hasz, hasm); return line; } /* * Construct a RTLINE from a RTMPOINT */ RTLINE * rtline_from_rtmpoint(const RTCTX *ctx, int srid, const RTMPOINT *mpoint) { uint32_t i; RTPOINTARRAY *pa = NULL; RTGEOM *rtgeom = (RTGEOM*)mpoint; RTPOINT4D pt; char hasz = rtgeom_has_z(ctx, rtgeom); char hasm = rtgeom_has_m(ctx, rtgeom); uint32_t npoints = mpoint->ngeoms; if ( rtgeom_is_empty(ctx, rtgeom) ) { return rtline_construct_empty(ctx, srid, hasz, hasm); } pa = ptarray_construct(ctx, hasz, hasm, npoints); for (i=0; i < npoints; i++) { rt_getPoint4d_p(ctx, mpoint->geoms[i]->point, 0, &pt); ptarray_set_point4d(ctx, pa, i, &pt); } RTDEBUGF(3, "rtline_from_rtmpoint: constructed pointarray for %d points", mpoint->ngeoms); return rtline_construct(ctx, srid, NULL, pa); } /** * Returns freshly allocated #RTPOINT that corresponds to the index where. * Returns NULL if the geometry is empty or the index invalid. */ RTPOINT* rtline_get_rtpoint(const RTCTX *ctx, const RTLINE *line, int where) { RTPOINT4D pt; RTPOINT *rtpoint; RTPOINTARRAY *pa; if ( rtline_is_empty(ctx, line) || where < 0 || where >= line->points->npoints ) return NULL; pa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(line->flags), RTFLAGS_GET_M(line->flags), 1); pt = rt_getPoint4d(ctx, line->points, where); ptarray_append_point(ctx, pa, &pt, RT_TRUE); rtpoint = rtpoint_construct(ctx, line->srid, NULL, pa); return rtpoint; } int rtline_add_rtpoint(const RTCTX *ctx, RTLINE *line, RTPOINT *point, int where) { RTPOINT4D pt; rt_getPoint4d_p(ctx, point->point, 0, &pt); if ( ptarray_insert_point(ctx, line->points, &pt, where) != RT_SUCCESS ) return RT_FAILURE; /* Update the bounding box */ if ( line->bbox ) { rtgeom_drop_bbox(ctx, rtline_as_rtgeom(ctx, line)); rtgeom_add_bbox(ctx, rtline_as_rtgeom(ctx, line)); } return RT_SUCCESS; } RTLINE * rtline_removepoint(const RTCTX *ctx, RTLINE *line, uint32_t index) { RTPOINTARRAY *newpa; RTLINE *ret; newpa = ptarray_removePoint(ctx, line->points, index); ret = rtline_construct(ctx, line->srid, NULL, newpa); rtgeom_add_bbox(ctx, (RTGEOM *) ret); return ret; } /* * Note: input will be changed, make sure you have permissions for this. */ void rtline_setPoint4d(const RTCTX *ctx, RTLINE *line, uint32_t index, RTPOINT4D *newpoint) { ptarray_set_point4d(ctx, line->points, index, newpoint); /* Update the box, if there is one to update */ if ( line->bbox ) { rtgeom_drop_bbox(ctx, (RTGEOM*)line); rtgeom_add_bbox(ctx, (RTGEOM*)line); } } /** * Re-write the measure ordinate (or add one, if it isn't already there) interpolating * the measure between the supplied start and end values. */ RTLINE* rtline_measured_from_rtline(const RTCTX *ctx, const RTLINE *rtline, double m_start, double m_end) { int i = 0; int hasm = 0, hasz = 0; int npoints = 0; double length = 0.0; double length_so_far = 0.0; double m_range = m_end - m_start; double m; RTPOINTARRAY *pa = NULL; RTPOINT3DZ p1, p2; if ( rtline->type != RTLINETYPE ) { rterror(ctx, "rtline_construct_from_rtline: only line types supported"); return NULL; } hasz = RTFLAGS_GET_Z(rtline->flags); hasm = 1; /* Null points or npoints == 0 will result in empty return geometry */ if ( rtline->points ) { npoints = rtline->points->npoints; length = ptarray_length_2d(ctx, rtline->points); rt_getPoint3dz_p(ctx, rtline->points, 0, &p1); } pa = ptarray_construct(ctx, hasz, hasm, npoints); for ( i = 0; i < npoints; i++ ) { RTPOINT4D q; RTPOINT2D a, b; rt_getPoint3dz_p(ctx, rtline->points, i, &p2); a.x = p1.x; a.y = p1.y; b.x = p2.x; b.y = p2.y; length_so_far += distance2d_pt_pt(ctx, &a, &b); if ( length > 0.0 ) m = m_start + m_range * length_so_far / length; /* #3172, support (valid) zero-length inputs */ else if ( length == 0.0 && npoints > 1 ) m = m_start + m_range * i / (npoints-1); else m = 0.0; q.x = p2.x; q.y = p2.y; q.z = p2.z; q.m = m; ptarray_set_point4d(ctx, pa, i, &q); p1 = p2; } return rtline_construct(ctx, rtline->srid, NULL, pa); } RTGEOM* rtline_remove_repeated_points(const RTCTX *ctx, const RTLINE *rtline, double tolerance) { RTPOINTARRAY* npts = ptarray_remove_repeated_points_minpoints(ctx, rtline->points, tolerance, 2); RTDEBUGF(3, "%s: npts %p", __func__, npts); return (RTGEOM*)rtline_construct(ctx, rtline->srid, rtline->bbox ? gbox_copy(ctx, rtline->bbox) : 0, npts); } int rtline_is_closed(const RTCTX *ctx, const RTLINE *line) { if (RTFLAGS_GET_Z(line->flags)) return ptarray_is_closed_3d(ctx, line->points); return ptarray_is_closed_2d(ctx, line->points); } int rtline_is_trajectory(const RTCTX *ctx, const RTLINE *line) { RTPOINT3DM p; int i, n; double m = -1 * FLT_MAX; if ( ! RTFLAGS_GET_M(line->flags) ) { rtnotice(ctx, "Line does not have M dimension"); return RT_FALSE; } n = line->points->npoints; if ( n < 2 ) return RT_TRUE; /* empty or single-point are "good" */ for (i=0; ipoints, i, &p); if ( p.m <= m ) { rtnotice(ctx, "Measure of vertex %d (%g) not bigger than measure of vertex %d (%g)", i, p.m, i-1, m); return RT_FALSE; } m = p.m; } return RT_TRUE; } RTLINE* rtline_force_dims(const RTCTX *ctx, const RTLINE *line, int hasz, int hasm) { RTPOINTARRAY *pdims = NULL; RTLINE *lineout; /* Return 2D empty */ if( rtline_is_empty(ctx, line) ) { lineout = rtline_construct_empty(ctx, line->srid, hasz, hasm); } else { pdims = ptarray_force_dims(ctx, line->points, hasz, hasm); lineout = rtline_construct(ctx, line->srid, NULL, pdims); } lineout->type = line->type; return lineout; } int rtline_is_empty(const RTCTX *ctx, const RTLINE *line) { if ( !line->points || line->points->npoints < 1 ) return RT_TRUE; return RT_FALSE; } int rtline_count_vertices(const RTCTX *ctx, RTLINE *line) { assert(line); if ( ! line->points ) return 0; return line->points->npoints; } RTLINE* rtline_simplify(const RTCTX *ctx, const RTLINE *iline, double dist, int preserve_collapsed) { static const int minvertices = 2; /* TODO: allow setting this */ RTLINE *oline; RTPOINTARRAY *pa; RTDEBUG(2, "function called"); /* Skip empty case */ if( rtline_is_empty(ctx, iline) ) return NULL; pa = ptarray_simplify(ctx, iline->points, dist, minvertices); if ( ! pa ) return NULL; /* Make sure single-point collapses have two points */ if ( pa->npoints == 1 ) { /* Make sure single-point collapses have two points */ if ( preserve_collapsed ) { RTPOINT4D pt; rt_getPoint4d_p(ctx, pa, 0, &pt); ptarray_append_point(ctx, pa, &pt, RT_TRUE); } /* Return null for collapse */ else { ptarray_free(ctx, pa); return NULL; } } oline = rtline_construct(ctx, iline->srid, NULL, pa); oline->type = iline->type; return oline; } double rtline_length(const RTCTX *ctx, const RTLINE *line) { if ( rtline_is_empty(ctx, line) ) return 0.0; return ptarray_length(ctx, line->points); } double rtline_length_2d(const RTCTX *ctx, const RTLINE *line) { if ( rtline_is_empty(ctx, line) ) return 0.0; return ptarray_length_2d(ctx, line->points); } RTLINE* rtline_grid(const RTCTX *ctx, const RTLINE *line, const gridspec *grid) { RTLINE *oline; RTPOINTARRAY *opa; opa = ptarray_grid(ctx, line->points, grid); /* Skip line3d with less then 2 points */ if ( opa->npoints < 2 ) return NULL; /* TODO: grid bounding box... */ oline = rtline_construct(ctx, line->srid, NULL, opa); return oline; } src/rtlinearreferencing.c000066400000000000000000001203051271715413500160500ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2015 Sandro Santilli * Copyright (C) 2011 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include "measures3d.h" static int segment_locate_along(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2, double m, double offset, RTPOINT4D *pn) { double m1 = p1->m; double m2 = p2->m; double mprop; /* M is out of range, no new point generated. */ if ( (m < FP_MIN(m1,m2)) || (m > FP_MAX(m1,m2)) ) { return RT_FALSE; } if( m1 == m2 ) { /* Degenerate case: same M on both points. If they are the same point we just return one of them. */ if ( p4d_same(ctx, p1,p2) ) { *pn = *p1; return RT_TRUE; } /* If the points are different we can out. Correct behavior is probably an mprop of 0.5? */ rterror(ctx, "Zero measure-length line encountered!"); return RT_FALSE; } /* M is in range, new point to be generated. */ mprop = (m - m1) / (m2 - m1); pn->x = p1->x + (p2->x - p1->x) * mprop; pn->y = p1->y + (p2->y - p1->y) * mprop; pn->z = p1->z + (p2->z - p1->z) * mprop; pn->m = m; /* Offset to the left or right, if necessary. */ if ( offset != 0.0 ) { double theta = atan2(p2->y - p1->y, p2->x - p1->x); pn->x -= sin(theta) * offset; pn->y += cos(theta) * offset; } return RT_TRUE; } static RTPOINTARRAY* ptarray_locate_along(const RTCTX *ctx, const RTPOINTARRAY *pa, double m, double offset) { int i; RTPOINT4D p1, p2, pn; RTPOINTARRAY *dpa = NULL; /* Can't do anything with degenerate point arrays */ if ( ! pa || pa->npoints < 2 ) return NULL; /* Walk through each segment in the point array */ for ( i = 1; i < pa->npoints; i++ ) { rt_getPoint4d_p(ctx, pa, i-1, &p1); rt_getPoint4d_p(ctx, pa, i, &p2); /* No derived point? Move to next segment. */ if ( segment_locate_along(ctx, &p1, &p2, m, offset, &pn) == RT_FALSE ) continue; /* No pointarray, make a fresh one */ if ( dpa == NULL ) dpa = ptarray_construct_empty(ctx, ptarray_has_z(ctx, pa), ptarray_has_m(ctx, pa), 8); /* Add our new point to the array */ ptarray_append_point(ctx, dpa, &pn, 0); } return dpa; } static RTMPOINT* rtline_locate_along(const RTCTX *ctx, const RTLINE *rtline, double m, double offset) { RTPOINTARRAY *opa = NULL; RTMPOINT *mp = NULL; RTGEOM *rtg = rtline_as_rtgeom(ctx, rtline); int hasz, hasm, srid; /* Return degenerates upwards */ if ( ! rtline ) return NULL; /* Create empty return shell */ srid = rtgeom_get_srid(ctx, rtg); hasz = rtgeom_has_z(ctx, rtg); hasm = rtgeom_has_m(ctx, rtg); if ( hasm ) { /* Find points along */ opa = ptarray_locate_along(ctx, rtline->points, m, offset); } else { RTLINE *rtline_measured = rtline_measured_from_rtline(ctx, rtline, 0.0, 1.0); opa = ptarray_locate_along(ctx, rtline_measured->points, m, offset); rtline_free(ctx, rtline_measured); } /* Return NULL as EMPTY */ if ( ! opa ) return rtmpoint_construct_empty(ctx, srid, hasz, hasm); /* Convert pointarray into a multipoint */ mp = rtmpoint_construct(ctx, srid, opa); ptarray_free(ctx, opa); return mp; } static RTMPOINT* rtmline_locate_along(const RTCTX *ctx, const RTMLINE *rtmline, double m, double offset) { RTMPOINT *rtmpoint = NULL; RTGEOM *rtg = rtmline_as_rtgeom(ctx, rtmline); int i, j; /* Return degenerates upwards */ if ( (!rtmline) || (rtmline->ngeoms < 1) ) return NULL; /* Construct return */ rtmpoint = rtmpoint_construct_empty(ctx, rtgeom_get_srid(ctx, rtg), rtgeom_has_z(ctx, rtg), rtgeom_has_m(ctx, rtg)); /* Locate along each sub-line */ for ( i = 0; i < rtmline->ngeoms; i++ ) { RTMPOINT *along = rtline_locate_along(ctx, rtmline->geoms[i], m, offset); if ( along ) { if ( ! rtgeom_is_empty(ctx, (RTGEOM*)along) ) { for ( j = 0; j < along->ngeoms; j++ ) { rtmpoint_add_rtpoint(ctx, rtmpoint, along->geoms[j]); } } /* Free the containing geometry, but leave the sub-geometries around */ along->ngeoms = 0; rtmpoint_free(ctx, along); } } return rtmpoint; } static RTMPOINT* rtpoint_locate_along(const RTCTX *ctx, const RTPOINT *rtpoint, double m, double offset) { double point_m = rtpoint_get_m(ctx, rtpoint); RTGEOM *rtg = rtpoint_as_rtgeom(ctx, rtpoint); RTMPOINT *r = rtmpoint_construct_empty(ctx, rtgeom_get_srid(ctx, rtg), rtgeom_has_z(ctx, rtg), rtgeom_has_m(ctx, rtg)); if ( FP_EQUALS(m, point_m) ) { rtmpoint_add_rtpoint(ctx, r, rtpoint_clone(ctx, rtpoint)); } return r; } static RTMPOINT* rtmpoint_locate_along(const RTCTX *ctx, const RTMPOINT *rtin, double m, double offset) { RTGEOM *rtg = rtmpoint_as_rtgeom(ctx, rtin); RTMPOINT *rtout = NULL; int i; /* Construct return */ rtout = rtmpoint_construct_empty(ctx, rtgeom_get_srid(ctx, rtg), rtgeom_has_z(ctx, rtg), rtgeom_has_m(ctx, rtg)); for ( i = 0; i < rtin->ngeoms; i++ ) { double point_m = rtpoint_get_m(ctx, rtin->geoms[i]); if ( FP_EQUALS(m, point_m) ) { rtmpoint_add_rtpoint(ctx, rtout, rtpoint_clone(ctx, rtin->geoms[i])); } } return rtout; } RTGEOM* rtgeom_locate_along(const RTCTX *ctx, const RTGEOM *rtin, double m, double offset) { if ( ! rtin ) return NULL; if ( ! rtgeom_has_m(ctx, rtin) ) rterror(ctx, "Input geometry does not have a measure dimension"); switch (rtin->type) { case RTPOINTTYPE: return (RTGEOM*)rtpoint_locate_along(ctx, (RTPOINT*)rtin, m, offset); case RTMULTIPOINTTYPE: return (RTGEOM*)rtmpoint_locate_along(ctx, (RTMPOINT*)rtin, m, offset); case RTLINETYPE: return (RTGEOM*)rtline_locate_along(ctx, (RTLINE*)rtin, m, offset); case RTMULTILINETYPE: return (RTGEOM*)rtmline_locate_along(ctx, (RTMLINE*)rtin, m, offset); /* Only line types supported right now */ /* TO DO: CurveString, CompoundCurve, MultiCurve */ /* TO DO: Point, MultiPoint */ default: rterror(ctx, "Only linear geometries are supported, %s provided.",rttype_name(ctx, rtin->type)); return NULL; } return NULL; } /** * Given a RTPOINT4D and an ordinate number, return * the value of the ordinate. * @param p input point * @param ordinate number (1=x, 2=y, 3=z, 4=m) * @return d value at that ordinate */ double rtpoint_get_ordinate(const RTCTX *ctx, const RTPOINT4D *p, char ordinate) { if ( ! p ) { rterror(ctx, "Null input geometry."); return 0.0; } if ( ! ( ordinate == 'X' || ordinate == 'Y' || ordinate == 'Z' || ordinate == 'M' ) ) { rterror(ctx, "Cannot extract %c ordinate.", ordinate); return 0.0; } if ( ordinate == 'X' ) return p->x; if ( ordinate == 'Y' ) return p->y; if ( ordinate == 'Z' ) return p->z; if ( ordinate == 'M' ) return p->m; /* X */ return p->x; } /** * Given a point, ordinate number and value, set that ordinate on the * point. */ void rtpoint_set_ordinate(const RTCTX *ctx, RTPOINT4D *p, char ordinate, double value) { if ( ! p ) { rterror(ctx, "Null input geometry."); return; } if ( ! ( ordinate == 'X' || ordinate == 'Y' || ordinate == 'Z' || ordinate == 'M' ) ) { rterror(ctx, "Cannot set %c ordinate.", ordinate); return; } RTDEBUGF(4, " setting ordinate %c to %g", ordinate, value); switch ( ordinate ) { case 'X': p->x = value; return; case 'Y': p->y = value; return; case 'Z': p->z = value; return; case 'M': p->m = value; return; } } /** * Given two points, a dimensionality, an ordinate, and an interpolation value * generate a new point that is proportionally between the input points, * using the values in the provided dimension as the scaling factors. */ int point_interpolate(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2, RTPOINT4D *p, int hasz, int hasm, char ordinate, double interpolation_value) { static char* dims = "XYZM"; double p1_value = rtpoint_get_ordinate(ctx, p1, ordinate); double p2_value = rtpoint_get_ordinate(ctx, p2, ordinate); double proportion; int i = 0; if ( ! ( ordinate == 'X' || ordinate == 'Y' || ordinate == 'Z' || ordinate == 'M' ) ) { rterror(ctx, "Cannot set %c ordinate.", ordinate); return 0; } if ( FP_MIN(p1_value, p2_value) > interpolation_value || FP_MAX(p1_value, p2_value) < interpolation_value ) { rterror(ctx, "Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value); return 0; } proportion = fabs((interpolation_value - p1_value) / (p2_value - p1_value)); for ( i = 0; i < 4; i++ ) { double newordinate = 0.0; if ( dims[i] == 'Z' && ! hasz ) continue; if ( dims[i] == 'M' && ! hasm ) continue; p1_value = rtpoint_get_ordinate(ctx, p1, dims[i]); p2_value = rtpoint_get_ordinate(ctx, p2, dims[i]); newordinate = p1_value + proportion * (p2_value - p1_value); rtpoint_set_ordinate(ctx, p, dims[i], newordinate); RTDEBUGF(4, " clip ordinate(%c) p1_value(%g) p2_value(%g) proportion(%g) newordinate(%g) ", dims[i], p1_value, p2_value, proportion, newordinate ); } return 1; } /** * Clip an input POINT between two values, on any ordinate input. */ RTCOLLECTION* rtpoint_clip_to_ordinate_range(const RTCTX *ctx, const RTPOINT *point, char ordinate, double from, double to) { RTCOLLECTION *rtgeom_out = NULL; char hasz, hasm; RTPOINT4D p4d; double ordinate_value; /* Nothing to do with NULL */ if ( ! point ) rterror(ctx, "Null input geometry."); /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } /* Read Z/M info */ hasz = rtgeom_has_z(ctx, rtpoint_as_rtgeom(ctx, point)); hasm = rtgeom_has_m(ctx, rtpoint_as_rtgeom(ctx, point)); /* Prepare return object */ rtgeom_out = rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, point->srid, hasz, hasm); /* Test if ordinate is in range */ rtpoint_getPoint4d_p(ctx, point, &p4d); ordinate_value = rtpoint_get_ordinate(ctx, &p4d, ordinate); if ( from <= ordinate_value && to >= ordinate_value ) { RTPOINT *rtp = rtpoint_clone(ctx, point); rtcollection_add_rtgeom(ctx, rtgeom_out, rtpoint_as_rtgeom(ctx, rtp)); } /* Set the bbox, if necessary */ if ( rtgeom_out->bbox ) { rtgeom_drop_bbox(ctx, (RTGEOM*)rtgeom_out); rtgeom_add_bbox(ctx, (RTGEOM*)rtgeom_out); } return rtgeom_out; } /** * Clip an input MULTIPOINT between two values, on any ordinate input. */ RTCOLLECTION* rtmpoint_clip_to_ordinate_range(const RTCTX *ctx, const RTMPOINT *mpoint, char ordinate, double from, double to) { RTCOLLECTION *rtgeom_out = NULL; char hasz, hasm; int i; /* Nothing to do with NULL */ if ( ! mpoint ) rterror(ctx, "Null input geometry."); /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } /* Read Z/M info */ hasz = rtgeom_has_z(ctx, rtmpoint_as_rtgeom(ctx, mpoint)); hasm = rtgeom_has_m(ctx, rtmpoint_as_rtgeom(ctx, mpoint)); /* Prepare return object */ rtgeom_out = rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, mpoint->srid, hasz, hasm); /* For each point, is its ordinate value between from and to? */ for ( i = 0; i < mpoint->ngeoms; i ++ ) { RTPOINT4D p4d; double ordinate_value; rtpoint_getPoint4d_p(ctx, mpoint->geoms[i], &p4d); ordinate_value = rtpoint_get_ordinate(ctx, &p4d, ordinate); if ( from <= ordinate_value && to >= ordinate_value ) { RTPOINT *rtp = rtpoint_clone(ctx, mpoint->geoms[i]); rtcollection_add_rtgeom(ctx, rtgeom_out, rtpoint_as_rtgeom(ctx, rtp)); } } /* Set the bbox, if necessary */ if ( rtgeom_out->bbox ) { rtgeom_drop_bbox(ctx, (RTGEOM*)rtgeom_out); rtgeom_add_bbox(ctx, (RTGEOM*)rtgeom_out); } return rtgeom_out; } /** * Clip an input MULTILINESTRING between two values, on any ordinate input. */ RTCOLLECTION* rtmline_clip_to_ordinate_range(const RTCTX *ctx, const RTMLINE *mline, char ordinate, double from, double to) { RTCOLLECTION *rtgeom_out = NULL; if ( ! mline ) { rterror(ctx, "Null input geometry."); return NULL; } if ( mline->ngeoms == 1) { rtgeom_out = rtline_clip_to_ordinate_range(ctx, mline->geoms[0], ordinate, from, to); } else { RTCOLLECTION *col; char hasz = rtgeom_has_z(ctx, rtmline_as_rtgeom(ctx, mline)); char hasm = rtgeom_has_m(ctx, rtmline_as_rtgeom(ctx, mline)); int i, j; char homogeneous = 1; size_t geoms_size = 0; rtgeom_out = rtcollection_construct_empty(ctx, RTMULTILINETYPE, mline->srid, hasz, hasm); RTFLAGS_SET_Z(rtgeom_out->flags, hasz); RTFLAGS_SET_M(rtgeom_out->flags, hasm); for ( i = 0; i < mline->ngeoms; i ++ ) { col = rtline_clip_to_ordinate_range(ctx, mline->geoms[i], ordinate, from, to); if ( col ) { /* Something was left after the clip. */ if ( rtgeom_out->ngeoms + col->ngeoms > geoms_size ) { geoms_size += 16; if ( rtgeom_out->geoms ) { rtgeom_out->geoms = rtrealloc(ctx, rtgeom_out->geoms, geoms_size * sizeof(RTGEOM*)); } else { rtgeom_out->geoms = rtalloc(ctx, geoms_size * sizeof(RTGEOM*)); } } for ( j = 0; j < col->ngeoms; j++ ) { rtgeom_out->geoms[rtgeom_out->ngeoms] = col->geoms[j]; rtgeom_out->ngeoms++; } if ( col->type != mline->type ) { homogeneous = 0; } /* Shallow free the struct, leaving the geoms behind. */ if ( col->bbox ) rtfree(ctx, col->bbox); rtfree(ctx, col->geoms); rtfree(ctx, col); } } if ( rtgeom_out->bbox ) { rtgeom_drop_bbox(ctx, (RTGEOM*)rtgeom_out); rtgeom_add_bbox(ctx, (RTGEOM*)rtgeom_out); } if ( ! homogeneous ) { rtgeom_out->type = RTCOLLECTIONTYPE; } } if ( ! rtgeom_out || rtgeom_out->ngeoms == 0 ) /* Nothing left after clip. */ { return NULL; } return rtgeom_out; } /** * Take in a LINESTRING and return a MULTILINESTRING of those portions of the * LINESTRING between the from/to range for the specified ordinate (XYZM) */ RTCOLLECTION* rtline_clip_to_ordinate_range(const RTCTX *ctx, const RTLINE *line, char ordinate, double from, double to) { RTPOINTARRAY *pa_in = NULL; RTCOLLECTION *rtgeom_out = NULL; RTPOINTARRAY *dp = NULL; int i; int added_last_point = 0; RTPOINT4D *p = NULL, *q = NULL, *r = NULL; double ordinate_value_p = 0.0, ordinate_value_q = 0.0; char hasz = rtgeom_has_z(ctx, rtline_as_rtgeom(ctx, line)); char hasm = rtgeom_has_m(ctx, rtline_as_rtgeom(ctx, line)); char dims = RTFLAGS_NDIMS(line->flags); /* Null input, nothing we can do. */ if ( ! line ) { rterror(ctx, "Null input geometry."); return NULL; } /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } RTDEBUGF(4, "from = %g, to = %g, ordinate = %c", from, to, ordinate); RTDEBUGF(4, "%s", rtgeom_to_ewkt(ctx, (RTGEOM*)line)); /* Asking for an ordinate we don't have. Error. */ if ( (ordinate == 'Z' && ! hasz) || (ordinate == 'M' && ! hasm) ) { rterror(ctx, "Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims); return NULL; } /* Prepare our working point objects. */ p = rtalloc(ctx, sizeof(RTPOINT4D)); q = rtalloc(ctx, sizeof(RTPOINT4D)); r = rtalloc(ctx, sizeof(RTPOINT4D)); /* Construct a collection to hold our outputs. */ rtgeom_out = rtcollection_construct_empty(ctx, RTMULTILINETYPE, line->srid, hasz, hasm); /* Get our input point array */ pa_in = line->points; for ( i = 0; i < pa_in->npoints; i++ ) { RTDEBUGF(4, "Point #%d", i); RTDEBUGF(4, "added_last_point %d", added_last_point); if ( i > 0 ) { *q = *p; ordinate_value_q = ordinate_value_p; } rt_getPoint4d_p(ctx, pa_in, i, p); ordinate_value_p = rtpoint_get_ordinate(ctx, p, ordinate); RTDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p); RTDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q); /* Is this point inside the ordinate range? Yes. */ if ( ordinate_value_p >= from && ordinate_value_p <= to ) { RTDEBUGF(4, " inside ordinate range (%g, %g)", from, to); if ( ! added_last_point ) { RTDEBUG(4," new ptarray required"); /* We didn't add the previous point, so this is a new segment. * Make a new point array. */ dp = ptarray_construct_empty(ctx, hasz, hasm, 32); /* We're transiting into the range so add an interpolated * point at the range boundary. * If we're on a boundary and crossing from the far side, * we also need an interpolated point. */ if ( i > 0 && ( /* Don't try to interpolate if this is the first point */ ( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */ ( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */ ( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */ { double interpolation_value; (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(ctx, q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(ctx, dp, r, RT_FALSE); RTDEBUGF(4, "[0] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } } /* Add the current vertex to the point array. */ ptarray_append_point(ctx, dp, p, RT_FALSE); if ( ordinate_value_p == from || ordinate_value_p == to ) { added_last_point = 2; /* Added on boundary. */ } else { added_last_point = 1; /* Added inside range. */ } } /* Is this point inside the ordinate range? No. */ else { RTDEBUGF(4, " added_last_point (%d)", added_last_point); if ( added_last_point == 1 ) { /* We're transiting out of the range, so add an interpolated point * to the point array at the range boundary. */ double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(ctx, q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(ctx, dp, r, RT_FALSE); RTDEBUGF(4, " [1] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } else if ( added_last_point == 2 ) { /* We're out and the last point was on the boundary. * If the last point was the near boundary, nothing to do. * If it was the far boundary, we need an interpolated point. */ if ( from != to && ( (ordinate_value_q == from && ordinate_value_p > from) || (ordinate_value_q == to && ordinate_value_p < to) ) ) { double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); point_interpolate(ctx, q, p, r, hasz, hasm, ordinate, interpolation_value); ptarray_append_point(ctx, dp, r, RT_FALSE); RTDEBUGF(4, " [2] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } } else if ( i && ordinate_value_q < from && ordinate_value_p > to ) { /* We just hopped over the whole range, from bottom to top, * so we need to add *two* interpolated points! */ dp = ptarray_construct(ctx, hasz, hasm, 2); /* Interpolate lower point. */ point_interpolate(ctx, p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(ctx, dp, 0, r); /* Interpolate upper point. */ point_interpolate(ctx, p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(ctx, dp, 1, r); } else if ( i && ordinate_value_q > to && ordinate_value_p < from ) { /* We just hopped over the whole range, from top to bottom, * so we need to add *two* interpolated points! */ dp = ptarray_construct(ctx, hasz, hasm, 2); /* Interpolate upper point. */ point_interpolate(ctx, p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(ctx, dp, 0, r); /* Interpolate lower point. */ point_interpolate(ctx, p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(ctx, dp, 1, r); } /* We have an extant point-array, save it out to a multi-line. */ if ( dp ) { RTDEBUG(4, "saving pointarray to multi-line (1)"); /* Only one point, so we have to make an rtpoint to hold this * and set the overall output type to a generic collection. */ if ( dp->npoints == 1 ) { RTPOINT *opoint = rtpoint_construct(ctx, line->srid, NULL, dp); rtgeom_out->type = RTCOLLECTIONTYPE; rtgeom_out = rtcollection_add_rtgeom(ctx, rtgeom_out, rtpoint_as_rtgeom(ctx, opoint)); } else { RTLINE *oline = rtline_construct(ctx, line->srid, NULL, dp); rtgeom_out = rtcollection_add_rtgeom(ctx, rtgeom_out, rtline_as_rtgeom(ctx, oline)); } /* Pointarray is now owned by rtgeom_out, so drop reference to it */ dp = NULL; } added_last_point = 0; } } /* Still some points left to be saved out. */ if ( dp && dp->npoints > 0 ) { RTDEBUG(4, "saving pointarray to multi-line (2)"); RTDEBUGF(4, "dp->npoints == %d", dp->npoints); RTDEBUGF(4, "rtgeom_out->ngeoms == %d", rtgeom_out->ngeoms); if ( dp->npoints == 1 ) { RTPOINT *opoint = rtpoint_construct(ctx, line->srid, NULL, dp); rtgeom_out->type = RTCOLLECTIONTYPE; rtgeom_out = rtcollection_add_rtgeom(ctx, rtgeom_out, rtpoint_as_rtgeom(ctx, opoint)); } else { RTLINE *oline = rtline_construct(ctx, line->srid, NULL, dp); rtgeom_out = rtcollection_add_rtgeom(ctx, rtgeom_out, rtline_as_rtgeom(ctx, oline)); } /* Pointarray is now owned by rtgeom_out, so drop reference to it */ dp = NULL; } rtfree(ctx, p); rtfree(ctx, q); rtfree(ctx, r); if ( rtgeom_out->bbox && rtgeom_out->ngeoms > 0 ) { rtgeom_drop_bbox(ctx, (RTGEOM*)rtgeom_out); rtgeom_add_bbox(ctx, (RTGEOM*)rtgeom_out); } return rtgeom_out; } RTCOLLECTION* rtgeom_clip_to_ordinate_range(const RTCTX *ctx, const RTGEOM *rtin, char ordinate, double from, double to, double offset) { RTCOLLECTION *out_col; RTCOLLECTION *out_offset; int i; if ( ! rtin ) rterror(ctx, "rtgeom_clip_to_ordinate_range: null input geometry!"); switch ( rtin->type ) { case RTLINETYPE: out_col = rtline_clip_to_ordinate_range(ctx, (RTLINE*)rtin, ordinate, from, to); break; case RTMULTILINETYPE: out_col = rtmline_clip_to_ordinate_range(ctx, (RTMLINE*)rtin, ordinate, from, to); break; case RTMULTIPOINTTYPE: out_col = rtmpoint_clip_to_ordinate_range(ctx, (RTMPOINT*)rtin, ordinate, from, to); break; case RTPOINTTYPE: out_col = rtpoint_clip_to_ordinate_range(ctx, (RTPOINT*)rtin, ordinate, from, to); break; default: rterror(ctx, "This function does not accept %s geometries.", rttype_name(ctx, rtin->type)); return NULL;; } /* Stop if result is NULL */ if ( out_col == NULL ) rterror(ctx, "rtgeom_clip_to_ordinate_range clipping routine returned NULL"); /* Return if we aren't going to offset the result */ if ( FP_EQUALS(offset, 0.0) || rtgeom_is_empty(ctx, rtcollection_as_rtgeom(ctx, out_col)) ) return out_col; /* Construct a collection to hold our outputs. */ /* Things get ugly: GEOS offset drops Z's and M's so we have to drop ours */ out_offset = rtcollection_construct_empty(ctx, RTMULTILINETYPE, rtin->srid, 0, 0); /* Try and offset the linear portions of the return value */ for ( i = 0; i < out_col->ngeoms; i++ ) { int type = out_col->geoms[i]->type; if ( type == RTPOINTTYPE ) { rtnotice(ctx, "rtgeom_clip_to_ordinate_range cannot offset a clipped point"); continue; } else if ( type == RTLINETYPE ) { /* rtgeom_offsetcurve(ctx, line, offset, quadsegs, joinstyle (round), mitrelimit) */ RTGEOM *rtoff = rtgeom_offsetcurve(ctx, rtgeom_as_rtline(ctx, out_col->geoms[i]), offset, 8, 1, 5.0); if ( ! rtoff ) { rterror(ctx, "rtgeom_offsetcurve returned null"); } rtcollection_add_rtgeom(ctx, out_offset, rtoff); } else { rterror(ctx, "rtgeom_clip_to_ordinate_range found an unexpected type (%s) in the offset routine",rttype_name(ctx, type)); } } return out_offset; } RTCOLLECTION* rtgeom_locate_between(const RTCTX *ctx, const RTGEOM *rtin, double from, double to, double offset) { if ( ! rtgeom_has_m(ctx, rtin) ) rterror(ctx, "Input geometry does not have a measure dimension"); return rtgeom_clip_to_ordinate_range(ctx, rtin, 'M', from, to, offset); } double rtgeom_interpolate_point(const RTCTX *ctx, const RTGEOM *rtin, const RTPOINT *rtpt) { RTPOINT4D p, p_proj; double ret = 0.0; if ( ! rtin ) rterror(ctx, "rtgeom_interpolate_point: null input geometry!"); if ( ! rtgeom_has_m(ctx, rtin) ) rterror(ctx, "Input geometry does not have a measure dimension"); if ( rtgeom_is_empty(ctx, rtin) || rtpoint_is_empty(ctx, rtpt) ) rterror(ctx, "Input geometry is empty"); switch ( rtin->type ) { case RTLINETYPE: { RTLINE *rtline = rtgeom_as_rtline(ctx, rtin); rtpoint_getPoint4d_p(ctx, rtpt, &p); ret = ptarray_locate_point(ctx, rtline->points, &p, NULL, &p_proj); ret = p_proj.m; break; } default: rterror(ctx, "This function does not accept %s geometries.", rttype_name(ctx, rtin->type)); } return ret; } /* * Time of closest point of approach * * Given two vectors (p1-p2 and q1-q2) and * a time range (t1-t2) return the time in which * a point p is closest to a point q on their * respective vectors, and the actual points * * Here we use algorithm from softsurfer.com * that can be found here * http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm * * @param p0 start of first segment, will be set to actual * closest point of approach on segment. * @param p1 end of first segment * @param q0 start of second segment, will be set to actual * closest point of approach on segment. * @param q1 end of second segment * @param t0 start of travel time * @param t1 end of travel time * * @return time of closest point of approach * */ static double segments_tcpa(const RTCTX *ctx, RTPOINT4D* p0, const RTPOINT4D* p1, RTPOINT4D* q0, const RTPOINT4D* q1, double t0, double t1) { RTPOINT3DZ pv; /* velocity of p, aka u */ RTPOINT3DZ qv; /* velocity of q, aka v */ RTPOINT3DZ dv; /* velocity difference */ RTPOINT3DZ w0; /* vector between first points */ /* rtnotice(ctx, "FROM %g,%g,%g,%g -- %g,%g,%g,%g", p0->x, p0->y, p0->z, p0->m, p1->x, p1->y, p1->z, p1->m); rtnotice(ctx, " TO %g,%g,%g,%g -- %g,%g,%g,%g", q0->x, q0->y, q0->z, q0->m, q1->x, q1->y, q1->z, q1->m); */ /* PV aka U */ pv.x = ( p1->x - p0->x ); pv.y = ( p1->y - p0->y ); pv.z = ( p1->z - p0->z ); /*rtnotice(ctx, "PV: %g, %g, %g", pv.x, pv.y, pv.z);*/ /* QV aka V */ qv.x = ( q1->x - q0->x ); qv.y = ( q1->y - q0->y ); qv.z = ( q1->z - q0->z ); /*rtnotice(ctx, "QV: %g, %g, %g", qv.x, qv.y, qv.z);*/ dv.x = pv.x - qv.x; dv.y = pv.y - qv.y; dv.z = pv.z - qv.z; /*rtnotice(ctx, "DV: %g, %g, %g", dv.x, dv.y, dv.z);*/ double dv2 = DOT(dv,dv); /*rtnotice(ctx, "DOT: %g", dv2);*/ if ( dv2 == 0.0 ) { /* Distance is the same at any time, we pick the earliest */ return t0; } /* Distance at any given time, with t0 */ w0.x = ( p0->x - q0->x ); w0.y = ( p0->y - q0->y ); w0.z = ( p0->z - q0->z ); /*rtnotice(ctx, "W0: %g, %g, %g", w0.x, w0.y, w0.z);*/ /* Check that at distance dt w0 is distance */ /* This is the fraction of measure difference */ double t = -DOT(w0,dv) / dv2; /*rtnotice(ctx, "CLOSEST TIME (fraction): %g", t);*/ if ( t > 1.0 ) { /* Getting closer as we move to the end */ /*rtnotice(ctx, "Converging");*/ t = 1; } else if ( t < 0.0 ) { /*rtnotice(ctx, "Diverging");*/ t = 0; } /* Interpolate the actual points now */ p0->x += pv.x * t; p0->y += pv.y * t; p0->z += pv.z * t; q0->x += qv.x * t; q0->y += qv.y * t; q0->z += qv.z * t; t = t0 + (t1 - t0) * t; /*rtnotice(ctx, "CLOSEST TIME (real): %g", t);*/ return t; } static int ptarray_collect_mvals(const RTCTX *ctx, const RTPOINTARRAY *pa, double tmin, double tmax, double *mvals) { RTPOINT4D pbuf; int i, n=0; for (i=0; inpoints; ++i) { rt_getPoint4d_p(ctx, pa, i, &pbuf); /* could be optimized */ if ( pbuf.m >= tmin && pbuf.m <= tmax ) mvals[n++] = pbuf.m; } return n; } static int compare_double(const void *pa, const void *pb) { double a = *((double *)pa); double b = *((double *)pb); if ( a < b ) return -1; else if ( a > b ) return 1; else return 0; } /* Return number of elements in unique array */ static int uniq(const RTCTX *ctx, double *vals, int nvals) { int i, last=0; for (i=1; inpoints; i++ ) { rt_getPoint4d_p(ctx, pa, i, &p2); if ( segment_locate_along(ctx, &p1, &p2, m, 0, p) == RT_TRUE ) return i-1; /* found */ p1 = p2; } return -1; /* not found */ } double rtgeom_tcpa(const RTCTX *ctx, const RTGEOM *g1, const RTGEOM *g2, double *mindist) { RTLINE *l1, *l2; int i; const RTGBOX *gbox1, *gbox2; double tmin, tmax; double *mvals; int nmvals = 0; double mintime; double mindist2 = FLT_MAX; /* minimum distance, squared */ if ( ! rtgeom_has_m(ctx, g1) || ! rtgeom_has_m(ctx, g2) ) { rterror(ctx, "Both input geometries must have a measure dimension"); return -1; } l1 = rtgeom_as_rtline(ctx, g1); l2 = rtgeom_as_rtline(ctx, g2); if ( ! l1 || ! l2 ) { rterror(ctx, "Both input geometries must be linestrings"); return -1; } if ( l1->points->npoints < 2 || l2->points->npoints < 2 ) { rterror(ctx, "Both input lines must have at least 2 points"); return -1; } /* WARNING: these ranges may be wider than real ones */ gbox1 = rtgeom_get_bbox(ctx, g1); gbox2 = rtgeom_get_bbox(ctx, g2); assert(gbox1); /* or the npoints check above would have failed */ assert(gbox2); /* or the npoints check above would have failed */ /* * Find overlapping M range * WARNING: may be larger than the real one */ tmin = FP_MAX(gbox1->mmin, gbox2->mmin); tmax = FP_MIN(gbox1->mmax, gbox2->mmax); if ( tmax < tmin ) { RTDEBUG(1, "Inputs never exist at the same time"); return -2; } // rtnotice(ctx, "Min:%g, Max:%g", tmin, tmax); /* * Collect M values in common time range from inputs */ mvals = rtalloc(ctx, sizeof(double) * ( l1->points->npoints + l2->points->npoints ) ); /* TODO: also clip the lines ? */ nmvals = ptarray_collect_mvals(ctx, l1->points, tmin, tmax, mvals); nmvals += ptarray_collect_mvals(ctx, l2->points, tmin, tmax, mvals + nmvals); /* Sort values in ascending order */ qsort(mvals, nmvals, sizeof(double), compare_double); /* Remove duplicated values */ nmvals = uniq(ctx, mvals, nmvals); if ( nmvals < 2 ) { { /* there's a single time, must be that one... */ double t0 = mvals[0]; RTPOINT4D p0, p1; RTDEBUGF(1, "Inputs only exist both at a single time (%g)", t0); if ( mindist ) { if ( -1 == ptarray_locate_along_linear(ctx, l1->points, t0, &p0, 0) ) { rtfree(ctx, mvals); rterror(ctx, "Could not find point with M=%g on first geom", t0); return -1; } if ( -1 == ptarray_locate_along_linear(ctx, l2->points, t0, &p1, 0) ) { rtfree(ctx, mvals); rterror(ctx, "Could not find point with M=%g on second geom", t0); return -1; } *mindist = distance3d_pt_pt(ctx, (POINT3D*)&p0, (POINT3D*)&p1); } rtfree(ctx, mvals); return t0; } } /* * For each consecutive pair of measures, compute time of closest point * approach and actual distance between points at that time */ mintime = tmin; for (i=1; ipoints, t0, &p0, 0); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z); seg = ptarray_locate_along_linear(ctx, l1->points, t1, &p1, seg); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z); seg = ptarray_locate_along_linear(ctx, l2->points, t0, &q0, 0); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z); seg = ptarray_locate_along_linear(ctx, l2->points, t1, &q1, seg); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z); t = segments_tcpa(ctx, &p0, &p1, &q0, &q1, t0, t1); /* rtnotice(ctx, "Closest points: %g,%g,%g and %g,%g,%g at time %g", p0.x, p0.y, p0.z, q0.x, q0.y, q0.z, t); */ dist2 = ( q0.x - p0.x ) * ( q0.x - p0.x ) + ( q0.y - p0.y ) * ( q0.y - p0.y ) + ( q0.z - p0.z ) * ( q0.z - p0.z ); if ( dist2 < mindist2 ) { mindist2 = dist2; mintime = t; // rtnotice(ctx, "MINTIME: %g", mintime); } } /* * Release memory */ rtfree(ctx, mvals); if ( mindist ) { *mindist = sqrt(mindist2); } /*rtnotice(ctx, "MINDIST: %g", sqrt(mindist2));*/ return mintime; } int rtgeom_cpa_within(const RTCTX *ctx, const RTGEOM *g1, const RTGEOM *g2, double maxdist) { RTLINE *l1, *l2; int i; const RTGBOX *gbox1, *gbox2; double tmin, tmax; double *mvals; int nmvals = 0; double maxdist2 = maxdist * maxdist; int within = RT_FALSE; if ( ! rtgeom_has_m(ctx, g1) || ! rtgeom_has_m(ctx, g2) ) { rterror(ctx, "Both input geometries must have a measure dimension"); return RT_FALSE; } l1 = rtgeom_as_rtline(ctx, g1); l2 = rtgeom_as_rtline(ctx, g2); if ( ! l1 || ! l2 ) { rterror(ctx, "Both input geometries must be linestrings"); return RT_FALSE; } if ( l1->points->npoints < 2 || l2->points->npoints < 2 ) { /* TODO: return distance between these two points */ rterror(ctx, "Both input lines must have at least 2 points"); return RT_FALSE; } /* WARNING: these ranges may be wider than real ones */ gbox1 = rtgeom_get_bbox(ctx, g1); gbox2 = rtgeom_get_bbox(ctx, g2); assert(gbox1); /* or the npoints check above would have failed */ assert(gbox2); /* or the npoints check above would have failed */ /* * Find overlapping M range * WARNING: may be larger than the real one */ tmin = FP_MAX(gbox1->mmin, gbox2->mmin); tmax = FP_MIN(gbox1->mmax, gbox2->mmax); if ( tmax < tmin ) { RTDEBUG(1, "Inputs never exist at the same time"); return RT_FALSE; } // rtnotice(ctx, "Min:%g, Max:%g", tmin, tmax); /* * Collect M values in common time range from inputs */ mvals = rtalloc(ctx, sizeof(double) * ( l1->points->npoints + l2->points->npoints ) ); /* TODO: also clip the lines ? */ nmvals = ptarray_collect_mvals(ctx, l1->points, tmin, tmax, mvals); nmvals += ptarray_collect_mvals(ctx, l2->points, tmin, tmax, mvals + nmvals); /* Sort values in ascending order */ qsort(mvals, nmvals, sizeof(double), compare_double); /* Remove duplicated values */ nmvals = uniq(ctx, mvals, nmvals); if ( nmvals < 2 ) { /* there's a single time, must be that one... */ double t0 = mvals[0]; RTPOINT4D p0, p1; RTDEBUGF(1, "Inputs only exist both at a single time (%g)", t0); if ( -1 == ptarray_locate_along_linear(ctx, l1->points, t0, &p0, 0) ) { rtnotice(ctx, "Could not find point with M=%g on first geom", t0); return RT_FALSE; } if ( -1 == ptarray_locate_along_linear(ctx, l2->points, t0, &p1, 0) ) { rtnotice(ctx, "Could not find point with M=%g on second geom", t0); return RT_FALSE; } if ( distance3d_pt_pt(ctx, (POINT3D*)&p0, (POINT3D*)&p1) <= maxdist ) within = RT_TRUE; rtfree(ctx, mvals); return within; } /* * For each consecutive pair of measures, compute time of closest point * approach and actual distance between points at that time */ for (i=1; i= 1 double t; #endif RTPOINT4D p0, p1, q0, q1; int seg; double dist2; // rtnotice(ctx, "T %g-%g", t0, t1); seg = ptarray_locate_along_linear(ctx, l1->points, t0, &p0, 0); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 1: %g, %g, %g", t0, seg, p0.x, p0.y, p0.z); seg = ptarray_locate_along_linear(ctx, l1->points, t1, &p1, seg); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 1: %g, %g, %g", t1, seg, p1.x, p1.y, p1.z); seg = ptarray_locate_along_linear(ctx, l2->points, t0, &q0, 0); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 2: %g, %g, %g", t0, seg, q0.x, q0.y, q0.z); seg = ptarray_locate_along_linear(ctx, l2->points, t1, &q1, seg); if ( -1 == seg ) continue; /* possible, if RTGBOX is approximated */ // rtnotice(ctx, "Measure %g on segment %d of line 2: %g, %g, %g", t1, seg, q1.x, q1.y, q1.z); #if RTGEOM_DEBUG_LEVEL >= 1 t = #endif segments_tcpa(ctx, &p0, &p1, &q0, &q1, t0, t1); /* rtnotice(ctx, "Closest points: %g,%g,%g and %g,%g,%g at time %g", p0.x, p0.y, p0.z, q0.x, q0.y, q0.z, t); */ dist2 = ( q0.x - p0.x ) * ( q0.x - p0.x ) + ( q0.y - p0.y ) * ( q0.y - p0.y ) + ( q0.z - p0.z ) * ( q0.z - p0.z ); if ( dist2 <= maxdist2 ) { RTDEBUGF(1, "Within distance %g at time %g, breaking", sqrt(dist2), t); within = RT_TRUE; break; } } /* * Release memory */ rtfree(ctx, mvals); return within; } src/rtmcurve.c000066400000000000000000000020771271715413500136740ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" src/rtmline.c000066400000000000000000000071521271715413500134760ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" void rtmline_release(const RTCTX *ctx, RTMLINE *rtmline) { rtgeom_release(ctx, rtmline_as_rtgeom(ctx, rtmline)); } RTMLINE * rtmline_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTMLINE *ret = (RTMLINE*)rtcollection_construct_empty(ctx, RTMULTILINETYPE, srid, hasz, hasm); return ret; } RTMLINE* rtmline_add_rtline(const RTCTX *ctx, RTMLINE *mobj, const RTLINE *obj) { return (RTMLINE*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION*)mobj, (RTGEOM*)obj); } /** * Re-write the measure ordinate (or add one, if it isn't already there) interpolating * the measure between the supplied start and end values. */ RTMLINE* rtmline_measured_from_rtmline(const RTCTX *ctx, const RTMLINE *rtmline, double m_start, double m_end) { int i = 0; int hasm = 0, hasz = 0; double length = 0.0, length_so_far = 0.0; double m_range = m_end - m_start; RTGEOM **geoms = NULL; if ( rtmline->type != RTMULTILINETYPE ) { rterror(ctx, "rtmline_measured_from_lmwline: only multiline types supported"); return NULL; } hasz = RTFLAGS_GET_Z(rtmline->flags); hasm = 1; /* Calculate the total length of the mline */ for ( i = 0; i < rtmline->ngeoms; i++ ) { RTLINE *rtline = (RTLINE*)rtmline->geoms[i]; if ( rtline->points && rtline->points->npoints > 1 ) { length += ptarray_length_2d(ctx, rtline->points); } } if ( rtgeom_is_empty(ctx, (RTGEOM*)rtmline) ) { return (RTMLINE*)rtcollection_construct_empty(ctx, RTMULTILINETYPE, rtmline->srid, hasz, hasm); } geoms = rtalloc(ctx, sizeof(RTGEOM*) * rtmline->ngeoms); for ( i = 0; i < rtmline->ngeoms; i++ ) { double sub_m_start, sub_m_end; double sub_length = 0.0; RTLINE *rtline = (RTLINE*)rtmline->geoms[i]; if ( rtline->points && rtline->points->npoints > 1 ) { sub_length = ptarray_length_2d(ctx, rtline->points); } sub_m_start = (m_start + m_range * length_so_far / length); sub_m_end = (m_start + m_range * (length_so_far + sub_length) / length); geoms[i] = (RTGEOM*)rtline_measured_from_rtline(ctx, rtline, sub_m_start, sub_m_end); length_so_far += sub_length; } return (RTMLINE*)rtcollection_construct(ctx, rtmline->type, rtmline->srid, NULL, rtmline->ngeoms, geoms); } void rtmline_free(const RTCTX *ctx, RTMLINE *mline) { int i; if ( ! mline ) return; if ( mline->bbox ) rtfree(ctx, mline->bbox); for ( i = 0; i < mline->ngeoms; i++ ) if ( mline->geoms && mline->geoms[i] ) rtline_free(ctx, mline->geoms[i]); if ( mline->geoms ) rtfree(ctx, mline->geoms); rtfree(ctx, mline); } src/rtmpoint.c000066400000000000000000000064341271715413500137020ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" void rtmpoint_release(const RTCTX *ctx, RTMPOINT *rtmpoint) { rtgeom_release(ctx, rtmpoint_as_rtgeom(ctx, rtmpoint)); } RTMPOINT * rtmpoint_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTMPOINT *ret = (RTMPOINT*)rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, srid, hasz, hasm); return ret; } RTMPOINT* rtmpoint_add_rtpoint(const RTCTX *ctx, RTMPOINT *mobj, const RTPOINT *obj) { RTDEBUG(4, "Called"); return (RTMPOINT*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION*)mobj, (RTGEOM*)obj); } RTMPOINT * rtmpoint_construct(const RTCTX *ctx, int srid, const RTPOINTARRAY *pa) { int i; int hasz = ptarray_has_z(ctx, pa); int hasm = ptarray_has_m(ctx, pa); RTMPOINT *ret = (RTMPOINT*)rtcollection_construct_empty(ctx, RTMULTIPOINTTYPE, srid, hasz, hasm); for ( i = 0; i < pa->npoints; i++ ) { RTPOINT *rtp; RTPOINT4D p; rt_getPoint4d_p(ctx, pa, i, &p); rtp = rtpoint_make(ctx, srid, hasz, hasm, &p); rtmpoint_add_rtpoint(ctx, ret, rtp); } return ret; } void rtmpoint_free(const RTCTX *ctx, RTMPOINT *mpt) { int i; if ( ! mpt ) return; if ( mpt->bbox ) rtfree(ctx, mpt->bbox); for ( i = 0; i < mpt->ngeoms; i++ ) if ( mpt->geoms && mpt->geoms[i] ) rtpoint_free(ctx, mpt->geoms[i]); if ( mpt->geoms ) rtfree(ctx, mpt->geoms); rtfree(ctx, mpt); } RTGEOM* rtmpoint_remove_repeated_points(const RTCTX *ctx, const RTMPOINT *mpoint, double tolerance) { uint32_t nnewgeoms; uint32_t i, j; RTGEOM **newgeoms; newgeoms = rtalloc(ctx, sizeof(RTGEOM *)*mpoint->ngeoms); nnewgeoms = 0; for (i=0; ingeoms; ++i) { /* Brute force, may be optimized by building an index */ int seen=0; for (j=0; jgeoms[i]) ) { seen=1; break; } } if ( seen ) continue; newgeoms[nnewgeoms++] = (RTGEOM*)rtpoint_clone(ctx, mpoint->geoms[i]); } return (RTGEOM*)rtcollection_construct(ctx, mpoint->type, mpoint->srid, mpoint->bbox ? gbox_copy(ctx, mpoint->bbox) : NULL, nnewgeoms, newgeoms); } src/rtmpoly.c000066400000000000000000000036351271715413500135340ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" void rtmpoly_release(const RTCTX *ctx, RTMPOLY *rtmpoly) { rtgeom_release(ctx, rtmpoly_as_rtgeom(ctx, rtmpoly)); } RTMPOLY * rtmpoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTMPOLY *ret = (RTMPOLY*)rtcollection_construct_empty(ctx, RTMULTIPOLYGONTYPE, srid, hasz, hasm); return ret; } RTMPOLY* rtmpoly_add_rtpoly(const RTCTX *ctx, RTMPOLY *mobj, const RTPOLY *obj) { return (RTMPOLY*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION*)mobj, (RTGEOM*)obj); } void rtmpoly_free(const RTCTX *ctx, RTMPOLY *mpoly) { int i; if ( ! mpoly ) return; if ( mpoly->bbox ) rtfree(ctx, mpoly->bbox); for ( i = 0; i < mpoly->ngeoms; i++ ) if ( mpoly->geoms && mpoly->geoms[i] ) rtpoly_free(ctx, mpoly->geoms[i]); if ( mpoly->geoms ) rtfree(ctx, mpoly->geoms); rtfree(ctx, mpoly); } src/rtmsurface.c000066400000000000000000000021321271715413500141700ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" src/rtout_encoded_polyline.c000066400000000000000000000104601271715413500165710ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2014 Kashif Rasul and * **********************************************************************/ #include "stringbuffer.h" #include "librttopo_geom_internal.h" static char * rtline_to_encoded_polyline(const RTCTX *ctx, const RTLINE*, int precision); static char * rtmmpoint_to_encoded_polyline(const RTCTX *ctx, const RTMPOINT*, int precision); static char * pointarray_to_encoded_polyline(const RTCTX *ctx, const RTPOINTARRAY*, int precision); /* takes a GEOMETRY and returns an Encoded Polyline representation */ extern char * rtgeom_to_encoded_polyline(const RTCTX *ctx, const RTGEOM *geom, int precision) { int type = geom->type; switch (type) { case RTLINETYPE: return rtline_to_encoded_polyline(ctx, (RTLINE*)geom, precision); case RTMULTIPOINTTYPE: return rtmmpoint_to_encoded_polyline(ctx, (RTMPOINT*)geom, precision); default: rterror(ctx, "rtgeom_to_encoded_polyline: '%s' geometry type not supported", rttype_name(ctx, type)); return NULL; } } static char * rtline_to_encoded_polyline(const RTCTX *ctx, const RTLINE *line, int precision) { return pointarray_to_encoded_polyline(ctx, line->points, precision); } static char * rtmmpoint_to_encoded_polyline(const RTCTX *ctx, const RTMPOINT *mpoint, int precision) { RTLINE *line = rtline_from_rtmpoint(ctx, mpoint->srid, mpoint); char *encoded_polyline = rtline_to_encoded_polyline(ctx, line, precision); rtline_free(ctx, line); return encoded_polyline; } static char * pointarray_to_encoded_polyline(const RTCTX *ctx, const RTPOINTARRAY *pa, int precision) { int i; const RTPOINT2D *prevPoint; int *delta = rtalloc(ctx, 2*sizeof(int)*pa->npoints); char *encoded_polyline = NULL; stringbuffer_t *sb; double scale = pow(10,precision); /* Take the double value and multiply it by 1x10^percision, rounding the result */ prevPoint = rt_getPoint2d_cp(ctx, pa, 0); delta[0] = round(prevPoint->y*scale); delta[1] = round(prevPoint->x*scale); /* points only include the offset from the previous point */ for (i=1; inpoints; i++) { const RTPOINT2D *point = rt_getPoint2d_cp(ctx, pa, i); delta[2*i] = round(point->y*scale) - round(prevPoint->y*scale); delta[(2*i)+1] = round(point->x*scale) - round(prevPoint->x*scale); prevPoint = point; } /* value to binary: a negative value must be calculated using its two's complement */ for (i=0; inpoints*2; i++) { /* Left-shift the binary value one bit */ delta[i] <<= 1; /* if value is negative, invert this encoding */ if (delta[i] < 0) { delta[i] = ~(delta[i]); } } sb = stringbuffer_create(ctx); for (i=0; inpoints*2; i++) { int numberToEncode = delta[i]; while (numberToEncode >= 0x20) { /* Place the 5-bit chunks into reverse order or each value with 0x20 if another bit chunk follows and add 63*/ int nextValue = (0x20 | (numberToEncode & 0x1f)) + 63; stringbuffer_aprintf(ctx, sb, "%c", (char)nextValue); if(92 == nextValue) stringbuffer_aprintf(ctx, sb, "%c", (char)nextValue); /* Break the binary value out into 5-bit chunks */ numberToEncode >>= 5; } numberToEncode += 63; stringbuffer_aprintf(ctx, sb, "%c", (char)numberToEncode); if(92 == numberToEncode) stringbuffer_aprintf(ctx, sb, "%c", (char)numberToEncode); } rtfree(ctx, delta); encoded_polyline = stringbuffer_getstringcopy(ctx, sb); stringbuffer_destroy(ctx, sb); return encoded_polyline; } src/rtout_geojson.c000066400000000000000000000514571271715413500147340ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2003 Refractions Research Inc. * Copyright 2009-2010 Olivier Courtin * **********************************************************************/ #include "librttopo_geom_internal.h" #include /* strlen */ #include static char * asgeojson_point(const RTCTX *ctx, const RTPOINT *point, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_line(const RTCTX *ctx, const RTLINE *line, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_poly(const RTCTX *ctx, const RTPOLY *poly, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_multipoint(const RTCTX *ctx, const RTMPOINT *mpoint, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_multiline(const RTCTX *ctx, const RTMLINE *mline, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_multipolygon(const RTCTX *ctx, const RTMPOLY *mpoly, char *srs, RTGBOX *bbox, int precision); static char * asgeojson_collection(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, RTGBOX *bbox, int precision); static size_t asgeojson_geom_size(const RTCTX *ctx, const RTGEOM *geom, RTGBOX *bbox, int precision); static size_t asgeojson_geom_buf(const RTCTX *ctx, const RTGEOM *geom, char *output, RTGBOX *bbox, int precision); static size_t pointArray_to_geojson(const RTCTX *ctx, RTPOINTARRAY *pa, char *buf, int precision); static size_t pointArray_geojson_size(const RTCTX *ctx, RTPOINTARRAY *pa, int precision); /** * Takes a GEOMETRY and returns a GeoJson representation */ char * rtgeom_to_geojson(const RTCTX *ctx, const RTGEOM *geom, char *srs, int precision, int has_bbox) { int type = geom->type; RTGBOX *bbox = NULL; RTGBOX tmp; if ( precision > OUT_MAX_DOUBLE_PRECISION ) precision = OUT_MAX_DOUBLE_PRECISION; if (has_bbox) { /* Whether these are geography or geometry, the GeoJSON expects a cartesian bounding box */ rtgeom_calculate_gbox_cartesian(ctx, geom, &tmp); bbox = &tmp; } switch (type) { case RTPOINTTYPE: return asgeojson_point(ctx, (RTPOINT*)geom, srs, bbox, precision); case RTLINETYPE: return asgeojson_line(ctx, (RTLINE*)geom, srs, bbox, precision); case RTPOLYGONTYPE: return asgeojson_poly(ctx, (RTPOLY*)geom, srs, bbox, precision); case RTMULTIPOINTTYPE: return asgeojson_multipoint(ctx, (RTMPOINT*)geom, srs, bbox, precision); case RTMULTILINETYPE: return asgeojson_multiline(ctx, (RTMLINE*)geom, srs, bbox, precision); case RTMULTIPOLYGONTYPE: return asgeojson_multipolygon(ctx, (RTMPOLY*)geom, srs, bbox, precision); case RTCOLLECTIONTYPE: return asgeojson_collection(ctx, (RTCOLLECTION*)geom, srs, bbox, precision); default: rterror(ctx, "rtgeom_to_geojson: '%s' geometry type not supported", rttype_name(ctx, type)); } /* Never get here */ return NULL; } /** * Handle SRS */ static size_t asgeojson_srs_size(const RTCTX *ctx, char *srs) { int size; size = sizeof("'crs':{'type':'name',"); size += sizeof("'properties':{'name':''}},"); size += strlen(srs) * sizeof(char); return size; } static size_t asgeojson_srs_buf(const RTCTX *ctx, char *output, char *srs) { char *ptr = output; ptr += sprintf(ptr, "\"crs\":{\"type\":\"name\","); ptr += sprintf(ptr, "\"properties\":{\"name\":\"%s\"}},", srs); return (ptr-output); } /** * Handle Bbox */ static size_t asgeojson_bbox_size(const RTCTX *ctx, int hasz, int precision) { int size; if (!hasz) { size = sizeof("\"bbox\":[,,,],"); size += 2 * 2 * (OUT_MAX_DIGS_DOUBLE + precision); } else { size = sizeof("\"bbox\":[,,,,,],"); size += 2 * 3 * (OUT_MAX_DIGS_DOUBLE + precision); } return size; } static size_t asgeojson_bbox_buf(const RTCTX *ctx, char *output, RTGBOX *bbox, int hasz, int precision) { char *ptr = output; if (!hasz) ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->xmax, precision, bbox->ymax); else ptr += sprintf(ptr, "\"bbox\":[%.*f,%.*f,%.*f,%.*f,%.*f,%.*f],", precision, bbox->xmin, precision, bbox->ymin, precision, bbox->zmin, precision, bbox->xmax, precision, bbox->ymax, precision, bbox->zmax); return (ptr-output); } /** * Point Geometry */ static size_t asgeojson_point_size(const RTCTX *ctx, const RTPOINT *point, char *srs, RTGBOX *bbox, int precision) { int size; size = pointArray_geojson_size(ctx, point->point, precision); size += sizeof("{'type':'Point',"); size += sizeof("'coordinates':}"); if ( rtpoint_is_empty(ctx, point) ) size += 2; /* [] */ if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(point->flags), precision); return size; } static size_t asgeojson_point_buf(const RTCTX *ctx, const RTPOINT *point, char *srs, char *output, RTGBOX *bbox, int precision) { char *ptr = output; ptr += sprintf(ptr, "{\"type\":\"Point\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(point->flags), precision); ptr += sprintf(ptr, "\"coordinates\":"); if ( rtpoint_is_empty(ctx, point) ) ptr += sprintf(ptr, "[]"); ptr += pointArray_to_geojson(ctx, point->point, ptr, precision); ptr += sprintf(ptr, "}"); return (ptr-output); } static char * asgeojson_point(const RTCTX *ctx, const RTPOINT *point, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_point_size(ctx, point, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_point_buf(ctx, point, srs, output, bbox, precision); return output; } /** * Line Geometry */ static size_t asgeojson_line_size(const RTCTX *ctx, const RTLINE *line, char *srs, RTGBOX *bbox, int precision) { int size; size = sizeof("{'type':'LineString',"); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(line->flags), precision); size += sizeof("'coordinates':[]}"); size += pointArray_geojson_size(ctx, line->points, precision); return size; } static size_t asgeojson_line_buf(const RTCTX *ctx, const RTLINE *line, char *srs, char *output, RTGBOX *bbox, int precision) { char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"LineString\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(line->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); ptr += pointArray_to_geojson(ctx, line->points, ptr, precision); ptr += sprintf(ptr, "]}"); return (ptr-output); } static char * asgeojson_line(const RTCTX *ctx, const RTLINE *line, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_line_size(ctx, line, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_line_buf(ctx, line, srs, output, bbox, precision); return output; } /** * Polygon Geometry */ static size_t asgeojson_poly_size(const RTCTX *ctx, const RTPOLY *poly, char *srs, RTGBOX *bbox, int precision) { size_t size; int i; size = sizeof("{\"type\":\"Polygon\","); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(poly->flags), precision); size += sizeof("\"coordinates\":["); for (i=0, size=0; inrings; i++) { size += pointArray_geojson_size(ctx, poly->rings[i], precision); size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_poly_buf(const RTCTX *ctx, const RTPOLY *poly, char *srs, char *output, RTGBOX *bbox, int precision) { int i; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"Polygon\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(poly->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); for (i=0; inrings; i++) { if (i) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); ptr += pointArray_to_geojson(ctx, poly->rings[i], ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr-output); } static char * asgeojson_poly(const RTCTX *ctx, const RTPOLY *poly, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_poly_size(ctx, poly, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_poly_buf(ctx, poly, srs, output, bbox, precision); return output; } /** * Multipoint Geometry */ static size_t asgeojson_multipoint_size(const RTCTX *ctx, const RTMPOINT *mpoint, char *srs, RTGBOX *bbox, int precision) { RTPOINT * point; int size; int i; size = sizeof("{'type':'MultiPoint',"); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(mpoint->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0; ingeoms; i++) { point = mpoint->geoms[i]; size += pointArray_geojson_size(ctx, point->point, precision); } size += sizeof(",") * i; return size; } static size_t asgeojson_multipoint_buf(const RTCTX *ctx, const RTMPOINT *mpoint, char *srs, char *output, RTGBOX *bbox, int precision) { RTPOINT *point; int i; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiPoint\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(mpoint->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += sprintf(ptr, ","); point = mpoint->geoms[i]; ptr += pointArray_to_geojson(ctx, point->point, ptr, precision); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static char * asgeojson_multipoint(const RTCTX *ctx, const RTMPOINT *mpoint, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_multipoint_size(ctx, mpoint, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_multipoint_buf(ctx, mpoint, srs, output, bbox, precision); return output; } /** * Multiline Geometry */ static size_t asgeojson_multiline_size(const RTCTX *ctx, const RTMLINE *mline, char *srs, RTGBOX *bbox, int precision) { RTLINE * line; int size; int i; size = sizeof("{'type':'MultiLineString',"); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(mline->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0 ; ingeoms; i++) { line = mline->geoms[i]; size += pointArray_geojson_size(ctx, line->points, precision); size += sizeof("[]"); } size += sizeof(",") * i; return size; } static size_t asgeojson_multiline_buf(const RTCTX *ctx, const RTMLINE *mline, char *srs, char *output, RTGBOX *bbox, int precision) { RTLINE *line; int i; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiLineString\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(mline->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); line = mline->geoms[i]; ptr += pointArray_to_geojson(ctx, line->points, ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static char * asgeojson_multiline(const RTCTX *ctx, const RTMLINE *mline, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_multiline_size(ctx, mline, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_multiline_buf(ctx, mline, srs, output, bbox, precision); return output; } /** * MultiPolygon Geometry */ static size_t asgeojson_multipolygon_size(const RTCTX *ctx, const RTMPOLY *mpoly, char *srs, RTGBOX *bbox, int precision) { RTPOLY *poly; int size; int i, j; size = sizeof("{'type':'MultiPolygon',"); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(mpoly->flags), precision); size += sizeof("'coordinates':[]}"); for (i=0; i < mpoly->ngeoms; i++) { poly = mpoly->geoms[i]; for (j=0 ; j nrings ; j++) { size += pointArray_geojson_size(ctx, poly->rings[j], precision); size += sizeof("[]"); } size += sizeof("[]"); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_multipolygon_buf(const RTCTX *ctx, const RTMPOLY *mpoly, char *srs, char *output, RTGBOX *bbox, int precision) { RTPOLY *poly; int i, j; char *ptr=output; ptr += sprintf(ptr, "{\"type\":\"MultiPolygon\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(mpoly->flags), precision); ptr += sprintf(ptr, "\"coordinates\":["); for (i=0; ingeoms; i++) { if (i) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); poly = mpoly->geoms[i]; for (j=0 ; j < poly->nrings ; j++) { if (j) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "["); ptr += pointArray_to_geojson(ctx, poly->rings[j], ptr, precision); ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]"); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static char * asgeojson_multipolygon(const RTCTX *ctx, const RTMPOLY *mpoly, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_multipolygon_size(ctx, mpoly, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_multipolygon_buf(ctx, mpoly, srs, output, bbox, precision); return output; } /** * Collection Geometry */ static size_t asgeojson_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, RTGBOX *bbox, int precision) { int i; int size; RTGEOM *subgeom; size = sizeof("{'type':'GeometryCollection',"); if (srs) size += asgeojson_srs_size(ctx, srs); if (bbox) size += asgeojson_bbox_size(ctx, RTFLAGS_GET_Z(col->flags), precision); size += sizeof("'geometries':"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += asgeojson_geom_size(ctx, subgeom, NULL, precision); } size += sizeof(",") * i; size += sizeof("]}"); return size; } static size_t asgeojson_collection_buf(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, char *output, RTGBOX *bbox, int precision) { int i; char *ptr=output; RTGEOM *subgeom; ptr += sprintf(ptr, "{\"type\":\"GeometryCollection\","); if (srs) ptr += asgeojson_srs_buf(ctx, ptr, srs); if (col->ngeoms && bbox) ptr += asgeojson_bbox_buf(ctx, ptr, bbox, RTFLAGS_GET_Z(col->flags), precision); ptr += sprintf(ptr, "\"geometries\":["); for (i=0; ingeoms; i++) { if (i) ptr += sprintf(ptr, ","); subgeom = col->geoms[i]; ptr += asgeojson_geom_buf(ctx, subgeom, ptr, NULL, precision); } ptr += sprintf(ptr, "]}"); return (ptr - output); } static char * asgeojson_collection(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, RTGBOX *bbox, int precision) { char *output; int size; size = asgeojson_collection_size(ctx, col, srs, bbox, precision); output = rtalloc(ctx, size); asgeojson_collection_buf(ctx, col, srs, output, bbox, precision); return output; } static size_t asgeojson_geom_size(const RTCTX *ctx, const RTGEOM *geom, RTGBOX *bbox, int precision) { int type = geom->type; size_t size = 0; switch (type) { case RTPOINTTYPE: size = asgeojson_point_size(ctx, (RTPOINT*)geom, NULL, bbox, precision); break; case RTLINETYPE: size = asgeojson_line_size(ctx, (RTLINE*)geom, NULL, bbox, precision); break; case RTPOLYGONTYPE: size = asgeojson_poly_size(ctx, (RTPOLY*)geom, NULL, bbox, precision); break; case RTMULTIPOINTTYPE: size = asgeojson_multipoint_size(ctx, (RTMPOINT*)geom, NULL, bbox, precision); break; case RTMULTILINETYPE: size = asgeojson_multiline_size(ctx, (RTMLINE*)geom, NULL, bbox, precision); break; case RTMULTIPOLYGONTYPE: size = asgeojson_multipolygon_size(ctx, (RTMPOLY*)geom, NULL, bbox, precision); break; default: rterror(ctx, "GeoJson: geometry not supported."); } return size; } static size_t asgeojson_geom_buf(const RTCTX *ctx, const RTGEOM *geom, char *output, RTGBOX *bbox, int precision) { int type = geom->type; char *ptr=output; switch (type) { case RTPOINTTYPE: ptr += asgeojson_point_buf(ctx, (RTPOINT*)geom, NULL, ptr, bbox, precision); break; case RTLINETYPE: ptr += asgeojson_line_buf(ctx, (RTLINE*)geom, NULL, ptr, bbox, precision); break; case RTPOLYGONTYPE: ptr += asgeojson_poly_buf(ctx, (RTPOLY*)geom, NULL, ptr, bbox, precision); break; case RTMULTIPOINTTYPE: ptr += asgeojson_multipoint_buf(ctx, (RTMPOINT*)geom, NULL, ptr, bbox, precision); break; case RTMULTILINETYPE: ptr += asgeojson_multiline_buf(ctx, (RTMLINE*)geom, NULL, ptr, bbox, precision); break; case RTMULTIPOLYGONTYPE: ptr += asgeojson_multipolygon_buf(ctx, (RTMPOLY*)geom, NULL, ptr, bbox, precision); break; default: if (bbox) rtfree(ctx, bbox); rterror(ctx, "GeoJson: geometry not supported."); } return (ptr-output); } /* * Print an ordinate value using at most the given number of decimal digits * * The actual number of printed decimal digits may be less than the * requested ones if out of significant digits. * * The function will not write more than maxsize bytes, including the * terminating NULL. Returns the number of bytes that would have been * written if there was enough space (excluding terminating NULL). * So a return of ``bufsize'' or more means that the string was * truncated and misses a terminating NULL. * * TODO: export ? * */ static int rtprint_double(const RTCTX *ctx, double d, int maxdd, char *buf, size_t bufsize) { double ad = fabs(d); int ndd = ad < 1 ? 0 : floor(log10(ad))+1; /* non-decimal digits */ if (fabs(d) < OUT_MAX_DOUBLE) { if ( maxdd > (OUT_MAX_DOUBLE_PRECISION - ndd) ) maxdd -= ndd; return snprintf(buf, bufsize, "%.*f", maxdd, d); } else { return snprintf(buf, bufsize, "%g", d); } } static size_t pointArray_to_geojson(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int precision) { int i; char *ptr; #define BUFSIZE OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION char x[BUFSIZE+1]; char y[BUFSIZE+1]; char z[BUFSIZE+1]; assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); /* Ensure a terminating NULL at the end of buffers * so that we don't need to check for truncation * inprint_double */ x[BUFSIZE] = '\0'; y[BUFSIZE] = '\0'; z[BUFSIZE] = '\0'; ptr = output; /* TODO: rewrite this loop to be simpler and possibly quicker */ if (!RTFLAGS_GET_Z(pa->flags)) { for (i=0; inpoints; i++) { const RTPOINT2D *pt; pt = rt_getPoint2d_cp(ctx, pa, i); rtprint_double(ctx, pt->x, precision, x, BUFSIZE); trim_trailing_zeros(ctx, x); rtprint_double(ctx, pt->y, precision, y, BUFSIZE); trim_trailing_zeros(ctx, y); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s]", x, y); } } else { for (i=0; inpoints; i++) { const RTPOINT3DZ *pt; pt = rt_getPoint3dz_cp(ctx, pa, i); rtprint_double(ctx, pt->x, precision, x, BUFSIZE); trim_trailing_zeros(ctx, x); rtprint_double(ctx, pt->y, precision, y, BUFSIZE); trim_trailing_zeros(ctx, y); rtprint_double(ctx, pt->z, precision, z, BUFSIZE); trim_trailing_zeros(ctx, z); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s,%s]", x, y, z); } } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_geojson_size(const RTCTX *ctx, RTPOINTARRAY *pa, int precision) { assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); if (RTFLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(",")) * 2 * pa->npoints + sizeof(",[]"); return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(",,")) * 3 * pa->npoints + sizeof(",[]"); } src/rtout_gml.c000066400000000000000000001707511271715413500140460ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011 Sandro Santilli * Copyright 2010-2012 Oslandia * Copyright 2001-2003 Refractions Research Inc. * **********************************************************************/ /** * @file GML output routines. * **********************************************************************/ #include #include "librttopo_geom_internal.h" static size_t asgml2_point_size(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, const char *prefix); static char * asgml2_point(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, const char *prefix); static size_t asgml2_line_size(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, const char *prefix); static char * asgml2_line(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, const char *prefix); static size_t asgml2_poly_size(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, const char *prefix); static char * asgml2_poly(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, const char *prefix); static size_t asgml2_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix); static char * asgml2_multi(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix); static size_t asgml2_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix); static char * asgml2_collection(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix); static size_t pointArray_toGML2(const RTCTX *ctx, RTPOINTARRAY *pa, char *buf, int precision); static size_t asgml3_point_size(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_point(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_line_size(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_line(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_circstring(const RTCTX *ctx, const RTCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id ); static size_t asgml3_poly_size(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_poly(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, int opts, int is_patch, const char *prefix, const char *id); static char * asgml3_curvepoly(const RTCTX *ctx, const RTCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_triangle_size(const RTCTX *ctx, const RTTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_triangle(const RTCTX *ctx, const RTTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_multi(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_psurface(const RTCTX *ctx, const RTPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_tin(const RTCTX *ctx, const RTTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t asgml3_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_collection(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id); static char * asgml3_compound(const RTCTX *ctx, const RTCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ); static char * asgml3_multicurve(const RTCTX *ctx, const RTMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ); static char * asgml3_multisurface(const RTCTX *ctx, const RTMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id); static size_t pointArray_toGML3(const RTCTX *ctx, RTPOINTARRAY *pa, char *buf, int precision, int opts); static size_t pointArray_GMLsize(const RTCTX *ctx, RTPOINTARRAY *pa, int precision); static char * gbox_to_gml2(const RTCTX *ctx, const RTGBOX *bbox, const char *srs, int precision, const char *prefix) { int size; RTPOINT4D pt; RTPOINTARRAY *pa; char *ptr, *output; size_t prefixlen = strlen(prefix); if ( ! bbox ) { size = ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = rtalloc(ctx, size); ptr += sprintf(ptr, "<%sBox", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); ptr += sprintf(ptr, "/>"); return output; } pa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(bbox->flags), 0, 2); pt.x = bbox->xmin; pt.y = bbox->ymin; if (RTFLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmin; ptarray_append_point(ctx, pa, &pt, RT_TRUE); pt.x = bbox->xmax; pt.y = bbox->ymax; if (RTFLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmax; ptarray_append_point(ctx, pa, &pt, RT_TRUE); size = pointArray_GMLsize(ctx, pa, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = rtalloc(ctx, size); if ( srs ) ptr += sprintf(ptr, "<%sBox srsName=\"%s\">", prefix, srs); else ptr += sprintf(ptr, "<%sBox>", prefix); ptr += sprintf(ptr, "<%scoordinates>", prefix); ptr += pointArray_toGML2(ctx, pa, ptr, precision); ptr += sprintf(ptr, "", prefix, prefix); ptarray_free(ctx, pa); return output; } static char * gbox_to_gml3(const RTCTX *ctx, const RTGBOX *bbox, const char *srs, int precision, int opts, const char *prefix) { int size; RTPOINT4D pt; RTPOINTARRAY *pa; char *ptr, *output; size_t prefixlen = strlen(prefix); int dimension = 2; if ( ! bbox ) { size = ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); ptr = output = rtalloc(ctx, size); ptr += sprintf(ptr, "<%sEnvelope", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); ptr += sprintf(ptr, "/>"); return output; } if (RTFLAGS_GET_Z(bbox->flags)) dimension = 3; pa = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(bbox->flags), 0, 1); pt.x = bbox->xmin; pt.y = bbox->ymin; if (RTFLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmin; ptarray_append_point(ctx, pa, &pt, RT_TRUE); size = pointArray_GMLsize(ctx, pa, precision) * 2; size += ( sizeof("//") + (prefixlen*3) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); if ( IS_DIMS(opts) ) size += sizeof(" srsDimension=. ."); ptr = output = rtalloc(ctx, size); ptr += sprintf(ptr, "<%sEnvelope", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if ( IS_DIMS(opts) ) ptr += sprintf(ptr, " srsDimension=\"%d\"", dimension); ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%slowerCorner>", prefix); ptr += pointArray_toGML3(ctx, pa, ptr, precision, opts); ptr += sprintf(ptr, "", prefix); ptarray_remove_point(ctx, pa, 0); pt.x = bbox->xmax; pt.y = bbox->ymax; if (RTFLAGS_GET_Z(bbox->flags)) pt.z = bbox->zmax; ptarray_append_point(ctx, pa, &pt, RT_TRUE); ptr += sprintf(ptr, "<%supperCorner>", prefix); ptr += pointArray_toGML3(ctx, pa, ptr, precision, opts); ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); ptarray_free(ctx, pa); return output; } extern char * rtgeom_extent_to_gml2(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, const char *prefix) { const RTGBOX* bbox = rtgeom_get_bbox(ctx, geom); /* if ( ! bbox ) { rterror(ctx, "rtgeom_extent_to_gml2: empty geometry doesn't have a bounding box"); return NULL; } */ char *ret = gbox_to_gml2(ctx, bbox, srs, precision, prefix); return ret; } extern char * rtgeom_extent_to_gml3(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, int opts, const char *prefix) { const RTGBOX* bbox = rtgeom_get_bbox(ctx, geom); /* if ( ! bbox ) { rterror(ctx, "rtgeom_extent_to_gml3: empty geometry doesn't have a bounding box"); return NULL; } */ return gbox_to_gml3(ctx, bbox, srs, precision, opts, prefix); } /** * @brief VERSION GML 2 * takes a GEOMETRY and returns a GML2 representation */ extern char * rtgeom_to_gml2(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, const char* prefix) { int type = geom->type; /* Return null for empty (#1377) */ if ( rtgeom_is_empty(ctx, geom) ) return NULL; switch (type) { case RTPOINTTYPE: return asgml2_point(ctx, (RTPOINT*)geom, srs, precision, prefix); case RTLINETYPE: return asgml2_line(ctx, (RTLINE*)geom, srs, precision, prefix); case RTPOLYGONTYPE: return asgml2_poly(ctx, (RTPOLY*)geom, srs, precision, prefix); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: return asgml2_multi(ctx, (RTCOLLECTION*)geom, srs, precision, prefix); case RTCOLLECTIONTYPE: return asgml2_collection(ctx, (RTCOLLECTION*)geom, srs, precision, prefix); case RTTRIANGLETYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: rterror(ctx, "Cannot convert %s to GML2. Try ST_AsGML(3, ) to generate GML3.", rttype_name(ctx, type)); return NULL; default: rterror(ctx, "rtgeom_to_gml2: '%s' geometry type not supported", rttype_name(ctx, type)); return NULL; } } static size_t asgml2_point_size(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, const char* prefix) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(ctx, point->point, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); return size; } static size_t asgml2_point_buf(const RTCTX *ctx, const RTPOINT *point, const char *srs, char *output, int precision, const char* prefix) { char *ptr = output; ptr += sprintf(ptr, "<%sPoint", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if ( rtpoint_is_empty(ctx, point) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%scoordinates>", prefix); ptr += pointArray_toGML2(ctx, point->point, ptr, precision); ptr += sprintf(ptr, "", prefix, prefix); return (ptr-output); } static char * asgml2_point(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_point_size(ctx, point, srs, precision, prefix); output = rtalloc(ctx, size); asgml2_point_buf(ctx, point, srs, output, precision, prefix); return output; } static size_t asgml2_line_size(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, const char *prefix) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(ctx, line->points, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); return size; } static size_t asgml2_line_buf(const RTCTX *ctx, const RTLINE *line, const char *srs, char *output, int precision, const char *prefix) { char *ptr=output; ptr += sprintf(ptr, "<%sLineString", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if ( rtline_is_empty(ctx, line) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%scoordinates>", prefix); ptr += pointArray_toGML2(ctx, line->points, ptr, precision); ptr += sprintf(ptr, "", prefix, prefix); return (ptr-output); } static char * asgml2_line(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_line_size(ctx, line, srs, precision, prefix); output = rtalloc(ctx, size); asgml2_line_buf(ctx, line, srs, output, precision, prefix); return output; } static size_t asgml2_poly_size(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, const char *prefix) { size_t size; int i; size_t prefixlen = strlen(prefix); size = sizeof("") + prefixlen*2; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); if ( rtpoly_is_empty(ctx, poly) ) return size; size += ( sizeof("/") + ( prefixlen*3) ) * 2; size += ( sizeof("/") + ( prefixlen*2) ) * 2 * poly->nrings; for (i=0; inrings; i++) size += pointArray_GMLsize(ctx, poly->rings[i], precision); return size; } static size_t asgml2_poly_buf(const RTCTX *ctx, const RTPOLY *poly, const char *srs, char *output, int precision, const char *prefix) { int i; char *ptr=output; ptr += sprintf(ptr, "<%sPolygon", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if ( rtpoly_is_empty(ctx, poly) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%souterBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); ptr += pointArray_toGML2(ctx, poly->rings[0], ptr, precision); ptr += sprintf(ptr, "", prefix, prefix, prefix); for (i=1; inrings; i++) { ptr += sprintf(ptr, "<%sinnerBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); ptr += pointArray_toGML2(ctx, poly->rings[i], ptr, precision); ptr += sprintf(ptr, "", prefix, prefix, prefix); } ptr += sprintf(ptr, "", prefix); return (ptr-output); } static char * asgml2_poly(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, const char *prefix) { char *output; int size; size = asgml2_poly_size(ctx, poly, srs, precision, prefix); output = rtalloc(ctx, size); asgml2_poly_buf(ctx, poly, srs, output, precision, prefix); return output; } /* * Compute max size required for GML version of this * inspected geometry. Will recurse when needed. * Don't call this with single-geoms inspected. */ static size_t asgml2_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix) { int i; size_t size; size_t prefixlen = strlen(prefix); RTGEOM *subgeom; /* the longest possible multi version */ size = sizeof(""); size += 2*prefixlen; if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_point_size(ctx, (RTPOINT*)subgeom, 0, precision, prefix); } else if (subgeom->type == RTLINETYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_line_size(ctx, (RTLINE*)subgeom, 0, precision, prefix); } else if (subgeom->type == RTPOLYGONTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml2_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, prefix); } } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml2_multi_buf(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, char *output, int precision, const char *prefix) { int type = col->type; char *ptr, *gmltype; int i; RTGEOM *subgeom; ptr = output; gmltype=""; if (type == RTMULTIPOINTTYPE) gmltype = "MultiPoint"; else if (type == RTMULTILINETYPE) gmltype = "MultiLineString"; else if (type == RTMULTIPOLYGONTYPE) gmltype = "MultiPolygon"; /* Open outmost tag */ ptr += sprintf(ptr, "<%s%s", prefix, gmltype); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (!col->ngeoms) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { ptr += sprintf(ptr, "<%spointMember>", prefix); ptr += asgml2_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, prefix); ptr += sprintf(ptr, "", prefix); } else if (subgeom->type == RTLINETYPE) { ptr += sprintf(ptr, "<%slineStringMember>", prefix); ptr += asgml2_line_buf(ctx, (RTLINE*)subgeom, 0, ptr, precision, prefix); ptr += sprintf(ptr, "", prefix); } else if (subgeom->type == RTPOLYGONTYPE) { ptr += sprintf(ptr, "<%spolygonMember>", prefix); ptr += asgml2_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, prefix); ptr += sprintf(ptr, "", prefix); } } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix, gmltype); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml2_multi(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix) { char *gml; size_t size; size = asgml2_multi_size(ctx, col, srs, precision, prefix); gml = rtalloc(ctx, size); asgml2_multi_buf(ctx, col, srs, gml, precision, prefix); return gml; } /* * Don't call this with single-geoms! */ static size_t asgml2_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix) { int i; size_t size; size_t prefixlen = strlen(prefix); RTGEOM *subgeom; size = sizeof(""); size += (prefixlen * 2); if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += ( sizeof("/") + prefixlen ) * 2; if ( subgeom->type == RTPOINTTYPE) { size += asgml2_point_size(ctx, (RTPOINT*)subgeom, 0, precision, prefix); } else if ( subgeom->type == RTLINETYPE) { size += asgml2_line_size(ctx, (RTLINE*)subgeom, 0, precision, prefix); } else if ( subgeom->type == RTPOLYGONTYPE) { size += asgml2_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, prefix); } else if ( rtgeom_is_collection(ctx, subgeom) ) { size += asgml2_collection_size(ctx, (RTCOLLECTION*)subgeom, 0, precision, prefix); } else rterror(ctx, "asgml2_collection_size: Unable to process geometry type!"); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml2_collection_buf(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, char *output, int precision, const char *prefix) { char *ptr; int i; RTGEOM *subgeom; ptr = output; /* Open outmost tag */ ptr += sprintf(ptr, "<%sMultiGeometry", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (!col->ngeoms) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; ptr += sprintf(ptr, "<%sgeometryMember>", prefix); if (subgeom->type == RTPOINTTYPE) { ptr += asgml2_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, prefix); } else if (subgeom->type == RTLINETYPE) { ptr += asgml2_line_buf(ctx, (RTLINE*)subgeom, 0, ptr, precision, prefix); } else if (subgeom->type == RTPOLYGONTYPE) { ptr += asgml2_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, prefix); } else if (rtgeom_is_collection(ctx, subgeom)) { if (subgeom->type == RTCOLLECTIONTYPE) ptr += asgml2_collection_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, prefix); else ptr += asgml2_multi_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, prefix); } ptr += sprintf(ptr, "", prefix); } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml2_collection(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, const char *prefix) { char *gml; size_t size; size = asgml2_collection_size(ctx, col, srs, precision, prefix); gml = rtalloc(ctx, size); asgml2_collection_buf(ctx, col, srs, gml, precision, prefix); return gml; } static size_t pointArray_toGML2(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int precision) { int i; char *ptr; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; ptr = output; if ( ! RTFLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { const RTPOINT2D *pt; pt = rt_getPoint2d_cp(ctx, pa, i); if (fabs(pt->x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt->x); else sprintf(x, "%g", pt->x); trim_trailing_zeros(ctx, x); if (fabs(pt->y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt->y); else sprintf(y, "%g", pt->y); trim_trailing_zeros(ctx, y); if ( i ) ptr += sprintf(ptr, " "); ptr += sprintf(ptr, "%s,%s", x, y); } } else { for (i=0; inpoints; i++) { const RTPOINT3DZ *pt; pt = rt_getPoint3dz_cp(ctx, pa, i); if (fabs(pt->x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt->x); else sprintf(x, "%g", pt->x); trim_trailing_zeros(ctx, x); if (fabs(pt->y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt->y); else sprintf(y, "%g", pt->y); trim_trailing_zeros(ctx, y); if (fabs(pt->z) < OUT_MAX_DOUBLE) sprintf(z, "%.*f", precision, pt->z); else sprintf(z, "%g", pt->z); trim_trailing_zeros(ctx, z); if ( i ) ptr += sprintf(ptr, " "); ptr += sprintf(ptr, "%s,%s,%s", x, y, z); } } return ptr-output; } /* * VERSION GML 3.1.1 */ /* takes a GEOMETRY and returns a GML representation */ extern char * rtgeom_to_gml3(const RTCTX *ctx, const RTGEOM *geom, const char *srs, int precision, int opts, const char *prefix, const char *id) { int type = geom->type; /* Return null for empty (#1377) */ if ( rtgeom_is_empty(ctx, geom) ) return NULL; switch (type) { case RTPOINTTYPE: return asgml3_point(ctx, (RTPOINT*)geom, srs, precision, opts, prefix, id); case RTLINETYPE: return asgml3_line(ctx, (RTLINE*)geom, srs, precision, opts, prefix, id); case RTCIRCSTRINGTYPE: return asgml3_circstring(ctx, (RTCIRCSTRING*)geom, srs, precision, opts, prefix, id ); case RTPOLYGONTYPE: return asgml3_poly(ctx, (RTPOLY*)geom, srs, precision, opts, 0, prefix, id); case RTCURVEPOLYTYPE: return asgml3_curvepoly(ctx, (RTCURVEPOLY*)geom, srs, precision, opts, prefix, id); case RTTRIANGLETYPE: return asgml3_triangle(ctx, (RTTRIANGLE*)geom, srs, precision, opts, prefix, id); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: return asgml3_multi(ctx, (RTCOLLECTION*)geom, srs, precision, opts, prefix, id); case RTPOLYHEDRALSURFACETYPE: return asgml3_psurface(ctx, (RTPSURFACE*)geom, srs, precision, opts, prefix, id); case RTTINTYPE: return asgml3_tin(ctx, (RTTIN*)geom, srs, precision, opts, prefix, id); case RTCOLLECTIONTYPE: return asgml3_collection(ctx, (RTCOLLECTION*)geom, srs, precision, opts, prefix, id); case RTCOMPOUNDTYPE: return asgml3_compound(ctx, (RTCOMPOUND*)geom, srs, precision, opts, prefix, id ); case RTMULTICURVETYPE: return asgml3_multicurve(ctx, (RTMCURVE*)geom, srs, precision, opts, prefix, id ); case RTMULTISURFACETYPE: return asgml3_multisurface(ctx, (RTMSURFACE*)geom, srs, precision, opts, prefix, id ); default: rterror(ctx, "rtgeom_to_gml3: '%s' geometry type not supported", rttype_name(ctx, type)); return NULL; } } static size_t asgml3_point_size(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(ctx, point->point, precision); size += ( sizeof("/") + (prefixlen*2) ) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_point_buf(const RTCTX *ctx, const RTPOINT *point, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr = output; int dimension=2; if (RTFLAGS_GET_Z(point->flags)) dimension = 3; ptr += sprintf(ptr, "<%sPoint", prefix); if ( srs ) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if ( id ) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); if ( rtpoint_is_empty(ctx, point) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); if (IS_DIMS(opts)) ptr += sprintf(ptr, "<%spos srsDimension=\"%d\">", prefix, dimension); else ptr += sprintf(ptr, "<%spos>", prefix); ptr += pointArray_toGML3(ctx, point->point, ptr, precision, opts); ptr += sprintf(ptr, "", prefix, prefix); return (ptr-output); } static char * asgml3_point(const RTCTX *ctx, const RTPOINT *point, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_point_size(ctx, point, srs, precision, opts, prefix, id); output = rtalloc(ctx, size); asgml3_point_buf(ctx, point, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_line_size(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size; size_t prefixlen = strlen(prefix); size = pointArray_GMLsize(ctx, line->points, precision); if ( opts & RT_GML_SHORTLINE ) { size += ( sizeof("/") + ( prefixlen * 2 ) ) * 2; } else { size += ( sizeof("/") + ( prefixlen * 4 ) ) * 2; } if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_line_buf(const RTCTX *ctx, const RTLINE *line, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr=output; int dimension=2; int shortline = ( opts & RT_GML_SHORTLINE ); if (RTFLAGS_GET_Z(line->flags)) dimension = 3; if ( shortline ) { ptr += sprintf(ptr, "<%sLineString", prefix); } else { ptr += sprintf(ptr, "<%sCurve", prefix); } if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); if ( rtline_is_empty(ctx, line) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); if ( ! shortline ) { ptr += sprintf(ptr, "<%ssegments>", prefix); ptr += sprintf(ptr, "<%sLineStringSegment>", prefix); } if (IS_DIMS(opts)) { ptr += sprintf(ptr, "<%sposList srsDimension=\"%d\">", prefix, dimension); } else { ptr += sprintf(ptr, "<%sposList>", prefix); } ptr += pointArray_toGML3(ctx, line->points, ptr, precision, opts); ptr += sprintf(ptr, "", prefix); if ( shortline ) { ptr += sprintf(ptr, "", prefix); } else { ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); } return (ptr-output); } static char * asgml3_line(const RTCTX *ctx, const RTLINE *line, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_line_size(ctx, line, srs, precision, opts, prefix, id); output = rtalloc(ctx, size); asgml3_line_buf(ctx, line, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_circstring_size(const RTCTX *ctx, const RTCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id) { int size = pointArray_GMLsize(ctx, circ->points, precision ); size_t prefixlen = strlen(prefix); size += 2 * ( sizeof( "/" ) + 2 * prefixlen ); size += 2 * ( sizeof( "/" ) + 2 * prefixlen ); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); return size; } static size_t asgml3_circstring_buf(const RTCTX *ctx, const RTCIRCSTRING *circ, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char* ptr = output; int dimension=2; if (RTFLAGS_GET_Z(circ->flags)) { dimension = 3; } ptr += sprintf(ptr, "<%sCurve", prefix); if (srs) { ptr += sprintf(ptr, " srsName=\"%s\"", srs); } if (id) { ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); } ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%ssegments>", prefix); ptr += sprintf(ptr, "<%sArcString>", prefix); ptr += sprintf(ptr, "<%sposList", prefix); if (IS_DIMS(opts)) { ptr += sprintf(ptr, " srsDimension=\"%d\"", dimension); } ptr += sprintf(ptr, ">"); ptr += pointArray_toGML3(ctx, circ->points, ptr, precision, opts); ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); ptr += sprintf(ptr, "", prefix); return (ptr-output); } static char * asgml3_circstring(const RTCTX *ctx, const RTCIRCSTRING *circ, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char *output; int size; size = asgml3_circstring_size(ctx, circ, srs, precision, opts, prefix, id); output = rtalloc(ctx, size ); asgml3_circstring_buf(ctx, circ, srs, output, precision, opts, prefix, id); return output; } static size_t asgml3_poly_size(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t size; size_t prefixlen = strlen(prefix); int i; size = ( sizeof("///") + (prefixlen*3) ) * 2; size += ( sizeof("//") + (prefixlen*2) ) * 2 * (poly->nrings - 1); size += ( sizeof("") + (prefixlen*2) ) * poly->nrings; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'") * poly->nrings; for (i=0; inrings; i++) size += pointArray_GMLsize(ctx, poly->rings[i], precision); return size; } static size_t asgml3_poly_buf(const RTCTX *ctx, const RTPOLY *poly, const char *srs, char *output, int precision, int opts, int is_patch, const char *prefix, const char *id) { int i; char *ptr=output; int dimension=2; if (RTFLAGS_GET_Z(poly->flags)) dimension = 3; if (is_patch) { ptr += sprintf(ptr, "<%sPolygonPatch", prefix); } else { ptr += sprintf(ptr, "<%sPolygon", prefix); } if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); if ( rtpoly_is_empty(ctx, poly) ) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%sexterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += sprintf(ptr, "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += sprintf(ptr, "<%sposList>", prefix); ptr += pointArray_toGML3(ctx, poly->rings[0], ptr, precision, opts); ptr += sprintf(ptr, "", prefix, prefix, prefix); for (i=1; inrings; i++) { ptr += sprintf(ptr, "<%sinterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += sprintf(ptr, "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += sprintf(ptr, "<%sposList>", prefix); ptr += pointArray_toGML3(ctx, poly->rings[i], ptr, precision, opts); ptr += sprintf(ptr, "", prefix, prefix, prefix); } if (is_patch) ptr += sprintf(ptr, "", prefix); else ptr += sprintf(ptr, "", prefix); return (ptr-output); } static char * asgml3_poly(const RTCTX *ctx, const RTPOLY *poly, const char *srs, int precision, int opts, int is_patch, const char *prefix, const char *id) { char *output; int size; size = asgml3_poly_size(ctx, poly, srs, precision, opts, prefix, id); output = rtalloc(ctx, size); asgml3_poly_buf(ctx, poly, srs, output, precision, opts, is_patch, prefix, id); return output; } static size_t asgml3_compound_size(const RTCTX *ctx, const RTCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ) { int i; size_t size; RTGEOM *subgeom; size_t prefixlen = strlen(prefix); size = ( sizeof( "" ) + 2 * prefixlen ); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); size += ( sizeof("") + 2 * prefixlen ); for(i= 0; i < col->ngeoms; ++i ) { subgeom = col->geoms[i]; if ( subgeom->type == RTLINETYPE ) { size += sizeof( "points, precision ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { size += sizeof( "") + 4 * prefixlen; size += pointArray_GMLsize(ctx, ((RTCIRCSTRING*)subgeom)->points, precision ); } else { continue; } if (IS_DIMS(opts)) { size += sizeof(" srsDimension='x'"); } } return size; } static size_t asgml3_compound_buf(const RTCTX *ctx, const RTCOMPOUND *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { RTGEOM *subgeom; int i; char* ptr = output; int dimension=2; if (RTFLAGS_GET_Z(col->flags)) { dimension = 3; } ptr += sprintf( ptr, "<%sCurve", prefix ); if (srs) { ptr += sprintf(ptr, " srsName=\"%s\"", srs); } if (id) { ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id ); } ptr += sprintf( ptr, ">" ); ptr += sprintf( ptr, "<%ssegments>", prefix ); for( i = 0; i < col->ngeoms; ++i ) { subgeom = col->geoms[i]; if( subgeom->type != RTLINETYPE && subgeom->type != RTCIRCSTRINGTYPE ) { continue; } if ( subgeom->type == RTLINETYPE ) { ptr += sprintf( ptr, "<%sLineStringSegment><%sposList", prefix, prefix ); if (IS_DIMS(opts)) { ptr += sprintf(ptr, " srsDimension=\"%d\"", dimension); } ptr += sprintf(ptr, ">"); ptr += pointArray_toGML3(ctx, ((RTCIRCSTRING*)subgeom)->points, ptr, precision, opts); ptr += sprintf( ptr, "", prefix, prefix ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { ptr += sprintf( ptr, "<%sArcString><%sposList" , prefix, prefix ); if (IS_DIMS(opts)) { ptr += sprintf(ptr, " srsDimension=\"%d\"", dimension); } ptr += sprintf(ptr, ">"); ptr += pointArray_toGML3(ctx, ((RTLINE*)subgeom)->points, ptr, precision, opts); ptr += sprintf( ptr, "", prefix, prefix ); } } ptr += sprintf( ptr, "", prefix ); ptr += sprintf( ptr, "", prefix ); return ( ptr - output ); } static char * asgml3_compound(const RTCTX *ctx, const RTCOMPOUND *col, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char* gml; size_t size; size = asgml3_compound_size(ctx, col, srs, precision, opts, prefix, id ); gml = rtalloc(ctx, size ); asgml3_compound_buf(ctx, col, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_curvepoly_size(const RTCTX *ctx, const RTCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t prefixlen = strlen(prefix); RTGEOM* subgeom; size_t size = sizeof( "nrings; ++i ) { if( i == 0 ) { size += sizeof( "" ) + 2 * prefixlen; } else { size += sizeof( "" ) + 2 * prefixlen; } subgeom = poly->rings[i]; if ( subgeom->type == RTLINETYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("points, precision ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("") + 2 * prefixlen; size += asgml3_circstring_size(ctx, (RTCIRCSTRING*)subgeom, srs, precision, opts, prefix, id); } else if( subgeom->type == RTCOMPOUNDTYPE ) { size += sizeof("") + 2 * prefixlen; size += sizeof("") + 2 * prefixlen; size += asgml3_compound_size(ctx, (RTCOMPOUND*)subgeom, srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_curvepoly_buf(const RTCTX *ctx, const RTCURVEPOLY* poly, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { int i; RTGEOM* subgeom; char *ptr=output; int dimension=2; if (RTFLAGS_GET_Z(poly->flags)) { dimension = 3; } ptr += sprintf( ptr, "<%sPolygon", prefix ); if (srs) { ptr += sprintf(ptr, " srsName=\"%s\"", srs); } if (id) { ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id ); } ptr += sprintf(ptr, ">"); for( i = 0; i < poly->nrings; ++i ) { if( i == 0 ) { ptr += sprintf( ptr, "<%sexterior>", prefix); } else { ptr += sprintf( ptr, "<%sinterior>", prefix); } subgeom = poly->rings[i]; if ( subgeom->type == RTLINETYPE ) { ptr += sprintf( ptr, "<%sLinearRing>", prefix ); ptr += sprintf( ptr, "<%sposList", prefix ); if (IS_DIMS(opts)) { ptr += sprintf(ptr, " srsDimension=\"%d\"", dimension); } ptr += sprintf( ptr, ">" ); ptr += pointArray_toGML3(ctx, ((RTLINE*)subgeom)->points, ptr, precision, opts); ptr += sprintf( ptr, "", prefix ); ptr += sprintf( ptr, "", prefix ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { ptr += sprintf( ptr, "<%sRing>", prefix ); ptr += sprintf( ptr, "<%scurveMember>", prefix ); ptr += asgml3_circstring_buf(ctx, (RTCIRCSTRING*)subgeom, srs, ptr, precision, opts, prefix, id ); ptr += sprintf( ptr, "", prefix ); ptr += sprintf( ptr, "", prefix ); } else if( subgeom->type == RTCOMPOUNDTYPE ) { ptr += sprintf( ptr, "<%sRing>", prefix ); ptr += sprintf( ptr, "<%scurveMember>", prefix ); ptr += asgml3_compound_buf(ctx, (RTCOMPOUND*)subgeom, srs, ptr, precision, opts, prefix, id ); ptr += sprintf( ptr, "", prefix ); ptr += sprintf( ptr, "", prefix ); } if( i == 0 ) { ptr += sprintf( ptr, "", prefix); } else { ptr += sprintf( ptr, "", prefix); } } ptr += sprintf( ptr, "", prefix ); return (ptr - output); } static char* asgml3_curvepoly(const RTCTX *ctx, const RTCURVEPOLY* poly, const char *srs, int precision, int opts, const char *prefix, const char *id) { char* gml; size_t size; size = asgml3_curvepoly_size(ctx, poly, srs, precision, opts, prefix, id ); gml = rtalloc(ctx, size ); asgml3_curvepoly_buf(ctx, poly, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_triangle_size(const RTCTX *ctx, const RTTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t size; size_t prefixlen = strlen(prefix); size = ( sizeof("///") + (prefixlen*3) ) * 2; size += sizeof("") + (prefixlen*2); if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(prefix) + strlen(id) + sizeof(" id=.."); if (IS_DIMS(opts)) size += sizeof(" srsDimension='x'"); size += pointArray_GMLsize(ctx, triangle->points, precision); return size; } static size_t asgml3_triangle_buf(const RTCTX *ctx, const RTTRIANGLE *triangle, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr=output; int dimension=2; if (RTFLAGS_GET_Z(triangle->flags)) dimension = 3; ptr += sprintf(ptr, "<%sTriangle", prefix); if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); ptr += sprintf(ptr, ">"); ptr += sprintf(ptr, "<%sexterior><%sLinearRing>", prefix, prefix); if (IS_DIMS(opts)) ptr += sprintf(ptr, "<%sposList srsDimension=\"%d\">", prefix, dimension); else ptr += sprintf(ptr, "<%sposList>", prefix); ptr += pointArray_toGML3(ctx, triangle->points, ptr, precision, opts); ptr += sprintf(ptr, "", prefix, prefix, prefix); ptr += sprintf(ptr, "", prefix); return (ptr-output); } static char * asgml3_triangle(const RTCTX *ctx, const RTTRIANGLE *triangle, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *output; int size; size = asgml3_triangle_size(ctx, triangle, srs, precision, opts, prefix, id); output = rtalloc(ctx, size); asgml3_triangle_buf(ctx, triangle, srs, output, precision, opts, prefix, id); return output; } /* * Compute max size required for GML version of this * inspected geometry. Will recurse when needed. * Don't call this with single-geoms inspected. */ static size_t asgml3_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { int i; size_t size; size_t prefixlen = strlen(prefix); RTGEOM *subgeom; /* the longest possible multi version */ size = sizeof("") + prefixlen*2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_point_size(ctx, (RTPOINT*)subgeom, 0, precision, opts, prefix, id); } else if (subgeom->type == RTLINETYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_line_size(ctx, (RTLINE*)subgeom, 0, precision, opts, prefix, id); } else if (subgeom->type == RTPOLYGONTYPE) { size += ( sizeof("/") + prefixlen ) * 2; size += asgml3_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, opts, prefix, id); } } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_multi_buf(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { int type = col->type; char *ptr, *gmltype; int i; RTGEOM *subgeom; ptr = output; gmltype=""; if (type == RTMULTIPOINTTYPE) gmltype = "MultiPoint"; else if (type == RTMULTILINETYPE) gmltype = "MultiCurve"; else if (type == RTMULTIPOLYGONTYPE) gmltype = "MultiSurface"; /* Open outmost tag */ ptr += sprintf(ptr, "<%s%s", prefix, gmltype); if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); if (!col->ngeoms) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { ptr += sprintf(ptr, "<%spointMember>", prefix); ptr += asgml3_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, opts, prefix, id); ptr += sprintf(ptr, "", prefix); } else if (subgeom->type == RTLINETYPE) { ptr += sprintf(ptr, "<%scurveMember>", prefix); ptr += asgml3_line_buf(ctx, (RTLINE*)subgeom, 0, ptr, precision, opts, prefix, id); ptr += sprintf(ptr, "", prefix); } else if (subgeom->type == RTPOLYGONTYPE) { ptr += sprintf(ptr, "<%ssurfaceMember>", prefix); ptr += asgml3_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, opts, 0, prefix, id); ptr += sprintf(ptr, "", prefix); } } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix, gmltype); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_multi(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_multi_size(ctx, col, srs, precision, opts, prefix, id); gml = rtalloc(ctx, size); asgml3_multi_buf(ctx, col, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_psurface_size(const RTCTX *ctx, const RTPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id) { int i; size_t size; size_t prefixlen = strlen(prefix); size = (sizeof("/") + prefixlen*2) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { size += asgml3_poly_size(ctx, psur->geoms[i], 0, precision, opts, prefix, id); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_psurface_buf(const RTCTX *ctx, const RTPSURFACE *psur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; int i; ptr = output; /* Open outmost tag */ ptr += sprintf(ptr, "<%sPolyhedralSurface", prefix); if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); ptr += sprintf(ptr, "><%spolygonPatches>", prefix); for (i=0; ingeoms; i++) { ptr += asgml3_poly_buf(ctx, psur->geoms[i], 0, ptr, precision, opts, 1, prefix, id); } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix, prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_psurface(const RTCTX *ctx, const RTPSURFACE *psur, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_psurface_size(ctx, psur, srs, precision, opts, prefix, id); gml = rtalloc(ctx, size); asgml3_psurface_buf(ctx, psur, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_tin_size(const RTCTX *ctx, const RTTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id) { int i; size_t size; size_t prefixlen = strlen(prefix); size = (sizeof("/") + prefixlen*2) * 2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { size += asgml3_triangle_size(ctx, tin->geoms[i], 0, precision, opts, prefix, id); } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asgml3_tin_buf(const RTCTX *ctx, const RTTIN *tin, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; int i; ptr = output; /* Open outmost tag */ ptr += sprintf(ptr, "<%sTin", prefix); if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); else ptr += sprintf(ptr, "><%strianglePatches>", prefix); for (i=0; ingeoms; i++) { ptr += asgml3_triangle_buf(ctx, tin->geoms[i], 0, ptr, precision, opts, prefix, id); } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix, prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_tin(const RTCTX *ctx, const RTTIN *tin, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_tin_size(ctx, tin, srs, precision, opts, prefix, id); gml = rtalloc(ctx, size); asgml3_tin_buf(ctx, tin, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { int i; size_t size; size_t prefixlen = strlen(prefix); RTGEOM *subgeom; size = sizeof("") + prefixlen*2; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += ( sizeof("/") + prefixlen ) * 2; if ( subgeom->type == RTPOINTTYPE ) { size += asgml3_point_size(ctx, (RTPOINT*)subgeom, 0, precision, opts, prefix, id); } else if ( subgeom->type == RTLINETYPE ) { size += asgml3_line_size(ctx, (RTLINE*)subgeom, 0, precision, opts, prefix, id); } else if ( subgeom->type == RTPOLYGONTYPE ) { size += asgml3_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, opts, prefix, id); } else if ( rtgeom_is_collection(ctx, subgeom) ) { size += asgml3_multi_size(ctx, (RTCOLLECTION*)subgeom, 0, precision, opts, prefix, id); } else rterror(ctx, "asgml3_collection_size: unknown geometry type"); } return size; } static size_t asgml3_collection_buf(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char *ptr; int i; RTGEOM *subgeom; ptr = output; /* Open outmost tag */ ptr += sprintf(ptr, "<%sMultiGeometry", prefix); if (srs) ptr += sprintf(ptr, " srsName=\"%s\"", srs); if (id) ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id); if (!col->ngeoms) { ptr += sprintf(ptr, "/>"); return (ptr-output); } ptr += sprintf(ptr, ">"); for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; ptr += sprintf(ptr, "<%sgeometryMember>", prefix); if ( subgeom->type == RTPOINTTYPE ) { ptr += asgml3_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, opts, prefix, id); } else if ( subgeom->type == RTLINETYPE ) { ptr += asgml3_line_buf(ctx, (RTLINE*)subgeom, 0, ptr, precision, opts, prefix, id); } else if ( subgeom->type == RTPOLYGONTYPE ) { ptr += asgml3_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, opts, 0, prefix, id); } else if ( rtgeom_is_collection(ctx, subgeom) ) { if ( subgeom->type == RTCOLLECTIONTYPE ) ptr += asgml3_collection_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, opts, prefix, id); else ptr += asgml3_multi_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, opts, prefix, id); } else rterror(ctx, "asgml3_collection_buf: unknown geometry type"); ptr += sprintf(ptr, "", prefix); } /* Close outmost tag */ ptr += sprintf(ptr, "", prefix); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asgml3_collection(const RTCTX *ctx, const RTCOLLECTION *col, const char *srs, int precision, int opts, const char *prefix, const char *id) { char *gml; size_t size; size = asgml3_collection_size(ctx, col, srs, precision, opts, prefix, id); gml = rtalloc(ctx, size); asgml3_collection_buf(ctx, col, srs, gml, precision, opts, prefix, id); return gml; } static size_t asgml3_multicurve_size(const RTCTX *ctx, const RTMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ) { size_t prefixlen = strlen(prefix); size_t size = sizeof( "" ) + 2 * prefixlen; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); RTGEOM* subgeom; int i; for( i = 0; i < cur->ngeoms; ++i ) { size += sizeof( "" ) + 2 * prefixlen; subgeom = cur->geoms[i]; if ( subgeom->type == RTLINETYPE ) { size += asgml3_line_size(ctx, (RTLINE*)subgeom, srs, precision, opts, prefix, id ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { size += asgml3_circstring_size(ctx, (RTCIRCSTRING*)subgeom, srs, precision, opts, prefix, id ); } else if( subgeom->type == RTCOMPOUNDTYPE ) { size += asgml3_compound_size(ctx, (RTCOMPOUND*)subgeom, srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_multicurve_buf(const RTCTX *ctx, const RTMCURVE* cur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id ) { char* ptr = output; RTGEOM* subgeom; int i; ptr += sprintf(ptr, "<%sMultiCurve", prefix ); if (srs) { ptr += sprintf(ptr, " srsName=\"%s\"", srs); } if (id) { ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id ); } ptr += sprintf( ptr, ">"); for( i = 0; i < cur->ngeoms; ++i ) { ptr += sprintf(ptr, "<%scurveMember>", prefix ); subgeom = cur->geoms[i]; if ( subgeom->type == RTLINETYPE ) { ptr += asgml3_line_buf(ctx, (RTLINE*)subgeom, srs, ptr, precision, opts, prefix, id ); } else if( subgeom->type == RTCIRCSTRINGTYPE ) { ptr += asgml3_circstring_buf(ctx, (RTCIRCSTRING*)subgeom, srs, ptr, precision, opts, prefix, id ); } else if( subgeom->type == RTCOMPOUNDTYPE ) { ptr += asgml3_compound_buf(ctx, (RTCOMPOUND*)subgeom, srs, ptr, precision, opts, prefix, id ); } ptr += sprintf(ptr, "", prefix ); } ptr += sprintf(ptr, "", prefix ); return (ptr - output); } static char * asgml3_multicurve(const RTCTX *ctx, const RTMCURVE* cur, const char *srs, int precision, int opts, const char *prefix, const char *id ) { char* gml; size_t size =asgml3_multicurve_size(ctx, cur, srs, precision, opts, prefix, id ); gml = rtalloc(ctx, size ); asgml3_multicurve_buf(ctx, cur, srs, gml, precision, opts, prefix, id ); return gml; } static size_t asgml3_multisurface_size(const RTCTX *ctx, const RTMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id) { size_t prefixlen = strlen(prefix); size_t size = sizeof( "" ) + 2 * prefixlen; if (srs) size += strlen(srs) + sizeof(" srsName=.."); if (id) size += strlen(id) + strlen(prefix) + sizeof(" id=.."); RTGEOM* subgeom; int i; for( i = 0; i < sur->ngeoms; ++i ) { subgeom = sur->geoms[i]; if( subgeom->type == RTPOLYGONTYPE ) { size += asgml3_poly_size(ctx, (RTPOLY*)sur->geoms[i], srs, precision, opts, prefix, id ); } else if( subgeom->type == RTCURVEPOLYTYPE ) { size += asgml3_curvepoly_size(ctx, (RTCURVEPOLY*)sur->geoms[i], srs, precision, opts, prefix, id ); } } return size; } static size_t asgml3_multisurface_buf(const RTCTX *ctx, const RTMSURFACE *sur, const char *srs, char *output, int precision, int opts, const char *prefix, const char *id) { char* ptr = output; int i; RTGEOM* subgeom; ptr += sprintf( ptr, "<%sMultiSurface", prefix ); if (srs) { ptr += sprintf(ptr, " srsName=\"%s\"", srs); } if (id) { ptr += sprintf(ptr, " %sid=\"%s\"", prefix, id ); } ptr += sprintf( ptr, ">" ); for( i = 0; i < sur->ngeoms; ++i ) { subgeom = sur->geoms[i]; if( subgeom->type == RTPOLYGONTYPE ) { ptr += asgml3_poly_buf(ctx, (RTPOLY*)sur->geoms[i], srs, ptr, precision, opts, 0, prefix, id ); } else if( subgeom->type == RTCURVEPOLYTYPE ) { ptr += asgml3_curvepoly_buf(ctx, (RTCURVEPOLY*)sur->geoms[i], srs, ptr, precision, opts, prefix, id ); } } ptr += sprintf( ptr, "", prefix ); return ptr - output; } static char * asgml3_multisurface(const RTCTX *ctx, const RTMSURFACE *sur, const char *srs, int precision, int opts, const char *prefix, const char *id) { char* gml; size_t size = asgml3_multisurface_size(ctx, sur, srs, precision, opts, prefix, id ); gml = rtalloc(ctx, size ); asgml3_multisurface_buf(ctx, sur, srs, gml, precision, opts, prefix, id ); return gml; } /* In GML3, inside or , coordinates are separated by a space separator * In GML3 also, lat/lon are reversed for geocentric data */ static size_t pointArray_toGML3(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int precision, int opts) { int i; char *ptr; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; ptr = output; if ( ! RTFLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { const RTPOINT2D *pt; pt = rt_getPoint2d_cp(ctx, pa, i); if (fabs(pt->x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt->x); else sprintf(x, "%g", pt->x); trim_trailing_zeros(ctx, x); if (fabs(pt->y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt->y); else sprintf(y, "%g", pt->y); trim_trailing_zeros(ctx, y); if ( i ) ptr += sprintf(ptr, " "); if (IS_DEGREE(opts)) ptr += sprintf(ptr, "%s %s", y, x); else ptr += sprintf(ptr, "%s %s", x, y); } } else { for (i=0; inpoints; i++) { const RTPOINT3DZ *pt; pt = rt_getPoint3dz_cp(ctx, pa, i); if (fabs(pt->x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt->x); else sprintf(x, "%g", pt->x); trim_trailing_zeros(ctx, x); if (fabs(pt->y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt->y); else sprintf(y, "%g", pt->y); trim_trailing_zeros(ctx, y); if (fabs(pt->z) < OUT_MAX_DOUBLE) sprintf(z, "%.*f", precision, pt->z); else sprintf(z, "%g", pt->z); trim_trailing_zeros(ctx, z); if ( i ) ptr += sprintf(ptr, " "); if (IS_DEGREE(opts)) ptr += sprintf(ptr, "%s %s %s", y, x, z); else ptr += sprintf(ptr, "%s %s %s", x, y, z); } } return ptr-output; } /* * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_GMLsize(const RTCTX *ctx, RTPOINTARRAY *pa, int precision) { if (RTFLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(", ")) * 2 * pa->npoints; return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(", ")) * 3 * pa->npoints; } src/rtout_kml.c000066400000000000000000000152441271715413500140450ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2006 Corporacion Autonoma Regional de Santander * Copyright 2010 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "stringbuffer.h" static int rtgeom_to_kml2_sb(const RTCTX *ctx, const RTGEOM *geom, int precision, const char *prefix, stringbuffer_t *sb); static int rtpoint_to_kml2_sb(const RTCTX *ctx, const RTPOINT *point, int precision, const char *prefix, stringbuffer_t *sb); static int rtline_to_kml2_sb(const RTCTX *ctx, const RTLINE *line, int precision, const char *prefix, stringbuffer_t *sb); static int rtpoly_to_kml2_sb(const RTCTX *ctx, const RTPOLY *poly, int precision, const char *prefix, stringbuffer_t *sb); static int rtcollection_to_kml2_sb(const RTCTX *ctx, const RTCOLLECTION *col, int precision, const char *prefix, stringbuffer_t *sb); static int ptarray_to_kml2_sb(const RTCTX *ctx, const RTPOINTARRAY *pa, int precision, stringbuffer_t *sb); /* * KML 2.2.0 */ /* takes a GEOMETRY and returns a KML representation */ char* rtgeom_to_kml2(const RTCTX *ctx, const RTGEOM *geom, int precision, const char *prefix) { stringbuffer_t *sb; int rv; char *kml; /* Can't do anything with empty */ if( rtgeom_is_empty(ctx, geom) ) return NULL; sb = stringbuffer_create(ctx); rv = rtgeom_to_kml2_sb(ctx, geom, precision, prefix, sb); if ( rv == RT_FAILURE ) { stringbuffer_destroy(ctx, sb); return NULL; } kml = stringbuffer_getstringcopy(ctx, sb); stringbuffer_destroy(ctx, sb); return kml; } static int rtgeom_to_kml2_sb(const RTCTX *ctx, const RTGEOM *geom, int precision, const char *prefix, stringbuffer_t *sb) { switch (geom->type) { case RTPOINTTYPE: return rtpoint_to_kml2_sb(ctx, (RTPOINT*)geom, precision, prefix, sb); case RTLINETYPE: return rtline_to_kml2_sb(ctx, (RTLINE*)geom, precision, prefix, sb); case RTPOLYGONTYPE: return rtpoly_to_kml2_sb(ctx, (RTPOLY*)geom, precision, prefix, sb); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: return rtcollection_to_kml2_sb(ctx, (RTCOLLECTION*)geom, precision, prefix, sb); default: rterror(ctx, "rtgeom_to_kml2: '%s' geometry type not supported", rttype_name(ctx, geom->type)); return RT_FAILURE; } } static int ptarray_to_kml2_sb(const RTCTX *ctx, const RTPOINTARRAY *pa, int precision, stringbuffer_t *sb) { int i, j; int dims = RTFLAGS_GET_Z(pa->flags) ? 3 : 2; RTPOINT4D pt; double *d; for ( i = 0; i < pa->npoints; i++ ) { rt_getPoint4d_p(ctx, pa, i, &pt); d = (double*)(&pt); if ( i ) stringbuffer_append(ctx, sb," "); for (j = 0; j < dims; j++) { if ( j ) stringbuffer_append(ctx, sb,","); if( fabs(d[j]) < OUT_MAX_DOUBLE ) { if ( stringbuffer_aprintf(ctx, sb, "%.*f", precision, d[j]) < 0 ) return RT_FAILURE; } else { if ( stringbuffer_aprintf(ctx, sb, "%g", d[j]) < 0 ) return RT_FAILURE; } stringbuffer_trim_trailing_zeroes(ctx, sb); } } return RT_SUCCESS; } static int rtpoint_to_kml2_sb(const RTCTX *ctx, const RTPOINT *point, int precision, const char *prefix, stringbuffer_t *sb) { /* Open point */ if ( stringbuffer_aprintf(ctx, sb, "<%sPoint><%scoordinates>", prefix, prefix) < 0 ) return RT_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(ctx, point->point, precision, sb) == RT_FAILURE ) return RT_FAILURE; /* Close point */ if ( stringbuffer_aprintf(ctx, sb, "", prefix, prefix) < 0 ) return RT_FAILURE; return RT_SUCCESS; } static int rtline_to_kml2_sb(const RTCTX *ctx, const RTLINE *line, int precision, const char *prefix, stringbuffer_t *sb) { /* Open linestring */ if ( stringbuffer_aprintf(ctx, sb, "<%sLineString><%scoordinates>", prefix, prefix) < 0 ) return RT_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(ctx, line->points, precision, sb) == RT_FAILURE ) return RT_FAILURE; /* Close linestring */ if ( stringbuffer_aprintf(ctx, sb, "", prefix, prefix) < 0 ) return RT_FAILURE; return RT_SUCCESS; } static int rtpoly_to_kml2_sb(const RTCTX *ctx, const RTPOLY *poly, int precision, const char *prefix, stringbuffer_t *sb) { int i, rv; /* Open polygon */ if ( stringbuffer_aprintf(ctx, sb, "<%sPolygon>", prefix) < 0 ) return RT_FAILURE; for ( i = 0; i < poly->nrings; i++ ) { /* Inner or outer ring opening tags */ if( i ) rv = stringbuffer_aprintf(ctx, sb, "<%sinnerBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); else rv = stringbuffer_aprintf(ctx, sb, "<%souterBoundaryIs><%sLinearRing><%scoordinates>", prefix, prefix, prefix); if ( rv < 0 ) return RT_FAILURE; /* Coordinate array */ if ( ptarray_to_kml2_sb(ctx, poly->rings[i], precision, sb) == RT_FAILURE ) return RT_FAILURE; /* Inner or outer ring closing tags */ if( i ) rv = stringbuffer_aprintf(ctx, sb, "", prefix, prefix, prefix); else rv = stringbuffer_aprintf(ctx, sb, "", prefix, prefix, prefix); if ( rv < 0 ) return RT_FAILURE; } /* Close polygon */ if ( stringbuffer_aprintf(ctx, sb, "", prefix) < 0 ) return RT_FAILURE; return RT_SUCCESS; } static int rtcollection_to_kml2_sb(const RTCTX *ctx, const RTCOLLECTION *col, int precision, const char *prefix, stringbuffer_t *sb) { int i, rv; /* Open geometry */ if ( stringbuffer_aprintf(ctx, sb, "<%sMultiGeometry>", prefix) < 0 ) return RT_FAILURE; for ( i = 0; i < col->ngeoms; i++ ) { rv = rtgeom_to_kml2_sb(ctx, col->geoms[i], precision, prefix, sb); if ( rv == RT_FAILURE ) return RT_FAILURE; } /* Close geometry */ if ( stringbuffer_aprintf(ctx, sb, "", prefix) < 0 ) return RT_FAILURE; return RT_SUCCESS; } src/rtout_svg.c000066400000000000000000000410121271715413500140510ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2003 Refractions Research Inc. * **********************************************************************/ /** @file * * SVG output routines. * Originally written by: Klaus Förster * Refactored by: Olivier Courtin (Camptocamp) * * BNF SVG Path: **********************************************************************/ #include "librttopo_geom_internal.h" static char * assvg_point(const RTCTX *ctx, const RTPOINT *point, int relative, int precision); static char * assvg_line(const RTCTX *ctx, const RTLINE *line, int relative, int precision); static char * assvg_polygon(const RTCTX *ctx, const RTPOLY *poly, int relative, int precision); static char * assvg_multipoint(const RTCTX *ctx, const RTMPOINT *mpoint, int relative, int precision); static char * assvg_multiline(const RTCTX *ctx, const RTMLINE *mline, int relative, int precision); static char * assvg_multipolygon(const RTCTX *ctx, const RTMPOLY *mpoly, int relative, int precision); static char * assvg_collection(const RTCTX *ctx, const RTCOLLECTION *col, int relative, int precision); static size_t assvg_geom_size(const RTCTX *ctx, const RTGEOM *geom, int relative, int precision); static size_t assvg_geom_buf(const RTCTX *ctx, const RTGEOM *geom, char *output, int relative, int precision); static size_t pointArray_svg_size(const RTCTX *ctx, RTPOINTARRAY *pa, int precision); static size_t pointArray_svg_rel(const RTCTX *ctx, RTPOINTARRAY *pa, char * output, int close_ring, int precision); static size_t pointArray_svg_abs(const RTCTX *ctx, RTPOINTARRAY *pa, char * output, int close_ring, int precision); /** * Takes a GEOMETRY and returns a SVG representation */ char * rtgeom_to_svg(const RTCTX *ctx, const RTGEOM *geom, int precision, int relative) { char *ret = NULL; int type = geom->type; /* Empty string for empties */ if( rtgeom_is_empty(ctx, geom) ) { ret = rtalloc(ctx, 1); ret[0] = '\0'; return ret; } switch (type) { case RTPOINTTYPE: ret = assvg_point(ctx, (RTPOINT*)geom, relative, precision); break; case RTLINETYPE: ret = assvg_line(ctx, (RTLINE*)geom, relative, precision); break; case RTPOLYGONTYPE: ret = assvg_polygon(ctx, (RTPOLY*)geom, relative, precision); break; case RTMULTIPOINTTYPE: ret = assvg_multipoint(ctx, (RTMPOINT*)geom, relative, precision); break; case RTMULTILINETYPE: ret = assvg_multiline(ctx, (RTMLINE*)geom, relative, precision); break; case RTMULTIPOLYGONTYPE: ret = assvg_multipolygon(ctx, (RTMPOLY*)geom, relative, precision); break; case RTCOLLECTIONTYPE: ret = assvg_collection(ctx, (RTCOLLECTION*)geom, relative, precision); break; default: rterror(ctx, "rtgeom_to_svg: '%s' geometry type not supported", rttype_name(ctx, type)); } return ret; } /** * Point Geometry */ static size_t assvg_point_size(const RTCTX *ctx, const RTPOINT *point, int circle, int precision) { size_t size; size = (OUT_MAX_DIGS_DOUBLE + precision) * 2; if (circle) size += sizeof("cx='' cy=''"); else size += sizeof("x='' y=''"); return size; } static size_t assvg_point_buf(const RTCTX *ctx, const RTPOINT *point, char * output, int circle, int precision) { char *ptr=output; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; RTPOINT2D pt; rt_getPoint2d_p(ctx, point->point, 0, &pt); if (fabs(pt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x); else sprintf(x, "%g", pt.x); trim_trailing_zeros(ctx, x); /* SVG Y axis is reversed, an no need to transform 0 into -0 */ if (fabs(pt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1 : pt.y); else sprintf(y, "%g", fabs(pt.y) ? pt.y * -1 : pt.y); trim_trailing_zeros(ctx, y); if (circle) ptr += sprintf(ptr, "x=\"%s\" y=\"%s\"", x, y); else ptr += sprintf(ptr, "cx=\"%s\" cy=\"%s\"", x, y); return (ptr-output); } static char * assvg_point(const RTCTX *ctx, const RTPOINT *point, int circle, int precision) { char *output; int size; size = assvg_point_size(ctx, point, circle, precision); output = rtalloc(ctx, size); assvg_point_buf(ctx, point, output, circle, precision); return output; } /** * Line Geometry */ static size_t assvg_line_size(const RTCTX *ctx, const RTLINE *line, int relative, int precision) { size_t size; size = sizeof("M "); size += pointArray_svg_size(ctx, line->points, precision); return size; } static size_t assvg_line_buf(const RTCTX *ctx, const RTLINE *line, char * output, int relative, int precision) { char *ptr=output; /* Start path with SVG MoveTo */ ptr += sprintf(ptr, "M "); if (relative) ptr += pointArray_svg_rel(ctx, line->points, ptr, 1, precision); else ptr += pointArray_svg_abs(ctx, line->points, ptr, 1, precision); return (ptr-output); } static char * assvg_line(const RTCTX *ctx, const RTLINE *line, int relative, int precision) { char *output; int size; size = assvg_line_size(ctx, line, relative, precision); output = rtalloc(ctx, size); assvg_line_buf(ctx, line, output, relative, precision); return output; } /** * Polygon Geometry */ static size_t assvg_polygon_size(const RTCTX *ctx, const RTPOLY *poly, int relative, int precision) { int i; size_t size=0; for (i=0; inrings; i++) size += pointArray_svg_size(ctx, poly->rings[i], precision) + sizeof(" "); size += sizeof("M Z") * poly->nrings; return size; } static size_t assvg_polygon_buf(const RTCTX *ctx, const RTPOLY *poly, char * output, int relative, int precision) { int i; char *ptr=output; for (i=0; inrings; i++) { if (i) ptr += sprintf(ptr, " "); /* Space beetween each ring */ ptr += sprintf(ptr, "M "); /* Start path with SVG MoveTo */ if (relative) { ptr += pointArray_svg_rel(ctx, poly->rings[i], ptr, 0, precision); ptr += sprintf(ptr, " z"); /* SVG closepath */ } else { ptr += pointArray_svg_abs(ctx, poly->rings[i], ptr, 0, precision); ptr += sprintf(ptr, " Z"); /* SVG closepath */ } } return (ptr-output); } static char * assvg_polygon(const RTCTX *ctx, const RTPOLY *poly, int relative, int precision) { char *output; int size; size = assvg_polygon_size(ctx, poly, relative, precision); output = rtalloc(ctx, size); assvg_polygon_buf(ctx, poly, output, relative, precision); return output; } /** * Multipoint Geometry */ static size_t assvg_multipoint_size(const RTCTX *ctx, const RTMPOINT *mpoint, int relative, int precision) { const RTPOINT *point; size_t size=0; int i; for (i=0 ; ingeoms ; i++) { point = mpoint->geoms[i]; size += assvg_point_size(ctx, point, relative, precision); } size += sizeof(",") * --i; /* Arbitrary comma separator */ return size; } static size_t assvg_multipoint_buf(const RTCTX *ctx, const RTMPOINT *mpoint, char *output, int relative, int precision) { const RTPOINT *point; int i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += sprintf(ptr, ","); /* Arbitrary comma separator */ point = mpoint->geoms[i]; ptr += assvg_point_buf(ctx, point, ptr, relative, precision); } return (ptr-output); } static char * assvg_multipoint(const RTCTX *ctx, const RTMPOINT *mpoint, int relative, int precision) { char *output; int size; size = assvg_multipoint_size(ctx, mpoint, relative, precision); output = rtalloc(ctx, size); assvg_multipoint_buf(ctx, mpoint, output, relative, precision); return output; } /** * Multiline Geometry */ static size_t assvg_multiline_size(const RTCTX *ctx, const RTMLINE *mline, int relative, int precision) { const RTLINE *line; size_t size=0; int i; for (i=0 ; ingeoms ; i++) { line = mline->geoms[i]; size += assvg_line_size(ctx, line, relative, precision); } size += sizeof(" ") * --i; /* SVG whitespace Separator */ return size; } static size_t assvg_multiline_buf(const RTCTX *ctx, const RTMLINE *mline, char *output, int relative, int precision) { const RTLINE *line; int i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += sprintf(ptr, " "); /* SVG whitespace Separator */ line = mline->geoms[i]; ptr += assvg_line_buf(ctx, line, ptr, relative, precision); } return (ptr-output); } static char * assvg_multiline(const RTCTX *ctx, const RTMLINE *mline, int relative, int precision) { char *output; int size; size = assvg_multiline_size(ctx, mline, relative, precision); output = rtalloc(ctx, size); assvg_multiline_buf(ctx, mline, output, relative, precision); return output; } /* * Multipolygon Geometry */ static size_t assvg_multipolygon_size(const RTCTX *ctx, const RTMPOLY *mpoly, int relative, int precision) { const RTPOLY *poly; size_t size=0; int i; for (i=0 ; ingeoms ; i++) { poly = mpoly->geoms[i]; size += assvg_polygon_size(ctx, poly, relative, precision); } size += sizeof(" ") * --i; /* SVG whitespace Separator */ return size; } static size_t assvg_multipolygon_buf(const RTCTX *ctx, const RTMPOLY *mpoly, char *output, int relative, int precision) { const RTPOLY *poly; int i; char *ptr=output; for (i=0 ; ingeoms ; i++) { if (i) ptr += sprintf(ptr, " "); /* SVG whitespace Separator */ poly = mpoly->geoms[i]; ptr += assvg_polygon_buf(ctx, poly, ptr, relative, precision); } return (ptr-output); } static char * assvg_multipolygon(const RTCTX *ctx, const RTMPOLY *mpoly, int relative, int precision) { char *output; int size; size = assvg_multipolygon_size(ctx, mpoly, relative, precision); output = rtalloc(ctx, size); assvg_multipolygon_buf(ctx, mpoly, output, relative, precision); return output; } /** * Collection Geometry */ static size_t assvg_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, int relative, int precision) { int i = 0; size_t size=0; const RTGEOM *subgeom; for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += assvg_geom_size(ctx, subgeom, relative, precision); } if ( i ) /* We have some geometries, so add space for delimiters. */ size += sizeof(";") * --i; if (size == 0) size++; /* GEOMETRYCOLLECTION EMPTY, space for null terminator */ return size; } static size_t assvg_collection_buf(const RTCTX *ctx, const RTCOLLECTION *col, char *output, int relative, int precision) { int i; char *ptr=output; const RTGEOM *subgeom; /* EMPTY GEOMETRYCOLLECTION */ if (col->ngeoms == 0) *ptr = '\0'; for (i=0; ingeoms; i++) { if (i) ptr += sprintf(ptr, ";"); subgeom = col->geoms[i]; ptr += assvg_geom_buf(ctx, subgeom, ptr, relative, precision); } return (ptr - output); } static char * assvg_collection(const RTCTX *ctx, const RTCOLLECTION *col, int relative, int precision) { char *output; int size; size = assvg_collection_size(ctx, col, relative, precision); output = rtalloc(ctx, size); assvg_collection_buf(ctx, col, output, relative, precision); return output; } static size_t assvg_geom_buf(const RTCTX *ctx, const RTGEOM *geom, char *output, int relative, int precision) { int type = geom->type; char *ptr=output; switch (type) { case RTPOINTTYPE: ptr += assvg_point_buf(ctx, (RTPOINT*)geom, ptr, relative, precision); break; case RTLINETYPE: ptr += assvg_line_buf(ctx, (RTLINE*)geom, ptr, relative, precision); break; case RTPOLYGONTYPE: ptr += assvg_polygon_buf(ctx, (RTPOLY*)geom, ptr, relative, precision); break; case RTMULTIPOINTTYPE: ptr += assvg_multipoint_buf(ctx, (RTMPOINT*)geom, ptr, relative, precision); break; case RTMULTILINETYPE: ptr += assvg_multiline_buf(ctx, (RTMLINE*)geom, ptr, relative, precision); break; case RTMULTIPOLYGONTYPE: ptr += assvg_multipolygon_buf(ctx, (RTMPOLY*)geom, ptr, relative, precision); break; default: rterror(ctx, "assvg_geom_buf: '%s' geometry type not supported.", rttype_name(ctx, type)); } return (ptr-output); } static size_t assvg_geom_size(const RTCTX *ctx, const RTGEOM *geom, int relative, int precision) { int type = geom->type; size_t size = 0; switch (type) { case RTPOINTTYPE: size = assvg_point_size(ctx, (RTPOINT*)geom, relative, precision); break; case RTLINETYPE: size = assvg_line_size(ctx, (RTLINE*)geom, relative, precision); break; case RTPOLYGONTYPE: size = assvg_polygon_size(ctx, (RTPOLY*)geom, relative, precision); break; case RTMULTIPOINTTYPE: size = assvg_multipoint_size(ctx, (RTMPOINT*)geom, relative, precision); break; case RTMULTILINETYPE: size = assvg_multiline_size(ctx, (RTMLINE*)geom, relative, precision); break; case RTMULTIPOLYGONTYPE: size = assvg_multipolygon_size(ctx, (RTMPOLY*)geom, relative, precision); break; default: rterror(ctx, "assvg_geom_size: '%s' geometry type not supported.", rttype_name(ctx, type)); } return size; } static size_t pointArray_svg_rel(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int close_ring, int precision) { int i, end; char *ptr; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; RTPOINT2D pt, lpt; ptr = output; if (close_ring) end = pa->npoints; else end = pa->npoints - 1; /* Starting point */ rt_getPoint2d_p(ctx, pa, 0, &pt); if (fabs(pt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x); else sprintf(x, "%g", pt.x); trim_trailing_zeros(ctx, x); if (fabs(pt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1 : pt.y); else sprintf(y, "%g", fabs(pt.y) ? pt.y * -1 : pt.y); trim_trailing_zeros(ctx, y); ptr += sprintf(ptr,"%s %s l", x, y); /* All the following ones */ for (i=1 ; i < end ; i++) { lpt = pt; rt_getPoint2d_p(ctx, pa, i, &pt); if (fabs(pt.x -lpt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x -lpt.x); else sprintf(x, "%g", pt.x -lpt.x); trim_trailing_zeros(ctx, x); /* SVG Y axis is reversed, an no need to transform 0 into -0 */ if (fabs(pt.y -lpt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y)); else sprintf(y, "%g", fabs(pt.y -lpt.y) ? (pt.y - lpt.y) * -1: (pt.y - lpt.y)); trim_trailing_zeros(ctx, y); ptr += sprintf(ptr," %s %s", x, y); } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_svg_abs(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int close_ring, int precision) { int i, end; char *ptr; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; RTPOINT2D pt; ptr = output; if (close_ring) end = pa->npoints; else end = pa->npoints - 1; for (i=0 ; i < end ; i++) { rt_getPoint2d_p(ctx, pa, i, &pt); if (fabs(pt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x); else sprintf(x, "%g", pt.x); trim_trailing_zeros(ctx, x); /* SVG Y axis is reversed, an no need to transform 0 into -0 */ if (fabs(pt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, fabs(pt.y) ? pt.y * -1:pt.y); else sprintf(y, "%g", fabs(pt.y) ? pt.y * -1:pt.y); trim_trailing_zeros(ctx, y); if (i == 1) ptr += sprintf(ptr, " L "); else if (i) ptr += sprintf(ptr, " "); ptr += sprintf(ptr,"%s %s", x, y); } return (ptr-output); } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_svg_size(const RTCTX *ctx, RTPOINTARRAY *pa, int precision) { return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(" ")) * 2 * pa->npoints + sizeof(" L "); } src/rtout_twkb.c000066400000000000000000000460411271715413500142300ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #include "rtout_twkb.h" /* * GeometryType, and dimensions */ static uint8_t rtgeom_twkb_type(const RTCTX *ctx, const RTGEOM *geom) { uint8_t twkb_type = 0; RTDEBUGF(2, "Entered rtgeom_twkb_type",0); switch ( geom->type ) { case RTPOINTTYPE: twkb_type = RTWKB_POINT_TYPE; break; case RTLINETYPE: twkb_type = RTWKB_LINESTRING_TYPE; break; case RTPOLYGONTYPE: twkb_type = RTWKB_POLYGON_TYPE; break; case RTMULTIPOINTTYPE: twkb_type = RTWKB_MULTIPOINT_TYPE; break; case RTMULTILINETYPE: twkb_type = RTWKB_MULTILINESTRING_TYPE; break; case RTMULTIPOLYGONTYPE: twkb_type = RTWKB_MULTIPOLYGON_TYPE; break; case RTCOLLECTIONTYPE: twkb_type = RTWKB_GEOMETRYCOLLECTION_TYPE; break; default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, geom->type), geom->type); } return twkb_type; } /** * Calculates the size of the bbox in varints in the form: * xmin, xdelta, ymin, ydelta */ static size_t sizeof_bbox(const RTCTX *ctx, TWKB_STATE *ts, int ndims) { int i; uint8_t buf[16]; size_t size = 0; RTDEBUGF(2, "Entered %s", __func__); for ( i = 0; i < ndims; i++ ) { size += varint_s64_encode_buf(ctx, ts->bbox_min[i], buf); size += varint_s64_encode_buf(ctx, (ts->bbox_max[i] - ts->bbox_min[i]), buf); } return size; } /** * Writes the bbox in varints in the form: * xmin, xdelta, ymin, ydelta */ static void write_bbox(const RTCTX *ctx, TWKB_STATE *ts, int ndims) { int i; RTDEBUGF(2, "Entered %s", __func__); for ( i = 0; i < ndims; i++ ) { bytebuffer_append_varint(ctx, ts->header_buf, ts->bbox_min[i]); bytebuffer_append_varint(ctx, ts->header_buf, (ts->bbox_max[i] - ts->bbox_min[i])); } } /** * Stores a pointarray as varints in the buffer * @register_npoints, controls whether an npoints entry is added to the buffer (used to skip npoints for point types) * @dimension, states the dimensionality of object this array is part of (0 = point, 1 = linear, 2 = areal) */ static int ptarray_to_twkb_buf(const RTCTX *ctx, const RTPOINTARRAY *pa, TWKB_GLOBALS *globals, TWKB_STATE *ts, int register_npoints, int minpoints) { int ndims = RTFLAGS_NDIMS(pa->flags); int i, j; bytebuffer_t b; bytebuffer_t *b_p; int64_t nextdelta[MAX_N_DIMS]; int npoints = 0; size_t npoints_offset = 0; RTDEBUGF(2, "Entered %s", __func__); /* Dispense with the empty case right away */ if ( pa->npoints == 0 && register_npoints ) { RTDEBUGF(4, "Register npoints:%d", pa->npoints); bytebuffer_append_uvarint(ctx, ts->geom_buf, pa->npoints); return 0; } /* If npoints is more than 127 it is unpredictable how many bytes npoints will need */ /* Then we have to store the deltas in a temp buffer to later add them after npoints */ /* If noints is below 128 we know 1 byte will be needed */ /* Then we can make room for that 1 byte at once and write to */ /* ordinary buffer */ if( pa->npoints > 127 ) { /* Independent buffer to hold the coordinates, so we can put the npoints */ /* into the stream once we know how many points we actually have */ bytebuffer_init_with_size(ctx, &b, 3 * ndims * pa->npoints); b_p = &b; } else { /* We give an alias to our ordinary buffer */ b_p = ts->geom_buf; if ( register_npoints ) { /* We do not store a pointer to the place where we want the npoints value */ /* Instead we store how far from the beginning of the buffer we want the value */ /* That is because we otherwise will get in trouble if the buffer is reallocated */ npoints_offset = b_p->writecursor - b_p->buf_start; /* We just move the cursor 1 step to make room for npoints byte */ /* We use the function append_byte even if we have no value yet, */ /* since that gives us the check for big enough buffer and moves the cursor */ bytebuffer_append_byte(ctx, b_p, 0); } } for ( i = 0; i < pa->npoints; i++ ) { double *dbl_ptr = (double*)rt_getPoint_internal(ctx, pa, i); int diff = 0; /* Write this coordinate to the buffer as a varint */ for ( j = 0; j < ndims; j++ ) { /* To get the relative coordinate we don't get the distance */ /* from the last point but instead the distance from our */ /* last accumulated point. This is important to not build up an */ /* accumulated error when rounding the coordinates */ nextdelta[j] = (int64_t) llround(globals->factor[j] * dbl_ptr[j]) - ts->accum_rels[j]; RTDEBUGF(4, "deltavalue: %d, ", nextdelta[j]); diff += llabs(nextdelta[j]); } /* Skipping the first point is not allowed */ /* If the sum(abs()) of all the deltas was zero, */ /* then this was a duplicate point, so we can ignore it */ if ( i > minpoints && diff == 0 ) continue; /* We really added a point, so... */ npoints++; /* Write this vertex to the temporary buffer as varints */ for ( j = 0; j < ndims; j++ ) { ts->accum_rels[j] += nextdelta[j]; bytebuffer_append_varint(ctx, b_p, nextdelta[j]); } /* See if this coordinate expands the bounding box */ if( globals->variant & TWKB_BBOX ) { for ( j = 0; j < ndims; j++ ) { if( ts->accum_rels[j] > ts->bbox_max[j] ) ts->bbox_max[j] = ts->accum_rels[j]; if( ts->accum_rels[j] < ts->bbox_min[j] ) ts->bbox_min[j] = ts->accum_rels[j]; } } } if ( pa->npoints > 127 ) { /* Now write the temporary results into the main buffer */ /* First the npoints */ if ( register_npoints ) bytebuffer_append_uvarint(ctx, ts->geom_buf, npoints); /* Now the coordinates */ bytebuffer_append_bytebuffer(ctx, ts->geom_buf, b_p); /* Clear our temporary buffer */ rtfree(ctx, b.buf_start); } else { /* If we didn't use a temp buffer, we just write that npoints value */ /* to where it belongs*/ if ( register_npoints ) varint_u64_encode_buf(ctx, npoints, b_p->buf_start + npoints_offset); } return 0; } /****************************************************************** * POINTS *******************************************************************/ static int rtpoint_to_twkb_buf(const RTCTX *ctx, const RTPOINT *pt, TWKB_GLOBALS *globals, TWKB_STATE *ts) { RTDEBUGF(2, "Entered %s", __func__); /* Set the coordinates (don't write npoints) */ ptarray_to_twkb_buf(ctx, pt->point, globals, ts, 0, 1); return 0; } /****************************************************************** * LINESTRINGS *******************************************************************/ static int rtline_to_twkb_buf(const RTCTX *ctx, const RTLINE *line, TWKB_GLOBALS *globals, TWKB_STATE *ts) { RTDEBUGF(2, "Entered %s", __func__); /* Set the coordinates (do write npoints) */ ptarray_to_twkb_buf(ctx, line->points, globals, ts, 1, 2); return 0; } /****************************************************************** * POLYGONS *******************************************************************/ static int rtpoly_to_twkb_buf(const RTCTX *ctx, const RTPOLY *poly, TWKB_GLOBALS *globals, TWKB_STATE *ts) { int i; /* Set the number of rings */ bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) poly->nrings); for ( i = 0; i < poly->nrings; i++ ) { /* Set the coordinates (do write npoints) */ ptarray_to_twkb_buf(ctx, poly->rings[i], globals, ts, 1, 4); } return 0; } /****************************************************************** * MULTI-GEOMETRYS (MultiPoint, MultiLinestring, MultiPolygon) *******************************************************************/ static int rtmulti_to_twkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { int i; int nempty = 0; RTDEBUGF(2, "Entered %s", __func__); RTDEBUGF(4, "Number of geometries in multi is %d", col->ngeoms); /* Deal with special case for MULTIPOINT: skip any empty points */ if ( col->type == RTMULTIPOINTTYPE ) { for ( i = 0; i < col->ngeoms; i++ ) if ( rtgeom_is_empty(ctx, col->geoms[i]) ) nempty++; } /* Set the number of geometries */ bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) (col->ngeoms - nempty)); /* We've been handed an idlist, so write it in */ if ( ts->idlist ) { for ( i = 0; i < col->ngeoms; i++ ) { /* Skip empty points in multipoints, we can't represent them */ if ( col->type == RTMULTIPOINTTYPE && rtgeom_is_empty(ctx, col->geoms[i]) ) continue; bytebuffer_append_varint(ctx, ts->geom_buf, ts->idlist[i]); } /* Empty it out to nobody else uses it now */ ts->idlist = NULL; } for ( i = 0; i < col->ngeoms; i++ ) { /* Skip empty points in multipoints, we can't represent them */ if ( col->type == RTMULTIPOINTTYPE && rtgeom_is_empty(ctx, col->geoms[i]) ) continue; rtgeom_to_twkb_buf(ctx, col->geoms[i], globals, ts); } return 0; } /****************************************************************** * GEOMETRYCOLLECTIONS *******************************************************************/ static int rtcollection_to_twkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, TWKB_GLOBALS *globals, TWKB_STATE *ts) { int i; RTDEBUGF(2, "Entered %s", __func__); RTDEBUGF(4, "Number of geometries in collection is %d", col->ngeoms); /* Set the number of geometries */ bytebuffer_append_uvarint(ctx, ts->geom_buf, (uint64_t) col->ngeoms); /* We've been handed an idlist, so write it in */ if ( ts->idlist ) { for ( i = 0; i < col->ngeoms; i++ ) bytebuffer_append_varint(ctx, ts->geom_buf, ts->idlist[i]); /* Empty it out to nobody else uses it now */ ts->idlist = NULL; } /* Write in the sub-geometries */ for ( i = 0; i < col->ngeoms; i++ ) { rtgeom_write_to_buffer(ctx, col->geoms[i], globals, ts); } return 0; } /****************************************************************** * Handle whole TWKB *******************************************************************/ static int rtgeom_to_twkb_buf(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts) { RTDEBUGF(2, "Entered %s", __func__); switch ( geom->type ) { case RTPOINTTYPE: { RTDEBUGF(4,"Type found is Point, %d", geom->type); return rtpoint_to_twkb_buf(ctx, (RTPOINT*) geom, globals, ts); } case RTLINETYPE: { RTDEBUGF(4,"Type found is Linestring, %d", geom->type); return rtline_to_twkb_buf(ctx, (RTLINE*) geom, globals, ts); } /* Polygon has 'nrings' and 'rings' elements */ case RTPOLYGONTYPE: { RTDEBUGF(4,"Type found is Polygon, %d", geom->type); return rtpoly_to_twkb_buf(ctx, (RTPOLY*)geom, globals, ts); } /* All these Collection types have 'ngeoms' and 'geoms' elements */ case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: { RTDEBUGF(4,"Type found is Multi, %d", geom->type); return rtmulti_to_twkb_buf(ctx, (RTCOLLECTION*)geom, globals, ts); } case RTCOLLECTIONTYPE: { RTDEBUGF(4,"Type found is collection, %d", geom->type); return rtcollection_to_twkb_buf(ctx, (RTCOLLECTION*) geom, globals, ts); } /* Unknown type! */ default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, (geom)->type), (geom)->type); } return 0; } static int rtgeom_write_to_buffer(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state) { int i, is_empty, has_z, has_m, ndims; size_t bbox_size = 0, optional_precision_byte = 0; uint8_t flag = 0, type_prec = 0; TWKB_STATE child_state; memset(&child_state, 0, sizeof(TWKB_STATE)); child_state.header_buf = bytebuffer_create_with_size(ctx, 16); child_state.geom_buf = bytebuffer_create_with_size(ctx, 64); child_state.idlist = parent_state->idlist; /* Read dimensionality from input */ has_z = rtgeom_has_z(ctx, geom); has_m = rtgeom_has_m(ctx, geom); ndims = rtgeom_ndims(ctx, geom); is_empty = rtgeom_is_empty(ctx, geom); /* Do we need extended precision? If we have a Z or M we do. */ optional_precision_byte = (has_z || has_m); /* Both X and Y dimension use the same precision */ globals->factor[0] = pow(10, globals->prec_xy); globals->factor[1] = globals->factor[0]; /* Z and M dimensions have their own precisions */ if ( has_z ) globals->factor[2] = pow(10, globals->prec_z); if ( has_m ) globals->factor[2 + has_z] = pow(10, globals->prec_m); /* Reset stats */ for ( i = 0; i < MAX_N_DIMS; i++ ) { /* Reset bbox calculation */ child_state.bbox_max[i] = INT64_MIN; child_state.bbox_min[i] = INT64_MAX; /* Reset acumulated delta values to get absolute values on next point */ child_state.accum_rels[i] = 0; } /* RTTYPE/PRECISION BYTE */ if ( abs(globals->prec_xy) > 7 ) rterror(ctx, "%s: X/Z precision cannot be greater than 7 or less than -7", __func__); /* Read the TWKB type number from the geometry */ RTTYPE_PREC_SET_TYPE(type_prec, rtgeom_twkb_type(ctx, geom)); /* Zig-zag the precision value before encoding it since it is a signed value */ TYPE_PREC_SET_PREC(type_prec, zigzag8(ctx, globals->prec_xy)); /* Write the type and precision byte */ bytebuffer_append_byte(ctx, child_state.header_buf, type_prec); /* METADATA BYTE */ /* Set first bit if we are going to store bboxes */ FIRST_BYTE_SET_BBOXES(flag, (globals->variant & TWKB_BBOX) && ! is_empty); /* Set second bit if we are going to store resulting size */ FIRST_BYTE_SET_SIZES(flag, globals->variant & TWKB_SIZE); /* There will be no ID-list (for now) */ FIRST_BYTE_SET_IDLIST(flag, parent_state->idlist && ! is_empty); /* Are there higher dimensions */ FIRST_BYTE_SET_EXTENDED(flag, optional_precision_byte); /* Empty? */ FIRST_BYTE_SET_EMPTY(flag, is_empty); /* Write the header byte */ bytebuffer_append_byte(ctx, child_state.header_buf, flag); /* EXTENDED PRECISION BYTE (OPTIONAL) */ /* If needed, write the extended dim byte */ if( optional_precision_byte ) { uint8_t flag = 0; if ( has_z && ( globals->prec_z > 7 || globals->prec_z < 0 ) ) rterror(ctx, "%s: Z precision cannot be negative or greater than 7", __func__); if ( has_m && ( globals->prec_m > 7 || globals->prec_m < 0 ) ) rterror(ctx, "%s: M precision cannot be negative or greater than 7", __func__); HIGHER_DIM_SET_HASZ(flag, has_z); HIGHER_DIM_SET_HASM(flag, has_m); HIGHER_DIM_SET_PRECZ(flag, globals->prec_z); HIGHER_DIM_SET_PRECM(flag, globals->prec_m); bytebuffer_append_byte(ctx, child_state.header_buf, flag); } /* It the geometry is empty, we're almost done */ if ( is_empty ) { /* If this output is sized, write the size of */ /* all following content, which is zero because */ /* there is none */ if ( globals->variant & TWKB_SIZE ) bytebuffer_append_byte(ctx, child_state.header_buf, 0); bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf, child_state.header_buf); bytebuffer_destroy(ctx, child_state.header_buf); bytebuffer_destroy(ctx, child_state.geom_buf); return 0; } /* Write the TWKB into the output buffer */ rtgeom_to_twkb_buf(ctx, geom, globals, &child_state); /*If we have a header_buf, we know that this function is called inside a collection*/ /*and then we have to merge the bboxes of the included geometries*/ /*and put the result to the parent (the collection)*/ if( (globals->variant & TWKB_BBOX) && parent_state->header_buf ) { RTDEBUG(4,"Merge bboxes"); for ( i = 0; i < ndims; i++ ) { if(child_state.bbox_min[i]bbox_min[i]) parent_state->bbox_min[i] = child_state.bbox_min[i]; if(child_state.bbox_max[i]>parent_state->bbox_max[i]) parent_state->bbox_max[i] = child_state.bbox_max[i]; } } /* Did we have a box? If so, how big? */ bbox_size = 0; if( globals->variant & TWKB_BBOX ) { RTDEBUG(4,"We want boxes and will calculate required size"); bbox_size = sizeof_bbox(ctx, &child_state, ndims); } /* Write the size if wanted */ if( globals->variant & TWKB_SIZE ) { /* Here we have to add what we know will be written to header */ /* buffer after size value is written */ size_t size_to_register = bytebuffer_getlength(ctx, child_state.geom_buf); size_to_register += bbox_size; bytebuffer_append_uvarint(ctx, child_state.header_buf, size_to_register); } if( globals->variant & TWKB_BBOX ) write_bbox(ctx, &child_state, ndims); bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf,child_state.header_buf); bytebuffer_append_bytebuffer(ctx, parent_state->geom_buf,child_state.geom_buf); bytebuffer_destroy(ctx, child_state.header_buf); bytebuffer_destroy(ctx, child_state.geom_buf); return 0; } /** * Convert RTGEOM to a char* in TWKB format. Caller is responsible for freeing * the returned array. */ uint8_t* rtgeom_to_twkb_with_idlist(const RTCTX *ctx, const RTGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size) { RTDEBUGF(2, "Entered %s", __func__); RTDEBUGF(2, "variant value %x", variant); TWKB_GLOBALS tg; TWKB_STATE ts; uint8_t *twkb; memset(&ts, 0, sizeof(TWKB_STATE)); memset(&tg, 0, sizeof(TWKB_GLOBALS)); tg.variant = variant; tg.prec_xy = precision_xy; tg.prec_z = precision_z; tg.prec_m = precision_m; if ( idlist && ! rtgeom_is_collection(ctx, geom) ) { rterror(ctx, "Only collections can support ID lists"); return NULL; } if ( ! geom ) { RTDEBUG(4,"Cannot convert NULL into TWKB."); rterror(ctx, "Cannot convert NULL into TWKB"); return NULL; } ts.idlist = idlist; ts.header_buf = NULL; ts.geom_buf = bytebuffer_create(ctx); rtgeom_write_to_buffer(ctx, geom, &tg, &ts); if ( twkb_size ) *twkb_size = bytebuffer_getlength(ctx, ts.geom_buf); twkb = ts.geom_buf->buf_start; rtfree(ctx, ts.geom_buf); return twkb; } uint8_t* rtgeom_to_twkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size) { return rtgeom_to_twkb_with_idlist(ctx, geom, NULL, variant, precision_xy, precision_z, precision_m, twkb_size); } src/rtout_twkb.h000066400000000000000000000100651271715413500142320ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2013 Nicklas Avén * Copyright 2013 Nicklas Avén * **********************************************************************/ /********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * Copyright 2013 Nicklas Avén * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include #include "bytebuffer.h" /* Maximum number of geometry dimmensions that internal arrays can hold */ #define MAX_N_DIMS 4 #define MAX_BBOX_SIZE 64 #define MAX_SIZE_SIZE 8 /** * Header true/false flags */ #define FIRST_BYTE_SET_BBOXES(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) #define FIRST_BYTE_SET_SIZES(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) #define FIRST_BYTE_SET_IDLIST(flag, bool) ((flag) = ((bool) ? (flag) | 0x04 : (flag) & (~0x04))) #define FIRST_BYTE_SET_EXTENDED(flag, bool) ((flag) = ((bool) ? (flag) | 0x08 : (flag) & (~0x08))) #define FIRST_BYTE_SET_EMPTY(flag, bool) ((flag) = ((bool) ? (flag) | 0x10 : (flag) & (~0x10))) /** * Macros for manipulating the 'type_precision' int. An int8_t used as follows: * Type 4 bits * Precision 4 bits */ #define RTTYPE_PREC_SET_TYPE(flag, type) ((flag) = ((flag) & 0xF0) | (((type) & 0x0F))) #define TYPE_PREC_SET_PREC(flag, prec) ((flag) = ((flag) & 0x0F) | (((prec) & 0x0F) << 4)) #define HIGHER_DIM_SET_HASZ(flag, bool) ((flag) = ((bool) ? (flag) | 0x01 : (flag) & (~0x01))) #define HIGHER_DIM_SET_HASM(flag, bool) ((flag) = ((bool) ? (flag) | 0x02 : (flag) & (~0x02))) #define HIGHER_DIM_SET_PRECZ(flag, prec) ((flag) = ((flag) & 0xE3) | (((prec) & 0x07) << 2)) #define HIGHER_DIM_SET_PRECM(flag, prec) ((flag) = ((flag) & 0x1F) | (((prec) & 0x07) << 5)) typedef struct { /* Options defined at start */ uint8_t variant; int8_t prec_xy; int8_t prec_z; int8_t prec_m; float factor[4]; /*What factor to multiply the coordiinates with to get the requested precision*/ } TWKB_GLOBALS; typedef struct { uint8_t variant; /*options that change at runtime*/ bytebuffer_t *header_buf; bytebuffer_t *geom_buf; int hasz; int hasm; const int64_t *idlist; int64_t bbox_min[MAX_N_DIMS]; int64_t bbox_max[MAX_N_DIMS]; int64_t accum_rels[MAX_N_DIMS]; /*Holds the acculmulated relative values*/ } TWKB_STATE; static int rtgeom_to_twkb_buf(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int rtpoint_to_twkb_buf(const RTCTX *ctx, const RTPOINT *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int rtline_to_twkb_buf(const RTCTX *ctx, const RTLINE *line, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int rtpoly_to_twkb_buf(const RTCTX *ctx, const RTPOLY *poly, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int rtcollection_to_twkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, TWKB_GLOBALS *global_values, TWKB_STATE *ts); static int rtgeom_write_to_buffer(const RTCTX *ctx, const RTGEOM *geom, TWKB_GLOBALS *global_values, TWKB_STATE *parent_state); src/rtout_wkb.c000066400000000000000000000606141271715413500140460ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" static uint8_t* rtgeom_to_wkb_buf(const RTCTX *ctx, const RTGEOM *geom, uint8_t *buf, uint8_t variant); static size_t rtgeom_to_wkb_size(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant); /* * Look-up table for hex writer */ static char *hexchr = "0123456789ABCDEF"; char* hexbytes_from_bytes(const RTCTX *ctx, uint8_t *bytes, size_t size) { char *hex; int i; if ( ! bytes || ! size ) { rterror(ctx, "hexbutes_from_bytes: invalid input"); return NULL; } hex = rtalloc(ctx, size * 2 + 1); hex[2*size] = '\0'; for( i = 0; i < size; i++ ) { /* Top four bits to 0-F */ hex[2*i] = hexchr[bytes[i] >> 4]; /* Bottom four bits to 0-F */ hex[2*i+1] = hexchr[bytes[i] & 0x0F]; } return hex; } /* * Optional SRID */ static int rtgeom_wkb_needs_srid(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant) { /* Sub-components of collections inherit their SRID from the parent. We force that behavior with the RTWKB_NO_SRID flag */ if ( variant & RTWKB_NO_SRID ) return RT_FALSE; /* We can only add an SRID if the geometry has one, and the RTWKB form is extended */ if ( (variant & RTWKB_EXTENDED) && rtgeom_has_srid(ctx, geom) ) return RT_TRUE; /* Everything else doesn't get an SRID */ return RT_FALSE; } /* * GeometryType */ static uint32_t rtgeom_wkb_type(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant) { uint32_t wkb_type = 0; switch ( geom->type ) { case RTPOINTTYPE: wkb_type = RTWKB_POINT_TYPE; break; case RTLINETYPE: wkb_type = RTWKB_LINESTRING_TYPE; break; case RTPOLYGONTYPE: wkb_type = RTWKB_POLYGON_TYPE; break; case RTMULTIPOINTTYPE: wkb_type = RTWKB_MULTIPOINT_TYPE; break; case RTMULTILINETYPE: wkb_type = RTWKB_MULTILINESTRING_TYPE; break; case RTMULTIPOLYGONTYPE: wkb_type = RTWKB_MULTIPOLYGON_TYPE; break; case RTCOLLECTIONTYPE: wkb_type = RTWKB_GEOMETRYCOLLECTION_TYPE; break; case RTCIRCSTRINGTYPE: wkb_type = RTWKB_CIRCULARSTRING_TYPE; break; case RTCOMPOUNDTYPE: wkb_type = RTWKB_COMPOUNDCURVE_TYPE; break; case RTCURVEPOLYTYPE: wkb_type = RTWKB_CURVEPOLYGON_TYPE; break; case RTMULTICURVETYPE: wkb_type = RTWKB_MULTICURVE_TYPE; break; case RTMULTISURFACETYPE: wkb_type = RTWKB_MULTISURFACE_TYPE; break; case RTPOLYHEDRALSURFACETYPE: wkb_type = RTWKB_POLYHEDRALSURFACE_TYPE; break; case RTTINTYPE: wkb_type = RTWKB_TIN_TYPE; break; case RTTRIANGLETYPE: wkb_type = RTWKB_TRIANGLE_TYPE; break; default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, geom->type), geom->type); } if ( variant & RTWKB_EXTENDED ) { if ( RTFLAGS_GET_Z(geom->flags) ) wkb_type |= RTWKBZOFFSET; if ( RTFLAGS_GET_M(geom->flags) ) wkb_type |= RTWKBMOFFSET; /* if ( geom->srid != SRID_UNKNOWN && ! (variant & RTWKB_NO_SRID) ) */ if ( rtgeom_wkb_needs_srid(ctx, geom, variant) ) wkb_type |= RTWKBSRIDFLAG; } else if ( variant & RTWKB_ISO ) { /* Z types are in the 1000 range */ if ( RTFLAGS_GET_Z(geom->flags) ) wkb_type += 1000; /* M types are in the 2000 range */ if ( RTFLAGS_GET_M(geom->flags) ) wkb_type += 2000; /* ZM types are in the 1000 + 2000 = 3000 range, see above */ } return wkb_type; } /* * Endian */ static uint8_t* endian_to_wkb_buf(const RTCTX *ctx, uint8_t *buf, uint8_t variant) { if ( variant & RTWKB_HEX ) { buf[0] = '0'; buf[1] = ((variant & RTWKB_NDR) ? '1' : '0'); return buf + 2; } else { buf[0] = ((variant & RTWKB_NDR) ? 1 : 0); return buf + 1; } } /* * SwapBytes? */ static inline int wkb_swap_bytes(const RTCTX *ctx, uint8_t variant) { /* If requested variant matches machine arch, we don't have to swap! */ if ( ((variant & RTWKB_NDR) && (getMachineEndian(ctx) == NDR)) || ((! (variant & RTWKB_NDR)) && (getMachineEndian(ctx) == XDR)) ) { return RT_FALSE; } return RT_TRUE; } /* * Integer32 */ static uint8_t* integer_to_wkb_buf(const RTCTX *ctx, const int ival, uint8_t *buf, uint8_t variant) { char *iptr = (char*)(&ival); int i = 0; if ( sizeof(int) != RTWKB_INT_SIZE ) { rterror(ctx, "Machine int size is not %d bytes!", RTWKB_INT_SIZE); } RTDEBUGF(4, "Writing value '%u'", ival); if ( variant & RTWKB_HEX ) { int swap = wkb_swap_bytes(ctx, variant); /* Machine/request arch mismatch, so flip byte order */ for ( i = 0; i < RTWKB_INT_SIZE; i++ ) { int j = (swap ? RTWKB_INT_SIZE - 1 - i : i); uint8_t b = iptr[j]; /* Top four bits to 0-F */ buf[2*i] = hexchr[b >> 4]; /* Bottom four bits to 0-F */ buf[2*i+1] = hexchr[b & 0x0F]; } return buf + (2 * RTWKB_INT_SIZE); } else { /* Machine/request arch mismatch, so flip byte order */ if ( wkb_swap_bytes(ctx, variant) ) { for ( i = 0; i < RTWKB_INT_SIZE; i++ ) { buf[i] = iptr[RTWKB_INT_SIZE - 1 - i]; } } /* If machine arch and requested arch match, don't flip byte order */ else { memcpy(buf, iptr, RTWKB_INT_SIZE); } return buf + RTWKB_INT_SIZE; } } /* * Float64 */ static uint8_t* double_to_wkb_buf(const RTCTX *ctx, const double d, uint8_t *buf, uint8_t variant) { char *dptr = (char*)(&d); int i = 0; if ( sizeof(double) != RTWKB_DOUBLE_SIZE ) { rterror(ctx, "Machine double size is not %d bytes!", RTWKB_DOUBLE_SIZE); } if ( variant & RTWKB_HEX ) { int swap = wkb_swap_bytes(ctx, variant); /* Machine/request arch mismatch, so flip byte order */ for ( i = 0; i < RTWKB_DOUBLE_SIZE; i++ ) { int j = (swap ? RTWKB_DOUBLE_SIZE - 1 - i : i); uint8_t b = dptr[j]; /* Top four bits to 0-F */ buf[2*i] = hexchr[b >> 4]; /* Bottom four bits to 0-F */ buf[2*i+1] = hexchr[b & 0x0F]; } return buf + (2 * RTWKB_DOUBLE_SIZE); } else { /* Machine/request arch mismatch, so flip byte order */ if ( wkb_swap_bytes(ctx, variant) ) { for ( i = 0; i < RTWKB_DOUBLE_SIZE; i++ ) { buf[i] = dptr[RTWKB_DOUBLE_SIZE - 1 - i]; } } /* If machine arch and requested arch match, don't flip byte order */ else { memcpy(buf, dptr, RTWKB_DOUBLE_SIZE); } return buf + RTWKB_DOUBLE_SIZE; } } /* * Empty */ static size_t empty_to_wkb_size(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant) { /* endian byte + type integer */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE; /* optional srid integer */ if ( rtgeom_wkb_needs_srid(ctx, geom, variant) ) size += RTWKB_INT_SIZE; /* Represent POINT EMPTY as POINT(NaN NaN) */ if ( geom->type == RTPOINTTYPE ) { const RTPOINT *pt = (RTPOINT*)geom; size += RTWKB_DOUBLE_SIZE * RTFLAGS_NDIMS(pt->point->flags); } /* num-elements */ else { size += RTWKB_INT_SIZE; } return size; } static uint8_t* empty_to_wkb_buf(const RTCTX *ctx, const RTGEOM *geom, uint8_t *buf, uint8_t variant) { uint32_t wkb_type = rtgeom_wkb_type(ctx, geom, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(ctx, buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, wkb_type, buf, variant); /* Set the SRID if necessary */ if ( rtgeom_wkb_needs_srid(ctx, geom, variant) ) buf = integer_to_wkb_buf(ctx, geom->srid, buf, variant); /* Represent POINT EMPTY as POINT(NaN NaN) */ if ( geom->type == RTPOINTTYPE ) { const RTPOINT *pt = (RTPOINT*)geom; static double nn = NAN; int i; for ( i = 0; i < RTFLAGS_NDIMS(pt->point->flags); i++ ) { buf = double_to_wkb_buf(ctx, nn, buf, variant); } } /* Everything else is flagged as empty using num-elements == 0 */ else { /* Set nrings/npoints/ngeoms to zero */ buf = integer_to_wkb_buf(ctx, 0, buf, variant); } return buf; } /* * RTPOINTARRAY */ static size_t ptarray_to_wkb_size(const RTCTX *ctx, const RTPOINTARRAY *pa, uint8_t variant) { int dims = 2; size_t size = 0; if ( variant & (RTWKB_ISO | RTWKB_EXTENDED) ) dims = RTFLAGS_NDIMS(pa->flags); /* Include the npoints if it's not a POINT type) */ if ( ! ( variant & RTWKB_NO_NPOINTS ) ) size += RTWKB_INT_SIZE; /* size of the double list */ size += pa->npoints * dims * RTWKB_DOUBLE_SIZE; return size; } static uint8_t* ptarray_to_wkb_buf(const RTCTX *ctx, const RTPOINTARRAY *pa, uint8_t *buf, uint8_t variant) { int dims = 2; int pa_dims = RTFLAGS_NDIMS(pa->flags); int i, j; double *dbl_ptr; /* SFSQL is artays 2-d. Extended and ISO use all available dimensions */ if ( (variant & RTWKB_ISO) || (variant & RTWKB_EXTENDED) ) dims = pa_dims; /* Set the number of points (if it's not a POINT type) */ if ( ! ( variant & RTWKB_NO_NPOINTS ) ) buf = integer_to_wkb_buf(ctx, pa->npoints, buf, variant); /* Bulk copy the coordinates when: dimensionality matches, output format */ /* is not hex, and output endian matches internal endian. */ if ( pa->npoints && (dims == pa_dims) && ! wkb_swap_bytes(ctx, variant) && ! (variant & RTWKB_HEX) ) { size_t size = pa->npoints * dims * RTWKB_DOUBLE_SIZE; memcpy(buf, rt_getPoint_internal(ctx, pa, 0), size); buf += size; } /* Copy coordinates one-by-one otherwise */ else { for ( i = 0; i < pa->npoints; i++ ) { RTDEBUGF(4, "Writing point #%d", i); dbl_ptr = (double*)rt_getPoint_internal(ctx, pa, i); for ( j = 0; j < dims; j++ ) { RTDEBUGF(4, "Writing dimension #%d (buf = %p)", j, buf); buf = double_to_wkb_buf(ctx, dbl_ptr[j], buf, variant); } } } RTDEBUGF(4, "Done (buf = %p)", buf); return buf; } /* * POINT */ static size_t rtpoint_to_wkb_size(const RTCTX *ctx, const RTPOINT *pt, uint8_t variant) { /* Endian flag + type number */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)pt) ) return empty_to_wkb_size(ctx, (RTGEOM*)pt, variant); /* Extended RTWKB needs space for optional SRID integer */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)pt, variant) ) size += RTWKB_INT_SIZE; /* Points */ size += ptarray_to_wkb_size(ctx, pt->point, variant | RTWKB_NO_NPOINTS); return size; } static uint8_t* rtpoint_to_wkb_buf(const RTCTX *ctx, const RTPOINT *pt, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)pt) ) return empty_to_wkb_buf(ctx, (RTGEOM*)pt, buf, variant); /* Set the endian flag */ RTDEBUGF(4, "Entering function, buf = %p", buf); buf = endian_to_wkb_buf(ctx, buf, variant); RTDEBUGF(4, "Endian set, buf = %p", buf); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, rtgeom_wkb_type(ctx, (RTGEOM*)pt, variant), buf, variant); RTDEBUGF(4, "Type set, buf = %p", buf); /* Set the optional SRID for extended variant */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)pt, variant) ) { buf = integer_to_wkb_buf(ctx, pt->srid, buf, variant); RTDEBUGF(4, "SRID set, buf = %p", buf); } /* Set the coordinates */ buf = ptarray_to_wkb_buf(ctx, pt->point, buf, variant | RTWKB_NO_NPOINTS); RTDEBUGF(4, "Pointarray set, buf = %p", buf); return buf; } /* * LINESTRING, CIRCULARSTRING */ static size_t rtline_to_wkb_size(const RTCTX *ctx, const RTLINE *line, uint8_t variant) { /* Endian flag + type number */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)line) ) return empty_to_wkb_size(ctx, (RTGEOM*)line, variant); /* Extended RTWKB needs space for optional SRID integer */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)line, variant) ) size += RTWKB_INT_SIZE; /* Size of point array */ size += ptarray_to_wkb_size(ctx, line->points, variant); return size; } static uint8_t* rtline_to_wkb_buf(const RTCTX *ctx, const RTLINE *line, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)line) ) return empty_to_wkb_buf(ctx, (RTGEOM*)line, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(ctx, buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, rtgeom_wkb_type(ctx, (RTGEOM*)line, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)line, variant) ) buf = integer_to_wkb_buf(ctx, line->srid, buf, variant); /* Set the coordinates */ buf = ptarray_to_wkb_buf(ctx, line->points, buf, variant); return buf; } /* * TRIANGLE */ static size_t rttriangle_to_wkb_size(const RTCTX *ctx, const RTTRIANGLE *tri, uint8_t variant) { /* endian flag + type number + number of rings */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE + RTWKB_INT_SIZE; /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)tri) ) return empty_to_wkb_size(ctx, (RTGEOM*)tri, variant); /* Extended RTWKB needs space for optional SRID integer */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)tri, variant) ) size += RTWKB_INT_SIZE; /* How big is this point array? */ size += ptarray_to_wkb_size(ctx, tri->points, variant); return size; } static uint8_t* rttriangle_to_wkb_buf(const RTCTX *ctx, const RTTRIANGLE *tri, uint8_t *buf, uint8_t variant) { /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)tri) ) return empty_to_wkb_buf(ctx, (RTGEOM*)tri, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(ctx, buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, rtgeom_wkb_type(ctx, (RTGEOM*)tri, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)tri, variant) ) buf = integer_to_wkb_buf(ctx, tri->srid, buf, variant); /* Set the number of rings (only one, it's a triangle, buddy) */ buf = integer_to_wkb_buf(ctx, 1, buf, variant); /* Write that ring */ buf = ptarray_to_wkb_buf(ctx, tri->points, buf, variant); return buf; } /* * POLYGON */ static size_t rtpoly_to_wkb_size(const RTCTX *ctx, const RTPOLY *poly, uint8_t variant) { /* endian flag + type number + number of rings */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE + RTWKB_INT_SIZE; int i = 0; /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)poly) ) return empty_to_wkb_size(ctx, (RTGEOM*)poly, variant); /* Extended RTWKB needs space for optional SRID integer */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)poly, variant) ) size += RTWKB_INT_SIZE; for ( i = 0; i < poly->nrings; i++ ) { /* Size of ring point array */ size += ptarray_to_wkb_size(ctx, poly->rings[i], variant); } return size; } static uint8_t* rtpoly_to_wkb_buf(const RTCTX *ctx, const RTPOLY *poly, uint8_t *buf, uint8_t variant) { int i; /* Only process empty at this level in the EXTENDED case */ if ( (variant & RTWKB_EXTENDED) && rtgeom_is_empty(ctx, (RTGEOM*)poly) ) return empty_to_wkb_buf(ctx, (RTGEOM*)poly, buf, variant); /* Set the endian flag */ buf = endian_to_wkb_buf(ctx, buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, rtgeom_wkb_type(ctx, (RTGEOM*)poly, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)poly, variant) ) buf = integer_to_wkb_buf(ctx, poly->srid, buf, variant); /* Set the number of rings */ buf = integer_to_wkb_buf(ctx, poly->nrings, buf, variant); for ( i = 0; i < poly->nrings; i++ ) { buf = ptarray_to_wkb_buf(ctx, poly->rings[i], buf, variant); } return buf; } /* * MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION * MULTICURVE, COMPOUNDCURVE, MULTISURFACE, CURVEPOLYGON, TIN, * POLYHEDRALSURFACE */ static size_t rtcollection_to_wkb_size(const RTCTX *ctx, const RTCOLLECTION *col, uint8_t variant) { /* Endian flag + type number + number of subgeoms */ size_t size = RTWKB_BYTE_SIZE + RTWKB_INT_SIZE + RTWKB_INT_SIZE; int i = 0; /* Extended RTWKB needs space for optional SRID integer */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)col, variant) ) size += RTWKB_INT_SIZE; for ( i = 0; i < col->ngeoms; i++ ) { /* size of subgeom */ size += rtgeom_to_wkb_size(ctx, (RTGEOM*)col->geoms[i], variant | RTWKB_NO_SRID); } return size; } static uint8_t* rtcollection_to_wkb_buf(const RTCTX *ctx, const RTCOLLECTION *col, uint8_t *buf, uint8_t variant) { int i; /* Set the endian flag */ buf = endian_to_wkb_buf(ctx, buf, variant); /* Set the geometry type */ buf = integer_to_wkb_buf(ctx, rtgeom_wkb_type(ctx, (RTGEOM*)col, variant), buf, variant); /* Set the optional SRID for extended variant */ if ( rtgeom_wkb_needs_srid(ctx, (RTGEOM*)col, variant) ) buf = integer_to_wkb_buf(ctx, col->srid, buf, variant); /* Set the number of sub-geometries */ buf = integer_to_wkb_buf(ctx, col->ngeoms, buf, variant); /* Write the sub-geometries. Sub-geometries do not get SRIDs, they inherit from their parents. */ for ( i = 0; i < col->ngeoms; i++ ) { buf = rtgeom_to_wkb_buf(ctx, col->geoms[i], buf, variant | RTWKB_NO_SRID); } return buf; } /* * GEOMETRY */ static size_t rtgeom_to_wkb_size(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant) { size_t size = 0; if ( geom == NULL ) return 0; /* Short circuit out empty geometries */ if ( (!(variant & RTWKB_EXTENDED)) && rtgeom_is_empty(ctx, geom) ) { return empty_to_wkb_size(ctx, geom, variant); } switch ( geom->type ) { case RTPOINTTYPE: size += rtpoint_to_wkb_size(ctx, (RTPOINT*)geom, variant); break; /* LineString and CircularString both have points elements */ case RTCIRCSTRINGTYPE: case RTLINETYPE: size += rtline_to_wkb_size(ctx, (RTLINE*)geom, variant); break; /* Polygon has nrings and rings elements */ case RTPOLYGONTYPE: size += rtpoly_to_wkb_size(ctx, (RTPOLY*)geom, variant); break; /* Triangle has one ring of three points */ case RTTRIANGLETYPE: size += rttriangle_to_wkb_size(ctx, (RTTRIANGLE*)geom, variant); break; /* All these Collection types have ngeoms and geoms elements */ case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTCOLLECTIONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: size += rtcollection_to_wkb_size(ctx, (RTCOLLECTION*)geom, variant); break; /* Unknown type! */ default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, geom->type), geom->type); } return size; } /* TODO handle the TRIANGLE type properly */ static uint8_t* rtgeom_to_wkb_buf(const RTCTX *ctx, const RTGEOM *geom, uint8_t *buf, uint8_t variant) { /* Do not simplify empties when outputting to canonical form */ if ( rtgeom_is_empty(ctx, geom) & ! (variant & RTWKB_EXTENDED) ) return empty_to_wkb_buf(ctx, geom, buf, variant); switch ( geom->type ) { case RTPOINTTYPE: return rtpoint_to_wkb_buf(ctx, (RTPOINT*)geom, buf, variant); /* LineString and CircularString both have 'points' elements */ case RTCIRCSTRINGTYPE: case RTLINETYPE: return rtline_to_wkb_buf(ctx, (RTLINE*)geom, buf, variant); /* Polygon has 'nrings' and 'rings' elements */ case RTPOLYGONTYPE: return rtpoly_to_wkb_buf(ctx, (RTPOLY*)geom, buf, variant); /* Triangle has one ring of three points */ case RTTRIANGLETYPE: return rttriangle_to_wkb_buf(ctx, (RTTRIANGLE*)geom, buf, variant); /* All these Collection types have 'ngeoms' and 'geoms' elements */ case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTCOMPOUNDTYPE: case RTCURVEPOLYTYPE: case RTMULTICURVETYPE: case RTMULTISURFACETYPE: case RTCOLLECTIONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: return rtcollection_to_wkb_buf(ctx, (RTCOLLECTION*)geom, buf, variant); /* Unknown type! */ default: rterror(ctx, "Unsupported geometry type: %s [%d]", rttype_name(ctx, geom->type), geom->type); } /* Return value to keep compiler happy. */ return 0; } /** * Convert RTGEOM to a char* in RTWKB format. Caller is responsible for freeing * the returned array. * * @param variant. Unsigned bitmask value. Accepts one of: RTWKB_ISO, RTWKB_EXTENDED, RTWKB_SFSQL. * Accepts any of: RTWKB_NDR, RTWKB_HEX. For example: Variant = ( RTWKB_ISO | RTWKB_NDR ) would * return the little-endian ISO form of RTWKB. For Example: Variant = ( RTWKB_EXTENDED | RTWKB_HEX ) * would return the big-endian extended form of RTWKB, as hex-encoded ASCII (the "canonical form"). * @param size_out If supplied, will return the size of the returned memory segment, * including the null terminator in the case of ASCII. */ uint8_t* rtgeom_to_wkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, size_t *size_out) { size_t buf_size; uint8_t *buf = NULL; uint8_t *wkb_out = NULL; /* Initialize output size */ if ( size_out ) *size_out = 0; if ( geom == NULL ) { RTDEBUG(4,"Cannot convert NULL into RTWKB."); rterror(ctx, "Cannot convert NULL into RTWKB."); return NULL; } /* Calculate the required size of the output buffer */ buf_size = rtgeom_to_wkb_size(ctx, geom, variant); RTDEBUGF(4, "RTWKB output size: %d", buf_size); if ( buf_size == 0 ) { RTDEBUG(4,"Error calculating output RTWKB buffer size."); rterror(ctx, "Error calculating output RTWKB buffer size."); return NULL; } /* Hex string takes twice as much space as binary + a null character */ if ( variant & RTWKB_HEX ) { buf_size = 2 * buf_size + 1; RTDEBUGF(4, "Hex RTWKB output size: %d", buf_size); } /* If neither or both variants are specified, choose the native order */ if ( ! (variant & RTWKB_NDR || variant & RTWKB_XDR) || (variant & RTWKB_NDR && variant & RTWKB_XDR) ) { if ( getMachineEndian(ctx) == NDR ) variant = variant | RTWKB_NDR; else variant = variant | RTWKB_XDR; } /* Allocate the buffer */ buf = rtalloc(ctx, buf_size); if ( buf == NULL ) { RTDEBUGF(4,"Unable to allocate %d bytes for RTWKB output buffer.", buf_size); rterror(ctx, "Unable to allocate %d bytes for RTWKB output buffer.", buf_size); return NULL; } /* Retain a pointer to the front of the buffer for later */ wkb_out = buf; /* Write the RTWKB into the output buffer */ buf = rtgeom_to_wkb_buf(ctx, geom, buf, variant); /* Null the last byte if this is a hex output */ if ( variant & RTWKB_HEX ) { *buf = '\0'; buf++; } RTDEBUGF(4,"buf (%p) - wkb_out (%p) = %d", buf, wkb_out, buf - wkb_out); /* The buffer pointer should now land at the end of the allocated buffer space. Let's check. */ if ( buf_size != (buf - wkb_out) ) { RTDEBUG(4,"Output RTWKB is not the same size as the allocated buffer."); rterror(ctx, "Output RTWKB is not the same size as the allocated buffer."); rtfree(ctx, wkb_out); return NULL; } /* Report output size */ if ( size_out ) *size_out = buf_size; return wkb_out; } char* rtgeom_to_hexwkb(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, size_t *size_out) { return (char*)rtgeom_to_wkb(ctx, geom, variant | RTWKB_HEX, size_out); } src/rtout_wkt.c000066400000000000000000000517731271715413500140760ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include "stringbuffer.h" static void rtgeom_to_wkt_sb(const RTCTX *ctx, const RTGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant); /* * ISO format uses both Z and M qualifiers. * Extended format only uses an M qualifier for 3DM variants, where it is not * clear what the third dimension represents. * SFSQL format never has more than two dimensions, so no qualifiers. */ static void dimension_qualifiers_to_wkt_sb(const RTCTX *ctx, const RTGEOM *geom, stringbuffer_t *sb, uint8_t variant) { /* Extended RTWKT: POINTM(0 0 0) */ #if 0 if ( (variant & RTWKT_EXTENDED) && ! (variant & RTWKT_IS_CHILD) && RTFLAGS_GET_M(geom->flags) && (!RTFLAGS_GET_Z(geom->flags)) ) #else if ( (variant & RTWKT_EXTENDED) && RTFLAGS_GET_M(geom->flags) && (!RTFLAGS_GET_Z(geom->flags)) ) #endif { stringbuffer_append(ctx, sb, "M"); /* "M" */ return; } /* ISO RTWKT: POINT ZM (0 0 0 0) */ if ( (variant & RTWKT_ISO) && (RTFLAGS_NDIMS(geom->flags) > 2) ) { stringbuffer_append(ctx, sb, " "); if ( RTFLAGS_GET_Z(geom->flags) ) stringbuffer_append(ctx, sb, "Z"); if ( RTFLAGS_GET_M(geom->flags) ) stringbuffer_append(ctx, sb, "M"); stringbuffer_append(ctx, sb, " "); } } /* * Write an empty token out, padding with a space if * necessary. */ static void empty_to_wkt_sb(const RTCTX *ctx, stringbuffer_t *sb) { if ( ! strchr(" ,(", stringbuffer_lastchar(ctx, sb)) ) /* "EMPTY" */ { stringbuffer_append(ctx, sb, " "); } stringbuffer_append(ctx, sb, "EMPTY"); } /* * Point array is a list of coordinates. Depending on output mode, * we may suppress some dimensions. ISO and Extended formats include * all dimensions. Standard OGC output only includes X/Y coordinates. */ static void ptarray_to_wkt_sb(const RTCTX *ctx, const RTPOINTARRAY *ptarray, stringbuffer_t *sb, int precision, uint8_t variant) { /* OGC only includes X/Y */ int dimensions = 2; int i, j; /* ISO and extended formats include all dimensions */ if ( variant & ( RTWKT_ISO | RTWKT_EXTENDED ) ) dimensions = RTFLAGS_NDIMS(ptarray->flags); /* Opening paren? */ if ( ! (variant & RTWKT_NO_PARENS) ) stringbuffer_append(ctx, sb, "("); /* Digits and commas */ for (i = 0; i < ptarray->npoints; i++) { double *dbl_ptr = (double*)rt_getPoint_internal(ctx, ptarray, i); /* Commas before ever coord but the first */ if ( i > 0 ) stringbuffer_append(ctx, sb, ","); for (j = 0; j < dimensions; j++) { /* Spaces before every ordinate but the first */ if ( j > 0 ) stringbuffer_append(ctx, sb, " "); stringbuffer_aprintf(ctx, sb, "%.*g", precision, dbl_ptr[j]); } } /* Closing paren? */ if ( ! (variant & RTWKT_NO_PARENS) ) stringbuffer_append(ctx, sb, ")"); } /* * A four-dimensional point will have different outputs depending on variant. * ISO: POINT ZM (0 0 0 0) * Extended: POINT(0 0 0 0) * OGC: POINT(0 0) * A three-dimensional m-point will have different outputs too. * ISO: POINT M (0 0 0) * Extended: POINTM(0 0 0) * OGC: POINT(0 0) */ static void rtpoint_to_wkt_sb(const RTCTX *ctx, const RTPOINT *pt, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "POINT"); /* "POINT" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)pt, sb, variant); } if ( rtpoint_is_empty(ctx, pt) ) { empty_to_wkt_sb(ctx, sb); return; } ptarray_to_wkt_sb(ctx, pt->point, sb, precision, variant); } /* * LINESTRING(0 0 0, 1 1 1) */ static void rtline_to_wkt_sb(const RTCTX *ctx, const RTLINE *line, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "LINESTRING"); /* "LINESTRING" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)line, sb, variant); } if ( rtline_is_empty(ctx, line) ) { empty_to_wkt_sb(ctx, sb); return; } ptarray_to_wkt_sb(ctx, line->points, sb, precision, variant); } /* * POLYGON(0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1) */ static void rtpoly_to_wkt_sb(const RTCTX *ctx, const RTPOLY *poly, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "POLYGON"); /* "POLYGON" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)poly, sb, variant); } if ( rtpoly_is_empty(ctx, poly) ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); for ( i = 0; i < poly->nrings; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); ptarray_to_wkt_sb(ctx, poly->rings[i], sb, precision, variant); } stringbuffer_append(ctx, sb, ")"); } /* * CIRCULARSTRING */ static void rtcircstring_to_wkt_sb(const RTCTX *ctx, const RTCIRCSTRING *circ, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "CIRCULARSTRING"); /* "CIRCULARSTRING" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)circ, sb, variant); } if ( rtcircstring_is_empty(ctx, circ) ) { empty_to_wkt_sb(ctx, sb); return; } ptarray_to_wkt_sb(ctx, circ->points, sb, precision, variant); } /* * Multi-points do not wrap their sub-members in parens, unlike other multi-geometries. * MULTPOINT(0 0, 1 1) instead of MULTIPOINT((0 0),(1 1)) */ static void rtmpoint_to_wkt_sb(const RTCTX *ctx, const RTMPOINT *mpoint, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "MULTIPOINT"); /* "MULTIPOINT" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)mpoint, sb, variant); } if ( mpoint->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mpoint->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* We don't want type strings or parens on our subgeoms */ rtpoint_to_wkt_sb(ctx, mpoint->geoms[i], sb, precision, variant | RTWKT_NO_PARENS | RTWKT_NO_TYPE ); } stringbuffer_append(ctx, sb, ")"); } /* * MULTILINESTRING */ static void rtmline_to_wkt_sb(const RTCTX *ctx, const RTMLINE *mline, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "MULTILINESTRING"); /* "MULTILINESTRING" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)mline, sb, variant); } if ( mline->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mline->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* We don't want type strings on our subgeoms */ rtline_to_wkt_sb(ctx, mline->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); } stringbuffer_append(ctx, sb, ")"); } /* * MULTIPOLYGON */ static void rtmpoly_to_wkt_sb(const RTCTX *ctx, const RTMPOLY *mpoly, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "MULTIPOLYGON"); /* "MULTIPOLYGON" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)mpoly, sb, variant); } if ( mpoly->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mpoly->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* We don't want type strings on our subgeoms */ rtpoly_to_wkt_sb(ctx, mpoly->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); } stringbuffer_append(ctx, sb, ")"); } /* * Compound curves provide type information for their curved sub-geometries * but not their linestring sub-geometries. * COMPOUNDCURVE((0 0, 1 1), CURVESTRING(1 1, 2 2, 3 3)) */ static void rtcompound_to_wkt_sb(const RTCTX *ctx, const RTCOMPOUND *comp, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "COMPOUNDCURVE"); /* "COMPOUNDCURVE" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)comp, sb, variant); } if ( comp->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < comp->ngeoms; i++ ) { int type = comp->geoms[i]->type; if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* Linestring subgeoms don't get type identifiers */ if ( type == RTLINETYPE ) { rtline_to_wkt_sb(ctx, (RTLINE*)comp->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); } /* But circstring subgeoms *do* get type identifiers */ else if ( type == RTCIRCSTRINGTYPE ) { rtcircstring_to_wkt_sb(ctx, (RTCIRCSTRING*)comp->geoms[i], sb, precision, variant ); } else { rterror(ctx, "rtcompound_to_wkt_sb: Unknown type received %d - %s", type, rttype_name(ctx, type)); } } stringbuffer_append(ctx, sb, ")"); } /* * Curve polygons provide type information for their curved rings * but not their linestring rings. * CURVEPOLYGON((0 0, 1 1, 0 1, 0 0), CURVESTRING(0 0, 1 1, 0 1, 0.5 1, 0 0)) */ static void rtcurvepoly_to_wkt_sb(const RTCTX *ctx, const RTCURVEPOLY *cpoly, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "CURVEPOLYGON"); /* "CURVEPOLYGON" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)cpoly, sb, variant); } if ( cpoly->nrings < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < cpoly->nrings; i++ ) { int type = cpoly->rings[i]->type; if ( i > 0 ) stringbuffer_append(ctx, sb, ","); switch (type) { case RTLINETYPE: /* Linestring subgeoms don't get type identifiers */ rtline_to_wkt_sb(ctx, (RTLINE*)cpoly->rings[i], sb, precision, variant | RTWKT_NO_TYPE ); break; case RTCIRCSTRINGTYPE: /* But circstring subgeoms *do* get type identifiers */ rtcircstring_to_wkt_sb(ctx, (RTCIRCSTRING*)cpoly->rings[i], sb, precision, variant ); break; case RTCOMPOUNDTYPE: /* And compoundcurve subgeoms *do* get type identifiers */ rtcompound_to_wkt_sb(ctx, (RTCOMPOUND*)cpoly->rings[i], sb, precision, variant ); break; default: rterror(ctx, "rtcurvepoly_to_wkt_sb: Unknown type received %d - %s", type, rttype_name(ctx, type)); } } stringbuffer_append(ctx, sb, ")"); } /* * Multi-curves provide type information for their curved sub-geometries * but not their linear sub-geometries. * MULTICURVE((0 0, 1 1), CURVESTRING(0 0, 1 1, 2 2)) */ static void rtmcurve_to_wkt_sb(const RTCTX *ctx, const RTMCURVE *mcurv, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "MULTICURVE"); /* "MULTICURVE" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)mcurv, sb, variant); } if ( mcurv->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < mcurv->ngeoms; i++ ) { int type = mcurv->geoms[i]->type; if ( i > 0 ) stringbuffer_append(ctx, sb, ","); switch (type) { case RTLINETYPE: /* Linestring subgeoms don't get type identifiers */ rtline_to_wkt_sb(ctx, (RTLINE*)mcurv->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); break; case RTCIRCSTRINGTYPE: /* But circstring subgeoms *do* get type identifiers */ rtcircstring_to_wkt_sb(ctx, (RTCIRCSTRING*)mcurv->geoms[i], sb, precision, variant ); break; case RTCOMPOUNDTYPE: /* And compoundcurve subgeoms *do* get type identifiers */ rtcompound_to_wkt_sb(ctx, (RTCOMPOUND*)mcurv->geoms[i], sb, precision, variant ); break; default: rterror(ctx, "rtmcurve_to_wkt_sb: Unknown type received %d - %s", type, rttype_name(ctx, type)); } } stringbuffer_append(ctx, sb, ")"); } /* * Multi-surfaces provide type information for their curved sub-geometries * but not their linear sub-geometries. * MULTISURFACE(((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) */ static void rtmsurface_to_wkt_sb(const RTCTX *ctx, const RTMSURFACE *msurf, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "MULTISURFACE"); /* "MULTISURFACE" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)msurf, sb, variant); } if ( msurf->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ for ( i = 0; i < msurf->ngeoms; i++ ) { int type = msurf->geoms[i]->type; if ( i > 0 ) stringbuffer_append(ctx, sb, ","); switch (type) { case RTPOLYGONTYPE: /* Linestring subgeoms don't get type identifiers */ rtpoly_to_wkt_sb(ctx, (RTPOLY*)msurf->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); break; case RTCURVEPOLYTYPE: /* But circstring subgeoms *do* get type identifiers */ rtcurvepoly_to_wkt_sb(ctx, (RTCURVEPOLY*)msurf->geoms[i], sb, precision, variant); break; default: rterror(ctx, "rtmsurface_to_wkt_sb: Unknown type received %d - %s", type, rttype_name(ctx, type)); } } stringbuffer_append(ctx, sb, ")"); } /* * Geometry collections provide type information for all their curved sub-geometries * but not their linear sub-geometries. * GEOMETRYCOLLECTION(POLYGON((0 0, 1 1, 1 0, 0 0)), CURVEPOLYGON(CURVESTRING(0 0, 1 1, 2 2, 0 1, 0 0))) */ static void rtcollection_to_wkt_sb(const RTCTX *ctx, const RTCOLLECTION *collection, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "GEOMETRYCOLLECTION"); /* "GEOMETRYCOLLECTION" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)collection, sb, variant); } if ( collection->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are children */ for ( i = 0; i < collection->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); rtgeom_to_wkt_sb(ctx, (RTGEOM*)collection->geoms[i], sb, precision, variant ); } stringbuffer_append(ctx, sb, ")"); } /* * TRIANGLE */ static void rttriangle_to_wkt_sb(const RTCTX *ctx, const RTTRIANGLE *tri, stringbuffer_t *sb, int precision, uint8_t variant) { if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "TRIANGLE"); /* "TRIANGLE" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)tri, sb, variant); } if ( rttriangle_is_empty(ctx, tri) ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); /* Triangles have extraneous brackets */ ptarray_to_wkt_sb(ctx, tri->points, sb, precision, variant); stringbuffer_append(ctx, sb, ")"); } /* * TIN */ static void rttin_to_wkt_sb(const RTCTX *ctx, const RTTIN *tin, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "TIN"); /* "TIN" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)tin, sb, variant); } if ( tin->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } stringbuffer_append(ctx, sb, "("); for ( i = 0; i < tin->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* We don't want type strings on our subgeoms */ rttriangle_to_wkt_sb(ctx, tin->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); } stringbuffer_append(ctx, sb, ")"); } /* * POLYHEDRALSURFACE */ static void rtpsurface_to_wkt_sb(const RTCTX *ctx, const RTPSURFACE *psurf, stringbuffer_t *sb, int precision, uint8_t variant) { int i = 0; if ( ! (variant & RTWKT_NO_TYPE) ) { stringbuffer_append(ctx, sb, "POLYHEDRALSURFACE"); /* "POLYHEDRALSURFACE" */ dimension_qualifiers_to_wkt_sb(ctx, (RTGEOM*)psurf, sb, variant); } if ( psurf->ngeoms < 1 ) { empty_to_wkt_sb(ctx, sb); return; } variant = variant | RTWKT_IS_CHILD; /* Inform the sub-geometries they are childre */ stringbuffer_append(ctx, sb, "("); for ( i = 0; i < psurf->ngeoms; i++ ) { if ( i > 0 ) stringbuffer_append(ctx, sb, ","); /* We don't want type strings on our subgeoms */ rtpoly_to_wkt_sb(ctx, psurf->geoms[i], sb, precision, variant | RTWKT_NO_TYPE ); } stringbuffer_append(ctx, sb, ")"); } /* * Generic GEOMETRY */ static void rtgeom_to_wkt_sb(const RTCTX *ctx, const RTGEOM *geom, stringbuffer_t *sb, int precision, uint8_t variant) { RTDEBUGF(4, "rtgeom_to_wkt_sb: type %s, hasz %d, hasm %d", rttype_name(ctx, geom->type), (geom->type), RTFLAGS_GET_Z(geom->flags)?1:0, RTFLAGS_GET_M(geom->flags)?1:0); switch (geom->type) { case RTPOINTTYPE: rtpoint_to_wkt_sb(ctx, (RTPOINT*)geom, sb, precision, variant); break; case RTLINETYPE: rtline_to_wkt_sb(ctx, (RTLINE*)geom, sb, precision, variant); break; case RTPOLYGONTYPE: rtpoly_to_wkt_sb(ctx, (RTPOLY*)geom, sb, precision, variant); break; case RTMULTIPOINTTYPE: rtmpoint_to_wkt_sb(ctx, (RTMPOINT*)geom, sb, precision, variant); break; case RTMULTILINETYPE: rtmline_to_wkt_sb(ctx, (RTMLINE*)geom, sb, precision, variant); break; case RTMULTIPOLYGONTYPE: rtmpoly_to_wkt_sb(ctx, (RTMPOLY*)geom, sb, precision, variant); break; case RTCOLLECTIONTYPE: rtcollection_to_wkt_sb(ctx, (RTCOLLECTION*)geom, sb, precision, variant); break; case RTCIRCSTRINGTYPE: rtcircstring_to_wkt_sb(ctx, (RTCIRCSTRING*)geom, sb, precision, variant); break; case RTCOMPOUNDTYPE: rtcompound_to_wkt_sb(ctx, (RTCOMPOUND*)geom, sb, precision, variant); break; case RTCURVEPOLYTYPE: rtcurvepoly_to_wkt_sb(ctx, (RTCURVEPOLY*)geom, sb, precision, variant); break; case RTMULTICURVETYPE: rtmcurve_to_wkt_sb(ctx, (RTMCURVE*)geom, sb, precision, variant); break; case RTMULTISURFACETYPE: rtmsurface_to_wkt_sb(ctx, (RTMSURFACE*)geom, sb, precision, variant); break; case RTTRIANGLETYPE: rttriangle_to_wkt_sb(ctx, (RTTRIANGLE*)geom, sb, precision, variant); break; case RTTINTYPE: rttin_to_wkt_sb(ctx, (RTTIN*)geom, sb, precision, variant); break; case RTPOLYHEDRALSURFACETYPE: rtpsurface_to_wkt_sb(ctx, (RTPSURFACE*)geom, sb, precision, variant); break; default: rterror(ctx, "rtgeom_to_wkt_sb: Type %d - %s unsupported.", geom->type, rttype_name(ctx, geom->type)); } } /** * RTWKT emitter function. Allocates a new *char and fills it with the RTWKT * representation. If size_out is not NULL, it will be set to the size of the * allocated *char. * * @param variant Bitmasked value, accepts one of RTWKT_ISO, RTWKT_SFSQL, RTWKT_EXTENDED. * @param precision Number of significant digits in the output doubles. * @param size_out If supplied, will return the size of the returned string, * including the null terminator. */ char* rtgeom_to_wkt(const RTCTX *ctx, const RTGEOM *geom, uint8_t variant, int precision, size_t *size_out) { stringbuffer_t *sb; char *str = NULL; if ( geom == NULL ) return NULL; sb = stringbuffer_create(ctx); /* Extended mode starts with an "SRID=" section for geoms that have one */ if ( (variant & RTWKT_EXTENDED) && rtgeom_has_srid(ctx, geom) ) { stringbuffer_aprintf(ctx, sb, "SRID=%d;", geom->srid); } rtgeom_to_wkt_sb(ctx, geom, sb, precision, variant); if ( stringbuffer_getstring(ctx, sb) == NULL ) { rterror(ctx, "Uh oh"); return NULL; } str = stringbuffer_getstringcopy(ctx, sb); if ( size_out ) *size_out = stringbuffer_getlength(ctx, sb) + 1; stringbuffer_destroy(ctx, sb); return str; } src/rtout_x3d.c000066400000000000000000000701011271715413500137510ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2011-2015 Arrival 3D * **********************************************************************/ /** * @file X3D output routines. * **********************************************************************/ #include #include "librttopo_geom_internal.h" /** defid is the id of the coordinate can be used to hold other elements DEF='abc' transform='' etc. **/ static size_t asx3d3_point_size(const RTCTX *ctx, const RTPOINT *point, char *srs, int precision, int opts, const char *defid); static char * asx3d3_point(const RTCTX *ctx, const RTPOINT *point, char *srs, int precision, int opts, const char *defid); static size_t asx3d3_line_size(const RTCTX *ctx, const RTLINE *line, char *srs, int precision, int opts, const char *defid); static char * asx3d3_line(const RTCTX *ctx, const RTLINE *line, char *srs, int precision, int opts, const char *defid); static size_t asx3d3_poly_size(const RTCTX *ctx, const RTPOLY *poly, char *srs, int precision, int opts, const char *defid); static size_t asx3d3_triangle_size(const RTCTX *ctx, const RTTRIANGLE *triangle, char *srs, int precision, int opts, const char *defid); static char * asx3d3_triangle(const RTCTX *ctx, const RTTRIANGLE *triangle, char *srs, int precision, int opts, const char *defid); static size_t asx3d3_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precisioSn, int opts, const char *defid); static char * asx3d3_multi(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid); static char * asx3d3_psurface(const RTCTX *ctx, const RTPSURFACE *psur, char *srs, int precision, int opts, const char *defid); static char * asx3d3_tin(const RTCTX *ctx, const RTTIN *tin, char *srs, int precision, int opts, const char *defid); static size_t asx3d3_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid); static char * asx3d3_collection(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid); static size_t pointArray_toX3D3(const RTCTX *ctx, RTPOINTARRAY *pa, char *buf, int precision, int opts, int is_closed); static size_t pointArray_X3Dsize(const RTCTX *ctx, RTPOINTARRAY *pa, int precision); /* * VERSION X3D 3.0.2 http://www.web3d.org/specifications/x3d-3.0.dtd */ /* takes a GEOMETRY and returns an X3D representation */ extern char * rtgeom_to_x3d3(const RTCTX *ctx, const RTGEOM *geom, char *srs, int precision, int opts, const char *defid) { int type = geom->type; switch (type) { case RTPOINTTYPE: return asx3d3_point(ctx, (RTPOINT*)geom, srs, precision, opts, defid); case RTLINETYPE: return asx3d3_line(ctx, (RTLINE*)geom, srs, precision, opts, defid); case RTPOLYGONTYPE: { /** We might change this later, but putting a polygon in an indexed face set * seems like the simplest way to go so treat just like a mulitpolygon */ RTCOLLECTION *tmp = (RTCOLLECTION*)rtgeom_as_multi(ctx, geom); char *ret = asx3d3_multi(ctx, tmp, srs, precision, opts, defid); rtcollection_free(ctx, tmp); return ret; } case RTTRIANGLETYPE: return asx3d3_triangle(ctx, (RTTRIANGLE*)geom, srs, precision, opts, defid); case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: return asx3d3_multi(ctx, (RTCOLLECTION*)geom, srs, precision, opts, defid); case RTPOLYHEDRALSURFACETYPE: return asx3d3_psurface(ctx, (RTPSURFACE*)geom, srs, precision, opts, defid); case RTTINTYPE: return asx3d3_tin(ctx, (RTTIN*)geom, srs, precision, opts, defid); case RTCOLLECTIONTYPE: return asx3d3_collection(ctx, (RTCOLLECTION*)geom, srs, precision, opts, defid); default: rterror(ctx, "rtgeom_to_x3d3: '%s' geometry type not supported", rttype_name(ctx, type)); return NULL; } } static size_t asx3d3_point_size(const RTCTX *ctx, const RTPOINT *point, char *srs, int precision, int opts, const char *defid) { int size; /* size_t defidlen = strlen(defid); */ size = pointArray_X3Dsize(ctx, point->point, precision); /* size += ( sizeof("/") + (defidlen*2) ) * 2; */ /* if (srs) size += strlen(srs) + sizeof(" srsName=.."); */ return size; } static size_t asx3d3_point_buf(const RTCTX *ctx, const RTPOINT *point, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr = output; /* int dimension=2; */ /* if (RTFLAGS_GET_Z(point->flags)) dimension = 3; */ /* if ( srs ) { ptr += sprintf(ptr, "<%sPoint srsName=\"%s\">", defid, srs); } else*/ /* ptr += sprintf(ptr, "%s", defid); */ /* ptr += sprintf(ptr, "<%spos>", defid); */ ptr += pointArray_toX3D3(ctx, point->point, ptr, precision, opts, 0); /* ptr += sprintf(ptr, "", defid, defid); */ return (ptr-output); } static char * asx3d3_point(const RTCTX *ctx, const RTPOINT *point, char *srs, int precision, int opts, const char *defid) { char *output; int size; size = asx3d3_point_size(ctx, point, srs, precision, opts, defid); output = rtalloc(ctx, size); asx3d3_point_buf(ctx, point, srs, output, precision, opts, defid); return output; } static size_t asx3d3_line_size(const RTCTX *ctx, const RTLINE *line, char *srs, int precision, int opts, const char *defid) { int size; size_t defidlen = strlen(defid); size = pointArray_X3Dsize(ctx, line->points, precision)*2; if ( X3D_USE_GEOCOORDS(opts) ) { size += ( sizeof("") + defidlen ) * 2; } else { size += ( sizeof("") + defidlen ) * 2; } /* if (srs) size += strlen(srs) + sizeof(" srsName=.."); */ return size; } static size_t asx3d3_line_buf(const RTCTX *ctx, const RTLINE *line, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr=output; /* int dimension=2; */ RTPOINTARRAY *pa; /* if (RTFLAGS_GET_Z(line->flags)) dimension = 3; */ pa = line->points; ptr += sprintf(ptr, "", defid, pa->npoints); if ( X3D_USE_GEOCOORDS(opts) ) ptr += sprintf(ptr, "points, ptr, precision, opts, rtline_is_closed(ctx, (RTLINE *) line)); ptr += sprintf(ptr, "' />"); ptr += sprintf(ptr, ""); return (ptr-output); } static size_t asx3d3_line_coords(const RTCTX *ctx, const RTLINE *line, char *output, int precision, int opts) { char *ptr=output; /* ptr += sprintf(ptr, ""); */ ptr += pointArray_toX3D3(ctx, line->points, ptr, precision, opts, rtline_is_closed(ctx, line)); return (ptr-output); } /* Calculate the coordIndex property of the IndexedLineSet for the multilinestring */ static size_t asx3d3_mline_coordindex(const RTCTX *ctx, const RTMLINE *mgeom, char *output) { char *ptr=output; RTLINE *geom; int i, j, k, si; RTPOINTARRAY *pa; int np; j = 0; for (i=0; i < mgeom->ngeoms; i++) { geom = (RTLINE *) mgeom->geoms[i]; pa = geom->points; np = pa->npoints; si = j; /* start index of first point of linestring */ for (k=0; k < np ; k++) { if (k) { ptr += sprintf(ptr, " "); } /** if the linestring is closed, we put the start point index * for the last vertex to denote use first point * and don't increment the index **/ if (!rtline_is_closed(ctx, geom) || k < (np -1) ) { ptr += sprintf(ptr, "%d", j); j += 1; } else { ptr += sprintf(ptr,"%d", si); } } if (i < (mgeom->ngeoms - 1) ) { ptr += sprintf(ptr, " -1 "); /* separator for each linestring */ } } return (ptr-output); } /* Calculate the coordIndex property of the IndexedLineSet for a multipolygon This is not ideal -- would be really nice to just share this function with psurf, but I'm not smart enough to do that yet*/ static size_t asx3d3_mpoly_coordindex(const RTCTX *ctx, const RTMPOLY *psur, char *output) { char *ptr=output; RTPOLY *patch; int i, j, k, l; int np; j = 0; for (i=0; ingeoms; i++) { patch = (RTPOLY *) psur->geoms[i]; for (l=0; l < patch->nrings; l++) { np = patch->rings[l]->npoints - 1; for (k=0; k < np ; k++) { if (k) { ptr += sprintf(ptr, " "); } ptr += sprintf(ptr, "%d", (j + k)); } j += k; if (l < (patch->nrings - 1) ) { /** @todo TODO: Decide the best way to render holes * Evidentally according to my X3D expert the X3D consortium doesn't really * support holes and it's an issue of argument among many that feel it should. He thinks CAD x3d extensions to spec might. * What he has done and others developing X3D exports to simulate a hole is to cut around it. * So if you have a donut, you would cut it into half and have 2 solid polygons. Not really sure the best way to handle this. * For now will leave it as polygons stacked on top of each other -- which is what we are doing here and perhaps an option * to color differently. It's not ideal but the alternative sounds complicated. **/ ptr += sprintf(ptr, " -1 "); /* separator for each inner ring. Ideally we should probably triangulate and cut around as others do */ } } if (i < (psur->ngeoms - 1) ) { ptr += sprintf(ptr, " -1 "); /* separator for each subgeom */ } } return (ptr-output); } /** Return the linestring as an X3D LineSet */ static char * asx3d3_line(const RTCTX *ctx, const RTLINE *line, char *srs, int precision, int opts, const char *defid) { char *output; int size; size = sizeof("") + asx3d3_line_size(ctx, line, srs, precision, opts, defid); output = rtalloc(ctx, size); asx3d3_line_buf(ctx, line, srs, output, precision, opts, defid); return output; } /** Compute the string space needed for the IndexedFaceSet representation of the polygon **/ static size_t asx3d3_poly_size(const RTCTX *ctx, const RTPOLY *poly, char *srs, int precision, int opts, const char *defid) { size_t size; size_t defidlen = strlen(defid); int i; size = ( sizeof("") + (defidlen*3) ) * 2 + 6 * (poly->nrings - 1); for (i=0; inrings; i++) size += pointArray_X3Dsize(ctx, poly->rings[i], precision); return size; } /** Compute the X3D coordinates of the polygon **/ static size_t asx3d3_poly_buf(const RTCTX *ctx, const RTPOLY *poly, char *srs, char *output, int precision, int opts, int is_patch, const char *defid) { int i; char *ptr=output; ptr += pointArray_toX3D3(ctx, poly->rings[0], ptr, precision, opts, 1); for (i=1; inrings; i++) { ptr += sprintf(ptr, " "); /* inner ring points start */ ptr += pointArray_toX3D3(ctx, poly->rings[i], ptr, precision, opts,1); } return (ptr-output); } static size_t asx3d3_triangle_size(const RTCTX *ctx, const RTTRIANGLE *triangle, char *srs, int precision, int opts, const char *defid) { size_t size; size_t defidlen = strlen(defid); /** 6 for the 3 sides and space to separate each side **/ size = sizeof("") + defidlen + 6; size += pointArray_X3Dsize(ctx, triangle->points, precision); return size; } static size_t asx3d3_triangle_buf(const RTCTX *ctx, const RTTRIANGLE *triangle, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr=output; ptr += pointArray_toX3D3(ctx, triangle->points, ptr, precision, opts, 1); return (ptr-output); } static char * asx3d3_triangle(const RTCTX *ctx, const RTTRIANGLE *triangle, char *srs, int precision, int opts, const char *defid) { char *output; int size; size = asx3d3_triangle_size(ctx, triangle, srs, precision, opts, defid); output = rtalloc(ctx, size); asx3d3_triangle_buf(ctx, triangle, srs, output, precision, opts, defid); return output; } /** * Compute max size required for X3D version of this * inspected geometry. Will recurse when needed. * Don't call this with single-geoms inspected. */ static size_t asx3d3_multi_size(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid) { int i; size_t size; size_t defidlen = strlen(defid); RTGEOM *subgeom; /* the longest possible multi version needs to hold DEF=defid and coordinate breakout */ if ( X3D_USE_GEOCOORDS(opts) ) size = sizeof(""); else size = sizeof("") + defidlen; /* if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); */ for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { /* size += ( sizeof("point=''") + defidlen ) * 2; */ size += asx3d3_point_size(ctx, (RTPOINT*)subgeom, 0, precision, opts, defid); } else if (subgeom->type == RTLINETYPE) { /* size += ( sizeof("/") + defidlen ) * 2; */ size += asx3d3_line_size(ctx, (RTLINE*)subgeom, 0, precision, opts, defid); } else if (subgeom->type == RTPOLYGONTYPE) { /* size += ( sizeof("/") + defidlen ) * 2; */ size += asx3d3_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, opts, defid); } } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asx3d3_multi_buf(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr, *x3dtype; int i; int dimension=2; if (RTFLAGS_GET_Z(col->flags)) dimension = 3; RTGEOM *subgeom; ptr = output; x3dtype=""; switch (col->type) { case RTMULTIPOINTTYPE: x3dtype = "PointSet"; if ( dimension == 2 ){ /** Use Polypoint2D instead **/ x3dtype = "Polypoint2D"; ptr += sprintf(ptr, "<%s %s point='", x3dtype, defid); } else { ptr += sprintf(ptr, "<%s %s>", x3dtype, defid); } break; case RTMULTILINETYPE: x3dtype = "IndexedLineSet"; ptr += sprintf(ptr, "<%s %s coordIndex='", x3dtype, defid); ptr += asx3d3_mline_coordindex(ctx, (const RTMLINE *)col, ptr); ptr += sprintf(ptr, "'>"); break; case RTMULTIPOLYGONTYPE: x3dtype = "IndexedFaceSet"; ptr += sprintf(ptr, "<%s %s coordIndex='", x3dtype, defid); ptr += asx3d3_mpoly_coordindex(ctx, (const RTMPOLY *)col, ptr); ptr += sprintf(ptr, "'>"); break; default: rterror(ctx, "asx3d3_multi_buf: '%s' geometry type not supported", rttype_name(ctx, col->type)); return 0; } if (dimension == 3){ if ( X3D_USE_GEOCOORDS(opts) ) ptr += sprintf(ptr, "ngeoms; i++) { subgeom = col->geoms[i]; if (subgeom->type == RTPOINTTYPE) { ptr += asx3d3_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, opts, defid); ptr += sprintf(ptr, " "); } else if (subgeom->type == RTLINETYPE) { ptr += asx3d3_line_coords(ctx, (RTLINE*)subgeom, ptr, precision, opts); ptr += sprintf(ptr, " "); } else if (subgeom->type == RTPOLYGONTYPE) { ptr += asx3d3_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, opts, 0, defid); ptr += sprintf(ptr, " "); } } /* Close outmost tag */ if (dimension == 3){ ptr += sprintf(ptr, "' />", x3dtype); } else { ptr += sprintf(ptr, "' />"); } return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asx3d3_multi(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid) { char *x3d; size_t size; size = asx3d3_multi_size(ctx, col, srs, precision, opts, defid); x3d = rtalloc(ctx, size); asx3d3_multi_buf(ctx, col, srs, x3d, precision, opts, defid); return x3d; } static size_t asx3d3_psurface_size(const RTCTX *ctx, const RTPSURFACE *psur, char *srs, int precision, int opts, const char *defid) { int i; size_t size; size_t defidlen = strlen(defid); if ( X3D_USE_GEOCOORDS(opts) ) size = sizeof("") + defidlen; else size = sizeof("") + defidlen; for (i=0; ingeoms; i++) { size += asx3d3_poly_size(ctx, psur->geoms[i], 0, precision, opts, defid)*5; /** need to make space for coordIndex values too including -1 separating each poly**/ } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asx3d3_psurface_buf(const RTCTX *ctx, const RTPSURFACE *psur, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr; int i; int j; int k; int np; RTPOLY *patch; ptr = output; /* Open outmost tag */ ptr += sprintf(ptr, ""); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asx3d3_psurface(const RTCTX *ctx, const RTPSURFACE *psur, char *srs, int precision, int opts, const char *defid) { char *x3d; size_t size; size = asx3d3_psurface_size(ctx, psur, srs, precision, opts, defid); x3d = rtalloc(ctx, size); asx3d3_psurface_buf(ctx, psur, srs, x3d, precision, opts, defid); return x3d; } static size_t asx3d3_tin_size(const RTCTX *ctx, const RTTIN *tin, char *srs, int precision, int opts, const char *defid) { int i; size_t size; size_t defidlen = strlen(defid); /* int dimension=2; */ /** Need to make space for size of additional attributes, ** the coordIndex has a value for each edge for each triangle plus a space to separate so we need at least that much extra room ***/ size = sizeof("") + defidlen + tin->ngeoms*12; for (i=0; ingeoms; i++) { size += (asx3d3_triangle_size(ctx, tin->geoms[i], 0, precision, opts, defid) * 20); /** 3 is to make space for coordIndex **/ } return size; } /* * Don't call this with single-geoms inspected! */ static size_t asx3d3_tin_buf(const RTCTX *ctx, const RTTIN *tin, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr; int i; int k; /* int dimension=2; */ ptr = output; ptr += sprintf(ptr, ""); return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asx3d3_tin(const RTCTX *ctx, const RTTIN *tin, char *srs, int precision, int opts, const char *defid) { char *x3d; size_t size; size = asx3d3_tin_size(ctx, tin, srs, precision, opts, defid); x3d = rtalloc(ctx, size); asx3d3_tin_buf(ctx, tin, srs, x3d, precision, opts, defid); return x3d; } static size_t asx3d3_collection_size(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid) { int i; size_t size; size_t defidlen = strlen(defid); RTGEOM *subgeom; /* size = sizeof("") + defidlen*2; */ size = defidlen*2; /** if ( srs ) size += strlen(srs) + sizeof(" srsName=.."); **/ for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; size += ( sizeof("") + defidlen ) * 2; /** for collections we need to wrap each in a shape tag to make valid **/ if ( subgeom->type == RTPOINTTYPE ) { size += asx3d3_point_size(ctx, (RTPOINT*)subgeom, 0, precision, opts, defid); } else if ( subgeom->type == RTLINETYPE ) { size += asx3d3_line_size(ctx, (RTLINE*)subgeom, 0, precision, opts, defid); } else if ( subgeom->type == RTPOLYGONTYPE ) { size += asx3d3_poly_size(ctx, (RTPOLY*)subgeom, 0, precision, opts, defid); } else if ( subgeom->type == RTTINTYPE ) { size += asx3d3_tin_size(ctx, (RTTIN*)subgeom, 0, precision, opts, defid); } else if ( subgeom->type == RTPOLYHEDRALSURFACETYPE ) { size += asx3d3_psurface_size(ctx, (RTPSURFACE*)subgeom, 0, precision, opts, defid); } else if ( rtgeom_is_collection(ctx, subgeom) ) { size += asx3d3_multi_size(ctx, (RTCOLLECTION*)subgeom, 0, precision, opts, defid); } else rterror(ctx, "asx3d3_collection_size: unknown geometry type"); } return size; } static size_t asx3d3_collection_buf(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, char *output, int precision, int opts, const char *defid) { char *ptr; int i; RTGEOM *subgeom; ptr = output; /* Open outmost tag */ /** @TODO: decide if we need outtermost tags, this one was just a copy from gml so is wrong **/ #ifdef PGIS_X3D_OUTERMOST_TAGS if ( srs ) { ptr += sprintf(ptr, "<%sMultiGeometry srsName=\"%s\">", defid, srs); } else { ptr += sprintf(ptr, "<%sMultiGeometry>", defid); } #endif for (i=0; ingeoms; i++) { subgeom = col->geoms[i]; ptr += sprintf(ptr, "", defid); if ( subgeom->type == RTPOINTTYPE ) { ptr += asx3d3_point_buf(ctx, (RTPOINT*)subgeom, 0, ptr, precision, opts, defid); } else if ( subgeom->type == RTLINETYPE ) { ptr += asx3d3_line_buf(ctx, (RTLINE*)subgeom, 0, ptr, precision, opts, defid); } else if ( subgeom->type == RTPOLYGONTYPE ) { ptr += asx3d3_poly_buf(ctx, (RTPOLY*)subgeom, 0, ptr, precision, opts, 0, defid); } else if ( subgeom->type == RTTINTYPE ) { ptr += asx3d3_tin_buf(ctx, (RTTIN*)subgeom, srs, ptr, precision, opts, defid); } else if ( subgeom->type == RTPOLYHEDRALSURFACETYPE ) { ptr += asx3d3_psurface_buf(ctx, (RTPSURFACE*)subgeom, srs, ptr, precision, opts, defid); } else if ( rtgeom_is_collection(ctx, subgeom) ) { if ( subgeom->type == RTCOLLECTIONTYPE ) ptr += asx3d3_collection_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, opts, defid); else ptr += asx3d3_multi_buf(ctx, (RTCOLLECTION*)subgeom, 0, ptr, precision, opts, defid); } else rterror(ctx, "asx3d3_collection_buf: unknown geometry type"); ptr += printf(ptr, ""); } /* Close outmost tag */ #ifdef PGIS_X3D_OUTERMOST_TAGS ptr += sprintf(ptr, "", defid); #endif return (ptr-output); } /* * Don't call this with single-geoms inspected! */ static char * asx3d3_collection(const RTCTX *ctx, const RTCOLLECTION *col, char *srs, int precision, int opts, const char *defid) { char *x3d; size_t size; size = asx3d3_collection_size(ctx, col, srs, precision, opts, defid); x3d = rtalloc(ctx, size); asx3d3_collection_buf(ctx, col, srs, x3d, precision, opts, defid); return x3d; } /** In X3D3, coordinates are separated by a space separator */ static size_t pointArray_toX3D3(const RTCTX *ctx, RTPOINTARRAY *pa, char *output, int precision, int opts, int is_closed) { int i; char *ptr; char x[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char y[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; char z[OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION+1]; ptr = output; if ( ! RTFLAGS_GET_Z(pa->flags) ) { for (i=0; inpoints; i++) { /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/ if ( !is_closed || i < (pa->npoints - 1) ) { RTPOINT2D pt; rt_getPoint2d_p(ctx, pa, i, &pt); if (fabs(pt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x); else sprintf(x, "%g", pt.x); trim_trailing_zeros(ctx, x); if (fabs(pt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt.y); else sprintf(y, "%g", pt.y); trim_trailing_zeros(ctx, y); if ( i ) ptr += sprintf(ptr, " "); if ( ( opts & RT_X3D_FLIP_XY) ) ptr += sprintf(ptr, "%s %s", y, x); else ptr += sprintf(ptr, "%s %s", x, y); } } } else { for (i=0; inpoints; i++) { /** Only output the point if it is not the last point of a closed object or it is a non-closed type **/ if ( !is_closed || i < (pa->npoints - 1) ) { RTPOINT4D pt; rt_getPoint4d_p(ctx, pa, i, &pt); if (fabs(pt.x) < OUT_MAX_DOUBLE) sprintf(x, "%.*f", precision, pt.x); else sprintf(x, "%g", pt.x); trim_trailing_zeros(ctx, x); if (fabs(pt.y) < OUT_MAX_DOUBLE) sprintf(y, "%.*f", precision, pt.y); else sprintf(y, "%g", pt.y); trim_trailing_zeros(ctx, y); if (fabs(pt.z) < OUT_MAX_DOUBLE) sprintf(z, "%.*f", precision, pt.z); else sprintf(z, "%g", pt.z); trim_trailing_zeros(ctx, z); if ( i ) ptr += sprintf(ptr, " "); if ( ( opts & RT_X3D_FLIP_XY) ) ptr += sprintf(ptr, "%s %s %s", y, x, z); else ptr += sprintf(ptr, "%s %s %s", x, y, z); } } } return ptr-output; } /** * Returns maximum size of rendered pointarray in bytes. */ static size_t pointArray_X3Dsize(const RTCTX *ctx, RTPOINTARRAY *pa, int precision) { if (RTFLAGS_NDIMS(pa->flags) == 2) return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(" ")) * 2 * pa->npoints; return (OUT_MAX_DIGS_DOUBLE + precision + sizeof(" ")) * 3 * pa->npoints; } src/rtpoint.c000066400000000000000000000165111271715413500135220ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "rttopo_config.h" /*#define RTGEOM_DEBUG_LEVEL 4*/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /* * Convenience functions to hide the RTPOINTARRAY * TODO: obsolete this */ int rtpoint_getPoint2d_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT2D *out) { return rt_getPoint2d_p(ctx, point->point, 0, out); } /* convenience functions to hide the RTPOINTARRAY */ int rtpoint_getPoint3dz_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT3DZ *out) { return rt_getPoint3dz_p(ctx, point->point,0,out); } int rtpoint_getPoint3dm_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT3DM *out) { return rt_getPoint3dm_p(ctx, point->point,0,out); } int rtpoint_getPoint4d_p(const RTCTX *ctx, const RTPOINT *point, RTPOINT4D *out) { return rt_getPoint4d_p(ctx, point->point,0,out); } double rtpoint_get_x(const RTCTX *ctx, const RTPOINT *point) { RTPOINT4D pt; if ( rtpoint_is_empty(ctx, point) ) rterror(ctx, "rtpoint_get_x called with empty geometry"); rt_getPoint4d_p(ctx, point->point, 0, &pt); return pt.x; } double rtpoint_get_y(const RTCTX *ctx, const RTPOINT *point) { RTPOINT4D pt; if ( rtpoint_is_empty(ctx, point) ) rterror(ctx, "rtpoint_get_y called with empty geometry"); rt_getPoint4d_p(ctx, point->point, 0, &pt); return pt.y; } double rtpoint_get_z(const RTCTX *ctx, const RTPOINT *point) { RTPOINT4D pt; if ( rtpoint_is_empty(ctx, point) ) rterror(ctx, "rtpoint_get_z called with empty geometry"); if ( ! RTFLAGS_GET_Z(point->flags) ) rterror(ctx, "rtpoint_get_z called without z dimension"); rt_getPoint4d_p(ctx, point->point, 0, &pt); return pt.z; } double rtpoint_get_m(const RTCTX *ctx, const RTPOINT *point) { RTPOINT4D pt; if ( rtpoint_is_empty(ctx, point) ) rterror(ctx, "rtpoint_get_m called with empty geometry"); if ( ! RTFLAGS_GET_M(point->flags) ) rterror(ctx, "rtpoint_get_m called without m dimension"); rt_getPoint4d_p(ctx, point->point, 0, &pt); return pt.m; } /* * Construct a new point. point will not be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ RTPOINT * rtpoint_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *point) { RTPOINT *result; uint8_t flags = 0; if (point == NULL) return NULL; /* error */ result = rtalloc(ctx, sizeof(RTPOINT)); result->type = RTPOINTTYPE; RTFLAGS_SET_Z(flags, RTFLAGS_GET_Z(point->flags)); RTFLAGS_SET_M(flags, RTFLAGS_GET_M(point->flags)); RTFLAGS_SET_BBOX(flags, bbox?1:0); result->flags = flags; result->srid = srid; result->point = point; result->bbox = bbox; return result; } RTPOINT * rtpoint_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTPOINT *result = rtalloc(ctx, sizeof(RTPOINT)); result->type = RTPOINTTYPE; result->flags = gflags(ctx, hasz, hasm, 0); result->srid = srid; result->point = ptarray_construct(ctx, hasz, hasm, 0); result->bbox = NULL; return result; } RTPOINT * rtpoint_make2d(const RTCTX *ctx, int srid, double x, double y) { RTPOINT4D p = {x, y, 0.0, 0.0}; RTPOINTARRAY *pa = ptarray_construct_empty(ctx, 0, 0, 1); ptarray_append_point(ctx, pa, &p, RT_TRUE); return rtpoint_construct(ctx, srid, NULL, pa); } RTPOINT * rtpoint_make3dz(const RTCTX *ctx, int srid, double x, double y, double z) { RTPOINT4D p = {x, y, z, 0.0}; RTPOINTARRAY *pa = ptarray_construct_empty(ctx, 1, 0, 1); ptarray_append_point(ctx, pa, &p, RT_TRUE); return rtpoint_construct(ctx, srid, NULL, pa); } RTPOINT * rtpoint_make3dm(const RTCTX *ctx, int srid, double x, double y, double m) { RTPOINT4D p = {x, y, 0.0, m}; RTPOINTARRAY *pa = ptarray_construct_empty(ctx, 0, 1, 1); ptarray_append_point(ctx, pa, &p, RT_TRUE); return rtpoint_construct(ctx, srid, NULL, pa); } RTPOINT * rtpoint_make4d(const RTCTX *ctx, int srid, double x, double y, double z, double m) { RTPOINT4D p = {x, y, z, m}; RTPOINTARRAY *pa = ptarray_construct_empty(ctx, 1, 1, 1); ptarray_append_point(ctx, pa, &p, RT_TRUE); return rtpoint_construct(ctx, srid, NULL, pa); } RTPOINT * rtpoint_make(const RTCTX *ctx, int srid, int hasz, int hasm, const RTPOINT4D *p) { RTPOINTARRAY *pa = ptarray_construct_empty(ctx, hasz, hasm, 1); ptarray_append_point(ctx, pa, p, RT_TRUE); return rtpoint_construct(ctx, srid, NULL, pa); } void rtpoint_free(const RTCTX *ctx, RTPOINT *pt) { if ( ! pt ) return; if ( pt->bbox ) rtfree(ctx, pt->bbox); if ( pt->point ) ptarray_free(ctx, pt->point); rtfree(ctx, pt); } void printRTPOINT(const RTCTX *ctx, RTPOINT *point) { rtnotice(ctx, "RTPOINT {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(point->flags)); rtnotice(ctx, " BBOX = %i", RTFLAGS_GET_BBOX(point->flags) ? 1 : 0 ); rtnotice(ctx, " SRID = %i", (int)point->srid); printPA(ctx, point->point); rtnotice(ctx, "}"); } /* @brief Clone RTPOINT object. Serialized point lists are not copied. * * @see ptarray_clone */ RTPOINT * rtpoint_clone(const RTCTX *ctx, const RTPOINT *g) { RTPOINT *ret = rtalloc(ctx, sizeof(RTPOINT)); RTDEBUG(2, "rtpoint_clone called"); memcpy(ret, g, sizeof(RTPOINT)); ret->point = ptarray_clone(ctx, g->point); if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); return ret; } void rtpoint_release(const RTCTX *ctx, RTPOINT *rtpoint) { rtgeom_release(ctx, rtpoint_as_rtgeom(ctx, rtpoint)); } /* check coordinate equality */ char rtpoint_same(const RTCTX *ctx, const RTPOINT *p1, const RTPOINT *p2) { return ptarray_same(ctx, p1->point, p2->point); } RTPOINT* rtpoint_force_dims(const RTCTX *ctx, const RTPOINT *point, int hasz, int hasm) { RTPOINTARRAY *pdims = NULL; RTPOINT *pointout; /* Return 2D empty */ if( rtpoint_is_empty(ctx, point) ) { pointout = rtpoint_construct_empty(ctx, point->srid, hasz, hasm); } else { /* Artays we duplicate the ptarray and return */ pdims = ptarray_force_dims(ctx, point->point, hasz, hasm); pointout = rtpoint_construct(ctx, point->srid, NULL, pdims); } pointout->type = point->type; return pointout; } int rtpoint_is_empty(const RTCTX *ctx, const RTPOINT *point) { if ( ! point->point || point->point->npoints < 1 ) return RT_TRUE; return RT_FALSE; } RTPOINT * rtpoint_grid(const RTCTX *ctx, const RTPOINT *point, const gridspec *grid) { RTPOINTARRAY *opa = ptarray_grid(ctx, point->point, grid); return rtpoint_construct(ctx, point->srid, NULL, opa); } src/rtpoly.c000066400000000000000000000351271271715413500133600ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2012 Sandro Santilli * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ /* basic RTPOLY manipulation */ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #define CHECK_POLY_RINGS_ZM 1 /* construct a new RTPOLY. arrays (points/points per ring) will NOT be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ RTPOLY* rtpoly_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, uint32_t nrings, RTPOINTARRAY **points) { RTPOLY *result; int hasz, hasm; #ifdef CHECK_POLY_RINGS_ZM char zm; uint32_t i; #endif if ( nrings < 1 ) rterror(ctx, "rtpoly_construct: need at least 1 ring"); hasz = RTFLAGS_GET_Z(points[0]->flags); hasm = RTFLAGS_GET_M(points[0]->flags); #ifdef CHECK_POLY_RINGS_ZM zm = RTFLAGS_GET_ZM(points[0]->flags); for (i=1; iflags) ) rterror(ctx, "rtpoly_construct: mixed dimensioned rings"); } #endif result = (RTPOLY*) rtalloc(ctx, sizeof(RTPOLY)); result->type = RTPOLYGONTYPE; result->flags = gflags(ctx, hasz, hasm, 0); RTFLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->nrings = nrings; result->maxrings = nrings; result->rings = points; result->bbox = bbox; return result; } RTPOLY* rtpoly_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTPOLY *result = rtalloc(ctx, sizeof(RTPOLY)); result->type = RTPOLYGONTYPE; result->flags = gflags(ctx, hasz,hasm,0); result->srid = srid; result->nrings = 0; result->maxrings = 1; /* Allocate room for ring, just in case. */ result->rings = rtalloc(ctx, result->maxrings * sizeof(RTPOINTARRAY*)); result->bbox = NULL; return result; } void rtpoly_free(const RTCTX *ctx, RTPOLY *poly) { int t; if( ! poly ) return; if ( poly->bbox ) rtfree(ctx, poly->bbox); for (t=0; tnrings; t++) { if ( poly->rings[t] ) ptarray_free(ctx, poly->rings[t]); } if ( poly->rings ) rtfree(ctx, poly->rings); rtfree(ctx, poly); } void printRTPOLY(const RTCTX *ctx, RTPOLY *poly) { int t; rtnotice(ctx, "RTPOLY {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(poly->flags)); rtnotice(ctx, " SRID = %i", (int)poly->srid); rtnotice(ctx, " nrings = %i", (int)poly->nrings); for (t=0; tnrings; t++) { rtnotice(ctx, " RING # %i :",t); printPA(ctx, poly->rings[t]); } rtnotice(ctx, "}"); } /* @brief Clone RTLINE object. Serialized point lists are not copied. * * @see ptarray_clone */ RTPOLY * rtpoly_clone(const RTCTX *ctx, const RTPOLY *g) { int i; RTPOLY *ret = rtalloc(ctx, sizeof(RTPOLY)); memcpy(ret, g, sizeof(RTPOLY)); ret->rings = rtalloc(ctx, sizeof(RTPOINTARRAY *)*g->nrings); for ( i = 0; i < g->nrings; i++ ) { ret->rings[i] = ptarray_clone(ctx, g->rings[i]); } if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); return ret; } /* Deep clone RTPOLY object. RTPOINTARRAY are copied, as is ring array */ RTPOLY * rtpoly_clone_deep(const RTCTX *ctx, const RTPOLY *g) { int i; RTPOLY *ret = rtalloc(ctx, sizeof(RTPOLY)); memcpy(ret, g, sizeof(RTPOLY)); if ( g->bbox ) ret->bbox = gbox_copy(ctx, g->bbox); ret->rings = rtalloc(ctx, sizeof(RTPOINTARRAY *)*g->nrings); for ( i = 0; i < ret->nrings; i++ ) { ret->rings[i] = ptarray_clone_deep(ctx, g->rings[i]); } RTFLAGS_SET_READONLY(ret->flags,0); return ret; } /** * Add a ring to a polygon. Point array will be referenced, not copied. */ int rtpoly_add_ring(const RTCTX *ctx, RTPOLY *poly, RTPOINTARRAY *pa) { if( ! poly || ! pa ) return RT_FAILURE; /* We have used up our storage, add some more. */ if( poly->nrings >= poly->maxrings ) { int new_maxrings = 2 * (poly->nrings + 1); poly->rings = rtrealloc(ctx, poly->rings, new_maxrings * sizeof(RTPOINTARRAY*)); poly->maxrings = new_maxrings; } /* Add the new ring entry. */ poly->rings[poly->nrings] = pa; poly->nrings++; return RT_SUCCESS; } void rtpoly_force_clockwise(const RTCTX *ctx, RTPOLY *poly) { int i; /* No-op empties */ if ( rtpoly_is_empty(ctx, poly) ) return; /* External ring */ if ( ptarray_isccw(ctx, poly->rings[0]) ) ptarray_reverse(ctx, poly->rings[0]); /* Internal rings */ for (i=1; inrings; i++) if ( ! ptarray_isccw(ctx, poly->rings[i]) ) ptarray_reverse(ctx, poly->rings[i]); } void rtpoly_release(const RTCTX *ctx, RTPOLY *rtpoly) { rtgeom_release(ctx, rtpoly_as_rtgeom(ctx, rtpoly)); } void rtpoly_reverse(const RTCTX *ctx, RTPOLY *poly) { int i; if ( rtpoly_is_empty(ctx, poly) ) return; for (i=0; inrings; i++) ptarray_reverse(ctx, poly->rings[i]); } RTPOLY * rtpoly_segmentize2d(const RTCTX *ctx, RTPOLY *poly, double dist) { RTPOINTARRAY **newrings; uint32_t i; newrings = rtalloc(ctx, sizeof(RTPOINTARRAY *)*poly->nrings); for (i=0; inrings; i++) { newrings[i] = ptarray_segmentize2d(ctx, poly->rings[i], dist); if ( ! newrings[i] ) { while (i--) ptarray_free(ctx, newrings[i]); rtfree(ctx, newrings); return NULL; } } return rtpoly_construct(ctx, poly->srid, NULL, poly->nrings, newrings); } /* * check coordinate equality * ring and coordinate order is considered */ char rtpoly_same(const RTCTX *ctx, const RTPOLY *p1, const RTPOLY *p2) { uint32_t i; if ( p1->nrings != p2->nrings ) return 0; for (i=0; inrings; i++) { if ( ! ptarray_same(ctx, p1->rings[i], p2->rings[i]) ) return 0; } return 1; } /* * Construct a polygon from a RTLINE being * the shell and an array of RTLINE (possibly NULL) being holes. * Pointarrays from intput geoms are cloned. * SRID must be the same for each input line. * Input lines must have at least 4 points, and be closed. */ RTPOLY * rtpoly_from_rtlines(const RTCTX *ctx, const RTLINE *shell, uint32_t nholes, const RTLINE **holes) { uint32_t nrings; RTPOINTARRAY **rings = rtalloc(ctx, (nholes+1)*sizeof(RTPOINTARRAY *)); int srid = shell->srid; RTPOLY *ret; if ( shell->points->npoints < 4 ) rterror(ctx, "rtpoly_from_rtlines: shell must have at least 4 points"); if ( ! ptarray_is_closed_2d(ctx, shell->points) ) rterror(ctx, "rtpoly_from_rtlines: shell must be closed"); rings[0] = ptarray_clone_deep(ctx, shell->points); for (nrings=1; nrings<=nholes; nrings++) { const RTLINE *hole = holes[nrings-1]; if ( hole->srid != srid ) rterror(ctx, "rtpoly_from_rtlines: mixed SRIDs in input lines"); if ( hole->points->npoints < 4 ) rterror(ctx, "rtpoly_from_rtlines: holes must have at least 4 points"); if ( ! ptarray_is_closed_2d(ctx, hole->points) ) rterror(ctx, "rtpoly_from_rtlines: holes must be closed"); rings[nrings] = ptarray_clone_deep(ctx, hole->points); } ret = rtpoly_construct(ctx, srid, NULL, nrings, rings); return ret; } RTGEOM* rtpoly_remove_repeated_points(const RTCTX *ctx, const RTPOLY *poly, double tolerance) { uint32_t i; RTPOINTARRAY **newrings; newrings = rtalloc(ctx, sizeof(RTPOINTARRAY *)*poly->nrings); for (i=0; inrings; i++) { newrings[i] = ptarray_remove_repeated_points_minpoints(ctx, poly->rings[i], tolerance, 4); } return (RTGEOM*)rtpoly_construct(ctx, poly->srid, poly->bbox ? gbox_copy(ctx, poly->bbox) : NULL, poly->nrings, newrings); } RTPOLY* rtpoly_force_dims(const RTCTX *ctx, const RTPOLY *poly, int hasz, int hasm) { RTPOLY *polyout; /* Return 2D empty */ if( rtpoly_is_empty(ctx, poly) ) { polyout = rtpoly_construct_empty(ctx, poly->srid, hasz, hasm); } else { RTPOINTARRAY **rings = NULL; int i; rings = rtalloc(ctx, sizeof(RTPOINTARRAY*) * poly->nrings); for( i = 0; i < poly->nrings; i++ ) { rings[i] = ptarray_force_dims(ctx, poly->rings[i], hasz, hasm); } polyout = rtpoly_construct(ctx, poly->srid, NULL, poly->nrings, rings); } polyout->type = poly->type; return polyout; } int rtpoly_is_empty(const RTCTX *ctx, const RTPOLY *poly) { if ( (poly->nrings < 1) || (!poly->rings) || (!poly->rings[0]) || (poly->rings[0]->npoints < 1) ) return RT_TRUE; return RT_FALSE; } int rtpoly_count_vertices(const RTCTX *ctx, RTPOLY *poly) { int i = 0; int v = 0; /* vertices */ assert(poly); for ( i = 0; i < poly->nrings; i ++ ) { v += poly->rings[i]->npoints; } return v; } RTPOLY* rtpoly_simplify(const RTCTX *ctx, const RTPOLY *ipoly, double dist, int preserve_collapsed) { int i; RTPOLY *opoly = rtpoly_construct_empty(ctx, ipoly->srid, RTFLAGS_GET_Z(ipoly->flags), RTFLAGS_GET_M(ipoly->flags)); RTDEBUGF(2, "%s: simplifying polygon with %d rings", __func__, ipoly->nrings); if ( rtpoly_is_empty(ctx, ipoly) ) { rtpoly_free(ctx, opoly); return NULL; } for ( i = 0; i < ipoly->nrings; i++ ) { RTPOINTARRAY *opts; int minvertices = 0; /* We'll still let holes collapse, but if we're preserving */ /* and this is a shell, we ensure it is kept */ if ( preserve_collapsed && i == 0 ) minvertices = 4; opts = ptarray_simplify(ctx, ipoly->rings[i], dist, minvertices); RTDEBUGF(3, "ring%d simplified from %d to %d points", i, ipoly->rings[i]->npoints, opts->npoints); /* Less points than are needed to form a closed ring, we can't use this */ if ( opts->npoints < 4 ) { RTDEBUGF(3, "ring%d skipped (% pts)", i, opts->npoints); ptarray_free(ctx, opts); if ( i ) continue; else break; /* Don't scan holes if shell is collapsed */ } /* Add ring to simplified polygon */ if( rtpoly_add_ring(ctx, opoly, opts) == RT_FAILURE ) { rtpoly_free(ctx, opoly); return NULL; } } RTDEBUGF(3, "simplified polygon with %d rings", ipoly->nrings); opoly->type = ipoly->type; if( rtpoly_is_empty(ctx, opoly) ) { rtpoly_free(ctx, opoly); return NULL; } return opoly; } /** * Find the area of the outer ring - sum (area of inner rings). */ double rtpoly_area(const RTCTX *ctx, const RTPOLY *poly) { double poly_area = 0.0; int i; if ( ! poly ) rterror(ctx, "rtpoly_area called with null polygon pointer!"); for ( i=0; i < poly->nrings; i++ ) { RTPOINTARRAY *ring = poly->rings[i]; double ringarea = 0.0; /* Empty or messed-up ring. */ if ( ring->npoints < 3 ) continue; ringarea = fabs(ptarray_signed_area(ctx, ring)); if ( i == 0 ) /* Outer ring, positive area! */ poly_area += ringarea; else /* Inner ring, negative area! */ poly_area -= ringarea; } return poly_area; } /** * Compute the sum of polygon rings length. * Could use a more numerically stable calculator... */ double rtpoly_perimeter(const RTCTX *ctx, const RTPOLY *poly) { double result=0.0; int i; RTDEBUGF(2, "in rtgeom_polygon_perimeter (%d rings)", poly->nrings); for (i=0; inrings; i++) result += ptarray_length(ctx, poly->rings[i]); return result; } /** * Compute the sum of polygon rings length (forcing 2d computation). * Could use a more numerically stable calculator... */ double rtpoly_perimeter_2d(const RTCTX *ctx, const RTPOLY *poly) { double result=0.0; int i; RTDEBUGF(2, "in rtgeom_polygon_perimeter (%d rings)", poly->nrings); for (i=0; inrings; i++) result += ptarray_length_2d(ctx, poly->rings[i]); return result; } int rtpoly_is_closed(const RTCTX *ctx, const RTPOLY *poly) { int i = 0; if ( poly->nrings == 0 ) return RT_TRUE; for ( i = 0; i < poly->nrings; i++ ) { if (RTFLAGS_GET_Z(poly->flags)) { if ( ! ptarray_is_closed_3d(ctx, poly->rings[i]) ) return RT_FALSE; } else { if ( ! ptarray_is_closed_2d(ctx, poly->rings[i]) ) return RT_FALSE; } } return RT_TRUE; } int rtpoly_startpoint(const RTCTX *ctx, const RTPOLY* poly, RTPOINT4D* pt) { if ( poly->nrings < 1 ) return RT_FAILURE; return ptarray_startpoint(ctx, poly->rings[0], pt); } int rtpoly_contains_point(const RTCTX *ctx, const RTPOLY *poly, const RTPOINT2D *pt) { int i; if ( rtpoly_is_empty(ctx, poly) ) return RT_FALSE; if ( ptarray_contains_point(ctx, poly->rings[0], pt) == RT_OUTSIDE ) return RT_FALSE; for ( i = 1; i < poly->nrings; i++ ) { if ( ptarray_contains_point(ctx, poly->rings[i], pt) == RT_INSIDE ) return RT_FALSE; } return RT_TRUE; } RTPOLY* rtpoly_grid(const RTCTX *ctx, const RTPOLY *poly, const gridspec *grid) { RTPOLY *opoly; int ri; #if 0 /* * TODO: control this assertion * it is assumed that, since the grid size will be a pixel, * a visible ring should show at least a white pixel inside, * thus, for a square, that would be grid_xsize*grid_ysize */ double minvisiblearea = grid->xsize * grid->ysize; #endif RTDEBUGF(3, "rtpoly_grid: applying grid to polygon with %d rings", poly->nrings); opoly = rtpoly_construct_empty(ctx, poly->srid, rtgeom_has_z(ctx, (RTGEOM*)poly), rtgeom_has_m(ctx, (RTGEOM*)poly)); for (ri=0; rinrings; ri++) { RTPOINTARRAY *ring = poly->rings[ri]; RTPOINTARRAY *newring; newring = ptarray_grid(ctx, ring, grid); /* Skip ring if not composed by at least 4 pts (3 segments) */ if ( newring->npoints < 4 ) { ptarray_free(ctx, newring); RTDEBUGF(3, "grid_polygon3d: ring%d skipped ( <4 pts )", ri); if ( ri ) continue; else break; /* this is the external ring, no need to work on holes */ } if ( ! rtpoly_add_ring(ctx, opoly, newring) ) { rterror(ctx, "rtpoly_grid, memory error"); return NULL; } } RTDEBUGF(3, "rtpoly_grid: simplified polygon with %d rings", opoly->nrings); if ( ! opoly->nrings ) { rtpoly_free(ctx, opoly); return NULL; } return opoly; } src/rtprint.c000066400000000000000000000315661271715413500135340ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2010-2015 Paul Ramsey * Copyright (C) 2011 Sandro Santilli * **********************************************************************/ #include #include #include "librttopo_geom_internal.h" /* Ensures the given lat and lon are in the "normal" range: * -90 to +90 for lat, -180 to +180 for lon. */ static void rtprint_normalize_latlon(const RTCTX *ctx, double *lat, double *lon) { /* First remove all the truly excessive trips around the world via up or down. */ while (*lat > 270) { *lat -= 360; } while (*lat < -270) { *lat += 360; } /* Now see if latitude is past the top or bottom of the world. * Past 90 or -90 puts us on the other side of the earth, * so wrap latitude and add 180 to longitude to reflect that. */ if (*lat > 90) { *lat = 180 - *lat; *lon += 180; } if (*lat < -90) { *lat = -180 - *lat; *lon += 180; } /* Now make sure lon is in the normal range. Wrapping longitude * has no effect on latitude. */ while (*lon > 180) { *lon -= 360; } while (*lon < -180) { *lon += 360; } } /* Converts a single double to DMS given the specified DMS format string. * Symbols are specified since N/S or E/W are the only differences when printing * lat vs. lon. They are only used if the "C" (compass dir) token appears in the * format string. * NOTE: Format string and symbols are required to be in UTF-8. */ static char * rtdouble_to_dms(const RTCTX *ctx, double val, const char *pos_dir_symbol, const char *neg_dir_symbol, const char * format) { /* 3 numbers, 1 sign or compass dir, and 5 possible strings (degree signs, spaces, misc text, etc) between or around them.*/ #define NUM_PIECES 9 #define WORK_SIZE 1024 char pieces[NUM_PIECES][WORK_SIZE]; int current_piece = 0; int is_negative = 0; double degrees = 0.0; double minutes = 0.0; double seconds = 0.0; int compass_dir_piece = -1; int reading_deg = 0; int deg_digits = 0; int deg_has_decpoint = 0; int deg_dec_digits = 0; int deg_piece = -1; int reading_min = 0; int min_digits = 0; int min_has_decpoint = 0; int min_dec_digits = 0; int min_piece = -1; int reading_sec = 0; int sec_digits = 0; int sec_has_decpoint = 0; int sec_dec_digits = 0; int sec_piece = -1; int format_length = ((NULL == format) ? 0 : strlen(format)); char * result; int index, following_byte_index; int multibyte_char_width = 1; /* Initialize the working strs to blank. We may not populate all of them, and * this allows us to concat them all at the end without worrying about how many * we actually needed. */ for (index = 0; index < NUM_PIECES; index++) { pieces[index][0] = '\0'; } /* If no format is provided, use a default. */ if (0 == format_length) { /* C2B0 is UTF-8 for the degree symbol. */ format = "D\xC2\xB0""M'S.SSS\"C"; format_length = strlen(format); } else if (format_length > WORK_SIZE) { /* Sanity check, we don't want to overwrite an entire piece of work and no one should need a 1K-sized * format string anyway. */ rterror(ctx, "Bad format, exceeds maximum length (%d).", WORK_SIZE); } for (index = 0; index < format_length; index++) { char next_char = format[index]; switch (next_char) { case 'D': if (reading_deg) { /* If we're reading degrees, add another digit. */ deg_has_decpoint ? deg_dec_digits++ : deg_digits++; } else { /* If we're not reading degrees, we are now. */ current_piece++; deg_piece = current_piece; if (deg_digits > 0) { rterror(ctx, "Bad format, cannot include degrees (DD.DDD) more than once."); } reading_deg = 1; reading_min = 0; reading_sec = 0; deg_digits++; } break; case 'M': if (reading_min) { /* If we're reading minutes, add another digit. */ min_has_decpoint ? min_dec_digits++ : min_digits++; } else { /* If we're not reading minutes, we are now. */ current_piece++; min_piece = current_piece; if (min_digits > 0) { rterror(ctx, "Bad format, cannot include minutes (MM.MMM) more than once."); } reading_deg = 0; reading_min = 1; reading_sec = 0; min_digits++; } break; case 'S': if (reading_sec) { /* If we're reading seconds, add another digit. */ sec_has_decpoint ? sec_dec_digits++ : sec_digits++; } else { /* If we're not reading seconds, we are now. */ current_piece++; sec_piece = current_piece; if (sec_digits > 0) { rterror(ctx, "Bad format, cannot include seconds (SS.SSS) more than once."); } reading_deg = 0; reading_min = 0; reading_sec = 1; sec_digits++; } break; case 'C': /* We're done reading anything else we might have been reading. */ if (reading_deg || reading_min || reading_sec) { /* We were reading something, that means this is the next piece. */ reading_deg = 0; reading_min = 0; reading_sec = 0; } current_piece++; if (compass_dir_piece >= 0) { rterror(ctx, "Bad format, cannot include compass dir (C) more than once."); } /* The compass dir is a piece all by itself. */ compass_dir_piece = current_piece; current_piece++; break; case '.': /* If we're reading deg, min, or sec, we want a decimal point for it. */ if (reading_deg) { deg_has_decpoint = 1; } else if (reading_min) { min_has_decpoint = 1; } else if (reading_sec) { sec_has_decpoint = 1; } else { /* Not reading anything, just pass through the '.' */ strncat(pieces[current_piece], &next_char, 1); } break; default: /* Any other char is just passed through unchanged. But it does mean we are done reading D, M, or S.*/ if (reading_deg || reading_min || reading_sec) { /* We were reading something, that means this is the next piece. */ current_piece++; reading_deg = 0; reading_min = 0; reading_sec = 0; } /* Check if this is a multi-byte UTF-8 character. If so go ahead and read the rest of the bytes as well. */ multibyte_char_width = 1; if (next_char & 0x80) { if ((next_char & 0xF8) == 0xF0) { multibyte_char_width += 3; } else if ((next_char & 0xF0) == 0xE0) { multibyte_char_width += 2; } else if ((next_char & 0xE0) == 0xC0) { multibyte_char_width += 1; } else { rterror(ctx, "Bad format, invalid high-order byte found first, format string may not be UTF-8."); } } if (multibyte_char_width > 1) { if (index + multibyte_char_width >= format_length) { rterror(ctx, "Bad format, UTF-8 character first byte found with insufficient following bytes, format string may not be UTF-8."); } for (following_byte_index = (index + 1); following_byte_index < (index + multibyte_char_width); following_byte_index++) { if ((format[following_byte_index] & 0xC0) != 0x80) { rterror(ctx, "Bad format, invalid byte found following leading byte of multibyte character, format string may not be UTF-8."); } } } /* Copy all the character's bytes into the current piece. */ strncat(pieces[current_piece], &(format[index]), multibyte_char_width); /* Now increment index past the rest of those bytes. */ index += multibyte_char_width - 1; break; } if (current_piece >= NUM_PIECES) { rterror(ctx, "Internal error, somehow needed more pieces than it should."); } } if (deg_piece < 0) { rterror(ctx, "Bad format, degrees (DD.DDD) must be included."); } /* Divvy the number up into D, DM, or DMS */ if (val < 0) { val *= -1; is_negative = 1; } degrees = val; if (min_digits > 0) { degrees = (long)degrees; minutes = (val - degrees) * 60; } if (sec_digits > 0) { if (0 == min_digits) { rterror(ctx, "Bad format, cannot include seconds (SS.SSS) without including minutes (MM.MMM)."); } minutes = (long)minutes; seconds = (val - (degrees + (minutes / 60))) * 3600; } /* Handle the compass direction. If not using compass dir, display degrees as a positive/negative number. */ if (compass_dir_piece >= 0) { strcpy(pieces[compass_dir_piece], is_negative ? neg_dir_symbol : pos_dir_symbol); } else if (is_negative) { degrees *= -1; } /* Format the degrees into their string piece. */ if (deg_digits + deg_dec_digits + 2 > WORK_SIZE) { rterror(ctx, "Bad format, degrees (DD.DDD) number of digits was greater than our working limit."); } if(deg_piece >= 0) { sprintf(pieces[deg_piece], "%*.*f", deg_digits, deg_dec_digits, degrees); } if (min_piece >= 0) { /* Format the minutes into their string piece. */ if (min_digits + min_dec_digits + 2 > WORK_SIZE) { rterror(ctx, "Bad format, minutes (MM.MMM) number of digits was greater than our working limit."); } sprintf(pieces[min_piece], "%*.*f", min_digits, min_dec_digits, minutes); } if (sec_piece >= 0) { /* Format the seconds into their string piece. */ if (sec_digits + sec_dec_digits + 2 > WORK_SIZE) { rterror(ctx, "Bad format, seconds (SS.SSS) number of digits was greater than our working limit."); } sprintf(pieces[sec_piece], "%*.*f", sec_digits, sec_dec_digits, seconds); } /* Allocate space for the result. Leave plenty of room for excess digits, negative sign, etc.*/ result = (char*)rtalloc(ctx, format_length + WORK_SIZE); /* Append all the pieces together. There may be less than 9, but in that case the rest will be blank. */ strcpy(result, pieces[0]); for (index = 1; index < NUM_PIECES; index++) { strcat(result, pieces[index]); } return result; } /* Print two doubles (lat and lon) in DMS form using the specified format. * First normalizes them so they will display as -90 to 90 and -180 to 180. * Format string may be null or 0-length, in which case a default format will be used. * NOTE: Format string is required to be in UTF-8. * NOTE2: returned string is rtalloc'ed, caller is responsible to rtfree it up */ static char * rtdoubles_to_latlon(const RTCTX *ctx, double lat, double lon, const char * format) { char * lat_text; char * lon_text; char * result; /* Normalize lat/lon to the normal (-90 to 90, -180 to 180) range. */ rtprint_normalize_latlon(ctx, &lat, &lon); /* This is somewhat inefficient as the format is parsed twice. */ lat_text = rtdouble_to_dms(ctx, lat, "N", "S", format); lon_text = rtdouble_to_dms(ctx, lon, "E", "W", format); /* lat + lon + a space between + the null terminator. */ result = (char*)rtalloc(ctx, strlen(lat_text) + strlen(lon_text) + 2); sprintf(result, "%s %s", lat_text, lon_text); rtfree(ctx, lat_text); rtfree(ctx, lon_text); return result; } /* Print the X (lon) and Y (lat) of the given point in DMS form using * the specified format. * First normalizes the values so they will display as -90 to 90 and -180 to 180. * Format string may be null or 0-length, in which case a default format will be used. * NOTE: Format string is required to be in UTF-8. * NOTE2: returned string is rtalloc'ed, caller is responsible to rtfree it up */ char* rtpoint_to_latlon(const RTCTX *ctx, const RTPOINT * pt, const char *format) { const RTPOINT2D *p; if (NULL == pt) { rterror(ctx, "Cannot convert a null point into formatted text."); } if (rtgeom_is_empty(ctx, (RTGEOM *)pt)) { rterror(ctx, "Cannot convert an empty point into formatted text."); } p = rt_getPoint2d_cp(ctx, pt->point, 0); return rtdoubles_to_latlon(ctx, p->y, p->x, format); } src/rtpsurface.c000066400000000000000000000122301271715413500141730ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" RTPSURFACE* rtpsurface_add_rtpoly(const RTCTX *ctx, RTPSURFACE *mobj, const RTPOLY *obj) { return (RTPSURFACE*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION*)mobj, (RTGEOM*)obj); } void rtpsurface_free(const RTCTX *ctx, RTPSURFACE *psurf) { int i; if ( ! psurf ) return; if ( psurf->bbox ) rtfree(ctx, psurf->bbox); for ( i = 0; i < psurf->ngeoms; i++ ) if ( psurf->geoms && psurf->geoms[i] ) rtpoly_free(ctx, psurf->geoms[i]); if ( psurf->geoms ) rtfree(ctx, psurf->geoms); rtfree(ctx, psurf); } void printRTPSURFACE(const RTCTX *ctx, RTPSURFACE *psurf) { int i, j; RTPOLY *patch; if (psurf->type != RTPOLYHEDRALSURFACETYPE) rterror(ctx, "printRTPSURFACE called with something else than a POLYHEDRALSURFACE"); rtnotice(ctx, "RTPSURFACE {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(psurf->flags)); rtnotice(ctx, " SRID = %i", (int)psurf->srid); rtnotice(ctx, " ngeoms = %i", (int)psurf->ngeoms); for (i=0; ingeoms; i++) { patch = (RTPOLY *) psurf->geoms[i]; for (j=0; jnrings; j++) { rtnotice(ctx, " RING # %i :",j); printPA(ctx, patch->rings[j]); } } rtnotice(ctx, "}"); } /* * TODO rewrite all this stuff to be based on a truly topological model */ struct struct_psurface_arcs { double ax, ay, az; double bx, by, bz; int cnt, face; }; typedef struct struct_psurface_arcs *psurface_arcs; /* We supposed that the geometry is valid we could have wrong result if not */ int rtpsurface_is_closed(const RTCTX *ctx, const RTPSURFACE *psurface) { int i, j, k; int narcs, carc; int found; psurface_arcs arcs; RTPOINT4D pa, pb; RTPOLY *patch; /* If surface is not 3D, it's can't be closed */ if (!RTFLAGS_GET_Z(psurface->flags)) return 0; /* If surface is less than 4 faces hard to be closed too */ if (psurface->ngeoms < 4) return 0; /* Max theorical arcs number if no one is shared ... */ for (i=0, narcs=0 ; i < psurface->ngeoms ; i++) { patch = (RTPOLY *) psurface->geoms[i]; narcs += patch->rings[0]->npoints - 1; } arcs = rtalloc(ctx, sizeof(struct struct_psurface_arcs) * narcs); for (i=0, carc=0; i < psurface->ngeoms ; i++) { patch = (RTPOLY *) psurface->geoms[i]; for (j=0; j < patch->rings[0]->npoints - 1; j++) { rt_getPoint4d_p(ctx, patch->rings[0], j, &pa); rt_getPoint4d_p(ctx, patch->rings[0], j+1, &pb); /* remove redundant points if any */ if (pa.x == pb.x && pa.y == pb.y && pa.z == pb.z) continue; /* Make sure to order the 'lower' point first */ if ( (pa.x > pb.x) || (pa.x == pb.x && pa.y > pb.y) || (pa.x == pb.x && pa.y == pb.y && pa.z > pb.z) ) { pa = pb; rt_getPoint4d_p(ctx, patch->rings[0], j, &pb); } for (found=0, k=0; k < carc ; k++) { if ( ( arcs[k].ax == pa.x && arcs[k].ay == pa.y && arcs[k].az == pa.z && arcs[k].bx == pb.x && arcs[k].by == pb.y && arcs[k].bz == pb.z && arcs[k].face != i) ) { arcs[k].cnt++; found = 1; /* Look like an invalid PolyhedralSurface anyway not a closed one */ if (arcs[k].cnt > 2) { rtfree(ctx, arcs); return 0; } } } if (!found) { arcs[carc].cnt=1; arcs[carc].face=i; arcs[carc].ax = pa.x; arcs[carc].ay = pa.y; arcs[carc].az = pa.z; arcs[carc].bx = pb.x; arcs[carc].by = pb.y; arcs[carc].bz = pb.z; carc++; /* Look like an invalid PolyhedralSurface anyway not a closed one */ if (carc > narcs) { rtfree(ctx, arcs); return 0; } } } } /* A polyhedron is closed if each edge is shared by exactly 2 faces */ for (k=0; k < carc ; k++) { if (arcs[k].cnt != 2) { rtfree(ctx, arcs); return 0; } } rtfree(ctx, arcs); /* Invalid Polyhedral case */ if (carc < psurface->ngeoms) return 0; return 1; } src/rtspheroid.c000066400000000000000000000546621271715413500142170ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009 Paul Ramsey * Copyright (C) 2009 David Skea * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeodetic.h" #include "rtgeom_log.h" /* GeographicLib */ #if PROJ_GEODESIC #include #endif /** * Initialize spheroid object based on major and minor axis */ void spheroid_init(const RTCTX *ctx, SPHEROID *s, double a, double b) { s->a = a; s->b = b; s->f = (a - b) / a; s->e_sq = (a*a - b*b)/(a*a); s->radius = (2.0 * a + b ) / 3.0; } #if ! PROJ_GEODESIC static double spheroid_mu2(const RTCTX *ctx, double alpha, const SPHEROID *s) { double b2 = POW2(s->b); return POW2(cos(alpha)) * (POW2(s->a) - b2) / b2; } static double spheroid_big_a(const RTCTX *ctx, double u2) { return 1.0 + (u2 / 16384.0) * (4096.0 + u2 * (-768.0 + u2 * (320.0 - 175.0 * u2))); } static double spheroid_big_b(const RTCTX *ctx, double u2) { return (u2 / 1024.0) * (256.0 + u2 * (-128.0 + u2 * (74.0 - 47.0 * u2))); } #endif /* ! PROJ_GEODESIC */ #if PROJ_GEODESIC /** * Computes the shortest distance along the surface of the spheroid * between two points, using the inverse geodesic problem from * GeographicLib (Karney 2013). * * @param a - location of first point * @param b - location of second point * @param s - spheroid to calculate on * @return spheroidal distance between a and b in spheroid units */ double spheroid_distance(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = a->lat * 180.0 / M_PI; double lon1 = a->lon * 180.0 / M_PI; double lat2 = b->lat * 180.0 / M_PI; double lon2 = b->lon * 180.0 / M_PI; double s12; /* return distance */ geod_inverse(&gd, lat1, lon1, lat2, lon2, &s12, 0, 0); return s12; } /** * Computes the forward azimuth of the geodesic joining two points on * the spheroid, using the inverse geodesic problem (Karney 2013). * * @param r - location of first point * @param s - location of second point * @return azimuth of line joining r to s (but not reverse) */ double spheroid_direction(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = a->lat * 180.0 / M_PI; double lon1 = a->lon * 180.0 / M_PI; double lat2 = b->lat * 180.0 / M_PI; double lon2 = b->lon * 180.0 / M_PI; double azi1; /* return azimuth */ geod_inverse(&gd, lat1, lon1, lat2, lon2, 0, &azi1, 0); return azi1 * M_PI / 180.0; } /** * Given a location, an azimuth and a distance, computes the location of * the projected point. Using the direct geodesic problem from * GeographicLib (Karney 2013). * * @param r - location of first point * @param distance - distance in meters * @param azimuth - azimuth in radians * @return g - location of projected point */ int spheroid_project(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g) { struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); double lat1 = r->lat * 180.0 / M_PI; double lon1 = r->lon * 180.0 / M_PI; double lat2, lon2; /* return projected position */ geod_direct(&gd, lat1, lon1, azimuth * 180.0 / M_PI, distance, &lat2, &lon2, 0); g->lat = lat2 * M_PI / 180.0; g->lon = lon2 * M_PI / 180.0; return RT_SUCCESS; } static double ptarray_area_spheroid(const RTCTX *ctx, const RTPOINTARRAY *pa, const SPHEROID *spheroid) { /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; struct geod_geodesic gd; geod_init(&gd, spheroid->a, spheroid->f); struct geod_polygon poly; geod_polygon_init(&poly, 0); int i; double area; /* returned polygon area */ RTPOINT2D p; /* long/lat units are degrees */ /* Pass points from point array; don't close the linearring */ for ( i = 0; i < pa->npoints - 1; i++ ) { rt_getPoint2d_p(ctx, pa, i, &p); geod_polygon_addpoint(&gd, &poly, p.y, p.x); RTDEBUGF(4, "geod_polygon_addpoint %d: %.12g %.12g", i, p.y, p.x); } i = geod_polygon_compute(&gd, &poly, 0, 1, &area, 0); if ( i != pa->npoints - 1 ) { rterror(ctx, "ptarray_area_spheroid: different number of points %d vs %d", i, pa->npoints - 1); } RTDEBUGF(4, "geod_polygon_compute area: %.12g", area); return fabs(area); } /* Above use GeographicLib */ #else /* ! PROJ_GEODESIC */ /* Below use pre-version 2.2 geodesic functions */ /** * Computes the shortest distance along the surface of the spheroid * between two points. Based on Vincenty's formula for the geodetic * inverse problem as described in "Geocentric Datum of Australia * Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1a.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_inverse.jsp * * @param a - location of first point. * @param b - location of second point. * @param s - spheroid to calculate on * @return spheroidal distance between a and b in spheroid units. */ double spheroid_distance(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const SPHEROID *spheroid) { double lambda = (b->lon - a->lon); double f = spheroid->f; double omf = 1 - spheroid->f; double u1, u2; double cos_u1, cos_u2; double sin_u1, sin_u2; double big_a, big_b, delta_sigma; double alpha, sin_alpha, cos_alphasq, c; double sigma, sin_sigma, cos_sigma, cos2_sigma_m, sqrsin_sigma, last_lambda, omega; double cos_lambda, sin_lambda; double distance; int i = 0; /* Same point => zero distance */ if ( geographic_point_equals(ctx, a, b) ) { return 0.0; } u1 = atan(omf * tan(a->lat)); cos_u1 = cos(u1); sin_u1 = sin(u1); u2 = atan(omf * tan(b->lat)); cos_u2 = cos(u2); sin_u2 = sin(u2); omega = lambda; do { cos_lambda = cos(lambda); sin_lambda = sin(lambda); sqrsin_sigma = POW2(cos_u2 * sin_lambda) + POW2((cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos_lambda)); sin_sigma = sqrt(sqrsin_sigma); cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos_lambda; sigma = atan2(sin_sigma, cos_sigma); sin_alpha = cos_u1 * cos_u2 * sin_lambda / sin(sigma); /* Numerical stability issue, ensure asin is not NaN */ if ( sin_alpha > 1.0 ) alpha = M_PI_2; else if ( sin_alpha < -1.0 ) alpha = -1.0 * M_PI_2; else alpha = asin(sin_alpha); cos_alphasq = POW2(cos(alpha)); cos2_sigma_m = cos(sigma) - (2.0 * sin_u1 * sin_u2 / cos_alphasq); /* Numerical stability issue, cos2 is in range */ if ( cos2_sigma_m > 1.0 ) cos2_sigma_m = 1.0; if ( cos2_sigma_m < -1.0 ) cos2_sigma_m = -1.0; c = (f / 16.0) * cos_alphasq * (4.0 + f * (4.0 - 3.0 * cos_alphasq)); last_lambda = lambda; lambda = omega + (1.0 - c) * f * sin(alpha) * (sigma + c * sin(sigma) * (cos2_sigma_m + c * cos(sigma) * (-1.0 + 2.0 * POW2(cos2_sigma_m)))); i++; } while ( (i < 999) && (lambda != 0.0) && (fabs((last_lambda - lambda)/lambda) > 1.0e-9) ); u2 = spheroid_mu2(ctx, alpha, spheroid); big_a = spheroid_big_a(ctx, u2); big_b = spheroid_big_b(ctx, u2); delta_sigma = big_b * sin_sigma * (cos2_sigma_m + (big_b / 4.0) * (cos_sigma * (-1.0 + 2.0 * POW2(cos2_sigma_m)) - (big_b / 6.0) * cos2_sigma_m * (-3.0 + 4.0 * sqrsin_sigma) * (-3.0 + 4.0 * POW2(cos2_sigma_m)))); distance = spheroid->b * big_a * (sigma - delta_sigma); /* Algorithm failure, distance == NaN, fallback to sphere */ if ( distance != distance ) { rterror(ctx, "spheroid_distance returned NaN: (%.20g %.20g) (%.20g %.20g) a = %.20g b = %.20g",a->lat, a->lon, b->lat, b->lon, spheroid->a, spheroid->b); return spheroid->radius * sphere_distance(ctx, a, b); } return distance; } /** * Computes the direction of the geodesic joining two points on * the spheroid. Based on Vincenty's formula for the geodetic * inverse problem as described in "Geocentric Datum of Australia * Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1a.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_inverse.jsp * * @param r - location of first point * @param s - location of second point * @return azimuth of line joining r and s */ double spheroid_direction(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid) { int i = 0; double lambda = s->lon - r->lon; double omf = 1 - spheroid->f; double u1 = atan(omf * tan(r->lat)); double cos_u1 = cos(u1); double sin_u1 = sin(u1); double u2 = atan(omf * tan(s->lat)); double cos_u2 = cos(u2); double sin_u2 = sin(u2); double omega = lambda; double alpha, sigma, sin_sigma, cos_sigma, cos2_sigma_m, sqr_sin_sigma, last_lambda; double sin_alpha, cos_alphasq, C, alphaFD; do { sqr_sin_sigma = POW2(cos_u2 * sin(lambda)) + POW2((cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos(lambda))); sin_sigma = sqrt(sqr_sin_sigma); cos_sigma = sin_u1 * sin_u2 + cos_u1 * cos_u2 * cos(lambda); sigma = atan2(sin_sigma, cos_sigma); sin_alpha = cos_u1 * cos_u2 * sin(lambda) / sin(sigma); /* Numerical stability issue, ensure asin is not NaN */ if ( sin_alpha > 1.0 ) alpha = M_PI_2; else if ( sin_alpha < -1.0 ) alpha = -1.0 * M_PI_2; else alpha = asin(sin_alpha); cos_alphasq = POW2(cos(alpha)); cos2_sigma_m = cos(sigma) - (2.0 * sin_u1 * sin_u2 / cos_alphasq); /* Numerical stability issue, cos2 is in range */ if ( cos2_sigma_m > 1.0 ) cos2_sigma_m = 1.0; if ( cos2_sigma_m < -1.0 ) cos2_sigma_m = -1.0; C = (spheroid->f / 16.0) * cos_alphasq * (4.0 + spheroid->f * (4.0 - 3.0 * cos_alphasq)); last_lambda = lambda; lambda = omega + (1.0 - C) * spheroid->f * sin(alpha) * (sigma + C * sin(sigma) * (cos2_sigma_m + C * cos(sigma) * (-1.0 + 2.0 * POW2(cos2_sigma_m)))); i++; } while ( (i < 999) && (lambda != 0) && (fabs((last_lambda - lambda) / lambda) > 1.0e-9) ); alphaFD = atan2((cos_u2 * sin(lambda)), (cos_u1 * sin_u2 - sin_u1 * cos_u2 * cos(lambda))); if (alphaFD < 0.0) { alphaFD = alphaFD + 2.0 * M_PI; } if (alphaFD > 2.0 * M_PI) { alphaFD = alphaFD - 2.0 * M_PI; } return alphaFD; } /** * Given a location, an azimuth and a distance, computes the * location of the projected point. Based on Vincenty's formula * for the geodetic direct problem as described in "Geocentric * Datum of Australia Technical Manual", Chapter 4. Tested against: * http://mascot.gdbc.gov.bc.ca/mascot/util1b.html * and * http://www.ga.gov.au/nmd/geodesy/datums/vincenty_direct.jsp * * @param r - location of first point. * @param distance - distance in meters. * @param azimuth - azimuth in radians. * @return s - location of projected point. */ int spheroid_project(const RTCTX *ctx, const GEOGRAPHIC_POINT *r, const SPHEROID *spheroid, double distance, double azimuth, GEOGRAPHIC_POINT *g) { double omf = 1 - spheroid->f; double tan_u1 = omf * tan(r->lat); double u1 = atan(tan_u1); double sigma, last_sigma, delta_sigma, two_sigma_m; double sigma1, sin_alpha, alpha, cos_alphasq; double u2, A, B; double lat2, lambda, lambda2, C, omega; int i = 0; if (azimuth < 0.0) { azimuth = azimuth + M_PI * 2.0; } if (azimuth > (M_PI * 2.0)) { azimuth = azimuth - M_PI * 2.0; } sigma1 = atan2(tan_u1, cos(azimuth)); sin_alpha = cos(u1) * sin(azimuth); alpha = asin(sin_alpha); cos_alphasq = 1.0 - POW2(sin_alpha); u2 = spheroid_mu2(ctx, alpha, spheroid); A = spheroid_big_a(ctx, u2); B = spheroid_big_b(ctx, u2); sigma = (distance / (spheroid->b * A)); do { two_sigma_m = 2.0 * sigma1 + sigma; delta_sigma = B * sin(sigma) * (cos(two_sigma_m) + (B / 4.0) * (cos(sigma) * (-1.0 + 2.0 * POW2(cos(two_sigma_m)) - (B / 6.0) * cos(two_sigma_m) * (-3.0 + 4.0 * POW2(sin(sigma))) * (-3.0 + 4.0 * POW2(cos(two_sigma_m)))))); last_sigma = sigma; sigma = (distance / (spheroid->b * A)) + delta_sigma; i++; } while (i < 999 && fabs((last_sigma - sigma) / sigma) > 1.0e-9); lat2 = atan2((sin(u1) * cos(sigma) + cos(u1) * sin(sigma) * cos(azimuth)), (omf * sqrt(POW2(sin_alpha) + POW2(sin(u1) * sin(sigma) - cos(u1) * cos(sigma) * cos(azimuth))))); lambda = atan2((sin(sigma) * sin(azimuth)), (cos(u1) * cos(sigma) - sin(u1) * sin(sigma) * cos(azimuth))); C = (spheroid->f / 16.0) * cos_alphasq * (4.0 + spheroid->f * (4.0 - 3.0 * cos_alphasq)); omega = lambda - (1.0 - C) * spheroid->f * sin_alpha * (sigma + C * sin(sigma) * (cos(two_sigma_m) + C * cos(sigma) * (-1.0 + 2.0 * POW2(cos(two_sigma_m))))); lambda2 = r->lon + omega; g->lat = lat2; g->lon = lambda2; return RT_SUCCESS; } static inline double spheroid_prime_vertical_radius_of_curvature(const RTCTX *ctx, double latitude, const SPHEROID *spheroid) { return spheroid->a / (sqrt(1.0 - spheroid->e_sq * POW2(sin(latitude)))); } static inline double spheroid_parallel_arc_length(const RTCTX *ctx, double latitude, double deltaLongitude, const SPHEROID *spheroid) { return spheroid_prime_vertical_radius_of_curvature(ctx, latitude, spheroid) * cos(latitude) * deltaLongitude; } /** * Computes the area on the spheroid of a box bounded by meridians and * parallels. The box is defined by two points, the South West corner * and the North East corner. Formula based on Bagratuni 1967. * * @param southWestCorner - lower left corner of bounding box. * @param northEastCorner - upper right corner of bounding box. * @return area in square meters. */ static double spheroid_boundingbox_area(const RTCTX *ctx, const GEOGRAPHIC_POINT *southWestCorner, const GEOGRAPHIC_POINT *northEastCorner, const SPHEROID *spheroid) { double z0 = (northEastCorner->lon - southWestCorner->lon) * POW2(spheroid->b) / 2.0; double e = sqrt(spheroid->e_sq); double sinPhi1 = sin(southWestCorner->lat); double sinPhi2 = sin(northEastCorner->lat); double t1p1 = sinPhi1 / (1.0 - spheroid->e_sq * sinPhi1 * sinPhi1); double t1p2 = sinPhi2 / (1.0 - spheroid->e_sq * sinPhi2 * sinPhi2); double oneOver2e = 1.0 / (2.0 * e); double t2p1 = oneOver2e * log((1.0 + e * sinPhi1) / (1.0 - e * sinPhi1)); double t2p2 = oneOver2e * log((1.0 + e * sinPhi2) / (1.0 - e * sinPhi2)); return z0 * (t1p2 + t2p2) - z0 * (t1p1 + t2p1); } /** * This function doesn't work for edges crossing the dateline or in the southern * hemisphere. Points are pre-conditioned in ptarray_area_spheroid. */ static double spheroid_striparea(const RTCTX *ctx, const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, double latitude_min, const SPHEROID *spheroid) { GEOGRAPHIC_POINT A, B, mL, nR; double deltaLng, baseArea, topArea; double bE, tE, ratio, sign; A = *a; B = *b; mL.lat = latitude_min; mL.lon = FP_MIN(A.lon, B.lon); nR.lat = FP_MIN(A.lat, B.lat); nR.lon = FP_MAX(A.lon, B.lon); RTDEBUGF(4, "mL (%.12g %.12g)", mL.lat, mL.lon); RTDEBUGF(4, "nR (%.12g %.12g)", nR.lat, nR.lon); baseArea = spheroid_boundingbox_area(ctx, &mL, &nR, spheroid); RTDEBUGF(4, "baseArea %.12g", baseArea); mL.lat = FP_MIN(A.lat, B.lat); mL.lon = FP_MIN(A.lon, B.lon); nR.lat = FP_MAX(A.lat, B.lat); nR.lon = FP_MAX(A.lon, B.lon); RTDEBUGF(4, "mL (%.12g %.12g)", mL.lat, mL.lon); RTDEBUGF(4, "nR (%.12g %.12g)", nR.lat, nR.lon); topArea = spheroid_boundingbox_area(ctx, &mL, &nR, spheroid); RTDEBUGF(4, "topArea %.12g", topArea); deltaLng = B.lon - A.lon; RTDEBUGF(4, "deltaLng %.12g", deltaLng); bE = spheroid_parallel_arc_length(ctx, A.lat, deltaLng, spheroid); tE = spheroid_parallel_arc_length(ctx, B.lat, deltaLng, spheroid); RTDEBUGF(4, "bE %.12g", bE); RTDEBUGF(4, "tE %.12g", tE); ratio = (bE + tE)/tE; sign = signum(B.lon - A.lon); return (baseArea + topArea / ratio) * sign; } static double ptarray_area_spheroid(const RTCTX *ctx, const RTPOINTARRAY *pa, const SPHEROID *spheroid) { GEOGRAPHIC_POINT a, b; RTPOINT2D p; int i; double area = 0.0; RTGBOX gbox2d; int in_south = RT_FALSE; double delta_lon_tolerance; double latitude_min; gbox2d.flags = gflags(ctx, 0, 0, 0); /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; /* Get the raw min/max values for the latitudes */ ptarray_calculate_gbox_cartesian(ctx, pa, &gbox2d); if ( signum(gbox2d.ymin) != signum(gbox2d.ymax) ) rterror(ctx, "ptarray_area_spheroid: cannot handle ptarray that crosses equator"); /* Geodetic bbox < 0.0 implies geometry is entirely in southern hemisphere */ if ( gbox2d.ymax < 0.0 ) in_south = RT_TRUE; RTDEBUGF(4, "gbox2d.ymax %.12g", gbox2d.ymax); /* Tolerance for strip area calculation */ if ( in_south ) { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymin) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(fabs(gbox2d.ymax)); } else { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymax) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(gbox2d.ymin); } /* Initialize first point */ rt_getPoint2d_p(ctx, pa, 0, &p); geographic_point_init(ctx, p.x, p.y, &a); for ( i = 1; i < pa->npoints; i++ ) { GEOGRAPHIC_POINT a1, b1; double strip_area = 0.0; double delta_lon = 0.0; RTDEBUGF(4, "edge #%d", i); rt_getPoint2d_p(ctx, pa, i, &p); geographic_point_init(ctx, p.x, p.y, &b); a1 = a; b1 = b; /* Flip into north if in south */ if ( in_south ) { a1.lat = -1.0 * a1.lat; b1.lat = -1.0 * b1.lat; } RTDEBUGF(4, "in_south %d", in_south); RTDEBUGF(4, "crosses_dateline(ctx, a, b) %d", crosses_dateline(ctx, &a, &b) ); if ( crosses_dateline(ctx, &a, &b) ) { double shift; if ( a1.lon > 0.0 ) shift = (M_PI - a1.lon) + 0.088; /* About 5deg more */ else shift = (M_PI - b1.lon) + 0.088; /* About 5deg more */ RTDEBUGF(4, "shift: %.8g", shift); RTDEBUGF(4, "before shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); point_shift(ctx, &a1, shift); point_shift(ctx, &b1, shift); RTDEBUGF(4, "after shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); } delta_lon = fabs(b1.lon - a1.lon); RTDEBUGF(4, "a1(%.18g %.18g) b1(%.18g %.18g)", a1.lat, a1.lon, b1.lat, b1.lon); RTDEBUGF(4, "delta_lon %.18g", delta_lon); RTDEBUGF(4, "delta_lon_tolerance %.18g", delta_lon_tolerance); if ( delta_lon > 0.0 ) { if ( delta_lon < delta_lon_tolerance ) { strip_area = spheroid_striparea(ctx, &a1, &b1, latitude_min, spheroid); RTDEBUGF(4, "strip_area %.12g", strip_area); area += strip_area; } else { GEOGRAPHIC_POINT p, q; double step = floor(delta_lon / delta_lon_tolerance); double distance = spheroid_distance(ctx, &a1, &b1, spheroid); double pDistance = 0.0; int j = 0; RTDEBUGF(4, "step %.18g", step); RTDEBUGF(4, "distance %.18g", distance); step = distance / step; RTDEBUGF(4, "step %.18g", step); p = a1; while (pDistance < (distance - step * 1.01)) { double azimuth = spheroid_direction(ctx, &p, &b1, spheroid); j++; RTDEBUGF(4, " iteration %d", j); RTDEBUGF(4, " azimuth %.12g", azimuth); pDistance = pDistance + step; RTDEBUGF(4, " pDistance %.12g", pDistance); spheroid_project(ctx, &p, spheroid, step, azimuth, &q); strip_area = spheroid_striparea(ctx, &p, &q, latitude_min, spheroid); RTDEBUGF(4, " strip_area %.12g", strip_area); area += strip_area; RTDEBUGF(4, " area %.12g", area); p.lat = q.lat; p.lon = q.lon; } strip_area = spheroid_striparea(ctx, &p, &b1, latitude_min, spheroid); area += strip_area; } } /* B gets incremented in the next loop, so we save the value here */ a = b; } return fabs(area); } #endif /* else ! PROJ_GEODESIC */ /** * Calculate the area of an RTGEOM. Anything except POLYGON, MULTIPOLYGON * and GEOMETRYCOLLECTION return zero immediately. Multi's recurse, polygons * calculate external ring area and subtract internal ring area. A RTGBOX is * required to check relationship to equator an outside point. * WARNING: Does NOT WORK for polygons over equator or pole. */ double rtgeom_area_spheroid(const RTCTX *ctx, const RTGEOM *rtgeom, const SPHEROID *spheroid) { int type; assert(rtgeom); /* No area in nothing */ if ( rtgeom_is_empty(ctx, rtgeom) ) return 0.0; /* Read the geometry type number */ type = rtgeom->type; /* Anything but polygons and collections returns zero */ if ( ! ( type == RTPOLYGONTYPE || type == RTMULTIPOLYGONTYPE || type == RTCOLLECTIONTYPE ) ) return 0.0; /* Actually calculate area */ if ( type == RTPOLYGONTYPE ) { RTPOLY *poly = (RTPOLY*)rtgeom; int i; double area = 0.0; /* Just in case there's no rings */ if ( poly->nrings < 1 ) return 0.0; /* First, the area of the outer ring */ area += ptarray_area_spheroid(ctx, poly->rings[0], spheroid); /* Subtract areas of inner rings */ for ( i = 1; i < poly->nrings; i++ ) { area -= ptarray_area_spheroid(ctx, poly->rings[i], spheroid); } return area; } /* Recurse into sub-geometries to get area */ if ( type == RTMULTIPOLYGONTYPE || type == RTCOLLECTIONTYPE ) { RTCOLLECTION *col = (RTCOLLECTION*)rtgeom; int i; double area = 0.0; for ( i = 0; i < col->ngeoms; i++ ) { area += rtgeom_area_spheroid(ctx, col->geoms[i], spheroid); } return area; } /* Shouldn't get here. */ return 0.0; } src/rtstroke.c000066400000000000000000000616671271715413500137140ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include #include "librttopo_geom_internal.h" /* #define RTGEOM_DEBUG_LEVEL 4 */ #include "rtgeom_log.h" RTMLINE* rtmcurve_stroke(const RTCTX *ctx, const RTMCURVE *mcurve, uint32_t perQuad); RTMPOLY* rtmsurface_stroke(const RTCTX *ctx, const RTMSURFACE *msurface, uint32_t perQuad); RTCOLLECTION* rtcollection_stroke(const RTCTX *ctx, const RTCOLLECTION *collection, uint32_t perQuad); RTGEOM* pta_unstroke(const RTCTX *ctx, const RTPOINTARRAY *points, int type, int srid); RTGEOM* rtline_unstroke(const RTCTX *ctx, const RTLINE *line); RTGEOM* rtpolygon_unstroke(const RTCTX *ctx, const RTPOLY *poly); RTGEOM* rtmline_unstroke(const RTCTX *ctx, const RTMLINE *mline); RTGEOM* rtmpolygon_unstroke(const RTCTX *ctx, const RTMPOLY *mpoly); RTGEOM* rtgeom_unstroke(const RTCTX *ctx, const RTGEOM *geom); /* * Determines (recursively in the case of collections) whether the geometry * contains at least on arc geometry or segment. */ int rtgeom_has_arc(const RTCTX *ctx, const RTGEOM *geom) { RTCOLLECTION *col; int i; RTDEBUG(2, "rtgeom_has_arc called."); switch (geom->type) { case RTPOINTTYPE: case RTLINETYPE: case RTPOLYGONTYPE: case RTTRIANGLETYPE: case RTMULTIPOINTTYPE: case RTMULTILINETYPE: case RTMULTIPOLYGONTYPE: case RTPOLYHEDRALSURFACETYPE: case RTTINTYPE: return RT_FALSE; case RTCIRCSTRINGTYPE: return RT_TRUE; /* It's a collection that MAY contain an arc */ default: col = (RTCOLLECTION *)geom; for (i=0; ingeoms; i++) { if (rtgeom_has_arc(ctx, col->geoms[i]) == RT_TRUE) return RT_TRUE; } return RT_FALSE; } } /******************************************************************************* * Begin curve segmentize functions ******************************************************************************/ static double interpolate_arc(const RTCTX *ctx, double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3) { RTDEBUGF(4,"angle %.05g a1 %.05g a2 %.05g a3 %.05g zm1 %.05g zm2 %.05g zm3 %.05g",angle,a1,a2,a3,zm1,zm2,zm3); /* Counter-clockwise sweep */ if ( a1 < a2 ) { if ( angle <= a2 ) return zm1 + (zm2-zm1) * (angle-a1) / (a2-a1); else return zm2 + (zm3-zm2) * (angle-a2) / (a3-a2); } /* Clockwise sweep */ else { if ( angle >= a2 ) return zm1 + (zm2-zm1) * (a1-angle) / (a1-a2); else return zm2 + (zm3-zm2) * (a2-angle) / (a2-a3); } } static RTPOINTARRAY * rtcircle_stroke(const RTCTX *ctx, const RTPOINT4D *p1, const RTPOINT4D *p2, const RTPOINT4D *p3, uint32_t perQuad) { RTPOINT2D center; RTPOINT2D *t1 = (RTPOINT2D*)p1; RTPOINT2D *t2 = (RTPOINT2D*)p2; RTPOINT2D *t3 = (RTPOINT2D*)p3; RTPOINT4D pt; int p2_side = 0; int clockwise = RT_TRUE; double radius; /* Arc radius */ double increment; /* Angle per segment */ double a1, a2, a3, angle; RTPOINTARRAY *pa; int is_circle = RT_FALSE; RTDEBUG(2, "rtcircle_calculate_gbox called."); radius = rt_arc_center(ctx, t1, t2, t3, ¢er); p2_side = rt_segment_side(ctx, t1, t3, t2); /* Matched start/end points imply circle */ if ( p1->x == p3->x && p1->y == p3->y ) is_circle = RT_TRUE; /* Negative radius signals straight line, p1/p2/p3 are colinear */ if ( (radius < 0.0 || p2_side == 0) && ! is_circle ) return NULL; /* The side of the p1/p3 line that p2 falls on dictates the sweep direction from p1 to p3. */ if ( p2_side == -1 ) clockwise = RT_TRUE; else clockwise = RT_FALSE; increment = fabs(M_PI_2 / perQuad); /* Angles of each point that defines the arc section */ a1 = atan2(p1->y - center.y, p1->x - center.x); a2 = atan2(p2->y - center.y, p2->x - center.x); a3 = atan2(p3->y - center.y, p3->x - center.x); /* p2 on left side => clockwise sweep */ if ( clockwise ) { increment *= -1; /* Adjust a3 down so we can decrement from a1 to a3 cleanly */ if ( a3 > a1 ) a3 -= 2.0 * M_PI; if ( a2 > a1 ) a2 -= 2.0 * M_PI; } /* p2 on right side => counter-clockwise sweep */ else { /* Adjust a3 up so we can increment from a1 to a3 cleanly */ if ( a3 < a1 ) a3 += 2.0 * M_PI; if ( a2 < a1 ) a2 += 2.0 * M_PI; } /* Override angles for circle case */ if( is_circle ) { a3 = a1 + 2.0 * M_PI; a2 = a1 + M_PI; increment = fabs(increment); clockwise = RT_FALSE; } /* Initialize point array */ pa = ptarray_construct_empty(ctx, 1, 1, 32); /* Sweep from a1 to a3 */ ptarray_append_point(ctx, pa, p1, RT_FALSE); for ( angle = a1 + increment; clockwise ? angle > a3 : angle < a3; angle += increment ) { pt.x = center.x + radius * cos(angle); pt.y = center.y + radius * sin(angle); pt.z = interpolate_arc(ctx, angle, a1, a2, a3, p1->z, p2->z, p3->z); pt.m = interpolate_arc(ctx, angle, a1, a2, a3, p1->m, p2->m, p3->m); ptarray_append_point(ctx, pa, &pt, RT_FALSE); } return pa; } RTLINE * rtcircstring_stroke(const RTCTX *ctx, const RTCIRCSTRING *icurve, uint32_t perQuad) { RTLINE *oline; RTPOINTARRAY *ptarray; RTPOINTARRAY *tmp; uint32_t i, j; RTPOINT4D p1, p2, p3, p4; RTDEBUGF(2, "rtcircstring_stroke called., dim = %d", icurve->points->flags); ptarray = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(icurve->points->flags), RTFLAGS_GET_M(icurve->points->flags), 64); for (i = 2; i < icurve->points->npoints; i+=2) { RTDEBUGF(3, "rtcircstring_stroke: arc ending at point %d", i); rt_getPoint4d_p(ctx, icurve->points, i - 2, &p1); rt_getPoint4d_p(ctx, icurve->points, i - 1, &p2); rt_getPoint4d_p(ctx, icurve->points, i, &p3); tmp = rtcircle_stroke(ctx, &p1, &p2, &p3, perQuad); if (tmp) { RTDEBUGF(3, "rtcircstring_stroke: generated %d points", tmp->npoints); for (j = 0; j < tmp->npoints; j++) { rt_getPoint4d_p(ctx, tmp, j, &p4); ptarray_append_point(ctx, ptarray, &p4, RT_TRUE); } ptarray_free(ctx, tmp); } else { RTDEBUG(3, "rtcircstring_stroke: points are colinear, returning curve points as line"); for (j = i - 2 ; j < i ; j++) { rt_getPoint4d_p(ctx, icurve->points, j, &p4); ptarray_append_point(ctx, ptarray, &p4, RT_TRUE); } } } rt_getPoint4d_p(ctx, icurve->points, icurve->points->npoints-1, &p1); ptarray_append_point(ctx, ptarray, &p1, RT_TRUE); oline = rtline_construct(ctx, icurve->srid, NULL, ptarray); return oline; } RTLINE * rtcompound_stroke(const RTCTX *ctx, const RTCOMPOUND *icompound, uint32_t perQuad) { RTGEOM *geom; RTPOINTARRAY *ptarray = NULL, *ptarray_out = NULL; RTLINE *tmp = NULL; uint32_t i, j; RTPOINT4D p; RTDEBUG(2, "rtcompound_stroke called."); ptarray = ptarray_construct_empty(ctx, RTFLAGS_GET_Z(icompound->flags), RTFLAGS_GET_M(icompound->flags), 64); for (i = 0; i < icompound->ngeoms; i++) { geom = icompound->geoms[i]; if (geom->type == RTCIRCSTRINGTYPE) { tmp = rtcircstring_stroke(ctx, (RTCIRCSTRING *)geom, perQuad); for (j = 0; j < tmp->points->npoints; j++) { rt_getPoint4d_p(ctx, tmp->points, j, &p); ptarray_append_point(ctx, ptarray, &p, RT_TRUE); } rtline_free(ctx, tmp); } else if (geom->type == RTLINETYPE) { tmp = (RTLINE *)geom; for (j = 0; j < tmp->points->npoints; j++) { rt_getPoint4d_p(ctx, tmp->points, j, &p); ptarray_append_point(ctx, ptarray, &p, RT_TRUE); } } else { rterror(ctx, "Unsupported geometry type %d found.", geom->type, rttype_name(ctx, geom->type)); return NULL; } } ptarray_out = ptarray_remove_repeated_points(ctx, ptarray, 0.0); ptarray_free(ctx, ptarray); return rtline_construct(ctx, icompound->srid, NULL, ptarray_out); } RTPOLY * rtcurvepoly_stroke(const RTCTX *ctx, const RTCURVEPOLY *curvepoly, uint32_t perQuad) { RTPOLY *ogeom; RTGEOM *tmp; RTLINE *line; RTPOINTARRAY **ptarray; int i; RTDEBUG(2, "rtcurvepoly_stroke called."); ptarray = rtalloc(ctx, sizeof(RTPOINTARRAY *)*curvepoly->nrings); for (i = 0; i < curvepoly->nrings; i++) { tmp = curvepoly->rings[i]; if (tmp->type == RTCIRCSTRINGTYPE) { line = rtcircstring_stroke(ctx, (RTCIRCSTRING *)tmp, perQuad); ptarray[i] = ptarray_clone_deep(ctx, line->points); rtline_free(ctx, line); } else if (tmp->type == RTLINETYPE) { line = (RTLINE *)tmp; ptarray[i] = ptarray_clone_deep(ctx, line->points); } else if (tmp->type == RTCOMPOUNDTYPE) { line = rtcompound_stroke(ctx, (RTCOMPOUND *)tmp, perQuad); ptarray[i] = ptarray_clone_deep(ctx, line->points); rtline_free(ctx, line); } else { rterror(ctx, "Invalid ring type found in CurvePoly."); return NULL; } } ogeom = rtpoly_construct(ctx, curvepoly->srid, NULL, curvepoly->nrings, ptarray); return ogeom; } RTMLINE * rtmcurve_stroke(const RTCTX *ctx, const RTMCURVE *mcurve, uint32_t perQuad) { RTMLINE *ogeom; RTGEOM **lines; int i; RTDEBUGF(2, "rtmcurve_stroke called, geoms=%d, dim=%d.", mcurve->ngeoms, RTFLAGS_NDIMS(mcurve->flags)); lines = rtalloc(ctx, sizeof(RTGEOM *)*mcurve->ngeoms); for (i = 0; i < mcurve->ngeoms; i++) { const RTGEOM *tmp = mcurve->geoms[i]; if (tmp->type == RTCIRCSTRINGTYPE) { lines[i] = (RTGEOM *)rtcircstring_stroke(ctx, (RTCIRCSTRING *)tmp, perQuad); } else if (tmp->type == RTLINETYPE) { lines[i] = (RTGEOM *)rtline_construct(ctx, mcurve->srid, NULL, ptarray_clone_deep(ctx, ((RTLINE *)tmp)->points)); } else if (tmp->type == RTCOMPOUNDTYPE) { lines[i] = (RTGEOM *)rtcompound_stroke(ctx, (RTCOMPOUND *)tmp, perQuad); } else { rterror(ctx, "Unsupported geometry found in MultiCurve."); return NULL; } } ogeom = (RTMLINE *)rtcollection_construct(ctx, RTMULTILINETYPE, mcurve->srid, NULL, mcurve->ngeoms, lines); return ogeom; } RTMPOLY * rtmsurface_stroke(const RTCTX *ctx, const RTMSURFACE *msurface, uint32_t perQuad) { RTMPOLY *ogeom; RTGEOM *tmp; RTPOLY *poly; RTGEOM **polys; RTPOINTARRAY **ptarray; int i, j; RTDEBUG(2, "rtmsurface_stroke called."); polys = rtalloc(ctx, sizeof(RTGEOM *)*msurface->ngeoms); for (i = 0; i < msurface->ngeoms; i++) { tmp = msurface->geoms[i]; if (tmp->type == RTCURVEPOLYTYPE) { polys[i] = (RTGEOM *)rtcurvepoly_stroke(ctx, (RTCURVEPOLY *)tmp, perQuad); } else if (tmp->type == RTPOLYGONTYPE) { poly = (RTPOLY *)tmp; ptarray = rtalloc(ctx, sizeof(RTPOINTARRAY *)*poly->nrings); for (j = 0; j < poly->nrings; j++) { ptarray[j] = ptarray_clone_deep(ctx, poly->rings[j]); } polys[i] = (RTGEOM *)rtpoly_construct(ctx, msurface->srid, NULL, poly->nrings, ptarray); } } ogeom = (RTMPOLY *)rtcollection_construct(ctx, RTMULTIPOLYGONTYPE, msurface->srid, NULL, msurface->ngeoms, polys); return ogeom; } RTCOLLECTION * rtcollection_stroke(const RTCTX *ctx, const RTCOLLECTION *collection, uint32_t perQuad) { RTCOLLECTION *ocol; RTGEOM *tmp; RTGEOM **geoms; int i; RTDEBUG(2, "rtcollection_stroke called."); geoms = rtalloc(ctx, sizeof(RTGEOM *)*collection->ngeoms); for (i=0; ingeoms; i++) { tmp = collection->geoms[i]; switch (tmp->type) { case RTCIRCSTRINGTYPE: geoms[i] = (RTGEOM *)rtcircstring_stroke(ctx, (RTCIRCSTRING *)tmp, perQuad); break; case RTCOMPOUNDTYPE: geoms[i] = (RTGEOM *)rtcompound_stroke(ctx, (RTCOMPOUND *)tmp, perQuad); break; case RTCURVEPOLYTYPE: geoms[i] = (RTGEOM *)rtcurvepoly_stroke(ctx, (RTCURVEPOLY *)tmp, perQuad); break; case RTCOLLECTIONTYPE: geoms[i] = (RTGEOM *)rtcollection_stroke(ctx, (RTCOLLECTION *)tmp, perQuad); break; default: geoms[i] = rtgeom_clone(ctx, tmp); break; } } ocol = rtcollection_construct(ctx, RTCOLLECTIONTYPE, collection->srid, NULL, collection->ngeoms, geoms); return ocol; } RTGEOM * rtgeom_stroke(const RTCTX *ctx, const RTGEOM *geom, uint32_t perQuad) { RTGEOM * ogeom = NULL; switch (geom->type) { case RTCIRCSTRINGTYPE: ogeom = (RTGEOM *)rtcircstring_stroke(ctx, (RTCIRCSTRING *)geom, perQuad); break; case RTCOMPOUNDTYPE: ogeom = (RTGEOM *)rtcompound_stroke(ctx, (RTCOMPOUND *)geom, perQuad); break; case RTCURVEPOLYTYPE: ogeom = (RTGEOM *)rtcurvepoly_stroke(ctx, (RTCURVEPOLY *)geom, perQuad); break; case RTMULTICURVETYPE: ogeom = (RTGEOM *)rtmcurve_stroke(ctx, (RTMCURVE *)geom, perQuad); break; case RTMULTISURFACETYPE: ogeom = (RTGEOM *)rtmsurface_stroke(ctx, (RTMSURFACE *)geom, perQuad); break; case RTCOLLECTIONTYPE: ogeom = (RTGEOM *)rtcollection_stroke(ctx, (RTCOLLECTION *)geom, perQuad); break; default: ogeom = rtgeom_clone(ctx, geom); } return ogeom; } /** * Return ABC angle in radians * TODO: move to rtalgorithm */ static double rt_arc_angle(const RTCTX *ctx, const RTPOINT2D *a, const RTPOINT2D *b, const RTPOINT2D *c) { RTPOINT2D ab, cb; ab.x = b->x - a->x; ab.y = b->y - a->y; cb.x = b->x - c->x; cb.y = b->y - c->y; double dot = (ab.x * cb.x + ab.y * cb.y); /* dot product */ double cross = (ab.x * cb.y - ab.y * cb.x); /* cross product */ double alpha = atan2(cross, dot); return alpha; } /** * Returns RT_TRUE if b is on the arc formed by a1/a2/a3, but not within * that portion already described by a1/a2/a3 */ static int pt_continues_arc(const RTCTX *ctx, const RTPOINT4D *a1, const RTPOINT4D *a2, const RTPOINT4D *a3, const RTPOINT4D *b) { RTPOINT2D center; RTPOINT2D *t1 = (RTPOINT2D*)a1; RTPOINT2D *t2 = (RTPOINT2D*)a2; RTPOINT2D *t3 = (RTPOINT2D*)a3; RTPOINT2D *tb = (RTPOINT2D*)b; double radius = rt_arc_center(ctx, t1, t2, t3, ¢er); double b_distance, diff; /* Co-linear a1/a2/a3 */ if ( radius < 0.0 ) return RT_FALSE; b_distance = distance2d_pt_pt(ctx, tb, ¢er); diff = fabs(radius - b_distance); RTDEBUGF(4, "circle_radius=%g, b_distance=%g, diff=%g, percentage=%g", radius, b_distance, diff, diff/radius); /* Is the point b on the circle? */ if ( diff < EPSILON_SQLMM ) { int a2_side = rt_segment_side(ctx, t1, t3, t2); int b_side = rt_segment_side(ctx, t1, t3, tb); double angle1 = rt_arc_angle(ctx, t1, t2, t3); double angle2 = rt_arc_angle(ctx, t2, t3, tb); /* Is the angle similar to the previous one ? */ diff = fabs(angle1 - angle2); RTDEBUGF(4, " angle1: %g, angle2: %g, diff:%g", angle1, angle2, diff); if ( diff > EPSILON_SQLMM ) { return RT_FALSE; } /* Is the point b on the same side of a1/a3 as the mid-point a2 is? */ /* If not, it's in the unbounded part of the circle, so it continues the arc, return true. */ if ( b_side != a2_side ) return RT_TRUE; } return RT_FALSE; } static RTGEOM* linestring_from_pa(const RTCTX *ctx, const RTPOINTARRAY *pa, int srid, int start, int end) { int i = 0, j = 0; RTPOINT4D p; RTPOINTARRAY *pao = ptarray_construct(ctx, ptarray_has_z(ctx, pa), ptarray_has_m(ctx, pa), end-start+2); RTDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end); for( i = start; i < end + 2; i++ ) { rt_getPoint4d_p(ctx, pa, i, &p); ptarray_set_point4d(ctx, pao, j++, &p); } return rtline_as_rtgeom(ctx, rtline_construct(ctx, srid, NULL, pao)); } static RTGEOM* circstring_from_pa(const RTCTX *ctx, const RTPOINTARRAY *pa, int srid, int start, int end) { RTPOINT4D p0, p1, p2; RTPOINTARRAY *pao = ptarray_construct(ctx, ptarray_has_z(ctx, pa), ptarray_has_m(ctx, pa), 3); RTDEBUGF(4, "srid=%d, start=%d, end=%d", srid, start, end); rt_getPoint4d_p(ctx, pa, start, &p0); ptarray_set_point4d(ctx, pao, 0, &p0); rt_getPoint4d_p(ctx, pa, (start+end+1)/2, &p1); ptarray_set_point4d(ctx, pao, 1, &p1); rt_getPoint4d_p(ctx, pa, end+1, &p2); ptarray_set_point4d(ctx, pao, 2, &p2); return rtcircstring_as_rtgeom(ctx, rtcircstring_construct(ctx, srid, NULL, pao)); } static RTGEOM* geom_from_pa(const RTCTX *ctx, const RTPOINTARRAY *pa, int srid, int is_arc, int start, int end) { RTDEBUGF(4, "srid=%d, is_arc=%d, start=%d, end=%d", srid, is_arc, start, end); if ( is_arc ) return circstring_from_pa(ctx, pa, srid, start, end); else return linestring_from_pa(ctx, pa, srid, start, end); } RTGEOM* pta_unstroke(const RTCTX *ctx, const RTPOINTARRAY *points, int type, int srid) { int i = 0, j, k; RTPOINT4D a1, a2, a3, b; RTPOINT4D first, center; char *edges_in_arcs; int found_arc = RT_FALSE; int current_arc = 1; int num_edges; int edge_type; /* non-zero if edge is part of an arc */ int start, end; RTCOLLECTION *outcol; /* Minimum number of edges, per quadrant, required to define an arc */ const unsigned int min_quad_edges = 2; /* Die on null input */ if ( ! points ) rterror(ctx, "pta_unstroke called with null pointarray"); /* Null on empty input? */ if ( points->npoints == 0 ) return NULL; /* We can't desegmentize anything shorter than four points */ if ( points->npoints < 4 ) { /* Return a linestring here*/ rterror(ctx, "pta_unstroke needs implementation for npoints < 4"); } /* Allocate our result array of vertices that are part of arcs */ num_edges = points->npoints - 1; edges_in_arcs = rtalloc(ctx, num_edges + 1); memset(edges_in_arcs, 0, num_edges + 1); /* We make a candidate arc of the first two edges, */ /* And then see if the next edge follows it */ while( i < num_edges-2 ) { unsigned int arc_edges; double num_quadrants; double angle; found_arc = RT_FALSE; /* Make candidate arc */ rt_getPoint4d_p(ctx, points, i , &a1); rt_getPoint4d_p(ctx, points, i+1, &a2); rt_getPoint4d_p(ctx, points, i+2, &a3); memcpy(&first, &a1, sizeof(RTPOINT4D)); for( j = i+3; j < num_edges+1; j++ ) { RTDEBUGF(4, "i=%d, j=%d", i, j); rt_getPoint4d_p(ctx, points, j, &b); /* Does this point fall on our candidate arc? */ if ( pt_continues_arc(ctx, &a1, &a2, &a3, &b) ) { /* Yes. Mark this edge and the two preceding it as arc components */ RTDEBUGF(4, "pt_continues_arc #%d", current_arc); found_arc = RT_TRUE; for ( k = j-1; k > j-4; k-- ) edges_in_arcs[k] = current_arc; } else { /* No. So we're done with this candidate arc */ RTDEBUG(4, "pt_continues_arc = false"); current_arc++; break; } memcpy(&a1, &a2, sizeof(RTPOINT4D)); memcpy(&a2, &a3, sizeof(RTPOINT4D)); memcpy(&a3, &b, sizeof(RTPOINT4D)); } /* Jump past all the edges that were added to the arc */ if ( found_arc ) { /* Check if an arc was composed by enough edges to be * really considered an arc * See http://trac.osgeo.org/postgis/ticket/2420 */ arc_edges = j - 1 - i; RTDEBUGF(4, "arc defined by %d edges found", arc_edges); if ( first.x == b.x && first.y == b.y ) { RTDEBUG(4, "arc is a circle"); num_quadrants = 4; } else { rt_arc_center(ctx, (RTPOINT2D*)&first, (RTPOINT2D*)&b, (RTPOINT2D*)&a1, (RTPOINT2D*)¢er); angle = rt_arc_angle(ctx, (RTPOINT2D*)&first, (RTPOINT2D*)¢er, (RTPOINT2D*)&b); int p2_side = rt_segment_side(ctx, (RTPOINT2D*)&first, (RTPOINT2D*)&a1, (RTPOINT2D*)&b); if ( p2_side >= 0 ) angle = -angle; if ( angle < 0 ) angle = 2 * M_PI + angle; num_quadrants = ( 4 * angle ) / ( 2 * M_PI ); RTDEBUGF(4, "arc angle (%g %g, %g %g, %g %g) is %g (side is %d), quandrants:%g", first.x, first.y, center.x, center.y, b.x, b.y, angle, p2_side, num_quadrants); } /* a1 is first point, b is last point */ if ( arc_edges < min_quad_edges * num_quadrants ) { RTDEBUGF(4, "Not enough edges for a %g quadrants arc, %g needed", num_quadrants, min_quad_edges * num_quadrants); for ( k = j-1; k >= i; k-- ) edges_in_arcs[k] = 0; } i = j-1; } else { /* Mark this edge as a linear edge */ edges_in_arcs[i] = 0; i = i+1; } } #if RTGEOM_DEBUG_LEVEL > 3 { char *edgestr = rtalloc(ctx, num_edges+1); for ( i = 0; i < num_edges; i++ ) { if ( edges_in_arcs[i] ) edgestr[i] = 48 + edges_in_arcs[i]; else edgestr[i] = '.'; } edgestr[num_edges] = 0; RTDEBUGF(3, "edge pattern %s", edgestr); rtfree(ctx, edgestr); } #endif start = 0; edge_type = edges_in_arcs[0]; outcol = rtcollection_construct_empty(ctx, RTCOMPOUNDTYPE, srid, ptarray_has_z(ctx, points), ptarray_has_m(ctx, points)); for( i = 1; i < num_edges; i++ ) { if( edge_type != edges_in_arcs[i] ) { end = i - 1; rtcollection_add_rtgeom(ctx, outcol, geom_from_pa(ctx, points, srid, edge_type, start, end)); start = i; edge_type = edges_in_arcs[i]; } } rtfree(ctx, edges_in_arcs); /* not needed anymore */ /* Roll out last item */ end = num_edges - 1; rtcollection_add_rtgeom(ctx, outcol, geom_from_pa(ctx, points, srid, edge_type, start, end)); /* Strip down to singleton if only one entry */ if ( outcol->ngeoms == 1 ) { RTGEOM *outgeom = outcol->geoms[0]; outcol->ngeoms = 0; rtcollection_free(ctx, outcol); return outgeom; } return rtcollection_as_rtgeom(ctx, outcol); } RTGEOM * rtline_unstroke(const RTCTX *ctx, const RTLINE *line) { RTDEBUG(2, "rtline_unstroke called."); if ( line->points->npoints < 4 ) return rtline_as_rtgeom(ctx, rtline_clone(ctx, line)); else return pta_unstroke(ctx, line->points, line->flags, line->srid); } RTGEOM * rtpolygon_unstroke(const RTCTX *ctx, const RTPOLY *poly) { RTGEOM **geoms; int i, hascurve = 0; RTDEBUG(2, "rtpolygon_unstroke called."); geoms = rtalloc(ctx, sizeof(RTGEOM *)*poly->nrings); for (i=0; inrings; i++) { geoms[i] = pta_unstroke(ctx, poly->rings[i], poly->flags, poly->srid); if (geoms[i]->type == RTCIRCSTRINGTYPE || geoms[i]->type == RTCOMPOUNDTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; inrings; i++) { rtfree(ctx, geoms[i]); /* TODO: should this be rtgeom_free instead ? */ } return rtgeom_clone(ctx, (RTGEOM *)poly); } return (RTGEOM *)rtcollection_construct(ctx, RTCURVEPOLYTYPE, poly->srid, NULL, poly->nrings, geoms); } RTGEOM * rtmline_unstroke(const RTCTX *ctx, const RTMLINE *mline) { RTGEOM **geoms; int i, hascurve = 0; RTDEBUG(2, "rtmline_unstroke called."); geoms = rtalloc(ctx, sizeof(RTGEOM *)*mline->ngeoms); for (i=0; ingeoms; i++) { geoms[i] = rtline_unstroke(ctx, (RTLINE *)mline->geoms[i]); if (geoms[i]->type == RTCIRCSTRINGTYPE || geoms[i]->type == RTCOMPOUNDTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; ingeoms; i++) { rtfree(ctx, geoms[i]); /* TODO: should this be rtgeom_free instead ? */ } return rtgeom_clone(ctx, (RTGEOM *)mline); } return (RTGEOM *)rtcollection_construct(ctx, RTMULTICURVETYPE, mline->srid, NULL, mline->ngeoms, geoms); } RTGEOM * rtmpolygon_unstroke(const RTCTX *ctx, const RTMPOLY *mpoly) { RTGEOM **geoms; int i, hascurve = 0; RTDEBUG(2, "rtmpoly_unstroke called."); geoms = rtalloc(ctx, sizeof(RTGEOM *)*mpoly->ngeoms); for (i=0; ingeoms; i++) { geoms[i] = rtpolygon_unstroke(ctx, (RTPOLY *)mpoly->geoms[i]); if (geoms[i]->type == RTCURVEPOLYTYPE) { hascurve = 1; } } if (hascurve == 0) { for (i=0; ingeoms; i++) { rtfree(ctx, geoms[i]); /* TODO: should this be rtgeom_free instead ? */ } return rtgeom_clone(ctx, (RTGEOM *)mpoly); } return (RTGEOM *)rtcollection_construct(ctx, RTMULTISURFACETYPE, mpoly->srid, NULL, mpoly->ngeoms, geoms); } RTGEOM * rtgeom_unstroke(const RTCTX *ctx, const RTGEOM *geom) { RTDEBUG(2, "rtgeom_unstroke called."); switch (geom->type) { case RTLINETYPE: return rtline_unstroke(ctx, (RTLINE *)geom); case RTPOLYGONTYPE: return rtpolygon_unstroke(ctx, (RTPOLY *)geom); case RTMULTILINETYPE: return rtmline_unstroke(ctx, (RTMLINE *)geom); case RTMULTIPOLYGONTYPE: return rtmpolygon_unstroke(ctx, (RTMPOLY *)geom); default: return rtgeom_clone(ctx, geom); } } src/rttin.c000066400000000000000000000110321271715413500131540ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2001-2006 Refractions Research Inc. * **********************************************************************/ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" RTTIN* rttin_add_rttriangle(const RTCTX *ctx, RTTIN *mobj, const RTTRIANGLE *obj) { return (RTTIN*)rtcollection_add_rtgeom(ctx, (RTCOLLECTION*)mobj, (RTGEOM*)obj); } void rttin_free(const RTCTX *ctx, RTTIN *tin) { int i; if ( ! tin ) return; if ( tin->bbox ) rtfree(ctx, tin->bbox); for ( i = 0; i < tin->ngeoms; i++ ) if ( tin->geoms && tin->geoms[i] ) rttriangle_free(ctx, tin->geoms[i]); if ( tin->geoms ) rtfree(ctx, tin->geoms); rtfree(ctx, tin); } void printRTTIN(const RTCTX *ctx, RTTIN *tin) { int i; RTTRIANGLE *triangle; if (tin->type != RTTINTYPE) rterror(ctx, "printRTTIN called with something else than a TIN"); rtnotice(ctx, "RTTIN {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(tin->flags)); rtnotice(ctx, " SRID = %i", (int)tin->srid); rtnotice(ctx, " ngeoms = %i", (int)tin->ngeoms); for (i=0; ingeoms; i++) { triangle = (RTTRIANGLE *) tin->geoms[i]; printPA(ctx, triangle->points); } rtnotice(ctx, "}"); } /* * TODO rewrite all this stuff to be based on a truly topological model */ struct struct_tin_arcs { double ax, ay, az; double bx, by, bz; int cnt, face; }; typedef struct struct_tin_arcs *tin_arcs; /* We supposed that the geometry is valid we could have wrong result if not */ int rttin_is_closed(const RTCTX *ctx, const RTTIN *tin) { int i, j, k; int narcs, carc; int found; tin_arcs arcs; RTPOINT4D pa, pb; RTTRIANGLE *patch; /* If surface is not 3D, it's can't be closed */ if (!RTFLAGS_GET_Z(tin->flags)) return 0; /* Max theorical arcs number if no one is shared ... */ narcs = 3 * tin->ngeoms; arcs = rtalloc(ctx, sizeof(struct struct_tin_arcs) * narcs); for (i=0, carc=0; i < tin->ngeoms ; i++) { patch = (RTTRIANGLE *) tin->geoms[i]; for (j=0; j < 3 ; j++) { rt_getPoint4d_p(ctx, patch->points, j, &pa); rt_getPoint4d_p(ctx, patch->points, j+1, &pb); /* Make sure to order the 'lower' point first */ if ( (pa.x > pb.x) || (pa.x == pb.x && pa.y > pb.y) || (pa.x == pb.x && pa.y == pb.y && pa.z > pb.z) ) { pa = pb; rt_getPoint4d_p(ctx, patch->points, j, &pb); } for (found=0, k=0; k < carc ; k++) { if ( ( arcs[k].ax == pa.x && arcs[k].ay == pa.y && arcs[k].az == pa.z && arcs[k].bx == pb.x && arcs[k].by == pb.y && arcs[k].bz == pb.z && arcs[k].face != i) ) { arcs[k].cnt++; found = 1; /* Look like an invalid TIN anyway not a closed one */ if (arcs[k].cnt > 2) { rtfree(ctx, arcs); return 0; } } } if (!found) { arcs[carc].cnt=1; arcs[carc].face=i; arcs[carc].ax = pa.x; arcs[carc].ay = pa.y; arcs[carc].az = pa.z; arcs[carc].bx = pb.x; arcs[carc].by = pb.y; arcs[carc].bz = pb.z; carc++; /* Look like an invalid TIN anyway not a closed one */ if (carc > narcs) { rtfree(ctx, arcs); return 0; } } } } /* A TIN is closed if each edge is shared by exactly 2 faces */ for (k=0; k < carc ; k++) { if (arcs[k].cnt != 2) { rtfree(ctx, arcs); return 0; } } rtfree(ctx, arcs); /* Invalid TIN case */ if (carc < tin->ngeoms) return 0; return 1; } src/rttree.c000066400000000000000000000161041271715413500133260ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009-2012 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "rtgeom_log.h" #include "rttree.h" /** * Internal nodes have their point references set to NULL. */ static int rect_node_is_leaf(const RTCTX *ctx, const RECT_NODE *node) { return (node->p1 != NULL); } /** * Recurse from top of node tree and free all children. * does not free underlying point array. */ void rect_tree_free(const RTCTX *ctx, RECT_NODE *node) { if ( node->left_node ) { rect_tree_free(ctx, node->left_node); node->left_node = 0; } if ( node->right_node ) { rect_tree_free(ctx, node->right_node); node->right_node = 0; } rtfree(ctx, node); } /* 0 => no containment */ int rect_tree_contains_point(const RTCTX *ctx, const RECT_NODE *node, const RTPOINT2D *pt, int *on_boundary) { if ( FP_CONTAINS_INCL(node->ymin, pt->y, node->ymax) ) { if ( rect_node_is_leaf(ctx, node) ) { double side = rt_segment_side(ctx, node->p1, node->p2, pt); if ( side == 0 ) *on_boundary = RT_TRUE; return (side < 0 ? -1 : 1 ); } else { return rect_tree_contains_point(ctx, node->left_node, pt, on_boundary) + rect_tree_contains_point(ctx, node->right_node, pt, on_boundary); } } /* printf("NOT in measure range\n"); */ return 0; } int rect_tree_intersects_tree(const RTCTX *ctx, const RECT_NODE *n1, const RECT_NODE *n2) { RTDEBUGF(4,"n1 (%.9g %.9g,%.9g %.9g) vs n2 (%.9g %.9g,%.9g %.9g)",n1->xmin,n1->ymin,n1->xmax,n1->ymax,n2->xmin,n2->ymin,n2->xmax,n2->ymax); /* There can only be an edge intersection if the rectangles overlap */ if ( ! ( FP_GT(n1->xmin, n2->xmax) || FP_GT(n2->xmin, n1->xmax) || FP_GT(n1->ymin, n2->ymax) || FP_GT(n2->ymin, n1->ymax) ) ) { RTDEBUG(4," interaction found"); /* We can only test for a true intersection if the nodes are both leaf nodes */ if ( rect_node_is_leaf(ctx, n1) && rect_node_is_leaf(ctx, n2) ) { RTDEBUG(4," leaf node test"); /* Check for true intersection */ if ( rt_segment_intersects(ctx, n1->p1, n1->p2, n2->p1, n2->p2) ) return RT_TRUE; else return RT_FALSE; } else { RTDEBUG(4," internal node found, recursing"); /* Recurse to children */ if ( rect_node_is_leaf(ctx, n1) ) { if ( rect_tree_intersects_tree(ctx, n2->left_node, n1) || rect_tree_intersects_tree(ctx, n2->right_node, n1) ) return RT_TRUE; else return RT_FALSE; } else { if ( rect_tree_intersects_tree(ctx, n1->left_node, n2) || rect_tree_intersects_tree(ctx, n1->right_node, n2) ) return RT_TRUE; else return RT_FALSE; } } } else { RTDEBUG(4," no interaction found"); return RT_FALSE; } } /** * Create a new leaf node, calculating a measure value for each point on the * edge and storing pointers back to the end points for later. */ RECT_NODE* rect_node_leaf_new(const RTCTX *ctx, const RTPOINTARRAY *pa, int i) { RTPOINT2D *p1, *p2; RECT_NODE *node; p1 = (RTPOINT2D*)rt_getPoint_internal(ctx, pa, i); p2 = (RTPOINT2D*)rt_getPoint_internal(ctx, pa, i+1); /* Zero length edge, doesn't get a node */ if ( FP_EQUALS(p1->x, p2->x) && FP_EQUALS(p1->y, p2->y) ) return NULL; node = rtalloc(ctx, sizeof(RECT_NODE)); node->p1 = p1; node->p2 = p2; node->xmin = FP_MIN(p1->x,p2->x); node->xmax = FP_MAX(p1->x,p2->x); node->ymin = FP_MIN(p1->y,p2->y); node->ymax = FP_MAX(p1->y,p2->y); node->left_node = NULL; node->right_node = NULL; return node; } /** * Create a new internal node, calculating the new measure range for the node, * and storing pointers to the child nodes. */ RECT_NODE* rect_node_internal_new(const RTCTX *ctx, RECT_NODE *left_node, RECT_NODE *right_node) { RECT_NODE *node = rtalloc(ctx, sizeof(RECT_NODE)); node->p1 = NULL; node->p2 = NULL; node->xmin = FP_MIN(left_node->xmin, right_node->xmin); node->xmax = FP_MAX(left_node->xmax, right_node->xmax); node->ymin = FP_MIN(left_node->ymin, right_node->ymin); node->ymax = FP_MAX(left_node->ymax, right_node->ymax); node->left_node = left_node; node->right_node = right_node; return node; } /** * Build a tree of nodes from a point array, one node per edge, and each * with an associated measure range along a one-dimensional space. We * can then search that space as a range tree. */ RECT_NODE* rect_tree_new(const RTCTX *ctx, const RTPOINTARRAY *pa) { int num_edges, num_children, num_parents; int i, j; RECT_NODE **nodes; RECT_NODE *node; RECT_NODE *tree; if ( pa->npoints < 2 ) { return NULL; } /* ** First create a flat list of nodes, one per edge. ** For each vertex, transform into our one-dimensional measure. ** Hopefully, when projected, the points turn into a fairly ** uniformly distributed collection of measures. */ num_edges = pa->npoints - 1; nodes = rtalloc(ctx, sizeof(RECT_NODE*) * pa->npoints); j = 0; for ( i = 0; i < num_edges; i++ ) { node = rect_node_leaf_new(ctx, pa, i); if ( node ) /* Not zero length? */ { nodes[j] = node; j++; } } /* ** If we sort the nodelist first, we'll get a more balanced tree ** in the end, but at the cost of sorting. For now, we just ** build the tree knowing that point arrays tend to have a ** reasonable amount of sorting already. */ num_children = j; num_parents = num_children / 2; while ( num_parents > 0 ) { j = 0; while ( j < num_parents ) { /* ** Each new parent includes pointers to the children, so even though ** we are over-writing their place in the list, we still have references ** to them via the tree. */ nodes[j] = rect_node_internal_new(ctx, nodes[2*j], nodes[(2*j)+1]); j++; } /* Odd number of children, just copy the last node up a level */ if ( num_children % 2 ) { nodes[j] = nodes[num_children - 1]; num_parents++; } num_children = num_parents; num_parents = num_children / 2; } /* Take a reference to the head of the tree*/ tree = nodes[0]; /* Free the old list structure, leaving the tree in place */ rtfree(ctx, nodes); return tree; } src/rttree.h000066400000000000000000000032571271715413500133400ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2009-2012 Paul Ramsey * **********************************************************************/ typedef struct rect_node { double xmin; double xmax; double ymin; double ymax; struct rect_node *left_node; struct rect_node *right_node; RTPOINT2D *p1; RTPOINT2D *p2; } RECT_NODE; int rect_tree_contains_point(const RTCTX *ctx, const RECT_NODE *tree, const RTPOINT2D *pt, int *on_boundary); int rect_tree_intersects_tree(const RTCTX *ctx, const RECT_NODE *tree1, const RECT_NODE *tree2); void rect_tree_free(const RTCTX *ctx, RECT_NODE *node); RECT_NODE* rect_node_leaf_new(const RTCTX *ctx, const RTPOINTARRAY *pa, int i); RECT_NODE* rect_node_internal_new(const RTCTX *ctx, RECT_NODE *left_node, RECT_NODE *right_node); RECT_NODE* rect_tree_new(const RTCTX *ctx, const RTPOINTARRAY *pa); src/rttriangle.c000066400000000000000000000132461271715413500142000ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2010 - Oslandia * **********************************************************************/ /* basic RTTRIANGLE manipulation */ #include #include #include #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /* construct a new RTTRIANGLE. * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ RTTRIANGLE* rttriangle_construct(const RTCTX *ctx, int srid, RTGBOX *bbox, RTPOINTARRAY *points) { RTTRIANGLE *result; result = (RTTRIANGLE*) rtalloc(ctx, sizeof(RTTRIANGLE)); result->type = RTTRIANGLETYPE; result->flags = points->flags; RTFLAGS_SET_BBOX(result->flags, bbox?1:0); result->srid = srid; result->points = points; result->bbox = bbox; return result; } RTTRIANGLE* rttriangle_construct_empty(const RTCTX *ctx, int srid, char hasz, char hasm) { RTTRIANGLE *result = rtalloc(ctx, sizeof(RTTRIANGLE)); result->type = RTTRIANGLETYPE; result->flags = gflags(ctx, hasz,hasm,0); result->srid = srid; result->points = ptarray_construct_empty(ctx, hasz, hasm, 1); result->bbox = NULL; return result; } void rttriangle_free(const RTCTX *ctx, RTTRIANGLE *triangle) { if ( ! triangle ) return; if (triangle->bbox) rtfree(ctx, triangle->bbox); if (triangle->points) ptarray_free(ctx, triangle->points); rtfree(ctx, triangle); } void printRTTRIANGLE(const RTCTX *ctx, RTTRIANGLE *triangle) { if (triangle->type != RTTRIANGLETYPE) rterror(ctx, "printRTTRIANGLE called with something else than a Triangle"); rtnotice(ctx, "RTTRIANGLE {"); rtnotice(ctx, " ndims = %i", (int)RTFLAGS_NDIMS(triangle->flags)); rtnotice(ctx, " SRID = %i", (int)triangle->srid); printPA(ctx, triangle->points); rtnotice(ctx, "}"); } /* @brief Clone RTTRIANGLE object. Serialized point lists are not copied. * * @see ptarray_clone */ RTTRIANGLE * rttriangle_clone(const RTCTX *ctx, const RTTRIANGLE *g) { RTDEBUGF(2, "rttriangle_clone called with %p", g); return (RTTRIANGLE *)rtline_clone(ctx, (const RTLINE *)g); } void rttriangle_force_clockwise(const RTCTX *ctx, RTTRIANGLE *triangle) { if ( ptarray_isccw(ctx, triangle->points) ) ptarray_reverse(ctx, triangle->points); } void rttriangle_reverse(const RTCTX *ctx, RTTRIANGLE *triangle) { if( rttriangle_is_empty(ctx, triangle) ) return; ptarray_reverse(ctx, triangle->points); } void rttriangle_release(const RTCTX *ctx, RTTRIANGLE *rttriangle) { rtgeom_release(ctx, rttriangle_as_rtgeom(ctx, rttriangle)); } /* check coordinate equality */ char rttriangle_same(const RTCTX *ctx, const RTTRIANGLE *t1, const RTTRIANGLE *t2) { char r = ptarray_same(ctx, t1->points, t2->points); RTDEBUGF(5, "returning %d", r); return r; } /* * Construct a triangle from a RTLINE being * the shell * Pointarray from intput geom are cloned. * Input line must have 4 points, and be closed. */ RTTRIANGLE * rttriangle_from_rtline(const RTCTX *ctx, const RTLINE *shell) { RTTRIANGLE *ret; RTPOINTARRAY *pa; if ( shell->points->npoints != 4 ) rterror(ctx, "rttriangle_from_rtline: shell must have exactly 4 points"); if ( (!RTFLAGS_GET_Z(shell->flags) && !ptarray_is_closed_2d(ctx, shell->points)) || (RTFLAGS_GET_Z(shell->flags) && !ptarray_is_closed_3d(ctx, shell->points)) ) rterror(ctx, "rttriangle_from_rtline: shell must be closed"); pa = ptarray_clone_deep(ctx, shell->points); ret = rttriangle_construct(ctx, shell->srid, NULL, pa); if (rttriangle_is_repeated_points(ctx, ret)) rterror(ctx, "rttriangle_from_rtline: some points are repeated in triangle"); return ret; } char rttriangle_is_repeated_points(const RTCTX *ctx, RTTRIANGLE *triangle) { char ret; RTPOINTARRAY *pa; pa = ptarray_remove_repeated_points(ctx, triangle->points, 0.0); ret = ptarray_same(ctx, pa, triangle->points); ptarray_free(ctx, pa); return ret; } int rttriangle_is_empty(const RTCTX *ctx, const RTTRIANGLE *triangle) { if ( !triangle->points || triangle->points->npoints < 1 ) return RT_TRUE; return RT_FALSE; } /** * Find the area of the outer ring */ double rttriangle_area(const RTCTX *ctx, const RTTRIANGLE *triangle) { double area=0.0; int i; RTPOINT2D p1; RTPOINT2D p2; if (! triangle->points->npoints) return area; /* empty triangle */ for (i=0; i < triangle->points->npoints-1; i++) { rt_getPoint2d_p(ctx, triangle->points, i, &p1); rt_getPoint2d_p(ctx, triangle->points, i+1, &p2); area += ( p1.x * p2.y ) - ( p1.y * p2.x ); } area /= 2.0; return fabs(area); } double rttriangle_perimeter(const RTCTX *ctx, const RTTRIANGLE *triangle) { if( triangle->points ) return ptarray_length(ctx, triangle->points); else return 0.0; } double rttriangle_perimeter_2d(const RTCTX *ctx, const RTTRIANGLE *triangle) { if( triangle->points ) return ptarray_length_2d(ctx, triangle->points); else return 0.0; } src/rtutil.c000066400000000000000000000223001271715413500133370ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2004-2016 Sandro Santilli * Copyright (C) 2006 Mark Leslie * Copyright (C) 2008-2009 Mark Cave-Ayland * Copyright (C) 2009-2015 Paul Ramsey * Copyright (C) 2010 Olivier Courtin * **********************************************************************/ #include #include #include #include #include /* for tolower */ /* Global variables */ #include "rttopo_config.h" #include "librttopo_geom_internal.h" #include "rtgeom_log.h" /* Default allocators */ static void * default_allocator(size_t size); static void default_freeor(void *mem); static void * default_reallocator(void *mem, size_t size); #define RT_MSG_MAXLEN 256 static char *rtgeomTypeName[] = { "Unknown", "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon", "GeometryCollection", "CircularString", "CompoundCurve", "CurvePolygon", "MultiCurve", "MultiSurface", "PolyhedralSurface", "Triangle", "Tin" }; /* * Default rtnotice/rterror handlers * * Since variadic functions cannot pass their parameters directly, we need * wrappers for these functions to convert the arguments into a va_list * structure. */ void rtnotice(const RTCTX *ctx, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*ctx->notice_logger)(fmt, ap, ctx->notice_logger_arg); va_end(ap); } void rterror(const RTCTX *ctx, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*ctx->error_logger)(fmt, ap, ctx->error_logger_arg); va_end(ap); } void rtdebug(const RTCTX *ctx, int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); /* Call the supplied function */ (*ctx->debug_logger)(level, fmt, ap, ctx->debug_logger_arg); va_end(ap); } /* * Default allocators * * We include some default allocators that use malloc/free/realloc * along with stdout/stderr since this is the most common use case * */ static void * default_allocator(size_t size) { void *mem = malloc(size); return mem; } static void default_freeor(void *mem) { free(mem); } static void * default_reallocator(void *mem, size_t size) { void *ret = realloc(mem, size); return ret; } static void default_noticereporter(const char *fmt, va_list ap, void *arg) { char msg[RT_MSG_MAXLEN+1]; vsnprintf (msg, RT_MSG_MAXLEN, fmt, ap); msg[RT_MSG_MAXLEN]='\0'; printf("%s\n", msg); } static void default_debuglogger(int level, const char *fmt, va_list ap, void *arg) { char msg[RT_MSG_MAXLEN+1]; if ( RTGEOM_DEBUG_LEVEL >= level ) { /* Space pad the debug output */ int i; for ( i = 0; i < level; i++ ) msg[i] = ' '; vsnprintf(msg+i, RT_MSG_MAXLEN-i, fmt, ap); msg[RT_MSG_MAXLEN]='\0'; printf("%s\n", msg); } } static void default_errorreporter(const char *fmt, va_list ap, void *arg) { char msg[RT_MSG_MAXLEN+1]; vsnprintf (msg, RT_MSG_MAXLEN, fmt, ap); msg[RT_MSG_MAXLEN]='\0'; fprintf(stderr, "%s\n", msg); exit(1); } RTCTX * rtgeom_init(rtallocator allocator, rtreallocator reallocator, rtfreeor freeor) { RTCTX *ctx = allocator ? allocator(sizeof(RTCTX)) : default_allocator(sizeof(RTCTX)); memset(ctx, '\0', sizeof(RTCTX)); ctx->rtalloc_var = default_allocator; ctx->rtrealloc_var = default_reallocator; ctx->rtfree_var = default_freeor; if ( allocator ) ctx->rtalloc_var = allocator; if ( reallocator ) ctx->rtrealloc_var = reallocator; if ( freeor ) ctx->rtfree_var = freeor; ctx->notice_logger = default_noticereporter; ctx->error_logger = default_errorreporter; ctx->debug_logger = default_debuglogger; return ctx; } void rtgeom_finish(RTCTX *ctx) { if (ctx->gctx != NULL) GEOS_finish_r(ctx->gctx); ctx->rtfree_var(ctx); } void rtgeom_set_error_logger(RTCTX *ctx, rtreporter logger, void *arg) { ctx->error_logger = logger; ctx->error_logger_arg = arg; } void rtgeom_set_notice_logger(RTCTX *ctx, rtreporter logger, void *arg) { ctx->notice_logger = logger; ctx->notice_logger_arg = arg; } void rtgeom_set_debug_logger(RTCTX *ctx, rtdebuglogger logger, void *arg) { ctx->debug_logger = logger; ctx->debug_logger_arg = arg; } const char* rttype_name(const RTCTX *ctx, uint8_t type) { if ( type > 15 ) { /* assert(0); */ return "Invalid type"; } return rtgeomTypeName[(int ) type]; } void * rtalloc(const RTCTX *ctx, size_t size) { void *mem = ctx->rtalloc_var(size); RTDEBUGF(5, "rtalloc: %d@%p", size, mem); return mem; } void * rtrealloc(const RTCTX *ctx, void *mem, size_t size) { RTDEBUGF(5, "rtrealloc: %d@%p", size, mem); return ctx->rtrealloc_var(mem, size); } void rtfree(const RTCTX *ctx, void *mem) { ctx->rtfree_var(mem); } /* * Removes trailing zeros and dot for a %f formatted number. * Modifies input. */ void trim_trailing_zeros(const RTCTX *ctx, char *str) { char *ptr, *totrim=NULL; int len; int i; RTDEBUGF(3, "input: %s", str); ptr = strchr(str, '.'); if ( ! ptr ) return; /* no dot, no decimal digits */ RTDEBUGF(3, "ptr: %s", ptr); len = strlen(ptr); for (i=len-1; i; i--) { if ( ptr[i] != '0' ) break; totrim=&ptr[i]; } if ( totrim ) { if ( ptr == totrim-1 ) *ptr = '\0'; else *totrim = '\0'; } RTDEBUGF(3, "output: %s", str); } /* * Returns a new string which contains a maximum of maxlength characters starting * from startpos and finishing at endpos (0-based indexing). If the string is * truncated then the first or last characters are replaced by "..." as * appropriate. * * The caller should specify start or end truncation by setting the truncdirection * parameter as follows: * 0 - start truncation (i.e. characters are removed from the beginning) * 1 - end trunctation (i.e. characters are removed from the end) */ char * rtmessage_truncate(const RTCTX *ctx, char *str, int startpos, int endpos, int maxlength, int truncdirection) { char *output; char *outstart; /* Allocate space for new string */ output = rtalloc(ctx, maxlength + 4); output[0] = '\0'; /* Start truncation */ if (truncdirection == 0) { /* Calculate the start position */ if (endpos - startpos < maxlength) { outstart = str + startpos; strncat(output, outstart, endpos - startpos + 1); } else { if (maxlength >= 3) { /* Add "..." prefix */ outstart = str + endpos + 1 - maxlength + 3; strncat(output, "...", 3); strncat(output, outstart, maxlength - 3); } else { /* maxlength is too small; just output "..." */ strncat(output, "...", 3); } } } /* End truncation */ if (truncdirection == 1) { /* Calculate the end position */ if (endpos - startpos < maxlength) { outstart = str + startpos; strncat(output, outstart, endpos - startpos + 1); } else { if (maxlength >= 3) { /* Add "..." suffix */ outstart = str + startpos; strncat(output, outstart, maxlength - 3); strncat(output, "...", 3); } else { /* maxlength is too small; just output "..." */ strncat(output, "...", 3); } } } return output; } char getMachineEndian(const RTCTX *ctx) { static int endian_check_int = 1; /* dont modify this!!! */ return *((char *) &endian_check_int); /* 0 = big endian | xdr, * 1 = little endian | ndr */ } void error_if_srid_mismatch(const RTCTX *ctx, int srid1, int srid2) { if ( srid1 != srid2 ) { rterror(ctx, "Operation on mixed SRID geometries"); } } int clamp_srid(const RTCTX *ctx, int srid) { int newsrid = srid; if ( newsrid <= 0 ) { if ( newsrid != SRID_UNKNOWN ) { newsrid = SRID_UNKNOWN; rtnotice(ctx, "SRID value %d converted to the officially unknown SRID value %d", srid, newsrid); } } else if ( srid > SRID_MAXIMUM ) { newsrid = SRID_USER_MAXIMUM + 1 + /* -1 is to reduce likelyhood of clashes */ /* NOTE: must match implementation in postgis_restore.pl */ ( srid % ( SRID_MAXIMUM - SRID_USER_MAXIMUM - 1 ) ); rtnotice(ctx, "SRID value %d > SRID_MAXIMUM converted to %d", srid, newsrid); } return newsrid; } src/stringbuffer.c000066400000000000000000000203661271715413500145260ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2002 Thamer Alharbash * Copyright 2009 Paul Ramsey * **********************************************************************/ #include "librttopo_geom_internal.h" #include "stringbuffer.h" /** * Allocate a new stringbuffer_t. Use stringbuffer_destroy to free. */ stringbuffer_t* stringbuffer_create(const RTCTX *ctx) { return stringbuffer_create_with_size(ctx, STRINGBUFFER_STARTSIZE); } /** * Allocate a new stringbuffer_t. Use stringbuffer_destroy to free. */ stringbuffer_t* stringbuffer_create_with_size(const RTCTX *ctx, size_t size) { stringbuffer_t *s; s = rtalloc(ctx, sizeof(stringbuffer_t)); s->str_start = rtalloc(ctx, size); s->str_end = s->str_start; s->capacity = size; memset(s->str_start,0,size); return s; } /** * Free the stringbuffer_t and all memory managed within it. */ void stringbuffer_destroy(const RTCTX *ctx, stringbuffer_t *s) { if ( s->str_start ) rtfree(ctx, s->str_start); if ( s ) rtfree(ctx, s); } /** * Reset the stringbuffer_t. Useful for starting a fresh string * without the expense of freeing and re-allocating a new * stringbuffer_t. */ void stringbuffer_clear(const RTCTX *ctx, stringbuffer_t *s) { s->str_start[0] = '\0'; s->str_end = s->str_start; } /** * If necessary, expand the stringbuffer_t internal buffer to accomodate the * specified additional size. */ static inline void stringbuffer_makeroom(const RTCTX *ctx, stringbuffer_t *s, size_t size_to_add) { size_t current_size = (s->str_end - s->str_start); size_t capacity = s->capacity; size_t required_size = current_size + size_to_add; while (capacity < required_size) capacity *= 2; if ( capacity > s->capacity ) { s->str_start = rtrealloc(ctx, s->str_start, capacity); s->capacity = capacity; s->str_end = s->str_start + current_size; } } /** * Return the last character in the buffer. */ char stringbuffer_lastchar(const RTCTX *ctx, stringbuffer_t *s) { if( s->str_end == s->str_start ) return 0; return *(s->str_end - 1); } /** * Append the specified string to the stringbuffer_t. */ void stringbuffer_append(const RTCTX *ctx, stringbuffer_t *s, const char *a) { int alen = strlen(a); /* Length of string to append */ int alen0 = alen + 1; /* Length including null terminator */ stringbuffer_makeroom(ctx, s, alen0); memcpy(s->str_end, a, alen0); s->str_end += alen; } /** * Returns a reference to the internal string being managed by * the stringbuffer. The current string will be null-terminated * within the internal string. */ const char* stringbuffer_getstring(const RTCTX *ctx, stringbuffer_t *s) { return s->str_start; } /** * Returns a newly allocated string large enough to contain the * current state of the string. Caller is responsible for * freeing the return value. */ char* stringbuffer_getstringcopy(const RTCTX *ctx, stringbuffer_t *s) { size_t size = (s->str_end - s->str_start) + 1; char *str = rtalloc(ctx, size); memcpy(str, s->str_start, size); str[size - 1] = '\0'; return str; } /** * Returns the length of the current string, not including the * null terminator (same behavior as strlen()). */ int stringbuffer_getlength(const RTCTX *ctx, stringbuffer_t *s) { return (s->str_end - s->str_start); } /** * Clear the stringbuffer_t and re-start it with the specified string. */ void stringbuffer_set(const RTCTX *ctx, stringbuffer_t *s, const char *str) { stringbuffer_clear(ctx, s); stringbuffer_append(ctx, s, str); } /** * Copy the contents of src into dst. */ void stringbuffer_copy(const RTCTX *ctx, stringbuffer_t *dst, stringbuffer_t *src) { stringbuffer_set(ctx, dst, stringbuffer_getstring(ctx, src)); } /** * Appends a formatted string to the current string buffer, * using the format and argument list provided. Returns -1 on error, * check errno for reasons, documented in the printf man page. */ static int stringbuffer_avprintf(const RTCTX *ctx, stringbuffer_t *s, const char *fmt, va_list ap) { int maxlen = (s->capacity - (s->str_end - s->str_start)); int len = 0; /* Length of the output */ va_list ap2; /* Make a copy of the variadic arguments, in case we need to print twice */ /* Print to our buffer */ va_copy(ap2, ap); len = vsnprintf(s->str_end, maxlen, fmt, ap2); va_end(ap2); /* Propogate errors up */ if ( len < 0 ) #if defined(__MINGW64_VERSION_MAJOR) len = _vscprintf(fmt, ap2);/**Assume windows flaky vsnprintf that returns -1 if initial buffer to small and add more space **/ #else return len; #endif /* We didn't have enough space! */ /* Either Unix vsnprint returned write length larger than our buffer */ /* or Windows vsnprintf returned an error code. */ if ( len >= maxlen ) { stringbuffer_makeroom(ctx, s, len + 1); maxlen = (s->capacity - (s->str_end - s->str_start)); /* Try to print a second time */ len = vsnprintf(s->str_end, maxlen, fmt, ap); /* Printing error? Error! */ if ( len < 0 ) return len; /* Too long still? Error! */ if ( len >= maxlen ) return -1; } /* Move end pointer forward and return. */ s->str_end += len; return len; } /** * Appends a formatted string to the current string buffer, * using the format and argument list provided. * Returns -1 on error, check errno for reasons, * as documented in the printf man page. */ int stringbuffer_aprintf(const RTCTX *ctx, stringbuffer_t *s, const char *fmt, ...) { int r; va_list ap; va_start(ap, fmt); r = stringbuffer_avprintf(ctx, s, fmt, ap); va_end(ap); return r; } /** * Trims whitespace off the end of the stringbuffer. Returns * the number of characters trimmed. */ int stringbuffer_trim_trailing_white(const RTCTX *ctx, stringbuffer_t *s) { char *ptr = s->str_end; int dist = 0; /* Roll backwards until we hit a non-space. */ while( ptr > s->str_start ) { ptr--; if( (*ptr == ' ') || (*ptr == '\t') ) { continue; } else { ptr++; dist = s->str_end - ptr; *ptr = '\0'; s->str_end = ptr; return dist; } } return dist; } /** * Trims zeroes off the end of the last number in the stringbuffer. * The number has to be the very last thing in the buffer. Only the * last number will be trimmed. Returns the number of characters * trimmed. * * eg: 1.22000 -> 1.22 * 1.0 -> 1 * 0.0 -> 0 */ int stringbuffer_trim_trailing_zeroes(const RTCTX *ctx, stringbuffer_t *s) { char *ptr = s->str_end; char *decimal_ptr = NULL; int dist; if ( s->str_end - s->str_start < 2) return 0; /* Roll backwards to find the decimal for this number */ while( ptr > s->str_start ) { ptr--; if ( *ptr == '.' ) { decimal_ptr = ptr; break; } if ( (*ptr >= '0') && (*ptr <= '9' ) ) continue; else break; } /* No decimal? Nothing to trim! */ if ( ! decimal_ptr ) return 0; ptr = s->str_end; /* Roll backwards again, with the decimal as stop point, trimming contiguous zeroes */ while( ptr >= decimal_ptr ) { ptr--; if ( *ptr == '0' ) continue; else break; } /* Huh, we get anywhere. Must not have trimmed anything. */ if ( ptr == s->str_end ) return 0; /* If we stopped at the decimal, we want to null that out. It we stopped on a numeral, we want to preserve that, so push the pointer forward one space. */ if ( *ptr != '.' ) ptr++; /* Add null terminator re-set the end of the stringbuffer. */ *ptr = '\0'; dist = s->str_end - ptr; s->str_end = ptr; return dist; } src/stringbuffer.h000066400000000000000000000046251271715413500145330ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright 2002 Thamer Alharbash * Copyright 2009 Paul Ramsey * **********************************************************************/ #ifndef _STRINGBUFFER_H #define _STRINGBUFFER_H 1 #include "librttopo_geom_internal.h" #include #include #include #include #define STRINGBUFFER_STARTSIZE 128 typedef struct { size_t capacity; char *str_end; char *str_start; } stringbuffer_t; extern stringbuffer_t *stringbuffer_create_with_size(const RTCTX *ctx, size_t size); extern stringbuffer_t *stringbuffer_create(const RTCTX *ctx); extern void stringbuffer_destroy(const RTCTX *ctx, stringbuffer_t *sb); extern void stringbuffer_clear(const RTCTX *ctx, stringbuffer_t *sb); void stringbuffer_set(const RTCTX *ctx, stringbuffer_t *sb, const char *s); void stringbuffer_copy(const RTCTX *ctx, stringbuffer_t *sb, stringbuffer_t *src); extern void stringbuffer_append(const RTCTX *ctx, stringbuffer_t *sb, const char *s); extern int stringbuffer_aprintf(const RTCTX *ctx, stringbuffer_t *sb, const char *fmt, ...); extern const char *stringbuffer_getstring(const RTCTX *ctx, stringbuffer_t *sb); extern char *stringbuffer_getstringcopy(const RTCTX *ctx, stringbuffer_t *sb); extern int stringbuffer_getlength(const RTCTX *ctx, stringbuffer_t *sb); extern char stringbuffer_lastchar(const RTCTX *ctx, stringbuffer_t *s); extern int stringbuffer_trim_trailing_white(const RTCTX *ctx, stringbuffer_t *s); extern int stringbuffer_trim_trailing_zeroes(const RTCTX *ctx, stringbuffer_t *s); #endif /* _STRINGBUFFER_H */ src/varint.c000066400000000000000000000127451271715413500133330ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2014 Sandro Santilli * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #include "varint.h" #include "rtgeom_log.h" #include "librttopo_geom.h" /* -------------------------------------------------------------------------------- */ static size_t _varint_u64_encode_buf(const RTCTX *ctx, uint64_t val, uint8_t *buf) { uint8_t grp; uint64_t q = val; uint8_t *ptr = buf; while (1) { /* We put the 7 least significant bits in grp */ grp = 0x7f & q; /* We rightshift our input value 7 bits */ /* which means that the 7 next least significant bits */ /* becomes the 7 least significant */ q = q >> 7; /* Check if, after our rightshifting, we still have */ /* anything to read in our input value. */ if ( q > 0 ) { /* In the next line quite a lot is happening. */ /* Since there is more to read in our input value */ /* we signal that by setting the most siginicant bit */ /* in our byte to 1. */ /* Then we put that byte in our buffer and move the pointer */ /* forward one step */ *ptr = 0x80 | grp; ptr++; } else { /* The same as above, but since there is nothing more */ /* to read in our input value we leave the most significant bit unset */ *ptr = grp; ptr++; return ptr - buf; } } /* This cannot happen */ rterror(ctx, "%s: Got out of infinite loop. Consciousness achieved.", __func__); return (size_t)0; } size_t varint_u64_encode_buf(const RTCTX *ctx, uint64_t val, uint8_t *buf) { return _varint_u64_encode_buf(ctx, val, buf); } size_t varint_u32_encode_buf(const RTCTX *ctx, uint32_t val, uint8_t *buf) { return _varint_u64_encode_buf(ctx, (uint64_t)val, buf); } size_t varint_s64_encode_buf(const RTCTX *ctx, int64_t val, uint8_t *buf) { return _varint_u64_encode_buf(ctx, zigzag64(ctx, val), buf); } size_t varint_s32_encode_buf(const RTCTX *ctx, int32_t val, uint8_t *buf) { return _varint_u64_encode_buf(ctx, (uint64_t)zigzag32(ctx, val), buf); } /* Read from signed 64bit varint */ int64_t varint_s64_decode(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end, size_t *size) { return unzigzag64(ctx, varint_u64_decode(ctx, the_start, the_end, size)); } /* Read from unsigned 64bit varint */ uint64_t varint_u64_decode(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end, size_t *size) { uint64_t nVal = 0; int nShift = 0; uint8_t nByte; const uint8_t *ptr = the_start; /* Check so we don't read beyond the twkb */ while( ptr < the_end ) { nByte = *ptr; /* Hibit is set, so this isn't the last byte */ if (nByte & 0x80) { /* We get here when there is more to read in the input varInt */ /* Here we take the least significant 7 bits of the read */ /* byte and put it in the most significant place in the result variable. */ nVal |= ((uint64_t)(nByte & 0x7f)) << nShift; /* move the "cursor" of the input buffer step (8 bits) */ ptr++; /* move the cursor in the resulting variable (7 bits) */ nShift += 7; } else { /* move the "cursor" one step */ ptr++; /* Move the last read byte to the most significant */ /* place in the result and return the whole result */ *size = ptr - the_start; return nVal | ((uint64_t)nByte << nShift); } } rterror(ctx, "%s: varint extends past end of buffer", __func__); return 0; } size_t varint_size(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end) { const uint8_t *ptr = the_start; /* Check so we don't read beyond the twkb */ while( ptr < the_end ) { /* Hibit is set, this isn't the last byte */ if (*ptr & 0x80) { ptr++; } else { ptr++; return ptr - the_start; } } return 0; } uint64_t zigzag64(const RTCTX *ctx, int64_t val) { return (val << 1) ^ (val >> 63); } uint32_t zigzag32(const RTCTX *ctx, int32_t val) { return (val << 1) ^ (val >> 31); } uint8_t zigzag8(const RTCTX *ctx, int8_t val) { return (val << 1) ^ (val >> 7); } int64_t unzigzag64(const RTCTX *ctx, uint64_t val) { if ( val & 0x01 ) return -1 * (int64_t)((val+1) >> 1); else return (int64_t)(val >> 1); } int32_t unzigzag32(const RTCTX *ctx, uint32_t val) { if ( val & 0x01 ) return -1 * (int32_t)((val+1) >> 1); else return (int32_t)(val >> 1); } int8_t unzigzag8(const RTCTX *ctx, uint8_t val) { if ( val & 0x01 ) return -1 * (int8_t)((val+1) >> 1); else return (int8_t)(val >> 1); } src/varint.h000066400000000000000000000041431271715413500133310ustar00rootroot00000000000000/********************************************************************** * * rttopo - topology library * http://git.osgeo.org/gogs/rttopo/librttopo * * rttopo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * rttopo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rttopo. If not, see . * ********************************************************************** * * Copyright (C) 2014 Sandro Santilli * Copyright (C) 2013 Nicklas Avén * **********************************************************************/ #ifndef _LIBRTGEOM_VARINT_H #define _LIBRTGEOM_VARINT_H 1 #include "librttopo_geom_internal.h" #include #include /* NEW SIGNATURES */ size_t varint_u32_encode_buf(const RTCTX *ctx, uint32_t val, uint8_t *buf); size_t varint_s32_encode_buf(const RTCTX *ctx, int32_t val, uint8_t *buf); size_t varint_u64_encode_buf(const RTCTX *ctx, uint64_t val, uint8_t *buf); size_t varint_s64_encode_buf(const RTCTX *ctx, int64_t val, uint8_t *buf); int64_t varint_s64_decode(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end, size_t *size); uint64_t varint_u64_decode(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end, size_t *size); size_t varint_size(const RTCTX *ctx, const uint8_t *the_start, const uint8_t *the_end); uint64_t zigzag64(const RTCTX *ctx, int64_t val); uint32_t zigzag32(const RTCTX *ctx, int32_t val); uint8_t zigzag8(const RTCTX *ctx, int8_t val); int64_t unzigzag64(const RTCTX *ctx, uint64_t val); int32_t unzigzag32(const RTCTX *ctx, uint32_t val); int8_t unzigzag8(const RTCTX *ctx, uint8_t val); #endif /* !defined _LIBRTGEOM_VARINT_H */