Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge pull request #1 from elemoine/aggregate

Aggregate
  • Loading branch information...
commit cbe6c062c35d7d4799dd2b9b8532185bfd089f81 2 parents 613fd42 + 4b70934
 Stéphane Brunner authored March 28, 2012
2  .gitignore
... ...
@@ -0,0 +1,2 @@
  1
+*.pyc
  2
+/GeoAlchemy.egg-info
94  doc/tutorial.rst
Source Rendered
@@ -256,8 +256,8 @@ Functions to obtain geometry value in different formats
256 256
     >>> binascii.hexlify(session.scalar(s.geom.wkb))
257 257
     '01010000007b14ae47e15a54c03333333333d34240'
258 258
     
259  
-Note that for all commands above a new query had to be made to the database. Internally
260  
-GeoAlchemy uses Well-Known-Binary (WKB) to fetch the geometry, that belongs to an object of a mapped class. 
  259
+Note that for all commands above a new query had to be made to the database. By default 
  260
+GeoAlchemy uses Well-Known-Binary (WKB) internally to fetch the geometry, that belongs to an object of a mapped class. 
261 261
 All the time an object is queried, the geometry for this object is loaded in WKB.
262 262
 
263 263
 You can also access this internal WKB geometry directly and use it for example to create a
@@ -269,6 +269,11 @@ the database.
269 269
     >>> binascii.hexlify(s.geom.geom_wkb)
270 270
 	'01010000007b14ae47e15a54c03333333333d34240'
271 271
 
  272
+Alternatively, passing the argument *wkt_internal=True* in the *Geometry* 
  273
+definition will cause GeoAlchemy to use Well-Known-Text (WKT) internally.
  274
+This allows the use of *coords*, *geom_type* and *geom_wkt* commands (examples in section below) 
  275
+without additional queries to the database.
  276
+(This feature currently only works with the PostGIS dialect)
272 277
 
273 278
 Functions to obtain the geometry type, coordinates, etc
274 279
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -286,6 +291,8 @@ Functions to obtain the geometry type, coordinates, etc
286 291
     37.649999999999999
287 292
     >>> s.geom.coords(session)
288 293
     [-81.420000000000002, 37.649999999999999]
  294
+    >>> s.geom.geom_type(session)
  295
+    Point
289 296
 
290 297
 Spatial operations that return new geometries
291 298
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -305,6 +312,24 @@ Spatial operations that return new geometries
305 312
     >>> session.scalar(cv_geom.wkt)
306 313
     'POLYGON((-81.2 37.89,-81.03 38.04,-80.3 38.2,-81.2 37.89))'
307 314
 
  315
+GeoAlchemy also provides aggregate functions, namely ``union``, ``collect``,
  316
+and ``extent``. Note that the latter is a bit different, because it does not
  317
+return a geometry but a ``BOX`` string. See the examples below:
  318
+
  319
+.. code-block:: python
  320
+
  321
+    >>> e = session.query(functions.extent(Lake.lake_geom)).filter(Lake.lake_geom != None).scalar()
  322
+    'BOX(-89.1329617834395 42.565127388535,-88.0846337579618 43.243949044586)'
  323
+    >>> u = session.query(functions.geometry_type(functions.union(Lake.lake_geom))).filter(Lake.lake_geom != None).scalar()
  324
+    'ST_MultiPolygon'
  325
+    >>> c = session.query(functions.geometry_type(functions.collect(Lake.lake_geom))).filter(Lake.lake_geom != None).scalar()
  326
+    'ST_MultiPolygon'
  327
+
  328
+.. note::
  329
+
  330
+   In this example the filter is needed because of a limitation in GeoAlchemy.
  331
+   Without the filter no the SQL query doesn't include a ``FROM`` clause.
  332
+
308 333
 Spatial relations for filtering features
309 334
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310 335
 
@@ -339,59 +364,20 @@ more complex queries can be made.
339 364
 	>>> session.scalar(pg_functions.gml(functions.transform(point, 2249)))
