예제 #1
0
    def _proto_read(self, layer):
        """ Read features for the layer based on the self.request. """
        proto = self._get_protocol_for_layer(layer)
        if layer.public:
            return proto.read(self.request)
        user = self.request.user
        if user is None:
            raise HTTPForbidden()
        cls = proto.mapped_class
        geom_attr = proto.geom_attr
        ras = DBSession.query(RestrictionArea.area,
                              functions.srid(RestrictionArea.area))
        ras = ras.join(RestrictionArea.roles)
        ras = ras.join(RestrictionArea.layers)
        ras = ras.filter(Role.id == user.role.id)
        ras = ras.filter(Layer.id == layer.id)
        collect_ra = []
        use_srid = -1
        for ra, srid in ras.all():
            if ra is None:
                return proto.read(self.request)
            else:
                use_srid = srid
                collect_ra.append(wkb.loads(str(ra.geom_wkb)))
        if len(collect_ra) == 0:  # pragma: no cover
            raise HTTPForbidden()

        ra = cascaded_union(collect_ra)
        filter_ = and_(
            create_filter(self.request, cls, geom_attr),
            functions.gcontains(func.st_geomfromtext(ra.wkt, use_srid),
                                getattr(cls, geom_attr)))

        return proto.read(self.request, filter=filter_)
예제 #2
0
    def _proto_read(self, layer):
        """ Read features for the layer based on the self.request. """
        proto = self._get_protocol_for_layer(layer)
        if layer.public:
            return proto.read(self.request)
        user = self.request.user
        if user is None:
            raise HTTPForbidden()
        cls = proto.mapped_class
        geom_attr = proto.geom_attr
        ras = DBSession.query(RestrictionArea.area, functions.srid(RestrictionArea.area))
        ras = ras.join(RestrictionArea.roles)
        ras = ras.join(RestrictionArea.layers)
        ras = ras.filter(Role.id == user.role.id)
        ras = ras.filter(Layer.id == layer.id)
        collect_ra = []
        use_srid = -1
        for ra, srid in ras.all():
            if ra is None:
                return proto.read(self.request)
            else:
                use_srid = srid
                collect_ra.append(wkb.loads(str(ra.geom_wkb)))
        if len(collect_ra) == 0:  # pragma: no cover
            raise HTTPForbidden()

        ra = cascaded_union(collect_ra)
        filter_ = and_(
            create_filter(self.request, cls, geom_attr),
            functions.gcontains(
                func.st_geomfromtext(ra.wkt, use_srid),
                getattr(cls, geom_attr)
            )
        )

        return proto.read(self.request, filter=filter_)
