Changeset 3569 for framework

Show
Ignore:
Timestamp:
06/16/10 21:46:02 (2 years ago)
Author:
elemoine
Message:

use Geo Alchemy? in Map Fish? (merge Tobias' sandbox into trunk) (closes #524)

Location:
framework/server/trunk
Files:
11 modified
42 copied

Legend:

Unmodified
Added
Removed
  • framework/server/trunk

    • Property svn:mergeinfo set to /sandbox/tsauerwein/server:3435-3568
  • framework/server/trunk/mapfish/commands.py

    r3387 r3569  
    245245                schema = None 
    246246 
     247            # get geometry type 
     248            if not config.has_option(sectionName, 'geomtype'): 
     249                geomtype = 'Geometry' 
     250            else: 
     251                raw_geomtype = config.get(sectionName, 'geomtype') 
     252                # check if the value is valid (geometries supported by GeoAlchemy) 
     253                valid_types = ['Geometry', 'Point', 'Curve', 'LineString', 'Polygon', 
     254                                'MultiPoint', 'MultiLineString', 'MultiPolygon', 'GeometryCollection'] 
     255                 
     256                if raw_geomtype in valid_types: 
     257                    geomtype = raw_geomtype 
     258                else: 
     259                    raise BadCommand('Geometry type "%s" is unknown, valid values are: %s'  
     260                                        % (raw_geomtype, valid_types)) 
     261 
    247262            fileOp = FileOp(source_dir=os.path.join( 
    248263                os.path.dirname(__file__), 'templates')) 
     
    268283            # set template vars 
    269284            modelClass = util.class_name_from_module_name(singularName) 
    270             modelTabObj = name + '_table' 
    271285 
    272286            # setup the model 
    273287            fileOp.template_vars.update( 
    274288                {'modelClass': modelClass, 
    275                  'modelTabObj': modelTabObj, 
    276289                 'table': table, 
    277290                 'epsg': epsg, 
    278291                 'geomColName': geomColName, 
     292                 'geomType': geomtype, 
    279293                 'basePkg': basePkg, 
    280294                 'schema': schema}) 
  • framework/server/trunk/mapfish/lib/filters/spatial.py

    r3083 r3569  
    2020from mapfish.lib.filters import Filter 
    2121 
    22 from sqlalchemy.sql import func, and_ 
    23  
    2422from geojson import loads, GeoJSON 
    25  
    2623 
    2724from shapely.geometry import asShape 
    2825from shapely.geometry.point import Point 
    2926from shapely.geometry.polygon import Polygon 
     27 
     28from geoalchemy import WKBSpatialElement 
     29from geoalchemy.functions import functions 
     30 
     31from mapfish.sqlalchemygeom import within_distance 
    3032 
    3133class Spatial(Filter): 
     
    3840      geom_column 
    3941          the Column object corresponding to the geometry 
    40           column. 
     42          column (contains the database column type). 
    4143 
    4244      \**kwargs 
     
    4446            the EPSG code of the lon, lat or box values, see 
    4547            below. 
     48             
     49          additional_params 
     50            additional parameters used for the database function 
     51            call (currently only used for Oracle) 
    4652 
    47           for Spatial.BOX filter: 
     53 
     54          For Spatial.BOX filter: 
    4855 
    4956          box 
     
    5663            the projection system of the lon/lat coordinates. 
    5764         
    58           for Spatial.WITHIN filter: 
     65         
     66          For Spatial.WITHIN filter: 
    5967 
    6068          lon 
     
    7381            the projection system of the lon/lat coordinates. 
    7482 
    75           for Spatial.GEOMETRY filter: 
     83 
     84          For Spatial.GEOMETRY filter: 
    7685             
    7786          geometry 
     
    98107        else: 
    99108            self.epsg = self.geom_column.type.srid 
     109        self.additional_params = self.values.get('additional_params', None) 
    100110 
    101111    def to_sql_expr(self): 
     
    110120            geometry = loads(self.values['geometry'], object_hook=factory) 
    111121            geometry = asShape(geometry) 
    112                         
     122         
    113123        if self.epsg != self.geom_column.type.srid: 
    114             geom_column = func.transform(self.geom_column, self.epsg) 
     124            geom_column = functions.transform(self.geom_column, self.epsg) 
    115125        else: 
    116126            geom_column = self.geom_column 
    117127 
    118128        tolerance = self.values['tolerance'] 
    119         pg_geometry = func.geomfromtext(geometry.wkt, self.epsg) 
    120         return and_(func.expand(pg_geometry, tolerance).op('&&')(geom_column), 
    121                     func.distance(geom_column, pg_geometry) <= tolerance) 
     129        wkb_geometry = WKBSpatialElement(buffer(geometry.wkb), self.epsg) 
     130         
     131        if self.additional_params is None: 
     132            return within_distance(geom_column, wkb_geometry, tolerance) 
     133        else: 
     134            return within_distance(geom_column, wkb_geometry, tolerance, self.additional_params) 
    122135 
    123136    def __box_to_geometry(self): 
  • framework/server/trunk/mapfish/lib/protocol.py

    r3462 r3569  
    2727from shapely.geometry import asShape 
    2828 
    29 from sqlalchemy.sql import select, asc, desc 
     29from sqlalchemy.sql import asc, desc 
     30 
     31from geoalchemy import WKBSpatialElement 
    3032 
    3133from geojson import dumps as _dumps, loads, Feature, FeatureCollection, GeoJSON 
     
    6466    return _dumps(obj, cls=cls, **kwargs) 
    6567 
    66 def create_geom_filter(request, mapped_class): 
     68def create_geom_filter(request, mapped_class, **kwargs): 
    6769    """Create MapFish geometry filter based on the request params. Either 
    68     a box or within or geometry filter, depending on the request params.""" 
     70    a box or within or geometry filter, depending on the request params. 
     71    Additional named arguments are passed to the spatial filter.""" 
    6972 
    7073    geom_column = mapped_class.geometry_column() 
     
    9497            box=box.split(','), 
    9598            tolerance=tolerance, 
    96             epsg=epsg 
     99            epsg=epsg, 
     100            **kwargs 
    97101        ) 
    98102    elif 'lon' and 'lat' in request.params: 
     
    104108            lat=float(request.params['lat']), 
    105109            tolerance=tolerance, 
    106             epsg=epsg 
     110            epsg=epsg, 
     111            **kwargs 
    107112        ) 
    108113    elif 'geometry' in request.params: 
     
    113118            geometry=request.params['geometry'], 
    114119            tolerance=tolerance, 
    115             epsg=epsg 
     120            epsg=epsg, 
     121            **kwargs 
    116122        ) 
    117123    return filter 
     
    149155    return filter 
    150156 
    151 def create_default_filter(request, mapped_class): 
    152     """ Create MapFish default filter based on the request params.""" 
    153  
    154     geom_filter = create_geom_filter(request, mapped_class)  
     157def create_default_filter(request, mapped_class, **kwargs): 
     158    """ Create MapFish default filter based on the request params. Additional 
     159    named arguments are passed to the spatial filter.""" 
     160 
     161    geom_filter = create_geom_filter(request, mapped_class, **kwargs)  
    155162    attr_filter = create_attr_filter(request, mapped_class) 
    156163 
     
    227234                return dumps( 
    228235                    FeatureCollection( 
    229                         [self._filter_attrs(o.toFeature(), request) for o in objects if o.geometry] 
     236                        [self._filter_attrs(o.toFeature(), request) for o in objects if o.geometry is not None] 
    230237                    ) 
    231238                ) 
     
    296303 
    297304        order_by = self._get_order_by(request) 
    298         if order_by: 
     305        if order_by is not None: 
    299306            query = query.order_by(order_by) 
    300307 
     
    361368                obj = self.mapped_class() 
    362369                create = True 
    363             obj.geometry = asShape(feature.geometry) 
    364             for key in feature.properties: 
    365                 obj[key] = feature.properties[key] 
     370            self.__copy_attributes(feature, obj) 
    366371            if create: 
    367372                self.Session.add(obj) 
     
    389394        if self.before_update is not None: 
    390395            self.before_update(request, feature, obj) 
    391         obj.geometry = asShape(feature.geometry) 
    392         for key in feature.properties: 
    393             obj[key] = feature.properties[key] 
     396        self.__copy_attributes(feature, obj) 
    394397        self.Session.commit() 
    395398        response.status = 201 
     
    409412        response.status = 204 
    410413        return 
    411  
     414     
     415    def __copy_attributes(self, json_feature, obj): 
     416        """Updates the passed-in object with the values 
     417        from the GeoJSON feature.""" 
     418        # create a Shapely geometry from GeoJSON and persist the geometry using WKB 
     419        shape = asShape(json_feature.geometry) 
     420        srid = self.mapped_class.geometry_column().type.srid 
     421        obj.geometry = WKBSpatialElement(buffer(shape.wkb), srid=srid) 
     422        # also store the Shapely geometry so that we can use it to return the geometry as GeoJSON 
     423        obj.geometry.shape = shape 
     424        for key in json_feature.properties: 
     425            obj[key] = json_feature.properties[key] 
  • framework/server/trunk/mapfish/plugins/pgrouting.py

    r3083 r3569  
    2222def shortest_path(engine, sql, source_id, target_id, 
    2323                  directed = False, has_reverse_cost = False): 
    24     # return array of: step, vertex_id, edge_id, cost 
    25  
     24    """Calculates the shortest path using the Dijkstra algorithm of the library pgrouting. 
     25    Returns an array of: vertex_id, edge_id, cost 
     26     
     27    see: http://pgrouting.postlbs.org/wiki/Dijkstra 
     28    """ 
    2629    return engine.execute("SELECT * FROM \ 
    2730                           shortest_path('%(sql)s', %(source_id)s, %(target_id)s, \ 
    28                                           %(directed)s, %(has_reverse_cost)s) ORDER BY step" 
    29                           %{'sql': sql.replace("'", r"\'"), 
     31                                          %(directed)s, %(has_reverse_cost)s)" 
     32                          %{'sql': sql.replace("'", r"''"), 
    3033                            'source_id': source_id, 
    3134                            'target_id': target_id, 
  • framework/server/trunk/mapfish/sqlalchemygeom.py

    r3453 r3569  
    1818# 
    1919 
    20 __all__ = ['Geometry', 'GeometryTableMixIn'] 
     20__all__ = ['GeometryTableMixIn'] 
    2121 
    2222 
     
    2828  ------- 
    2929from sqlalchemy import * 
    30 from sqlalchemygeom import Geometry 
     30from mapfish.sqlalchemygeom import GeometryTableMixIn 
     31from geoalchemy import GeometryColumn, Geometry 
    3132 
    3233# see: http://www.sqlalchemy.org/docs/dbengine.html 
     
    3637metadata.connect(db) 
    3738 
    38 wifi_t = Table('wifi', metadata, 
    39                Column('gid', Integer, primary_key=True), 
    40                # add more columns here ... 
    41                Column('the_geom', Geometry(4326)) 
    42                ) 
     39Base = declarative_base(metadata=metadata) 
     40 
     41class Wifi(Base, GeometryTableMixIn): 
     42    __tablename__ = 'wifi' 
     43    gid = Column(types.Integer, primary_key=True) 
     44    # add more columns here ... 
     45    the_geom = GeometryColumn(Geometry(dimension=2, srid=4326)) 
     46 
    4347 
    4448# basic select 
    45 r = wifi_t.select(wifi_t.c.gid == 10).execute() 
     49r = Wifi.__table__.select(Wifi.gid == 10).execute() 
    4650w = r.fetchone() 
    4751print w.the_geom 
     
    4953# advanced select 
    5054from shapely.geometry.point import Point 
    51 from binascii import b2a_hex 
     55from geoalchemy import functions, WKBSpatialElement 
     56 
    5257me = Point(532778, 152205) 
    5358 
    54 r = wifi_t.select(metadata.engine.func.distance(wifi_t.c.the_geom, b2a_hex(me.wkb)) < 100).execute() 
     59r = Wifi.__table__.select(functions.distance(Wifi.the_geom, WKBSpatialElement(buffer(me.wkb))) < 100).execute() 
    5560print [(i.gid, i.the_geom.distance(me)) for i in r] 
    5661 
    5762## update 
    58 #u = wifi_t.update(wifi_t.c.gid == 10) 
     63#u = Wifi.__table__.update(Wifi.the_geom == 10) 
    5964#w.the_geom.y += 9.0 
    6065#u.execute(the_geom = w.the_geom) 
    6166""" 
    6267 
    63 from sqlalchemy.types import TypeEngine 
    64 from sqlalchemy import Table 
    6568from shapely.wkb import loads 
    66  
    6769from geojson import Feature 
    6870 
    69 class Geometry(TypeEngine): 
    70      
    71     """The SQLAlchemy Geometry type. 
    72      
    73     Use a Geometry instance when defining a geometry column in an 
    74     ``sqlalchemy.schema.Table`` object of the model. 
    75  
    76     Example:: 
    77  
    78         wifi_table = Table('wifi', metadata, 
    79             Column('gid', Integer, primary_key=True), 
    80             Column('the_geom', Geometry(4326)) 
    81         ) 
    82  
    83     srid 
    84         The SRID of the geometry column, defaults to -1 (no SRID). 
    85  
    86     dims 
    87         The number of dimensions of the geometry column, defaults 
    88         to 2. 
    89  
    90     """ 
    91  
    92     def __init__(self, srid=-1, dims=2): 
    93         super(Geometry, self).__init__() 
    94         self.srid = srid 
    95         self.dims = dims 
    96  
    97     def get_col_spec(self): 
    98         return 'GEOMETRY()' 
    99  
    100     def compare_values(self, x, y): 
    101         t = (x, y) 
    102         if None not in t: 
    103             return x.equals(y) 
    104         return t == (None, None) 
    105  
    106     def bind_processor(self, dialect): 
    107         # convert value from a geometry object to database 
    108         def convert(value): 
    109             if value is None: 
    110                 return None 
    111             else: 
    112                 return "SRID=%s;%s" % (self.srid, value.wkb.encode('hex')) 
    113         return convert 
    114  
    115     def result_processor(self, dialect): 
    116         # convert value from database to a geometry object 
    117         def convert(value): 
    118             if value is None: 
    119                 return None 
    120             else: 
    121                 return loads(value.decode('hex')) 
    122         return convert 
     71from geoalchemy import Geometry as GeometryBase 
     72from geoalchemy.functions import BaseFunction, parse_clause 
     73from geoalchemy.geometry import GeometryExtensionColumn 
     74from geoalchemy.spatialite import SQLiteSpatialDialect 
     75 
     76from sqlalchemy.ext.compiler import compiles 
     77from sqlalchemy.sql import and_, text, table, column  
     78from sqlalchemy import select, func 
     79from sqlalchemy.schema import Column 
     80 
     81from sqlalchemy.dialects.postgresql.base import PGDialect 
     82from sqlalchemy.dialects.sqlite.base import SQLiteDialect 
     83from sqlalchemy.dialects.mysql.base import MySQLDialect 
     84from sqlalchemy.dialects.oracle.base import OracleDialect 
    12385 
    12486class GeometryTableMixIn(object): 
     
    150112 
    151113       Example:: 
    152  
    153            lines_table = Table( 
    154                "lines", metadata, 
    155                Column('the_geom', Geometry(4326)), 
    156                autoload=True, autoload_with=engine) 
    157  
    158            class Line(GeometryTableMixIn): 
    159                # for GeometryTableMixIn to do its job the __table__ property 
    160                # must be set here 
    161                __table__ = lines_table 
    162  
    163            mapper(Line, lines_table) 
     114        
     115            Base = declarative_base(metadata=metadata) 
     116 
     117            class Line(Base, GeometryTableMixIn): 
     118                __tablename__ = 'lines' 
     119                __table_args__ = { 
     120                        'autoload' : True, 
     121                        'autoload_with' : engine 
     122                    } 
     123                 
     124                the_geom = GeometryColumn(Geometry(dimension=2, srid=4326)) 
    164125 
    165126    """ 
    166127 
    167128    exported_keys = None 
    168     __geometry_column__ = None 
    169     __primary_key_column__ = None 
     129    __column_cache__ = None 
    170130 
    171131    def _getfid(self): 
     
    191151 
    192152    def __setitem__(self, key, val): 
    193          if key in self.__table__.c.keys(): 
    194              setattr(self, key, val) 
     153        if key in self.__table__.c.keys(): 
     154            setattr(self, key, val) 
    195155 
    196156    def __contains__(self, key): 
     
    200160    def geometry_column(cls): 
    201161        """ Returns the table's geometry column or None if the table has no geometry column. """ 
    202         if not cls.__geometry_column__: 
    203             columns = [c for c in cls.__table__.columns if isinstance(c.type, Geometry)] 
     162        if cls.__column_cache__ is None or "geometry" not in cls.__column_cache__: 
     163            columns = [c for c in cls.__table__.columns if isinstance(c.type, GeometryBase)] 
    204164            if not columns: 
    205165                return None 
     
    207167                raise Exception("There is more than one geometry column") 
    208168            else: 
    209                 cls.__geometry_column__ = columns.pop() 
    210         return cls.__geometry_column__ 
     169                column = columns.pop() 
     170                cls.__column_cache__ = dict(geometry=column) 
     171        return cls.__column_cache__["geometry"]  
    211172 
    212173    @classmethod 
    213174    def primary_key_column(cls): 
    214175        """ Returns the table's primary key column """ 
    215         if not cls.__primary_key_column__: 
     176        if cls.__column_cache__ is None or "primary_key" not in cls.__column_cache__: 
    216177            keys = [k for k in cls.__table__.primary_key] 
    217178            if not keys: 
     
    220181                raise Exception("There is more than one primary key column") 
    221182            else: 
    222                 cls.__primary_key_column__ = keys.pop() 
    223         return cls.__primary_key_column__ 
    224              
     183                cls.__column_cache__ = dict(primary_key=keys.pop()) 
     184        return cls.__column_cache__["primary_key"] 
     185 
    225186    def toFeature(self): 
    226187        """Create and return a ``geojson.Feature`` object from this mapped object.""" 
     
    238199            if k != fid_column and k != geom_column and hasattr(self, k): 
    239200                attributes[k] = getattr(self, k) 
     201         
     202        if hasattr(self.geometry, 'shape') and self.geometry.shape is not None: 
     203            # we already have the geometry as Shapely geometry (when updating/inserting) 
     204            geometry = self.geometry.shape 
     205        else: 
     206            # create a Shapely geometry from the WKB geometry returned from the database 
     207            geometry = loads(str(self.geometry.geom_wkb)) 
    240208 
    241209        return Feature(id=self.fid,  
    242                        geometry=self.geometry, 
     210                       geometry=geometry, 
    243211                       properties=attributes, 
    244                        bbox=self.geometry.bounds) 
     212                       bbox=geometry.bounds) 
     213 
     214 
     215class within_distance(BaseFunction): 
     216    """This class is used as SQLAlchemy function to query features that are  
     217    within a certain distance of a geometry.  
     218    When it is used inside a query, the SQLAlchemy compiler calls the  
     219    method __compile_within_distance. 
     220    """ 
     221    pass 
     222 
     223@compiles(within_distance) 
     224def __compile_within_distance(element, compiler, **kw): 
     225    if isinstance(compiler.dialect, PGDialect): 
     226        function = __within_distance_pg 
     227    elif isinstance(compiler.dialect, MySQLDialect): 
     228        function = __within_distance_mysql 
     229    elif isinstance(compiler.dialect, SQLiteDialect): 
     230        function = __within_distance_spatialite 
     231    elif isinstance(compiler.dialect, OracleDialect): 
     232        function = __within_distance_oracle 
     233    else: 
     234        raise NotImplementedError("Operation 'within_distance' is not supported by '%s'" % (compiler.dialect)) 
     235     
     236    arguments = list(element.arguments) 
     237    return compiler.process(function(compiler, parse_clause(arguments.pop(0), compiler),  
     238                                     parse_clause(arguments.pop(0), compiler), arguments.pop(0), *arguments)) 
     239     
     240def __within_distance_pg(compiler, geom1, geom2, distance): 
     241    """Implementation of within_distance for PostGIS 
     242     
     243    ST_DWithin in early versions of PostGIS 1.3 does not work when 
     244    distance = 0. So we are directly using the (correct) internal definition. 
     245    Note that the definition changed in version 1.3.4, see also: 
     246    http://postgis.refractions.net/docs/ST_DWithin.html 
     247    """ 
     248    return and_(func.ST_Expand(geom2, distance).op('&&')(geom1), 
     249                    func.ST_Expand(geom1, distance).op('&&')(geom2), 
     250                    func.ST_Distance(geom1, geom2) <= distance) 
     251 
     252def __within_distance_mysql(compiler, geom1, geom2, distance): 
     253    """Implementation of within_distance for MySQL 
     254     
     255    MySQL does not support the function distance, so we are doing 
     256    a kind of "mbr_within_distance". 
     257    The MBR of 'geom2' is expanded with the amount of 'distance' by 
     258    manually changing the coordinates. Then we test if 'geom1' intersects 
     259    this expanded MBR. 
     260    """ 
     261    mbr = func.ExteriorRing(func.Envelope(geom2)) 
     262     
     263    lower_left = func.StartPoint(mbr) 
     264    upper_right = func.PointN(mbr, 3) 
     265     
     266    xmin = func.X(lower_left) 
     267    ymin = func.Y(lower_left) 
     268    xmax = func.X(upper_right) 
     269    ymax = func.Y(upper_right) 
     270     
     271    return func.Intersects( 
     272            geom1, 
     273            func.GeomFromText( 
     274                func.Concat('Polygon((', 
     275                       xmin - distance, ' ', ymin - distance, ',', 
     276                       xmax + distance, ' ', ymin - distance, ',', 
     277                       xmax + distance, ' ', ymax + distance, ',', 
     278                       xmin - distance, ' ', ymax + distance, ',', 
     279                       xmin - distance, ' ', ymin - distance, '))'), func.srid(geom2) 
     280                )                                               
     281            ) 
     282 
     283def __within_distance_spatialite(compiler, geom1, geom2, distance): 
     284    """Implementation of within_distance for Spatialite 
     285    """ 
     286    if isinstance(geom1, GeometryExtensionColumn) and geom1.type.spatial_index and SQLiteSpatialDialect.supports_rtree(compiler.dialect): 
     287        """If querying on a geometry column that also has a spatial index, 
     288        then make use of this index. 
     289         
     290        see: http://www.gaia-gis.it/spatialite/spatialite-tutorial-2.3.1.html#t8 and 
     291        http://groups.google.com/group/spatialite-users/browse_thread/thread/34609c7a711ac92d/7688ced3f909039c?lnk=gst&q=index#f6dbc235471574db 
     292        """ 
     293        return and_( 
     294                    func.Distance(geom1, geom2) <= distance, 
     295                    table(geom1.table.fullname, column("rowid")).c.rowid.in_( 
     296                        select([table("idx_%s_%s" % (geom1.table.fullname, geom1.key), column("pkid")).c.pkid]).where( 
     297                            and_(text('xmin') >= func.MbrMinX(geom2) - distance, 
     298                            and_(text('xmax') <= func.MbrMaxX(geom2) + distance, 
     299                            and_(text('ymin') >= func.MbrMinY(geom2) - distance, 
     300                                 text('ymax') <= func.MbrMaxY(geom2) + distance))) 
     301                            ) 
     302                        ) 
     303                    ) 
     304         
     305    else: 
     306        return func.Distance(geom1, geom2) <= distance 
     307     
     308 
     309 
     310def __within_distance_oracle(compiler, geom1, geom2, distance, additional_params={}): 
     311    """Implementation of within_distance for Oracle 
     312     
     313    If the first parameter is a geometry column, then the Oracle operator SDO_WITHIN_DISTANCE 
     314    is called and Oracle makes use of the spatial index of this column. 
     315     
     316    If the first parameter is not a geometry column but a function, which is the case when a coordinate  
     317    transformation had to be added by the spatial filter, then the function SDO_GEOM.WITHIN_DISTANCE 
     318    is called. SDO_GEOM.WITHIN_DISTANCE does not make use of a spatial index and requires 
     319    additional parameters: either a tolerance value or a dimension information array (DIMINFO) 
     320    for both geometries. These parameters can be specified when defining the spatial filter, e.g.:: 
     321     
     322        additional_params={'tol': '0.005'} 
     323         
     324        or 
     325         
     326        from sqlalchemy.sql.expression import text 
     327        diminfo = text("MDSYS.SDO_DIM_ARRAY("\ 
     328            "MDSYS.SDO_DIM_ELEMENT('LONGITUDE', -180, 180, 0.000000005),"\ 
     329            "MDSYS.SDO_DIM_ELEMENT('LATITUDE', -90, 90, 0.000000005)"\ 
     330            ")") 
     331        additional_params={'dim1': diminfo, 'dim2': diminfo} 
     332         
     333        filter = create_default_filter(request, Spot, additional_params=additional_params) 
     334        proto.count(request, filter=filter) 
     335         
     336    For its distance calculation Oracle by default uses meter as unit for geodetic data (like EPSG:4326)  
     337    and otherwise the 'unit of measurement associated with the data'. The unit used for the 'distance' value 
     338    can be changed by adding an entry to 'additional_params'. Valid units are defined in the  
     339    view 'sdo_dist_units':: 
     340     
     341        additional_params={'params': 'unit=km'} 
     342         
     343    SDO_WITHIN_DISTANCE accepts further parameters, which can also be set using the name 'params' 
     344    together with the unit:: 
     345     
     346        additional_params={'params': 'unit=km max_resolution=10'} 
     347         
     348     
     349    Valid options for 'additional_params' are: 
     350     
     351        params 
     352            A String containing additional parameters, for example the unit. 
     353             
     354        tol 
     355            The tolerance value used for the SDO_GEOM.WITHIN_DISTANCE function call. 
     356             
     357        dim1 and dim2 
     358            If the parameter 'tol' is not set, these two parameters have to be set. 'dim1' is the DIMINFO 
     359            for the first geometry (the reprojected geometry column) and 'dim2' is the DIMINFO for 
     360            the second geometry (the input geometry from the request). Values for 'dim1' and 'dim2' 
     361            have to be SQLAlchemy expressions, either literal text (text(..)) or a select query. 
     362             
     363    Note that 'tol' or 'dim1'/'dim2' only have to be set when the input geometry from the request  
     364    uses a different CRS than the geometry column! 
     365     
     366     
     367    SDO_WITHIN_DISTANCE: http://download.oracle.com/docs/cd/E11882_01/appdev.112/e11830/sdo_operat.htm#i77653 
     368    SDO_GEOM.WITHIN_DISTANCE: http://download.oracle.com/docs/cd/E11882_01/appdev.112/e11830/sdo_objgeom.htm#i856373 
     369    DIMINFO: http://download.oracle.com/docs/cd/E11882_01/appdev.112/e11830/sdo_objrelschema.htm#i1010905 
     370    TOLERANCE: http://download.oracle.com/docs/cd/E11882_01/appdev.112/e11830/sdo_intro.htm#i884589 
     371     
     372    """ 
     373    params = additional_params.get('params', '') 
     374     
     375    if isinstance(geom1, Column): 
     376        return (func.SDO_WITHIN_DISTANCE(geom1, geom2,  
     377                                     'distance=%s %s' % (distance, params)) == 'TRUE') 
     378    else: 
     379        dim1 = additional_params.get('dim1', None) 
     380        dim2 = additional_params.get('dim2', None) 
     381         
     382        if dim1 is not None and dim2 is not None: 
     383            return (func.SDO_GEOM.WITHIN_DISTANCE(geom1, dim1,  
     384                                             distance,  
     385                                             geom2, dim2,  
     386                                             params) == 'TRUE') 
     387        else: 
     388            tol = additional_params.get('tol', None) 
     389             
     390            if tol is not None: 
     391                return (func.SDO_GEOM.WITHIN_DISTANCE(geom1,  
     392                                             distance,  
     393                                             geom2, 
     394                                             tol,  
     395                                             params) == 'TRUE') 
     396            else: 
     397                raise Exception('No dimension information ("dim1" and "dim2") or '\ 
     398                                'tolerance value ("tol") specified for calling '\ 
     399                                'SDO_GEOM.WITHIN_DISTANCE on Oracle, which is '\ 
     400                                'required when reprojecting.') 
  • framework/server/trunk/mapfish/templates/model.py_tmpl

    r1589 r3569  
    1 from sqlalchemy import Column, Table, types 
    2 from sqlalchemy.orm import mapper 
     1from sqlalchemy import Column, types 
    32 
    4 from mapfish.sqlalchemygeom import Geometry 
     3from geoalchemy import GeometryColumn, {{geomType}} 
     4 
    55from mapfish.sqlalchemygeom import GeometryTableMixIn 
     6from {{basePkg}}.model.meta import engine, Base 
    67 
    7 from {{basePkg}}.model.meta import metadata, engine 
    8  
    9 {{modelTabObj}} = Table( 
    10     '{{table}}', metadata, 
    11     Column('{{geomColName}}', Geometry({{epsg}})), 
     8class {{modelClass}}(Base, GeometryTableMixIn): 
     9    __tablename__ = '{{table}}' 
     10    __table_args__ = { 
    1211    {{if schema is not None}} 
    13     schema='{{schema}}', 
     12        "schema": '{{schema}}', 
    1413    {{endif}} 
    15     autoload=True, autoload_with=engine) 
    16  
    17 class {{modelClass}}(GeometryTableMixIn): 
    18     # for GeometryTableMixIn to do its job the __table__ property 
    19     # must be set here 
    20     __table__ = {{modelTabObj}} 
    21  
    22 mapper({{modelClass}}, {{modelTabObj}}) 
     14        "autoload": True, 
     15        "autoload_with": engine 
     16    } 
     17    {{geomColName}} = GeometryColumn({{geomType}}(dimension=2, srid={{epsg}})) 
  • framework/server/trunk/mapfish/templates/project/layers.ini_tmpl

    r1589 r3569  
    88# table*: the name of the table in the database 
    99# epsg*: the geometry column's EPSG code 
    10 # geomcolumn*: the name of the table's geometry colum 
     10# geomcolumn*: the name of the table's geometry column 
     11# geomtype: the geometry type of the geometry column (default: Geometry) 
    1112# schema: the name of the schema hosting the table 
    1213 
     
    1920#epsg=4326 
    2021#geomcolumn=the_geom 
     22#geomtype=Point 
    2123#schema=my_schema 
  • framework/server/trunk/mapfish/tests/test_filter.py

    r2729 r3569  
    22    filters are correct. These tests are SQLAlchemy integration tests. """ 
    33 
     4import unittest 
    45from nose import with_setup 
    5  
    6 from sqlalchemy import MetaData, Table, Column 
     6from nose.tools import eq_, ok_ 
     7 
     8from sqlalchemy import MetaData, Table, Column, create_engine 
    79from sqlalchemy.types import Integer, Unicode 
    810from sqlalchemy.orm import mapper 
    911from sqlalchemy.sql import func, and_ 
    10  
    11 from shapely import wkt 
     12from sqlalchemy.ext.declarative import declarative_base 
     13 
     14from shapely import wkt, wkb 
    1215from shapely.geometry.polygon import Polygon 
    1316 
    1417from geojson import dumps 
    1518 
    16 from mapfish.sqlalchemygeom import Geometry, GeometryTableMixIn 
     19from geoalchemy import GeometryColumn, Geometry 
     20 
     21from mapfish.sqlalchemygeom import GeometryTableMixIn 
    1722from mapfish.lib.filters import spatial, comparison, featureid, logical 
    1823 
    1924# 
    2025# Setup 
    21 #  
    22  
    23 table = Table("table", MetaData(), 
    24     Column("id", Integer, primary_key=True), 
    25     Column("text", Unicode), 
    26     Column("geom", Geometry(4326)) 
    27 ) 
    28  
    29 class MappedClass(GeometryTableMixIn): 
    30     __table__ = table 
    31  
    32 mapper(MappedClass, table) 
    33  
    3426# 
    35 # Test Spatial 
    36 #  
    37  
    38 def test_spatial_box(): 
    39     # with epsg undefined 
    40     filter = spatial.Spatial( 
    41         spatial.Spatial.BOX, 
    42         MappedClass.geometry_column(), 
    43         box=[-180, -90, 180, 90], 
    44         tolerance=1 
    45     ) 
    46     filter = filter.to_sql_expr() 
    47     params = filter.compile().params 
    48     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && "table".geom) AND distance("table".geom, geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    49     assert wkt.loads(params["geomfromtext_1"]).equals(wkt.loads('POLYGON ((-180 -90, -180 90, 180 90, 180 -90, -180 -90))')) 
    50     assert params["geomfromtext_2"] == 4326 
    51     assert params["expand_1"] == 1 
    52     assert params["distance_1"] == 1 
    53  
    54     # with epsg defined 
    55     filter = spatial.Spatial( 
    56         spatial.Spatial.BOX, 
    57         MappedClass.geometry_column(), 
    58         box=[-180, -90, 180, 90], 
    59         tolerance=1, 
    60         epsg=900913 
    61     ) 
    62     filter = filter.to_sql_expr() 
    63     params = filter.compile().params 
    64     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && transform("table".geom, :transform_1)) AND distance(transform("table".geom, :transform_1), geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    65     assert wkt.loads(params["geomfromtext_1"]).equals(wkt.loads('POLYGON ((-180 -90, -180 90, 180 90, 180 -90, -180 -90))')) 
    66     assert params["geomfromtext_2"] == 900913 
    67     assert params["expand_1"] == 1 
    68     assert params["transform_1"] == 900913 
    69     assert params["distance_1"] == 1 
    70  
    71 def test_spatial_within(): 
    72     # with epsg undefined 
    73     filter = spatial.Spatial( 
    74         spatial.Spatial.WITHIN, 
    75         MappedClass.geometry_column(), 
    76         lon=40, lat=5, tolerance=1 
    77     ) 
    78     filter = filter.to_sql_expr() 
    79     params = filter.compile().params 
    80     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && "table".geom) AND distance("table".geom, geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    81     assert wkt.loads(params["geomfromtext_1"]).equals(wkt.loads('POINT (40 5)')) 
    82     assert params["geomfromtext_2"] == 4326 
    83     assert params["expand_1"] == 1 
    84     assert params["distance_1"] == 1 
    85   
    86     # with epsg defined 
    87     filter = spatial.Spatial( 
    88         spatial.Spatial.WITHIN, 
    89         MappedClass.geometry_column(), 
    90         lon=40, lat=5, tolerance=1, epsg=900913  
    91     ) 
    92     filter = filter.to_sql_expr() 
    93     params = filter.compile().params 
    94     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && transform("table".geom, :transform_1)) AND distance(transform("table".geom, :transform_1), geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    95     assert wkt.loads(params["geomfromtext_1"]).equals(wkt.loads('POINT (40 5)')) 
    96     assert params["geomfromtext_2"] == 900913 
    97     assert params["expand_1"] == 1 
    98     assert params["transform_1"] == 900913 
    99     assert params["distance_1"] == 1 
    100  
    101 def test_spatial_geometry(): 
    102     poly = Polygon(((1, 2), (1, 3), (2, 3), (2, 2), (1, 2))) 
    103  
    104     # with epsg undefined 
    105     filter = spatial.Spatial( 
    106         spatial.Spatial.GEOMETRY, 
    107         MappedClass.geometry_column(), 
    108         geometry=dumps(poly), 
    109         tolerance=1 
    110     ) 
    111     filter = filter.to_sql_expr() 
    112     params = filter.compile().params 
    113     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && "table".geom) AND distance("table".geom, geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    114     assert wkt.loads(params["geomfromtext_1"]).equals(poly) 
    115     assert params["geomfromtext_2"] == 4326 
    116     assert params["expand_1"] == 1 
    117     assert params["distance_1"] == 1 
    118  
    119     # with epsg defined 
    120     filter = spatial.Spatial( 
    121         spatial.Spatial.GEOMETRY, 
    122         MappedClass.geometry_column(), 
    123         geometry=dumps(poly), 
    124         tolerance=1, 
    125         epsg=900913 
    126     ) 
    127     filter = filter.to_sql_expr() 
    128     params = filter.compile().params 
    129     assert str(filter) == '(expand(geomfromtext(:geomfromtext_1, :geomfromtext_2), :expand_1) && transform("table".geom, :transform_1)) AND distance(transform("table".geom, :transform_1), geomfromtext(:geomfromtext_1, :geomfromtext_2)) <= :distance_1' 
    130     assert wkt.loads(params["geomfromtext_1"]).equals(poly) 
    131     assert params["geomfromtext_2"] == 900913 
    132     assert params["expand_1"] == 1 
    133     assert params["transform_1"] == 900913 
    134     assert params["distance_1"] == 1 
    135  
    136 # 
    137 # Test Comparison 
    138 # 
    139  
    140 def test_comparison_equalto(): 
    141     filter = comparison.Comparison( 
    142         comparison.Comparison.EQUAL_TO, 
    143         MappedClass.id, 
    144         value=1 
    145     ) 
    146     filter = filter.to_sql_expr() 
    147     params = filter.compile().params 
    148     assert str(filter) == '"table".id = :id_1' 
    149     assert params["id_1"] == 1 
    150  
    151 def test_comparison_notequalto(): 
    152     filter = comparison.Comparison( 
    153         comparison.Comparison.NOT_EQUAL_TO, 
    154         MappedClass.id, 
    155         value=1 
    156     ) 
    157     filter = filter.to_sql_expr() 
    158     params = filter.compile().params 
    159     assert str(filter) == '"table".id != :id_1' 
    160     assert params["id_1"] == 1 
    161  
    162 def test_comparison_lowerthan(): 
    163     filter = comparison.Comparison( 
    164         comparison.Comparison.LOWER_THAN, 
    165         MappedClass.id, 
    166         value=1 
    167     ) 
    168     filter = filter.to_sql_expr() 
    169     params = filter.compile().params 
    170     assert str(filter) == '"table".id < :id_1' 
    171     assert params["id_1"] == 1 
    172  
    173 def test_comparison_lowerthanorequalto(): 
    174     filter = comparison.Comparison( 
    175         comparison.Comparison.LOWER_THAN_OR_EQUAL_TO, 
    176         MappedClass.id, 
    177         value=1 
    178     ) 
    179     filter = filter.to_sql_expr() 
    180     params = filter.compile().params 
    181     assert str(filter) == '"table".id <= :id_1' 
    182     assert params["id_1"] == 1 
    183  
    184 def test_comparison_greaterthan(): 
    185     filter = comparison.Comparison( 
    186         comparison.Comparison.GREATER_THAN, 
    187         MappedClass.id, 
    188         value=1 
    189     ) 
    190     filter = filter.to_sql_expr() 
    191     params = filter.compile().params 
    192     assert str(filter) == '"table".id > :id_1' 
    193     assert params["id_1"] == 1 
    194  
    195 def test_comparison_greaterthanorequalto(): 
    196     filter = comparison.Comparison( 
    197         comparison.Comparison.GREATER_THAN_OR_EQUAL_TO, 
    198         MappedClass.id, 
    199         value=1 
    200     ) 
    201     filter = filter.to_sql_expr() 
    202     params = filter.compile().params 
    203     assert str(filter) == '"table".id >= :id_1' 
    204     assert params["id_1"] == 1 
    205  
    206 def test_comparison_between(): 
    207     filter = comparison.Comparison( 
    208         comparison.Comparison.BETWEEN, 
    209         MappedClass.id, 
    210         lower_bound=1, upper_bound=2 
    211     ) 
    212     filter = filter.to_sql_expr() 
    213     params = filter.compile().params 
    214     assert str(filter) == '"table".id <= :id_1 AND "table".id >= :id_2' 
    215     assert params["id_1"] == 2 
    216     assert params["id_2"] == 1 
    217  
    218 def test_comparison_like(): 
    219     filter = comparison.Comparison( 
    220         comparison.Comparison.LIKE, 
    221         MappedClass.text, 
    222         value="foo" 
    223     ) 
    224     filter = filter.to_sql_expr() 
    225     params = filter.compile().params 
    226     assert str(filter) == '"table".text LIKE :text_1' 
    227     assert params["text_1"] == "foo" 
    228  
    229 def test_comparison_ilike(): 
    230     filter = comparison.Comparison( 
    231         comparison.Comparison.ILIKE, 
    232         MappedClass.text, 
    233         value="foo" 
    234     ) 
    235     filter = filter.to_sql_expr() 
    236     params = filter.compile().params 
    237     assert str(filter) == 'lower("table".text) LIKE lower(:text_1)' 
    238     assert params["text_1"] == "foo" 
    239  
    240 # 
    241 # Test FeatureId 
    242 # 
    243  
    244 def test_featureid(): 
    245     filter = featureid.FeatureId( 
    246         MappedClass.id, 
    247         1 
    248     ) 
    249     filter = filter.to_sql_expr() 
    250     params = filter.compile().params 
    251     assert str(filter) == '"table".id = :id_1' 
    252     assert params["id_1"] == 1 
    253  
    254 # 
    255 # Test Logical 
    256 # 
     27 
     28# create a dummy engine, no connection is established, we only need the database dialect   
     29engine = create_engine('postgresql://user:user@no_connection/no_db', echo=True) 
     30Base = declarative_base(metadata=MetaData()) 
     31 
     32class MappedClass(Base, GeometryTableMixIn): 
     33    __tablename__ = "table" 
     34    id = Column(Integer, primary_key=True) 
     35    text = Column(Unicode) 
     36    geom = GeometryColumn(Geometry(dimension=2, srid=4326)) 
     37 
     38 
     39def _compiled_to_string(compiled_filter): 
     40    """Helper method which converts a compiled SQL expression 
     41    into a string. 
     42    """ 
     43    return unicode(compiled_filter).encode('ascii', 'backslashreplace') 
     44 
     45 
     46class Test(unittest.TestCase): 
     47     
     48    # 
     49    # Test Spatial 
     50    #  
     51 
     52    def test_spatial_box(self): 
     53        # with epsg undefined 
     54        filter = spatial.Spatial( 
     55            spatial.Spatial.BOX, 
     56            MappedClass.geometry_column(), 
     57            box=[-180, -90, 180, 90], 
     58            tolerance=1 
     59        ) 
     60        compiled_filter = filter.to_sql_expr().compile(engine) 
     61        params = compiled_filter.params 
     62        filter_str = _compiled_to_string(compiled_filter) 
     63        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && "table".geom) AND (ST_Expand("table".geom, %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance("table".geom, GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     64        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(wkt.loads('POLYGON ((-180 -90, -180 90, 180 90, 180 -90, -180 -90))')) 
     65        assert params["GeomFromWKB_2"] == 4326 
     66        assert params["ST_Expand_1"] == 1 
     67        assert params["ST_Distance_1"] == 1 
     68     
     69        # with epsg defined 
     70        filter = spatial.Spatial( 
     71            spatial.Spatial.BOX, 
     72            MappedClass.geometry_column(), 
     73            box=[-180, -90, 180, 90], 
     74            tolerance=1, 
     75            epsg=900913 
     76        ) 
     77        compiled_filter = filter.to_sql_expr().compile(engine) 
     78        params = compiled_filter.params 
     79        filter_str = _compiled_to_string(compiled_filter) 
     80        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && ST_Transform("table".geom, %(param_1)s)) AND (ST_Expand(ST_Transform("table".geom, %(param_2)s), %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance(ST_Transform("table".geom, %(param_3)s), GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     81        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(wkt.loads('POLYGON ((-180 -90, -180 90, 180 90, 180 -90, -180 -90))')) 
     82        assert params["GeomFromWKB_2"] == 900913 
     83        assert params["ST_Expand_1"] == 1 
     84        assert params["param_1"] == 900913 
     85        assert params["ST_Distance_1"] == 1 
     86     
     87    def test_spatial_within(self): 
     88        # with epsg undefined 
     89        filter = spatial.Spatial( 
     90            spatial.Spatial.WITHIN, 
     91            MappedClass.geometry_column(), 
     92            lon=40, lat=5, tolerance=1 
     93        ) 
     94        compiled_filter = filter.to_sql_expr().compile(engine) 
     95        params = compiled_filter.params 
     96        filter_str = _compiled_to_string(compiled_filter) 
     97        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && "table".geom) AND (ST_Expand("table".geom, %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance("table".geom, GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     98        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(wkt.loads('POINT (40 5)')) 
     99        assert params["GeomFromWKB_2"] == 4326 
     100        assert params["ST_Expand_1"] == 1 
     101        assert params["ST_Distance_1"] == 1 
    257102      
    258 def test_logical_not(): 
    259     filter = logical.Logical( 
    260         logical.Logical.NOT, 
    261         filters=[ 
    262             comparison.Comparison( 
    263                 comparison.Comparison.EQUAL_TO, 
    264                 MappedClass.id, 
    265                 value=1 
    266             ) 
    267         ] 
    268     ) 
    269     filter = filter.to_sql_expr() 
    270     params = filter.compile().params 
    271     assert str(filter) == '"table".id != :id_1' 
    272     assert params["id_1"] == 1 
    273  
    274     filter =  logical.Logical( 
    275         logical.Logical.NOT, 
    276         filters=[ 
    277             comparison.Comparison( 
    278                 comparison.Comparison.LIKE, 
    279                 MappedClass.text, 
    280                 value="foo" 
    281             ) 
    282         ] 
    283     ) 
    284     filter = filter.to_sql_expr() 
    285     params = filter.compile().params 
    286     assert str(filter) == '"table".text NOT LIKE :text_1' 
    287     assert params["text_1"] == "foo" 
    288  
    289 def test_logical_and(): 
    290     filter = logical.Logical( 
    291         logical.Logical.AND, 
    292         filters=[ 
    293             comparison.Comparison( 
    294                 comparison.Comparison.EQUAL_TO, 
    295                 MappedClass.id, 
    296                 value=1 
    297             ), 
    298             comparison.Comparison( 
    299                 comparison.Comparison.LIKE, 
    300                 MappedClass.text, 
    301                 value="foo" 
    302             ) 
    303         ] 
    304     ) 
    305     filter = filter.to_sql_expr() 
    306     params = filter.compile().params 
    307     assert str(filter) == '"table".id = :id_1 AND "table".text LIKE :text_1' 
    308     assert params["id_1"] == 1 
    309     assert params["text_1"] == "foo" 
    310  
    311 def test_logical_or(): 
    312     filter = logical.Logical( 
    313         logical.Logical.OR, 
    314         filters=[ 
    315             comparison.Comparison( 
    316                 comparison.Comparison.EQUAL_TO, 
    317                 MappedClass.id, 
    318                 value=1 
    319             ), 
    320             comparison.Comparison( 
    321                 comparison.Comparison.LIKE, 
    322                 MappedClass.text, 
    323                 value="foo" 
    324             ) 
    325         ] 
    326     ) 
    327     filter = filter.to_sql_expr() 
    328     params = filter.compile().params 
    329     assert str(filter) == '"table".id = :id_1 OR "table".text LIKE :text_1' 
    330     assert params["id_1"] == 1 
    331     assert params["text_1"] == "foo" 
     103        # with epsg defined 
     104        filter = spatial.Spatial( 
     105            spatial.Spatial.WITHIN, 
     106            MappedClass.geometry_column(), 
     107            lon=40, lat=5, tolerance=1, epsg=900913  
     108        ) 
     109        compiled_filter = filter.to_sql_expr().compile(engine) 
     110        params = compiled_filter.params 
     111        filter_str = _compiled_to_string(compiled_filter) 
     112        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && ST_Transform("table".geom, %(param_1)s)) AND (ST_Expand(ST_Transform("table".geom, %(param_2)s), %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance(ST_Transform("table".geom, %(param_3)s), GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     113        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(wkt.loads('POINT (40 5)')) 
     114        assert params["GeomFromWKB_2"] == 900913 
     115        assert params["ST_Expand_1"] == 1 
     116        assert params["param_1"] == 900913 
     117        assert params["ST_Distance_1"] == 1 
     118     
     119    def test_spatial_geometry(self): 
     120        poly = Polygon(((1, 2), (1, 3), (2, 3), (2, 2), (1, 2))) 
     121     
     122        # with epsg undefined 
     123        filter = spatial.Spatial( 
     124            spatial.Spatial.GEOMETRY, 
     125            MappedClass.geometry_column(), 
     126            geometry=dumps(poly), 
     127            tolerance=1 
     128        ) 
     129        compiled_filter = filter.to_sql_expr().compile(engine) 
     130        params = compiled_filter.params 
     131        filter_str = _compiled_to_string(compiled_filter) 
     132        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && "table".geom) AND (ST_Expand("table".geom, %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance("table".geom, GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     133        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(poly) 
     134        assert params["GeomFromWKB_2"] == 4326 
     135        assert params["ST_Expand_1"] == 1 
     136        assert params["ST_Distance_1"] == 1 
     137     
     138        # with epsg defined 
     139        filter = spatial.Spatial( 
     140            spatial.Spatial.GEOMETRY, 
     141            MappedClass.geometry_column(), 
     142            geometry=dumps(poly), 
     143            tolerance=1, 
     144            epsg=900913 
     145        ) 
     146        compiled_filter = filter.to_sql_expr().compile(engine) 
     147        params = compiled_filter.params 
     148        filter_str = _compiled_to_string(compiled_filter) 
     149        eq_(filter_str, '(ST_Expand(GeomFromWKB(%(GeomFromWKB_1)s, %(GeomFromWKB_2)s), %(ST_Expand_1)s) && ST_Transform("table".geom, %(param_1)s)) AND (ST_Expand(ST_Transform("table".geom, %(param_2)s), %(ST_Expand_2)s) && GeomFromWKB(%(GeomFromWKB_3)s, %(GeomFromWKB_4)s)) AND ST_Distance(ST_Transform("table".geom, %(param_3)s), GeomFromWKB(%(GeomFromWKB_5)s, %(GeomFromWKB_6)s)) <= %(ST_Distance_1)s') 
     150        assert wkb.loads(str(params["GeomFromWKB_1"])).equals(poly) 
     151        assert params["GeomFromWKB_2"] == 900913 
     152        assert params["ST_Expand_1"] == 1 
     153        assert params["param_1"] == 900913 
     154        assert params["ST_Distance_1"] == 1 
     155     
     156    # 
     157    # Test Comparison 
     158    # 
     159     
     160    def test_comparison_equalto(self): 
     161        filter = comparison.Comparison( 
     162            comparison.Comparison.EQUAL_TO, 
     163            MappedClass.id, 
     164            value=1 
     165        ) 
     166        filter = filter.to_sql_expr() 
     167        params = filter.compile().params 
     168        assert str(filter) == '"table".id = :id_1' 
     169        assert params["id_1"] == 1 
     170     
     171    def test_comparison_notequalto(self): 
     172        filter = comparison.Comparison( 
     173            comparison.Comparison.NOT_EQUAL_TO, 
     174            MappedClass.id, 
     175            value=1 
     176        ) 
     177        filter = filter.to_sql_expr() 
     178        params = filter.compile().params 
     179        assert str(filter) == '"table".id != :id_1' 
     180        assert params["id_1"] == 1 
     181     
     182    def test_comparison_lowerthan(self): 
     183        filter = comparison.Comparison( 
     184            comparison.Comparison.LOWER_THAN, 
     185            MappedClass.id, 
     186            value=1 
     187        ) 
     188        filter = filter.to_sql_expr() 
     189        params = filter.compile().params 
     190        assert str(filter) == '"table".id < :id_1' 
     191        assert params["id_1"] == 1 
     192     
     193    def test_comparison_lowerthanorequalto(self): 
     194        filter = comparison.Comparison( 
     195            comparison.Comparison.LOWER_THAN_OR_EQUAL_TO, 
     196            MappedClass.id, 
     197            value=1 
     198        ) 
     199        filter = filter.to_sql_expr() 
     200        params = filter.compile().params 
     201        assert str(filter) == '"table".id <= :id_1' 
     202        assert params["id_1"] == 1 
     203     
     204    def test_comparison_greaterthan(self): 
     205        filter = comparison.Comparison( 
     206            comparison.Comparison.GREATER_THAN, 
     207            MappedClass.id, 
     208            value=1 
     209        ) 
     210        filter = filter.to_sql_expr() 
     211        params = filter.compile().params 
     212        assert str(filter) == '"table".id > :id_1' 
     213        assert params["id_1"] == 1 
     214     
     215    def test_comparison_greaterthanorequalto(self): 
     216        filter = comparison.Comparison( 
     217            comparison.Comparison.GREATER_THAN_OR_EQUAL_TO, 
     218            MappedClass.id, 
     219            value=1 
     220        ) 
     221        filter = filter.to_sql_expr() 
     222        params = filter.compile().params 
     223        assert str(filter) == '"table".id >= :id_1' 
     224        assert params["id_1"] == 1 
     225     
     226    def test_comparison_between(self): 
     227        filter = comparison.Comparison( 
     228            comparison.Comparison.BETWEEN, 
     229            MappedClass.id, 
     230            lower_bound=1, upper_bound=2 
     231        ) 
     232        filter = filter.to_sql_expr() 
     233        params = filter.compile().params 
     234        assert str(filter) == '"table".id <= :id_1 AND "table".id >= :id_2' 
     235        assert params["id_1"] == 2 
     236        assert params["id_2"] == 1 
     237     
     238    def test_comparison_like(self): 
     239        filter = comparison.Comparison( 
     240            comparison.Comparison.LIKE, 
     241            MappedClass.text, 
     242            value="foo" 
     243        ) 
     244        filter = filter.to_sql_expr() 
     245        params = filter.compile().params 
     246        assert str(filter) == '"table".text LIKE :text_1' 
     247        assert params["text_1"] == "foo" 
     248     
     249    def test_comparison_ilike(self): 
     250        filter = comparison.Comparison( 
     251            comparison.Comparison.ILIKE, 
     252            MappedClass.text, 
     253            value="foo" 
     254        ) 
     255        filter = filter.to_sql_expr() 
     256        params = filter.compile().params 
     257        assert str(filter) == 'lower("table".text) LIKE lower(:text_1)' 
     258        assert params["text_1"] == "foo" 
     259     
     260    # 
     261    # Test FeatureId 
     262    # 
     263     
     264    def test_featureid(self): 
     265        filter = featureid.FeatureId( 
     266            MappedClass.id, 
     267            1 
     268        ) 
     269        filter = filter.to_sql_expr() 
     270        params = filter.compile().params 
     271        assert str(filter) == '"table".id = :id_1' 
     272        assert params["id_1"] == 1 
     273     
     274    # 
     275    # Test Logical 
     276    # 
     277          
     278    def test_logical_not(self): 
     279        filter = logical.Logical( 
     280            logical.Logical.NOT, 
     281            filters=[ 
     282                comparison.Comparison( 
     283                    comparison.Comparison.EQUAL_TO, 
     284                    MappedClass.id, 
     285                    value=1 
     286                ) 
     287            ] 
     288        ) 
     289        filter = filter.to_sql_expr() 
     290        params = filter.compile().params 
     291        assert str(filter) == '"table".id != :id_1' 
     292        assert params["id_1"] == 1 
     293     
     294        filter =  logical.Logical( 
     295            logical.Logical.NOT, 
     296            filters=[ 
     297                comparison.Comparison( 
     298                    comparison.Comparison.LIKE, 
     299                    MappedClass.text, 
     300                    value="foo" 
     301                ) 
     302            ] 
     303        ) 
     304        filter = filter.to_sql_expr() 
     305        params = filter.compile().params 
     306        assert str(filter) == '"table".text NOT LIKE :text_1' 
     307        assert params["text_1"] == "foo" 
     308     
     309    def test_logical_and(self): 
     310        filter = logical.Logical( 
     311            logical.Logical.AND, 
     312            filters=[ 
     313                comparison.Comparison( 
     314                    comparison.Comparison.EQUAL_TO, 
     315                    MappedClass.id, 
     316                    value=1 
     317                ), 
     318                comparison.Comparison( 
     319                    comparison.Comparison.LIKE, 
     320                    MappedClass.text, 
     321                    value="foo" 
     322                ) 
     323            ] 
     324        ) 
     325        filter = filter.to_sql_expr() 
     326        params = filter.compile().params 
     327        assert str(filter) == '"table".id = :id_1 AND "table".text LIKE :text_1' 
     328        assert params["id_1"] == 1 
     329        assert params["text_1"] == "foo" 
     330     
     331    def test_logical_or(self): 
     332        filter = logical.Logical( 
     333            logical.Logical.OR, 
     334            filters=[ 
     335                comparison.Comparison( 
     336                    comparison.Comparison.EQUAL_TO, 
     337                    MappedClass.id, 
     338                    value=1 
     339                ), 
     340                comparison.Comparison( 
     341                    comparison.Comparison.LIKE, 
     342                    MappedClass.text, 
     343                    value="foo" 
     344                ) 
     345            ] 
     346        ) 
     347        filter = filter.to_sql_expr() 
     348        params = filter.compile().params 
     349        assert str(filter) == '"table".id = :id_1 OR "table".text LIKE :text_1' 
     350        assert params["id_1"] == 1 
     351        assert params["text_1"] == "foo" 
  • framework/server/trunk/mapfish/tests/test_protocol.py

    r2731 r3569  
    33from StringIO import StringIO 
    44 
    5 from nose import with_setup 
    6  
    7 from sqlalchemy import MetaData, Table, Column 
     5import unittest 
     6 
     7from sqlalchemy import MetaData, Column, create_engine 
    88from sqlalchemy import types 
    99from sqlalchemy import orm 
     10from sqlalchemy.ext.declarative import declarative_base 
    1011 
    1112from geojson import dumps, Feature 
     
    1314from shapely.geometry.polygon import Polygon 
    1415 
    15 from mapfish.sqlalchemygeom import Geometry, GeometryTableMixIn 
     16from geoalchemy import GeometryColumn, Geometry 
     17 
     18from mapfish.sqlalchemygeom import GeometryTableMixIn 
    1619from mapfish.lib.filters import logical, comparison, spatial 
    1720 
     
    2023#  
    2124 
    22 table = Table("table", MetaData(), 
    23     Column("id", types.Integer, primary_key=True), 
    24     Column("text", types.Unicode), 
    25     Column("geom", Geometry(4326)) 
    26 ) 
    27  
    28 class MappedClass(GeometryTableMixIn): 
    29     __table__ = table 
    30  
    31 orm.mapper(MappedClass, table) 
     25Base = declarative_base(metadata=MetaData()) 
     26 
     27class MappedClass(Base, GeometryTableMixIn): 
     28    __tablename__ = "table" 
     29    id = Column(types.Integer, primary_key=True) 
     30    text = Column(types.Unicode) 
     31    geom = GeometryColumn(Geometry(dimension=2, srid=4326)) 
    3232 
    3333class FakeRequest(object): 
     
    4646 
    4747# create a session in the same way it's done in a typical Pylons app 
    48 sm = orm.sessionmaker(autoflush=True, autocommit=False) 
     48engine = create_engine('postgresql://user:user@no_connection/no_db', echo=True) 
     49sm = orm.sessionmaker(autoflush=True, autocommit=False, bind=engine) 
    4950Session = orm.scoped_session(sm) 
    5051 
     52def query_to_str(query): 
     53    """Helper method which compiles a query using a database engine 
     54    """ 
     55    return unicode(query.statement.compile(engine)).encode('ascii', 'backslashreplace') 
    5156# 
    5257# Test 
    5358#  
    5459 
    55 from mapfish.lib.protocol import create_geom_filter 
    56 def test_create_geom_filter(): 
    57     request = FakeRequest( 
    58         {"box": "-45,-5,40,0", "tolerance": "1", "epsg": "900913"} 
    59     ) 
    60     filter = create_geom_filter(request, MappedClass) 
    61     assert isinstance(filter, spatial.Spatial) 
    62     assert filter.type == spatial.Spatial.BOX 
    63     assert filter.values["tolerance"] == 1 
    64     assert filter.values["epsg"] == 900913 
    65  
    66     request = FakeRequest( 
    67         {"bbox": "-45,-5,40,0", "tolerance": "1", "epsg": "900913"} 
    68     ) 
    69     filter = create_geom_filter(request, MappedClass) 
    70     assert isinstance(filter, spatial.Spatial) 
    71     assert filter.type == spatial.Spatial.BOX 
    72     assert filter.values["tolerance"] == 1 
    73     assert filter.values["epsg"] == 900913 
    74  
    75     request = FakeRequest( 
    76         {"lon": "-45", "lat": "5", "tolerance": "1", "epsg": "900913"} 
    77     ) 
    78     filter = create_geom_filter(request, MappedClass) 
    79     assert isinstance(filter, spatial.Spatial) 
    80     assert filter.type == spatial.Spatial.WITHIN 
    81     assert filter.values["tolerance"] == 1 
    82     assert filter.values["epsg"] == 900913 
    83  
    84     poly = Polygon(((1, 2), (1, 3), (2, 3), (2, 2), (1, 2))) 
    85     request = FakeRequest( 
    86         {"geometry": dumps(poly), "tolerance": "1", "epsg": "900913"} 
    87     ) 
    88     filter = create_geom_filter(request, MappedClass) 
    89     assert isinstance(filter, spatial.Spatial) 
    90     assert filter.type == spatial.Spatial.GEOMETRY 
    91     assert filter.values["tolerance"] == 1 
    92     assert filter.values["epsg"] == 900913 
    93  
    94     request = FakeRequest({}) 
    95     filter = create_geom_filter(request, MappedClass) 
    96     assert filter is None 
    97  
    98 from mapfish.lib.protocol import create_attr_filter 
    99 def test_create_attr_filter(): 
    100     request = FakeRequest( 
    101         {"queryable": "id", "id__eq": "1"} 
    102     ) 
    103     filter = create_attr_filter(request, MappedClass) 
    104     assert isinstance(filter, comparison.Comparison) 
    105     assert filter.type == comparison.Comparison.EQUAL_TO 
    106     assert filter.values["value"] == "1" 
    107  
    108     request = FakeRequest( 
    109         {"queryable": "id", "id__lt": "1"} 
    110     ) 
    111     filter = create_attr_filter(request, MappedClass) 
    112     assert isinstance(filter, comparison.Comparison) 
    113     assert filter.type == comparison.Comparison.LOWER_THAN 
    114     assert filter.values["value"] == "1" 
    115  
    116     request = FakeRequest( 
    117         {"queryable": "id", "id__lte": "1"} 
    118     ) 
    119     filter = create_attr_filter(request, MappedClass) 
    120     assert isinstance(filter, comparison.Comparison) 
    121     assert filter.type == comparison.Comparison.LOWER_THAN_OR_EQUAL_TO 
    122     assert filter.values["value"] == "1" 
    123  
    124     request = FakeRequest( 
    125         {"queryable": "id", "id__gt": "1"} 
    126     ) 
    127     filter = create_attr_filter(request, MappedClass) 
    128     assert isinstance(filter, comparison.Comparison) 
    129     assert filter.type == comparison.Comparison.GREATER_THAN 
    130     assert filter.values["value"] == "1" 
    131  
    132     request = FakeRequest( 
    133         {"queryable": "id", "id__gte": "1"} 
    134     ) 
    135     filter = create_attr_filter(request, MappedClass) 
    136     assert isinstance(filter, comparison.Comparison) 
    137     assert filter.type == comparison.Comparison.GREATER_THAN_OR_EQUAL_TO 
    138     assert filter.values["value"] == "1" 
    139  
    140     request = FakeRequest( 
    141         {"queryable": "text", "text__like": "foo"} 
    142     ) 
    143     filter = create_attr_filter(request, MappedClass) 
    144     assert isinstance(filter, comparison.Comparison) 
    145     assert filter.type == comparison.Comparison.LIKE 
    146     assert filter.values["value"] == "foo" 
    147  
    148     request = FakeRequest( 
    149         {"queryable": "text", "text__ilike": "foo"} 
    150     ) 
    151     filter = create_attr_filter(request, MappedClass) 
    152     assert isinstance(filter, comparison.Comparison) 
    153     assert filter.type == comparison.Comparison.ILIKE 
    154     assert filter.values["value"] == "foo" 
    155  
    156     request = FakeRequest( 
    157         {"queryable": "text,id", "text__ilike": "foo", "id__eq": "1"} 
    158     ) 
    159     filter = create_attr_filter(request, MappedClass) 
    160     assert isinstance(filter, logical.Logical) 
    161     assert filter.type == logical.Logical.AND 
    162     assert len(filter.filters) == 2 
    163  
    164     request = FakeRequest( 
    165         {"text__ilike": "foo", "id__eq": "1"} 
    166     ) 
    167     filter = create_attr_filter(request, MappedClass) 
    168     assert filter is None 
    169  
    170 from mapfish.lib.protocol import asbool 
    171 def test_asbool(): 
    172     assert asbool(0) == False 
    173     assert asbool(1) == True 
    174     assert asbool(2) == True 
    175     assert asbool("0") == False 
    176     assert asbool("1") == True 
    177     assert asbool("false") == False 
    178     assert asbool("true") == True 
    179     assert asbool("False") == False 
    180     assert asbool("True") == True 
    181     assert asbool(u"0") == False 
    182     assert asbool(u"1") == True 
    183     assert asbool(u"false") == False 
    184     assert asbool(u"true") == True 
    185     assert asbool(u"False") == False 
    186     assert asbool(u"True") == True 
    187  
    188 from mapfish.lib.protocol import Protocol 
    189 def test_protocol_query(): 
    190     proto = Protocol(Session, MappedClass) 
    191  
    192     request = FakeRequest({}) 
    193     query = proto._query(request, execute=False) 
    194     assert "SELECT" in str(query) 
    195  
    196     request = FakeRequest({"queryable": "id", "id__eq": "1"}) 
    197     query = proto._query(request, execute=False) 
    198     assert "WHERE" in str(query) 
    199  
    200     request = FakeRequest({"queryable": "id", "id__eq": "1"}) 
    201     filter = create_attr_filter(request, MappedClass) 
    202     query = proto._query(FakeRequest({}), filter=filter, execute=False) 
    203     assert "WHERE" in str(query) 
    204  
    205     request = FakeRequest({"limit": "2"}) 
    206     query = proto._query(request, execute=False) 
    207     assert "LIMIT 2" in str(query) 
    208  
    209     request = FakeRequest({"maxfeatures": "2"}) 
    210     query = proto._query(request, execute=False) 
    211     assert "LIMIT 2" in str(query) 
    212  
    213     request = FakeRequest({"limit": "2", "offset": "10"}) 
    214     query = proto._query(request, execute=False) 
    215     assert "OFFSET 10" in str(query) 
    216  
    217     request = FakeRequest({"order_by": "text"}) 
    218     query = proto._query(request, execute=False) 
    219     assert "ORDER BY" in str(query) 
    220     assert "ASC" in str(query) 
    221  
    222     request = FakeRequest({"sort": "text"}) 
    223     query = proto._query(request, execute=False) 
    224     assert "ORDER BY" in str(query) 
    225     assert "ASC" in str(query) 
    226  
    227     request = FakeRequest({"order_by": "text", "dir": "DESC"}) 
    228     query = proto._query(request, execute=False) 
    229     assert "ORDER BY" in str(query) 
    230     assert "DESC" in str(query) 
    231  
    232 def test_protocol_create(): 
    233     proto = Protocol(Session, MappedClass) 
    234     request = FakeRequest({}) 
    235     request.body = '{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {"text": "foo"}, "geometry": {"type": "Point", "coordinates": [45, 5]}}, {"type": "Feature", "properties": {"text": "foo"}, "geometry": {"type": "Point", "coordinates": [45, 5]}}]}' 
    236     response = FakeResponse() 
    237     proto.create(request, response, execute=False) 
    238     assert response.status ==  201 
    239     assert len(Session.new) == 2 
    240     for obj in Session.new: 
    241         assert obj["text"] == "foo" 
    242         assert obj.geometry.x == 45 
    243         assert obj.geometry.y == 5 
    244     Session.rollback() 
     60class Test(unittest.TestCase): 
     61 
     62    def test_create_geom_filter(self): 
     63        from mapfish.lib.protocol import create_geom_filter 
     64        request = FakeRequest( 
     65            {"box": "-45,-5,40,0", "tolerance": "1", "epsg": "900913"} 
     66        ) 
     67        filter = create_geom_filter(request, MappedClass) 
     68        assert isinstance(filter, spatial.Spatial) 
     69        assert filter.type == spatial.Spatial.BOX 
     70        assert filter.values["tolerance"] == 1 
     71        assert filter.values["epsg"] == 900913 
     72     
     73        request = FakeRequest( 
     74            {"bbox": "-45,-5,40,0", "tolerance": "1", "epsg": "900913"} 
     75        ) 
     76        filter = create_geom_filter(request, MappedClass) 
     77        assert isinstance(filter, spatial.Spatial) 
     78        assert filter.type == spatial.Spatial.BOX 
     79        assert filter.values["tolerance"] == 1 
     80        assert filter.values["epsg"] == 900913 
     81     
     82        request = FakeRequest( 
     83            {"lon": "-45", "lat": "5", "tolerance": "1", "epsg": "900913"} 
     84        ) 
     85        filter = create_geom_filter(request, MappedClass) 
     86        assert isinstance(filter, spatial.Spatial) 
     87        assert filter.type == spatial.Spatial.WITHIN 
     88        assert filter.values["tolerance"] == 1 
     89        assert filter.values["epsg"] == 900913 
     90     
     91        poly = Polygon(((1, 2), (1, 3), (2, 3), (2, 2), (1, 2))) 
     92        request = FakeRequest( 
     93            {"geometry": dumps(poly), "tolerance": "1", "epsg": "900913"} 
     94        ) 
     95        filter = create_geom_filter(request, MappedClass) 
     96        assert isinstance(filter, spatial.Spatial) 
     97        assert filter.type == spatial.Spatial.GEOMETRY 
     98        assert filter.values["tolerance"] == 1 
     99        assert filter.values["epsg"] == 900913 
     100     
     101        request = FakeRequest({}) 
     102        filter = create_geom_filter(request, MappedClass) 
     103        assert filter is None 
     104         
     105        request = FakeRequest({"lon": "-45", "lat": "5"}) 
     106        filter = create_geom_filter(request, MappedClass, additional_params='unit=KM') 
     107        assert filter.additional_params is not None 
     108     
     109     
     110    def test_create_attr_filter(self): 
     111        from mapfish.lib.protocol import create_attr_filter 
     112        request = FakeRequest( 
     113            {"queryable": "id", "id__eq": "1"} 
     114        ) 
     115        filter = create_attr_filter(request, MappedClass) 
     116        assert isinstance(filter, comparison.Comparison) 
     117        assert filter.type == comparison.Comparison.EQUAL_TO 
     118        assert filter.values["value"] == "1" 
     119     
     120        request = FakeRequest( 
     121            {"queryable": "id", "id__lt": "1"} 
     122        ) 
     123        filter = create_attr_filter(request, MappedClass) 
     124        assert isinstance(filter, comparison.Comparison) 
     125        assert filter.type == comparison.Comparison.LOWER_THAN 
     126        assert filter.values["value"] == "1" 
     127     
     128        request = FakeRequest( 
     129            {"queryable": "id", "id__lte": "1"} 
     130        ) 
     131        filter = create_attr_filter(request, MappedClass) 
     132        assert isinstance(filter, comparison.Comparison) 
     133        assert filter.type == comparison.Comparison.LOWER_THAN_OR_EQUAL_TO 
     134        assert filter.values["value"] == "1" 
     135     
     136        request = FakeRequest( 
     137            {"queryable": "id", "id__gt": "1"} 
     138        ) 
     139        filter = create_attr_filter(request, MappedClass) 
     140        assert isinstance(filter, comparison.Comparison) 
     141        assert filter.type == comparison.Comparison.GREATER_THAN 
     142        assert filter.values["value"] == "1" 
     143     
     144        request = FakeRequest( 
     145            {"queryable": "id", "id__gte": "1"} 
     146        ) 
     147        filter = create_attr_filter(request, MappedClass) 
     148        assert isinstance(filter, comparison.Comparison) 
     149        assert filter.type == comparison.Comparison.GREATER_THAN_OR_EQUAL_TO 
     150        assert filter.values["value"] == "1" 
     151     
     152        request = FakeRequest( 
     153            {"queryable": "text", "text__like": "foo"} 
     154        ) 
     155        filter = create_attr_filter(request, MappedClass) 
     156        assert isinstance(filter, comparison.Comparison) 
     157        assert filter.type == comparison.Comparison.LIKE 
     158        assert filter.values["value"] == "foo" 
     159     
     160        request = FakeRequest( 
     161            {"queryable": "text", "text__ilike": "foo"} 
     162        ) 
     163        filter = create_attr_filter(request, MappedClass) 
     164        assert isinstance(filter, comparison.Comparison) 
     165        assert filter.type == comparison.Comparison.ILIKE 
     166        assert filter.values["value"] == "foo" 
     167     
     168        request = FakeRequest( 
     169            {"queryable": "text,id", "text__ilike": "foo", "id__eq": "1"} 
     170        ) 
     171        filter = create_attr_filter(request, MappedClass) 
     172        assert isinstance(filter, logical.Logical) 
     173        assert filter.type == logical.Logical.AND 
     174        assert len(filter.filters) == 2 
     175     
     176        request = FakeRequest( 
     177            {"text__ilike": "foo", "id__eq": "1"} 
     178        ) 
     179        filter = create_attr_filter(request, MappedClass) 
     180        assert filter is None 
     181     
     182     
     183    def test_asbool(self): 
     184        from mapfish.lib.protocol import asbool 
     185        assert asbool(0) == False 
     186        assert asbool(1) == True 
     187        assert asbool(2) == True 
     188        assert asbool("0") == False 
     189        assert asbool("1") == True 
     190        assert asbool("false") == False 
     191        assert asbool("true") == True 
     192        assert asbool("False") == False 
     193        assert asbool("True") == True 
     194        assert asbool(u"0") == False 
     195        assert asbool(u"1") == True 
     196        assert asbool(u"false") == False 
     197        assert asbool(u"true") == True 
     198        assert asbool(u"False") == False 
     199        assert asbool(u"True") == True 
     200     
     201    def test_protocol_query(self): 
     202        from mapfish.lib.protocol import Protocol, create_attr_filter 
     203        proto = Protocol(Session, MappedClass) 
     204     
     205        request = FakeRequest({}) 
     206        query = proto._query(request, execute=False) 
     207        stmt = query.statement 
     208        stmtm_str = stmt.compile(engine) 
     209        assert "SELECT" in query_to_str(query) 
     210     
     211        request = FakeRequest({"queryable": "id", "id__eq": "1"}) 
     212        query = proto._query(request, execute=False) 
     213        assert "WHERE" in query_to_str(query) 
     214     
     215        request = FakeRequest({"queryable": "id", "id__eq": "1"}) 
     216        filter = create_attr_filter(request, MappedClass) 
     217        query = proto._query(FakeRequest({}), filter=filter, execute=False) 
     218        assert "WHERE" in query_to_str(query) 
     219     
     220        request = FakeRequest({"limit": "2"}) 
     221        query = proto._query(request, execute=False) 
     222        assert "LIMIT 2" in query_to_str(query) 
     223     
     224        request = FakeRequest({"maxfeatures": "2"}) 
     225        query = proto._query(request, execute=False) 
     226        assert "LIMIT 2" in query_to_str(query) 
     227     
     228        request = FakeRequest({"limit": "2", "offset": "10"}) 
     229        query = proto._query(request, execute=False) 
     230        assert "OFFSET 10" in query_to_str(query) 
     231     
     232        request = FakeRequest({"order_by": "text"}) 
     233        query = proto._query(request, execute=False) 
     234        assert "ORDER BY" in query_to_str(query) 
     235        assert "ASC" in query_to_str(query) 
     236     
     237        request = FakeRequest({"sort": "text"}) 
     238        query = proto._query(request, execute=False) 
     239        assert "ORDER BY" in query_to_str(query) 
     240        assert "ASC" in query_to_str(query) 
     241     
     242        request = FakeRequest({"order_by": "text", "dir": "DESC"}) 
     243        query = proto._query(request, execute=False) 
     244        assert "ORDER BY" in query_to_str(query) 
     245        assert "DESC" in query_to_str(query) 
     246     
     247    def test_protocol_create(self): 
     248        from mapfish.lib.protocol import Protocol 
     249        proto = Protocol(Session, MappedClass) 
     250        request = FakeRequest({}) 
     251        request.body = '{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {"text": "foo"}, "geometry": {"type": "Point", "coordinates": [45, 5]}}, {"type": "Feature", "properties": {"text": "foo"}, "geometry": {"type": "Point", "coordinates": [45, 5]}}]}' 
     252        response = FakeResponse() 
     253        proto.create(request, response, execute=False) 
     254        assert response.status ==  201 
     255        assert len(Session.new) == 2 
     256        for obj in Session.new: 
     257            assert obj["text"] == "foo" 
     258            assert obj.geometry.shape.x == 45 
     259            assert obj.geometry.shape.y == 5 
     260        Session.rollback() 
  • framework/server/trunk/setup.py

    r3140 r3569  
    2727import sys 
    2828 
    29  
    30 requirements = ['SQLAlchemy>=0.5.0,<=0.5.99', 
     29requirements = ['SQLAlchemy>=0.6.1,<=0.6.99', 
    3130                'Pylons>=0.9.7,<=0.9.7.99', 
    32                 'geojson>=1.0,<=1.0.99'] 
     31                'geojson>=1.0,<=1.0.99', 
     32                'GeoAlchemy>=0.3,<=0.3.99'] 
    3333 
    3434# Shapely and Psychopg2 cannot be installed on Windows via python eggs 
     
    4343 
    4444setup(name                 = 'mapfish', 
    45       version              = '1.3dev', 
     45      version              = '2.0dev', 
    4646      license              = 'LGPLv3', 
    4747      install_requires     = requirements, 
     
    103103      -------------- 
    104104 
    105       MapFish 1.3dev described in this page is the current stable version. 
     105      MapFish 2.0dev described in this page is the current stable version. 
    106106 
    107107      Download and Installation