340 365
 	'<gml:Point srsName="EPSG:2249"><gml:coordinates>-2369733.76351267,1553066.7062767</gml:coordinates></gml:Point>'
341 366
 	
342  
-
343  
-Performing Aggregation Queries
344  
-------------------------------
345  
-
346  
-With GeoAlchemy we also have aggregation function like extent, union and collect.
347  
-An example to illustrate that, this script:
  367
+Spatial queries with python comparison operators
  368
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
348 369
 
349 370
 .. code-block:: python
350 371
 
351  
-    from sqlalchemy import create_engine, MetaData, Column, Integer
352  
-    from sqlalchemy.orm import sessionmaker
353  
-    from sqlalchemy.ext.declarative import declarative_base
354  
-    from geoalchemy import  GeometryColumn, Polygon, GeometryDDL
355  
-    from geoalchemy.functions import functions
356  
-    from geoalchemy.postgis import pg_functions
357  
-
358  
-    engine = create_engine('postgresql://gis:gis@localhost/gis', echo=True)
359  
-    metadata = MetaData(engine)
360  
-    session = sessionmaker(bind=engine)()
361  
-    Base = declarative_base(metadata=metadata)
362  
-
363  
-    class Overlap(Base):
364  
-        __tablename__ = 'overlap'
365  
-        id = Column(Integer, primary_key=True)
366  
-        geom = GeometryColumn(Polygon(2))
367  
-    
368  
-    session.add_all([
369  
-            Overlap(geom='POLYGON((0 0,0 50,50 50,50 0,0 0))'),
370  
-            Overlap(geom='POLYGON((20 20,20 80,80 80,80 20,20 20))'),
371  
-    ])
372  
-    
373  
-    print "Extent:"
374  
-    print session.query(functions.extent(Overlap.geom)). \
375  
-            filter(Overlap.geom != None).one()
376  
-
377  
-    print "Union:"
378  
-    print session.query(functions.union(Overlap.geom)). \
379  
-            filter(Overlap.geom != None).one()
380  
-
381  
-    print "Collect:"
382  
-    print session.query(functions.collect(Overlap.geom)). \
383  
-            filter(Overlap.geom != None).one()
384  
-
385  
-Return::
386  
-
387  
-    Extent:
388  
-    ('BOX(0 0,80 80)',)
389  
-    Union:
390  
-    (u'POLYGON((0 0,0 50,20 50,20 80,80 80,80 20,50 20,50 0,0 0))',)
391  
-    Collect:
392  
-    (u'MULTIPOLYGON(((0 0,0 50,50 50,50 0,0 0)),((20 20,20 80,80 80,80 20,20 20)))',)
393  
-
394  
-.. note::
  372
+    >>> s = session.query(Spot).first()
  373
+    >>>
  374
+    >>> session.query(Spot).filter(Spot.geom == s.geom).count()
  375
+    1L
  376
+    >>> session.query(Spot).filter(Spot.geom != s.geom).count()
  377
+    2L
  378
+    >>> session.query(Spot).filter(Spot.geom == None).count()
  379
+    0L
  380
+    >>> session.query(Spot).filter(Spot.geom != None).count()
  381
+    3L
395 382
 
396  
-   In this example the filter is needed because of a limitation of GeoAlchemy that 
397  
-   don't add the FROM part in the SQL query.
  383
+The *equal* (*==*) and *not equal* (*!=*) python operators construct queries with *ST_Equals()* and *NOT ST_Equals()* PostGIS (or dialect equivalent) functions respectively. In addition, utilising the operators in comparison with *None* will be replaced with *IS NULL* and *IS NOT NULL* respectively.
17  geoalchemy/base.py
@@ -13,6 +13,8 @@ class SpatialElement(object):
13 13
     """Represents a geometry value."""
14 14
 
15 15
     def __str__(self):
  16
+        if isinstance(self.desc, SpatialElement):
  17
+            return self.desc.desc
16 18
         return self.desc
