Revert "Revert "Merge branch 'feature/coordinate_index' into spatial_index""

This reverts commit 36c8b1e747.
This commit is contained in:
Carson Farmer
2014-07-12 11:24:18 -05:00
parent f39c83f5db
commit 3893bbe346
7 changed files with 104 additions and 42 deletions
+6 -1
View File
@@ -1,8 +1,13 @@
About
=====
Coming soon...
Known issues
------------
- The ``geopy`` API has changed significantly over recent versions.
``geopy 0.99`` is currently supported (though it is known to fail
with Python 3.2, it should work with other supported python
versions).
.. toctree::
:maxdepth: 2
+2 -2
View File
@@ -55,7 +55,7 @@ def geocode(strings, provider='googlev3', **kwargs):
"""
import geopy
from geopy.geocoders.base import GeocoderResultError
from geopy.geocoders.base import GeocoderQueryError
if not isinstance(strings, pd.Series):
strings = pd.Series(strings)
@@ -81,7 +81,7 @@ def geocode(strings, provider='googlev3', **kwargs):
for i, s in iteritems(strings):
try:
results[i] = coder.geocode(s)
except (GeocoderResultError, ValueError):
except (GeocoderQueryError, ValueError):
results[i] = (None, None)
time.sleep(_throttle_time(provider))
+27 -1
View File
@@ -3,8 +3,10 @@ from warnings import warn
import numpy as np
from pandas import Series, DataFrame
from pandas.core.indexing import _NDFrameIndexer
from pandas.util.decorators import cache_readonly
import pyproj
from shapely.geometry import shape, Polygon, Point
from shapely.geometry import box, shape, Polygon, Point
from shapely.geometry.collection import GeometryCollection
from shapely.geometry.base import BaseGeometry
from shapely.ops import transform
@@ -26,6 +28,28 @@ def _convert_array_args(args):
args = ([args[0]],)
return args
class _CoordinateIndexer(_NDFrameIndexer):
""" Indexing by coordinate slices """
def _getitem_tuple(self, tup):
obj = self.obj
xs, ys = tup
# handle numeric values as x and/or y coordinate index
if type(xs) is not slice:
xs = slice(xs, xs)
if type(ys) is not slice:
ys = slice(ys, ys)
# don't know how to handle step; should this raise?
if xs.step is not None or ys.step is not None:
warn("Ignoring step - full interval is used.")
xmin, ymin, xmax, ymax = obj.total_bounds
bbox = box(xs.start or xmin,
ys.start or ymin,
xs.stop or xmax,
ys.stop or ymax)
idx = obj.intersects(bbox)
return obj[idx]
class GeoSeries(GeoPandasBase, Series):
"""A Series object designed to store shapely geometry objects."""
_metadata = ['name', 'crs']
@@ -261,3 +285,5 @@ class GeoSeries(GeoPandasBase, Series):
def __sub__(self, other):
"""Implement - operator as for builtin set type"""
return self.difference(other)
GeoSeries._create_indexer('cx', _CoordinateIndexer)
+1 -1
View File
@@ -1,5 +1,5 @@
psycopg2>=2.5.1
geopy==0.96.3
geopy==0.99
matplotlib>=1.2.1
descartes>=1.0
pytest-cov
+16 -4
View File
@@ -1,5 +1,7 @@
from __future__ import absolute_import
import sys
from fiona.crs import from_epsg
import pandas as pd
from shapely.geometry import Point
@@ -14,7 +16,10 @@ def _skip_if_no_geopy():
try:
import geopy
except ImportError:
raise nose.SkipTest("Geopy not installed. Skipping")
raise nose.SkipTest("Geopy not installed. Skipping tests.")
except SyntaxError:
raise nose.SkipTest("Geopy is known to be broken on Python 3.2. "
"Skipping tests.")
class TestGeocode(unittest.TestCase):
def setUp(self):
@@ -25,6 +30,7 @@ class TestGeocode(unittest.TestCase):
def test_prepare_result(self):
# Calls _prepare_result with sample results from the geocoder call
# loop
from geopandas.geocode import _prepare_geocode_result
p0 = Point(12.3, -45.6) # Treat these as lat/lon
p1 = Point(-23.4, 56.7)
d = {'a': ('address0', p0.coords[0]),
@@ -48,6 +54,7 @@ class TestGeocode(unittest.TestCase):
self.assertAlmostEqual(coords[1], test[0])
def test_prepare_result_none(self):
from geopandas.geocode import _prepare_geocode_result
p0 = Point(12.3, -45.6) # Treat these as lat/lon
d = {'a': ('address0', p0.coords[0]),
'b': (None, None)}
@@ -63,17 +70,22 @@ class TestGeocode(unittest.TestCase):
self.assert_(pd.np.isnan(row['address']))
def test_bad_provider(self):
from geopandas.geocode import geocode
with self.assertRaises(ValueError):
geocode(['cambridge, ma'], 'badprovider')
def test_googlev3(self):
g = geocode(self.locations, provider='googlev3')
from geopandas.geocode import geocode
g = geocode(self.locations, provider='googlev3', timeout=2)
self.assertIsInstance(g, gpd.GeoDataFrame)
def test_openmapquest(self):
g = geocode(self.locations, provider='openmapquest')
from geopandas.geocode import geocode
g = geocode(self.locations, provider='openmapquest', timeout=2)
self.assertIsInstance(g, gpd.GeoDataFrame)
@unittest.skip('Nominatim server is unreliable for tests.')
def test_nominatim(self):
g = geocode(self.locations, provider='nominatim')
from geopandas.geocode import geocode
g = geocode(self.locations, provider='nominatim', timeout=2)
self.assertIsInstance(g, gpd.GeoDataFrame)
+45 -33
View File
@@ -6,7 +6,7 @@ import numpy as np
from numpy.testing import assert_array_equal
from pandas.util.testing import assert_series_equal, assert_frame_equal
from pandas import Series, DataFrame
from shapely.geometry import Point, LineString, Polygon
from shapely.geometry import Point, LinearRing, LineString, Polygon
from shapely.geometry.collection import GeometryCollection
from geopandas import GeoSeries, GeoDataFrame
@@ -21,6 +21,13 @@ class TestGeomMethods(unittest.TestCase):
self.t1 = Polygon([(0, 0), (1, 0), (1, 1)])
self.t2 = Polygon([(0, 0), (1, 1), (0, 1)])
self.sq = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
self.inner_sq = Polygon([(0.25, 0.25), (0.75, 0.25), (0.75, 0.75),
(0.25, 0.75)])
self.nested_squares = Polygon(self.sq.boundary,
[self.inner_sq.boundary])
self.p0 = Point(5, 5)
self.g0 = GeoSeries([self.t1, self.t2, self.sq, self.inner_sq,
self.nested_squares, self.p0])
self.g1 = GeoSeries([self.t1, self.sq])
self.g2 = GeoSeries([self.sq, self.t1])
self.g3 = GeoSeries([self.t1, self.t2])
@@ -40,6 +47,11 @@ class TestGeomMethods(unittest.TestCase):
self.l2 = LineString([(0, 0), (1, 0), (1, 1), (0, 1)])
self.g5 = GeoSeries([self.l1, self.l2])
# Crossed lines
self.l3 = LineString([(0, 0), (1, 1)])
self.l4 = LineString([(0, 1), (1, 0)])
self.crossed_lines = GeoSeries([self.l3, self.l4])
# Placeholder for testing, will just drop in different geometries
# when needed
self.gdf1 = GeoDataFrame({'geometry' : self.g1,
@@ -214,7 +226,6 @@ class TestGeomMethods(unittest.TestCase):
index=self.g1.index,
columns=['minx', 'miny', 'maxx', 'maxy'])
result = self.g1.bounds
assert_frame_equal(expected, result)
@@ -223,46 +234,45 @@ class TestGeomMethods(unittest.TestCase):
assert_frame_equal(expected, result)
def test_contains(self):
expected = np.array([True] * len(self.g1))
assert_array_equal(expected, self.g1.contains(self.t1))
expected = np.array([False] * len(self.g1))
assert_array_equal(expected, self.g1.contains(Point(5,5)))
expected = [True, False, True, False, False, False]
assert_array_equal(expected, self.g0.contains(self.t1))
def test_length(self):
expected = Series(np.array([2 + np.sqrt(2), 4]), index=self.g1.index)
self._test_unary_real('length', expected, self.g1)
@unittest.skip('TODO')
def test_crosses(self):
# TODO
pass
expected = [False, False, False, False, False, False]
assert_array_equal(expected, self.g0.crosses(self.t1))
expected = [False, True]
assert_array_equal(expected, self.crossed_lines.crosses(self.l3))
@unittest.skip('TODO')
def test_disjoint(self):
# TODO
pass
expected = [False, False, False, False, False, True]
assert_array_equal(expected, self.g0.disjoint(self.t1))
@unittest.skip('TODO')
def test_intersects(self):
# TODO
pass
expected = [True, True, True, True, True, False]
assert_array_equal(expected, self.g0.intersects(self.t1))
@unittest.skip('TODO')
def test_overlaps(self):
# TODO
pass
expected = [True, True, False, False, False, False]
assert_array_equal(expected, self.g0.overlaps(self.inner_sq))
expected = [False, False]
assert_array_equal(expected, self.g4.overlaps(self.t1))
@unittest.skip('TODO')
def test_touches(self):
# TODO
pass
expected = [False, True, False, False, False, False]
assert_array_equal(expected, self.g0.touches(self.t1))
@unittest.skip('TODO')
def test_within(self):
# TODO
pass
expected = [True, False, False, False, False, False]
assert_array_equal(expected, self.g0.within(self.t1))
expected = [True, True, True, True, True, False]
assert_array_equal(expected, self.g0.within(self.sq))
def test_is_valid(self):
expected = Series(np.array([True] * len(self.g1)), self.g1.index)
@@ -280,15 +290,17 @@ class TestGeomMethods(unittest.TestCase):
expected = Series(np.array([True] * len(self.g1)), self.g1.index)
self._test_unary_real('is_simple', expected, self.g1)
@unittest.skip('TODO')
def test_exterior(self):
# TODO
pass
exp_exterior = GeoSeries([LinearRing(p.boundary) for p in self.g3])
for expected, computed in zip(exp_exterior, self.g3.exterior):
assert computed.equals(expected)
@unittest.skip('TODO')
def test_interiors(self):
# TODO
pass
square_series = GeoSeries(self.nested_squares)
exp_interiors = GeoSeries([LinearRing(self.inner_sq.boundary)])
for expected, computed in zip(exp_interiors, square_series.interiors):
assert computed[0].equals(expected)
def test_interpolate(self):
expected = GeoSeries([Point(0.5, 1.0), Point(0.75, 1.0)])
@@ -305,7 +317,7 @@ class TestGeomMethods(unittest.TestCase):
self._test_binary_real('project', expected, self.g5, p)
expected = Series([1.0, 0.5], index=self.g5.index)
self._test_binary_real('project', expected, self.g5, p,
self._test_binary_real('project', expected, self.g5, p,
normalized=True)
def test_translate_tuple(self):
+7
View File
@@ -139,5 +139,12 @@ class TestSeries(unittest.TestCase):
# XXX: method works inconsistently for different pandas versions
#self.na_none.fillna(method='backfill')
def test_coord_slice(self):
""" Test CoordinateSlicer """
# need some better test cases
self.assertTrue(geom_equals(self.g3, self.g3.cx[:, :]))
self.assertTrue(geom_equals(self.g3[[True, False]], self.g3.cx[0.9:, :0.1]))
self.assertTrue(geom_equals(self.g3[[False, True]], self.g3.cx[0:0.1, 0.9:1.0]))
if __name__ == '__main__':
unittest.main()