diff --git a/.gitignore b/.gitignore index 3d5a618..a8d7cd8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ doc/_build geopandas.egg-info geopandas/version.py *.py~ +doc/_static/world_* +examples/nybb_*.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1881611..3980a09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,7 +78,7 @@ install: - if [[ $PANDAS == 'master' ]]; then pip install git+https://github.com/pydata/pandas.git; else pip wheel pandas==$PANDAS; pip install pandas==$PANDAS; fi script: - - py.test tests --cov geopandas -v --cov-report term-missing + - py.test geopandas --cov geopandas -v --cov-report term-missing after_success: - coveralls diff --git a/README.md b/README.md index 744836b..4a410bc 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ GeoPandas objects also know how to plot themselves. GeoPandas uses [descartes]( >>> g.plot() -GeoPandas also implements alternate constructors that can read any data format recognized by [fiona](http://toblerity.github.io/fiona). To read a [file containing the boroughs of New York City](http://www.nyc.gov/html/dcp/download/bytes/nybb_14aav.zip): +GeoPandas also implements alternate constructors that can read any data format recognized by [fiona](http://toblerity.github.io/fiona). To read a [file containing the boroughs of New York City](http://www1.nyc.gov/assets/planning/download/zip/data-maps/open-data/nybb_16a.zip): >>> boros = GeoDataFrame.from_file('nybb.shp') >>> boros.set_index('BoroCode', inplace=True) diff --git a/doc/environment.yml b/doc/environment.yml new file mode 100644 index 0000000..1c81308 --- /dev/null +++ b/doc/environment.yml @@ -0,0 +1,16 @@ +name: geopandas_docs +channels: +- conda-forge +dependencies: +- python=3.4 +- pandas +- shapely +- fiona +- pyproj +- six +- geopy +- matplotlib +- descartes +- sphinx +- sphinx_rtd_theme +- ipython=4.0.1 diff --git a/doc/source/_example_data/naturalearth_cities.README.html b/doc/source/_example_data/naturalearth_cities.README.html new file mode 100644 index 0000000..2f5786f --- /dev/null +++ b/doc/source/_example_data/naturalearth_cities.README.html @@ -0,0 +1,336 @@ + + + + + + + + +Populated Places | Natural Earth + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + + « 1:10m Cultural Vectors  +
+

Populated Places

+
+
+
pop_thumb
+
City and town points, from Tokyo to Wasilla, Cairo to Kandahar + +
+
+
+

About

+

Point symbols with name attributes. Includes all admin-0 and many admin-1 capitals, major cities and towns, plus a sampling of smaller towns in sparsely inhabited regions. We favor regional significance over population census in determining our selection of places. Use the scale rankings to filter the number of towns that appear on your map.

+

pop_banner

+

LandScan derived population estimates are provided for 90% of our cities. Those lacking population estimates are often in sparsely inhabited areas. We provide a range of population values that account for the total “metropolitan” population rather than it’s administrative boundary population. Use the PopMax column to size your town labels. Starting in version 1.1, popMax has been throttled down to the UN estimated metro population for the ~500 largest urban areas in the world. This affects towns in China, India, and parts of Africa where our Landscan counting method usually over estimated.

+

Population estimates were derived from the LANDSCAN dataset maintained and distributed by the Oak Ridge National Laboratory. These data were converted from raster to vector and pixels with fewer than 200 persons per square kilometer were removed from the dataset as they were classified as rural. Once urban pixels were selected, these pixels were aggregated into contiguous units. Concurrently Thiessen polygons were created based on the selected city points. The Thiessen polygons were used to intersect the contiguous city boundaries to produce bounded areas for the cities. As a result, our estimates capture a metropolitan and micropolitan populations per city regardless of administrative units.

+

Once intersected, the contiguous polygons were recalculated, using aerial interpolation assuming uniform population distribution within each pixel, to determine the population total. This process was conducted multiple times, for each scale level, to produce population estimates for each city at nested scales of 1:300 million, 1:110 million, 1:50 million, 1:20 million, and 1:10 million.

+ +

Population ranks

+

Are calculated as rank_max and rank_min using this general VB formula that can be pasted into ArcMap Field Calculator advanced area (set your output to x):

+

+a = [pop_max]

+

if( a > 10000000 ) then +x = 14 +elseif( a > 5000000 ) then +x = 13 +elseif( a > 1000000 ) then +x = 12 +elseif( a > 500000 ) then +x = 11 +elseif( a > 200000 ) then +x = 10 +elseif( a > 100000 ) then +x = 9 +elseif( a > 50000 ) then +x = 8 +elseif( a > 20000 ) then +x = 7 +elseif( a > 10000 ) then +x = 6 +elseif( a > 5000 ) then +x = 5 +elseif( a > 2000 ) then +x = 4 +elseif( a > 1000 ) then +x = 3 +elseif( a > 200 ) then +x = 2 +elseif( a > 0 ) then +x = 1 +else +x = 0 +end if

+

Issues

+

While we don’t want to show every admin-1 capital, for those countries where we show most admin-1 capitals, we should have a complete set. If you find we are missing one, please log it in the Cx tool at right.

+

Version History

+
    +
  • + 2.0.0 +
  • +
  • + 1.4.0 +
  • +
  • + 1.3.0 +
  • +
  • + 1.1.0 +
  • +
  • + 0.9.0 +
  • +
+ +

The master changelog is available on Github ยป +

+ +
+
+Share and Enjoy: +
+
    +
  • Twitter
  • +
  • Facebook
  • +
  • Digg
  • +
  • del.icio.us
  • +
  • Google Bookmarks
  • +
  • Slashdot
  • +
  • StumbleUpon
  • +
  • email
  • +
  • LinkedIn
  • +
  • Reddit
  • +