17 19
 
18 20
     def __repr__(self):
@@ -30,6 +32,8 @@ def __get_wkt(self, session):
30 32
         if isinstance(self, WKTSpatialElement):
31 33
             # for WKTSpatialElement we don't need to make a new query
32 34
             return self.desc 
  35
+        elif isinstance(self.desc, WKTSpatialElement):
  36
+            return self.desc.desc
33 37
         else:
34 38
             return session.scalar(self.wkt)       
35 39
 
@@ -134,6 +138,13 @@ def geom_wkb(self):
134 138
             return self.desc.desc
135 139
         else:
136 140
             return None
  141
+    
  142
+    @property    
  143
+    def geom_wkt(self):
  144
+        if self.desc is not None and isinstance(self.desc, WKTSpatialElement):
  145
+            return self.desc.desc
  146
+        else:
  147
+            return None
137 148
 
138 149
 class GeometryBase(UserDefinedType):
139 150
     """Base Geometry column type for all spatial databases.
@@ -141,10 +152,12 @@ class GeometryBase(UserDefinedType):
141 152
 
142 153
     name = 'GEOMETRY'
143 154
 
144  
-    def __init__(self, dimension=2, srid=4326, spatial_index=True, **kwargs):
  155
+    def __init__(self, dimension=2, srid=4326, spatial_index=True,
  156
+                 wkt_internal=False, **kwargs):
145 157
         self.dimension = dimension
146 158
         self.srid = srid
147 159
         self.spatial_index = spatial_index
  160
+        self.wkt_internal = wkt_internal
148 161
         self.kwargs = kwargs
149 162
         super(GeometryBase, self).__init__()
150 163
 
@@ -175,7 +188,7 @@ def process(value):
175 188
     def adapt(self, cls, **kwargs):
176 189
         return cls(dimension=self.dimension, srid=self.srid,
177 190
                    spatial_index=self.spatial_index,
178  
-                   **self.kwargs)
  191
+                   wkt_internal=self.wkt_internal, **self.kwargs)
179 192
 
180 193
 # ORM integration
181 194
 
23  geoalchemy/functions.py
@@ -373,23 +373,32 @@ class _within_distance(BaseFunction):
373 373
         pass
374 374
     
375 375
     class union(ReturnsGeometryFunction):
376  
-        """union(geometry set)i
377  
-        
378  
-        Actually supported only by PostGIS
  376
+        """Union(geometry set)
  377
+
  378
+        Union is an aggregte function. It is typically used
  379
+        in ``SELECT`` clauses.
  380
+
  381
+        *Only supported in PostgreSQL/PostGIS currently.*
379 382
         """
380 383
         pass
381 384
             
382 385
     class collect(ReturnsGeometryFunction):
383  
-        """collect(geometry set)
  386
+        """Collect(geometry set)
384 387
         
385  
-        Actually supported only by PostGIS
  388
+        Collect is an aggregate function. It is typically used
  389
+        in ``SELECT`` clauses.
  390
+
  391
+        *Only supported in PostgreSQL/PostGIS currently.*
386 392
         """
387 393
         pass
388 394
     
389 395
     class extent(BaseFunction):
390  
-        """extent(geometry set)
  396
+        """Extent(geometry set)
391 397
         
392  
-        Actually supported only by PostGIS
  398
+        Extent is an aggregate function. It is typically used
  399
+        in ``SELECT`` clauses.
  400
+
  401
+        *Only supported in PostgreSQL/PostGIS currently.*
393 402
         """
394 403
         pass
395 404
     
11  geoalchemy/geometry.py
... ...
@@ -1,9 +1,12 @@
1  
-from sqlalchemy import Column, Table
  1
+import warnings
  2
+
  3
+from sqlalchemy import Column, Table, exc
2 4
 from sqlalchemy.orm import column_property
3 5
 from sqlalchemy.orm.interfaces import AttributeExtension
4 6
 from sqlalchemy.sql.expression import Alias
