Beispiel #1
0
    def _truncate_last_segment(self, segment, start_point, exact_len, current_length):
        # Function is called when last segment is too long.
        # Previous length is current_length - current_segment_length
        # We have to take only (exact_len - previous_len) first meters of current_segment_length

        # Detect "correct" orientation of segment
        query = self.session.query(func.st_equals(func.ST_StartPoint(segment.geom), start_point))

        previous_length = current_length - segment.length
        take_only = exact_len - previous_length
        truncate_point = float(take_only) / float(segment.length)

        # Correct SRID: 32632 (UTM WGS84 32 North)
        if not query.first()[0]:
            shorter_line = self.session.query(func.st_asewkb(func.st_Line_Substring(segment.geom, 0, truncate_point)),
                                              func.ST_Length(
                                                  cast(func.st_Line_Substring(segment.geom, 0, truncate_point),
                                                       Geography))).first()
        else:
            shorter_line = self.session.query(func.st_asewkb(func.st_Line_Substring(segment.geom,
                                                                                    1 - truncate_point, 1)),
                                              func.ST_Length(
                                                  cast(func.st_Line_Substring(segment.geom, 1 - truncate_point, 1),
                                                       Geography))).first()

        segment_labels = []
        for label in segment.labels:
            segment_labels.append(model.Label(label=label.label, label_rate=label.label_rate))

        shorter_segment = model.Segment(name=segment.name,
                                        geom=WKBElement(shorter_line[0]),
                                        length=shorter_line[1],
                                        labels=segment_labels)
        return shorter_segment
Beispiel #2
0
    def _split_start_line(self, point, segment):
        # Find segment's point nearest to point
        nearest_point = self.session.query(func.st_closestpoint(segment.geom, point)).first()

        # Locate nearest point on segment. Function returns the position on segment as rate
        located_point = self.session.query(func.st_line_locate_point(segment.geom, nearest_point)).first()

        # Split segment. First half starts from 0 and ends at located_point
        first_half = self.session.query(func.st_asewkb(func.st_Line_Substring(segment.geom, 0, located_point)),
                                        func.ST_Distance_Sphere(func.st_Line_Substring(segment.geom, 0, located_point),
                                                                nearest_point),
                                        func.ST_Length(cast(func.st_Line_Substring(segment.geom, 0, located_point),
                                                            Geography))).first()

        # Split segment. Second half starts from located_point and ends at 1
        second_half = self.session.query(func.st_asewkb(func.st_Line_Substring(segment.geom, located_point, 1)),
                                         func.ST_Distance_Sphere(func.st_Line_Substring(segment.geom, located_point, 1),
                                                                 nearest_point),
                                         func.ST_Length(cast(func.st_Line_Substring(segment.geom, located_point, 1),
                                                             Geography))).first()

        # Create temporary segments (do not add these segments to session), with their own labels
        first_segment_labels = []
        second_segment_labels = []

        for label in segment.labels:
            first_segment_labels.append(model.Label(label=label.label, label_rate=label.label_rate))
            second_segment_labels.append(model.Label(label=label.label, label_rate=label.label_rate))

        first_segment = model.Segment(name=segment.name,
                                      geom=WKBElement(first_half[0]),
                                      length=first_half[2],
                                      labels=first_segment_labels)
        second_segment = model.Segment(name=segment.name,
                                       geom=WKBElement(second_half[0]),
                                       length=second_half[2],
                                       labels=second_segment_labels)

        # If located point is the same as start point, return only second half
        if located_point[0] == 0.0:
            return [second_segment]
        # If located point is the same as end point, return only first half
        if located_point[0] == 1.0:
            return [first_segment]

        # Else, return two splitted segments
        return [first_segment, second_segment]
Beispiel #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()
Beispiel #4
0
def _pg_wkb(wkb):
    _wkb = wkb.hex()
    return _query_scalar_bytes(
        sa_func.st_asewkb(sa_func.st_geomfromwkb(sa_func.decode(_wkb, 'hex')),
                          'NDR'))
Beispiel #5
0
def _pg_wkt_to_wkb_ext(wkt):
    return _query_scalar_bytes(
        sa_func.st_asewkb(sa_func.st_geomfromtext(wkt), 'NDR'))