+
+ + +
+ +
+ + +
+ + + + +
+ +
+ + + + + + + + + + + \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_cities.VERSION.txt b/doc/source/_example_data/naturalearth_cities.VERSION.txt new file mode 100644 index 0000000..359a5b9 --- /dev/null +++ b/doc/source/_example_data/naturalearth_cities.VERSION.txt @@ -0,0 +1 @@ +2.0.0 \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_cities.cpg b/doc/source/_example_data/naturalearth_cities.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/doc/source/_example_data/naturalearth_cities.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_cities.dbf b/doc/source/_example_data/naturalearth_cities.dbf new file mode 100644 index 0000000..d9b9726 Binary files /dev/null and b/doc/source/_example_data/naturalearth_cities.dbf differ diff --git a/doc/source/_example_data/naturalearth_cities.prj b/doc/source/_example_data/naturalearth_cities.prj new file mode 100644 index 0000000..a30c00a --- /dev/null +++ b/doc/source/_example_data/naturalearth_cities.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_cities.shp b/doc/source/_example_data/naturalearth_cities.shp new file mode 100644 index 0000000..d180259 Binary files /dev/null and b/doc/source/_example_data/naturalearth_cities.shp differ diff --git a/doc/source/_example_data/naturalearth_cities.shx b/doc/source/_example_data/naturalearth_cities.shx new file mode 100644 index 0000000..e7eca86 Binary files /dev/null and b/doc/source/_example_data/naturalearth_cities.shx differ diff --git a/doc/source/_example_data/naturalearth_lowres.cpg b/doc/source/_example_data/naturalearth_lowres.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/doc/source/_example_data/naturalearth_lowres.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_lowres.dbf b/doc/source/_example_data/naturalearth_lowres.dbf new file mode 100644 index 0000000..bd20a04 Binary files /dev/null and b/doc/source/_example_data/naturalearth_lowres.dbf differ diff --git a/doc/source/_example_data/naturalearth_lowres.prj b/doc/source/_example_data/naturalearth_lowres.prj new file mode 100644 index 0000000..a30c00a --- /dev/null +++ b/doc/source/_example_data/naturalearth_lowres.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/doc/source/_example_data/naturalearth_lowres.shp b/doc/source/_example_data/naturalearth_lowres.shp new file mode 100644 index 0000000..32a78cd Binary files /dev/null and b/doc/source/_example_data/naturalearth_lowres.shp differ diff --git a/doc/source/_example_data/naturalearth_lowres.shx b/doc/source/_example_data/naturalearth_lowres.shx new file mode 100644 index 0000000..507f2c2 Binary files /dev/null and b/doc/source/_example_data/naturalearth_lowres.shx differ diff --git a/doc/source/conf.py b/doc/source/conf.py index 653eb9d..c713508 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -25,9 +25,11 @@ import sys, os # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] +extensions = ['IPython.sphinxext.ipython_console_highlighting', + 'IPython.sphinxext.ipython_directive'] # Add any paths that contain templates here, relative to this directory. + templates_path = ['_templates'] # The suffix of source filenames. @@ -93,10 +95,9 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -if os.environ.get('READTHEDOCS', None) == 'True': - html_theme = 'default' -else: - html_theme = 'nature' +import sphinx_rtd_theme +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # 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 diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst new file mode 100644 index 0000000..a384ab2 --- /dev/null +++ b/doc/source/contributing.rst @@ -0,0 +1,281 @@ +Contributing to GeoPandas +========================= + +(Contribution guidelines largely copied from `pandas `_) + +Overview +-------- + +Contributions to GeoPandas are very welcome. They are likely to +be accepted more quickly if they follow these guidelines. + +At this stage of GeoPandas development, the priorities are to define a +simple, usable, and stable API and to have clean, maintainable, +readable code. Performance matters, but not at the expense of those +goals. + +In general, GeoPandas follows the conventions of the pandas project +where applicable. + +In particular, when submitting a pull request: + +- All existing tests should pass. Please make sure that the test + suite passes, both locally and on + `Travis CI `_. Status on + Travis will be visible on a pull request. If you want to enable + Travis CI on your own fork, please read the pandas guidelines link + above or the + `getting started docs `_. + +- New functionality should include tests. Please write reasonable + tests for your code and make sure that they pass on your pull request. + +- Classes, methods, functions, etc. should have docstrings. The first + line of a docstring should be a standalone summary. Parameters and + return values should be ducumented explicitly. + +- GeoPandas supports python 2 (2.6+) and python 3 (3.2+) with a single + code base. Use modern python idioms when possible that are + compatibile with both major versions, and use the + `six `_ library where helpful to smooth + over the differences. Use ``from __future__ import`` statements where + appropriate. Test code locally in both python 2 and python 3 when + possible (all supported versions will be automatically tested on + Travis CI). + +- Follow PEP 8 when possible. + +- Imports should be grouped with standard library imports first, + 3rd-party libraries next, and geopandas imports third. Within each + grouping, imports should be alphabetized. Always use absolute + imports when possible, and explicit relative imports for local + imports when necessary in tests. + + +Seven Steps for Contributing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are seven basic steps to contributing to *geopandas*: + +1) Fork the *geopandas* git repository +2) Create a development environment +3) Install *geopandas* dependencies +4) Make a ``development`` build of *geopandas* +5) Make changes to code and add tests +6) Update the documentation +7) Submit a Pull Request + +Each of these 7 steps is detailed below. + + +1) Forking the *geopandas* repository using Git +------------------------------------------------ + +To the new user, working with Git is one of the more daunting aspects of contributing to *geopandas**. +It can very quickly become overwhelming, but sticking to the guidelines below will help keep the process +straightforward and mostly trouble free. As always, if you are having difficulties please +feel free to ask for help. + +The code is hosted on `GitHub `_. To +contribute you will need to sign up for a `free GitHub account +`_. We use `Git `_ for +version control to allow many people to work together on the project. + +Some great resources for learning Git: + +* Software Carpentry's `Git Tutorial `_ +* `Atlassian `_ +* the `GitHub help pages `_. +* Matthew Brett's `Pydagogue `_. + +Getting started with Git +~~~~~~~~~~~~~~~~~~~~~~~~~ + +`GitHub has instructions `__ for installing git, +setting up your SSH key, and configuring git. All these steps need to be completed before +you can work seamlessly between your local repository and GitHub. + +.. _contributing.forking: + +Forking +~~~~~~~~ + +You will need your own fork to work on the code. Go to the `geopandas project +page `_ and hit the ``Fork`` button. You will +want to clone your fork to your machine:: + + git clone git@github.com:your-user-name/geopandas.git geopandas-yourname + cd geopandas-yourname + git remote add upstream git://github.com/geopandas/geopandas.git + +This creates the directory `geopandas-yourname` and connects your repository to +the upstream (main project) *geopandas* repository. + +The testing suite will run automatically on Travis-CI once your pull request is +submitted. However, if you wish to run the test suite on a branch prior to +submitting the pull request, then Travis-CI needs to be hooked up to your +GitHub repository. Instructions for doing so are `here +`__. + +Creating a branch +~~~~~~~~~~~~~~~~~~ + +You want your master branch to reflect only production-ready code, so create a +feature branch for making your changes. For example:: + + git branch shiny-new-feature + git checkout shiny-new-feature + +The above can be simplified to:: + + git checkout -b shiny-new-feature + +This changes your working directory to the shiny-new-feature branch. Keep any +changes in this branch specific to one bug or feature so it is clear +what the branch brings to *geopandas*. You can have many shiny-new-features +and switch in between them using the git checkout command. + +To update this branch, you need to retrieve the changes from the master branch:: + + git fetch upstream + git rebase upstream/master + +This will replay your commits on top of the latest geopandas git master. If this +leads to merge conflicts, you must resolve these before submitting your pull +request. If you have uncommitted changes, you will need to ``stash`` them prior +to updating. This will effectively store your changes and they can be reapplied +after updating. + +.. _contributing.dev_env: + +2) Creating a development environment +--------------------------------------- +A development environment is a virtual space where you can keep an independent installation of *geopandas*. +This makes it easy to keep both a stable version of python in one place you use for work, and a development +version (which you may break while playing with code) in another. + +An easy way to create a *geopandas* development environment is as follows: + +- Install either `Anaconda `_ or + `miniconda `_ +- Make sure that you have :ref:`cloned the repository ` +- ``cd`` to the *geopandas** source directory + +Tell conda to create a new environment, named ``geopandas_dev``, or any other name you would like +for this environment, by running:: + + conda create -n geopandas_dev + +For a python 3 environment:: + + conda create -n geopandas_dev python=3.4 + +This will create the new environment, and not touch any of your existing environments, +nor any existing python installation. + +To work in this environment, Windows users should ``activate`` it as follows:: + + activate geopandas_dev + +Mac OSX and Linux users should use:: + + source activate geopandas_dev + +You will then see a confirmation message to indicate you are in the new development environment. + +To view your environments:: + + conda info -e + +To return to you home root environment:: + + deactivate + +See the full conda docs `here `__. + +At this point you can easily do a *development* install, as detailed in the next sections. + +3) Installing Dependencies +-------------------------- + +To run *geopandas* in an development environment, you must first install +*geopandas*'s dependencies. We suggest doing so using the following commands +(executed after your development environment has been activated):: + + conda install -c conda-forge fiona shapely pyproj rtree + conda install pandas + + +This should install all necessary dependencies. + +4) Making a development build +----------------------------- + +Once dependencies are in place, make an in-place build by navigating to the git +clone of the *geopandas* repository and running:: + + python setup.py develop + + +5) Making changes and writing tests +------------------------------------- + +*geopandas* is serious about testing and strongly encourages contributors to embrace +`test-driven development (TDD) `_. +This development process "relies on the repetition of a very short development cycle: +first the developer writes an (initially failing) automated test case that defines a desired +improvement or new function, then produces the minimum amount of code to pass that test." +So, before actually writing any code, you should write your tests. Often the test can be +taken from the original GitHub issue. However, it is always worth considering additional +use cases and writing corresponding tests. + +Adding tests is one of the most common requests after code is pushed to *geopandas*. Therefore, +it is worth getting in the habit of writing tests ahead of time so this is never an issue. + +Like many packages, *geopandas* uses the `Nose testing system +`_ and the convenient +extensions in `numpy.testing +`_. + +Writing tests +~~~~~~~~~~~~~ + +All tests should go into the ``tests`` directory. This folder contains many +current examples of tests, and we suggest looking to these for inspiration. + +The ``.util`` module has some special ``assert`` functions that +make it easier to make statements about whether GeoSeries or GeoDataFrame +objects are equivalent. The easiest way to verify that your code is correct is to +explicitly construct the result you expect, then compare the actual result to +the expected correct result, using eg the function ``assert_geoseries_equal``. + +Running the test suite +~~~~~~~~~~~~~~~~~~~~~~ + +The tests can then be run directly inside your Git clone (without having to +install *geopandas*) by typing:: + + nosetests -v + +6) Updating the Documentation +----------------------------- + +*geopandas* documentation resides in the `doc` folder. Changes to the docs are +make by modifying the appropriate file in the `source` folder within `doc`. +*geopandas* docs us reStructuredText syntax, `which is explained here `_ +and the docstrings follow the `Numpy Docstring standard `_. + +Once you have made your changes, you can build the docs by navigating to the `doc` folder and typing:: + + make html + +The resulting html pages will be located in `doc/build/html`. + + +7) Submitting a Pull Request +------------------------------ + +Once you've made changes and pushed them to your forked repository, you then +submit a pull request to have them integrated into the *geopandas* code base. + +You can find a pull request (or PR) tutorial in the `GitHub's Help Docs `_. diff --git a/doc/source/data_structures.rst b/doc/source/data_structures.rst new file mode 100644 index 0000000..96fb953 --- /dev/null +++ b/doc/source/data_structures.rst @@ -0,0 +1,127 @@ +.. currentmodule:: geopandas + +.. ipython:: python + :suppress: + + import geopandas as gpd + world = gpd.GeoDataFrame().from_file('_example_data/naturalearth_lowres.shp') + world = world.rename(columns={'geometry': 'borders'}).set_geometry('borders') + +Data Structures +========================================= + +GeoPandas implements two main data structures, a ``GeoSeries`` and a +``GeoDataFrame``. These are subclasses of pandas ``Series`` and +``DataFrame``, respectively. + +GeoSeries +--------- + +A ``GeoSeries`` is essentially a vector where each entry in the vector +is a set of shapes corresponding to one observation. An entry may consist +of only one shape (like a single polygon) or multiple shapes that are +meant to be thought of as one observation (like the many polygons that +make up the State of Hawaii or a country like Indonesia). + +*geopandas* has three basic classes of geometric objects (which are actually *shapely* objects): + +* Points / Multi-Points +* Lines / Multi-Lines +* Polygons / Multi-Polygons + +Note that all entries in a ``GeoSeries`` need not be of the same geometric type, although certain export operations will fail if this is not the case. + +Overview of Attributes and Methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``GeoSeries`` class implements nearly all of the attributes and +methods of Shapely objects. When applied to a ``GeoSeries``, they +will apply elementwise to all geometries in the series. Binary +operations can be applied between two ``GeoSeries``, in which case the +operation is carried out elementwise. The two series will be aligned +by matching indices. Binary operations can also be applied to a +single geometry, in which case the operation is carried out for each +element of the series with that geometry. In either case, a +``Series`` or a ``GeoSeries`` will be returned, as appropriate. + +A short summary of a few attributes and methods for GeoSeries is +presented here, and a full list can be found in the :doc:`all attributes and methods page `. +There is also a family of methods for creating new shapes by expanding +existing shapes or applying set-theoretic operations like "union" described +in :doc:`geometric manipulations `. + +Attributes +^^^^^^^^^^^^^^^ +* ``area``: shape area (units of projection -- see :doc:`projections `) +* ``bounds``: tuple of max and min coordinates on each axis for each shape +* ``total_bounds``: tuple of max and min coordinates on each axis for entire GeoSeries +* ``geom_type``: type of geometry. +* ``is_valid``: tests if coordinates make a shape that is reasonable geometric shape (`according to this `_). + +Basic Methods +^^^^^^^^^^^^^^ + +* ``distance(other)``: returns ``Series`` with minimum distance from each entry to ``other`` +* ``centroid``: returns ``GeoSeries`` of centroids +* ``representative_point()``: returns ``GeoSeries`` of points that are guaranteed to be within each geometry. It does **NOT** return centroids. +* ``to_crs()``: change coordinate reference system. See :doc:`projections ` +* ``plot()``: plot ``GeoSeries``. See :doc:`mapping `. + +Relationship Tests +^^^^^^^^^^^^^^^^^^^ + +* ``almost_equals(other)``: is shape almost the same as ``other`` (good when floating point precision issues make shapes slightly different) +* ``contains(other)``: is shape contained within ``other`` +* ``intersects(other)``: does shape intersect ``other`` + + +GeoDataFrame +------------ + +A ``GeoDataFrame`` is a tabular data structure that contains a ``GeoSeries``. + +The most important property of a ``GeoDataFrame`` is that it always has one ``GeoSeries`` column that holds a special status. This ``GeoSeries`` is referred to as the ``GeoDataFrame``'s "geometry". When a spatial method is applied to a ``GeoDataFrame`` (or a spatial attribute like ``area`` is called), this commands will always act on the "geometry" column. + +The "geometry" column -- no matter its name -- can be accessed through the ``geometry`` attribute (``gdf.geometry``), and the name of the ``geometry`` column can be found by typing ``gdf.geometry.name``. + +A ``GeoDataFrame`` may also contain other columns with geometrical (shapely) objects, but only one column can be the active geometry at a time. To change which column is the active geometry column, use the ``set_geometry`` method. + +An example using the ``worlds`` GeoDataFrame: + +.. ipython:: python + + world.head() + #Plot countries + @savefig world_borders.png width=3in + world.plot(); + +Currently, the column named "borders" with country borders is the active +geometry column: + +.. ipython:: python + + world.geometry.name + +Now, we create centroids and make it the geometry: + +.. ipython:: python + + world['centroid_column'] = world.centroid + world = world.set_geometry('centroid_column') + + @savefig world_centroids.png width=3in + world.plot(); + + +**Note:** A ``GeoDataFrame`` keeps track of the active column by name, so if you rename the active geometry column, you must also reset the geometry:: + + gdf = gdf.rename(columns={'old_name': 'new_name'}).set_geometry('new_name') + +**Note 2:** Somewhat confusingly, by default when you use the ``read_file`` command, the column containing spatial objects from the file is named "geometry" by default, and will be set as the active geometry column. However, despite using the same term for the name of the column and the name of the special attribute that keeps track of the active column, they are distinct. You can easily shift the active geometry column to a different ``GeoSeries`` with the ``set_geometry`` command. Further, ``gdf.geometry`` will always return the active geometry column, *not* the column named ``geometry``. If you wish to call a column named "geometry", and a different column is the active geometry column, use ``gdf['geometry']``, not ``gdf.geometry``. + +Attributes and Methods +~~~~~~~~~~~~~~~~~~~~~~ + +Any of the attributes calls or methods described for a ``GeoSeries`` will work on a ``GeoDataFrame`` -- effectively, they are just applied to the "geometry" ``GeoSeries``. + +However, ``GeoDataFrames`` also have a few extra methods for input and output which are described on the :doc:`Input and Output ` page and for geocoding with are described in :doc:`Geocoding `. diff --git a/doc/source/geocoding.rst b/doc/source/geocoding.rst new file mode 100644 index 0000000..f6cb026 --- /dev/null +++ b/doc/source/geocoding.rst @@ -0,0 +1,17 @@ + +Geocoding +========== + +[TO BE COMPLETED] + + +.. function:: geopandas.geocode.geocode(strings, provider='googlev3', **kwargs) + + Geocode a list of strings and return a GeoDataFrame containing the + resulting points in its ``geometry`` column. Available + ``provider``s include ``googlev3``, ``bing``, ``google``, ``yahoo``, + ``mapquest``, and ``openmapquest``. ``**kwargs`` will be passed as + parameters to the appropriate geocoder. + + Requires `geopy`_. Please consult the Terms of Service for the + chosen provider. diff --git a/doc/source/geometric_manipulations.rst b/doc/source/geometric_manipulations.rst new file mode 100644 index 0000000..2996ee2 --- /dev/null +++ b/doc/source/geometric_manipulations.rst @@ -0,0 +1,243 @@ +Geometric Manipulations +======================== + + + + +Set-theoretic Methods +~~~~~~~~~~~~~~~~~~~~~ + +.. attribute:: GeoSeries.boundary + + Returns a ``GeoSeries`` of lower dimensional objects representing + each geometries's set-theoretic `boundary`. + +.. method:: GeoSeries.difference(other) + + Returns a ``GeoSeries`` of the points in each geometry that + are not in the *other* object. + +.. method:: GeoSeries.intersection(other) + + Returns a ``GeoSeries`` of the intersection of each object with the `other` + geometric object. + +.. method:: GeoSeries.symmetric_difference(other) + + Returns a ``GeoSeries`` of the points in each object not in the `other` + geometric object, and the points in the `other` not in this object. + +.. method:: GeoSeries.union(other) + + Returns a ``GeoSeries`` of the union of points from each object and the + `other` geometric object. + + +.. attribute:: GeoSeries.unary_union + + Return a geometry containing the union of all geometries in the ``GeoSeries``. + + +Constructive Methods +~~~~~~~~~~~~~~~~~~~~~ + +.. method:: GeoSeries.buffer(distance, resolution=16) + + Returns a ``GeoSeries`` of geometries representing all points within a given `distance` + of each geometric object. + +.. attribute:: GeoSeries.convex_hull + + Returns a ``GeoSeries`` of geometries representing the smallest + convex `Polygon` containing all the points in each 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`. + +.. attribute:: GeoSeries.envelope + + Returns a ``GeoSeries`` of geometries representing the point or + smallest rectangular polygon (with sides parallel to the coordinate + axes) that contains each object. + +.. method:: GeoSeries.simplify(tolerance, preserve_topology=True) + + Returns a ``GeoSeries`` containing a simplified representation of + each object. + +Affine transformations +~~~~~~~~~~~~~~~~~~~~~~~ + +.. method:: GeoSeries.rotate(self, angle, origin='center', use_radians=False) + + Rotate the coordinates of the GeoSeries. + +.. method:: GeoSeries.scale(self, xfact=1.0, yfact=1.0, zfact=1.0, origin='center') + + Scale the geometries of the GeoSeries along each (x, y, z) dimensio. + +.. method:: GeoSeries.skew(self, angle, origin='center', use_radians=False) + + Shear/Skew the geometries of the GeoSeries by angles along x and y dimensions. + +.. method:: GeoSeries.translate(self, angle, origin='center', use_radians=False) + + Shift the coordinates of the GeoSeries. + +`Aggregating methods` + + + + +.. sourcecode:: python + + >>> p1 = Polygon([(0, 0), (1, 0), (1, 1)]) + >>> p2 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]) + >>> p3 = Polygon([(2, 0), (3, 0), (3, 1), (2, 1)]) + >>> g = GeoSeries([p1, p2, p3]) + >>> g + 0 POLYGON ((0.0000000000000000 0.000000000000000... + 1 POLYGON ((0.0000000000000000 0.000000000000000... + 2 POLYGON ((2.0000000000000000 0.000000000000000... + dtype: object + +.. image:: _static/test.png + +Some geographic operations return normal pandas object. The ``area`` property of a ``GeoSeries`` will return a ``pandas.Series`` containing the area of each item in the ``GeoSeries``: + +.. sourcecode:: python + + >>> print g.area + 0 0.5 + 1 1.0 + 2 1.0 + dtype: float64 + +Other operations return GeoPandas objects: + +.. sourcecode:: python + + >>> g.buffer(0.5) + Out[15]: + 0 POLYGON ((-0.3535533905932737 0.35355339059327... + 1 POLYGON ((-0.5000000000000000 0.00000000000000... + 2 POLYGON ((1.5000000000000000 0.000000000000000... + dtype: object + +.. image:: _static/test_buffer.png + +GeoPandas objects also know how to plot themselves. GeoPandas uses `descartes`_ to generate a `matplotlib`_ plot. To generate a plot of our GeoSeries, use: + +.. sourcecode:: python + + >>> g.plot() + +GeoPandas also implements alternate constructors that can read any data format recognized by `fiona`_. To read a `file containing the boroughs of New York City`_: + +.. sourcecode:: python + + >>> boros = GeoDataFrame.from_file('nybb.shp') + >>> boros.set_index('BoroCode', inplace=True) + >>> boros.sort() + >>> boros + BoroName Shape_Area Shape_Leng \ + BoroCode + 1 Manhattan 6.364422e+08 358532.956418 + 2 Bronx 1.186804e+09 464517.890553 + 3 Brooklyn 1.959432e+09 726568.946340 + 4 Queens 3.049947e+09 861038.479299 + 5 Staten Island 1.623853e+09 330385.036974 + + geometry + BoroCode + 1 (POLYGON ((981219.0557861328125000 188655.3157... + 2 (POLYGON ((1012821.8057861328125000 229228.264... + 3 (POLYGON ((1021176.4790039062500000 151374.796... + 4 (POLYGON ((1029606.0765991210937500 156073.814... + 5 (POLYGON ((970217.0223999023437500 145643.3322... + +.. image:: _static/nyc.png + +.. sourcecode:: python + + >>> boros['geometry'].convex_hull + 0 POLYGON ((915517.6877458114176989 120121.88125... + 1 POLYGON ((1000721.5317993164062500 136681.7761... + 2 POLYGON ((988872.8212280273437500 146772.03179... + 3 POLYGON ((977855.4451904296875000 188082.32238... + 4 POLYGON ((1017949.9776000976562500 225426.8845... + dtype: object + +.. image:: _static/nyc_hull.png + +To demonstrate a more complex operation, we'll generate a +``GeoSeries`` containing 2000 random points: + +.. sourcecode:: python + + >>> from shapely.geometry import Point + >>> xmin, xmax, ymin, ymax = 900000, 1080000, 120000, 280000 + >>> xc = (xmax - xmin) * np.random.random(2000) + xmin + >>> yc = (ymax - ymin) * np.random.random(2000) + ymin + >>> pts = GeoSeries([Point(x, y) for x, y in zip(xc, yc)]) + +Now draw a circle with fixed radius around each point: + +.. sourcecode:: python + + >>> circles = pts.buffer(2000) + +We can collapse these circles into a single shapely MultiPolygon +geometry with + +.. sourcecode:: python + + >>> mp = circles.unary_union + +To extract the part of this geometry contained in each borough, we can +just use: + +.. sourcecode:: python + + >>> holes = boros['geometry'].intersection(mp) + +.. image:: _static/holes.png + +and to get the area outside of the holes: + +.. sourcecode:: python + + >>> boros_with_holes = boros['geometry'].difference(mp) + +.. image:: _static/boros_with_holes.png + +Note that this can be simplified a bit, since ``geometry`` is +available as an attribute on a ``GeoDataFrame``, and the +``intersection`` and ``difference`` methods are implemented with the +"&" and "-" operators, respectively. For example, the latter could +have been expressed simply as ``boros.geometry - mp``. + +It's easy to do things like calculate the fractional area in each +borough that are in the holes: + +.. sourcecode:: python + + >>> holes.area / boros.geometry.area + BoroCode + 1 0.602015 + 2 0.523457 + 3 0.585901 + 4 0.577020 + 5 0.559507 + dtype: float64 + +.. _Descartes: https://pypi.python.org/pypi/descartes +.. _matplotlib: http://matplotlib.org +.. _fiona: http://toblerity.github.io/fiona +.. _geopy: https://github.com/geopy/geopy +.. _geo_interface: https://gist.github.com/sgillies/2217756 +.. _file containing the boroughs of New York City: http://www.nyc.gov/html/dcp/download/bytes/nybb_14aav.zip + +.. toctree:: + :maxdepth: 2 + + diff --git a/doc/source/index.rst b/doc/source/index.rst index 6ea251b..17d8c81 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -24,10 +24,18 @@ operations in python that would otherwise require a spatial database such as PostGIS. .. toctree:: - :maxdepth: 2 + :maxdepth: 2 Installation - User Guide + Data Structures + Reading and Writing Files + Making Maps + Managing Projections + Geometric Manipulations + Merging Data + Geocoding + Reference to All Attributes and Methods + Contributing to GeoPandas About Indices and tables @@ -36,4 +44,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/doc/source/install.rst b/doc/source/install.rst index 268e5af..4153f5a 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -1,16 +1,19 @@ Installation ============ +Installing GeoPandas +--------------------- + To install the released version, you can use pip:: pip install geopandas -or you can install the conda package from the IOOS channel:: +or you can install the conda package from the conda-forge channel:: - conda install -c ioos geopandas + conda install -c conda-forge geopandas You may install the latest development version by cloning the -`GitHub`_ repository and using the setup script:: +`GitHub` repository and using the setup script:: git clone https://github.com/geopandas/geopandas.git cd geopandas @@ -23,12 +26,10 @@ repository with:: pip install git+git://github.com/geopandas/geopandas.git - Dependencies ------------- +-------------- -GeoPandas supports Python versions 2.6, 2.7, and 3.3+. The required -dependencies are: +Installation via `conda` should also install all dependencies, but a complete list is as follows: - `numpy`_ - `pandas`_ (version 0.13 or later) @@ -41,6 +42,7 @@ Further, optional dependencies are: - `geopy`_ 0.99 (optional; for geocoding) - `psycopg2`_ (optional; for PostGIS connection) +- `rtree`_ (optional; spatial index to improve performance) For plotting, these additional packages may be used: @@ -48,39 +50,40 @@ For plotting, these additional packages may be used: - `descartes`_ - `pysal`_ -Further, `rtree`_ is an optional dependency. ``rtree`` requires the C library -`libspatialindex`_. If using brew, you can install using -``brew install Spatialindex``. +These can be installed independently via the following set of commands:: -Testing -------- + conda install -c conda-forge fiona shapely pyproj rtree + conda install pandas -To run the current set of tests from the source directory, run:: - - nosetests -v - -from a command line. - -Tests are automatically run on all commits on the GitHub repository, -including pull requests, on `Travis CI`_. .. _PyPI: https://pypi.python.org/pypi/geopandas + .. _GitHub: https://github.com/geopandas/geopandas + .. _numpy: http://www.numpy.org + .. _pandas: http://pandas.pydata.org + .. _shapely: http://toblerity.github.io/shapely + .. _fiona: http://toblerity.github.io/fiona + .. _Descartes: https://pypi.python.org/pypi/descartes + .. _matplotlib: http://matplotlib.org + .. _geopy: https://github.com/geopy/geopy + .. _six: https://pythonhosted.org/six + .. _psycopg2: https://pypi.python.org/pypi/psycopg2 + .. _pysal: http://pysal.org + .. _pyproj: https://github.com/jswhit/pyproj + .. _rtree: https://github.com/Toblerity/rtree + .. _libspatialindex: https://github.com/libspatialindex/libspatialindex + .. _Travis CI: https://travis-ci.org/geopandas/geopandas - - -.. toctree:: - :maxdepth: 2 diff --git a/doc/source/io.rst b/doc/source/io.rst new file mode 100644 index 0000000..65f33f0 --- /dev/null +++ b/doc/source/io.rst @@ -0,0 +1,28 @@ + +Reading and Writing Files +========================================= + + + +Reading Spatial Data +--------------------- + +*geopandas* can read almost any vector-based spatial data format including ESRI shapefile, GeoJSON files and more using the command:: + + gpd.read_file() + +which returns a GeoDataFrame object. (This is possible because *geopandas* makes use of the great `fiona `_ library, which in turn makes use of a massive open-source program called `GDAL/OGR `_ designed to facilitate spatial data transformations). + +Any arguments passed to ``read_file()`` after the file name will be passed directly to ``fiona.open``, which does the actual data importation. In general, ``read_file`` is pretty smart and should do what you want without extra arguments, but for more help, type:: + + import fiona; help(fiona.open) + +Among other things, one can explicitly set the driver (shapefile, GeoJSON) with the ``driver`` keyword, or pick a single layer from a multi-layered file with the ``layer`` keyword. + +*geopandas* can also get data from a PostGIS database using the ``read_postgis()`` command. + + +Writing Spatial Data +--------------------- + +GeoDataFrames can be exported to many different standard formats using the ``GeoDataFrame.to_file()`` method. For a full list of supported formats, type ``import fiona; fiona.supported_drivers``. diff --git a/doc/source/mapping.rst b/doc/source/mapping.rst new file mode 100644 index 0000000..2ce1cb6 --- /dev/null +++ b/doc/source/mapping.rst @@ -0,0 +1,115 @@ +.. currentmodule:: geopandas + +.. ipython:: python + :suppress: + + import geopandas as gpd + world = gpd.GeoDataFrame().from_file('_example_data/naturalearth_lowres.shp') + cities = gpd.GeoDataFrame().from_file('_example_data/naturalearth_cities.shp') + + + +Mapping Tools +========================================= + + +*geopandas* provides a high-level interface to the ``matplotlib`` library for making maps. Mapping shapes is as easy as using the ``plot()`` method on a ``GeoSeries`` or ``GeoDataFrame``. + +.. ipython:: python + + # Examine country GeoDataFrame + world.head() + + # Basic plot, random colors + @savefig world_randomcolors.png width=5in + world.plot(); + +Note that in general, any options one can pass to `pyplot `_ in ``matplotlib`` (or `style options that work for lines `_) can be passed to the ``plot()`` method. + + +Chloropleth Maps +----------------- + +*geopandas* makes it easy to create Chloropleth maps (maps where the color of each shape is based on the value of an associated variable). Simply use the plot command with the ``column`` argument set to the column whose values you want used to assign colors. + +.. ipython:: python + + # Plot by GDP per capta + world = world[(world.pop_est>0) & (world.name!="Antarctica")] + world['gdp_per_cap'] = world.gdp_md_est / world.pop_est + @savefig world_gdp_per_cap.png width=5in + world.plot(column='gdp_per_cap'); + + +Choosing colors +~~~~~~~~~~~~~~~~ + +One can also modify the colors used by ``plot`` with the ``cmap`` option (for a full list of colormaps, see the `matplotlib website `_): + +.. ipython:: python + + @savefig world_gdp_per_cap_red.png width=5in + world.plot(column='gdp_per_cap', cmap='OrRd'); + + +The way color maps are scaled can also be manipulated with the ``scheme`` option (if you have ``pysal`` installed, which can be accomplished via ``conda install pysal``). By default, ``scheme`` is set to 'equal_intervals', but it can also be adjusted to any other `pysal option `_, like 'quantiles', 'percentiles', etc. + +.. ipython:: python + + @savefig world_gdp_per_cap_quantiles.png width=5in + world.plot(column='gdp_per_cap', cmap='OrRd', scheme='quantiles'); + + +Maps with Layers +----------------- + +There are two strategies for making a map with multiple layers -- one more succinct, and one that is a littel more flexible. + +Before combining maps, however, remember to always ensure they share a common CRS (so they will align). + +.. ipython:: python + + # Look at capitals + # Note use of standard `pyplot` line style options + @savefig capitals.png width=5in + cities.plot(marker='*', color='green', markersize=5); + + # Check crs + cities = cities.to_crs(world.crs) + + # Now we can overlay over country outlines + # And yes, there are lots of island capitals + # apparently in the middle of the ocean! + +**Method 1** + +.. ipython:: python + + base = world.plot(color='white') + @savefig capitals_over_countries_1.png width=5in + cities.plot(ax=base, marker='o', color='red', markersize=5); + +**Method 2: Using matplotlib objects** + +.. ipython:: python + + import matplotlib.pyplot as plt + fig, ax = plt.subplots() + + # set aspect to equal. This is done automatically + # when using *geopandas* plot on it's own, but not when + # working with pyplot directly. + ax.set_aspect('equal') + + world.plot(ax=ax, color='white') + cities.plot(ax=ax, marker='o', color='red', markersize=5) + @savefig capitals_over_countries_2.png width=5in + plt.show(); + + +Other Resources +----------------- +Links to jupyter Notebooks for different mapping tasks: + +`Making Heat Maps `_ + diff --git a/doc/source/mergingdata.rst b/doc/source/mergingdata.rst new file mode 100644 index 0000000..66233af --- /dev/null +++ b/doc/source/mergingdata.rst @@ -0,0 +1,15 @@ + +Merging Data +========================================= + + +Attribute Joins +---------------- + +[TO BE COMPLETED -- EXAMPLES OF JOINING GDF WITH PANDAS DATAFRAME] + + +Spatial Joins +---------------- + +[TO BE COMPLETED -- EXAMPLES OF SPATIAL JOINS] diff --git a/doc/source/projections.rst b/doc/source/projections.rst new file mode 100644 index 0000000..de4f03c --- /dev/null +++ b/doc/source/projections.rst @@ -0,0 +1,71 @@ +.. currentmodule:: geopandas + +.. ipython:: python + :suppress: + + import geopandas as gpd + world = gpd.GeoDataFrame().from_file('_example_data/naturalearth_lowres.shp') + + + +Managing Projections +========================================= + + + +Coordinate Reference Systems +----------------------------- + +CRS are important because the geometric shapes in a GeoSeries or GeoDataFrame object are simply a collection of coordinates in an arbitrary space. A CRS tells Python how those coordinates related to places on the Earth. + +CRS are referred to using codes called `proj4 strings `_. You can find the codes for most commonly used projections from `www.spatialreference.org `_ or `remotesensing.org `_. + +The same CRS can often be referred to in many ways. For example, one of the most commonly used CRS is the WGS84 latitude-longitude projection. One `proj4` representation of this projection is: ``"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"``. But common projections can also be referred to by `EPSG` codes, so this same projection can also called using the `proj4` string ``"+init=epsg:4326"``. + +*geopandas* can accept lots of representations of CRS, including the `proj4` string itself (``"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"``) or parameters broken out in a dictionary: ``{'proj': 'latlong', 'ellps': 'WGS84', 'datum': 'WGS84', 'no_defs': True}``). In addition, some functions will take `EPSG` codes directly. + +For reference, a few very common projections and their proj4 strings: + +* WGS84 Latitude/Longitude: ``"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"`` or ``"+init=epsg:4326"`` +* UTM Zones (North): ``"+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"`` +* UTM Zones (South): ``"+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs +south"`` + +Setting a Projection +---------------------- + +There are two relevant operations for projections: setting a projection and re-projecting. + +Setting a projection may be necessary when for some reason *geopandas* has coordinate data (x-y values), but no information about how those coordinates refer to locations in the real world. Setting a projection is how one tells *geopandas* how to interpret coordinates. If no CRS is set, *geopandas* geometry operations will still work, but coordinate transformations will not be possible and exported files may not be interpreted correctly by other software. + +Be aware that **most of the time** you don't have to set a projection. Data loaded from a reputable source (using the ``from_file()`` command) *should* always include projection information. You can see an objects current CRS through the ``crs`` attribute: ``my_geoseries.crs``. + +From time to time, however, you may get data that does not include a projection. In this situation, you have to set the CRS so *geopandas* knows how to interpret the coordinates. + +For example, if you convert a spreadsheet of latitudes and longitudes into a GeoSeries by hand, you would set the projection by assigning the WGS84 latitude-longitude CRS to the ``crs`` attribute: + +.. sourcecode:: python + + my_geoseries.crs = {'init' :'epsg:4326'} + + +Re-Projecting +---------------- + +Re-projecting is the process of changing the representation of locations from one coordinate system to another. All projections of locations on the Earth into a two-dimensional plane `are distortions `_, the projection that is best for your application may be different from the projection associated with the data you import. In these cases, data can be re-projected using the ``to_crs`` command: + +.. ipython:: python + + # Check original projection + # (it's Platte Carre! x-y are long and lat) + world.crs + + # Visualize + @savefig world_starting.png width=3in + world.plot(); + + # Reproject to Mercator (after dropping Antartica) + world = world[(world.name != "Antarctica") & (world.name != "Fr. S. Antarctic Lands")] + world = world.to_crs({'init': 'epsg:3395'}) # world.to_crs(epsg=3395) would also work + @savefig world_reproj.png width=3in + world.plot(); + diff --git a/doc/source/user.rst b/doc/source/reference.rst similarity index 63% rename from doc/source/user.rst rename to doc/source/reference.rst index 05c8e3c..a05e840 100644 --- a/doc/source/user.rst +++ b/doc/source/reference.rst @@ -1,24 +1,7 @@ -GeoPandas User Guide -==================== -GeoPandas implements two main data structures, a ``GeoSeries`` and a -``GeoDataFrame``. These are subclasses of pandas ``Series`` and -``DataFrame``, respectively. -GeoSeries ---------- - -A ``GeoSeries`` contains a sequence of geometries. - -The ``GeoSeries`` class implements nearly all of the attributes and -methods of Shapely objects. When applied to a ``GeoSeries``, they -will apply elementwise to all geometries in the series. Binary -operations can be applied between two ``GeoSeries``, in which case the -operation is carried out elementwise. The two series will be aligned -by matching indices. Binary operations can also be applied to a -single geometry, in which case the operation is carried out for each -element of the series with that geometry. In either case, a -``Series`` or a ``GeoSeries`` will be returned, as appropriate. +Reference +=========================== The following Shapely methods and attributes are available on ``GeoSeries`` objects: @@ -327,174 +310,3 @@ All pandas ``DataFrame`` methods are also available, although they may not operate in a meaningful way on the ``geometry`` column and may not return a ``GeoDataFrame`` result even when it would be appropriate to do so. - -Geopandas functions -------------------- - -.. function:: geopandas.geocode.geocode(strings, provider='googlev3', **kwargs) - - Geocode a list of strings and return a GeoDataFrame containing the - resulting points in its ``geometry`` column. Available - ``provider``s include ``googlev3``, ``bing``, ``google``, ``yahoo``, - ``mapquest``, and ``openmapquest``. ``**kwargs`` will be passed as - parameters to the appropriate geocoder. - - Requires `geopy`_. Please consult the Terms of Service for the - chosen provider. - -Examples --------- - -.. sourcecode:: python - - >>> p1 = Polygon([(0, 0), (1, 0), (1, 1)]) - >>> p2 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]) - >>> p3 = Polygon([(2, 0), (3, 0), (3, 1), (2, 1)]) - >>> g = GeoSeries([p1, p2, p3]) - >>> g - 0 POLYGON ((0.0000000000000000 0.000000000000000... - 1 POLYGON ((0.0000000000000000 0.000000000000000... - 2 POLYGON ((2.0000000000000000 0.000000000000000... - dtype: object - -.. image:: _static/test.png - -Some geographic operations return normal pandas object. The ``area`` property of a ``GeoSeries`` will return a ``pandas.Series`` containing the area of each item in the ``GeoSeries``: - -.. sourcecode:: python - - >>> print g.area - 0 0.5 - 1 1.0 - 2 1.0 - dtype: float64 - -Other operations return GeoPandas objects: - -.. sourcecode:: python - - >>> g.buffer(0.5) - Out[15]: - 0 POLYGON ((-0.3535533905932737 0.35355339059327... - 1 POLYGON ((-0.5000000000000000 0.00000000000000... - 2 POLYGON ((1.5000000000000000 0.000000000000000... - dtype: object - -.. image:: _static/test_buffer.png - -GeoPandas objects also know how to plot themselves. GeoPandas uses `descartes`_ to generate a `matplotlib`_ plot. To generate a plot of our GeoSeries, use: - -.. sourcecode:: python - - >>> g.plot() - -GeoPandas also implements alternate constructors that can read any data format recognized by `fiona`_. To read a `file containing the boroughs of New York City`_: - -.. sourcecode:: python - - >>> boros = GeoDataFrame.from_file('nybb.shp') - >>> boros.set_index('BoroCode', inplace=True) - >>> boros.sort() - >>> boros - BoroName Shape_Area Shape_Leng \ - BoroCode - 1 Manhattan 6.364422e+08 358532.956418 - 2 Bronx 1.186804e+09 464517.890553 - 3 Brooklyn 1.959432e+09 726568.946340 - 4 Queens 3.049947e+09 861038.479299 - 5 Staten Island 1.623853e+09 330385.036974 - - geometry - BoroCode - 1 (POLYGON ((981219.0557861328125000 188655.3157... - 2 (POLYGON ((1012821.8057861328125000 229228.264... - 3 (POLYGON ((1021176.4790039062500000 151374.796... - 4 (POLYGON ((1029606.0765991210937500 156073.814... - 5 (POLYGON ((970217.0223999023437500 145643.3322... - -.. image:: _static/nyc.png - -.. sourcecode:: python - - >>> boros['geometry'].convex_hull - 0 POLYGON ((915517.6877458114176989 120121.88125... - 1 POLYGON ((1000721.5317993164062500 136681.7761... - 2 POLYGON ((988872.8212280273437500 146772.03179... - 3 POLYGON ((977855.4451904296875000 188082.32238... - 4 POLYGON ((1017949.9776000976562500 225426.8845... - dtype: object - -.. image:: _static/nyc_hull.png - -To demonstrate a more complex operation, we'll generate a -``GeoSeries`` containing 2000 random points: - -.. sourcecode:: python - - >>> from shapely.geometry import Point - >>> xmin, xmax, ymin, ymax = 900000, 1080000, 120000, 280000 - >>> xc = (xmax - xmin) * np.random.random(2000) + xmin - >>> yc = (ymax - ymin) * np.random.random(2000) + ymin - >>> pts = GeoSeries([Point(x, y) for x, y in zip(xc, yc)]) - -Now draw a circle with fixed radius around each point: - -.. sourcecode:: python - - >>> circles = pts.buffer(2000) - -We can collapse these circles into a single shapely MultiPolygon -geometry with - -.. sourcecode:: python - - >>> mp = circles.unary_union - -To extract the part of this geometry contained in each borough, we can -just use: - -.. sourcecode:: python - - >>> holes = boros['geometry'].intersection(mp) - -.. image:: _static/holes.png - -and to get the area outside of the holes: - -.. sourcecode:: python - - >>> boros_with_holes = boros['geometry'].difference(mp) - -.. image:: _static/boros_with_holes.png - -Note that this can be simplified a bit, since ``geometry`` is -available as an attribute on a ``GeoDataFrame``, and the -``intersection`` and ``difference`` methods are implemented with the -"&" and "-" operators, respectively. For example, the latter could -have been expressed simply as ``boros.geometry - mp``. - -It's easy to do things like calculate the fractional area in each -borough that are in the holes: - -.. sourcecode:: python - - >>> holes.area / boros.geometry.area - BoroCode - 1 0.602015 - 2 0.523457 - 3 0.585901 - 4 0.577020 - 5 0.559507 - dtype: float64 - -.. _Descartes: https://pypi.python.org/pypi/descartes -.. _matplotlib: http://matplotlib.org -.. _fiona: http://toblerity.github.io/fiona -.. _geopy: https://github.com/geopy/geopy -.. _geo_interface: https://gist.github.com/sgillies/2217756 -.. _file containing the boroughs of New York City: http://www.nyc.gov/html/dcp/download/bytes/nybb_14aav.zip - -.. toctree:: - :maxdepth: 2 - - diff --git a/examples/nyc_boros.py b/examples/nyc_boros.py index 6db6ec5..3e70210 100644 --- a/examples/nyc_boros.py +++ b/examples/nyc_boros.py @@ -14,7 +14,7 @@ from geopandas import GeoSeries, GeoDataFrame np.random.seed(1) DPI = 100 -# http://www.nyc.gov/html/dcp/download/bytes/nybb_14aav.zip +# http://www1.nyc.gov/assets/planning/download/zip/data-maps/open-data/nybb_16a.zip boros = GeoDataFrame.from_file('nybb.shp') boros.set_index('BoroCode', inplace=True) boros.sort() diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py index 75925a0..78ec62b 100644 --- a/geopandas/geodataframe.py +++ b/geopandas/geodataframe.py @@ -350,6 +350,12 @@ class GeoDataFrame(GeoPandasBase, DataFrame): joining points are assumed to be lines in the current projection, not geodesics. Objects crossing the dateline (or other projection boundary) will have undesirable behavior. + + `to_crs` passes the `crs` argument to the `Proj` function from the + `pyproj` library (with the option `preserve_units=True`). It can + therefore accept proj4 projections in any format + supported by `Proj`, including dictionaries, or proj4 strings. + """ if inplace: df = self @@ -429,8 +435,11 @@ class GeoDataFrame(GeoPandasBase, DataFrame): return GeoDataFrame(data).__finalize__(self) def plot(self, *args, **kwargs): + return plot_dataframe(self, *args, **kwargs) + plot.__doc__ = plot_dataframe.__doc__ + def _dataframe_set_geometry(self, col, drop=False, inplace=False, crs=None): if inplace: diff --git a/geopandas/geoseries.py b/geopandas/geoseries.py index 3fea310..e4c3529 100644 --- a/geopandas/geoseries.py +++ b/geopandas/geoseries.py @@ -1,4 +1,5 @@ from functools import partial +import json from warnings import warn import numpy as np @@ -238,9 +239,10 @@ class GeoSeries(GeoPandasBase, Series): else: return False - def plot(self, *args, **kwargs): return plot_series(self, *args, **kwargs) + + plot.__doc__ = plot_series.__doc__ # # Additional methods @@ -254,6 +256,12 @@ class GeoSeries(GeoPandasBase, Series): joining points are assumed to be lines in the current projection, not geodesics. Objects crossing the dateline (or other projection boundary) will have undesirable behavior. + + `to_crs` passes the `crs` argument to the `Proj` function from the + `pyproj` library (with the option `preserve_units=True`). It can + therefore accept proj4 projections in any format + supported by `Proj`, including dictionaries, or proj4 strings. + """ from fiona.crs import from_epsg if self.crs is None: @@ -264,8 +272,8 @@ class GeoSeries(GeoPandasBase, Series): crs = from_epsg(epsg) except TypeError: raise TypeError('Must set either crs or epsg for output.') - proj_in = pyproj.Proj(preserve_units=True, **self.crs) - proj_out = pyproj.Proj(preserve_units=True, **crs) + proj_in = pyproj.Proj(self.crs, preserve_units=True) + proj_out = pyproj.Proj(crs, preserve_units=True) project = partial(pyproj.transform, proj_in, proj_out) result = self.apply(lambda geom: transform(project, geom)) result.__class__ = GeoSeries @@ -273,6 +281,16 @@ class GeoSeries(GeoPandasBase, Series): result._invalidate_sindex() return result + def to_json(self, **kwargs): + """ + Returns a GeoJSON string representation of the GeoSeries. + + Parameters + ---------- + *kwargs* that will be passed to json.dumps(). + """ + return json.dumps(self.__geo_interface__, **kwargs) + # # Implement standard operators for GeoSeries # diff --git a/geopandas/io/file.py b/geopandas/io/file.py index f842fde..d407615 100644 --- a/geopandas/io/file.py +++ b/geopandas/io/file.py @@ -13,8 +13,9 @@ def read_file(filename, **kwargs): Returns a GeoDataFrame from a file. *filename* is either the absolute or relative path to the file to be - opened and *kwargs* are keyword args to be passed to the method when - opening the file. + opened and *kwargs* are keyword args to be passed to the `open` method + in the fiona library when opening the file. For more information on + possible keywords, type: ``import fiona; help(fiona.open)`` """ bbox = kwargs.pop('bbox', None) with fiona.open(filename, **kwargs) as f: diff --git a/tests/__init__.py b/geopandas/io/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to geopandas/io/tests/__init__.py diff --git a/tests/test_io.py b/geopandas/io/tests/test_io.py similarity index 63% rename from tests/test_io.py rename to geopandas/io/tests/test_io.py index ec04bd7..e8e87f9 100644 --- a/tests/test_io.py +++ b/geopandas/io/tests/test_io.py @@ -2,23 +2,22 @@ from __future__ import absolute_import import fiona -from geopandas import GeoDataFrame, read_postgis, read_file -import tests.util -from .util import PANDAS_NEW_SQL_API, unittest +from geopandas import read_postgis, read_file +from geopandas.tests.util import download_nybb, connect, create_db, \ + PANDAS_NEW_SQL_API, unittest, validate_boro_df class TestIO(unittest.TestCase): def setUp(self): - nybb_filename = tests.util.download_nybb() - path = '/nybb_14a_av/nybb.shp' + nybb_filename, nybb_zip_path = download_nybb() vfs = 'zip://' + nybb_filename - self.df = read_file(path, vfs=vfs) - with fiona.open(path, vfs=vfs) as f: + self.df = read_file(nybb_zip_path, vfs=vfs) + with fiona.open(nybb_zip_path, vfs=vfs) as f: self.crs = f.crs def test_read_postgis_default(self): - con = tests.util.connect('test_geopandas') - if con is None or not tests.util.create_db(self.df): + con = connect('test_geopandas') + if con is None or not create_db(self.df): raise unittest.case.SkipTest() try: @@ -30,11 +29,11 @@ class TestIO(unittest.TestCase): con = con.connect() con.close() - tests.util.validate_boro_df(self, df) + validate_boro_df(self, df) def test_read_postgis_custom_geom_col(self): - con = tests.util.connect('test_geopandas') - if con is None or not tests.util.create_db(self.df): + con = connect('test_geopandas') + if con is None or not create_db(self.df): raise unittest.case.SkipTest() try: @@ -49,9 +48,9 @@ class TestIO(unittest.TestCase): con = con.connect() con.close() - tests.util.validate_boro_df(self, df) + validate_boro_df(self, df) def test_read_file(self): df = self.df.rename(columns=lambda x: x.lower()) - tests.util.validate_boro_df(self, df) + validate_boro_df(self, df) self.assert_(df.crs == self.crs) diff --git a/requirements_rtd.txt b/geopandas/tests/__init__.py similarity index 100% rename from requirements_rtd.txt rename to geopandas/tests/__init__.py diff --git a/tests/baseline_images/test_plotting/lines_plot.png b/geopandas/tests/baseline_images/test_plotting/lines_plot.png similarity index 100% rename from tests/baseline_images/test_plotting/lines_plot.png rename to geopandas/tests/baseline_images/test_plotting/lines_plot.png diff --git a/tests/baseline_images/test_plotting/points_plot.png b/geopandas/tests/baseline_images/test_plotting/points_plot.png similarity index 100% rename from tests/baseline_images/test_plotting/points_plot.png rename to geopandas/tests/baseline_images/test_plotting/points_plot.png diff --git a/tests/baseline_images/test_plotting/poly_plot.png b/geopandas/tests/baseline_images/test_plotting/poly_plot.png similarity index 100% rename from tests/baseline_images/test_plotting/poly_plot.png rename to geopandas/tests/baseline_images/test_plotting/poly_plot.png diff --git a/tests/baseline_images/test_plotting/poly_plot_with_kwargs.png b/geopandas/tests/baseline_images/test_plotting/poly_plot_with_kwargs.png similarity index 100% rename from tests/baseline_images/test_plotting/poly_plot_with_kwargs.png rename to geopandas/tests/baseline_images/test_plotting/poly_plot_with_kwargs.png diff --git a/tests/test_geocode.py b/geopandas/tests/test_geocode.py similarity index 98% rename from tests/test_geocode.py rename to geopandas/tests/test_geocode.py index 529ab0a..5f7b23a 100644 --- a/tests/test_geocode.py +++ b/geopandas/tests/test_geocode.py @@ -11,7 +11,7 @@ from geopandas import GeoSeries from geopandas.tools import geocode, reverse_geocode from geopandas.tools.geocoding import _prepare_geocode_result -from .util import unittest, mock, assert_geoseries_equal +from geopandas.tests.util import unittest, mock, assert_geoseries_equal def _skip_if_no_geopy(): diff --git a/tests/test_geodataframe.py b/geopandas/tests/test_geodataframe.py similarity index 97% rename from tests/test_geodataframe.py rename to geopandas/tests/test_geodataframe.py index ea4a59e..2796841 100644 --- a/tests/test_geodataframe.py +++ b/geopandas/tests/test_geodataframe.py @@ -12,8 +12,8 @@ from shapely.geometry import Point, Polygon import fiona from geopandas import GeoDataFrame, read_file, GeoSeries -from .util import unittest, download_nybb, assert_geoseries_equal, connect, \ - create_db, validate_boro_df, PANDAS_NEW_SQL_API +from geopandas.tests.util import assert_geoseries_equal, connect, create_db, \ + download_nybb, PACKAGE_DIR, PANDAS_NEW_SQL_API, unittest, validate_boro_df class TestDataFrame(unittest.TestCase): @@ -21,10 +21,10 @@ class TestDataFrame(unittest.TestCase): def setUp(self): N = 10 - nybb_filename = download_nybb() + nybb_filename, nybb_zip_path = download_nybb() - self.df = read_file('/nybb_14a_av/nybb.shp', vfs='zip://' + nybb_filename) - with fiona.open('/nybb_14a_av/nybb.shp', vfs='zip://' + nybb_filename) as f: + self.df = read_file(nybb_zip_path, vfs='zip://' + nybb_filename) + with fiona.open(nybb_zip_path, vfs='zip://' + nybb_filename) as f: self.schema = f.schema self.tempdir = tempfile.mkdtemp() self.boros = self.df['BoroName'] @@ -32,7 +32,7 @@ class TestDataFrame(unittest.TestCase): self.df2 = GeoDataFrame([ {'geometry': Point(x, y), 'value1': x + y, 'value2': x * y} for x, y in zip(range(N), range(N))], crs=self.crs) - self.df3 = read_file('examples/null_geom.geojson') + self.df3 = read_file(os.path.join(PACKAGE_DIR, 'examples', 'null_geom.geojson')) self.line_paths = self.df3['Name'] def tearDown(self): @@ -373,8 +373,8 @@ class TestDataFrame(unittest.TestCase): self.assertTrue(all(df2['geometry'].geom_almost_equals(utm['geometry'], decimal=2))) def test_from_features(self): - nybb_filename = download_nybb() - with fiona.open('/nybb_14a_av/nybb.shp', + nybb_filename, nybb_zip_path = download_nybb() + with fiona.open(nybb_zip_path, vfs='zip://' + nybb_filename) as f: features = list(f) crs = f.crs diff --git a/tests/test_geom_methods.py b/geopandas/tests/test_geom_methods.py similarity index 99% rename from tests/test_geom_methods.py rename to geopandas/tests/test_geom_methods.py index ccda5b1..0bccec3 100644 --- a/tests/test_geom_methods.py +++ b/geopandas/tests/test_geom_methods.py @@ -14,10 +14,11 @@ from shapely.ops import unary_union from geopandas import GeoSeries, GeoDataFrame from geopandas.base import GeoPandasBase -from .util import ( +from geopandas.tests.util import ( unittest, geom_equals, geom_almost_equals, assert_geoseries_equal ) + class TestGeomMethods(unittest.TestCase): def setUp(self): diff --git a/tests/test_geoseries.py b/geopandas/tests/test_geoseries.py similarity index 81% rename from tests/test_geoseries.py rename to geopandas/tests/test_geoseries.py index 91c11f5..42b585b 100644 --- a/tests/test_geoseries.py +++ b/geopandas/tests/test_geoseries.py @@ -1,16 +1,16 @@ from __future__ import absolute_import import os +import json import shutil import tempfile import numpy as np from numpy.testing import assert_array_equal -from pandas import Series from shapely.geometry import (Polygon, Point, LineString, MultiPoint, MultiLineString, MultiPolygon) from shapely.geometry.base import BaseGeometry from geopandas import GeoSeries -from .util import unittest, geom_equals, geom_almost_equals +from geopandas.tests.util import unittest, geom_equals class TestSeries(unittest.TestCase): @@ -116,6 +116,12 @@ class TestSeries(unittest.TestCase): self.assertTrue(all(self.g3.geom_equals(s))) # TODO: compare crs + def test_to_json(self): + """Test whether GeoSeries.to_json works and returns an actual json file.""" + json_str = self.g3.to_json() + json_dict = json.loads(json_str) + # TODO : verify the output is a valid GeoJSON. + def test_representative_point(self): self.assertTrue(np.alltrue(self.g1.contains(self.g1.representative_point()))) self.assertTrue(np.alltrue(self.g2.contains(self.g2.representative_point()))) @@ -151,5 +157,28 @@ class TestSeries(unittest.TestCase): self.assertEqual(len(self.g1.__geo_interface__['features']), self.g1.shape[0]) + def test_proj4strings(self): + # As string + reprojected = self.g3.to_crs('+proj=utm +zone=30N') + reprojected_back = reprojected.to_crs(epsg=4326) + self.assertTrue(np.alltrue(self.g3.geom_almost_equals(reprojected_back))) + + # As dict + reprojected = self.g3.to_crs({'proj': 'utm', 'zone': '30N'}) + reprojected_back = reprojected.to_crs(epsg=4326) + self.assertTrue(np.alltrue(self.g3.geom_almost_equals(reprojected_back))) + + # Set to equivalent string, convert, compare to original + copy = self.g3.copy() + copy.crs = '+init=epsg:4326' + reprojected = copy.to_crs({'proj': 'utm', 'zone': '30N'}) + reprojected_back = reprojected.to_crs(epsg=4326) + self.assertTrue(np.alltrue(self.g3.geom_almost_equals(reprojected_back))) + + # Conversions by different format + reprojected_string = self.g3.to_crs('+proj=utm +zone=30N') + reprojected_dict = self.g3.to_crs({'proj': 'utm', 'zone': '30N'}) + self.assertTrue(np.alltrue(reprojected_string.geom_almost_equals(reprojected_dict))) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_overlay.py b/geopandas/tests/test_overlay.py similarity index 94% rename from tests/test_overlay.py rename to geopandas/tests/test_overlay.py index 4d0961f..0586fac 100644 --- a/tests/test_overlay.py +++ b/geopandas/tests/test_overlay.py @@ -1,10 +1,13 @@ from __future__ import absolute_import + import tempfile import shutil + from shapely.geometry import Point + from geopandas import GeoDataFrame, read_file from geopandas.tools import overlay -from .util import unittest, download_nybb +from geopandas.tests.util import unittest, download_nybb class TestDataFrame(unittest.TestCase): @@ -12,9 +15,9 @@ class TestDataFrame(unittest.TestCase): def setUp(self): N = 10 - nybb_filename = download_nybb() + nybb_filename, nybb_zip_path = download_nybb() - self.polydf = read_file('/nybb_14a_av/nybb.shp', vfs='zip://' + nybb_filename) + self.polydf = read_file(nybb_zip_path, vfs='zip://' + nybb_filename) self.tempdir = tempfile.mkdtemp() self.crs = {'init': 'epsg:4326'} b = [int(x) for x in self.polydf.total_bounds] diff --git a/tests/test_plotting.py b/geopandas/tests/test_plotting.py similarity index 100% rename from tests/test_plotting.py rename to geopandas/tests/test_plotting.py diff --git a/tests/test_sindex.py b/geopandas/tests/test_sindex.py similarity index 89% rename from tests/test_sindex.py rename to geopandas/tests/test_sindex.py index 3ed3201..d978d20 100644 --- a/tests/test_sindex.py +++ b/geopandas/tests/test_sindex.py @@ -1,13 +1,7 @@ -import shutil -import tempfile -import numpy as np -from numpy.testing import assert_array_equal -from pandas import Series, read_csv -from shapely.geometry import (Polygon, Point, LineString, - MultiPoint, MultiLineString, MultiPolygon) -from shapely.geometry.base import BaseGeometry +from shapely.geometry import Polygon, Point + from geopandas import GeoSeries, GeoDataFrame, base, read_file -from .util import unittest, geom_equals, geom_almost_equals +from geopandas.tests.util import unittest, download_nybb @unittest.skipIf(not base.HAS_SINDEX, 'Rtree absent, skipping') @@ -85,9 +79,8 @@ class TestFrameSindex(unittest.TestCase): class TestJoinSindex(unittest.TestCase): def setUp(self): - self.boros = read_file( - "/nybb_14a_av/nybb.shp", - vfs="zip://examples/nybb_14aav.zip") + nybb_filename, nybb_zip_path = download_nybb() + self.boros = read_file(nybb_zip_path, vfs='zip://' + nybb_filename) def test_merge_geo(self): # First check that we gets hits from the boros frame. @@ -122,4 +115,3 @@ class TestJoinSindex(unittest.TestCase): self.assertEqual( [merged.ix[hit.object]['BoroName'] for hit in hits], ['Bronx', 'Queens']) - diff --git a/tests/test_types.py b/geopandas/tests/test_types.py similarity index 98% rename from tests/test_types.py rename to geopandas/tests/test_types.py index f4b0dad..f5f1d59 100644 --- a/tests/test_types.py +++ b/geopandas/tests/test_types.py @@ -5,7 +5,7 @@ from shapely.geometry import Point from pandas import Series, DataFrame from geopandas import GeoSeries, GeoDataFrame -from .util import unittest +from geopandas.tests.util import unittest OLD_PANDAS = issubclass(Series, np.ndarray) diff --git a/tests/util.py b/geopandas/tests/util.py similarity index 89% rename from tests/util.py rename to geopandas/tests/util.py index b619a1f..51f2f94 100644 --- a/tests/util.py +++ b/geopandas/tests/util.py @@ -1,17 +1,22 @@ import io import os.path +import sys +import zipfile + from six.moves.urllib.request import urlopen +from pandas.util.testing import assert_isinstance from geopandas import GeoDataFrame, GeoSeries +HERE = os.path.abspath(os.path.dirname(__file__)) +PACKAGE_DIR = os.path.dirname(os.path.dirname(HERE)) + # Compatibility layer for Python 2.6: try loading unittest2 -import sys if sys.version_info[:2] == (2, 6): try: import unittest2 as unittest except ImportError: import unittest - else: import unittest @@ -36,16 +41,26 @@ else: def download_nybb(): - """ Returns the path to the NYC boroughs file. Downloads if necessary. """ + """ Returns the path to the NYC boroughs file. Downloads if necessary. + + returns tuple (zip file name, shapefile's name and path within zip file)""" # Data from http://www.nyc.gov/html/dcp/download/bytes/nybb_14aav.zip # saved as geopandas/examples/nybb_14aav.zip. - filename = 'nybb_14aav.zip' - full_path_name = os.path.join('examples', filename) + filename = 'nybb_16a.zip' + full_path_name = os.path.join(PACKAGE_DIR, 'examples', filename) if not os.path.exists(full_path_name): with io.open(full_path_name, 'wb') as f: - response = urlopen('http://www.nyc.gov/html/dcp/download/bytes/{0}'.format(filename)) + response = urlopen('http://www1.nyc.gov/assets/planning/download/zip/data-maps/open-data/{0}'.format(filename)) f.write(response.read()) - return full_path_name + + shp_zip_path = None + zf = zipfile.ZipFile(full_path_name, 'r') + # finds path name in zip file + for zip_filename_path in zf.namelist(): + if zip_filename_path.endswith('nybb.shp'): + break + + return full_path_name, ('/' + zip_filename_path) def validate_boro_df(test, df): @@ -149,13 +164,6 @@ def geom_almost_equals(this, that): return (this.geom_almost_equals(that) | (this.is_empty & that.is_empty)).all() -# TODO: Remove me when standardizing on pandas 0.13, which already includes -# this test util. -def assert_isinstance(obj, klass_or_tuple): - assert isinstance(obj, klass_or_tuple), "type: %r != %r" % ( - type(obj).__name__, - getattr(klass_or_tuple, '__name__', - klass_or_tuple)) def assert_geoseries_equal(left, right, check_dtype=False, check_index_type=False, diff --git a/geopandas/tools/tests/__init__.py b/geopandas/tools/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_sjoin.py b/geopandas/tools/tests/test_sjoin.py similarity index 92% rename from tests/test_sjoin.py rename to geopandas/tools/tests/test_sjoin.py index c7a8d71..c3b85dd 100644 --- a/tests/test_sjoin.py +++ b/geopandas/tools/tests/test_sjoin.py @@ -1,12 +1,13 @@ - from __future__ import absolute_import + import tempfile import shutil + import numpy as np from shapely.geometry import Point from geopandas import GeoDataFrame, read_file, base -from .util import unittest, download_nybb +from geopandas.tests.util import unittest, download_nybb from geopandas.tools import sjoin @@ -14,8 +15,8 @@ from geopandas.tools import sjoin class TestSpatialJoin(unittest.TestCase): def setUp(self): - nybb_filename = download_nybb() - self.polydf = read_file('/nybb_14a_av/nybb.shp', vfs='zip://' + nybb_filename) + nybb_filename, nybb_zip_path = download_nybb() + self.polydf = read_file(nybb_zip_path, vfs='zip://' + nybb_filename) self.tempdir = tempfile.mkdtemp() self.crs = {'init': 'epsg:4326'} N = 20 @@ -55,7 +56,7 @@ class TestSpatialJoin(unittest.TestCase): # points within polygons df = sjoin(self.pointdf, self.polydf, how="left", op="within") self.assertEquals(df.shape, (21,8)) - self.assertAlmostEquals(df.ix[1]['Shape_Leng'], 330454.175933) + self.assertEquals(df.ix[1]['BoroName'], 'Staten Island') # points contain polygons? never happens so we should have nulls df = sjoin(self.pointdf, self.polydf, how="left", op="contains") diff --git a/tests/test_tools.py b/geopandas/tools/tests/test_tools.py similarity index 88% rename from tests/test_tools.py rename to geopandas/tools/tests/test_tools.py index fbe7759..6c73bf9 100644 --- a/tests/test_tools.py +++ b/geopandas/tools/tests/test_tools.py @@ -1,18 +1,20 @@ from __future__ import absolute_import + from shapely.geometry import Point, MultiPoint, LineString from geopandas import GeoSeries from geopandas.tools import collect -from .util import unittest +from geopandas.tests.util import unittest + class TestTools(unittest.TestCase): def setUp(self): - self.p1 = Point(0,0) - self.p2 = Point(1,1) - self.p3 = Point(2,2) + self.p1 = Point(0, 0) + self.p2 = Point(1, 1) + self.p3 = Point(2, 2) self.mpc = MultiPoint([self.p1, self.p2, self.p3]) self.mp1 = MultiPoint([self.p1, self.p2]) - self.line1 = LineString([(3,3), (4,4)]) + self.line1 = LineString([(3, 3), (4, 4)]) def test_collect_single(self): result = collect(self.p1) diff --git a/readthedocs.yml b/readthedocs.yml new file mode 100644 index 0000000..1c401c1 --- /dev/null +++ b/readthedocs.yml @@ -0,0 +1,5 @@ +conda: + file: doc/environment.yml +python: + version: 3 + setup_py_install: true diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1