5 7
 from sqlalchemy.sql import expression
6 8
 from sqlalchemy.ext.compiler import compiles
  9
+from sqlalchemy.dialects.postgresql.base import PGDialect
7 10
 
8 11
 from geoalchemy.base import GeometryBase, _to_gis, SpatialComparator
9 12
 from geoalchemy.dialect import DialectManager
@@ -155,6 +158,12 @@ class GeometryExtensionColumn(Column):
155 158
 def compile_column(element, compiler, **kw):
156 159
     if isinstance(element.table, (Table, Alias)):
157 160
         if kw.has_key("within_columns_clause") and kw["within_columns_clause"] == True:
  161
+            if element.type.wkt_internal:
  162
+                if isinstance(compiler.dialect, PGDialect):
  163
+                    return compiler.process(functions.wkt(element))
  164
+                warnings.warn("WKT Internal GeometryColumn type not "
  165
+                    "compatible with %s dialect. Defaulting back to WKB"
  166
+                    % compiler.dialect.name, exc.SAWarning)
158 167
             return compiler.process(functions.wkb(element))
159 168
         
160 169
     return compiler.visit_column(element) 
4  geoalchemy/postgis.py
... ...
@@ -1,7 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from sqlalchemy import select, func, and_
3 3
 from geoalchemy.base import SpatialComparator, PersistentSpatialElement, \
4  
-    WKBSpatialElement
  4
+    WKBSpatialElement, WKTSpatialElement
5 5
 from geoalchemy.dialect import SpatialDialect 
6 6
 from geoalchemy.functions import functions, BaseFunction
7 7
 
@@ -118,6 +118,8 @@ def _get_function_mapping(self):
118 118
         return PGSpatialDialect.__functions
119 119
     
120 120
     def process_result(self, value, type):
  121
+        if type.wkt_internal:
  122
+            return PGPersistentSpatialElement(WKTSpatialElement(value, type.srid))
121 123
         return PGPersistentSpatialElement(WKBSpatialElement(value, type.srid))
122 124
     
123 125
     def handle_ddl_before_drop(self, bind, table, column):
75  geoalchemy/tests/test_postgis.py
@@ -15,7 +15,7 @@
15 15
 
16 16
 
17 17
 
18  
-engine = create_engine('postgresql://gis:gis@localhost/gis', echo=True)
  18
+engine = create_engine('postgresql://gis:gis@localhost/gis')
19 19
 metadata = MetaData(engine)
20 20
 session = sessionmaker(bind=engine)()
21 21
 Base = declarative_base(metadata=metadata)
@@ -32,7 +32,7 @@ class Lake(Base):
32 32
 
33 33
     lake_id = Column(Integer, primary_key=True)
34 34
     lake_name = Column(String)
35  
-    lake_geom = GeometryColumn(Geometry(2), comparator=PGComparator)
  35
+    lake_geom = GeometryColumn(Geometry(2, wkt_internal=True), comparator=PGComparator)
36 36
 
