pax_global_header00006660000000000000000000000064132320006260014504gustar00rootroot0000000000000052 comment=f9111c833a511f243235f4b9128d20f10dd7ec73 Shapely-1.6.4/000077500000000000000000000000001323200062600131215ustar00rootroot00000000000000Shapely-1.6.4/.dockerignore000066400000000000000000000001661323200062600156000ustar00rootroot00000000000000# The wheel-building image only depends on requirements-dev.txt. Ignore all # other files. * .* !requirements-dev.txt Shapely-1.6.4/.gitignore000066400000000000000000000002771323200062600151170ustar00rootroot00000000000000*.pyc *.pyo *.c *.so VERSION.txt Shapely.egg-info/ build/ dist/ docs/_build/ docs/shapely.*.txt docs/shapely.txt docs/modules.txt .cache/ .idea/ *.pyd *.pdb wheels/ .coverage ignore/ venv/ Shapely-1.6.4/.travis.yml000066400000000000000000000013121323200062600152270ustar00rootroot00000000000000language: python sudo: false cache: pip python: - "2.6" - "2.7" - "3.3" - "3.4" - "3.5" env: - "TRAVIS_SPEEDUP_OPTS=--with-speedups" - "TRAVIS_SPEEDUP_OPTS=--without-speedups" addons: apt: packages: - libgeos-dev - python-numpy before_install: - if [[ $TRAVIS_PYTHON_VERSION == "2.6" ]]; then pip install unittest2; fi - pip install pip setuptools --upgrade - pip install --install-option="--no-cython-compile" cython install: - pip install -e .[test] - pip install coveralls script: "py.test --cov shapely --cov-report term-missing ${TRAVIS_SPEEDUP_OPTS}" after_success: - coveralls || echo "!! intermittent coveralls failure" notifications: email: false Shapely-1.6.4/CHANGES.txt000066400000000000000000000451201323200062600147340ustar00rootroot00000000000000Changes ======= 1.6.4 (2017-01-24) ------------------ - Handle a ``TypeError`` that can occur when geometries are torn down (#473, #528). - 1.6.3 (2017-12-09) ------------------ - AttributeError is no longer raised when accessing __geo_interface__ of an empty polygon (#450). - ``asShape`` now handles empty coordinates in mappings as ``shape`` does (#542). Please note that ``asShape`` is likely to be deprecated in a future version of Shapely. - Check for length of LineString coordinates in speed mode, preventing crashes when using LineStrings with only one coordinate (#546). 1.6.2 (2017-10-30) ------------------ - A 1.6.2.post1 release has been made to fix a problem with macosx wheels uploaded to PyPI. 1.6.2 (2017-10-26) ------------------ - Splitting a linestring by one of its end points will now succeed instead of failing with a ``ValueError`` (#524, #533). - Missing documentation of a geometry's ``overlaps`` predicate has been added (#522). 1.6.1 (2017-09-01) ------------------ - Avoid ``STRTree`` crashes due to dangling references (#505) by maintaining references to added geometries. - Reduce log level to debug when reporting on calls to ctypes ``CDLL()`` that don't succeed and are retried (#515). - Clarification: applications like GeoPandas that need an empty geometry object should use ``BaseGeometry()`` instead of ``Point()`` or ``Polygon()``. An ``EmptyGeometry`` class has been added in the master development branch and will be available in the next non-bugfix release. 1.6.0 (2017-08-21) ------------------ Shapely 1.6.0 adds new attributes to existing geometry classes and new functions (``split()`` and ``polylabel()``) to the shapely.ops module. Exceptions are consolidated in a shapely.errors module and logging practices have been improved. Shapely's optional features depending on Numpy are now gathered into a requirements set named "vectorized" and these may be installed like ``pip install shapely[vectorized]``. Much of the work on 1.6.0 was aimed to improve the project's build and packaging scripts and to minimize run-time dependencies. Shapely now vendorizes packaging to use during builds only and never again invokes the geos-config utility at run-time. In addition to the changes listed under the alpha and beta pre-releases below, the following change has been made to the project: - Project documentation is now hosted at https://shapely.readthedocs.io/en/latest/. Thank you all for using, promoting, and contributing to the Shapely project. 1.6b5 (2017-08-18) ------------------ Bug fixes: - Passing a single coordinate to ``LineString()`` with speedups disabled now raises a ValueError as happens with speedups enabled. This resolves #509. 1.6b4 (2017-02-15) ------------------ Bug fixes: - Isolate vendorized packaging in a _vendor directory, remove obsolete dist-info, and remove packaging from project requirements (resolves #468). 1.6b3 (2016-12-31) ------------------ Bug fixes: - Level for log messages originating from the GEOS notice handler reduced from WARNING to INFO (#447). - Permit speedups to be imported again without Numpy (#444). 1.6b2 (2016-12-12) ------------------ New features: - Add support for GeometryCollection to shape and asShape functions (#422). 1.6b1 (2016-12-12) ------------------ Bug fixes: - Implemented __array_interface__ for empty Points and LineStrings (#403). 1.6a3 (2016-12-01) ------------------ Bug fixes: - Remove accidental hard requirement of Numpy (#431). Packaging: - Put Numpy in an optional requirement set named "vectorized" (#431). 1.6a2 (2016-11-09) ------------------ Bug fixes: - Shapely no longer configures logging in ``geos.py`` (#415). Refactoring: - Consolidation of exceptions in ``shapely.errors``. - ``UnsupportedGEOSVersionError`` is raised when GEOS < 3.3.0 (#407). Packaging: - Added new library search paths to assist Anaconda (#413). - geos-config will now be bypassed when NO_GEOS_CONFIG env var is set. This allows configuration of Shapely builds on Linux systems that for whatever reasons do not include the geos-config program (#322). 1.6a1 (2016-09-14) ------------------ New features: - A new error derived from NotImplementedError, with a more useful message, is raised when the GEOS backend doesn't support a called method (#216). - The ``project()`` method of LineString has been extended to LinearRing geometries (#286). - A new ``minimum_rotated_rectangle`` attribute has been added to the base geometry class (#354). - A new ``shapely.ops.polylabel()`` function has been added. It computes a point suited for labeling concave polygons (#395). - A new ``shapely.ops.split()`` function has been added. It splits a geometry by another geometry of lesser dimension: polygon by line, line by point (#293, #371). - ``Polygon.from_bounds()`` constructs a Polygon from bounding coordinates (#392). - Support for testing with Numpy 1.4.1 has been added (#301). - Support creating all kinds of empty geometries from empty lists of Python objects (#397, #404). Refactoring: - Switch from ``SingleSidedBuffer()`` to ``OffsetCurve()`` for GEOS >= 3.3 (#270). - Cython speedups are now enabled by default (#252). Packaging: - Packaging 16.7, a setup dependency, is vendorized (#314). - Infrastructure for building manylinux1 wheels has been added (#391). - The system's ``geos-config`` program is now only checked when ``setup.py`` is executed, never during normal use of the module (#244). - Added new library search paths to assist PyInstaller (#382) and Windows (#343). 1.5.17 (2016-08-31) ------------------- - Bug fix: eliminate memory leak in geom_factory() (#408). - Bug fix: remove mention of negative distances in parallel_offset and note that vertices of right hand offset lines are reversed (#284). 1.5.16 (2016-05-26) ------------------- - Bug fix: eliminate memory leak when unpickling geometry objects (#384, #385). - Bug fix: prevent crashes when attempting to pickle a prepared geometry, raising ``PicklingError`` instead (#386). - Packaging: extension modules in the OS X wheels uploaded to PyPI link only libgeos_c.dylib now (you can verify and compare to previous releases with ``otool -L shapely/vectorized/_vectorized.so``). 1.5.15 (2016-03-29) ------------------- - Bug fix: use uintptr_t to store pointers instead of long in _geos.pxi, preventing an overflow error (#372, #373). Note that this bug fix was erroneously reported to have been made in 1.5.14, but was not. 1.5.14 (2016-03-27) ------------------- - Bug fix: use ``type()`` instead of ``isinstance()`` when evaluating geometry equality, preventing instances of base and derived classes from being mistaken for equals (#317). - Bug fix: ensure that empty geometries are created when constructors have no args (#332, #333). - Bug fix: support app "freezing" better on Windows by not relying on the ``__file__`` attribute (#342, #377). - Bug fix: ensure that empty polygons evaluate to be ``==`` (#355). - Bug fix: filter out empty geometries that can cause segfaults when creating and loading STRtrees (#345, #348). - Bug fix: no longer attempt to reuse GEOS DLLs already loaded by Rasterio or Fiona on OS X (#374, #375). 1.5.13 (2015-10-09) ------------------- - Restore setup and runtime discovery and loading of GEOS shared library to state at version 1.5.9 (#326). - On OS X we try to reuse any GEOS shared library that may have been loaded via import of Fiona or Rasterio in order to avoid a bug involving the GEOS AbstractSTRtree (#324, #327). 1.5.12 (2015-08-27) ------------------- - Remove configuration of root logger from libgeos.py (#312). - Skip test_fallbacks on Windows (#308). - Call setlocale(locale.LC_ALL, "") instead of resetlocale() on Windows when tearing down the locale test (#308). - Fix for Sphinx warnings (#309). - Addition of .cache, .idea, .pyd, .pdb to .gitignore (#310). 1.5.11 (2015-08-23) ------------------- - Remove packaging module requirement added in 1.5.10 (#305). Distutils can't parse versions using 'rc', but if we stick to 'a' and 'b' we will be fine. 1.5.10 (2015-08-22) ------------------- - Monkey patch affinity module by absolute reference (#299). - Raise TopologicalError in relate() instead of crashing (#294, #295, #303). 1.5.9 (2015-05-27) ------------------ - Fix for 64 bit speedups compatibility (#274). 1.5.8 (2015-04-29) ------------------ - Setup file encoding bug fix (#254). - Support for pyinstaller (#261). - Major prepared geometry operation fix for Windows (#268, #269). - Major fix for OS X binary wheel (#262). 1.5.7 (2015-03-16) ------------------ - Test and fix buggy error and notice handlers (#249). 1.5.6 (2015-02-02) ------------------ - Fix setup regression (#232, #234). - SVG representation improvements (#233, #237). 1.5.5 (2015-01-20) ------------------ - MANIFEST changes to restore _geox.pxi (#231). 1.5.4 (2015-01-19) ------------------ - Fixed OS X binary wheel library load path (#224). 1.5.3 (2015-01-12) ------------------ - Fixed ownership and potential memory leak in polygonize (#223). - Wider release of binary wheels for OS X. 1.5.2 (2015-01-04) ------------------ - Fail installation if GEOS dependency is not met, preventing update breakage (#218, #219). 1.5.1 (2014-12-04) ------------------ - Restore geometry hashing (#209). 1.5.0 (2014-12-02) ------------------ - Affine transformation speedups (#197). - New `==` rich comparison (#195). - Geometry collection constructor (#200). - ops.snap() backed by GEOSSnap (#201). - Clearer exceptions in cases of topological invalidity (#203). 1.4.4 (2014-11-02) ------------------ - Proper conversion of numpy float32 vals to coords (#186). 1.4.3 (2014-10-01) ------------------ - Fix for endianness bug in WKB writer (#174). 1.4.2 (2014-09-29) ------------------ - Fix bungled 1.4.1 release (#176). 1.4.1 (2014-09-23) ------------------ - Return of support for GEOS 3.2 (#176, #178). 1.4.0 (2014-09-08) ------------------ - SVG representations for IPython's inline image protocol. - Efficient and fast vectorized contains(). - Change mitre_limit default to 5.0; raise ValueError with 0.0 (#139). - Allow mix of tuples and Points in sped-up LineString ctor (#152). - New STRtree class (#73). - Add ops.nearest_points() (#147). - Faster creation of geometric objects from others (cloning) (#165). - Removal of tests from package. 1.3.3 (2014-07-23) ------------------ - Allow single-part geometries as argument to ops.cacaded_union() (#135). - Support affine transformations of LinearRings (#112). 1.3.2 (2014-05-13) ------------------ - Let LineString() take a sequence of Points (#130). 1.3.1 (2014-04-22) ------------------ - More reliable proxy cleanup on exit (#106). - More robust DLL loading on all platforms (#114). 1.3.0 (2013-12-31) ------------------ - Include support for Python 3.2 and 3.3 (#56), minimum version is now 2.6. - Switch to GEOS WKT/WKB Reader/Writer API, with defaults changed to enable 3D output dimensions, and to 'trim' WKT output for GEOS >=3.3.0. - Use GEOS version instead of GEOS C API version to determine library capabilities (#65). 1.2.19 (2013-12-30) ------------------- - Add buffering style options (#55). 1.2.18 (2013-07-23) -------------------- - Add shapely.ops.transform. - Permit empty sequences in collection constructors (#49, #50). - Individual polygons in MultiPolygon.__geo_interface__ are changed to tuples to match Polygon.__geo_interface__ (#51). - Add shapely.ops.polygonize_full (#57). 1.2.17 (2013-01-27) ------------------- - Avoid circular import between wkt/wkb and geometry.base by moving calls to GEOS serializers to the latter module. - Set _ndim when unpickling (issue #6). - Don't install DLLs to Python's DLL directory (#37). - Add affinity module of affine transformation (#31). - Fix NameError that blocked installation with PyPy (#40, #41). 1.2.16 (2012-09-18) ------------------- - Add ops.unary_union function. - Alias ops.cascaded_union to ops.unary_union when GEOS CAPI >= (1,7,0). - Add geos_version_string attribute to shapely.geos. - Ensure parent is set when child geometry is accessed. - Generate _speedups.c using Cython when building from repo when missing, stale, or the build target is "sdist". - The is_simple predicate of invalid, self-intersecting linear rings now returns ``False``. - Remove VERSION.txt from repo, it's now written by the distutils setup script with value of shapely.__version__. 1.2.15 (2012-06-27) ------------------- - Eliminate numerical sensitivity in a method chaining test (Debian bug #663210). - Account for cascaded union of random buffered test points being a polygon or multipolygon (Debian bug #666655). - Use Cython to build speedups if it is installed. - Avoid stumbling over SVN revision numbers in GEOS C API version strings. 1.2.14 (2012-01-23) ------------------- - A geometry's coords property is now sliceable, yielding a list of coordinate values. - Homogeneous collections are now sliceable, yielding a new collection of the same type. 1.2.13 (2011-09-16) ------------------- - Fixed errors in speedups on 32bit systems when GEOS references memory above 2GB. - Add shapely.__version__ attribute. - Update the manual. 1.2.12 (2011-08-15) ------------------- - Build Windows distributions with VC7 or VC9 as appropriate. - More verbose report on failure to speed up. - Fix for prepared geometries broken in 1.2.11. - DO NOT INSTALL 1.2.11 1.2.11 (2011-08-04) ------------------- - Ignore AttributeError during exit. - PyPy 1.5 support. - Prevent operation on prepared geometry crasher (#12). - Optional Cython speedups for Windows. - Linux 3 platform support. 1.2.10 (2011-05-09) ------------------- - Add optional Cython speedups. - Add is_cww predicate to LinearRing. - Add function that forces orientation of Polygons. - Disable build of speedups on Windows pending packaging work. 1.2.9 (2011-03-31) ------------------ - Remove extra glob import. - Move examples to shapely.examples. - Add box() constructor for rectangular polygons. - Fix extraneous imports. 1.2.8 (2011-12-03) ------------------ - New parallel_offset method (#6). - Support for Python 2.4. 1.2.7 (2010-11-05) ------------------ - Support for Windows eggs. 1.2.6 (2010-10-21) ------------------ - The geoms property of an empty collection yields [] instead of a ValueError (#3). - The coords and geometry type sproperties have the same behavior as above. - Ensure that z values carry through into products of operations (#4). 1.2.5 (2010-09-19) ------------------ - Stop distributing docs/_build. - Include library fallbacks in test_dlls.py for linux platform. 1.2.4 (2010-09-09) ------------------ - Raise AttributeError when there's no backend support for a method. - Raise OSError if libgeos_c.so (or variants) can't be found and loaded. - Add geos_c DLL loading support for linux platforms where find_library doesn't work. 1.2.3 (2010-08-17) ------------------ - Add mapping function. - Fix problem with GEOSisValidReason symbol for GEOS < 3.1. 1.2.2 (2010-07-23) ------------------ - Add representative_point method. 1.2.1 (2010-06-23) ------------------ - Fixed bounds of singular polygons. - Added shapely.validation.explain_validity function (#226). 1.2 (2010-05-27) ---------------- - Final release. 1.2rc2 (2010-05-26) ------------------- - Add examples and tests to MANIFEST.in. - Release candidate 2. 1.2rc1 (2010-05-25) ------------------- - Release candidate. 1.2b7 (2010-04-22) ------------------ - Memory leak associated with new empty geometry state fixed. 1.2b6 (2010-04-13) ------------------ - Broken GeometryCollection fixed. 1.2b5 (2010-04-09) ------------------ - Objects can be constructed from others of the same type, thereby making copies. Collections can be constructed from sequences of objects, also making copies. - Collections are now iterators over their component objects. - New code for manual figures, using the descartes package. 1.2b4 (2010-03-19) ------------------ - Adds support for the "sunos5" platform. 1.2b3 (2010-02-28) ------------------ - Only provide simplification implementations for GEOS C API >= 1.5. 1.2b2 (2010-02-19) ------------------ - Fix cascaded_union bug introduced in 1.2b1 (#212). 1.2b1 (2010-02-18) ------------------ - Update the README. Remove cruft from setup.py. Add some version 1.2 metadata regarding required Python version (>=2.5,<3) and external dependency (libgeos_c >= 3.1). 1.2a6 (2010-02-09) ------------------ - Add accessor for separate arrays of X and Y values (#210). TODO: fill gap here 1.2a1 (2010-01-20) ------------------ - Proper prototyping of WKB writer, and avoidance of errors on 64-bit systems (#191). - Prototype libgeos_c functions in a way that lets py2exe apps import shapely (#189). 1.2 Branched (2009-09-19) 1.0.12 (2009-04-09) ------------------- - Fix for references held by topology and predicate descriptors. 1.0.11 (2008-11-20) ------------------- - Work around bug in GEOS 2.2.3, GEOSCoordSeq_getOrdinate not exported properly (#178). 1.0.10 (2008-11-17) ------------------- - Fixed compatibility with GEOS 2.2.3 that was broken in 1.0.8 release (#176). 1.0.9 (2008-11-16) ------------------ - Find and load MacPorts libgeos. 1.0.8 (2008-11-01) ------------------ - Fill out GEOS function result and argument types to prevent faults on a 64-bit arch. 1.0.7 (2008-08-22) ------------------ - Polygon rings now have the same dimensions as parent (#168). - Eliminated reference cycles in polygons (#169). 1.0.6 (2008-07-10) ------------------ - Fixed adaptation of multi polygon data. - Raise exceptions earlier from binary predicates. - Beginning distributing new windows DLLs (#166). 1.0.5 (2008-05-20) ------------------ - Added access to GEOS polygonizer function. - Raise exception when insufficient coordinate tuples are passed to LinearRing constructor (#164). 1.0.4 (2008-05-01) ------------------ - Disentangle Python and topological equality (#163). - Add shape(), a factory that copies coordinates from a geo interface provider. To be used instead of asShape() unless you really need to store coordinates outside shapely for efficient use in other code. - Cache GEOS geometries in adapters (#163). 1.0.3 (2008-04-09) ------------------ - Do not release GIL when calling GEOS functions (#158). - Prevent faults when chaining multiple GEOS operators (#159). 1.0.2 (2008-02-26) ------------------ - Fix loss of dimensionality in polygon rings (#155). 1.0.1 (2008-02-08) ------------------ - Allow chaining expressions involving coordinate sequences and geometry parts (#151). - Protect against abnormal use of coordinate accessors (#152). - Coordinate sequences now implement the numpy array protocol (#153). 1.0 (2008-01-18) ---------------- - Final release. 1.0 RC2 (2008-01-16) -------------------- - Added temporary solution for #149. 1.0 RC1 (2008-01-14) -------------------- - First release candidate Shapely-1.6.4/CITATION.txt000066400000000000000000000004731323200062600151000ustar00rootroot00000000000000If you use Shapely for any published work, please cite it using the reference below: @Misc{, author = {Sean Gillies and others}, organization = {toblerity.org}, title = {Shapely: manipulation and analysis of geometric objects}, year = {2007--}, url = "https://github.com/Toblerity/Shapely" } Shapely-1.6.4/CODE_OF_CONDUCT.md000066400000000000000000000036751323200062600157330ustar00rootroot00000000000000# Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) Shapely-1.6.4/CREDITS.txt000066400000000000000000000046351323200062600147670ustar00rootroot00000000000000Credits ======= Shapely is written by: * Sean Gillies * Oliver Tonnhofer * Joshua Arnott * Mike Toews * Jacob Wasserman * Aron Bierbaum * Allan Adair * Johannes Schönberger * georgeouzou * Phil Elson * Howard Butler * Kelsey Jordahl * dokai * Kevin Wurster * Gabi Davar * Thibault Deutsch * Dave Collins * fredj * Brad Hards * David Baumgold * Henry Walshaw * Jinkun Wang * Marc Jansen * Sampo Syrjanen * Steve M. Kim * Thomas Kluyver * Morris Tweed * Naveen Michaud-Agrawal * Jeethu Rao * Peter Sagerson * Jason Sanford * mindw * Jamie Hall * James Spencer * Stephan Hügel * Bas Couwenberg * James Douglass * Tobias Sauerwein * WANG Aiyong * Brandon Wood * BertrandGervais * Andy Freeland * Benjamin Root * giumas * Leandro Lima * Maarten Vermeyen * joelostblom * Marco De Nadai * Johan Euphrosine See also: https://github.com/Toblerity/Shapely/graphs/contributors. Additional help from: * Justin Bronn (GeoDjango) for ctypes inspiration * Martin Davis (JTS) * Sandro Santilli, Mateusz Loskot, Paul Ramsey, et al (GEOS Project) Major portions of this work were supported by a grant (for Pleiades_) from the U.S. National Endowment for the Humanities (http://www.neh.gov). .. _Pleiades: http://pleiades.stoa.org Shapely-1.6.4/DLLs_AMD64_VC9/000077500000000000000000000000001323200062600152335ustar00rootroot00000000000000Shapely-1.6.4/DLLs_AMD64_VC9/README.txt000066400000000000000000000000131323200062600167230ustar00rootroot00000000000000AMD64 DLLs Shapely-1.6.4/DLLs_x86_VC7/000077500000000000000000000000001323200062600151035ustar00rootroot00000000000000Shapely-1.6.4/DLLs_x86_VC7/README.txt000066400000000000000000000000111323200062600165710ustar00rootroot00000000000000x86 DLLs Shapely-1.6.4/DLLs_x86_VC9/000077500000000000000000000000001323200062600151055ustar00rootroot00000000000000Shapely-1.6.4/DLLs_x86_VC9/README.txt000066400000000000000000000000111323200062600165730ustar00rootroot00000000000000x86 DLLs Shapely-1.6.4/Dockerfile.wheels000066400000000000000000000011571323200062600164050ustar00rootroot00000000000000FROM quay.io/pypa/manylinux1_x86_64 ENV GEOS_VERSION 3.5.0 # Install geos RUN mkdir -p /src \ && cd /src \ && curl -f -L -O http://download.osgeo.org/geos/geos-$GEOS_VERSION.tar.bz2 \ && tar jxf geos-$GEOS_VERSION.tar.bz2 \ && cd /src/geos-$GEOS_VERSION \ && ./configure \ && make \ && make install \ && rm -rf /src # Bake dev requirements into the Docker image for faster builds ADD requirements-dev.txt /tmp/requirements-dev.txt RUN for PYBIN in /opt/python/*/bin; do \ $PYBIN/pip install -r /tmp/requirements-dev.txt ; \ done WORKDIR /io CMD ["/io/build-linux-wheels.sh"] Shapely-1.6.4/GEOS-C-API.txt000066400000000000000000000060071323200062600152110ustar00rootroot00000000000000Ctypes declarations for functions present in GEOS C API 1.4, but not present in 1.3, are listed below: lgeos.GEOS_getWKBOutputDims.restype = ctypes.c_int lgeos.GEOS_getWKBByteOrder.restype = ctypes.c_int lgeos.GEOS_setWKBByteOrder.restype = ctypes.c_int lgeos.GEOS_setWKBByteOrder.argtypes = [ctypes.c_int] lgeos.GEOSGeomFromHEX_buf.restype = ctypes.c_void_p lgeos.GEOSGeomFromHEX_buf.argtypes = [ctypes.c_void_p, ctypes.c_size_t] lgeos.GEOSSimplify.restype = ctypes.c_void_p lgeos.GEOSSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double] lgeos.GEOSTopologyPreserveSimplify.restype = ctypes.c_void_p lgeos.GEOSTopologyPreserveSimplify.argtypes = [ctypes.c_void_p, ctypes.c_double] lgeos.GEOSEqualsExact.restype = ctypes.c_int lgeos.GEOSEqualsExact.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_double] lgeos.GEOSNormalize.restype = ctypes.c_int lgeos.GEOSNormalize.argtypes = [ctypes.c_void_p] lgeos.GEOSWKTReader_create.restype = ctypes.c_void_p lgeos.GEOSWKTReader_destroy.restype = None lgeos.GEOSWKTReader_destroy.argtypes = [ctypes.c_void_p] lgeos.GEOSWKTReader_read.restype = ctypes.c_void_p lgeos.GEOSWKTReader_read.argtypes = [ctypes.c_void_p, ctypes.c_char_p] lgeos.GEOSWKTWriter_create.restype = ctypes.c_void_p lgeos.GEOSWKTWriter_destroy.restype = None lgeos.GEOSWKTWriter_destroy.argtypes = [ctypes.c_void_p] lgeos.GEOSWKTWriter_write.restype = ctypes.c_char_p lgeos.GEOSWKTWriter_write.argtypes = [ctypes.c_void_p, ctypes.c_void_p] lgeos.GEOSWKBReader_create.restype = ctypes.c_void_p lgeos.GEOSWKBReader_destroy.restype = None lgeos.GEOSWKBReader_destroy.argtypes = [ctypes.c_void_p] lgeos.GEOSWKBReader_read.restype = ctypes.c_void_p lgeos.GEOSWKBReader_read.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t] lgeos.GEOSWKBReader_readHEX.restype = ctypes.c_void_p lgeos.GEOSWKBReader_readHEX.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t] lgeos.GEOSWKBWriter_create.restype = ctypes.c_void_p lgeos.GEOSWKBWriter_destroy.restype = None lgeos.GEOSWKBWriter_destroy.argtypes = [ctypes.c_void_p] lgeos.GEOSWKBWriter_getOutputDimension.restype = ctypes.c_int lgeos.GEOSWKBWriter_getOutputDimension.argtypes = [ctypes.c_void_p] lgeos.GEOSWKBWriter_setOutputDimension.restype = None lgeos.GEOSWKBWriter_setOutputDimension.argtypes = [ctypes.c_void_p, ctypes.c_int] lgeos.GEOSWKBWriter_getByteOrder.restype = ctypes.c_int lgeos.GEOSWKBWriter_getByteOrder.argtypes = [ctypes.c_void_p] lgeos.GEOSWKBWriter_setByteOrder.restype = None lgeos.GEOSWKBWriter_setByteOrder.argtypes = [ctypes.c_void_p, ctypes.c_int] lgeos.GEOSWKBWriter_getIncludeSRID.restype = ctypes.c_char lgeos.GEOSWKBWriter_getIncludeSRID.argtypes = [ctypes.c_void_p] lgeos.GEOSWKBWriter_setIncludeSRID.restype = None lgeos.GEOSWKBWriter_setIncludeSRID.argtypes = [ctypes.c_void_p, ctypes.c_char] Furthermore, the following unneeded declarations are removed to avoid problems with Debian's 2.2.3 package: lgeos.GEOSCoordSeq_getOrdinate.restype = ctypes.c_int lgeos.GEOSCoordSeq_getOrdinate.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p] Shapely-1.6.4/LICENSE.txt000066400000000000000000000027571323200062600147570ustar00rootroot00000000000000 Copyright (c) 2007, Sean C. Gillies All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sean C. Gillies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Shapely-1.6.4/MANIFEST.in000066400000000000000000000006331323200062600146610ustar00rootroot00000000000000prune manual prune debian prune docs prune DLLs_AMD64 prune DLLs_x86 exclude *.txt exclude MANIFEST.in include CHANGES.txt CREDITS.txt LICENSE.txt README.rst VERSION.txt recursive-include _vendor *.py recursive-include tests *.py *.txt recursive-include shapely/examples *.py recursive-include shapely/speedups *.pyx recursive-include shapely/vectorized *.pyx recursive-include shapely *.pxi include docs/*.rst Shapely-1.6.4/README.rst000066400000000000000000000114511323200062600146120ustar00rootroot00000000000000======= Shapely ======= .. image:: https://travis-ci.org/Toblerity/Shapely.png?branch=master :target: https://travis-ci.org/Toblerity/Shapely .. image:: https://coveralls.io/repos/github/Toblerity/Shapely/badge.svg?branch=master :target: https://coveralls.io/github/Toblerity/Shapely?branch=master Manipulation and analysis of geometric objects in the Cartesian plane. .. image:: https://c2.staticflickr.com/6/5560/31301790086_b3472ea4e9_c.jpg :width: 800 :height: 378 Shapely is a BSD-licensed Python package for manipulation and analysis of planar geometric objects. It is based on the widely deployed `GEOS `__ (the engine of `PostGIS `__) and `JTS `__ (from which GEOS is ported) libraries. Shapely is not concerned with data formats or coordinate systems, but can be readily integrated with packages that are. For more details, see: * `Shapely GitHub repository `__ * `Shapely documentation and manual `__ Requirements ============ Shapely 1.6 requires * Python >=2.6 (including Python 3.x) * GEOS >=3.3 Installing Shapely 1.6 ====================== Shapely may be installed from a source distribution or one of several kinds of built distribution. Built distributions ------------------- Windows users have two good installation options: the wheels at http://www.lfd.uci.edu/~gohlke/pythonlibs/#shapely and the Anaconda platform's [conda-forge](https://conda-forge.github.io/) channel. OS X and Linux users can get Shapely wheels with GEOS included from the Python Package Index with a recent version of pip (8+): .. code-block:: console $ pip install shapely A few extra speedups that require Numpy can be had by running .. code-block:: console $ pip install shapely[vectorized] Shapely is available via system package management tools like apt, yum, and Homebrew, and is also provided by popular Python distributions like Canopy and Anaconda. Source distributions -------------------- If you want to build Shapely from source for compatibility with other modules that depend on GEOS (such as cartopy or osgeo.ogr) or want to use a different version of GEOS than the one included in the project wheels you should first install the GEOS library, Cython, and Numpy on your system (using apt, yum, brew, or other means) and then direct pip to ignore the binary wheels. .. code-block:: console $ pip install shapely --no-binary shapely If you've installed GEOS to a standard location, the geos-config program will be used to get compiler and linker options. If geos-config is not on your executable, it can be specified with a GEOS_CONFIG environment variable, e.g.: .. code-block:: console $ GEOS_CONFIG=/path/to/geos-config pip install shapely Usage ===== Here is the canonical example of building an approximately circular patch by buffering a point. .. code-block:: pycon >>> from shapely.geometry import Point >>> patch = Point(0.0, 0.0).buffer(10.0) >>> patch >>> patch.area 313.65484905459385 See the manual for comprehensive usage snippets and the dissolve.py and intersect.py examples. Integration =========== Shapely does not read or write data files, but it can serialize and deserialize using several well known formats and protocols. The shapely.wkb and shapely.wkt modules provide dumpers and loaders inspired by Python's pickle module. .. code-block:: pycon >>> from shapely.wkt import dumps, loads >>> dumps(loads('POINT (0 0)')) 'POINT (0.0000000000000000 0.0000000000000000)' Shapely can also integrate with other Python GIS packages using GeoJSON-like dicts. .. code-block:: pycon >>> import json >>> from shapely.geometry import mapping, shape >>> s = shape(json.loads('{"type": "Point", "coordinates": [0.0, 0.0]}')) >>> s >>> print(json.dumps(mapping(s))) {"type": "Point", "coordinates": [0.0, 0.0]} Development and Testing ======================= Dependencies for developing Shapely are listed in requirements-dev.txt. Cython and Numpy are not required for production installations, only for development. Use of a virtual environment is strongly recommended. .. code-block:: console $ virtualenv . $ source bin/activate (env)$ pip install -r requirements-dev.txt (env)$ pip install -e . We use py.test to run Shapely's suite of unittests and doctests. .. code-block:: console (env)$ python -m pytest Support ======= Questions about using Shapely may be asked on the `GIS StackExchange `__ using the "shapely" tag. Bugs may be reported at https://github.com/Toblerity/Shapely/issues. Shapely-1.6.4/_vendor/000077500000000000000000000000001323200062600145555ustar00rootroot00000000000000Shapely-1.6.4/_vendor/__init__.py000066400000000000000000000000001323200062600166540ustar00rootroot00000000000000Shapely-1.6.4/_vendor/packaging/000077500000000000000000000000001323200062600165015ustar00rootroot00000000000000Shapely-1.6.4/_vendor/packaging/__about__.py000066400000000000000000000013201323200062600207550ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function __all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", ] __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" __version__ = "16.7" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" __license__ = "BSD or Apache License, Version 2.0" __copyright__ = "Copyright 2014-2016 %s" % __author__ Shapely-1.6.4/_vendor/packaging/__init__.py000066400000000000000000000010011323200062600206020ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function from .__about__ import ( __author__, __copyright__, __email__, __license__, __summary__, __title__, __uri__, __version__ ) __all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", ] Shapely-1.6.4/_vendor/packaging/_compat.py000066400000000000000000000015341323200062600205000ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import sys PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 # flake8: noqa if PY3: string_types = str, else: string_types = basestring, def with_metaclass(meta, *bases): """ Create a base class with a metaclass. """ # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, 'temporary_class', (), {}) Shapely-1.6.4/_vendor/packaging/_structures.py000066400000000000000000000026101323200062600214340ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function class Infinity(object): def __repr__(self): return "Infinity" def __hash__(self): return hash(repr(self)) def __lt__(self, other): return False def __le__(self, other): return False def __eq__(self, other): return isinstance(other, self.__class__) def __ne__(self, other): return not isinstance(other, self.__class__) def __gt__(self, other): return True def __ge__(self, other): return True def __neg__(self): return NegativeInfinity Infinity = Infinity() class NegativeInfinity(object): def __repr__(self): return "-Infinity" def __hash__(self): return hash(repr(self)) def __lt__(self, other): return True def __le__(self, other): return True def __eq__(self, other): return isinstance(other, self.__class__) def __ne__(self, other): return not isinstance(other, self.__class__) def __gt__(self, other): return False def __ge__(self, other): return False def __neg__(self): return Infinity NegativeInfinity = NegativeInfinity() Shapely-1.6.4/_vendor/packaging/markers.py000066400000000000000000000173041323200062600205240ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import operator import os import platform import sys from pyparsing import ParseException, ParseResults, stringStart, stringEnd from pyparsing import ZeroOrMore, Group, Forward, QuotedString from pyparsing import Literal as L # noqa from ._compat import string_types from .specifiers import Specifier, InvalidSpecifier __all__ = [ "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", "Marker", "default_environment", ] class InvalidMarker(ValueError): """ An invalid marker was found, users should refer to PEP 508. """ class UndefinedComparison(ValueError): """ An invalid operation was attempted on a value that doesn't support it. """ class UndefinedEnvironmentName(ValueError): """ A name was attempted to be used that does not exist inside of the environment. """ class Node(object): def __init__(self, value): self.value = value def __str__(self): return str(self.value) def __repr__(self): return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) class Variable(Node): pass class Value(Node): pass VARIABLE = ( L("implementation_version") | L("platform_python_implementation") | L("implementation_name") | L("python_full_version") | L("platform_release") | L("platform_version") | L("platform_machine") | L("platform_system") | L("python_version") | L("sys_platform") | L("os_name") | L("os.name") | # PEP-345 L("sys.platform") | # PEP-345 L("platform.version") | # PEP-345 L("platform.machine") | # PEP-345 L("platform.python_implementation") | # PEP-345 L("python_implementation") | # undocumented setuptools legacy L("extra") ) ALIASES = { 'os.name': 'os_name', 'sys.platform': 'sys_platform', 'platform.version': 'platform_version', 'platform.machine': 'platform_machine', 'platform.python_implementation': 'platform_python_implementation', 'python_implementation': 'platform_python_implementation' } VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) VERSION_CMP = ( L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") ) MARKER_OP = VERSION_CMP | L("not in") | L("in") MARKER_VALUE = QuotedString("'") | QuotedString('"') MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) BOOLOP = L("and") | L("or") MARKER_VAR = VARIABLE | MARKER_VALUE MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) LPAREN = L("(").suppress() RPAREN = L(")").suppress() MARKER_EXPR = Forward() MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) MARKER = stringStart + MARKER_EXPR + stringEnd def _coerce_parse_result(results): if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: return results def _format_marker(marker, first=True): assert isinstance(marker, (list, tuple, string_types)) # Sometimes we have a structure like [[...]] which is a single item list # where the single item is itself it's own list. In that case we want skip # the rest of this function so that we don't get extraneous () on the # outside. if (isinstance(marker, list) and len(marker) == 1 and isinstance(marker[0], (list, tuple))): return _format_marker(marker[0]) if isinstance(marker, list): inner = (_format_marker(m, first=False) for m in marker) if first: return " ".join(inner) else: return "(" + " ".join(inner) + ")" elif isinstance(marker, tuple): return '{0} {1} "{2}"'.format(*marker) else: return marker _operators = { "in": lambda lhs, rhs: lhs in rhs, "not in": lambda lhs, rhs: lhs not in rhs, "<": operator.lt, "<=": operator.le, "==": operator.eq, "!=": operator.ne, ">=": operator.ge, ">": operator.gt, } def _eval_op(lhs, op, rhs): try: spec = Specifier("".join([op, rhs])) except InvalidSpecifier: pass else: return spec.contains(lhs) oper = _operators.get(op) if oper is None: raise UndefinedComparison( "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) ) return oper(lhs, rhs) _undefined = object() def _get_env(environment, name): value = environment.get(name, _undefined) if value is _undefined: raise UndefinedEnvironmentName( "{0!r} does not exist in evaluation environment.".format(name) ) return value def _evaluate_markers(markers, environment): groups = [[]] for marker in markers: assert isinstance(marker, (list, tuple, string_types)) if isinstance(marker, list): groups[-1].append(_evaluate_markers(marker, environment)) elif isinstance(marker, tuple): lhs, op, rhs = marker if isinstance(lhs, Variable): lhs_value = _get_env(environment, lhs.value) rhs_value = rhs.value else: lhs_value = lhs.value rhs_value = _get_env(environment, rhs.value) groups[-1].append(_eval_op(lhs_value, op, rhs_value)) else: assert marker in ["and", "or"] if marker == "or": groups.append([]) return any(all(item) for item in groups) def format_full_version(info): version = '{0.major}.{0.minor}.{0.micro}'.format(info) kind = info.releaselevel if kind != 'final': version += kind[0] + str(info.serial) return version def default_environment(): if hasattr(sys, 'implementation'): iver = format_full_version(sys.implementation.version) implementation_name = sys.implementation.name else: iver = '0' implementation_name = '' return { "implementation_name": implementation_name, "implementation_version": iver, "os_name": os.name, "platform_machine": platform.machine(), "platform_release": platform.release(), "platform_system": platform.system(), "platform_version": platform.version(), "python_full_version": platform.python_version(), "platform_python_implementation": platform.python_implementation(), "python_version": platform.python_version()[:3], "sys_platform": sys.platform, } class Marker(object): def __init__(self, marker): try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( marker, marker[e.loc:e.loc + 8]) raise InvalidMarker(err_str) def __str__(self): return _format_marker(self._markers) def __repr__(self): return "".format(str(self)) def evaluate(self, environment=None): """Evaluate a marker. Return the boolean from evaluating the given marker against the environment. environment is an optional argument to override all or part of the determined environment. The environment is determined from the current Python process. """ current_environment = default_environment() if environment is not None: current_environment.update(environment) return _evaluate_markers(self._markers, current_environment) Shapely-1.6.4/_vendor/packaging/requirements.py000066400000000000000000000102571323200062600216030ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import string import re from pyparsing import stringStart, stringEnd, originalTextFor, ParseException from pyparsing import ZeroOrMore, Word, Optional, Regex, Combine from pyparsing import Literal as L # noqa from six.moves.urllib import parse as urlparse from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet class InvalidRequirement(ValueError): """ An invalid requirement was found, users should refer to PEP 508. """ ALPHANUM = Word(string.ascii_letters + string.digits) LBRACKET = L("[").suppress() RBRACKET = L("]").suppress() LPAREN = L("(").suppress() RPAREN = L(")").suppress() COMMA = L(",").suppress() SEMICOLON = L(";").suppress() AT = L("@").suppress() PUNCTUATION = Word("-_.") IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) NAME = IDENTIFIER("name") EXTRA = IDENTIFIER URI = Regex(r'[^ ]+')("url") URL = (AT + URI) EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False)("_raw_spec") _VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) _VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") MARKER_EXPR.setParseAction( lambda s, l, t: Marker(s[t._original_start:t._original_end]) ) MARKER_SEPERATOR = SEMICOLON MARKER = MARKER_SEPERATOR + MARKER_EXPR VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) URL_AND_MARKER = URL + Optional(MARKER) NAMED_REQUIREMENT = \ NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd class Requirement(object): """Parse a requirement. Parse a given requirement string into its parts, such as name, specifier, URL, and extras. Raises InvalidRequirement on a badly-formed requirement string. """ # TODO: Can we test whether something is contained within a requirement? # If so how do we do that? Do we need to test against the _name_ of # the thing as well as the version? What about the markers? # TODO: Can we normalize the name and extra name? def __init__(self, requirement_string): try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( "Invalid requirement, parse error at \"{0!r}\"".format( requirement_string[e.loc:e.loc + 8])) self.name = req.name if req.url: parsed_url = urlparse.urlparse(req.url) if not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc): raise InvalidRequirement("Invalid URL given") self.url = req.url else: self.url = None self.extras = set(req.extras.asList() if req.extras else []) self.specifier = SpecifierSet(req.specifier) self.marker = req.marker if req.marker else None def __str__(self): parts = [self.name] if self.extras: parts.append("[{0}]".format(",".join(sorted(self.extras)))) if self.specifier: parts.append(str(self.specifier)) if self.url: parts.append("@ {0}".format(self.url)) if self.marker: parts.append("; {0}".format(self.marker)) return "".join(parts) def __repr__(self): return "".format(str(self)) Shapely-1.6.4/_vendor/packaging/specifiers.py000066400000000000000000000665711323200062600212260ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import abc import functools import itertools import re from ._compat import string_types, with_metaclass from .version import Version, LegacyVersion, parse class InvalidSpecifier(ValueError): """ An invalid specifier was found, users should refer to PEP 440. """ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def __str__(self): """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. """ @abc.abstractmethod def __hash__(self): """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod def __eq__(self, other): """ Returns a boolean representing whether or not the two Specifier like objects are equal. """ @abc.abstractmethod def __ne__(self, other): """ Returns a boolean representing whether or not the two Specifier like objects are not equal. """ @abc.abstractproperty def prereleases(self): """ Returns whether or not pre-releases as a whole are allowed by this specifier. """ @prereleases.setter def prereleases(self, value): """ Sets whether or not pre-releases as a whole are allowed by this specifier. """ @abc.abstractmethod def contains(self, item, prereleases=None): """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod def filter(self, iterable, prereleases=None): """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. """ class _IndividualSpecifier(BaseSpecifier): _operators = {} def __init__(self, spec="", prereleases=None): match = self._regex.search(spec) if not match: raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) self._spec = ( match.group("operator").strip(), match.group("version").strip(), ) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases def __repr__(self): pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None else "" ) return "<{0}({1!r}{2})>".format( self.__class__.__name__, str(self), pre, ) def __str__(self): return "{0}{1}".format(*self._spec) def __hash__(self): return hash(self._spec) def __eq__(self, other): if isinstance(other, string_types): try: other = self.__class__(other) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented return self._spec == other._spec def __ne__(self, other): if isinstance(other, string_types): try: other = self.__class__(other) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented return self._spec != other._spec def _get_operator(self, op): return getattr(self, "_compare_{0}".format(self._operators[op])) def _coerce_version(self, version): if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property def operator(self): return self._spec[0] @property def version(self): return self._spec[1] @property def prereleases(self): return self._prereleases @prereleases.setter def prereleases(self, value): self._prereleases = value def __contains__(self, item): return self.contains(item) def contains(self, item, prereleases=None): # Determine if prereleases are to be allowed or not. if prereleases is None: prereleases = self.prereleases # Normalize item to a Version or LegacyVersion, this allows us to have # a shortcut for ``"2.0" in Specifier(">=2") item = self._coerce_version(item) # Determine if we should be supporting prereleases in this specifier # or not, if we do not support prereleases than we can short circuit # logic if this version is a prereleases. if item.is_prerelease and not prereleases: return False # Actually do the comparison to determine if this item is contained # within this Specifier or not. return self._get_operator(self.operator)(item, self.version) def filter(self, iterable, prereleases=None): yielded = False found_prereleases = [] kw = {"prereleases": prereleases if prereleases is not None else True} # Attempt to iterate over all the values in the iterable and if any of # them match, yield them. for version in iterable: parsed_version = self._coerce_version(version) if self.contains(parsed_version, **kw): # If our version is a prerelease, and we were not set to allow # prereleases, then we'll store it for later incase nothing # else matches this specifier. if (parsed_version.is_prerelease and not (prereleases or self.prereleases)): found_prereleases.append(version) # Either this is not a prerelease, or we should have been # accepting prereleases from the begining. else: yielded = True yield version # Now that we've iterated over everything, determine if we've yielded # any values, and if we have not and we have any prereleases stored up # then we will go ahead and yield the prereleases. if not yielded and found_prereleases: for version in found_prereleases: yield version class LegacySpecifier(_IndividualSpecifier): _regex_str = ( r""" (?P(==|!=|<=|>=|<|>)) \s* (?P [^,;\s)]* # Since this is a "legacy" specifier, and the version # string can be just about anything, we match everything # except for whitespace, a semi-colon for marker support, # a closing paren since versions can be enclosed in # them, and a comma since it's a version separator. ) """ ) _regex = re.compile( r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) _operators = { "==": "equal", "!=": "not_equal", "<=": "less_than_equal", ">=": "greater_than_equal", "<": "less_than", ">": "greater_than", } def _coerce_version(self, version): if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version def _compare_equal(self, prospective, spec): return prospective == self._coerce_version(spec) def _compare_not_equal(self, prospective, spec): return prospective != self._coerce_version(spec) def _compare_less_than_equal(self, prospective, spec): return prospective <= self._coerce_version(spec) def _compare_greater_than_equal(self, prospective, spec): return prospective >= self._coerce_version(spec) def _compare_less_than(self, prospective, spec): return prospective < self._coerce_version(spec) def _compare_greater_than(self, prospective, spec): return prospective > self._coerce_version(spec) def _require_version_compare(fn): @functools.wraps(fn) def wrapped(self, prospective, spec): if not isinstance(prospective, Version): return False return fn(self, prospective, spec) return wrapped class Specifier(_IndividualSpecifier): _regex_str = ( r""" (?P(~=|==|!=|<=|>=|<|>|===)) (?P (?: # The identity operators allow for an escape hatch that will # do an exact string match of the version you wish to install. # This will not be parsed by PEP 440 and we cannot determine # any semantic meaning from it. This operator is discouraged # but included entirely as an escape hatch. (?<====) # Only match for the identity operator \s* [^\s]* # We just match everything, except for whitespace # since we are only testing for strict identity. ) | (?: # The (non)equality operators allow for wild card and local # versions to be specified so we have to define these two # operators separately to enable that. (?<===|!=) # Only match for equals and not equals \s* v? (?:[0-9]+!)? # epoch [0-9]+(?:\.[0-9]+)* # release (?: # pre release [-_\.]? (a|b|c|rc|alpha|beta|pre|preview) [-_\.]? [0-9]* )? (?: # post release (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) )? # You cannot use a wild card and a dev or local version # together so group them with a | and make them optional. (?: (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local | \.\* # Wild card syntax of .* )? ) | (?: # The compatible operator requires at least two digits in the # release segment. (?<=~=) # Only match for the compatible operator \s* v? (?:[0-9]+!)? # epoch [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) (?: # pre release [-_\.]? (a|b|c|rc|alpha|beta|pre|preview) [-_\.]? [0-9]* )? (?: # post release (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) )? (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release ) | (?: # All other operators only allow a sub set of what the # (non)equality operators do. Specifically they do not allow # local versions to be specified nor do they allow the prefix # matching wild cards. (?=": "greater_than_equal", "<": "less_than", ">": "greater_than", "===": "arbitrary", } @_require_version_compare def _compare_compatible(self, prospective, spec): # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to # implement this in terms of the other specifiers instead of # implementing it ourselves. The only thing we need to do is construct # the other specifiers. # We want everything but the last item in the version, but we want to # ignore post and dev releases and we want to treat the pre-release as # it's own separate segment. prefix = ".".join( list( itertools.takewhile( lambda x: (not x.startswith("post") and not x.startswith("dev")), _version_split(spec), ) )[:-1] ) # Add the prefix notation to the end of our string prefix += ".*" return (self._get_operator(">=")(prospective, spec) and self._get_operator("==")(prospective, prefix)) @_require_version_compare def _compare_equal(self, prospective, spec): # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. prospective = Version(prospective.public) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. spec = _version_split(spec[:-2]) # Remove the trailing .* # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. prospective = _version_split(str(prospective)) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. prospective = prospective[:len(spec)] # Pad out our two sides with zeros so that they both equal the same # length. spec, prospective = _pad_version(spec, prospective) else: # Convert our spec string into a Version spec = Version(spec) # If the specifier does not have a local segment, then we want to # act as if the prospective version also does not have a local # segment. if not spec.local: prospective = Version(prospective.public) return prospective == spec @_require_version_compare def _compare_not_equal(self, prospective, spec): return not self._compare_equal(prospective, spec) @_require_version_compare def _compare_less_than_equal(self, prospective, spec): return prospective <= Version(spec) @_require_version_compare def _compare_greater_than_equal(self, prospective, spec): return prospective >= Version(spec) @_require_version_compare def _compare_less_than(self, prospective, spec): # Convert our spec to a Version instance, since we'll want to work with # it as a version. spec = Version(spec) # Check to see if the prospective version is less than the spec # version. If it's not we can short circuit and just return False now # instead of doing extra unneeded work. if not prospective < spec: return False # This special case is here so that, unless the specifier itself # includes is a pre-release version, that we do not accept pre-release # versions for the version mentioned in the specifier (e.g. <3.1 should # not match 3.1.dev0, but should match 3.0.dev0). if not spec.is_prerelease and prospective.is_prerelease: if Version(prospective.base_version) == Version(spec.base_version): return False # If we've gotten to here, it means that prospective version is both # less than the spec version *and* it's not a pre-release of the same # version in the spec. return True @_require_version_compare def _compare_greater_than(self, prospective, spec): # Convert our spec to a Version instance, since we'll want to work with # it as a version. spec = Version(spec) # Check to see if the prospective version is greater than the spec # version. If it's not we can short circuit and just return False now # instead of doing extra unneeded work. if not prospective > spec: return False # This special case is here so that, unless the specifier itself # includes is a post-release version, that we do not accept # post-release versions for the version mentioned in the specifier # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). if not spec.is_postrelease and prospective.is_postrelease: if Version(prospective.base_version) == Version(spec.base_version): return False # Ensure that we do not allow a local version of the version mentioned # in the specifier, which is techincally greater than, to match. if prospective.local is not None: if Version(prospective.base_version) == Version(spec.base_version): return False # If we've gotten to here, it means that prospective version is both # greater than the spec version *and* it's not a pre-release of the # same version in the spec. return True def _compare_arbitrary(self, prospective, spec): return str(prospective).lower() == str(spec).lower() @property def prereleases(self): # If there is an explicit prereleases set for this, then we'll just # blindly use that. if self._prereleases is not None: return self._prereleases # Look at all of our specifiers and determine if they are inclusive # operators, and if they are if they are including an explicit # prerelease. operator, version = self._spec if operator in ["==", ">=", "<=", "~=", "==="]: # The == specifier can include a trailing .*, if it does we # want to remove before parsing. if operator == "==" and version.endswith(".*"): version = version[:-2] # Parse the version, and if it is a pre-release than this # specifier allows pre-releases. if parse(version).is_prerelease: return True return False @prereleases.setter def prereleases(self, value): self._prereleases = value _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") def _version_split(version): result = [] for item in version.split("."): match = _prefix_regex.search(item) if match: result.extend(match.groups()) else: result.append(item) return result def _pad_version(left, right): left_split, right_split = [], [] # Get the release segment of our versions left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) # Get the rest of our versions left_split.append(left[len(left_split[0]):]) right_split.append(right[len(right_split[0]):]) # Insert our padding left_split.insert( 1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])), ) right_split.insert( 1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])), ) return ( list(itertools.chain(*left_split)), list(itertools.chain(*right_split)), ) class SpecifierSet(BaseSpecifier): def __init__(self, specifiers="", prereleases=None): # Split on , to break each indidivual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. parsed = set() for specifier in specifiers: try: parsed.add(Specifier(specifier)) except InvalidSpecifier: parsed.add(LegacySpecifier(specifier)) # Turn our parsed specifiers into a frozen set and save them for later. self._specs = frozenset(parsed) # Store our prereleases value so we can use it later to determine if # we accept prereleases or not. self._prereleases = prereleases def __repr__(self): pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None else "" ) return "".format(str(self), pre) def __str__(self): return ",".join(sorted(str(s) for s in self._specs)) def __hash__(self): return hash(self._specs) def __and__(self, other): if isinstance(other, string_types): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): return NotImplemented specifier = SpecifierSet() specifier._specs = frozenset(self._specs | other._specs) if self._prereleases is None and other._prereleases is not None: specifier._prereleases = other._prereleases elif self._prereleases is not None and other._prereleases is None: specifier._prereleases = self._prereleases elif self._prereleases == other._prereleases: specifier._prereleases = self._prereleases else: raise ValueError( "Cannot combine SpecifierSets with True and False prerelease " "overrides." ) return specifier def __eq__(self, other): if isinstance(other, string_types): other = SpecifierSet(other) elif isinstance(other, _IndividualSpecifier): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs == other._specs def __ne__(self, other): if isinstance(other, string_types): other = SpecifierSet(other) elif isinstance(other, _IndividualSpecifier): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented return self._specs != other._specs def __len__(self): return len(self._specs) def __iter__(self): return iter(self._specs) @property def prereleases(self): # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: return self._prereleases # If we don't have any specifiers, and we don't have a forced value, # then we'll just return None since we don't know if this should have # pre-releases or not. if not self._specs: return None # Otherwise we'll see if any of the given specifiers accept # prereleases, if any of them do we'll return True, otherwise False. return any(s.prereleases for s in self._specs) @prereleases.setter def prereleases(self, value): self._prereleases = value def __contains__(self, item): return self.contains(item) def contains(self, item, prereleases=None): # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): item = parse(item) # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. if prereleases is None: prereleases = self.prereleases # We can determine if we're going to allow pre-releases by looking to # see if any of the underlying items supports them. If none of them do # and this item is a pre-release then we do not allow it and we can # short circuit that here. # Note: This means that 1.0.dev1 would not be contained in something # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 if not prereleases and item.is_prerelease: return False # We simply dispatch to the underlying specs here to make sure that the # given version is contained within all of them. # Note: This use of all() here means that an empty set of specifiers # will always return True, this is an explicit design decision. return all( s.contains(item, prereleases=prereleases) for s in self._specs ) def filter(self, iterable, prereleases=None): # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. if prereleases is None: prereleases = self.prereleases # If we have any specifiers, then we want to wrap our iterable in the # filter method for each one, this will act as a logical AND amongst # each specifier. if self._specs: for spec in self._specs: iterable = spec.filter(iterable, prereleases=bool(prereleases)) return iterable # If we do not have any specifiers, then we need to have a rough filter # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: filtered = [] found_prereleases = [] for item in iterable: # Ensure that we some kind of Version class for this item. if not isinstance(item, (LegacyVersion, Version)): parsed_version = parse(item) else: parsed_version = item # Filter out any item which is parsed as a LegacyVersion if isinstance(parsed_version, LegacyVersion): continue # Store any item which is a pre-release for later unless we've # already found a final version or we are accepting prereleases if parsed_version.is_prerelease and not prereleases: if not filtered: found_prereleases.append(item) else: filtered.append(item) # If we've found no items except for pre-releases, then we'll go # ahead and use the pre-releases if not filtered and found_prereleases and prereleases is None: return found_prereleases return filtered Shapely-1.6.4/_vendor/packaging/utils.py000066400000000000000000000006451323200062600202200ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import re _canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): # This is taken from PEP 503. return _canonicalize_regex.sub("-", name).lower() Shapely-1.6.4/_vendor/packaging/version.py000066400000000000000000000264441323200062600205520ustar00rootroot00000000000000# This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. from __future__ import absolute_import, division, print_function import collections import itertools import re from ._structures import Infinity __all__ = [ "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" ] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"], ) def parse(version): """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is a valid PEP 440 version or a legacy version. """ try: return Version(version) except InvalidVersion: return LegacyVersion(version) class InvalidVersion(ValueError): """ An invalid version was found, users should refer to PEP 440. """ class _BaseVersion(object): def __hash__(self): return hash(self._key) def __lt__(self, other): return self._compare(other, lambda s, o: s < o) def __le__(self, other): return self._compare(other, lambda s, o: s <= o) def __eq__(self, other): return self._compare(other, lambda s, o: s == o) def __ge__(self, other): return self._compare(other, lambda s, o: s >= o) def __gt__(self, other): return self._compare(other, lambda s, o: s > o) def __ne__(self, other): return self._compare(other, lambda s, o: s != o) def _compare(self, other, method): if not isinstance(other, _BaseVersion): return NotImplemented return method(self._key, other._key) class LegacyVersion(_BaseVersion): def __init__(self, version): self._version = str(version) self._key = _legacy_cmpkey(self._version) def __str__(self): return self._version def __repr__(self): return "".format(repr(str(self))) @property def public(self): return self._version @property def base_version(self): return self._version @property def local(self): return None @property def is_prerelease(self): return False @property def is_postrelease(self): return False _legacy_version_component_re = re.compile( r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, ) _legacy_version_replacement_map = { "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", } def _parse_version_parts(s): for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) if not part or part == ".": continue if part[:1] in "0123456789": # pad for numeric comparison yield part.zfill(8) else: yield "*" + part # ensure that alpha/beta/candidate are before final yield "*final" def _legacy_cmpkey(version): # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, # as before all PEP 440 versions. epoch = -1 # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. parts = [] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag if part < "*final": while parts and parts[-1] == "*final-": parts.pop() # remove trailing zeros from each series of numeric parts while parts and parts[-1] == "00000000": parts.pop() parts.append(part) parts = tuple(parts) return epoch, parts # Deliberately not anchored to the start and end of the string, to make it # easier for 3rd party code to reuse VERSION_PATTERN = r""" v? (?: (?:(?P[0-9]+)!)? # epoch (?P[0-9]+(?:\.[0-9]+)*) # release segment (?P
                                          # pre-release
            [-_\.]?
            (?P(a|b|c|rc|alpha|beta|pre|preview))
            [-_\.]?
            (?P[0-9]+)?
        )?
        (?P                                         # post release
            (?:-(?P[0-9]+))
            |
            (?:
                [-_\.]?
                (?Ppost|rev|r)
                [-_\.]?
                (?P[0-9]+)?
            )
        )?
        (?P                                          # dev release
            [-_\.]?
            (?Pdev)
            [-_\.]?
            (?P[0-9]+)?
        )?
    )
    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
"""


class Version(_BaseVersion):

    _regex = re.compile(
        r"^\s*" + VERSION_PATTERN + r"\s*$",
        re.VERBOSE | re.IGNORECASE,
    )

    def __init__(self, version):
        # Validate the version and parse it into pieces
        match = self._regex.search(version)
        if not match:
            raise InvalidVersion("Invalid version: '{0}'".format(version))

        # Store the parsed out pieces of the version
        self._version = _Version(
            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
            release=tuple(int(i) for i in match.group("release").split(".")),
            pre=_parse_letter_version(
                match.group("pre_l"),
                match.group("pre_n"),
            ),
            post=_parse_letter_version(
                match.group("post_l"),
                match.group("post_n1") or match.group("post_n2"),
            ),
            dev=_parse_letter_version(
                match.group("dev_l"),
                match.group("dev_n"),
            ),
            local=_parse_local_version(match.group("local")),
        )

        # Generate a key which will be used for sorting
        self._key = _cmpkey(
            self._version.epoch,
            self._version.release,
            self._version.pre,
            self._version.post,
            self._version.dev,
            self._version.local,
        )

    def __repr__(self):
        return "".format(repr(str(self)))

    def __str__(self):
        parts = []

        # Epoch
        if self._version.epoch != 0:
            parts.append("{0}!".format(self._version.epoch))

        # Release segment
        parts.append(".".join(str(x) for x in self._version.release))

        # Pre-release
        if self._version.pre is not None:
            parts.append("".join(str(x) for x in self._version.pre))

        # Post-release
        if self._version.post is not None:
            parts.append(".post{0}".format(self._version.post[1]))

        # Development release
        if self._version.dev is not None:
            parts.append(".dev{0}".format(self._version.dev[1]))

        # Local version segment
        if self._version.local is not None:
            parts.append(
                "+{0}".format(".".join(str(x) for x in self._version.local))
            )

        return "".join(parts)

    @property
    def public(self):
        return str(self).split("+", 1)[0]

    @property
    def base_version(self):
        parts = []

        # Epoch
        if self._version.epoch != 0:
            parts.append("{0}!".format(self._version.epoch))

        # Release segment
        parts.append(".".join(str(x) for x in self._version.release))

        return "".join(parts)

    @property
    def local(self):
        version_string = str(self)
        if "+" in version_string:
            return version_string.split("+", 1)[1]

    @property
    def is_prerelease(self):
        return bool(self._version.dev or self._version.pre)

    @property
    def is_postrelease(self):
        return bool(self._version.post)


def _parse_letter_version(letter, number):
    if letter:
        # We consider there to be an implicit 0 in a pre-release if there is
        # not a numeral associated with it.
        if number is None:
            number = 0

        # We normalize any letters to their lower case form
        letter = letter.lower()

        # We consider some words to be alternate spellings of other words and
        # in those cases we want to normalize the spellings to our preferred
        # spelling.
        if letter == "alpha":
            letter = "a"
        elif letter == "beta":
            letter = "b"
        elif letter in ["c", "pre", "preview"]:
            letter = "rc"
        elif letter in ["rev", "r"]:
            letter = "post"

        return letter, int(number)
    if not letter and number:
        # We assume if we are given a number, but we are not given a letter
        # then this is using the implicit post release syntax (e.g. 1.0-1)
        letter = "post"

        return letter, int(number)


_local_version_seperators = re.compile(r"[\._-]")


def _parse_local_version(local):
    """
    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
    """
    if local is not None:
        return tuple(
            part.lower() if not part.isdigit() else int(part)
            for part in _local_version_seperators.split(local)
        )


def _cmpkey(epoch, release, pre, post, dev, local):
    # When we compare a release version, we want to compare it with all of the
    # trailing zeros removed. So we'll use a reverse the list, drop all the now
    # leading zeros until we come to something non zero, then take the rest
    # re-reverse it back into the correct order and make it a tuple and use
    # that for our sorting key.
    release = tuple(
        reversed(list(
            itertools.dropwhile(
                lambda x: x == 0,
                reversed(release),
            )
        ))
    )

    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
    # We'll do this by abusing the pre segment, but we _only_ want to do this
    # if there is not a pre or a post segment. If we have one of those then
    # the normal sorting rules will handle this case correctly.
    if pre is None and post is None and dev is not None:
        pre = -Infinity
    # Versions without a pre-release (except as noted above) should sort after
    # those with one.
    elif pre is None:
        pre = Infinity

    # Versions without a post segment should sort before those with one.
    if post is None:
        post = -Infinity

    # Versions without a development segment should sort after those with one.
    if dev is None:
        dev = Infinity

    if local is None:
        # Versions without a local segment should sort before those with one.
        local = -Infinity
    else:
        # Versions with a local segment need that segment parsed to implement
        # the sorting rules in PEP440.
        # - Alpha numeric segments sort before numeric segments
        # - Alpha numeric segments sort lexicographically
        # - Numeric segments sort numerically
        # - Shorter versions sort before longer versions when the prefixes
        #   match exactly
        local = tuple(
            (i, "") if isinstance(i, int) else (-Infinity, i)
            for i in local
        )

    return epoch, release, pre, post, dev, local
Shapely-1.6.4/build-linux-wheels.sh000077500000000000000000000013221323200062600171770ustar00rootroot00000000000000#!/bin/bash
set -eu

# Checking for /.dockerenv is a hacky way to determine whether or not we're
# already running in a Docker container. Note that this is not guaranteed to
# exist in all versions and drivers and may need to be changed later.
if [ ! -e /.dockerenv ]; then
    docker build -f Dockerfile.wheels --pull -t shapely-wheelbuilder .
    exec docker run -v `pwd`:/io shapely-wheelbuilder "$@"
fi

ORIGINAL_PATH=$PATH
UNREPAIRED_WHEELS=/tmp/wheels

# Compile wheels
for PYBIN in /opt/python/*/bin; do
    PATH=${PYBIN}:$ORIGINAL_PATH
    python setup.py bdist_wheel -d ${UNREPAIRED_WHEELS}
done

# Bundle GEOS into the wheels
for whl in ${UNREPAIRED_WHEELS}/*.whl; do
    auditwheel repair ${whl} -w wheels
done
Shapely-1.6.4/build-scripts/000077500000000000000000000000001323200062600157055ustar00rootroot00000000000000Shapely-1.6.4/build-scripts/macosx-10.6-intel.sh000066400000000000000000000006071323200062600212310ustar00rootroot00000000000000#!/bin/bash

# Dependent on the Kyngchaos Frameworks:
# http://www.kyngchaos.com/software/frameworks

export GEOS_CONFIG="/Library/Frameworks/GEOS.framework/Versions/3/unix/bin/geos-config"
CFLAGS="`$GEOS_CONFIG --cflags`" LDFLAGS="`$GEOS_CONFIG --clibs`" python setup.py bdist_wheel
delocate-wheel -w fixed_wheels --require-archs=intel -v dist/Shapely-1.5.2-cp27-none-macosx_10_6_intel.whl
Shapely-1.6.4/build-wheels.sh000066400000000000000000000016371323200062600160500ustar00rootroot00000000000000#!/bin/bash

# Automation of this is a TODO. For now, it depends on manually built libraries
# as detailed in https://gist.github.com/sgillies/a8a2fb910a98a8566d0a.

export MACOSX_DEPLOYMENT_TARGET=10.6
export GEOS_CONFIG="/usr/local/bin/geos-config"

VERSION=$1

source $HOME/envs/pydotorg27/bin/activate
touch shapely/speedups/*.pyx
touch shapely/vectorized/*.pyx
CFLAGS="`$GEOS_CONFIG --cflags`" LDFLAGS="`$GEOS_CONFIG --libs`" python setup.py bdist_wheel -d wheels/$VERSION
source $HOME/envs/pydotorg34/bin/activate
touch shapely/speedups/*.pyx
touch shapely/vectorized/*.pyx
CFLAGS="`$GEOS_CONFIG --cflags`" LDFLAGS="`$GEOS_CONFIG --libs`" python setup.py bdist_wheel -d wheels/$VERSION

parallel delocate-wheel -w fixed_wheels/$VERSION --require-archs=intel -v {} ::: wheels/$VERSION/*.whl
parallel cp {} {.}.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl ::: fixed_wheels/$VERSION/*.whl
Shapely-1.6.4/docs/000077500000000000000000000000001323200062600140515ustar00rootroot00000000000000Shapely-1.6.4/docs/Makefile000066400000000000000000000063331323200062600155160ustar00rootroot00000000000000# Makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
PAPER         =
BUILDDIR      = _build

# Internal variables.
PAPEROPT_a4     = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest

help:
	@echo "Please use \`make ' where  is one of"
	@echo "  html      to make standalone HTML files"
	@echo "  dirhtml   to make HTML files named index.html in directories"
	@echo "  pickle    to make pickle files"
	@echo "  json      to make JSON files"
	@echo "  htmlhelp  to make HTML files and a HTML help project"
	@echo "  qthelp    to make HTML files and a qthelp project"
	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
	@echo "  changes   to make an overview of all changed/added/deprecated items"
	@echo "  linkcheck to check all external links for integrity"
	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"

clean:
	-rm -rf $(BUILDDIR)/* build/plot_directive/*

html: apidocs
	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

dirhtml:
	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."

pickle:
	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
	@echo
	@echo "Build finished; now you can process the pickle files."

json:
	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
	@echo
	@echo "Build finished; now you can process the JSON files."

htmlhelp: apidocs
	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
	@echo
	@echo "Build finished; now you can run HTML Help Workshop with the" \
	      ".hhp project file in $(BUILDDIR)/htmlhelp."

qthelp:
	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
	@echo
	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Shapely.qhcp"
	@echo "To view the help file:"
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Shapely.qhc"

latex:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo
	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
	      "run these through (pdf)latex."

changes:
	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
	@echo
	@echo "The overview file is in $(BUILDDIR)/changes."

linkcheck:
	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
	@echo
	@echo "Link check complete; look for any errors in the above output " \
	      "or in $(BUILDDIR)/linkcheck/output.txt."

doctest:
	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
	@echo "Testing of doctests in the sources finished, look at the " \
	      "results in $(BUILDDIR)/doctest/output.txt."

apidocs:
	sphinx-apidoc -f -s txt -o . ../shapely
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

Shapely-1.6.4/docs/code/000077500000000000000000000000001323200062600147635ustar00rootroot00000000000000Shapely-1.6.4/docs/code/buffer.py000066400000000000000000000027321323200062600166120ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

def plot_line(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, color=GRAY, linewidth=3, solid_capstyle='round', zorder=1)

line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1
ax = fig.add_subplot(121)

plot_line(ax, line)

dilated = line.buffer(0.5, cap_style=3)
patch1 = PolygonPatch(dilated, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch1)

ax.set_title('a) dilation, cap_style=3')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

patch2a = PolygonPatch(dilated, fc=GRAY, ec=GRAY, alpha=0.5, zorder=1)
ax.add_patch(patch2a)

eroded = dilated.buffer(-0.3)

# GeoJSON-like data works as well

polygon = eroded.__geo_interface__
# >>> geo['type']
# 'Polygon'
# >>> geo['coordinates'][0][:2]
# ((0.50502525316941682, 0.78786796564403572), (0.5247963548222736, 0.8096820147509064))
patch2b = PolygonPatch(polygon, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch2b)

ax.set_title('b) erosion, join_style=1')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/cascaded_union.py000066400000000000000000000017511323200062600203000ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Point
from shapely.ops import cascaded_union
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

polygons = [Point(i, 0).buffer(0.7) for i in range(5)]

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1
ax = fig.add_subplot(121)

for ob in polygons:
    p = PolygonPatch(ob, fc=GRAY, ec=GRAY, alpha=0.5, zorder=1)
    ax.add_patch(p)

ax.set_title('a) polygons')

xrange = [-2, 6]
yrange = [-2, 2]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

u = cascaded_union(polygons)
patch2b = PolygonPatch(u, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch2b)

ax.set_title('b) union')

xrange = [-2, 6]
yrange = [-2, 2]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/convex_hull.py000066400000000000000000000022471323200062600176700ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import MultiPoint

from descartes.patch import PolygonPatch

from figures import SIZE

fig = pyplot.figure(1, figsize=SIZE, dpi=90)
fig.set_frameon(True)

# 1
ax = fig.add_subplot(121)

points2 = MultiPoint([(0, 0), (2, 2)])
for p in points2:
    ax.plot(p.x, p.y, 'o', color='#999999')
hull2 = points2.convex_hull
x, y = hull2.xy
ax.plot(x, y, color='#6699cc', linewidth=3, alpha=0.5, zorder=2)

ax.set_title('a) N = 2')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

points1 = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])

for p in points1:
    ax.plot(p.x, p.y, 'o', color='#999999')
hull1 = points1.convex_hull
patch1 = PolygonPatch(hull1, facecolor='#6699cc', edgecolor='#6699cc', alpha=0.5, zorder=2)
ax.add_patch(patch1)

ax.set_title('b) N > 2')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()


Shapely-1.6.4/docs/code/difference.py000066400000000000000000000024301323200062600174260ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Point
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

a = Point(1, 1).buffer(1.5)
b = Point(2, 1).buffer(1.5)

# 1
ax = fig.add_subplot(121)

patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch1)
patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch2)
c = a.difference(b)
patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patchc)

ax.set_title('a.difference(b)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch1)
patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch2)
c = b.difference(a)
patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patchc)

ax.set_title('b.difference(a)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/figures.py000066400000000000000000000001601323200062600167760ustar00rootroot00000000000000from math import sqrt

GM = (sqrt(5)-1.0)/2.0
W = 8.0
H = W*GM
SIZE = (W, H)

BLUE = '#6699cc'
GRAY = '#999999'
Shapely-1.6.4/docs/code/geometrycollection.py000066400000000000000000000031461323200062600212500ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString
from figures import SIZE

BLUE =   '#6699cc'
YELLOW = '#ffcc33'
GREEN =  '#339933'
GRAY =   '#999999'

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color=GRAY, zorder=1)

fig = pyplot.figure(1, figsize=SIZE, dpi=90) #1, figsize=(10, 4), dpi=180)

a = LineString([(0, 0), (1, 1), (1,2), (2,2)])
b = LineString([(0, 0), (1, 1), (2,1), (2,2)])

# 1: disconnected multilinestring
ax = fig.add_subplot(121)

plot_coords(ax, a)
plot_coords(ax, b)

x, y = a.xy
ax.plot(x, y, color=YELLOW, alpha=0.5, linewidth=3, solid_capstyle='round', zorder=2)

x, y = b.xy
ax.plot(x, y, color=GREEN, alpha=0.5, linewidth=3, solid_capstyle='round', zorder=2)

ax.set_title('a) lines')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2: invalid self-touching ring
ax = fig.add_subplot(122)

x, y = a.xy
ax.plot(x, y, color=GRAY, alpha=0.7, linewidth=1, solid_capstyle='round', zorder=1)
x, y = b.xy
ax.plot(x, y, color=GRAY, alpha=0.7, linewidth=1, solid_capstyle='round', zorder=1)

for ob in a.intersection(b):
    x, y = ob.xy
    if len(x) == 1:
        ax.plot(x, y, 'o', color=BLUE, zorder=2)
    else:
        ax.plot(x, y, color=BLUE, alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)

ax.set_title('b) collection')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/intersection-sym-difference.py000066400000000000000000000027571323200062600227540ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Point
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

a = Point(1, 1).buffer(1.5)
b = Point(2, 1).buffer(1.5)

# 1
ax = fig.add_subplot(121)

patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch1)
patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch2)
c = a.intersection(b)
patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patchc)

ax.set_title('a.intersection(b)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch1)
patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch2)
c = a.symmetric_difference(b)

if c.geom_type == 'Polygon':
    patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
    ax.add_patch(patchc)
elif c.geom_type == 'MultiPolygon':
    for p in c:
        patchp = PolygonPatch(p, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
        ax.add_patch(patchp)

ax.set_title('a.symmetric_difference(b)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/linearring.py000066400000000000000000000023651323200062600174750ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry.polygon import LinearRing

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ff3333'
    }

def v_color(ob):
    return COLOR[ob.is_valid]

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color='#999999', zorder=1)

def plot_line(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1: valid ring
ax = fig.add_subplot(121)
ring = LinearRing([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 0.8), (0, 0)])

plot_coords(ax, ring)
plot_line(ax, ring)

ax.set_title('a) valid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2: invalid self-touching ring
ax = fig.add_subplot(122)
ring2 = LinearRing([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])

plot_coords(ax, ring2)
plot_line(ax, ring2)

ax.set_title('b) invalid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/linestring.py000066400000000000000000000024631323200062600175200ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ffcc33'
    }

def v_color(ob):
    return COLOR[ob.is_simple]

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color='#999999', zorder=1)

def plot_bounds(ax, ob):
    x, y = zip(*list((p.x, p.y) for p in ob.boundary))
    ax.plot(x, y, 'o', color='#000000', zorder=1)

def plot_line(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1: simple line
ax = fig.add_subplot(121)
line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])

plot_coords(ax, line)
plot_bounds(ax, line)
plot_line(ax, line)

ax.set_title('a) simple')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_ylim(*yrange)
ax.set_yticks(list(range(*yrange)) + [yrange[-1]])
ax.set_aspect(1)

#2: complex line
ax = fig.add_subplot(122)
line2 = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (-1, 1), (1, 0)])

plot_coords(ax, line2)
plot_bounds(ax, line2)
plot_line(ax, line2)

ax.set_title('b) complex')

xrange = [-2, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_ylim(*yrange)
ax.set_yticks(list(range(*yrange)) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/minimum_rotated_rectangle.py000066400000000000000000000022731323200062600225620ustar00rootroot00000000000000from shapely.geometry import MultiPoint, Polygon, LineString
import matplotlib.pyplot as plt
from descartes.patch import PolygonPatch

from figures import SIZE

fig = plt.figure(1, figsize=SIZE, dpi=90)
fig.set_frameon(True)

# 1
ax = fig.add_subplot(121)

mp = MultiPoint([(0, 0), (0.5, 1.5), (1, 0.5), (0.5, 0.5)])
rect = mp.minimum_rotated_rectangle

for p in mp:
	ax.plot(p.x, p.y, 'o', color='#999999')
patch = PolygonPatch(rect, facecolor='#6699cc', edgecolor='#6699cc', alpha=0.5, zorder=2)
ax.add_patch(patch)
ax.set_title('a) MultiPoint')

xr = [-1, 2]
yr = [-1, 2]
ax.set_xlim(*xr)
ax.set_xticks(range(*xr) + [xr[-1]])
ax.set_ylim(*yr)
ax.set_yticks(range(*yr) + [yr[-1]])
ax.set_aspect(1)

# 2
ax = fig.add_subplot(122)
ls = LineString([(-0.5, 1.2), (0.5, 0), (1, 1), (1.5, 0), (1.5, 0.5)])
rect = ls.minimum_rotated_rectangle

ax.plot(*ls.xy, color='#333333', linewidth=3, alpha=0.5, zorder=2)
patch = PolygonPatch(rect, facecolor='#6699cc', edgecolor='#6699cc', alpha=0.5, zorder=2)
ax.add_patch(patch)

xr = [-1, 2]
yr = [-1, 2]
ax.set_xlim(*xr)
ax.set_xticks(range(*xr) + [xr[-1]])
ax.set_ylim(*yr)
ax.set_yticks(range(*yr) + [yr[-1]])
ax.set_aspect(1)

ax.set_title('b) LineString')

plt.show()
Shapely-1.6.4/docs/code/multilinestring.py000066400000000000000000000030261323200062600205670ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import MultiLineString

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ffcc33'
    }

def v_color(ob):
    return COLOR[ob.is_simple]

def plot_coords(ax, ob):
    for line in ob:
        x, y = line.xy
        ax.plot(x, y, 'o', color='#999999', zorder=1)

def plot_bounds(ax, ob):
    x, y = zip(*list((p.x, p.y) for p in ob.boundary))
    ax.plot(x, y, 'o', color='#000000', zorder=1)

def plot_lines(ax, ob):
    for line in ob:
        x, y = line.xy
        ax.plot(x, y, color=v_color(ob), alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2)

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1: disconnected multilinestring
ax = fig.add_subplot(121)

mline1 = MultiLineString([((0, 0), (1, 1)), ((0, 2),  (1, 1.5), (1.5, 1), (2, 0))])

plot_coords(ax, mline1)
plot_bounds(ax, mline1)
plot_lines(ax, mline1)

ax.set_title('a) simple')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2: invalid self-touching ring
ax = fig.add_subplot(122)

mline2 = MultiLineString([((0, 0), (1, 1), (1.5, 1)), ((0, 2), (1, 1.5), (1.5, 1), (2, 0))])

plot_coords(ax, mline2)
plot_bounds(ax, mline2)
plot_lines(ax, mline2)

ax.set_title('b) complex')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/multipolygon.py000066400000000000000000000031061323200062600200770ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import MultiPolygon
from descartes.patch import PolygonPatch

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ff3333'
    }

def v_color(ob):
    return COLOR[ob.is_valid]

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color='#999999', zorder=1)
    
fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1: valid multi-polygon
ax = fig.add_subplot(121)

a = [(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)]
b = [(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)]

multi1 = MultiPolygon([[a, []], [b, []]])

for polygon in multi1:
    plot_coords(ax, polygon.exterior)
    patch = PolygonPatch(polygon, facecolor=v_color(multi1), edgecolor=v_color(multi1), alpha=0.5, zorder=2)
    ax.add_patch(patch)

ax.set_title('a) valid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2: invalid self-touching ring
ax = fig.add_subplot(122)

c = [(0, 0), (0, 1.5), (1, 1.5), (1, 0), (0, 0)]
d = [(1, 0.5), (1, 2), (2, 2), (2, 0.5), (1, 0.5)]

multi2 = MultiPolygon([[c, []], [d, []]])

for polygon in multi2:
    plot_coords(ax, polygon.exterior)
    patch = PolygonPatch(polygon, facecolor=v_color(multi2), edgecolor=v_color(multi2), alpha=0.5, zorder=2)
    ax.add_patch(patch)

ax.set_title('b) invalid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/parallel_offset.py000066400000000000000000000037511323200062600205050ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

def plot_coords(ax, x, y, color='#999999', zorder=1):
    ax.plot(x, y, 'o', color=color, zorder=zorder)

def plot_line(ax, ob, color=GRAY):
    parts = hasattr(ob, 'geoms') and ob or [ob]
    for part in parts:
        x, y = part.xy
        ax.plot(x, y, color=color, linewidth=3, solid_capstyle='round', zorder=1)

def set_limits(ax, x_range, y_range):
    ax.set_xlim(*x_range)
    ax.set_xticks(range(*x_range) + [x_range[-1]])
    ax.set_ylim(*y_range)
    ax.set_yticks(range(*y_range) + [y_range[-1]])
    ax.set_aspect(1)

line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
line_bounds = line.bounds
ax_range = [int(line_bounds[0] - 1.0), int(line_bounds[2] + 1.0)]
ay_range = [int(line_bounds[1] - 1.0), int(line_bounds[3] + 1.0)]

fig = pyplot.figure(1, figsize=(SIZE[0], 2 * SIZE[1]), dpi=90)

# 1
ax = fig.add_subplot(221)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'left', join_style=1)
plot_line(ax, offset, color=BLUE)

ax.set_title('a) left, round')
set_limits(ax, ax_range, ay_range)

#2
ax = fig.add_subplot(222)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)

offset = line.parallel_offset(0.5, 'left', join_style=2)
plot_line(ax, offset, color=BLUE)

ax.set_title('b) left, mitred')
set_limits(ax, ax_range, ay_range)

#3
ax = fig.add_subplot(223)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'left', join_style=3)
plot_line(ax, offset, color=BLUE)

ax.set_title('c) left, beveled')
set_limits(ax, ax_range, ay_range)

#4
ax = fig.add_subplot(224)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'right', join_style=1)
plot_line(ax, offset, color=BLUE)

ax.set_title('d) right, round')
set_limits(ax, ax_range, ay_range)

pyplot.show()

Shapely-1.6.4/docs/code/parallel_offset_mitre.py000066400000000000000000000041001323200062600216720ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

def plot_coords(ax, x, y, color='#999999', zorder=1):
    ax.plot(x, y, 'o', color=color, zorder=zorder)

def plot_line(ax, ob, color=GRAY):
    parts = hasattr(ob, 'geoms') and ob or [ob]
    for part in parts:
        x, y = part.xy
        ax.plot(x, y, color=color, linewidth=3, solid_capstyle='round', zorder=1)

def set_limits(ax, x_range, y_range):
    ax.set_xlim(*x_range)
    ax.set_xticks(range(*x_range) + [x_range[-1]])
    ax.set_ylim(*y_range)
    ax.set_yticks(range(*y_range) + [y_range[-1]])
    ax.set_aspect(1)

line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
line_bounds = line.bounds
ax_range = [int(line_bounds[0] - 1.0), int(line_bounds[2] + 1.0)]
ay_range = [int(line_bounds[1] - 1.0), int(line_bounds[3] + 1.0)]

fig = pyplot.figure(1, figsize=(SIZE[0], 2 * SIZE[1]), dpi=90)

# 1
ax = fig.add_subplot(221)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'left', join_style=2, mitre_limit=0.1)
plot_line(ax, offset, color=BLUE)

ax.set_title('a) left, limit=0.1')
set_limits(ax, ax_range, ay_range)

#2
ax = fig.add_subplot(222)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)

offset = line.parallel_offset(0.5, 'left', join_style=2, mitre_limit=10.0)
plot_line(ax, offset, color=BLUE)

ax.set_title('b) left, limit=10.0')
set_limits(ax, ax_range, ay_range)

#3
ax = fig.add_subplot(223)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'right', join_style=2, mitre_limit=0.1)
plot_line(ax, offset, color=BLUE)

ax.set_title('c) right, limit=0.1')
set_limits(ax, ax_range, ay_range)

#4
ax = fig.add_subplot(224)

plot_line(ax, line)
x, y = list(line.coords)[0]
plot_coords(ax, x, y)
offset = line.parallel_offset(0.5, 'right', join_style=2, mitre_limit=10.0)
plot_line(ax, offset, color=BLUE)

ax.set_title('d) right, limit=10.0')
set_limits(ax, ax_range, ay_range)

pyplot.show()

Shapely-1.6.4/docs/code/polygon.py000066400000000000000000000031061323200062600170240ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Polygon
from descartes.patch import PolygonPatch

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ff3333'
    }

def v_color(ob):
    return COLOR[ob.is_valid]

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color='#999999', zorder=1)
    
fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 1: valid polygon
ax = fig.add_subplot(121)

ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
int = [(1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)][::-1]
polygon = Polygon(ext, [int])

plot_coords(ax, polygon.interiors[0])
plot_coords(ax, polygon.exterior)

patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
ax.add_patch(patch)

ax.set_title('a) valid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2: invalid self-touching ring
ax = fig.add_subplot(122)
ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
int = [(1, 0), (0, 1), (0.5, 1.5), (1.5, 0.5), (1, 0)][::-1]
polygon = Polygon(ext, [int])

plot_coords(ax, polygon.interiors[0])
plot_coords(ax, polygon.exterior)

patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
ax.add_patch(patch)

ax.set_title('b) invalid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/polygon2.py000066400000000000000000000034061323200062600171110ustar00rootroot00000000000000from matplotlib import pyplot
from matplotlib.patches import Circle
from shapely.geometry import Polygon
from descartes.patch import PolygonPatch

from figures import SIZE

COLOR = {
    True:  '#6699cc',
    False: '#ff3333'
    }

def v_color(ob):
    return COLOR[ob.is_valid]

def plot_coords(ax, ob):
    x, y = ob.xy
    ax.plot(x, y, 'o', color='#999999', zorder=1)
    
fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# 3: invalid polygon, ring touch along a line
ax = fig.add_subplot(121)

ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
int = [(0.5, 0), (1.5, 0), (1.5, 1), (0.5, 1), (0.5, 0)]
polygon = Polygon(ext, [int])

plot_coords(ax, polygon.interiors[0])
plot_coords(ax, polygon.exterior)

patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
ax.add_patch(patch)

ax.set_title('c) invalid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#4: invalid self-touching ring
ax = fig.add_subplot(122)
ext = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]
int_1 = [(0.5, 0.25), (1.5, 0.25), (1.5, 1.25), (0.5, 1.25), (0.5, 0.25)]
int_2 = [(0.5, 1.25), (1, 1.25), (1, 1.75), (0.5, 1.75)]
# int_2 = [
polygon = Polygon(ext, [int_1, int_2])

plot_coords(ax, polygon.interiors[0])
plot_coords(ax, polygon.interiors[1])
plot_coords(ax, polygon.exterior)

patch = PolygonPatch(polygon, facecolor=v_color(polygon), edgecolor=v_color(polygon), alpha=0.5, zorder=2)
ax.add_patch(patch)

ax.set_title('d) invalid')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/code/rotate.py000066400000000000000000000025461323200062600166420ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import LineString
from shapely import affinity

from figures import SIZE, BLUE, GRAY


def add_origin(ax, geom, origin):
    x, y = xy = affinity.interpret_origin(geom, origin, 2)
    ax.plot(x, y, 'o', color=GRAY, zorder=1)
    ax.annotate(str(xy), xy=xy, ha='center',
                textcoords='offset points', xytext=(0, 8))


def plot_line(ax, ob, color):
    x, y = ob.xy
    ax.plot(x, y, color=color, alpha=0.7, linewidth=3,
            solid_capstyle='round', zorder=2)

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

line = LineString([(1, 3), (1, 1), (4, 1)])

xrange = [0, 5]
yrange = [0, 4]

# 1
ax = fig.add_subplot(121)

plot_line(ax, line, GRAY)
plot_line(ax, affinity.rotate(line, 90, 'center'), BLUE)
add_origin(ax, line, 'center')

ax.set_title(u"90\N{DEGREE SIGN}, default origin (center)")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

# 2
ax = fig.add_subplot(122)

plot_line(ax, line, GRAY)
plot_line(ax, affinity.rotate(line, 90, 'centroid'), BLUE)
add_origin(ax, line, 'centroid')

ax.set_title(u"90\N{DEGREE SIGN}, origin='centroid'")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()
Shapely-1.6.4/docs/code/scale.py000066400000000000000000000032611323200062600164260ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Polygon
from shapely import affinity
from descartes.patch import PolygonPatch

from figures import SIZE, BLUE, GRAY


def add_origin(ax, geom, origin):
    x, y = xy = affinity.interpret_origin(geom, origin, 2)
    ax.plot(x, y, 'o', color=GRAY, zorder=1)
    ax.annotate(str(xy), xy=xy, ha='center',
                textcoords='offset points', xytext=(0, 8))

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

triangle = Polygon([(1, 1), (2, 3), (3, 1)])

xrange = [0, 5]
yrange = [0, 4]

# 1
ax = fig.add_subplot(121)

patch = PolygonPatch(triangle, facecolor=GRAY, edgecolor=GRAY,
                     alpha=0.5, zorder=1)
triangle_a = affinity.scale(triangle, xfact=1.5, yfact=-1)
patch_a = PolygonPatch(triangle_a, facecolor=BLUE, edgecolor=BLUE,
                       alpha=0.5, zorder=2)
ax.add_patch(patch)
ax.add_patch(patch_a)

add_origin(ax, triangle, 'center')

ax.set_title("a) xfact=1.5, yfact=-1")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

# 2
ax = fig.add_subplot(122)

patch = PolygonPatch(triangle, facecolor=GRAY, edgecolor=GRAY,
                     alpha=0.5, zorder=1)
triangle_b = affinity.scale(triangle, xfact=2, origin=(1, 1))
patch_b = PolygonPatch(triangle_b, facecolor=BLUE, edgecolor=BLUE,
                       alpha=0.5, zorder=2)
ax.add_patch(patch)
ax.add_patch(patch_b)

add_origin(ax, triangle, (1, 1))

ax.set_title("b) xfact=2, origin=(1, 1)")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()
Shapely-1.6.4/docs/code/simplify.py000066400000000000000000000023051323200062600171710ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import MultiPoint, Point
from descartes.patch import PolygonPatch

from figures import SIZE, BLUE, GRAY

fig = pyplot.figure(1, figsize=SIZE, dpi=90) #1, figsize=SIZE, dpi=90)

p = Point(1, 1).buffer(1.5)

# 1
ax = fig.add_subplot(121)

q = p.simplify(0.2)

patch1a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY, alpha=0.5, zorder=1)
ax.add_patch(patch1a)

patch1b = PolygonPatch(q, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch1b)

ax.set_title('a) tolerance 0.2')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

#2
ax = fig.add_subplot(122)

r = p.simplify(0.5)

patch2a = PolygonPatch(p, facecolor=GRAY, edgecolor=GRAY, alpha=0.5, zorder=1)
ax.add_patch(patch2a)

patch2b = PolygonPatch(r, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patch2b)

ax.set_title('b) tolerance 0.5')

xrange = [-1, 3]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()


Shapely-1.6.4/docs/code/skew.py000066400000000000000000000047211323200062600163120ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.wkt import loads as load_wkt
from shapely import affinity
from descartes.patch import PolygonPatch

from figures import SIZE, BLUE, GRAY


def add_origin(ax, geom, origin):
    x, y = xy = affinity.interpret_origin(geom, origin, 2)
    ax.plot(x, y, 'o', color=GRAY, zorder=1)
    ax.annotate(str(xy), xy=xy, ha='center',
                textcoords='offset points', xytext=(0, 8))

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

# Geometry from JTS TestBuilder with fixed precision model of 100.0
# Using CreateShape > FontGlyphSanSerif and A = triangle.wkt from scale.py
R = load_wkt('''\
POLYGON((2.218 2.204, 2.273 2.18, 2.328 2.144, 2.435 2.042, 2.541 1.895,
  2.647 1.702, 3 1, 2.626 1, 2.298 1.659, 2.235 1.777, 2.173 1.873,
  2.112 1.948, 2.051 2.001, 1.986 2.038, 1.91 2.064, 1.823 2.08, 1.726 2.085,
  1.347 2.085, 1.347 1, 1 1, 1 3.567, 1.784 3.567, 1.99 3.556, 2.168 3.521,
  2.319 3.464, 2.441 3.383, 2.492 3.334, 2.536 3.279, 2.604 3.152,
  2.644 3.002, 2.658 2.828, 2.651 2.712, 2.63 2.606, 2.594 2.51, 2.545 2.425,
  2.482 2.352, 2.407 2.29, 2.319 2.241, 2.218 2.204),
 (1.347 3.282, 1.347 2.371, 1.784 2.371, 1.902 2.378, 2.004 2.4, 2.091 2.436,
  2.163 2.487, 2.219 2.552, 2.259 2.63, 2.283 2.722, 2.291 2.828, 2.283 2.933,
  2.259 3.025, 2.219 3.103, 2.163 3.167, 2.091 3.217, 2.004 3.253, 1.902 3.275,
  1.784 3.282, 1.347 3.282))''')

xrange = [0, 5]
yrange = [0, 4]

# 1
ax = fig.add_subplot(121)

patch1a = PolygonPatch(R, facecolor=GRAY, edgecolor=GRAY,
                       alpha=0.5, zorder=1)
skewR = affinity.skew(R, xs=20, origin=(1, 1))
patch1b = PolygonPatch(skewR, facecolor=BLUE, edgecolor=BLUE,
                       alpha=0.5, zorder=2)
ax.add_patch(patch1a)
ax.add_patch(patch1b)

add_origin(ax, R, (1, 1))

ax.set_title("a) xs=20, origin(1, 1)")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

# 2
ax = fig.add_subplot(122)

patch2a = PolygonPatch(R, facecolor=GRAY, edgecolor=GRAY,
                       alpha=0.5, zorder=1)
skewR = affinity.skew(R, ys=30)
patch2b = PolygonPatch(skewR, facecolor=BLUE, edgecolor=BLUE,
                       alpha=0.5, zorder=2)
ax.add_patch(patch2a)
ax.add_patch(patch2b)

add_origin(ax, R, 'center')

ax.set_title("b) ys=30")

ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()
Shapely-1.6.4/docs/code/triangulate.py000066400000000000000000000012201323200062600176470ustar00rootroot00000000000000from shapely.geometry import MultiPoint
from shapely.ops import triangulate

from matplotlib import pyplot
from descartes.patch import PolygonPatch
from figures import SIZE, BLUE, GRAY

points = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
triangles = triangulate(points)

fig = pyplot.figure(1, figsize=SIZE, dpi=90)
fig.set_frameon(True)
ax = fig.add_subplot(111)

for triangle in triangles:
    patch = PolygonPatch(triangle, facecolor=BLUE, edgecolor=BLUE, alpha=0.5, zorder=2)
    ax.add_patch(patch)

for point in points:
    pyplot.plot(point.x, point.y, 'o', color=GRAY)

pyplot.xlim(-0.5, 3.5)
pyplot.ylim(-0.5, 2.5)
pyplot.show()
Shapely-1.6.4/docs/code/union.py000066400000000000000000000026211323200062600164660ustar00rootroot00000000000000from matplotlib import pyplot
from shapely.geometry import Point
from descartes import PolygonPatch

from figures import SIZE, BLUE, GRAY

fig = pyplot.figure(1, figsize=SIZE, dpi=90)

a = Point(1, 1).buffer(1.5)
b = Point(2, 1).buffer(1.5)

# 1
ax = fig.add_subplot(121)

patch1 = PolygonPatch(a, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch1)
patch2 = PolygonPatch(b, fc=GRAY, ec=GRAY, alpha=0.2, zorder=1)
ax.add_patch(patch2)
c = a.union(b)
patchc = PolygonPatch(c, fc=BLUE, ec=BLUE, alpha=0.5, zorder=2)
ax.add_patch(patchc)

ax.set_title('a.union(b)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

def plot_line(ax, ob, color=GRAY):
    x, y = ob.xy
    ax.plot(x, y, color, linewidth=3, solid_capstyle='round', zorder=1)

#2
ax = fig.add_subplot(122)

plot_line(ax, a.exterior)
plot_line(ax, b.exterior)

u = a.exterior.union(b.exterior)
if u.geom_type in ['LineString', 'LinearRing', 'Point']:
    plot_line(ax, u, color=BLUE)
elif u.geom_type is 'MultiLineString':
    for p in u:
        plot_line(ax, p, color=BLUE)

ax.set_title('a.boundary.union(b.boundary)')

xrange = [-1, 4]
yrange = [-1, 3]
ax.set_xlim(*xrange)
ax.set_xticks(range(*xrange) + [xrange[-1]])
ax.set_ylim(*yrange)
ax.set_yticks(range(*yrange) + [yrange[-1]])
ax.set_aspect(1)

pyplot.show()

Shapely-1.6.4/docs/conf.py000066400000000000000000000154031323200062600153530ustar00rootroot00000000000000# -*- coding: utf-8 -*-
#
# Shapely documentation build configuration file, created by
# sphinx-quickstart on Mon Apr 12 11:07:08 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('sphinxext'))

# Load latest source tree
sys.path.insert(0, os.path.abspath('..'))

import shapely

# For pyplots in code/, load functions here first, so they are visible
from shapely import geometry, affinity, wkt, wkb
from shapely.ops import cascaded_union

# -- General configuration -----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
    'matplotlib.sphinxext.only_directives',
    'matplotlib.sphinxext.plot_directive',
    'sphinx.ext.autodoc',
    #'sphinx.ext.pngmath', # <----- pick one, not both
    'sphinx.ext.mathjax', # <--/
]

# Add any paths that contain templates here, relative to this directory.
#templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'Shapely'
copyright = '2011-2013, Sean Gillies, Aron Bierbaum, Kai Lautaportti ' \
            'and others'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = shapely.__version__
# The full version, including alpha/beta/rc tags.
release = shapely.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of documents that shouldn't be included in the build.
#unused_docs = []

# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []


# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme = 'haiku'
html_theme = 'sphinxdoc'
# html_theme = 'shapely'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['themes']

# The name for this set of Sphinx documents.  If None, it defaults to
# " v documentation".
html_title = "Shapely 1.6 documentation"

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_use_modindex = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a  tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'Shapelydoc'


# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'Shapely.tex', 'Shapely Documentation',
   'Sean Gillies', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_use_modindex = True
Shapely-1.6.4/docs/design.rst000066400000000000000000000026031323200062600160550ustar00rootroot00000000000000============
Design Notes
============

Shapely provides classes that implement, more or less, the interfaces in the
OGC's simple feature acess specification [1]_. The classes are defined in
similarly named modules under ``shapely.geometry``: ``Point`` is in
``shapely.geometry.point``, ``MultiPolygon`` is in
``shapely.geometry.multipolygon``. These classes derive from
``shapely.geometry.base.BaseGeometry``. The simple features methods of
``BaseGeometry`` call functions registered in a class variable ``impl``. For
example, ``BaseGeometry.area`` calls ``BaseGeometry.impl['area']``.

The default registry is in the ``shapely.impl`` module. Its items are classes
that operate on single geometric objects or pairs of geometric objects.
Pluggability is a goal of this design, but we're not there yet. Some work needs
to be done before anybody can use CGAL as a Shapely backend.

In sum, Shapely's stack is 4 layers:

* Python geometry classes in ``shapely.geometry``
* An implementation registry: an abstraction that permits alternate geometry
  engines, even a mix of geometry engines. The default is in ``shapely.impl``.
* The GEOS implementations of methods for the registry in ``shapely.geos``.
* libgeos: algorithms written in C++.

.. [1] John R. Herring, Ed.,
   “OpenGIS Implementation Specification for Geographic information - Simple
   feature access - Part 1: Common architecture,” Oct. 2006.

Shapely-1.6.4/docs/images/000077500000000000000000000000001323200062600153165ustar00rootroot00000000000000Shapely-1.6.4/docs/images/4511827859_b5822043b7_o.png000066400000000000000000002116611323200062600207540ustar00rootroot00000000000000PNG


IHDR V%4sBIT|d	pHYsaa?i IDATxy|SU߽ӦYZ@\@aE}qEDEdQ|}UQ@eZhYPk&]%MǝFBM~>h{ν=O<֬YcZ.55~;7_0~x^<B]TBcvmAnn.`ܹ8~8233Cdd$y[7K!}գ2xP
ǽkh@PP&N={F	!OzB
@1fh4|'/233ЀÇZn@FF]dBS%K菞z)|GHk駟TTTZsZC!$$
2X#''SNGQBHW.xWZ=^y_v:|6$|g?~QBHhFH"000qD<7|O<֫qf}
@A:Js{9lܸ%TAe+W47Ѷ@e-Tv+hF
h4"//P(xb~wDFFB*\:t(z=ť_*{io
GwEeT' XwFmmi<$$$`Μ9XRB雨%Z@d`ܸqpssCEEvލ;v`ٲenWƘ1cp]wa娯ʕ+入KQBHzBX!>>ǎ?n
.D^^>3IcǎA"#<(8qtq%QB!bEaѢE-#Gpyv;գϿ+*{c.$''#>>IIIzP!@?>BHz!B!n(!Bرcyս]zWcYرck׮*,\gn۷;oMo		MzdXl^8m!}…onn/Xd2Gaar?>{uuupB>'Zf
nf(v<̘1Ʋe6qӧyyyx;`ս]7l0|G3g0tPL:f:tÆ
CVV֮]gyp_[nŨQnzoժU/uVddd`ƍزe}]2wuJJJpa10.[~1o?Ę~ZЪh"hZà zٲe

T*Ehh(>c}={G\.G\\\'{E\\
|}}/0FKD",YbձlܸHII1[̙3~)/^YfsSO=w;wb֬Yf]xs9s`ڴifA*bڴi->ĉشi{?~cǎ\.^y3x衇hjy7-.O{:~y|'={6l="##P(0i$l۶Z-Z'''x{{cƍ]:;ﴻk̝;@/رcK/!((ƍoA3?05k{3f`#F`1~z6tP&J[`h41qfWf1^z%$			a}zGeL&QF.cFbre˖1^c`0_|0X̼ŋ>W,  ]|M6YnG_YYz!ʔJ%>}:KOOtm۶#F0\T*{XAAA}jvwֲ߳C2XrssYCC[t)g218p^EE3gfEDD?޽{;]ܹs89|28]tɴ̱c󬸸m;rw&Xee۷3gggs0鏬GgϞmٳYll,c/O,66=C1ƚئM3+--e1ܹsYxx8;x +((`Ǐg~-c쏺[oe+KKKc&MbF2͍},???~EFFe˖1۱csrrb`ܹslǎWZBBB,>glÆ
ӓUUUonnf_e13TʚܶVe<ϳ4cϟg~~~/0[cVe7|3{'Lߋ``W^er-]eggC1ooo/v^gk֬a/^d?gNNNO>1z:1
dwfyyylҥL.2c)))L"+V\w^xgZgpp0`[neweDZϛmvxKJJX@@KJJbٌVGeaaaݺZK,a&M2{رc8v׵^ }]8-p6qDyf:[̙3ʕ+l̙,88t6׫5ѣGYAA_M$;cLIR6i$222Nc?8q"KLLdlӦML..l˖-ʕ+,;;L"Gm8ϡȑ#GLV3O2L&@VXƌc^^^^@7/ys1T*ْ%K\~Ϟ=իWq_~is-u7fDZjcz+۰az_}sssc	+Cزexg,11'N0ooo1&
yVRR>q,77g/28I$<[n]evD"Qږo=f-]
>콭[2k˳>Nkkξ_Ƅ:70gb}1Ỉ7wL{uaHH{G3.XokF#6m[f
cjd[ݻbk.f4Yii)4i8}WkOc4e?~#<fPo⡇H$8998xyyKKK7|'Oo6{z7x#aԩSQSSJk^3M.7x/Q\\___L:<#G4m!JO[uZw.]BBB0j(B???ٳ<@0[@8g7oF\\j5<<<,*WGY^>QQQL|(..'g?۷cÆ
3랱xb9rv¤I...jhh1|%%%1[N$%%%6w뒒1GOCR`0	f[o@^n:\r


hnnFcc#Ο?LfC3l0h4PTHNNFRRY@`ܹsi&DDD`ԩ!hYb
T*(++3mg֬Y|eLˠCh3{w2s-7W\AMM
[T*ddd`̙?ܹsI₶nZ~1[.\0]t	P*		AFF{=xqA[UR0.www},_B"==		J2]x"-|MZ
fBTT~,]k׮5["JҪcq]}vX>,N8.8K[:~[tixѶ-ӧOC&A"ѣG&$$wށD"D"c=VD۷^{
UUU@EE)IXXGH:j&M>"^^^P(`pذa\\\p),z->}}(++CPP=KbӦMTjqw믿aoKϑhT*ŅZm¿f|ؼy3
LKXYYYHHHJ2}*(3(//o:ak8{-zzߤj	^bb"b޽lHmȑhllӧqM7uLqqqHKKKP{=܃'x8UOg4MիW;rJtsNVEDD8Df<>77aaaV=,Dtƶzu)(JީS0~xVV屦||';s~yfYSNΝ;M=$N>mv>;_O_D|


@x\`LbzԩS`SKuhllĹsL 555HIIs=z)))ڵk
8we,2j(455GnsSNa٘;w.edd؊^ܹs}~M7AVˈ#Yrz8{,n&aƍtRuT*FD?̙3o ((t
xwMTDL0"00(((\.;x	Xlz)cŊXxZ[EFFbΝ8|0"##_~1--ģ>-[`ժUXh.^[A/ЪEE``떀4<<\\߭711<ϛշ{+T{NHRݻׯ_M-upbΝ^8j5.FAAN$%%?Gss3JJJPRRb2tPL6
?8~w$''駟ƼyZ!._ϣ.\0kHLLDtt4L˃`0҅mʔ)¢E'N`Ŋ_jQx:m̘1Xf
x
`x뭷̖0a~a̘1^^^x}fΜGyf{[VYZL2þ}0b6ׯ7qrr–-[0j(DGGسgHEEE"ɘ1c1adddয়~2צma0n:DGG[nAEEN:eֲі|ռA|Ozx'1|9&:t,(_륗^Bcc#|DDD8~8b…Xn]=Ә1cfϞx`fߗ%YZ"&&~-KDGGcƍXbcd}vV>Ϸz\%˴t޽{1fbΝZ؈};I"%N:cMVʂuk2S*O?Ԕ1!…S4/cb
&$$m1!*fN:nT*B`cƌ1}ݳg;v,spp`RdzѴ믿BCC8kmg,`{_UU,X\\\L.##m\.gr۷oyޔswξm۶1WWVzj*$	`3g4e)//gӧOg2y{{+Wʢm6|4<ϛGaZ^_>=LR1ggg裏		ikr>=mssssٝwɔJ%d/Uue=3DbJ]j9;;q[oe111f%$$0777Do.F^8>CgclO$''#>>IIIԟ IDATױBCCzj,XBH2뙁~|Ç[=aGV^V2vdرX|9̙czW^.]jW}ƌOOO:u
O>$׿M88%&&…`
֟;w.\\\lgƀ'55|BHDFFZեvK];Nnk3RQQ////yhbYb@H5o diH!T6m¦Mz}ΨQpYk:_
B'B!ž={^3h>Z@!BH{njLgϞEUUu(!B!qݚ,E!BҷQB!B%
>'
@!BHCGE!BW((!B! B!_c`B!y|B!>&"$F46!9$&
@H5
HɯFFI-J4
k4,E"H$b140"#]0e7B<Zm.jqr9Vm02Ppw	G~#Op\	tptt;\=eD`	ZK*޳EcᬔجB`CE2M]~H.

F(2Herr0PQ$`Z)Ej$eH7@(eϲB'r[R qchnnF]]4
~H.ơ%5zfEićGa>d.o0Vq6s4xdbFtB`DFc'Sy<8BB7***]Z<{g$r㹐dJjs...pqqT>.T?ZܽzEUذ?
z#!pph:s=Hoo\e	5ڣKe @xQSS&<ӃB"
>>
@US8RC,ψ8xxx@R!+3v,YD"APPq9+wFB*.[N?1DA"+X,FXX8\bj	!XiiiFyy9cfr **
}MN~jjƍn+%}‘#G0oveVoD"8::Քƾr
.^OOOx]!jjT?? :ZmnZd	ꫯ***OmrvGAE=4uMo}LE.APHwHh(!qT*Ź,<-Ɵo	yMmOAArssMZ̙37tyy]F$BB+W%K_>ƀ-[[:҂ㄛ '^{
Ǣ"3\rND"App0ڽ1P]]b߿7n8xسG`֒uQ͙L9 ݰb
|زeFe>s ̛7ͳ>UZ,#_\?BR=;V7֖ruu^|8uښC|/h4T
z=ϟƝq???6ٮO>n>}}m=BsB ֐Ʉ`yyo
]{cE#  a';99/_FԩSoE7ŧH(q;V	^{M;)hDH^k֬ڵkxb׫qpuup7nD\\m
&eRZ:17&?v;-t;@ŭ:kxzzZW#6"E:
wrr2{kI[S=ږ:!88ئ7`*
r@jkC:pn-//Tx
?w};N	7~~-8N	7_"#VtχZFyy9ʄm긨P(Gbʔ)	i+'O>jռ.)~=_QW6-_p(
\xz;"##]'ݘ/&v;-xuV)"TZVb
+W )
c#g8BWlѢxˠ~7!nE"nz Z8;vGKr0&'"99zTnVT

Eff&.]n}Ww'/KOZR[t֞/M` gBNPAE- Vxwj*L6
3fjqOƌ˗/7M入KFѻQ&0e Wc)]}|qW
3bmN뱺;ZF-'+,칹2r!V۵q |#N.qvX:FAPP<<(q<}qΐ!Cp1˸ ?	os讄`DCCEc%1TWcdP:>}93|}}?#C\nʺ)	MF$<`mS2>+$$$Xl\\9҃pGH<]RWW		\0W\/fJv0h*'OOɻڑZX}I(of4"5
W
0Dpw"|l7omT*5\=mDuTgZv{WJ_S@͵*a㨭Ő!Cڝ|\]]Q^^]ʀ#GMD"%AgmR1mWcЅO}X۔9NI<ƄL>>>v1.
"&ʺ+IY)())]ɨ<}|q)'ex9셭ɀ/N!1Jٹex!.H<t@e"3z{k}}=xvV|}=]MMBz2
BBw2yaQUHvv6rrrf;WխG֭Bh.99A1u*N2A&#!{qYdXdN@ii)|m.rLpppmjF-WWW!9[i#{n&l<uMBBBw#777jdUa4A)(w1.?!]
ш:8}MM
}`鐓h44668(J)d²Z!6**? Y7SZcEw6Ξ=''NSFJmw;{Vh%UGXٳג˅Av6¼.ÆٶQB,"ýqb1lTꐑgnƯFA~>&@luAČ8_!5+aa]
Bz=j5{ňy8::"fl!
z!Czq<==y Gt:pǯc?LeqH@	R
 zՈmFٳge
8r9J%chllDff&RRR Jq
7`&	F#<Q@Ə^77	~aaD(Cs0~Z@zpK0ڊh3eq@f22l;HZrq]>Z<*w2!uPB,6sҊkȨ(h4F
F[=xee%rss0_o"Ów#YHȀU]4
`42r~Y;D.WHSeöc9kFԐht!8ppp/
tҊkؤPH{!9O(E]7#c{NN6z...t=S97pB8T	&СC())&ydggnnnP(ŐH$V?LdDnn. HL.dSrtM//aBȞ*dj .Ϊ.Xս.eN///fuGC4ݮn)Y>rvIXM!;

\GtcI1Fssx:Λ1.r1tp񁛛[ݰL}n,T
fAuU?Ϸ4RYeu(nI
tƸ(wPNWQ!
=A*&{fbw\79|IYUrtR(]Ŷc9X<%:l?M\Je6 %sF5f^MQ@(5OāsP6CEр1/eu#qatE`DQU\=,+3"sU>g!/
ߏ˗/#%%iiiyr455GK IDAT	AR=|pssCQQ탳Bs7}Z
Lk(++BZSLi+-Mi
>\]۟@ZF
@
@H+4`[;aB@pp.ff")
[RNϹP*:t(ʶ[58\.Vq&%xHn21n⁓iY+!yz\),BrƜwJ4
]ɽ=-sb(l~G*bȑ1bj5JJJP]]Fnnnprr޽{cB1FkL@@di7|p^ ?_owִ|X m^/~~~6ٞ'qyy2UOOAAAw/jPOR!33*^
7G/*wⷌJ!,,&]R)|Ko0PUU2ŝqVMsN6ՕMJn(--Euu5LA;<<<&֩Sݭ
G.&I#)
~61RX@gdⳟs.rQQCv5oy+Tr1|]tCۂGPTT??_!777`R1PWЌ첺u-$bՍ6NIHc4M3ty,U9RDaذafFpww{%Cd4AHȂR111t<==m
QQQ200~xDGG]Vƺj՛ ;3zGz=H_w1W#U-T*p.[!~*lKȁCaHǞ3E-MpRJ06yC%4
uŽ7co0mu-B,HO:;:iwk6aɀg
1*^̋RVVǏ
NNN		JD"1[Oh4|2.]89<ϣ=>OO[nɮFnCFFrrr٭z)))ptthN󈊊B\\\uvK+^/X
>O(!*ޛ7vw}eY^}lN&$&C6Z@/-}iK_
m߫¯-@KlfC&$fXlyd~~<#He;s]&<:y}j1+"mj
Z`M/J–Z(;3??O Sptrn:
;1䵓C9[`Jz -6v|lI":%p077dzx⠗+Mk*vUQǏ^bֆEݎnx<޽S]/TǖK
q(͆%x8*9"ҵB!//-< uuu˺֧R)z{{qڌD$!o[pM{\
2b0fVTl63"P!̛șQ`/HDJazzz8lF4a_TZEqq1`>BG.eD
&3d%l6؈,ƽ^ǻ7wT&jk=޽{Njn



^(RDc1NJذIYW6-|g'|;wM3iw^ػW<νNoB!=:r6ڻU䕹h.3aWՒLxWXRRQQᠿ>_JHujg_wNrt٩\^zb*c>n~a{[NC2B.MʖNaa!}љ|k,UTa޽RUUpNss3LMMe0p:J<3;+ҕv8x-jjĂ~
#YY>7"x؛q\Kn2%	&&&zq%F#:WU-oqÿ[f4VdWF2	GeM
J2#^N-Al6FwW'T/gO;SM7UdJf<aj6ƷEͼc&zs.-6,kqs.i2L_o?ǟ֜QQZ K/aۗ->X,jkk'*8̐eO{ރ0=-m4|>͛O?ß)<(:-]-IB(,%|x[8RZJAAYd2I4%4;KclV]$rUU"%iߙp)\u+gqy"$~qՠRE6අzȪYf1tBzDy-yk5Ef6W`k(+HX.NP1"7SԔE(RJ`hhl{l'{:**s1樭D&Hmmˎ`0H$vNz8ݣa$boAiԧND>yQ$R,vVZ-xv룧~C'zm[@[C$? 7b?C!_Қ9W<{zDClB!8Ql8J^r91:QO̝Ofh4f
^8:z;֙LD㩜4K,ZܯR2у賟MYzG=qԝ0\v}|ˍtЀæM?@$!c=(
--HhN8g߀[Ç?o%͙uJONp\{-|k05PXxʞ,Z[1FwTjD%W'`+4;&_ӹjyĢOΊL*DmZ-٥6vDjE"!
jEڐ#_it:G?B$&GT^--G61}xW*)Dxۢ&l/]hh)U#"簾>!>Dm?c
Tnf)>vfzz+A(rw4Heȴ<)gwjǸ״Sh502rr9FC}}/**fllV{A,ˌktp"
22}oDB,@A_ѣ"'[	cdC:h?7{I|(,q5'7
4D4l}.ZY"%}nGFڝQsS/}υH!R)1B嗿~peUj
J^HtNRZZM۷\M,1RVxl2DAXdrBUL166F7Ħ*UTrl^4$LOOsW~NPHIG
zϩ-z"߄+UB [1u4*"2.,Q_H33/((UV&g_k4VE!n]{<""q(Q>Ha	d}>O
*@T^>> -m᠔B>[V҄/8ll&rIׅDQ]$xlhRug-:ؽgjq1Ʉ^ep"
5M$QZ$	GenF$	K/='Nڂ ɤ"{+N'"H,Y~#ȷDMxzXݷ޽B,)|`?s[n3jZV=$q9u,7$!ԎǶDչwȍʻUF7wt2OCcc^keÙي 
E=WZ`Ĩ
(PyQK+l)DsXW;vsJw>ru]¦DSO]3S2EXg~TD/^{MtyVhsرv뮻p_Gʢy`Ƥ:j-&"?~!2ł_]sY|~x,h4BD"졇D/G)!TQYTStS_߀;Grfrݳh$Trbl
E/rt:&iAqGi߸5*L<`OEoRzy#Ma?x)u]b~_#ˋ/	8|X'
Z^yE,sA:K].Q8
bo6N|9UyĜdYpRWʚG *kvwVL'5mF"!"6~;\avvV(V^#ᠲp8̨÷>|m57m-cKu!xFs&@=YTTMQQ===R5zx<\.wy־t}sݤ]Bb'g?Cҩdj=R]}D:pZ
Es$ICw_
Ѩ`m]
UԗZ{6qmk1뜴-pzqԗZ$TZ457SSSëLɔLDtAmSGѣh4JmFTT*++IR̞_KDQZ-W
"2T=^~Y,x?DMF[,RcJDѦgWG|dV+\.a}ʪ
5դb̒fT9$`3(+\\$~{g
hU•$9v2ȏ_Eh"l큳e~~D2E}6UywP\\Laa!yz}~#3=&RLmnd^/v{leYX~Bl$zXʉ|ՊbtID֢H#I"@DA.ԥ]eC
5Ф(·w~.wSih,ZG1::~Z$fHRuSǙLz-ewWQQIJkPH4EH4x?WQ  ^Di$I٥㪬O5)wL
WbCg{쩩)b7l)kw0KЛz\R\\Lg{G+1㛋%L25V'sj@t2:!8/m&E&Z+ދѣGmԂ$}Ex.6cvu4.zo\6%$Ay$k_|EC>*kUIZ&#P]l=v$x^_[Z4$Qvsq(13??,y;N7Ē<81M`41<	zƧy8f6aml4
7x#<444	{H$BQ>\ N!yD=oaa~@:%W^Ȳx7\#MI^xA-HDeMbkHR-vuORR)Y87EIbT^kaf3v;ӡ8Dp8͖'w_yH4H(E˵hiYD"LNN	^91vձ0oE]}/O?4IjR)BmmmyѨ7!IΨG{DG>`<~Z|rP/q^  6*@68k#rgvHcs&N3lB<8z|>gB0dpZ7o+>Йشi3555X,EE$Ifٴy3Zo=˳GWK[o%pqV133C*!/ST!e1nEE/g^Gr睢3b||O|1DʆF Y
ݻ)))A|e8poNQQw}7PF)q޹Cwv80ڵq6ܹsM15,FM6f=B	6Wgc5ę7%}*HIPFL,/|2djj'xؽ?z_Ss<TUU-+^$n7%%%a&֞CF~l޽={PWWܚrH+rՎ(MM#=Rc޽W%?O}inu*J5ەeniak!d`D"*րdI]]333p4}K_l6/_ve477~6. sssf,sQͤ[h^ϟ8|

155x$I$	"sɔ>pe=5.e^h4RSSb^If.W8x^G%%%=tc6q\9=f*eszb05PVl]Fzsw~G4:4ER~p%
pS,?O"=8d! *5.H$xǹ*֭nGY)k.otRZhbp~ 
133í&Zig6Tt>HG`,F.` @oooљ4,3::f׷(EN3DX˞+퇷Ƿ5/w¸3$j<pDF%7Q
rޘL&eʜ'c[A(	O7E]Qv/=AT64j$Dضmymݺg}X,_uV#_u3::>J9d2 5.׶fyyC	
:Z|[\?'MqqEPgP(/YG0^91jGfnq5|
ʻ:zyh48NL9.r]Z3Qk͛a`@G)ሕq"ʐkNV2+Iq26FqSӲ{:ؘY^`0HHWg?}C(*fa2jȲL4%	QQdBsE7X" nſEEN% kwu_|ʦM2RX.sssHg"I/|vڲ2Sf{D	3VeHG2~/X{;:H{&UUu+|q IDAToq
Wh9>?/٢ў:P2/SeM
5g?Yv3|##Hf_޻h<FS]S~D"IĢm7<F[}3aT6qX
|`x,9eehiYx
nUta"N}'oߞ7$IWRQdW9~(R\./(,;tPZ
%%B$H/һ.ؘ]&QMD܏~$>&>Aq,
*@rN??0)Y02KHl/[5:LPDtlX:%,6d괃A-]SCAP\juNRYwjj.{t,/5d>l&-{ʍ@ɲ-M̲rp_D"M%SSS~nf˰M$<

QVVFEEŒzJJJp\LNNviW]%R{~cO1EjU|%"J2?/۶MP55e,v:Bdr>1Ǐg\Dj~8vL]pynbV+qG$)/dvVkVX*C '$3{رcol6|+;`~~/}Kj3qǒŽ'
"E]{*(2^/VYHx2Ij"Ix{n'RYYyF*++9y8HhYl6[ܿ"HK,Olj\SX,x:ZUe[y]GO>$hU)8O?Js122BcccV:$QRRfk_
~EooiQ'PT

axϮJ>?VcDzgbCcW_-;xNzøV!P.N_:
*3J8\~g.TXU3>O344>ȃ>$IPSSCkk+/ws:nocc2p8K1Jx|U7&455eu\^O}C6׺ho_~89~?mmm&>dY122BMM
;w\8`xx8kڙf_[n`;R8!kn`Ť+[Hc7߄/QTJD0M&7wE)~Me:73#e2	$)[ȯQ?e000oγ>lGgγ$RS\j1Lx<<GލQ~N"Qp'EkwwMMvj*))ADrտXR" 059l(Dki[[96߯tffr57*ѣnf7rIvڵƝ?0wZ߿@.G@aa!zeDi``?:()E^2Vi**೟ÇEؘ+1ı6oK.xʆF *yg<g44,TTT^~bYC]ȲCo`ZqvZEESOYFYV/7=uZ6;&면X,;0oLciص[/-IC.?]GWǃ٠aV**A4#DkR&h4()){n-c1177Պν΅԰cD*hؘ#MQhWiFJ񣢲Q+)Y/W,l6۶mC*^7a2(//Y<#'c|jnRʠo!MMMZM`n-lx%HNZ$6%c>ޙ)>yS=-x{099b7==4V퀮8$E53h4B__O={(jhhIF,&AeHu蓱QDDhnMB\U\@-)s0giiiY5乬n~$s$D^G*dX8~K1M'{uQqe0pݔ088?>PNNxfykN] `hp+\ӲU馉YZFN8/]wu
q,UIlx^eܼYR?k5GN‚w`@LE""EqQQ*Fy3Lb=H4&'zSP`_qjQ@cU6}.tvy&S2~[Z.7470888٨eKʝ䷮!MpJϿ,LLL񌰹}Ov*0kjZ[[|} șcdbtt4>KruoA  R+)>@YxexQ;ލ(?.TbtZ
E4`2E??|yG%O-˗~P^^N$azz:lӦ$zyN叽5Jxe4$QWWG2?^|mJj$>uS=S.~x+9->6mZ#$Ae(YE%KT5GO9	RU]* ҖZ(5j!˲">@0GF/ѠωfctfD**͛7_Hssh.;XV&155,ˊ5=RIa,##04f^G1؁>1BF{:D *Ya/65FVBV8y$&
-{iFN̨l!312̄F sxx8gEJb0cll̌b)HziiiYEk_g)Y#\Eu/r֘QAFt9G!lTQ
8FXd;]+frr2ŋ,ˌ100MND*ڏlOzXBH\GQR\gAAAQN'$)2CCCh4
f^_ﴵl0	χenSK^LTQɊQq\ֹt-qa&'CCC0;;eOg	FGG
>~Cz]uHٞFzQywzzTUU+׿rB[l!L׷l"2'Ok.,³_(FEW6D"_}l+ " 1
}|De`dLѰf$7"]P(hf]u\P/Ɉ.z]p8sss.l6J3Osp
Ćsn谪wl6+c,&In|IzzzϪN,L244]wuuuL0xV0O?/~5׋t|nI(枙RId*@T"8G:~gFGGn;Q֚B6We]ANNj+ӽ..m-Fm6ԕXƼDQ;opMZ,yYwi$aZ;g㔕QZZI*bzz1R7|3


+<(0?.""r~ȤG"̈HE.6z1JD%+R)LoI/7W_䯑$6UJ%S2$f64hJ,/P" 2D˂i/s<rt
ɴPr{ؿ?ǎc||l6&	Fpb10@D"A}}=W_}5źmg+ʟ{N,m6h7#S2	8 P^wb/JX'M*kUdդ#
%WB#7x<
͵k	I?cA
NMPZZh3BIan.̏_lYw;**255h\i,+p5\CGG===2::zVXȖ-[hiiYz9W"ZM,DI>Y+`rRD6+D%+
MG&H&몘2جV< K;륲Rѱ5
nwtzfif*nBR;Y,::: J1??O<GbX?}'2WI$Nx,УQضmɗR)fgg-n_^b*kWɊ
,nDedYFѐL&Fvгhȍlt~9LrI9On,mb


M9

O2\בX,Я#Hxz($aXp:n
{<#GYć, ߀LD#N$ {*yj$QRRBcc#---9ivQJVT8L\Urknn8_3O"H
ϒ*
˖vIa/ɔvu+**k,Jr8Nx
N8A<`0`2g߾}AKKR8^wEbk33/%,_I7i!tna!Y7`nnNyy9e!1H0??O07K/e۶m]T$qRc6N!^(sGy2_bLᓭ$)ڊ{9yl6FGSxU)Fep@XFТȌAKCv;HYLMM144d`0PUU59277ccc\.1LY+p1n?mMͻO|x&(-xpPWWwnx<cppݻw^
gxxUI/(//nݍA*u,&0>يx<՘?|VQnt}j6SGx3h$h(qMK1W59ѫbDEaH$D"7l---9rܞ0 jkkHDAAB!xyއ|="n)Nnu)UU9"MT +JzlO<={"
ɪ,2s}-?|yUt\LMM1$!$I¡0MeyGx)0x,g&"ANu)Yi}ʸəU0?\vرY_ZfG{S]]ùwm$R0LMM@ZԢiCA2%?V+F۷NSSMʹoKhll$4G;G2&5޾)^&&Fxg#Tk(%\nJR"l޼y!Kp7{VlHRB!Eh'ofKɤporv`tw/?a"b00'O‡>_"Ht:)Rf***8z(~_ET"vV
2>|
+hZZZ[&OL~1L(F2%gc#^NhJp8hjj_(>|#%Ǜz1Y,47,Bqq1MMӽ'}ŰX,\}LNNx$L200f.;9mF"+4h4R|`0PQQAWWBʒi-[Z|2	s1a|\q֊26'N/j:$cⱯ~.b}JKK19rD1UQY1.K%o"`hh(Ǔ$J:::鱖b9L1742<ޜc)b"ğiGRd6ֶvzE_'2?xqYиty$G7ehooeo$	z{{޽{%%%u]X,:;;".9n,cddcX˅N/kE R\
5#G8rD_H?i`"mizzz(((PFtח3kր,Yy{>B,!rp:rE	2_\Z?l/fZ͑.h.Z-
g=(:4Y篏0Ő$믿Y!SYYqYeo}I!={8z(brrՊjd2hHRDQ0PNGMM
CCCTUU)Q́ꪥ_f}UY
!ˢ{fhm='76mR55q+]$
x#{8"0=]^GYY~k"UG *"ѫCt\Un\Y`"`d$h{AM:Ej)//grޮIvwd"Bbu,r8q8>'n:;qv[F)3ADtvvrd8%u/UQIhصk.}133CII	ŋʲL0(++ǫ6K͛dhh	|>Ƃ(a444b.Zgb .Sn	Hfu_ju8b39`^Qc	+ӂLD4erRu4^u']8lz֯Μ5iUl@T5)Y?_䭾i(V*Q###466^b(2333zFSI-2HQX`eQ^OaA!fVUXZf=pXB%TVyo[/-_29e`"x:jjfϴ*@TC$nJ]]QL&FV,qHRq
7ԔujN&MM%IZycb4sh`_msX6%>a[/?PeY,#KLq(G,&j@~^yn"U:]vW/Qɚgw:FCAÉ'l:XVF#$L&D"B!3D1k)Id|$ZS
%1V)$Ilsd^3碸(ju!JE?^P@>lv;_=W_}5###x^hVKqq1TUUt:It4X,qJX X"
+ߍWK|B/Rτ}qYDžBIӢ6bsqbb"gcUdѷG)++[S;֨C*ʡ{Գը㲺Bo/tV#qmdֻfd`<d)j.捞iBPxE$
EV#Iy&cNj,^o|⼺%0444(|hڜwSS'"ݕZU<(gooϽIiZ6JD"xeFÕW^*kUd/Š+j$(*"Onm""̄bē2f#.Ⴛ5Uf*f|9 ֻeT9͌z<ngbbp@p>.l_: QjrK%ŢvbZZd59"`Gd&`~~^Q,T*{J~QmxsH(ⳟ,n̥^~|Ok蟡lU/N8`:CP]la[TU@Iq$I~i9``pw;=+s<}{4wwllSC'4BH`%M!ل$N6	K 	a	)u齨4i952Ei4󻮹'Asa38'f=dR8LNq1EXl\d2ML&=D-jkO?>#X#LF3HfVT*O>苓\E.n&<_k׮ߎM6Ł1AV[R]&v`\ϥXSmD]}982"-ZU
}LU$*BcJdާT_bV-хib2H$O8ccc(--z sENɝ$++sW|$j	㏣n=˅
,,kaشinV\>|[­ޚU谎CVkάTەi8a|Jtuv b\[[?==D"(((=#ėD,B48HAH"5ُX\F"((Q_?E`0<3$N.9"@Ν'PEE@s3P4
	ˡpgry:P$ЛrNK;։ũL2Q#7u"jp>zhE0Jט!۷`p^UR˜/~%zQS(G/:\h!044H^W/;qnuQ&E:eZr8F$A(¸߇ںaR|!1(4(])чaU[ 9u41Xl9N7sV~\rԕӭ[}bJj%łC`JǩJZmfw(F8?1ݍ8Coo/".	?Ad51x	˕S3b;SKG$;ÁFdB*Ys
aO({HjdSX_;(JJJx=7j5b
:3;6
Jjs+ntb1^Gk׮jEgg'###Z83Q8x8x0oXLCCԊuyyf8j= nZ00FFi`i)E?
̤`ZH(
E,+/:;;QSSr2˲ƥ^sQ"'/~J
w8xXQeF*3sxu5k˅OW4{h_ޮQ塺d,HPQQ>3qdЋ:(**⭗<;>TSuH$K k76m:Ŭͷ8WB@"ફ/6TVVʲ,a۱tRqSn'/F6\0Mŀeˀ+I|8[Κ8D!6l-[҂2~BKrb.N:I!L&Ӕs$!i7b;t)5)1ܝhZ>z8"#o`0@̉ۃ:ߔK.
<>??Fxlkn\Wx,ܘD/Q-#vv@"%~E6Zp۷k֬3Jx]((J\عs':;;a4QXX8cKV"j"
aXbP5u`Ov}Lhv;WItEvX,|xZ|Sx^.yyy+dX\E bŊشiX=ae˖שMcoA(ʹX45!Bbؤgd…^Z!4B oO<rO=JJJ~ydƪJEIKԯsx`.f;u:si~E~0z֢ݰZ''D0hjjBmmvW@_	%DRCPSCfko(߇f,:5N{/Ӣt;0PVV@ 
ۍ`0 a0PTT4 @2ĕW^.w}7^/jjji&lݺdL"Kxeb|qt:OT$on尳فWYơPTLSh4@ nzIol6XÐ|R\({j5E"+*7pQTVVξ+zp`hh++IkN\Dd0c^YѴ~K`6vt$4˒zزXwxvDBUUD"`rĝH(J.?9p8~'gٶڲjkLfSv>07&Gn*
5	) pAd͛7.cttMMMxgN[h\ﶹׇں:^dۍחΞ@oF݇|uz=&S%1(
r
P+x{O_:>8B0z{{ۋzh_Nq|ǥ-i})lj|E|>Ejx:)C Tev䋋ifE_p7	lH$~<<ųVWLhF$zEq:' c:
m3ۊDO
}7en"@B DVcƍظq|
o(db|*v@D"1

a0Fp\pm3W.ƚ/n?!

I'T9gF4Ś&
R{`H"FhkkC8F{{;hqDiA0FI_PSJQJ1glZHi$:voHol?JKDD,Z=eq!bSougC=$c:TX:ޏH$H4yL\jFkhKT*ŲeD&LV2Qźr4h='8@qqqʙ0uUO@ mZZJR_82-%2N7ybGuu٩h$txbzu:lTy}LTbxbx],^/>;N-gB	dXxi2i"a.ڕ+JOd2 \GL|@7ߜDz$^xZw9{@k+Ud,[8cR%N5xxy' IDAT*V;}.FG)Iwt>oG PiVҬg05A8T"I#CF6/8Ga/G.U5K/=Qy&şjg&D"JKގ/Ϟq1
"r(6*qσv#???[R)R)t:JJJt:wC+kPFF@ )Zc#ˣo~REEQ{-?+dbTRyy|FJw[3
 n71"L
!ߏ~·efz=x`C=,~HD"
h9*ˇ9CC4$1hݳHPT47o1NϽ2	R)}Hݽ^X$4oTRQjg?
y6HBme69xַ|_[G,&"SH{\@"EnƠ+Œtp8K~0_px |AZk0',8|(XfHa`0Ƞq;a@|0@&l)^RmQ"P[WfK'~&r@l,2:;i':Uc\.VwŨ'1do,(Ci$S#q.:eIxwT90hQ~|V@B	.
EL06U5R3>wozx
_V28S ch4_Ҏ'BCccҳ-0W'K0nǕ
x5;udvJb]- m@oAZCC,A&Ţo^BIi)ftFF@---M"feLXZoW֞^2ÑzQ*0O.jyynK8GT"#VKxJL0HFwߥ֢[%[GcEE34yoF`	aPRB'?
6@ 1zHںճ
^(嘑cgbcjJx
( <jVd(RY|Bb.<: @?0ƦT*V^5xays	$	JKp-CXR:E03*Ui;I"j1faO"?=&'O6@mj0E^I.M~z?VW	E|au=zZ֯9 @
Oo"SF!aҶs<߾	'¢bX,)Cd2d2zp w~b.KrmSә0068(>~0G4j5ލ.²$04Ds4^v52o͜~pedI+EP(瞣-LV<O~B!Ibuժ=f4Jd|9֬ɮi_ ^,@|F8***Sq8.[ǃ0PB+0y
&%5^$I'SjecY	A]v482V`4x~ZȕN|5۱N8>I`vI^2WY
HC\pxrpkQek[6R|P<^)GP߯w/rA^
,
8ëltяð(((d\.?,<##X/^Tɋq9t"!++SaI#[
_à!3}555Q-CMaMMq?	󹓜*	OSQA?|8֢:l4;|DLy¬Sۚ^je&[DTٿL!Xwi^ʳR|y9 @ Af'ҖeY8G8Nl62)
b	XE,A q0鳪aŽش|>H"526.I80D{{OnYKk"Z͆xZId
7twm|U>]{@dž3Ldڿ%ԐQ^?>_ڵB7\J@9uЊHUE$bQ֪N8ݺFC^tpxBcjcI<[ψ7G(..Iɜxh*pGUT0***rqj_-j3$@8mw:p~54-׬|۶VK$$D"zqO,]
y'p鉇h>_H[Z;/c	X;PYCD"㜆|Yp1bE4B9ƨ/8)y;7%$v00W[&rJih
&S>*IӮј
BH$d	|[QWЎv
{w}!\-)j^?9d_IH=0HX$tPȔꫂYD`QE!%wA˼x<.h"\[A;|
9*5Z-T"8Ch9VTոam1q>rØy68C(So$Z31e.ihf###8:	,~}
Kdb--;:͟&!yS*+w3!$yy$D>i7fHrM&UTPLU\L%[]#LmXXD,DI8ՅH8>Q_܁o&S>ʫ-ӶBq݆u:>}^LvO(
K3CCCu}%f/RekvK(J(2	D$XnɅlə˗.$LPPuhfK_z0l#>*hp%8Q%zl%|(H|$pZM{B*F(O꾩p,׋~7-_T&GSӒY=`0@raoAu]ZSЇGi1ޏL.9fn-gCtuJlnIJ,\h׼ndz|)lBQsɞBڕ߹}+S|$*U*H$dXO'yF&lh "D ,h(<j5)@1|T*tvvAy
,AР@mEHfB4
X[L-cņEdyfT*UNIR"pʹy[ng?4N_}R}bTy*';l0
2bVJoO-iF"k"{S]MӾlvL}FNvl@8= EQ#òr=vL&nm6J	,*X

!З0PTPT(((څkՅϩL`0VsIǃX<a'ZfWau:թ=|xxM8BM(#!3DH	rZ'(-h7h_O@e[%fS0	5$Q;Ŝ7P`a Euk/pÜma,6wC&6
/<?j5XRCI	\Ǔ88vTYԸvB:9ldX,ߏyM

hGY.֎DrҼU'tNu:җNd;M,Ne)mlÆ>ͨQaVe
Xh==4%f@&maPTTrx#+~?z(qp(|>?;H04ϟ9OpSDHh?<<؉WP09#c.L5\ ;nnRm-
ēˁ6[	~E}nqD CD`QqTՙD"h\᫗UC#c3ͨoUP[[+C G$S`0~Ò,xxfw?GEѱ|m:@>݆30st΃d	͒p=.B<~|!>X`ŹlL%>Fj55ꀿz{I)IpnaA,*bJfKalllm-xzidFEI{?2ezu;6Kn[$z9>s^rQ7Mʕ+qg@SK\}V+";ShIFG$xs3חLb&o w:眩`~Xz/{y6{ D`ѡk!h¨	FqĠq,-VNw|dFJNòezlBPI

ru"8A/hT"BZ*

ZqЎo%X,1z:f
kQdHg?KЭV2֨N'7mnnߩ:P@"-zc]K?4@qxk&	8_~'D"spV	/}4ZLX,˲F"U5XYXlv=0$E&\ߏ6455<bŊDV<G__㣮Q_(J	ZB9D"XE8!tB!1&>===XSm
3tV+_X[nɔ9@N"jn;0^}#%-d<,h/Hqq1\.'oG^ci5+W<F͡D$HGaa!ZZFW
-XɠRQ֣/@jqz7n)@f%Mn"29DɈ^!u\ܱ
{!ٟ2W~PӥTꢈlN>Z/sM
yxSpXD pDYgHtp8<Џnj `4gJX,V=_~T
VaweAaEEE	)X/ӹ気N_:U;N{"S8al|Hڤ}		l6jÙi)^J钛7&tiz瘗*utf>`~jw逯}
x5LR\:;Ix~;Krmc*ߑ*Ia0/@O9"^x!V^{`q>oA3)u\?HRYY2j52tLV5vvŊpF-GMJhv?jmJ6ݢ9LN5¹om-};@?U^jc2p秬lE2;~DT),z
Im3UC8\QjqJP%x|3>N#C@8*-TVVv\.|r}(J+#֬Y:N`0RhZ^>rQoXvӔ^*@:GͲwiVjǼZМJ+>6z~[$"Sl3EV,?⮼?!ysz^|@-|:"*%	T'Q$>pgxR]\tQfg,X&X,W^y_N^W^^./~ƃ1,ʲpdc|!Z IDATIq5
.T%Qttt@C7@VCV9@ V$!gq$u$(6m{@DzrǑGa|vγ^~9zTxNmQP@)IdL\ CGI@닐HhBmtTJłldkyۡ{z} seYb1,+|
v`!Htٲ9UR}oYT"O@x!hX\< =6"|"8|ĈXL' є8繊Xwd+ U2Im|s]T_O?79/nT	lBⱸx \.utΥ# ".a坚71 q9L&ͲĹM- 2]
B8Р0h,ѡD'l꜒P(f/sE
xBRrBͱgpd~)//K$n%#OaVndϴ2v,[1T(|''JTCB0"tpiWXN'~?x-,ysq
]v/NrN?\

cw=EVv2]xۃBmKW\A&_^}ڶS[\L-^7ݔvKáCсp8a  96TSӡ	K,I;S`aq
͏SVVұM.(q7
nqt%Ycw}>nR~lXDquaUi]{f1xՉƌA!,/ϭ6m¦MNmll4ٵUUC|pU?ۀ;0e6[#$g2_y'!Ӑy&ɶP"!ՒGeBeY8pH$dhJ:0::?矏BH+),,ŌJ:>7n܈իWgra 3`.|x7!Jm8ѕbP>2^YGن6BmvZsf}f^/\G @3+J%>8n'q9ICU"NYYRD"z
hhpH[n0
QXX8O,CCףغu+V\uD[V+  Hpb7$o&ny<$O#G8(VBSSo8L|~I
eDnԖda;^?`A-E(Ƒ#GЗ]F˅(T,\Rb^G}|n	n޼55/Cy
`FF(5Xݷ?D|0wO)Q2pgE0*A,kÁz$"QSSRsD_{mB|u]מ
.oxطj\N^+"9v?R@fHQEo
TnsFqՊ6 4EKC"rr-vލ2T9O H$}իWc۶m<êJ21v#WllqQRZm(|>XE$A؎1˱X_֕obtxx|λـʊZAcﷻ[PhZRTT*EyyK	ETq^G~C۔	rkei~D°k0;|pYq`G#c?kgN'}]8(
zd
v만NRx<߿{./r" X*%0iI
$"\/~4ٜ3	0;JF
1Zr"QHR*6
QąXWVWY6<!>f",\@8v"??Il6d2f4ǡX?u)
3,2fLARt/..XfJq2"EV:\\u	/s>DXu5}N8n8Css3BZt)sZ
4ywD"lFkk+֭['.2E`^	cx݅=+&P_<1jнtyoEoO`Y,iaq k pwo8680|E")//IlB\f	./4"O5pjjkVt)D(Nca0+*ݍKD@ y+rDǗ=؎8n_e=vz}ׯ'a`R\^דA{κgh޽8p


PRRτdjy?!}VD:JJˠT*'h@ɳJ8EJ*᫗U[ՅDH$AGG;Үqa,Q
gTqFh}0BQ{hxmϒǻ.\x;8<{**g
fi4x^BUUU5j\|N.j[4sMU//vp=έVt:\v}o7NEB.jj6g?~8yG(--EJhokEXcpXe/HLv +Q_9
xJY&	(1wPTucd2k(hbcԗ9R	X,7GRkߴcO>tIĄ͛,YϹw[a}^`9ڵg>
MzZ2BA_}gǃ{fyGB![q"KmG87$=[T_]4S늴xF<--Ga2壠`}"nvP_P؆8#S+B@MmZiw?wǡ6}%ǙZ
XNuET]}J	},{T*Eiii+Y$ɢv:"XzK'q	ӑLΎ|iw	R	Z=aNH>Ca52|8?F GSSÜwɕJ%JK˰6䣾ą?G Ga^,BC_ȋ/9W*9:G_n=,xYgG9X?_ұ^/e|YJ066>TTT{pS#H	)-@Ѡ~4LoEbU!.Zf~=p#g!cyh*A,bǰf(^؊O 1H_,bFa.ʜ!1U?FhL
̆
Gk+snhH~TJFz']pP45		<+Qr$Bv ޽`DBekkF t:]NU8C0DMM|ΧL gvL낾~Z͜*5Sa00xN]LX:=FXoWq.hL"U5ws*D{[FGG8,СCHsy;tτ
aPS[x6nu	g
|dln&[FFrѮk+#ZZ.l>|s-8rPćE	!oNۻk_࿣yeɒg;!HH<\J[PRv}2^hK444PH[撐9 [y9q,ے}t$Dy묳ZW~x_Gn.O9F$]wmlQ(عߖw	CuwOPhL(

ȴdzUDVØnuX5_=M=n荙:-z\X5f
n>o8EoUӪjQ'buO0fCNfd%lP[*peoFɮzsK
⮲5=mrhmmkB.`eAryyP(cǀ2&cO~ؿ22
emq_u:k|}o!>\LRK-Y{ɭ	@H`"v0D0tB!G6$ePB&/}+
him9`(!Oخ|ŻMGR94kc&

!փL
2z1mCJȪš}A,.΀ÉN'|'jPPX9$zQ2'tTAPTT~䙵(L$E9
ENZ#Z1* f!uuR{_:v$+QR"Nt;+dyW1u].㮩?"c
&~T1fkNǃ~\2q(PG,z0WDzZ)m:r(}LVVdtuɸ\.J)2Y?TiZ,]廍h 3M,xj,-6uu)oFjEEE)a	Bj5~c"+*+y&r&HTJ%46 |+/Ǚڀ[o	--|Ubb+QYɿ㷕@@\?oNŬ@p'WzbsjJ 	T*!HQdhiiAZZϟ?7'5?I:91M&}P,2N_]b\IʡP(V+s
(ԡ#dXX,d|%B2%%%P5xFBѯiUrܶ/IF}}=:zQ***PTT,X+"L"qepo /jϙv/ҥ|c~
|3ߚTQ23O|.؝+ϦRݻG}b2@,YY|`Ll~?_YZ@c$ɐwD`k֬5egڂE&79n$:V`GQD݃AOarP&рe.Ap]~FF(ϧVaf/{phTrW?|މI̓dS\Ǐ#=Xx_6hF[}b8iii0{Ky/-yB2E>"|OF27y¼}FC_M*/r|+yKx^L0=8~brxnj"77---`,}A477chhk֬INd(!ҩ1MH\שc@CA;?F
VV9w0c|;W"C\n7K,FgZݍWtkfcϡnfQJ#A"3ӂ=fa6/ ȢCEpgPosB-f2Q?H9Ndـ|J~a!Jt]DZUQUZZ*̑#|UI4drŖaOoT85_v+())'vO`I4*\ףP1iQB&F(F BqN^\ 2-VY㞨B\,lh 4j2jzGۇp^%iY.hrU)jx_waAalvD0$VJiii`hu:?HL	dG >G%?N%?$
t
91\io%z'7<bJW_QkHKKCWW^Q:C8Gqq1.곀D3
Iee'
%V}$=zp`(z0Z@yQXX8Ur\.z2c=ݨ5jBx>\5U
B8 VZDMGlI}N߅윜5x"ޡĕ$3@OOJXw;tDR]͓ދ|x:nwon~EAr< L0Ls=VN&=z


FYY6oތuQ1KȬBRAIbJ*Veb\Gvx!e,[6v.W^Z9c]~b\JBG?xPZF5VhzJӣ-J5hfS0 IDATL.15s
jT!I;O{%#B\6(|N'MP)(stʭ[S<^W{hhh@KK4M¶F.|I"APݍ%YZZ|D?P*0gZ0dNKxކfT
W^MOKK1=ΰLBW.7Ohhhbh-1
ݎ^%^)J%ܓVtOcaƠBg1YJʕR2&BWV\N\x+_W_W@Z
o&PUUTOBT,A dBڡR*`JZF^^A'^
uJW'^(q^GyEŘۃ 
ׇayF
R¡ ^/0k%`I&!CePB[3dEhFf:ql>I>
%<GySC2r{8̛-{ɬY|[	)jRpW׿5%sB!3$YP()S1ÁXJ|scŘJF@a?|0T
ҵ
p_L
VFWW'gEϐŒA"+=e9e_LXSKr,ex-D|v|$Ft;BW:U3jyB VVgn6>Og韦6	h4
p:4X,ƴ*Oqatz1gNv"p8ĕ
;w
=ȃ^`ޫG8ECUZZ?Lp7LɄ_ǦMfDv?
@ȸ''*W@,\іdШUX5׌kϝSf~N#i-.R9sJ%JJJ`0cs *r5ia`*}8FO
K3{ooף@eUur?>Doo/n8`$b˗DD6C|r9g/ɓΝHǁ_EBl6L&WANǮ]p5׈$Շ$2Pȅ*b!LT50(/QV+2ǂ s0tpfs޿=(-+?ϛЈhW+
xSD#qySAglU'po^VW_偍V\	χ.Q;Z
|>/cWV$3 d\2A@U?ұf3	cvu(pf{XwNIT`ho6%E.L 7TƾZy?f+VfnؓQT(..FWWN8!sģLhiqCCClJD0q{Gz`cNq$RχL[O]EsA}]Hx444(r/lPUge=y˅z477cyie{ؐoJG7Qc@k+og7Er!nxOoyW5~O?/^J477GՈtL&۷pX%wvY922-g[=Ѹ\.00:U`(=r8rXi]?\>PjTr|kS
8q6-I^P ''ZC=zF&	zj:at:Z-1gΜ< dR`PYU
yWRD8Fݎ<=,pBVdt1R	V:H
םW2>@GG*++*ӛ*twwc8uJUʘLؽ=ZȈ^_BpzʑA&nP(lxjO|x<mŸ`=Bꡒ_6R5vPg9ɪĿ|B!Cc@#O{8SIKx1̦.CCűb.b_(
̝;s΅E__-RTy.IڬLbg[*Ps8=d2WT@1W2]7?FP!m W`yZD^/k[	m<>uƆru32Ial&$@]pp(pw}=Oq8.?3:0fżƍ=JQ2||{4


P]]
јs@dKfelV=2/ׄ:aZ>c*Er1-C)0"+,=mYqjDM=AgggͶ2]0h֕U( Oi9sCl6~<l
}
%#vJOaއbRvKry*Ut:853S;#n	PT0Lذa>vZd2a˖-hjjx2TxJvi	
^~=QFtvv| B"oѶ+9:}i"/ƼyD9X0|>ֆG
+Mx⺹|$ͣgxUWxPiPZ
K8g~Ģ lI	|kڷy4*5>0իy~8+3b'O^fC=w}/2<V^ckjjj*Aر۷oGmm-."%JTAr32ok\5̪ё!Nh4 ǖPH:r1Q96Cc0,"Q(	M@/#GP__.47W/%ͣqPNL` ?S4yB8|=}ădur^xbv'"E$6/4R[nbiWx	hZڵ	˗/GEEmۆ{Nұ' (ԡ wu>iRDvvvG71Ai'+5EgPH_Rmr>澚^_ՏT
B;ז"7C4%5J5G(>]/<~VAY?>,<8vTtl|#bzdxbюIV@ZFUU#WAڵ[l"^$MnQ6~P#YȉM`Iqcdt[U2oCG::!AW0D[[[*r`7WEFXWDGhlv܉W]?_Ursy)XDrŖ1p$zy.NSpe_.GoorV?yߖOt3iz{{!PZZ*qIrQ2M1"]B-Z4.D}}}BI& r^n>?vv}Ҡ񠱱ǎcG100\dPatKInúEYQ(vӓԽP-M(OմJhmΝ_GCN5C0O\2+1˫k54J`n/w}+<s:`]I*,gR*vMOuCIx?^UQQcǎӪZCss3T*/_.(I*Hwlݻ^ziL=p@ԫp?8^y$KU9|cC:=ΎcNtv >uu+C\b#r~‡5}Dzzz0~L%"{-߬ƥ.ĽquWj9¡خn[.7ċ:475lJ)cp8mb^M}CtM,4kL!۶ҽӝ*+]7-~_#\K^6yyHxG?xn}K `͚5عs'Q>őϭalڴ	d6&	5kjbl'QQnݺ<}YsFW9ZA?Ai+7tn馘ƛjZl^Y+0cB*9'%YzdvQ61 k5*\Teh{p%vI
` tᎵ(FF
	4zRU廍8P99Z"@Af
woF9IIIꫯW_uCē$G5^exҸ~	@s3o6xW@/@mm^|{9/CC0;?ԪY]u|OIIMg'a?+PϿn]*]|1k<țj~ݼ…$j5\wyǏjEvv`}}}lP*ظq#c-@GgYLSO=[.Q(+sN?9kmmᅬx@!hJ%V=CCQLoBX;ɶR.kK=^vX,VL&hQ'
1|>׋@ 5pmMMVwNl6V1^Z
LP(sj1?Rn8ɷf鑦Un~8X 	zE>]8Ҋ6:(
0qU⺕X5
\g8:.rdxq)3Q_D):	l
.7yW|V>?ThO<O+ <y`b1UjJԱ7Cǒ%sL	eddkšCpA`0`0@ьtN|p\#UUUXb%9?}ׯǦM'֭[b
\qxGxO ++9e&v~Q3k'	kfcY	{vlPePգ&@0\sLX$97jP_WF	>Ñtf
UOUűH&zw!77WfAcEdL~lqm;\@^"3*/mT
`012hTgySb>"Z?ݮ3{"-o}Aۚ<m,~m|	txo-p-|djT(]wO>ɷ{O>:%t(
,[,@cc#ZZZ
;R	łs碼|ڻ@HvAou zTUUa޽xquAP`͚5ضmf+3?:`PTT$1].ثʘ*|Bl^nz\s0cգ"I5]OߪCc/(l)CN'Ǻ94^P4ҴS_etA?ڃVM;dxx}r^t&
gawOJhcƠy;ޝ{?s~}:X^*޶A
*{`Onk-ՙy`_:@}=5|@#IDAT7v\qml%JBuu5V@

1BKtȞ}͘RsІvjۃ0jO@:Ƿ̋#\ ~VoNbh1<<ݎ~u*|9p@yyy͝q~?jO /Cu&;癙M9cηY|;ŒH 
|&7Č6zu\|%Ļ
ؾHnU
RV6Si)N5TG
Ɍfa679Fr)Z1Vx|s}'zw^V.'|GZ
UJ(J~7c[YY%M^R+:Cnnn'H^
*yY)$z~IEWD9,6<̯OڞHf&m,+XLD
R|ug?/$/ɷMȈ-|tv7
>Y2#e^WF
jkQV^WY? ҌYϴ27
iqӉ~݁F5r(0k,iY.d?|	ۅ¢>A7VChkmkJpNR3hX^jR݋@l\B[=
05|p8LxA!%rY|DD=[tN
^ky?	hjl^jI2|eyBѣq$px1^I뮻x]BrŤWk=ځdfZ`4F>cBN0,:|mc5
2F/ߎb^!4j
X8 @ L9F_\,2ÝG4j5/&vǃ~n4:B̃+Ǥ	I
@ȌRpㅅX5ߊwu
 `
QcM+K82EF)Ǻ9lQ6=h
w?L@^|FEf]!]P\̿!(!3XN_xn-n{0	 !$vB!@!ćB!d( QB!L25B!qBB!$|BPB!h׮]|B4)=B!d&` nBB!$ oBA2B2}B!B$C!B!D2B!B$C!B!D2B!B$C!B!D2B!B$C!B!D2B!B$C!B!D2B!B$C!B!D2B!B$C!B!D2Xn
Rq9^cڵHKKd–-[$	!$uG	!P'$/e]


Rd:-_=Qo>ff0XFFۼy3kllxܞ={؍7JJJRdl>Td=˘ba '|rǧ~4
[d	o'7^[{_ORmA(ͣy4h%FI688ya۶mػw/x
,Zwy'yQUcl߾袋'_xl6r[*tl4T믿4\n׿N|駒'㽾FNձd}:hͣEhy4Q(I1A8Kؽ{7|zGuXp!rT1fأI9rs΅L6Ww…GJ6Jv"gS}'>y4c&Nhr|rwiy4hͣ$
@D[nEc-_pmmmCYYZm+OFEET*Քo:cߺu#<2Td=x-Z*ˇ,X@LWg}c?Sd>immSz4Q_IűOͣ4ƋĢytͣR$^z%v0r93Ll>~߾}lڵL36o%5j*&ɘ cd2٘ǧcxxO{KK˨Ǧ؇rssZfK,s%/kX|T{n7[7$*ZjJ\Pq<Hct:;->lnnL&?s{rx>>?!eZFTP(|8066׫sp?:.C~nEWWHH$B(r10(Jy2\|c1>b@2,,,Nx?0H__>=9?5_WELo:@AzzzBb[aI&R)BO?4W\хV^xۼ-/CZT*.c|j"+"
*|AI$l
$/|!%-6ᾼ߾~ úz"V(X_C:}1/M
#oP.f``@.s\˗/}N3:0~tm:pIB1aR)_bn$jDZ;p5bZUڃaNww7/===VK^,W~̵4zZVTbeel&>7מB1::ҶY^^T*/}$Mp*,~P`rrx2=Ő9.ߟ'[4K22”]#T_|̌֓rp>J?xo#:U9:23;hH$8=͵4GT5$b\.Nj/VKGdllׯNS8&k5%6L֧tX,xH\楗^\.+E{rL$7֏8M8z8"x9ӟt:̌{8"2ͻmpDlE.n~~y{(ۋýP:Y=0<۷7?V}Oww7]]]-/ʐ*@*(w _Ng(9y\0LD}(ޝǖ>^"xwi77o$Dn"GΥVQV)r9of<=Wݢ_-Ż.k	m1]ETbmmÈ6I\8J<Ẅin7nG$appbH2g{Sfy3jOO1]f~+7e/y8Ż1>m*2wUU-UBwjJV݃c!i{gZ=5"&RCj(ĉ8~6GSx=B-*_ɗѝ^qz//-4_Xk=00xlb*՚}o[=
q*f~V\b7J0?5pmtHVlrk>K=VH$+X])2fߚНUq({U|_R.oRy)EL(
n;prXAlB?lŻ]xb;W ujʶEZMlA*՚)"
Ms^0u"VQ/[``󹆊w9/ŻHCjySCR=*ܻC>A٬Cy0Ż]zM3{2Ǧo_
??zwK
,%Z.& 
^G%NrC"dɚգ:`0H8|z*mϭ8bkkL&Co[ƺa%ewH<իNDr9s#Y/`kՅeqqRąv`px.xnؖ]K.r.b۷oߏmMcⰰ_cwX-\xD"ap0`'?_YG$NQːۆ߅#:t:<1^3n߾͏c\B8n~%[P%@Չj\\.GPT*RV՜D"탴VQO<*;;;ܹsVvcpͳregPr^w3j_>/6u_wsj54A CPB
"j&K4m(ޝɶ~cfgg1nw9/Żșܸq7|W6^qZ=IJ2aIh7fUk#X8"e*ޝvs~~f0Coo~.9"gH$y&MR99!S_P¾Sf;p ⇑(\(m8J~whλ6].#_Oz@S)"v	Bܼyh4GWոs<!ldl]t\D"[Yl=?{GEgMxRgKo)=(Sߟ ry4OoJ/)"Fr
{iwvx"t륲L~:or\Ļ\]:]Tx\B++<3bHK~:pŻ]tlnn1k(Oxc"w)ޛo{{J!'4)O8:A.x9⽹1]e*aHڇt6wP(ENxor&OBm7tmxQNn\.?VxopŻ]twsi#͢x?]{[;(E"SJyz$M~NwaՃh^_Y<C:À՟nV7wNg.&y3
Ih.4]T~~\l6çns>SSSI0i>x?]{G;(HbwS)rf8.ӏkxpŻ)[L.r*N}'o)Onޑw;R]T	lopScvxo1Żȩv)4#x0%;:AnGw#>;vswoC&b$;xͿ^_=.U%+
.\h͎Mw;|DQV6Ytn	B|q'ƻ3P?ZcZ2|yycm9:?ܩfI$zlcu{<|f&QDn?~p$GE>=ՁNn?`ϯ7q\̍8CC`]]]uTcTl`"###9XC]ADT&7}xD"j5nݺEww7$]|EazI7Hn?p{ mK~:MWWw)Ua,֚?.N묊h|1@p8}>
+=R*y&9w	Bj5ޛOruDW^gŻD"yoajzux^yUYfffy*{CpT՝#z{xu*].ݤiVVVݟ(Cn?HQ>|6|XvS?0us|VBE~.f|ooMl{ܵ"]o[ޕ2ޣ([[[d2)OxW7[JUo}sPۋ&=(j7s-T5 9Y^_czzzl;Ż󱴴D?}xk|j2N,zHRۋ\.smi_%P?կ:%.]gs\5lyP(D6Ν;n7n<;C$hgSߟ^///3==M0hsD=k'WCP(+QTXYYի}PU~Fwd$r|Ry(Ż}B!)|JWDvx"a"CJ%_u{)(V$,V{8&LjIrz}ܾcDr0Ww^-):j|eeK.vyȻ2HUpC[Rĝ;wz>Gu
Gx@ @\g.AB;;x..v[=i3VGmV{(bwwk7ޫU^~GK=E,UxZq{}>>צKn=MPdc'Wp柿ؚUVC$b7M2O>vvvx9oE׌e#F@kfr5g'
Pz>G1Wn7]MlV{,*,%3j;6=[[[K̃4;.6tww{2k\rvˊ*x%ɐ;.ܬ{:C
e\V`nCP"iս
@3~q݌&;
àۼi}.IbιCO `ggh4jy92e{TVV|\."밿`Τ;ca+KfX=G)4`ejV^5?̖#z(Ew,ƍ#"mnrr}ki0xvS7}rp(_gaƛlM٬vݱvxH	^H$ IDATowD"Jk{yr&{"Uǿ۷Sr1{>HHd@Pz(欈`{{:/h/Cl>(
7+_[[[LLL>Ϻu"'wn7x~tb)E"ɤ8f3wktuuqz(t`zql7ɔŤWDqc:՚A:n~eL?nGG7&LoP( '?Bbwvvp&S^ $*Z=nN@ݴ~w2޻ёr	WNXup\Y
a.n]C G
~7{ `H.xB.k梅p2gC<_9J9Yut9s|q߶Ww
{J2T߇=p*P(rU,\]g>2k!!Dx<8~ݭQ75Z`ǻPRsrZq]؉\.[rW.^:wǾ;llq#P?{qŅffgtxx蝵l8w/iU,^~[{x^GFw϶I.DNZ#NZﯾ*KKK\xԠrld\.GEsn+Jނ{
@	z{{-DQ|>._n7{6ߧT*Y={z|˅r
9=8c{,q7c`3{*Z|0Ub\nBNMfp\xwnfK芻
{?z%'jUSeDz0
m7Mnf].*[`e7ǟ@˹Se䓿؟/Cl|m83UޠzO~e}묬ꫯM*㴷dV8?͘&c`[<̌nlf0JڔߝVM:+{գWd~;|r83Ĥ`o̓Zr#(Lvd
AXf)))O<ŬNSejAwJUUۭwpo"E{k	U[ {]Z%V_|>Of0ev`d^]=7C6{QNXu
Y {]T"e+~Z{y*N?lv"hm8-EV܆iS8M*"d%J%zzzhh!E{kEbs@
rh
{
IURHBWܥ5Zp]xO.?MY*(J0`~nK3&;cvvyz-{_|[f&Q[#Qո4૦
Q0;x$	Z؅xKlNL&ȃQ89:栧?UUnSCo?5ǽ)ڭf{݌AmuL!=*h+2L6}_JR&TdH$v`¡ cm?5N}{r_18_{:Jq݃Amp2*9"NJh/U?nB"'TL&`_cLw`c߈rۋ|cN֐jJ.㩧2
R7@nbH&g&?g
yeY2hϗ=XnBbXKר|&L#fJ%?g
v29m;<<0[}p?'Ez\|z2`oT{>3C.aU\0jOVX*ӄD"VsN.T0͜帿υDwPzapXoo|]~^pr4ux=='=i=^[XQr߃VVIR\pW_}U@`$_&[(f|mIqraQ:05oƂ,|&'vJ0)5*
v_\VlA訂tpm$4q=[:v)G?\h@ V;,M3iC̳=w=y0E{[7	y
{CÝ;
`{{
~iy:>bK
Gv3=/Oq!hoL&<_6_J`OOgyƑ?wE؎\jkkk{OvYEz++ˤAԆ
v;,L&~뷸|C:
wE؊<+E/Ns2R[Zrt{|xmrT;3X]]^pZґhP[VcggMFAW	Y=,ivkynݺxo
v{ZessFFFmV:.b
s3T*B?yvGv8E=R)nf"_V
v0ckkZƳ>O<6?
wEXNސL&&L+]e(%Jt.ּUU`Olnnr||gD"VTv!LG=!<*fYUKЬE>gxx{6y
=:"b{C
o>3§&bq2Q[TbuuĂl{'n*%?022bК].P7D.hw0888`scrx"$
vu+ڥ
QK+)ڝVb.?wpzC'W]\tvskt100H<c=m+ڥ
QK)ڝ0,d>ÀJ͠T=. H$:v{{ipWK(`+(OR!S*T*TU\.nG  vwαYޞ.
(*viw
VhS7D.VRK;Sw	wE4!
v]ڕE+ڥi
Q(ڥ);].M`o]B.Fv1!
vE].R7D.vhv`92Wwsov1!
vU^yE8]qᾝ*'߽gfvN.`o]jkk_|nE8]QʕޢPq3w^!:
(wK0dffeoTr	rƟ|&
s.(ڭ`o]˿r17׺vi]c.YglhGqoO|Q
v0xרT*\rE.p`gW{9"[:'2>>N8z8ho-{C$>ZOE]ap?ʗwoH$z8ho{C4

ݒ}*<bۇ_NՀqgƃ5
QoI0lY<(Q)Ld/0>>dͧ`o]laamZ;hG`fm
"]agNQ7!
vqJoM" 6}v9+4mÍk{yfggQ7D.b~~c.^}),

2IW8ܒ+)R7!
vi'ZD"A0l0
vi%[nLK_Wm
E
QK;Z^^&rʕG.`+26O#[=SK[nD^EF.V]Wkqx9h7!
viw|
ƚE܏]v~c\xOC9E9
QKXZZ H4e9EC.vbp~'M0'
Y=S7Nt5hSQKPyh7JBZ0n7^cK.vdp!x(`o]:QV#L288h
v2%7slR#2b?C +B4W)lGeCV`o]:.J)Zl{0%QCpRBUslqe``Ki:]VᾼQ7D.۸nW)WӓjfapppR(<=q,npp5nﲻK"gt.4]Vᾝ.|VN'R`[PȨ5CP`uul6d>\.
X؇R 0CCCw\Q,܋M9(Bh
QT*ՔA<ݕ烃VW1{fJ?/~Q{D
vq2[{2UׁP?
{C"KӦOe9*1\Ća3=fy0/yCfffm;ǿQ
vi

@ބw.Q7D.`aP(L"
;E{]<3lCWWCz t[{-(OY=GP>ptT2ugeh{r`.]1K.fw(ExpͿRB\n\w;G;ύsd2MZD
vd
D5W>D̠`iP(Cfhȣ#z{{Miuc-].bpYOe#S4_,#ϛͰ`%h~|XP(C.	[`,qa0<
vI$|py/tRMDK`M
xZ͠X,Z^,")
j5S`c}٦|^7<5o100В7*ENgp8dYؒ]:CCCA.#]>??\̰GUh2.7L
vU.d2Y=)EӃ'ɘ17M۾ӣㆯNwn䔩>*Fys[P؇bddls\8M,*j86)!bbb+
vGgpr<+&͚~EE"411
R	o=n4h"##

s_(XYY8S'y:Ir@X`rr\Z"2ðzwÿ''؝ ϳXv{ό(ElT*

144Ԕ}xo~P0(xLǁr6;;}/\CM)EW_e{{˗/73V>/Dh`0rQ(yGGGx\j,>Oӆ45u
'Tkn7aP.9>>&͒:<TWSX6H.\.z)^{5r]]r"hx<;L&lX;*S/_\`yȕ+u\E{݇G˗x"DQ:ĈZƷ-`Io;E{ݍ7x7r
pQڟĈ:ܷC#zP,s̕ǮvP5~255qwfp6Zc<]?_;\y*@Y>b308hGio
v/c=ڮu/6x`'}JUŷ>0;7uh{[|M.]"h`,LobYooYZZʕ+s4
viwyz8MPstW:ӽ"k~~_,?jor뀋_`vpx[|Mfggr	D{Q/orqz
vxY\\˄B!w
"IDATcD{`ll#DŽ{fVٹKm|#^wN㇋wƁY."w+|;!q%~ؠFM}(إ9&
*ys.|=37׸p&>"r\.wjsssx}Uəuoׯ_oe	2[d3M+\3eYn^4%
~~QL=(E,RwzLOOҹlJLNN
v$wl?{q|陖5LT%g3CoCa/bddq`GK/̌#4_M[xVo}}}l

vDwl¿"LNNǭҙw8+S<_%7H$LLL8n4Es0677I&<<7?dppQG[r2^M388Ȉ ZVY]]/_=?ݼ{K8iE
v1K>_fooQl},)Jdxx'6^0~:?ωD"LMM~Z]'_ 1>1i˥,++2'xf5s7?_~ȈmO\
vij;õk׈bn0HRxZ[[[TU.\@"qW.	LJ{N2"bzH\f}}&_-*
޸K+mTH+oP.bppǙB;w8::b||/}K-y7YYY!166f"m\꯶y[=^dLRaww$~>w','$Sz	GF,>clV."-U,yw[vT*.]]]|s{qq%4C,)?(ne#hE(SnRA(Hl=H\g2(7-\x%q/^
@VKWtbիW;UOus?*WNH:==\}_~.ϝ4ӗ]c5
y[0,SƓ`p%ݮԣG8t$IVNҦinaHŵAϷnWYVh^_*|$:;;SוT*XW߬h|sϷE*%5Z*Jy}ipW'z>~J2z?.˲Tl6xצ84LT.~(tt*͋2seʲLwֵW>5b}}_NƲy*^U\.q7Py8|xk4H>Z6ɝƥey^xT_?+Isll\},4L455DJAЧ-5a7zvww5d۶*Ջ9u7^\Qi2}F#A ˲nu}Ya9YiooO;;;ۓ$ؔi'MSM&FsHiV޽{ܔyo'FSGPo}Q*IrlKv
,Km˲,i,˔$Vʲ![,^UZծ^ϕ7u}97k$iaA*lǑm;<ϕ4UGHQH{A>\um,bu\\nWԓ5q.۞ʲl6Gq05Vժ׵v=/|?0bʲ8#۶gst|bgPʊt5i1n\T::u48Q0N%Jrі[TUjJjUZ1:9Otc/ЫP0(LE׋~?tތ'"_DϮ{mΈ^O_0b\t)n7'_J:XթγO\y)]_x9L|㟾9XYYE}x|U7ooNy@|؃qbǧ7gE2'qOz+>kGU
݌=︳o}W~p-~8~xxKw`;WvO"ށ77K	w`";#܁(O%ށ[vLxn09wT=FR\AY^:9Rxx_[_|r|W>WQEe^/{Hd	wvNcDQ1b}}=B1o1; fK ag;LfU`F]!܁	v@Cu;#DvpFM'܁v`+ ܁w	vVCJCwh1"ڡ~;`#ڡ;`v"ڡ;`!ڡބ;4`%ڡ;4`vCCD%!
 ؁퐋p;W`CCN~vKC	w1Lh;Ԑ`&IC3wLhP"ܡB#ܡ&$a;0mKv`D;4p)vh>S ؁Y&H&ڡ=;L` ڡ];`"ڡ};`$ڡ;`&ڡ;Au ڡ݄;lCu!CvND;!=;P7ICvD;p;PWp;PgF*;"id ځwMYv`'F@&pQ;h%id$ځ&؁D;[;hB`^	wR@v`?;&؁~	wjIM"ځIԊ`F"ܩ4h&IS)4h&MS	4hA3Sh:Lpg&;&T	v-D;0m@v`,˪XZZo}xwSg=Gro/>W.F(bP1w5O@c<#VWW((2xcc}PS%ܷzj>}:N<n7`˭k]/O@#:#Ͻ}.>ޭzLΝCmkkkk7'/T0Y=<{\/a|ӂhΈ͸pg0}ogΜv7qڵOU?7?;C׺nK_ЌΈ/x"mEQDQī:w}>`3;6EYQЗ`0>LV}>n]MLpߢEzlj{`z8Ѓ>^WTL?Nbcc#GnNXYY8r_|6&o1^/;p:UL@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wH@@p;$  	wHi0IENDB`Shapely-1.6.4/docs/images/simplify.png000066400000000000000000000466531323200062600176760ustar00rootroot00000000000000PNG


IHDR,6sBIT|d	pHYsaa?i IDATxisվ6K`RˎxvHLv>;! Suyߟ"ϛ읰lN'ijc[ԚgE#rARW傸W_Z_kEQ@DDDDDfԺ"""""z9w""""NDDDD܉z;Q`p'""""DDDDD=0w""""NDDDD܉z;Q`p'""""DDDDD=0w""""NDDDD܉z;Q`p'""""DDDDD=0w""""NDDDD܉z;Q`p'""""DDDDD=0w""""NDDDD܉z;Q`p'""""DDDDD=0w""""NDDDD܉z;Q`p'""""DDDDD=.0$!͢R@oCufj?NZ!I:,<=bhGԒAQE"hpɲ\.L&t:bL&ԍ6ĕ T3lV+**æ$†8,rzazd2u?V!"4rsL&S;~.OBRLpx;"^7Zq:-nF#H;5    #ˡ#h4aa`ٚnX`4a4-H4`=
`@  p:(ˈHE2=`̭@eZ
JrJ.`gΜ(j"edHO>E"@>o^7L-fF&	.نX_ǐ\O+EQPIZ#dYnB!btt^5::DP.a0044xv;,KZVRH͊F0|۫1
1X?_gVkrbB,b43g r&":%EQH$OB$Ɩ|>prˏGSEQ`166֜aNbp*JD$roGiGI"2 nBQJ&(`xx_!2
r|3[===
uZ]>F3WfPw\p:SVὕz|>|{>ȏbjj
_9tjZ
XYY?15j@LK",f3BP(tZ
Dq1rC#GFF0??lHDԏ*
ױg(73?:qcjjSb`peH+++@V?|x$@].0|>_[9*l6("bbُwui$IdYF={sss8{,h"XZ
O<*l41::ʵSt"td cuu(JǞmV`7ήo\lڍGJUqƭO1_+Hd2d2bł̙3w$(
>}C$8=[,'7t#7T*$	R)e8annP:2wz)EQCEX,~8c88g+a 4يQeib|p!NНS.tRoyΰQߑe+++駟d`aQ	$7t3X,"L"JA$K.arr^%2VWWO?!Nraxx+łW#chhH7UX(H&PdS>Y(
0\.Wӄnkh|>.],'yPt@VǏ?#bddDd`=n(\lzs1k\׮,	
J%!JjŋX\\bV"9JKKKx*~?FFF{J;B{V!cooL.:Ν;I:*>|_~r9;c]MuqC=mc\cH0R "Hh4bqq/^<"~<|,#bxxX	v;~._Q
"1B{amm
.jP b :VޔeD"z]Yz2xY+
vvvN1::˗/up"H/D0>>n=w@mF>,{aL-ᅦ`gx+̒$KfEH#"zT*1ET˪ќc|[2QVk}J$xV+&&&z.Ao]Q$	D"(z3}<3::z䰝*	@!f[3EAP(H0}1<,,ˈFcu(X__\.cddúZw@mD"lP@$|WX^^>V[iuviMG IRsKJUBЩ`>½q3Dvbwnb
zn߾\.	oqa\]xr
A|}5 BcdsAwv2`2pUwh";;;s:&''uӠޠhކƵk.:O)G믿ndT Sa6Ӏh@  mJ.fQ1VOf=/x=%I677fq%O(":1Y㧟~Ԕfk
e8<+{V/A0>>~hȫվ%Ȕ\lj2P:YJcpZWB󐛁(C$0>fKD-?G,Ù3g022ޠ.2vvv "lkC}&(Joq:d}0\.AP,!"Y_q^̚尹	+Wٳ/zBQLOOſC{;R)lmmpڵkZDm'E7tbzz⢺$`BMVk.f-W?L&/w":T^Ƿ~bjjJw[Bho{xT*@XĻヒ/rRO0zwb}}pcccZcrQ\}ۍ YEA.(Ȥ0(bE8 H?*
>Sb1!nޠ.2vww033+Wp0jFj(
UadɈ`0A^HZ"#.j5?L6X[[y5yܼyyZc
z:a|G{q=X,֭[HӘF<0*t8 |EA:!n·R	X,q|>%Rn޼	I0??I^
s'pޣ,R`vv8R`G8#Q:R.fM$PeLz-%[kke|	$
h4[nd2annNz!Enƍx38~O{R*BWDcvww?aT^!(

D1T*#_K$.Y^^_|׋i]֏»$IX[[CRG}1Kc`p!_)f	P(ĝDI$q$"*U	NnuiDa?3ALNNr"C{C/zuy\z333ZDG#V׷n`u81<<߯˙EQdF߲yU"YZZW_}2k»,D:G}(DF!2|>d4[X__׺,"u|W:a۱p
53k(IZWth4^/>3DQK#`9u;[
ylc1BH¸]D"K"6D"su»`4nݺT*uI:88hp K$$ňm?ǵ. O?bONBx7djESrypFyv{҉vxnč#Zy&٬e)dYܼy6
333ll6cnn$͛T*ZDo:VO?E><63&.gS70"GXԸ*":bМ)z![V#O?EVӺ$j]gdY۷177ͼ`PҨy^X-f,,C\ƭ[PV.Z֭[(˘e fho»nDQ۷!˲%suo6fffp]|G`0 $`anntww%
Ν;HӘf/}hh3337|u9wƒ066׋,N+
A$t:155-,--i]'O`jj
NSr`h?^^cccx.|wޅC8,ŀ!CCCWGbc)(
?k݃(ZGD/ "ݻp8ϧu90O/p8׋wrapY@sKl!!quOA@Drp8gߝH*>38Ni]'n0055:#azzH&#Q?q\pmX}qr^T¿/錢(׿Rim~:zfӈb.ஹOd8t7HSok,RJ'farrߴ-y199ŨrpOѺTb;w`xxPsQ*uF0ho
_#HhW5%	|~_raho/x<ܹsgh]#z}+-nװBW&	`D@~3fbb6
o߆$A4`$I۷a0>>u90w{߽^Ν;wF>|Objjjߠ'7RA@lx_=Ӯ8"w}\.]L2wûb">|u9K?)
x]{(V^Wh8\CXnǙ3gd2MqD.H_ř3gt䕡;=B;J%Ho(ʁm*5`5	a_Q'BO@깱7fQ)/y0wEᩪapﲽ=,//cttcrpIi>	;{h4bbbhG4VVVF1>>vm5fbyyhTr>F!2K

犢.J+F#!T޽{22QFԧ"DQt%Fcmm
lV"{ϰX,P,f|>_+#:hhhCNgEt:qFԧ߿өYo;C{KxԵz?6)x1AhK@,'`u"-0v@|ZqwwxH'#EH'_ލF#0?~bc_tYQn-`@0$`-	FbH":\.cggkh
~?VVV -v:cpoOT*:$$ TҿP(y(Eԫס(JWdhgiރ JOn36ZYYnly
9N'~KinBhee!Zvxw:G{j5lɔ,"a*n@ h4\ "@.C4l;C;H7{]f}};{lmmp"`659%褆tرk}h4buuUL&|}
v:n@ ZڈGç IDATMVVV)&@0$tu^2	ale|FP[YYdNѭ؁lyy3_`1E,=t&e-	TꉔD&d4![C|>iF#"wi6EzГPDfu23Lx-=w j-@GNfhFxwݨjCfX d2-&VE va \SO}ll"/tvjNw	ɝ6apoH$r7"`Zz5=.0q\nQD=qh'vjNwɝ6ap?zrpPOKp<,rN\.|>6vNw˅=)}9'rk?gTkkƝ:S}~JO>m߮(Tρ5wIz܉Zkg;C;uC'{ϝcp?w@“RB!e`%q{jW;C;uS;ۇ^߾$NzQZ|XէJjr9m#ҙv3H$Z*R2u߃h4AUD]8͌;C;ir^#~
鴺nhD Т,rݰlx#łL&MaD:NaZONzЮp8Sbp?L&
|_]Ł@0D`0@
vhe2NzҎn2`ZOZ
iuv'R0
'6wtDf1)Dv5	#ٌ@ " ?H1(+ϯ\%0hƝᝓ;~B oOh[@`E	zfj(ZJ4@Jjځ5P/N4ᝓ;~BbZmlCX|VFN!'Y}Ptm^p3S9ix1P#4jXI- i`0"95tD
ҏeh^t޸G]OAy%(
,~?fSskHGDPvFoˎ9sz'젬(Tw~a48Hu}\|Dv/N8h4n3	 4 ZJjE>'\.N8bP(t>~B$5Xz(vy; lz=X!I8<{xC;wшj	UUF%`#00%@,2;
:IZ3S?;Jx0Pc68`0.H36K1DP̸3 xYx0$I"a6.H3!! )&j5AKQj}34H^9s:'$IP
.:3DJ34l<j,5Ժ"͙f~P4/ZF#C;
Vd2qbO@Qk&+V (֌Nѿ[[i=Ny:' ID0LZCCCCp:fpUP
8ډ?w	fκ	$  
8kAb0i"+1^lgh']#$~X~"j]ROb$DDt(}s"'D<Y.H7EA2\~uLr4w;pAmcD2?RkAKI'`X @D^G*Һ"(
(c{
,;*`*4}\ÿS{	1`\GVE\i]nYY4V+`$?ρڳ?[eNf3EE!\VC*ĬNq,Nz͟	Y,,8-,#Ѡ0p/d90ӠjhNUh99X,"$	GJMQf|Jd0`6m^N/hO,:^	 Lj]f,*U	aDĩ1NEPg@OjBe8-c1KKc:lAgXZnNei1Pc.@RA.Ӹ*T*dX4:gSh={x;v3~B.9؞q1 E0P\Vr-HcnjO@$

u>~B^r,` Np&7,#\0>T*|>m#Ҙ}qB,(z+0P㇮10QhPR)ju\?˲JA޳;/N8P'vq
'+	E40bn5|ywE
>=9r:0B@]*I5i
+#b|E11ҏgxr<	x<}r	ԭ(pZg&N8(f;Nᤡ@)|}3H5ˣT*iTQj5$	\3/e"`=pxwҳӄv)}̭!%I(WBA&wyC
yA0Bcg0! HzQ/Sb,i?|n.D;Ɍ{;I;B{^GZep?%S8l+!.H&ZEQ\JdCV$I~PVQN-ļG)AL&r}wۀ^u`f}ܣμskH'x&zRZbA9('s;uCC;WyhSe;,op"~aDmN!jXLߙ3gYIz0L/~J/sRc1@CQ.E֮>g1S't*oo'~>w L&AeDQ*xdI>Xw"s;S'C;ۋ
FGG[@x98OR&"fd籿,wjNvF{PfGzd"!d5~9(xQvw:NvFcccH-YwAduYCt:
vC\.|CN'эQ	m2??Br|Y5d- c.zt/#N^wgxVh/Ճ(:{LNNl6#L~AjuT˕\P@Tn$ΤȲFc rfw:nv@}bk6q}&f333HR-wTy*Qmܭ'I>-p'oEEA*fsЀapoy喻{PDPreD'IR$V׳,dh~~laZfhd2mFgΜ8Գ^`E8POn%Lh4bffuL2ӳxڌFcsRvRd%j'EQ
'J011znkڍmB{Mf~~ɴm677l!h\Cg ɠR=)S¡;u`"@6EV=,:⴨OXYy"=BN@j}=Lbpc:{w Uh6χ@ н/: `0`aatq1+ՎGtZJlvYH$0;;˝l6cvvDz0-C$Id2XXXt*pyFDіׇ[C>I=D"j^{_j2@4h{l6,.."\j0NgPV55Yx% ;tܹsb-&uD\j#ы=IShcccCHA&{CheXΝ~^cV.T#Nw2

⡋R,"gۉҥK(niAPx׵)`@0w3*0R$Қ( O#jQ=j`xmz	,#bvvG"{]t酻,
@._@TreDjH%RO}^>G>ǥKQ\tޤg{TU1 ;,ٳk9>f.R%m'*xE{{{|j]Dnrr>OYw)7?={.u{(-{MF$joQ)ôfdor|Md2z{B;e2 ܻ caaHYV~r9+ՖReY6FFF0;;,.c^Dp9am]`0`ww5[i-
61ߡX,J˗/sC._JXL
Iovwwa4;h]`pÁwyxBP*5]DZ"`QPO}ӧOȾEX\\ӧOuq6û1
qp8Z30ܻ>0><6.REf#0"f᭷~aD護ނjΎ֥`x=vEQA?^r
{F\|*Y5d:H]!2q炀ŴZ6E*{ժMDjC*|jûuAjX˗a42Jv]622rBՅ?䰓V)NCqEØӦ85??ahP]z
ֺ஁\"Rljbg\ƂT.k,T-˺X]z
R}]KHp8x<]UIB&Ѩ:R	|U. Hc.T-Zzlxoj]#.\(677
~!' D""fYۍ~[o6n7u20wC$I(.\u9]#FW^d֘EfsiQבLq^tlooRڵkXtt @׮]CR.3
Ю(
677a2pU.Hr:z*,h3~f֐DKRx+ }T*u90C;!իp:Z356>>7xHyRSq1Izb/T*lmmaff>ϟPT.g{hx)x
k]cpׁzp-"@.#Lj\|>R߷l;N/.2D:c0ax?Z
aƧ:`4@c&<@TDQ?T*ڵkC=7
>/•+Wd{.BBqu$IB:bX=7N#ރ /H3 C,C:ֺޏGPd2r
\.udrr/^.2=j2R;q|($.W_$677Q,.Оd/ٳZC`pיw}X__GXH%wh,JW
χ+WG\r>[
0L/|>uLLLtTbpшk׮AbU Hh]L&TÂ_*v;_ξvcZ'ncuuUή^R	kkk׮]c_f|Gp\XwHq'nܸ㪉zč7kkkWt~ګ*VWWrl6k]SQW	ʕ*٬eQ*r{V~:<e)x<\~J&`xo^հ
ł7nfi]]\._ǐ8	A>PDD
#7֛AeYn>q.^]~?_E:b$/YPbj](^T*wЮ(
666P*pu|>K`p?x&X[[E"cQEQN\'oMbffF번fffpeb1D"w89;?D8ֺ$:pjo?>EVql6!AQ$IHETP__}C҈~ܻw`5]1
`0ўR/zuy\z;=~z	0bJRz=0<.j,EQP(~?Q1#9}ZHD]/333ί{/vIJ>cccZDރ8n޼b14)Ef $j :$D1R
x%X9Z?'
'O>.RQׯsޣ,R`vv8$kI@ 8gJDQD2@]1@U
Y'_hEQܺu&	sssԺ$:!WVhSSSEvjL&cs1n׸j:jx<(B0Rgק||>5n\~{QS>͛71;;˥uIrxОJ.1zwb}}pccc$*V!!6R\("Nd8\,.0>c0DDT*|駈bC8ߋ]]e"affW\>'E{tbzz@F5
bF0$@$]VC"@\\o\~&2^{5Pz~-B.]V,M(#.\.M\r;ё=ywޅ(]L/wT*-8|A뒨PZ_|
Cց)m3
5IDAT$gp;GeR)",BWB󐛀(C$0>tH<>sb19s###sxshe;;;E333_~>ާEG_nczzRxbf0
ۨR@E$quyŦgMIl6K.O.V! 2~ܿSSS
zzr(xqy]apsDoF.KU80$54!.feX<>EQf!bds/6aL&-L&|<厈fggwA^$^%5)5+D"mn\v
`P벨$Iꫯ˅#͢ˊ:$OslB0$ 
q#$شRt
l0OR4׿O?"bgg>ڋ"籰?Ϻx2A>@"Ki#**3q@׋ q3EAP(H0@,C4ۻ8㿹̅30Y%FR|AEd5PY0Ä֍
$~
EQs>Ч~e~.Mї_~)˲z>OY3ާ1]UTRXFq}_?kb15W>s-s
o$c&fŮy2MS֡#Kd;U8ǏjwwWmYApeѷ~Ǐ+Nkiii*\GO[ni6
@
WpK
}WzٹϜ
]-iXb*˪jo՛;-˚6=j|,Lsg_2Xvq>3/t:}j6S}*}ڢbܹO>䭚k;-s_5r_)9lfVڼr% u:Z:9k&):9C:/n뚟֧Wmkm1|7r]WJEz/<z+%w{R2WRUV;ךFmnݒJKE)qO `*nY__W,SZU^2}Zݲ,HdR}b pض'ODz,KRIɤ7ݚ\+9jy
H&A~VPqia.xߗizt}n0á׵!EBs>
>c޽{H	!⺮>}N~_BAzKOo@JR&WJNyU#k1ٻ~"/Bht:{(x<Ɔev3;;{Ǜ*Ç{[}F|׳gϴN\.z~U@zҚJK*KjrS
?'W9G@,M}1wæq6|w}@乮Q
o"ޯ+ڃ PJ>|;w%/E
@jJT*T.d=@]ì4;k0k"qfSҽnU^h4i:>>xNJaSSRgaӗz:/t~8Gɶ7o2
 4;;F`z4'l6oɲ,b1eYe2T*uf&CؒyU*3W/|ߗij5KO]r \וeYDz,K0T*FCr iBOٔ8aȿ8G$W{ϊF{r`aq#D+stɉNqt:-0;L*H(5I+$T*5??L&#˲jdz{@7ߓr]7x-˒m۲,KIRKER	r^Dd.=GGG'x<tvA}/^:G/i2GyUp1PU"q|ɉnt:t:%	yAR3ƶdW=aj>V=_dŢŢ
B+)yz^87O˲7D"X2uKNP&nL&sY"1\Ux<8g~ƶ_v{	ճn%c&Tϟt:t:9^
g۶zlۖm/'~:NAQ~6ypQP)D{                                               /pUqIENDB`Shapely-1.6.4/docs/index.rst000066400000000000000000000004001323200062600157040ustar00rootroot00000000000000=======
Shapely
=======

Documentation Contents
======================

.. toctree::
   :maxdepth: 2

   The Project 
   User Manual 
   API Documentation 

Indices and tables
==================

* :ref:`genindex`
* :ref:`search`
Shapely-1.6.4/docs/manual.rst000066400000000000000000002345071323200062600160730ustar00rootroot00000000000000.. _manual:

=======================
The Shapely User Manual
=======================

:Author: Sean Gillies, 
:Version: 1.6
:Date: |today|
:Copyright:
  This work is licensed under a `Creative Commons Attribution 3.0
  United States License`__.

.. __: http://creativecommons.org/licenses/by/3.0/us/

:Abstract:
  This document explains how to use the Shapely Python package for
  computational geometry.

.. _intro:

Introduction
============

Deterministic spatial analysis is an important component of computational
approaches to problems in agriculture, ecology, epidemiology, sociology, and
many other fields. What is the surveyed perimeter/area ratio of these patches
of animal habitat? Which properties in this town intersect with the 50-year
flood contour from this new flooding model? What are the extents of findspots
for ancient ceramic wares with maker's marks "A" and "B", and where do the
extents overlap? What's the path from home to office that best skirts
identified zones of location based spam? These are just a few of the possible
questions addressable using non-statistical spatial analysis, and more
specifically, computational geometry.

Shapely is a Python package for set-theoretic analysis and manipulation of
planar features using (via Python's :mod:`ctypes` module) functions from the
well known and widely deployed GEOS_ library. GEOS, a port of the `Java
Topology Suite`_ (JTS), is the geometry engine of the PostGIS_ spatial
extension for the PostgreSQL RDBMS. The designs of JTS and GEOS are largely
guided by the `Open Geospatial Consortium`_'s Simple Features Access
Specification [1]_ and Shapely adheres mainly to the same set of standard
classes and operations. Shapely is thereby deeply rooted in the conventions of
the geographic information systems (GIS) world, but aspires to be equally
useful to programmers working on non-conventional problems.

The first premise of Shapely is that Python programmers should be able to
perform PostGIS type geometry operations outside of an RDBMS. Not all
geographic data originate or reside in a RDBMS or are best processed using SQL.
We can load data into a spatial RDBMS to do work, but if there's no mandate to
manage (the "M" in "RDBMS") the data over time in the database we're using the
wrong tool for the job. The second premise is that the persistence,
serialization, and map projection of features are significant, but orthogonal
problems. You may not need a hundred GIS format readers and writers or the
multitude of State Plane projections, and Shapely doesn't burden you with them.
The third premise is that Python idioms trump GIS (or Java, in this case, since
the GEOS library is derived from JTS, a Java project) idioms.

If you enjoy and profit from idiomatic Python, appreciate packages that do one
thing well, and agree that a spatially enabled RDBMS is often enough the wrong
tool for your computational geometry job, Shapely might be for you.

.. _intro-spatial-data-model:

Spatial Data Model
------------------

The fundamental types of geometric objects implemented by Shapely are points,
curves, and surfaces. Each is associated with three sets of (possibly infinite)
points in the plane. The `interior`, `boundary`, and `exterior` sets of a
feature are mutually exclusive and their union coincides with the entire plane
[2]_.

* A `Point` has an `interior` set of exactly one point, a `boundary` set of
  exactly no points, and an `exterior` set of all other points. A `Point` has
  a topological dimension of 0.

* A `Curve` has an `interior` set consisting of the infinitely many points
  along its length (imagine a `Point` dragged in space), a `boundary` set
  consisting of its two end points, and an `exterior` set of all other points.
  A `Curve` has a topological dimension of 1.

* A `Surface` has an `interior` set consisting of the infinitely many points
  within (imagine a `Curve` dragged in space to cover an area), a `boundary`
  set consisting of one or more `Curves`, and an `exterior` set of all other
  points including those within holes that might exist in the surface. A
  `Surface` has a topological dimension of 2.

That may seem a bit esoteric, but will help clarify the meanings of Shapely's
spatial predicates, and it's as deep into theory as this manual will go.
Consequences of point-set theory, including some that manifest themselves as
"gotchas", for different classes will be discussed later in this manual.

The point type is implemented by a `Point` class; curve by the `LineString` and
`LinearRing` classes; and surface by a `Polygon` class. Shapely implements no
smooth (`i.e.` having continuous tangents) curves. All curves must be
approximated by linear splines. All rounded patches must be approximated by
regions bounded by linear splines.

Collections of points are implemented by a `MultiPoint` class, collections of
curves by a `MultiLineString` class, and collections of surfaces by a
`MultiPolygon` class. These collections aren't computationally significant, but
are useful for modeling certain kinds of features. A Y-shaped line feature, for
example, is well modeled as a whole by a `MultiLineString`.

The standard data model has additional constraints specific to certain types
of geometric objects that will be discussed in following sections of this
manual.

See also http://www.vividsolutions.com/jts/discussion.htm#spatialDataModel
for more illustrations of this data model.

.. _intro-relationships:

Relationships
-------------

The spatial data model is accompanied by a group of natural language
relationships between geometric objects – `contains`, `intersects`, `overlaps`,
`touches`, etc. – and a theoretical framework for understanding them using the
3x3 matrix of the mutual intersections of their component point sets [2]_: the
DE-9IM. A comprehensive review of the relationships in terms of the DE-9IM is
found in [4]_ and will not be reiterated in this manual.

.. _intro-operations:

Operations
----------

Following the JTS technical specs [5]_, this manual will make a distinction
between constructive (`buffer`, `convex hull`) and set-theoretic operations
(`intersection`, `union`, etc.). The individual operations will be fully
described in a following section of the manual.

.. _intro-coordinate-systems:

Coordinate Systems
------------------

Even though the Earth is not flat – and for that matter not exactly spherical –
there are many analytic problems that can be approached by transforming Earth
features to a Cartesian plane, applying tried and true algorithms, and then
transforming the results back to geographic coordinates.  This practice is as
old as the tradition of accurate paper maps.

Shapely does not support coordinate system transformations. All operations on
two or more features presume that the features exist in the same Cartesian
plane.

.. _objects:

Geometric Objects
=================

Geometric objects are created in the typical Python fashion, using the classes
themselves as instance factories. A few of their intrinsic properties will be
discussed in this sections, others in the following sections on operations and
serializations.

Instances of ``Point``, ``LineString``, and ``LinearRing`` have as their most
important attribute a finite sequence of coordinates that determines their
interior, boundary, and exterior point sets. A line string can be determined by
as few as 2 points, but contains an infinite number of points. Coordinate
sequences are immutable. A third `z` coordinate value may be used when
constructing instances, but has no effect on geometric analysis.  All
operations are performed in the `x-y` plane.

In all constructors, numeric values are converted to type ``float``. In other
words, ``Point(0, 0)`` and ``Point(0.0, 0.0)`` produce geometrically equivalent
instances. Shapely does not check the topological simplicity or validity of
instances when they are constructed as the cost is unwarranted in most cases.
Validating factories are easily implemented using the :attr:``is_valid``
predicate by users that require them.

.. note::

   Shapely is a planar geometry library and `z`, the height
   above or below the plane, is ignored in geometric analysis. There is
   a potential pitfall for users here: coordinate tuples that differ only in
   `z` are not distinguished from each other and their application can result
   in suprisingly invalid geometry objects. For example, ``LineString([(0, 0,
   0), (0, 0, 1)])`` does not return a vertical line of unit length, but an invalid line
   in the plane with zero length. Similarly, ``Polygon([(0, 0, 0), (0, 0, 1),
   (1, 1, 1)])`` is not bounded by a closed ring and is invalid.


General Attributes and Methods
------------------------------

.. attribute:: object.area

  Returns the area (``float``) of the object.

.. attribute:: object.bounds

  Returns a ``(minx, miny, maxx, maxy)`` tuple (``float`` values) that bounds
  the object.

.. attribute:: object.length

  Returns the length (``float``) of the object.

.. attribute:: object.geom_type

  Returns a string specifying the `Geometry Type` of the object in accordance
  with [1]_.

.. code-block:: pycon

  >>> print Point(0, 0).geom_type
  Point

.. method:: object.distance(other)

  Returns the minimum distance (``float``) to the `other` geometric object.

.. code-block:: pycon

  >>> Point(0,0).distance(Point(1,1))
  1.4142135623730951

.. method:: object.hausdorff_distance(other)

  Returns the Hausdorff distance (``float``) to the `other` geometric object.
  The Hausdorff distance is the furthest distance from any point on the first
  geometry to any point on the second geometry.

  `New in Shapely 1.6.0`

.. code-block:: pycon

  >>> point = Point(1, 1)
  >>> line = LineString([(2, 0), (2, 4), (3, 4)])
  >>> point.hausdorff_distance(line)
  3.605551275463989
  >>> point.distance(Point(3, 4))
  3.605551275463989

.. method:: object.representative_point()

  Returns a cheaply computed point that is guaranteed to be within the
  geometric object.

.. note::
  This is not in general the same as the centroid.

.. code-block:: pycon

  >>> donut = Point(0, 0).buffer(2.0).difference(Point(0, 0).buffer(1.0))
  >>> donut.centroid.wkt
  'POINT (-0.0000000000000001 -0.0000000000000000)'
  >>> donut.representative_point().wkt
  'POINT (-1.5000000000000000 0.0000000000000000)'

.. _points:

Points
------

.. class:: Point(coordinates)

  The `Point` constructor takes positional coordinate values or point tuple
  parameters.

.. code-block:: pycon

  >>> from shapely.geometry import Point
  >>> point = Point(0.0, 0.0)
  >>> q = Point((0.0, 0.0))

A `Point` has zero area and zero length.

.. code-block:: pycon

  >>> point.area
  0.0
  >>> point.length
  0.0

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> point.bounds
  (0.0, 0.0, 0.0, 0.0)

Coordinate values are accessed via `coords`, `x`, `y`, and `z` properties.

.. code-block:: pycon

  >>> list(point.coords)
  [(0.0, 0.0)]
  >>> point.x
  0.0
  >>> point.y
  0.0

Coordinates may also be sliced. `New in version 1.2.14`.

.. code-block:: pycon

  >>> point.coords[:]
  [(0.0, 0.0)]

The `Point` constructor also accepts another `Point` instance, thereby making
a copy.

.. code-block:: pycon

  >>> Point(point)
  

.. _linestrings:

LineStrings
-----------

.. class:: LineString(coordinates)

  The `LineString` constructor takes an ordered sequence of 2 or more
  ``(x, y[, z])`` point tuples.

The constructed `LineString` object represents one or more connected linear
splines between the points. Repeated points in the ordered sequence are
allowed, but may incur performance penalties and should be avoided. A
`LineString` may cross itself (*i.e.* be `complex` and not `simple`).

.. plot:: code/linestring.py

Figure 1. A simple `LineString` on the left, a complex `LineString` on the
right. The (`MultiPoint`) boundary of each is shown in black, the other points
that describe the lines are shown in grey.

A `LineString` has zero area and non-zero length.

.. code-block:: pycon

  >>> from shapely.geometry import LineString
  >>> line = LineString([(0, 0), (1, 1)])
  >>> line.area
  0.0
  >>> line.length
  1.4142135623730951

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> line.bounds
  (0.0, 0.0, 1.0, 1.0)

The defining coordinate values are accessed via the `coords` property.

.. code-block:: pycon

  >>> len(line.coords)
  2
  >>> list(line.coords)
  [(0.0, 0.0), (1.0, 1.0)]

Coordinates may also be sliced. `New in version 1.2.14`.

.. code-block:: pycon

  >>> point.coords[:]
  [(0.0, 0.0), (1.0, 1.0)]
  >>> point.coords[1:]
  [(1.0, 1.0)]

The constructor also accepts another `LineString` instance, thereby making a
copy.

.. code-block:: pycon

  >>> LineString(line)
  

A `LineString` may also be constructed using a a sequence of mixed `Point`
instances or coordinate tuples. The individual coordinates are copied into
the new object.

.. code-block:: pycon

  >>> LineString([Point(0.0, 1.0), (2.0, 3.0), Point(4.0, 5.0)])
  

.. _linearrings:

LinearRings
-----------

.. class:: LinearRing(coordinates)

  The `LinearRing` constructor takes an ordered sequence of ``(x, y[, z])``
  point tuples.

The sequence may be explicitly closed by passing identical values in the first
and last indices. Otherwise, the sequence will be implicitly closed by copying
the first tuple to the last index. As with a `LineString`, repeated points in
the ordered sequence are allowed, but may incur performance penalties and
should be avoided. A `LinearRing` may not cross itself, and may not touch
itself at a single point.

.. plot:: code/linearring.py

Figure 2. A valid `LinearRing` on the left, an invalid self-touching
`LinearRing` on the right. The points that describe the rings are shown in
grey. A ring's boundary is `empty`.

.. note::
   Shapely will not prevent the creation of such rings, but exceptions will be
   raised when they are operated on.

A `LinearRing` has zero area and non-zero length.

.. code-block:: pycon

  >>> from shapely.geometry.polygon import LinearRing
  >>> ring = LinearRing([(0, 0), (1, 1), (1, 0)])
  >>> ring.area
  0.0
  >>> ring.length
  3.4142135623730949

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> ring.bounds
  (0.0, 0.0, 1.0, 1.0)

Defining coordinate values are accessed via the `coords` property.

.. code-block:: pycon

  >>> len(ring.coords)
  4
  >>> list(ring.coords)
  [(0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]

The `LinearRing` constructor also accepts another `LineString` or `LinearRing`
instance, thereby making a copy.

.. code-block:: pycon

  >>> LinearRring(ring)
  

As with `LineString`, a sequence of `Point` instances is not a valid
constructor parameter.

.. _polygons:

Polygons
--------

.. class:: Polygon(shell [,holes=None])

  The `Polygon` constructor takes two positional parameters. The first is an
  ordered sequence of ``(x, y[, z])`` point tuples and is treated exactly as in
  the `LinearRing` case. The second is an optional unordered sequence of
  ring-like sequences specifying the interior boundaries or "holes" of the
  feature.

Rings of a `valid` `Polygon` may not cross each other, but may touch at a
single point only.  Again, Shapely will not prevent the creation of invalid
features, but exceptions will be raised when they are operated on.

.. plot:: code/polygon.py

Figure 3. On the left, a valid `Polygon` with one interior ring that touches
the exterior ring at one point, and on the right a `Polygon` that is `invalid`
because its interior ring touches the exterior ring at more than one point. The
points that describe the rings are shown in grey.

.. plot:: code/polygon2.py

Figure 4. On the left, a `Polygon` that is `invalid` because its exterior and
interior rings touch along a line, and on the right, a `Polygon` that is
`invalid` because its interior rings touch along a line.

A `Polygon` has non-zero area and non-zero length.

.. code-block:: pycon

  >>> from shapely.geometry import Polygon
  >>> polygon = Polygon([(0, 0), (1, 1), (1, 0)])
  >>> polygon.area
  0.5
  >>> polygon.length
  3.4142135623730949

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> polygon.bounds
  (0.0, 0.0, 1.0, 1.0)

Component rings are accessed via `exterior` and `interiors` properties.

.. code-block:: pycon

  >>> list(polygon.exterior.coords)
  [(0.0, 0.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]
  >>> list(polygon.interiors)
  []

The `Polygon` constructor also accepts instances of `LineString` and
`LinearRing`.

.. code-block:: pycon

  >>> coords = [(0, 0), (1, 1), (1, 0)]
  >>> r = LinearRing(coords)
  >>> s = Polygon(r)
  >>> s.area
  0.5
  >>> t = Polygon(s.buffer(1.0).exterior, [r])
  >>> t.area
  6.5507620529190334

Rectangular polygons occur commonly, and can be conveniently constructed using
the :func:`shapely.geometry.box()` function.

.. function:: shapely.geometry.box(minx, miny, maxx, maxy, ccw=True)

  Makes a rectangular polygon from the provided bounding box values, with
  counter-clockwise order by default.

  `New in version 1.2.9`.

For example:

.. code-block:: pycon

  >>> from shapely.geometry import box
  >>> b = box(0.0, 0.0, 1.0, 1.0)
  >>> b
  
  >>> list(b.exterior.coords)
  [(1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0), (1.0, 0.0)]

This is the first appearance of an explicit polygon handedness in Shapely.

To obtain a polygon with a known orientation, use
:func:`shapely.geometry.polygon.orient()`:

.. function:: shapely.geometry.polygon.orient(polygon, sign=1.0)

  Returns a properly oriented copy of the given polygon. The signed area of the
  result will have the given sign. A sign of 1.0 means that the coordinates of
  the product's exterior ring will be oriented counter-clockwise.

  `New in version 1.2.10`.

.. _collections:

Collections
-----------

Heterogeneous collections of geometric objects may result from some Shapely
operations. For example, two `LineStrings` may intersect along a line and at a
point. To represent these kind of results, Shapely provides frozenset_-like,
immutable collections of geometric objects.  The collections may be homogeneous
(`MultiPoint` etc.) or heterogeneous.

.. code-block:: python

  >>> a = LineString([(0, 0), (1, 1), (1,2), (2,2)])
  >>> b = LineString([(0, 0), (1, 1), (2,1), (2,2)])
  >>> x = a.intersection(b)
  >>> x
  
  >>> from pprint import pprint
  >>> pprint(list(x))
  [,
   ]

.. plot:: code/geometrycollection.py
   :class: figure

Figure 5. a) a green and a yellow line that intersect along a line and at a
single point; b) the intersection (in blue) is a collection containing one
`LineString` and one `Point`.

Members of a `GeometryCollection` are accessed via the `geoms` property or via
the iterator protocol using ``in``  or ``list()``.

.. code-block:: pycon

  >>> pprint(list(x.geoms))
  [,
   ]
  >>> pprint(list(x))
  [,
   ]

Homogeneous collections can also be sliced, resulting in a new object of the
same type.

.. code-block:: pycon

  >>> from shapely.geometry import MultiPoint
  >>> m = MultiPoint([(0, 0), (1, 1), (1,2), (2,2)])
  >>> m[:1].wkt
  'MULTIPOINT (0.0000000000000000 0.0000000000000000)'
  >>> m[3:].wkt
  'MULTIPOINT (2.0000000000000000 2.0000000000000000)'
  >>> m[4:].wkt
  'GEOMETRYCOLLECTION EMPTY'

`New in version 1.2.14`.

.. note::

  When possible, it is better to use one of the homogeneous collection types
  described below.

.. _multipoints:

Collections of Points
---------------------

.. class:: MultiPoint(points)

  The `MultiPoint` constructor takes a sequence of ``(x, y[, z ])`` point
  tuples.

A `MultiPoint` has zero area and zero length.

.. code-block:: pycon

  >>> from shapely.geometry import MultiPoint
  >>> points = MultiPoint([(0.0, 0.0), (1.0, 1.0)])
  >>> points.area
  0.0
  >>> points.length
  0.0

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> points.bounds
  (0.0, 0.0, 1.0, 1.0)

Members of a multi-point collection are accessed via the ``geoms`` property or
via the iterator protocol using ``in`` or :func:`list`.

.. code-block:: pycon

  >>> import pprint
  >>> pprint.pprint(list(points.geoms))
  [,
   ]
  >>> pprint.pprint(list(points))
  [,
   ]

The constructor also accepts another `MultiPoint` instance or an unordered
sequence of `Point` instances, thereby making copies.

.. code-block:: pycon

  >>> MultiPoint([Point(0, 0), Point(1, 1)])
  

.. _multilinestrings:

Collections of Lines
--------------------

.. class:: MultiLineString(lines)

  The `MultiLineString` constructor takes a sequence of line-like sequences or
  objects.

.. plot:: code/multilinestring.py

Figure 6. On the left, a `simple`, disconnected `MultiLineString`, and on the
right, a non-simple `MultiLineString`. The points defining the objects are
shown in gray, the boundaries of the objects in black.

A `MultiLineString` has zero area and non-zero length.

.. code-block:: pycon

  >>> from shapely.geometry import MultiLineString
  >>> coords = [((0, 0), (1, 1)), ((-1, 0), (1, 0))]
  >>> lines = MultiLineString(coords)
  >>> lines.area
  0.0
  >>> lines.length
  3.4142135623730949

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> lines.bounds
  (-1.0, 0.0, 1.0, 1.0)

Its members are instances of `LineString` and are accessed via the ``geoms``
property or via the iterator protocol using ``in`` or ``list()``.

.. code-block:: pycon

  >>> len(lines.geoms)
  2
  >>> pprint.pprint(list(lines.geoms))
  [,
   ]
  >>> pprint.pprint(list(lines))
  [,
   ]

The constructor also accepts another instance of `MultiLineString` or an
unordered sequence of `LineString` instances, thereby making copies.

.. code-block:: pycon

  >>> MultiLineString(lines)
  
  >>> MultiLineString(lines.geoms)
  

.. _multipolygons:

Collections of Polygons
-----------------------

.. class:: MultiPolygon(polygons)

  The `MultiPolygon` constructor takes a sequence of exterior ring and
  hole list tuples: [((a1, ..., aM), [(b1, ..., bN), ...]), ...].

More clearly, the constructor also accepts an unordered sequence of `Polygon`
instances, thereby making copies.

.. code-block:: pycon

  >>> polygons = MultiPolygon([polygon, s, t])
  >>> len(polygons.geoms)
  3

.. plot:: code/multipolygon.py

Figure 7. On the left, a `valid` `MultiPolygon` with 2 members, and on the
right, a `MultiPolygon` that is invalid because its members touch at an
infinite number of points (along a line).

Its `x-y` bounding box is a ``(minx, miny, maxx, maxy)`` tuple.

.. code-block:: pycon

  >>> polygons.bounds
  (-1.0, -1.0, 2.0, 2.0)

Its members are instances of `Polygon` and are accessed via the ``geoms``
property or via the iterator protocol using ``in`` or ``list()``.

.. code-block:: pycon

  >>> len(polygons.geoms)
  3
  >>> len(polygons)
  3

.. _empties:

Empty features
--------------

An "empty" feature is one with a point set that coincides with the empty set;
not ``None``, but like ``set([])``. Empty features can be created by calling
the various constructors with no arguments. Almost no operations are supported
by empty features.

.. code-block:: pycon

  >>> line = LineString()
  >>> line.is_empty
  True
  >>> line.length
  0.0
  >>> line.bounds
  ()
  >>> line.coords
  []

The coordinates of a empty feature can be set, after which the geometry is no
longer empty.

.. code-block:: pycon

  >>> line.coords = [(0, 0), (1, 1)]
  >>> line.is_empty
  False
  >>> line.length
  1.4142135623730951
  >>> line.bounds
  (0.0, 0.0, 1.0, 1.0)

Linear Referencing Methods
--------------------------

It can be useful to specify position along linear features such as `LineStrings`
and `MultiLineStrings` with a 1-dimensional referencing system. Shapely
supports linear referencing based on length or distance, evaluating the
distance along a geometric object to the projection of a given point, or the
point at a given distance along the object.

.. note::

  Linear referencing methods require GEOS 3.2.0 or later.

.. method:: object.interpolate(distance[, normalized=False])

  Return a point at the specified distance along a linear geometric object.

If the `normalized` arg is ``True``, the distance will be interpreted as a
fraction of the geometric object's length.

.. code-block:: pycon

  >>> ip = LineString([(0, 0), (0, 1), (1, 1)]).interpolate(1.5)
  >>> ip
  
  >>> ip.wkt
  'POINT (0.5000000000000000 1.0000000000000000)'
  >>> LineString([(0, 0), (0, 1), (1, 1)]).interpolate(0.75, normalized=True).wkt
  'POINT (0.5000000000000000 1.0000000000000000)'

.. method:: object.project(other[, normalized=False])

  Returns the distance along this geometric object to a point nearest the
  `other` object.

If the `normalized` arg is ``True``, return the distance normalized to the
length of the object. The :meth:`project` method is the inverse of
:meth:`interpolate`.

.. code-block:: pycon

  >>> LineString([(0, 0), (0, 1), (1, 1)]).project(ip)
  1.5
  >>> LineString([(0, 0), (0, 1), (1, 1)]).project(ip, normalized=True)
  0.75

For example, the linear referencing methods might be used to cut lines at a
specified distance.

.. code-block:: python

  def cut(line, distance):
      # Cuts a line in two at a distance from its starting point
      if distance <= 0.0 or distance >= line.length:
          return [LineString(line)]
      coords = list(line.coords)
      for i, p in enumerate(coords):
          pd = line.project(Point(p))
          if pd == distance:
              return [
                  LineString(coords[:i+1]),
                  LineString(coords[i:])]
          if pd > distance:
              cp = line.interpolate(distance)
              return [
                  LineString(coords[:i] + [(cp.x, cp.y)]),
                  LineString([(cp.x, cp.y)] + coords[i:])]

.. code-block:: pycon

  >>> line = LineString([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)])
  >>> pprint([list(x.coords) for x in cut(line, 1.0)])
  [[(0.0, 0.0), (1.0, 0.0)],
   [(1.0, 0.0), (2.0, 0.0), (3.0, 0.0), (4.0, 0.0), (5.0, 0.0)]]
  >>> pprint([list(x.coords) for x in cut(line, 2.5)])
  [[(0.0, 0.0), (1.0, 0.0), (2.0, 0.0), (2.5, 0.0)],
   [(2.5, 0.0), (3.0, 0.0), (4.0, 0.0), (5.0, 0.0)]]

.. _predicates:

Predicates and Relationships
============================

Objects of the types explained in :ref:`objects` provide standard [1]_
predicates as attributes (for unary predicates) and methods (for binary
predicates). Whether unary or binary, all return ``True`` or ``False``.

.. _unary-predicates:

Unary Predicates
----------------

Standard unary predicates are implemented as read-only property attributes. An
example will be shown for each.

.. attribute:: object.has_z

  Returns ``True`` if the feature has not only `x` and `y`, but also `z`
  coordinates for 3D (or so-called, 2.5D) geometries.

.. code-block:: pycon

  >>> Point(0, 0).has_z
  False
  >>> Point(0, 0, 0).has_z
  True

.. attribute:: object.is_ccw

  Returns ``True`` if coordinates are in counter-clockwise order (bounding a
  region with positive signed area). This method applies to `LinearRing`
  objects only.

  `New in version 1.2.10`.

.. code-block:: pycon

  >>> LinearRing([(1,0), (1,1), (0,0)]).is_ccw
  True

A ring with an undesired orientation can be reversed like this:

.. code-block:: pycon

  >>> ring = LinearRing([(0,0), (1,1), (1,0)])
  >>> ring.is_ccw
  False
  >>> ring.coords = list(ring.coords)[::-1]
  >>> ring.is_ccw
  True

.. attribute:: object.is_empty

  Returns ``True`` if the feature's `interior` and `boundary` (in point set
  terms) coincide with the empty set.

.. code-block:: pycon

  >>> Point().is_empty
  True
  >>> Point(0, 0).is_empty
  False

.. note::

   With the help of the :mod:`operator` module's :func:`attrgetter` function,
   unary predicates such as ``is_empty`` can be easily used as predicates for
   the built in :func:`filter` or :func:`itertools.ifilter`.

.. code-block:: pycon

  >>> from operator import attrgetter
  >>> empties = filter(attrgetter('is_empty'), [Point(), Point(0, 0)])
  >>> len(empties)
  1

.. attribute:: object.is_ring

  Returns ``True`` if the feature is closed. A closed feature's `boundary`
  coincides with the empty set.

.. code-block:: pycon

  >>> LineString([(0, 0), (1, 1), (1, -1)]).is_ring
  False
  >>> LinearRing([(0, 0), (1, 1), (1, -1)]).is_ring
  True

This property is applicable to `LineString` and `LinearRing` instances, but
meaningless for others.

.. attribute:: object.is_simple

  Returns ``True`` if the feature does not cross itself.

.. note::

   The simplicity test is meaningful only for `LineStrings` and `LinearRings`.

.. code-block:: pycon

  >>> LineString([(0, 0), (1, 1), (1, -1), (0, 1)]).is_simple
  False

Operations on non-simple `LineStrings` are fully supported by Shapely.

.. attribute:: object.is_valid

  Returns ``True`` if a feature is "valid" in the sense of [1]_.

A valid `LinearRing` may not cross itself or touch itself at a single point. A
valid `Polygon` may not possess any overlapping exterior or interior rings. A
valid `MultiPolygon` may not collect any overlapping polygons. Operations on
invalid features may fail.

.. code-block:: pycon

  >>> MultiPolygon([Point(0, 0).buffer(2.0), Point(1, 1).buffer(2.0)]).is_valid
  False

The two points above are close enough that the polygons resulting from the
buffer operations (explained in a following section) overlap.

.. note::

  The ``is_valid`` predicate can be used to write a validating decorator that
  could ensure that only valid objects are returned from a constructor
  function.

.. code-block:: python

  from functools import wraps
  def validate(func):
      @wraps(func)
      def wrapper(*args, **kwargs):
          ob = func(*args, **kwargs)
          if not ob.is_valid:
              raise TopologicalError(
                  "Given arguments do not determine a valid geometric object")
          return ob
      return wrapper

.. code-block:: pycon

  >>> @validate
  ... def ring(coordinates):
  ...     return LinearRing(coordinates)
  ...
  >>> coords = [(0, 0), (1, 1), (1, -1), (0, 1)]
  >>> ring(coords)
  Traceback (most recent call last):
    File "", line 1, in 
    File "", line 7, in wrapper
  shapely.geos.TopologicalError: Given arguments do not determine a valid geometric object

.. _binary-predicates:

Binary Predicates
-----------------

Standard binary predicates are implemented as methods. These predicates
evaluate topological, set-theoretic relationships. In a few cases the results
may not be what one might expect starting from different assumptions. All take
another geometric object as argument and return ``True`` or ``False``.

.. method:: object.__eq__(other)

  Returns ``True`` if the two objects are of the same geometric type, and
  the coordinates of the two objects match precisely.

.. method:: object.equals(other)

  Returns ``True`` if the set-theoretic `boundary`, `interior`, and `exterior`
  of the object coincide with those of the other.

The coordinates passed to the object constructors are of these sets, and
determine them, but are not the entirety of the sets. This is a potential
"gotcha" for new users.  Equivalent lines, for example, can be constructed
differently.

.. code-block:: pycon

  >>> a = LineString([(0, 0), (1, 1)])
  >>> b = LineString([(0, 0), (0.5, 0.5), (1, 1)])
  >>> c = LineString([(0, 0), (0, 0), (1, 1)])
  >>> a.equals(b)
  True
  >>> a == b
  False
  >>> b.equals(c)
  True
  >>> b == c
  False

.. method:: object.almost_equals(other[, decimal=6])

  Returns ``True`` if the object is approximately equal to the `other` at all
  points to specified `decimal` place precision.

.. method:: object.contains(other)

  Returns ``True`` if no points of `other` lie in the exterior of the `object`
  and at least one point of the interior of `other` lies in the interior of
  `object`.

This predicate applies to all types, and is inverse to :meth:`within`. The
expression ``a.contains(b) == b.within(a)`` always evaluates to ``True``.

.. code-block:: pycon

  >>> coords = [(0, 0), (1, 1)]
  >>> LineString(coords).contains(Point(0.5, 0.5))
  True
  >>> Point(0.5, 0.5).within(LineString(coords))
  True

A line's endpoints are part of its `boundary` and are therefore not contained.

.. code-block:: pycon

  >>> LineString(coords).contains(Point(1.0, 1.0))
  False

.. note::

  Binary predicates can be used directly as predicates for ``filter()`` or
  ``itertools.ifilter()``.

.. code-block:: pycon

  >>> line = LineString(coords)
  >>> contained = filter(line.contains, [Point(), Point(0.5, 0.5)])
  >>> len(contained)
  1
  >>> [p.wkt for p in contained]
  ['POINT (0.5000000000000000 0.5000000000000000)']

.. method:: object.crosses(other)

  Returns ``True`` if the `interior` of the object intersects the `interior` of
  the other but does not contain it, and the dimension of the intersection is
  less than the dimension of the one or the other.

.. code-block:: pycon

  >>> LineString(coords).crosses(LineString([(0, 1), (1, 0)]))
  True

A line does not cross a point that it contains.

.. code-block:: pycon

  >>> LineString(coords).crosses(Point(0.5, 0.5))
  False

.. method:: object.disjoint(other)

  Returns ``True`` if the `boundary` and `interior` of the object do not
  intersect at all with those of the other.

.. code-block:: pycon

  >>> Point(0, 0).disjoint(Point(1, 1))
  True

This predicate applies to all types and is the inverse of :meth:`intersects`.

.. method:: object.intersects(other)

  Returns ``True`` if the `boundary` or `interior` of the object intersect in
  any way with those of the other.

In other words, geometric objects intersect if they have any boundary or 
interior point in common.

.. method:: object.overlaps(other)

  Returns ``True`` if the objects intersect (see above) but neither contains
  the other.

.. method:: object.touches(other)

  Returns ``True`` if the objects have at least one point in common and their
  interiors do not intersect with any part of the other.

Overlapping features do not therefore `touch`, another potential "gotcha". For
example, the following lines touch at ``(1, 1)``, but do not overlap.

.. code-block:: pycon

  >>> a = LineString([(0, 0), (1, 1)])
  >>> b = LineString([(1, 1), (2, 2)])
  >>> a.touches(b)
  True

.. method:: object.within(other)

  Returns ``True`` if the object's `boundary` and `interior` intersect only
  with the `interior` of the other (not its `boundary` or `exterior`).

This applies to all types and is the inverse of :meth:`contains`.

Used in a ``sorted()`` `key`, :meth:`within` makes it easy to spatially sort
objects. Let's say we have 4 stereotypic features: a point that is contained by
a polygon which is itself contained by another polygon, and a free spirited
point contained by none

.. code-block:: pycon

  >>> a = Point(2, 2)
  >>> b = Polygon([[1, 1], [1, 3], [3, 3], [3, 1]])
  >>> c = Polygon([[0, 0], [0, 4], [4, 4], [4, 0]])
  >>> d = Point(-1, -1)

and that copies of these are collected into a list

.. code-block:: pycon

  >>> features = [c, a, d, b, c]

that we'd prefer to have ordered as ``[d, c, c, b, a]`` in reverse containment
order. As explained in the Python `Sorting HowTo`_, we can define a key
function that operates on each list element and returns a value for comparison.
Our key function will be a wrapper class that implements ``__lt__()`` using
Shapely's binary :meth:`within` predicate.

.. code-block:: python

  class Within(object):
      def __init__(self, o):
          self.o = o
      def __lt__(self, other):
          return self.o.within(other.o)

As the howto says, the `less than` comparison is guaranteed to be used in
sorting. That's what we'll rely on to spatially sort, and the reason why we use
:meth:`within` in reverse instead of :meth:`contains`. Trying it out on features
`d` and `c`, we see that it works.

.. code-block:: pycon

  >>> d < c
  True
  >>> Within(d) < Within(c)
  False

It also works on the list of features, producing the order we want.

.. code-block:: pycon

  >>> [d, c, c, b, a] == sorted(features, key=Within, reverse=True)
  True

DE-9IM Relationships
--------------------

The :meth:`relate` method tests all the DE-9IM [4]_ relationships between
objects, of which the named relationship predicates above are a subset.

.. method:: object.relate(other)

    Returns a string representation of the DE-9IM matrix of relationships
    between an object's `interior`, `boundary`, `exterior` and those of another
    geometric object.

The named relationship predicates (:meth:`contains`, etc.) are typically
implemented as wrappers around :meth:`relate`.

Two different points have mainly ``F`` (false) values in their matrix; the
intersection of their `external` sets (the 9th element) is a ``2`` dimensional
object (the rest of the plane). The intersection of the `interior` of one with
the `exterior` of the other is a ``0`` dimensional object (3rd and 7th elements
of the matrix).

.. code-block:: pycon

  >>> Point(0, 0).relate(Point(1, 1))
  'FF0FFF0F2'

The matrix for a line and a point on the line has more "true" (not ``F``)
elements.

.. code-block:: pycon

  >>> Point(0, 0).relate(LineString([(0, 0), (1, 1)]))
  'F0FFFF102'

.. method:: object.relate_pattern(other, pattern)

    Returns True if the DE-9IM string code for the relationship between the
    geometries satisfies the pattern, otherwise False.

The :meth:`relate_pattern` compares the DE-9IM code string for two geometries
against a specified pattern. If the string matches the pattern then ``True`` is
returned, otherwise ``False``. The pattern specified can be an exact match
(``0``, ``1`` or ``2``), a boolean match (``T`` or ``F``), or a wildcard
(``*``). For example, the pattern for the `within` predicate is ``T*****FF*``.

.. code-block:: pycon

  >> point = Point(0.5, 0.5)
  >> square = Polygon([(0, 0), (0, 1), (1, 1), (1, 0)])
  >> square.relate_pattern(point, 'T*****FF*')
  True
  >> point.within(square)
  True

Note that the order or the geometries is significant, as demonstrated below.
In this example the square contains the point, but the point does not contain
the square.

.. code-block:: pycon

  >>> point.relate(square)
  '0FFFFF212'
  >>> square.relate(point)
  '0F2FF1FF2'

Further discussion of the DE-9IM matrix is beyond the scope of this manual. See
[4]_ and http://pypi.python.org/pypi/de9im.

.. _analysis-methods:

Spatial Analysis Methods
========================

As well as boolean attributes and methods, Shapely provides analysis methods
that return new geometric objects.

.. _set-theoretic-methods:

Set-theoretic Methods
---------------------

Almost every binary predicate method has a counterpart that returns a new
geometric object. In addition, the set-theoretic `boundary` of an object is
available as a read-only attribute.

.. attribute:: object.boundary

  Returns a lower dimensional object representing the object's set-theoretic
  `boundary`.

The boundary of a polygon is a line, the boundary of a line is a collection of
points. The boundary of a point is an empty (null) collection.

.. code-block:: pycon

  >> coords = [((0, 0), (1, 1)), ((-1, 0), (1, 0))]
  >>> lines = MultiLineString(coords)
  >>> lines.boundary
  
  >>> pprint(list(lines.boundary))
  [,
   ,
   ,
   ]
  >>> lines.boundary.boundary
  
  >>> lines.boundary.boundary.is_empty
  True

See the figures in :ref:`linestrings` and :ref:`multilinestrings` for the
illustration of lines and their boundaries.

.. attribute:: object.centroid

  Returns a representation of the object's geometric centroid (point).

.. code-block:: pycon

  >>> LineString([(0, 0), (1, 1)]).centroid
  
  >>> LineString([(0, 0), (1, 1)]).centroid.wkt
  'POINT (0.5000000000000000 0.5000000000000000)'

.. note::

  The centroid of an object might be one of its points, but this is not
  guaranteed.

.. method:: object.difference(other)

  Returns a representation of the points making up this geometric object that
  do not make up the *other* object.

.. code-block:: pycon

  >>> a = Point(1, 1).buffer(1.5)
  >>> b = Point(2, 1).buffer(1.5)
  >>> a.difference(b)
  

.. note::

  The :meth:`buffer` method is used to produce approximately circular polygons
  in the examples of this section; it will be explained in detail later in this
  manual.

.. plot:: code/difference.py

Figure 8. Differences between two approximately circular polygons.

.. note::

  Shapely can not represent the difference between an object and a lower
  dimensional object (such as the difference between a polygon and a line or
  point) as a single object, and in these cases the difference method returns a
  copy of the object named ``self``.

.. method:: object.intersection(other)

  Returns a representation of the intersection of this object with the `other`
  geometric object.

.. code-block:: pycon

  >>> a = Point(1, 1).buffer(1.5)
  >>> b = Point(2, 1).buffer(1.5)
  >>> a.intersection(b)
  

See the figure under :meth:`symmetric_difference` below.

.. method:: object.symmetric_difference(other)

  Returns a representation of the points in this object not in the `other`
  geometric object, and the points in the `other` not in this geometric object.

.. code-block:: pycon

  >>> a = Point(1, 1).buffer(1.5)
  >>> b = Point(2, 1).buffer(1.5)
  >>> a.symmetric_difference(b)
  

.. plot:: code/intersection-sym-difference.py

.. method:: object.union(other)

  Returns a representation of the union of points from this object and the
  `other` geometric object.

The type of object returned depends on the relationship between the operands.
The union of polygons (for example) will be a polygon or a multi-polygon
depending on whether they intersect or not.

.. code-block:: pycon

  >>> a = Point(1, 1).buffer(1.5)
  >>> b = Point(2, 1).buffer(1.5)
  >>> a.union(b)
  

The semantics of these operations vary with type of geometric object.  For
example, compare the boundary of the union of polygons to the union of their
boundaries.

.. code-block:: pycon

  >>> a.union(b).boundary
  
  >>> a.boundary.union(b.boundary)
  

.. plot:: code/union.py

.. note::

  :meth:`union` is an expensive way to find the cumulative union
  of many objects. See :func:`shapely.ops.cascaded_union` for a more effective
  method.

Constructive Methods
--------------------

Shapely geometric object have several methods that yield new objects not
derived from set-theoretic analysis.

.. method:: object.buffer(distance, resolution=16, cap_style=1, join_style=1, mitre_limit=5.0)

  Returns an approximate representation of all points within a given `distance`
  of the this geometric object.

  The styles of caps are specified by integer values: 1 (round), 2 (flat),
  3 (square). These values are also enumerated by the object
  :class:`shapely.geometry.CAP_STYLE` (see below).

  The styles of joins between offset segments are specified by integer values:
  1 (round), 2 (mitre), and 3 (bevel). These values are also enumerated by the
  object :class:`shapely.geometry.JOIN_STYLE` (see below).

.. data:: shapely.geometry.CAP_STYLE

   ========= =====
   Attribute Value
   ========= =====
   round        1
   flat         2
   square       3
   ========= =====

.. data:: shapely.geometry.JOIN_STYLE

   ========= =====
   Attribute Value
   ========= =====
   round         1
   mitre         2
   bevel         3
   ========= =====

.. code-block:: pycon

  >>> from shapely.geometry import CAP_STYLE, JOIN_STYLE
  >>> CAP_STYLE.flat
  2
  >>> JOIN_STYLE.bevel
  3

A positive distance has an effect of dilation; a negative distance, erosion.
The optional `resolution` argument determines the number of segments used to
approximate a quarter circle around a point.

.. code-block:: pycon

  >>> line = LineString([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
  >>> dilated = line.buffer(0.5)
  >>> eroded = dilated.buffer(-0.3)

.. plot:: code/buffer.py

Figure 9. Dilation of a line (left) and erosion of a polygon (right). New
object is shown in blue.

The default (`resolution` of 16) buffer of a point is a polygonal patch with
99.8% of the area of the circular disk it approximates.

.. code-block:: pycon

  >>> p = Point(0, 0).buffer(10.0)
  >>> len(p.exterior.coords)
  66
  >>> p.area
  313.65484905459385

With a `resolution` of 1, the buffer is a square patch.

.. code-block:: pycon

  >>> q = Point(0, 0).buffer(10.0, 1)
  >>> len(q.exterior.coords)
  5
  >>> q.area
  200.0

Passed a `distance` of 0, :meth:`buffer` can sometimes be used to "clean" self-touching
or self-crossing polygons such as the classic "bowtie". Users have reported 
that very small distance values sometimes produce cleaner results than 0. Your
mileage may vary when cleaning surfaces.

.. code-block:: pycon

  >>> coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
  >>> bowtie = Polygon(coords)
  >>> bowtie.is_valid
  False
  >>> clean = bowtie.buffer(0)
  >>> clean.is_valid
  True
  >>> clean
  
  >>> len(clean)
  2
  >>> list(clean[0].exterior.coords)
  [(0.0, 0.0), (0.0, 2.0), (1.0, 1.0), (0.0, 0.0)]
  >>> list(clean[1].exterior.coords)
  [(1.0, 1.0), (2.0, 2.0), (2.0, 0.0), (1.0, 1.0)]

Buffering splits the polygon in two at the point where they touch.

.. attribute:: object.convex_hull

  Returns a representation of the smallest convex `Polygon` containing all the
  points in the object unless the number of points in the object is less than
  three. For two points, the convex hull collapses to a `LineString`; for 1, a
  `Point`.

.. code-block:: pycon

  >>> Point(0, 0).convex_hull
  
  >>> MultiPoint([(0, 0), (1, 1)]).convex_hull
  
  >>> MultiPoint([(0, 0), (1, 1), (1, -1)]).convex_hull
  

.. plot:: code/convex_hull.py

Figure 10. Convex hull (blue) of 2 points (left) and of 6 points (right).

.. attribute:: object.envelope

  Returns a representation of the point or smallest rectangular polygon (with
  sides parallel to the coordinate axes) that contains the object.

.. code-block:: pycon

  >>> Point(0, 0).envelope
  
  >>> MultiPoint([(0, 0), (1, 1)]).envelope
  

.. attribute:: object.minimum_rotated_rectangle
  
  Returns the general minimum bounding rectangle that contains the object. 
  Unlike envelope this rectangle is not constrained to be parallel to the 
  coordinate axes. If the convex hull of the object is a degenerate (line or point) 
  this degenerate is returned.

  `New in Shapely 1.6.0`

.. code-block:: pycon
  
  >>> Point(0, 0).minimum_rotated_rectangle
  
  >>> MultiPoint([(0,0),(1,1),(2,0.5)]).minimum_rotated_rectangle
  

.. plot:: code/minimum_rotated_rectangle.py

Figure 11. Minimum rotated rectangle for a multipoint feature (left) and a 
linestring feature (right).

.. method:: object.parallel_offset(distance, side, resolution=16, join_style=1, mitre_limit=5.0)

  Returns a LineString or MultiLineString geometry at a distance from the
  object on its right or its left side.

  Distance must be a positive float value. The side parameter may be 'left' or
  'right'. The resolution of the offset around each vertex of the object is
  parameterized as in the buffer method.

  The join style is for outside corners between line segments. Accepted integer
  values are 1 (round), 2 (mitre), and 3 (bevel). See also
  :data:`shapely.geometry.JOIN_STYLE`.

  Severely mitered corners can be controlled by the mitre_limit parameter
  (spelled in British English, en-gb). The ratio of the distance from the
  corner to the end of the mitred offset corner is the miter ratio. Corners
  with a ratio which exceed the limit will be beveled.

.. note::

  This method is only available for `LinearRing` and `LineString`  objects.

.. plot:: code/parallel_offset.py

Figure 12. Three styles of parallel offset lines on the left side of a simple
line string (its starting point shown as a circle) and one offset on the right
side, a multipart.

The effect of the `mitre_limit` parameter is shown below.

.. plot:: code/parallel_offset_mitre.py

Figure 13. Large and small mitre_limit values for left and right offsets.

.. method:: object.simplify(tolerance, preserve_topology=True)

  Returns a simplified representation of the geometric object.

All points in the simplified object will be within the `tolerance` distance of
the original geometry. By default a slower algorithm is used that preserves
topology. If preserve topology is set to ``False`` the much quicker
Douglas-Peucker algorithm [6]_ is used.

.. code-block:: pycon

  >>> p = Point(0.0, 0.0)
  >>> x = p.buffer(1.0)
  >>> x.area
  3.1365484905459389
  >>> len(x.exterior.coords)
  66
  >>> s = x.simplify(0.05, preserve_topology=False)
  >>> s.area
  3.0614674589207187
  >>> len(s.exterior.coords)
  17

.. plot:: code/simplify.py

Figure 14. Simplification of a nearly circular polygon using a tolerance of 0.2
(left) and 0.5 (right).

.. note::

  `Invalid` geometric objects may result from simplification that does not
  preserve topology and simplification may be sensitive to the order of
  coordinates: two geometries differing only in order of coordinates may be
  simplified differently.


Affine Transformations
======================

A collection of affine transform functions are in the :mod:`shapely.affinity`
module, which return transformed geometries by either directly supplying
coefficients to an affine transformation matrix, or by using a specific, named
transform (`rotate`, `scale`, etc.). The functions can be used with all
geometry types (except `GeometryCollection`), and 3D types are either
preserved or supported by 3D affine transformations.

`New in version 1.2.17`.

.. function:: shapely.affinity.affine_transform(geom, matrix)

  Returns a transformed geometry using an affine transformation matrix.

  The coefficient ``matrix`` is provided as a list or tuple with 6 or 12 items
  for 2D or 3D transformations, respectively.

  For 2D affine transformations, the 6 parameter ``matrix`` is:

    ``[a, b, d, e, xoff, yoff]``

  which represents the augmented matrix:

  .. math::
    \begin{bmatrix} x' & y' & 1 \end{bmatrix} =
    \begin{bmatrix} x  & y  & 1 \end{bmatrix}
    \begin{bmatrix}
      a & b & x_\mathrm{off} \\
      d & e & y_\mathrm{off} \\
      0 & 0 & 1
    \end{bmatrix}

  or the equations for the transformed coordinates:

  .. math::
    x' &= a x + b y + x_\mathrm{off} \\
    y' &= d x + e y + y_\mathrm{off}.

  For 3D affine transformations, the 12 parameter ``matrix`` is:

    ``[a, b, c, d, e, f, g, h, i, xoff, yoff, zoff]``

  which represents the augmented matrix:

  .. math::
    \begin{bmatrix} x' & y' & z' & 1 \end{bmatrix} =
    \begin{bmatrix} x  & y  & z  & 1 \end{bmatrix}
    \begin{bmatrix}
      a & b & c & x_\mathrm{off} \\
      d & e & f & y_\mathrm{off} \\
      g & h & i & z_\mathrm{off} \\
      0 & 0 & 0 & 1
    \end{bmatrix}

  or the equations for the transformed coordinates:

  .. math::
    x' &= a x + b y + c z + x_\mathrm{off} \\
    y' &= d x + e y + f z + y_\mathrm{off} \\
    z' &= g x + h y + i z + z_\mathrm{off}.

.. function:: shapely.affinity.rotate(geom, angle, origin='center', use_radians=False)

  Returns a rotated geometry on a 2D plane.

  The angle of rotation can be specified in either degrees (default) or
  radians by setting ``use_radians=True``. Positive angles are
  counter-clockwise and negative are clockwise rotations.

  The point of origin can be a keyword ``'center'`` for the bounding box
  center (default), ``'centroid'`` for the geometry's centroid, a `Point` object
  or a coordinate tuple ``(x0, y0)``.

  The affine transformation matrix for 2D rotation with angle :math:`\theta` is:

  .. math::
    \begin{bmatrix}
      \cos{\theta} & -\sin{\theta} & x_\mathrm{off} \\
      \sin{\theta} &  \cos{\theta} & y_\mathrm{off} \\
            0      &        0      & 1
    \end{bmatrix}

  where the offsets are calculated from the origin :math:`(x_0, y_0)`:

  .. math::
    x_\mathrm{off} &= x_0 - x_0 \cos{\theta} + y_0 \sin{\theta} \\
    y_\mathrm{off} &= y_0 - x_0 \sin{\theta} - y_0 \cos{\theta}

  .. code-block:: pycon

    >>> from shapely import affinity
    >>> line = LineString([(1, 3), (1, 1), (4, 1)])
    >>> rotated_a = affinity.rotate(line, 90)
    >>> rotated_b = affinity.rotate(line, 90, origin='centroid')

  .. plot:: code/rotate.py

  Figure 15. Rotation of a `LineString` (gray) by an angle of 90°
  counter-clockwise (blue) using different origins.

.. function:: shapely.affinity.scale(geom, xfact=1.0, yfact=1.0, zfact=1.0, origin='center')

  Returns a scaled geometry, scaled by factors along each dimension.

  The point of origin can be a keyword ``'center'`` for the 2D bounding box
  center (default), ``'centroid'`` for the geometry's 2D centroid, a `Point`
  object or a coordinate tuple ``(x0, y0, z0)``.

  Negative scale factors will mirror or reflect coordinates.

  The general 3D affine transformation matrix for scaling is:

  .. math::
    \begin{bmatrix}
      x_\mathrm{fact} & 0               & 0               & x_\mathrm{off} \\
      0               & y_\mathrm{fact} & 0               & y_\mathrm{off} \\
      0               & 0               & z_\mathrm{fact} & z_\mathrm{off} \\
      0               & 0               & 0               & 1
    \end{bmatrix}

  where the offsets are calculated from the origin :math:`(x_0, y_0, z_0)`:

  .. math::
    x_\mathrm{off} &= x_0 - x_0 x_\mathrm{fact} \\
    y_\mathrm{off} &= y_0 - y_0 y_\mathrm{fact} \\
    z_\mathrm{off} &= z_0 - z_0 z_\mathrm{fact}

  .. code-block:: pycon

    >>> triangle = Polygon([(1, 1), (2, 3), (3, 1)])
    >>> triangle_a = affinity.scale(triangle, xfact=1.5, yfact=-1)
    >>> triangle_a.exterior.coords[:]
    [(0.5, 3.0), (2.0, 1.0), (3.5, 3.0), (0.5, 3.0)]
    >>> triangle_b = affinity.scale(triangle, xfact=2, origin=(1,1))
    >>> triangle_b.exterior.coords[:]
    [(1.0, 1.0), (3.0, 3.0), (5.0, 1.0), (1.0, 1.0)]

  .. plot:: code/scale.py

  Figure 16. Scaling of a gray triangle to blue result: a) by a factor of 1.5
  along x-direction, with reflection across y-axis; b) by a factor of 2 along
  x-direction with custom origin at (1, 1).

.. function:: shapely.affinity.skew(geom, xs=0.0, ys=0.0, origin='center', use_radians=False)

  Returns a skewed geometry, sheared by angles along x and y dimensions.

  The shear angle can be specified in either degrees (default) or radians
  by setting ``use_radians=True``.

  The point of origin can be a keyword ``'center'`` for the bounding box
  center (default), ``'centroid'`` for the geometry's centroid, a `Point`
  object or a coordinate tuple ``(x0, y0)``.

  The general 2D affine transformation matrix for skewing is:

  .. math::
    \begin{bmatrix}
      1 & \tan{x_s} & x_\mathrm{off} \\
      \tan{y_s} & 1 & y_\mathrm{off} \\
      0 & 0 & 1
    \end{bmatrix}

  where the offsets are calculated from the origin :math:`(x_0, y_0)`:

  .. math::
    x_\mathrm{off} &= -y_0 \tan{x_s} \\
    y_\mathrm{off} &= -x_0 \tan{y_s}

  .. plot:: code/skew.py

  Figure 17. Skewing of a gray "R" to blue result: a) by a shear angle of 20°
  along the x-direction and an origin at (1, 1); b) by a shear angle of 30°
  along the y-direction, using default origin.

.. function:: shapely.affinity.translate(geom, xoff=0.0, yoff=0.0, zoff=0.0)

  Returns a translated geometry shifted by offsets along each dimension.

  The general 3D affine transformation matrix for translation is:

  .. math::
    \begin{bmatrix}
      1 & 0 & 0 & x_\mathrm{off} \\
      0 & 1 & 0 & y_\mathrm{off} \\
      0 & 0 & 1 & z_\mathrm{off} \\
      0 & 0 & 0 & 1
    \end{bmatrix}


Other Transformations
=====================

Shapely supports map projections and other arbitrary transformations of geometric objects.

.. function:: shapely.ops.transform(func, geom)

  Applies `func` to all coordinates of `geom` and returns a new
  geometry of the same type from the transformed coordinates.

  `func` maps x, y, and optionally z to output xp, yp, zp. The input
  parameters may iterable types like lists or arrays or single values.
  The output shall be of the same type: scalars in, scalars out;
  lists in, lists out.

  `New in version 1.2.18`.

For example, here is an identity function applicable to both types of input
(scalar or array).

.. code-block:: python

    def id_func(x, y, z=None):
        return tuple(filter(None, [x, y, z]))

    g2 = transform(id_func, g1)

A partially applied transform function from pyproj satisfies the requirements
for `func`.

.. code-block:: python

    from shapely.ops import transform
    from functools import partial
    import pyproj

    project = partial(
        pyproj.transform,
        pyproj.Proj(init='epsg:4326'),
        pyproj.Proj(init='epsg:26913'))

    g2 = transform(project, g1)

Lambda expressions such as the one in

.. code-block:: python

    g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)

also satisfy the requirements for `func`.


Other Operations
================

Merging Linear Features
-----------------------

Sequences of touching lines can be merged into `MultiLineStrings` or `Polygons`
using functions in the :mod:`shapely.ops` module.

.. function:: shapely.ops.polygonize(lines)

  Returns an iterator over polygons constructed from the input `lines`.

  As with the :class:`MultiLineString` constructor, the input elements may be
  any line-like object.

  .. code-block:: pycon

    >>> from shapely.ops import polygonize
    >>> lines = [
    ...     ((0, 0), (1, 1)),
    ...     ((0, 0), (0, 1)),
    ...     ((0, 1), (1, 1)),
    ...     ((1, 1), (1, 0)),
    ...     ((1, 0), (0, 0))
    ...     ]
    >>> pprint(list(polygonize(lines)))
    [,
     ]

.. function:: shapely.ops.polygonize_full(lines)

  Creates polygons from a source of lines, returning the polygons
  and leftover geometries.

  The source may be a MultiLineString, a sequence of LineString objects,
  or a sequence of objects than can be adapted to LineStrings.

  Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring
  lines). Each are a geometry collection.

  Dangles are edges which have one or both ends which are not incident on
  another edge endpoint. Cut edges are connected at both ends but do not
  form part of polygon. Invalid ring lines form rings which are invalid
  (bowties, etc).

  `New in version 1.2.18.`

  .. code-block:: pycon

    >>> lines = [
    ...     ((0, 0), (1, 1)),
    ...     ((0, 0), (0, 1)),
    ...     ((0, 1), (1, 1)),
    ...     ((1, 1), (1, 0)),
    ...     ((1, 0), (0, 0)),
    ...     ((5, 5), (6, 6)),
    ...     ((1, 1), (100, 100)),
    ...     ]
    >>> result, dangles, cuts, invalids = polygonize_full(lines)
    >>> len(result)
    2
    >>> list(result.geoms)
    [, ]
    >>> list(cuts.geoms)
    [, ]

.. function:: shapely.ops.linemerge(lines)

  Returns a `LineString` or `MultiLineString` representing the merger of all
  contiguous elements of `lines`.

  As with :func:`shapely.ops.polygonize`, the input elements may be any
  line-like object.

.. code-block:: python

    >>> from shapely.ops import linemerge
    >>> linemerge(lines)
    
    >>> pprint(list(linemerge(lines)))
    [,
     ,
     ]

Cascading Unions
----------------

The :func:`~shapely.ops.cascaded_union` function in `shapely.ops` is more
efficient than accumulating with :meth:`union`.

.. plot:: code/cascaded_union.py

.. function:: shapely.ops.cascaded_union(geoms)

  Returns a representation of the union of the given geometric objects.

  .. code-block:: pycon

    >>> from shapely.ops import cascaded_union
    >>> polygons = [Point(i, 0).buffer(0.7) for i in range(5)]
    >>> cascaded_union(polygons)
    

  The function is particularly useful in dissolving `MultiPolygons`.

  .. code-block:: pycon

    >>> m = MultiPolygon(polygons)
    >>> m.area
    7.6845438018375516
    >>> cascaded_union(m).area
    6.6103013551167971

  .. note::

     In 1.2.16 :func:`shapely.ops.cascaded_union` is superceded by
     :func:`shapely.ops.unary_union` if GEOS 3.2+ is used. The unary union
     function can operate on different geometry types, not only polygons as is
     the case for the older cascaded unions.

.. function:: shapely.ops.unary_union(geoms)

  Returns a representation of the union of the given geometric objects.

Delaunay triangulation
----------------------

The :func:`~shapely.ops.triangulate` function in `shapely.ops` calculates a
Delaunay triangulation from a collection of points.

.. plot:: code/triangulate.py

.. function:: shapely.ops.triangulate(geom, tolerance=0.0, edges=False)

   Returns a Delaunary triangulation of the vertices of the input geometry.

   The source may be any geometry type. All vertices of the geometry will be
   used as the points of the triangulation.

   The `tolerance` keyword argument sets the snapping tolerance used to improve
   the robustness of the triangulation computation. A tolerance of 0.0 specifies
   that no snapping will take place.

   If the `edges` keyword argument is `False` a list of `Polygon` triangles
   will be returned. Otherwise a list of `LineString` edges is returned.

   `New in version  1.4.0`

.. code-block:: pycon

  >>> from shapely.ops import triangulate
  >>> points = MultiPoint([(0, 0), (1, 1), (0, 2), (2, 2), (3, 1), (1, 0)])
  >>> triangles = triangulate(points)
  >>> pprint([triangle.wkt for triangle in triangles])
  ['POLYGON ((0 2, 0 0, 1 1, 0 2))',
   'POLYGON ((0 2, 1 1, 2 2, 0 2))',
   'POLYGON ((2 2, 1 1, 3 1, 2 2))',
   'POLYGON ((3 1, 1 1, 1 0, 3 1))',
   'POLYGON ((1 0, 1 1, 0 0, 1 0))']

Nearest points
--------------

The :func:`~shapely.ops.nearest_points` function in `shapely.ops` calculates
the nearest points in a pair of geometries.

.. function:: shapely.ops.nearest_points(geom1, geom2)

   Returns a tuple of the nearest points in the input geometries. The points are
   returned in the same order as the input geometries.

   `New in version 1.4.0`.

.. code-block:: pycon

  >>> from shapely.ops import nearest_points
  >>> triangle = Polygon([(0, 0), (1, 0), (0.5, 1), (0, 0)])
  >>> square = Polygon([(0, 2), (1, 2), (1, 3), (0, 3), (0, 2)])
  >>> [o.wkt for o in nearest_points(triangle, square)]
  ['POINT (0.5 1)', 'POINT (0.5 2)']

Note that the nearest points may not be existing vertices in the geometries.

Snapping
--------

The :func:`~shapely.ops.snap` function in `shapely.ops` snaps the vertices in
one geometry to the vertices in a second geometry with a given tolerance.

.. function:: shapely.ops.snap(geom1, geom2, tolerance)

   Snaps vertices in `geom1` to vertices in the `geom2`. A copy of the snapped
   geometry is returned. The input geometries are not modified.

   The `tolerance` argument specifies the minimum distance between vertices for
   them to be snapped.

   `New in version 1.5.0`

.. code-block:: pycon

  >>> from shapely.ops import snap
  >>> square = Polygon([(1,1), (2, 1), (2, 2), (1, 2), (1, 1)])
  >>> line = LineString([(0,0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)])
  >>> result = snap(line, square, 0.5)
  >>> result.wkt
  'LINESTRING (0 0, 1 1, 2 1, 2.6 0.5)'

Shared paths
------------

The :func:`~shapely.ops.shared_paths` function in `shapely.ops` finds the shared
paths between two lineal geometries.

.. function:: shapely.ops.shared_paths(geom1, geom2)

   Finds the shared paths between `geom1` and `geom2`, where both geometries
   are `LineStrings`.
   
   A `GeometryCollection` is returned with two elements. The first element is a
   `MultiLineString` containing shared paths with the same direction for both
   inputs. The second element is a MultiLineString containing shared paths with
   the opposite direction for the two inputs.
   
   `New in version 1.6.0`

.. code-block:: pycon

  >>> from shapely.ops import shared_paths
  >>> g1 = LineString([(0, 0), (10, 0), (10, 5), (20, 5)])
  >>> g2 = LineString([(5, 0), (30, 0), (30, 5), (0, 5)])
  >>> forward, backward = shared_paths(g1, g2)
  >>> forward.wkt
  'MULTILINESTRING ((5 0, 10 0))'
  >>> backward.wkt
  'MULTILINESTRING ((10 5, 20 5))'

Splitting
---------

The :func:`~shapely.ops.split` function in `shapely.ops` splits a geometry by another geometry.

.. function:: shapely.ops.split(geom, splitter)

   Splits a geometry by another geometry and returns a collection of geometries. This function is the theoretical
   opposite of the union of the split geometry parts. If the splitter does not split the geometry, a  collection with a single geometry equal to the input geometry is returned.
  
   The function supports:

   * Splitting a (Multi)LineString by a (Multi)Point or (Multi)LineString or (Multi)Polygon boundary
   
   * Splitting a (Multi)Polygon by a LineString

   It may be convenient to snap the splitter with low tolerance to the geometry. For example in the case of splitting a line by a point, the point must be exactly on the line, for the line to be correctly split.
   When splitting a line by a polygon, the boundary of the polygon is used for the operation.
   When splitting a line by another line, a ValueError is raised if the two overlap at some segment.

   `New in version 1.6.0`

.. code-block:: pycon

  >>> pt = Point((1, 1))
  >>> line = LineString([(0,0), (2,2)])
  >>> result = split(line, pt)
  >>> result.wkt
  'GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), LINESTRING (1 1, 2 2))'

Prepared Geometry Operations
----------------------------

Shapely geometries can be processed into a state that supports more efficient
batches of operations.

.. function:: prepared.prep(ob)

  Creates and returns a prepared geometric object.

To test one polygon containment against a large batch of points, one should
first use the :func:`prepared.prep` function.

.. code-block:: pycon

  >>> from shapely.geometry import Point
  >>> from shapely.prepared import prep
  >>> points = [...] # large list of points
  >>> polygon = Point(0.0, 0.0).buffer(1.0)
  >>> prepared_polygon = prep(polygon)
  >>> prepared_polygon
  
  >>> hits = filter(prepared_polygon.contains, points)

Prepared geometries instances have the following methods: ``contains``,
``contains_properly``, ``covers``, and ``intersects``. All have exactly the
same arguments and usage as their counterparts in non-prepared geometric
objects.

Diagnostics
-----------

.. function:: validation.explain_validity(ob):

  Returns a string explaining the validity or invalidity of the object.

  `New in version 1.2.1`.

The messages may or may not have a representation of a problem point that can
be parsed out.

.. code-block:: pycon

  >>> coords = [(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)]
  >>> p = Polygon(coords)
  >>> from shapely.validation import explain_validity
  >>> explain_validity(p)
  'Ring Self-intersection[1 1]'

The Shapely version, GEOS library version, and GEOS C API version are
accessible via :attr:`shapely.__version__`,
:attr:`shapely.geos.geos_version_string`, and
:attr:`shapely.geos.geos_capi_version`.

.. code-block:: pycon

  >>> import shapely
  >>> shapely.__version__
  '1.3.0'
  >>> import shapely.geos
  >>> shapely.geos.geos_version
  (3, 3, 0)
  >>> shapely.geos.geos_version_string
  '3.3.0-CAPI-1.7.0'


STR-packed R-tree
=================

Shapely provides an interface to the query-only GEOS R-tree packed using the
Sort-Tile-Recursive algorithm. Pass a list of geometry objects to the STRtree
constructor to create an R-tree that you can query with another geometric object.

.. class:: strtree.STRtree(geometries)

  The `STRtree` constructor takes a sequence of geometric objects.

  These are copied and stored in the R-tree.

  `New in version 1.4.0`.

Query-only means in this case that the R-tree, once created, is immutable. You
cannot add or remove geometries.

.. code-block:: pycon

  >>> from shapely.geometry import Point
  >>> from shapely.strtree import STRtree
  >>> points = [Point(i, i) for i in range(10)]
  >>> tree = STRtree(points)
  >>> tree.query(Point(2,2).buffer(0.99))
  >>> [o.wkt for o in tree.query(Point(2,2).buffer(0.99))]
  ['POINT (2 2)']
  >>> [o.wkt for o in tree.query(Point(2,2).buffer(1.0))]
  ['POINT (1 1)', 'POINT (2 2)', 'POINT (3 3)']


Interoperation
==============

Shapely provides 4 avenues for interoperation with other software.

Well-Known Formats
------------------

A `Well Known Text` (WKT) or `Well Known Binary` (WKB) representation [1]_ of
any geometric object can be had via its :attr:`wkt` or :attr:`wkb` attribute.
These representations allow interchange with many GIS programs. PostGIS, for
example, trades in hex-encoded WKB.

.. code-block:: pycon

  >>> Point(0, 0).wkt
  'POINT (0.0000000000000000 0.0000000000000000)'
  >>> Point(0, 0).wkb.encode('hex')
  '010100000000000000000000000000000000000000'

The `shapely.wkt` and `shapely.wkb` modules provide `dumps()` and `loads()`
functions that work almost exactly as their `pickle` and `simplejson` module
counterparts. To serialize a geometric object to a binary or text string, use
``dumps()``. To deserialize a string and get a new geometric object of the
appropriate type, use ``loads()``.

.. function:: shapely.wkb.dumps(ob)

  Returns a WKB representation of `ob`.

.. function:: shapely.wkb.loads(wkb)

  Returns a geometric object from a WKB representation `wkb`.

.. code-block:: pycon

  >> from shapely.wkb import dumps, loads
  >>> wkb = dumps(Point(0, 0))
  >>> print wkb.encode('hex')
  010100000000000000000000000000000000000000
  >>> loads(wkb).wkt
  'POINT (0.0000000000000000 0.0000000000000000)'

All of Shapely's geometry types are supported by these functions.

.. function:: shapely.wkt.dumps(ob)

  Returns a WKT representation of `ob`.

.. function:: shapely.wkt.loads(wkt)

  Returns a geometric object from a WKT representation `wkt`.

.. code-block:: pycon

  >> wkt = dumps(Point(0, 0))
  >>> print wkt
  POINT (0.0000000000000000 0.0000000000000000)
  >>> loads(wkt).wkt
  'POINT (0.0000000000000000 0.0000000000000000)'

Numpy and Python Arrays
-----------------------

All geometric objects with coordinate sequences (`Point`, `LinearRing`,
`LineString`) provide the Numpy array interface and can thereby be converted or
adapted to Numpy arrays.

.. code-block:: pycon

  >>> from numpy import array
  >>> array(Point(0, 0))
  array([ 0.,  0.])
  >>> array(LineString([(0, 0), (1, 1)]))
  array([[ 0.,  0.],
         [ 1.,  1.]])

The :func:`numpy.asarray` function does not copy coordinate values – at the
price of slower Numpy access to the coordinates of Shapely objects.

.. note::

  The Numpy array interface is provided without a dependency on Numpy itself.

The coordinates of the same types of geometric objects can be had as standard
Python arrays of `x` and `y` values via the :attr:`xy` attribute.

.. code-block:: pycon

  >>> Point(0, 0).xy
  (array('d', [0.0]), array('d', [0.0]))
  >>> LineString([(0, 0), (1, 1)]).xy
  (array('d', [0.0, 1.0]), array('d', [0.0, 1.0]))

The :func:`shapely.geometry.asShape` family of functions can be used to wrap
Numpy coordinate arrays so that they can then be analyzed using Shapely while
maintaining their original storage. A 1 x 2 array can be adapted to a point

.. code-block:: pycon

  >>> from shapely.geometry import asPoint
  >>> pa = asPoint(array([0.0, 0.0]))
  >>> pa.wkt
  'POINT (0.0000000000000000 0.0000000000000000)'

and a N x 2 array can be adapted to a line string

.. code-block:: pycon

  >>> from shapely.geometry import asLineString
  >>> la = asLineString(array([[1.0, 2.0], [3.0, 4.0]]))
  >>> la.wkt
  'LINESTRING (1.0000000000000000 2.0000000000000000, 3.0000000000000000 4.0000000000000000)'

There is no Numpy array representation of a polygon.

Python Geo Interface
--------------------

Any object that provides the GeoJSON-like `Python geo interface`_ can be
adapted and used as a Shapely geometry using the
:func:`shapely.geometry.asShape` or :func:`shapely.geometry.shape` functions.

.. function:: shapely.geometry.asShape(context)

  Adapts the context to a geometry interface. The coordinates remain stored in
  the context.

.. function:: shapely.geometry.shape(context)

   Returns a new, independent geometry with coordinates `copied` from the
   context.

For example, a dictionary:

.. code-block:: pycon

  >>> from shapely.geometry import shape
  >>> data = {"type": "Point", "coordinates": (0.0, 0.0)}
  >>> geom = shape(data)
  >>> geom.geom_type
  'Point'
  >>> list(geom.coords)
  [(0.0, 0.0)]

Or a simple placemark-type object:

.. code-block:: pycon

  >>> class GeoThing(object):
  ...     def __init__(self, d):
  ...         self.__geo_interface__ = d
  >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
  >>> geom = shape(thing)
  >>> geom.geom_type
  'Point'
  >>> list(geom.coords)
  [(0.0, 0.0)]

The GeoJSON-like mapping of a geometric object can be obtained using
:func:`shapely.geometry.mapping`.

.. function:: shapely.geometry.mapping(ob)

  Returns a new, independent geometry with coordinates `copied` from the
  context.

  `New in version 1.2.3`.

  For example, using the same `GeoThing` class:

.. code-block:: pycon

  >>> from shapely.geometry import mapping
  >>> thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
  >>> m = mapping(thing)
  >>> m['type']
  'Point'
  >>> m['coordinates']
  (0.0, 0.0)}


Performance
===========

Shapely uses the GEOS_ library for all operations. GEOS is written in C++ and
used in many applications and you can expect that all operations are highly
optimized. The creation of new geometries with many coordinates, however,
involves some overhead that might slow down your code.

.. versionadded:: 1.2.10

The :mod:`shapely.speedups` module contains performance enhancements written in
C. They are automatically installed when Python has access to a compiler and
GEOS development headers during installation. 

You can check if the speedups are installed with the :attr:`available`
attribute. To enable the speedups call :func:`enable`. You can revert to the
default implementation with :func:`disable`.

.. code-block:: pycon

  >>> from shapely import speedups
  >>> speedups.available
  True
  >>> speedups.enable()

.. versionadded:: 1.6.0

Speedups are now enabled by default if they are available. You can check if
speedups are enabled with the :attr:`enabled` attribute.

.. code-block:: pycon

  >>> from shapely import speedups
  >>> speedups.enabled
  True

Conclusion
==========

We hope that you will enjoy and profit from using Shapely. Questions and
comments are welcome on the GIS-Python email list_. This manual will be updated
and improved regularly. Its source is available at
http://github.com/Toblerity/Shapely/tree/master/docs/.


References
==========

.. [1] John R. Herring, Ed.,
   “OpenGIS Implementation Specification for Geographic information - Simple
   feature access - Part 1: Common architecture,” Oct. 2006.

.. [2] M.J. Egenhofer and John R. Herring,
   Categorizing Binary Topological Relations Between Regions, Lines, and Points
   in Geographic Databases,  Orono, ME: University of Maine, 1991.

.. [3] E. Clementini, P. Di Felice, and P. van Oosterom,
   “A Small Set of Formal Topological Relationships Suitable for End-User
   Interaction,” Third International Symposium on Large Spatial Databases
   (SSD). Lecture Notes in Computer Science no. 692, David Abel and Beng Chin
   Ooi, Eds.,  Singapore: Springer Verlag, 1993, pp. 277-295.

.. [4] C. Strobl, “Dimensionally Extended Nine-Intersection Model (DE-9IM),”
   Encyclopedia of GIS, S. Shekhar and H. Xiong, Eds.,
   Springer, 2008, pp. 240-245. [|Strobl-PDF|_]

.. [5] Martin Davis, “JTS Technical Specifications,” Mar. 2003. [|JTS-PDF|_]

.. [6] David H. Douglas and Thomas K. Peucker,
   “Algorithms for the Reduction of the Number of Points Required to Represent
   a Digitized Line or its Caricature,” Cartographica: The International
   Journal for Geographic Information and Geovisualization,  vol. 10, Dec.
   1973, pp. 112-122.


.. _GEOS: http://trac.osgeo.org/geos/
.. _Java Topology Suite: https://www.locationtech.org/projects/technology.jts
.. _JTS: https://www.locationtech.org/projects/technology.jts
.. _PostGIS: http://postgis.refractions.net
.. _record: http://pypi.python.org/pypi/Shapely
.. _wiki: http://trac.gispython.org/lab/wiki/Shapely
.. _Open Geospatial Consortium: http://www.opengeospatial.org/
.. _Davis: http://lin-ear-th-inking.blogspot.com/2007/06/subtleties-of-ogc-covers-spatial.html
.. _Understanding spatial relations: http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm
.. _Strobl-PDF: http://giswiki.hsr.ch/images/3/3d/9dem_springer.pdf
.. |Strobl-PDF| replace:: PDF
.. _JTS-PDF: http://www.vividsolutions.com/jts/bin/JTS%20Technical%20Specs.pdf
.. |JTS-PDF| replace:: PDF
.. _frozenset: http://docs.python.org/library/stdtypes.html#frozenset
.. _Sorting HowTo: http://wiki.python.org/moin/HowTo/Sorting/
.. _Python geo interface: http://gist.github.com/2217756
.. _list: http://lists.gispython.org/mailman/listinfo/community
Shapely-1.6.4/docs/project.rst000066400000000000000000000001261323200062600162500ustar00rootroot00000000000000.. include:: ../README.rst

.. include:: ../CREDITS.txt

.. include:: ../CHANGES.txt

Shapely-1.6.4/docs/sphinxext/000077500000000000000000000000001323200062600161035ustar00rootroot00000000000000Shapely-1.6.4/docs/sphinxext/apigen.py000066400000000000000000000364531323200062600177330ustar00rootroot00000000000000"""Attempt to generate templates for module reference with Sphinx

XXX - we exclude extension modules

To include extension modules, first identify them as valid in the
``_uri2path`` method, then handle them in the ``_parse_module`` script.

We get functions and classes by parsing the text of .py files.
Alternatively we could import the modules for discovery, and we'd have
to do that for extension modules.  This would involve changing the
``_parse_module`` method to work via import and introspection, and
might involve changing ``discover_modules`` (which determines which
files are modules, and therefore which module URIs will be passed to
``_parse_module``).

NOTE: this is a modified version of a script originally shipped with the
PyMVPA project, which we've adapted for NIPY use.  PyMVPA is an MIT-licensed
project."""

# Stdlib imports
import os
import re

# Functions and classes
class ApiDocWriter(object):
    ''' Class for automatic detection and parsing of API docs
    to Sphinx-parsable reST format'''

    # only separating first two levels
    rst_section_levels = ['*', '=', '-', '~', '^']

    def __init__(self,
                 package_name,
                 rst_extension='.rst',
                 package_skip_patterns=None,
                 module_skip_patterns=None,
                 ):
        ''' Initialize package for parsing

        Parameters
        ----------
        package_name : string
            Name of the top-level package.  *package_name* must be the
            name of an importable package
        rst_extension : string, optional
            Extension for reST files, default '.rst'
        package_skip_patterns : None or sequence of {strings, regexps}
            Sequence of strings giving URIs of packages to be excluded
            Operates on the package path, starting at (including) the
            first dot in the package path, after *package_name* - so,
            if *package_name* is ``sphinx``, then ``sphinx.util`` will
            result in ``.util`` being passed for earching by these
            regexps.  If is None, gives default. Default is:
            ['\.tests$']
        module_skip_patterns : None or sequence
            Sequence of strings giving URIs of modules to be excluded
            Operates on the module name including preceding URI path,
            back to the first dot after *package_name*.  For example
            ``sphinx.util.console`` results in the string to search of
            ``.util.console``
            If is None, gives default. Default is:
            ['\.setup$', '\._']
        '''
        if package_skip_patterns is None:
            package_skip_patterns = ['\\.tests$']
        if module_skip_patterns is None:
            module_skip_patterns = ['\\.setup$', '\\._']
        self.package_name = package_name
        self.rst_extension = rst_extension
        self.package_skip_patterns = package_skip_patterns
        self.module_skip_patterns = module_skip_patterns

    def get_package_name(self):
        return self._package_name

    def set_package_name(self, package_name):
        ''' Set package_name

        >>> docwriter = ApiDocWriter('sphinx')
        >>> import sphinx
        >>> docwriter.root_path == sphinx.__path__[0]
        True
        >>> docwriter.package_name = 'docutils'
        >>> import docutils
        >>> docwriter.root_path == docutils.__path__[0]
        True
        '''
        # It's also possible to imagine caching the module parsing here
        self._package_name = package_name
        self.root_module = __import__(package_name)
        self.root_path = self.root_module.__path__[0]
        self.written_modules = None

    package_name = property(get_package_name, set_package_name, None,
                            'get/set package_name')

    def _get_object_name(self, line):
        ''' Get second token in line
        >>> docwriter = ApiDocWriter('sphinx')
        >>> docwriter._get_object_name("  def func():  ")
        'func'
        >>> docwriter._get_object_name("  class Klass(object):  ")
        'Klass'
        >>> docwriter._get_object_name("  class Klass:  ")
        'Klass'
        '''
        name = line.split()[1].split('(')[0].strip()
        # in case we have classes which are not derived from object
        # ie. old style classes
        return name.rstrip(':')

    def _uri2path(self, uri):
        ''' Convert uri to absolute filepath

        Parameters
        ----------
        uri : string
            URI of python module to return path for

        Returns
        -------
        path : None or string
            Returns None if there is no valid path for this URI
            Otherwise returns absolute file system path for URI

        Examples
        --------
        >>> docwriter = ApiDocWriter('sphinx')
        >>> import sphinx
        >>> modpath = sphinx.__path__[0]
        >>> res = docwriter._uri2path('sphinx.builder')
        >>> res == os.path.join(modpath, 'builder.py')
        True
        >>> res = docwriter._uri2path('sphinx')
        >>> res == os.path.join(modpath, '__init__.py')
        True
        >>> docwriter._uri2path('sphinx.does_not_exist')

        '''
        if uri == self.package_name:
            return os.path.join(self.root_path, '__init__.py')
        path = uri.replace('.', os.path.sep)
        path = path.replace(self.package_name + os.path.sep, '')
        path = os.path.join(self.root_path, path)
        # XXX maybe check for extensions as well?
        if os.path.exists(path + '.py'): # file
            path += '.py'
        elif os.path.exists(os.path.join(path, '__init__.py')):
            path = os.path.join(path, '__init__.py')
        else:
            return None
        return path

    def _path2uri(self, dirpath):
        ''' Convert directory path to uri '''
        relpath = dirpath.replace(self.root_path, self.package_name)
        if relpath.startswith(os.path.sep):
            relpath = relpath[1:]
        return relpath.replace(os.path.sep, '.')

    def _parse_module(self, uri):
        ''' Parse module defined in *uri* '''
        filename = self._uri2path(uri)
        if filename is None:
            # nothing that we could handle here.
            return ([],[])
        f = open(filename, 'rt')
        functions, classes = self._parse_lines(f)
        f.close()
        return functions, classes
    
    def _parse_lines(self, linesource):
        ''' Parse lines of text for functions and classes '''
        functions = []
        classes = []
        for line in linesource:
            if line.startswith('def ') and line.count('('):
                # exclude private stuff
                name = self._get_object_name(line)
                if not name.startswith('_'):
                    functions.append(name)
            elif line.startswith('class '):
                # exclude private stuff
                name = self._get_object_name(line)
                if not name.startswith('_'):
                    classes.append(name)
            else:
                pass
        functions.sort()
        classes.sort()
        return functions, classes

    def generate_api_doc(self, uri):
        '''Make autodoc documentation template string for a module

        Parameters
        ----------
        uri : string
            python location of module - e.g 'sphinx.builder'

        Returns
        -------
        S : string
            Contents of API doc
        '''
        # get the names of all classes and functions
        functions, classes = self._parse_module(uri)
        if not len(functions) and not len(classes):
            print 'WARNING: Empty -',uri  # dbg
            return ''

        # Make a shorter version of the uri that omits the package name for
        # titles 
        uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
        
        ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'

        chap_title = uri_short
        ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title)
               + '\n\n')

        # Set the chapter title to read 'module' for all modules except for the
        # main packages
        if '.' in uri:
            title = 'Module: :mod:`' + uri_short + '`'
        else:
            title = ':mod:`' + uri_short + '`'
        ad += title + '\n' + self.rst_section_levels[2] * len(title)

        if len(classes):
            ad += '\nInheritance diagram for ``%s``:\n\n' % uri
            ad += '.. inheritance-diagram:: %s \n' % uri
            ad += '   :parts: 3\n'

        ad += '\n.. automodule:: ' + uri + '\n'
        ad += '\n.. currentmodule:: ' + uri + '\n'
        multi_class = len(classes) > 1
        multi_fx = len(functions) > 1
        if multi_class:
            ad += '\n' + 'Classes' + '\n' + \
                  self.rst_section_levels[2] * 7 + '\n'
        elif len(classes) and multi_fx:
            ad += '\n' + 'Class' + '\n' + \
                  self.rst_section_levels[2] * 5 + '\n'
        for c in classes:
            ad += '\n:class:`' + c + '`\n' \
                  + self.rst_section_levels[multi_class + 2 ] * \
                  (len(c)+9) + '\n\n'
            ad += '\n.. autoclass:: ' + c + '\n'
            # must NOT exclude from index to keep cross-refs working
            ad += '  :members:\n' \
                  '  :undoc-members:\n' \
                  '  :show-inheritance:\n' \
                  '  :inherited-members:\n' \
                  '\n' \
                  '  .. automethod:: __init__\n'
        if multi_fx:
            ad += '\n' + 'Functions' + '\n' + \
                  self.rst_section_levels[2] * 9 + '\n\n'
        elif len(functions) and multi_class:
            ad += '\n' + 'Function' + '\n' + \
                  self.rst_section_levels[2] * 8 + '\n\n'
        for f in functions:
            # must NOT exclude from index to keep cross-refs working
            ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
        return ad

    def _survives_exclude(self, matchstr, match_type):
        ''' Returns True if *matchstr* does not match patterns

        ``self.package_name`` removed from front of string if present

        Examples
        --------
        >>> dw = ApiDocWriter('sphinx')
        >>> dw._survives_exclude('sphinx.okpkg', 'package')
        True
        >>> dw.package_skip_patterns.append('^\\.badpkg$')
        >>> dw._survives_exclude('sphinx.badpkg', 'package')
        False
        >>> dw._survives_exclude('sphinx.badpkg', 'module')
        True
        >>> dw._survives_exclude('sphinx.badmod', 'module')
        True
        >>> dw.module_skip_patterns.append('^\\.badmod$')
        >>> dw._survives_exclude('sphinx.badmod', 'module')
        False
        '''
        if match_type == 'module':
            patterns = self.module_skip_patterns
        elif match_type == 'package':
            patterns = self.package_skip_patterns
        else:
            raise ValueError('Cannot interpret match type "%s"' 
                             % match_type)
        # Match to URI without package name
        L = len(self.package_name)
        if matchstr[:L] == self.package_name:
            matchstr = matchstr[L:]
        for pat in patterns:
            try:
                pat.search
            except AttributeError:
                pat = re.compile(pat)
            if pat.search(matchstr):
                return False
        return True

    def discover_modules(self):
        ''' Return module sequence discovered from ``self.package_name`` 


        Parameters
        ----------
        None

        Returns
        -------
        mods : sequence
            Sequence of module names within ``self.package_name``

        Examples
        --------
        >>> dw = ApiDocWriter('sphinx')
        >>> mods = dw.discover_modules()
        >>> 'sphinx.util' in mods
        True
        >>> dw.package_skip_patterns.append('\.util$')
        >>> 'sphinx.util' in dw.discover_modules()
        False
        >>> 
        '''
        modules = [self.package_name]
        # raw directory parsing
        for dirpath, dirnames, filenames in os.walk(self.root_path):
            # Check directory names for packages
            root_uri = self._path2uri(os.path.join(self.root_path,
                                                   dirpath))
            for dirname in dirnames[:]: # copy list - we modify inplace
                package_uri = '.'.join((root_uri, dirname))
                if (self._uri2path(package_uri) and
                    self._survives_exclude(package_uri, 'package')):
                    modules.append(package_uri)
                else:
                    dirnames.remove(dirname)
            # Check filenames for modules
            for filename in filenames:
                module_name = filename[:-3]
                module_uri = '.'.join((root_uri, module_name))
                if (self._uri2path(module_uri) and
                    self._survives_exclude(module_uri, 'module')):
                    modules.append(module_uri)
        return sorted(modules)
    
    def write_modules_api(self, modules,outdir):
        # write the list
        written_modules = []
        for m in modules:
            api_str = self.generate_api_doc(m)
            if not api_str:
                continue
            # write out to file
            outfile = os.path.join(outdir,
                                   m + self.rst_extension)
            fileobj = open(outfile, 'wt')
            fileobj.write(api_str)
            fileobj.close()
            written_modules.append(m)
        self.written_modules = written_modules

    def write_api_docs(self, outdir):
        """Generate API reST files.

        Parameters
        ----------
        outdir : string
            Directory name in which to store files
            We create automatic filenames for each module
            
        Returns
        -------
        None

        Notes
        -----
        Sets self.written_modules to list of written modules
        """
        if not os.path.exists(outdir):
            os.mkdir(outdir)
        # compose list of modules
        modules = self.discover_modules()
        self.write_modules_api(modules,outdir)
        
    def write_index(self, outdir, froot='gen', relative_to=None):
        """Make a reST API index file from written files

        Parameters
        ----------
        path : string
            Filename to write index to
        outdir : string
            Directory to which to write generated index file
        froot : string, optional
            root (filename without extension) of filename to write to
            Defaults to 'gen'.  We add ``self.rst_extension``.
        relative_to : string
            path to which written filenames are relative.  This
            component of the written file path will be removed from
            outdir, in the generated index.  Default is None, meaning,
            leave path as it is.
        """
        if self.written_modules is None:
            raise ValueError('No modules written')
        # Get full filename path
        path = os.path.join(outdir, froot+self.rst_extension)
        # Path written into index is relative to rootpath
        if relative_to is not None:
            relpath = outdir.replace(relative_to + os.path.sep, '')
        else:
            relpath = outdir
        idx = open(path,'wt')
        w = idx.write
        w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
        w('.. toctree::\n\n')
        for f in self.written_modules:
            w('   %s\n' % os.path.join(relpath,f))
        idx.close()
Shapely-1.6.4/docs/sphinxext/docscrape.py000066400000000000000000000347661323200062600204400ustar00rootroot00000000000000"""Extract reference documentation from the NumPy source tree.

"""

import inspect
import textwrap
import re
import pydoc
from StringIO import StringIO
from warnings import warn
4
class Reader(object):
    """A line-based string reader.

    """
    def __init__(self, data):
        """
        Parameters
        ----------
        data : str
           String with lines separated by '\n'.

        """
        if isinstance(data,list):
            self._str = data
        else:
            self._str = data.split('\n') # store string as list of lines

        self.reset()

    def __getitem__(self, n):
        return self._str[n]

    def reset(self):
        self._l = 0 # current line nr

    def read(self):
        if not self.eof():
            out = self[self._l]
            self._l += 1
            return out
        else:
            return ''

    def seek_next_non_empty_line(self):
        for l in self[self._l:]:
            if l.strip():
                break
            else:
                self._l += 1

    def eof(self):
        return self._l >= len(self._str)

    def read_to_condition(self, condition_func):
        start = self._l
        for line in self[start:]:
            if condition_func(line):
                return self[start:self._l]
            self._l += 1
            if self.eof():
                return self[start:self._l+1]
        return []

    def read_to_next_empty_line(self):
        self.seek_next_non_empty_line()
        def is_empty(line):
            return not line.strip()
        return self.read_to_condition(is_empty)

    def read_to_next_unindented_line(self):
        def is_unindented(line):
            return (line.strip() and (len(line.lstrip()) == len(line)))
        return self.read_to_condition(is_unindented)

    def peek(self,n=0):
        if self._l + n < len(self._str):
            return self[self._l + n]
        else:
            return ''

    def is_empty(self):
        return not ''.join(self._str).strip()


class NumpyDocString(object):
    def __init__(self,docstring):
        docstring = textwrap.dedent(docstring).split('\n')

        self._doc = Reader(docstring)
        self._parsed_data = {
            'Signature': '',
            'Summary': [''],
            'Extended Summary': [],
            'Parameters': [],
            'Returns': [],
            'Raises': [],
            'Warns': [],
            'Other Parameters': [],
            'Attributes': [],
            'Methods': [],
            'See Also': [],
            'Notes': [],
            'Warnings': [],
            'References': '',
            'Examples': '',
            'index': {}
            }

        self._parse()

    def __getitem__(self,key):
        return self._parsed_data[key]

    def __setitem__(self,key,val):
        if not self._parsed_data.has_key(key):
            warn("Unknown section %s" % key)
        else:
            self._parsed_data[key] = val

    def _is_at_section(self):
        self._doc.seek_next_non_empty_line()

        if self._doc.eof():
            return False

        l1 = self._doc.peek().strip()  # e.g. Parameters

        if l1.startswith('.. index::'):
            return True

        l2 = self._doc.peek(1).strip() #    ---------- or ==========
        return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))

    def _strip(self,doc):
        i = 0
        j = 0
        for i,line in enumerate(doc):
            if line.strip(): break

        for j,line in enumerate(doc[::-1]):
            if line.strip(): break

        return doc[i:len(doc)-j]

    def _read_to_next_section(self):
        section = self._doc.read_to_next_empty_line()

        while not self._is_at_section() and not self._doc.eof():
            if not self._doc.peek(-1).strip(): # previous line was empty
                section += ['']

            section += self._doc.read_to_next_empty_line()

        return section

    def _read_sections(self):
        while not self._doc.eof():
            data = self._read_to_next_section()
            name = data[0].strip()

            if name.startswith('..'): # index section
                yield name, data[1:]
            elif len(data) < 2:
                yield StopIteration
            else:
                yield name, self._strip(data[2:])

    def _parse_param_list(self,content):
        r = Reader(content)
        params = []
        while not r.eof():
            header = r.read().strip()
            if ' : ' in header:
                arg_name, arg_type = header.split(' : ')[:2]
            else:
                arg_name, arg_type = header, ''

            desc = r.read_to_next_unindented_line()
            desc = dedent_lines(desc)

            params.append((arg_name,arg_type,desc))

        return params

    
    _name_rgx = re.compile(r"^\s*(:(?P\w+):`(?P[a-zA-Z0-9_.-]+)`|"
                           r" (?P[a-zA-Z0-9_.-]+))\s*", re.X)
    def _parse_see_also(self, content):
        """
        func_name : Descriptive text
            continued text
        another_func_name : Descriptive text
        func_name1, func_name2, :meth:`func_name`, func_name3

        """
        items = []

        def parse_item_name(text):
            """Match ':role:`name`' or 'name'"""
            m = self._name_rgx.match(text)
            if m:
                g = m.groups()
                if g[1] is None:
                    return g[3], None
                else:
                    return g[2], g[1]
            raise ValueError("%s is not a item name" % text)

        def push_item(name, rest):
            if not name:
                return
            name, role = parse_item_name(name)
            items.append((name, list(rest), role))
            del rest[:]

        current_func = None
        rest = []
        
        for line in content:
            if not line.strip(): continue

            m = self._name_rgx.match(line)
            if m and line[m.end():].strip().startswith(':'):
                push_item(current_func, rest)
                current_func, line = line[:m.end()], line[m.end():]
                rest = [line.split(':', 1)[1].strip()]
                if not rest[0]:
                    rest = []
            elif not line.startswith(' '):
                push_item(current_func, rest)
                current_func = None
                if ',' in line:
                    for func in line.split(','):
                        push_item(func, [])
                elif line.strip():
                    current_func = line
            elif current_func is not None:
                rest.append(line.strip())
        push_item(current_func, rest)
        return items

    def _parse_index(self, section, content):
        """
        .. index: default
           :refguide: something, else, and more

        """
        def strip_each_in(lst):
            return [s.strip() for s in lst]

        out = {}
        section = section.split('::')
        if len(section) > 1:
            out['default'] = strip_each_in(section[1].split(','))[0]
        for line in content:
            line = line.split(':')
            if len(line) > 2:
                out[line[1]] = strip_each_in(line[2].split(','))
        return out
    
    def _parse_summary(self):
        """Grab signature (if given) and summary"""
        if self._is_at_section():
            return

        summary = self._doc.read_to_next_empty_line()
        summary_str = " ".join([s.strip() for s in summary]).strip()
        if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str):
            self['Signature'] = summary_str
            if not self._is_at_section():
                self['Summary'] = self._doc.read_to_next_empty_line()
        else:
            self['Summary'] = summary

        if not self._is_at_section():
            self['Extended Summary'] = self._read_to_next_section()
    
    def _parse(self):
        self._doc.reset()
        self._parse_summary()

        for (section,content) in self._read_sections():
            if not section.startswith('..'):
                section = ' '.join([s.capitalize() for s in section.split(' ')])
            if section in ('Parameters', 'Attributes', 'Methods',
                           'Returns', 'Raises', 'Warns'):
                self[section] = self._parse_param_list(content)
            elif section.startswith('.. index::'):
                self['index'] = self._parse_index(section, content)
            elif section == 'See Also':
                self['See Also'] = self._parse_see_also(content)
            else:
                self[section] = content

    # string conversion routines

    def _str_header(self, name, symbol='-'):
        return [name, len(name)*symbol]

    def _str_indent(self, doc, indent=4):
        out = []
        for line in doc:
            out += [' '*indent + line]
        return out

    def _str_signature(self):
        if self['Signature']:
            return [self['Signature'].replace('*','\*')] + ['']
        else:
            return ['']

    def _str_summary(self):
        if self['Summary']:
            return self['Summary'] + ['']
        else:
            return []

    def _str_extended_summary(self):
        if self['Extended Summary']:
            return self['Extended Summary'] + ['']
        else:
            return []

    def _str_param_list(self, name):
        out = []
        if self[name]:
            out += self._str_header(name)
            for param,param_type,desc in self[name]:
                out += ['%s : %s' % (param, param_type)]
                out += self._str_indent(desc)
            out += ['']
        return out

    def _str_section(self, name):
        out = []
        if self[name]:
            out += self._str_header(name)
            out += self[name]
            out += ['']
        return out

    def _str_see_also(self, func_role):
        if not self['See Also']: return []
        out = []
        out += self._str_header("See Also")
        last_had_desc = True
        for func, desc, role in self['See Also']:
            if role:
                link = ':%s:`%s`' % (role, func)
            elif func_role:
                link = ':%s:`%s`' % (func_role, func)
            else:
                link = "`%s`_" % func
            if desc or last_had_desc:
                out += ['']
                out += [link]
            else:
                out[-1] += ", %s" % link
            if desc:
                out += self._str_indent([' '.join(desc)])
                last_had_desc = True
            else:
                last_had_desc = False
        out += ['']
        return out

    def _str_index(self):
        idx = self['index']
        out = []
        out += ['.. index:: %s' % idx.get('default','')]
        for section, references in idx.iteritems():
            if section == 'default':
                continue
            out += ['   :%s: %s' % (section, ', '.join(references))]
        return out

    def __str__(self, func_role=''):
        out = []
        out += self._str_signature()
        out += self._str_summary()
        out += self._str_extended_summary()
        for param_list in ('Parameters','Returns','Raises'):
            out += self._str_param_list(param_list)
        out += self._str_section('Warnings')
        out += self._str_see_also(func_role)
        for s in ('Notes','References','Examples'):
            out += self._str_section(s)
        out += self._str_index()
        return '\n'.join(out)


def indent(str,indent=4):
    indent_str = ' '*indent
    if str is None:
        return indent_str
    lines = str.split('\n')
    return '\n'.join(indent_str + l for l in lines)

def dedent_lines(lines):
    """Deindent a list of lines maximally"""
    return textwrap.dedent("\n".join(lines)).split("\n")

def header(text, style='-'):
    return text + '\n' + style*len(text) + '\n'


class FunctionDoc(NumpyDocString):
    def __init__(self, func, role='func', doc=None):
        self._f = func
        self._role = role # e.g. "func" or "meth"
        if doc is None:
            doc = inspect.getdoc(func) or ''
        try:
            NumpyDocString.__init__(self, doc)
        except ValueError, e:
            print '*'*78
            print "ERROR: '%s' while parsing `%s`" % (e, self._f)
            print '*'*78
            #print "Docstring follows:"
            #print doclines
            #print '='*78

        if not self['Signature']:
            func, func_name = self.get_func()
            try:
                # try to read signature
                argspec = inspect.getargspec(func)
                argspec = inspect.formatargspec(*argspec)
                argspec = argspec.replace('*','\*')
                signature = '%s%s' % (func_name, argspec)
            except TypeError, e:
                signature = '%s()' % func_name
            self['Signature'] = signature

    def get_func(self):
        func_name = getattr(self._f, '__name__', self.__class__.__name__)
        if inspect.isclass(self._f):
            func = getattr(self._f, '__call__', self._f.__init__)
        else:
            func = self._f
        return func, func_name
            
    def __str__(self):
        out = ''

        func, func_name = self.get_func()
        signature = self['Signature'].replace('*', '\*')

        roles = {'func': 'function',
                 'meth': 'method'}

        if self._role:
            if not roles.has_key(self._role):
                print "Warning: invalid role %s" % self._role
            out += '.. %s:: %s\n    \n\n' % (roles.get(self._role,''),
                                             func_name)

        out += super(FunctionDoc, self).__str__(func_role=self._role)
        return out


class ClassDoc(NumpyDocString):
    def __init__(self,cls,modulename='',func_doc=FunctionDoc,doc=None):
        if not inspect.isclass(cls):
            raise ValueError("Initialise using a class. Got %r" % cls)
        self._cls = cls

        if modulename and not modulename.endswith('.'):
            modulename += '.'
        self._mod = modulename
        self._name = cls.__name__
        self._func_doc = func_doc

        if doc is None:
            doc = pydoc.getdoc(cls)

        NumpyDocString.__init__(self, doc)

    @property
    def methods(self):
        return [name for name,func in inspect.getmembers(self._cls)
                if not name.startswith('_') and callable(func)]

    def __str__(self):
        out = ''
        out += super(ClassDoc, self).__str__()
        out += "\n\n"

        #for m in self.methods:
        #    print "Parsing `%s`" % m
        #    out += str(self._func_doc(getattr(self._cls,m), 'meth')) + '\n\n'
        #    out += '.. index::\n   single: %s; %s\n\n' % (self._name, m)

        return out


Shapely-1.6.4/docs/sphinxext/docscrape_sphinx.py000066400000000000000000000102071323200062600220110ustar00rootroot00000000000000import re, inspect, textwrap, pydoc
from docscrape import NumpyDocString, FunctionDoc, ClassDoc

class SphinxDocString(NumpyDocString):
    # string conversion routines
    def _str_header(self, name, symbol='`'):
        return ['.. rubric:: ' + name, '']

    def _str_field_list(self, name):
        return [':' + name + ':']

    def _str_indent(self, doc, indent=4):
        out = []
        for line in doc:
            out += [' '*indent + line]
        return out

    def _str_signature(self):
        return ['']
        if self['Signature']:
            return ['``%s``' % self['Signature']] + ['']
        else:
            return ['']

    def _str_summary(self):
        return self['Summary'] + ['']

    def _str_extended_summary(self):
        return self['Extended Summary'] + ['']

    def _str_param_list(self, name):
        out = []
        if self[name]:
            out += self._str_field_list(name)
            out += ['']
            for param,param_type,desc in self[name]:
                out += self._str_indent(['**%s** : %s' % (param.strip(),
                                                          param_type)])
                out += ['']
                out += self._str_indent(desc,8)
                out += ['']
        return out

    def _str_section(self, name):
        out = []
        if self[name]:
            out += self._str_header(name)
            out += ['']
            content = textwrap.dedent("\n".join(self[name])).split("\n")
            out += content
            out += ['']
        return out

    def _str_see_also(self, func_role):
        out = []
        if self['See Also']:
            see_also = super(SphinxDocString, self)._str_see_also(func_role)
            out = ['.. seealso::', '']
            out += self._str_indent(see_also[2:])
        return out

    def _str_warnings(self):
        out = []
        if self['Warnings']:
            out = ['.. warning::', '']
            out += self._str_indent(self['Warnings'])
        return out

    def _str_index(self):
        idx = self['index']
        out = []
        if len(idx) == 0:
            return out

        out += ['.. index:: %s' % idx.get('default','')]
        for section, references in idx.iteritems():
            if section == 'default':
                continue
            elif section == 'refguide':
                out += ['   single: %s' % (', '.join(references))]
            else:
                out += ['   %s: %s' % (section, ','.join(references))]
        return out

    def _str_references(self):
        out = []
        if self['References']:
            out += self._str_header('References')
            if isinstance(self['References'], str):
                self['References'] = [self['References']]
            out.extend(self['References'])
            out += ['']
        return out

    def __str__(self, indent=0, func_role="obj"):
        out = []
        out += self._str_signature()
        out += self._str_index() + ['']
        out += self._str_summary()
        out += self._str_extended_summary()
        for param_list in ('Parameters', 'Attributes', 'Methods',
                           'Returns','Raises'):
            out += self._str_param_list(param_list)
        out += self._str_warnings()
        out += self._str_see_also(func_role)
        out += self._str_section('Notes')
        out += self._str_references()
        out += self._str_section('Examples')
        out = self._str_indent(out,indent)
        return '\n'.join(out)

class SphinxFunctionDoc(SphinxDocString, FunctionDoc):
    pass

class SphinxClassDoc(SphinxDocString, ClassDoc):
    pass

def get_doc_object(obj, what=None, doc=None):
    if what is None:
        if inspect.isclass(obj):
            what = 'class'
        elif inspect.ismodule(obj):
            what = 'module'
        elif callable(obj):
            what = 'function'
        else:
            what = 'object'
    if what == 'class':
        return SphinxClassDoc(obj, '', func_doc=SphinxFunctionDoc, doc=doc)
    elif what in ('function', 'method'):
        return SphinxFunctionDoc(obj, '', doc=doc)
    else:
        if doc is None:
            doc = pydoc.getdoc(obj)
        return SphinxDocString(doc)

Shapely-1.6.4/docs/sphinxext/inheritance_diagram.py000066400000000000000000000325201323200062600224340ustar00rootroot00000000000000"""
Defines a docutils directive for inserting inheritance diagrams.

Provide the directive with one or more classes or modules (separated
by whitespace).  For modules, all of the classes in that module will
be used.

Example::

   Given the following classes:

   class A: pass
   class B(A): pass
   class C(A): pass
   class D(B, C): pass
   class E(B): pass

   .. inheritance-diagram: D E

   Produces a graph like the following:

               A
              / \
             B   C
            / \ /
           E   D

The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX.
"""

import inspect
import os
import re
import subprocess
try:
    from hashlib import md5
except ImportError:
    from md5 import md5

from docutils.nodes import Body, Element
from docutils.parsers.rst import directives
from sphinx.roles import xfileref_role

def my_import(name):
    """Module importer - taken from the python documentation.

    This function allows importing names with dots in them."""
    
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

class DotException(Exception):
    pass

class InheritanceGraph(object):
    """
    Given a list of classes, determines the set of classes that
    they inherit from all the way to the root "object", and then
    is able to generate a graphviz dot graph from them.
    """
    def __init__(self, class_names, show_builtins=False):
        """
        *class_names* is a list of child classes to show bases from.

        If *show_builtins* is True, then Python builtins will be shown
        in the graph.
        """
        self.class_names = class_names
        self.classes = self._import_classes(class_names)
        self.all_classes = self._all_classes(self.classes)
        if len(self.all_classes) == 0:
            raise ValueError("No classes found for inheritance diagram")
        self.show_builtins = show_builtins

    py_sig_re = re.compile(r'''^([\w.]*\.)?    # class names
                           (\w+)  \s* $        # optionally arguments
                           ''', re.VERBOSE)

    def _import_class_or_module(self, name):
        """
        Import a class using its fully-qualified *name*.
        """
        try:
            path, base = self.py_sig_re.match(name).groups()
        except:
            raise ValueError(
                "Invalid class or module '%s' specified for inheritance diagram" % name)
        fullname = (path or '') + base
        path = (path and path.rstrip('.'))
        if not path:
            path = base
        try:
            module = __import__(path, None, None, [])
            # We must do an import of the fully qualified name.  Otherwise if a
            # subpackage 'a.b' is requested where 'import a' does NOT provide
            # 'a.b' automatically, then 'a.b' will not be found below.  This
            # second call will force the equivalent of 'import a.b' to happen
            # after the top-level import above.
            my_import(fullname)
            
        except ImportError:
            raise ValueError(
                "Could not import class or module '%s' specified for inheritance diagram" % name)

        try:
            todoc = module
            for comp in fullname.split('.')[1:]:
                todoc = getattr(todoc, comp)
        except AttributeError:
            raise ValueError(
                "Could not find class or module '%s' specified for inheritance diagram" % name)

        # If a class, just return it
        if inspect.isclass(todoc):
            return [todoc]
        elif inspect.ismodule(todoc):
            classes = []
            for cls in todoc.__dict__.values():
                if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
                    classes.append(cls)
            return classes
        raise ValueError(
            "'%s' does not resolve to a class or module" % name)

    def _import_classes(self, class_names):
        """
        Import a list of classes.
        """
        classes = []
        for name in class_names:
            classes.extend(self._import_class_or_module(name))
        return classes

    def _all_classes(self, classes):
        """
        Return a list of all classes that are ancestors of *classes*.
        """
        all_classes = {}

        def recurse(cls):
            all_classes[cls] = None
            for c in cls.__bases__:
                if c not in all_classes:
                    recurse(c)

        for cls in classes:
            recurse(cls)

        return all_classes.keys()

    def class_name(self, cls, parts=0):
        """
        Given a class object, return a fully-qualified name.  This
        works for things I've tested in matplotlib so far, but may not
        be completely general.
        """
        module = cls.__module__
        if module == '__builtin__':
            fullname = cls.__name__
        else:
            fullname = "%s.%s" % (module, cls.__name__)
        if parts == 0:
            return fullname
        name_parts = fullname.split('.')
        return '.'.join(name_parts[-parts:])

    def get_all_class_names(self):
        """
        Get all of the class names involved in the graph.
        """
        return [self.class_name(x) for x in self.all_classes]

    # These are the default options for graphviz
    default_graph_options = {
        "rankdir": "LR",
        "size": '"8.0, 12.0"'
        }
    default_node_options = {
        "shape": "box",
        "fontsize": 10,
        "height": 0.25,
        "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",
        "style": '"setlinewidth(0.5)"'
        }
    default_edge_options = {
        "arrowsize": 0.5,
        "style": '"setlinewidth(0.5)"'
        }

    def _format_node_options(self, options):
        return ','.join(["%s=%s" % x for x in options.items()])
    def _format_graph_options(self, options):
        return ''.join(["%s=%s;\n" % x for x in options.items()])

    def generate_dot(self, fd, name, parts=0, urls={},
                     graph_options={}, node_options={},
                     edge_options={}):
        """
        Generate a graphviz dot graph from the classes that
        were passed in to __init__.

        *fd* is a Python file-like object to write to.

        *name* is the name of the graph

        *urls* is a dictionary mapping class names to http urls

        *graph_options*, *node_options*, *edge_options* are
        dictionaries containing key/value pairs to pass on as graphviz
        properties.
        """
        g_options = self.default_graph_options.copy()
        g_options.update(graph_options)
        n_options = self.default_node_options.copy()
        n_options.update(node_options)
        e_options = self.default_edge_options.copy()
        e_options.update(edge_options)

        fd.write('digraph %s {\n' % name)
        fd.write(self._format_graph_options(g_options))

        for cls in self.all_classes:
            if not self.show_builtins and cls in __builtins__.values():
                continue

            name = self.class_name(cls, parts)

            # Write the node
            this_node_options = n_options.copy()
            url = urls.get(self.class_name(cls))
            if url is not None:
                this_node_options['URL'] = '"%s"' % url
            fd.write('  "%s" [%s];\n' %
                     (name, self._format_node_options(this_node_options)))

            # Write the edges
            for base in cls.__bases__:
                if not self.show_builtins and base in __builtins__.values():
                    continue

                base_name = self.class_name(base, parts)
                fd.write('  "%s" -> "%s" [%s];\n' %
                         (base_name, name,
                          self._format_node_options(e_options)))
        fd.write('}\n')

    def run_dot(self, args, name, parts=0, urls={},
                graph_options={}, node_options={}, edge_options={}):
        """
        Run graphviz 'dot' over this graph, returning whatever 'dot'
        writes to stdout.

        *args* will be passed along as commandline arguments.

        *name* is the name of the graph

        *urls* is a dictionary mapping class names to http urls

        Raises DotException for any of the many os and
        installation-related errors that may occur.
        """
        try:
            dot = subprocess.Popen(['dot'] + list(args),
                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                                   close_fds=True)
        except OSError:
            raise DotException("Could not execute 'dot'.  Are you sure you have 'graphviz' installed?")
        except ValueError:
            raise DotException("'dot' called with invalid arguments")
        except:
            raise DotException("Unexpected error calling 'dot'")

        self.generate_dot(dot.stdin, name, parts, urls, graph_options,
                          node_options, edge_options)
        dot.stdin.close()
        result = dot.stdout.read()
        returncode = dot.wait()
        if returncode != 0:
            raise DotException("'dot' returned the errorcode %d" % returncode)
        return result

class inheritance_diagram(Body, Element):
    """
    A docutils node to use as a placeholder for the inheritance
    diagram.
    """
    pass

def inheritance_diagram_directive(name, arguments, options, content, lineno,
                                  content_offset, block_text, state,
                                  state_machine):
    """
    Run when the inheritance_diagram directive is first encountered.
    """
    node = inheritance_diagram()

    class_names = arguments

    # Create a graph starting with the list of classes
    graph = InheritanceGraph(class_names)

    # Create xref nodes for each target of the graph's image map and
    # add them to the doc tree so that Sphinx can resolve the
    # references to real URLs later.  These nodes will eventually be
    # removed from the doctree after we're done with them.
    for name in graph.get_all_class_names():
        refnodes, x = xfileref_role(
            'class', ':class:`%s`' % name, name, 0, state)
        node.extend(refnodes)
    # Store the graph object so we can use it to generate the
    # dot file later
    node['graph'] = graph
    # Store the original content for use as a hash
    node['parts'] = options.get('parts', 0)
    node['content'] = " ".join(class_names)
    return [node]

def get_graph_hash(node):
    return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]

def html_output_graph(self, node):
    """
    Output the graph for HTML.  This will insert a PNG with clickable
    image map.
    """
    graph = node['graph']
    parts = node['parts']

    graph_hash = get_graph_hash(node)
    name = "inheritance%s" % graph_hash
    path = '_images'
    dest_path = os.path.join(setup.app.builder.outdir, path)
    if not os.path.exists(dest_path):
        os.makedirs(dest_path)
    png_path = os.path.join(dest_path, name + ".png")
    path = setup.app.builder.imgpath

    # Create a mapping from fully-qualified class names to URLs.
    urls = {}
    for child in node:
        if child.get('refuri') is not None:
            urls[child['reftitle']] = child.get('refuri')
        elif child.get('refid') is not None:
            urls[child['reftitle']] = '#' + child.get('refid')

    # These arguments to dot will save a PNG file to disk and write
    # an HTML image map to stdout.
    image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'],
                              name, parts, urls)
    return ('%s' %
            (path, name, name, image_map))

def latex_output_graph(self, node):
    """
    Output the graph for LaTeX.  This will insert a PDF.
    """
    graph = node['graph']
    parts = node['parts']

    graph_hash = get_graph_hash(node)
    name = "inheritance%s" % graph_hash
    dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images'))
    if not os.path.exists(dest_path):
        os.makedirs(dest_path)
    pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf"))

    graph.run_dot(['-Tpdf', '-o%s' % pdf_path],
                  name, parts, graph_options={'size': '"6.0,6.0"'})
    return '\n\\includegraphics{%s}\n\n' % pdf_path

def visit_inheritance_diagram(inner_func):
    """
    This is just a wrapper around html/latex_output_graph to make it
    easier to handle errors and insert warnings.
    """
    def visitor(self, node):
        try:
            content = inner_func(self, node)
        except DotException, e:
            # Insert the exception as a warning in the document
            warning = self.document.reporter.warning(str(e), line=node.line)
            warning.parent = node
            node.children = [warning]
        else:
            source = self.document.attributes['source']
            self.body.append(content)
            node.children = []
    return visitor

def do_nothing(self, node):
    pass

def setup(app):
    setup.app = app
    setup.confdir = app.confdir

    app.add_node(
        inheritance_diagram,
        latex=(visit_inheritance_diagram(latex_output_graph), do_nothing),
        html=(visit_inheritance_diagram(html_output_graph), do_nothing))
    app.add_directive(
        'inheritance-diagram', inheritance_diagram_directive,
        False, (1, 100, 0), parts = directives.nonnegative_int)
Shapely-1.6.4/docs/sphinxext/ipython_console_highlighting.py000066400000000000000000000101271323200062600244170ustar00rootroot00000000000000"""reST directive for syntax-highlighting ipython interactive sessions.

XXX - See what improvements can be made based on the new (as of Sept 2009)
'pycon' lexer for the python console.  At the very least it will give better
highlighted tracebacks.
"""

#-----------------------------------------------------------------------------
# Needed modules

# Standard library
import re

# Third party
from pygments.lexer import Lexer, do_insertions
from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, 
                                   PythonTracebackLexer)
from pygments.token import Comment, Generic

from sphinx import highlighting

#-----------------------------------------------------------------------------
# Global constants
line_re = re.compile('.*?\n')

#-----------------------------------------------------------------------------
# Code begins - classes and functions

class IPythonConsoleLexer(Lexer):
    """
    For IPython console output or doctests, such as:

    .. sourcecode:: ipython

      In [1]: a = 'foo'

      In [2]: a
      Out[2]: 'foo'

      In [3]: print a
      foo

      In [4]: 1 / 0

    Notes:

      - Tracebacks are not currently supported.

      - It assumes the default IPython prompts, not customized ones.
    """
    
    name = 'IPython console session'
    aliases = ['ipython']
    mimetypes = ['text/x-ipython-console']
    input_prompt = re.compile("(In \[[0-9]+\]: )|(   \.\.\.+:)")
    output_prompt = re.compile("(Out\[[0-9]+\]: )|(   \.\.\.+:)")
    continue_prompt = re.compile("   \.\.\.+:")
    tb_start = re.compile("\-+")

    def get_tokens_unprocessed(self, text):
        pylexer = PythonLexer(**self.options)
        tblexer = PythonTracebackLexer(**self.options)

        curcode = ''
        insertions = []
        for match in line_re.finditer(text):
            line = match.group()
            input_prompt = self.input_prompt.match(line)
            continue_prompt = self.continue_prompt.match(line.rstrip())
            output_prompt = self.output_prompt.match(line)
            if line.startswith("#"):
                insertions.append((len(curcode),
                                   [(0, Comment, line)]))
            elif input_prompt is not None:
                insertions.append((len(curcode),
                                   [(0, Generic.Prompt, input_prompt.group())]))
                curcode += line[input_prompt.end():]
            elif continue_prompt is not None:
                insertions.append((len(curcode),
                                   [(0, Generic.Prompt, continue_prompt.group())]))
                curcode += line[continue_prompt.end():]
            elif output_prompt is not None:
                # Use the 'error' token for output.  We should probably make
                # our own token, but error is typicaly in a bright color like
                # red, so it works fine for our output prompts.
                insertions.append((len(curcode),
                                   [(0, Generic.Error, output_prompt.group())]))
                curcode += line[output_prompt.end():]
            else:
                if curcode:
                    for item in do_insertions(insertions,
                                              pylexer.get_tokens_unprocessed(curcode)):
                        yield item
                        curcode = ''
                        insertions = []
                yield match.start(), Generic.Output, line
        if curcode:
            for item in do_insertions(insertions,
                                      pylexer.get_tokens_unprocessed(curcode)):
                yield item


def setup(app):
    """Setup as a sphinx extension."""

    # This is only a lexer, so adding it below to pygments appears sufficient.
    # But if somebody knows that the right API usage should be to do that via
    # sphinx, by all means fix it here.  At least having this setup.py
    # suppresses the sphinx warning we'd get without it.
    pass

#-----------------------------------------------------------------------------
# Register the extension as a valid pygments lexer
highlighting.lexers['ipython'] = IPythonConsoleLexer()
Shapely-1.6.4/docs/sphinxext/numpydoc.py000066400000000000000000000077321323200062600203240ustar00rootroot00000000000000"""
========
numpydoc
========

Sphinx extension that handles docstrings in the Numpy standard format. [1]

It will:

- Convert Parameters etc. sections to field lists.
- Convert See Also section to a See also entry.
- Renumber references.
- Extract the signature from the docstring, if it can't be determined otherwise.

.. [1] http://projects.scipy.org/scipy/numpy/wiki/CodingStyleGuidelines#docstring-standard

"""

import os, re, pydoc
from docscrape_sphinx import get_doc_object, SphinxDocString
import inspect

def mangle_docstrings(app, what, name, obj, options, lines,
                      reference_offset=[0]):
    if what == 'module':
        # Strip top title
        title_re = re.compile(r'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
                              re.I|re.S)
        lines[:] = title_re.sub('', "\n".join(lines)).split("\n")
    else:
        doc = get_doc_object(obj, what, "\n".join(lines))
        lines[:] = str(doc).split("\n")

    if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
           obj.__name__:
        if hasattr(obj, '__module__'):
            v = dict(full_name="%s.%s" % (obj.__module__, obj.__name__))
        else:
            v = dict(full_name=obj.__name__)
        lines += ['', '.. htmlonly::', '']
        lines += ['    %s' % x for x in
                  (app.config.numpydoc_edit_link % v).split("\n")]

    # replace reference numbers so that there are no duplicates
    references = []
    for l in lines:
        l = l.strip()
        if l.startswith('.. ['):
            try:
                references.append(int(l[len('.. ['):l.index(']')]))
            except ValueError:
                print "WARNING: invalid reference in %s docstring" % name

    # Start renaming from the biggest number, otherwise we may
    # overwrite references.
    references.sort()
    if references:
        for i, line in enumerate(lines):
            for r in references:
                new_r = reference_offset[0] + r
                lines[i] = lines[i].replace('[%d]_' % r,
                                            '[%d]_' % new_r)
                lines[i] = lines[i].replace('.. [%d]' % r,
                                            '.. [%d]' % new_r)

    reference_offset[0] += len(references)

def mangle_signature(app, what, name, obj, options, sig, retann):
    # Do not try to inspect classes that don't define `__init__`
    if (inspect.isclass(obj) and
        'initializes x; see ' in pydoc.getdoc(obj.__init__)):
        return '', ''

    if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
    if not hasattr(obj, '__doc__'): return

    doc = SphinxDocString(pydoc.getdoc(obj))
    if doc['Signature']:
        sig = re.sub("^[^(]*", "", doc['Signature'])
        return sig, ''

def initialize(app):
    try:
        app.connect('autodoc-process-signature', mangle_signature)
    except:
        monkeypatch_sphinx_ext_autodoc()

def setup(app, get_doc_object_=get_doc_object):
    global get_doc_object
    get_doc_object = get_doc_object_
    
    app.connect('autodoc-process-docstring', mangle_docstrings)
    app.connect('builder-inited', initialize)
    app.add_config_value('numpydoc_edit_link', None, True)

#------------------------------------------------------------------------------
# Monkeypatch sphinx.ext.autodoc to accept argspecless autodocs (Sphinx < 0.5)
#------------------------------------------------------------------------------

def monkeypatch_sphinx_ext_autodoc():
    global _original_format_signature
    import sphinx.ext.autodoc

    if sphinx.ext.autodoc.format_signature is our_format_signature:
        return

    print "[numpydoc] Monkeypatching sphinx.ext.autodoc ..."
    _original_format_signature = sphinx.ext.autodoc.format_signature
    sphinx.ext.autodoc.format_signature = our_format_signature

def our_format_signature(what, obj):
    r = mangle_signature(None, what, None, obj, None, None, None)
    if r is not None:
        return r[0]
    else:
        return _original_format_signature(what, obj)
Shapely-1.6.4/environment.yml000066400000000000000000000002511323200062600162060ustar00rootroot00000000000000name: _shapely
channels:
    - defaults
    - conda-forge
dependencies:
    - python>=3.5
    - cython
    - descartes
    - geos>=3.3
    - matplotlib
    - numpy>=1.9
Shapely-1.6.4/readthedocs.yml000066400000000000000000000001101323200062600161210ustar00rootroot00000000000000python:
  version: 3
  pip_install: true
conda:
  file: environment.yml
Shapely-1.6.4/requirements-dev.txt000066400000000000000000000000751323200062600171630ustar00rootroot00000000000000setuptools
Numpy>=1.4.1
Cython>=0.19
descartes==1.0.1
pytest
Shapely-1.6.4/setup.cfg000066400000000000000000000001261323200062600147410ustar00rootroot00000000000000[tool:pytest]
testpaths = tests
addopts = --ignore _vendor

[bdist_wheel]
universal=1
Shapely-1.6.4/setup.py000077500000000000000000000326401323200062600146430ustar00rootroot00000000000000#!/usr/bin/env python

# Build or install Shapely distributions
#
# This script has two different uses.
#
# 1) Installing from a source distribution, whether via
#
#      ``python setup.py install``
#
#    after downloading a source distribution, or
#
#      ``pip install shapely``
#
#    on a platform for which pip cannot find a wheel. This will most
#    often be the case for Linux, since the project is not yet
#    publishing Linux wheels. This will never be the case on Windows and
#    rarely the case on OS X; both are wheels-first platforms.
#
# 2) Building distributions (source or wheel) from a repository. This
#    includes using Cython to generate C source for the speedups and
#    vectorize modules from Shapely's .pyx files.
#
# On import, Shapely loads a GEOS shared library. GEOS is a run time
# requirement. Additionally, the speedups and vectorized C extension
# modules need GEOS headers and libraries to be built. Shapely versions
# >=1.3 require GEOS >= 3.3.
#
# For the first use case (see 1, above), we aim to treat GEOS as if it
# were a Python requirement listed in ``install_requires``. That is, in
# an environment with Shapely 1.2.x and GEOS 3.2, the command ``pip
# install shapely >=1.3 --no-use-wheel`` (whether wheels are explicitly
# opted against or are not published for the platform) should fail with
# a warning and advice to upgrade GEOS to >=3.3.
#
# In case 1, the environment's GEOS version is determined by executing
# the geos-config script. If the GEOS version returned by that script is
# incompatible with the Shapely source distribution or no geos-config
# script can be found, this setup script will fail.
#
# For the second use case (see 2, distribution building, above), we
# allow the requirements to be loosened. If this script finds that the
# environment variable NO_GEOS_CHECK is set, geos-config will not be
# executed and no attempt will be made to enforce requirements as in the
# second case.
#
# For both cases, a geos-config not in the environment's $PATH may be
# used by setting the environment variable GEOS_CONFIG to the path to
# a geos-config script.
#
# NB: within this setup scripts, software versions are evaluated according
# to https://www.python.org/dev/peps/pep-0440/.

import errno
import glob
import itertools as it
import logging
import os
import platform
import re
import shutil
import subprocess
import sys
try:
    # If possible, use setuptools
    from setuptools import setup
    from setuptools.extension import Extension
    from setuptools.command.build_ext import build_ext as distutils_build_ext
except ImportError:
    from distutils.core import setup
    from distutils.extension import Extension
    from distutils.command.build_ext import build_ext as distutils_build_ext
from distutils.errors import CCompilerError, DistutilsExecError, \
    DistutilsPlatformError

from _vendor.packaging.version import Version

# Get geos_version from GEOS dynamic library, which depends on
# GEOS_LIBRARY_PATH and/or GEOS_CONFIG environment variables
from shapely._buildcfg import geos_version_string, geos_version, \
        geos_config, get_geos_config

logging.basicConfig()
log = logging.getLogger(__file__)

# python -W all setup.py ...
if 'all' in sys.warnoptions:
    log.level = logging.DEBUG


class GEOSConfig(object):
    """Interface to config options from the `geos-config` utility
    """

    def __init__(self, cmd):
        self.cmd = cmd

    def get(self, option):
        try:
            stdout, stderr = subprocess.Popen(
                [self.cmd, option],
                stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
        except OSError as ex:
            # e.g., [Errno 2] No such file or directory
            raise OSError("Could not find geos-config script")
        if stderr and not stdout:
            raise ValueError(stderr.strip())
        if sys.version_info[0] >= 3:
            result = stdout.decode('ascii').strip()
        else:
            result = stdout.strip()
        log.debug('%s %s: %r', self.cmd, option, result)
        return result

    def version(self):
        match = re.match(r'(\d+)\.(\d+)\.(\d+)', self.get('--version').strip())
        return tuple(map(int, match.groups()))

# Get the version from the shapely module.
shapely_version = None
with open('shapely/__init__.py', 'r') as fp:
    for line in fp:
        if line.startswith("__version__"):
            shapely_version = Version(
                line.split("=")[1].strip().strip("\"'"))
            break

if not shapely_version:
    raise ValueError("Could not determine Shapely's version")

# Allow GEOS_CONFIG to be bypassed in favor of CFLAGS and LDFLAGS
# vars set by build environment.
if os.environ.get('NO_GEOS_CONFIG'):
    geos_config = None
else:
    geos_config = GEOSConfig(os.environ.get('GEOS_CONFIG', 'geos-config'))

# Fail installation if the GEOS shared library does not meet the minimum
# version. We ship it with Shapely for Windows, so no need to check on
# that platform.
geos_version = None
if geos_config and not os.environ.get('NO_GEOS_CHECK') or sys.platform == 'win32':
    try:
        log.info(
            "Shapely >= 1.3 requires GEOS >= 3.3. "
            "Checking for GEOS version...")
        geos_version = geos_config.version()
        log.info("Found GEOS version: %s", geos_version)
        if (set(sys.argv).intersection(['install', 'build', 'build_ext']) and
                shapely_version >= Version("1.3") and geos_version < (3, 3)):
            log.critical(
                "Shapely >= 1.3 requires GEOS >= 3.3. "
                "Install GEOS 3.3+ and reinstall Shapely.")
            sys.exit(1)
    except OSError as exc:
        log.warn(
            "Failed to determine system's GEOS version: %s. "
            "Installation continuing. GEOS version will be "
            "checked on import of shapely.", exc)

# Handle UTF-8 encoding of certain text files.
open_kwds = {}
if sys.version_info >= (3,):
    open_kwds['encoding'] = 'utf-8'

with open('VERSION.txt', 'w', **open_kwds) as fp:
    fp.write(str(shapely_version))

with open('README.rst', 'r', **open_kwds) as fp:
    readme = fp.read()

with open('CREDITS.txt', 'r', **open_kwds) as fp:
    credits = fp.read()

with open('CHANGES.txt', 'r', **open_kwds) as fp:
    changes = fp.read()

long_description = readme + '\n\n' + credits + '\n\n' + changes

extra_reqs = {
    'test': ['pytest', 'pytest-cov'],
    'vectorized': ['numpy']}

extra_reqs['all'] = list(it.chain.from_iterable(extra_reqs.values()))

# Make a dict of setup arguments. Some items will be updated as
# the script progresses.
setup_args = dict(
    name                = 'Shapely',
    version             = str(shapely_version),
    requires            = ['Python (>=2.6)', 'libgeos_c (>=3.3)'],
    description         = 'Geometric objects, predicates, and operations',
    license             = 'BSD',
    keywords            = 'geometry topology gis',
    author              = 'Sean Gillies',
    author_email        = 'sean.gillies@gmail.com',
    maintainer          = 'Sean Gillies',
    maintainer_email    = 'sean.gillies@gmail.com',
    url                 = 'https://github.com/Toblerity/Shapely',
    long_description    = long_description,
    packages            = [
        'shapely',
        'shapely.geometry',
        'shapely.algorithms',
        'shapely.examples',
        'shapely.speedups',
        'shapely.vectorized',
    ],
    classifiers         = [
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: Developers',
        'Intended Audience :: Science/Research',
        'License :: OSI Approved :: BSD License',
        'Operating System :: OS Independent',
        'Programming Language :: Python :: 2.6',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Topic :: Scientific/Engineering :: GIS',
    ],
    cmdclass           = {},
    extras_require     = extra_reqs,
    package_data={
        'shapely': ['shapely/_geos.pxi']},
    include_package_data=True
)

# Add DLLs for Windows.
if sys.platform == 'win32':
    try:
        os.mkdir('shapely/DLLs')
    except OSError as ex:
        if ex.errno != errno.EEXIST:
            raise
    if '(AMD64)' in sys.version:
        for dll in glob.glob('DLLs_AMD64_VC9/*.dll'):
            shutil.copy(dll, 'shapely/DLLs')
    elif sys.version_info[0:2] == (2, 5):
        for dll in glob.glob('DLLs_x86_VC7/*.dll'):
            shutil.copy(dll, 'shapely/DLLs')
    else:
        for dll in glob.glob('DLLs_x86_VC9/*.dll'):
            shutil.copy(dll, 'shapely/DLLs')
    setup_args['package_data']['shapely'].append('shapely/DLLs/*.dll')

# Prepare build opts and args for the speedups extension module.
include_dirs = []
library_dirs = []
libraries = []
extra_link_args = []

# If NO_GEOS_CONFIG is set in the environment, geos-config will not
# be called and CFLAGS and LDFLAGS environment variables must be set
# instead like
#
# CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib -lgeos_c"
#
# Or, equivalently:
#
# CFLAGS="$(geos-config --cflags)" LDFLAGS="$(geos-config --clibs)"

if geos_version and geos_config:
    # Collect other options from GEOS configuration.
    for item in geos_config.get('--cflags').split():
        if item.startswith("-I"):
            include_dirs.extend(item[2:].split(":"))
    for item in geos_config.get('--clibs').split():
        if item.startswith("-L"):
            library_dirs.extend(item[2:].split(":"))
        elif item.startswith("-l"):
            libraries.append(item[2:])
        else:
            # e.g. -framework GEOS
            extra_link_args.append(item)


# Optional compilation of speedups
# setuptools stuff from Bob Ippolito's simplejson project
if sys.platform == 'win32' and sys.version_info > (2, 6):
    # 2.6's distutils.msvc9compiler can raise an IOError when failing to
    # find the compiler
    ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError,
                  IOError)
else:
    ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)


class BuildFailed(Exception):
    pass


def construct_build_ext(build_ext):
    class WrappedBuildExt(build_ext):
        # This class allows C extension building to fail.

        def run(self):
            try:
                build_ext.run(self)
            except DistutilsPlatformError as x:
                raise BuildFailed(x)

        def build_extension(self, ext):
            try:
                build_ext.build_extension(self, ext)
            except ext_errors as x:
                raise BuildFailed(x)

    return WrappedBuildExt

if (hasattr(platform, 'python_implementation')
        and platform.python_implementation() == 'PyPy'):
    # python_implementation is only available since 2.6
    ext_modules = []
    libraries = []

if os.path.exists("MANIFEST.in"):
    pyx_file = "shapely/speedups/_speedups.pyx"
    c_file = "shapely/speedups/_speedups.c"

    force_cython = False
    if 'sdist' in sys.argv:
        force_cython = True

    try:
        if (force_cython or not os.path.exists(c_file)
                or os.path.getmtime(pyx_file) > os.path.getmtime(c_file)):
            log.info("Updating C extension with Cython.")
            subprocess.check_call(["cython", "shapely/speedups/_speedups.pyx"])
    except (subprocess.CalledProcessError, OSError):
        log.warn("Could not (re)create C extension with Cython.")
        if force_cython:
            raise
    if not os.path.exists(c_file):
        log.warn("speedup extension not found")

ext_modules = [
    Extension("shapely.speedups._speedups", ["shapely/speedups/_speedups.c"],
        include_dirs=include_dirs, library_dirs=library_dirs,
        libraries=libraries, extra_link_args=extra_link_args)]

cmd_classes = setup_args.setdefault('cmdclass', {})

try:
    import numpy
    from Cython.Distutils import build_ext as cython_build_ext
    from distutils.extension import Extension as DistutilsExtension

    if 'build_ext' in setup_args['cmdclass']:
        raise ValueError('We need to put the Cython build_ext in '
                         'cmd_classes, but it is already defined.')
    setup_args['cmdclass']['build_ext'] = cython_build_ext

    include_dirs.append(numpy.get_include())
    libraries.append(numpy.get_include())

    ext_modules.append(DistutilsExtension(
        "shapely.vectorized._vectorized",
        sources=["shapely/vectorized/_vectorized.pyx"],
        include_dirs=include_dirs,
        library_dirs=library_dirs,
        libraries=libraries,
        extra_link_args=extra_link_args,
    ))
except ImportError:
    log.info("Numpy or Cython not available, shapely.vectorized submodule "
             "not being built.")

try:
    # try building with speedups
    existing_build_ext = setup_args['cmdclass'].\
        get('build_ext', distutils_build_ext)
    setup_args['cmdclass']['build_ext'] = \
        construct_build_ext(existing_build_ext)
    setup(ext_modules=ext_modules, **setup_args)
except BuildFailed as ex:
    BUILD_EXT_WARNING = "The C extension could not be compiled, " \
                        "speedups are not enabled."
    log.warn(ex)
    log.warn(BUILD_EXT_WARNING)
    log.warn("Failure information, if any, is above.")
    log.warn("I'm retrying the build without the C extension now.")

    # Remove any previously defined build_ext command class.
    if 'build_ext' in setup_args['cmdclass']:
        del setup_args['cmdclass']['build_ext']

    if 'build_ext' in cmd_classes:
        del cmd_classes['build_ext']

    setup(**setup_args)

    log.warn(BUILD_EXT_WARNING)
    log.info("Plain-Python installation succeeded.")
Shapely-1.6.4/shapely/000077500000000000000000000000001323200062600145665ustar00rootroot00000000000000Shapely-1.6.4/shapely/__init__.py000066400000000000000000000000261323200062600166750ustar00rootroot00000000000000__version__ = "1.6.4"
Shapely-1.6.4/shapely/_buildcfg.py000066400000000000000000000201761323200062600170640ustar00rootroot00000000000000"""
Minimal proxy to a GEOS C dynamic library, which is system dependant

Two environment variables influence this module: GEOS_LIBRARY_PATH and/or
GEOS_CONFIG.

If GEOS_LIBRARY_PATH is set to a path to a GEOS C shared library, this is
used. Otherwise GEOS_CONFIG can be set to a path to `geos-config`. If
`geos-config` is already on the PATH environment variable, then it will
be used to help better guess the name for the GEOS C dynamic library.
"""

from ctypes import CDLL, cdll, c_void_p, c_char_p
from ctypes.util import find_library
import os
import logging
import re
import subprocess
import sys


# Add message handler to this module's logger
log = logging.getLogger(__name__)
ch = logging.StreamHandler()
log.addHandler(ch)

if 'all' in sys.warnoptions:
    # show GEOS messages in console with: python -W all
    log.setLevel(logging.DEBUG)


# The main point of this module is to load a dynamic library to this variable
lgeos = None

# First try: use GEOS_LIBRARY_PATH environment variable
if 'GEOS_LIBRARY_PATH' in os.environ:
    geos_library_path = os.environ['GEOS_LIBRARY_PATH']
    try:
        lgeos = CDLL(geos_library_path)
    except:
        log.warn('cannot open shared object from GEOS_LIBRARY_PATH: %s',
                 geos_library_path)
    if lgeos:
        if hasattr(lgeos, 'GEOSversion'):
            log.debug('found GEOS C library using GEOS_LIBRARY_PATH')
        else:
            raise OSError(
                'shared object GEOS_LIBRARY_PATH is not a GEOS C library: '
                + str(geos_library_path))

# Second try: use GEOS_CONFIG environment variable
if 'GEOS_CONFIG' in os.environ:
    geos_config = os.environ['GEOS_CONFIG']
    log.debug('geos_config: %s', geos_config)
else:
    geos_config = 'geos-config'


def get_geos_config(option):
    '''Get configuration option from the `geos-config` development utility

    Path to utility is set with a module-level `geos_config` variable, which
    can be changed or unset.
    '''
    geos_config = globals().get('geos_config')
    if not geos_config or not isinstance(geos_config, str):
        raise OSError('Path to geos-config is not set')
    try:
        stdout, stderr = subprocess.Popen(
            [geos_config, option],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    except OSError as ex:
        # e.g., [Errno 2] No such file or directory
        raise OSError(
            'Could not find geos-config %r: %s' % (geos_config, ex))
    if stderr and not stdout:
        raise ValueError(stderr.strip())
    if sys.version_info[0] >= 3:
        result = stdout.decode('ascii').strip()
    else:
        result = stdout.strip()
    log.debug('%s %s: %r', geos_config, option, result)
    return result

# Now try and use the utility to load from `geos-config --clibs` with
# some magic smoke to guess the other parts of the library name
try:
    clibs = get_geos_config('--clibs')
except OSError:
    geos_config = None
if not lgeos and geos_config:
    base = ''
    name = 'geos_c'
    for item in clibs.split():
        if item.startswith("-L"):
            base = item[2:]
        elif item.startswith("-l"):
            name = item[2:]
    # Now guess the actual library name using a list of possible formats
    if sys.platform == 'win32':
        # Unlikely, since geos-config is a shell script, but you never know...
        fmts = ['{name}.dll']
    elif sys.platform == 'darwin':
        fmts = ['lib{name}.dylib', '{name}.dylib', '{name}.framework/{name}']
    elif os.name == 'posix':
        fmts = ['lib{name}.so', 'lib{name}.so.1']
    guesses = []
    for fmt in fmts:
        lib_name = fmt.format(name=name)
        geos_library_path = os.path.join(base, lib_name)
        try:
            lgeos = CDLL(geos_library_path)
            break
        except:
            guesses.append(geos_library_path)
    if lgeos:
        if hasattr(lgeos, 'GEOSversion'):
            log.debug('found GEOS C library using geos-config')
        else:
            raise OSError(
                'shared object found by geos-config is not a GEOS C library: '
                + str(geos_library_path))
    else:
        log.warn("cannot open shared object from '%s --clibs': %r",
                 geos_config, clibs)
        log.warn("there were %d guess(es) for this path:\n\t%s",
                 len(guesses), '\n\t'.join(guesses))


# Platform-specific attempts, and build `free` object

def load_dll(libname, fallbacks=None):
    lib = find_library(libname)
    dll = None
    if lib is not None:
        try:
            log.debug("Trying `CDLL(%s)`", lib)
            dll = CDLL(lib)
        except OSError:
            log.warn("Failed `CDLL(%s)`", lib)
            pass

    if not dll and fallbacks is not None:
        for name in fallbacks:
            try:
                log.debug("Trying `CDLL(%s)`", name)
                dll = CDLL(name)
            except OSError:
                # move on to the next fallback
                log.warn("Failed `CDLL(%s)`", name)
                pass

    if dll:
        log.debug("Library path: %r", lib or name)
        log.debug("DLL: %r", dll)
        return dll
    else:
        # No shared library was loaded. Raise OSError.
        raise OSError(
            "Could not find library {0} or load any of its variants {1}".format(
                libname, fallbacks or []))


if sys.platform.startswith('linux'):
    if not lgeos:
        lgeos = load_dll('geos_c',
                         fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
    free = load_dll('c').free
    free.argtypes = [c_void_p]
    free.restype = None

elif sys.platform == 'darwin':
    if not lgeos:
        if hasattr(sys, 'frozen'):
            # .app file from py2app
            alt_paths = [os.path.join(os.environ['RESOURCEPATH'],
                         '..', 'Frameworks', 'libgeos_c.dylib')]
        else:
            alt_paths = [
                # The Framework build from Kyng Chaos
                "/Library/Frameworks/GEOS.framework/Versions/Current/GEOS",
                # macports
                '/opt/local/lib/libgeos_c.dylib',
            ]
        lgeos = load_dll('geos_c', fallbacks=alt_paths)

    free = load_dll('c', fallbacks=['/usr/lib/libc.dylib']).free
    free.argtypes = [c_void_p]
    free.restype = None

elif sys.platform == 'win32':
    if not lgeos:
        try:
            egg_dlls = os.path.abspath(
                os.path.join(os.path.dirname(__file__), "DLLs"))
            wininst_dlls = os.path.abspath(os.__file__ + "../../../DLLs")
            original_path = os.environ['PATH']
            os.environ['PATH'] = "%s;%s;%s" % \
                (egg_dlls, wininst_dlls, original_path)
            lgeos = CDLL("geos_c.dll")
        except (ImportError, WindowsError, OSError):
            raise

    def free(m):
        try:
            cdll.msvcrt.free(m)
        except WindowsError:
            # TODO: http://web.archive.org/web/20070810024932/
            #     + http://trac.gispython.org/projects/PCL/ticket/149
            pass

elif sys.platform == 'sunos5':
    if not lgeos:
        lgeos = load_dll('geos_c',
                         fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
    free = CDLL('libc.so.1').free
    free.argtypes = [c_void_p]
    free.restype = None

else:  # other *nix systems
    if not lgeos:
        lgeos = load_dll('geos_c',
                         fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
    free = load_dll('c', fallbacks=['libc.so.6']).free
    free.argtypes = [c_void_p]
    free.restype = None

# TODO: what to do with 'free'? It isn't used.


def _geos_version():
    # extern const char GEOS_DLL *GEOSversion();
    GEOSversion = lgeos.GEOSversion
    GEOSversion.restype = c_char_p
    GEOSversion.argtypes = []
    # #define GEOS_CAPI_VERSION "@VERSION@-CAPI-@CAPI_VERSION@"
    geos_version_string = GEOSversion()
    if sys.version_info[0] >= 3:
        geos_version_string = geos_version_string.decode('ascii')

    res = re.findall(r'(\d+)\.(\d+)\.(\d+)', geos_version_string)
    assert len(res) == 2, res
    geos_version = tuple(int(x) for x in res[0])
    capi_version = tuple(int(x) for x in res[1])

    return geos_version_string, geos_version, capi_version

geos_version_string, geos_version, geos_capi_version = _geos_version()
Shapely-1.6.4/shapely/_geos.pxi000066400000000000000000000066031323200062600164110ustar00rootroot00000000000000# The beginnings of a Cython definition of GEOS. In the future much of this
# could be auto-generated.

from libc.stdint cimport uintptr_t


cdef extern from "geos_c.h":
    ctypedef void *GEOSContextHandle_t
    ctypedef struct GEOSGeometry
    ctypedef struct GEOSCoordSequence
    ctypedef struct GEOSPreparedGeometry

    GEOSCoordSequence *GEOSCoordSeq_create_r(GEOSContextHandle_t, unsigned int, unsigned int) nogil
    GEOSCoordSequence *GEOSGeom_getCoordSeq_r(GEOSContextHandle_t, GEOSGeometry *) nogil

    int GEOSCoordSeq_getSize_r(GEOSContextHandle_t, GEOSCoordSequence *, unsigned int *) nogil
    int GEOSCoordSeq_setX_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    int GEOSCoordSeq_setY_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    int GEOSCoordSeq_setZ_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double) nogil
    int GEOSCoordSeq_getX_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil
    int GEOSCoordSeq_getY_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil
    int GEOSCoordSeq_getZ_r(GEOSContextHandle_t, GEOSCoordSequence *, int, double *) nogil

    GEOSGeometry *GEOSGeom_createPoint_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
    GEOSGeometry *GEOSGeom_createLineString_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
    GEOSGeometry *GEOSGeom_createLinearRing_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil
    GEOSGeometry *GEOSGeom_clone_r(GEOSContextHandle_t, GEOSGeometry *) nogil
    GEOSCoordSequence *GEOSCoordSeq_clone_r(GEOSContextHandle_t, GEOSCoordSequence *) nogil

    void GEOSGeom_destroy_r(GEOSContextHandle_t, GEOSGeometry *) nogil

    char GEOSPreparedContains_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedContainsProperly_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedCoveredBy_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedCovers_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedCrosses_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedDisjoint_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedIntersects_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedOverlaps_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedTouches_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil
    char GEOSPreparedWithin_r(GEOSContextHandle_t, const GEOSPreparedGeometry*, const GEOSGeometry*) nogil

    char GEOSHasZ_r(GEOSContextHandle_t, GEOSGeometry *) nogil
    char GEOSisRing_r(GEOSContextHandle_t, GEOSGeometry *) nogil
    char GEOSisClosed_r(GEOSContextHandle_t, GEOSGeometry *) nogil


cdef GEOSContextHandle_t get_geos_context_handle():
    # Note: This requires that lgeos is defined, so needs to be imported as:
    from shapely.geos import lgeos
    cdef uintptr_t handle = lgeos.geos_handle
    return handle


cdef GEOSPreparedGeometry *geos_from_prepared(shapely_geom) except *:
    """Get the Prepared GEOS geometry pointer from the given shapely geometry."""
    cdef uintptr_t geos_geom = shapely_geom._geom
    return geos_geom
Shapely-1.6.4/shapely/affinity.py000077500000000000000000000204561323200062600167630ustar00rootroot00000000000000"""Affine transforms, both in general and specific, named transforms."""

from math import sin, cos, tan, pi

__all__ = ['affine_transform', 'rotate', 'scale', 'skew', 'translate']


def affine_transform(geom, matrix):
    """Returns a transformed geometry using an affine transformation matrix.

    The coefficient matrix is provided as a list or tuple with 6 or 12 items
    for 2D or 3D transformations, respectively.

    For 2D affine transformations, the 6 parameter matrix is::

        [a, b, d, e, xoff, yoff]

    which represents the augmented matrix::

                            / a  b xoff \ 
        [x' y' 1] = [x y 1] | d  e yoff |
                            \ 0  0   1  /

    or the equations for the transformed coordinates::

        x' = a * x + b * y + xoff
        y' = d * x + e * y + yoff

    For 3D affine transformations, the 12 parameter matrix is::

        [a, b, c, d, e, f, g, h, i, xoff, yoff, zoff]

    which represents the augmented matrix::

                                 / a  b  c xoff \ 
        [x' y' z' 1] = [x y z 1] | d  e  f yoff |
                                 | g  h  i zoff |
                                 \ 0  0  0   1  /

    or the equations for the transformed coordinates::

        x' = a * x + b * y + c * z + xoff
        y' = d * x + e * y + f * z + yoff
        z' = g * x + h * y + i * z + zoff
    """
    if geom.is_empty:
        return geom
    if len(matrix) == 6:
        ndim = 2
        a, b, d, e, xoff, yoff = matrix
        if geom.has_z:
            ndim = 3
            i = 1.0
            c = f = g = h = zoff = 0.0
            matrix = a, b, c, d, e, f, g, h, i, xoff, yoff, zoff
    elif len(matrix) == 12:
        ndim = 3
        a, b, c, d, e, f, g, h, i, xoff, yoff, zoff = matrix
        if not geom.has_z:
            ndim = 2
            matrix = a, b, d, e, xoff, yoff
    else:
        raise ValueError("'matrix' expects either 6 or 12 coefficients")

    def affine_pts(pts):
        """Internal function to yield affine transform of coordinate tuples"""
        if ndim == 2:
            for x, y in pts:
                xp = a * x + b * y + xoff
                yp = d * x + e * y + yoff
                yield (xp, yp)
        elif ndim == 3:
            for x, y, z in pts:
                xp = a * x + b * y + c * z + xoff
                yp = d * x + e * y + f * z + yoff
                zp = g * x + h * y + i * z + zoff
                yield (xp, yp, zp)

    # Process coordinates from each supported geometry type
    if geom.type in ('Point', 'LineString', 'LinearRing'):
        return type(geom)(list(affine_pts(geom.coords)))
    elif geom.type == 'Polygon':
        ring = geom.exterior
        shell = type(ring)(list(affine_pts(ring.coords)))
        holes = list(geom.interiors)
        for pos, ring in enumerate(holes):
            holes[pos] = type(ring)(list(affine_pts(ring.coords)))
        return type(geom)(shell, holes)
    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
        # Recursive call
        # TODO: fix GeometryCollection constructor
        return type(geom)([affine_transform(part, matrix)
                           for part in geom.geoms])
    else:
        raise ValueError('Type %r not recognized' % geom.type)


def interpret_origin(geom, origin, ndim):
    """Returns interpreted coordinate tuple for origin parameter.

    This is a helper function for other transform functions.

    The point of origin can be a keyword 'center' for the 2D bounding box
    center, 'centroid' for the geometry's 2D centroid, a Point object or a
    coordinate tuple (x0, y0, z0).
    """
    # get coordinate tuple from 'origin' from keyword or Point type
    if origin == 'center':
        # bounding box center
        minx, miny, maxx, maxy = geom.bounds
        origin = ((maxx + minx)/2.0, (maxy + miny)/2.0)
    elif origin == 'centroid':
        origin = geom.centroid.coords[0]
    elif isinstance(origin, str):
        raise ValueError("'origin' keyword %r is not recognized" % origin)
    elif hasattr(origin, 'type') and origin.type == 'Point':
        origin = origin.coords[0]

    # origin should now be tuple-like
    if len(origin) not in (2, 3):
        raise ValueError("Expected number of items in 'origin' to be "
                         "either 2 or 3")
    if ndim == 2:
        return origin[0:2]
    else:  # 3D coordinate
        if len(origin) == 2:
            return origin + (0.0,)
        else:
            return origin


def rotate(geom, angle, origin='center', use_radians=False):
    """Returns a rotated geometry on a 2D plane.

    The angle of rotation can be specified in either degrees (default) or
    radians by setting ``use_radians=True``. Positive angles are
    counter-clockwise and negative are clockwise rotations.

    The point of origin can be a keyword 'center' for the bounding box
    center (default), 'centroid' for the geometry's centroid, a Point object
    or a coordinate tuple (x0, y0).

    The affine transformation matrix for 2D rotation is:

      / cos(r) -sin(r) xoff \ 
      | sin(r)  cos(r) yoff |
      \   0       0      1  /

    where the offsets are calculated from the origin Point(x0, y0):

        xoff = x0 - x0 * cos(r) + y0 * sin(r)
        yoff = y0 - x0 * sin(r) - y0 * cos(r)
    """
    if not use_radians:  # convert from degrees
        angle *= pi/180.0
    cosp = cos(angle)
    sinp = sin(angle)
    if abs(cosp) < 2.5e-16:
        cosp = 0.0
    if abs(sinp) < 2.5e-16:
        sinp = 0.0
    x0, y0 = interpret_origin(geom, origin, 2)

    matrix = (cosp, -sinp, 0.0,
              sinp,  cosp, 0.0,
              0.0,    0.0, 1.0,
              x0 - x0 * cosp + y0 * sinp, y0 - x0 * sinp - y0 * cosp, 0.0)
    return affine_transform(geom, matrix)


def scale(geom, xfact=1.0, yfact=1.0, zfact=1.0, origin='center'):
    """Returns a scaled geometry, scaled by factors along each dimension.

    The point of origin can be a keyword 'center' for the 2D bounding box
    center (default), 'centroid' for the geometry's 2D centroid, a Point
    object or a coordinate tuple (x0, y0, z0).

    Negative scale factors will mirror or reflect coordinates.

    The general 3D affine transformation matrix for scaling is:

        / xfact  0    0   xoff \ 
        |   0  yfact  0   yoff |
        |   0    0  zfact zoff |
        \   0    0    0     1  /

    where the offsets are calculated from the origin Point(x0, y0, z0):

        xoff = x0 - x0 * xfact
        yoff = y0 - y0 * yfact
        zoff = z0 - z0 * zfact
    """
    x0, y0, z0 = interpret_origin(geom, origin, 3)

    matrix = (xfact, 0.0, 0.0,
              0.0, yfact, 0.0,
              0.0, 0.0, zfact,
              x0 - x0 * xfact, y0 - y0 * yfact, z0 - z0 * zfact)
    return affine_transform(geom, matrix)


def skew(geom, xs=0.0, ys=0.0, origin='center', use_radians=False):
    """Returns a skewed geometry, sheared by angles along x and y dimensions.

    The shear angle can be specified in either degrees (default) or radians
    by setting ``use_radians=True``.

    The point of origin can be a keyword 'center' for the bounding box
    center (default), 'centroid' for the geometry's centroid, a Point object
    or a coordinate tuple (x0, y0).

    The general 2D affine transformation matrix for skewing is:

        /   1    tan(xs) xoff \ 
        | tan(ys)  1     yoff |
        \   0      0       1  /

    where the offsets are calculated from the origin Point(x0, y0):

        xoff = -y0 * tan(xs)
        yoff = -x0 * tan(ys)
    """
    if not use_radians:  # convert from degrees
        xs *= pi/180.0
        ys *= pi/180.0
    tanx = tan(xs)
    tany = tan(ys)
    if abs(tanx) < 2.5e-16:
        tanx = 0.0
    if abs(tany) < 2.5e-16:
        tany = 0.0
    x0, y0 = interpret_origin(geom, origin, 2)

    matrix = (1.0, tanx, 0.0,
              tany, 1.0, 0.0,
              0.0,  0.0, 1.0,
              -y0 * tanx, -x0 * tany, 0.0)
    return affine_transform(geom, matrix)


def translate(geom, xoff=0.0, yoff=0.0, zoff=0.0):
    """Returns a translated geometry shifted by offsets along each dimension.

    The general 3D affine transformation matrix for translation is:

        / 1  0  0 xoff \ 
        | 0  1  0 yoff |
        | 0  0  1 zoff |
        \ 0  0  0   1  /
    """
    matrix = (1.0, 0.0, 0.0,
              0.0, 1.0, 0.0,
              0.0, 0.0, 1.0,
              xoff, yoff, zoff)
    return affine_transform(geom, matrix)
Shapely-1.6.4/shapely/algorithms/000077500000000000000000000000001323200062600167375ustar00rootroot00000000000000Shapely-1.6.4/shapely/algorithms/__init__.py000066400000000000000000000000001323200062600210360ustar00rootroot00000000000000Shapely-1.6.4/shapely/algorithms/cga.py000066400000000000000000000007171323200062600200500ustar00rootroot00000000000000
def signed_area(ring):
    """Return the signed area enclosed by a ring in linear time using the 
    algorithm at: http://www.cgafaq.info/wiki/Polygon_Area.
    """
    xs, ys = ring.coords.xy
    xs.append(xs[1])
    ys.append(ys[1])
    return sum(xs[i]*(ys[i+1]-ys[i-1]) for i in range(1, len(ring.coords)))/2.0

def is_ccw_impl(name):
    """Predicate implementation"""
    def is_ccw_op(ring):
        return signed_area(ring) >= 0.0
    return is_ccw_op

Shapely-1.6.4/shapely/algorithms/polylabel.py000066400000000000000000000103351323200062600212760ustar00rootroot00000000000000from ..geometry import Point, LineString
from ..geos import TopologicalError
from heapq import heappush, heappop


class Cell(object):
    """A `Cell`'s centroid property is a potential solution to finding the pole
    of inaccessibility for a given polygon. Rich comparison operators are used
    for sorting `Cell` objects in a priority queue based on the potential
    maximum distance of any theoretical point within a cell to a given
    polygon's exterior boundary.
    """
    def __init__(self, x, y, h, polygon):
        self.x = x
        self.y = y
        self.h = h  # half of cell size
        self.centroid = Point(x, y)  # cell centroid, potential solution

        # distance from cell centroid to polygon exterior
        self.distance = self._dist(polygon)

        # max distance to polygon exterior within a cell
        self.max_distance = self.distance + h * 1.4142135623730951  # sqrt(2)

    # rich comparison operators for sorting in minimum priority queue
    def __lt__(self, other):
        return self.max_distance > other.max_distance

    def __le__(self, other):
        return self.max_distance >= other.max_distance

    def __eq__(self, other):
        return self.max_distance == other.max_distance

    def __ne__(self, other):
        return self.max_distance != other.max_distance

    def __gt__(self, other):
        return self.max_distance < other.max_distance

    def __ge__(self, other):
        return self.max_distance <= other.max_distance

    def _dist(self, polygon):
        """Signed distance from Cell centroid to polygon outline. The returned
        value is negative if the point is outside of the polygon exterior
        boundary.
        """
        inside = polygon.contains(self.centroid)
        distance = self.centroid.distance(LineString(polygon.exterior.coords))
        if inside:
            return distance
        return -distance


def polylabel(polygon, tolerance=1.0):
    """Finds pole of inaccessibility for a given polygon. Based on
    Vladimir Agafonkin's https://github.com/mapbox/polylabel

    Parameters
    ----------
    polygon : shapely.geometry.Polygon
    tolerance : int or float, optional
                `tolerance` represents the highest resolution in units of the
                input geometry that will be considered for a solution. (default
                value is 1.0).

    Returns
    -------
    shapely.geometry.Point
        A point representing the pole of inaccessibility for the given input
        polygon.

    Raises
    ------
    shapely.geos.TopologicalError
        If the input polygon is not a valid geometry.

    Example
    -------
    >>> polygon = LineString([(0, 0), (50, 200), (100, 100), (20, 50),
    ... (-100, -20), (-150, -200)]).buffer(100)
    >>> label = polylabel(polygon, tolerance=10)
    >>> label.wkt
    'POINT (59.35615556364569 121.8391962974644)'
    """
    if not polygon.is_valid:
        raise TopologicalError('Invalid polygon')
    minx, miny, maxx, maxy = polygon.bounds
    cell_size = min(maxx - minx, maxy - miny)
    h = cell_size / 2.0
    cell_queue = []

    # First best cell approximation is one constructed from the centroid
    # of the polygon
    x, y = polygon.centroid.coords[0]
    best_cell = Cell(x, y, 0, polygon)

    # build a regular square grid covering the polygon
    x = minx
    while x < maxx:
        y = miny
        while y < maxy:
            heappush(cell_queue, Cell(x + h, y + h, h, polygon))
            y += cell_size
        x += cell_size

    # minimum priority queue
    while cell_queue:
        cell = heappop(cell_queue)

        # update the best cell if we find a better one
        if cell.distance > best_cell.distance:
            best_cell = cell

        # continue to the next iteration if we cant find a better solution
        # based on tolerance
        if cell.max_distance - best_cell.distance <= tolerance:
            continue

        # split the cell into quadrants
        h = cell.h / 2.0
        heappush(cell_queue, Cell(cell.x - h, cell.y - h, h, polygon))
        heappush(cell_queue, Cell(cell.x + h, cell.y - h, h, polygon))
        heappush(cell_queue, Cell(cell.x - h, cell.y + h, h, polygon))
        heappush(cell_queue, Cell(cell.x + h, cell.y + h, h, polygon))

    return best_cell.centroid
Shapely-1.6.4/shapely/coords.py000066400000000000000000000126211323200062600164330ustar00rootroot00000000000000"""Coordinate sequence utilities
"""

import sys
from array import array
from ctypes import byref, c_double, c_uint

from shapely.geos import lgeos
from shapely.topology import Validating

if sys.version_info[0] < 3:
    range = xrange


class CoordinateSequence(object):
    """
    Iterative access to coordinate tuples from the parent geometry's coordinate
    sequence.

    Example:

      >>> from shapely.wkt import loads
      >>> g = loads('POINT (0.0 0.0)')
      >>> list(g.coords)
      [(0.0, 0.0)]

    """

    # Attributes
    # ----------
    # _cseq : c_void_p
    #     Ctypes pointer to GEOS coordinate sequence
    # _ndim : int
    #     Number of dimensions (2 or 3, generally)
    # __p__ : object
    #     Parent (Shapely) geometry
    _cseq = None
    _ndim = None
    __p__ = None

    def __init__(self, parent):
        self.__p__ = parent

    def _update(self):
        self._ndim = self.__p__._ndim
        self._cseq = lgeos.GEOSGeom_getCoordSeq(self.__p__._geom)

    def __len__(self):
        self._update()
        cs_len = c_uint(0)
        lgeos.GEOSCoordSeq_getSize(self._cseq, byref(cs_len))
        return cs_len.value

    def __iter__(self):
        self._update()
        dx = c_double()
        dy = c_double()
        dz = c_double()
        has_z = self._ndim == 3
        for i in range(self.__len__()):
            lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
            lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
            if has_z:
                lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
                yield (dx.value, dy.value, dz.value)
            else:
                yield (dx.value, dy.value)

    def __getitem__(self, key):
        self._update()
        dx = c_double()
        dy = c_double()
        dz = c_double()
        m = self.__len__()
        has_z = self._ndim == 3
        if isinstance(key, int):
            if key + m < 0 or key >= m:
                raise IndexError("index out of range")
            if key < 0:
                i = m + key
            else:
                i = key
            lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
            lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
            if has_z:
                lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
                return (dx.value, dy.value, dz.value)
            else:
                return (dx.value, dy.value)
        elif isinstance(key, slice):
            res = []
            start, stop, stride = key.indices(m)
            for i in range(start, stop, stride):
                lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(dx))
                lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(dy))
                if has_z:
                    lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(dz))
                    res.append((dx.value, dy.value, dz.value))
                else:
                    res.append((dx.value, dy.value))
            return res
        else:
            raise TypeError("key must be an index or slice")

    @property
    def ctypes(self):
        self._update()
        has_z = self._ndim == 3
        n = self._ndim
        m = self.__len__()
        array_type = c_double * (m * n)
        data = array_type()
        temp = c_double()
        for i in range(m):
            lgeos.GEOSCoordSeq_getX(self._cseq, i, byref(temp))
            data[n*i] = temp.value
            lgeos.GEOSCoordSeq_getY(self._cseq, i, byref(temp))
            data[n*i+1] = temp.value
            if has_z:
                lgeos.GEOSCoordSeq_getZ(self._cseq, i, byref(temp))
                data[n*i+2] = temp.value
        return data

    def array_interface(self):
        """Provide the Numpy array protocol."""
        if sys.byteorder == 'little':
            typestr = ' maxx: maxx = x
            lgeos.GEOSCoordSeq_getY(cs, i, byref(temp))
            y = temp.value
            if y < miny: miny = y
            if y > maxy: maxy = y
        return (minx, miny, maxx, maxy)
Shapely-1.6.4/shapely/ctypes_declarations.py000066400000000000000000000413271323200062600212060ustar00rootroot00000000000000'''Prototyping of the GEOS C API

See header file: geos-x.y.z/capi/geos_c.h
'''

from ctypes import CFUNCTYPE, POINTER, c_void_p, c_char_p, \
    c_size_t, c_byte, c_uint, c_int, c_double, py_object

from .errors import UnsupportedGEOSVersionError


EXCEPTION_HANDLER_FUNCTYPE = CFUNCTYPE(None, c_char_p, c_void_p)

# Derived pointer types
c_size_t_p = POINTER(c_size_t)


class allocated_c_char_p(c_char_p):
    '''char pointer return type'''
    pass


def prototype(lgeos, geos_version):
    """Protype functions in geos_c.h for different version of GEOS

    Use the GEOS version, not the C API version.
    """

    if not geos_version >= (3, 3, 0):
        raise UnsupportedGEOSVersionError(
            "Shapely requires GEOS version 3.3.0 or newer.")

    # Initialization, cleanup, version.

    lgeos.initGEOS.restype = None
    lgeos.initGEOS.argtypes = [
        EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]

    lgeos.finishGEOS.restype = None
    lgeos.finishGEOS.argtypes = []

    lgeos.GEOSversion.restype = c_char_p
    lgeos.GEOSversion.argtypes = []

    # These functions are DEPRECATED.  Please use the new Reader and
    # writer APIS!

    lgeos.GEOSGeomFromWKT.restype = c_void_p
    lgeos.GEOSGeomFromWKT.argtypes = [c_char_p]

    lgeos.GEOSGeomToWKT.restype = allocated_c_char_p
    lgeos.GEOSGeomToWKT.argtypes = [c_void_p]

    lgeos.GEOS_setWKBOutputDims.restype = c_int
    lgeos.GEOS_setWKBOutputDims.argtypes = [c_int]

    lgeos.GEOSGeomFromWKB_buf.restype = c_void_p
    lgeos.GEOSGeomFromWKB_buf.argtypes = [c_void_p, c_size_t]

    lgeos.GEOSGeomToWKB_buf.restype = allocated_c_char_p
    lgeos.GEOSGeomToWKB_buf.argtypes = [c_void_p, c_size_t_p]

    # Coordinate sequence

    lgeos.GEOSCoordSeq_create.restype = c_void_p
    lgeos.GEOSCoordSeq_create.argtypes = [c_uint, c_uint]

    lgeos.GEOSCoordSeq_clone.restype = c_void_p
    lgeos.GEOSCoordSeq_clone.argtypes = [c_void_p]

    lgeos.GEOSGeom_clone.restype = c_void_p
    lgeos.GEOSGeom_clone.argtypes = [c_void_p]

    lgeos.GEOSCoordSeq_destroy.restype = None
    lgeos.GEOSCoordSeq_destroy.argtypes = [c_void_p]

    lgeos.GEOSCoordSeq_setX.restype = c_int
    lgeos.GEOSCoordSeq_setX.argtypes = [c_void_p, c_uint, c_double]

    lgeos.GEOSCoordSeq_setY.restype = c_int
    lgeos.GEOSCoordSeq_setY.argtypes = [c_void_p, c_uint, c_double]

    lgeos.GEOSCoordSeq_setZ.restype = c_int
    lgeos.GEOSCoordSeq_setZ.argtypes = [c_void_p, c_uint, c_double]

    lgeos.GEOSCoordSeq_setOrdinate.restype = c_int
    lgeos.GEOSCoordSeq_setOrdinate.argtypes = [
        c_void_p, c_uint, c_uint, c_double]

    lgeos.GEOSCoordSeq_getX.restype = c_int
    lgeos.GEOSCoordSeq_getX.argtypes = [c_void_p, c_uint, c_void_p]

    lgeos.GEOSCoordSeq_getY.restype = c_int
    lgeos.GEOSCoordSeq_getY.argtypes = [c_void_p, c_uint, c_void_p]

    lgeos.GEOSCoordSeq_getZ.restype = c_int
    lgeos.GEOSCoordSeq_getZ.argtypes = [c_void_p, c_uint, c_void_p]

    lgeos.GEOSCoordSeq_getSize.restype = c_int
    lgeos.GEOSCoordSeq_getSize.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSCoordSeq_getDimensions.restype = c_int
    lgeos.GEOSCoordSeq_getDimensions.argtypes = [c_void_p, c_void_p]

    # Linear refeferencing

    if geos_version >= (3, 2, 0):

        lgeos.GEOSProject.restype = c_double
        lgeos.GEOSProject.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSInterpolate.restype = c_void_p
        lgeos.GEOSInterpolate.argtypes = [c_void_p, c_double]

        lgeos.GEOSProjectNormalized.restype = c_double
        lgeos.GEOSProjectNormalized.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSInterpolateNormalized.restype = c_void_p
        lgeos.GEOSInterpolateNormalized.argtypes = [c_void_p, c_double]

    # Buffer related

    lgeos.GEOSBuffer.restype = c_void_p
    lgeos.GEOSBuffer.argtypes = [c_void_p, c_double, c_int]

    if geos_version >= (3, 2, 0):

        lgeos.GEOSBufferWithStyle.restype = c_void_p
        lgeos.GEOSBufferWithStyle.argtypes = [
            c_void_p, c_double, c_int, c_int, c_int, c_double]

        if geos_version >= (3, 3, 0):

            lgeos.GEOSOffsetCurve.restype = c_void_p
            lgeos.GEOSOffsetCurve.argtypes = [
                c_void_p, c_double, c_int, c_int, c_double]

        else:

            # deprecated in GEOS 3.3.0 in favour of GEOSOffsetCurve
            lgeos.GEOSSingleSidedBuffer.restype = c_void_p
            lgeos.GEOSSingleSidedBuffer.argtypes = [
                c_void_p, c_double, c_int, c_int, c_double, c_int]

    # Geometry constructors

    lgeos.GEOSGeom_createPoint.restype = c_void_p
    lgeos.GEOSGeom_createPoint.argtypes = [c_void_p]

    lgeos.GEOSGeom_createLinearRing.restype = c_void_p
    lgeos.GEOSGeom_createLinearRing.argtypes = [c_void_p]

    lgeos.GEOSGeom_createLineString.restype = c_void_p
    lgeos.GEOSGeom_createLineString.argtypes = [c_void_p]

    lgeos.GEOSGeom_createPolygon.restype = c_void_p
    lgeos.GEOSGeom_createPolygon.argtypes = [c_void_p, c_void_p, c_uint]

    lgeos.GEOSGeom_createCollection.restype = c_void_p
    lgeos.GEOSGeom_createCollection.argtypes = [c_int, c_void_p, c_uint]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSGeom_createEmptyCollection.restype = c_void_p
        lgeos.GEOSGeom_createEmptyCollection.argtypes = [c_int]

    lgeos.GEOSGeom_clone.restype = c_void_p
    lgeos.GEOSGeom_clone.argtypes = [c_void_p]

    # Memory management

    lgeos.GEOSGeom_destroy.restype = None
    lgeos.GEOSGeom_destroy.argtypes = [c_void_p]

    # Topology operations
    # Return NULL on exception

    lgeos.GEOSEnvelope.restype = c_void_p
    lgeos.GEOSEnvelope.argtypes = [c_void_p]

    lgeos.GEOSIntersection.restype = c_void_p
    lgeos.GEOSIntersection.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSConvexHull.restype = c_void_p
    lgeos.GEOSConvexHull.argtypes = [c_void_p]

    lgeos.GEOSDifference.restype = c_void_p
    lgeos.GEOSDifference.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSSymDifference.restype = c_void_p
    lgeos.GEOSSymDifference.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSBoundary.restype = c_void_p
    lgeos.GEOSBoundary.argtypes = [c_void_p]

    lgeos.GEOSUnion.restype = c_void_p
    lgeos.GEOSUnion.argtypes = [c_void_p, c_void_p]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSUnaryUnion.restype = c_void_p
        lgeos.GEOSUnaryUnion.argtypes = [c_void_p]

    if geos_version >= (3, 1, 0):
        # deprecated in 3.3.0: use GEOSUnaryUnion instead
        lgeos.GEOSUnionCascaded.restype = c_void_p
        lgeos.GEOSUnionCascaded.argtypes = [c_void_p]

    lgeos.GEOSPointOnSurface.restype = c_void_p
    lgeos.GEOSPointOnSurface.argtypes = [c_void_p]

    lgeos.GEOSGetCentroid.restype = c_void_p
    lgeos.GEOSGetCentroid.argtypes = [c_void_p]

    lgeos.GEOSPolygonize.restype = c_void_p
    lgeos.GEOSPolygonize.argtypes = [c_void_p, c_uint]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSPolygonize_full.restype = c_void_p
        lgeos.GEOSPolygonize_full.argtypes = [
            c_void_p, c_void_p, c_void_p, c_void_p]

    if geos_version >= (3, 4, 0):
        lgeos.GEOSDelaunayTriangulation.restype = c_void_p
        lgeos.GEOSDelaunayTriangulation.argtypes = [c_void_p, c_double, c_int]

    lgeos.GEOSLineMerge.restype = c_void_p
    lgeos.GEOSLineMerge.argtypes = [c_void_p]

    lgeos.GEOSSimplify.restype = c_void_p
    lgeos.GEOSSimplify.argtypes = [c_void_p, c_double]

    lgeos.GEOSTopologyPreserveSimplify.restype = c_void_p
    lgeos.GEOSTopologyPreserveSimplify.argtypes = [c_void_p, c_double]

    # Binary predicates
    # Return 2 on exception, 1 on true, 0 on false

    lgeos.GEOSDisjoint.restype = c_byte
    lgeos.GEOSDisjoint.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSTouches.restype = c_byte
    lgeos.GEOSTouches.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSIntersects.restype = c_byte
    lgeos.GEOSIntersects.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSCrosses.restype = c_byte
    lgeos.GEOSCrosses.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSWithin.restype = c_byte
    lgeos.GEOSWithin.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSContains.restype = c_byte
    lgeos.GEOSContains.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSOverlaps.restype = c_byte
    lgeos.GEOSOverlaps.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSCovers.restype = c_byte
    lgeos.GEOSCovers.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSEquals.restype = c_byte
    lgeos.GEOSEquals.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSEqualsExact.restype = c_byte
    lgeos.GEOSEqualsExact.argtypes = [c_void_p, c_void_p, c_double]

    # Unary predicate
    # Return 2 on exception, 1 on true, 0 on false

    lgeos.GEOSisEmpty.restype = c_byte
    lgeos.GEOSisEmpty.argtypes = [c_void_p]

    lgeos.GEOSisValid.restype = c_byte
    lgeos.GEOSisValid.argtypes = [c_void_p]

    if geos_version >= (3, 1, 0):
        lgeos.GEOSisValidReason.restype = allocated_c_char_p
        lgeos.GEOSisValidReason.argtypes = [c_void_p]

    lgeos.GEOSisSimple.restype = c_byte
    lgeos.GEOSisSimple.argtypes = [c_void_p]

    lgeos.GEOSisRing.restype = c_byte
    lgeos.GEOSisRing.argtypes = [c_void_p]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSisClosed.restype = c_byte
        lgeos.GEOSisClosed.argtypes = [c_void_p]

    lgeos.GEOSHasZ.restype = c_byte
    lgeos.GEOSHasZ.argtypes = [c_void_p]

    # Dimensionally Extended 9 Intersection Model related

    lgeos.GEOSRelate.restype = allocated_c_char_p
    lgeos.GEOSRelate.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSRelatePattern.restype = c_byte
    lgeos.GEOSRelatePattern.argtypes = [c_void_p, c_void_p, c_char_p]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSRelatePatternMatch.restype = c_byte
        lgeos.GEOSRelatePatternMatch.argtypes = [c_char_p, c_char_p]

    # Prepared Geometry Binary predicates
    # Return 2 on exception, 1 on true, 0 on false

    if geos_version >= (3, 1, 0):

        lgeos.GEOSPrepare.restype = c_void_p
        lgeos.GEOSPrepare.argtypes = [c_void_p]

        lgeos.GEOSPreparedGeom_destroy.restype = None
        lgeos.GEOSPreparedGeom_destroy.argtypes = [c_void_p]

        lgeos.GEOSPreparedDisjoint.restype = c_byte
        lgeos.GEOSPreparedDisjoint.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedTouches.restype = c_byte
        lgeos.GEOSPreparedTouches.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedIntersects.restype = c_byte
        lgeos.GEOSPreparedIntersects.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedCrosses.restype = c_byte
        lgeos.GEOSPreparedCrosses.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedWithin.restype = c_byte
        lgeos.GEOSPreparedWithin.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedContains.restype = c_byte
        lgeos.GEOSPreparedContains.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedContainsProperly.restype = c_byte
        lgeos.GEOSPreparedContainsProperly.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedOverlaps.restype = c_byte
        lgeos.GEOSPreparedOverlaps.argtypes = [c_void_p, c_void_p]

        lgeos.GEOSPreparedCovers.restype = c_byte
        lgeos.GEOSPreparedCovers.argtypes = [c_void_p, c_void_p]

    # Geometry info

    lgeos.GEOSGeomType.restype = c_char_p
    lgeos.GEOSGeomType.argtypes = [c_void_p]

    lgeos.GEOSGeomTypeId.restype = c_int
    lgeos.GEOSGeomTypeId.argtypes = [c_void_p]

    lgeos.GEOSGetSRID.restype = c_int
    lgeos.GEOSGetSRID.argtypes = [c_void_p]

    lgeos.GEOSSetSRID.restype = None
    lgeos.GEOSSetSRID.argtypes = [c_void_p, c_int]

    lgeos.GEOSGetNumGeometries.restype = c_int
    lgeos.GEOSGetNumGeometries.argtypes = [c_void_p]

    lgeos.GEOSGetGeometryN.restype = c_void_p
    lgeos.GEOSGetGeometryN.argtypes = [c_void_p, c_int]

    lgeos.GEOSGetNumInteriorRings.restype = c_int
    lgeos.GEOSGetNumInteriorRings.argtypes = [c_void_p]

    lgeos.GEOSGetInteriorRingN.restype = c_void_p
    lgeos.GEOSGetInteriorRingN.argtypes = [c_void_p, c_int]

    lgeos.GEOSGetExteriorRing.restype = c_void_p
    lgeos.GEOSGetExteriorRing.argtypes = [c_void_p]

    lgeos.GEOSGetNumCoordinates.restype = c_int
    lgeos.GEOSGetNumCoordinates.argtypes = [c_void_p]

    lgeos.GEOSGeom_getCoordSeq.restype = c_void_p
    lgeos.GEOSGeom_getCoordSeq.argtypes = [c_void_p]

    lgeos.GEOSGeom_getDimensions.restype = c_int
    lgeos.GEOSGeom_getDimensions.argtypes = [c_void_p]

    # Misc functions

    lgeos.GEOSArea.restype = c_double
    lgeos.GEOSArea.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSLength.restype = c_int
    lgeos.GEOSLength.argtypes = [c_void_p, c_void_p]

    lgeos.GEOSDistance.restype = c_int
    lgeos.GEOSDistance.argtypes = [c_void_p, c_void_p, c_void_p]

    if geos_version >= (3, 2, 0):
        lgeos.GEOSHausdorffDistance.restype = c_int
        lgeos.GEOSHausdorffDistance.argtypes = [c_void_p, c_void_p, c_void_p]

    # Reader and Writer APIs

    lgeos.GEOSWKTReader_create.restype = c_void_p
    lgeos.GEOSWKTReader_create.argtypes = []

    lgeos.GEOSWKTReader_destroy.restype = None
    lgeos.GEOSWKTReader_destroy.argtypes = [c_void_p]

    lgeos.GEOSWKTReader_read.restype = c_void_p
    lgeos.GEOSWKTReader_read.argtypes = [c_void_p, c_char_p]

    lgeos.GEOSWKTWriter_create.restype = c_void_p
    lgeos.GEOSWKTWriter_create.argtypes = []

    lgeos.GEOSWKTWriter_destroy.restype = None
    lgeos.GEOSWKTWriter_destroy.argtypes = [c_void_p]

    lgeos.GEOSWKTWriter_write.restype = allocated_c_char_p
    lgeos.GEOSWKTWriter_write.argtypes = [c_void_p, c_void_p]

    if geos_version >= (3, 3, 0):

        lgeos.GEOSWKTWriter_setTrim.restype = None
        lgeos.GEOSWKTWriter_setTrim.argtypes = [c_void_p, c_int]

        lgeos.GEOSWKTWriter_setRoundingPrecision.restype = None
        lgeos.GEOSWKTWriter_setRoundingPrecision.argtypes = [c_void_p, c_int]

        lgeos.GEOSWKTWriter_setOutputDimension.restype = None
        lgeos.GEOSWKTWriter_setOutputDimension.argtypes = [c_void_p, c_int]

        lgeos.GEOSWKTWriter_getOutputDimension.restype = c_int
        lgeos.GEOSWKTWriter_getOutputDimension.argtypes = [c_void_p]

        lgeos.GEOSWKTWriter_setOld3D.restype = None
        lgeos.GEOSWKTWriter_setOld3D.argtypes = [c_void_p, c_int]

    lgeos.GEOSWKBReader_create.restype = c_void_p
    lgeos.GEOSWKBReader_create.argtypes = []

    lgeos.GEOSWKBReader_destroy.restype = None
    lgeos.GEOSWKBReader_destroy.argtypes = [c_void_p]

    lgeos.GEOSWKBReader_read.restype = c_void_p
    lgeos.GEOSWKBReader_read.argtypes = [c_void_p, c_char_p, c_size_t]

    lgeos.GEOSWKBReader_readHEX.restype = c_void_p
    lgeos.GEOSWKBReader_readHEX.argtypes = [c_void_p, c_char_p, c_size_t]

    lgeos.GEOSWKBWriter_create.restype = c_void_p
    lgeos.GEOSWKBWriter_create.argtypes = []

    lgeos.GEOSWKBWriter_destroy.restype = None
    lgeos.GEOSWKBWriter_destroy.argtypes = [c_void_p]

    lgeos.GEOSWKBWriter_write.restype = allocated_c_char_p
    lgeos.GEOSWKBWriter_write.argtypes = [c_void_p, c_void_p, c_size_t_p]

    lgeos.GEOSWKBWriter_writeHEX.restype = allocated_c_char_p
    lgeos.GEOSWKBWriter_writeHEX.argtypes = [c_void_p, c_void_p, c_size_t_p]

    lgeos.GEOSWKBWriter_getOutputDimension.restype = c_int
    lgeos.GEOSWKBWriter_getOutputDimension.argtypes = [c_void_p]

    lgeos.GEOSWKBWriter_setOutputDimension.restype = None
    lgeos.GEOSWKBWriter_setOutputDimension.argtypes = [c_void_p, c_int]

    lgeos.GEOSWKBWriter_getByteOrder.restype = c_int
    lgeos.GEOSWKBWriter_getByteOrder.argtypes = [c_void_p]

    lgeos.GEOSWKBWriter_setByteOrder.restype = None
    lgeos.GEOSWKBWriter_setByteOrder.argtypes = [c_void_p, c_int]

    lgeos.GEOSWKBWriter_getIncludeSRID.restype = c_int
    lgeos.GEOSWKBWriter_getIncludeSRID.argtypes = [c_void_p]

    lgeos.GEOSWKBWriter_setIncludeSRID.restype = None
    lgeos.GEOSWKBWriter_setIncludeSRID.argtypes = [c_void_p, c_int]

    if geos_version >= (3, 1, 1):
        lgeos.GEOSFree.restype = None
        lgeos.GEOSFree.argtypes = [c_void_p]

    if geos_version >= (3, 3, 0):
        lgeos.GEOSSnap.restype = c_void_p
        lgeos.GEOSSnap.argtypes = [c_void_p, c_void_p, c_double]

        lgeos.GEOSSharedPaths.restype = c_void_p
        lgeos.GEOSSharedPaths.argtypes = [c_void_p, c_void_p]

    if geos_version >= (3, 4, 0):
        lgeos.GEOSNearestPoints.restype = c_void_p
        lgeos.GEOSNearestPoints.argtypes = [c_void_p, c_void_p]

    if geos_version >= (3, 4, 2):
        lgeos.GEOSQueryCallback = CFUNCTYPE(None, c_void_p, c_void_p)

        lgeos.GEOSSTRtree_query.argtypes = [
            c_void_p, c_void_p, lgeos.GEOSQueryCallback, py_object]
        lgeos.GEOSSTRtree_query.restype = None

        lgeos.GEOSSTRtree_create.argtypes = [c_int]
        lgeos.GEOSSTRtree_create.restype = c_void_p

        lgeos.GEOSSTRtree_insert.argtypes = [c_void_p, c_void_p, py_object]
        lgeos.GEOSSTRtree_insert.restype = None

        lgeos.GEOSSTRtree_remove.argtypes = [c_void_p, c_void_p, py_object]
        lgeos.GEOSSTRtree_remove.restype = None

        lgeos.GEOSSTRtree_destroy.argtypes = [c_void_p]
        lgeos.GEOSSTRtree_destroy.restype = None
Shapely-1.6.4/shapely/errors.py000066400000000000000000000013201323200062600164500ustar00rootroot00000000000000"""Shapely errors."""


class ShapelyError(Exception):
    """Base error class."""


class UnsupportedGEOSVersionError(ShapelyError):
    """Raised when the system's GEOS library version is unsupported."""


class ReadingError(ShapelyError):
    """A WKT or WKB reading error."""


class WKBReadingError(ReadingError):
    """A WKB reading error."""


class WKTReadingError(ReadingError):
    """A WKT reading error."""


class DimensionError(ShapelyError):
    """An error in the number of coordinate dimensions."""


class TopologicalError(ShapelyError):
    """A geometry is invalid or topologically incorrect."""


class PredicateError(ShapelyError):
    """A geometric predicate has failed to return True/False."""
Shapely-1.6.4/shapely/examples/000077500000000000000000000000001323200062600164045ustar00rootroot00000000000000Shapely-1.6.4/shapely/examples/__init__.py000066400000000000000000000000221323200062600205070ustar00rootroot00000000000000# Examples module
Shapely-1.6.4/shapely/examples/dissolve.py000066400000000000000000000033231323200062600206070ustar00rootroot00000000000000# dissolve.py
#
# Demonstrate how Shapely can be used to build up a collection of patches by 
# dissolving circular regions and how Shapely supports plotting of the results.

from functools import partial
import random

import pylab

from shapely.geometry import Point
from shapely.ops import cascaded_union

# Use a partial function to make 100 points uniformly distributed in a 40x40 
# box centered on 0,0.
r = partial(random.uniform, -20.0, 20.0)
points = [Point(r(), r()) for i in range(100)]

# Buffer the points, producing 100 polygon spots
spots = [p.buffer(2.5) for p in points]

# Perform a cascaded union of the polygon spots, dissolving them into a 
# collection of polygon patches
patches = cascaded_union(spots)

if __name__ == "__main__":
    # Illustrate the results using matplotlib's pylab interface
    pylab.figure(num=None, figsize=(4, 4), dpi=180)
    
    for patch in patches.geoms:
        assert patch.geom_type in ['Polygon']
        assert patch.is_valid
    
        # Fill and outline each patch
        x, y = patch.exterior.xy
        pylab.fill(x, y, color='#cccccc', aa=True) 
        pylab.plot(x, y, color='#666666', aa=True, lw=1.0)
    
        # Do the same for the holes of the patch
        for hole in patch.interiors:
            x, y = hole.xy
            pylab.fill(x, y, color='#ffffff', aa=True) 
            pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
    
    # Plot the original points
    pylab.plot([p.x for p in points], [p.y for p in points], 'b,', alpha=0.75)
    
    # Write the number of patches and the total patch area to the figure
    pylab.text(-25, 25, 
        "Patches: %d, total area: %.2f" % (len(patches.geoms), patches.area))
    
    pylab.savefig('dissolve.png')
    
Shapely-1.6.4/shapely/examples/geoms.py000066400000000000000000000022421323200062600200700ustar00rootroot00000000000000from numpy import asarray
import pylab
from shapely.geometry import Point, LineString, Polygon

polygon = Polygon(((-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)))

point_r = Point(-1.5, 1.2)
point_g = Point(-1.0, 1.0)
point_b = Point(-0.5, 0.5)

line_r = LineString(((-0.5, 0.5), (0.5, 0.5)))
line_g = LineString(((1.0, -1.0), (1.8, 0.5)))
line_b = LineString(((-1.8, -1.2), (1.8, 0.5)))

def plot_point(g, o, l):
    pylab.plot([g.x], [g.y], o, label=l)

def plot_line(g, o):
    a = asarray(g)
    pylab.plot(a[:,0], a[:,1], o)

def fill_polygon(g, o):
    a = asarray(g.exterior)
    pylab.fill(a[:,0], a[:,1], o, alpha=0.5)

def fill_multipolygon(g, o):
    for g in g.geoms:
        fill_polygon(g, o)

if __name__ == "__main__":
    from numpy import asarray
    import pylab
    
    fig = pylab.figure(1, figsize=(4, 3), dpi=150)
    #pylab.axis([-2.0, 2.0, -1.5, 1.5])
    pylab.axis('tight')

    a = asarray(polygon.exterior)
    pylab.fill(a[:,0], a[:,1], 'c')

    plot_point(point_r, 'ro', 'b')
    plot_point(point_g, 'go', 'c')
    plot_point(point_b, 'bo', 'd')

    plot_line(line_r, 'r')
    plot_line(line_g, 'g')
    plot_line(line_b, 'b')

    pylab.show()


Shapely-1.6.4/shapely/examples/intersect.py000066400000000000000000000051021323200062600207540ustar00rootroot00000000000000# intersect.py
#
# Demonstrate how Shapely can be used to analyze and plot the intersection of
# a trajectory and regions in space.

from functools import partial
import random

import pylab

from shapely.geometry import LineString, Point
from shapely.ops import cascaded_union

# Build patches as in dissolved.py
r = partial(random.uniform, -20.0, 20.0)
points = [Point(r(), r()) for i in range(100)]
spots = [p.buffer(2.5) for p in points]
patches = cascaded_union(spots)

# Represent the following geolocation parameters
#
# initial position: -25, -25
# heading: 45.0
# speed: 50*sqrt(2)
#
# as a line
vector = LineString(((-25.0, -25.0), (25.0, 25.0)))

# Find intercepted and missed patches. List the former so we can count them
# later
intercepts = [patch for patch in patches.geoms if vector.intersects(patch)]
misses = (patch for patch in patches.geoms if not vector.intersects(patch))

# Plot the intersection
intersection = vector.intersection(patches)
assert intersection.geom_type in ['MultiLineString']

if __name__ == "__main__":
    # Illustrate the results using matplotlib's pylab interface
    pylab.figure(num=None, figsize=(4, 4), dpi=180)
    
    # Plot the misses
    for spot in misses:
        x, y = spot.exterior.xy
        pylab.fill(x, y, color='#cccccc', aa=True) 
        pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
    
        # Do the same for the holes of the patch
        for hole in spot.interiors:
            x, y = hole.xy
            pylab.fill(x, y, color='#ffffff', aa=True) 
            pylab.plot(x, y, color='#999999', aa=True, lw=1.0)
    
    # Plot the intercepts
    for spot in intercepts:
        x, y = spot.exterior.xy
        pylab.fill(x, y, color='red', alpha=0.25, aa=True) 
        pylab.plot(x, y, color='red', alpha=0.5, aa=True, lw=1.0)
    
        # Do the same for the holes of the patch
        for hole in spot.interiors:
            x, y = hole.xy
            pylab.fill(x, y, color='#ffffff', aa=True) 
            pylab.plot(x, y, color='red', alpha=0.5, aa=True, lw=1.0)
    
    # Draw the projected trajectory
    pylab.arrow(-25, -25, 50, 50, color='#999999', aa=True,
        head_width=1.0, head_length=1.0)
    
    for segment in intersection.geoms:
        x, y = segment.xy
        pylab.plot(x, y, color='red', aa=True, lw=1.5)
    
    # Write the number of patches and the total patch area to the figure
    pylab.text(-28, 25, 
        "Patches: %d/%d (%d), total length: %.1f" \
         % (len(intercepts), len(patches.geoms), 
            len(intersection.geoms), intersection.length))
    
    pylab.savefig('intersect.png')
    
Shapely-1.6.4/shapely/ftools.py000066400000000000000000000055641323200062600164600ustar00rootroot00000000000000# Backport some of functools from Python 2.5 standard library for Shapely 
# on Python 2.4
#
# Python module wrapper for _functools C module
# to allow utilities written in Python to be added
# to the functools module.
# Written by Nick Coghlan 
# Copyright (C) 2006 Python Software Foundation.
# See C source code for _functools credits/copyright
#
# _functools module written and maintained
# by Hye-Shik Chang 
# with adaptations by Raymond Hettinger 
# Copyright (c) 2004, 2005, 2006 Python Software Foundation.
# All rights reserved.

def _partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(
            *(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def _update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        setattr(wrapper, attr, getattr(wrapped, attr))
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

def _wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return _partial(_update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

# Use stdlib's functools if available
try:
    from functools import partial, update_wrapper, wraps
    have_functools = 1
except ImportError:
    partial = _partial
    update_wrapper = _update_wrapper
    wraps = _wraps
    have_functools = 0

Shapely-1.6.4/shapely/geometry/000077500000000000000000000000001323200062600164215ustar00rootroot00000000000000Shapely-1.6.4/shapely/geometry/__init__.py000066400000000000000000000016041323200062600205330ustar00rootroot00000000000000"""Geometry classes and factories
"""

from .base import CAP_STYLE, JOIN_STYLE
from .geo import box, shape, asShape, mapping
from .point import Point, asPoint
from .linestring import LineString, asLineString
from .polygon import Polygon, asPolygon, LinearRing, asLinearRing
from .multipoint import MultiPoint, asMultiPoint
from .multilinestring import MultiLineString, asMultiLineString
from .multipolygon import MultiPolygon, asMultiPolygon
from .collection import GeometryCollection

__all__ = [
    'box', 'shape', 'asShape', 'Point', 'asPoint', 'LineString',
    'asLineString', 'Polygon', 'asPolygon', 'MultiPoint', 'asMultiPoint',
    'MultiLineString', 'asMultiLineString', 'MultiPolygon', 'asMultiPolygon',
    'GeometryCollection', 'mapping', 'LinearRing', 'asLinearRing',
    'CAP_STYLE', 'JOIN_STYLE',
]

# This needs to be called here to avoid circular references
import shapely.speedups
Shapely-1.6.4/shapely/geometry/base.py000066400000000000000000000750661323200062600177230ustar00rootroot00000000000000"""Base geometry class and utilities

Note: a third, z, coordinate value may be used when constructing
geometry objects, but has no effect on geometric analysis. All
operations are performed in the x-y plane. Thus, geometries with
different z values may intersect or be equal.
"""

from binascii import a2b_hex
from ctypes import pointer, c_size_t, c_char_p, c_void_p
from itertools import islice
import math
import sys
from warnings import warn

from shapely.affinity import affine_transform
from shapely.coords import CoordinateSequence
from shapely.errors import WKBReadingError, WKTReadingError
from shapely.ftools import wraps
from shapely.geos import WKBWriter, WKTWriter
from shapely.geos import lgeos
from shapely.impl import DefaultImplementation, delegated


if sys.version_info[0] < 3:
    range = xrange
    integer_types = (int, long)
else:
    integer_types = (int,)


try:
    import numpy as np
    integer_types = integer_types + (np.integer,)
except ImportError:
    pass


GEOMETRY_TYPES = [
    'Point',
    'LineString',
    'LinearRing',
    'Polygon',
    'MultiPoint',
    'MultiLineString',
    'MultiPolygon',
    'GeometryCollection',
]


def dump_coords(geom):
    """Dump coordinates of a geometry in the same order as data packing"""
    if not isinstance(geom, BaseGeometry):
        raise ValueError('Must be instance of a geometry class; found ' +
                         geom.__class__.__name__)
    elif geom.type in ('Point', 'LineString', 'LinearRing'):
        return geom.coords[:]
    elif geom.type == 'Polygon':
        return geom.exterior.coords[:] + [i.coords[:] for i in geom.interiors]
    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
        # Recursive call
        return [dump_coords(part) for part in geom]
    else:
        raise ValueError('Unhandled geometry type: ' + repr(geom.type))


def geometry_type_name(g):
    if g is None:
        raise ValueError("Null geometry has no type")
    return GEOMETRY_TYPES[lgeos.GEOSGeomTypeId(g)]


def geom_factory(g, parent=None):
    # Abstract geometry factory for use with topological methods below
    if not g:
        raise ValueError("No Shapely geometry can be created from null value")
    ob = BaseGeometry()
    geom_type = geometry_type_name(g)
    # TODO: check cost of dynamic import by profiling
    mod = __import__(
        'shapely.geometry',
        globals(),
        locals(),
        [geom_type],
        )
    ob.__class__ = getattr(mod, geom_type)
    ob._geom = g
    ob.__p__ = parent
    if lgeos.methods['has_z'](g):
        ob._ndim = 3
    else:
        ob._ndim = 2
    ob._is_empty = False
    return ob


def geom_from_wkt(data):
    warn("`geom_from_wkt` is deprecated. Use `geos.wkt_reader.read(data)`.",
         DeprecationWarning)
    if sys.version_info[0] >= 3:
        data = data.encode('ascii')
    geom = lgeos.GEOSGeomFromWKT(c_char_p(data))
    if not geom:
        raise WKTReadingError(
            "Could not create geometry because of errors while reading input.")
    return geom_factory(geom)


def geom_to_wkt(ob):
    warn("`geom_to_wkt` is deprecated. Use `geos.wkt_writer.write(ob)`.",
         DeprecationWarning)
    if ob is None or ob._geom is None:
        raise ValueError("Null geometry supports no operations")
    return lgeos.GEOSGeomToWKT(ob._geom)


def deserialize_wkb(data):
    geom = lgeos.GEOSGeomFromWKB_buf(c_char_p(data), c_size_t(len(data)))
    if not geom:
        raise WKBReadingError(
            "Could not create geometry because of errors while reading input.")
    return geom


def geom_from_wkb(data):
    warn("`geom_from_wkb` is deprecated. Use `geos.wkb_reader.read(data)`.",
         DeprecationWarning)
    return geom_factory(deserialize_wkb(data))


def geom_to_wkb(ob):
    warn("`geom_to_wkb` is deprecated. Use `geos.wkb_writer.write(ob)`.",
         DeprecationWarning)
    if ob is None or ob._geom is None:
        raise ValueError("Null geometry supports no operations")
    size = c_size_t()
    return lgeos.GEOSGeomToWKB_buf(c_void_p(ob._geom), pointer(size))


def geos_geom_from_py(ob, create_func=None):
    """Helper function for geos_*_from_py functions in each geom type.

    If a create_func is specified the coodinate sequence is cloned and a new
    geometry is created with it, otherwise the geometry is cloned directly.
    This behaviour is useful for converting between LineString and LinearRing
    objects.
    """
    if create_func is None:
        geom = lgeos.GEOSGeom_clone(ob._geom)
    else:
        cs = lgeos.GEOSGeom_getCoordSeq(ob._geom)
        cs = lgeos.GEOSCoordSeq_clone(cs)
        geom = create_func(cs)

    N = ob._ndim

    return geom, N


def exceptNull(func):
    """Decorator which helps avoid GEOS operations on null pointers."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not args[0]._geom or args[0].is_empty:
            raise ValueError("Null/empty geometry supports no operations")
        return func(*args, **kwargs)
    return wrapper


class CAP_STYLE(object):
    round = 1
    flat = 2
    square = 3


class JOIN_STYLE(object):
    round = 1
    mitre = 2
    bevel = 3

EMPTY = deserialize_wkb(a2b_hex(b'010700000000000000'))


class BaseGeometry(object):
    """
    Provides GEOS spatial predicates and topological operations.

    """

    # Attributes
    # ----------
    # __geom__ : c_void_p
    #     Cached ctypes pointer to GEOS geometry. Not to be accessed.
    # _geom : c_void_p
    #     Property by which the GEOS geometry is accessed.
    # __p__ : object
    #     Parent (Shapely) geometry
    # _ctypes_data : object
    #     Cached ctypes data buffer
    # _ndim : int
    #     Number of dimensions (2 or 3, generally)
    # _crs : object
    #     Coordinate reference system. Available for Shapely extensions, but
    #     not implemented here.
    # _other_owned : bool
    #     True if this object's GEOS geometry is owned by another as in the
    #     case of a multipart geometry member.
    __geom__ = EMPTY
    __p__ = None
    _ctypes_data = None
    _ndim = None
    _crs = None
    _other_owned = False
    _is_empty = True

    # Backend config
    impl = DefaultImplementation

    # a reference to the so/dll proxy to preserve access during clean up
    _lgeos = lgeos

    def empty(self, val=EMPTY):
        # TODO: defer cleanup to the implementation. We shouldn't be
        # explicitly calling a lgeos method here.
        if not self._is_empty and not self._other_owned and self.__geom__:
            try:
                self._lgeos.GEOSGeom_destroy(self.__geom__)
            except (AttributeError, TypeError):
                pass  # _lgeos might be empty on shutdown
        self._is_empty = True
        self.__geom__ = val

    def __del__(self):
        self.empty(val=None)
        self.__p__ = None

    def __str__(self):
        return self.wkt

    # To support pickling
    def __reduce__(self):
        return (self.__class__, (), self.wkb)

    def __setstate__(self, state):
        self.empty()
        self.__geom__ = deserialize_wkb(state)
        self._is_empty = False
        if lgeos.methods['has_z'](self.__geom__):
            self._ndim = 3
        else:
            self._ndim = 2

    @property
    def _geom(self):
        return self.__geom__

    @_geom.setter
    def _geom(self, val):
        self.empty()
        self._is_empty = val in [EMPTY, None]
        self.__geom__ = val

    # Operators
    # ---------

    def __and__(self, other):
        return self.intersection(other)

    def __or__(self, other):
        return self.union(other)

    def __sub__(self, other):
        return self.difference(other)

    def __xor__(self, other):
        return self.symmetric_difference(other)

    def __eq__(self, other):
        return (
            type(other) == type(self) and
            tuple(self.coords) == tuple(other.coords)
        )

    def __ne__(self, other):
        return not self.__eq__(other)

    __hash__ = None

    # Array and ctypes interfaces
    # ---------------------------

    @property
    def ctypes(self):
        """Return ctypes buffer"""
        raise NotImplementedError

    @property
    def array_interface_base(self):
        if sys.byteorder == 'little':
            typestr = ''
        else:
            # Establish SVG canvas that will fit all the data + small space
            xmin, ymin, xmax, ymax = self.bounds
            if xmin == xmax and ymin == ymax:
                # This is a point; buffer using an arbitrary size
                xmin, ymin, xmax, ymax = self.buffer(1).bounds
            else:
                # Expand bounds by a fraction of the data ranges
                expand = 0.04  # or 4%, same as R plots
                widest_part = max([xmax - xmin, ymax - ymin])
                expand_amount = widest_part * expand
                xmin -= expand_amount
                ymin -= expand_amount
                xmax += expand_amount
                ymax += expand_amount
            dx = xmax - xmin
            dy = ymax - ymin
            width = min([max([100., dx]), 300])
            height = min([max([100., dy]), 300])
            try:
                scale_factor = max([dx, dy]) / max([width, height])
            except ZeroDivisionError:
                scale_factor = 1.
            view_box = "{0} {1} {2} {3}".format(xmin, ymin, dx, dy)
            transform = "matrix(1,0,0,-1,0,{0})".format(ymax + ymin)
            return svg_top + (
                'width="{1}" height="{2}" viewBox="{0}" '
                'preserveAspectRatio="xMinYMin meet">'
                '{4}'
                ).format(view_box, width, height, transform,
                         self.svg(scale_factor))

    @property
    def geom_type(self):
        """Name of the geometry's type, such as 'Point'"""
        return self.geometryType()

    # Real-valued properties and methods
    # ----------------------------------

    @property
    def area(self):
        """Unitless area of the geometry (float)"""
        return self.impl['area'](self)

    def distance(self, other):
        """Unitless distance to other geometry (float)"""
        return self.impl['distance'](self, other)

    def hausdorff_distance(self, other):
        """Unitless hausdorff distance to other geometry (float)"""
        return self.impl['hausdorff_distance'](self, other)

    @property
    def length(self):
        """Unitless length of the geometry (float)"""
        return self.impl['length'](self)

    # Topological properties
    # ----------------------

    @property
    def boundary(self):
        """Returns a lower dimension geometry that bounds the object

        The boundary of a polygon is a line, the boundary of a line is a
        collection of points. The boundary of a point is an empty (null)
        collection.
        """
        return geom_factory(self.impl['boundary'](self))

    @property
    def bounds(self):
        """Returns minimum bounding region (minx, miny, maxx, maxy)"""
        if self.is_empty:
            return ()
        else:
            return self.impl['bounds'](self)

    @property
    def centroid(self):
        """Returns the geometric center of the object"""
        return geom_factory(self.impl['centroid'](self))

    @delegated
    def representative_point(self):
        """Returns a point guaranteed to be within the object, cheaply."""
        return geom_factory(self.impl['representative_point'](self))

    @property
    def convex_hull(self):
        """Imagine an elastic band stretched around the geometry: that's a
        convex hull, more or less

        The convex hull of a three member multipoint, for example, is a
        triangular polygon.
        """
        return geom_factory(self.impl['convex_hull'](self))

    @property
    def envelope(self):
        """A figure that envelopes the geometry"""
        return geom_factory(self.impl['envelope'](self))

    @property
    def minimum_rotated_rectangle(self):
        """Returns the general minimum bounding rectangle of
        the geometry. Can possibly be rotated. If the convex hull
        of the object is a degenerate (line or point) this same degenerate
        is returned.
        """
        # first compute the convex hull
        hull = self.convex_hull
        try:
            coords = hull.exterior.coords
        except AttributeError:  # may be a Point or a LineString
            return hull
        # generate the edge vectors between the convex hull's coords
        edges = ((pt2[0] - pt1[0], pt2[1] - pt1[1]) for pt1, pt2 in zip(
            coords, islice(coords, 1, None)))

        def _transformed_rects():
            for dx, dy in edges:
                # compute the normalized direction vector of the edge
                # vector.
                length = math.sqrt(dx ** 2 + dy ** 2)
                ux, uy = dx / length, dy / length
                # compute the normalized perpendicular vector
                vx, vy = -uy, ux
                # transform hull from the original coordinate system to
                # the coordinate system defined by the edge and compute
                # the axes-parallel bounding rectangle.
                transf_rect = affine_transform(
                    hull, (ux, uy, vx, vy, 0, 0)).envelope
                # yield the transformed rectangle and a matrix to
                # transform it back to the original coordinate system.
                yield (transf_rect, (ux, vx, uy, vy, 0, 0))

        # check for the minimum area rectangle and return it
        transf_rect, inv_matrix = min(
            _transformed_rects(), key=lambda r: r[0].area)
        return affine_transform(transf_rect, inv_matrix)

    def buffer(self, distance, resolution=16, quadsegs=None,
               cap_style=CAP_STYLE.round, join_style=JOIN_STYLE.round,
               mitre_limit=5.0):
        """Returns a geometry with an envelope at a distance from the object's
        envelope

        A negative distance has a "shrink" effect. A zero distance may be used
        to "tidy" a polygon. The resolution of the buffer around each vertex of
        the object increases by increasing the resolution keyword parameter
        or second positional parameter. Note: the use of a `quadsegs` parameter
        is deprecated and will be gone from the next major release.

        The styles of caps are: CAP_STYLE.round (1), CAP_STYLE.flat (2), and
        CAP_STYLE.square (3).

        The styles of joins between offset segments are: JOIN_STYLE.round (1),
        JOIN_STYLE.mitre (2), and JOIN_STYLE.bevel (3).

        The mitre limit ratio is used for very sharp corners. The mitre ratio
        is the ratio of the distance from the corner to the end of the mitred
        offset corner. When two line segments meet at a sharp angle, a miter
        join will extend the original geometry. To prevent unreasonable
        geometry, the mitre limit allows controlling the maximum length of the
        join corner. Corners with a ratio which exceed the limit will be
        beveled.

        Example:

          >>> from shapely.wkt import loads
          >>> g = loads('POINT (0.0 0.0)')
          >>> g.buffer(1.0).area        # 16-gon approx of a unit radius circle
          3.1365484905459389
          >>> g.buffer(1.0, 128).area   # 128-gon approximation
          3.1415138011443009
          >>> g.buffer(1.0, 3).area     # triangle approximation
          3.0
          >>> list(g.buffer(1.0, cap_style='square').exterior.coords)
          [(1.0, 1.0), (1.0, -1.0), (-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0)]
          >>> g.buffer(1.0, cap_style='square').area
          4.0
        """
        if quadsegs is not None:
            warn(
                "The `quadsegs` argument is deprecated. Use `resolution`.",
                DeprecationWarning)
            res = quadsegs
        else:
            res = resolution
        if mitre_limit == 0.0:
            raise ValueError(
                'Cannot compute offset from zero-length line segment')
        if cap_style == CAP_STYLE.round and join_style == JOIN_STYLE.round:
            return geom_factory(self.impl['buffer'](self, distance, res))

        if 'buffer_with_style' not in self.impl:
            raise NotImplementedError("Styled buffering not available for "
                                      "GEOS versions < 3.2.")

        return geom_factory(self.impl['buffer_with_style'](self, distance, res,
                                                           cap_style,
                                                           join_style,
                                                           mitre_limit))

    @delegated
    def simplify(self, tolerance, preserve_topology=True):
        """Returns a simplified geometry produced by the Douglas-Peucker
        algorithm

        Coordinates of the simplified geometry will be no more than the
        tolerance distance from the original. Unless the topology preserving
        option is used, the algorithm may produce self-intersecting or
        otherwise invalid geometries.
        """
        if preserve_topology:
            op = self.impl['topology_preserve_simplify']
        else:
            op = self.impl['simplify']
        return geom_factory(op(self, tolerance))

    # Binary operations
    # -----------------

    def difference(self, other):
        """Returns the difference of the geometries"""
        return geom_factory(self.impl['difference'](self, other))

    def intersection(self, other):
        """Returns the intersection of the geometries"""
        return geom_factory(self.impl['intersection'](self, other))

    def symmetric_difference(self, other):
        """Returns the symmetric difference of the geometries
        (Shapely geometry)"""
        return geom_factory(self.impl['symmetric_difference'](self, other))

    def union(self, other):
        """Returns the union of the geometries (Shapely geometry)"""
        return geom_factory(self.impl['union'](self, other))

    # Unary predicates
    # ----------------

    @property
    def has_z(self):
        """True if the geometry's coordinate sequence(s) have z values (are
        3-dimensional)"""
        return bool(self.impl['has_z'](self))

    @property
    def is_empty(self):
        """True if the set of points in this geometry is empty, else False"""
        return (self._geom is None) or bool(self.impl['is_empty'](self))

    @property
    def is_ring(self):
        """True if the geometry is a closed ring, else False"""
        return bool(self.impl['is_ring'](self))

    @property
    def is_closed(self):
        """True if the geometry is closed, else False

        Applicable only to 1-D geometries."""
        if self.geom_type == 'LinearRing':
            return True
        elif self.geom_type == 'LineString':
            if 'is_closed' in self.impl:
                return bool(self.impl['is_closed'](self))
            else:
                return self.coords[0] == self.coords[-1]
        else:
            return False

    @property
    def is_simple(self):
        """True if the geometry is simple, meaning that any self-intersections
        are only at boundary points, else False"""
        return bool(self.impl['is_simple'](self))

    @property
    def is_valid(self):
        """True if the geometry is valid (definition depends on sub-class),
        else False"""
        return bool(self.impl['is_valid'](self))

    # Binary predicates
    # -----------------

    def relate(self, other):
        """Returns the DE-9IM intersection matrix for the two geometries
        (string)"""
        return self.impl['relate'](self, other)

    def covers(self, other):
        """Returns True if the geometry covers the other, else False"""
        return bool(self.impl['covers'](self, other))

    def contains(self, other):
        """Returns True if the geometry contains the other, else False"""
        return bool(self.impl['contains'](self, other))

    def crosses(self, other):
        """Returns True if the geometries cross, else False"""
        return bool(self.impl['crosses'](self, other))

    def disjoint(self, other):
        """Returns True if geometries are disjoint, else False"""
        return bool(self.impl['disjoint'](self, other))

    def equals(self, other):
        """Returns True if geometries are equal, else False
        
        Refers to point-set equality (or topological equality), and is equivalent to
        (self.within(other) & self.contains(other))
        """
        return bool(self.impl['equals'](self, other))

    def intersects(self, other):
        """Returns True if geometries intersect, else False"""
        return bool(self.impl['intersects'](self, other))

    def overlaps(self, other):
        """Returns True if geometries overlap, else False"""
        return bool(self.impl['overlaps'](self, other))

    def touches(self, other):
        """Returns True if geometries touch, else False"""
        return bool(self.impl['touches'](self, other))

    def within(self, other):
        """Returns True if geometry is within the other, else False"""
        return bool(self.impl['within'](self, other))

    def equals_exact(self, other, tolerance):
        """Returns True if geometries are equal to within a specified
        tolerance
        
        Refers to coordinate equality, which requires coordinates to be equal 
        and in the same order for all components of a geometry
        """
        return bool(self.impl['equals_exact'](self, other, tolerance))

    def almost_equals(self, other, decimal=6):
        """Returns True if geometries are equal at all coordinates to a
        specified decimal place

        Refers to approximate coordinate equality, which requires coordinates be
        approximately equal and in the same order for all components of a geometry.
        """
        return self.equals_exact(other, 0.5 * 10**(-decimal))

    def relate_pattern(self, other, pattern):
        """Returns True if the DE-9IM string code for the relationship between
        the geometries satisfies the pattern, else False"""
        pattern = c_char_p(pattern.encode('ascii'))
        return bool(self.impl['relate_pattern'](self, other, pattern))

    # Linear referencing
    # ------------------

    @delegated
    def project(self, other, normalized=False):
        """Returns the distance along this geometry to a point nearest the
        specified point

        If the normalized arg is True, return the distance normalized to the
        length of the linear geometry.
        """
        if normalized:
            op = self.impl['project_normalized']
        else:
            op = self.impl['project']
        return op(self, other)

    @delegated
    def interpolate(self, distance, normalized=False):
        """Return a point at the specified distance along a linear geometry

        If the normalized arg is True, the distance will be interpreted as a
        fraction of the geometry's length.
        """
        if normalized:
            op = self.impl['interpolate_normalized']
        else:
            op = self.impl['interpolate']
        return geom_factory(op(self, distance))


class BaseMultipartGeometry(BaseGeometry):

    def shape_factory(self, *args):
        # Factory for part instances, usually a geometry class
        raise NotImplementedError("To be implemented by derived classes")

    @property
    def ctypes(self):
        raise NotImplementedError(
            "Multi-part geometries have no ctypes representations")

    @property
    def __array_interface__(self):
        """Provide the Numpy array protocol."""
        raise NotImplementedError("Multi-part geometries do not themselves "
                                  "provide the array interface")

    def _get_coords(self):
        raise NotImplementedError("Sub-geometries may have coordinate "
                                  "sequences, but collections do not")

    def _set_coords(self, ob):
        raise NotImplementedError("Sub-geometries may have coordinate "
                                  "sequences, but collections do not")

    @property
    def coords(self):
        raise NotImplementedError(
            "Multi-part geometries do not provide a coordinate sequence")

    @property
    def geoms(self):
        if self.is_empty:
            return []
        return GeometrySequence(self, self.shape_factory)

    def __iter__(self):
        if not self.is_empty:
            return iter(self.geoms)
        else:
            return iter([])

    def __len__(self):
        if not self.is_empty:
            return len(self.geoms)
        else:
            return 0

    def __getitem__(self, index):
        if not self.is_empty:
            return self.geoms[index]
        else:
            return ()[index]

    def __eq__(self, other):
        return (
            type(other) == type(self) and
            len(self) == len(other) and
            all(x == y for x, y in zip(self, other))
        )

    def __ne__(self, other):
        return not self.__eq__(other)

    __hash__ = None

    def svg(self, scale_factor=1., color=None):
        """Returns a group of SVG elements for the multipart geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        color : str, optional
            Hex string for stroke or fill color. Default is to use "#66cc99"
            if geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if color is None:
            color = "#66cc99" if self.is_valid else "#ff3333"
        return '' + \
            ''.join(p.svg(scale_factor, color) for p in self) + \
            ''


class GeometrySequence(object):
    """
    Iterative access to members of a homogeneous multipart geometry.
    """

    # Attributes
    # ----------
    # _factory : callable
    #     Returns instances of Shapely geometries
    # _geom : c_void_p
    #     Ctypes pointer to the parent's GEOS geometry
    # _ndim : int
    #     Number of dimensions (2 or 3, generally)
    # __p__ : object
    #     Parent (Shapely) geometry
    shape_factory = None
    _geom = None
    __p__ = None
    _ndim = None

    def __init__(self, parent, type):
        self.shape_factory = type
        self.__p__ = parent

    def _update(self):
        self._geom = self.__p__._geom
        self._ndim = self.__p__._ndim

    def _get_geom_item(self, i):
        g = self.shape_factory()
        g._other_owned = True
        g._geom = lgeos.GEOSGetGeometryN(self._geom, i)
        g._ndim = self._ndim
        g.__p__ = self
        return g

    def __iter__(self):
        self._update()
        for i in range(self.__len__()):
            yield self._get_geom_item(i)

    def __len__(self):
        self._update()
        return lgeos.GEOSGetNumGeometries(self._geom)

    def __getitem__(self, key):
        self._update()
        m = self.__len__()
        if isinstance(key, integer_types):
            if key + m < 0 or key >= m:
                raise IndexError("index out of range")
            if key < 0:
                i = m + key
            else:
                i = key
            return self._get_geom_item(i)
        elif isinstance(key, slice):
            if type(self) == HeterogeneousGeometrySequence:
                raise TypeError(
                    "Heterogenous geometry collections are not sliceable")
            res = []
            start, stop, stride = key.indices(m)
            for i in range(start, stop, stride):
                res.append(self._get_geom_item(i))
            return type(self.__p__)(res or None)
        else:
            raise TypeError("key must be an index or slice")

    @property
    def _longest(self):
        max = 0
        for g in iter(self):
            l = len(g.coords)
            if l > max:
                max = l


class HeterogeneousGeometrySequence(GeometrySequence):
    """
    Iterative access to a heterogeneous sequence of geometries.
    """

    def __init__(self, parent):
        super(HeterogeneousGeometrySequence, self).__init__(parent, None)

    def _get_geom_item(self, i):
        sub = lgeos.GEOSGetGeometryN(self._geom, i)
        g = geom_factory(sub, parent=self)
        g._other_owned = True
        return g


class EmptyGeometry(BaseGeometry):
    def __init__(self):
        """Create an empty geometry."""
        BaseGeometry.__init__(self)


def _test():
    """Test runner"""
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()
Shapely-1.6.4/shapely/geometry/collection.py000066400000000000000000000037571323200062600211420ustar00rootroot00000000000000"""Multi-part collections of geometries
"""

from ctypes import c_void_p

from shapely.geos import lgeos
from shapely.geometry.base import BaseGeometry
from shapely.geometry.base import BaseMultipartGeometry
from shapely.geometry.base import HeterogeneousGeometrySequence
from shapely.geometry.base import geos_geom_from_py


class GeometryCollection(BaseMultipartGeometry):

    """A heterogenous collection of geometries

    Attributes
    ----------
    geoms : sequence
        A sequence of Shapely geometry instances
    """

    def __init__(self, geoms=None):
        """
        Parameters
        ----------
        geoms : list
            A list of shapely geometry instances, which may be heterogenous.
        
        Example
        -------
        Create a GeometryCollection with a Point and a LineString
        
          >>> p = Point(51, -1)
          >>> l = LineString([(52, -1), (49, 2)])
          >>> gc = GeometryCollection([p, l])
        """
        BaseMultipartGeometry.__init__(self)
        if not geoms:
            pass
        else:
            self._geom, self._ndim = geos_geometrycollection_from_py(geoms)

    @property
    def __geo_interface__(self):
        geometries = []
        for geom in self.geoms:
            geometries.append(geom.__geo_interface__)
        return dict(type='GeometryCollection', geometries=geometries)

    @property
    def geoms(self):
        if self.is_empty:
            return []
        return HeterogeneousGeometrySequence(self)

def geos_geometrycollection_from_py(ob):
    """Creates a GEOS GeometryCollection from a list of geometries"""
    L = len(ob)
    N = 2
    subs = (c_void_p * L)()
    for l in range(L):
        assert(isinstance(ob[l], BaseGeometry))
        if ob[l].has_z:
            N = 3
        geom, n = geos_geom_from_py(ob[l])
        subs[l] = geom
    
    return (lgeos.GEOSGeom_createCollection(7, subs, L), N)

# Test runner
def _test():
    import doctest
    doctest.testmod()


if __name__ == "__main__":
    _test()

Shapely-1.6.4/shapely/geometry/geo.py000066400000000000000000000055771323200062600175630ustar00rootroot00000000000000"""
Geometry factories based on the geo interface
"""

from .point import Point, asPoint
from .linestring import LineString, asLineString
from .polygon import Polygon, asPolygon
from .multipoint import MultiPoint, asMultiPoint
from .multilinestring import MultiLineString, asMultiLineString
from .multipolygon import MultiPolygon, MultiPolygonAdapter
from .collection import GeometryCollection


def box(minx, miny, maxx, maxy, ccw=True):
    """Returns a rectangular polygon with configurable normal vector"""
    coords = [(maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny)]
    if not ccw:
        coords = coords[::-1]
    return Polygon(coords)

def shape(context):
    """Returns a new, independent geometry with coordinates *copied* from the
    context.
    """
    if hasattr(context, "__geo_interface__"):
        ob = context.__geo_interface__
    else:
        ob = context
    geom_type = ob.get("type").lower()
    if geom_type == "point":
        return Point(ob["coordinates"])
    elif geom_type == "linestring":
        return LineString(ob["coordinates"])
    elif geom_type == "polygon":
        return Polygon(ob["coordinates"][0], ob["coordinates"][1:])
    elif geom_type == "multipoint":
        return MultiPoint(ob["coordinates"])
    elif geom_type == "multilinestring":
        return MultiLineString(ob["coordinates"])
    elif geom_type == "multipolygon":
        return MultiPolygon(ob["coordinates"], context_type='geojson')
    elif geom_type == "geometrycollection":
        geoms = [shape(g) for g in ob.get("geometries", [])]
        return GeometryCollection(geoms)
    else:
        raise ValueError("Unknown geometry type: %s" % geom_type)

def asShape(context):
    """Adapts the context to a geometry interface. The coordinates remain
    stored in the context.
    """
    if hasattr(context, "__geo_interface__"):
        ob = context.__geo_interface__
    else:
        ob = context

    try:
        geom_type = ob.get("type").lower()
    except AttributeError:
        raise ValueError("Context does not provide geo interface")

    if geom_type == "point":
        return asPoint(ob["coordinates"])
    elif geom_type == "linestring":
        return asLineString(ob["coordinates"])
    elif geom_type == "polygon":
        return asPolygon(ob["coordinates"][0], ob["coordinates"][1:])
    elif geom_type == "multipoint":
        return asMultiPoint(ob["coordinates"])
    elif geom_type == "multilinestring":
        return asMultiLineString(ob["coordinates"])
    elif geom_type == "multipolygon":
        return MultiPolygonAdapter(ob["coordinates"], context_type='geojson')
    elif geom_type == "geometrycollection":
        geoms = [asShape(g) for g in ob.get("geometries", [])]
        return GeometryCollection(geoms)
    else:
        raise ValueError("Unknown geometry type: %s" % geom_type)

def mapping(ob):
    """Returns a GeoJSON-like mapping"""
    return ob.__geo_interface__
Shapely-1.6.4/shapely/geometry/linestring.py000066400000000000000000000177601323200062600211640ustar00rootroot00000000000000"""Line strings and related utilities
"""

import sys

if sys.version_info[0] < 3:
    range = xrange

from ctypes import c_double, cast, POINTER

from shapely.geos import lgeos, TopologicalError
from shapely.geometry.base import (
    BaseGeometry, geom_factory, JOIN_STYLE, geos_geom_from_py
)
from shapely.geometry.proxy import CachingGeometryProxy
from shapely.geometry.point import Point

__all__ = ['LineString', 'asLineString']


class LineString(BaseGeometry):
    """
    A one-dimensional figure comprising one or more line segments

    A LineString has non-zero length and zero area. It may approximate a curve
    and need not be straight. Unlike a LinearRing, a LineString is not closed.
    """

    def __init__(self, coordinates=None):
        """
        Parameters
        ----------
        coordinates : sequence
            A sequence of (x, y [,z]) numeric coordinate pairs or triples or
            an object that provides the numpy array interface, including
            another instance of LineString.

        Example
        -------
        Create a line with two segments

          >>> a = LineString([[0, 0], [1, 0], [1, 1]])
          >>> a.length
          2.0
        """
        BaseGeometry.__init__(self)
        if coordinates is not None:
            self._set_coords(coordinates)

    @property
    def __geo_interface__(self):
        return {
            'type': 'LineString',
            'coordinates': tuple(self.coords)
            }

    def svg(self, scale_factor=1., stroke_color=None):
        """Returns SVG polyline element for the LineString geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        stroke_color : str, optional
            Hex string for stroke color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if stroke_color is None:
            stroke_color = "#66cc99" if self.is_valid else "#ff3333"
        pnt_format = " ".join(["{0},{1}".format(*c) for c in self.coords])
        return (
            ''
            ).format(pnt_format, 2. * scale_factor, stroke_color)

    @property
    def ctypes(self):
        if not self._ctypes_data:
            self._ctypes_data = self.coords.ctypes
        return self._ctypes_data

    def array_interface(self):
        """Provide the Numpy array protocol."""
        if self.is_empty:
            ai = {'version': 3, 'typestr': '>> x, y = LineString(((0, 0), (1, 1))).xy
          >>> list(x)
          [0.0, 1.0]
          >>> list(y)
          [0.0, 1.0]
        """
        return self.coords.xy

    def parallel_offset(
            self, distance, side='right',
            resolution=16, join_style=JOIN_STYLE.round, mitre_limit=5.0):

        """Returns a LineString or MultiLineString geometry at a distance from
        the object on its right or its left side.

        The side parameter may be 'left' or 'right' (default is 'right'). The
        resolution of the buffer around each vertex of the object increases by
        increasing the resolution keyword parameter or third positional
        parameter. Vertices of right hand offset lines will be ordered in
        reverse.

        The join style is for outside corners between line segments. Accepted
        values are JOIN_STYLE.round (1), JOIN_STYLE.mitre (2), and
        JOIN_STYLE.bevel (3).

        The mitre ratio limit is used for very sharp corners. It is the ratio
        of the distance from the corner to the end of the mitred offset corner.
        When two line segments meet at a sharp angle, a miter join will extend
        far beyond the original geometry. To prevent unreasonable geometry, the
        mitre limit allows controlling the maximum length of the join corner.
        Corners with a ratio which exceed the limit will be beveled.
        """
        if mitre_limit == 0.0:
            raise ValueError(
                'Cannot compute offset from zero-length line segment')
        try:
            return geom_factory(self.impl['parallel_offset'](
                self, distance, resolution, join_style, mitre_limit, side))
        except OSError:
            raise TopologicalError()


class LineStringAdapter(CachingGeometryProxy, LineString):

    def __init__(self, context):
        self.context = context
        self.factory = geos_linestring_from_py

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.context.__array_interface__
            n = array['shape'][1]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.context[0])

    @property
    def __array_interface__(self):
        """Provide the Numpy array protocol."""
        try:
            return self.context.__array_interface__
        except AttributeError:
            return self.array_interface()

    _get_coords = BaseGeometry._get_coords

    def _set_coords(self, ob):
        raise NotImplementedError(
            "Adapters can not modify their coordinate sources")

    coords = property(_get_coords)


def asLineString(context):
    """Adapt an object the LineString interface"""
    return LineStringAdapter(context)


def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
    # If a LineString is passed in, clone it and return
    # If a LinearRing is passed in, clone the coord seq and return a
    # LineString.
    #
    # NB: access to coordinates using the array protocol has been moved
    # entirely to the speedups module.

    if isinstance(ob, LineString):
        if type(ob) == LineString:
            return geos_geom_from_py(ob)
        else:
            return geos_geom_from_py(ob, lgeos.GEOSGeom_createLineString)

    try:
        m = len(ob)
    except TypeError:  # Iterators, e.g. Python 3 zip
        ob = list(ob)
        m = len(ob)

    if m == 0:
        return None
    elif m == 1:
        raise ValueError("LineStrings must have at least 2 coordinate tuples")

    def _coords(o):
        if isinstance(o, Point):
            return o.coords[0]
        else:
            return o

    try:
        n = len(_coords(ob[0]))
    except TypeError:
        raise ValueError(
            "Input %s is the wrong shape for a LineString" % str(ob))
    assert n == 2 or n == 3

    # Create a coordinate sequence
    if update_geom is not None:
        cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
        if n != update_ndim:
            raise ValueError(
                "Wrong coordinate dimensions; this geometry has "
                "dimensions: %d" % update_ndim)
    else:
        cs = lgeos.GEOSCoordSeq_create(m, n)

    # add to coordinate sequence
    for i in range(m):
        coords = _coords(ob[i])
        # Because of a bug in the GEOS C API,
        # always set X before Y
        lgeos.GEOSCoordSeq_setX(cs, i, coords[0])
        lgeos.GEOSCoordSeq_setY(cs, i, coords[1])
        if n == 3:
            try:
                lgeos.GEOSCoordSeq_setZ(cs, i, coords[2])
            except IndexError:
                raise ValueError("Inconsistent coordinate dimensionality")

    if update_geom is not None:
        return None
    else:
        return lgeos.GEOSGeom_createLineString(cs), n


def update_linestring_from_py(geom, ob):
    geos_linestring_from_py(ob, geom._geom, geom._ndim)
Shapely-1.6.4/shapely/geometry/multilinestring.py000066400000000000000000000076671323200062600222440ustar00rootroot00000000000000"""Collections of linestrings and related utilities
"""

import sys

if sys.version_info[0] < 3:
    range = xrange

from ctypes import c_double, c_void_p, cast, POINTER

from shapely.geos import lgeos
from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
from shapely.geometry import linestring
from shapely.geometry.proxy import CachingGeometryProxy

__all__ = ['MultiLineString', 'asMultiLineString']


class MultiLineString(BaseMultipartGeometry):
    """
    A collection of one or more line strings
    
    A MultiLineString has non-zero length and zero area.

    Attributes
    ----------
    geoms : sequence
        A sequence of LineStrings
    """

    def __init__(self, lines=None):
        """
        Parameters
        ----------
        lines : sequence
            A sequence of line-like coordinate sequences or objects that
            provide the numpy array interface, including instances of
            LineString.

        Example
        -------
        Construct a collection containing one line string.

          >>> lines = MultiLineString( [[[0.0, 0.0], [1.0, 2.0]]] )
        """
        super(MultiLineString, self).__init__()

        if not lines:
            # allow creation of empty multilinestrings, to support unpickling
            pass
        else:
            self._geom, self._ndim = geos_multilinestring_from_py(lines)

    def shape_factory(self, *args):
        return linestring.LineString(*args)

    @property
    def __geo_interface__(self):
        return {
            'type': 'MultiLineString',
            'coordinates': tuple(tuple(c for c in g.coords) for g in self.geoms)
            }

    def svg(self, scale_factor=1., stroke_color=None):
        """Returns a group of SVG polyline elements for the LineString geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        stroke_color : str, optional
            Hex string for stroke color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if stroke_color is None:
            stroke_color = "#66cc99" if self.is_valid else "#ff3333"
        return '' + \
            ''.join(p.svg(scale_factor, stroke_color) for p in self) + \
            ''


class MultiLineStringAdapter(CachingGeometryProxy, MultiLineString):
    
    context = None
    _other_owned = False

    def __init__(self, context):
        self.context = context
        self.factory = geos_multilinestring_from_py

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.context[0].__array_interface__
            n = array['shape'][1]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.context[0][0])


def asMultiLineString(context):
    """Adapts a sequence of objects to the MultiLineString interface"""
    return MultiLineStringAdapter(context)


def geos_multilinestring_from_py(ob):
    # ob must be either a MultiLineString, a sequence, or 
    # array of sequences or arrays
    
    if isinstance(ob, MultiLineString):
         return geos_geom_from_py(ob)

    obs = getattr(ob, 'geoms', ob)
    L = len(obs)
    assert L >= 1
    exemplar = obs[0]
    try:
        N = len(exemplar[0])
    except TypeError:
        N = exemplar._ndim
    if N not in (2, 3):
        raise ValueError("Invalid coordinate dimensionality")

    # Array of pointers to point geometries
    subs = (c_void_p * L)()
    
    # add to coordinate sequence
    for l in range(L):
        geom, ndims = linestring.geos_linestring_from_py(obs[l])
        subs[l] = cast(geom, c_void_p)
            
    return (lgeos.GEOSGeom_createCollection(5, subs, L), N)


# Test runner
def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()
Shapely-1.6.4/shapely/geometry/multipoint.py000066400000000000000000000115701323200062600212030ustar00rootroot00000000000000"""Collections of points and related utilities
"""

import sys

if sys.version_info[0] < 3:
    range = xrange

from ctypes import byref, c_double, c_void_p, cast, POINTER
from ctypes import ArgumentError

from shapely.geos import lgeos
from shapely.geometry.base import (
    BaseMultipartGeometry, exceptNull, geos_geom_from_py)
from shapely.geometry import point
from shapely.geometry.proxy import CachingGeometryProxy

__all__ = ['MultiPoint', 'asMultiPoint']


class MultiPoint(BaseMultipartGeometry):

    """A collection of one or more points

    A MultiPoint has zero area and zero length.

    Attributes
    ----------
    geoms : sequence
        A sequence of Points
    """

    def __init__(self, points=None):
        """
        Parameters
        ----------
        points : sequence
            A sequence of (x, y [,z]) numeric coordinate pairs or triples or a
            sequence of objects that implement the numpy array interface,
            including instaces of Point.

        Example
        -------
        Construct a 2 point collection

          >>> ob = MultiPoint([[0.0, 0.0], [1.0, 2.0]])
          >>> len(ob.geoms)
          2
          >>> type(ob.geoms[0]) == Point
          True
        """
        super(MultiPoint, self).__init__()

        if points is None or len(points) == 0:
            # allow creation of empty multipoints, to support unpickling
            pass
        else:
            self._geom, self._ndim = geos_multipoint_from_py(points)

    def shape_factory(self, *args):
        return point.Point(*args)

    @property
    def __geo_interface__(self):
        return {
            'type': 'MultiPoint',
            'coordinates': tuple([g.coords[0] for g in self.geoms])
            }

    def svg(self, scale_factor=1., fill_color=None):
        """Returns a group of SVG circle elements for the MultiPoint geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG circle diameters.  Default is 1.
        fill_color : str, optional
            Hex string for fill color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if fill_color is None:
            fill_color = "#66cc99" if self.is_valid else "#ff3333"
        return '' + \
            ''.join(p.svg(scale_factor, fill_color) for p in self) + \
            ''

    @property
    @exceptNull
    def ctypes(self):
        if not self._ctypes_data:
            temp = c_double()
            n = self._ndim
            m = len(self.geoms)
            array_type = c_double * (m * n)
            data = array_type()
            for i in range(m):
                g = self.geoms[i]._geom
                cs = lgeos.GEOSGeom_getCoordSeq(g)
                lgeos.GEOSCoordSeq_getX(cs, 0, byref(temp))
                data[n*i] = temp.value
                lgeos.GEOSCoordSeq_getY(cs, 0, byref(temp))
                data[n*i+1] = temp.value
                if n == 3: # TODO: use hasz
                    lgeos.GEOSCoordSeq_getZ(cs, 0, byref(temp))
                    data[n*i+2] = temp.value
            self._ctypes_data = data
        return self._ctypes_data

    @exceptNull
    def array_interface(self):
        """Provide the Numpy array protocol."""
        ai = self.array_interface_base
        ai.update({'shape': (len(self.geoms), self._ndim)})
        return ai
    __array_interface__ = property(array_interface)


class MultiPointAdapter(CachingGeometryProxy, MultiPoint):

    context = None
    _other_owned = False

    def __init__(self, context):
        self.context = context
        self.factory = geos_multipoint_from_py

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.context.__array_interface__
            n = array['shape'][1]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.context[0])

    @property
    def __array_interface__(self):
        """Provide the Numpy array protocol."""
        try:
            return self.context.__array_interface__
        except AttributeError:
            return self.array_interface()


def asMultiPoint(context):
    """Adapt a sequence of objects to the MultiPoint interface"""
    return MultiPointAdapter(context)


def geos_multipoint_from_py(ob):
    if isinstance(ob, MultiPoint):
        return geos_geom_from_py(ob)

    m = len(ob)
    try:
        n = len(ob[0])
    except TypeError:
        n = ob[0]._ndim
    assert n == 2 or n == 3

    # Array of pointers to point geometries
    subs = (c_void_p * m)()

    # add to coordinate sequence
    for i in range(m):
        coords = ob[i]
        geom, ndims = point.geos_point_from_py(coords)
        subs[i] = cast(geom, c_void_p)

    return lgeos.GEOSGeom_createCollection(4, subs, m), n
Shapely-1.6.4/shapely/geometry/multipolygon.py000066400000000000000000000126621323200062600215440ustar00rootroot00000000000000"""Collections of polygons and related utilities
"""

import sys

if sys.version_info[0] < 3:
    range = xrange

from ctypes import c_void_p, cast

from shapely.geos import lgeos
from shapely.geometry.base import BaseMultipartGeometry, geos_geom_from_py
from shapely.geometry import polygon
from shapely.geometry.proxy import CachingGeometryProxy

__all__ = ['MultiPolygon', 'asMultiPolygon']


class MultiPolygon(BaseMultipartGeometry):

    """A collection of one or more polygons
    
    If component polygons overlap the collection is `invalid` and some
    operations on it may fail.
    
    Attributes
    ----------
    geoms : sequence
        A sequence of `Polygon` instances
    """

    def __init__(self, polygons=None, context_type='polygons'):
        """
        Parameters
        ----------
        polygons : sequence
            A sequence of (shell, holes) tuples where shell is the sequence
            representation of a linear ring (see linearring.py) and holes is
            a sequence of such linear rings

        Example
        -------
        Construct a collection from a sequence of coordinate tuples

          >>> ob = MultiPolygon( [
          ...     (
          ...     ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), 
          ...     [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))]
          ...     )
          ... ] )
          >>> len(ob.geoms)
          1
          >>> type(ob.geoms[0]) == Polygon
          True
        """
        super(MultiPolygon, self).__init__()

        if not polygons:
            # allow creation of empty multipolygons, to support unpickling
            pass
        elif context_type == 'polygons':
            self._geom, self._ndim = geos_multipolygon_from_polygons(polygons)
        elif context_type == 'geojson':
            self._geom, self._ndim = geos_multipolygon_from_py(polygons)

    def shape_factory(self, *args):
        return polygon.Polygon(*args)

    @property
    def __geo_interface__(self):
        allcoords = []
        for geom in self.geoms:
            coords = []
            coords.append(tuple(geom.exterior.coords))
            for hole in geom.interiors:
                coords.append(tuple(hole.coords))
            allcoords.append(tuple(coords))
        return {
            'type': 'MultiPolygon',
            'coordinates': allcoords
            }

    def svg(self, scale_factor=1., fill_color=None):
        """Returns group of SVG path elements for the MultiPolygon geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        fill_color : str, optional
            Hex string for fill color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if fill_color is None:
            fill_color = "#66cc99" if self.is_valid else "#ff3333"
        return '' + \
            ''.join(p.svg(scale_factor, fill_color) for p in self) + \
            ''


class MultiPolygonAdapter(CachingGeometryProxy, MultiPolygon):
    
    context = None
    _other_owned = False

    def __init__(self, context, context_type='polygons'):
        self.context = context
        if context_type == 'geojson':
            self.factory = geos_multipolygon_from_py
        elif context_type == 'polygons':
            self.factory = geos_multipolygon_from_polygons

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.context[0][0].__array_interface__
            n = array['shape'][1]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.context[0][0][0])


def asMultiPolygon(context):
    """Adapts a sequence of objects to the MultiPolygon interface"""
    return MultiPolygonAdapter(context)


def geos_multipolygon_from_py(ob):
    """ob must provide Python geo interface coordinates."""
    L = len(ob)
    assert L >= 1
    
    N = len(ob[0][0][0])
    assert N == 2 or N == 3

    subs = (c_void_p * L)()
    for l in range(L):
        geom, ndims = polygon.geos_polygon_from_py(ob[l][0], ob[l][1:])
        subs[l] = cast(geom, c_void_p)
            
    return (lgeos.GEOSGeom_createCollection(6, subs, L), N)


def geos_multipolygon_from_polygons(arg):
    """
    ob must be either a MultiPolygon, sequence or array of sequences 
    or arrays.
    
    """
    if isinstance(arg, MultiPolygon):
        return geos_geom_from_py(arg)

    obs = getattr(arg, 'geoms', arg)
    obs = [ob for ob in obs
           if ob and not (isinstance(ob, polygon.Polygon) and ob.is_empty)]
    L = len(obs)

    # Bail immediately if we have no input points.
    if L <= 0:
        return (lgeos.GEOSGeom_createEmptyCollection(6), 3)

    exemplar = obs[0]
    try:
        N = len(exemplar[0][0])
    except TypeError:
        N = exemplar._ndim
    
    assert N == 2 or N == 3

    subs = (c_void_p * L)()

    for i, ob in enumerate(obs):
        if isinstance(ob, polygon.Polygon):
            shell = ob.exterior
            holes = ob.interiors
        else:
            shell = ob[0]
            holes = ob[1]

        geom, ndims = polygon.geos_polygon_from_py(shell, holes)
        subs[i] = cast(geom, c_void_p)

    return (lgeos.GEOSGeom_createCollection(6, subs, L), N)

# Test runner
def _test():
    import doctest
    doctest.testmod()

if __name__ == "__main__":
    _test()
Shapely-1.6.4/shapely/geometry/point.py000066400000000000000000000142751323200062600201350ustar00rootroot00000000000000"""Points and related utilities
"""

from ctypes import c_double
from ctypes import cast, POINTER

from shapely.errors import DimensionError
from shapely.geos import lgeos
from shapely.geometry.base import BaseGeometry, geos_geom_from_py
from shapely.geometry.proxy import CachingGeometryProxy

__all__ = ['Point', 'asPoint']


class Point(BaseGeometry):
    """
    A zero dimensional feature

    A point has zero length and zero area.

    Attributes
    ----------
    x, y, z : float
        Coordinate values

    Example
    -------
      >>> p = Point(1.0, -1.0)
      >>> print(p)
      POINT (1.0000000000000000 -1.0000000000000000)
      >>> p.y
      -1.0
      >>> p.x
      1.0
    """

    def __init__(self, *args):
        """
        Parameters
        ----------
        There are 2 cases:

        1) 1 parameter: this must satisfy the numpy array protocol.
        2) 2 or more parameters: x, y, z : float
            Easting, northing, and elevation.
        """
        BaseGeometry.__init__(self)
        if len(args) > 0:
            self._set_coords(*args)

    # Coordinate getters and setters

    @property
    def x(self):
        """Return x coordinate."""
        return self.coords[0][0]

    @property
    def y(self):
        """Return y coordinate."""
        return self.coords[0][1]

    @property
    def z(self):
        """Return z coordinate."""
        if self._ndim != 3:
            raise DimensionError("This point has no z coordinate.")
        return self.coords[0][2]

    @property
    def __geo_interface__(self):
        return {
            'type': 'Point',
            'coordinates': self.coords[0]
            }

    def svg(self, scale_factor=1., fill_color=None):
        """Returns SVG circle element for the Point geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG circle diameter.  Default is 1.
        fill_color : str, optional
            Hex string for fill color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if fill_color is None:
            fill_color = "#66cc99" if self.is_valid else "#ff3333"
        return (
            ''
            ).format(self, 3. * scale_factor, 1. * scale_factor, fill_color)

    @property
    def ctypes(self):
        if not self._ctypes_data:
            array_type = c_double * self._ndim
            array = array_type()
            xy = self.coords[0]
            array[0] = xy[0]
            array[1] = xy[1]
            if self._ndim == 3:
                array[2] = xy[2]
            self._ctypes_data = array
        return self._ctypes_data

    def array_interface(self):
        """Provide the Numpy array protocol."""
        if self.is_empty:
            ai = {'version': 3, 'typestr': '>> x, y = Point(0, 0).xy
          >>> list(x)
          [0.0]
          >>> list(y)
          [0.0]
        """
        return self.coords.xy


class PointAdapter(CachingGeometryProxy, Point):

    _other_owned = False

    def __init__(self, context):
        self.context = context
        self.factory = geos_point_from_py

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.context.__array_interface__
            n = array['shape'][0]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.context)

    @property
    def __array_interface__(self):
        """Provide the Numpy array protocol."""
        try:
            return self.context.__array_interface__
        except AttributeError:
            return self.array_interface()

    _get_coords = BaseGeometry._get_coords

    def _set_coords(self, ob):
        raise NotImplementedError("Adapters can not modify their sources")

    coords = property(_get_coords)


def asPoint(context):
    """Adapt an object to the Point interface"""
    return PointAdapter(context)


def geos_point_from_py(ob, update_geom=None, update_ndim=0):
    """Create a GEOS geom from an object that is a Point, a coordinate sequence
    or that provides the array interface.

    Returns the GEOS geometry and the number of its dimensions.
    """
    if isinstance(ob, Point):
        return geos_geom_from_py(ob)

    # Accept either (x, y) or [(x, y)]
    if not hasattr(ob, '__getitem__'):  # Iterators, e.g. Python 3 zip
        ob = list(ob)

    if isinstance(ob[0], tuple):
        coords = ob[0]
    else:
        coords = ob
    n = len(coords)
    dx = c_double(coords[0])
    dy = c_double(coords[1])
    dz = None
    if n == 3:
        dz = c_double(coords[2])

    if update_geom:
        cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
        if n != update_ndim:
            raise ValueError(
                "Wrong coordinate dimensions; this geometry has dimensions: "
                "%d" % update_ndim)
    else:
        cs = lgeos.GEOSCoordSeq_create(1, n)

    # Because of a bug in the GEOS C API, always set X before Y
    lgeos.GEOSCoordSeq_setX(cs, 0, dx)
    lgeos.GEOSCoordSeq_setY(cs, 0, dy)
    if n == 3:
        lgeos.GEOSCoordSeq_setZ(cs, 0, dz)

    if update_geom:
        return None
    else:
        return lgeos.GEOSGeom_createPoint(cs), n


def update_point_from_py(geom, ob):
    geos_point_from_py(ob, geom._geom, geom._ndim)
Shapely-1.6.4/shapely/geometry/polygon.py000066400000000000000000000357511323200062600204750ustar00rootroot00000000000000"""Polygons and their linear ring components
"""

import sys

if sys.version_info[0] < 3:
    range = xrange

from ctypes import c_double, c_void_p, cast, POINTER
from ctypes import ArgumentError
import weakref

from shapely.algorithms.cga import signed_area
#from shapely.coords import required
from shapely.geos import lgeos
from shapely.geometry.base import BaseGeometry, geos_geom_from_py
from shapely.geometry.linestring import LineString, LineStringAdapter
from shapely.geometry.proxy import PolygonProxy

__all__ = ['Polygon', 'asPolygon', 'LinearRing', 'asLinearRing']


class LinearRing(LineString):
    """
    A closed one-dimensional feature comprising one or more line segments

    A LinearRing that crosses itself or touches itself at a single point is
    invalid and operations on it may fail.
    """

    def __init__(self, coordinates=None):
        """
        Parameters
        ----------
        coordinates : sequence
            A sequence of (x, y [,z]) numeric coordinate pairs or triples

        Rings are implicitly closed. There is no need to specific a final
        coordinate pair identical to the first.

        Example
        -------
        Construct a square ring.

          >>> ring = LinearRing( ((0, 0), (0, 1), (1 ,1 ), (1 , 0)) )
          >>> ring.is_closed
          True
          >>> ring.length
          4.0
        """
        BaseGeometry.__init__(self)
        if coordinates is not None:
            self._set_coords(coordinates)

    @property
    def __geo_interface__(self):
        return {
            'type': 'LinearRing',
            'coordinates': tuple(self.coords)
            }

    # Coordinate access

    _get_coords = BaseGeometry._get_coords

    def _set_coords(self, coordinates):
        self.empty()
        ret = geos_linearring_from_py(coordinates)
        if ret is not None:
            self._geom, self._ndim = ret

    coords = property(_get_coords, _set_coords)

    def __setstate__(self, state):
        """WKB doesn't differentiate between LineString and LinearRing so we
        need to move the coordinate sequence into the correct geometry type"""
        super(LinearRing, self).__setstate__(state)
        cs = lgeos.GEOSGeom_getCoordSeq(self.__geom__)
        cs_clone = lgeos.GEOSCoordSeq_clone(cs)
        lgeos.GEOSGeom_destroy(self.__geom__)
        self.__geom__ = lgeos.GEOSGeom_createLinearRing(cs_clone)

    @property
    def is_ccw(self):
        """True is the ring is oriented counter clock-wise"""
        return bool(self.impl['is_ccw'](self))

    @property
    def is_simple(self):
        """True if the geometry is simple, meaning that any self-intersections
        are only at boundary points, else False"""
        return LineString(self).is_simple


class LinearRingAdapter(LineStringAdapter):

    __p__ = None

    def __init__(self, context):
        self.context = context
        self.factory = geos_linearring_from_py

    @property
    def __geo_interface__(self):
        return {
            'type': 'LinearRing',
            'coordinates': tuple(self.coords)
            }

    coords = property(BaseGeometry._get_coords)


def asLinearRing(context):
    """Adapt an object to the LinearRing interface"""
    return LinearRingAdapter(context)


class InteriorRingSequence(object):

    _factory = None
    _geom = None
    __p__ = None
    _ndim = None
    _index = 0
    _length = 0
    __rings__ = None
    _gtag = None

    def __init__(self, parent):
        self.__p__ = parent
        self._geom = parent._geom
        self._ndim = parent._ndim

    def __iter__(self):
        self._index = 0
        self._length = self.__len__()
        return self

    def __next__(self):
        if self._index < self._length:
            ring = self._get_ring(self._index)
            self._index += 1
            return ring
        else:
            raise StopIteration

    if sys.version_info[0] < 3:
        next = __next__

    def __len__(self):
        return lgeos.GEOSGetNumInteriorRings(self._geom)

    def __getitem__(self, key):
        m = self.__len__()
        if isinstance(key, int):
            if key + m < 0 or key >= m:
                raise IndexError("index out of range")
            if key < 0:
                i = m + key
            else:
                i = key
            return self._get_ring(i)
        elif isinstance(key, slice):
            res = []
            start, stop, stride = key.indices(m)
            for i in range(start, stop, stride):
                res.append(self._get_ring(i))
            return res
        else:
            raise TypeError("key must be an index or slice")

    @property
    def _longest(self):
        max = 0
        for g in iter(self):
            l = len(g.coords)
            if l > max:
                max = l

    def gtag(self):
        return hash(repr(self.__p__))

    def _get_ring(self, i):
        gtag = self.gtag()
        if gtag != self._gtag:
            self.__rings__ = {}
        if i not in self.__rings__:
            g = lgeos.GEOSGetInteriorRingN(self._geom, i)
            ring = LinearRing()
            ring._geom = g
            ring.__p__ = self
            ring._other_owned = True
            ring._ndim = self._ndim
            self.__rings__[i] = weakref.ref(ring)
        return self.__rings__[i]()


class Polygon(BaseGeometry):
    """
    A two-dimensional figure bounded by a linear ring

    A polygon has a non-zero area. It may have one or more negative-space
    "holes" which are also bounded by linear rings. If any rings cross each
    other, the feature is invalid and operations on it may fail.

    Attributes
    ----------
    exterior : LinearRing
        The ring which bounds the positive space of the polygon.
    interiors : sequence
        A sequence of rings which bound all existing holes.
    """

    _exterior = None
    _interiors = []
    _ndim = 2

    def __init__(self, shell=None, holes=None):
        """
        Parameters
        ----------
        shell : sequence
            A sequence of (x, y [,z]) numeric coordinate pairs or triples
        holes : sequence
            A sequence of objects which satisfy the same requirements as the
            shell parameters above

        Example
        -------
        Create a square polygon with no holes

          >>> coords = ((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.))
          >>> polygon = Polygon(coords)
          >>> polygon.area
          1.0
        """
        BaseGeometry.__init__(self)

        if shell is not None:
            ret = geos_polygon_from_py(shell, holes)
            if ret is not None:
                self._geom, self._ndim = ret
            else:
                self.empty()

    @property
    def exterior(self):
        if self.is_empty:
            return None
        elif self._exterior is None or self._exterior() is None:
            g = lgeos.GEOSGetExteriorRing(self._geom)
            ring = LinearRing()
            ring._geom = g
            ring.__p__ = self
            ring._other_owned = True
            ring._ndim = self._ndim
            self._exterior = weakref.ref(ring)
        return self._exterior()

    @property
    def interiors(self):
        if self.is_empty:
            return []
        return InteriorRingSequence(self)

    def __eq__(self, other):
        if not isinstance(other, Polygon):
            return False
        check_empty = (self.is_empty, other.is_empty)
        if all(check_empty):
            return True
        elif any(check_empty):
            return False
        my_coords = [
            tuple(self.exterior.coords),
            [tuple(interior.coords) for interior in self.interiors]
        ]
        other_coords = [
            tuple(other.exterior.coords),
            [tuple(interior.coords) for interior in other.interiors]
        ]
        return my_coords == other_coords

    def __ne__(self, other):
        return not self.__eq__(other)

    __hash__ = None

    @property
    def ctypes(self):
        if not self._ctypes_data:
            self._ctypes_data = self.exterior.ctypes
        return self._ctypes_data

    @property
    def __array_interface__(self):
        raise NotImplementedError(
        "A polygon does not itself provide the array interface. Its rings do.")

    def _get_coords(self):
        raise NotImplementedError(
        "Component rings have coordinate sequences, but the polygon does not")

    def _set_coords(self, ob):
        raise NotImplementedError(
        "Component rings have coordinate sequences, but the polygon does not")

    @property
    def coords(self):
        raise NotImplementedError(
        "Component rings have coordinate sequences, but the polygon does not")

    @property
    def __geo_interface__(self):
        if not self.exterior:
            coords = []
        else:
            coords = [tuple(self.exterior.coords)]
            for hole in self.interiors:
                coords.append(tuple(hole.coords))
        return {
            'type': 'Polygon',
            'coordinates': tuple(coords)}

    def svg(self, scale_factor=1., fill_color=None):
        """Returns SVG path element for the Polygon geometry.

        Parameters
        ==========
        scale_factor : float
            Multiplication factor for the SVG stroke-width.  Default is 1.
        fill_color : str, optional
            Hex string for fill color. Default is to use "#66cc99" if
            geometry is valid, and "#ff3333" if invalid.
        """
        if self.is_empty:
            return ''
        if fill_color is None:
            fill_color = "#66cc99" if self.is_valid else "#ff3333"
        exterior_coords = [
            ["{0},{1}".format(*c) for c in self.exterior.coords]]
        interior_coords = [
            ["{0},{1}".format(*c) for c in interior.coords]
            for interior in self.interiors]
        path = " ".join([
            "M {0} L {1} z".format(coords[0], " L ".join(coords[1:]))
            for coords in exterior_coords + interior_coords])
        return (
            ''
            ).format(2. * scale_factor, path, fill_color)

    @classmethod
    def from_bounds(cls, xmin, ymin, xmax, ymax):
        """Construct a `Polygon()` from spatial bounds."""
        return cls([
            (xmin, ymin),
            (xmin, ymax),
            (xmax, ymax),
            (xmax, ymin)])


class PolygonAdapter(PolygonProxy, Polygon):

    def __init__(self, shell, holes=None):
        self.shell = shell
        self.holes = holes
        self.context = (shell, holes)
        self.factory = geos_polygon_from_py

    @property
    def _ndim(self):
        try:
            # From array protocol
            array = self.shell.__array_interface__
            n = array['shape'][1]
            assert n == 2 or n == 3
            return n
        except AttributeError:
            # Fall back on list
            return len(self.shell[0])


def asPolygon(shell, holes=None):
    """Adapt objects to the Polygon interface"""
    return PolygonAdapter(shell, holes)


def orient(polygon, sign=1.0):
    s = float(sign)
    rings = []
    ring = polygon.exterior
    if signed_area(ring)/s >= 0.0:
        rings.append(ring)
    else:
        rings.append(list(ring.coords)[::-1])
    for ring in polygon.interiors:
        if signed_area(ring)/s <= 0.0:
            rings.append(ring)
        else:
            rings.append(list(ring.coords)[::-1])
    return Polygon(rings[0], rings[1:])


def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
    # If a LinearRing is passed in, clone it and return
    # If a LineString is passed in, clone the coord seq and return a
    # LinearRing.
    #
    # NB: access to coordinates using the array protocol has been moved
    # entirely to the speedups module.

    if isinstance(ob, LineString):
        if type(ob) == LinearRing:
            return geos_geom_from_py(ob)
        elif ob.is_closed and len(ob.coords) >= 4:
            return geos_geom_from_py(ob, lgeos.GEOSGeom_createLinearRing)
        else:
            ob = list(ob.coords)

    try:
        m = len(ob)
    except TypeError:  # Iterators, e.g. Python 3 zip
        ob = list(ob)
        m = len(ob)

    if m == 0:
        return None

    n = len(ob[0])
    if m < 3:
        raise ValueError(
            "A LinearRing must have at least 3 coordinate tuples")
    assert (n == 2 or n == 3)

    # Add closing coordinates if not provided
    if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
        M = m + 1
    else:
        M = m

    # Create a coordinate sequence
    if update_geom is not None:
        if n != update_ndim:
            raise ValueError(
                "Coordinate dimensions mismatch: target geom has {} dims, "
                "update geom has {} dims".format(n, update_ndim))
        cs = lgeos.GEOSGeom_getCoordSeq(update_geom)
    else:
        cs = lgeos.GEOSCoordSeq_create(M, n)

    # add to coordinate sequence
    for i in range(m):
        coords = ob[i]
        # Because of a bug in the GEOS C API,
        # always set X before Y
        lgeos.GEOSCoordSeq_setX(cs, i, coords[0])
        lgeos.GEOSCoordSeq_setY(cs, i, coords[1])
        if n == 3:
            try:
                lgeos.GEOSCoordSeq_setZ(cs, i, coords[2])
            except IndexError:
                raise ValueError("Inconsistent coordinate dimensionality")

    # Add closing coordinates to sequence?
    if M > m:
        coords = ob[0]
        # Because of a bug in the GEOS C API,
        # always set X before Y
        lgeos.GEOSCoordSeq_setX(cs, M-1, coords[0])
        lgeos.GEOSCoordSeq_setY(cs, M-1, coords[1])
        if n == 3:
            lgeos.GEOSCoordSeq_setZ(cs, M-1, coords[2])

    if update_geom is not None:
        return None
    else:
        return lgeos.GEOSGeom_createLinearRing(cs), n


def update_linearring_from_py(geom, ob):
    geos_linearring_from_py(ob, geom._geom, geom._ndim)


def geos_polygon_from_py(shell, holes=None):

    if shell is None:
        return None

    if isinstance(shell, Polygon):
        return geos_geom_from_py(shell)

    if shell is not None:
        ret = geos_linearring_from_py(shell)
        if ret is None:
            return None

        geos_shell, ndim = ret
        if holes is not None and len(holes) > 0:
            ob = holes
            L = len(ob)
            exemplar = ob[0]
            try:
                N = len(exemplar[0])
            except TypeError:
                N = exemplar._ndim
            if not L >= 1:
                raise ValueError("number of holes must be non zero")
            if not N in (2, 3):
                raise ValueError("insufficiant coordinate dimension")

            # Array of pointers to ring geometries
            geos_holes = (c_void_p * L)()

            # add to coordinate sequence
            for l in range(L):
                geom, ndim = geos_linearring_from_py(ob[l])
                geos_holes[l] = cast(geom, c_void_p)
        else:
            geos_holes = POINTER(c_void_p)()
            L = 0

        return (
            lgeos.GEOSGeom_createPolygon(
                c_void_p(geos_shell), geos_holes, L), ndim)
Shapely-1.6.4/shapely/geometry/proxy.py000066400000000000000000000025471323200062600201640ustar00rootroot00000000000000"""Proxy for coordinates stored outside Shapely geometries
"""

from shapely.geometry.base import deserialize_wkb, EMPTY
from shapely.geos import lgeos


class CachingGeometryProxy(object):

    context = None
    factory = None
    __geom__ = EMPTY
    _gtag = None

    def __init__(self, context):
        self.context = context

    @property
    def _is_empty(self):
        return self.__geom__ in [EMPTY, None]

    def empty(self, val=EMPTY):
        if not self._is_empty and self.__geom__:
            lgeos.GEOSGeom_destroy(self.__geom__)
        self.__geom__ = val

    @property
    def _geom(self):
        """Keeps the GEOS geometry in synch with the context."""
        gtag = self.gtag()
        if gtag != self._gtag or self._is_empty:
            self.empty()
            if len(self.context) > 0:
                self.__geom__, n = self.factory(self.context)
        self._gtag = gtag
        return self.__geom__
        
    def gtag(self):
        return hash(repr(self.context))


class PolygonProxy(CachingGeometryProxy):

    @property
    def _geom(self):
        """Keeps the GEOS geometry in synch with the context."""
        gtag = self.gtag()
        if gtag != self._gtag or self._is_empty:
            self.empty()
            self.__geom__, n = self.factory(self.context[0], self.context[1])
        self._gtag = gtag
        return self.__geom__
Shapely-1.6.4/shapely/geos.py000066400000000000000000000712741323200062600161100ustar00rootroot00000000000000"""
Proxies for libgeos, GEOS-specific exceptions, and utilities
"""

import atexit
from ctypes import (
    CDLL, cdll, pointer, string_at, DEFAULT_MODE, c_void_p, c_size_t, c_char_p)
from ctypes.util import find_library
import glob
import logging
import os
import re
import sys
import threading

from .ctypes_declarations import prototype, EXCEPTION_HANDLER_FUNCTYPE
from .errors import WKBReadingError, WKTReadingError, TopologicalError, PredicateError
from . import ftools


# Add message handler to this module's logger
LOG = logging.getLogger(__name__)

# Find and load the GEOS and C libraries
# If this ever gets any longer, we'll break it into separate modules

def load_dll(libname, fallbacks=None, mode=DEFAULT_MODE):
    lib = find_library(libname)
    dll = None
    if lib is not None:
        try:
            LOG.debug("Trying `CDLL(%s)`", lib)
            dll = CDLL(lib, mode=mode)
        except OSError:
            LOG.debug("Failed `CDLL(%s)`", lib)
            pass

    if not dll and fallbacks is not None:
        for name in fallbacks:
            try:
                LOG.debug("Trying `CDLL(%s)`", name)
                dll = CDLL(name, mode=mode)
            except OSError:
                # move on to the next fallback
                LOG.debug("Failed `CDLL(%s)`", name)
                pass

    if dll:
        LOG.debug("Library path: %r", lib or name)
        LOG.debug("DLL: %r", dll)
        return dll
    else:
        # No shared library was loaded. Raise OSError.
        raise OSError(
            "Could not find lib {0} or load any of its variants {1}.".format(
                libname, fallbacks or []))

_lgeos = None

if sys.platform.startswith('linux'):
    # Test to see if we have a wheel repaired by 'auditwheel' containing its
    # own libgeos_c
    geos_whl_so = glob.glob(os.path.abspath(os.path.join(os.path.dirname(
        __file__), '.libs/libgeos_c-*.so.*')))
    if len(geos_whl_so) == 1:
        _lgeos = CDLL(geos_whl_so[0])
        LOG.debug("Found GEOS DLL: %r, using it.", _lgeos)
    else:
        alt_paths = [
            'libgeos_c.so.1',
            'libgeos_c.so',
            # anaconda
            os.path.join(sys.prefix, "lib", "libgeos_c.so"),
        ]
        _lgeos = load_dll('geos_c', fallbacks=alt_paths)
    free = load_dll('c').free
    free.argtypes = [c_void_p]
    free.restype = None

elif sys.platform == 'darwin':
    # Test to see if we have a delocated wheel with a GEOS dylib.
    geos_whl_dylib = os.path.abspath(os.path.join(os.path.dirname(
        __file__), '.dylibs/libgeos_c.1.dylib'))
    if os.path.exists(geos_whl_dylib):
        _lgeos = CDLL(geos_whl_dylib)
        LOG.debug("Found GEOS DLL: %r, using it.", _lgeos)

    else:
        if hasattr(sys, 'frozen'):
            try:
                # .app file from py2app
                alt_paths = [os.path.join(
                    os.environ['RESOURCEPATH'], '..', 'Frameworks',
                    'libgeos_c.dylib')]
            except KeyError:
                # binary from pyinstaller
                alt_paths = [
                    os.path.join(sys.executable, 'libgeos_c.dylib')]
                if hasattr(sys, '_MEIPASS'):
                    alt_paths.append(
                        os.path.join(sys._MEIPASS, 'libgeos_c.1.dylib'))
        else:
            alt_paths = [
                # anaconda
                os.path.join(sys.prefix, "lib", "libgeos_c.dylib"),
                # The Framework build from Kyng Chaos
                "/Library/Frameworks/GEOS.framework/Versions/Current/GEOS",
                # macports
                '/opt/local/lib/libgeos_c.dylib',
            ]
        _lgeos = load_dll('geos_c', fallbacks=alt_paths)

    free = load_dll('c').free
    free.argtypes = [c_void_p]
    free.restype = None

elif sys.platform == 'win32':
    try:
        egg_dlls = os.path.abspath(
            os.path.join(os.path.dirname(__file__), 'DLLs'))
        if hasattr(sys, "frozen"):
            wininst_dlls = os.path.normpath(
                os.path.abspath(sys.executable + '../../DLLS'))
        else:
            wininst_dlls = os.path.abspath(os.__file__ + "../../../DLLs")
        original_path = os.environ['PATH']
        os.environ['PATH'] = "%s;%s;%s" % \
            (egg_dlls, wininst_dlls, original_path)
        _lgeos = load_dll("geos_c.dll", fallbacks=[
            os.path.join(sys.prefix, "Library", "lib", "geos_c.dll"),
        ])
    except (ImportError, WindowsError, OSError):
        raise

    def free(m):
        try:
            cdll.msvcrt.free(m)
        except WindowsError:
            # XXX: See http://trac.gispython.org/projects/PCL/ticket/149
            pass

elif sys.platform == 'sunos5':
    _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
    free = CDLL('libc.so.1').free
    free.argtypes = [c_void_p]
    free.restype = None
else:  # other *nix systems
    _lgeos = load_dll('geos_c', fallbacks=['libgeos_c.so.1', 'libgeos_c.so'])
    free = load_dll('c', fallbacks=['libc.so.6']).free
    free.argtypes = [c_void_p]
    free.restype = None


def _geos_version():
    GEOSversion = _lgeos.GEOSversion
    GEOSversion.restype = c_char_p
    GEOSversion.argtypes = []
    geos_version_string = GEOSversion()
    if sys.version_info[0] >= 3:
        geos_version_string = geos_version_string.decode('ascii')
    res = re.findall(r'(\d+)\.(\d+)\.(\d+)', geos_version_string)
    assert len(res) == 2, res
    geos_version = tuple(int(x) for x in res[0])
    capi_version = tuple(int(x) for x in res[1])
    return geos_version_string, geos_version, capi_version

geos_version_string, geos_version, geos_capi_version = _geos_version()


# If we have the new interface, then record a baseline so that we know what
# additional functions are declared in ctypes_declarations.
if geos_version >= (3, 1, 0):
    start_set = set(_lgeos.__dict__)

# Apply prototypes for the libgeos_c functions
prototype(_lgeos, geos_version)

# If we have the new interface, automatically detect all function
# declarations, and declare their re-entrant counterpart.
if geos_version >= (3, 1, 0):
    end_set = set(_lgeos.__dict__)
    new_func_names = end_set - start_set

    for func_name in new_func_names:
        new_func_name = "%s_r" % func_name
        if hasattr(_lgeos, new_func_name):
            new_func = getattr(_lgeos, new_func_name)
            old_func = getattr(_lgeos, func_name)
            new_func.restype = old_func.restype
            if old_func.argtypes is None:
                # Handle functions that didn't take an argument before,
                # finishGEOS.
                new_func.argtypes = [c_void_p]
            else:
                new_func.argtypes = [c_void_p] + old_func.argtypes
            if old_func.errcheck is not None:
                new_func.errcheck = old_func.errcheck

    # Handle special case.
    _lgeos.initGEOS_r.restype = c_void_p
    _lgeos.initGEOS_r.argtypes = \
        [EXCEPTION_HANDLER_FUNCTYPE, EXCEPTION_HANDLER_FUNCTYPE]
    _lgeos.finishGEOS_r.argtypes = [c_void_p]


def make_logging_callback(func):
    """Error or notice handler callback producr

    Wraps a logger method, func, as a GEOS callback.
    """
    def callback(fmt, *fmt_args):
        fmt = fmt.decode('ascii')
        conversions = re.findall(r'%.', fmt)
        args = [
            string_at(arg).decode('ascii')
            for spec, arg in zip(conversions, fmt_args)
            if spec == '%s' and arg is not None]

        func(fmt, *args)

    return callback

error_handler = make_logging_callback(LOG.error)
notice_handler = make_logging_callback(LOG.info)

error_h = EXCEPTION_HANDLER_FUNCTYPE(error_handler)
notice_h = EXCEPTION_HANDLER_FUNCTYPE(notice_handler)


class WKTReader(object):

    _lgeos = None
    _reader = None

    def __init__(self, lgeos):
        """Create WKT Reader"""
        self._lgeos = lgeos
        self._reader = self._lgeos.GEOSWKTReader_create()

    def __del__(self):
        """Destroy WKT Reader"""
        if self._lgeos is not None:
            self._lgeos.GEOSWKTReader_destroy(self._reader)
            self._reader = None
            self._lgeos = None

    def read(self, text):
        """Returns geometry from WKT"""
        if sys.version_info[0] >= 3:
            text = text.encode('ascii')
        geom = self._lgeos.GEOSWKTReader_read(self._reader, c_char_p(text))
        if not geom:
            raise WKTReadingError(
                "Could not create geometry because of errors "
                "while reading input.")
        # avoid circular import dependency
        from shapely.geometry.base import geom_factory
        return geom_factory(geom)


class WKTWriter(object):

    _lgeos = None
    _writer = None

    # Establish default output settings
    defaults = {}

    if geos_version >= (3, 3, 0):

        defaults['trim'] = True
        defaults['output_dimension'] = 3

        # GEOS' defaults for methods without "get"
        _trim = False
        _rounding_precision = -1
        _old_3d = False

        @property
        def trim(self):
            """Trimming of unnecessary decimals (default: True)"""
            return getattr(self, '_trim')

        @trim.setter
        def trim(self, value):
            self._trim = bool(value)
            self._lgeos.GEOSWKTWriter_setTrim(self._writer, self._trim)

        @property
        def rounding_precision(self):
            """Rounding precision when writing the WKT.
            A precision of -1 (default) disables it."""
            return getattr(self, '_rounding_precision')

        @rounding_precision.setter
        def rounding_precision(self, value):
            self._rounding_precision = int(value)
            self._lgeos.GEOSWKTWriter_setRoundingPrecision(
                self._writer, self._rounding_precision)

        @property
        def output_dimension(self):
            """Output dimension, either 2 or 3 (default)"""
            return self._lgeos.GEOSWKTWriter_getOutputDimension(
                self._writer)

        @output_dimension.setter
        def output_dimension(self, value):
            self._lgeos.GEOSWKTWriter_setOutputDimension(
                self._writer, int(value))

        @property
        def old_3d(self):
            """Show older style for 3D WKT, without 'Z' (default: False)"""
            return getattr(self, '_old_3d')

        @old_3d.setter
        def old_3d(self, value):
            self._old_3d = bool(value)
            self._lgeos.GEOSWKTWriter_setOld3D(self._writer, self._old_3d)

    def __init__(self, lgeos, **settings):
        """Create WKT Writer

        Note: writer defaults are set differently for GEOS 3.3.0 and up.
        For example, with 'POINT Z (1 2 3)':

            newer: POINT Z (1 2 3)
            older: POINT (1.0000000000000000 2.0000000000000000)

        The older formatting can be achieved for GEOS 3.3.0 and up by setting
        the properties:
            trim = False
            output_dimension = 2
        """
        self._lgeos = lgeos
        self._writer = self._lgeos.GEOSWKTWriter_create()

        applied_settings = self.defaults.copy()
        applied_settings.update(settings)
        for name in applied_settings:
            setattr(self, name, applied_settings[name])

    def __setattr__(self, name, value):
        """Limit setting attributes"""
        if hasattr(self, name):
            object.__setattr__(self, name, value)
        else:
            raise AttributeError('%r object has no attribute %r' %
                                 (self.__class__.__name__, name))

    def __del__(self):
        """Destroy WKT Writer"""
        if self._lgeos is not None:
            self._lgeos.GEOSWKTWriter_destroy(self._writer)
            self._writer = None
            self._lgeos = None

    def write(self, geom):
        """Returns WKT string for geometry"""
        if geom is None or geom._geom is None:
            raise ValueError("Null geometry supports no operations")
        result = self._lgeos.GEOSWKTWriter_write(self._writer, geom._geom)
        text = string_at(result)
        lgeos.GEOSFree(result)
        if sys.version_info[0] >= 3:
            return text.decode('ascii')
        else:
            return text


class WKBReader(object):

    _lgeos = None
    _reader = None

    def __init__(self, lgeos):
        """Create WKB Reader"""
        self._lgeos = lgeos
        self._reader = self._lgeos.GEOSWKBReader_create()

    def __del__(self):
        """Destroy WKB Reader"""
        if self._lgeos is not None:
            self._lgeos.GEOSWKBReader_destroy(self._reader)
            self._reader = None
            self._lgeos = None

    def read(self, data):
        """Returns geometry from WKB"""
        geom = self._lgeos.GEOSWKBReader_read(
            self._reader, c_char_p(data), c_size_t(len(data)))
        if not geom:
            raise WKBReadingError(
                "Could not create geometry because of errors "
                "while reading input.")
        # avoid circular import dependency
        from shapely import geometry
        return geometry.base.geom_factory(geom)

    def read_hex(self, data):
        """Returns geometry from WKB hex"""
        if sys.version_info[0] >= 3:
            data = data.encode('ascii')
        geom = self._lgeos.GEOSWKBReader_readHEX(
            self._reader, c_char_p(data), c_size_t(len(data)))
        if not geom:
            raise WKBReadingError(
                "Could not create geometry because of errors "
                "while reading input.")
        # avoid circular import dependency
        from shapely import geometry
        return geometry.base.geom_factory(geom)


class WKBWriter(object):

    _lgeos = None
    _writer = None

    # EndianType enum in ByteOrderValues.h
    _ENDIAN_BIG = 0
    _ENDIAN_LITTLE = 1

    # Establish default output setting
    defaults = {'output_dimension': 3}

    @property
    def output_dimension(self):
        """Output dimension, either 2 or 3 (default)"""
        return self._lgeos.GEOSWKBWriter_getOutputDimension(self._writer)

    @output_dimension.setter
    def output_dimension(self, value):
        self._lgeos.GEOSWKBWriter_setOutputDimension(
            self._writer, int(value))

    @property
    def big_endian(self):
        """Byte order is big endian, True (default) or False"""
        return (self._lgeos.GEOSWKBWriter_getByteOrder(self._writer) ==
                self._ENDIAN_BIG)

    @big_endian.setter
    def big_endian(self, value):
        self._lgeos.GEOSWKBWriter_setByteOrder(
            self._writer, self._ENDIAN_BIG if value else self._ENDIAN_LITTLE)

    @property
    def include_srid(self):
        """Include SRID, True or False (default)"""
        return bool(self._lgeos.GEOSWKBWriter_getIncludeSRID(self._writer))

    @include_srid.setter
    def include_srid(self, value):
        self._lgeos.GEOSWKBWriter_setIncludeSRID(self._writer, bool(value))

    def __init__(self, lgeos, **settings):
        """Create WKB Writer"""
        self._lgeos = lgeos
        self._writer = self._lgeos.GEOSWKBWriter_create()

        applied_settings = self.defaults.copy()
        applied_settings.update(settings)
        for name in applied_settings:
            setattr(self, name, applied_settings[name])

    def __setattr__(self, name, value):
        """Limit setting attributes"""
        if hasattr(self, name):
            object.__setattr__(self, name, value)
        else:
            raise AttributeError('%r object has no attribute %r' %
                                 (self.__class__.__name__, name))

    def __del__(self):
        """Destroy WKB Writer"""
        if self._lgeos is not None:
            self._lgeos.GEOSWKBWriter_destroy(self._writer)
            self._writer = None
            self._lgeos = None

    def write(self, geom):
        """Returns WKB byte string for geometry"""
        if geom is None or geom._geom is None:
            raise ValueError("Null geometry supports no operations")
        size = c_size_t()
        result = self._lgeos.GEOSWKBWriter_write(
            self._writer, geom._geom, pointer(size))
        data = string_at(result, size.value)
        lgeos.GEOSFree(result)
        return data

    def write_hex(self, geom):
        """Returns WKB hex string for geometry"""
        if geom is None or geom._geom is None:
            raise ValueError("Null geometry supports no operations")
        size = c_size_t()
        result = self._lgeos.GEOSWKBWriter_writeHEX(
            self._writer, geom._geom, pointer(size))
        data = string_at(result, size.value)
        lgeos.GEOSFree(result)
        if sys.version_info[0] >= 3:
            return data.decode('ascii')
        else:
            return data


# Errcheck functions for ctypes

def errcheck_wkb(result, func, argtuple):
    """Returns bytes from a C pointer"""
    if not result:
        return None
    size_ref = argtuple[-1]
    size = size_ref.contents
    retval = string_at(result, size.value)[:]
    lgeos.GEOSFree(result)
    return retval


def errcheck_just_free(result, func, argtuple):
    """Returns string from a C pointer"""
    retval = string_at(result)
    lgeos.GEOSFree(result)
    if sys.version_info[0] >= 3:
        return retval.decode('ascii')
    else:
        return retval


def errcheck_null_exception(result, func, argtuple):
    """Wraps errcheck_just_free

    Raises TopologicalError if result is NULL.
    """
    if not result:
        raise TopologicalError(
            "The operation '{0}' could not be performed."
            "Likely cause is invalidity of the geometry.".format(
                func.__name__))
    return errcheck_just_free(result, func, argtuple)


def errcheck_predicate(result, func, argtuple):
    """Result is 2 on exception, 1 on True, 0 on False"""
    if result == 2:
        raise PredicateError("Failed to evaluate %s" % repr(func))
    return result


class LGEOSBase(threading.local):
    """Proxy for GEOS C API

    This is a base class. Do not instantiate.
    """
    methods = {}

    def __init__(self, dll):
        self._lgeos = dll
        self.geos_handle = None

    def __del__(self):
        """Cleanup GEOS related processes"""
        if self._lgeos is not None:
            self._lgeos.finishGEOS()
            self._lgeos = None
            self.geos_handle = None


class LGEOS300(LGEOSBase):
    """Proxy for GEOS 3.0.0-CAPI-1.4.1
    """
    geos_version = (3, 0, 0)
    geos_capi_version = (1, 4, 0)

    def __init__(self, dll):
        super(LGEOS300, self).__init__(dll)
        self.geos_handle = self._lgeos.initGEOS(notice_h, error_h)
        keys = list(self._lgeos.__dict__.keys())
        for key in keys:
            setattr(self, key, getattr(self._lgeos, key))
        self.GEOSFree = self._lgeos.free
        # Deprecated
        self.GEOSGeomToWKB_buf.errcheck = errcheck_wkb
        self.GEOSGeomToWKT.errcheck = errcheck_just_free
        self.GEOSRelate.errcheck = errcheck_null_exception
        for pred in (
                self.GEOSDisjoint,
                self.GEOSTouches,
                self.GEOSIntersects,
                self.GEOSCrosses,
                self.GEOSWithin,
                self.GEOSContains,
                self.GEOSOverlaps,
                self.GEOSEquals,
                self.GEOSEqualsExact,
                self.GEOSRelatePattern,
                self.GEOSisEmpty,
                self.GEOSisValid,
                self.GEOSisSimple,
                self.GEOSisRing,
                self.GEOSHasZ):
            pred.errcheck = errcheck_predicate

        self.methods['area'] = self.GEOSArea
        self.methods['boundary'] = self.GEOSBoundary
        self.methods['buffer'] = self.GEOSBuffer
        self.methods['centroid'] = self.GEOSGetCentroid
        self.methods['representative_point'] = self.GEOSPointOnSurface
        self.methods['convex_hull'] = self.GEOSConvexHull
        self.methods['distance'] = self.GEOSDistance
        self.methods['envelope'] = self.GEOSEnvelope
        self.methods['length'] = self.GEOSLength
        self.methods['has_z'] = self.GEOSHasZ
        self.methods['is_empty'] = self.GEOSisEmpty
        self.methods['is_ring'] = self.GEOSisRing
        self.methods['is_simple'] = self.GEOSisSimple
        self.methods['is_valid'] = self.GEOSisValid
        self.methods['disjoint'] = self.GEOSDisjoint
        self.methods['touches'] = self.GEOSTouches
        self.methods['intersects'] = self.GEOSIntersects
        self.methods['crosses'] = self.GEOSCrosses
        self.methods['within'] = self.GEOSWithin
        self.methods['contains'] = self.GEOSContains
        self.methods['overlaps'] = self.GEOSOverlaps
        self.methods['equals'] = self.GEOSEquals
        self.methods['equals_exact'] = self.GEOSEqualsExact
        self.methods['relate'] = self.GEOSRelate
        self.methods['difference'] = self.GEOSDifference
        self.methods['symmetric_difference'] = self.GEOSSymDifference
        self.methods['union'] = self.GEOSUnion
        self.methods['intersection'] = self.GEOSIntersection
        self.methods['relate_pattern'] = self.GEOSRelatePattern
        self.methods['simplify'] = self.GEOSSimplify
        self.methods['topology_preserve_simplify'] = \
            self.GEOSTopologyPreserveSimplify


class LGEOS310(LGEOSBase):
    """Proxy for GEOS 3.1.0-CAPI-1.5.0
    """
    geos_version = (3, 1, 0)
    geos_capi_version = (1, 5, 0)

    def __init__(self, dll):
        super(LGEOS310, self).__init__(dll)
        self.geos_handle = self._lgeos.initGEOS_r(notice_h, error_h)
        keys = list(self._lgeos.__dict__.keys())
        for key in [x for x in keys if not x.endswith('_r')]:
            if key + '_r' in keys:
                reentr_func = getattr(self._lgeos, key + '_r')
                attr = ftools.partial(reentr_func, self.geos_handle)
                attr.__name__ = reentr_func.__name__
                setattr(self, key, attr)
            else:
                setattr(self, key, getattr(self._lgeos, key))
        if not hasattr(self, 'GEOSFree'):
            # GEOS < 3.1.1
            self.GEOSFree = self._lgeos.free
        # Deprecated
        self.GEOSGeomToWKB_buf.func.errcheck = errcheck_wkb
        self.GEOSGeomToWKT.func.errcheck = errcheck_just_free
        self.GEOSRelate.func.errcheck = errcheck_null_exception
        for pred in (
                self.GEOSDisjoint,
                self.GEOSTouches,
                self.GEOSIntersects,
                self.GEOSCrosses,
                self.GEOSWithin,
                self.GEOSContains,
                self.GEOSOverlaps,
                self.GEOSCovers,
                self.GEOSEquals,
                self.GEOSEqualsExact,
                self.GEOSPreparedDisjoint,
                self.GEOSPreparedTouches,
                self.GEOSPreparedCrosses,
                self.GEOSPreparedWithin,
                self.GEOSPreparedOverlaps,
                self.GEOSPreparedContains,
                self.GEOSPreparedContainsProperly,
                self.GEOSPreparedCovers,
                self.GEOSPreparedIntersects,
                self.GEOSRelatePattern,
                self.GEOSisEmpty,
                self.GEOSisValid,
                self.GEOSisSimple,
                self.GEOSisRing,
                self.GEOSHasZ):
            pred.func.errcheck = errcheck_predicate

        self.GEOSisValidReason.func.errcheck = errcheck_just_free

        self.methods['area'] = self.GEOSArea
        self.methods['boundary'] = self.GEOSBoundary
        self.methods['buffer'] = self.GEOSBuffer
        self.methods['centroid'] = self.GEOSGetCentroid
        self.methods['representative_point'] = self.GEOSPointOnSurface
        self.methods['convex_hull'] = self.GEOSConvexHull
        self.methods['distance'] = self.GEOSDistance
        self.methods['envelope'] = self.GEOSEnvelope
        self.methods['length'] = self.GEOSLength
        self.methods['has_z'] = self.GEOSHasZ
        self.methods['is_empty'] = self.GEOSisEmpty
        self.methods['is_ring'] = self.GEOSisRing
        self.methods['is_simple'] = self.GEOSisSimple
        self.methods['is_valid'] = self.GEOSisValid
        self.methods['disjoint'] = self.GEOSDisjoint
        self.methods['touches'] = self.GEOSTouches
        self.methods['intersects'] = self.GEOSIntersects
        self.methods['crosses'] = self.GEOSCrosses
        self.methods['within'] = self.GEOSWithin
        self.methods['contains'] = self.GEOSContains
        self.methods['overlaps'] = self.GEOSOverlaps
        self.methods['covers'] = self.GEOSCovers
        self.methods['equals'] = self.GEOSEquals
        self.methods['equals_exact'] = self.GEOSEqualsExact
        self.methods['relate'] = self.GEOSRelate
        self.methods['difference'] = self.GEOSDifference
        self.methods['symmetric_difference'] = self.GEOSSymDifference
        self.methods['union'] = self.GEOSUnion
        self.methods['intersection'] = self.GEOSIntersection
        self.methods['prepared_disjoint'] = self.GEOSPreparedDisjoint
        self.methods['prepared_touches'] = self.GEOSPreparedTouches
        self.methods['prepared_intersects'] = self.GEOSPreparedIntersects
        self.methods['prepared_crosses'] = self.GEOSPreparedCrosses
        self.methods['prepared_within'] = self.GEOSPreparedWithin
        self.methods['prepared_contains'] = self.GEOSPreparedContains
        self.methods['prepared_contains_properly'] = \
            self.GEOSPreparedContainsProperly
        self.methods['prepared_overlaps'] = self.GEOSPreparedOverlaps
        self.methods['prepared_covers'] = self.GEOSPreparedCovers
        self.methods['relate_pattern'] = self.GEOSRelatePattern
        self.methods['simplify'] = self.GEOSSimplify
        self.methods['topology_preserve_simplify'] = \
            self.GEOSTopologyPreserveSimplify
        self.methods['cascaded_union'] = self.GEOSUnionCascaded


class LGEOS311(LGEOS310):
    """Proxy for GEOS 3.1.1-CAPI-1.6.0
    """
    geos_version = (3, 1, 1)
    geos_capi_version = (1, 6, 0)

    def __init__(self, dll):
        super(LGEOS311, self).__init__(dll)


class LGEOS320(LGEOS311):
    """Proxy for GEOS 3.2.0-CAPI-1.6.0
    """
    geos_version = (3, 2, 0)
    geos_capi_version = (1, 6, 0)

    def __init__(self, dll):
        super(LGEOS320, self).__init__(dll)

        if geos_version >= (3, 2, 0):

            def parallel_offset(geom, distance, resolution=16, join_style=1,
                                mitre_limit=5.0, side='right'):
                side = side == 'left'
                if distance < 0:
                    distance = abs(distance)
                    side = not side
                return self.GEOSSingleSidedBuffer(
                    geom, distance, resolution, join_style, mitre_limit, side)

            self.methods['parallel_offset'] = parallel_offset

        self.methods['project'] = self.GEOSProject
        self.methods['project_normalized'] = self.GEOSProjectNormalized
        self.methods['interpolate'] = self.GEOSInterpolate
        self.methods['interpolate_normalized'] = \
            self.GEOSInterpolateNormalized
        self.methods['buffer_with_style'] = self.GEOSBufferWithStyle
        self.methods['hausdorff_distance'] = self.GEOSHausdorffDistance


class LGEOS330(LGEOS320):
    """Proxy for GEOS 3.3.0-CAPI-1.7.0
    """
    geos_version = (3, 3, 0)
    geos_capi_version = (1, 7, 0)

    def __init__(self, dll):
        super(LGEOS330, self).__init__(dll)

        # GEOS 3.3.8 from homebrew has, but doesn't advertise
        # GEOSPolygonize_full. We patch it in explicitly here.
        key = 'GEOSPolygonize_full'
        func = getattr(self._lgeos, key + '_r')
        attr = ftools.partial(func, self.geos_handle)
        attr.__name__ = func.__name__
        setattr(self, key, attr)

        for pred in (self.GEOSisClosed,):
            pred.func.errcheck = errcheck_predicate

        def parallel_offset(geom, distance, resolution=16, join_style=1,
                            mitre_limit=5.0, side='right'):
            if side == 'right':
                distance *= -1
            return self.GEOSOffsetCurve(
                geom, distance, resolution, join_style, mitre_limit)

        self.methods['parallel_offset'] = parallel_offset
        self.methods['unary_union'] = self.GEOSUnaryUnion
        self.methods['is_closed'] = self.GEOSisClosed
        self.methods['cascaded_union'] = self.methods['unary_union']
        self.methods['snap'] = self.GEOSSnap
        self.methods['shared_paths'] = self.GEOSSharedPaths


class LGEOS340(LGEOS330):
    """Proxy for GEOS 3.4.0-CAPI-1.8.0
    """
    geos_version = (3, 4, 0)
    geos_capi_version = (1, 8, 0)

    def __init__(self, dll):
        super(LGEOS340, self).__init__(dll)
        self.methods['delaunay_triangulation'] = self.GEOSDelaunayTriangulation
        self.methods['nearest_points'] = self.GEOSNearestPoints


if geos_version >= (3, 4, 0):
    L = LGEOS340
elif geos_version >= (3, 3, 0):
    L = LGEOS330
elif geos_version >= (3, 2, 0):
    L = LGEOS320
elif geos_version >= (3, 1, 1):
    L = LGEOS311
elif geos_version >= (3, 1, 0):
    L = LGEOS310
else:
    L = LGEOS300

lgeos = L(_lgeos)


def cleanup(proxy):
    del proxy

atexit.register(cleanup, lgeos)
Shapely-1.6.4/shapely/impl.py000066400000000000000000000127731323200062600161130ustar00rootroot00000000000000"""Implementation of the intermediary layer between Shapely and GEOS

This is layer number 2 from the list below.

1) geometric objects: the Python OO API.
2) implementation map: an abstraction that permits different backends.
3) backend: callable objects that take Shapely geometric objects as arguments
   and, with GEOS as a backend, translate them to C data structures.
4) GEOS library: algorithms implemented in C++.

Shapely 1.2 includes a GEOS backend and it is the default.
"""

from .ftools import wraps

from shapely.algorithms import cga
from shapely.coords import BoundsOp
from shapely.geos import lgeos
from shapely.linref import ProjectOp, InterpolateOp
from shapely.predicates import BinaryPredicate, UnaryPredicate
from shapely.topology import BinaryRealProperty, BinaryTopologicalOp
from shapely.topology import UnaryRealProperty, UnaryTopologicalOp


class ImplementationError(
        AttributeError, KeyError, NotImplementedError):
    """To be raised when the registered implementation does not
    support the requested method."""


def delegated(func):
    """A delegated method raises AttributeError in the absence of backend
    support."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except KeyError:
            raise ImplementationError(
                "Method '%s' not provided by registered "
                "implementation '%s'" % (func.__name__, args[0].impl))
    return wrapper

# Map geometry methods to their GEOS delegates


class BaseImpl(object):
    """Base class for registrable implementations."""

    def __init__(self, values):
        self.map = dict(values)

    def update(self, values):
        self.map.update(values)

    def __getitem__(self, key):
        try:
            return self.map[key]
        except KeyError:
            raise ImplementationError(
                "Method '%s' not provided by registered "
                "implementation '%s'" % (key, self.map))

    def __contains__(self, key):
        return key in self.map


class GEOSImpl(BaseImpl):
    """GEOS implementation"""

    def __repr__(self):
        return '' % (
            lgeos.geos_capi_version,)


IMPL300 = {
    'area': (UnaryRealProperty, 'area'),
    'distance': (BinaryRealProperty, 'distance'),
    'length': (UnaryRealProperty, 'length'),
    #
    'boundary': (UnaryTopologicalOp, 'boundary'),
    'bounds': (BoundsOp, None),
    'centroid': (UnaryTopologicalOp, 'centroid'),
    'representative_point': (UnaryTopologicalOp, 'representative_point'),
    'envelope': (UnaryTopologicalOp, 'envelope'),
    'convex_hull': (UnaryTopologicalOp, 'convex_hull'),
    'buffer': (UnaryTopologicalOp, 'buffer'),
    #
    'difference': (BinaryTopologicalOp, 'difference'),
    'intersection': (BinaryTopologicalOp, 'intersection'),
    'symmetric_difference': (BinaryTopologicalOp, 'symmetric_difference'),
    'union': (BinaryTopologicalOp, 'union'),
    #
    'has_z': (UnaryPredicate, 'has_z'),
    'is_empty': (UnaryPredicate, 'is_empty'),
    'is_ring': (UnaryPredicate, 'is_ring'),
    'is_simple': (UnaryPredicate, 'is_simple'),
    'is_valid': (UnaryPredicate, 'is_valid'),
    #
    'relate': (BinaryPredicate, 'relate'),
    'contains': (BinaryPredicate, 'contains'),
    'crosses': (BinaryPredicate, 'crosses'),
    'disjoint': (BinaryPredicate, 'disjoint'),
    'equals': (BinaryPredicate, 'equals'),
    'intersects': (BinaryPredicate, 'intersects'),
    'overlaps': (BinaryPredicate, 'overlaps'),
    'touches': (BinaryPredicate, 'touches'),
    'within': (BinaryPredicate, 'within'),
    'covers': (BinaryPredicate, 'covers'),
    'equals_exact': (BinaryPredicate, 'equals_exact'),
    'relate_pattern': (BinaryPredicate, 'relate_pattern'),

    # First pure Python implementation
    'is_ccw': (cga.is_ccw_impl, 'is_ccw'),
    }

IMPL310 = {
    'simplify': (UnaryTopologicalOp, 'simplify'),
    'topology_preserve_simplify':
        (UnaryTopologicalOp, 'topology_preserve_simplify'),
    'prepared_disjoint': (BinaryPredicate, 'prepared_disjoint'),
    'prepared_touches': (BinaryPredicate, 'prepared_touches'),
    'prepared_crosses': (BinaryPredicate, 'prepared_crosses'),
    'prepared_within': (BinaryPredicate, 'prepared_within'),
    'prepared_overlaps': (BinaryPredicate, 'prepared_overlaps'),
    'prepared_intersects': (BinaryPredicate, 'prepared_intersects'),
    'prepared_contains': (BinaryPredicate, 'prepared_contains'),
    'prepared_contains_properly':
        (BinaryPredicate, 'prepared_contains_properly'),
    'prepared_covers': (BinaryPredicate, 'prepared_covers'),
    }

IMPL311 = {
    }

IMPL320 = {
    'parallel_offset': (UnaryTopologicalOp, 'parallel_offset'),
    'project_normalized': (ProjectOp, 'project_normalized'),
    'project': (ProjectOp, 'project'),
    'interpolate_normalized': (InterpolateOp, 'interpolate_normalized'),
    'interpolate': (InterpolateOp, 'interpolate'),
    'buffer_with_style': (UnaryTopologicalOp, 'buffer_with_style'),
    'hausdorff_distance': (BinaryRealProperty, 'hausdorff_distance'),
    }

IMPL330 = {
    'is_closed': (UnaryPredicate, 'is_closed')}


def impl_items(defs):
    return [(k, v[0](v[1])) for k, v in list(defs.items())]

imp = GEOSImpl(dict(impl_items(IMPL300)))
if lgeos.geos_version >= (3, 1, 0):
    imp.update(impl_items(IMPL310))
if lgeos.geos_version >= (3, 1, 1):
    imp.update(impl_items(IMPL311))
if lgeos.geos_version >= (3, 2, 0):
    imp.update(impl_items(IMPL320))
if lgeos.geos_version >= (3, 3, 0):
    imp.update(impl_items(IMPL330))

DefaultImplementation = imp
Shapely-1.6.4/shapely/iterops.py000066400000000000000000000021551323200062600166300ustar00rootroot00000000000000"""
Iterative forms of operations
"""

from shapely.geos import PredicateError
from shapely.topology import Delegating


class IterOp(Delegating):

    """A generating non-data descriptor.
    """

    def __call__(self, context, iterator, value=True):
        if context._geom is None:
            raise ValueError("Null geometry supports no operations")
        for item in iterator:
            try:
                this_geom, ob = item
            except TypeError:
                this_geom = item
                ob = this_geom
            if not this_geom._geom:
                raise ValueError("Null geometry supports no operations")
            try:
                retval = self.fn(context._geom, this_geom._geom)
            except Exception as err:
                self._check_topology(err, context, this_geom)
            if bool(retval) == value:
                yield ob


# utilities
disjoint = IterOp('disjoint')
touches = IterOp('touches')
intersects = IterOp('intersects')
crosses = IterOp('crosses')
within = IterOp('within')
contains = IterOp('contains')
overlaps = IterOp('overlaps')
equals = IterOp('equals')
Shapely-1.6.4/shapely/linref.py000066400000000000000000000012451323200062600164210ustar00rootroot00000000000000"""Linear referencing
"""

from shapely.topology import Delegating


class LinearRefBase(Delegating):
    def _validate_line(self, ob):
        super(LinearRefBase, self)._validate(ob)
        if not ob.geom_type in ['LinearRing', 'LineString', 'MultiLineString']:
            raise TypeError("Only linear types support this operation")

class ProjectOp(LinearRefBase):
    def __call__(self, this, other):
        self._validate_line(this)
        self._validate(other)
        return self.fn(this._geom, other._geom)

class InterpolateOp(LinearRefBase):
    def __call__(self, this, distance):
        self._validate_line(this)
        return self.fn(this._geom, distance)


Shapely-1.6.4/shapely/ops.py000066400000000000000000000430011323200062600157370ustar00rootroot00000000000000"""Support for various GEOS geometry operations
"""

import sys

if sys.version_info[0] < 3:
    from itertools import izip
else:
    izip = zip

from ctypes import byref, c_void_p, c_double

from shapely.geos import lgeos
from shapely.geometry.base import geom_factory, BaseGeometry
from shapely.geometry import asShape, asLineString, asMultiLineString, Point, MultiPoint,\
                             LineString, MultiLineString, Polygon, GeometryCollection

__all__ = ['cascaded_union', 'linemerge', 'operator', 'polygonize',
           'polygonize_full', 'transform', 'unary_union', 'triangulate', 'split']


class CollectionOperator(object):

    def shapeup(self, ob):
        if isinstance(ob, BaseGeometry):
            return ob
        else:
            try:
                return asShape(ob)
            except ValueError:
                return asLineString(ob)

    def polygonize(self, lines):
        """Creates polygons from a source of lines

        The source may be a MultiLineString, a sequence of LineString objects,
        or a sequence of objects than can be adapted to LineStrings.
        """
        source = getattr(lines, 'geoms', None) or lines
        try:
            source = iter(source)
        except TypeError:
            source = [source]
        finally:
            obs = [self.shapeup(l) for l in source]
        geom_array_type = c_void_p * len(obs)
        geom_array = geom_array_type()
        for i, line in enumerate(obs):
            geom_array[i] = line._geom
        product = lgeos.GEOSPolygonize(byref(geom_array), len(obs))
        collection = geom_factory(product)
        for g in collection.geoms:
            clone = lgeos.GEOSGeom_clone(g._geom)
            g = geom_factory(clone)
            g._other_owned = False
            yield g

    def polygonize_full(self, lines):
        """Creates polygons from a source of lines, returning the polygons
        and leftover geometries.

        The source may be a MultiLineString, a sequence of LineString objects,
        or a sequence of objects than can be adapted to LineStrings.

        Returns a tuple of objects: (polygons, dangles, cut edges, invalid ring
        lines). Each are a geometry collection.

        Dangles are edges which have one or both ends which are not incident on
        another edge endpoint. Cut edges are connected at both ends but do not
        form part of polygon. Invalid ring lines form rings which are invalid
        (bowties, etc).
        """
        source = getattr(lines, 'geoms', None) or lines
        try:
            source = iter(source)
        except TypeError:
            source = [source]
        finally:
            obs = [self.shapeup(l) for l in source]
        L = len(obs)
        subs = (c_void_p * L)()
        for i, g in enumerate(obs):
            subs[i] = g._geom
        collection = lgeos.GEOSGeom_createCollection(5, subs, L)
        dangles = c_void_p()
        cuts = c_void_p()
        invalids = c_void_p()
        product = lgeos.GEOSPolygonize_full(
            collection, byref(dangles), byref(cuts), byref(invalids))
        return (
            geom_factory(product),
            geom_factory(dangles),
            geom_factory(cuts),
            geom_factory(invalids)
            )

    def linemerge(self, lines):
        """Merges all connected lines from a source

        The source may be a MultiLineString, a sequence of LineString objects,
        or a sequence of objects than can be adapted to LineStrings.  Returns a
        LineString or MultiLineString when lines are not contiguous.
        """
        source = None
        if hasattr(lines, 'type') and lines.type == 'MultiLineString':
            source = lines
        elif hasattr(lines, '__iter__'):
            try:
                source = asMultiLineString([ls.coords for ls in lines])
            except AttributeError:
                source = asMultiLineString(lines)
        if source is None:
            raise ValueError("Cannot linemerge %s" % lines)
        result = lgeos.GEOSLineMerge(source._geom)
        return geom_factory(result)

    def cascaded_union(self, geoms):
        """Returns the union of a sequence of geometries

        This is the most efficient method of dissolving many polygons.
        """
        try:
            L = len(geoms)
        except TypeError:
            geoms = [geoms]
            L = 1
        subs = (c_void_p * L)()
        for i, g in enumerate(geoms):
            subs[i] = g._geom
        collection = lgeos.GEOSGeom_createCollection(6, subs, L)
        return geom_factory(lgeos.methods['cascaded_union'](collection))

    def unary_union(self, geoms):
        """Returns the union of a sequence of geometries

        This method replaces :meth:`cascaded_union` as the
        prefered method for dissolving many polygons.

        """
        try:
            L = len(geoms)
        except TypeError:
            geoms = [geoms]
            L = 1
        subs = (c_void_p * L)()
        for i, g in enumerate(geoms):
            subs[i] = g._geom
        collection = lgeos.GEOSGeom_createCollection(6, subs, L)
        return geom_factory(lgeos.methods['unary_union'](collection))

operator = CollectionOperator()
polygonize = operator.polygonize
polygonize_full = operator.polygonize_full
linemerge = operator.linemerge
cascaded_union = operator.cascaded_union
unary_union = operator.unary_union


def triangulate(geom, tolerance=0.0, edges=False):
    """Creates the Delaunay triangulation and returns a list of geometries

    The source may be any geometry type. All vertices of the geometry will be
    used as the points of the triangulation.

    From the GEOS documentation:
    tolerance is the snapping tolerance used to improve the robustness of
    the triangulation computation. A tolerance of 0.0 specifies that no
    snapping will take place.

    If edges is False, a list of Polygons (triangles) will be returned.
    Otherwise the list of LineString edges is returned.

    """
    func = lgeos.methods['delaunay_triangulation']
    gc = geom_factory(func(geom._geom, tolerance, int(edges)))
    return [g for g in gc.geoms]

class ValidateOp(object):
    def __call__(self, this):
        return lgeos.GEOSisValidReason(this._geom)

validate = ValidateOp()


def transform(func, geom):
    """Applies `func` to all coordinates of `geom` and returns a new
    geometry of the same type from the transformed coordinates.

    `func` maps x, y, and optionally z to output xp, yp, zp. The input
    parameters may iterable types like lists or arrays or single values.
    The output shall be of the same type. Scalars in, scalars out.
    Lists in, lists out.

    For example, here is an identity function applicable to both types
    of input.

      def id_func(x, y, z=None):
          return tuple(filter(None, [x, y, z]))

      g2 = transform(id_func, g1)

    A partially applied transform function from pyproj satisfies the
    requirements for `func`.

      from functools import partial
      import pyproj

      project = partial(
          pyproj.transform,
          pyproj.Proj(init='epsg:4326'),
          pyproj.Proj(init='epsg:26913'))

      g2 = transform(project, g1)

    Lambda expressions such as the one in

      g2 = transform(lambda x, y, z=None: (x+1.0, y+1.0), g1)

    also satisfy the requirements for `func`.
    """
    if geom.is_empty:
        return geom
    if geom.type in ('Point', 'LineString', 'LinearRing', 'Polygon'):

        # First we try to apply func to x, y, z sequences. When func is
        # optimized for sequences, this is the fastest, though zipping
        # the results up to go back into the geometry constructors adds
        # extra cost.
        try:
            if geom.type in ('Point', 'LineString', 'LinearRing'):
                return type(geom)(zip(*func(*izip(*geom.coords))))
            elif geom.type == 'Polygon':
                shell = type(geom.exterior)(
                    zip(*func(*izip(*geom.exterior.coords))))
                holes = list(type(ring)(zip(*func(*izip(*ring.coords))))
                             for ring in geom.interiors)
                return type(geom)(shell, holes)

        # A func that assumes x, y, z are single values will likely raise a
        # TypeError, in which case we'll try again.
        except TypeError:
            if geom.type in ('Point', 'LineString', 'LinearRing'):
                return type(geom)([func(*c) for c in geom.coords])
            elif geom.type == 'Polygon':
                shell = type(geom.exterior)(
                    [func(*c) for c in geom.exterior.coords])
                holes = list(type(ring)([func(*c) for c in ring.coords])
                             for ring in geom.interiors)
                return type(geom)(shell, holes)

    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
        return type(geom)([transform(func, part) for part in geom.geoms])
    else:
        raise ValueError('Type %r not recognized' % geom.type)


def nearest_points(g1, g2):
    """Returns the calculated nearest points in the input geometries

    The points are returned in the same order as the input geometries.
    """
    seq = lgeos.methods['nearest_points'](g1._geom, g2._geom)
    if seq is None:
        if g1.is_empty:
            raise ValueError('The first input geometry is empty')
        else:
            raise ValueError('The second input geometry is empty')
    x1 = c_double()
    y1 = c_double()
    x2 = c_double()
    y2 = c_double()
    lgeos.GEOSCoordSeq_getX(seq, 0, byref(x1))
    lgeos.GEOSCoordSeq_getY(seq, 0, byref(y1))
    lgeos.GEOSCoordSeq_getX(seq, 1, byref(x2))
    lgeos.GEOSCoordSeq_getY(seq, 1, byref(y2))
    p1 = Point(x1.value, y1.value)
    p2 = Point(x2.value, y2.value)
    return (p1, p2)

def snap(g1, g2, tolerance):
    """Snap one geometry to another with a given tolerance

    Vertices of the first geometry are snapped to vertices of the second
    geometry. The resulting snapped geometry is returned. The input geometries
    are not modified.

    Parameters
    ----------
    g1 : geometry
        The first geometry
    g2 : geometry
        The second geometry
    tolerence : float
        The snapping tolerance

    Example
    -------
    >>> square = Polygon([(1,1), (2, 1), (2, 2), (1, 2), (1, 1)])
    >>> line = LineString([(0,0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)])
    >>> result = snap(line, square, 0.5)
    >>> result.wkt
    'LINESTRING (0 0, 1 1, 2 1, 2.6 0.5)'
    """
    return(geom_factory(lgeos.methods['snap'](g1._geom, g2._geom, tolerance)))

def shared_paths(g1, g2):
    """Find paths shared between the two given lineal geometries

    Returns a GeometryCollection with two elements:
     - First element is a MultiLineString containing shared paths with the
       same direction for both inputs.
     - Second element is a MultiLineString containing shared paths with the
       opposite direction for the two inputs.

    Parameters
    ----------
    g1 : geometry
        The first geometry
    g2 : geometry
        The second geometry
    """
    if not isinstance(g1, LineString):
        raise TypeError("First geometry must be a LineString")
    if not isinstance(g2, LineString):
        raise TypeError("Second geometry must be a LineString")
    return(geom_factory(lgeos.methods['shared_paths'](g1._geom, g2._geom)))


class SplitOp(object):

    @staticmethod
    def _split_polygon_with_line(poly, splitter):
        """Split a Polygon with a LineString"""

        assert(isinstance(poly, Polygon))
        assert(isinstance(splitter, LineString))

        union = poly.boundary.union(splitter)

        # some polygonized geometries may be holes, we do not want them
        # that's why we test if the original polygon (poly) contains
        # an inner point of polygonized geometry (pg)
        return [pg for pg in polygonize(union) if poly.contains(pg.representative_point())]

    @staticmethod
    def _split_line_with_line(line, splitter):
        """Split a LineString with another (Multi)LineString or (Multi)Polygon"""

        # if splitter is a polygon, pick it's boundary
        if splitter.type in ('Polygon', 'MultiPolygon'):
            splitter = splitter.boundary

        assert(isinstance(line, LineString))
        assert(isinstance(splitter, LineString) or isinstance(splitter, MultiLineString))

        if splitter.crosses(line):
            # The lines cross --> return multilinestring from the split
            return line.difference(splitter)
        elif splitter.relate_pattern(line, '1********'):
            # The lines overlap at some segment (linear intersection of interiors)
            raise ValueError('Input geometry segment overlaps with the splitter.')
        else:
            # The lines do not cross --> return collection with identity line
            return [line]

    @staticmethod
    def _split_line_with_point(line, splitter):
        """Split a LineString with a Point"""

        assert(isinstance(line, LineString))
        assert(isinstance(splitter, Point))

        # check if point is in the interior of the line
        if not line.relate_pattern(splitter, '0********'):
            # point not on line interior --> return collection with single identity line
            # (REASONING: Returning a list with the input line reference and creating a
            # GeometryCollection at the general split function prevents unnecessary copying
            # of linestrings in multipoint splitting function)
            return [line]
        elif line.coords[0] == splitter.coords[0]:
            # if line is a closed ring the previous test doesn't behave as desired
            return [line]

        # point is on line, get the distance from the first point on line
        distance_on_line = line.project(splitter)
        coords = list(line.coords)
        # split the line at the point and create two new lines
        # TODO: can optimize this by accumulating the computed point-to-point distances
        for i, p in enumerate(coords):
            pd = line.project(Point(p))
            if pd == distance_on_line:
                return [
                    LineString(coords[:i+1]),
                    LineString(coords[i:])
                ]
            elif distance_on_line < pd:
                # we must interpolate here because the line might use 3D points
                cp = line.interpolate(distance_on_line)
                ls1_coords = coords[:i]
                ls1_coords.append(cp.coords[0])
                ls2_coords = [cp.coords[0]]
                ls2_coords.extend(coords[i:])
                return [LineString(ls1_coords), LineString(ls2_coords)]

    @staticmethod
    def _split_line_with_multipoint(line, splitter):
        """Split a LineString with a MultiPoint"""

        assert(isinstance(line, LineString))
        assert(isinstance(splitter, MultiPoint))

        chunks = [line]
        for pt in splitter.geoms:
            new_chunks = []
            for chunk in filter(lambda x: not x.is_empty, chunks):
                # add the newly split 2 lines or the same line if not split
                new_chunks.extend(SplitOp._split_line_with_point(chunk, pt))
            chunks = new_chunks

        return chunks

    @staticmethod
    def split(geom, splitter):
        """
        Splits a geometry by another geometry and returns a collection of geometries. This function is the theoretical
        opposite of the union of the split geometry parts. If the splitter does not split the geometry, a collection
        with a single geometry equal to the input geometry is returned.
        The function supports:
          - Splitting a (Multi)LineString by a (Multi)Point or (Multi)LineString or (Multi)Polygon
          - Splitting a (Multi)Polygon by a LineString

        It may be convenient to snap the splitter with low tolerance to the geometry. For example in the case
        of splitting a line by a point, the point must be exactly on the line, for the line to be correctly split.
        When splitting a line by a polygon, the boundary of the polygon is used for the operation.
        When splitting a line by another line, a ValueError is raised if the two overlap at some segment.

        Parameters
        ----------
        geom : geometry
            The geometry to be split
        splitter : geometry
            The geometry that will split the input geom

        Example
        -------
        >>> pt = Point((1, 1))
        >>> line = LineString([(0,0), (2,2)])
        >>> result = split(line, pt)
        >>> result.wkt
        'GEOMETRYCOLLECTION (LINESTRING (0 0, 1 1), LINESTRING (1 1, 2 2))'
        """

        if geom.type in ('MultiLineString', 'MultiPolygon'):
             return GeometryCollection([i for part in geom.geoms for i in SplitOp.split(part, splitter).geoms])

        elif geom.type == 'LineString':
            if splitter.type in ('LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'):
                split_func = SplitOp._split_line_with_line
            elif splitter.type in ('Point'):
                split_func = SplitOp._split_line_with_point
            elif splitter.type in ('MultiPoint'):
                split_func =  SplitOp._split_line_with_multipoint
            else:
                raise ValueError("Splitting a LineString with a %s is not supported" % splitter.type)

        elif geom.type == 'Polygon':
            if splitter.type == 'LineString':
                split_func = SplitOp._split_polygon_with_line
            else:
                raise ValueError("Splitting a Polygon with a %s is not supported" % splitter.type)

        else:
            raise ValueError("Splitting %s geometry is not supported" % geom.type)

        return GeometryCollection(split_func(geom, splitter))

split = SplitOp.split
Shapely-1.6.4/shapely/predicates.py000066400000000000000000000012041323200062600172600ustar00rootroot00000000000000"""
Support for GEOS spatial predicates
"""

from shapely.geos import PredicateError, TopologicalError
from shapely.topology import Delegating


class BinaryPredicate(Delegating):

    def __call__(self, this, other, *args):
        self._validate(this)
        self._validate(other, stop_prepared=True)
        try:
            return self.fn(this._geom, other._geom, *args)
        except PredicateError as err:
            # Dig deeper into causes of errors.
            self._check_topology(err, this, other)


class UnaryPredicate(Delegating):

    def __call__(self, this):
        self._validate(this)
        return self.fn(this._geom)
Shapely-1.6.4/shapely/prepared.py000066400000000000000000000055221323200062600167460ustar00rootroot00000000000000"""
Support for GEOS prepared geometry operations.
"""

from shapely.geos import lgeos
from shapely.impl import DefaultImplementation, delegated
from pickle import PicklingError


class PreparedGeometry(object):
    """
    A geometry prepared for efficient comparison to a set of other geometries.
    
    Example:
      
      >>> from shapely.geometry import Point, Polygon
      >>> triangle = Polygon(((0.0, 0.0), (1.0, 1.0), (1.0, -1.0)))
      >>> p = prep(triangle)
      >>> p.intersects(Point(0.5, 0.5))
      True
    """
   
    impl = DefaultImplementation
    
    def __init__(self, context):
        self.context = context
        self.__geom__ = lgeos.GEOSPrepare(self.context._geom)
    
    def __del__(self):
        if self.__geom__ is not None:
            try:
                lgeos.GEOSPreparedGeom_destroy(self.__geom__)
            except AttributeError:
                pass # lgeos might be empty on shutdown
        self.__geom__ = None
        self.context = None
    
    @property
    def _geom(self):
        return self.__geom__

    @delegated
    def contains(self, other):
        """Returns True if the geometry contains the other, else False"""
        return bool(self.impl['prepared_contains'](self, other))

    @delegated
    def contains_properly(self, other):
        """Returns True if the geometry properly contains the other, else False"""
        return bool(self.impl['prepared_contains_properly'](self, other))

    @delegated
    def covers(self, other):
        """Returns True if the geometry covers the other, else False"""
        return bool(self.impl['prepared_covers'](self, other))

    @delegated
    def crosses(self, other):
        """Returns True if the geometries cross, else False"""
        return bool(self.impl['prepared_crosses'](self, other))

    @delegated
    def disjoint(self, other):
        """Returns True if geometries are disjoint, else False"""
        return bool(self.impl['prepared_disjoint'](self, other))

    @delegated
    def intersects(self, other):
        """Returns True if geometries intersect, else False"""
        return bool(self.impl['prepared_intersects'](self, other))

    @delegated
    def overlaps(self, other):
        """Returns True if geometries overlap, else False"""
        return bool(self.impl['prepared_overlaps'](self, other))

    @delegated
    def touches(self, other):
        """Returns True if geometries touch, else False"""
        return bool(self.impl['prepared_touches'](self, other))

    @delegated
    def within(self, other):
        """Returns True if geometry is within the other, else False"""
        return bool(self.impl['prepared_within'](self, other))

    def __reduce__(self):
        raise PicklingError("Prepared geometries cannot be pickled.")

def prep(ob):
    """Creates and returns a prepared geometric object."""
    return PreparedGeometry(ob)

Shapely-1.6.4/shapely/speedups/000077500000000000000000000000001323200062600164165ustar00rootroot00000000000000Shapely-1.6.4/shapely/speedups/__init__.py000066400000000000000000000056001323200062600205300ustar00rootroot00000000000000import warnings

from shapely.geometry import linestring, polygon
from shapely import coords
import shapely.affinity

from ..ftools import wraps


try:
    from shapely.speedups import _speedups
    available = True
    import_error_msg = None
except ImportError:
    import sys
    available = False
    # TODO: This does not appear to do anything useful
    import_error_msg = sys.exc_info()[1]


def method_wrapper(f):
    def wrapper(*args, **kwargs):
        return f(*args, **kwargs)
    return wraps(f)(wrapper)

__all__ = ['available', 'enable', 'disable', 'enabled']
_orig = {}

# keep track of whether speedups are enabled
enabled = False


def enable():
    """Enable Cython speedups

    The shapely.speedups module contains performance enhancements written in C.
    They are automaticaly installed when Python has access to a compiler and
    GEOS development headers during installation, and are enabled by default.

    You can check if speedups are installed with the `available` attribute, and
    check if they have been enabled with the `enabled` attribute.

    >>> from shapely import speedups
    >>> speedups.available
    True
    >>> speedups.enable()
    >>> speedups.enabled
    True
    """
    if not available:
        warnings.warn("shapely.speedups not available", RuntimeWarning)
        return

    if _orig:
        return

    _orig['CoordinateSequence.ctypes'] = coords.CoordinateSequence.ctypes
    coords.CoordinateSequence.ctypes = property(_speedups.coordseq_ctypes)

    _orig['CoordinateSequence.__iter__'] = coords.CoordinateSequence.__iter__
    coords.CoordinateSequence.__iter__ = method_wrapper(
        _speedups.coordseq_iter)

    _orig['geos_linestring_from_py'] = linestring.geos_linestring_from_py
    linestring.geos_linestring_from_py = _speedups.geos_linestring_from_py

    _orig['geos_linearring_from_py'] = polygon.geos_linearring_from_py
    polygon.geos_linearring_from_py = _speedups.geos_linearring_from_py

    _orig['affine_transform'] = shapely.affinity.affine_transform

    # copy docstring from original function
    def affine_transform(geom, matrix):
        return _speedups.affine_transform(geom, matrix)
    affine_transform.__doc__ = shapely.affinity.affine_transform.__doc__
    shapely.affinity.affine_transform = affine_transform

    global enabled
    enabled = True


def disable():
    """Disable Cython speedups
    """
    if not _orig:
        return

    coords.CoordinateSequence.ctypes = _orig['CoordinateSequence.ctypes']
    coords.CoordinateSequence.__iter__ = _orig['CoordinateSequence.__iter__']
    linestring.geos_linestring_from_py = _orig['geos_linestring_from_py']
    polygon.geos_linearring_from_py = _orig['geos_linearring_from_py']
    shapely.affinity.affine_transform = _orig['affine_transform']
    _orig.clear()

    global enabled
    enabled = False

# if cython speedups are available, use them by default
if available:
    enable()
Shapely-1.6.4/shapely/speedups/_speedups.pyx000066400000000000000000000424071323200062600211560ustar00rootroot00000000000000# geos_linestring_from_py was transcribed from shapely.geometry.linestring
# geos_linearring_from_py was transcribed from shapely.geometry.polygon
# coordseq_ctypes was transcribed from shapely.coords.CoordinateSequence.ctypes
#
# Copyright (c) 2007, Sean C. Gillies
# Transcription to cython: Copyright (c) 2011, Oliver Tonnhofer

import ctypes
import logging

from shapely.geos import lgeos
from shapely.geometry import Point, LineString, LinearRing
from shapely.geometry.base import geom_factory

include "../_geos.pxi"

from libc.stdint cimport uintptr_t


log = logging.getLogger(__name__)

try:
    import numpy
    has_numpy = True
except ImportError:
    log.info("Numpy was not imported, continuing without requires()")
    has_numpy = False
    numpy = None


cdef inline GEOSGeometry *cast_geom(uintptr_t geom_addr):
    return geom_addr


cdef inline GEOSContextHandle_t cast_handle(uintptr_t handle_addr):
    return handle_addr


cdef inline GEOSCoordSequence *cast_seq(uintptr_t handle_addr):
    return handle_addr


def destroy(geom):
    GEOSGeom_destroy_r(cast_handle(lgeos.geos_handle), cast_geom(geom))


def required(ob):
    """Return an object that meets Shapely requirements for self-owned
    C-continguous data, copying if necessary, or just return the original
    object."""
    if has_numpy and hasattr(ob, '__array_interface__'):
        return numpy.require(ob, numpy.float64, ["C", "OWNDATA"])
    else:
        return ob


def geos_linestring_from_py(ob, update_geom=None, update_ndim=0):
    cdef double *cp
    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef GEOSCoordSequence *cs
    cdef GEOSGeometry *g
    cdef double dx, dy, dz
    cdef int i, n, m, sm, sn

    # If a LineString is passed in, just clone it and return
    # If a LinearRing is passed in, clone the coord seq and return a LineString
    if isinstance(ob, LineString):
        g = cast_geom(ob._geom)
        if GEOSHasZ_r(handle, g):
            n = 3
        else:
            n = 2

        if type(ob) == LineString:
            return GEOSGeom_clone_r(handle, g), n
        else:
            cs = GEOSGeom_getCoordSeq_r(handle, g)
            cs = GEOSCoordSeq_clone_r(handle, cs)
            return GEOSGeom_createLineString_r(handle, cs), n

    # If numpy is present, we use numpy.require to ensure that we have a
    # C-continguous array that owns its data. View data will be copied.
    ob = required(ob)
    try:
        # From array protocol
        array = ob.__array_interface__
        assert len(array['shape']) == 2
        m = array['shape'][0]
        if m < 2:
            raise ValueError(
                "LineStrings must have at least 2 coordinate tuples")
        try:
            n = array['shape'][1]
        except IndexError:
            raise ValueError(
                "Input %s is the wrong shape for a LineString" % str(ob))
        assert n == 2 or n == 3

        # Make pointer to the coordinate array
        if isinstance(array['data'], ctypes.Array):
            cp = ctypes.addressof(array['data'])
        else:
            cp = array['data'][0]

        # Use strides to properly index into cp
        # ob[i, j] == cp[sm*i + sn*j]
        # Just to avoid a referenced before assignment warning.
        dx = 0
        if array.get('strides', None):
            sm = array['strides'][0]/sizeof(dx)
            sn = array['strides'][1]/sizeof(dx)
        else:
            sm = n
            sn = 1

        # Create a coordinate sequence
        if update_geom is not None:
            cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
            if n != update_ndim:
                raise ValueError(
                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
                % update_ndim)
        else:
            cs = GEOSCoordSeq_create_r(handle, m, n)

        # add to coordinate sequence
        for i in xrange(m):
            dx = cp[sm*i]
            dy = cp[sm*i+sn]
            dz = 0
            if n == 3:
                dz = cp[sm*i+2*sn]
                
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, i, dx)
            GEOSCoordSeq_setY_r(handle, cs, i, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, i, dz)

    except AttributeError:
        # Fall back on list
        try:
            m = len(ob)
        except TypeError:  # Iterators, e.g. Python 3 zip
            ob = list(ob)
            m = len(ob)

        if m == 0:
            return None
        elif m < 2:
            raise ValueError(
                "LineStrings must have at least 2 coordinate tuples")

        def _coords(o):
            if isinstance(o, Point):
                return o.coords[0]
            else:
                return o

        try:
            n = len(_coords(ob[0]))
        except TypeError:
            raise ValueError(
                "Input %s is the wrong shape for a LineString" % str(ob))
        assert n == 2 or n == 3

        # Create a coordinate sequence
        if update_geom is not None:
            cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
            if n != update_ndim:
                raise ValueError(
                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
                % update_ndim)
        else:
            cs = GEOSCoordSeq_create_r(handle, m, n)

        # add to coordinate sequence
        for i in xrange(m):
            coords = _coords(ob[i])
            dx = coords[0]
            dy = coords[1]
            dz = 0
            if n == 3:
                if len(coords) != 3:
                    raise ValueError("Inconsistent coordinate dimensionality")
                dz = coords[2]
            
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, i, dx)
            GEOSCoordSeq_setY_r(handle, cs, i, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, i, dz)

    if update_geom is not None:
        return None
    else:
        return GEOSGeom_createLineString_r(handle, cs), n


def geos_linearring_from_py(ob, update_geom=None, update_ndim=0):
    cdef double *cp
    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef GEOSGeometry *g
    cdef GEOSCoordSequence *cs
    cdef double dx, dy, dz
    cdef unsigned int m
    cdef int i, n, M, sm, sn

    # If a LinearRing is passed in, just clone it and return
    # If a LineString is passed in, clone the coord seq and return a LinearRing
    if isinstance(ob, LineString):
        g = cast_geom(ob._geom)
        if GEOSHasZ_r(handle, g):
            n = 3
        else:
            n = 2

        if type(ob) == LinearRing:
            return GEOSGeom_clone_r(handle, g), n
        else:
            cs = GEOSGeom_getCoordSeq_r(handle, g)
            GEOSCoordSeq_getSize_r(handle, cs, &m)
            if GEOSisClosed_r(handle, g) and m >= 4:
                cs = GEOSCoordSeq_clone_r(handle, cs)
                return GEOSGeom_createLinearRing_r(handle, cs), n

    # If numpy is present, we use numpy.require to ensure that we have a
    # C-continguous array that owns its data. View data will be copied.
    ob = required(ob)
    try:
        # From array protocol
        array = ob.__array_interface__
        assert len(array['shape']) == 2
        m = array['shape'][0]
        n = array['shape'][1]
        if m < 3:
            raise ValueError(
                "A LinearRing must have at least 3 coordinate tuples")
        assert n == 2 or n == 3

        # Make pointer to the coordinate array
        if isinstance(array['data'], ctypes.Array):
            cp = ctypes.addressof(array['data'])
        else:
            cp = array['data'][0]

        # Use strides to properly index into cp
        # ob[i, j] == cp[sm*i + sn*j]
        dx = 0  # Just to avoid a referenced before assignment warning.
        if array.get('strides', None):
            sm = array['strides'][0]/sizeof(dx)
            sn = array['strides'][1]/sizeof(dx)
        else:
            sm = n
            sn = 1

        # Add closing coordinates to sequence?
        # Check whether the first set of coordinates matches the last.
        # If not, we'll have to close the ring later
        if (cp[0] != cp[sm*(m-1)] or cp[sn] != cp[sm*(m-1)+sn] or
            (n == 3 and cp[2*sn] != cp[sm*(m-1)+2*sn])):
            M = m + 1
        else:
            M = m

        # Create a coordinate sequence
        if update_geom is not None:
            cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
            if n != update_ndim:
                raise ValueError(
                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
                % update_ndim)
        else:
            cs = GEOSCoordSeq_create_r(handle, M, n)

        # add to coordinate sequence
        for i in xrange(m):
            dx = cp[sm*i]
            dy = cp[sm*i+sn]
            dz = 0
            if n == 3:
                dz = cp[sm*i+2*sn]
        
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, i, dx)
            GEOSCoordSeq_setY_r(handle, cs, i, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, i, dz)

        # Add closing coordinates to sequence?
        if M > m:
            dx = cp[0]
            dy = cp[sn]
            dz = 0
            if n == 3:
                dz = cp[2*sn]
        
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
            GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)
            
    except AttributeError:
        # Fall back on list
        try:
            m = len(ob)
        except TypeError:  # Iterators, e.g. Python 3 zip
            ob = list(ob)
            m = len(ob)

        if m == 0:
            return None

        n = len(ob[0])
        if m < 3:
            raise ValueError(
                "A LinearRing must have at least 3 coordinate tuples")
        assert (n == 2 or n == 3)

        # Add closing coordinates if not provided
        if m == 3 or ob[0][0] != ob[-1][0] or ob[0][1] != ob[-1][1]:
            M = m + 1
        else:
            M = m

        # Create a coordinate sequence
        if update_geom is not None:
            cs = GEOSGeom_getCoordSeq_r(handle, cast_geom(update_geom))
            if n != update_ndim:
                raise ValueError(
                "Wrong coordinate dimensions; this geometry has dimensions: %d" \
                % update_ndim)
        else:
            cs = GEOSCoordSeq_create_r(handle, M, n)
        
        # add to coordinate sequence
        for i in xrange(m):
            coords = ob[i]
            dx = coords[0]
            dy = coords[1]
            dz = 0
            if n == 3:
                dz = coords[2]
        
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, i, dx)
            GEOSCoordSeq_setY_r(handle, cs, i, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, i, dz)

        # Add closing coordinates to sequence?
        if M > m:
            coords = ob[0]
            dx = coords[0]
            dy = coords[1]
            dz = 0
            if n == 3:
                dz = coords[2]
        
            # Because of a bug in the GEOS C API, 
            # always set X before Y
            GEOSCoordSeq_setX_r(handle, cs, M-1, dx)
            GEOSCoordSeq_setY_r(handle, cs, M-1, dy)
            if n == 3:
                GEOSCoordSeq_setZ_r(handle, cs, M-1, dz)

    if update_geom is not None:
        return None
    else:
        return GEOSGeom_createLinearRing_r(handle, cs), n


def coordseq_ctypes(self):
    cdef int i, n, m
    cdef double temp = 0
    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef GEOSCoordSequence *cs
    cdef double *data_p
    self._update()
    n = self._ndim
    m = self.__len__()
    array_type = ctypes.c_double * (m * n)
    data = array_type()
    
    cs = cast_seq(self._cseq)
    data_p = ctypes.addressof(data)
    
    for i in xrange(m):
        GEOSCoordSeq_getX_r(handle, cs, i, &temp)
        data_p[n*i] = temp
        GEOSCoordSeq_getY_r(handle, cs, i, &temp)
        data_p[n*i+1] = temp
        if n == 3: # TODO: use hasz
            GEOSCoordSeq_getZ_r(handle, cs, i, &temp)
            data_p[n*i+2] = temp
    return data

def coordseq_iter(self):
    cdef int i
    cdef double dx
    cdef double dy
    cdef double dz
    cdef int has_z

    self._update()

    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef GEOSCoordSequence *cs
    cs = cast_seq(self._cseq)

    has_z = self._ndim == 3
    for i in range(self.__len__()):
        GEOSCoordSeq_getX_r(handle, cs, i, &dx)
        GEOSCoordSeq_getY_r(handle, cs, i, &dy)
        if has_z == 1:
            GEOSCoordSeq_getZ_r(handle, cs, i, &dz)
            yield (dx, dy, dz)
        else:
            yield (dx, dy)

cdef GEOSCoordSequence* transform(GEOSCoordSequence* cs,
                                  int ndim,
                                  double a,
                                  double b,
                                  double c,
                                  double d,
                                  double e,
                                  double f,
                                  double g,
                                  double h,
                                  double i,
                                  double xoff,
                                  double yoff,
                                  double zoff):
    """Performs an affine transformation on a GEOSCoordSequence
    
    Returns the transformed coordinate sequence
    """
    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef unsigned int m
    cdef GEOSCoordSequence *cs_t
    cdef double x, y, z
    cdef double x_t, y_t, z_t
    
    # create a new coordinate sequence with the same size and dimensions
    GEOSCoordSeq_getSize_r(handle, cs, &m)
    cs_t = GEOSCoordSeq_create_r(handle, m, ndim)
    
    # perform the transform
    for n in range(0, m):
        GEOSCoordSeq_getX_r(handle, cs, n, &x)
        GEOSCoordSeq_getY_r(handle, cs, n, &y)
        x_t = a * x + b * y + xoff
        y_t = d * x + e * y + yoff
        GEOSCoordSeq_setX_r(handle, cs_t, n, x_t)
        GEOSCoordSeq_setY_r(handle, cs_t, n, y_t)
    if ndim == 3:
        for n in range(0, m):
            GEOSCoordSeq_getZ_r(handle, cs, n, &z)
            z_t = g * x + h * y + i * z + zoff
            GEOSCoordSeq_setZ_r(handle, cs_t, n, z_t)
    
    return cs_t

cpdef affine_transform(geom, matrix):
    cdef double a, b, c, d, e, f, g, h, i, xoff, yoff, zoff
    if geom.is_empty:
        return geom
    if len(matrix) == 6:
        ndim = 2
        a, b, d, e, xoff, yoff = matrix
        if geom.has_z:
            ndim = 3
            i = 1.0
            c = f = g = h = zoff = 0.0
            matrix = a, b, c, d, e, f, g, h, i, xoff, yoff, zoff
    elif len(matrix) == 12:
        ndim = 3
        a, b, c, d, e, f, g, h, i, xoff, yoff, zoff = matrix
        if not geom.has_z:
            ndim = 2
            matrix = a, b, d, e, xoff, yoff
    else:
        raise ValueError("'matrix' expects either 6 or 12 coefficients")
    
    cdef GEOSContextHandle_t handle = cast_handle(lgeos.geos_handle)
    cdef GEOSCoordSequence *cs
    cdef GEOSCoordSequence *cs_t
    cdef GEOSGeometry *the_geom
    cdef GEOSGeometry *the_geom_t
    cdef int m, n
    cdef double x, y, z
    cdef double x_t, y_t, z_t
    
    # Process coordinates from each supported geometry type
    if geom.type in ('Point', 'LineString', 'LinearRing'):
        the_geom = cast_geom(geom._geom)
        cs = GEOSGeom_getCoordSeq_r(handle, the_geom)
        
        # perform the transformation
        cs_t = transform(cs, ndim, a, b, c, d, e, f, g, h, i, xoff, yoff, zoff)
        
        # create a new geometry from the coordinate sequence
        if geom.type == 'Point':
            the_geom_t = GEOSGeom_createPoint_r(handle, cs_t)
        elif geom.type == 'LineString':
            the_geom_t = GEOSGeom_createLineString_r(handle, cs_t)
        elif geom.type == 'LinearRing':
            the_geom_t = GEOSGeom_createLinearRing_r(handle, cs_t)
        
        # return the geometry as a Python object
        return geom_factory(the_geom_t)
    elif geom.type == 'Polygon':
        ring = geom.exterior
        shell = affine_transform(ring, matrix)
        holes = list(geom.interiors)
        for pos, ring in enumerate(holes):
            holes[pos] = affine_transform(ring, matrix)
        return type(geom)(shell, holes)
    elif geom.type.startswith('Multi') or geom.type == 'GeometryCollection':
        return type(geom)([affine_transform(part, matrix) for part in geom.geoms])
    else:
        raise ValueError('Type %r not recognized' % geom.type)
Shapely-1.6.4/shapely/strtree.py000066400000000000000000000040221323200062600166260ustar00rootroot00000000000000from shapely.geos import lgeos
import ctypes

class STRtree:
    """
    STRtree is an R-tree that is created using the Sort-Tile-Recursive
    algorithm. STRtree takes a sequence of geometry objects as initialization
    parameter. After initialization the query method can be used to make a
    spatial query over those objects.

    >>> from shapely.geometry import Polygon
    >>> polys = [ Polygon(((0, 0), (1, 0), (1, 1))), Polygon(((0, 1), (0, 0), (1, 0))), Polygon(((100, 100), (101, 100), (101, 101))) ]
    >>> s = STRtree(polys)
    >>> query_geom = Polygon(((-1, -1), (2, 0), (2, 2), (-1, 2)))
    >>> result = s.query(query_geom)
    >>> polys[0] in result
    True
    >>> polys[1] in result
    True
    >>> polys[2] in result
    False
    >>> # Test empty tree
    >>> s = STRtree([])
    >>> s.query(query_geom)
    []
    >>> # Test tree with one object
    >>> s = STRtree([polys[0]])
    >>> result = s.query(query_geom)
    >>> polys[0] in result
    True
    """

    def __init__(self, geoms):
        # filter empty geometries out of the input
        geoms = [geom for geom in geoms if not geom.is_empty]
        self._n_geoms = len(geoms)
        # GEOS STRtree capacity has to be > 1
        self._tree_handle = lgeos.GEOSSTRtree_create(max(2, len(geoms)))
        for geom in geoms:
            lgeos.GEOSSTRtree_insert(self._tree_handle, geom._geom, ctypes.py_object(geom))

        # Keep references to geoms.
        self._geoms = list(geoms)

    def __del__(self):
        if self._tree_handle is not None:
            lgeos.GEOSSTRtree_destroy(self._tree_handle)
            self._tree_handle = None

    def query(self, geom):
        if self._n_geoms == 0:
            return []

        result = []

        def callback(item, userdata):
            geom = ctypes.cast(item, ctypes.py_object).value
            result.append(geom)

        lgeos.GEOSSTRtree_query(self._tree_handle, geom._geom, lgeos.GEOSQueryCallback(callback), None)

        return result

if __name__ == "__main__":
    import doctest
    doctest.testmod()
Shapely-1.6.4/shapely/topology.py000066400000000000000000000043211323200062600170140ustar00rootroot00000000000000"""
Intermediaries supporting GEOS topological operations

These methods all take Shapely geometries and other Python objects and delegate
to GEOS functions via ctypes.

These methods return ctypes objects that should be recast by the caller.
"""

from ctypes import byref, c_double
from shapely.geos import TopologicalError, lgeos


class Validating(object):

    def _validate(self, ob, stop_prepared=False):
        if ob is None or ob._geom is None:
            raise ValueError("Null geometry supports no operations")
        if stop_prepared and not hasattr(ob, 'type'):
            raise ValueError("Prepared geometries cannot be operated on")


class Delegating(Validating):

    def __init__(self, name):
        self.fn = lgeos.methods[name]

    def _check_topology(self, err, *geoms):
        """Raise TopologicalError if geoms are invalid.

        Else, raise original error.
        """
        for geom in geoms:
            if not geom.is_valid:
                raise TopologicalError(
                    "The operation '%s' could not be performed. "
                    "Likely cause is invalidity of the geometry %s" % (
                        self.fn.__name__, repr(geom)))
        raise err


class BinaryRealProperty(Delegating):

    def __call__(self, this, other):
        self._validate(this)
        self._validate(other, stop_prepared=True)
        d = c_double()
        retval = self.fn(this._geom, other._geom, byref(d))
        return d.value


class UnaryRealProperty(Delegating):

    def __call__(self, this):
        self._validate(this)
        d = c_double()
        retval = self.fn(this._geom, byref(d))
        return d.value


class BinaryTopologicalOp(Delegating):

    def __call__(self, this, other, *args):
        self._validate(this)
        self._validate(other, stop_prepared=True)
        product = self.fn(this._geom, other._geom, *args)
        if product is None:
            err = TopologicalError(
                    "This operation could not be performed. Reason: unknown")
            self._check_topology(err, this, other)
        return product


class UnaryTopologicalOp(Delegating):

    def __call__(self, this, *args):
        self._validate(this)
        return self.fn(this._geom, *args)
Shapely-1.6.4/shapely/validation.py000066400000000000000000000002541323200062600172730ustar00rootroot00000000000000# TODO: allow for implementations using other than GEOS

import sys

from shapely.geos import lgeos

def explain_validity(ob):
    return lgeos.GEOSisValidReason(ob._geom)
Shapely-1.6.4/shapely/vectorized/000077500000000000000000000000001323200062600167445ustar00rootroot00000000000000Shapely-1.6.4/shapely/vectorized/__init__.py000066400000000000000000000001671323200062600210610ustar00rootroot00000000000000"""Provides multi-point element-wise operations such as ``contains``."""

from ._vectorized import (contains, touches)
Shapely-1.6.4/shapely/vectorized/_vectorized.pyx000066400000000000000000000076241323200062600220340ustar00rootroot00000000000000import cython
cimport cpython.array

import numpy as np
cimport numpy as np

import shapely.prepared


include "../_geos.pxi"


# Define the predicate function, which returns True/False from functions such as GEOSPreparedContains_r
# and GEOSPreparedWithin_r.
ctypedef char (* predicate)(GEOSContextHandle_t, const GEOSPreparedGeometry *, const GEOSGeometry *) nogil


def contains(geometry, x, y):
    """
    Vectorized (element-wise) version of `contains` which checks whether
    multiple points are contained by a single geometry.

    Parameters
    ----------
    geometry : PreparedGeometry or subclass of BaseGeometry
        The geometry which is to be checked to see whether each point is
        contained within. The geometry will be "prepared" if it is not already
        a PreparedGeometry instance.
    x : array
        The x coordinates of the points to check. 
    y : array
        The y coordinates of the points to check.

    Returns
    -------
    Mask of points contained by the given `geometry`.

    """
    return _predicated_elementwise(geometry, x, y, GEOSPreparedContains_r)


def touches(geometry, x, y):
    """
    Vectorized (element-wise) version of `touches` which checks whether
    multiple points touch the exterior of a single geometry.

    Parameters
    ----------
    geometry : PreparedGeometry or subclass of BaseGeometry
        The geometry which is to be checked to see whether each point is
        contained within. The geometry will be "prepared" if it is not already
        a PreparedGeometry instance.
    x : array
        The x coordinates of the points to check. 
    y : array
        The y coordinates of the points to check.

    Returns
    -------
    Mask of points which touch the exterior of the given `geometry`.

    """
    return _predicated_elementwise(geometry, x, y, GEOSPreparedTouches_r)


cdef _predicated_elementwise(geometry, x, y, predicate fn):
    """
    Implements elementwise True/False testing, given a predicate function
    such as "contains" or "touches". x and y arrays can be of any type, order
    and dtype, and will be cast for appropriate calling to "_predicated_1d".

    """
    # Support coordinate sequences and other array like objects.
    x, y = np.asanyarray(x), np.asanyarray(y)
    if x.shape != y.shape:
        raise ValueError('X and Y shapes must be equivalent.')

    if x.dtype != np.float64:
        x = x.astype(np.float64)
    if y.dtype != np.float64:
        y = y.astype(np.float64)

    result = _predicated_1d(geometry, x.ravel(), y.ravel(), fn)
    return result.reshape(x.shape)



@cython.boundscheck(False)
@cython.wraparound(False)
cdef _predicated_1d(geometry, np.double_t[:] x, np.double_t[:] y, predicate fn):
    
    cdef Py_ssize_t idx
    cdef unsigned int n = x.size
    cdef np.ndarray[np.uint8_t, ndim=1, cast=True] result = np.empty(n, dtype=np.uint8)
    cdef GEOSContextHandle_t geos_handle
    cdef GEOSPreparedGeometry *geos_prepared_geom
    cdef GEOSCoordSequence *cs
    cdef GEOSGeometry *point

    # Prepare the geometry if it hasn't already been prepared.
    if not isinstance(geometry, shapely.prepared.PreparedGeometry):
        geometry = shapely.prepared.prep(geometry)

    geos_h = get_geos_context_handle()
    geos_geom = geos_from_prepared(geometry)

    with nogil:
        for idx in xrange(n):
            # Construct a coordinate sequence with our x, y values.
            cs = GEOSCoordSeq_create_r(geos_h, 1, 2)
            GEOSCoordSeq_setX_r(geos_h, cs, 0, x[idx])
            GEOSCoordSeq_setY_r(geos_h, cs, 0, y[idx])
            
            # Construct a point with this sequence.
            p = GEOSGeom_createPoint_r(geos_h, cs)
            
            # Put the result of whether the point is "contained" by the
            # prepared geometry into the result array. 
            result[idx] =  fn(geos_h, geos_geom, p)
            GEOSGeom_destroy_r(geos_h, p)

    return result.view(dtype=np.bool)
Shapely-1.6.4/shapely/wkb.py000066400000000000000000000017361323200062600157320ustar00rootroot00000000000000"""Load/dump geometries using the well-known binary (WKB) format
"""

from shapely import geos

# Pickle-like convenience functions

def loads(data, hex=False):
    """Load a geometry from a WKB byte string, or hex-encoded string if
    ``hex=True``.
    """
    reader = geos.WKBReader(geos.lgeos)
    if hex:
        return reader.read_hex(data)
    else:
        return reader.read(data)

def load(fp, hex=False):
    """Load a geometry from an open file."""
    data = fp.read()
    return loads(data, hex=hex)

def dumps(ob, hex=False, **kw):
    """Dump a WKB representation of a geometry to a byte string, or a
    hex-encoded string if ``hex=True``.

    See available keyword output settings in ``shapely.geos.WKBWriter``."""
    writer = geos.WKBWriter(geos.lgeos, **kw)
    if hex:
        return writer.write_hex(ob)
    else:
        return writer.write(ob)


def dump(ob, fp, hex=False, **kw):
    """Dump a geometry to an open file."""
    fp.write(dumps(ob, hex=hex, **kw))
Shapely-1.6.4/shapely/wkt.py000066400000000000000000000012711323200062600157460ustar00rootroot00000000000000"""Load/dump geometries using the well-known text (WKT) format
"""

from shapely import geos

# Pickle-like convenience functions

def loads(data):
    """Load a geometry from a WKT string."""
    return geos.WKTReader(geos.lgeos).read(data)

def load(fp):
    """Load a geometry from an open file."""
    data = fp.read()
    return loads(data)

def dumps(ob, trim=False, **kw):
    """Dump a WKT representation of a geometry to a string.

    See available keyword output settings in ``shapely.geos.WKTWriter``.
    """
    return geos.WKTWriter(geos.lgeos, trim=trim, **kw).write(ob)

def dump(ob, fp, **settings):
    """Dump a geometry to an open file."""
    fp.write(dumps(ob, **settings))
Shapely-1.6.4/tests/000077500000000000000000000000001323200062600142635ustar00rootroot00000000000000Shapely-1.6.4/tests/__init__.py000077500000000000000000000015011323200062600163740ustar00rootroot00000000000000import sys

from shapely.geos import geos_version_string, lgeos, WKTWriter
from shapely import speedups


test_int_types = [int]

try:
    import numpy
    numpy_version = numpy.version.version
    test_int_types.extend([int, numpy.int16, numpy.int32, numpy.int64])
except ImportError:
    numpy = False
    numpy_version = 'not available'

# Show some diagnostic information; handy for Travis CI
print('Python version: ' + sys.version.replace('\n', ' '))
print('GEOS version: ' + geos_version_string)
print('Numpy version: ' + numpy_version)
print('Cython speedups: ' + str(speedups.available))

if lgeos.geos_version >= (3, 3, 0):
    # Remove any WKT writer defaults to pass tests for all versions of GEOS
    WKTWriter.defaults = {}

if sys.version_info[0:2] <= (2, 6):
    import unittest2 as unittest
else:
    import unittest
Shapely-1.6.4/tests/binascii_hex.txt000066400000000000000000000026161323200062600174560ustar00rootroot00000000000000Round-tripping geometries through hex-encoded binary
====================================================

Hex-encoded binary is the PostGIS geometry representation, and so this is a
test of the ability to store Shapely geometries in PostGIS.

Point
-----

  >>> from shapely.geometry import Point
  >>> point = Point(0.0, 0.0)
  >>> from binascii import a2b_hex, b2a_hex
  >>> x = b2a_hex(point.wkb)
  >>> from shapely import wkb
  >>> shape = wkb.loads(a2b_hex(x))
  >>> shape # doctest: +ELLIPSIS
  

LineString
----------

  >>> from shapely.geometry import LineString
  >>> line = LineString(((0.0, 0.0), (1.0, 1.0)))
  >>> x = b2a_hex(line.wkb)
  >>> shape = wkb.loads(a2b_hex(x))
  >>> shape # doctest: +ELLIPSIS
  

Polygon
----------

  >>> from shapely.geometry import Polygon
  >>> polygon = Polygon(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)))
  >>> x = b2a_hex(polygon.wkb)
  >>> shape = wkb.loads(a2b_hex(x))
  >>> shape # doctest: +ELLIPSIS
  

Polygon with hole
-----------------

  >>> polygon = Polygon(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)), [((0.1,0.1), (0.1,0.2), (0.2,0.2), (0.2,0.1))])
  >>> x = b2a_hex(polygon.wkb)
  >>> shape = wkb.loads(a2b_hex(x))
  >>> shape # doctest: +ELLIPSIS
  

Shapely-1.6.4/tests/conftest.py000066400000000000000000000016501323200062600164640ustar00rootroot00000000000000import sys

import pytest

def pytest_addoption(parser):
    parser.addoption("--with-speedups", action="store_true", default=False,
                     help="Run tests with speedups.")
    parser.addoption("--without-speedups", action="store_true", default=False,
                     help="Run tests without speedups.")

def pytest_runtest_setup(item):
    if item.config.getoption("--with-speedups"):
        import shapely.speedups
        if not shapely.speedups.available:
            print("Speedups have been demanded but are unavailable")
            sys.exit(1)
        shapely.speedups.enable()
        assert(shapely.speedups.enabled is True)
        print("Speedups enabled for %s." % item.name)
    elif item.config.getoption("--without-speedups"):
        import shapely.speedups
        shapely.speedups.disable()
        assert(shapely.speedups.enabled is False)
        print("Speedups disabled for %s." % item.name)
Shapely-1.6.4/tests/rungrind.dist000066400000000000000000000002251323200062600167770ustar00rootroot00000000000000#!/bin/sh
#export PYTHONPATH=YOUR_CUSTOM_PATH
valgrind --tool=memcheck --leak-check=yes --suppressions=valgrind-python.supp python test_doctests.py

Shapely-1.6.4/tests/test_affinity.py000077500000000000000000000260601323200062600175140ustar00rootroot00000000000000from . import unittest
from math import pi
from shapely import affinity
from shapely.wkt import loads as load_wkt
from shapely.geometry import Point


class AffineTestCase(unittest.TestCase):

    def test_affine_params(self):
        g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
        self.assertRaises(
            TypeError, affinity.affine_transform, g, None)
        self.assertRaises(
            TypeError, affinity.affine_transform, g, '123456')
        self.assertRaises(ValueError, affinity.affine_transform, g,
                          [1, 2, 3, 4, 5, 6, 7, 8, 9])
        self.assertRaises(AttributeError, affinity.affine_transform, None,
                          [1, 2, 3, 4, 5, 6])

    def test_affine_geom_types(self):

        # identity matrices, which should result with no transformation
        matrix2d = (1, 0,
                    0, 1,
                    0, 0)
        matrix3d = (1, 0, 0,
                    0, 1, 0,
                    0, 0, 1,
                    0, 0, 0)

        # empty in, empty out
        empty2d = load_wkt('MULTIPOLYGON EMPTY')
        self.assertTrue(affinity.affine_transform(empty2d, matrix2d).is_empty)

        def test_geom(g2, g3=None):
            self.assertFalse(g2.has_z)
            a2 = affinity.affine_transform(g2, matrix2d)
            self.assertFalse(a2.has_z)
            self.assertTrue(g2.equals(a2))
            if g3 is not None:
                self.assertTrue(g3.has_z)
                a3 = affinity.affine_transform(g3, matrix3d)
                self.assertTrue(a3.has_z)
                self.assertTrue(g3.equals(a3))
            return

        pt2d = load_wkt('POINT(12.3 45.6)')
        pt3d = load_wkt('POINT(12.3 45.6 7.89)')
        test_geom(pt2d, pt3d)
        ls2d = load_wkt('LINESTRING(0.9 3.4, 0.7 2, 2.5 2.7)')
        ls3d = load_wkt('LINESTRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5)')
        test_geom(ls2d, ls3d)
        lr2d = load_wkt('LINEARRING(0.9 3.4, 0.7 2, 2.5 2.7, 0.9 3.4)')
        lr3d = load_wkt(
            'LINEARRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5, 0.9 3.4 3.3)')
        test_geom(lr2d, lr3d)
        test_geom(load_wkt('POLYGON((0.9 2.3, 0.5 1.1, 2.4 0.8, 0.9 2.3), '
                           '(1.1 1.7, 0.9 1.3, 1.4 1.2, 1.1 1.7), '
                           '(1.6 1.3, 1.7 1, 1.9 1.1, 1.6 1.3))'))
        test_geom(load_wkt(
            'MULTIPOINT ((-300 300), (700 300), (-800 -1100), (200 -300))'))
        test_geom(load_wkt(
            'MULTILINESTRING((0 0, -0.7 -0.7, 0.6 -1), '
            '(-0.5 0.5, 0.7 0.6, 0 -0.6))'))
        test_geom(load_wkt(
            'MULTIPOLYGON(((900 4300, -1100 -400, 900 -800, 900 4300)), '
            '((1200 4300, 2300 4400, 1900 1000, 1200 4300)))'))
        test_geom(load_wkt('GEOMETRYCOLLECTION(POINT(20 70),'
                      ' POLYGON((60 70, 13 35, 60 -30, 60 70)),'
                      ' LINESTRING(60 70, 50 100, 80 100))'))

    def test_affine_2d(self):
        g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
        # custom scale and translate
        expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)')
        matrix2d = (2, 0,
                    0, 2.5,
                    -5, 4.1)
        a2 = affinity.affine_transform(g, matrix2d)
        self.assertTrue(a2.almost_equals(expected2d))
        self.assertFalse(a2.has_z)
        # Make sure a 3D matrix does not make a 3D shape from a 2D input
        matrix3d = (2, 0, 0,
                    0, 2.5, 0,
                    0, 0, 10,
                    -5, 4.1, 100)
        a3 = affinity.affine_transform(g, matrix3d)
        self.assertTrue(a3.almost_equals(expected2d))
        self.assertFalse(a3.has_z)

    def test_affine_3d(self):
        g2 = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)')
        g3 = load_wkt('LINESTRING(2.4 4.1 100.2, 2.4 3 132.8, 3 3 128.6)')
        # custom scale and translate
        matrix2d = (2, 0,
                    0, 2.5,
                    -5, 4.1)
        matrix3d = (2, 0, 0,
                    0, 2.5, 0,
                    0, 0, 0.3048,
                    -5, 4.1, 100)
        # Combinations of 2D and 3D geometries and matrices
        a22 = affinity.affine_transform(g2, matrix2d)
        a23 = affinity.affine_transform(g2, matrix3d)
        a32 = affinity.affine_transform(g3, matrix2d)
        a33 = affinity.affine_transform(g3, matrix3d)
        # Check dimensions
        self.assertFalse(a22.has_z)
        self.assertFalse(a23.has_z)
        self.assertTrue(a32.has_z)
        self.assertTrue(a33.has_z)
        # 2D equality checks
        expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)')
        expected3d = load_wkt('LINESTRING(-0.2 14.35 130.54096, '
                              '-0.2 11.6 140.47744, 1 11.6 139.19728)')
        expected32 = load_wkt('LINESTRING(-0.2 14.35 100.2, '
                              '-0.2 11.6 132.8, 1 11.6 128.6)')
        self.assertTrue(a22.almost_equals(expected2d))
        self.assertTrue(a23.almost_equals(expected2d))
        # Do explicit 3D check of coordinate values
        for a, e in zip(a32.coords, expected32.coords):
            for ap, ep in zip(a, e):
                self.assertAlmostEqual(ap, ep)
        for a, e in zip(a33.coords, expected3d.coords):
            for ap, ep in zip(a, e):
                self.assertAlmostEqual(ap, ep)


class TransformOpsTestCase(unittest.TestCase):

    def test_rotate(self):
        ls = load_wkt('LINESTRING(240 400, 240 300, 300 300)')
        # counter-clockwise degrees
        rls = affinity.rotate(ls, 90)
        els = load_wkt('LINESTRING(220 320, 320 320, 320 380)')
        self.assertTrue(rls.equals(els))
        # retest with named parameters for the same result
        rls = affinity.rotate(geom=ls, angle=90, origin='center')
        self.assertTrue(rls.equals(els))
        # clockwise radians
        rls = affinity.rotate(ls, -pi/2, use_radians=True)
        els = load_wkt('LINESTRING(320 380, 220 380, 220 320)')
        self.assertTrue(rls.equals(els))
        ## other `origin` parameters
        # around the centroid
        rls = affinity.rotate(ls, 90, origin='centroid')
        els = load_wkt('LINESTRING(182.5 320, 282.5 320, 282.5 380)')
        self.assertTrue(rls.equals(els))
        # around the second coordinate tuple
        rls = affinity.rotate(ls, 90, origin=ls.coords[1])
        els = load_wkt('LINESTRING(140 300, 240 300, 240 360)')
        self.assertTrue(rls.equals(els))
        # around the absolute Point of origin
        rls = affinity.rotate(ls, 90, origin=Point(0, 0))
        els = load_wkt('LINESTRING(-400 240, -300 240, -300 300)')
        self.assertTrue(rls.equals(els))

    def test_scale(self):
        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
        # test defaults of 1.0
        sls = affinity.scale(ls)
        self.assertTrue(sls.equals(ls))
        # different scaling in different dimensions
        sls = affinity.scale(ls, 2, 3, 0.5)
        els = load_wkt('LINESTRING(210 500 5, 210 200 15, 330 200 10)')
        self.assertTrue(sls.equals(els))
        # Do explicit 3D check of coordinate values
        for a, b in zip(sls.coords, els.coords):
            for ap, bp in zip(a, b):
                self.assertEqual(ap, bp)
        # retest with named parameters for the same result
        sls = affinity.scale(geom=ls, xfact=2, yfact=3, zfact=0.5,
                             origin='center')
        self.assertTrue(sls.equals(els))
        ## other `origin` parameters
        # around the centroid
        sls = affinity.scale(ls, 2, 3, 0.5, origin='centroid')
        els = load_wkt('LINESTRING(228.75 537.5, 228.75 237.5, 348.75 237.5)')
        self.assertTrue(sls.equals(els))
        # around the second coordinate tuple
        sls = affinity.scale(ls, 2, 3, 0.5, origin=ls.coords[1])
        els = load_wkt('LINESTRING(240 600, 240 300, 360 300)')
        self.assertTrue(sls.equals(els))
        # around some other 3D Point of origin
        sls = affinity.scale(ls, 2, 3, 0.5, origin=Point(100, 200, 1000))
        els = load_wkt('LINESTRING(380 800 505, 380 500 515, 500 500 510)')
        self.assertTrue(sls.equals(els))
        # Do explicit 3D check of coordinate values
        for a, b in zip(sls.coords, els.coords):
            for ap, bp in zip(a, b):
                self.assertEqual(ap, bp)

    def test_skew(self):
        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
        # test default shear angles of 0.0
        sls = affinity.skew(ls)
        self.assertTrue(sls.equals(ls))
        # different shearing in x- and y-directions
        sls = affinity.skew(ls, 15, -30)
        els = load_wkt('LINESTRING (253.39745962155615 417.3205080756888, '
                       '226.60254037844385 317.3205080756888, '
                       '286.60254037844385 282.67949192431126)')
        self.assertTrue(sls.almost_equals(els))
        # retest with radians for the same result
        sls = affinity.skew(ls, pi/12, -pi/6, use_radians=True)
        self.assertTrue(sls.almost_equals(els))
        # retest with named parameters for the same result
        sls = affinity.skew(geom=ls, xs=15, ys=-30,
                            origin='center', use_radians=False)
        self.assertTrue(sls.almost_equals(els))
        ## other `origin` parameters
        # around the centroid
        sls = affinity.skew(ls, 15, -30, origin='centroid')
        els = load_wkt('LINESTRING(258.42150697963973 406.49519052838332, '
                       '231.6265877365273980 306.4951905283833185, '
                       '291.6265877365274264 271.8541743770057337)')
        self.assertTrue(sls.almost_equals(els))
        # around the second coordinate tuple
        sls = affinity.skew(ls, 15, -30, origin=ls.coords[1])
        els = load_wkt('LINESTRING(266.7949192431123038 400, 240 300, '
                       '300 265.3589838486224153)')
        self.assertTrue(sls.almost_equals(els))
        # around the absolute Point of origin
        sls = affinity.skew(ls, 15, -30, origin=Point(0, 0))
        els = load_wkt('LINESTRING(347.179676972449101 261.435935394489832, '
                       '320.3847577293367976 161.4359353944898317, '
                       '380.3847577293367976 126.7949192431122754)')
        self.assertTrue(sls.almost_equals(els))

    def test_translate(self):
        ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)')
        # test default offset of 0.0
        tls = affinity.translate(ls)
        self.assertTrue(tls.equals(ls))
        # test all offsets
        tls = affinity.translate(ls, 100, 400, -10)
        els = load_wkt('LINESTRING(340 800 0, 340 700 20, 400 700 10)')
        self.assertTrue(tls.equals(els))
        # Do explicit 3D check of coordinate values
        for a, b in zip(tls.coords, els.coords):
            for ap, bp in zip(a, b):
                self.assertEqual(ap, bp)
        # retest with named parameters for the same result
        tls = affinity.translate(geom=ls, xoff=100, yoff=400, zoff=-10)
        self.assertTrue(tls.equals(els))


def test_suite():
    loader = unittest.TestLoader()
    return unittest.TestSuite([
        loader.loadTestsFromTestCase(AffineTestCase),
        loader.loadTestsFromTestCase(TransformOpsTestCase)])
Shapely-1.6.4/tests/test_box.py000066400000000000000000000013541323200062600164670ustar00rootroot00000000000000from . import unittest
from shapely import geometry


class BoxTestCase(unittest.TestCase):

    def test_ccw(self):
        b = geometry.box(0, 0, 1, 1, ccw=True)
        self.assertEqual(b.exterior.coords[0], (1.0, 0.0))
        self.assertEqual(b.exterior.coords[1], (1.0, 1.0))

    def test_ccw_default(self):
        b = geometry.box(0, 0, 1, 1)
        self.assertEqual(b.exterior.coords[0], (1.0, 0.0))
        self.assertEqual(b.exterior.coords[1], (1.0, 1.0))

    def test_cw(self):
        b = geometry.box(0, 0, 1, 1, ccw=False)
        self.assertEqual(b.exterior.coords[0], (0.0, 0.0))
        self.assertEqual(b.exterior.coords[1], (0.0, 1.0))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(BoxTestCase)
Shapely-1.6.4/tests/test_cga.py000066400000000000000000000023721323200062600164320ustar00rootroot00000000000000from . import unittest
from shapely.geometry.polygon import LinearRing, orient, Polygon


class RingOrientationTestCase(unittest.TestCase):
    def test_ccw(self):
        ring = LinearRing([(1, 0), (0, 1), (0, 0)])
        self.assertTrue(ring.is_ccw)

    def test_cw(self):
        ring = LinearRing([(0, 0), (0, 1), (1, 0)])
        self.assertFalse(ring.is_ccw)


class PolygonOrienterTestCase(unittest.TestCase):
    def test_no_holes(self):
        ring = LinearRing([(0, 0), (0, 1), (1, 0)])
        polygon = Polygon(ring)
        self.assertFalse(polygon.exterior.is_ccw)
        polygon = orient(polygon, 1)
        self.assertTrue(polygon.exterior.is_ccw)

    def test_holes(self):
        polygon = Polygon([(0, 0), (0, 1), (1, 0)],
                          [[(0.5, 0.25), (0.25, 0.5), (0.25, 0.25)]])
        self.assertFalse(polygon.exterior.is_ccw)
        self.assertTrue(polygon.interiors[0].is_ccw)
        polygon = orient(polygon, 1)
        self.assertTrue(polygon.exterior.is_ccw)
        self.assertFalse(polygon.interiors[0].is_ccw)


def test_suite():
    loader = unittest.TestLoader()
    return unittest.TestSuite([
        loader.loadTestsFromTestCase(RingOrientationTestCase),
        loader.loadTestsFromTestCase(PolygonOrienterTestCase)])
Shapely-1.6.4/tests/test_collection.py000066400000000000000000000042411323200062600200300ustar00rootroot00000000000000from . import unittest
from shapely.geometry import LineString
from shapely.geometry.collection import GeometryCollection
from shapely.geometry import shape
from shapely.geometry import asShape


class CollectionTestCase(unittest.TestCase):

    def test_array_interface(self):
        m = GeometryCollection()
        self.assertEqual(len(m), 0)
        self.assertEqual(m.geoms, [])

    def test_child_with_deleted_parent(self):
        # test that we can remove a collection while having
        # childs around
        a = LineString([(0, 0), (1, 1), (1, 2), (2, 2)])
        b = LineString([(0, 0), (1, 1), (2, 1), (2, 2)])
        collection = a.intersection(b)

        child = collection.geoms[0]
        # delete parent of child
        del collection

        # access geometry, this should not seg fault as 1.2.15 did
        self.assertIsNotNone(child.wkt)

    def test_geointerface(self):
        d = {"type": "GeometryCollection","geometries": [
                {"type": "Point", "coordinates": (0, 3)},
                {"type": "LineString", "coordinates": ((2, 0), (1, 0))}
            ]}

        # asShape
        m = asShape(d)
        self.assertEqual(m.geom_type, "GeometryCollection")
        self.assertEqual(len(m), 2)
        geom_types = [g.geom_type for g in m.geoms]
        self.assertIn("Point", geom_types)
        self.assertIn("LineString", geom_types)

        # shape
        m = shape(d)
        self.assertEqual(m.geom_type, "GeometryCollection")
        self.assertEqual(len(m), 2)
        geom_types = [g.geom_type for g in m.geoms]
        self.assertIn("Point", geom_types)
        self.assertIn("LineString", geom_types)

    def test_empty_geointerface(self):
        d = {"type": "GeometryCollection", "geometries": []}

        # asShape
        m = asShape(d)
        self.assertEqual(m.geom_type, "GeometryCollection")
        self.assertEqual(len(m), 0)
        self.assertEqual(m.geoms, [])

        # shape
        m = shape(d)
        self.assertEqual(m.geom_type, "GeometryCollection")
        self.assertEqual(len(m), 0)
        self.assertEqual(m.geoms, [])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(CollectionTestCase)
Shapely-1.6.4/tests/test_coords.py000066400000000000000000000022411323200062600171640ustar00rootroot00000000000000from . import unittest, numpy
from shapely import geometry


class CoordsTestCase(unittest.TestCase):
    """
    Shapely assumes contiguous C-order float64 data for internal ops.
    Data should be converted to contiguous float64 if numpy exists.
    c9a0707 broke this a little bit.
    """

    @unittest.skipIf(not numpy, 'Numpy required')
    def test_data_promotion(self):
        coords = numpy.array([[ 12, 34 ], [ 56, 78 ]], dtype=numpy.float32)
        processed_coords = numpy.array(
            geometry.LineString(coords).coords
        )

        self.assertEqual(
            coords.tolist(),
            processed_coords.tolist()
        )

    @unittest.skipIf(not numpy, 'Numpy required')
    def test_data_destriding(self):
        coords = numpy.array([[ 12, 34 ], [ 56, 78 ]], dtype=numpy.float32)

        # Easy way to introduce striding: reverse list order
        processed_coords = numpy.array(
            geometry.LineString(coords[::-1]).coords
        )

        self.assertEqual(
            coords[::-1].tolist(),
            processed_coords.tolist()
        )


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(CoordsTestCase)
Shapely-1.6.4/tests/test_default_impl.py000066400000000000000000000011671323200062600203460ustar00rootroot00000000000000import pytest

from shapely.geometry import Point
from shapely.impl import delegated, ImplementationError


def test_error():
    with pytest.raises(ImplementationError):
        Point(0, 0).impl['bogus']()
    with pytest.raises(NotImplementedError):
        Point(0, 0).impl['bogus']()
    with pytest.raises(KeyError):
        Point(0, 0).impl['bogus']()


def test_delegated():
    class Poynt(Point):
        @delegated
        def bogus(self):
            return self.impl['bogus']()
    with pytest.raises(ImplementationError):
        Poynt(0, 0).bogus()
    with pytest.raises(AttributeError):
        Poynt(0, 0).bogus()
Shapely-1.6.4/tests/test_delaunay.py000066400000000000000000000023031323200062600174740ustar00rootroot00000000000000try:
    import unittest2 as unittest
except ImportError:
    import unittest

from shapely.geometry import Polygon, LineString, Point
from shapely.ops import triangulate
from shapely.geos import geos_version

@unittest.skipIf(geos_version < (3, 4, 0), 
                 "Delaunay triangulation not supported")
class DelaunayTriangulation(unittest.TestCase):
    """
    Only testing the number of triangles and their type here.
    This doesn't actually test the points in the resulting geometries.

    """
    def setUp(self):
        self.p = Polygon([(0,0), (1,0), (1,1), (0,1)])

    def test_polys(self):
        polys = triangulate(self.p)
        self.assertEqual(len(polys), 2)
        for p in polys:
            self.assert_(isinstance(p, Polygon))

    def test_lines(self):
        polys = triangulate(self.p, edges=True)
        self.assertEqual(len(polys), 5)
        for p in polys:
            self.assert_(isinstance(p, LineString))

    def test_point(self):
        p = Point(1,1)
        polys = triangulate(p)
        self.assertEqual(len(polys), 0)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(DelaunayTriangulation)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_delegated.py000066400000000000000000000015651323200062600176210ustar00rootroot00000000000000from . import unittest
from shapely.geometry import Point
from shapely.impl import BaseImpl
from shapely.geometry.base import delegated


class Geometry(object):

    impl = BaseImpl({})

    @property
    @delegated
    def foo(self):
        return self.impl['foo']()


class WrapperTestCase(unittest.TestCase):
    """When the backend has no support for a method, we get an AttributeError
    """

    def test_delegated(self):
        self.assertRaises(AttributeError, getattr, Geometry(), 'foo')

    def test_defaultimpl(self):
        project_impl = Point.impl.map.pop('project', None)
        try:
            self.assertRaises(AttributeError, Point(0, 0).project, 1.0)
        finally:
            if project_impl is not None:
                Point.impl.map['project'] = project_impl


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(WrapperTestCase)
Shapely-1.6.4/tests/test_dlls.py000066400000000000000000000014331323200062600166330ustar00rootroot00000000000000import sys
import os

from . import unittest
from shapely.geos import load_dll


class LoadingTestCase(unittest.TestCase):

    def test_load(self):
        self.assertRaises(OSError, load_dll, 'geosh_c')
    @unittest.skipIf(sys.platform == "win32", "FIXME: adapt test for win32")
    def test_fallbacks(self):
        load_dll('geos_c', fallbacks=[
            os.path.join(sys.prefix, "lib", "libgeos_c.dylib"), # anaconda (Mac OS X)
            '/opt/local/lib/libgeos_c.dylib',  # MacPorts
            '/usr/local/lib/libgeos_c.dylib',  # homebrew (Mac OS X)
            os.path.join(sys.prefix, "lib", "libgeos_c.so"), # anaconda (Linux)
            'libgeos_c.so.1',
            'libgeos_c.so'])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(LoadingTestCase)
Shapely-1.6.4/tests/test_doctests.py000066400000000000000000000017311323200062600175260ustar00rootroot00000000000000import os
import doctest
from . import unittest
from glob import glob

optionflags = (doctest.REPORT_ONLY_FIRST_FAILURE |
               doctest.NORMALIZE_WHITESPACE |
               doctest.ELLIPSIS)


def list_doctests():
    print(__file__)
    source_files = glob(os.path.join(os.path.dirname(__file__), '*.txt'))
    return [filename for filename in source_files]


def open_file(filename, mode='r'):
    """Helper function to open files from within the tests package."""
    return open(os.path.join(os.path.dirname(__file__), filename), mode)


def setUp(test):
    test.globs.update(dict(open_file=open_file,))


def test_suite():
    return unittest.TestSuite(
        [doctest.DocFileSuite(os.path.basename(filename),
                              optionflags=optionflags,
                              setUp=setUp)
         for filename
         in list_doctests()])

if __name__ == "__main__":
    runner = unittest.TextTestRunner(verbosity=1)
    runner.run(test_suite())
Shapely-1.6.4/tests/test_emptiness.py000066400000000000000000000044641323200062600177130ustar00rootroot00000000000000from . import unittest
from shapely.geometry.base import BaseGeometry, EmptyGeometry
import shapely.geometry as sgeom
from shapely.geometry.polygon import LinearRing

from shapely.geometry import MultiPolygon, mapping, shape, asShape


empty_generator = lambda: iter([])

class EmptinessTestCase(unittest.TestCase):

    def test_empty_class(self):
        g = EmptyGeometry()
        self.assertTrue(g._is_empty)

    def test_empty_base(self):
        g = BaseGeometry()
        self.assertTrue(g._is_empty)

    def test_emptying_point(self):
        p = sgeom.Point(0, 0)
        self.assertFalse(p._is_empty)
        p.empty()
        self.assertTrue(p._is_empty)

    def test_none_geom(self):
        p = BaseGeometry()
        p._geom = None
        self.assertTrue(p.is_empty)

    def test_empty_point(self):
        self.assertTrue(sgeom.Point().is_empty)

    def test_empty_multipoint(self):
        self.assertTrue(sgeom.MultiPoint().is_empty)

    def test_empty_geometry_collection(self):
        self.assertTrue(sgeom.GeometryCollection().is_empty)

    def test_empty_linestring(self):
        self.assertTrue(sgeom.LineString().is_empty)
        self.assertTrue(sgeom.LineString(None).is_empty)
        self.assertTrue(sgeom.LineString([]).is_empty)
        self.assertTrue(sgeom.LineString(empty_generator()).is_empty)

    def test_empty_multilinestring(self):
        self.assertTrue(sgeom.MultiLineString([]).is_empty)

    def test_empty_polygon(self):
        self.assertTrue(sgeom.Polygon().is_empty)
        self.assertTrue(sgeom.Polygon(None).is_empty)
        self.assertTrue(sgeom.Polygon([]).is_empty)
        self.assertTrue(sgeom.Polygon(empty_generator()).is_empty)

    def test_empty_multipolygon(self):
        self.assertTrue(sgeom.MultiPolygon([]).is_empty)

    def test_empty_linear_ring(self):
        self.assertTrue(LinearRing().is_empty)
        self.assertTrue(LinearRing(None).is_empty)
        self.assertTrue(LinearRing([]).is_empty)
        self.assertTrue(LinearRing(empty_generator()).is_empty)


def test_asshape_empty():
    empty_mp = MultiPolygon()
    empty_json = mapping(empty_mp)
    empty_asShape = asShape(empty_json)
    assert empty_asShape.is_empty


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(EmptinessTestCase)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_empty_polygons.py000066400000000000000000000012441323200062600207650ustar00rootroot00000000000000from shapely.geometry import MultiPolygon, Point, Polygon


def test_empty_polygon():
    """No constructor arg makes an empty polygon geometry."""
    assert Polygon().is_empty


def test_empty_multipolygon():
    """No constructor arg makes an empty multipolygon geometry."""
    assert MultiPolygon().is_empty


def test_multipolygon_empty_polygon():
    """An empty polygon passed to MultiPolygon() makes an empty
    multipolygon geometry."""
    assert MultiPolygon([Polygon()]).is_empty


def test_multipolygon_empty_among_polygon():
    """An empty polygon passed to MultiPolygon() is ignored."""
    assert len(MultiPolygon([Point(0,0).buffer(1.0), Polygon()])) == 1
Shapely-1.6.4/tests/test_equality.py000066400000000000000000000017131323200062600175330ustar00rootroot00000000000000from . import unittest
from shapely import geometry


class PointEqualityTestCase(unittest.TestCase):

    def test_equals_exact(self):
        p1 = geometry.Point(1.0, 1.0)
        p2 = geometry.Point(2.0, 2.0)
        self.assertFalse(p1.equals(p2))
        self.assertFalse(p1.equals_exact(p2, 0.001))

    def test_almost_equals_default(self):
        p1 = geometry.Point(1.0, 1.0)
        p2 = geometry.Point(1.0+1e-7, 1.0+1e-7)  # almost equal to 6 places
        p3 = geometry.Point(1.0+1e-6, 1.0+1e-6)  # not almost equal
        self.assertTrue(p1.almost_equals(p2))
        self.assertFalse(p1.almost_equals(p3))

    def test_almost_equals(self):
        p1 = geometry.Point(1.0, 1.0)
        p2 = geometry.Point(1.1, 1.1)
        self.assertFalse(p1.equals(p2))
        self.assertTrue(p1.almost_equals(p2, 0))
        self.assertFalse(p1.almost_equals(p2, 1))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PointEqualityTestCase)
Shapely-1.6.4/tests/test_geointerface.py000066400000000000000000000062701323200062600203340ustar00rootroot00000000000000from . import unittest

from shapely.geometry import asShape
from shapely.geometry.multipoint import MultiPointAdapter
from shapely.geometry.linestring import LineStringAdapter
from shapely.geometry.multilinestring import MultiLineStringAdapter
from shapely.geometry.polygon import Polygon, PolygonAdapter
from shapely.geometry.multipolygon import MultiPolygonAdapter
from shapely import wkt


class GeoThing(object):
    def __init__(self, d):
        self.__geo_interface__ = d


class GeoInterfaceTestCase(unittest.TestCase):

    def test_geointerface(self):
        # Adapt a dictionary
        d = {"type": "Point", "coordinates": (0.0, 0.0)}
        shape = asShape(d)
        self.assertEqual(shape.geom_type, 'Point')
        self.assertEqual(tuple(shape.coords), ((0.0, 0.0),))

        # Adapt an object that implements the geo protocol
        shape = None
        thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
        shape = asShape(thing)
        self.assertEqual(shape.geom_type, 'Point')
        self.assertEqual(tuple(shape.coords), ((0.0, 0.0),))

        # Check line string
        shape = asShape(
            {'type': 'LineString', 'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
        self.assertIsInstance(shape, LineStringAdapter)
        self.assertEqual(tuple(shape.coords), ((-1.0, -1.0), (1.0, 1.0)))

        # polygon
        shape = asShape(
            {'type': 'Polygon',
             'coordinates':
                (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)),
                 ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1)))}
        )
        self.assertIsInstance(shape, PolygonAdapter)
        self.assertEqual(
            tuple(shape.exterior.coords),
            ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)))
        self.assertEqual(len(shape.interiors), 1)

        # multi point
        shape = asShape({'type': 'MultiPoint',
                         'coordinates': ((1.0, 2.0), (3.0, 4.0))})
        self.assertIsInstance(shape, MultiPointAdapter)
        self.assertEqual(len(shape.geoms), 2)

        # multi line string
        shape = asShape({'type': 'MultiLineString',
                         'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})
        self.assertIsInstance(shape, MultiLineStringAdapter)
        self.assertEqual(len(shape.geoms), 1)

        # multi polygon
        shape = asShape(
            {'type': 'MultiPolygon',
             'coordinates':
                [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)),
                  ((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1))
                  )]})
        self.assertIsInstance(shape, MultiPolygonAdapter)
        self.assertEqual(len(shape.geoms), 1)


def test_empty_wkt_polygon():
    """Confirm fix for issue #450"""
    g = wkt.loads('POLYGON EMPTY')
    assert g.__geo_interface__['type'] == 'Polygon'
    assert g.__geo_interface__['coordinates'] == ()


def test_empty_polygon():
    """Confirm fix for issue #450"""
    g = Polygon()
    assert g.__geo_interface__['type'] == 'Polygon'
    assert g.__geo_interface__['coordinates'] == ()


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(GeoInterfaceTestCase)
Shapely-1.6.4/tests/test_geomseq.py000066400000000000000000000006111323200062600173320ustar00rootroot00000000000000from . import unittest
from shapely import geometry


class MultiLineTestCase(unittest.TestCase):
    def test_array_interface(self):
        m = geometry.MultiLineString([((0, 0), (1, 1)), ((2, 2), (3, 3))])
        ai = m.geoms[0].__array_interface__
        self.assertEqual(ai['shape'], (2, 2))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MultiLineTestCase)
Shapely-1.6.4/tests/test_geos_err_handler.py000066400000000000000000000037611323200062600212050ustar00rootroot00000000000000import logging

import pytest

from shapely.geometry import LineString
from shapely.errors import ReadingError
from shapely.wkt import loads


def test_error_handler_exception(tmpdir):
    """Error logged in addition to exception"""
    logger = logging.getLogger('shapely.geos')
    logfile = str(tmpdir.join('test_error.log'))
    fh = logging.FileHandler(logfile)
    logger.addHandler(fh)

    # This calls error_handler with a format string of "%s" and one
    # value.
    with pytest.raises(ReadingError):
        loads('POINT (LOLWUT)')

    log = open(logfile).read()
    assert "Expected number but encountered word: 'LOLWUT'" in log


def test_error_handler(tmpdir):
    logger = logging.getLogger('shapely.geos')
    logfile = str(tmpdir.join('test_error.log'))
    fh = logging.FileHandler(logfile)
    logger.addHandler(fh)

    # This operation calls error_handler with a format string that
    # has *no* conversion specifiers.
    LineString([(0, 0), (2, 2)]).project(LineString([(1, 1), (1.5, 1.5)]))

    log = open(logfile).read()
    assert "third argument of GEOSProject_r must be Point*" in log


def test_info_handler(tmpdir):
    logger = logging.getLogger('shapely.geos')
    logfile = str(tmpdir.join('test_error.log'))
    fh = logging.FileHandler(logfile)
    logger.addHandler(fh)
    logger.setLevel(logging.INFO)

    g = loads('MULTIPOLYGON (((10 20, 10 120, 60 70, 30 70, 30 40, 60 40, 60 70, 90 20, 10 20)))')
    assert not g.is_valid

    log = open(logfile).read()
    assert "Ring Self-intersection at or near point 60 70" in log


def test_info_handler_quiet(tmpdir):
    logger = logging.getLogger('shapely.geos')
    logfile = str(tmpdir.join('test_error.log'))
    fh = logging.FileHandler(logfile)
    logger.addHandler(fh)
    logger.setLevel(logging.WARNING)

    g = loads('MULTIPOLYGON (((10 20, 10 120, 60 70, 30 70, 30 40, 60 40, 60 70, 90 20, 10 20)))')
    assert not g.is_valid

    log = open(logfile).read()
    assert "Ring Self-intersection at or near point 60 70" not in log
Shapely-1.6.4/tests/test_getitem.py000066400000000000000000000126551323200062600173430ustar00rootroot00000000000000from . import unittest
from shapely import geometry


class CoordsGetItemTestCase(unittest.TestCase):

    def test_index_2d_coords(self):
        c = [(float(x), float(-x)) for x in range(4)]
        g = geometry.LineString(c)
        for i in range(-4, 4):
            self.assertTrue(g.coords[i] == c[i])
        self.assertRaises(IndexError, lambda: g.coords[4])
        self.assertRaises(IndexError, lambda: g.coords[-5])

    def test_index_3d_coords(self):
        c = [(float(x), float(-x), float(x*2)) for x in range(4)]
        g = geometry.LineString(c)
        for i in range(-4, 4):
            self.assertTrue(g.coords[i] == c[i])
        self.assertRaises(IndexError, lambda: g.coords[4])
        self.assertRaises(IndexError, lambda: g.coords[-5])

    def test_index_coords_misc(self):
        g = geometry.LineString()  # empty
        self.assertRaises(IndexError, lambda: g.coords[0])
        self.assertRaises(TypeError, lambda: g.coords[0.0])

    def test_slice_2d_coords(self):
        c = [(float(x), float(-x)) for x in range(4)]
        g = geometry.LineString(c)
        self.assertTrue(g.coords[1:] == c[1:])
        self.assertTrue(g.coords[:-1] == c[:-1])
        self.assertTrue(g.coords[::-1] == c[::-1])
        self.assertTrue(g.coords[::2] == c[::2])
        self.assertTrue(g.coords[:4] == c[:4])
        self.assertTrue(g.coords[4:] == c[4:] == [])

    def test_slice_3d_coords(self):
        c = [(float(x), float(-x), float(x*2)) for x in range(4)]
        g = geometry.LineString(c)
        self.assertTrue(g.coords[1:] == c[1:])
        self.assertTrue(g.coords[:-1] == c[:-1])
        self.assertTrue(g.coords[::-1] == c[::-1])
        self.assertTrue(g.coords[::2] == c[::2])
        self.assertTrue(g.coords[:4] == c[:4])
        self.assertTrue(g.coords[4:] == c[4:] == [])


class MultiGeomGetItemTestCase(unittest.TestCase):

    def test_index_multigeom(self):
        c = [(float(x), float(-x)) for x in range(4)]
        g = geometry.MultiPoint(c)
        for i in range(-4, 4):
            self.assertTrue(g[i].equals(geometry.Point(c[i])))
        self.assertRaises(IndexError, lambda: g[4])
        self.assertRaises(IndexError, lambda: g[-5])

    def test_index_multigeom_misc(self):
        g = geometry.MultiLineString()  # empty
        self.assertRaises(IndexError, lambda: g[0])
        self.assertRaises(TypeError, lambda: g[0.0])

    def test_slice_multigeom(self):
        c = [(float(x), float(-x)) for x in range(4)]
        g = geometry.MultiPoint(c)
        self.assertEqual(type(g[:]), type(g))
        self.assertEqual(len(g[:]), len(g))
        self.assertTrue(g[1:].equals(geometry.MultiPoint(c[1:])))
        self.assertTrue(g[:-1].equals(geometry.MultiPoint(c[:-1])))
        self.assertTrue(g[::-1].equals(geometry.MultiPoint(c[::-1])))
        self.assertTrue(g[4:].is_empty)


class LinearRingGetItemTestCase(unittest.TestCase):

    def test_index_linearring(self):
        shell = geometry.polygon.LinearRing([(0.0, 0.0), (70.0, 120.0),
                                             (140.0, 0.0), (0.0, 0.0)])
        holes = [geometry.polygon.LinearRing([(60.0, 80.0), (80.0, 80.0),
                                              (70.0, 60.0), (60.0, 80.0)]),
                 geometry.polygon.LinearRing([(30.0, 10.0), (50.0, 10.0),
                                              (40.0, 30.0), (30.0, 10.0)]),
                 geometry.polygon.LinearRing([(90.0, 10), (110.0, 10.0),
                                              (100.0, 30.0), (90.0, 10.0)])]
        g = geometry.Polygon(shell, holes)
        for i in range(-3, 3):
            self.assertTrue(g.interiors[i].equals(holes[i]))
        self.assertRaises(IndexError, lambda: g.interiors[3])
        self.assertRaises(IndexError, lambda: g.interiors[-4])

    def test_index_linearring_misc(self):
        g = geometry.Polygon()  # empty
        self.assertRaises(IndexError, lambda: g.interiors[0])
        self.assertRaises(TypeError, lambda: g.interiors[0.0])

    def test_slice_linearring(self):
        shell = geometry.polygon.LinearRing([(0.0, 0.0), (70.0, 120.0),
                                             (140.0, 0.0), (0.0, 0.0)])
        holes = [geometry.polygon.LinearRing([(60.0, 80.0), (80.0, 80.0),
                                              (70.0, 60.0), (60.0, 80.0)]),
                 geometry.polygon.LinearRing([(30.0, 10.0), (50.0, 10.0),
                                              (40.0, 30.0), (30.0, 10.0)]),
                 geometry.polygon.LinearRing([(90.0, 10), (110.0, 10.0),
                                              (100.0, 30.0), (90.0, 10.0)])]
        g = geometry.Polygon(shell, holes)
        t = [a.equals(b) for (a, b) in zip(g.interiors[1:], holes[1:])]
        self.assertTrue(all(t))
        t = [a.equals(b) for (a, b) in zip(g.interiors[:-1], holes[:-1])]
        self.assertTrue(all(t))
        t = [a.equals(b) for (a, b) in zip(g.interiors[::-1], holes[::-1])]
        self.assertTrue(all(t))
        t = [a.equals(b) for (a, b) in zip(g.interiors[::2], holes[::2])]
        self.assertTrue(all(t))
        t = [a.equals(b) for (a, b) in zip(g.interiors[:3], holes[:3])]
        self.assertTrue(all(t))
        self.assertTrue(g.interiors[3:] == holes[3:] == [])


def test_suite():
    loader = unittest.TestLoader()
    return unittest.TestSuite([
        loader.loadTestsFromTestCase(CoordsGetItemTestCase),
        loader.loadTestsFromTestCase(MultiGeomGetItemTestCase),
        loader.loadTestsFromTestCase(LinearRingGetItemTestCase)])
Shapely-1.6.4/tests/test_hash.py000066400000000000000000000012371323200062600166220ustar00rootroot00000000000000from shapely.geometry import Point, MultiPoint, Polygon, GeometryCollection


def test_point():
    g = Point(0, 0)
    try:
        assert hash(g)
        return False
    except TypeError:
        return True


def test_multipoint():
    g = MultiPoint([(0, 0)])
    try:
        assert hash(g)
        return False
    except TypeError:
        return True


def test_polygon():
    g = Point(0, 0).buffer(1.0)
    try:
        assert hash(g)
        return False
    except TypeError:
        return True


def test_collection():
    g = GeometryCollection([Point(0, 0)])
    try:
        assert hash(g)
        return False
    except TypeError:
        return True
Shapely-1.6.4/tests/test_invalid_geometries.py000066400000000000000000000020121323200062600215400ustar00rootroot00000000000000'''Test recovery from operation on invalid geometries
'''

from . import unittest
from shapely.geometry import Polygon
from shapely.topology import TopologicalError


class InvalidGeometriesTestCase(unittest.TestCase):

    def test_invalid_intersection(self):
        # Make a self-intersecting polygon
        polygon_invalid = Polygon(((0, 0), (1, 1), (1, -1), (0, 1), (0, 0)))
        self.assertFalse(polygon_invalid.is_valid)

        # Intersect with a valid polygon
        polygon = Polygon(((-.5, -.5), (-.5, .5), (.5, .5), (.5, -5)))
        self.assertTrue(polygon.is_valid)
        self.assertTrue(polygon_invalid.intersects(polygon))
        self.assertRaises(TopologicalError,
                          polygon_invalid.intersection, polygon)
        self.assertRaises(TopologicalError,
                          polygon.intersection, polygon_invalid)
        return


def test_suite():
    loader = unittest.TestLoader()
    return unittest.TestSuite([
        loader.loadTestsFromTestCase(InvalidGeometriesTestCase)])
Shapely-1.6.4/tests/test_iterops.py000066400000000000000000000047651323200062600173750ustar00rootroot00000000000000"""Test operator iterations
"""
from . import unittest
from shapely import iterops
from shapely.geometry import Point, Polygon
from shapely.geos import TopologicalError


class IterOpsTestCase(unittest.TestCase):

    def test_iterops(self):

        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0))
        polygon = Polygon(coords)
        points = [Point(0.5, 0.5), Point(2.0, 2.0)]

        # List of the points contained by the polygon
        self.assertTrue(
            all([isinstance(x, Point)
                 for x in iterops.contains(polygon, points, True)]))

        # 'True' is the default value
        self.assertTrue(
            all([isinstance(x, Point)
                 for x in iterops.contains(polygon, points)]))

        # Test a false value
        self.assertTrue(
            all([isinstance(x, Point)
                 for x in iterops.contains(polygon, points, False)]))

        # If the provided iterator yields tuples, the second value will be
        # yielded
        self.assertEqual(
            list(iterops.contains(polygon, [(p, p.coords[:])
                 for p in points], False)),
            [[(2.0, 2.0)]])

        # Just to demonstrate that the important thing is that the second
        # parameter is an iterator:
        self.assertEqual(
            list(iterops.contains(polygon, iter((p, p.coords[:])
                 for p in points))),
            [[(0.5, 0.5)]])


    def test_err(self):
        # bowtie polygon.
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0))
        polygon = Polygon(coords)
        self.assertFalse(polygon.is_valid)
        points = [Point(0.5, 0.5).buffer(2.0), Point(2.0, 2.0).buffer(3.0)]
        # List of the points contained by the polygon
        self.assertTrue(
            all([isinstance(x, Polygon)
                 for x in iterops.intersects(polygon, points, True)]))

    def test_topological_error(self):
        p1 = [(339, 346), (459, 346), (399, 311), (340, 277), (399, 173),
              (280, 242), (339, 415), (280, 381), (460, 207), (339, 346)]
        polygon1 = Polygon(p1)

        p2 = [(339, 207), (280, 311), (460, 138), (399, 242), (459, 277),
              (459, 415), (399, 381), (519, 311), (520, 242), (519, 173),
              (399, 450), (339, 207)]
        polygon2 = Polygon(p2)

        with self.assertRaises(TopologicalError):
            list(iterops.within(polygon1, [polygon2]))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(IterOpsTestCase)
Shapely-1.6.4/tests/test_linear_referencing.py000066400000000000000000000060131323200062600215150ustar00rootroot00000000000000from . import unittest
from shapely.geos import geos_version
from shapely.geometry import Point, LineString, MultiLineString


class LinearReferencingTestCase(unittest.TestCase):

    def setUp(self):
        self.point = Point(1, 1)
        self.line1 = LineString(([0, 0], [2, 0]))
        self.line2 = LineString(([3, 0], [3, 6]))
        self.multiline = MultiLineString([
            list(self.line1.coords), list(self.line2.coords)
        ])

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_line1_project(self):
        self.assertEqual(self.line1.project(self.point), 1.0)
        self.assertEqual(self.line1.project(self.point, normalized=True), 0.5)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_line2_project(self):
        self.assertEqual(self.line2.project(self.point), 1.0)
        self.assertAlmostEqual(
            self.line2.project(self.point, normalized=True), 0.16666666666, 8)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_multiline_project(self):
        self.assertEqual(self.multiline.project(self.point), 1.0)
        self.assertEqual(
            self.multiline.project(self.point, normalized=True), 0.125)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_not_supported_project(self):
        with self.assertRaises(TypeError):
            self.point.buffer(1.0).project(self.point)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_not_on_line_project(self):
        # Points that aren't on the line project to 0.
        self.assertEqual(self.line1.project(Point(-10, -10)), 0.0)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_line1_interpolate(self):
        self.assertTrue(self.line1.interpolate(0.5).equals(Point(0.5, 0.0)))
        self.assertTrue(
            self.line1.interpolate(0.5, normalized=True).equals(Point(1, 0)))

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_line2_interpolate(self):
        self.assertTrue(self.line2.interpolate(0.5).equals(Point(3.0, 0.5)))
        self.assertTrue(
            self.line2.interpolate(0.5, normalized=True).equals(Point(3, 3)))

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_multiline_interpolate(self):
        self.assertTrue(self.multiline.interpolate(0.5).equals(Point(0.5, 0)))
        self.assertTrue(
            self.multiline.interpolate(0.5, normalized=True).equals(
                Point(3.0, 2.0)))

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_line_ends_interpolate(self):
        # Distances greater than length of the line or less than
        # zero yield the line's ends.
        self.assertTrue(self.line1.interpolate(-1000).equals(Point(0.0, 0.0)))
        self.assertTrue(self.line1.interpolate(1000).equals(Point(2.0, 0.0)))


def test_suite():
    loader = unittest.TestLoader()
    return loader.loadTestsFromTestCase(LinearReferencingTestCase)
Shapely-1.6.4/tests/test_linemerge.py000066400000000000000000000030641323200062600176460ustar00rootroot00000000000000from . import unittest
from shapely.geometry import LineString, MultiLineString
from shapely.ops import linemerge


class LineMergeTestCase(unittest.TestCase):

    def test_linemerge(self):

        lines = MultiLineString(
            [((0, 0), (1, 1)),
             ((2, 0), (2, 1), (1, 1))])
        result = linemerge(lines)
        self.assertIsInstance(result, LineString)
        self.assertFalse(result.is_ring)
        self.assertEqual(len(result.coords), 4)
        self.assertEqual(result.coords[0], (0.0, 0.0))
        self.assertEqual(result.coords[3], (2.0, 0.0))

        lines2 = MultiLineString(
            [((0, 0), (1, 1)),
             ((0, 0), (2, 0), (2, 1), (1, 1))])
        result = linemerge(lines2)
        self.assertTrue(result.is_ring)
        self.assertEqual(len(result.coords), 5)

        lines3 = [
            LineString(((0, 0), (1, 1))),
            LineString(((0, 0), (0, 1))),
        ]
        result = linemerge(lines3)
        self.assertFalse(result.is_ring)
        self.assertEqual(len(result.coords), 3)
        self.assertEqual(result.coords[0], (0.0, 1.0))
        self.assertEqual(result.coords[2], (1.0, 1.0))

        lines4 = [
            ((0, 0), (1, 1)),
            ((0, 0), (0, 1)),
        ]
        self.assertTrue(result.equals(linemerge(lines4)))

        lines5 = [
            ((0, 0), (1, 1)),
            ((1, 0), (0, 1)),
        ]
        result = linemerge(lines5)
        self.assertEqual(result.type, 'MultiLineString')


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(LineMergeTestCase)
Shapely-1.6.4/tests/test_linestring.py000066400000000000000000000133041323200062600200530ustar00rootroot00000000000000from . import unittest, numpy
from shapely.geos import lgeos
from shapely.geometry import LineString, asLineString, Point, LinearRing


class LineStringTestCase(unittest.TestCase):

    def test_linestring(self):

        # From coordinate tuples
        line = LineString(((1.0, 2.0), (3.0, 4.0)))
        self.assertEqual(len(line.coords), 2)
        self.assertEqual(line.coords[:], [(1.0, 2.0), (3.0, 4.0)])

        # From Points
        line2 = LineString((Point(1.0, 2.0), Point(3.0, 4.0)))
        self.assertEqual(len(line2.coords), 2)
        self.assertEqual(line2.coords[:], [(1.0, 2.0), (3.0, 4.0)])

        # From mix of tuples and Points
        line3 = LineString((Point(1.0, 2.0), (2.0, 3.0), Point(3.0, 4.0)))
        self.assertEqual(len(line3.coords), 3)
        self.assertEqual(line3.coords[:], [(1.0, 2.0), (2.0, 3.0), (3.0, 4.0)])

        # Bounds
        self.assertEqual(line.bounds, (1.0, 2.0, 3.0, 4.0))

        # Coordinate access
        self.assertEqual(tuple(line.coords), ((1.0, 2.0), (3.0, 4.0)))
        self.assertEqual(line.coords[0], (1.0, 2.0))
        self.assertEqual(line.coords[1], (3.0, 4.0))
        with self.assertRaises(IndexError):
            line.coords[2]  # index out of range

        # Geo interface
        self.assertEqual(line.__geo_interface__,
                         {'type': 'LineString',
                          'coordinates': ((1.0, 2.0), (3.0, 4.0))})

        # Coordinate modification
        line.coords = ((-1.0, -1.0), (1.0, 1.0))
        self.assertEqual(line.__geo_interface__,
                         {'type': 'LineString',
                          'coordinates': ((-1.0, -1.0), (1.0, 1.0))})

        # Adapt a coordinate list to a line string
        coords = [[5.0, 6.0], [7.0, 8.0]]
        la = asLineString(coords)
        self.assertEqual(la.coords[:], [(5.0, 6.0), (7.0, 8.0)])

        # Test Non-operability of Null geometry
        l_null = LineString()
        self.assertEqual(l_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
        self.assertEqual(l_null.length, 0.0)

        # Check that we can set coordinates of a null geometry
        l_null.coords = [(0, 0), (1, 1)]
        self.assertAlmostEqual(l_null.length, 1.4142135623730951)

    def test_equals_argument_order(self):
        """
        Test equals predicate functions correctly regardless of the order
        of the inputs. See issue #317. 
        """
        coords = ((0, 0), (1, 0), (1, 1), (0, 0))
        ls = LineString(coords)
        lr = LinearRing(coords)
        
        self.assertFalse(ls.__eq__(lr))  # previously incorrectly returned True
        self.assertFalse(lr.__eq__(ls))
        self.assertFalse(ls == lr)
        self.assertFalse(lr == ls)
        
        ls_clone = LineString(coords)
        lr_clone = LinearRing(coords)

        self.assertTrue(ls.__eq__(ls_clone))
        self.assertTrue(lr.__eq__(lr_clone))
        self.assertTrue(ls == ls_clone)
        self.assertTrue(lr == lr_clone)


    def test_from_linestring(self):
        line = LineString(((1.0, 2.0), (3.0, 4.0)))
        copy = LineString(line)
        self.assertEqual(len(copy.coords), 2)
        self.assertEqual(copy.coords[:], [(1.0, 2.0), (3.0, 4.0)])
        self.assertEqual('LineString',
                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))


    def test_from_linestring_z(self):
        coords = [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]
        line = LineString(coords)
        copy = LineString(line)
        self.assertEqual(len(copy.coords), 2)
        self.assertEqual(copy.coords[:], coords)
        self.assertEqual('LineString',
                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))


    def test_from_linearring(self):
        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
        ring = LinearRing(coords)
        copy = LineString(ring)
        self.assertEqual(len(copy.coords), 4)
        self.assertEqual(copy.coords[:], coords)
        self.assertEqual('LineString',
                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))


    @unittest.skipIf(not numpy, 'Numpy required')
    def test_numpy(self):

        from numpy import array, asarray
        from numpy.testing import assert_array_equal

        # Construct from a numpy array
        line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
        self.assertEqual(len(line.coords), 2)
        self.assertEqual(line.coords[:], [(0.0, 0.0), (1.0, 2.0)])

        line = LineString(((1.0, 2.0), (3.0, 4.0)))
        la = asarray(line)
        expected = array([[1.0, 2.0], [3.0, 4.0]])
        assert_array_equal(la, expected)

        # Coordinate sequences can be adapted as well
        la = asarray(line.coords)
        assert_array_equal(la, expected)

        # Adapt a Numpy array to a line string
        a = array([[1.0, 2.0], [3.0, 4.0]])
        la = asLineString(a)
        assert_array_equal(la.context, a)
        self.assertEqual(la.coords[:], [(1.0, 2.0), (3.0, 4.0)])

        # Now, the inverse
        self.assertEqual(la.__array_interface__,
                         la.context.__array_interface__)

        pas = asarray(la)
        assert_array_equal(pas, array([[1.0, 2.0], [3.0, 4.0]]))

        # From Array.txt
        a = asarray([[0.0, 0.0], [2.0, 2.0], [1.0, 1.0]])
        line = LineString(a)
        self.assertEqual(line.coords[:], [(0.0, 0.0), (2.0, 2.0), (1.0, 1.0)])

        data = line.ctypes
        self.assertEqual(data[0], 0.0)
        self.assertEqual(data[5], 1.0)

        b = asarray(line)
        assert_array_equal(b, array([[0., 0.], [2., 2.], [1., 1.]]))

        # Test array interface of empty linestring
        le = LineString()
        a = asarray(le)
        assert(a.shape[0], 0)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(LineStringTestCase)
Shapely-1.6.4/tests/test_locale.py000066400000000000000000000025711323200062600171400ustar00rootroot00000000000000'''Test locale independence of WKT
'''
from . import unittest
import sys
import locale
from shapely.wkt import loads, dumps

# Set locale to one that uses a comma as decimal seperator
# TODO: try a few other common locales
if sys.platform == 'win32':
    test_locales = {
        'Portuguese': 'portuguese_brazil',
    }
else:
    test_locales = {
        'Portuguese': 'pt_BR.UTF-8',
    }

do_test_locale = False


def setUpModule():
    global do_test_locale
    for name in test_locales:
        try:
            test_locale = test_locales[name]
            locale.setlocale(locale.LC_ALL, test_locale)
            do_test_locale = True
            break
        except:
            pass
    if not do_test_locale:
        raise unittest.SkipTest('test locale not found')


def tearDownModule():
    if sys.platform == 'win32':
        locale.setlocale(locale.LC_ALL, "")
    else:
        locale.resetlocale()


class LocaleTestCase(unittest.TestCase):

    #@unittest.skipIf(not do_test_locale, 'test locale not found')

    def test_wkt_locale(self):

        # Test reading and writing
        p = loads('POINT (0.0 0.0)')
        self.assertEqual(p.x, 0.0)
        self.assertEqual(p.y, 0.0)
        wkt = dumps(p)
        self.assertTrue(wkt.startswith('POINT'))
        self.assertFalse(',' in wkt)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(LocaleTestCase)
Shapely-1.6.4/tests/test_mapping.py000066400000000000000000000005521323200062600173310ustar00rootroot00000000000000from . import unittest
from shapely.geometry import Point, mapping


class MappingTestCase(unittest.TestCase):
    def test_point(self):
        m = mapping(Point(0, 0))
        self.assertEqual(m['type'], 'Point')
        self.assertEqual(m['coordinates'], (0.0, 0.0))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MappingTestCase)
Shapely-1.6.4/tests/test_minimum_rotated_rectangle.py000066400000000000000000000026111323200062600231150ustar00rootroot00000000000000from . import unittest
from shapely import geometry

class MinimumRotatedRectangleTestCase(unittest.TestCase):
    
    def test_minimum_rectangle(self):
        poly = geometry.Polygon([(0,1), (1, 2), (2, 1), (1, 0), (0, 1)])
        rect = poly.minimum_rotated_rectangle
        self.assertIsInstance(rect, geometry.Polygon)
        self.assertEqual(rect.area - poly.area < 0.1, True)
        self.assertEqual(len(rect.exterior.coords), 5)
        
        ls = geometry.LineString([(0,1), (1, 2), (2, 1), (1, 0)])
        rect = ls.minimum_rotated_rectangle
        self.assertIsInstance(rect, geometry.Polygon)
        self.assertIsInstance(rect, geometry.Polygon)
        self.assertEqual(rect.area - ls.convex_hull.area < 0.1, True)
        self.assertEqual(len(rect.exterior.coords), 5)

    def test_digenerate(self):
        rect = geometry.Point((0,1)).minimum_rotated_rectangle
        self.assertIsInstance(rect, geometry.Point)
        self.assertEqual(len(rect.coords), 1)
        self.assertEqual(rect.coords[0], (0,1))

        rect = geometry.LineString([(0,0),(2,2)]).minimum_rotated_rectangle
        self.assertIsInstance(rect, geometry.LineString)
        self.assertEqual(len(rect.coords), 2)
        self.assertEqual(rect.coords[0], (0,0))
        self.assertEqual(rect.coords[1], (2,2))

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MinimumRotatedRectangleTestCase)Shapely-1.6.4/tests/test_multi.py000066400000000000000000000004431323200062600170270ustar00rootroot00000000000000from . import unittest, test_int_types

class MultiGeometryTestCase(unittest.TestCase):
    def subgeom_access_test(self, cls, geoms):
        geom = cls(geoms)
        for t in test_int_types:
            for i, g in enumerate(geoms):
                self.assertEqual(geom[t(i)], geoms[i])
Shapely-1.6.4/tests/test_multilinestring.py000066400000000000000000000062001323200062600211230ustar00rootroot00000000000000from . import unittest, numpy, test_int_types
from .test_multi import MultiGeometryTestCase
from shapely.geos import lgeos
from shapely.geometry import LineString, MultiLineString, asMultiLineString
from shapely.geometry.base import dump_coords


class MultiLineStringTestCase(MultiGeometryTestCase):

    def test_multilinestring(self):

        # From coordinate tuples
        geom = MultiLineString((((1.0, 2.0), (3.0, 4.0)),))
        self.assertIsInstance(geom, MultiLineString)
        self.assertEqual(len(geom.geoms), 1)
        self.assertEqual(dump_coords(geom), [[(1.0, 2.0), (3.0, 4.0)]])

        # From lines
        a = LineString(((1.0, 2.0), (3.0, 4.0)))
        ml = MultiLineString([a])
        self.assertEqual(len(ml.geoms), 1)
        self.assertEqual(dump_coords(ml), [[(1.0, 2.0), (3.0, 4.0)]])

        # From another multi-line
        ml2 = MultiLineString(ml)
        self.assertEqual(len(ml2.geoms), 1)
        self.assertEqual(dump_coords(ml2), [[(1.0, 2.0), (3.0, 4.0)]])

        # Sub-geometry Access
        geom = MultiLineString([(((0.0, 0.0), (1.0, 2.0)))])
        self.assertIsInstance(geom[0], LineString)
        self.assertEqual(dump_coords(geom[0]), [(0.0, 0.0), (1.0, 2.0)])
        with self.assertRaises(IndexError):  # index out of range
            geom.geoms[1]

        # Geo interface
        self.assertEqual(geom.__geo_interface__,
                         {'type': 'MultiLineString',
                          'coordinates': (((0.0, 0.0), (1.0, 2.0)),)})


    def test_from_multilinestring_z(self):
        coords1 = [(0.0, 1.0, 2.0), (3.0, 4.0, 5.0)]
        coords2 = [(6.0, 7.0, 8.0), (9.0, 10.0, 11.0)]

        # From coordinate tuples
        ml = MultiLineString([coords1, coords2])
        copy = MultiLineString(ml)
        self.assertIsInstance(copy, MultiLineString)
        self.assertEqual('MultiLineString',
                         lgeos.GEOSGeomType(copy._geom).decode('ascii'))
        self.assertEqual(len(copy.geoms), 2)
        self.assertEqual(dump_coords(copy.geoms[0]), coords1)
        self.assertEqual(dump_coords(copy.geoms[1]), coords2)


    @unittest.skipIf(not numpy, 'Numpy required')
    def test_numpy(self):

        from numpy import array
        from numpy.testing import assert_array_equal

        # Construct from a numpy array
        geom = MultiLineString([array(((0.0, 0.0), (1.0, 2.0)))])
        self.assertIsInstance(geom, MultiLineString)
        self.assertEqual(len(geom.geoms), 1)
        self.assertEqual(dump_coords(geom), [[(0.0, 0.0), (1.0, 2.0)]])

        # Adapt a sequence of Numpy arrays to a multilinestring
        a = [array(((1.0, 2.0), (3.0, 4.0)))]
        geoma = asMultiLineString(a)
        assert_array_equal(geoma.context, [array([[1., 2.], [3., 4.]])])
        self.assertEqual(dump_coords(geoma), [[(1.0, 2.0), (3.0, 4.0)]])

        # TODO: is there an inverse?

    def test_subgeom_access(self):
        line0 = LineString([(0.0, 1.0), (2.0, 3.0)])
        line1 = LineString([(4.0, 5.0), (6.0, 7.0)])
        self.subgeom_access_test(MultiLineString, [line0, line1])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MultiLineStringTestCase)
Shapely-1.6.4/tests/test_multipoint.py000066400000000000000000000055131323200062600201040ustar00rootroot00000000000000from . import unittest, numpy, test_int_types
from .test_multi import MultiGeometryTestCase
from shapely.geometry import Point, MultiPoint, asMultiPoint
from shapely.geometry.base import dump_coords


class MultiPointTestCase(MultiGeometryTestCase):

    def test_multipoint(self):

        # From coordinate tuples
        geom = MultiPoint(((1.0, 2.0), (3.0, 4.0)))
        self.assertEqual(len(geom.geoms), 2)
        self.assertEqual(dump_coords(geom), [[(1.0, 2.0)], [(3.0, 4.0)]])

        # From points
        geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
        self.assertEqual(len(geom.geoms), 2)
        self.assertEqual(dump_coords(geom), [[(1.0, 2.0)], [(3.0, 4.0)]])

        # From another multi-point
        geom2 = MultiPoint(geom)
        self.assertEqual(len(geom2.geoms), 2)
        self.assertEqual(dump_coords(geom2), [[(1.0, 2.0)], [(3.0, 4.0)]])

        # Sub-geometry Access
        self.assertIsInstance(geom.geoms[0], Point)
        self.assertEqual(geom.geoms[0].x, 1.0)
        self.assertEqual(geom.geoms[0].y, 2.0)
        with self.assertRaises(IndexError):  # index out of range
            geom.geoms[2]

        # Geo interface
        self.assertEqual(geom.__geo_interface__,
                         {'type': 'MultiPoint',
                          'coordinates': ((1.0, 2.0), (3.0, 4.0))})

        # Adapt a coordinate list to a line string
        coords = [[5.0, 6.0], [7.0, 8.0]]
        geoma = asMultiPoint(coords)
        self.assertEqual(dump_coords(geoma), [[(5.0, 6.0)], [(7.0, 8.0)]])

    @unittest.skipIf(not numpy, 'Numpy required')
    def test_numpy(self):

        from numpy import array, asarray
        from numpy.testing import assert_array_equal

        # Construct from a numpy array
        geom = MultiPoint(array([[0.0, 0.0], [1.0, 2.0]]))
        self.assertIsInstance(geom, MultiPoint)
        self.assertEqual(len(geom.geoms), 2)
        self.assertEqual(dump_coords(geom), [[(0.0, 0.0)], [(1.0, 2.0)]])

        # Geo interface (cont.)
        geom = MultiPoint((Point(1.0, 2.0), Point(3.0, 4.0)))
        assert_array_equal(array(geom), array([[1., 2.], [3., 4.]]))

        # Adapt a Numpy array to a multipoint
        a = array([[1.0, 2.0], [3.0, 4.0]])
        geoma = asMultiPoint(a)
        assert_array_equal(geoma.context, array([[1., 2.], [3., 4.]]))
        self.assertEqual(dump_coords(geoma), [[(1.0, 2.0)], [(3.0, 4.0)]])

        # Now, the inverse
        self.assertEqual(geoma.__array_interface__,
                         geoma.context.__array_interface__)

        pas = asarray(geoma)
        assert_array_equal(pas, array([[1., 2.], [3., 4.]]))

    def test_subgeom_access(self):
        p0 = Point(1.0, 2.0)
        p1 = Point(3.0, 4.0)
        self.subgeom_access_test(MultiPoint, [p0, p1])

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MultiPointTestCase)
Shapely-1.6.4/tests/test_multipolygon.py000066400000000000000000000062271323200062600204450ustar00rootroot00000000000000from . import unittest, numpy, test_int_types
from .test_multi import MultiGeometryTestCase
from shapely.geometry import Polygon, MultiPolygon, asMultiPolygon
from shapely.geometry.base import dump_coords


class MultiPolygonTestCase(MultiGeometryTestCase):

    def test_multipolygon(self):

        # From coordinate tuples
        geom = MultiPolygon(
            [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
              [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))])])
        self.assertIsInstance(geom, MultiPolygon)
        self.assertEqual(len(geom.geoms), 1)
        self.assertEqual(
            dump_coords(geom),
            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
               (0.25, 0.25)]]])

        # Or from polygons
        p = Polygon(((0, 0), (0, 1), (1, 1), (1, 0)),
                    [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))])
        geom = MultiPolygon([p])
        self.assertEqual(len(geom.geoms), 1)
        self.assertEqual(
            dump_coords(geom),
            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
               (0.25, 0.25)]]])

        # Or from another multi-polygon
        geom2 = MultiPolygon(geom)
        self.assertEqual(len(geom2.geoms), 1)
        self.assertEqual(
            dump_coords(geom2),
            [[(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
              [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
               (0.25, 0.25)]]])

        # Sub-geometry Access
        self.assertIsInstance(geom.geoms[0], Polygon)
        self.assertEqual(
            dump_coords(geom.geoms[0]),
            [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0),
             [(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25),
              (0.25, 0.25)]])
        with self.assertRaises(IndexError):  # index out of range
            geom.geoms[1]

        # Geo interface
        self.assertEqual(
            geom.__geo_interface__,
            {'type': 'MultiPolygon',
             'coordinates': [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0),
                               (1.0, 0.0), (0.0, 0.0)),
                              ((0.25, 0.25), (0.25, 0.5), (0.5, 0.5),
                               (0.5, 0.25), (0.25, 0.25)))]})

        # Adapter
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
        holes_coords = [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
        mpa = asMultiPolygon([(coords, holes_coords)])
        self.assertEqual(len(mpa.geoms), 1)
        self.assertEqual(len(mpa.geoms[0].exterior.coords), 5)
        self.assertEqual(len(mpa.geoms[0].interiors), 1)
        self.assertEqual(len(mpa.geoms[0].interiors[0].coords), 5)

    def test_subgeom_access(self):
        poly0 = Polygon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)])
        poly1 = Polygon([(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25)])
        self.subgeom_access_test(MultiPolygon, [poly0, poly1])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(MultiPolygonTestCase)
Shapely-1.6.4/tests/test_ndarrays.py000066400000000000000000000031131323200062600175150ustar00rootroot00000000000000# Tests of support for Numpy ndarrays. See
# https://github.com/sgillies/shapely/issues/26 for discussion.
# Requires numpy.

import sys

if sys.version_info[0] >= 3:
    from functools import reduce

from . import unittest
from shapely import geometry

try:
    import numpy
except ImportError:
    numpy = False


class TransposeTestCase(unittest.TestCase):

    @unittest.skipIf(not numpy, 'numpy not installed')
    def test_multipoint(self):
        arr = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
        tarr = arr.T
        shape = geometry.asMultiPoint(tarr)
        coords = reduce(lambda x, y: x + y, [list(g.coords) for g in shape])
        self.assertEqual(
            coords,
            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
        )

    @unittest.skipIf(not numpy, 'numpy not installed')
    def test_linestring(self):
        a = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
        t = a.T
        s = geometry.asLineString(t)
        self.assertEqual(
            list(s.coords),
            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
        )

    @unittest.skipIf(not numpy, 'numpy not installed')
    def test_polygon(self):
        a = numpy.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
        t = a.T
        s = geometry.asPolygon(t)
        self.assertEqual(
            list(s.exterior.coords),
            [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
        )


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(TransposeTestCase)
Shapely-1.6.4/tests/test_nearest.py000066400000000000000000000013011323200062600173300ustar00rootroot00000000000000from . import unittest

from shapely.geometry import Point
from shapely.geos import geos_version
from shapely.ops import nearest_points

@unittest.skipIf(geos_version < (3, 4, 0), 'GEOS 3.4.0 required')
class Nearest(unittest.TestCase):
    def test_nearest(self):
        first, second = nearest_points(
                        Point(0, 0).buffer(1.0), Point(3, 0).buffer(1.0))
        self.assertAlmostEqual(first.x, 1.0, 7)
        self.assertAlmostEqual(second.x, 2.0, 7)
        self.assertAlmostEqual(first.y, 0.0, 7)
        self.assertAlmostEqual(second.y, 0.0, 7)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(Nearest)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_operations.py000066400000000000000000000061671323200062600200710ustar00rootroot00000000000000from . import unittest
import pytest
from shapely.geometry import Point, LineString, Polygon, MultiPoint, \
                             GeometryCollection
from shapely.wkt import loads
from shapely.geos import TopologicalError, geos_version

class OperationsTestCase(unittest.TestCase):

    def test_operations(self):
        point = Point(0.0, 0.0)

        # General geometry
        self.assertEqual(point.area, 0.0)
        self.assertEqual(point.length, 0.0)
        self.assertAlmostEqual(point.distance(Point(-1.0, -1.0)),
                               1.4142135623730951)

        # Topology operations

        # Envelope
        self.assertIsInstance(point.envelope, Point)

        # Intersection
        self.assertIsInstance(point.intersection(Point(-1, -1)),
                              GeometryCollection)

        # Buffer
        self.assertIsInstance(point.buffer(10.0), Polygon)
        self.assertIsInstance(point.buffer(10.0, 32), Polygon)

        # Simplify
        p = loads('POLYGON ((120 120, 121 121, 122 122, 220 120, 180 199, '
                  '160 200, 140 199, 120 120))')
        expected = loads('POLYGON ((120 120, 140 199, 160 200, 180 199, '
                         '220 120, 120 120))')
        s = p.simplify(10.0, preserve_topology=False)
        self.assertTrue(s.equals_exact(expected, 0.001))

        p = loads('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),'
                  '(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
        expected = loads(
            'POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),'
            '(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
        s = p.simplify(10.0, preserve_topology=True)
        self.assertTrue(s.equals_exact(expected, 0.001))

        # Convex Hull
        self.assertIsInstance(point.convex_hull, Point)

        # Differences
        self.assertIsInstance(point.difference(Point(-1, 1)), Point)

        self.assertIsInstance(point.symmetric_difference(Point(-1, 1)),
                              MultiPoint)

        # Boundary
        self.assertIsInstance(point.boundary, GeometryCollection)

        # Union
        self.assertIsInstance(point.union(Point(-1, 1)), MultiPoint)

        self.assertIsInstance(point.representative_point(), Point)

        self.assertIsInstance(point.centroid, Point)

    def test_relate(self):
        # Relate
        self.assertEqual(Point(0, 0).relate(Point(-1, -1)), 'FF0FFF0F2')

        # issue #294: should raise TopologicalError on exception
        invalid_polygon = loads('POLYGON ((40 100, 80 100, 80 60, 40 60, 40 100), (60 60, 80 60, 80 40, 60 40, 60 60))')
        assert(not invalid_polygon.is_valid)
        with pytest.raises(TopologicalError):
            invalid_polygon.relate(invalid_polygon)

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_hausdorff_distance(self):
        point = Point(1, 1)
        line = LineString([(2, 0), (2, 4), (3, 4)])

        distance = point.hausdorff_distance(line)
        self.assertEqual(distance, point.distance(Point(3, 4)))

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(OperationsTestCase)
Shapely-1.6.4/tests/test_operators.py000066400000000000000000000043501323200062600177140ustar00rootroot00000000000000from . import unittest
from shapely.geometry import Point, MultiPoint, Polygon, LineString


class OperatorsTestCase(unittest.TestCase):

    def test_point(self):
        point = Point(0, 0)
        point2 = Point(-1, 1)
        self.assertTrue(point.union(point2).equals(point | point2))
        self.assertTrue((point & point2).is_empty)
        self.assertTrue(point.equals(point - point2))
        self.assertTrue(
            point.symmetric_difference(point2).equals(point ^ point2))
        self.assertNotEqual(point, point2)
        point_dupe = Point(0, 0)
        self.assertEqual(point, point_dupe)

    def test_multipoint(self):
        mp1 = MultiPoint(((0, 0), (1, 1)))
        mp1_dup = MultiPoint(((0, 0), (1, 1)))
        mp1_rev = MultiPoint(((1, 1), (0, 0)))
        mp2 = MultiPoint(((0, 0), (1, 1), (2, 2)))
        mp3 = MultiPoint(((0, 0), (1, 1), (2, 3)))

        self.assertEqual(mp1, mp1_dup)
        self.assertNotEqual(mp1, mp1_rev)  # is this correct?
        self.assertNotEqual(mp1, mp2)
        self.assertNotEqual(mp2, mp3)

        p = Point(0, 0)
        mp = MultiPoint([(0, 0)])
        self.assertNotEqual(p, mp)
        self.assertNotEqual(mp, p)

    def test_polygon(self):
        shell = ((0, 0), (3, 0), (3, 3), (0, 3))
        hole = ((1, 1), (2, 1), (2, 2), (1, 2))
        p_solid = Polygon(shell)
        p2_solid = Polygon(shell)
        p_hole = Polygon(shell, holes=[hole])
        p2_hole = Polygon(shell, holes=[hole])

        self.assertEqual(p_solid, p2_solid)
        self.assertEqual(p_hole, p2_hole)
        self.assertNotEqual(p_solid, p_hole)

        shell2 = ((-5, 2), (10.5, 3), (7, 3))
        p3_hole = Polygon(shell2, holes=[hole])
        self.assertNotEqual(p_hole, p3_hole)

    def test_linestring(self):
        line1 = LineString([(0,0), (1,1), (2,2)])
        line2 = LineString([(0,0), (2,2)])
        line2_dup = LineString([(0,0), (2,2)])
        # .equals() indicates these are the same
        self.assertTrue(line1.equals(line2))
        # but == indicates these are different
        self.assertNotEqual(line1, line2)
        # but dupes are the same with ==
        self.assertEqual(line2, line2_dup)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(OperatorsTestCase)
Shapely-1.6.4/tests/test_parallel_offset.py000066400000000000000000000031351323200062600210400ustar00rootroot00000000000000from . import unittest
from shapely.geos import geos_version
from shapely.geometry import LineString, LinearRing
from shapely.wkt import loads

class OperationsTestCase(unittest.TestCase):
    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_parallel_offset_linestring(self):
        line1 = LineString([(0, 0), (10, 0)])
        left = line1.parallel_offset(5, 'left')
        self.assertEqual(left, LineString([(0, 5), (10, 5)]))
        right = line1.parallel_offset(5, 'right')
        self.assertEqual(right, LineString([(10, -5), (0, -5)]))
        right = line1.parallel_offset(-5, 'left')
        self.assertEqual(right, LineString([(10, -5), (0, -5)]))
        left = line1.parallel_offset(-5, 'right')
        self.assertEqual(left, LineString([(0, 5), (10, 5)]))

        # by default, parallel_offset is right-handed
        self.assertEqual(line1.parallel_offset(5), right)

        line2 = LineString([(0, 0), (5, 0), (5, -5)])
        self.assertEqual(line2.parallel_offset(2, 'left', resolution=1),
                         LineString([(0, 2), (5, 2), (7, 0), (7, -5)]))
        self.assertEqual(line2.parallel_offset(2, 'left', join_style=2,
                         resolution=1),
                         LineString([(0, 2), (7, 2), (7, -5)]))

    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
    def test_parallel_offset_linear_ring(self):
        lr1 = LinearRing([(0, 0), (5, 0), (5, 5), (0, 5), (0, 0)])
        self.assertEqual(lr1.parallel_offset(2, 'left', resolution=1),
                         LineString([(2, 2), (3, 2), (3, 3), (2, 3), (2, 2)]))
Shapely-1.6.4/tests/test_persist.py000066400000000000000000000042261323200062600173710ustar00rootroot00000000000000"""Persistence tests
"""
from . import unittest
import pickle
from shapely import wkb, wkt
from shapely.geometry import Point
import struct


class PersistTestCase(unittest.TestCase):

    @staticmethod
    def _byte(i):
        """Convert an integer in the range [0, 256) to a byte."""
        if bytes == str: # Python 2
            return chr(i)
        else: # Python 3
            return int(i)

    def test_pickle(self):

        p = Point(0.0, 0.0)
        data = pickle.dumps(p)
        q = pickle.loads(data)
        self.assertTrue(q.equals(p))

    def test_wkb(self):

        p = Point(0.0, 0.0)
        wkb_big_endian = wkb.dumps(p, big_endian=True)
        wkb_little_endian = wkb.dumps(p, big_endian=False)
        # Regardless of byte order, loads ought to correctly recover the
        # geometry
        self.assertTrue(p.equals(wkb.loads(wkb_big_endian)))
        self.assertTrue(p.equals(wkb.loads(wkb_little_endian)))

    def test_wkb_dumps_endianness(self):

        p = Point(0.5, 2.0)
        wkb_big_endian = wkb.dumps(p, big_endian=True)
        wkb_little_endian = wkb.dumps(p, big_endian=False)
        self.assertNotEqual(wkb_big_endian, wkb_little_endian)
        # According to WKB specification in section 3.3 of OpenGIS
        # Simple Features Specification for SQL, revision 1.1, the
        # first byte of a WKB representation indicates byte order.
        # Big-endian is 0, little-endian is 1.
        self.assertEqual(wkb_big_endian[0], self._byte(0))
        self.assertEqual(wkb_little_endian[0], self._byte(1))
        # Check that the doubles (0.5, 2.0) are in correct byte order
        double_size = struct.calcsize('d')
        self.assertEqual(
            wkb_big_endian[(-2 * double_size):],
            struct.pack('>2d', p.x, p.y))
        self.assertEqual(
            wkb_little_endian[(-2 * double_size):],
            struct.pack('<2d', p.x, p.y))

    def test_wkt(self):
        p = Point(0.0, 0.0)
        text = wkt.dumps(p)
        self.assertTrue(text.startswith('POINT'))
        pt = wkt.loads(text)
        self.assertTrue(pt.equals(p))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PersistTestCase)
Shapely-1.6.4/tests/test_pickle.py000066400000000000000000000020751323200062600171470ustar00rootroot00000000000000import pytest
from shapely.geometry import Point, LineString, LinearRing, Polygon, MultiPoint

import sys
if sys.version_info[0] >= 3:
    from pickle import dumps, loads, HIGHEST_PROTOCOL
else:
    from cPickle import dumps, loads, HIGHEST_PROTOCOL

TEST_DATA = {
    "point2d": (Point, [(1.0, 2.0)]),
    "point3d": (Point, [(1.0, 2.0, 3.0)]),
    "linestring": (LineString, [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)]),
    "linearring": (LinearRing, [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
    "polygon": (Polygon, [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
    "multipoint": (MultiPoint, [(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)]),
}
TEST_NAMES, TEST_DATA = zip(*TEST_DATA.items())
@pytest.mark.parametrize("cls,coords", TEST_DATA, ids=TEST_NAMES)
def test_pickle_round_trip(cls, coords):
    geom1 = cls(coords)
    assert geom1.has_z == (len(coords[0]) == 3)
    data = dumps(geom1, HIGHEST_PROTOCOL)
    geom2 = loads(data)
    assert geom2.has_z == geom1.has_z
    assert type(geom2) is type(geom1)
    assert geom2.type == geom1.type
    assert geom2.wkt == geom1.wkt
Shapely-1.6.4/tests/test_point.py000066400000000000000000000107771323200062600170410ustar00rootroot00000000000000from . import unittest, numpy
from shapely.geometry import Point, asPoint
from shapely.errors import DimensionError


class LineStringTestCase(unittest.TestCase):

    def test_point(self):

        # Test 2D points
        p = Point(1.0, 2.0)
        self.assertEqual(p.x, 1.0)
        self.assertEqual(p.y, 2.0)
        self.assertEqual(p.coords[:], [(1.0, 2.0)])
        self.assertEqual(str(p), p.wkt)
        self.assertFalse(p.has_z)
        with self.assertRaises(DimensionError):
            p.z

        # Check 3D
        p = Point(1.0, 2.0, 3.0)
        self.assertEqual(p.coords[:], [(1.0, 2.0, 3.0)])
        self.assertEqual(str(p), p.wkt)
        self.assertTrue(p.has_z)
        self.assertEqual(p.z, 3.0)

        # From coordinate sequence
        p = Point((3.0, 4.0))
        self.assertEqual(p.coords[:], [(3.0, 4.0)])

        # From another point
        q = Point(p)
        self.assertEqual(q.coords[:], [(3.0, 4.0)])

        # Coordinate access
        self.assertEqual(p.x, 3.0)
        self.assertEqual(p.y, 4.0)
        self.assertEqual(tuple(p.coords), ((3.0, 4.0),))
        self.assertEqual(p.coords[0], (3.0, 4.0))
        with self.assertRaises(IndexError):  # index out of range
            p.coords[1]

        # Bounds
        self.assertEqual(p.bounds, (3.0, 4.0, 3.0, 4.0))

        # Geo interface
        self.assertEqual(p.__geo_interface__,
                         {'type': 'Point', 'coordinates': (3.0, 4.0)})

        # Modify coordinates
        p.coords = (2.0, 1.0)
        self.assertEqual(p.__geo_interface__,
                         {'type': 'Point', 'coordinates': (2.0, 1.0)})

        # Alternate method
        p.coords = ((0.0, 0.0),)
        self.assertEqual(p.__geo_interface__,
                         {'type': 'Point', 'coordinates': (0.0, 0.0)})

        # Adapt a coordinate list to a point
        coords = [3.0, 4.0]
        pa = asPoint(coords)
        self.assertEqual(pa.coords[0], (3.0, 4.0))
        self.assertEqual(pa.distance(p), 5.0)

        # Move the coordinates and watch the distance change
        coords[0] = 1.0
        self.assertEqual(pa.coords[0], (1.0, 4.0))
        self.assertAlmostEqual(pa.distance(p), 4.123105625617661)

        # Test Non-operability of Null geometry
        p_null = Point()
        self.assertEqual(p_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
        self.assertEqual(p_null.coords[:], [])
        self.assertEqual(p_null.area, 0.0)

        # Check that we can set coordinates of a null geometry
        p_null.coords = (1, 2)
        self.assertEqual(p_null.coords[:], [(1.0, 2.0)])

    @unittest.skipIf(not numpy, 'Numpy required')
    def test_numpy(self):

        from numpy import array, asarray
        from numpy.testing import assert_array_equal

        # Construct from a numpy array
        p = Point(array([1.0, 2.0]))
        self.assertEqual(p.coords[:], [(1.0, 2.0)])

        # Adapt a Numpy array to a point
        a = array([1.0, 2.0])
        pa = asPoint(a)
        assert_array_equal(pa.context, array([1.0, 2.0]))
        self.assertEqual(pa.coords[:], [(1.0, 2.0)])

        # Now, the inverse
        self.assertEqual(pa.__array_interface__,
                         pa.context.__array_interface__)

        pas = asarray(pa)
        assert_array_equal(pas, array([1.0, 2.0]))

        # Adapt a coordinate list to a point
        coords = [3.0, 4.0]
        pa = asPoint(coords)
        coords[0] = 1.0

        # Now, the inverse (again?)
        self.assertIsNotNone(pa.__array_interface__)
        pas = asarray(pa)
        assert_array_equal(pas, array([1.0, 4.0]))

        # From Array.txt
        p = Point(0.0, 0.0, 1.0)
        coords = p.coords[0]
        self.assertEqual(coords, (0.0, 0.0, 1.0))
        self.assertIsNotNone(p.ctypes)

        # Convert to Numpy array, passing through Python sequence
        a = asarray(coords)
        self.assertEqual(a.ndim, 1)
        self.assertEqual(a.size, 3)
        self.assertEqual(a.shape, (3,))

        # Convert to Numpy array, passing through a ctypes array
        b = asarray(p)
        self.assertEqual(b.size, 3)
        self.assertEqual(b.shape, (3,))
        assert_array_equal(b, array([0.0, 0.0, 1.0]))

        # Make a point from a Numpy array
        a = asarray([1.0, 1.0, 0.0])
        p = Point(*list(a))
        self.assertEqual(p.coords[:], [(1.0, 1.0, 0.0)])

        # Test array interface of empty geometry
        pe = Point()
        a = asarray(pe)
        self.assertEqual(a.shape[0], 0)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(LineStringTestCase)
Shapely-1.6.4/tests/test_polygon.py000066400000000000000000000222141323200062600173640ustar00rootroot00000000000000"""Polygons and Linear Rings
"""
from . import unittest, numpy
from shapely.wkb import loads as load_wkb
from shapely.geos import lgeos
from shapely.geometry import Point, Polygon, asPolygon
from shapely.geometry.polygon import LinearRing, LineString, asLinearRing
from shapely.geometry.base import dump_coords


class PolygonTestCase(unittest.TestCase):

    def test_polygon(self):

        # Initialization
        # Linear rings won't usually be created by users, but by polygons
        coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
        ring = LinearRing(coords)
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], ring.coords[-1])
        self.assertTrue(ring.is_ring)

        # Coordinate modification
        ring.coords = ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0))
        self.assertEqual(
            ring.__geo_interface__,
            {'type': 'LinearRing',
             'coordinates': ((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0),
                             (0.0, 0.0))})

        # Test ring adapter
        coords = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]
        ra = asLinearRing(coords)
        self.assertTrue(ra.wkt.upper().startswith('LINEARRING'))
        self.assertEqual(dump_coords(ra),
                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0),
                          (0.0, 0.0)])
        coords[3] = [2.0, -1.0]
        self.assertEqual(dump_coords(ra),
                         [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
                          (0.0, 0.0)])

        # Construct a polygon, exterior ring only
        polygon = Polygon(coords)
        self.assertEqual(len(polygon.exterior.coords), 5)

        # Ring Access
        self.assertIsInstance(polygon.exterior, LinearRing)
        ring = polygon.exterior
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], (0., 0.))
        self.assertTrue(ring.is_ring)
        self.assertEqual(len(polygon.interiors), 0)

        # Create a new polygon from WKB
        data = polygon.wkb
        polygon = None
        ring = None
        polygon = load_wkb(data)
        ring = polygon.exterior
        self.assertEqual(len(ring.coords), 5)
        self.assertEqual(ring.coords[0], ring.coords[4])
        self.assertEqual(ring.coords[0], (0., 0.))
        self.assertTrue(ring.is_ring)
        polygon = None

        # Interior rings (holes)
        polygon = Polygon(coords, [((0.25, 0.25), (0.25, 0.5),
                                    (0.5, 0.5), (0.5, 0.25))])
        self.assertEqual(len(polygon.exterior.coords), 5)
        self.assertEqual(len(polygon.interiors[0].coords), 5)
        with self.assertRaises(IndexError):  # index out of range
            polygon.interiors[1]

        # Test from another Polygon
        copy = Polygon(polygon)
        self.assertEqual(len(polygon.exterior.coords), 5)
        self.assertEqual(len(polygon.interiors[0].coords), 5)
        with self.assertRaises(IndexError):  # index out of range
            polygon.interiors[1]

        # Coordinate getters and setters raise exceptions
        self.assertRaises(NotImplementedError, polygon._get_coords)
        with self.assertRaises(NotImplementedError):
            polygon.coords

        # Geo interface
        self.assertEqual(
            polygon.__geo_interface__,
            {'type': 'Polygon',
             'coordinates': (((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0),
                             (0.0, 0.0)), ((0.25, 0.25), (0.25, 0.5),
                             (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)))})

        # Adapter
        hole_coords = [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
        pa = asPolygon(coords, hole_coords)
        self.assertEqual(len(pa.exterior.coords), 5)
        self.assertEqual(len(pa.interiors), 1)
        self.assertEqual(len(pa.interiors[0].coords), 5)

        # Test Non-operability of Null rings
        r_null = LinearRing()
        self.assertEqual(r_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
        self.assertEqual(r_null.length, 0.0)

        # Check that we can set coordinates of a null geometry
        r_null.coords = [(0, 0), (1, 1), (1, 0)]
        self.assertAlmostEqual(r_null.length, 3.414213562373095)

        # Error handling
        with self.assertRaises(ValueError):
            # A LinearRing must have at least 3 coordinate tuples
            Polygon([[1, 2], [2, 3]])


    def test_linearring_from_closed_linestring(self):
        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
        line = LineString(coords)
        ring = LinearRing(line)
        self.assertEqual(len(ring.coords), 4)
        self.assertEqual(ring.coords[:], coords)
        self.assertEqual('LinearRing',
                         lgeos.GEOSGeomType(ring._geom).decode('ascii'))


    def test_linearring_from_unclosed_linestring(self):
        coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
        line = LineString(coords[:-1])  # Pass in unclosed line
        ring = LinearRing(line)
        self.assertEqual(len(ring.coords), 4)
        self.assertEqual(ring.coords[:], coords)
        self.assertEqual('LinearRing',
                         lgeos.GEOSGeomType(ring._geom).decode('ascii'))


    @unittest.skipIf(not numpy, 'Numpy required')
    def test_numpy(self):

        from numpy import array, asarray
        from numpy.testing import assert_array_equal

        a = asarray(((0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)))
        polygon = Polygon(a)
        self.assertEqual(len(polygon.exterior.coords), 5)
        self.assertEqual(dump_coords(polygon.exterior),
                         [(0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)])
        self.assertEqual(len(polygon.interiors), 0)
        b = asarray(polygon.exterior)
        self.assertEqual(b.shape, (5, 2))
        assert_array_equal(
            b, array([(0., 0.), (0., 1.), (1., 1.), (1., 0.), (0., 0.)]))

    def test_dimensions(self):

        # Background: see http://trac.gispython.org/lab/ticket/168
    # http://lists.gispython.org/pipermail/community/2008-August/001859.html

        coords = ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
                  (1.0, 0.0, 0.0))
        polygon = Polygon(coords)
        self.assertEqual(polygon._ndim, 3)
        gi = polygon.__geo_interface__
        self.assertEqual(
            gi['coordinates'],
            (((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
              (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)),))

        e = polygon.exterior
        self.assertEqual(e._ndim, 3)
        gi = e.__geo_interface__
        self.assertEqual(
            gi['coordinates'],
            ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
             (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)))

    def test_attribute_chains(self):

        # Attribute Chaining
        # See also ticket #151.
        p = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
        self.assertEqual(
            list(p.boundary.coords),
            [(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0), (0.0, 0.0)])

        ec = list(Point(0.0, 0.0).buffer(1.0, 1).exterior.coords)
        self.assertIsInstance(ec, list)  # TODO: this is a poor test

        # Test chained access to interiors
        p = Polygon(
            ((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)),
            [((-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25))]
        )
        self.assertEqual(p.area, 0.75)

        """Not so much testing the exact values here, which are the
        responsibility of the geometry engine (GEOS), but that we can get
        chain functions and properties using anonymous references.
        """
        self.assertEqual(
            list(p.interiors[0].coords),
            [(-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25),
             (-0.25, 0.25)])
        xy = list(p.interiors[0].buffer(1).exterior.coords)[0]
        self.assertEqual(len(xy), 2)

        # Test multiple operators, boundary of a buffer
        ec = list(p.buffer(1).boundary.coords)
        self.assertIsInstance(ec, list)  # TODO: this is a poor test

    def test_empty_equality(self):
        # Test equals operator, including empty geometries
        # see issue #338

        point1 = Point(0, 0)
        polygon1 = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
        polygon2 = Polygon(((0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)))
        polygon_empty1 = Polygon()
        polygon_empty2 = Polygon()

        self.assertNotEqual(point1, polygon1)
        self.assertEqual(polygon_empty1, polygon_empty2)
        self.assertNotEqual(polygon1, polygon_empty1)
        self.assertEqual(polygon1, polygon2)
        self.assertNotEqual(None, polygon_empty1)

    def test_from_bounds(self):
        xmin, ymin, xmax, ymax = -180, -90, 180, 90
        coords = [
            (xmin, ymin),
            (xmin, ymax),
            (xmax, ymax),
            (xmax, ymin)]
        self.assertEqual(
            Polygon(coords),
            Polygon.from_bounds(xmin, ymin, xmax, ymax))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PolygonTestCase)
Shapely-1.6.4/tests/test_polygonize.py000077500000000000000000000031661323200062600201040ustar00rootroot00000000000000from . import unittest
from shapely.geos import geos_version
from shapely.geometry import Point, LineString, Polygon
from shapely.geometry.base import dump_coords
from shapely.ops import polygonize, polygonize_full


class PolygonizeTestCase(unittest.TestCase):

    def test_polygonize(self):
        lines = [
            LineString(((0, 0), (1, 1))),
            LineString(((0, 0), (0, 1))),
            LineString(((0, 1), (1, 1))),
            LineString(((1, 1), (1, 0))),
            LineString(((1, 0), (0, 0))),
            LineString(((5, 5), (6, 6))),
            Point(0, 0),
            ]
        result = list(polygonize(lines))
        self.assertTrue(all([isinstance(x, Polygon) for x in result]))

    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
    def test_polygonize_full(self):

        lines2 = [
            ((0, 0), (1, 1)),
            ((0, 0), (0, 1)),
            ((0, 1), (1, 1)),
            ((1, 1), (1, 0)),
            ((1, 0), (0, 0)),
            ((5, 5), (6, 6)),
            ((1, 1), (100, 100)),
            ]

        result2, dangles, cuts, invalids = polygonize_full(lines2)
        self.assertEqual(len(result2), 2)
        self.assertTrue(all([isinstance(x, Polygon) for x in result2]))
        self.assertEqual(list(dangles.geoms), [])
        self.assertTrue(all([isinstance(x, LineString) for x in cuts.geoms]))

        self.assertEqual(
            dump_coords(cuts),
            [[(1.0, 1.0), (100.0, 100.0)], [(5.0, 5.0), (6.0, 6.0)]])
        self.assertEqual(list(invalids.geoms), [])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PolygonizeTestCase)
Shapely-1.6.4/tests/test_polylabel.py000066400000000000000000000043651323200062600176670ustar00rootroot00000000000000from . import unittest
from shapely.algorithms.polylabel import polylabel, Cell
from shapely.geometry import LineString, Point, Polygon
from shapely.geos import TopologicalError


class PolylabelTestCase(unittest.TestCase):
    def test_polylabel(self):
        """
        Finds pole of inaccessibility for a polygon with a tolerance of 10

        """
        polygon = LineString([(0, 0), (50, 200), (100, 100), (20, 50),
                              (-100, -20), (-150, -200)]).buffer(100)
        label = polylabel(polygon, tolerance=10)
        expected = Point(59.35615556364569, 121.8391962974644)
        self.assertTrue(expected.almost_equals(label))

    def test_invalid_polygon(self):
        """
        Makes sure that the polylabel function throws an exception when provided
        an invalid polygon.

        """
        bowtie_polygon = Polygon([(0, 0), (0, 20), (10, 10), (20, 20),
                                  (20, 0), (10, 10), (0, 0)])
        self.assertRaises(TopologicalError, polylabel, bowtie_polygon)

    def test_cell_sorting(self):
        """
        Tests rich comparison operators of Cells for use in the polylabel
        minimum priority queue.

        """
        polygon = Point(0, 0).buffer(100)
        cell1 = Cell(0, 0, 50, polygon)  # closest
        cell2 = Cell(50, 50, 50, polygon)  # furthest
        self.assertLess(cell1, cell2)
        self.assertLessEqual(cell1, cell2)
        self.assertFalse(cell2 <= cell1)
        self.assertEqual(cell1, cell1)
        self.assertFalse(cell1 == cell2)
        self.assertNotEqual(cell1, cell2)
        self.assertFalse(cell1 != cell1)
        self.assertGreater(cell2, cell1)
        self.assertFalse(cell1 > cell2)
        self.assertGreaterEqual(cell2, cell1)
        self.assertFalse(cell1 >= cell2)

    def test_concave_polygon(self):
        """
        Finds pole of inaccessibility for a concave polygon and ensures that
        the point is inside.

        """
        concave_polygon = LineString([(500, 0), (0, 0), (0, 500),
                                      (500, 500)]).buffer(100)
        label = polylabel(concave_polygon)
        self.assertTrue(concave_polygon.contains(label))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PolylabelTestCase)
Shapely-1.6.4/tests/test_predicates.py000066400000000000000000000047121323200062600200230ustar00rootroot00000000000000"""Test GEOS predicates
"""
from . import unittest
from shapely.geometry import Point, Polygon
from shapely.geos import TopologicalError, PredicateError
import pytest

class PredicatesTestCase(unittest.TestCase):

    def test_binary_predicates(self):

        point = Point(0.0, 0.0)

        self.assertTrue(point.disjoint(Point(-1.0, -1.0)))
        self.assertFalse(point.touches(Point(-1.0, -1.0)))
        self.assertFalse(point.crosses(Point(-1.0, -1.0)))
        self.assertFalse(point.within(Point(-1.0, -1.0)))
        self.assertFalse(point.contains(Point(-1.0, -1.0)))
        self.assertFalse(point.equals(Point(-1.0, -1.0)))
        self.assertFalse(point.touches(Point(-1.0, -1.0)))
        self.assertTrue(point.equals(Point(0.0, 0.0)))
        self.assertTrue(point.covers(Point(0.0, 0.0)))
        self.assertFalse(point.covers(Point(-1.0, -1.0)))

    def test_unary_predicates(self):

        point = Point(0.0, 0.0)

        self.assertFalse(point.is_empty)
        self.assertTrue(point.is_valid)
        self.assertTrue(point.is_simple)
        self.assertFalse(point.is_ring)
        self.assertFalse(point.has_z)

    def test_binary_predicate_exceptions(self):

        p1 = [(339, 346), (459,346), (399,311), (340, 277), (399, 173),
              (280, 242), (339, 415), (280, 381), (460, 207), (339, 346)]
        p2 = [(339, 207), (280, 311), (460, 138), (399, 242), (459, 277),
              (459, 415), (399, 381), (519, 311), (520, 242), (519, 173),
              (399, 450), (339, 207)]
        self.assertRaises(TopologicalError, Polygon(p1).within, Polygon(p2))

    def test_relate_pattern(self):

        # a pair of partially overlapping polygons, and a nearby point
        g1 = Polygon([(0, 0), (0, 1), (3, 1), (3, 0), (0, 0)])
        g2 = Polygon([(1, -1), (1, 2), (2, 2), (2, -1), (1, -1)])
        g3 = Point(5, 5)

        assert(g1.relate(g2) == '212101212')
        assert(g1.relate_pattern(g2, '212101212'))
        assert(g1.relate_pattern(g2, '*********'))
        assert(g1.relate_pattern(g2, '2********'))
        assert(g1.relate_pattern(g2, 'T********'))
        assert(not g1.relate_pattern(g2, '112101212'))
        assert(not g1.relate_pattern(g2, '1********'))
        assert(g1.relate_pattern(g3, 'FF2FF10F2'))

        # an invalid pattern should raise an exception
        with pytest.raises(PredicateError):
            g1.relate_pattern(g2, 'fail')

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PredicatesTestCase)
Shapely-1.6.4/tests/test_prepared.py000066400000000000000000000045531323200062600175050ustar00rootroot00000000000000from . import unittest
from shapely.geos import geos_version
from shapely import prepared
from shapely import geometry


class PreparedGeometryTestCase(unittest.TestCase):

    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
    def test_prepared(self):
        polygon = geometry.Polygon([
            (0, 0), (1, 0), (1, 1), (0, 1)
        ])
        p = prepared.PreparedGeometry(polygon)
        self.assertTrue(p.contains(geometry.Point(0.5, 0.5)))
        self.assertFalse(p.contains(geometry.Point(0.5, 1.5)))

    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
    def test_op_not_allowed(self):
        p = prepared.PreparedGeometry(geometry.Point(0.0, 0.0).buffer(1.0))
        self.assertRaises(ValueError, geometry.Point(0.0, 0.0).union, p)

    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
    def test_predicate_not_allowed(self):
        p = prepared.PreparedGeometry(geometry.Point(0.0, 0.0).buffer(1.0))
        self.assertRaises(ValueError, geometry.Point(0.0, 0.0).contains, p)

    @unittest.skipIf(geos_version < (3, 1, 0), 'GEOS 3.1.0 required')
    def test_prepared_predicates(self):
        # check prepared predicates give the same result as regular predicates
        polygon1 = geometry.Polygon([
            (0, 0), (0, 1), (1, 1), (1, 0), (0, 0)
        ])
        polygon2 = geometry.Polygon([
            (0.5, 0.5), (1.5, 0.5), (1.0, 1.0), (0.5, 0.5)
        ])
        point2 = geometry.Point(0.5, 0.5)
        polygon_empty = geometry.Polygon()
        prepared_polygon1 = prepared.PreparedGeometry(polygon1)
        for geom2 in (polygon2, point2, polygon_empty):
            self.assertTrue(polygon1.disjoint(geom2) == prepared_polygon1.disjoint(geom2))
            self.assertTrue(polygon1.touches(geom2) == prepared_polygon1.touches(geom2))
            self.assertTrue(polygon1.intersects(geom2) == prepared_polygon1.intersects(geom2))
            self.assertTrue(polygon1.crosses(geom2) == prepared_polygon1.crosses(geom2))
            self.assertTrue(polygon1.within(geom2) == prepared_polygon1.within(geom2))
            self.assertTrue(polygon1.contains(geom2) == prepared_polygon1.contains(geom2))
            self.assertTrue(polygon1.overlaps(geom2) == prepared_polygon1.overlaps(geom2))

def test_suite():
    loader = unittest.TestLoader()
    return loader.loadTestsFromTestCase(PreparedGeometryTestCase)
Shapely-1.6.4/tests/test_products_z.py000066400000000000000000000010051323200062600200640ustar00rootroot00000000000000from . import unittest
from shapely.geometry import LineString


class ProductZTestCase(unittest.TestCase):

    def test_line_intersection(self):
        line1 = LineString([(0, 0, 0), (1, 1, 1)])
        line2 = LineString([(0, 1, 1), (1, 0, 0)])
        interxn = line1.intersection(line2)
        self.assertTrue(interxn.has_z)
        self.assertEqual(interxn._ndim, 3)
        self.assertTrue(0.0 <= interxn.z <= 1.0)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(ProductZTestCase)
Shapely-1.6.4/tests/test_shared_paths.py000066400000000000000000000033621323200062600203450ustar00rootroot00000000000000from . import unittest

from shapely.geometry import Point, LineString, Polygon, MultiLineString, \
                             GeometryCollection
from shapely.geos import geos_version
from shapely.ops import shared_paths

@unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
class SharedPaths(unittest.TestCase):
    def test_shared_paths_forward(self):
        g1 = LineString([(0, 0), (10, 0), (10, 5), (20, 5)])
        g2 = LineString([(5, 0), (15, 0)])
        result = shared_paths(g1, g2)
        
        self.assertTrue(isinstance(result, GeometryCollection))
        self.assertTrue(len(result) == 2)
        a, b = result
        self.assertTrue(isinstance(a, MultiLineString))
        self.assertTrue(len(a) == 1)
        self.assertEqual(a[0].coords[:], [(5, 0), (10, 0)])
        self.assertTrue(b.is_empty)

    def test_shared_paths_forward(self):
        g1 = LineString([(0, 0), (10, 0), (10, 5), (20, 5)])
        g2 = LineString([(15, 0), (5, 0)])
        result = shared_paths(g1, g2)
        
        self.assertTrue(isinstance(result, GeometryCollection))
        self.assertTrue(len(result) == 2)
        a, b = result
        self.assertTrue(isinstance(b, MultiLineString))
        self.assertTrue(len(b) == 1)
        self.assertEqual(b[0].coords[:], [(5, 0), (10, 0)])
        self.assertTrue(a.is_empty)
    
    def test_wrong_type(self):
        g1 = Point(0, 0)
        g2 = LineString([(5, 0), (15, 0)])
        
        with self.assertRaises(TypeError):
            result = shared_paths(g1, g2)
            
        with self.assertRaises(TypeError):
            result = shared_paths(g2, g1)

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(SharedPaths)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_singularity.py000066400000000000000000000007621323200062600202530ustar00rootroot00000000000000from . import unittest
from shapely.geometry import Polygon


class PolygonTestCase(unittest.TestCase):

    def test_polygon_3(self):
        p = (1.0, 1.0)
        poly = Polygon([p, p, p])
        self.assertEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))

    def test_polygon_5(self):
        p = (1.0, 1.0)
        poly = Polygon([p, p, p, p, p])
        self.assertEqual(poly.bounds, (1.0, 1.0, 1.0, 1.0))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(PolygonTestCase)
Shapely-1.6.4/tests/test_snap.py000066400000000000000000000020551323200062600166370ustar00rootroot00000000000000from . import unittest

from shapely.geometry import LineString, Polygon
from shapely.geos import geos_version
from shapely.ops import snap

@unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
class Snap(unittest.TestCase):
    def test_snap(self):
        
        # input geometries
        square = Polygon([(1,1), (2, 1), (2, 2), (1, 2), (1, 1)])
        line = LineString([(0,0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)])
                
        square_coords = square.exterior.coords[:]
        line_coords = line.coords[:]

        result = snap(line, square, 0.5)

        # test result is correct
        self.assertTrue(isinstance(result, LineString))
        self.assertEqual(result.coords[:], [(0.0, 0.0), (1.0, 1.0), (2.0, 1.0), (2.6, 0.5)])
        
        # test inputs have not been modified
        self.assertEqual(square.exterior.coords[:], square_coords)
        self.assertEqual(line.coords[:], line_coords)

def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(Snap)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_split.py000066400000000000000000000165541323200062600170420ustar00rootroot00000000000000from shapely.ops import split

from . import unittest

from shapely.geometry import Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection
from shapely.ops import cascaded_union, linemerge

class TestSplitGeometry(unittest.TestCase):
	# helper class for testing below
	def helper(self, geom, splitter, expected_chunks):
		s = split(geom, splitter)
		self.assertEqual(s.type, "GeometryCollection")
		self.assertEqual(len(s), expected_chunks)
		if expected_chunks > 1:
			# split --> expected collection that when merged is again equal to original geometry
			if s.geoms[0].type == 'LineString':
				self.assertTrue(linemerge(s).simplify(0.000001).equals(geom))
			elif s.geoms[0].type == 'Polygon':
				union = cascaded_union(s).simplify(0.000001)
				self.assertTrue(union.equals(geom))
				self.assertEqual(union.area, geom.area)
			else:
				raise ValueError
		elif expected_chunks == 1:
			# not split --> expected equal to line
			self.assertTrue(s[0].equals(geom))

	def test_split_closed_line_with_point(self):
		# point at start/end of closed ring -> return equal
		# see GH #524
		ls = LineString([(0,0), (0, 1), (1, 1), (1, 0), (0, 0)])
		splitter = Point(0, 0)
		self.helper(ls, splitter, 1)


class TestSplitPolygon(TestSplitGeometry):
	poly_simple = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)])
	poly_hole = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)], [[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]])

	def test_split_poly_with_line(self):
		# crossing at 2 points --> return 2 segments
		splitter = LineString([(1, 3), (1, -3)])
		self.helper(self.poly_simple, splitter, 2)
		self.helper(self.poly_hole, splitter, 2)

		# touching the boundary--> return equal
		splitter = LineString([(0, 2), (5, 2)])
		self.helper(self.poly_simple, splitter, 1)
		self.helper(self.poly_hole, splitter, 1)

		# inside the polygon --> return equal
		splitter = LineString([(0.2, 0.2), (1.7, 1.7), (3, 2)])
		self.helper(self.poly_simple, splitter, 1)
		self.helper(self.poly_hole, splitter, 1)

		# outside the polygon --> return equal
		splitter = LineString([(0, 3), (3, 3) , (3, 0)])
		self.helper(self.poly_simple, splitter, 1)
		self.helper(self.poly_hole, splitter, 1)

	def test_split_poly_with_other(self):
		with self.assertRaises(ValueError):
			split(self.poly_simple, Point(1, 1))
		with self.assertRaises(ValueError):
			split(self.poly_simple, MultiPoint([(1, 1), (3, 4)]))
		with self.assertRaises(ValueError):
			split(self.poly_simple, self.poly_hole)

class TestSplitLine(TestSplitGeometry):
	ls = LineString([(0, 0), (1.5, 1.5), (3.0, 4.0)])

	def test_split_line_with_point(self):
		# point on line interior --> return 2 segments
		splitter = Point(1, 1)
		self.helper(self.ls, splitter, 2)

		# point on line point --> return 2 segments
		splitter = Point(1.5, 1.5)
		self.helper(self.ls, splitter, 2)

		# point on boundary --> return equal
		splitter = Point(3, 4)
		self.helper(self.ls, splitter, 1)

		# point on exterior of line --> return equal
		splitter = Point(2, 2)
		self.helper(self.ls, splitter, 1)

	def test_split_line_with_multipoint(self):
		# points on line interior --> return 4 segments
		splitter = MultiPoint([(1,1), (1.5, 1.5), (0.5, 0.5)])
		self.helper(self.ls, splitter, 4)

		# points on line interior and boundary -> return 2 segments
		splitter = MultiPoint([(1, 1), (3, 4)])
		self.helper(self.ls, splitter, 2)

		# point on linear interior but twice --> return 2 segments
		splitter = MultiPoint([(1, 1), (1.5, 1.5), (1, 1)])
		self.helper(self.ls, splitter, 3)

	def test_split_line_with_line(self):
		# crosses at one point --> return 2 segments
		splitter = LineString([(0, 1), (1, 0)])
		self.helper(self.ls, splitter, 2)

		# crosses at two points --> return 3 segments
		splitter = LineString([(0, 1), (1, 0), (1, 2)])
		self.helper(self.ls, splitter, 3)

		# overlaps --> raise
		splitter = LineString([(0, 0), (15, 15)])
		with self.assertRaises(ValueError):
			self.helper(self.ls, splitter, 1)

		# does not cross --> return equal
		splitter = LineString([(0, 1), (0, 2)])
		self.helper(self.ls, splitter, 1)

		# is touching the boundary --> return equal
		splitter = LineString([(-1, 1), (1, -1)])
		self.assertTrue(splitter.touches(self.ls))
		self.helper(self.ls, splitter, 1)

	def test_split_line_with_multiline(self):
		# crosses at one point --> return 2 segments
		splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 0), (2, -2)]])
		self.helper(self.ls, splitter, 2)

		# crosses at two points --> return 3 segments
		splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 2), (2, 0)]])
		self.helper(self.ls, splitter, 3)

		# crosses at three points --> return 4 segments
		splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 2), (2, 0), (2.2, 3.2)]])
		self.helper(self.ls, splitter, 4)

		# overlaps --> raise
		splitter = MultiLineString([[(0, 0), (1.5, 1.5)], [(1.5, 1.5), (3, 4)]])
		with self.assertRaises(ValueError):
			self.helper(self.ls, splitter, 1)

		# does not cross --> return equal
		splitter = MultiLineString([[(0, 1), (0, 2)], [(1, 0), (2, 0)]])
		self.helper(self.ls, splitter, 1)

	def test_split_line_with_polygon(self):
		# crosses at two points --> return 3 segments
		splitter = Polygon([(1, 0), (1, 2), (2, 2), (2, 0), (1, 0)])
		self.helper(self.ls, splitter, 3)

		# crosses at one point and touches boundary --> return 2 segments
		splitter = Polygon([(0, 0), (1, 2), (2, 2), (1, 0), (0, 0)])
		self.helper(self.ls, splitter, 2)

		# exterior crosses at one point and touches at (0, 0)
		# interior crosses at two points
		splitter = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)], [[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]])
		self.helper(self.ls, splitter, 4)

	def test_split_line_with_multipolygon(self):
		poly1 = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)]) # crosses at one point and touches at (0, 0)
		poly2 = Polygon([(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]) # crosses at two points
		poly3 = Polygon([(0, 0), (0, -2), (-2, -2), (-2, 0), (0, 0)]) # not crossing
		splitter = MultiPolygon([poly1, poly2, poly3])
		self.helper(self.ls, splitter, 4)

class TestSplitMulti(TestSplitGeometry):

	def test_split_multiline_with_point(self):
		# a cross-like multilinestring with a point in the middle --> return 4 line segments
		l1 = LineString([(0, 1), (2, 1)])
		l2 = LineString([(1, 0), (1, 2)])
		ml = MultiLineString([l1, l2])
		splitter = Point((1, 1))
		self.helper(ml, splitter, 4)

	def test_split_multiline_with_multipoint(self):
		# a cross-like multilinestring with a point in middle, a point on one of the lines and a point in the exterior
		# --> return 4+1 line segments
		l1 = LineString([(0, 1), (3, 1)])
		l2 = LineString([(1, 0), (1, 2)])
		ml = MultiLineString([l1, l2])
		splitter = MultiPoint([(1, 1), (2, 1), (4, 2)])
		self.helper(ml, splitter, 5)

	def test_split_multipolygon_with_line(self):
		# two polygons with a crossing line --> return 4 triangles
		poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
		poly2 = Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
		mpoly = MultiPolygon([poly1, poly2])
		ls = LineString([(-1, -1), (3, 3)])
		self.helper(mpoly, ls, 4)

		# two polygons away from the crossing line --> return identity
		poly1 = Polygon([(10, 10), (10, 11), (11, 11), (11, 10), (10, 10)])
		poly2 = Polygon([(-10, -10), (-10, -11), (-11, -11), (-11, -10), (-10, -10)])
		mpoly = MultiPolygon([poly1, poly2])
		ls = LineString([(-1, -1), (3, 3)])
		self.helper(mpoly, ls, 2)
Shapely-1.6.4/tests/test_strtree.py000066400000000000000000000040711323200062600173660ustar00rootroot00000000000000import gc

from . import unittest

from shapely.strtree import STRtree
from shapely.geometry import Point, Polygon
from shapely.geos import geos_version


@unittest.skipIf(geos_version < (3, 4, 2), 'GEOS 3.4.2 required')
class STRTestCase(unittest.TestCase):
    def test_query(self):
        points = [Point(i, i) for i in range(10)]
        tree = STRtree(points)
        results = tree.query(Point(2,2).buffer(0.99))
        self.assertEqual(len(results), 1)
        results = tree.query(Point(2,2).buffer(1.0))
        self.assertEqual(len(results), 3)

    def test_insert_empty_geometry(self):
        """
        Passing nothing but empty geometries results in an empty strtree.
        The query segfaults if the empty geometry was actually inserted.
        """
        empty = Polygon()
        geoms = [empty]
        tree = STRtree(geoms)
        assert(tree._n_geoms == 0)
        query = Polygon([(0,0),(1,1),(2,0),(0,0)])
        results = tree.query(query)

    def test_query_empty_geometry(self):
        """
        Empty geometries should be filtered out.
        The query segfaults if the empty geometry was actually inserted.
        """
        empty = Polygon()
        point = Point(1, 0.5)
        geoms = [empty, point]
        tree = STRtree(geoms)
        assert(tree._n_geoms == 1)
        query = Polygon([(0,0),(1,1),(2,0),(0,0)])
        results = tree.query(query)
        self.assertEqual(len(results), 1)
        self.assertEqual(results[0], point)

    def test_references(self):
        """Don't crash due to dangling references"""
        empty = Polygon()
        point = Point(1, 0.5)
        geoms = [empty, point]
        tree = STRtree(geoms)
        assert(tree._n_geoms == 1)

        empty = None
        point = None
        gc.collect()

        query = Polygon([(0,0),(1,1),(2,0),(0,0)])
        results = tree.query(query)
        self.assertEqual(len(results), 1)
        self.assertEqual(results[0], Point(1, 0.5))


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(STRTestCase)

if __name__ == '__main__':
    unittest.main()
Shapely-1.6.4/tests/test_styles.py000066400000000000000000000010531323200062600172160ustar00rootroot00000000000000from . import unittest
from shapely.geometry import CAP_STYLE, JOIN_STYLE


class StylesTest(unittest.TestCase):

    def test_cap(self):
        self.assertEqual(CAP_STYLE.round, 1)
        self.assertEqual(CAP_STYLE.flat, 2)
        self.assertEqual(CAP_STYLE.square, 3)

    def test_join(self):
        self.assertEqual(JOIN_STYLE.round, 1)
        self.assertEqual(JOIN_STYLE.mitre, 2)
        self.assertEqual(JOIN_STYLE.bevel, 3)


def test_suite():
    return unittest.TestSuite([
        unittest.TestLoader().loadTestsFromTestCase(StylesTest)])
Shapely-1.6.4/tests/test_svg.py000066400000000000000000000176061323200062600165050ustar00rootroot00000000000000# Tests SVG output and validity
import os
from xml.dom.minidom import parseString as parse_xml_string

from . import unittest
from shapely.geometry import Point, MultiPoint, LineString, MultiLineString,\
    Polygon, MultiPolygon
from shapely.geometry.collection import GeometryCollection


class SvgTestCase(unittest.TestCase):

    def assertSVG(self, geom, expected, **kwrds):
        """Helper function to check XML and debug SVG"""
        svg_elem = geom.svg(**kwrds)
        try:
            parse_xml_string(svg_elem)
        except:
            raise AssertionError(
                'XML is not valid for SVG element: ' + str(svg_elem))
        svg_doc = geom._repr_svg_()
        try:
            doc = parse_xml_string(svg_doc)
        except:
            raise AssertionError(
                'XML is not valid for SVG doucment: ' + str(svg_doc))
        svg_output_dir = None
        # svg_output_dir = '.'  # useful for debugging SVG files
        if svg_output_dir:
            fname = geom.type
            if geom.is_empty:
                fname += '_empty'
            if not geom.is_valid:
                fname += '_invalid'
            if kwrds:
                fname += '_' + \
                    ','.join(str(k) + '=' + str(kwrds[k]) for k in kwrds)
            svg_path = os.path.join(svg_output_dir, fname + '.svg')
            with open(svg_path, 'w') as fp:
                fp.write(doc.toprettyxml())
        self.assertEqual(svg_elem, expected)

    def test_point(self):
        # Empty
        self.assertSVG(Point(), '')
        # Valid
        g = Point(6, 7)
        self.assertSVG(
            g,
            '')
        self.assertSVG(
            g,
            '',
            scale_factor=5)

    def test_multipoint(self):
        # Empty
        self.assertSVG(MultiPoint(), '')
        # Valid
        g = MultiPoint([(6, 7), (3, 4)])
        self.assertSVG(
            g,
            ''
            '')
        self.assertSVG(
            g,
            ''
            '',
            scale_factor=5)

    def test_linestring(self):
        # Empty
        self.assertSVG(LineString(), '')
        # Valid
        g = LineString([(5, 8), (496, -6), (530, 20)])
        self.assertSVG(
            g,
            '')
        self.assertSVG(
            g,
            '',
            scale_factor=5)
        # Invalid
        self.assertSVG(
            LineString([(0, 0), (0, 0)]),
            '')

    def test_multilinestring(self):
        # Empty
        self.assertSVG(MultiLineString(), '')
        # Valid
        self.assertSVG(
            MultiLineString([[(6, 7), (3, 4)], [(2, 8), (9, 1)]]),
            ''
            '')
        # Invalid
        self.assertSVG(
            MultiLineString([[(2, 3), (2, 3)], [(2, 8), (9, 1)]]),
            ''
            '')

    def test_polygon(self):
        # Empty
        self.assertSVG(Polygon(), '')
        # Valid
        g = Polygon([(35, 10), (45, 45), (15, 40), (10, 20), (35, 10)],
                    [[(20, 30), (35, 35), (30, 20), (20, 30)]])
        self.assertSVG(
            g,
            '')
        self.assertSVG(
            g,
            '',
            scale_factor=5)
        # Invalid
        self.assertSVG(
            Polygon([(0, 40), (0, 0), (40, 40), (40, 0), (0, 40)]),
            '')

    def test_multipolygon(self):
        # Empty
        self.assertSVG(MultiPolygon(), '')
        # Valid
        self.assertSVG(
            MultiPolygon([
                Polygon([(40, 40), (20, 45), (45, 30), (40, 40)]),
                Polygon([(20, 35), (10, 30), (10, 10), (30, 5), (45, 20),
                         (20, 35)],
                        [[(30, 20), (20, 15), (20, 25), (30, 20)]])
                ]),
            ''
            '')
        # Invalid
        self.assertSVG(
            MultiPolygon([
                Polygon([(140, 140), (120, 145), (145, 130), (140, 140)]),
                Polygon([(0, 40), (0, 0), (40, 40), (40, 0), (0, 40)])
            ]),
            ''
            '')

    def test_collection(self):
        # Empty
        self.assertSVG(GeometryCollection(), '')
        # Valid
        self.assertSVG(
            Point(7, 3).union(LineString([(4, 2), (8, 4)])),
            ''
            '')
        # Invalid
        self.assertSVG(
            Point(7, 3).union(LineString([(4, 2), (4, 2)])),
            ''
            '')


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(SvgTestCase)
Shapely-1.6.4/tests/test_transform.py000066400000000000000000000057101323200062600177120ustar00rootroot00000000000000from . import unittest
from shapely import geometry
from shapely.ops import transform


class IdentityTestCase(unittest.TestCase):
    """New geometry/coordseq method 'xy' makes numpy interop easier"""

    def func(self, x, y, z=None):
        return tuple([c for c in [x, y, z] if c])

    def test_empty(self):
        g = geometry.Point()
        h = transform(self.func, g)
        self.assertTrue(h.is_empty)

    def test_point(self):
        g = geometry.Point(0, 1)
        h = transform(self.func, g)
        self.assertEqual(h.geom_type, 'Point')
        self.assertEqual(list(h.coords), [(0, 1)])

    def test_line(self):
        g = geometry.LineString(((0, 1), (2, 3)))
        h = transform(self.func, g)
        self.assertEqual(h.geom_type, 'LineString')
        self.assertEqual(list(h.coords), [(0, 1), (2, 3)])

    def test_linearring(self):
        g = geometry.LinearRing(((0, 1), (2, 3), (2, 2), (0, 1)))
        h = transform(self.func, g)
        self.assertEqual(h.geom_type, 'LinearRing')
        self.assertEqual(list(h.coords), [(0, 1), (2, 3), (2, 2), (0, 1)])

    def test_polygon(self):
        g = geometry.Point(0, 1).buffer(1.0)
        h = transform(self.func, g)
        self.assertEqual(h.geom_type, 'Polygon')
        self.assertAlmostEqual(g.area, h.area)

    def test_multipolygon(self):
        g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
        h = transform(self.func, g)
        self.assertEqual(h.geom_type, 'MultiPolygon')
        self.assertAlmostEqual(g.area, h.area)


class LambdaTestCase(unittest.TestCase):
    """New geometry/coordseq method 'xy' makes numpy interop easier"""

    def test_point(self):
        g = geometry.Point(0, 1)
        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
        self.assertEqual(h.geom_type, 'Point')
        self.assertEqual(list(h.coords), [(1.0, 2.0)])

    def test_line(self):
        g = geometry.LineString(((0, 1), (2, 3)))
        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
        self.assertEqual(h.geom_type, 'LineString')
        self.assertEqual(list(h.coords), [(1.0, 2.0), (3.0, 4.0)])

    def test_polygon(self):
        g = geometry.Point(0, 1).buffer(1.0)
        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
        self.assertEqual(h.geom_type, 'Polygon')
        self.assertAlmostEqual(g.area, h.area)
        self.assertAlmostEqual(h.centroid.x, 1.0)
        self.assertAlmostEqual(h.centroid.y, 2.0)

    def test_multipolygon(self):
        g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
        h = transform(lambda x, y, z=None: (x+1.0, y+1.0), g)
        self.assertEqual(h.geom_type, 'MultiPolygon')
        self.assertAlmostEqual(g.area, h.area)
        self.assertAlmostEqual(h.centroid.x, 1.0)
        self.assertAlmostEqual(h.centroid.y, 3.5)


def test_suite():
    loader = unittest.TestLoader()
    return unittest.TestSuite([
        loader.loadTestsFromTestCase(IdentityTestCase),
        loader.loadTestsFromTestCase(LambdaTestCase)])
Shapely-1.6.4/tests/test_union.py000066400000000000000000000046261323200062600170340ustar00rootroot00000000000000from . import unittest
import random
from itertools import islice
from shapely.geos import geos_version
from shapely.ftools import partial
from shapely.geometry import Point, MultiPolygon
from shapely.ops import cascaded_union, unary_union


def halton(base):
    """Returns an iterator over an infinite Halton sequence"""
    def value(index):
        result = 0.0
        f = 1.0 / base
        i = index
        while i > 0:
            result += f * (i % base)
            i = i // base
            f = f / base
        return result
    i = 1
    while i > 0:
        yield value(i)
        i += 1


class UnionTestCase(unittest.TestCase):

    def test_cascaded_union(self):

        # Use a partial function to make 100 points uniformly distributed
        # in a 40x40 box centered on 0,0.

        r = partial(random.uniform, -20.0, 20.0)
        points = [Point(r(), r()) for i in range(100)]

        # Buffer the points, producing 100 polygon spots
        spots = [p.buffer(2.5) for p in points]

        # Perform a cascaded union of the polygon spots, dissolving them
        # into a collection of polygon patches
        u = cascaded_union(spots)
        self.assertTrue(u.geom_type in ('Polygon', 'MultiPolygon'))

    def setUp(self):
        # Instead of random points, use deterministic, pseudo-random Halton
        # sequences for repeatability sake.
        self.coords = zip(
            list(islice(halton(5), 20, 120)),
            list(islice(halton(7), 20, 120)),
        )

    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
    def test_unary_union(self):
        patches = [Point(xy).buffer(0.05) for xy in self.coords]
        u = unary_union(patches)
        self.assertEqual(u.geom_type, 'MultiPolygon')
        self.assertAlmostEqual(u.area, 0.71857254056)

    @unittest.skipIf(geos_version < (3, 3, 0), 'GEOS 3.3.0 required')
    def test_unary_union_multi(self):
        # Test of multipart input based on comment by @schwehr at
        # https://github.com/Toblerity/Shapely/issues/47#issuecomment-21809308
        patches = MultiPolygon([Point(xy).buffer(0.05) for xy in self.coords])
        self.assertAlmostEqual(unary_union(patches).area,
                               0.71857254056)
        self.assertAlmostEqual(unary_union([patches, patches]).area,
                               0.71857254056)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(UnionTestCase)
Shapely-1.6.4/tests/test_validation.py000066400000000000000000000005341323200062600200300ustar00rootroot00000000000000from . import unittest
from shapely.geometry import Point
from shapely.validation import explain_validity


class ValidationTestCase(unittest.TestCase):
    def test_valid(self):
        self.assertEqual(explain_validity(Point(0, 0)), 'Valid Geometry')


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(ValidationTestCase)
Shapely-1.6.4/tests/test_vectorized.py000066400000000000000000000076711323200062600200650ustar00rootroot00000000000000from . import unittest, numpy
from shapely.geometry import Point, box, MultiPolygon


try:
    import numpy as np
    has_numpy = True
except ImportError:
    has_numpy = False


@unittest.skipIf(not has_numpy, 'numpy required')
class VectorizedContainsTestCase(unittest.TestCase):
    def assertContainsResults(self, geom, x, y):
        from shapely.vectorized import contains
        result = contains(geom, x, y)
        x = np.asanyarray(x)
        y = np.asanyarray(y) 

        self.assertIsInstance(result, np.ndarray)
        self.assertEqual(result.dtype, np.bool)

        result_flat = result.flat
        x_flat, y_flat = x.flat, y.flat

        # Do the equivalent operation, only slowly, comparing the result
        # as we go.
        for idx in range(x.size):
            self.assertEqual(result_flat[idx], geom.contains(Point(x_flat[idx],
                                                                   y_flat[idx])))
        return result

    def construct_torus(self):
        point = Point(0, 0)
        return point.buffer(5).symmetric_difference(point.buffer(2.5))

    def test_contains_poly(self):
        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
        self.assertContainsResults(self.construct_torus(), x, y)

    def test_contains_point(self):
        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
        self.assertContainsResults(Point(x[0], y[0]), x, y)
    
    def test_contains_linestring(self):
        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
        self.assertContainsResults(Point(x[0], y[0]), x, y)
    
    def test_contains_multipoly(self):
        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
        # Construct a geometry of the torus cut in half vertically.
        cut_poly = box(-1, -10, -2.5, 10)
        geom = self.construct_torus().difference(cut_poly)
        self.assertIsInstance(geom, MultiPolygon)
        self.assertContainsResults(geom, x, y)

    def test_y_array_order(self):
        y, x = np.mgrid[-10:10:5j, -5:15:5j]
        y = y.copy('f')
        self.assertContainsResults(self.construct_torus(), x, y)
    
    def test_x_array_order(self):
        y, x = np.mgrid[-10:10:5j, -5:15:5j]
        x = x.copy('f')
        self.assertContainsResults(self.construct_torus(), x, y)
    
    def test_xy_array_order(self):
        y, x = np.mgrid[-10:10:5j, -5:15:5j]
        x = x.copy('f')
        y = y.copy('f')
        result = self.assertContainsResults(self.construct_torus(), x, y)
        # We always return a C_CONTIGUOUS array.
        self.assertTrue(result.flags['C_CONTIGUOUS'])
    
    def test_array_dtype(self):
        y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
        x = x.astype(np.int16)
        self.assertContainsResults(self.construct_torus(), x, y)
    
    def test_array_2d(self):
        y, x = np.mgrid[-10:10:15j, -5:15:16j]
        result = self.assertContainsResults(self.construct_torus(), x, y)
        self.assertEqual(result.shape, x.shape)

    def test_shapely_xy_attr_contains(self):
        g = Point(0, 0).buffer(10.0)
        self.assertContainsResults(self.construct_torus(), *g.exterior.xy)


@unittest.skipIf(not has_numpy, 'numpy required')
class VectorizedTouchesTestCase(unittest.TestCase):
    def test_touches(self):
        from shapely.vectorized import touches
        y, x = np.mgrid[-2:3:6j, -1:3:5j]
        geom = box(0, -1, 2, 2)
        result = touches(geom, x, y)
        expected = np.array([[False, False, False, False, False],
                             [False,  True,  True,  True, False],
                             [False,  True, False,  True, False],
                             [False,  True, False,  True, False],
                             [False,  True,  True,  True, False],
                             [False, False, False, False, False]], dtype=bool)
        from numpy.testing import assert_array_equal
        assert_array_equal(result, expected)


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(VectorizedContainsTestCase)
Shapely-1.6.4/tests/test_wkt.py000066400000000000000000000014401323200062600165000ustar00rootroot00000000000000from math import pi

import pytest

from shapely.geometry import LineString, Point
from shapely.wkt import dumps


@pytest.fixture(scope="module")
def pipi():
    return Point((pi, -pi))


@pytest.fixture(scope="module")
def pipi4():
    return Point((pi*4, -pi*4))


def test_wkt(pipi):
    """.wkt and wkt.dumps() both do not trim by default."""
    assert pipi.wkt == "POINT ({0:.16f} {1:.16f})".format(pi, -pi)


def test_wkt(pipi4):
    """.wkt and wkt.dumps() both do not trim by default."""
    assert pipi4.wkt == "POINT ({0:.16f} {1:.16f})".format(pi*4, -pi*4)


def test_dumps(pipi4):
    assert dumps(pipi4) == "POINT ({0:.16f} {1:.16f})".format(pi*4, -pi*4)


def test_dumps_precision(pipi4):
    assert dumps(pipi4, rounding_precision=4) == "POINT ({0:.4f} {1:.4f})".format(pi*4, -pi*4)
Shapely-1.6.4/tests/test_xy.py000066400000000000000000000007611323200062600163400ustar00rootroot00000000000000from . import unittest
from shapely import geometry


class XYTestCase(unittest.TestCase):
    """New geometry/coordseq method 'xy' makes numpy interop easier"""

    def test_arrays(self):
        x, y = geometry.LineString(((0, 0), (1, 1))).xy
        self.assertEqual(len(x), 2)
        self.assertEqual(list(x), [0.0, 1.0])
        self.assertEqual(len(y), 2)
        self.assertEqual(list(y), [0.0, 1.0])


def test_suite():
    return unittest.TestLoader().loadTestsFromTestCase(XYTestCase)
Shapely-1.6.4/tests/threading_test.py000066400000000000000000000020101323200062600176320ustar00rootroot00000000000000import threading
from binascii import b2a_hex


def main():
    num_threads = 10
    use_threads = True

    if not use_threads:
        # Run core code
        runShapelyBuilding()
    else:
        threads = [threading.Thread(target=runShapelyBuilding, name=str(i),
                                    args=(i,)) for i in range(num_threads)]
        for t in threads:
            t.start()
        for t in threads:
            t.join()


def runShapelyBuilding(num):
    print("%s: Running shapely tests on wkb" % num)
    import shapely.geos
    print("%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle))
    import shapely.wkt
    import shapely.wkb
    p = shapely.wkt.loads("POINT (0 0)")
    print("%s WKT: %s" % (num, shapely.wkt.dumps(p)))
    wkb = shapely.wkb.dumps(p)
    print("%s WKB: %s" % (num, b2a_hex(wkb)))

    for i in range(10):
        shapely.wkb.loads(wkb)

    print("%s GEOS Handle: %s" % (num, shapely.geos.lgeos.geos_handle))
    print("Done %s" % num)


if __name__ == '__main__':
    main()
Shapely-1.6.4/tests/valgrind-python.supp000066400000000000000000000073531323200062600203310ustar00rootroot00000000000000#
# This is a valgrind suppression file that should be used when using valgrind.
#
#  Here's an example of running valgrind:
#
#	cd python/dist/src
#	valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
#		./python -E -tt ./Lib/test/regrtest.py -u bsddb,network
#
# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
# to use the preferred suppressions with Py_ADDRESS_IN_RANGE.
#
# If you do not want to recompile Python, you can uncomment
# suppressions for PyObject_Free and PyObject_Realloc.
#
# See Misc/README.valgrind for more information.

# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Addr4
   fun:Py_ADDRESS_IN_RANGE
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Value4
   fun:Py_ADDRESS_IN_RANGE
}

{
   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
   Memcheck:Cond
   fun:Py_ADDRESS_IN_RANGE
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Addr4
   fun:PyObject_Free
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Value4
   fun:PyObject_Free
}

{
   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
   Memcheck:Cond
   fun:PyObject_Free
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Addr4
   fun:PyObject_Realloc
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Value4
   fun:PyObject_Realloc
}

{
  ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
  Memcheck:Cond
  fun:PyObject_Realloc
}

###
### All the suppressions below are for errors that occur within libraries
### that Python uses.  The problems to not appear to be related to Python's
### use of the libraries.
###
{
   GDBM problems, see test_gdbm
   Memcheck:Param
   write(buf)
   fun:write
   fun:gdbm_open

}

###
### These occur from somewhere within the SSL, when running
###  test_socket_sll.  They are too general to leave on by default.
###
###{
###   somewhere in SSL stuff
###   Memcheck:Cond
###   fun:memset
###}
###{
###   somewhere in SSL stuff
###   Memcheck:Value4
###   fun:memset
###}
###
###{
###   somewhere in SSL stuff
###   Memcheck:Cond
###   fun:MD5_Update
###}
###
###{
###   somewhere in SSL stuff
###   Memcheck:Value4
###   fun:MD5_Update
###}

#
# All of these problems come from using test_socket_ssl
#
{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_bin2bn
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_num_bits_word
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:BN_num_bits_word
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_mod_exp_mont_word
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_mod_exp_mont
}

{
   from test_socket_ssl
   Memcheck:Param
   write(buf)
   fun:write
   obj:/usr/lib/libcrypto.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:RSA_verify
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:RSA_verify
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:DES_set_key_unchecked
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:DES_encrypt2
}

{
   from test_socket_ssl
   Memcheck:Cond
   obj:/usr/lib/libssl.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Value4
   obj:/usr/lib/libssl.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BUF_MEM_grow_clean
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:memcpy
   fun:ssl3_read_bytes
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:SHA1_Update
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:SHA1_Update
}


# some extra lxml specific (?) suppressions..

{
   Test
   Memcheck:Param
   sigaction(act)
   fun:__libc_sigaction
}

{
   ld
   Memcheck:Cond
   obj:/lib/ld-2.6.so
   obj:/lib/ld-2.6.so
   obj:*
}

{
   ld
   Memcheck:Addr4
   obj:/lib/ld-2.6.so
   obj:/lib/ld-2.6.so
   obj:*
}
Shapely-1.6.4/tools/000077500000000000000000000000001323200062600142615ustar00rootroot00000000000000Shapely-1.6.4/tools/README.txt000066400000000000000000000000001323200062600157450ustar00rootroot00000000000000