[php-geos] 01/03: Imported Upstream version 1.0.0~rc1
Bas Couwenberg
sebastic at debian.org
Tue Aug 30 12:17:36 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository php-geos.
commit 644609655248eaefae856f1d457b2d78d424d596
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Tue Aug 30 11:51:22 2016 +0200
Imported Upstream version 1.0.0~rc1
---
.drone.yml | 15 +
.gitignore | 72 +
COPYING | 17 +
CREDITS | 3 +
LGPL-2 | 502 +++++
MIT-LICENSE | 22 +
Makefile.frag | 3 +
NEWS | 4 +
README.md | 52 +
TODO | 9 +
autogen.sh | 3 +
config.m4 | 101 +
docker/build-test/Dockerfile.alpine | 22 +
docker/build-test/Dockerfile.trisquel | 12 +
docker/build-test/Makefile | 19 +
geos.c | 3433 +++++++++++++++++++++++++++++++++
package.xml | 74 +
php_geos.h | 55 +
phpunit.xml | 7 +
test/crashme.php | 25 +
tests/000_General.phpt | 51 +
tests/001_Geometry.phpt | 1935 +++++++++++++++++++
tests/002_WKTWriter.phpt | 209 ++
tests/003_WKTReader.phpt | 113 ++
tests/004_WKBWriter.phpt | 170 ++
tests/README.md | 14 +
tests/TestHelper.php | 141 ++
27 files changed, 7083 insertions(+)
diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..b5bdb23
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,15 @@
+# See http://readme.drone.io/usage/overview
+pipeline:
+ build:
+ image: docker.kbt.io/php-geos/build-test:trisquel
+ commands:
+ - export PATH=${PHP_BIN}:${PATH}
+ - php --version
+ - ./autogen.sh
+ - ./configure
+ - make
+ - make check
+matrix:
+ PHP_BIN:
+ - /opt/php-7.0.7/bin/
+ - /usr/bin/ # this is php5
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d531376
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,72 @@
+*~
+*.swp
+*.lo
+*.la
+.deps
+.libs
+Makefile
+Makefile.fragments
+Makefile.global
+Makefile.objects
+acinclude.m4
+aclocal.m4
+autom4te.cache
+build
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.nice
+config.status
+config.sub
+configure
+configure.in
+conftest
+conftest.c
+include
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+modules
+scan_makefile_in.awk
+*.dsw
+*.plg
+*.opt
+*.ncb
+Release
+Release_inline
+Debug
+Release_TS
+Release_TSDbg
+Release_TS_inline
+Debug_TS
+memcached*.tgz
+run-tests.php
+cscope.out
+php_memcached.loT
+tests/*.log
+tests/*.mem
+tests/*.out
+tests/*.diff
+tests/*.php
+tests/*.exp
+tests/*.sh
+tests/*/*.log
+tests/*/*.mem
+tests/*/*.out
+tests/*/*.diff
+tests/*/*.php
+tests/*/*.exp
+tests/*/*.sh
+tests/*/*/*.log
+tests/*/*/*.mem
+tests/*/*/*.out
+tests/*/*/*.diff
+tests/*/*/*.php
+tests/*/*/*.exp
+tests/*/*/*.sh
+tmp-php.ini
+!/docker/**/Makefile
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5799769
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,17 @@
+
+This PHP extension is covered under the following licenses:
+
+- geos.c and php_geos.h have been extracted from the GEOS library
+ and are covered under the LGPL 2.1 are are copyright
+
+ Sandro Santilli <strk at kbt.io>
+
+ See the LGPL-2 file for details.
+
+- the remainder of the system including the "PECL-ization" of
+ the extension are covered under the MIT license and is
+ copyright
+
+ J Smith <dark.panda at gmail.com> <jay at php.net>
+
+ See the MIT-LICENSE file for details.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..0c6410d
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,3 @@
+geos
+Sandro Santilli <strk at kbt.io> - GEOS
+J Smith <dark.panda at gmail.com> <jay at php.net> - PHP packaging
diff --git a/LGPL-2 b/LGPL-2
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/LGPL-2
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/MIT-LICENSE b/MIT-LICENSE
new file mode 100644
index 0000000..a547fa8
--- /dev/null
+++ b/MIT-LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2016 J Smith <dark.panda at gmail.com> <jay at php.net>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Makefile.frag b/Makefile.frag
new file mode 100644
index 0000000..7328668
--- /dev/null
+++ b/Makefile.frag
@@ -0,0 +1,3 @@
+
+check:
+ REPORT_EXIT_STATUS=1 NO_INTERACTION=1 make test
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..24235ff
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,4 @@
+1.0.0 - 201608XX
+
+ First stand-alone release
+ (previous versions were shipped as part of GEOS since 20100618)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1de3193
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+PHP module for GEOS
+===================
+
+[![build status]
+(https://drone.osgeo.kbt.io/api/badges/geos/php-geos/status.svg?branch=master)]
+(https://drone.osgeo.kbt.io/geos/php-geos?branch=master, alt=build status)
+
+The code in this directory provides a PHP module to make use
+of functionalities of the [GEOS library] (http://geos.osgeo.org).
+
+The bindings are linked to the C-API, for betters stability.
+
+# Building
+
+## Requirements
+
+You need:
+
+ - PHP development files
+ ( often found in packages named something like 'php5-dev' )
+
+ - GEOS development files
+ ( often found in packages named something like 'libgeos-dev' )
+
+## Procedure
+
+ git clone https://git.osgeo.org/gogs/geos/php-geos.git
+ cd php-geos
+ ./autogen.sh
+ ./configure
+ make # generates modules/geos.so
+
+# Testing
+
+Automated testing is executed on 'make check'.
+
+You'll need phpunit installed for this to work. To install:
+
+ pear install --force --alldeps phpunit/phpunit
+
+# Installing
+
+As root (or owner with write access to the directory
+returned by php-config --extension-dir), run:
+
+ make install
+
+# Using
+
+ ... TODO ...
+ (reference some automatically built doc?)
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..a9631a8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,9 @@
+In order of priority
+
+- Find a way to have GEOSGeometry contents shown on var_dump
+- Documentation !! (doxygen-based?)
+- Add interface for algorithms (Orientation Index) ?
+- Add interfaces for prepared geometries ?
+- Add interfaces for STRTree ?
+- Add interfaces for GEOSCoordSeq ?
+- Add Geometry Constructors ?
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..4b2f22f
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+phpize
diff --git a/config.m4 b/config.m4
new file mode 100644
index 0000000..71b1461
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,101 @@
+dnl
+dnl $ Id: $
+dnl vim:se ts=2 sw=2 et:
+
+PHP_ARG_ENABLE(geos, whether to enable geos support,
+[ --enable-geos Enable geos support])
+
+PHP_ARG_WITH(geos-config, for geos-config,
+[ --with-geos-config[=DIR] Use geos-config])
+
+if test -z "$PHP_DEBUG"; then
+ AC_ARG_ENABLE(debug,
+ [ --enable-debug compile with debugging symbols],[
+ PHP_DEBUG=$enableval
+ ],[ PHP_DEBUG=no
+ ])
+fi
+
+if test "$PHP_GEOS" != "no"; then
+ if test "$PHP_GEOS_CONFIG" != "yes"; then
+ if test -x "$PHP_GEOS_CONFIG"; then
+ GEOS_CONFIG="$PHP_GEOS_CONFIG"
+ fi
+ else
+ for i in /opt/local/bin /opt/bin /usr/local/bin /usr/bin ""; do
+ if test -x $i/geos-config; then
+ GEOS_CONFIG="$i/geos-config"
+ break;
+ fi
+ done
+ fi
+
+ if test -n "$GEOS_CONFIG"; then
+ GEOS_VERSION=`$GEOS_CONFIG --version`
+ GEOS_INCLUDE=`$GEOS_CONFIG --includes`
+ GEOS_LDFLAGS=`$GEOS_CONFIG --ldflags`
+ AC_MSG_RESULT([Using GEOS version $GEOS_VERSION])
+ AC_DEFINE(HAVE_GEOS_C_H,1,[Whether to have geos_c.h])
+ fi
+
+ if test -z "$GEOS_INCLUDE"; then
+ AC_MSG_ERROR(Cannot find geos_c.h. Please specify correct GEOS installation path)
+ fi
+
+ if test -z "$GEOS_LDFLAGS"; then
+ AC_MSG_ERROR(Cannot find geos_c.so. Please specify correct GEOS installation path)
+ fi
+
+ old_CFLAGS=$CFLAGS
+ CFLAGS="-I$GEOS_INCLUDE $CFLAGS"
+
+ old_LDFLAGS=$LDFLAGS
+ LDFLAGS="$GEOS_LDFLAGS $LDFLAGS"
+
+ AC_CHECK_HEADER(geos_c.h,, AC_MSG_ERROR(Can't find GEOS includes))
+ AC_CHECK_LIB(geos_c, initGEOS_r,, AC_MSG_ERROR([Unable to build the GEOS: a newer libgeos is required]))
+ AC_CHECK_LIB(geos_c, finishGEOS_r,, AC_MSG_ERROR([Unable to build the GEOS: a newer libgeos is required]))
+ AC_CHECK_LIB(geos_c, GEOSClipByRect_r, AC_DEFINE(HAVE_GEOS_CLIP_BY_RECT,1,[Whether we have GEOSClipByRect_r]))
+ AC_CHECK_LIB(geos_c, GEOSCoveredBy_r, AC_DEFINE(HAVE_GEOS_COVERED_BY,1,[Whether we have GEOSCoveredBy_r]))
+ AC_CHECK_LIB(geos_c, GEOSCovers_r, AC_DEFINE(HAVE_GEOS_COVERS,1,[Whether we have GEOSCovers_r]))
+ AC_CHECK_LIB(geos_c, GEOSDelaunayTriangulation_r, AC_DEFINE(HAVE_GEOS_DELAUNAY_TRIANGULATION,1,[Whether we have GEOSDelaunayTriangulation_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetEndPoint_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_END_POINT,1,[Whether we have GEOSGeomGetEndPoint_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetNumPoints_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_NUM_POINTS,1,[Whether we have GEOSGeomGetNumPoints_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetPointN_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_POINT_N,1,[Whether we have GEOSGeomGetPointN_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetStartPoint_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_START_POINT,1,[Whether we have GEOSGeomGetStartPoint_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetX_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_X,1,[Whether we have GEOSGeomGetX_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeomGetY_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_Y,1,[Whether we have GEOSGeomGetY_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeom_extractUniquePoints_r, AC_DEFINE(HAVE_GEOS_GEOM_EXTRACT_UNIQUE_POINTS,1,[Whether we have GEOSGeom_extractUniquePoints_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeom_getCoordinateDimension_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_COORDINATE_DIMENSION,1,[Whether we have GEOSGeom_getCoordinateDimension_r]))
+ AC_CHECK_LIB(geos_c, GEOSNode_r, AC_DEFINE(HAVE_GEOS_NODE,1,[Whether we have GEOSNode_r]))
+ AC_CHECK_LIB(geos_c, GEOSOffsetCurve_r, AC_DEFINE(HAVE_GEOS_OFFSET_CURVE,1,[Whether we have GEOSOffsetCurve_r]))
+ AC_CHECK_LIB(geos_c, GEOSPolygonize_full_r, AC_DEFINE(HAVE_GEOS_POLYGONIZE_FULL,1,[Whether we have GEOSPolygonize_full_r]))
+ AC_CHECK_LIB(geos_c, GEOSRelateBoundaryNodeRule_r, AC_DEFINE(HAVE_GEOS_RELATE_BOUNDARY_NODE_RULE,1,[Whether we have GEOSRelateBoundaryNodeRule_r]))
+ AC_CHECK_LIB(geos_c, GEOSRelatePatternMatch_r, AC_DEFINE(HAVE_GEOS_RELATE_PATTERN_MATCH,1,[Whether we have GEOSRelatePatternMatch_r]))
+ AC_CHECK_LIB(geos_c, GEOSSharedPaths_r, AC_DEFINE(HAVE_GEOS_SHARED_PATHS,1,[Whether we have GEOSSharedPaths_r]))
+ AC_CHECK_LIB(geos_c, GEOSSnap_r, AC_DEFINE(HAVE_GEOS_SNAP,1,[Whether we have GEOSSnap_r]))
+ AC_CHECK_LIB(geos_c, GEOSUnaryUnion_r, AC_DEFINE(HAVE_GEOS_UNARY_UNION,1,[Whether we have GEOSUnaryUnion_r]))
+ AC_CHECK_LIB(geos_c, GEOSVoronoiDiagram_r, AC_DEFINE(HAVE_GEOS_VORONOI_DIAGRAM,1,[Whether we have GEOSVoronoiDiagram_r]))
+ AC_CHECK_LIB(geos_c, GEOSisClosed_r, AC_DEFINE(HAVE_GEOS_IS_CLOSED,1,[Whether we have GEOSisClosed_r]))
+ AC_CHECK_LIB(geos_c, GEOSisValidDetail_r, AC_DEFINE(HAVE_GEOS_IS_VALID_DETAIL,1,[Whether we have GEOSisValidDetail_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeom_setPrecision_r, AC_DEFINE(HAVE_GEOS_GEOM_SET_PRECISION,1,[Whether we have GEOSGeom_setPrecision_r]))
+ AC_CHECK_LIB(geos_c, GEOSGeom_getPrecision_r, AC_DEFINE(HAVE_GEOS_GEOM_GET_PRECISION,1,[Whether we have GEOSGeom_getPrecision_r]))
+
+ AC_CHECK_LIB(geos_c, GEOSWKTWriter_setTrim_r, AC_DEFINE(HAVE_GEOS_WKT_WRITER_SET_TRIM,1,[Whether we have GEOSWKTWriter_setTrim_r]))
+ AC_CHECK_LIB(geos_c, GEOSWKTWriter_setRoundingPrecision_r, AC_DEFINE(HAVE_GEOS_WKT_WRITER_SET_ROUNDING_PRECISION,1,[Whether we have GEOSWKTWriter_setRoundingPrecision_r]))
+ AC_CHECK_LIB(geos_c, GEOSWKTWriter_setOutputDimension_r, AC_DEFINE(HAVE_GEOS_WKT_WRITER_SET_OUTPUT_DIMENSION,1,[Whether we have GEOSWKTWriter_setOutputDimension_r]))
+ AC_CHECK_LIB(geos_c, GEOSWKTWriter_getOutputDimension_r, AC_DEFINE(HAVE_GEOS_WKT_WRITER_GET_OUTPUT_DIMENSION,1,[Whether we have GEOSWKTWriter_getOutputDimension_r]))
+ AC_CHECK_LIB(geos_c, GEOSWKTWriter_setOld3D_r, AC_DEFINE(HAVE_GEOS_WKT_WRITER_SET_OLD_3D,1,[Whether we have GEOSWKTWriter_setOld3D_r]))
+
+ AC_TRY_COMPILE(geos_c.h, GEOS_PREC_NO_TOPO, AC_DEFINE(HAVE_GEOS_PREC_NO_TOPO,1,[Whether we have GEOS_PREC_NO_TOPO]))
+ AC_TRY_COMPILE(geos_c.h, GEOS_PREC_KEEP_COLLAPSED, AC_DEFINE(HAVE_GEOS_PREC_KEEP_COLLAPSED,1,[Whether we have GEOS_PREC_KEEP_COLLAPSED]))
+
+ CFLAGS=$old_CFLAGS
+ LDFLAGS=$old_LDFLAGS
+
+ PHP_ADD_LIBRARY(geos_c, 1, GEOS_SHARED_LIBADD)
+ PHP_ADD_MAKEFILE_FRAGMENT(Makefile.frag)
+ PHP_SUBST(GEOS_SHARED_LIBADD)
+ PHP_ADD_INCLUDE($GEOS_INCLUDE, 1)
+ PHP_NEW_EXTENSION(geos, geos.c, $ext_shared,,)
+fi
diff --git a/docker/build-test/Dockerfile.alpine b/docker/build-test/Dockerfile.alpine
new file mode 100644
index 0000000..1f6c755
--- /dev/null
+++ b/docker/build-test/Dockerfile.alpine
@@ -0,0 +1,22 @@
+FROM index.docker.io/alpine:latest
+RUN apk add --update make && rm -rf /var/cache/apk/*
+RUN apk add --update gcc && rm -rf /var/cache/apk/*
+# Splitting install of php5-dev in steps to reduce memory requirements
+RUN apk add --update ncurses-terminfo-base && rm -rf /var/cache/apk/*
+RUN apk add --update ncurses-terminfo && rm -rf /var/cache/apk/*
+RUN apk add --update pcre && rm -rf /var/cache/apk/*
+RUN apk add --update php5-common && rm -rf /var/cache/apk/*
+RUN apk add --update readline && rm -rf /var/cache/apk/*
+RUN apk add --update libxml2 && rm -rf /var/cache/apk/*
+RUN apk add --update libpcre16 && rm -rf /var/cache/apk/*
+RUN apk add --update libpcre32 && rm -rf /var/cache/apk/*
+RUN apk add --update pcre-dev && rm -rf /var/cache/apk/*
+RUN apk add --update php5-cli && rm -rf /var/cache/apk/*
+RUN apk add --update php5-dev && rm -rf /var/cache/apk/*
+
+# Autoconf brings in perl
+RUN apk add --update autoconf && rm -rf /var/cache/apk/*
+
+#RUN apk add --update autoconf && rm -rf /var/cache/apk/*
+#RUN apk add --update git
+
diff --git a/docker/build-test/Dockerfile.trisquel b/docker/build-test/Dockerfile.trisquel
new file mode 100644
index 0000000..c1e73b9
--- /dev/null
+++ b/docker/build-test/Dockerfile.trisquel
@@ -0,0 +1,12 @@
+FROM kpengboy/trisquel
+RUN apt-get update -qq && apt-get install -y build-essential
+RUN apt-get update -qq && apt-get install -y autoconf
+RUN apt-get update -qq && apt-get install -y php5-dev
+RUN apt-get update -qq && apt-get install -y libgeos-dev
+RUN curl -o php-7.0.7.tar.bz2 http://ar2.php.net/distributions/php-7.0.7.tar.bz2
+RUN tar xjf php-7.0.7.tar.bz2
+RUN apt-get update -qq && apt-get install -y libxml2-dev
+RUN cd php-7.0.7 && \
+ ./configure --prefix=/opt/php-7.0.7 --enable-debug && \
+ make install
+RUN cd .. && rm -rf php-7.0.7*
diff --git a/docker/build-test/Makefile b/docker/build-test/Makefile
new file mode 100644
index 0000000..e28c853
--- /dev/null
+++ b/docker/build-test/Makefile
@@ -0,0 +1,19 @@
+DOCKER=docker
+REGISTRY=docker.kbt.io
+NAME=$(REGISTRY)/php-geos/build-test
+
+all: alpine trisquel
+
+push: alpine-push trisquel-push
+
+alpine:
+ $(DOCKER) build -t $(NAME):alpine - < Dockerfile.alpine
+
+alpine-push:
+ $(DOCKER) push $(NAME):alpine
+
+trisquel:
+ $(DOCKER) build -t $(NAME):trisquel - < Dockerfile.trisquel
+
+trisquel-push:
+ $(DOCKER) push $(NAME):trisquel
diff --git a/geos.c b/geos.c
new file mode 100755
index 0000000..3f449f6
--- /dev/null
+++ b/geos.c
@@ -0,0 +1,3433 @@
+/***********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://trac.osgeo.org/geos
+ *
+ * Copyright (C) 2010 Sandro Santilli <strk at kbt.io>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ ***********************************************************************/
+
+/* PHP stuff */
+#include "php.h"
+#include "ext/standard/info.h" /* for php_info_... */
+#include "Zend/zend_exceptions.h" /* for zend_throw_exception_object */
+
+/* GEOS stuff */
+#include "geos_c.h"
+
+/* Own stuff */
+#include "php_geos.h"
+
+static ZEND_DECLARE_MODULE_GLOBALS(geos);
+static PHP_GINIT_FUNCTION(geos);
+
+PHP_MINIT_FUNCTION(geos);
+PHP_MSHUTDOWN_FUNCTION(geos);
+PHP_RINIT_FUNCTION(geos);
+PHP_RSHUTDOWN_FUNCTION(geos);
+PHP_MINFO_FUNCTION(geos);
+PHP_FUNCTION(GEOSVersion);
+PHP_FUNCTION(GEOSPolygonize);
+PHP_FUNCTION(GEOSLineMerge);
+
+#ifdef HAVE_GEOS_SHARED_PATHS
+PHP_FUNCTION(GEOSSharedPaths);
+#endif
+
+#ifdef HAVE_GEOS_RELATE_PATTERN_MATCH
+PHP_FUNCTION(GEOSRelateMatch);
+#endif
+
+#if PHP_VERSION_ID < 50399
+#define zend_function_entry function_entry
+#endif
+
+#if PHP_VERSION_ID >= 70000
+# define GEOS_PHP_DTOR_OBJECT zend_object
+# define zend_object_value zend_object *
+# define zend_uint size_t
+# define MAKE_STD_ZVAL(x) x = emalloc(sizeof(zval))
+# define GEOS_PHP_RETURN_STRING(x) { RETVAL_STRING((x)); efree((x)); return; }
+# define GEOS_PHP_RETURN_STRINGL(x,s) { RETVAL_STRINGL((x),(s)); efree((x)); return; }
+# define GEOS_PHP_ADD_ASSOC_ARRAY(a,k,v) { add_assoc_string((a), (k), (v)); efree((v)); }
+# define GEOS_PHP_ADD_ASSOC_ZVAL(a,k,v) { add_assoc_zval((a), (k), (v)); efree((v)); }
+# define GEOS_PHP_HASH_GET_CUR_KEY(s,k,i) zend_hash_get_current_key((s), (k), (i))
+# define GEOS_PHP_HASH_GET_CUR_DATA(h,d) ( d = zend_hash_get_current_data((h)) )
+# define GEOS_PHP_ZVAL zval *
+#else /* PHP_VERSION_ID < 70000 */
+# define GEOS_PHP_DTOR_OBJECT void
+# define GEOS_PHP_RETURN_STRING(x) RETURN_STRING((x),0)
+# define GEOS_PHP_RETURN_STRINGL(x,s) RETURN_STRINGL((x),(s),0)
+# define GEOS_PHP_ADD_ASSOC_ARRAY(a,k,v) add_assoc_string((a), (k), (v), 0)
+# define GEOS_PHP_ADD_ASSOC_ZVAL(a,k,v) add_assoc_zval((a), (k), (v))
+# define GEOS_PHP_HASH_GET_CUR_KEY(s,k,i) zend_hash_get_current_key((s), (k), (i), 0)
+# define zend_string char
+# define ZSTR_VAL(x) (x)
+# define GEOS_PHP_HASH_GET_CUR_DATA(h,d) zend_hash_get_current_data((h),(void**)&(d))
+# define GEOS_PHP_ZVAL zval **
+#endif
+
+
+static zend_function_entry geos_functions[] = {
+ PHP_FE(GEOSVersion, NULL)
+ PHP_FE(GEOSPolygonize, NULL)
+ PHP_FE(GEOSLineMerge, NULL)
+
+# ifdef HAVE_GEOS_SHARED_PATHS
+ PHP_FE(GEOSSharedPaths, NULL)
+# endif
+
+# ifdef HAVE_GEOS_RELATE_PATTERN_MATCH
+ PHP_FE(GEOSRelateMatch, NULL)
+# endif
+ {NULL, NULL, NULL}
+};
+
+zend_module_entry geos_module_entry = {
+ STANDARD_MODULE_HEADER,
+ PHP_GEOS_EXTNAME,
+ geos_functions,
+ PHP_MINIT(geos), /* module init function */
+ PHP_MSHUTDOWN(geos), /* module shutdown function */
+ PHP_RINIT(geos), /* request init function */
+ PHP_RSHUTDOWN(geos), /* request shutdown function */
+ PHP_MINFO(geos), /* module info function */
+ PHP_GEOS_VERSION,
+ PHP_MODULE_GLOBALS(geos), /* globals descriptor */
+ PHP_GINIT(geos), /* globals ctor */
+ NULL, /* globals dtor */
+ NULL, /* post deactivate */
+ STANDARD_MODULE_PROPERTIES_EX
+};
+
+#ifdef COMPILE_DL_GEOS
+ZEND_GET_MODULE(geos)
+#endif
+
+/* -- Utility functions ---------------------- */
+
+static void noticeHandler(const char *fmt, ...)
+{
+ TSRMLS_FETCH();
+ char message[256];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(message, sizeof(message) - 1, fmt, args);
+ va_end(args);
+
+ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s", message);
+}
+
+static void errorHandler(const char *fmt, ...)
+{
+ TSRMLS_FETCH();
+ char message[256];
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(message, sizeof(message) - 1, fmt, args);
+ va_end(args);
+
+ /* TODO: use a GEOSException ? */
+ zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C),
+ 1 TSRMLS_CC, "%s", message);
+
+}
+
+typedef struct Proxy_t {
+#if PHP_VERSION_ID >= 70000
+ int id;
+ void* relay;
+ zend_object std;
+#else
+ zend_object std;
+ void* relay;
+#endif
+} Proxy;
+
+#if PHP_VERSION_ID >= 70000
+static inline Proxy *php_geos_fetch_object(zend_object *obj) {
+ return (Proxy *)((char *) obj - XtOffsetOf(Proxy, std));
+}
+# define Z_GEOS_OBJ_P(zv) (Proxy *)((char *) (Z_OBJ_P(zv)) - XtOffsetOf(Proxy, std))
+#else
+# ifdef Z_OBJ
+# define Z_GEOS_OBJ_P(zv) (Proxy*)Z_OBJ(*val TSRMLS_CC)
+# else
+# define Z_GEOS_OBJ_P(zv) (Proxy*)zend_object_store_get_object(val TSRMLS_CC)
+# endif
+#endif
+
+static void
+setRelay(zval* val, void* obj) {
+ TSRMLS_FETCH();
+
+ Proxy* proxy = Z_GEOS_OBJ_P(val);
+
+ proxy->relay = obj;
+}
+
+static inline void *
+getRelay(zval* val, zend_class_entry* ce) {
+ TSRMLS_FETCH();
+
+ Proxy* proxy = Z_GEOS_OBJ_P(val);
+
+ if ( proxy->std.ce != ce ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "Relay object is not an %s", ce->name);
+ }
+ if ( ! proxy->relay ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "Relay object for object of type %s is not set", ce->name);
+ }
+ return proxy->relay;
+}
+
+static long getZvalAsLong(GEOS_PHP_ZVAL val)
+{
+ long ret;
+ zval tmp;
+
+#if PHP_VERSION_ID >= 70000
+ tmp = *val;
+#else
+ tmp = **val;
+#endif
+ zval_copy_ctor(&tmp);
+ convert_to_long(&tmp);
+ ret = Z_LVAL(tmp);
+ zval_dtor(&tmp);
+ return ret;
+}
+
+static long getZvalAsDouble(GEOS_PHP_ZVAL val)
+{
+ double ret;
+ zval tmp;
+
+#if PHP_VERSION_ID >= 70000
+ tmp = *val;
+#else
+ tmp = **val;
+#endif
+ zval_copy_ctor(&tmp);
+ convert_to_double(&tmp);
+ ret = Z_DVAL(tmp);
+ zval_dtor(&tmp);
+ return ret;
+}
+
+static zend_object_value
+Gen_create_obj (zend_class_entry *type,
+ void (*dtor)(GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC),
+ zend_object_handlers* handlers)
+{
+ TSRMLS_FETCH();
+
+#if PHP_VERSION_ID >= 70000
+
+ Proxy *obj = (Proxy *) ecalloc(1, sizeof(Proxy) + zend_object_properties_size(type));
+
+ zend_object_std_init(&obj->std, type TSRMLS_CC);
+ object_properties_init(&obj->std, type);
+
+ obj->std.handlers = handlers;
+
+ /* TODO: install the destructor ? (dtor) ! */
+ /* TODO: do not allocate a full Proxy if we're going to use an object */
+
+ return &obj->std;
+
+#else /* PHP_VERSION_ID < 70000 */
+
+ zend_object_value retval;
+
+ Proxy *obj = (Proxy *)ecalloc(1, sizeof(Proxy));
+
+ obj->std.ce = type;
+
+ ALLOC_HASHTABLE(obj->std.properties);
+ zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+#if PHP_VERSION_ID < 50399
+ zend_hash_copy(obj->std.properties, &type->default_properties,
+ (copy_ctor_func_t)zval_add_ref, NULL, sizeof(zval *));
+#else
+ object_properties_init(&(obj->std), type);
+#endif
+
+ retval.handle = zend_objects_store_put(obj, NULL, dtor, NULL TSRMLS_CC);
+ retval.handlers = handlers;
+
+ return retval;
+
+#endif /* PHP_VERSION_ID < 70000 */
+}
+
+
+/* -- class GEOSGeometry -------------------- */
+
+PHP_METHOD(Geometry, __construct);
+PHP_METHOD(Geometry, __toString);
+PHP_METHOD(Geometry, project);
+PHP_METHOD(Geometry, interpolate);
+PHP_METHOD(Geometry, buffer);
+
+#ifdef HAVE_GEOS_OFFSET_CURVE
+PHP_METHOD(Geometry, offsetCurve);
+#endif
+
+PHP_METHOD(Geometry, envelope);
+PHP_METHOD(Geometry, intersection);
+PHP_METHOD(Geometry, convexHull);
+PHP_METHOD(Geometry, difference);
+PHP_METHOD(Geometry, symDifference);
+PHP_METHOD(Geometry, boundary);
+PHP_METHOD(Geometry, union); /* also does union cascaded */
+PHP_METHOD(Geometry, pointOnSurface);
+PHP_METHOD(Geometry, centroid);
+PHP_METHOD(Geometry, relate);
+
+#ifdef HAVE_GEOS_RELATE_BOUNDARY_NODE_RULE
+PHP_METHOD(Geometry, relateBoundaryNodeRule);
+#endif
+
+PHP_METHOD(Geometry, simplify); /* also does topology-preserving */
+PHP_METHOD(Geometry, normalize);
+
+#ifdef HAVE_GEOS_GEOM_SET_PRECISION
+PHP_METHOD(Geometry, setPrecision);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_PRECISION
+PHP_METHOD(Geometry, getPrecision);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_EXTRACT_UNIQUE_POINTS
+PHP_METHOD(Geometry, extractUniquePoints);
+#endif
+
+PHP_METHOD(Geometry, disjoint);
+PHP_METHOD(Geometry, touches);
+PHP_METHOD(Geometry, intersects);
+PHP_METHOD(Geometry, crosses);
+PHP_METHOD(Geometry, within);
+PHP_METHOD(Geometry, contains);
+PHP_METHOD(Geometry, overlaps);
+
+#ifdef HAVE_GEOS_COVERS
+PHP_METHOD(Geometry, covers);
+#endif
+
+#ifdef HAVE_GEOS_COVERED_BY
+PHP_METHOD(Geometry, coveredBy);
+#endif
+
+PHP_METHOD(Geometry, equals);
+PHP_METHOD(Geometry, equalsExact);
+PHP_METHOD(Geometry, isEmpty);
+
+#ifdef HAVE_GEOS_IS_VALID_DETAIL
+PHP_METHOD(Geometry, checkValidity);
+#endif
+
+PHP_METHOD(Geometry, isSimple);
+PHP_METHOD(Geometry, isRing);
+PHP_METHOD(Geometry, hasZ);
+
+#ifdef HAVE_GEOS_IS_CLOSED
+PHP_METHOD(Geometry, isClosed);
+#endif
+
+PHP_METHOD(Geometry, typeName);
+PHP_METHOD(Geometry, typeId);
+PHP_METHOD(Geometry, getSRID);
+PHP_METHOD(Geometry, setSRID);
+PHP_METHOD(Geometry, numGeometries);
+PHP_METHOD(Geometry, geometryN);
+PHP_METHOD(Geometry, numInteriorRings);
+
+#ifdef HAVE_GEOS_GEOM_GET_NUM_POINTS
+PHP_METHOD(Geometry, numPoints);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_X
+PHP_METHOD(Geometry, getX);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_Y
+PHP_METHOD(Geometry, getY);
+#endif
+
+PHP_METHOD(Geometry, interiorRingN);
+PHP_METHOD(Geometry, exteriorRing);
+PHP_METHOD(Geometry, numCoordinates);
+PHP_METHOD(Geometry, dimension);
+
+#ifdef HAVE_GEOS_GEOM_GET_COORDINATE_DIMENSION
+PHP_METHOD(Geometry, coordinateDimension);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_POINT_N
+PHP_METHOD(Geometry, pointN);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_START_POINT
+PHP_METHOD(Geometry, startPoint);
+#endif
+
+#ifdef HAVE_GEOS_GEOM_GET_END_POINT
+PHP_METHOD(Geometry, endPoint);
+#endif
+
+PHP_METHOD(Geometry, area);
+PHP_METHOD(Geometry, length);
+PHP_METHOD(Geometry, distance);
+PHP_METHOD(Geometry, hausdorffDistance);
+
+#ifdef HAVE_GEOS_SNAP
+PHP_METHOD(Geometry, snapTo);
+#endif
+
+#ifdef HAVE_GEOS_NODE
+PHP_METHOD(Geometry, node);
+#endif
+
+#ifdef HAVE_GEOS_DELAUNAY_TRIANGULATION
+PHP_METHOD(Geometry, delaunayTriangulation);
+#endif
+
+#ifdef HAVE_GEOS_VORONOI_DIAGRAM
+PHP_METHOD(Geometry, voronoiDiagram);
+#endif
+
+#ifdef HAVE_GEOS_CLIP_BY_RECT
+PHP_METHOD(Geometry, clipByRect);
+#endif
+
+static zend_function_entry Geometry_methods[] = {
+ PHP_ME(Geometry, __construct, NULL, 0)
+ PHP_ME(Geometry, __toString, NULL, 0)
+ PHP_ME(Geometry, project, NULL, 0)
+ PHP_ME(Geometry, interpolate, NULL, 0)
+ PHP_ME(Geometry, buffer, NULL, 0)
+
+# ifdef HAVE_GEOS_OFFSET_CURVE
+ PHP_ME(Geometry, offsetCurve, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, envelope, NULL, 0)
+ PHP_ME(Geometry, intersection, NULL, 0)
+ PHP_ME(Geometry, convexHull, NULL, 0)
+ PHP_ME(Geometry, difference, NULL, 0)
+ PHP_ME(Geometry, symDifference, NULL, 0)
+ PHP_ME(Geometry, boundary, NULL, 0)
+ PHP_ME(Geometry, union, NULL, 0)
+ PHP_ME(Geometry, pointOnSurface, NULL, 0)
+ PHP_ME(Geometry, centroid, NULL, 0)
+ PHP_ME(Geometry, relate, NULL, 0)
+
+# ifdef HAVE_GEOS_RELATE_BOUNDARY_NODE_RULE
+ PHP_ME(Geometry, relateBoundaryNodeRule, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, simplify, NULL, 0)
+ PHP_ME(Geometry, normalize, NULL, 0)
+
+# ifdef HAVE_GEOS_GEOM_SET_PRECISION
+ PHP_ME(Geometry, setPrecision, NULL, 0)
+# endif
+
+# if HAVE_GEOS_GEOM_GET_PRECISION
+ PHP_ME(Geometry, getPrecision, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_EXTRACT_UNIQUE_POINTS
+ PHP_ME(Geometry, extractUniquePoints, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, disjoint, NULL, 0)
+ PHP_ME(Geometry, touches, NULL, 0)
+ PHP_ME(Geometry, intersects, NULL, 0)
+ PHP_ME(Geometry, crosses, NULL, 0)
+ PHP_ME(Geometry, within, NULL, 0)
+ PHP_ME(Geometry, contains, NULL, 0)
+ PHP_ME(Geometry, overlaps, NULL, 0)
+
+# ifdef HAVE_GEOS_COVERS
+ PHP_ME(Geometry, covers, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_COVERED_BY
+ PHP_ME(Geometry, coveredBy, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, equals, NULL, 0)
+ PHP_ME(Geometry, equalsExact, NULL, 0)
+ PHP_ME(Geometry, isEmpty, NULL, 0)
+
+# ifdef HAVE_GEOS_IS_VALID_DETAIL
+ PHP_ME(Geometry, checkValidity, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, isSimple, NULL, 0)
+ PHP_ME(Geometry, isRing, NULL, 0)
+ PHP_ME(Geometry, hasZ, NULL, 0)
+
+# ifdef HAVE_GEOS_IS_CLOSED
+ PHP_ME(Geometry, isClosed, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, typeName, NULL, 0)
+ PHP_ME(Geometry, typeId, NULL, 0)
+ PHP_ME(Geometry, getSRID, NULL, 0)
+ PHP_ME(Geometry, setSRID, NULL, 0)
+ PHP_ME(Geometry, numGeometries, NULL, 0)
+ PHP_ME(Geometry, geometryN, NULL, 0)
+ PHP_ME(Geometry, numInteriorRings, NULL, 0)
+
+# ifdef HAVE_GEOS_GEOM_GET_NUM_POINTS
+ PHP_ME(Geometry, numPoints, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_GET_X
+ PHP_ME(Geometry, getX, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_GET_Y
+ PHP_ME(Geometry, getY, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, interiorRingN, NULL, 0)
+ PHP_ME(Geometry, exteriorRing, NULL, 0)
+ PHP_ME(Geometry, numCoordinates, NULL, 0)
+ PHP_ME(Geometry, dimension, NULL, 0)
+
+# ifdef HAVE_GEOS_GEOM_GET_COORDINATE_DIMENSION
+ PHP_ME(Geometry, coordinateDimension, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_GET_POINT_N
+ PHP_ME(Geometry, pointN, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_GET_START_POINT
+ PHP_ME(Geometry, startPoint, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_GEOM_GET_END_POINT
+ PHP_ME(Geometry, endPoint, NULL, 0)
+# endif
+
+ PHP_ME(Geometry, area, NULL, 0)
+ PHP_ME(Geometry, length, NULL, 0)
+ PHP_ME(Geometry, distance, NULL, 0)
+ PHP_ME(Geometry, hausdorffDistance, NULL, 0)
+
+# if HAVE_GEOS_SNAP
+ PHP_ME(Geometry, snapTo, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_NODE
+ PHP_ME(Geometry, node, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_DELAUNAY_TRIANGULATION
+ PHP_ME(Geometry, delaunayTriangulation, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_VORONOI_DIAGRAM
+ PHP_ME(Geometry, voronoiDiagram, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_CLIP_BY_RECT
+ PHP_ME(Geometry, clipByRect, NULL, 0)
+# endif
+
+ {NULL, NULL, NULL}
+};
+
+static zend_class_entry *Geometry_ce_ptr;
+
+static zend_object_handlers Geometry_object_handlers;
+
+/* Geometry serializer */
+
+static GEOSWKBWriter* Geometry_serializer = 0;
+
+static GEOSWKBWriter* getGeometrySerializer()
+{
+ TSRMLS_FETCH();
+
+ if ( ! Geometry_serializer ) {
+ Geometry_serializer = GEOSWKBWriter_create_r(GEOS_G(handle));
+ GEOSWKBWriter_setIncludeSRID_r(GEOS_G(handle), Geometry_serializer, 1);
+ GEOSWKBWriter_setOutputDimension_r(GEOS_G(handle), Geometry_serializer, 3);
+ }
+ return Geometry_serializer;
+}
+
+static void delGeometrySerializer()
+{
+ TSRMLS_FETCH();
+
+ if ( Geometry_serializer ) {
+ GEOSWKBWriter_destroy_r(GEOS_G(handle), Geometry_serializer);
+ }
+}
+
+/* Geometry deserializer */
+
+static GEOSWKBReader* Geometry_deserializer = 0;
+
+static GEOSWKBReader* getGeometryDeserializer()
+{
+ TSRMLS_FETCH();
+
+ if ( ! Geometry_deserializer ) {
+ Geometry_deserializer = GEOSWKBReader_create_r(GEOS_G(handle));
+ }
+ return Geometry_deserializer;
+}
+
+static void delGeometryDeserializer()
+{
+ TSRMLS_FETCH();
+
+ if ( Geometry_deserializer ) {
+ GEOSWKBReader_destroy_r(GEOS_G(handle), Geometry_deserializer);
+ }
+}
+
+/* Serializer function for GEOSGeometry */
+
+static int
+Geometry_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len,
+ zend_serialize_data *data TSRMLS_DC)
+{
+ GEOSWKBWriter *serializer;
+ GEOSGeometry *geom;
+ char* ret;
+ size_t retsize;
+
+
+ serializer = getGeometrySerializer();
+ geom = (GEOSGeometry*)getRelay(object, Geometry_ce_ptr);
+
+ /* NOTE: we might be fine using binary here */
+ ret = (char*)GEOSWKBWriter_writeHEX_r(GEOS_G(handle), serializer, geom, &retsize);
+ /* we'll probably get an exception if ret is null */
+ if ( ! ret ) return FAILURE;
+
+ *buffer = (unsigned char*)estrndup(ret, retsize);
+ GEOSFree_r(GEOS_G(handle), ret);
+
+ *buf_len = retsize;
+
+ return SUCCESS;
+}
+
+static int
+Geometry_deserialize(GEOS_PHP_ZVAL object, zend_class_entry *ce, const unsigned char *buf,
+ zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
+{
+ GEOSWKBReader* deserializer;
+ GEOSGeometry* geom;
+
+ deserializer = getGeometryDeserializer();
+ geom = GEOSWKBReader_readHEX_r(GEOS_G(handle), deserializer, buf, buf_len);
+
+ /* check zend_class_entry being what we expect! */
+ if ( ce != Geometry_ce_ptr ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "Geometry_deserialize called with unexpected zend_class_entry");
+ return FAILURE;
+ }
+#if PHP_VERSION_ID >= 70000
+ object_init_ex(object, ce);
+ setRelay(object, geom);
+#else
+ object_init_ex(*object, ce);
+ setRelay(*object, geom);
+#endif
+
+ return SUCCESS;
+}
+
+/*
+ * Push components of the given geometry
+ * to the given array zval.
+ * Components geometries are cloned.
+ * NOTE: collection components are not descended into
+ */
+static void
+dumpGeometry(GEOSGeometry* g, zval* array)
+{
+ TSRMLS_FETCH();
+ int ngeoms, i;
+
+ ngeoms = GEOSGetNumGeometries_r(GEOS_G(handle), g);
+ for (i=0; i<ngeoms; ++i)
+ {
+ zval *tmp;
+ GEOSGeometry* cc;
+ const GEOSGeometry* c = GEOSGetGeometryN_r(GEOS_G(handle), g, i);
+ if ( ! c ) continue; /* should get an exception */
+ /* we _need_ to clone as this one is owned by 'g' */
+ cc = GEOSGeom_clone_r(GEOS_G(handle), c);
+ if ( ! cc ) continue; /* should get an exception */
+
+ MAKE_STD_ZVAL(tmp);
+ object_init_ex(tmp, Geometry_ce_ptr);
+ setRelay(tmp, cc);
+ add_next_index_zval(array, tmp);
+#if PHP_VERSION_ID >= 70000
+ efree(tmp);
+#endif
+ }
+}
+
+
+static void
+Geometry_dtor (GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC)
+{
+#if PHP_VERSION_ID < 70000
+ Proxy *obj = (Proxy *)object;
+#else
+ Proxy *obj = php_geos_fetch_object(object);
+#endif
+
+ GEOSGeom_destroy_r(GEOS_G(handle), (GEOSGeometry*)obj->relay);
+
+#if PHP_VERSION_ID >= 70000
+ //zend_object_std_dtor(&obj->std);
+#else
+ zend_hash_destroy(obj->std.properties);
+ FREE_HASHTABLE(obj->std.properties);
+
+ efree(obj);
+#endif
+}
+
+static zend_object_value
+Geometry_create_obj (zend_class_entry *type TSRMLS_DC)
+{
+ return Gen_create_obj(type, Geometry_dtor, &Geometry_object_handlers);
+}
+
+
+PHP_METHOD(Geometry, __construct)
+{
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "GEOSGeometry can't be constructed using new, check WKTReader");
+
+}
+
+PHP_METHOD(Geometry, __toString)
+{
+ GEOSGeometry *geom;
+ GEOSWKTWriter *writer;
+ char *wkt;
+ char *ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+ writer = GEOSWKTWriter_create_r(GEOS_G(handle));
+ /* NOTE: if we get an exception before reaching
+ * GEOSWKTWriter_destory below we'll be leaking memory.
+ * One fix could be storing the object in a refcounted
+ * zval.
+ */
+# ifdef HAVE_GEOS_WKT_WRITER_SET_TRIM
+ GEOSWKTWriter_setTrim_r(GEOS_G(handle), writer, 1);
+# endif
+
+ wkt = GEOSWKTWriter_write_r(GEOS_G(handle), writer, geom);
+ /* we'll probably get an exception if wkt is null */
+ if ( ! wkt ) RETURN_NULL();
+
+ GEOSWKTWriter_destroy_r(GEOS_G(handle), writer);
+
+
+ ret = estrdup(wkt);
+ GEOSFree_r(GEOS_G(handle), wkt);
+
+ GEOS_PHP_RETURN_STRING(ret);
+}
+
+PHP_METHOD(Geometry, project)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ zval *zobj;
+ zend_bool normalized = 0;
+ double ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|b", &zobj,
+ &normalized) == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ if ( normalized ) {
+ ret = GEOSProjectNormalized_r(GEOS_G(handle), this, other);
+ } else {
+ ret = GEOSProject_r(GEOS_G(handle), this, other);
+ }
+ if ( ret < 0 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(ret);
+}
+
+PHP_METHOD(Geometry, interpolate)
+{
+ GEOSGeometry *this;
+ double dist;
+ GEOSGeometry *ret;
+ zend_bool normalized = 0;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|b",
+ &dist, &normalized) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( normalized ) {
+ ret = GEOSInterpolateNormalized_r(GEOS_G(handle), this, dist);
+ } else {
+ ret = GEOSInterpolate_r(GEOS_G(handle), this, dist);
+ }
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::buffer(dist, [<styleArray>])
+ *
+ * styleArray keys supported:
+ * 'quad_segs'
+ * Type: int
+ * Number of segments used to approximate
+ * a quarter circle (defaults to 8).
+ * 'endcap'
+ * Type: long
+ * Endcap style (defaults to GEOSBUF_CAP_ROUND)
+ * 'join'
+ * Type: long
+ * Join style (defaults to GEOSBUF_JOIN_ROUND)
+ * 'mitre_limit'
+ * Type: double
+ * mitre ratio limit (only affects joins with GEOSBUF_JOIN_MITRE style)
+ * 'miter_limit' is also accepted as a synonym for 'mitre_limit'.
+ * 'single_sided'
+ * Type: bool
+ * If true buffer lines only on one side, so that the input line
+ * will be a portion of the boundary of the returned polygon.
+ * Only applies to lineal input. Defaults to false.
+ */
+PHP_METHOD(Geometry, buffer)
+{
+ GEOSGeometry *this;
+ double dist;
+ GEOSGeometry *ret;
+ GEOSBufferParams *params;
+ static const double default_mitreLimit = 5.0;
+ static const int default_endCapStyle = GEOSBUF_CAP_ROUND;
+ static const int default_joinStyle = GEOSBUF_JOIN_ROUND;
+ static const int default_quadSegs = 8;
+ long int quadSegs = default_quadSegs;
+ long int endCapStyle = default_endCapStyle;
+ long int joinStyle = default_joinStyle;
+ double mitreLimit = default_mitreLimit;
+ long singleSided = 0;
+ zval *style_val = NULL;
+ GEOS_PHP_ZVAL data;
+ HashTable *style;
+ zend_string *key;
+ ulong index;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|a",
+ &dist, &style_val) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ params = GEOSBufferParams_create_r(GEOS_G(handle));
+
+ if ( style_val )
+ {
+ style = HASH_OF(style_val);
+ while(GEOS_PHP_HASH_GET_CUR_KEY(style, &key, &index)
+ == HASH_KEY_IS_STRING)
+ {
+ if(!strcmp(ZSTR_VAL(key), "quad_segs"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ quadSegs = getZvalAsLong(data);
+ GEOSBufferParams_setQuadrantSegments_r(GEOS_G(handle), params, quadSegs);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "endcap"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ endCapStyle = getZvalAsLong(data);
+ GEOSBufferParams_setEndCapStyle_r(GEOS_G(handle), params, endCapStyle);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "join"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ joinStyle = getZvalAsLong(data);
+ GEOSBufferParams_setJoinStyle_r(GEOS_G(handle), params, joinStyle);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "mitre_limit"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ mitreLimit = getZvalAsDouble(data);
+ GEOSBufferParams_setMitreLimit_r(GEOS_G(handle), params, mitreLimit);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "single_sided"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ singleSided = getZvalAsLong(data);
+ GEOSBufferParams_setSingleSided_r(GEOS_G(handle), params, singleSided);
+ }
+
+ zend_hash_move_forward(style);
+ }
+ }
+
+ ret = GEOSBufferWithParams_r(GEOS_G(handle), this, params, dist);
+ GEOSBufferParams_destroy_r(GEOS_G(handle), params);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::offsetCurve(dist, [<styleArray>])
+ *
+ * styleArray keys supported:
+ * 'quad_segs'
+ * Type: int
+ * Number of segments used to approximate
+ * a quarter circle (defaults to 8).
+ * 'join'
+ * Type: long
+ * Join style (defaults to GEOSBUF_JOIN_ROUND)
+ * 'mitre_limit'
+ * Type: double
+ * mitre ratio limit (only affects joins with GEOSBUF_JOIN_MITRE style)
+ * 'miter_limit' is also accepted as a synonym for 'mitre_limit'.
+ */
+#ifdef HAVE_GEOS_OFFSET_CURVE
+PHP_METHOD(Geometry, offsetCurve)
+{
+ GEOSGeometry *this;
+ double dist;
+ GEOSGeometry *ret;
+ static const double default_mitreLimit = 5.0;
+ static const int default_joinStyle = GEOSBUF_JOIN_ROUND;
+ static const int default_quadSegs = 8;
+ long int quadSegs = default_quadSegs;
+ long int joinStyle = default_joinStyle;
+ double mitreLimit = default_mitreLimit;
+ zval *style_val = NULL;
+ GEOS_PHP_ZVAL data;
+ HashTable *style;
+ zend_string *key;
+ ulong index;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|a",
+ &dist, &style_val) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( style_val )
+ {
+ style = HASH_OF(style_val);
+ while(GEOS_PHP_HASH_GET_CUR_KEY(style, &key, &index)
+ == HASH_KEY_IS_STRING)
+ {
+ if(!strcmp(ZSTR_VAL(key), "quad_segs"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ quadSegs = getZvalAsLong(data);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "join"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ joinStyle = getZvalAsLong(data);
+ }
+ else if(!strcmp(ZSTR_VAL(key), "mitre_limit"))
+ {
+ GEOS_PHP_HASH_GET_CUR_DATA(style, data);
+ mitreLimit = getZvalAsDouble(data);
+ }
+
+ zend_hash_move_forward(style);
+ }
+ }
+
+ ret = GEOSOffsetCurve_r(GEOS_G(handle), this, dist, quadSegs, joinStyle, mitreLimit);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+PHP_METHOD(Geometry, envelope)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSEnvelope_r(GEOS_G(handle), this);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+PHP_METHOD(Geometry, intersection)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ GEOSGeometry *ret;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSIntersection_r(GEOS_G(handle), this, other);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::clipByRect(xmin,ymin,xmax,ymax)
+ */
+#ifdef HAVE_GEOS_CLIP_BY_RECT
+PHP_METHOD(Geometry, clipByRect)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+ double xmin,ymin,xmax,ymax;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dddd",
+ &xmin, &ymin, &xmax, &ymax) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ ret = GEOSClipByRect_r(GEOS_G(handle), this, xmin, ymin, xmax, ymax);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+PHP_METHOD(Geometry, convexHull)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSConvexHull_r(GEOS_G(handle), this);
+ if ( ret == NULL ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+PHP_METHOD(Geometry, difference)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ GEOSGeometry *ret;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSDifference_r(GEOS_G(handle), this, other);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+PHP_METHOD(Geometry, symDifference)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ GEOSGeometry *ret;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSSymDifference_r(GEOS_G(handle), this, other);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+PHP_METHOD(Geometry, boundary)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSBoundary_r(GEOS_G(handle), this);
+ if ( ret == NULL ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::union(otherGeom)
+ * GEOSGeometry::union()
+ */
+PHP_METHOD(Geometry, union)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ GEOSGeometry *ret;
+ zval *zobj = NULL;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( zobj ) {
+ other = getRelay(zobj, Geometry_ce_ptr);
+ ret = GEOSUnion_r(GEOS_G(handle), this, other);
+ } else {
+# ifdef HAVE_GEOS_UNARY_UNION
+ ret = GEOSUnaryUnion_r(GEOS_G(handle), this);
+# else
+ ret = GEOSUnionCascaded_r(GEOS_G(handle), this);
+# endif
+ }
+
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::pointOnSurface()
+ */
+PHP_METHOD(Geometry, pointOnSurface)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSPointOnSurface_r(GEOS_G(handle), this);
+ if ( ret == NULL ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::centroid()
+ */
+PHP_METHOD(Geometry, centroid)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGetCentroid_r(GEOS_G(handle), this);
+ if ( ret == NULL ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry::relate(otherGeom)
+ * GEOSGeometry::relate(otherGeom, pattern)
+ */
+PHP_METHOD(Geometry, relate)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ zval *zobj;
+ char* pat = NULL;
+#if PHP_VERSION_ID >= 70000
+ size_t patlen;
+#else
+ int patlen;
+#endif
+ int retInt;
+ zend_bool retBool;
+ char* retStr;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|s",
+ &zobj, &pat, &patlen) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ if ( ! pat ) {
+ /* we'll compute it */
+ pat = GEOSRelate_r(GEOS_G(handle), this, other);
+ if ( ! pat ) RETURN_NULL(); /* should get an exception first */
+ retStr = estrdup(pat);
+ GEOSFree_r(GEOS_G(handle), pat);
+ GEOS_PHP_RETURN_STRING(retStr);
+ } else {
+ retInt = GEOSRelatePattern_r(GEOS_G(handle), this, other, pat);
+ if ( retInt == 2 ) RETURN_NULL(); /* should get an exception first */
+ retBool = retInt;
+ RETURN_BOOL(retBool);
+ }
+
+}
+
+/**
+ * GEOSGeometry::relateBoundaryNodeRule(otherGeom, rule)
+ */
+#ifdef HAVE_GEOS_RELATE_BOUNDARY_NODE_RULE
+PHP_METHOD(Geometry, relateBoundaryNodeRule)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ zval *zobj;
+ char* pat;
+ long int bnr = GEOSRELATE_BNR_OGC;
+ char* retStr;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ol",
+ &zobj, &bnr) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ /* we'll compute it */
+ pat = GEOSRelateBoundaryNodeRule_r(GEOS_G(handle), this, other, bnr);
+ if ( ! pat ) RETURN_NULL(); /* should get an exception first */
+ retStr = estrdup(pat);
+ GEOSFree_r(GEOS_G(handle), pat);
+ GEOS_PHP_RETURN_STRING(retStr);
+}
+#endif
+
+/**
+ * GEOSGeometry GEOSGeometry::simplify(tolerance)
+ * GEOSGeometry GEOSGeometry::simplify(tolerance, preserveTopology)
+ */
+PHP_METHOD(Geometry, simplify)
+{
+ GEOSGeometry *this;
+ double tolerance;
+ zend_bool preserveTopology = 0;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|b",
+ &tolerance, &preserveTopology) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( preserveTopology ) {
+ ret = GEOSTopologyPreserveSimplify_r(GEOS_G(handle), this, tolerance);
+ } else {
+ ret = GEOSSimplify_r(GEOS_G(handle), this, tolerance);
+ }
+
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::setPrecision(gridsize, [flags])
+ */
+#ifdef HAVE_GEOS_GEOM_SET_PRECISION
+PHP_METHOD(Geometry, setPrecision)
+{
+ GEOSGeometry *this;
+ double gridSize;
+ long int flags = 0;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|l",
+ &gridSize, &flags) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ ret = GEOSGeom_setPrecision_r(GEOS_G(handle), this, gridSize, flags);
+
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+/**
+ * double GEOSGeometry::getPrecision()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_PRECISION
+PHP_METHOD(Geometry, getPrecision)
+{
+ GEOSGeometry *geom;
+ double prec;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ prec = GEOSGeom_getPrecision_r(GEOS_G(handle), geom);
+ if ( prec < 0 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(prec);
+}
+#endif
+
+/**
+ * GEOSGeometry GEOSGeometry::normalize()
+ */
+PHP_METHOD(Geometry, normalize)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeom_clone_r(GEOS_G(handle), this);
+
+ if ( ! ret ) RETURN_NULL();
+
+ GEOSNormalize_r(GEOS_G(handle), ret); /* exception should be gotten automatically */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::extractUniquePoints()
+ */
+#ifdef HAVE_GEOS_GEOM_EXTRACT_UNIQUE_POINTS
+PHP_METHOD(Geometry, extractUniquePoints)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeom_extractUniquePoints_r(GEOS_G(handle), this);
+ if ( ret == NULL ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+/**
+ * bool GEOSGeometry::disjoint(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, disjoint)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSDisjoint_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::touches(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, touches)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSTouches_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::intersects(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, intersects)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSIntersects_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::crosses(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, crosses)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSCrosses_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::within(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, within)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSWithin_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::contains(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, contains)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSContains_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::overlaps(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, overlaps)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSOverlaps_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::covers(GEOSGeometry)
+ */
+#ifdef HAVE_GEOS_COVERS
+PHP_METHOD(Geometry, covers)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSCovers_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+#endif
+
+/**
+ * bool GEOSGeometry::coveredBy(GEOSGeometry)
+ */
+#ifdef HAVE_GEOS_COVERED_BY
+PHP_METHOD(Geometry, coveredBy)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSCoveredBy_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+#endif
+
+/**
+ * bool GEOSGeometry::equals(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, equals)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o",
+ &zobj) == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSEquals_r(GEOS_G(handle), this, other);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::equalsExact(GEOSGeometry)
+ * bool GEOSGeometry::equalsExact(GEOSGeometry, double tolerance)
+ */
+PHP_METHOD(Geometry, equalsExact)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ int ret;
+ double tolerance = 0;
+ zend_bool retBool;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|d",
+ &zobj, &tolerance) == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSEqualsExact_r(GEOS_G(handle), this, other, tolerance);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::isEmpty()
+ */
+PHP_METHOD(Geometry, isEmpty)
+{
+ GEOSGeometry *this;
+ int ret;
+ zend_bool retBool;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSisEmpty_r(GEOS_G(handle), this);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * array GEOSGeometry::checkValidity()
+ */
+#ifdef HAVE_GEOS_IS_VALID_DETAIL
+PHP_METHOD(Geometry, checkValidity)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *location = NULL;
+ int ret;
+ char *reason = NULL;
+ zend_bool retBool;
+ char *reasonVal = NULL;
+ zval *locationVal = NULL;
+ long int flags = 0;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l",
+ &flags) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ ret = GEOSisValidDetail_r(GEOS_G(handle), this, flags, &reason, &location);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ if ( reason ) {
+ reasonVal = estrdup(reason);
+ GEOSFree_r(GEOS_G(handle), reason);
+ }
+
+ if ( location ) {
+ MAKE_STD_ZVAL(locationVal);
+ object_init_ex(locationVal, Geometry_ce_ptr);
+ setRelay(locationVal, location);
+ }
+
+ retBool = ret;
+
+ /* return value is an array */
+ array_init(return_value);
+ add_assoc_bool(return_value, "valid", retBool);
+ if ( reasonVal ) GEOS_PHP_ADD_ASSOC_ARRAY(return_value, "reason", reasonVal);
+ if ( locationVal ) GEOS_PHP_ADD_ASSOC_ZVAL(return_value, "location", locationVal);
+
+}
+#endif
+
+/**
+ * bool GEOSGeometry::isSimple()
+ */
+PHP_METHOD(Geometry, isSimple)
+{
+ GEOSGeometry *this;
+ int ret;
+ zend_bool retBool;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSisSimple_r(GEOS_G(handle), this);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::isRing()
+ */
+PHP_METHOD(Geometry, isRing)
+{
+ GEOSGeometry *this;
+ int ret;
+ zend_bool retBool;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSisRing_r(GEOS_G(handle), this);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::hasZ()
+ */
+PHP_METHOD(Geometry, hasZ)
+{
+ GEOSGeometry *this;
+ int ret;
+ zend_bool retBool;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSHasZ_r(GEOS_G(handle), this);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * bool GEOSGeometry::isClosed()
+ */
+#ifdef HAVE_GEOS_IS_CLOSED
+PHP_METHOD(Geometry, isClosed)
+{
+ GEOSGeometry *this;
+ int ret;
+ zend_bool retBool;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSisClosed_r(GEOS_G(handle), this);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+#endif
+
+/**
+ * string GEOSGeometry::typeName()
+ */
+PHP_METHOD(Geometry, typeName)
+{
+ GEOSGeometry *this;
+ char *typ;
+ char *typVal;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ /* TODO: define constant strings instead... */
+
+ typ = GEOSGeomType_r(GEOS_G(handle), this);
+ if ( ! typ ) RETURN_NULL(); /* should get an exception first */
+
+ typVal = estrdup(typ);
+ GEOSFree_r(GEOS_G(handle), typ);
+
+ GEOS_PHP_RETURN_STRING(typVal);
+}
+
+/**
+ * long GEOSGeometry::typeId()
+ */
+PHP_METHOD(Geometry, typeId)
+{
+ GEOSGeometry *this;
+ long typ;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ /* TODO: define constant strings instead... */
+
+ typ = GEOSGeomTypeId_r(GEOS_G(handle), this);
+ if ( typ == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(typ);
+}
+
+/**
+ * long GEOSGeometry::getSRID()
+ */
+PHP_METHOD(Geometry, getSRID)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGetSRID_r(GEOS_G(handle), geom);
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * void GEOSGeometry::setSRID(long)
+ */
+PHP_METHOD(Geometry, setSRID)
+{
+ GEOSGeometry *geom;
+ long int srid;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &srid) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ GEOSSetSRID_r(GEOS_G(handle), geom, srid);
+}
+
+/**
+ * long GEOSGeometry::numGeometries()
+ */
+PHP_METHOD(Geometry, numGeometries)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGetNumGeometries_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::geometryN()
+ */
+PHP_METHOD(Geometry, geometryN)
+{
+ GEOSGeometry *geom;
+ const GEOSGeometry *c;
+ GEOSGeometry *cc;
+ long int num;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &num) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( num >= GEOSGetNumGeometries_r(GEOS_G(handle), geom) ) RETURN_NULL();
+ c = GEOSGetGeometryN_r(GEOS_G(handle), geom, num);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+ cc = GEOSGeom_clone_r(GEOS_G(handle), c);
+ if ( ! cc ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, cc);
+}
+
+/**
+ * long GEOSGeometry::numInteriorRings()
+ */
+PHP_METHOD(Geometry, numInteriorRings)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGetNumInteriorRings_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * long GEOSGeometry::numPoints()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_NUM_POINTS
+PHP_METHOD(Geometry, numPoints)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeomGetNumPoints_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+#endif
+
+/**
+ * double GEOSGeometry::getX()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_X
+PHP_METHOD(Geometry, getX)
+{
+ GEOSGeometry *geom;
+ int ret;
+ double x;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeomGetX_r(GEOS_G(handle), geom, &x);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(x);
+}
+#endif
+
+/**
+ * double GEOSGeometry::getY()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_Y
+PHP_METHOD(Geometry, getY)
+{
+ GEOSGeometry *geom;
+ int ret;
+ double y;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeomGetY_r(GEOS_G(handle), geom, &y);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(y);
+}
+#endif
+
+/**
+ * GEOSGeometry GEOSGeometry::interiorRingN()
+ */
+PHP_METHOD(Geometry, interiorRingN)
+{
+ GEOSGeometry *geom;
+ const GEOSGeometry *c;
+ GEOSGeometry *cc;
+ long int num;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &num) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( num >= GEOSGetNumInteriorRings_r(GEOS_G(handle), geom) ) RETURN_NULL();
+ c = GEOSGetInteriorRingN_r(GEOS_G(handle), geom, num);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+ cc = GEOSGeom_clone_r(GEOS_G(handle), c);
+ if ( ! cc ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, cc);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::exteriorRing()
+ */
+PHP_METHOD(Geometry, exteriorRing)
+{
+ GEOSGeometry *geom;
+ const GEOSGeometry *c;
+ GEOSGeometry *cc;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ c = GEOSGetExteriorRing_r(GEOS_G(handle), geom);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+ cc = GEOSGeom_clone_r(GEOS_G(handle), c);
+ if ( ! cc ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, cc);
+}
+
+/**
+ * long GEOSGeometry::numCoordinates()
+ */
+PHP_METHOD(Geometry, numCoordinates)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGetNumCoordinates_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * long GEOSGeometry::dimension()
+ * 0:puntual 1:lineal 2:areal
+ */
+PHP_METHOD(Geometry, dimension)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeom_getDimensions_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * long GEOSGeometry::coordinateDimension()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_COORDINATE_DIMENSION
+PHP_METHOD(Geometry, coordinateDimension)
+{
+ GEOSGeometry *geom;
+ long int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSGeom_getCoordinateDimension_r(GEOS_G(handle), geom);
+ if ( ret == -1 ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_LONG(ret);
+}
+#endif
+
+/**
+ * GEOSGeometry GEOSGeometry::pointN()
+ */
+#ifdef HAVE_GEOS_GEOM_GET_POINT_N
+PHP_METHOD(Geometry, pointN)
+{
+ GEOSGeometry *geom;
+ GEOSGeometry *c;
+ long int num;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &num) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( num >= GEOSGeomGetNumPoints_r(GEOS_G(handle), geom) ) RETURN_NULL();
+ c = GEOSGeomGetPointN_r(GEOS_G(handle), geom, num);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, c);
+}
+#endif
+
+/**
+ * GEOSGeometry GEOSGeometry::startPoint()
+ */
+PHP_METHOD(Geometry, startPoint)
+{
+ GEOSGeometry *geom;
+ GEOSGeometry *c;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ c = GEOSGeomGetStartPoint_r(GEOS_G(handle), geom);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, c);
+}
+
+/**
+ * GEOSGeometry GEOSGeometry::endPoint()
+ */
+PHP_METHOD(Geometry, endPoint)
+{
+ GEOSGeometry *geom;
+ GEOSGeometry *c;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ c = GEOSGeomGetEndPoint_r(GEOS_G(handle), geom);
+ if ( ! c ) RETURN_NULL(); /* should get an exception first */
+
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, c);
+}
+
+/**
+ * double GEOSGeometry::area()
+ */
+PHP_METHOD(Geometry, area)
+{
+ GEOSGeometry *geom;
+ double area;
+ int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSArea_r(GEOS_G(handle), geom, &area);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(area);
+}
+
+/**
+ * double GEOSGeometry::length()
+ */
+PHP_METHOD(Geometry, length)
+{
+ GEOSGeometry *geom;
+ double length;
+ int ret;
+
+ geom = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSLength_r(GEOS_G(handle), geom, &length);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(length);
+}
+
+/**
+ * double GEOSGeometry::distance(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, distance)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ zval *zobj;
+ double dist;
+ int ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o",
+ &zobj) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSDistance_r(GEOS_G(handle), this, other, &dist);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(dist);
+}
+
+/**
+ * double GEOSGeometry::hausdorffDistance(GEOSGeometry)
+ */
+PHP_METHOD(Geometry, hausdorffDistance)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ zval *zobj;
+ double dist;
+ int ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o",
+ &zobj) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSHausdorffDistance_r(GEOS_G(handle), this, other, &dist);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ RETURN_DOUBLE(dist);
+}
+
+#ifdef HAVE_GEOS_SNAP
+PHP_METHOD(Geometry, snapTo)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *other;
+ GEOSGeometry *ret;
+ double tolerance;
+ zval *zobj;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "od", &zobj,
+ &tolerance) == FAILURE) {
+ RETURN_NULL();
+ }
+ other = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = GEOSSnap_r(GEOS_G(handle), this, other, tolerance);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+#ifdef HAVE_GEOS_NODE
+PHP_METHOD(Geometry, node)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ ret = GEOSNode_r(GEOS_G(handle), this);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+
+
+/* -- class GEOSWKTReader -------------------- */
+
+PHP_METHOD(WKTReader, __construct);
+PHP_METHOD(WKTReader, read);
+
+static zend_function_entry WKTReader_methods[] = {
+ PHP_ME(WKTReader, __construct, NULL, 0)
+ PHP_ME(WKTReader, read, NULL, 0)
+ {NULL, NULL, NULL}
+};
+
+static zend_class_entry *WKTReader_ce_ptr;
+
+static zend_object_handlers WKTReader_object_handlers;
+
+static void
+WKTReader_dtor (GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC)
+{
+#if PHP_VERSION_ID < 70000
+ Proxy *obj = (Proxy *)object;
+#else
+ Proxy *obj = php_geos_fetch_object(object);
+#endif
+
+ GEOSWKTReader *reader = (GEOSWKTReader*)obj->relay;
+ if (reader) {
+ GEOSWKTReader_destroy_r(GEOS_G(handle), reader);
+ }
+
+#if PHP_VERSION_ID < 70000
+ zend_hash_destroy(obj->std.properties);
+ FREE_HASHTABLE(obj->std.properties);
+
+ efree(obj);
+#endif
+}
+
+static zend_object_value
+WKTReader_create_obj (zend_class_entry *type TSRMLS_DC)
+{
+ return Gen_create_obj(type, WKTReader_dtor, &WKTReader_object_handlers);
+}
+
+
+PHP_METHOD(WKTReader, __construct)
+{
+ GEOSWKTReader* obj;
+ zval *object = getThis();
+
+ obj = GEOSWKTReader_create_r(GEOS_G(handle));
+ if ( ! obj ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "GEOSWKTReader_create() failed (didn't initGEOS?)");
+ }
+
+ setRelay(object, obj);
+}
+
+PHP_METHOD(WKTReader, read)
+{
+ GEOSWKTReader *reader;
+ GEOSGeometry *geom;
+ zend_string *wkt;
+#if PHP_VERSION_ID < 70000
+ int wktlen;
+#endif
+
+ reader = (GEOSWKTReader*)getRelay(getThis(), WKTReader_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+#if PHP_VERSION_ID >= 70000
+ "S", &wkt
+#else
+ "s", &wkt, &wktlen
+#endif
+ ) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ geom = GEOSWKTReader_read_r(GEOS_G(handle), reader, ZSTR_VAL(wkt));
+ /* we'll probably get an exception if geom is null */
+ if ( ! geom ) RETURN_NULL();
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, geom);
+
+}
+
+/* -- class GEOSWKTWriter -------------------- */
+
+PHP_METHOD(WKTWriter, __construct);
+PHP_METHOD(WKTWriter, write);
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_TRIM
+PHP_METHOD(WKTWriter, setTrim);
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_ROUNDING_PRECISION
+PHP_METHOD(WKTWriter, setRoundingPrecision);
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_OUTPUT_DIMENSION
+PHP_METHOD(WKTWriter, setOutputDimension);
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_GET_OUTPUT_DIMENSION
+PHP_METHOD(WKTWriter, getOutputDimension);
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_OLD_3D
+PHP_METHOD(WKTWriter, setOld3D);
+#endif
+
+static zend_function_entry WKTWriter_methods[] = {
+ PHP_ME(WKTWriter, __construct, NULL, 0)
+ PHP_ME(WKTWriter, write, NULL, 0)
+
+# ifdef HAVE_GEOS_WKT_WRITER_SET_TRIM
+ PHP_ME(WKTWriter, setTrim, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_WKT_WRITER_SET_ROUNDING_PRECISION
+ PHP_ME(WKTWriter, setRoundingPrecision, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_WKT_WRITER_SET_OUTPUT_DIMENSION
+ PHP_ME(WKTWriter, setOutputDimension, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_WKT_WRITER_GET_OUTPUT_DIMENSION
+ PHP_ME(WKTWriter, getOutputDimension, NULL, 0)
+# endif
+
+# ifdef HAVE_GEOS_WKT_WRITER_SET_OLD_3D
+ PHP_ME(WKTWriter, setOld3D, NULL, 0)
+# endif
+
+ {NULL, NULL, NULL}
+};
+
+static zend_class_entry *WKTWriter_ce_ptr;
+
+static zend_object_handlers WKTWriter_object_handlers;
+
+static void
+WKTWriter_dtor (GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC)
+{
+#if PHP_VERSION_ID < 70000
+ Proxy *obj = (Proxy *)object;
+#else
+ Proxy *obj = php_geos_fetch_object(object);
+#endif
+
+ GEOSWKTWriter_destroy_r(GEOS_G(handle), (GEOSWKTWriter*)obj->relay);
+
+#if PHP_VERSION_ID >= 70000
+ //zend_object_std_dtor(&obj->std);
+#else
+ zend_hash_destroy(obj->std.properties);
+ FREE_HASHTABLE(obj->std.properties);
+
+ efree(obj);
+#endif
+}
+
+static zend_object_value
+WKTWriter_create_obj (zend_class_entry *type TSRMLS_DC)
+{
+ return Gen_create_obj(type, WKTWriter_dtor, &WKTWriter_object_handlers);
+}
+
+PHP_METHOD(WKTWriter, __construct)
+{
+ GEOSWKTWriter* obj;
+ zval *object = getThis();
+
+ obj = GEOSWKTWriter_create_r(GEOS_G(handle));
+ if ( ! obj ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "GEOSWKTWriter_create() failed (didn't initGEOS?)");
+ }
+
+ setRelay(object, obj);
+}
+
+PHP_METHOD(WKTWriter, write)
+{
+ GEOSWKTWriter *writer;
+ zval *zobj;
+ GEOSGeometry *geom;
+ char* wkt;
+ char* retstr;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ geom = getRelay(zobj, Geometry_ce_ptr);
+
+ wkt = GEOSWKTWriter_write_r(GEOS_G(handle), writer, geom);
+ /* we'll probably get an exception if wkt is null */
+ if ( ! wkt ) RETURN_NULL();
+
+ retstr = estrdup(wkt);
+ GEOSFree_r(GEOS_G(handle), wkt);
+
+ GEOS_PHP_RETURN_STRING(retstr);
+}
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_TRIM
+PHP_METHOD(WKTWriter, setTrim)
+{
+ GEOSWKTWriter *writer;
+ zend_bool trimval;
+ char trim;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &trimval)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ trim = trimval;
+ GEOSWKTWriter_setTrim_r(GEOS_G(handle), writer, trim);
+}
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_ROUNDING_PRECISION
+PHP_METHOD(WKTWriter, setRoundingPrecision)
+{
+ GEOSWKTWriter *writer;
+ long int prec;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &prec)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ GEOSWKTWriter_setRoundingPrecision_r(GEOS_G(handle), writer, prec);
+}
+#endif
+
+/**
+ * void GEOSWKTWriter::setOutputDimension()
+ */
+#ifdef HAVE_GEOS_WKT_WRITER_SET_OUTPUT_DIMENSION
+PHP_METHOD(WKTWriter, setOutputDimension)
+{
+ GEOSWKTWriter *writer;
+ long int dim;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &dim)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ GEOSWKTWriter_setOutputDimension_r(GEOS_G(handle), writer, dim);
+}
+#endif
+
+/**
+ * long GEOSWKTWriter::getOutputDimension()
+ */
+#ifdef HAVE_GEOS_WKT_WRITER_GET_OUTPUT_DIMENSION
+PHP_METHOD(WKTWriter, getOutputDimension)
+{
+ GEOSWKTWriter *writer;
+ long int ret;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ ret = GEOSWKTWriter_getOutputDimension_r(GEOS_G(handle), writer);
+
+ RETURN_LONG(ret);
+}
+#endif
+
+#ifdef HAVE_GEOS_WKT_WRITER_SET_OLD_3D
+PHP_METHOD(WKTWriter, setOld3D)
+{
+ GEOSWKTWriter *writer;
+ zend_bool bval;
+ int val;
+
+ writer = (GEOSWKTWriter*)getRelay(getThis(), WKTWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &bval)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ val = bval;
+ GEOSWKTWriter_setOld3D_r(GEOS_G(handle), writer, val);
+}
+#endif
+
+/* -- class GEOSWKBWriter -------------------- */
+
+PHP_METHOD(WKBWriter, __construct);
+PHP_METHOD(WKBWriter, getOutputDimension);
+PHP_METHOD(WKBWriter, setOutputDimension);
+PHP_METHOD(WKBWriter, getByteOrder);
+PHP_METHOD(WKBWriter, setByteOrder);
+PHP_METHOD(WKBWriter, setIncludeSRID);
+PHP_METHOD(WKBWriter, getIncludeSRID);
+PHP_METHOD(WKBWriter, write);
+PHP_METHOD(WKBWriter, writeHEX);
+
+static zend_function_entry WKBWriter_methods[] = {
+ PHP_ME(WKBWriter, __construct, NULL, 0)
+ PHP_ME(WKBWriter, getOutputDimension, NULL, 0)
+ PHP_ME(WKBWriter, setOutputDimension, NULL, 0)
+ PHP_ME(WKBWriter, getByteOrder, NULL, 0)
+ PHP_ME(WKBWriter, setByteOrder, NULL, 0)
+ PHP_ME(WKBWriter, getIncludeSRID, NULL, 0)
+ PHP_ME(WKBWriter, setIncludeSRID, NULL, 0)
+ PHP_ME(WKBWriter, write, NULL, 0)
+ PHP_ME(WKBWriter, writeHEX, NULL, 0)
+ {NULL, NULL, NULL}
+};
+
+static zend_class_entry *WKBWriter_ce_ptr;
+
+static zend_object_handlers WKBWriter_object_handlers;
+
+static void
+WKBWriter_dtor (GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC)
+{
+#if PHP_VERSION_ID < 70000
+ Proxy *obj = (Proxy *)object;
+#else
+ Proxy *obj = php_geos_fetch_object(object);
+#endif
+
+ GEOSWKBWriter_destroy_r(GEOS_G(handle), (GEOSWKBWriter*)obj->relay);
+
+#if PHP_VERSION_ID >= 70000
+ //zend_object_std_dtor(&obj->std);
+#else
+ zend_hash_destroy(obj->std.properties);
+ FREE_HASHTABLE(obj->std.properties);
+
+ efree(obj);
+#endif
+}
+
+static zend_object_value
+WKBWriter_create_obj (zend_class_entry *type TSRMLS_DC)
+{
+ return Gen_create_obj(type, WKBWriter_dtor, &WKBWriter_object_handlers);
+}
+
+/**
+ * GEOSWKBWriter w = new GEOSWKBWriter()
+ */
+PHP_METHOD(WKBWriter, __construct)
+{
+ GEOSWKBWriter* obj;
+ zval *object = getThis();
+
+ obj = GEOSWKBWriter_create_r(GEOS_G(handle));
+ if ( ! obj ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "GEOSWKBWriter_create() failed (didn't initGEOS?)");
+ }
+
+ setRelay(object, obj);
+}
+
+/**
+ * long GEOSWKBWriter::getOutputDimension();
+ */
+PHP_METHOD(WKBWriter, getOutputDimension)
+{
+ GEOSWKBWriter *writer;
+ long int ret;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ ret = GEOSWKBWriter_getOutputDimension_r(GEOS_G(handle), writer);
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * void GEOSWKBWriter::setOutputDimension(dims);
+ */
+PHP_METHOD(WKBWriter, setOutputDimension)
+{
+ GEOSWKBWriter *writer;
+ long int dim;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &dim)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ GEOSWKBWriter_setOutputDimension_r(GEOS_G(handle), writer, dim);
+
+}
+
+/**
+ * string GEOSWKBWriter::write(GEOSGeometry)
+ */
+PHP_METHOD(WKBWriter, write)
+{
+ GEOSWKBWriter *writer;
+ zval *zobj;
+ GEOSGeometry *geom;
+ char *ret;
+ size_t retsize;
+ char* retstr;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ geom = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = (char*)GEOSWKBWriter_write_r(GEOS_G(handle), writer, geom, &retsize);
+ /* we'll probably get an exception if ret is null */
+ if ( ! ret ) RETURN_NULL();
+
+ retstr = estrndup(ret, retsize);
+ GEOSFree_r(GEOS_G(handle), ret);
+
+ GEOS_PHP_RETURN_STRINGL(retstr, retsize);
+}
+
+/**
+ * string GEOSWKBWriter::writeHEX(GEOSGeometry)
+ */
+PHP_METHOD(WKBWriter, writeHEX)
+{
+ GEOSWKBWriter *writer;
+ zval *zobj;
+ GEOSGeometry *geom;
+ char *ret;
+ size_t retsize; /* useless... */
+ char* retstr;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ geom = getRelay(zobj, Geometry_ce_ptr);
+
+ ret = (char*)GEOSWKBWriter_writeHEX_r(GEOS_G(handle), writer, geom, &retsize);
+ /* we'll probably get an exception if ret is null */
+ if ( ! ret ) RETURN_NULL();
+
+ retstr = estrndup(ret, retsize);
+ GEOSFree_r(GEOS_G(handle), ret);
+
+ GEOS_PHP_RETURN_STRING(retstr);
+}
+
+/**
+ * long GEOSWKBWriter::getByteOrder();
+ */
+PHP_METHOD(WKBWriter, getByteOrder)
+{
+ GEOSWKBWriter *writer;
+ long int ret;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ ret = GEOSWKBWriter_getByteOrder_r(GEOS_G(handle), writer);
+
+ RETURN_LONG(ret);
+}
+
+/**
+ * void GEOSWKBWriter::setByteOrder(dims);
+ */
+PHP_METHOD(WKBWriter, setByteOrder)
+{
+ GEOSWKBWriter *writer;
+ long int dim;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &dim)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ GEOSWKBWriter_setByteOrder_r(GEOS_G(handle), writer, dim);
+
+}
+
+/**
+ * bool GEOSWKBWriter::getIncludeSRID();
+ */
+PHP_METHOD(WKBWriter, getIncludeSRID)
+{
+ GEOSWKBWriter *writer;
+ int ret;
+ zend_bool retBool;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ ret = GEOSWKBWriter_getIncludeSRID_r(GEOS_G(handle), writer);
+ retBool = ret;
+
+ RETURN_BOOL(retBool);
+}
+
+/**
+ * void GEOSWKBWriter::setIncludeSRID(bool);
+ */
+PHP_METHOD(WKBWriter, setIncludeSRID)
+{
+ GEOSWKBWriter *writer;
+ int inc;
+ zend_bool incVal;
+
+ writer = (GEOSWKBWriter*)getRelay(getThis(), WKBWriter_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &incVal)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ inc = incVal;
+ GEOSWKBWriter_setIncludeSRID_r(GEOS_G(handle), writer, inc);
+}
+
+/* -- class GEOSWKBReader -------------------- */
+
+PHP_METHOD(WKBReader, __construct);
+PHP_METHOD(WKBReader, read);
+PHP_METHOD(WKBReader, readHEX);
+
+static zend_function_entry WKBReader_methods[] = {
+ PHP_ME(WKBReader, __construct, NULL, 0)
+ PHP_ME(WKBReader, read, NULL, 0)
+ PHP_ME(WKBReader, readHEX, NULL, 0)
+ {NULL, NULL, NULL}
+};
+
+static zend_class_entry *WKBReader_ce_ptr;
+
+static zend_object_handlers WKBReader_object_handlers;
+
+static void
+WKBReader_dtor (GEOS_PHP_DTOR_OBJECT *object TSRMLS_DC)
+{
+#if PHP_VERSION_ID < 70000
+ Proxy *obj = (Proxy *)object;
+#else
+ Proxy *obj = php_geos_fetch_object(object);
+#endif
+
+ GEOSWKBReader_destroy_r(GEOS_G(handle), (GEOSWKBReader*)obj->relay);
+
+#if PHP_VERSION_ID >= 70000
+ //zend_object_std_dtor(&obj->std);
+#else
+ zend_hash_destroy(obj->std.properties);
+ FREE_HASHTABLE(obj->std.properties);
+
+ efree(obj);
+#endif
+}
+
+static zend_object_value
+WKBReader_create_obj (zend_class_entry *type TSRMLS_DC)
+{
+ return Gen_create_obj(type, WKBReader_dtor, &WKBReader_object_handlers);
+}
+
+
+PHP_METHOD(WKBReader, __construct)
+{
+ GEOSWKBReader* obj;
+ zval *object = getThis();
+
+ obj = GEOSWKBReader_create_r(GEOS_G(handle));
+ if ( ! obj ) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "GEOSWKBReader_create() failed (didn't initGEOS?)");
+ }
+
+ setRelay(object, obj);
+}
+
+PHP_METHOD(WKBReader, read)
+{
+ GEOSWKBReader *reader;
+ GEOSGeometry *geom;
+ zend_string* wkb;
+ int wkblen;
+
+ reader = (GEOSWKBReader*)getRelay(getThis(), WKBReader_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+#if PHP_VERSION_ID >= 70000
+ "S", &wkb
+#else
+ "s", &wkb, &wkblen
+#endif
+ ) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+#if PHP_VERSION_ID >= 70000
+ wkblen = strlen(ZSTR_VAL(wkb));
+#endif
+
+ geom = GEOSWKBReader_read_r(GEOS_G(handle), reader, (unsigned char*)ZSTR_VAL(wkb), wkblen);
+ /* we'll probably get an exception if geom is null */
+ if ( ! geom ) RETURN_NULL();
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, geom);
+
+}
+
+PHP_METHOD(WKBReader, readHEX)
+{
+ GEOSWKBReader *reader;
+ GEOSGeometry *geom;
+ unsigned char* wkb;
+#if PHP_VERSION_ID >= 70000
+ size_t wkblen;
+#else
+ int wkblen;
+#endif
+
+ reader = (GEOSWKBReader*)getRelay(getThis(), WKBReader_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &wkb, &wkblen) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ geom = GEOSWKBReader_readHEX_r(GEOS_G(handle), reader, wkb, wkblen);
+ /* we'll probably get an exception if geom is null */
+ if ( ! geom ) RETURN_NULL();
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, geom);
+
+}
+
+
+/* -- Free functions ------------------------- */
+
+/**
+ * string GEOSVersion()
+ */
+PHP_FUNCTION(GEOSVersion)
+{
+ char *str;
+
+ str = estrdup(GEOSversion());
+ GEOS_PHP_RETURN_STRING(str);
+}
+
+/**
+ * array GEOSPolygonize(GEOSGeometry $geom)
+ *
+ * The returned array contains the following elements:
+ *
+ * - 'rings'
+ * Type: array of GEOSGeometry
+ * Rings that can be formed by the costituent
+ * linework of geometry.
+ * - 'cut_edges' (optional)
+ * Type: array of GEOSGeometry
+ * Edges which are connected at both ends but
+ * which do not form part of polygon.
+ * - 'dangles'
+ * Type: array of GEOSGeometry
+ * Edges which have one or both ends which are
+ * not incident on another edge endpoint
+ * - 'invalid_rings'
+ * Type: array of GEOSGeometry
+ * Edges which form rings which are invalid
+ * (e.g. the component lines contain a self-intersection)
+ *
+ */
+PHP_FUNCTION(GEOSPolygonize)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *rings;
+ GEOSGeometry *cut_edges;
+ GEOSGeometry *dangles;
+ GEOSGeometry *invalid_rings;
+ zval *array_elem;
+ zval *zobj;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+ this = getRelay(zobj, Geometry_ce_ptr);
+
+ rings = GEOSPolygonize_full_r(GEOS_G(handle), this, &cut_edges, &dangles, &invalid_rings);
+ if ( ! rings ) RETURN_NULL(); /* should get an exception first */
+
+ /* return value should be an array */
+ array_init(return_value);
+
+ MAKE_STD_ZVAL(array_elem);
+ array_init(array_elem);
+ dumpGeometry(rings, array_elem);
+ GEOSGeom_destroy_r(GEOS_G(handle), rings);
+ GEOS_PHP_ADD_ASSOC_ZVAL(return_value, "rings", array_elem);
+
+ MAKE_STD_ZVAL(array_elem);
+ array_init(array_elem);
+ dumpGeometry(cut_edges, array_elem);
+ GEOSGeom_destroy_r(GEOS_G(handle), cut_edges);
+ GEOS_PHP_ADD_ASSOC_ZVAL(return_value, "cut_edges", array_elem);
+
+ MAKE_STD_ZVAL(array_elem);
+ array_init(array_elem);
+ dumpGeometry(dangles, array_elem);
+ GEOSGeom_destroy_r(GEOS_G(handle), dangles);
+ GEOS_PHP_ADD_ASSOC_ZVAL(return_value, "dangles", array_elem);
+
+ MAKE_STD_ZVAL(array_elem);
+ array_init(array_elem);
+ dumpGeometry(invalid_rings, array_elem);
+ GEOSGeom_destroy_r(GEOS_G(handle), invalid_rings);
+ GEOS_PHP_ADD_ASSOC_ZVAL(return_value, "invalid_rings", array_elem);
+
+}
+
+/**
+ * array GEOSLineMerge(GEOSGeometry $geom)
+ */
+PHP_FUNCTION(GEOSLineMerge)
+{
+ GEOSGeometry *geom_in;
+ GEOSGeometry *geom_out;
+ zval *zobj;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zobj)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+ geom_in = getRelay(zobj, Geometry_ce_ptr);
+
+ geom_out = GEOSLineMerge_r(GEOS_G(handle), geom_in);
+ if ( ! geom_out ) RETURN_NULL(); /* should get an exception first */
+
+ /* return value should be an array */
+ array_init(return_value);
+ dumpGeometry(geom_out, return_value);
+ GEOSGeom_destroy_r(GEOS_G(handle), geom_out);
+}
+
+/**
+ * GEOSGeometry GEOSSharedPaths(GEOSGeometry $geom1, GEOSGeometry *geom2)
+ */
+#ifdef HAVE_GEOS_SHARED_PATHS
+PHP_FUNCTION(GEOSSharedPaths)
+{
+ GEOSGeometry *geom_in_1;
+ GEOSGeometry *geom_in_2;
+ GEOSGeometry *geom_out;
+ zval *zobj1, *zobj2;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "oo", &zobj1, &zobj2)
+ == FAILURE)
+ {
+ RETURN_NULL();
+ }
+ geom_in_1 = getRelay(zobj1, Geometry_ce_ptr);
+ geom_in_2 = getRelay(zobj2, Geometry_ce_ptr);
+
+ geom_out = GEOSSharedPaths_r(GEOS_G(handle), geom_in_1, geom_in_2);
+ if ( ! geom_out ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, geom_out);
+}
+#endif
+
+/**
+ * GEOSGeometry::delaunayTriangulation([<tolerance>], [<onlyEdges>])
+ *
+ * 'tolerance'
+ * Type: double
+ * snapping tolerance to use for improved robustness
+ * 'onlyEdges'
+ * Type: boolean
+ * if true will return a MULTILINESTRING, otherwise (the default)
+ * it will return a GEOMETRYCOLLECTION containing triangular POLYGONs.
+ */
+#ifdef HAVE_GEOS_DELAUNAY_TRIANGULATION
+PHP_METHOD(Geometry, delaunayTriangulation)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+ double tolerance = 0.0;
+ zend_bool edgeonly = 0;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|db",
+ &tolerance, &edgeonly) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ ret = GEOSDelaunayTriangulation_r(GEOS_G(handle), this, tolerance, edgeonly ? 1 : 0);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+/**
+ * GEOSGeometry::voronoiDiagram([<tolerance>], [<onlyEdges>], [<extent>])
+ *
+ * 'tolerance'
+ * Type: double
+ * snapping tolerance to use for improved robustness
+ * 'onlyEdges'
+ * Type: boolean
+ * if true will return a MULTILINESTRING, otherwise (the default)
+ * it will return a GEOMETRYCOLLECTION containing POLYGONs.
+ * 'extent'
+ * Type: geometry
+ * Clip returned diagram by the extent of the given geometry
+ */
+#ifdef HAVE_GEOS_VORONOI_DIAGRAM
+PHP_METHOD(Geometry, voronoiDiagram)
+{
+ GEOSGeometry *this;
+ GEOSGeometry *ret;
+ zval *zobj = 0;
+ GEOSGeometry *env = 0;
+ double tolerance = 0.0;
+ zend_bool edgeonly = 0;
+
+ this = (GEOSGeometry*)getRelay(getThis(), Geometry_ce_ptr);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|dbo",
+ &tolerance, &edgeonly, &zobj) == FAILURE) {
+ RETURN_NULL();
+ }
+
+ if ( zobj ) env = getRelay(zobj, Geometry_ce_ptr);
+ ret = GEOSVoronoiDiagram_r(GEOS_G(handle), this, env, tolerance, edgeonly ? 1 : 0);
+ if ( ! ret ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ object_init_ex(return_value, Geometry_ce_ptr);
+ setRelay(return_value, ret);
+}
+#endif
+
+/**
+ * bool GEOSRelateMatch(string matrix, string pattern)
+ */
+#ifdef HAVE_GEOS_RELATE_PATTERN_MATCH
+PHP_FUNCTION(GEOSRelateMatch)
+{
+ char* mat = NULL;
+ char* pat = NULL;
+#if PHP_VERSION_ID >= 70000
+ size_t matlen;
+ size_t patlen;
+#else
+ int matlen;
+ int patlen;
+#endif
+ int ret;
+ zend_bool retBool;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss",
+ &mat, &matlen, &pat, &patlen) == FAILURE)
+ {
+ RETURN_NULL();
+ }
+
+ ret = GEOSRelatePatternMatch_r(GEOS_G(handle), mat, pat);
+ if ( ret == 2 ) RETURN_NULL(); /* should get an exception first */
+
+ /* return_value is a zval */
+ retBool = ret;
+ RETURN_BOOL(retBool);
+}
+#endif
+
+/* ------ Initialization / Deinitialization / Meta ------------------ */
+
+/* per-module initialization */
+PHP_MINIT_FUNCTION(geos)
+{
+ zend_class_entry ce;
+
+ /* WKTReader */
+ INIT_CLASS_ENTRY(ce, "GEOSWKTReader", WKTReader_methods);
+ WKTReader_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+ WKTReader_ce_ptr->create_object = WKTReader_create_obj;
+ memcpy(&WKTReader_object_handlers,
+ zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ WKTReader_object_handlers.clone_obj = NULL;
+#if PHP_VERSION_ID >= 70000
+ WKTReader_object_handlers.offset = XtOffsetOf(Proxy, std);
+ WKTReader_object_handlers.free_obj = WKTReader_dtor;
+#endif
+
+ /* WKTWriter */
+ INIT_CLASS_ENTRY(ce, "GEOSWKTWriter", WKTWriter_methods);
+ WKTWriter_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+ WKTWriter_ce_ptr->create_object = WKTWriter_create_obj;
+ memcpy(&WKTWriter_object_handlers,
+ zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ WKTWriter_object_handlers.clone_obj = NULL;
+#if PHP_VERSION_ID >= 70000
+ WKTWriter_object_handlers.offset = XtOffsetOf(Proxy, std);
+ WKTWriter_object_handlers.free_obj = WKTWriter_dtor;
+#endif
+
+ /* Geometry */
+ INIT_CLASS_ENTRY(ce, "GEOSGeometry", Geometry_methods);
+ Geometry_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+ Geometry_ce_ptr->create_object = Geometry_create_obj;
+ memcpy(&Geometry_object_handlers,
+ zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ Geometry_object_handlers.clone_obj = NULL;
+ /* Geometry serialization */
+ Geometry_ce_ptr->serialize = Geometry_serialize;
+ Geometry_ce_ptr->unserialize = Geometry_deserialize;
+#if PHP_VERSION_ID >= 70000
+ Geometry_object_handlers.offset = XtOffsetOf(Proxy, std);
+ Geometry_object_handlers.free_obj = Geometry_dtor;
+#endif
+
+ /* WKBWriter */
+ INIT_CLASS_ENTRY(ce, "GEOSWKBWriter", WKBWriter_methods);
+ WKBWriter_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+ WKBWriter_ce_ptr->create_object = WKBWriter_create_obj;
+ memcpy(&WKBWriter_object_handlers,
+ zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ WKBWriter_object_handlers.clone_obj = NULL;
+#if PHP_VERSION_ID >= 70000
+ WKBWriter_object_handlers.offset = XtOffsetOf(Proxy, std);
+ WKBWriter_object_handlers.free_obj = WKBWriter_dtor;
+#endif
+
+ /* WKBReader */
+ INIT_CLASS_ENTRY(ce, "GEOSWKBReader", WKBReader_methods);
+ WKBReader_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+ WKBReader_ce_ptr->create_object = WKBReader_create_obj;
+ memcpy(&WKBReader_object_handlers,
+ zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ WKBReader_object_handlers.clone_obj = NULL;
+#if PHP_VERSION_ID >= 70000
+ WKBReader_object_handlers.offset = XtOffsetOf(Proxy, std);
+ WKBReader_object_handlers.free_obj = WKBReader_dtor;
+#endif
+
+
+ /* Constants */
+ REGISTER_LONG_CONSTANT("GEOSBUF_CAP_ROUND", GEOSBUF_CAP_ROUND,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSBUF_CAP_FLAT", GEOSBUF_CAP_FLAT,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSBUF_CAP_SQUARE", GEOSBUF_CAP_SQUARE,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSBUF_JOIN_ROUND", GEOSBUF_JOIN_ROUND,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSBUF_JOIN_MITRE", GEOSBUF_JOIN_MITRE,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSBUF_JOIN_BEVEL", GEOSBUF_JOIN_BEVEL,
+ CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("GEOS_POINT", GEOS_POINT,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_LINESTRING", GEOS_LINESTRING,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_LINEARRING", GEOS_LINEARRING,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_POLYGON", GEOS_POLYGON,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_MULTIPOINT", GEOS_MULTIPOINT,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_MULTILINESTRING", GEOS_MULTILINESTRING,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_MULTIPOLYGON", GEOS_MULTIPOLYGON,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOS_GEOMETRYCOLLECTION", GEOS_GEOMETRYCOLLECTION,
+ CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE",
+ GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE,
+ CONST_CS|CONST_PERSISTENT);
+
+# ifdef HAVE_GEOS_PREC_NO_TOPO
+ REGISTER_LONG_CONSTANT("GEOS_PREC_NO_TOPO", GEOS_PREC_NO_TOPO,
+ CONST_CS|CONST_PERSISTENT);
+# endif
+
+# ifdef HAVE_GEOS_PREC_KEEP_COLLAPSED
+ REGISTER_LONG_CONSTANT("GEOS_PREC_KEEP_COLLAPSED", GEOS_PREC_KEEP_COLLAPSED,
+ CONST_CS|CONST_PERSISTENT);
+# endif
+
+ REGISTER_LONG_CONSTANT("GEOSRELATE_BNR_MOD2", GEOSRELATE_BNR_MOD2,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSRELATE_BNR_OGC", GEOSRELATE_BNR_OGC,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSRELATE_BNR_ENDPOINT", GEOSRELATE_BNR_ENDPOINT,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSRELATE_BNR_MULTIVALENT_ENDPOINT",
+ GEOSRELATE_BNR_MULTIVALENT_ENDPOINT,
+ CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GEOSRELATE_BNR_MONOVALENT_ENDPOINT",
+ GEOSRELATE_BNR_MONOVALENT_ENDPOINT,
+ CONST_CS|CONST_PERSISTENT);
+
+ return SUCCESS;
+}
+
+/* per-module shutdown */
+PHP_MSHUTDOWN_FUNCTION(geos)
+{
+ delGeometrySerializer();
+ delGeometryDeserializer();
+ return SUCCESS;
+}
+
+/* per-request initialization */
+PHP_RINIT_FUNCTION(geos)
+{
+ GEOS_G(handle) = initGEOS_r(noticeHandler, errorHandler);
+ return SUCCESS;
+}
+
+/* pre-request destruction */
+PHP_RSHUTDOWN_FUNCTION(geos)
+{
+ finishGEOS_r(GEOS_G(handle));
+ return SUCCESS;
+}
+
+/* global initialization */
+PHP_GINIT_FUNCTION(geos)
+{
+ geos_globals->handle = NULL;
+}
+
+/* module info */
+PHP_MINFO_FUNCTION(geos)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2,
+ "GEOS - Geometry Engine Open Source", "enabled");
+ php_info_print_table_row(2,
+ "Version", PHP_GEOS_VERSION);
+ php_info_print_table_row(2,
+ "GEOS Version", GEOSversion());
+ php_info_print_table_end();
+}
diff --git a/package.xml b/package.xml
new file mode 100644
index 0000000..604ae3c
--- /dev/null
+++ b/package.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<package packagerversion="1.4.11" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>geos</name>
+ <channel>pecl.php.net</channel>
+ <summary>PHP extension for interfacing with the GEOS library</summary>
+ <description>
+ This extension provides a PECL package for the GEOS library.
+ </description>
+ <lead>
+ <name>J Smith</name>
+ <user>jay</user>
+ <email>jay at php.net</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Sandro Santilli</name>
+ <email>strk at kbt.io</email>
+ </lead>
+ <date>2016-01-19</date>
+ <version>
+ <release>0.0.1</release>
+ <api>0.0.1</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="">Mixed</license>
+ <notes>
+- First release
+ </notes>
+ <contents>
+ <dir name="/">
+ <file role="src" name="geos.c"/>
+ <file role="src" name="php_geos.h"/>
+ <file role="src" name="config.m4"/>
+ <file role="doc" name="COPYING"/>
+ <file role="doc" name="CREDITS"/>
+ <file role="doc" name="LGPL-2"/>
+ <file role="doc" name="MIT-LICENSE"/>
+ <dir name="/">
+ <file role="test" name="test.php"/>
+ <file role="test" name="crashme.php"/>
+ </dir>
+ </dir>
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.6.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.4.0b1</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <providesextension>geos</providesextension>
+ <extsrcrelease>
+ <configureoption name="with-geos-config" default="no" prompt="geos-config directory"/>
+ </extsrcrelease>
+ <changelog>
+ <release>
+ <stability><release>stable</release><api>stable</api></stability>
+ <version><release>0.0.1</release><api>0.0.1</api></version>
+ <date>2016-01-19</date>
+ <notes>
+- First release
+ </notes>
+ </release>
+ </changelog>
+</package>
diff --git a/php_geos.h b/php_geos.h
new file mode 100644
index 0000000..5350541
--- /dev/null
+++ b/php_geos.h
@@ -0,0 +1,55 @@
+/***********************************************************************
+ *
+ * GEOS - Geometry Engine Open Source
+ * http://trac.osgeo.org/geos
+ *
+ * Copyright (C) 2010 Sandro Santilli <strk at kbt.io>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ ***********************************************************************/
+
+#ifndef PHP_GEOS_H
+#define PHP_GEOS_H
+
+/* TODO: generate from ./configure ? */
+#define PHP_GEOS_VERSION "0.0"
+#define PHP_GEOS_EXTNAME "geos"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+extern zend_module_entry geos_module_entry;
+#define phpext_geos_ptr &geos_module_entry;
+
+#ifdef ZTS
+#define GEOS_G(v) TSRMG(geos_globals_id, zend_geos_globals *, v)
+#else
+#define GEOS_G(v) (geos_globals.v)
+#endif
+
+ZEND_BEGIN_MODULE_GLOBALS(geos)
+GEOSContextHandle_t handle;
+ZEND_END_MODULE_GLOBALS(geos)
+
+#endif /* PHP_GEOS_H */
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..d456448
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,7 @@
+<phpunit>
+ <testsuites>
+ <testsuite name="PHPT tests">
+ <directory suffix=".phpt">tests</directory>
+ </testsuite>
+ </testsuites>
+</phpunit>
diff --git a/test/crashme.php b/test/crashme.php
new file mode 100644
index 0000000..a0d0a42
--- /dev/null
+++ b/test/crashme.php
@@ -0,0 +1,25 @@
+<?php
+
+#
+# This file contains code attempting to segfault the extension
+#
+# Run with:
+# php -n -d enable_dl=On -d extension_dir=.. test.php
+#
+
+dl("geos.so");
+
+class Point extends GEOSWKTReader {
+ public function __construct()
+ {
+ parent::__construct();
+ }
+ public function test() {
+ return GEOSGeometry::numGeometries();
+ }
+};
+
+$p = new Point();
+$p->test();
+
+
diff --git a/tests/000_General.phpt b/tests/000_General.phpt
new file mode 100644
index 0000000..02ebe39
--- /dev/null
+++ b/tests/000_General.phpt
@@ -0,0 +1,51 @@
+--TEST--
+General tests
+--SKIPIF--
+<?php if (!extension_loaded('geos')) { print "geos extension not loaded\n"; exit(1); } ?>
+--FILE--
+<?php
+
+require './tests/TestHelper.php';
+
+class GeneralTest extends GEOSTest
+{
+ public function testGEOSVersion()
+ {
+ $this->assertContains('-CAPI-', GEOSVersion());
+ }
+
+ public function testConstants()
+ {
+ $this->assertEquals(1, GEOSBUF_CAP_ROUND);
+ $this->assertEquals(2, GEOSBUF_CAP_FLAT);
+ $this->assertEquals(3, GEOSBUF_CAP_SQUARE);
+
+ $this->assertEquals(1, GEOSBUF_JOIN_ROUND);
+ $this->assertEquals(2, GEOSBUF_JOIN_MITRE);
+ $this->assertEquals(3, GEOSBUF_JOIN_BEVEL);
+
+ $this->assertEquals(0, GEOS_POINT);
+ $this->assertEquals(1, GEOS_LINESTRING);
+ $this->assertEquals(2, GEOS_LINEARRING);
+ $this->assertEquals(3, GEOS_POLYGON);
+ $this->assertEquals(4, GEOS_MULTIPOINT);
+ $this->assertEquals(5, GEOS_MULTILINESTRING);
+ $this->assertEquals(6, GEOS_MULTIPOLYGON);
+ $this->assertEquals(7, GEOS_GEOMETRYCOLLECTION);
+
+ $this->assertEquals(1, GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE);
+
+ $this->assertEquals(1, GEOSRELATE_BNR_MOD2);
+ $this->assertEquals(1, GEOSRELATE_BNR_OGC);
+ $this->assertEquals(2, GEOSRELATE_BNR_ENDPOINT);
+ $this->assertEquals(3, GEOSRELATE_BNR_MULTIVALENT_ENDPOINT);
+ $this->assertEquals(4, GEOSRELATE_BNR_MONOVALENT_ENDPOINT);
+ }
+}
+
+GeneralTest::run();
+
+?>
+--EXPECT--
+GeneralTest->testGEOSVersion OK
+GeneralTest->testConstants OK
diff --git a/tests/001_Geometry.phpt b/tests/001_Geometry.phpt
new file mode 100644
index 0000000..ef7a0cb
--- /dev/null
+++ b/tests/001_Geometry.phpt
@@ -0,0 +1,1935 @@
+--TEST--
+Geometry tests
+--SKIPIF--
+<?php if (!extension_loaded('geos')) { print "geos extension not loaded\n"; exit(1); } ?>
+--FILE--
+<?php
+
+require './tests/TestHelper.php';
+
+class GeometryTest extends GEOSTest
+{
+ public function testGeometry_serialization()
+ {
+ $reader = new GEOSWKTReader();
+
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setTrim')) {
+ $writer->setTrim(TRUE);
+ }
+
+ if (method_exists(GEOSWKTWriter::class, 'setOutputDimension')) {
+ $writer->setOutputDimension(3);
+ }
+
+ $g = $reader->read('POINT(6 7 8)');
+ $g->setSRID(54);
+
+ $a = array('geom' => $g, 'name' => 'test geometry');
+ $srl = serialize($a);
+ $a2 = unserialize($srl);
+
+ $this->assertEquals('POINT Z (6 7 8)', $writer->write($a['geom']));
+
+ }
+
+ public function testGeometry_project()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(1 2)');
+ $g2 = $reader->read('POINT(3 4)');
+
+ /* The method only accept lineal geometries */
+ try {
+ $prj = $g->project($g2);
+ $this->assertTrue(FALSE); # this is just to fail if we get here
+ } catch (Exception $e) {
+ $this->assertContains('lineal', $e->getMessage());
+ }
+
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $g2 = $reader->read('POINT(0 0)');
+ $prj = $g->project($g2);
+ $this->assertEquals(0, $prj);
+ $prj = $g->project($g2, TRUE);
+ $this->assertEquals(0, $prj);
+
+ $g2 = $reader->read('POINT(10 0)');
+ $prj = $g->project($g2);
+ $this->assertEquals(10, $prj);
+ $prj = $g->project($g2, TRUE);
+ $this->assertEquals(1, $prj);
+
+ $g2 = $reader->read('POINT(5 0)');
+ $prj = $g->project($g2);
+ $this->assertEquals(5, $prj);
+ $prj = $g->project($g2, TRUE);
+ $this->assertEquals(0.5, $prj);
+
+ $g = $reader->read('MULTILINESTRING((0 0, 10 0),(20 10, 20 20))');
+
+ $g2 = $reader->read('POINT(20 0)');
+ $prj = $g->project($g2);
+ $this->assertEquals(10, $prj);
+ $prj = $g->project($g2, TRUE);
+ $this->assertEquals(0.5, $prj);
+
+ $g2 = $reader->read('POINT(20 5)');
+ $prj = $g->project($g2);
+ $this->assertEquals(10, $prj);
+ $prj = $g->project($g2, TRUE);
+ $this->assertEquals(0.5, $prj);
+
+
+ }
+
+ public function testGeometry_interpolate()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setTrim')) {
+ $writer->setTrim(TRUE);
+ }
+
+ /* The method only accept LineString geometries */
+ $g = $reader->read('POINT(1 2)');
+ try {
+ $prj = $g->interpolate(0);
+ $this->assertTrue(FALSE); # this is just to fail if we get here
+ } catch (Exception $e) {
+ $this->assertContains('LineString', $e->getMessage());
+ }
+
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $prj = $g->interpolate(0);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (0 0)', $writer->write($prj));
+ $prj = $g->interpolate(0, TRUE);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (0 0)', $writer->write($prj));
+
+ $prj = $g->interpolate(5);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (5 0)', $writer->write($prj));
+ $prj = $g->interpolate(0.5, TRUE);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (5 0)', $writer->write($prj));
+
+ /* return closest on longer distance */
+ $prj = $g->interpolate(20);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (10 0)', $writer->write($prj));
+ $prj = $g->interpolate(2, TRUE);
+ $this->assertNotNull($prj);
+ $this->assertEquals('POINT (10 0)', $writer->write($prj));
+
+ }
+
+ public function testGeometry_buffer()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->buffer(0);
+ $this->assertEquals('POLYGON EMPTY', $writer->write($b));
+
+ $b = $g->buffer(10);
+ $this->assertEquals('POLYGON ((10 0, 10 -2, 9 -4, 8 -6, 7 -7, 6 -8, 4 -9, 2 -10, 0 -10, -2 -10, -4 -9, -6 -8, -7 -7, -8 -6, -9 -4, -10 -2, -10 -0, -10 2, -9 4, -8 6, -7 7, -6 8, -4 9, -2 10, -0 10, 2 10, 4 9, 6 8, 7 7, 8 6, 9 4, 10 2, 10 0))', $writer->write($b));
+
+ # One segment per quadrant
+ $b = $g->buffer(10, array('quad_segs' => 1));
+ $this->assertEquals('POLYGON ((10 0, 0 -10, -10 -0, -0 10, 10 0))', $writer->write($b));
+
+ /* End cap styles */
+
+ $g = $reader->read('LINESTRING(0 0, 100 0)');
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 1,
+ 'endcap' => GEOSBUF_CAP_ROUND
+ ));
+ $this->assertEquals('POLYGON ((100 10, 110 0, 100 -10, 0 -10, -10 0, 0 10, 100 10))', $writer->write($b));
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 1,
+ 'endcap' => GEOSBUF_CAP_FLAT
+ ));
+ $this->assertEquals('POLYGON ((100 10, 100 -10, 0 -10, 0 10, 100 10))', $writer->write($b));
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 1,
+ 'endcap' => GEOSBUF_CAP_SQUARE
+ ));
+ $this->assertEquals('POLYGON ((100 10, 110 10, 110 -10, 0 -10, -10 -10, -10 10, 100 10))', $writer->write($b));
+
+ /* Join styles */
+
+ $g = $reader->read('LINESTRING(0 0, 100 0, 100 100)');
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_ROUND
+ ));
+ $this->assertEquals('POLYGON ((90 10, 90 100, 93 107, 100 110, 107 107, 110 100, 110 0, 107 -7, 100 -10, 0 -10, -7 -7, -10 0, -7 7, 0 10, 90 10))', $writer->write($b));
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_BEVEL
+ ));
+ $this->assertEquals('POLYGON ((90 10, 90 100, 93 107, 100 110, 107 107, 110 100, 110 0, 100 -10, 0 -10, -7 -7, -10 0, -7 7, 0 10, 90 10))', $writer->write($b));
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_MITRE
+ ));
+ $this->assertEquals('POLYGON ((90 10, 90 100, 93 107, 100 110, 107 107, 110 100, 110 -10, 0 -10, -7 -7, -10 0, -7 7, 0 10, 90 10))', $writer->write($b));
+
+ $b = $g->buffer(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_MITRE,
+ 'mitre_limit' => 1.0
+ ));
+ $this->assertEquals('POLYGON ((90 10, 90 100, 93 107, 100 110, 107 107, 110 100, 109 -5, 105 -9, 0 -10, -7 -7, -10 0, -7 7, 0 10, 90 10))', $writer->write($b));
+
+ /* Check that elements of the passed style array are not
+ * type-converted (buffer op will need to type-convert
+ * internally)
+ */
+ $ary = array('a' => 1);
+
+ $myStyle = array(
+ 'quad_segs' => "a string",
+ 'join' => "1",
+ 'endcap' => $ary,
+ 'mitre_limit' => 2 /* an int.. */
+ );
+ $this->assertEquals('string', gettype($myStyle['quad_segs']));
+ $this->assertEquals('string', gettype($myStyle['join']));
+ $this->assertEquals('array', gettype($myStyle['endcap']));
+ $this->assertEquals('integer', gettype($myStyle['mitre_limit']));
+ $b = $g->buffer(10, $myStyle);
+ $this->assertEquals('string', gettype($myStyle['quad_segs']));
+ $this->assertEquals('string', gettype($myStyle['join']));
+ $this->assertEquals('array', gettype($myStyle['endcap']));
+ $this->assertEquals('integer', gettype($myStyle['mitre_limit']));
+
+ /* Single-sided buffering */
+
+ $g = $reader->read('LINESTRING(0 0, 100 0)');
+
+ $b = $g->buffer(10, array(
+ 'single_sided' => true
+ ));
+ $this->assertEquals('POLYGON ((100 0, 0 0, 0 10, 100 10, 100 0))', $writer->write($b));
+
+ $b = $g->buffer(-10, array(
+ 'single_sided' => true
+ ));
+ $this->assertEquals('POLYGON ((0 0, 100 0, 100 -10, 0 -10, 0 0))', $writer->write($b));
+ }
+
+ public function testGeometry_offsetCurve()
+ {
+ if (!method_exists(GEOSGeometry::class, 'offsetCurve')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ $writer->setRoundingPrecision(0);
+
+ /* Join styles */
+
+ $g = $reader->read('LINESTRING(0 0, 100 0, 100 100)');
+
+ /* left, round join */
+ $b = $g->offsetCurve(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_ROUND
+ ));
+ $this->assertEquals('LINESTRING (0 10, 90 10, 90 100)', $writer->write($b));
+
+ /* right, round join */
+ $b = $g->offsetCurve(-10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_ROUND
+ ));
+ $this->assertEquals('LINESTRING (110 100, 110 0, 107 -7, 100 -10, 0 -10)', $writer->write($b));
+
+ /* left, bevel join */
+ $b = $g->offsetCurve(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_BEVEL
+ ));
+ $this->assertEquals('LINESTRING (0 10, 90 10, 90 100)', $writer->write($b));
+
+ /* right, bevel join */
+ $b = $g->offsetCurve(-10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_BEVEL
+ ));
+ $this->assertEquals('LINESTRING (110 100, 110 0, 100 -10, 0 -10)', $writer->write($b));
+
+ /* left, mitre join */
+ $b = $g->offsetCurve(10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_MITRE
+ ));
+ $this->assertEquals('LINESTRING (0 10, 90 10, 90 100)', $writer->write($b));
+
+ /* right, mitre join */
+ $b = $g->offsetCurve(-10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_MITRE
+ ));
+ $this->assertEquals('LINESTRING (110 100, 110 -10, 0 -10)', $writer->write($b));
+
+ /* right, mitre join limited */
+ $b = $g->offsetCurve(-10, array(
+ 'quad_segs' => 2,
+ 'join' => GEOSBUF_JOIN_MITRE,
+ 'mitre_limit' => 1.0
+ ));
+ $this->assertEquals('LINESTRING (110 100, 109 -5, 105 -9, 0 -10)', $writer->write($b));
+ }
+
+ public function testGeometry_envelope()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->envelope();
+ $this->assertEquals('POINT (0 0)', $writer->write($b));
+
+ $g = $reader->read('LINESTRING(0 0, 10 10)');
+ $b = $g->envelope();
+ $this->assertEquals('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))', $writer->write($b));
+ }
+
+ public function testGeometry_intersection()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ /* POINT - POINT */
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'POINT (0 0)', $writer->write($gi));
+ $g2 = $reader->read('POINT(1 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+
+ /* POINT - LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('POINT(5 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'POINT (5 0)', $writer->write($gi));
+ $g2 = $reader->read('POINT(12 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+
+ /* LINE - LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'POINT (5 0)', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(5 0, 20 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'LINESTRING (5 0, 10 0)', $writer->write($gi));
+
+ /* LINE - POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'LINESTRING (5 0, 5 10)', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(10 0, 20 0)');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'POINT (10 0)', $writer->write($gi));
+
+ /* POLY - POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POLYGON((5 -5, 5 5, 15 5, 15 -5, 5 -5))');
+ $gi = $g->intersection($g2);
+ $this->assertTrue($gi->equals($reader->read('POLYGON ((10 5, 10 0, 5 0, 5 5, 10 5))')));
+ $g2 = $reader->read('POLYGON((10 0, 20 0, 20 -5, 10 -5, 10 0))');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'POINT (10 0)', $writer->write($gi));
+ $g2 = $reader->read('POLYGON((8 0, 20 0, 20 -5, 10 -5, 8 0))');
+ $gi = $g->intersection($g2);
+ $this->assertEquals( 'LINESTRING (8 0, 10 0)', $writer->write($gi));
+ }
+
+ public function testGeometry_clipByRect()
+ {
+ if (!method_exists(GEOSGeometry::class, 'clipByRect')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setRoundingPrecision(0);
+
+ /* POINT */
+ $g = $reader->read('POINT(0 0)');
+ $gi = $g->clipByRect(-1,-1,1,1);
+ $this->assertEquals( 'POINT (0 0)', $writer->write($gi));
+ $gi = $g->clipByRect(1,1,2,2);
+ $this->assertEquals( 'GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+
+ /* LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $gi = $g->clipByRect(1,-1,2,1);
+ $this->assertEquals( 'LINESTRING (1 0, 2 0)', $writer->write($gi));
+
+ /* POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $gi = $g->clipByRect(1,1,5,5);
+ $this->assertEquals( 'POLYGON ((1 1, 1 5, 5 5, 5 1, 1 1))', $writer->write($gi));
+ $gi = $g->clipByRect(-1,-1,5,5);
+ $this->assertEquals( 'POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))', $writer->write($gi));
+ }
+
+ public function testGeometry_convexHull()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->convexHull();
+ $this->assertEquals('POINT (0 0)', $writer->write($b));
+
+ $g = $reader->read('LINESTRING(0 0, 10 10)');
+ $b = $g->convexHull();
+ $this->assertEquals('LINESTRING (0 0, 10 10)', $writer->write($b));
+
+ $g = $reader->read('POLYGON((0 0, 0 10, 5 5, 10 10, 10 0, 0 0))');
+ $b = $g->convexHull();
+ $this->assertEquals('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))', $writer->write($b));
+ }
+
+ public function testGeometry_difference()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ /* POINT - POINT */
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals( 'GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+ $g2 = $reader->read('POINT(1 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals('POINT (0 0)', $writer->write($gi));
+
+ /* LINE - POINT */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('POINT(5 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals('LINESTRING (0 0, 10 0)', $writer->write($gi));
+
+ /* POINT - LINE */
+ $g = $reader->read('POINT(5 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(0 1, 10 1)');
+ $gi = $g->difference($g2);
+ $this->assertEquals( 'POINT (5 0)', $writer->write($gi));
+
+ /* LINE - LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->difference($g2);
+ $this->assertEquals( 'MULTILINESTRING ((0 0, 5 0), (5 0, 10 0))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(5 0, 20 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals( 'LINESTRING (0 0, 5 0)', $writer->write($gi));
+
+ /* POLY - LINE */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->difference($g2);
+ $this->assertEquals('POLYGON ((5 0, 0 0, 0 10, 5 10, 10 10, 10 0, 5 0))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(10 0, 20 0)');
+ $gi = $g->difference($g2);
+ $this->assertEquals('POLYGON ((10 0, 0 0, 0 10, 10 10, 10 0))', $writer->write($gi));
+
+ /* POLY - POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POLYGON((5 -5, 5 5, 15 5, 15 -5, 5 -5))');
+ $gi = $g->difference($g2);
+ $this->assertEquals('POLYGON ((5 0, 0 0, 0 10, 10 10, 10 5, 5 5, 5 0))', $writer->write($gi));
+ }
+
+ public function testGeometry_symdifference()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ /* POINT - POINT */
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals( 'GEOMETRYCOLLECTION EMPTY', $writer->write($gi));
+ $g2 = $reader->read('POINT(1 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals( 'MULTIPOINT (0 0, 1 0)', $writer->write($gi));
+
+ /* LINE - POINT */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('POINT(5 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('LINESTRING (0 0, 10 0)', $writer->write($gi));
+
+ /* POINT - LINE */
+ $g = $reader->read('POINT(5 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals( 'LINESTRING (0 0, 10 0)', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(0 1, 10 1)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (POINT (5 0), LINESTRING (0 1, 10 1))', $writer->write($gi));
+
+ /* LINE - LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('MULTILINESTRING ((0 0, 5 0), (5 0, 10 0), (5 -10, 5 0), (5 0, 5 10))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(5 0, 20 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('MULTILINESTRING ((0 0, 5 0), (10 0, 20 0))', $writer->write($gi));
+
+ /* POLY - LINE */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (LINESTRING (5 -10, 5 0), POLYGON ((5 0, 0 0, 0 10, 5 10, 10 10, 10 0, 5 0)))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(10 0, 20 0)');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (LINESTRING (10 0, 20 0), POLYGON ((10 0, 0 0, 0 10, 10 10, 10 0)))', $writer->write($gi));
+
+ /* POLY - POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POLYGON((5 -5, 5 5, 15 5, 15 -5, 5 -5))');
+ $gi = $g->symDifference($g2);
+ $this->assertEquals('MULTIPOLYGON (((5 0, 0 0, 0 10, 10 10, 10 5, 5 5, 5 0)), ((5 0, 10 0, 10 5, 15 5, 15 -5, 5 -5, 5 0)))', $writer->write($gi));
+ }
+
+ public function testGeometry_boundary()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->boundary();
+ $this->assertEquals('GEOMETRYCOLLECTION EMPTY', $writer->write($b));
+
+ $g = $reader->read('LINESTRING(0 0, 10 10)');
+ $b = $g->boundary();
+ $this->assertEquals('MULTIPOINT (0 0, 10 10)', $writer->write($b));
+
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),( 5 5, 5 6, 6 6, 6 5, 5 5))');
+ $b = $g->boundary();
+ $this->assertEquals('MULTILINESTRING ((0 0, 10 0, 10 10, 0 10, 0 0), (5 5, 5 6, 6 6, 6 5, 5 5))', $writer->write($b));
+ }
+
+ public function testGeometry_union()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ /* POINT - POINT */
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals( 'POINT (0 0)', $writer->write($gi));
+ $g2 = $reader->read('POINT(1 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals( 'MULTIPOINT (0 0, 1 0)', $writer->write($gi));
+
+ /* LINE - POINT */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('POINT(5 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals('LINESTRING (0 0, 10 0)', $writer->write($gi));
+
+ /* POINT - LINE */
+ $g = $reader->read('POINT(5 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals( 'LINESTRING (0 0, 10 0)', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(0 1, 10 1)');
+ $gi = $g->union($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (POINT (5 0), LINESTRING (0 1, 10 1))', $writer->write($gi));
+
+ /* LINE - LINE */
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->union($g2);
+ $this->assertEquals('MULTILINESTRING ((0 0, 5 0), (5 0, 10 0), (5 -10, 5 0), (5 0, 5 10))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(5 0, 20 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals('MULTILINESTRING ((0 0, 5 0), (5 0, 10 0), (10 0, 20 0))', $writer->write($gi));
+
+ /* POLY - LINE */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('LINESTRING(5 -10, 5 10)');
+ $gi = $g->union($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (LINESTRING (5 -10, 5 0), POLYGON ((5 0, 0 0, 0 10, 5 10, 10 10, 10 0, 5 0)))', $writer->write($gi));
+ $g2 = $reader->read('LINESTRING(10 0, 20 0)');
+ $gi = $g->union($g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (LINESTRING (10 0, 20 0), POLYGON ((10 0, 0 0, 0 10, 10 10, 10 0)))', $writer->write($gi));
+
+ /* POLY - POLY */
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POLYGON((5 -5, 5 5, 15 5, 15 -5, 5 -5))');
+ $gi = $g->union($g2);
+ $this->assertEquals('POLYGON ((5 0, 0 0, 0 10, 10 10, 10 5, 15 5, 15 -5, 5 -5, 5 0))', $writer->write($gi));
+ }
+
+ public function testGeometry_unaryunion()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('MULTIPOLYGON(
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11)),
+ ((0 0, 11 0, 11 11, 0 11, 0 0))
+ ))');
+
+ $gu = $g->union();
+ $this->assertEquals('POLYGON ((1 0, 0 0, 0 1, 0 11, 10 11, 10 14, 14 14, 14 10, 11 10, 11 0, 1 0), (11 11, 12 11, 12 12, 11 12, 11 11))', $writer->write($gu));
+
+ $g = $reader->read('MULTILINESTRING(
+ (0 0, 1 0, 1 1, 0 1, 0 0),
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11),
+ (0 0, 11 0, 11 11, 0 11, 0 0)
+ )');
+
+ $gu = $g->union();
+ $this->assertEquals('MULTILINESTRING ((0 0, 1 0), (1 0, 1 1, 0 1), (0 1, 0 0), (1 0, 11 0, 11 10), (11 10, 11 11), (11 11, 10 11), (10 11, 0 11, 0 1), (11 11, 11 12, 12 12, 12 11, 11 11), (10 10, 10 11), (10 11, 10 14, 14 14, 14 10, 11 10), (11 10, 10 10))', $writer->write($gu));
+
+ $g = $reader->read('MULTIPOINT(
+ 0 0, 1 0, 1 1, 0 1, 0 0,
+ 10 10, 10 14, 14 14, 14 10, 10 10,
+ 11 11, 11 12, 12 12, 12 11, 11 11,
+ 0 0, 11 0, 11 11, 0 11, 0 0
+ )');
+
+ $gu = $g->union();
+ $this->assertEquals('MULTIPOINT (0 0, 0 1, 0 11, 1 0, 1 1, 10 10, 10 14, 11 0, 11 11, 11 12, 12 11, 12 12, 14 10, 14 14)', $writer->write($gu));
+
+ $g = $reader->read('GEOMETRYCOLLECTION(
+ MULTIPOLYGON(
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11)),
+ ((0 0, 11 0, 11 11, 0 11, 0 0))
+ ),
+ MULTILINESTRING(
+ (0 0, 1 0, 1 1, 0 1, 0 0),
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11),
+ (0 0, 11 0, 11 11, 0 11, 0 0),(-8 8, -8 6)
+ ),
+ MULTIPOINT(
+ 0 0, 1 0, 1 1, 0 1, 0 0,
+ 10 10, 10 14, 14 14, 14 10, 10 10,
+ 11 11, 11 12, 12 12, 12 11, 11 11,
+ 0 0, 11 0, 11 11, 0 11, 0 0, -10 -10
+ ))');
+
+ $gu = $g->union();
+ $this->assertEquals('GEOMETRYCOLLECTION (POINT (-10 -10), LINESTRING (-8 8, -8 6), POLYGON ((1 0, 0 0, 0 1, 0 11, 10 11, 10 14, 14 14, 14 10, 11 10, 11 0, 1 0), (11 12, 11 11, 12 11, 12 12, 11 12)))', $writer->write($gu));
+ }
+
+ public function testGeometry_pointOnSurface()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->pointOnSurface();
+ $this->assertEquals('POINT (0 0)', $writer->write($b));
+
+ $g = $reader->read('LINESTRING(0 0, 5 5, 10 10)');
+ $b = $g->pointOnSurface();
+ $this->assertEquals('POINT (5 5)', $writer->write($b));
+
+ $g = $reader->read('POLYGON((0 0, 0 10, 5 5, 10 10, 10 0, 0 0))');
+ $b = $g->pointOnSurface();
+ $this->assertEquals('POINT (1 8)', $writer->write($b));
+ }
+
+ public function testGeometry_centroid()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $b = $g->centroid();
+ $this->assertEquals('POINT (0 0)', $writer->write($b));
+
+ $g = $reader->read('LINESTRING(0 0, 10 10)');
+ $b = $g->centroid();
+ $this->assertEquals('POINT (5 5)', $writer->write($b));
+
+ $g = $reader->read('POLYGON((0 0, 0 10, 5 5, 10 10, 10 0, 0 0))');
+ $b = $g->centroid();
+ $this->assertEquals('POINT (5 4)', $writer->write($b));
+ }
+
+ public function testGeometry_relate()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+ $ret = $g->relate($g2);
+ $this->assertEquals('0FFFFFFF2', $ret);
+ $ret = $g->relate($g2, '0FFFFFFF2');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, '0*******T');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, '0*******1');
+ $this->assertEquals(FALSE, $ret);
+
+ $g = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(1 0)');
+ $ret = $g->relate($g2);
+ $this->assertEquals('FF0FFF0F2', $ret);
+ $ret = $g->relate($g2, 'FF0FFF0F2');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, 'F*******2');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, 'T*******2');
+ $this->assertEquals(FALSE, $ret);
+
+ $g = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POINT(1 0)');
+ $ret = $g->relate($g2);
+ $this->assertEquals('FF20F1FF2', $ret);
+ $ret = $g->relate($g2, 'FF20F1FF2');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, 'F****T**T');
+ $this->assertEquals(TRUE, $ret);
+ $ret = $g->relate($g2, 'T*******2');
+ $this->assertEquals(FALSE, $ret);
+
+ }
+
+ public function testGeometry_relateBoundaryNodeRule()
+ {
+ if (!method_exists(GEOSGeometry::class, 'relateBoundaryNodeRule')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('LINESTRING(0 0, 2 4, 5 5, 0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+
+ $ret = $g->relateBoundaryNodeRule($g2, GEOSRELATE_BNR_OGC);
+ $this->assertEquals('0F1FFFFF2', $ret);
+
+ $ret = $g->relateBoundaryNodeRule($g2, GEOSRELATE_BNR_ENDPOINT);
+ $this->assertEquals('FF10FFFF2', $ret);
+
+ }
+
+ public function testGeometry_polygonize()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('GEOMETRYCOLLECTION(
+ LINESTRING(0 0, 10 10),
+ LINESTRING(185 221, 100 100),
+ LINESTRING(185 221, 88 275, 180 316),
+ LINESTRING(185 221, 292 281, 180 316),
+ LINESTRING(189 98, 83 187, 185 221),
+ LINESTRING(189 98, 325 168, 185 221))
+ )');
+
+ $g2 = $reader->read('POINT(0 0)');
+
+ $ret = GEOSPolygonize($g);
+
+ $this->assertEquals('array', gettype($ret));
+ $this->assertEquals('array', gettype($ret['rings']));
+ $this->assertEquals('array', gettype($ret['cut_edges']));
+ $this->assertEquals('array', gettype($ret['dangles']));
+ $this->assertEquals('array', gettype($ret['invalid_rings']));
+
+ $this->assertEquals(2, count($ret['rings']));
+ $this->assertEquals('POLYGON ((185 221, 88 275, 180 316, 292 281, 185 221))', # JTS-confirmed!
+ $writer->write($ret['rings'][0]));
+ $this->assertEquals('POLYGON ((189 98, 83 187, 185 221, 325 168, 189 98))', # JTS-confirmed !
+ $writer->write($ret['rings'][1]));
+
+ $this->assertEquals(0, count($ret['cut_edges']));
+
+ $this->assertEquals(0, count($ret['invalid_rings']));
+
+ /*
+ * FIXME: the duplicated dangle (0 0, 10 10) is unexpected
+ */
+
+ $this->assertEquals(2, count($ret['dangles']));
+ $this->assertEquals('LINESTRING (185 221, 100 100)', # JTS-confirmed !
+ $writer->write($ret['dangles'][0]));
+ $this->assertEquals('LINESTRING (0 0, 10 10)', # JTS-confirmed !
+ $writer->write($ret['dangles'][1]));
+
+
+ ###########################################################
+
+ $g = $g->union($g2); /* Now make sure linestrings are noded */
+
+ $ret = GEOSPolygonize($g);
+
+ $this->assertEquals('array', gettype($ret));
+ $this->assertEquals('array', gettype($ret['rings']));
+ $this->assertEquals('array', gettype($ret['cut_edges']));
+ $this->assertEquals('array', gettype($ret['dangles']));
+ $this->assertEquals('array', gettype($ret['invalid_rings']));
+
+ $this->assertEquals(2, count($ret['dangles']));
+ $this->assertEquals('LINESTRING (132 146, 100 100)', $writer->write($ret['dangles'][0]));
+ $this->assertEquals('LINESTRING (0 0, 10 10)', $writer->write($ret['dangles'][1]));
+
+ $this->assertEquals(0, count($ret['invalid_rings']));
+
+ // TODO: test a polygonize run with cut lines and invalid_rings
+
+ }
+
+ public function testGeometry_lineMerge()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('MULTILINESTRING(
+ (0 0, 10 10),
+ (10 10, 10 0),
+ (5 0, 10 0),
+ (5 -5, 5 0)
+ )');
+
+ $ret = GEOSLineMerge($g);
+
+ $this->assertEquals('array', gettype($ret));
+ $this->assertEquals('1', count($ret));
+
+ $this->assertEquals('LINESTRING (0 0, 10 10, 10 0, 5 0, 5 -5)', $writer->write($ret[0]));
+
+ }
+
+ public function testGeometry_sharedPaths()
+ {
+ if (!function_exists('GEOSSharedPaths')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ /* LINE - LINE */
+ $g1 = $reader->read('LINESTRING(0 0, 50 0)');
+ $g2 = $reader->read('MULTILINESTRING((5 0, 15 0),(40 0, 30 0))');
+ $gs = GEOSSharedPaths($g1, $g2);
+ $this->assertEquals('GEOMETRYCOLLECTION (MULTILINESTRING ((5 0, 15 0)), MULTILINESTRING ((30 0, 40 0)))', $writer->write($gs));
+ }
+
+ public function testGeometry_simplify()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('LINESTRING(0 0, 3 4, 5 10, 10 0, 10 9, 5 11, 0 9)');
+ $gs = $g->simplify(2);
+ $this->assertEquals( 'LINESTRING (0 0, 5 10, 10 0, 10 9, 0 9)', $writer->write($gs));
+ $gs = $g->simplify(2, TRUE);
+ $this->assertEquals( 'LINESTRING (0 0, 5 10, 10 0, 10 9, 5 11, 0 9)', $writer->write($gs));
+ }
+
+ public function testGeometry_extractUniquePoints()
+ {
+ if (!method_exists(GEOSGeometry::class, 'extractUniquePoints')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read(
+ 'GEOMETRYCOLLECTION (
+ MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ ),
+ POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)),
+ LINESTRING (0 0, 2 3),
+ MULTIPOINT (0 0, 2 3),
+ POINT (9 0),
+ POINT(1 0)),
+ LINESTRING EMPTY
+ ');
+
+ $gs = $g->extractUniquePoints();
+ if ( ! $gs ) RETURN_NULL(); /* should get an exception before */
+
+ $this->assertEquals('MULTIPOINT (0 0, 1 0, 1 1, 0 1, 10 10, 10 14, 14 14, 14 10, 11 11, 11 12, 12 12, 12 11, 2 3, 3 4, 9 0)', $writer->write($gs));
+ }
+
+ public function testGeometry_relationalOps()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g1 = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('POINT(0 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) ); /* no bounds, can't touch */
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertTrue( $g1->within($g2) );
+ $this->assertTrue( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertTrue( $g1->equals($g2) );
+ $this->assertTrue( $g1->equalsExact($g2) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertTrue( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertTrue( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('POINT(0 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertTrue( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 10) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertTrue( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('POINT(5 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertTrue( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 10) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertTrue( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('LINESTRING(5 -5, 5 5)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertTrue( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertFalse( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('LINESTRING(5 0, 15 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertTrue( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertFalse( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('LINESTRING(0 0, 5 0, 10 0)');
+ $g2 = $reader->read('LINESTRING(0 0, 10 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertTrue( $g1->within($g2) );
+ $this->assertTrue( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertTrue( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertTrue( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertTrue( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POLYGON((5 -5, 5 5, 15 5, 15 -5, 5 -5))');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertTrue( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertFalse( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POINT(15 15)');
+
+ $this->assertTrue( $g1->disjoint($g2) );
+ $this->assertFalse( $g1->touches($g2) );
+ $this->assertFalse( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertFalse( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertFalse( $g1->coveredBy($g2) );
+ }
+
+ $g1 = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))');
+ $g2 = $reader->read('POINT(5 0)');
+
+ $this->assertFalse( $g1->disjoint($g2) );
+ $this->assertTrue( $g1->touches($g2) );
+ $this->assertTrue( $g1->intersects($g2) );
+ $this->assertFalse( $g1->crosses($g2) );
+ $this->assertFalse( $g1->within($g2) );
+ $this->assertFalse( $g1->contains($g2) );
+ $this->assertFalse( $g1->overlaps($g2) );
+ $this->assertFalse( $g1->equals($g2) );
+ $this->assertFalse( $g1->equalsExact($g2, 1) );
+
+ if (method_exists(GEOSGeometry::class, 'covers')) {
+ $this->assertTrue( $g1->covers($g2) );
+ }
+
+ if (method_exists(GEOSGeometry::class, 'coveredBy')) {
+ $this->assertFalse( $g1->coveredBy($g2) );
+ }
+
+ }
+
+ public function testGeometry_isEmpty()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g1 = $reader->read('POINT(0 0)');
+ $this->assertFalse( $g1->isEmpty() );
+
+ $g1 = $reader->read('POINT EMPTY');
+ $this->assertTrue( $g1->isEmpty() );
+
+ $g1 = $reader->read('LINESTRING(0 0, 10 0)');
+ $this->assertFalse( $g1->isEmpty() );
+
+ $g1 = $reader->read('LINESTRING EMPTY');
+ $this->assertTrue( $g1->isEmpty() );
+
+ $g1 = $reader->read('POLYGON((0 0, 10 0, 10 10, 0 0))');
+ $this->assertFalse( $g1->isEmpty() );
+
+ $g1 = $reader->read('POLYGON EMPTY');
+ $this->assertTrue( $g1->isEmpty() );
+
+ $g1 = $reader->read('GEOMETRYCOLLECTION(POINT(0 0))');
+ $this->assertFalse( $g1->isEmpty() );
+
+ $g1 = $reader->read('GEOMETRYCOLLECTION EMPTY');
+ $this->assertTrue( $g1->isEmpty() );
+ }
+
+ public function testGeometry_checkValidity()
+ {
+ if (!method_exists(GEOSGeometry::class, 'checkValidity')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POINT(0 0)');
+ $val = $g->checkValidity();
+ $this->assertType( 'array', $val );
+ $this->assertTrue( $val['valid'] );
+ $this->assertFalse( isset($val['reason']) );
+ $this->assertFalse( isset($val['location']) );
+
+ $g = $reader->read('POINT(0 NaN)');
+ $val = $g->checkValidity();
+ $this->assertType( 'array', $val );
+ $this->assertFalse( $val['valid'] );
+ $this->assertEquals( 'Invalid Coordinate', $val['reason'] );
+ $this->assertEquals( 'POINT (0 nan)',
+ $writer->write($val['location']) );
+
+ $g = $reader->read(
+ 'POLYGON((0 0, -10 10, 10 10, 0 0, 4 5, -4 5, 0 0)))'
+ );
+ $val = $g->checkValidity();
+ $this->assertType( 'array', $val );
+ $this->assertFalse( $val['valid'] );
+ $this->assertEquals( 'Ring Self-intersection', $val['reason'] );
+ $this->assertEquals( 'POINT (0 0)',
+ $writer->write($val['location']) );
+
+ $g = $reader->read(
+ 'POLYGON((0 0, -10 10, 10 10, 0 0, 4 5, -4 5, 0 0)))'
+ );
+ $flags = GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE;
+ $val = $g->checkValidity($flags);
+ $this->assertType( 'array', $val );
+ $this->assertTrue( $val['valid'] );
+ $this->assertFalse( isset($val['reason']) );
+ $this->assertFalse( isset($val['location']) );
+ }
+
+ public function testGeometry_isSimple()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertTrue( $g->isSimple() );
+
+ $g = $reader->read('LINESTRING(0 0, 10 0)');
+ $this->assertTrue( $g->isSimple() );
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 5, 5 -5)');
+ $this->assertFalse( $g->isSimple() );
+ }
+
+ public function testGeometry_isRing()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertFalse( $g->isRing() );
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 5, 5 -5)');
+ $this->assertFalse( $g->isRing() );
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 5, 0 0)');
+ $this->assertTrue( $g->isRing() );
+ }
+
+ public function testGeometry_hasZ()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertFalse( $g->hasZ() );
+
+ $g = $reader->read('POINT(0 0 0)');
+ $this->assertTrue( $g->hasZ() );
+
+ }
+
+ public function testGeometry_isClosed()
+ {
+ if (!method_exists(GEOSGeometry::class, 'isClosed')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ try {
+ $this->assertFalse( $g->isClosed() );
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('LineString', $e->getMessage());
+ }
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 5, 5 -5)');
+ $this->assertFalse( $g->isClosed() );
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 5, 0 0)');
+ $this->assertTrue( $g->isClosed() );
+ }
+
+ public function testGeometry_type()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals('Point', $g->typeName());
+ $this->assertEquals(GEOS_POINT, $g->typeId());
+
+ $g = $reader->read('MULTIPOINT (0 1, 2 3)');
+ $this->assertEquals('MultiPoint', $g->typeName());
+ $this->assertEquals(GEOS_MULTIPOINT, $g->typeId());
+
+ $g = $reader->read('LINESTRING (0 0, 2 3)');
+ $this->assertEquals('LineString', $g->typeName());
+ $this->assertEquals(GEOS_LINESTRING, $g->typeId());
+
+ $g = $reader->read('MULTILINESTRING ((0 1, 2 3), (10 10, 3 4))');
+ $this->assertEquals('MultiLineString', $g->typeName());
+ $this->assertEquals(GEOS_MULTILINESTRING, $g->typeId());
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals('Polygon', $g->typeName());
+ $this->assertEquals(GEOS_POLYGON, $g->typeId());
+
+ $g = $reader->read('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((10 10, 10 14, 14 14, 14 10, 10 10), (11 11, 11 12, 12 12, 12 11, 11 11)))');
+ $this->assertEquals('MultiPolygon', $g->typeName());
+ $this->assertEquals(GEOS_MULTIPOLYGON, $g->typeId());
+
+ $g = $reader->read('GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((10 10, 10 14, 14 14, 14 10, 10 10), (11 11, 11 12, 12 12, 12 11, 11 11))), POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)), MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)), LINESTRING (0 0, 2 3), MULTIPOINT (0 0, 2 3), POINT (9 0))');
+ $this->assertEquals('GeometryCollection', $g->typeName());
+ $this->assertEquals(GEOS_GEOMETRYCOLLECTION, $g->typeId());
+ }
+
+ public function testGeometry_srid()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals(0, $g->getSRID());
+ $g->setSRID(2);
+ $this->assertEquals(2, $g->getSRID());
+ }
+
+ public function testGeometry_numGeometries()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals(1, $g->numGeometries());
+
+ $g = $reader->read('MULTIPOINT (0 1, 2 3)');
+ $this->assertEquals(2, $g->numGeometries());
+
+ $g = $reader->read('LINESTRING (0 0, 2 3)');
+ $this->assertEquals(1, $g->numGeometries());
+
+ $g = $reader->read('MULTILINESTRING ((0 1, 2 3), (10 10, 3 4))');
+ $this->assertEquals(2, $g->numGeometries());
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals(1, $g->numGeometries());
+
+ $g = $reader->read('MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11)))');
+ $this->assertEquals(2, $g->numGeometries());
+
+ $g = $reader->read('GEOMETRYCOLLECTION (
+ MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ ),
+ POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)),
+ LINESTRING (0 0, 2 3),
+ MULTIPOINT (0 0, 2 3),
+ POINT (9 0))');
+ $this->assertEquals(6, $g->numGeometries());
+ }
+
+ public function testGeometry_geometryN()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $c = $g->geometryN(0);
+ $this->assertTrue( $g->equalsExact($c) );
+
+ $g = $reader->read('MULTIPOINT (0 1, 2 3)');
+ $this->assertEquals($reader->read('POINT(0 1)'), $g->geometryN(0));
+ $this->assertEquals($reader->read('POINT(2 3)'), $g->geometryN(1));
+
+ $c = $g->geometryN(2);
+ $this->assertNull( $c );
+
+ }
+
+ public function testGeometry_numInteriorRings()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals(0, $g->numInteriorRings());
+
+ $g = $reader->read('POLYGON (
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))');
+ $this->assertEquals(1, $g->numInteriorRings());
+
+ $g = $reader->read('POLYGON (
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11),
+ (13 11, 13 12, 13.5 12, 13.5 11, 13 11))');
+ $this->assertEquals(2, $g->numInteriorRings());
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->numInteriorRings();
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'Polygon', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_numPoints()
+ {
+ if (!method_exists(GEOSGeometry::class, 'numPoints')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('LINESTRING (0 0, 1 0, 1 1, 0 1)');
+ $this->assertEquals(4, $g->numPoints());
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->numPoints();
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'LineString', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_getXY()
+ {
+ if (!method_exists(GEOSGeometry::class, 'getX')) {
+ return;
+ }
+
+ if (!method_exists(GEOSGeometry::class, 'getY')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT (1 2)');
+ $this->assertEquals(1, $g->getX());
+ $this->assertEquals(2, $g->getY());
+
+ $g = $reader->read('LINESTRING (0 0, 1 1)');
+ try {
+ $g->getX();
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'Point', $e->getMessage() );
+ }
+
+ try {
+ $g->getY();
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'Point', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_interiorRingN()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POLYGON (
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))');
+ $r = $g->interiorRingN(0);
+ $this->assertEquals('LINEARRING (11 11, 11 12, 12 12, 12 11, 11 11)',
+ $writer->write($r) );
+
+ $g = $reader->read('POLYGON (
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11),
+ (13 11, 13 12, 13.5 12, 13.5 11, 13 11))');
+ $r = $g->interiorRingN(0);
+ $this->assertEquals('LINEARRING (11 11, 11 12, 12 12, 12 11, 11 11)',
+ $writer->write($r) );
+ $r = $g->interiorRingN(1);
+ $this->assertEquals('LINEARRING (13 11, 13 12, 14 12, 14 11, 13 11)',
+ $writer->write($r) );
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertNull($g->interiorRingN(0));
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->interiorRingN(0);
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'Polygon', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_exteriorRing()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POLYGON (
+ (10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))');
+ $r = $g->exteriorRing();
+ $this->assertEquals('LINEARRING (10 10, 10 14, 14 14, 14 10, 10 10)',
+ $writer->write($r) );
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->exteriorRing(0);
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'Polygon', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_numCoordinates()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals(1, $g->numCoordinates());
+
+ $g = $reader->read('MULTIPOINT (0 1, 2 3)');
+ $this->assertEquals(2, $g->numCoordinates());
+
+ $g = $reader->read('LINESTRING (0 0, 2 3)');
+ $this->assertEquals(2, $g->numCoordinates());
+
+ $g = $reader->read('MULTILINESTRING ((0 1, 2 3), (10 10, 3 4))');
+ $this->assertEquals(4, $g->numCoordinates());
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals(5, $g->numCoordinates());
+
+ $g = $reader->read('MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11)))');
+ $this->assertEquals(15, $g->numCoordinates());
+
+ $g = $reader->read('GEOMETRYCOLLECTION (
+ MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ ),
+ POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)),
+ LINESTRING (0 0, 2 3),
+ MULTIPOINT (0 0, 2 3),
+ POINT (9 0))');
+ $this->assertEquals(29, $g->numCoordinates());
+ }
+
+ public function testGeometry_dimension()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals(0, $g->dimension());
+
+ $g = $reader->read('MULTIPOINT (0 1, 2 3)');
+ $this->assertEquals(0, $g->dimension());
+
+ $g = $reader->read('LINESTRING (0 0, 2 3)');
+ $this->assertEquals(1, $g->dimension());
+
+ $g = $reader->read('MULTILINESTRING ((0 1, 2 3), (10 10, 3 4))');
+ $this->assertEquals(1, $g->dimension());
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals(2, $g->dimension());
+
+ $g = $reader->read('MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11)))');
+ $this->assertEquals(2, $g->dimension());
+
+ $g = $reader->read('GEOMETRYCOLLECTION (
+ MULTIPOLYGON (
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ ),
+ POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)),
+ LINESTRING (0 0, 2 3),
+ MULTIPOINT (0 0, 2 3),
+ POINT (9 0))');
+ $this->assertEquals(2, $g->dimension());
+ }
+
+ public function testGeometry_coordinateDimension()
+ {
+ if (!method_exists(GEOSGeometry::class, 'coordinateDimension')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(0 0)');
+ $this->assertEquals(2, $g->coordinateDimension());
+
+ $g = $reader->read('POINT(0 0 0)');
+ $this->assertEquals(3, $g->coordinateDimension());
+
+ }
+
+ public function testGeometry_pointN()
+ {
+ if (!method_exists(GEOSGeometry::class, 'pointN')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setRoundingPrecision(0);
+
+ $g = $reader->read('LINESTRING (10 10, 10 14, 14 14, 14 10)');
+ $this->assertEquals('POINT (10 10)', $writer->write($g->pointN(0)) );
+ $this->assertEquals('POINT (10 14)', $writer->write($g->pointN(1)) );
+ $this->assertEquals('POINT (14 14)', $writer->write($g->pointN(2)) );
+ $this->assertEquals('POINT (14 10)', $writer->write($g->pointN(3)) );
+ $this->assertNull( $g->pointN(4) );
+
+ $g = $reader->read('LINEARRING (11 11, 11 12, 12 11, 11 11)');
+ $this->assertEquals('POINT (11 11)', $writer->write($g->pointN(0)) );
+ $this->assertEquals('POINT (11 12)', $writer->write($g->pointN(1)) );
+ $this->assertEquals('POINT (12 11)', $writer->write($g->pointN(2)) );
+ $this->assertEquals('POINT (11 11)', $writer->write($g->pointN(3)) );
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->pointN(0);
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'LineString', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_startendPoint()
+ {
+ if (!method_exists(GEOSGeometry::class, 'startPoint')) {
+ return;
+ }
+
+ if (!method_exists(GEOSGeometry::class, 'endPoint')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('LINESTRING (10 10, 10 14, 14 14, 14 10)');
+ $this->assertEquals('POINT (10 10)', $writer->write($g->startPoint()) );
+ $this->assertEquals('POINT (14 10)', $writer->write($g->endPoint()) );
+ $this->assertNull( $g->pointN(4) );
+
+ $g = $reader->read('LINEARRING (11 11, 11 12, 12 11, 11 11)');
+ $this->assertEquals('POINT (11 11)', $writer->write($g->startPoint()) );
+ $this->assertEquals('POINT (11 11)', $writer->write($g->endPoint()) );
+
+ $g = $reader->read('POINT (0 0)');
+ try {
+ $g->pointN(0);
+ $this->assertTrue( FALSE );
+ } catch (Exception $e) {
+ $this->assertContains( 'LineString', $e->getMessage() );
+ }
+
+ }
+
+ public function testGeometry_area()
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ $writer->setRoundingPrecision(0);
+ }
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals( 1.0, $g->area() );
+
+ $g = $reader->read('POINT (0 0)');
+ $this->assertEquals( 0.0, $g->area() );
+
+ $g = $reader->read('LINESTRING (0 0 , 10 0)');
+ $this->assertEquals( 0.0, $g->area() );
+
+ }
+
+ public function testGeometry_length()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertEquals( 4.0, $g->length() );
+
+ $g = $reader->read('POINT (0 0)');
+ $this->assertEquals( 0.0, $g->length() );
+
+ $g = $reader->read('LINESTRING (0 0 , 10 0)');
+ $this->assertEquals( 10.0, $g->length() );
+
+ }
+
+ public function testGeometry_distance()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+
+ $g2 = $reader->read('POINT(0.5 0.5)');
+ $this->assertEquals( 0.0, $g->distance($g2) );
+
+ $g2 = $reader->read('POINT (-1 0)');
+ $this->assertEquals( 1.0, $g->distance($g2) );
+
+ $g2 = $reader->read('LINESTRING (3 0 , 10 0)');
+ $this->assertEquals( 2.0, $g->distance($g2) );
+
+ }
+
+ public function testGeometry_hausdorffDistance()
+ {
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+
+ $g2 = $reader->read('POINT(0 10)');
+ $this->assertEquals( 10.0, round($g->hausdorffDistance($g2)) );
+
+ $g2 = $reader->read('POINT (-1 0)');
+ $this->assertEquals( 2.0, round($g->hausdorffDistance($g2)) );
+
+ $g2 = $reader->read('LINESTRING (3 0 , 10 0)');
+ $this->assertEquals( 9.0, round($g->hausdorffDistance($g2)) );
+
+ }
+
+ public function testGeometry_delaunayTriangulation()
+ {
+ if (!method_exists(GEOSGeometry::class, 'delaunayTriangulation')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setRoundingPrecision(0);
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+
+ $b = $g->delaunayTriangulation();
+ $this->assertEquals('GEOMETRYCOLLECTION (POLYGON ((0 1, 0 0, 1 0, 0 1)), POLYGON ((0 1, 1 0, 1 1, 0 1)))', $writer->write($b));
+
+ $b = $g->delaunayTriangulation(0,true);
+ $this->assertEquals('MULTILINESTRING ((0 1, 1 1), (0 0, 0 1), (0 0, 1 0), (1 0, 1 1), (0 1, 1 0))', $writer->write($b));
+
+ }
+
+ public function testGeometry_voronoiDiagram()
+ {
+ if (!method_exists(GEOSGeometry::class, 'voronoiDiagram')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setRoundingPrecision(0);
+
+ $g = $reader->read('MULTIPOINT(0 0, 100 0, 100 100, 0 100)');
+
+ $b = $g->voronoiDiagram();
+ $this->assertEquals('GEOMETRYCOLLECTION (POLYGON ((50 50, 50 200, 200 200, 200 50, 50 50)), POLYGON ((50 -100, 50 50, 200 50, 200 -100, 50 -100)), POLYGON ((-100 50, -100 200, 50 200, 50 50, -100 50)), POLYGON ((-100 -100, -100 50, 50 50, 50 -100, -100 -100)))', $writer->write($b->normalize()));
+
+ $b = $g->voronoiDiagram(0, 1);
+ $this->assertEquals('MULTILINESTRING ((50 50, 200 50), (50 50, 50 200), (50 -100, 50 50), (-100 50, 50 50))', $writer->write($b->normalize()));
+
+ $b = $g->voronoiDiagram(0, 1, $g->buffer(1000));
+ $this->assertEquals('MULTILINESTRING ((50 50, 1100 50), (50 50, 50 1100), (50 -1000, 50 50), (-1000 50, 50 50))', $writer->write($b->normalize()));
+
+ }
+
+ public function testGeometry_snapTo()
+ {
+ if (!method_exists(GEOSGeometry::class, 'snapTo')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setTrim(true);
+
+ $g = $reader->read('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))');
+
+ $g2 = $reader->read('POINT(0.1 0)');
+
+ $snapped = $g->snapTo($g2, 0);
+ $this->assertEquals('POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))', $writer->write($snapped));
+
+ $snapped = $g->snapTo($g2, 0.5);
+ $this->assertEquals('POLYGON ((0.1 0, 1 0, 1 1, 0 1, 0.1 0))', $writer->write($snapped));
+ }
+
+ public function testGeometry_node()
+ {
+ if (!method_exists(GEOSGeometry::class, 'node')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKTWriter();
+ $writer->setTrim(true);
+
+ $g = $reader->read('LINESTRING(0 0, 10 0, 5 -5, 5 5)');
+
+ $noded = $g->node();
+ $this->assertEquals('MULTILINESTRING ((0 0, 5 0), (5 0, 10 0, 5 -5, 5 0), (5 0, 5 5))', $writer->write($noded));
+
+ }
+}
+
+GeometryTest::run();
+
+?>
+--EXPECT--
+GeometryTest->testGeometry_serialization OK
+GeometryTest->testGeometry_project OK
+GeometryTest->testGeometry_interpolate OK
+GeometryTest->testGeometry_buffer OK
+GeometryTest->testGeometry_offsetCurve OK
+GeometryTest->testGeometry_envelope OK
+GeometryTest->testGeometry_intersection OK
+GeometryTest->testGeometry_clipByRect OK
+GeometryTest->testGeometry_convexHull OK
+GeometryTest->testGeometry_difference OK
+GeometryTest->testGeometry_symdifference OK
+GeometryTest->testGeometry_boundary OK
+GeometryTest->testGeometry_union OK
+GeometryTest->testGeometry_unaryunion OK
+GeometryTest->testGeometry_pointOnSurface OK
+GeometryTest->testGeometry_centroid OK
+GeometryTest->testGeometry_relate OK
+GeometryTest->testGeometry_relateBoundaryNodeRule OK
+GeometryTest->testGeometry_polygonize OK
+GeometryTest->testGeometry_lineMerge OK
+GeometryTest->testGeometry_sharedPaths OK
+GeometryTest->testGeometry_simplify OK
+GeometryTest->testGeometry_extractUniquePoints OK
+GeometryTest->testGeometry_relationalOps OK
+GeometryTest->testGeometry_isEmpty OK
+GeometryTest->testGeometry_checkValidity OK
+GeometryTest->testGeometry_isSimple OK
+GeometryTest->testGeometry_isRing OK
+GeometryTest->testGeometry_hasZ OK
+GeometryTest->testGeometry_isClosed OK
+GeometryTest->testGeometry_type OK
+GeometryTest->testGeometry_srid OK
+GeometryTest->testGeometry_numGeometries OK
+GeometryTest->testGeometry_geometryN OK
+GeometryTest->testGeometry_numInteriorRings OK
+GeometryTest->testGeometry_numPoints OK
+GeometryTest->testGeometry_getXY OK
+GeometryTest->testGeometry_interiorRingN OK
+GeometryTest->testGeometry_exteriorRing OK
+GeometryTest->testGeometry_numCoordinates OK
+GeometryTest->testGeometry_dimension OK
+GeometryTest->testGeometry_coordinateDimension OK
+GeometryTest->testGeometry_pointN OK
+GeometryTest->testGeometry_startendPoint OK
+GeometryTest->testGeometry_area OK
+GeometryTest->testGeometry_length OK
+GeometryTest->testGeometry_distance OK
+GeometryTest->testGeometry_hausdorffDistance OK
+GeometryTest->testGeometry_delaunayTriangulation OK
+GeometryTest->testGeometry_voronoiDiagram OK
+GeometryTest->testGeometry_snapTo OK
+GeometryTest->testGeometry_node OK
diff --git a/tests/002_WKTWriter.phpt b/tests/002_WKTWriter.phpt
new file mode 100644
index 0000000..646cf81
--- /dev/null
+++ b/tests/002_WKTWriter.phpt
@@ -0,0 +1,209 @@
+--TEST--
+WKTWriter tests
+--SKIPIF--
+<?php if (!extension_loaded('geos')) { print "geos extension not loaded\n"; exit(1); } ?>
+--FILE--
+<?php
+
+require './tests/TestHelper.php';
+
+class WKTWriterTest extends GEOSTest
+{
+ public function testWKTWriter__construct()
+ {
+ $writer = new GEOSWKTWriter();
+ $this->assertNotNull($writer);
+ }
+
+ public function testWKTWriter_write()
+ {
+ $writer = new GEOSWKTWriter();
+ $reader = new GEOSWKTReader();
+
+ try {
+ $writer->write(1);
+ $this->assertTrue(FALSE); # this is just to fail if we get here
+ } catch (Exception $e) {
+ $this->assertContains('expects parameter 1', $e->getMessage());
+ }
+
+ $g = $reader->read('POINT(6 7)');
+
+ $this->assertEquals('POINT (6.0000000000000000 7.0000000000000000)',
+ $writer->write($g));
+ }
+
+ public function testWKTWriter_setTrim()
+ {
+ if (!method_exists(GEOSWKTWriter::class, 'setTrim')) {
+ return;
+ }
+
+ $writer = new GEOSWKTWriter();
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(6 7)');
+ $this->assertNotNull($g);
+
+ $writer->setTrim(TRUE);
+ $this->assertEquals('POINT (6 7)',
+ $writer->write($g));
+
+ $writer->setTrim(FALSE);
+ $this->assertEquals('POINT (6.0000000000000000 7.0000000000000000)',
+ $writer->write($g));
+
+ }
+
+ public function testWKT_roundTrip()
+ {
+ $r = new GEOSWKTReader();
+ $w = new GEOSWKTWriter();
+
+ if (method_exists(GEOSWKTWriter::class, 'setTrim')) {
+ $w->setTrim(TRUE);
+ }
+
+ $in[] = 'POINT (0 0)';
+ $in[] = 'POINT EMPTY';
+ $in[] = 'MULTIPOINT (0 1, 2 3)';
+ $in[] = 'MULTIPOINT EMPTY';
+ $in[] = 'LINESTRING (0 0, 2 3)';
+ $in[] = 'LINESTRING EMPTY';
+ $in[] = 'MULTILINESTRING ((0 1, 2 3), (10 10, 3 4))';
+ $in[] = 'MULTILINESTRING EMPTY';
+ $in[] = 'POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))';
+ $in[] = 'POLYGON EMPTY';
+ $in[] = 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((10 10, 10 14, 14 14, 14 10, 10 10), (11 11, 11 12, 12 12, 12 11, 11 11)))';
+ $in[] = 'MULTIPOLYGON EMPTY';
+ $in[] = 'GEOMETRYCOLLECTION (MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)), ((10 10, 10 14, 14 14, 14 10, 10 10), (11 11, 11 12, 12 12, 12 11, 11 11))), POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)), MULTILINESTRING ((0 0, 2 3), (10 10, 3 4)), LINESTRING (0 0, 2 3), MULTIPOINT (0 0, 2 3), POINT (9 0))';
+ $in[] = 'GEOMETRYCOLLECTION EMPTY';
+
+ foreach ($in as $i) {
+ $this->assertEquals($i, $w->write($r->read($i)));
+ }
+
+ }
+
+ public function testWKTWriter_setRoundingPrecision()
+ {
+ if (!method_exists(GEOSWKTWriter::class, 'setRoundingPrecision')) {
+ return;
+ }
+
+ $writer = new GEOSWKTWriter();
+ $reader = new GEOSWKTReader();
+
+ $g = $reader->read('POINT(6.123456 7.123456)');
+
+ $this->assertEquals('POINT (6.1234560000000000 7.1234560000000000)',
+ $writer->write($g));
+
+ $writer->setRoundingPrecision(2);
+ $this->assertEquals('POINT (6.12 7.12)', $writer->write($g));
+
+ $writer->setRoundingPrecision(5); /* rounds */
+ $this->assertEquals('POINT (6.12346 7.12346)', $writer->write($g));
+
+ $writer->setRoundingPrecision(1);
+ $this->assertEquals('POINT (6.1 7.1)', $writer->write($g));
+
+ $writer->setRoundingPrecision(0);
+ $this->assertEquals('POINT (6 7)', $writer->write($g));
+
+ }
+
+ public function testWKTWriter_getOutputDimension()
+ {
+ if (!method_exists(GEOSWKTWriter::class, 'getOutputDimension')) {
+ return;
+ }
+
+ $writer = new GEOSWKTWriter();
+ $this->assertEquals(2, $writer->getOutputDimension());
+ }
+
+ public function testWKTWriter_setOutputDimension()
+ {
+ if (!method_exists(GEOSWKTWriter::class, 'setOutputDimension')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $g3d = $reader->read('POINT(1 2 3)');
+ $g2d = $reader->read('POINT(3 2)');
+
+ $writer = new GEOSWKTWriter();
+ $writer->setTrim(TRUE);
+
+ # Only 2d by default
+ $this->assertEquals('POINT (1 2)', $writer->write($g3d));
+
+ # 3d if requested _and_ available
+ $writer->setOutputDimension(3);
+ $this->assertEquals('POINT Z (1 2 3)', $writer->write($g3d));
+ $this->assertEquals('POINT (3 2)', $writer->write($g2d));
+
+ # 1 is invalid
+ try {
+ $writer->setOutputDimension(1);
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('must be 2 or 3', $e->getMessage());
+ }
+
+ # 4 is invalid
+ try {
+ $writer->setOutputDimension(4);
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('must be 2 or 3', $e->getMessage());
+ }
+
+ }
+
+ public function testWKTWriter_setOld3D()
+ {
+ if (!method_exists(GEOSWKTWriter::class, 'setOld3D')) {
+ return;
+ }
+
+ $reader = new GEOSWKTReader();
+ $g3d = $reader->read('POINT(1 2 3)');
+
+ $writer = new GEOSWKTWriter();
+ $writer->setTrim(TRUE);
+
+ # New 3d WKT by default
+ $writer->setOutputDimension(3);
+ $this->assertEquals('POINT Z (1 2 3)', $writer->write($g3d));
+
+ # Switch to old
+ $writer->setOld3D(TRUE);
+ $this->assertEquals('POINT (1 2 3)', $writer->write($g3d));
+
+ # Old3d flag is not reset when changing dimensions
+ $writer->setOutputDimension(2);
+ $this->assertEquals('POINT (1 2)', $writer->write($g3d));
+ $writer->setOutputDimension(3);
+ $this->assertEquals('POINT (1 2 3)', $writer->write($g3d));
+
+ # Likewise, dimensions spec is not reset when changing old3d flag
+ $writer->setOld3D(FALSE);
+ $this->assertEquals('POINT Z (1 2 3)', $writer->write($g3d));
+
+ }
+}
+
+WKTWriterTest::run();
+
+?>
+--EXPECT--
+WKTWriterTest->testWKTWriter__construct OK
+WKTWriterTest->testWKTWriter_write OK
+WKTWriterTest->testWKTWriter_setTrim OK
+WKTWriterTest->testWKT_roundTrip OK
+WKTWriterTest->testWKTWriter_setRoundingPrecision OK
+WKTWriterTest->testWKTWriter_getOutputDimension OK
+WKTWriterTest->testWKTWriter_setOutputDimension OK
+WKTWriterTest->testWKTWriter_setOld3D OK
diff --git a/tests/003_WKTReader.phpt b/tests/003_WKTReader.phpt
new file mode 100644
index 0000000..7bcbf54
--- /dev/null
+++ b/tests/003_WKTReader.phpt
@@ -0,0 +1,113 @@
+--TEST--
+WKTReader tests
+--SKIPIF--
+<?php if (!extension_loaded('geos')) { print "geos extension not loaded\n"; exit(1); } ?>
+--FILE--
+<?php
+
+require './tests/TestHelper.php';
+
+class WKTReaderTest extends GEOSTest
+{
+ public function testWKTReader__construct()
+ {
+ $reader = new GEOSWKTReader();
+ $this->assertNotNull($reader);
+ }
+
+ public function testWKTReader_read()
+ {
+ $reader = new GEOSWKTReader();
+
+ /* Good WKT */
+ $geom = $reader->read('POINT(0 0)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('POINT(0 0 0)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('POINT Z (0 0 0)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('POINT EMPTY');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOINT(0 0 1, 2 3 4)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOINT Z (0 0 1, 2 3 4)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOINT((0 0), (2 3))');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOINT EMPTY');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('LINESTRING(0 0 1, 2 3 4)');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('LINESTRING EMPTY');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTILINESTRING((0 0 1, 2 3 4),
+ (10 10 2, 3 4 5))');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTILINESTRING Z ((0 0 1, 2 3 4),
+ (10 10 2, 3 4 5))');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('POLYGON EMPTY');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOLYGON(
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ )');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('MULTIPOLYGON EMPTY');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('GEOMETRYCOLLECTION(
+ MULTIPOLYGON(
+ ((0 0, 1 0, 1 1, 0 1, 0 0)),
+ ((10 10, 10 14, 14 14, 14 10, 10 10),
+ (11 11, 11 12, 12 12, 12 11, 11 11))
+ ),
+ POLYGON((0 0, 1 0, 1 1, 0 1, 0 0)),
+ MULTILINESTRING((0 0, 2 3), (10 10, 3 4)),
+ LINESTRING(0 0, 2 3),
+ MULTIPOINT(0 0, 2 3),
+ POINT(9 0)
+ )');
+ $this->assertNotNull($geom);
+ $geom = $reader->read('GEOMETRYCOLLECTION EMPTY');
+ $this->assertNotNull($geom);
+ }
+
+ public function testBogusWKT()
+ {
+ $reader = new GEOSWKTReader();
+
+ /* BOGUS WKT */
+ try {
+ $reader->read("MULTIDOT(0 1 2 3)");
+ $this->assertTrue(FALSE); # this is just to fail if we get here
+ } catch (Exception $e) {
+ $this->assertContains('ParseException', $e->getMessage());
+ }
+ }
+
+ public function testNoArgumentsToRead()
+ {
+ $reader = new GEOSWKTReader();
+
+ /* BOGUS call (#448) */
+ try {
+ $reader->read();
+ $this->assertTrue(FALSE); # this is just to fail if we get here
+ } catch (Exception $e) {
+ $this->assertContains('expects exactly 1 parameter',
+ $e->getMessage());
+ }
+ }
+}
+
+WKTReaderTest::run();
+
+?>
+--EXPECT--
+WKTReaderTest->testWKTReader__construct OK
+WKTReaderTest->testWKTReader_read OK
+WKTReaderTest->testBogusWKT OK
+WKTReaderTest->testNoArgumentsToRead OK
diff --git a/tests/004_WKBWriter.phpt b/tests/004_WKBWriter.phpt
new file mode 100644
index 0000000..d2b3c85
--- /dev/null
+++ b/tests/004_WKBWriter.phpt
@@ -0,0 +1,170 @@
+--TEST--
+WKBWriter tests
+--SKIPIF--
+<?php if (!extension_loaded('geos')) { print "geos extension not loaded\n"; exit(1); } ?>
+--FILE--
+<?php
+
+require './tests/TestHelper.php';
+
+class WKBWriterTest extends GEOSTest
+{
+ public function testWKBWriter__construct()
+ {
+ $writer = new GEOSWKBWriter();
+ $this->assertNotNull($writer);
+ }
+
+ public function testWKBWriter_getOutputDimension()
+ {
+ $writer = new GEOSWKBWriter();
+ $this->assertEquals(2, $writer->getOutputDimension());
+ }
+
+ public function testWKBWriter_setOutputDimension()
+ {
+ $writer = new GEOSWKBWriter();
+ $writer->setOutputDimension(3);
+ $this->assertEquals(3, $writer->getOutputDimension());
+ $writer->setOutputDimension(2);
+ $this->assertEquals(2, $writer->getOutputDimension());
+
+ # 1 is invalid
+ try {
+ $writer->setOutputDimension(1);
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('must be 2 or 3', $e->getMessage());
+ }
+
+ # 4 is invalid
+ try {
+ $writer->setOutputDimension(4);
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('must be 2 or 3', $e->getMessage());
+ }
+ }
+
+ public function testWKBWriter_getsetByteOrder()
+ {
+ $writer = new GEOSWKBWriter();
+
+ /* Machine-dependent */
+ $bo = $writer->getByteOrder();
+
+ $obo = $bo ? 0 : 1;
+ $writer->setByteOrder($obo);
+ $this->assertEquals($obo, $writer->getByteOrder());
+
+ # Anything different from 0 (BIG_ENDIAN) or 1 (LITTLE_ENDIAN)
+ # is invalid
+ try {
+ $writer->setByteOrder(5);
+ $this->assertTrue(FALSE);
+ } catch (Exception $e) {
+ $this->assertContains('LITTLE (1) or BIG (0)', $e->getMessage());
+ }
+ }
+
+ public function testWKBWriter_getsetIncludeSRID()
+ {
+ $writer = new GEOSWKBWriter();
+
+ $this->assertEquals(FALSE, $writer->getIncludeSRID());
+ $writer->setIncludeSRID(TRUE);
+ $this->assertEquals(TRUE, $writer->getIncludeSRID());
+ $writer->setIncludeSRID(FALSE);
+ $this->assertEquals(FALSE, $writer->getIncludeSRID());
+ }
+
+ /**
+ * @dataProvider providerWKBWriter_write
+ *
+ * @param integer $byteOrder The byte order: 0 for BIG endian, 1 for LITTLE endian.
+ * @param integer $inputDimension The input dimension: 2 or 3.
+ * @param integer $outputDimension The output dimension: 2 or 3.
+ * @param boolean $includeSrid Whether to include the SRID in the output.
+ * @param string $wkb The expected HEX WKB output.
+ */
+ public function runWKBWriter_write($byteOrder, $inputDimension, $outputDimension, $includeSrid, $wkb)
+ {
+ $reader = new GEOSWKTReader();
+ $writer = new GEOSWKBWriter();
+
+ $writer->setByteOrder($byteOrder);
+ $writer->setOutputDimension($outputDimension);
+ $writer->setIncludeSRID($includeSrid);
+
+ if ($inputDimension === 3) {
+ $g = $reader->read('POINT(6 7 8)');
+ $g->setSRID(53);
+ } else {
+ $g = $reader->read('POINT(6 7)');
+ $g->setSRID(43);
+ }
+
+ $this->assertEquals(hex2bin($wkb), $writer->write($g));
+ $this->assertEquals($wkb, $writer->writeHEX($g));
+ }
+
+ public function testWKBWriter_write()
+ {
+ // 2D input
+ $this->runWKBWriter_write(1, 2, 2, false, '010100000000000000000018400000000000001C40'); // 2D LITTLE endian
+ $this->runWKBWriter_write(1, 2, 2, true, '01010000202B00000000000000000018400000000000001C40'); // 2D LITTLE endian + SRID
+ $this->runWKBWriter_write(0, 2, 2, false, '00000000014018000000000000401C000000000000'); // 2D BIG endian
+ $this->runWKBWriter_write(0, 2, 2, true, '00200000010000002B4018000000000000401C000000000000'); // 2D BIG endian + SRID
+ $this->runWKBWriter_write(1, 2, 3, false, '010100000000000000000018400000000000001C40'); // 3D LITTLE endian
+ $this->runWKBWriter_write(1, 2, 3, true, '01010000202B00000000000000000018400000000000001C40'); // 3D LITTLE endian + SRID
+ $this->runWKBWriter_write(0, 2, 3, false, '00000000014018000000000000401C000000000000'); // 3D BIG endian
+ $this->runWKBWriter_write(0, 2, 3, true, '00200000010000002B4018000000000000401C000000000000'); // 3D BIG endian + SRID
+
+ // 3D input
+ $this->runWKBWriter_write(1, 3, 2, false, '010100000000000000000018400000000000001C40'); // 2D LITTLE endian
+ $this->runWKBWriter_write(1, 3, 2, true, '01010000203500000000000000000018400000000000001C40'); // 2D LITTLE endian + SRID
+ $this->runWKBWriter_write(0, 3, 2, false, '00000000014018000000000000401C000000000000'); // 2D BIG endian
+ $this->runWKBWriter_write(0, 3, 2, true, '0020000001000000354018000000000000401C000000000000'); // 2D BIG endian + SRID
+ $this->runWKBWriter_write(1, 3, 3, false, '010100008000000000000018400000000000001C400000000000002040'); // 3D LITTLE endian
+ $this->runWKBWriter_write(1, 3, 3, true, '01010000A03500000000000000000018400000000000001C400000000000002040'); // 3D LITTLE endian + SRID
+ $this->runWKBWriter_write(0, 3, 3, false, '00800000014018000000000000401C0000000000004020000000000000'); // 3D BIG endian
+ $this->runWKBWriter_write(0, 3, 3, true, '00A0000001000000354018000000000000401C0000000000004020000000000000'); // 3D BIG endian + SRID
+ }
+
+ public function testInvalidWriteThrowsException()
+ {
+ $writer = new GEOSWKBWriter();
+
+ try {
+ $writer->write(1);
+ $this->assertTrue(false);
+ } catch (ErrorException $e) {
+ $this->assertContains('expects parameter 1 to be object, integer given', $e->getMessage());
+ }
+ }
+
+ public function testInvalidWriteHEXThrowsException()
+ {
+ $writer = new GEOSWKBWriter();
+
+ try {
+ $writer->writeHEX(1);
+ $this->assertTrue(false);
+ } catch (ErrorException $e) {
+ $this->assertContains('expects parameter 1 to be object, integer given', $e->getMessage());
+ }
+ }
+}
+
+WKBWriterTest::run();
+
+?>
+--EXPECT--
+WKBWriterTest->testWKBWriter__construct OK
+WKBWriterTest->testWKBWriter_getOutputDimension OK
+WKBWriterTest->testWKBWriter_setOutputDimension OK
+WKBWriterTest->testWKBWriter_getsetByteOrder OK
+WKBWriterTest->testWKBWriter_getsetIncludeSRID OK
+WKBWriterTest->testWKBWriter_write OK
+WKBWriterTest->testInvalidWriteThrowsException OK
+WKBWriterTest->testInvalidWriteHEXThrowsException OK
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..57f54fd
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,14 @@
+Tests can be run manually from the top-level build dir using:
+
+ php -n -d extension=modules/geos.so tests/001_Geometry.phpt
+
+If you want to use valgrind, it is recommended to disable Zend
+memory management:
+
+ export USE_ZEND_ALLOC=0
+
+And avoid unload of modules:
+
+ export ZEND_DONT_UNLOAD_MODULES=1
+
+Read more on https://bugs.php.net/bugs-getting-valgrind-log.php
diff --git a/tests/TestHelper.php b/tests/TestHelper.php
new file mode 100644
index 0000000..c6dd050
--- /dev/null
+++ b/tests/TestHelper.php
@@ -0,0 +1,141 @@
+<?php
+
+# hex2bin for PHP < 5.4
+# https://gist.github.com/mcrumley/5672621
+if (!function_exists('hex2bin')) {
+ function hex2bin($str) {
+ $map = array(
+ '00'=>"\x00", '10'=>"\x10", '20'=>"\x20", '30'=>"\x30", '40'=>"\x40", '50'=>"\x50", '60'=>"\x60", '70'=>"\x70",
+ '01'=>"\x01", '11'=>"\x11", '21'=>"\x21", '31'=>"\x31", '41'=>"\x41", '51'=>"\x51", '61'=>"\x61", '71'=>"\x71",
+ '02'=>"\x02", '12'=>"\x12", '22'=>"\x22", '32'=>"\x32", '42'=>"\x42", '52'=>"\x52", '62'=>"\x62", '72'=>"\x72",
+ '03'=>"\x03", '13'=>"\x13", '23'=>"\x23", '33'=>"\x33", '43'=>"\x43", '53'=>"\x53", '63'=>"\x63", '73'=>"\x73",
+ '04'=>"\x04", '14'=>"\x14", '24'=>"\x24", '34'=>"\x34", '44'=>"\x44", '54'=>"\x54", '64'=>"\x64", '74'=>"\x74",
+ '05'=>"\x05", '15'=>"\x15", '25'=>"\x25", '35'=>"\x35", '45'=>"\x45", '55'=>"\x55", '65'=>"\x65", '75'=>"\x75",
+ '06'=>"\x06", '16'=>"\x16", '26'=>"\x26", '36'=>"\x36", '46'=>"\x46", '56'=>"\x56", '66'=>"\x66", '76'=>"\x76",
+ '07'=>"\x07", '17'=>"\x17", '27'=>"\x27", '37'=>"\x37", '47'=>"\x47", '57'=>"\x57", '67'=>"\x67", '77'=>"\x77",
+ '08'=>"\x08", '18'=>"\x18", '28'=>"\x28", '38'=>"\x38", '48'=>"\x48", '58'=>"\x58", '68'=>"\x68", '78'=>"\x78",
+ '09'=>"\x09", '19'=>"\x19", '29'=>"\x29", '39'=>"\x39", '49'=>"\x49", '59'=>"\x59", '69'=>"\x69", '79'=>"\x79",
+ '0a'=>"\x0a", '1a'=>"\x1a", '2a'=>"\x2a", '3a'=>"\x3a", '4a'=>"\x4a", '5a'=>"\x5a", '6a'=>"\x6a", '7a'=>"\x7a",
+ '0b'=>"\x0b", '1b'=>"\x1b", '2b'=>"\x2b", '3b'=>"\x3b", '4b'=>"\x4b", '5b'=>"\x5b", '6b'=>"\x6b", '7b'=>"\x7b",
+ '0c'=>"\x0c", '1c'=>"\x1c", '2c'=>"\x2c", '3c'=>"\x3c", '4c'=>"\x4c", '5c'=>"\x5c", '6c'=>"\x6c", '7c'=>"\x7c",
+ '0d'=>"\x0d", '1d'=>"\x1d", '2d'=>"\x2d", '3d'=>"\x3d", '4d'=>"\x4d", '5d'=>"\x5d", '6d'=>"\x6d", '7d'=>"\x7d",
+ '0e'=>"\x0e", '1e'=>"\x1e", '2e'=>"\x2e", '3e'=>"\x3e", '4e'=>"\x4e", '5e'=>"\x5e", '6e'=>"\x6e", '7e'=>"\x7e",
+ '0f'=>"\x0f", '1f'=>"\x1f", '2f'=>"\x2f", '3f'=>"\x3f", '4f'=>"\x4f", '5f'=>"\x5f", '6f'=>"\x6f", '7f'=>"\x7f",
+
+ '80'=>"\x80", '90'=>"\x90", 'a0'=>"\xa0", 'b0'=>"\xb0", 'c0'=>"\xc0", 'd0'=>"\xd0", 'e0'=>"\xe0", 'f0'=>"\xf0",
+ '81'=>"\x81", '91'=>"\x91", 'a1'=>"\xa1", 'b1'=>"\xb1", 'c1'=>"\xc1", 'd1'=>"\xd1", 'e1'=>"\xe1", 'f1'=>"\xf1",
+ '82'=>"\x82", '92'=>"\x92", 'a2'=>"\xa2", 'b2'=>"\xb2", 'c2'=>"\xc2", 'd2'=>"\xd2", 'e2'=>"\xe2", 'f2'=>"\xf2",
+ '83'=>"\x83", '93'=>"\x93", 'a3'=>"\xa3", 'b3'=>"\xb3", 'c3'=>"\xc3", 'd3'=>"\xd3", 'e3'=>"\xe3", 'f3'=>"\xf3",
+ '84'=>"\x84", '94'=>"\x94", 'a4'=>"\xa4", 'b4'=>"\xb4", 'c4'=>"\xc4", 'd4'=>"\xd4", 'e4'=>"\xe4", 'f4'=>"\xf4",
+ '85'=>"\x85", '95'=>"\x95", 'a5'=>"\xa5", 'b5'=>"\xb5", 'c5'=>"\xc5", 'd5'=>"\xd5", 'e5'=>"\xe5", 'f5'=>"\xf5",
+ '86'=>"\x86", '96'=>"\x96", 'a6'=>"\xa6", 'b6'=>"\xb6", 'c6'=>"\xc6", 'd6'=>"\xd6", 'e6'=>"\xe6", 'f6'=>"\xf6",
+ '87'=>"\x87", '97'=>"\x97", 'a7'=>"\xa7", 'b7'=>"\xb7", 'c7'=>"\xc7", 'd7'=>"\xd7", 'e7'=>"\xe7", 'f7'=>"\xf7",
+ '88'=>"\x88", '98'=>"\x98", 'a8'=>"\xa8", 'b8'=>"\xb8", 'c8'=>"\xc8", 'd8'=>"\xd8", 'e8'=>"\xe8", 'f8'=>"\xf8",
+ '89'=>"\x89", '99'=>"\x99", 'a9'=>"\xa9", 'b9'=>"\xb9", 'c9'=>"\xc9", 'd9'=>"\xd9", 'e9'=>"\xe9", 'f9'=>"\xf9",
+ '8a'=>"\x8a", '9a'=>"\x9a", 'aa'=>"\xaa", 'ba'=>"\xba", 'ca'=>"\xca", 'da'=>"\xda", 'ea'=>"\xea", 'fa'=>"\xfa",
+ '8b'=>"\x8b", '9b'=>"\x9b", 'ab'=>"\xab", 'bb'=>"\xbb", 'cb'=>"\xcb", 'db'=>"\xdb", 'eb'=>"\xeb", 'fb'=>"\xfb",
+ '8c'=>"\x8c", '9c'=>"\x9c", 'ac'=>"\xac", 'bc'=>"\xbc", 'cc'=>"\xcc", 'dc'=>"\xdc", 'ec'=>"\xec", 'fc'=>"\xfc",
+ '8d'=>"\x8d", '9d'=>"\x9d", 'ad'=>"\xad", 'bd'=>"\xbd", 'cd'=>"\xcd", 'dd'=>"\xdd", 'ed'=>"\xed", 'fd'=>"\xfd",
+ '8e'=>"\x8e", '9e'=>"\x9e", 'ae'=>"\xae", 'be'=>"\xbe", 'ce'=>"\xce", 'de'=>"\xde", 'ee'=>"\xee", 'fe'=>"\xfe",
+ '8f'=>"\x8f", '9f'=>"\x9f", 'af'=>"\xaf", 'bf'=>"\xbf", 'cf'=>"\xcf", 'df'=>"\xdf", 'ef'=>"\xef", 'ff'=>"\xff",
+ );
+
+ $strlen = strlen($str);
+
+ if ($strlen % 2 !== 0) {
+ user_error('Hexadecimal input string must have an even length', E_USER_WARNING);
+ return false;
+ }
+
+ if (strspn($str, '0123456789ABCDEFabcdef') !== $strlen) {
+ return false;
+ }
+
+ return strtr(strtolower($str), $map);
+ }
+}
+
+function exception_error_handler($errno, $errstr, $errfile, $errline ) {
+ throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
+}
+set_error_handler("exception_error_handler");
+
+class GEOSTest
+{
+ static public function run()
+ {
+ $instance = new static();
+
+ foreach (get_class_methods($instance) as $method) {
+ if (strpos($method, 'test') === 0) {
+ $class = get_class($instance);
+
+ try {
+ $instance->$method();
+ print "{$class}->{$method}\tOK" . PHP_EOL;
+ } catch (Exception $e) {
+ print "{$class}->{$method}\tERROR:" . PHP_EOL;
+ print $e->getMessage() . "\n";
+ debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+ throw $e;
+ }
+ }
+ }
+ }
+
+ public function assertContains($expected, $actual)
+ {
+ if (strpos($actual, $expected) === false) {
+ throw new Exception("Expected '{$actual}' to contain '{$expected}'.");
+ }
+ }
+
+ public function assertEquals($expected, $actual)
+ {
+ if ($actual != $expected) {
+ throw new Exception("Expected {$actual} to equal {$expected}.");
+ }
+ }
+
+ public function assertNull($actual)
+ {
+ if (!is_null($actual)) {
+ throw new Exception("Expected null.");
+ }
+ }
+
+ public function assertNotNull($actual)
+ {
+ if (is_null($actual)) {
+ throw new Exception("Expected not null.");
+ }
+ }
+
+ public function assertTrue($expected)
+ {
+ if ($expected !== true) {
+ throw new Exception("Expected true");
+ }
+ }
+
+ public function assertFalse($expected)
+ {
+ if ($expected !== false) {
+ throw new Exception("Expected false");
+ }
+ }
+
+ public function assertType($expectedType, $value)
+ {
+ $validType = false;
+
+ switch ($expectedType) {
+ case 'array':
+ $validType = is_array($value);
+ }
+
+ if (!$validType) {
+ throw new Exception("Expected type '{$expectedType}.'");
+ }
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/php-geos.git
More information about the Pkg-grass-devel
mailing list