37 37
 spots_table = Table('spots', metadata,
38 38
                     Column('spot_id', Integer, primary_key=True),
@@ -119,10 +119,19 @@ def test_geometry_type(self):
119 119
         eq_(session.scalar(functions.geometry_type(r.road_geom)), 'ST_LineString')
120 120
         ok_(session.query(Road).filter(Road.road_geom.geometry_type == 'ST_LineString').first())
121 121
 
  122
+    def test_geom_type(self):
  123
+        r = session.query(Road).get(1)
  124
+        l = session.query(Lake).get(1)
  125
+        s = session.query(Spot).get(1)
  126
+        eq_(r.road_geom.geom_type(session), 'LineString')
  127
+        eq_(l.lake_geom.geom_type(session), 'Polygon')
  128
+        eq_(s.spot_location.geom_type(session), 'Point')
  129
+
122 130
     def test_wkt(self):
123 131
         l = session.query(Lake).get(1)
124 132
         assert session.scalar(self.r.road_geom.wkt) == 'LINESTRING(-88.6748409363057 43.1035032292994,-88.6464173694267 42.9981688343949,-88.607961955414 42.9680732929936,-88.5160033566879 42.9363057770701,-88.4390925286624 43.0031847579618)'
125 133
         eq_(session.scalar(l.lake_geom.wkt),'POLYGON((-88.7968950764331 43.2305732929936,-88.7935511273885 43.1553344394904,-88.716640299363 43.1570064140127,-88.7250001719745 43.2339172420382,-88.7968950764331 43.2305732929936))')
  134
+        eq_(session.scalar(l.lake_geom.wkt), l.lake_geom.geom_wkt)
126 135
         ok_(not session.query(Spot).filter(Spot.spot_location.wkt == 'POINT(0,0)').first())
127 136
         ok_(session.query(Spot).get(1) is 
128 137
             session.query(Spot).filter(Spot.spot_location == 'POINT(-88.5945861592357 42.9480095987261)').first())
@@ -500,54 +509,54 @@ def test_intersection(self):
500 509
 
501 510
     def test_extent(self):
502 511
         l = session.query(functions.extent(Lake.lake_geom)). \
503  
-                filter(Lake.lake_geom != None).one()
  512
+                filter(Lake.lake_geom != None).scalar()
504 513
         r = session.query(functions.extent(Road.road_geom)). \
505  
-                filter(Road.road_geom != None).one()
  514
+                filter(Road.road_geom != None).scalar()
506 515
         s = session.query(functions.extent(Spot.spot_location)). \
507  
-                filter(Spot.spot_location != None).one()
  516
+                filter(Spot.spot_location != None).scalar()
508 517
         sh = session.query(functions.extent(Shape.shape_geom)). \
509  
-                filter(Shape.shape_geom != None).one()
  518
+                filter(Shape.shape_geom != None).scalar()
510 519
         
511  
-        eq_(l[0], "BOX(-89.1329617834395 42.565127388535,-88.0846337579618 43.243949044586)")
512  
-        eq_(r[0], "BOX(-89.2449842484076 42.5082802993631,-88.0110670509554 43.3175159681529)")
513  
-        eq_(s[0], "BOX(-89.201512910828 42.6269904904459,-88.3304141847134 43.1051752038217)")
514  
-        eq_(sh[0], "BOX(-88.7968950764331 42.5584395350319,-88.0110670509554 43.2339172420382)")
  520
+        eq_(l, "BOX(-89.1329617834395 42.565127388535,-88.0846337579618 43.243949044586)")
  521
+        eq_(r, "BOX(-89.2449842484076 42.5082802993631,-88.0110670509554 43.3175159681529)")
  522
+        eq_(s, "BOX(-89.201512910828 42.6269904904459,-88.3304141847134 43.1051752038217)")
  523
+        eq_(sh, "BOX(-88.7968950764331 42.5584395350319,-88.0110670509554 43.2339172420382)")
515 524
 
516 525
     def test_union(self):
517 526
         l = session.query(functions.geometry_type(functions.union(Lake.lake_geom))). \
518  
-                filter(Lake.lake_geom != None).one()
  527
+                filter(Lake.lake_geom != None).scalar()
519 528
         r = session.query(functions.geometry_type(functions.union(Road.road_geom))). \
520  
-                filter(Road.road_geom != None).one()
  529
+                filter(Road.road_geom != None).scalar()
521 530
         s = session.query(functions.geometry_type(functions.union(Spot.spot_location))). \
522  
-                filter(Spot.spot_location != None).one()
  531
+                filter(Spot.spot_location != None).scalar()
523 532
         sh = session.query(functions.geometry_type(functions.union(Shape.shape_geom))). \
524  
-                filter(Shape.shape_geom != None).one()
525  
-        la = session.query(functions.area(functions.union(Lake.lake_geom))). \
526  
-                filter(Lake.lake_geom != None).one()
  533
+                filter(Shape.shape_geom != None).scalar()
  534
+        la = session.query(functions.union(Lake.lake_geom).wkt). \
  535
+                filter(Lake.lake_geom != None).scalar()
527 536
         
528  
-        eq_(l[0], "ST_MultiPolygon")
529  
-        eq_(r[0], "ST_MultiLineString")
530  
-        eq_(s[0], "ST_MultiPoint")
531  
-        eq_(sh[0], "ST_GeometryCollection")
532  
-        eq_(float(la[0]), 0.121935268962943)
  537
+        eq_(l, "ST_MultiPolygon")
  538
+        eq_(r, "ST_MultiLineString")
  539
+        eq_(s, "ST_MultiPoint")
  540
+        eq_(sh, "ST_GeometryCollection")
  541
+        eq_(la, 'MULTIPOLYGON(((-88.1147292993631 42.7540605095542,-88.1548566878981 42.7824840764331,-88.1799363057325 42.7707802547771,-88.188296178344 42.7323248407643,-88.1832802547771 42.6955414012739,-88.1565286624204 42.6771496815287,-88.1448248407643 42.6336783439491,-88.131449044586 42.5718152866242,-88.1013535031847 42.565127388535,-88.1080414012739 42.5868630573248,-88.1164012738854 42.6119426751592,-88.1080414012739 42.6520700636943,-88.0980095541401 42.6838375796178,-88.0846337579618 42.7139331210191,-88.1013535031847 42.7423566878981,-88.1147292993631 42.7540605095542)),((-88.7878611897291 43.1554581337873,-88.7734872611465 43.0867834394905,-88.7517515923567 43.0299363057325,-88.7433917197452 42.9730891719745,-88.7517515923567 42.9145700636943,-88.7734872611465 42.8710987261147,-88.8102707006369 42.8343152866242,-88.8687898089172 42.815923566879,-88.9072452229299 42.8142515923567,-88.9440286624204 42.8292993630573,-88.9774681528663 42.8644108280255,-89.0042197452229 42.8961783439491,-89.0209394904459 42.9179140127389,-89.0343152866242 42.953025477707,-89.0694267515924 42.9898089171975,-89.112898089172 43.0132165605096,-89.1312898089172 43.0466560509554,-89.1329617834395 43.0884554140127,-89.1078821656051 43.1135350318471,-89.0694267515924 43.1335987261147,-89.0510350318471 43.1335987261147,-89.0393312101911 43.1386146496815,-89.0376592356688 43.1369426751592,-89.0292993630573 43.1519904458599,-89.0376592356688 43.175398089172,-89.0543789808917 43.203821656051,-89.0660828025478 43.2238853503185,-89.0710987261147 43.243949044586,-89.0410031847134 43.2389331210191,-89.0042197452229 43.2138535031847,-88.947372611465 43.1937898089172,-88.8738057324841 43.1620222929936,-88.7937087643429 43.1588812709654,-88.7968950764331 43.2305732929936,-88.7250001719745 43.2339172420382,-88.716640299363 43.1570064140127,-88.7878611897291 43.1554581337873)))')
533 542
 
534 543
     def test_collect(self):
535 544
         l = session.query(functions.geometry_type(functions.collect(Lake.lake_geom))). \
536  
-                filter(Lake.lake_geom != None).one()
  545
+                filter(Lake.lake_geom != None).scalar()
537 546
         r = session.query(functions.geometry_type(functions.collect(Road.road_geom))). \
538  
-                filter(Road.road_geom != None).one()
  547
+                filter(Road.road_geom != None).scalar()
539 548
         s = session.query(functions.geometry_type(functions.collect(Spot.spot_location))). \
540  
-                filter(Spot.spot_location != None).one()
  549
+                filter(Spot.spot_location != None).scalar()
541 550
         sh = session.query(functions.geometry_type(functions.collect(Shape.shape_geom))). \
542  
-                filter(Shape.shape_geom != None).one()
543  
-        la = session.query(functions.area(functions.collect(Lake.lake_geom))). \
544  
-                filter(Lake.lake_geom != None).one()
545  
-
546  
-        eq_(l[0], "ST_MultiPolygon")
547  
-        eq_(r[0], "ST_MultiLineString")
548  
-        eq_(s[0], "ST_MultiPoint")
549  
-        eq_(sh[0], "ST_GeometryCollection")
550  
-        eq_(float(la[0]), 0.122381342373046)
  551
+                filter(Shape.shape_geom != None).scalar()
  552
+        la = session.query(functions.collect(Lake.lake_geom).wkt). \
  553
+                filter(Lake.lake_geom != None).scalar()
  554
+
  555
+        eq_(l, "ST_MultiPolygon")
  556
+        eq_(r, "ST_MultiLineString")
  557
+        eq_(s, "ST_MultiPoint")
  558
+        eq_(sh, "ST_GeometryCollection")
  559
+        eq_(la, "MULTIPOLYGON(((-88.7968950764331 43.2305732929936,-88.7935511273885 43.1553344394904,-88.716640299363 43.1570064140127,-88.7250001719745 43.2339172420382,-88.7968950764331 43.2305732929936)),((-88.1147292993631 42.7540605095542,-88.1548566878981 42.7824840764331,-88.1799363057325 42.7707802547771,-88.188296178344 42.7323248407643,-88.1832802547771 42.6955414012739,-88.1565286624204 42.6771496815287,-88.1448248407643 42.6336783439491,-88.131449044586 42.5718152866242,-88.1013535031847 42.565127388535,-88.1080414012739 42.5868630573248,-88.1164012738854 42.6119426751592,-88.1080414012739 42.6520700636943,-88.0980095541401 42.6838375796178,-88.0846337579618 42.7139331210191,-88.1013535031847 42.7423566878981,-88.1147292993631 42.7540605095542)),((-89.0694267515924 43.1335987261147,-89.1078821656051 43.1135350318471,-89.1329617834395 43.0884554140127,-89.1312898089172 43.0466560509554,-89.112898089172 43.0132165605096,-89.0694267515924 42.9898089171975,-89.0343152866242 42.953025477707,-89.0209394904459 42.9179140127389,-89.0042197452229 42.8961783439491,-88.9774681528663 42.8644108280255,-88.9440286624204 42.8292993630573,-88.9072452229299 42.8142515923567,-88.8687898089172 42.815923566879,-88.8687898089172 42.815923566879,-88.8102707006369 42.8343152866242,-88.7734872611465 42.8710987261147,-88.7517515923567 42.9145700636943,-88.7433917197452 42.9730891719745,-88.7517515923567 43.0299363057325,-88.7734872611465 43.0867834394905,-88.7885352038217 43.158678388535,-88.8738057324841 43.1620222929936,-88.947372611465 43.1937898089172,-89.0042197452229 43.2138535031847,-89.0410031847134 43.2389331210191,-89.0710987261147 43.243949044586,-89.0660828025478 43.2238853503185,-89.0543789808917 43.203821656051,-89.0376592356688 43.175398089172,-89.0292993630573 43.1519904458599,-89.0376592356688 43.1369426751592,-89.0393312101911 43.1386146496815,-89.0393312101911 43.1386146496815,-89.0510350318471 43.1335987261147,-89.0694267515924 43.1335987261147)),((-88.9122611464968 43.038296178344,-88.9222929936306 43.0399681528663,-88.9323248407643 43.0282643312102,-88.9206210191083 43.0182324840764,-88.9105891719745 43.0165605095542,-88.9005573248408 43.0232484076433,-88.9072452229299 43.0282643312102,-88.9122611464968 43.038296178344)))")
551 560
 
552 561
     def test_within_distance(self):
553 562
         ok_(session.scalar(functions._within_distance('POINT(-88.9139332929936 42.5082802993631)', 'POINT(-88.9139332929936 35.5082802993631)', 10)))

0 notes on commit cbe6c06

Please sign in to comment.
Something went wrong with that request. Please try again.