예제 #3
0
    def __call__(self):
        tableinfo = TableInfo.from_layer(self.layer)
        tableinfo.setup_metadata(self.layer._tablename)
        table = tableinfo.table

        columns = [
            table.columns.id,
        ]
        where = []

        srsid = self.layer.srs_id if self._srs is None else self._srs.id

        geomcol = table.columns.geom
        geomexpr = func.st_transform(geomcol, srsid)

        if self._clip_by_box is not None:
            if _clipbybox2d_exists():
                clip = func.st_setsrid(
                    func.st_makeenvelope(*self._clip_by_box.bounds),
                    self._clip_by_box.srid)
                geomexpr = func.st_clipbybox2d(geomexpr, clip)
            else:
                clip = func.st_setsrid(
                    func.st_geomfromtext(self._clip_by_box.wkt),
                    self._clip_by_box.srid)
                geomexpr = func.st_intersection(geomexpr, clip)

        if self._simplify is not None:
            geomexpr = func.st_simplifypreservetopology(
                geomexpr, self._simplify)

        if self._geom:
            if self._single_part:

                class geom(ColumnElement):
                    def __init__(self, base):
                        self.base = base

                @compiles(geom)
                def compile(expr, compiler, **kw):
                    return "(%s).geom" % str(compiler.process(expr.base))

                columns.append(
                    func.st_asewkb(geom(func.st_dump(geomexpr))).label('geom'))
            else:
                columns.append(func.st_asewkb(geomexpr).label('geom'))

        if self._geom_len:
            columns.append(
                func.st_length(
                    func.geography(func.st_transform(geomexpr,
                                                     4326))).label('geom_len'))

        if self._box:
            columns.extend((
                func.st_xmin(geomexpr).label('box_left'),
                func.st_ymin(geomexpr).label('box_bottom'),
                func.st_xmax(geomexpr).label('box_right'),
                func.st_ymax(geomexpr).label('box_top'),
            ))

        selected_fields = []
        for f in tableinfo.fields:
            if not self._fields or f.keyname in self._fields:
                columns.append(table.columns[f.key].label(f.keyname))
                selected_fields.append(f)

        if self._filter_by:
            for k, v in six.iteritems(self._filter_by):
                if k == 'id':
                    where.append(table.columns.id == v)
                else:
                    where.append(table.columns[tableinfo[k].key] == v)

        if self._filter:
            token = []
            for k, o, v in self._filter:
                supported_operators = (
                    "eq",
                    "ge",
                    "gt",
                    "ilike",
                    "in",
                    "le",
                    "like",
                    "lt",
                    "ne",
                    "notin",
                    "startswith",
                )
                if o not in supported_operators:
                    raise ValueError(
                        "Invalid operator '%s'. Only %r are supported." %
                        (o, supported_operators))

                if v and o in ['in', 'notin']:
                    v = v.split(',')

                if o in [
                        "ilike",
                        "in",
                        "like",
                        "notin",
                        "startswith",
                ]:
                    o += "_op"

                op = getattr(db.sql.operators, o)
                if k == "id":
                    token.append(op(table.columns.id, v))
                else:
                    token.append(op(table.columns[tableinfo[k].key], v))

            where.append(db.and_(*token))

        if self._filter_sql:
            token = []
            for _filter_sql_item in self._filter_sql:
                if len(_filter_sql_item) == 3:
                    table_column, op, val = _filter_sql_item
                    if table_column == 'id':
                        token.append(op(table.columns.id, val))
                    else:
                        token.append(
                            op(table.columns[tableinfo[table_column].key],
                               val))
                elif len(_filter_sql_item) == 4:
                    table_column, op, val1, val2 = _filter_sql_item
                    token.append(
                        op(table.columns[tableinfo[table_column].key], val1,
                           val2))

            where.append(db.and_(*token))

        if self._like:
            token = []
            for f in tableinfo.fields:
                token.append(
                    cast(table.columns[f.key],
                         db.Unicode).ilike("%" + self._like + "%"))

            where.append(db.or_(*token))

        if self._intersects:
            intgeom = func.st_setsrid(
                func.st_geomfromtext(self._intersects.wkt),
                self._intersects.srid)
            where.append(
                func.st_intersects(
                    geomcol, func.st_transform(intgeom, self.layer.srs_id)))

        order_criterion = []
        if self._order_by:
            for order, colname in self._order_by:
                order_criterion.append(
                    dict(asc=db.asc, desc=db.desc)[order](
                        table.columns[tableinfo[colname].key]))
        order_criterion.append(table.columns.id)

        class QueryFeatureSet(FeatureSet):
            fields = selected_fields
            layer = self.layer

            _geom = self._geom
            _geom_len = self._geom_len
            _box = self._box
            _limit = self._limit
            _offset = self._offset

            def __iter__(self):
                query = sql.select(
                    columns,
                    whereclause=db.and_(*where),
                    limit=self._limit,
                    offset=self._offset,
                    order_by=order_criterion,
                )
                rows = DBSession.connection().execute(query)
                for row in rows:
                    fdict = dict(
                        (f.keyname, row[f.keyname]) for f in selected_fields)
                    if self._geom:
                        geom = geom_from_wkb(row['geom'].tobytes(
                        ) if six.PY3 else six.binary_type(row['geom']))
                    else:
                        geom = None

                    calculated = dict()
                    if self._geom_len:
                        calculated['geom_len'] = row['geom_len']

                    yield Feature(
                        layer=self.layer,
                        id=row.id,
                        fields=fdict,
                        geom=geom,
                        calculations=calculated,
                        box=box(row.box_left, row.box_bottom, row.box_right,
                                row.box_top) if self._box else None)

            @property
            def total_count(self):
                query = sql.select([
                    func.count(table.columns.id),
                ],
                                   whereclause=db.and_(*where))
                res = DBSession.connection().execute(query)
                for row in res:
                    return row[0]

        return QueryFeatureSet()
