mirror of
https://github.com/wassname/geopandas.git
synced 2026-09-09 11:22:50 +08:00
Revert "Revert "Merge branch 'feature/coordinate_index' into spatial_index""
This reverts commit 36c8b1e747.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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,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
@@ -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
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user