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
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]
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()
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'))
def _pg_wkt_to_wkb_ext(wkt): return _query_scalar_bytes( sa_func.st_asewkb(sa_func.st_geomfromtext(wkt), 'NDR'))