예제 #4
0
    def __call__(self):
        tab = self.layer._sa_table(True)

        idcol = tab.columns[self.layer.column_id]
        columns = [idcol.label('id')]
        where = []

        geomcol = tab.columns[self.layer.column_geom]

        srs = self.layer.srs if self._srs is None else self._srs

        if srs.id != self.layer.geometry_srid:
            geomexpr = func.st_transform(geomcol, srs.id)
        else:
            geomexpr = geomcol

        if self._geom:
            if self._geom_format == 'WKB':
                geomexpr = func.st_asbinary(geomexpr, 'NDR')
            else:
                geomexpr = func.st_astext(geomexpr)

            columns.append(geomexpr.label('geom'))

        fieldmap = []
        for idx, fld in enumerate(self.layer.fields, start=1):
            if self._fields is None or fld.keyname in self._fields:
                clabel = 'f%d' % idx
                columns.append(
                    getattr(tab.columns, fld.column_name).label(clabel))
                fieldmap.append((fld.keyname, clabel))

        if self._filter_by:
            for k, v in self._filter_by.items():
                if k == 'id':
                    where.append(idcol == v)
                else:
                    field = self.layer.field_by_keyname(k)
                    where.append(tab.columns[field.column_name] == v)

        if self._filter:
            token = []
            for k, o, v in self._filter:
                supported_operators = (
                    'eq',
                    'ne',
                    'isnull',
                    'ge',
                    'gt',
                    'le',
                    'lt',
                    'like',
                    'ilike',
                )
                if o not in supported_operators:
                    raise ValueError(
                        "Invalid operator '%s'. Only %r are supported." %
                        (o, supported_operators))

                if o == 'like':
                    o = 'like_op'
                elif o == 'ilike':
                    o = 'ilike_op'
                elif o == "isnull":
                    if v == 'yes':
                        o = 'is_'
                    elif v == 'no':
                        o = 'isnot'
                    else:
                        raise ValueError(
                            "Invalid value '%s' for operator '%s'." % (v, o))
                    v = db.sql.null()

                op = getattr(db.sql.operators, o)
                if k == 'id':
                    column = idcol
                else:
                    field = self.layer.field_by_keyname(k)
                    column = tab.columns[field.column_name]

                token.append(op(column, v))

            where.append(db.and_(True, *token))

        if self._like:
            token = []
            for fld in self.layer.fields:
                token.append(
                    db.sql.cast(tab.columns[fld.column_name],
                                db.Unicode).ilike('%' + self._like + '%'))

            where.append(db.or_(*token))

        if self._intersects:
            reproject = self._intersects.srid is not None \
                and self._intersects.srid != self.layer.geometry_srid
            int_srs = SRS.filter_by(id=self._intersects.srid).one() \
                if reproject else self.layer.srs

            int_geom = func.st_geomfromtext(self._intersects.wkt)
            if int_srs.is_geographic:
                # Prevent tolerance condition error
                bound_geom = func.st_makeenvelope(-180, -89.9, 180, 89.9)
                int_geom = func.st_intersection(bound_geom, int_geom)
            int_geom = func.st_setsrid(int_geom, int_srs.id)
            if reproject:
                int_geom = func.st_transform(int_geom,
                                             self.layer.geometry_srid)

            where.append(func.st_intersects(geomcol, int_geom))

        if self._box:
            columns.extend((
                func.st_xmin(geomexpr).label('box_left'),
                func.st_ymin(geomexpr).label('box_bottom'),
                func.st_xmax(geomexpr).label('box_right'),
                func.st_ymax(geomexpr).label('box_top'),
            ))

        gt = self.layer.geometry_type
        where.append(func.geometrytype(geomcol) == gt)

        order_criterion = []
        if self._order_by:
            for order, k in self._order_by:
                field = self.layer.field_by_keyname(k)
                order_criterion.append(
                    dict(asc=db.asc,
                         desc=db.desc)[order](tab.columns[field.column_name]))
        order_criterion.append(idcol)

        class QueryFeatureSet(FeatureSet):
            layer = self.layer

            _geom = self._geom
            _geom_format = self._geom_format
            _box = self._box
            _fields = self._fields
            _limit = self._limit
            _offset = self._offset

            def __iter__(self):
                query = sql.select(*columns) \
                    .where(db.and_(True, *where)) \
                    .limit(self._limit) \
                    .offset(self._offset) \
                    .order_by(*order_criterion)

                conn = self.layer.connection.get_connection()

                try:
                    result = conn.execute(query)
                    for row in result.mappings():
                        fdict = dict((k, row[l]) for k, l in fieldmap)

                        if self._geom:
                            if self._geom_format == 'WKB':
                                geom_data = row['geom'].tobytes()
                                geom = Geometry.from_wkb(geom_data,
                                                         validate=False)
                            else:
                                geom = Geometry.from_wkt(row['geom'],
                                                         validate=False)
                        else:
                            geom = None

                        yield Feature(
                            layer=self.layer,
                            id=row['id'],
                            fields=fdict,
                            geom=geom,
                            box=box(row['box_left'], row['box_bottom'],
                                    row['box_right'], row['box_top'])
                            if self._box else None)
                except SQLAlchemyError as exc:
                    raise ExternalDatabaseError(sa_error=exc)
                finally:
                    conn.close()

            @property
            def total_count(self):
                conn = self.layer.connection.get_connection()

                try:
                    query = sql.select(func.count(idcol)) \
                        .where(db.and_(True, *where))
                    result = conn.execute(query)
                    return result.scalar()
                except SQLAlchemyError as exc:
                    raise ExternalDatabaseError(sa_error=exc)
                finally:
                    conn.close()

        return QueryFeatureSet()
예제 #5
0
def _pg_wkt_to_wkb_ext(wkt):
    return _query_scalar_bytes(
        sa_func.st_asewkb(sa_func.st_geomfromtext(wkt), 'NDR'))
예제 #6
0
def _pg_wkt_to_wkb_iso(wkt):
    return _query_scalar_bytes(
        sa_func.st_asbinary(sa_func.st_geomfromtext(wkt), 'NDR'))