def get_boundingpolygon(self): """ TODO: Implement ncell bbox order of lines creation (assumes 0,0 is x) -----3----- | | 4 2 | | x----1----- """ if self._ndim == 2: # CGRID nx,ny = self._xarray.shape one = MultiLineString([((self._xarray[i][0],self._yarray[i][0]),(self._xarray[i+1][0],self._yarray[i+1][0])) for i in range(nx-1)]) two = MultiLineString([((self._xarray[nx-1][j],self._yarray[nx-1][j]),(self._xarray[nx-1][j+1],self._yarray[nx-1][j+1])) for j in range(ny-1)]) three = MultiLineString([((self._xarray[i][ny-1],self._yarray[i][ny-1]),(self._xarray[i-1][ny-1],self._yarray[i-1][ny-1])) for i in reversed(list(range(1,nx)))]) four = MultiLineString([((self._xarray[0][j],self._yarray[0][j]),(self._xarray[0][j-1],self._yarray[0][j-1])) for j in reversed(list(range(1,ny)))]) m = one.union(two).union(three).union(four) else: # RGRID nx,ny = self._xarray.shape[0], self._yarray.shape[0] one = LineString([(self._xarray[i], self._yarray[0]) for i in range(nx)]) two = LineString([(self._xarray[-1], self._yarray[i]) for i in range(ny)]) three = LineString([(self._xarray[i], self._yarray[-1]) for i in reversed(list(range(nx)))]) four = LineString([(self._xarray[0], self._yarray[i]) for i in reversed(list(range(ny)))]) m = MultiLineString([one,two,three,four]) polygons = list(polygonize(m)) # -- polygonize returns a list of polygons, including interior features, the largest in area "should" be the full feature assert len(polygons) > 0, "Could not determine a polygon" polygon = sorted(polygons, key=lambda x: x.area)[-1] return polygon
class LinearReferencingTestCase(unittest.TestCase): def setUp(self): self.point = Point(1, 1) self.line1 = LineString(([0, 0], [2, 0])) self.line2 = LineString(([3, 0], [3, 6])) self.multiline = MultiLineString([ list(self.line1.coords), list(self.line2.coords) ]) def test_line1_project(self): self.assertEqual(self.line1.project(self.point), 1.0) self.assertEqual(self.line1.project(self.point, normalized=True), 0.5) def test_line2_project(self): self.assertEqual(self.line2.project(self.point), 1.0) self.assertAlmostEqual(self.line2.project(self.point, normalized=True), 0.16666666666, 8) def test_multiline_project(self): self.assertEqual(self.multiline.project(self.point), 1.0) self.assertEqual(self.multiline.project(self.point, normalized=True), 0.125) def test_not_supported_project(self): self.assertRaises(TypeError, self.point.buffer(1.0).project, self.point) def test_not_on_line_project(self): # Points that aren't on the line project to 0. self.assertEqual(self.line1.project(Point(-10,-10)), 0.0) def test_line1_interpolate(self): self.failUnless(self.line1.interpolate(0.5).equals(Point(0.5, 0.0))) self.failUnless( self.line1.interpolate(0.5, normalized=True).equals( Point(1.0, 0.0))) def test_line2_interpolate(self): self.failUnless(self.line2.interpolate(0.5).equals(Point(3.0, 0.5))) self.failUnless( self.line2.interpolate(0.5, normalized=True).equals( Point(3.0, 3.0))) def test_multiline_interpolate(self): self.failUnless(self.multiline.interpolate(0.5).equals( Point(0.5, 0.0))) self.failUnless( self.multiline.interpolate(0.5, normalized=True).equals( Point(3.0, 2.0))) def test_line_ends_interpolate(self): # Distances greater than length of the line or less than # zero yield the line's ends. self.failUnless(self.line1.interpolate(-1000).equals(Point(0.0, 0.0))) self.failUnless(self.line1.interpolate(1000).equals(Point(2.0, 0.0)))
def processAlgorithm(self, progress): try: from shapely.ops import polygonize from shapely.geometry import Point, MultiLineString except ImportError: raise GeoAlgorithmExecutionException( 'Polygonize algorithm requires shapely module!') vlayer = dataobjects.getObjectFromUri( self.getParameterValue(self.INPUT)) output = self.getOutputFromName(self.OUTPUT) vprovider = vlayer.dataProvider() if self.getParameterValue(self.FIELDS): fields = vprovider.fields() else: fields = QgsFields() if self.getParameterValue(self.GEOMETRY): fieldsCount = fields.count() fields.append(QgsField('area', QVariant.Double, 'double', 16, 2)) fields.append(QgsField('perimeter', QVariant.Double, 'double', 16, 2)) allLinesList = [] features = vector.features(vlayer) current = 0 total = 40.0 / float(len(features)) for inFeat in features: inGeom = inFeat.geometry() if inGeom.isMultipart(): allLinesList.extend(inGeom.asMultiPolyline()) else: allLinesList.append(inGeom.asPolyline()) current += 1 progress.setPercentage(int(current * total)) progress.setPercentage(40) allLines = MultiLineString(allLinesList) allLines = allLines.union(Point(0, 0)) progress.setPercentage(45) polygons = list(polygonize([allLines])) progress.setPercentage(50) writer = output.getVectorWriter(fields, QGis.WKBPolygon, vlayer.crs()) outFeat = QgsFeature() current = 0 total = 50.0 / float(len(polygons)) for polygon in polygons: outFeat.setGeometry(QgsGeometry.fromWkt(polygon.wkt)) if self.getParameterValue(self.GEOMETRY): outFeat.setAttributes([None] * fieldsCount + [polygon.area, polygon.length]) writer.addFeature(outFeat) current += 1 progress.setPercentage(50 + int(current * total)) del writer
def check_intersections(self): checks_done = MultiLineString() for subpath, elem in self.get_line_strings(): line = subpath.as_linestring() if not line.is_simple: # TODO: find location of self-intersection and introduce some # tolerance # checks_done = checks_done.union(line) yield CheckerResult("self-intersection found", elem) # continue if checks_done.intersects(line): intersection = checks_done.intersection(line) yield CheckerResult("intersection found", elem, extra={"intersection": intersection}) checks_done = checks_done.union(line)
def union(self): '''new method using Union function''' global polyCount inFeat = QgsFeature() progress = 0. self.emit(SIGNAL('progress'), 0) self.parent.layer = getMapLayerByName(self.ui.cmbLayer.currentText()) provider = self.parent.layer.dataProvider() #user can't toggle edit mode of line layer while polygonizing, plugin automatically turn it off QObject.connect(self.parent.layer,SIGNAL("editingStarted()"), self.parent.startEditing) allAttrs = provider.attributeIndexes() provider.select(allAttrs) provider.select() step = 45. / self.parent.layer.featureCount() allLinesList = [] allLinesListExtend = allLinesList.extend allLinesListAppend = allLinesList.append while provider.nextFeature(inFeat): geom = inFeat.geometry() if geom.isMultipart(): allLinesListExtend(geom.asMultiPolyline() ) else: allLinesListAppend(geom.asPolyline()) progress += step self.emit(SIGNAL('progress'), progress) allLines = MultiLineString(allLinesList) allLines = allLines.union(Point(0,0)) polygons = list(polygonize([allLines])) polyCount = len(polygons) #if no polygons where created then exit from thread if polyCount == 0: QObject.disconnect(self.parent.polygonizeThread,SIGNAL("finished()"), self.parent.threadFinished) self.emit(SIGNAL('noPolygons')) return else: if self.ui.cbOutput.isChecked(): self.saveAsFile(polygons, progress) else: self.saveInMemory(polygons, progress)
def plot_buff(G_, ax, buff=20, color='yellow', alpha=0.3, title='Proposal Snapping', title_fontsize=8, outfile='', dpi=200, verbose=False): '''plot buffer around graph using shapely buffer''' # get lines line_list = [] for u, v, key, data in G_.edges(keys=True, data=True): if verbose: print("u, v, key:", u, v, key) print(" data:", data) geom = data['geometry'] line_list.append(geom) mls = MultiLineString(line_list) mls_buff = mls.buffer(buff) if verbose: print("type(mls_buff) == MultiPolygon:", type(mls_buff) == shapely.geometry.MultiPolygon) if type(mls_buff) == shapely.geometry.Polygon: mls_buff_list = [mls_buff] else: mls_buff_list = mls_buff for poly in mls_buff_list: x,y = poly.exterior.xy coords = np.stack((x,y), axis=1) interiors = poly.interiors #coords_inner = np.stack((x_inner,y_inner), axis=1) if len(interiors) == 0: #ax.plot(x, y, color='#6699cc', alpha=0.0, linewidth=3, solid_capstyle='round', zorder=2) ax.add_patch(matplotlib.patches.Polygon(coords, alpha=alpha, color=color)) else: path = pathify(poly) patch = PathPatch(path, facecolor=color, edgecolor=color, alpha=alpha) ax.add_patch(patch) ax.axis('off') if len(title) > 0: ax.set_title(title, fontsize=title_fontsize) #outfile = os.path.join(res_dir, 'gt_raw_buffer.png') if outfile: plt.savefig(outfile, dpi=dpi) return ax
def __init__(self,trip_object): # initialize some variables self.trip = trip_object # trip object that this is a match for self.geometry = MultiLineString() # MultiLineString shapely geom self.OSRM_response = {} # python-parsed OSRM response object self.confidence = 0 # # error radius to use for map matching, same for all points self.error_radius = conf['error_radius'] self.default_route_used = False; # fire off a query to OSRM with the default parameters self.query_OSRM() if not self.OSRM_match_is_sufficient: # try again with a larger error radius self.error_radius *= 1.5 self.query_OSRM() # still no good? if not self.OSRM_match_is_sufficient: # Try a default geometry if self.get_default_route(): self.locate_vehicles_on_default_route() else: return # bad match, no default else: # have a workable OSRM match geometry self.parse_OSRM_geometry() self.locate_vehicles_on_OSRM_route() if len(self.trip.vehicles) > 2: self.locate_stops_on_route() # report on what happened self.print_outcome()
def setUp(self): self.point = Point(1, 1) self.line1 = LineString(([0, 0], [2, 0])) self.line2 = LineString(([3, 0], [3, 6])) self.multiline = MultiLineString([ list(self.line1.coords), list(self.line2.coords) ])
def get_default_route(self): """Check if a default route geometry is available; if so, we'll need to parse things into the same format, just as though this had come from OSRM.""" # get the default if there is one route_geom = db.get_route_geom( self.trip.direction_id, self.trip.last_seen ) if route_geom: # default available self.default_route_used = True self.confidence = 1 self.geometry = MultiLineString([route_geom]) return True else: # no default return False
def __init__(self, bus, end_condition, world=None): bus.register('scan', 'rot', 'orientation', 'sim_time_sec', 'origin', 'pose2d', 'acc', 'artf') self.bus = bus self.end_condition = end_condition self.world = world if world is not None else MultiLineString() self._handlers = collections.defaultdict(lambda: self.on_default) self.set_handler('request_origin', self.on_request_origin) self.set_handler('desired_speed', self.on_desired_speed) self.set_handler('pose2d', self.on_pose) self.set_handler('pose3d', self.on_pose) self.origin = [7.0, 3.0, 0.0] self.xyz = [0.0, 0.0, 0.0] self.orientation = quaternion.identity() self.speed_forward = 0.0 self.speed_angular = 0.0 self.time = datetime.timedelta()
def round_multilinestring_coords(mls, precision): """ Round the coordinates of a shapely MultiLineString to some decimal precision. Parameters ---------- mls : shapely MultiLineString the MultiLineString to round the coordinates of precision : int decimal precision to round coordinates to Returns ------- MultiLineString """ return MultiLineString([round_linestring_coords(ls, precision) for ls in mls])
def projected_multigeometry(geom): ''' Accept an unprojected geometry and return a projected multigeometry. ''' geoms = getattr(geom, 'geoms', [geom]) if geom.type in ('LineString', 'MultiLineString'): projected = MultiLineString(list(map(_m, geoms))) elif geom.type in ('Polygon', 'MultiPolygon'): parts = [(_m(poly.exterior), list(map(_m, poly.interiors))) for poly in geoms] projected = MultiPolygon(parts) else: raise ValueError("Can't generalize a %s geometry" % geom.type) return projected
def test_rect_grid_multilinestring_in_one_cell_shapely(): # avoid test fail when shapely not available try: import shapely except: return gr = get_rect_grid() ix = GridIntersect(gr) result = ix.intersect_linestring( MultiLineString([ LineString([(1., 1), (9., 1.)]), LineString([(1., 9.), (9., 9.)]) ])) assert len(result) == 1 assert result.lengths == 16. assert result.cellids[0] == (1, 0) return result
def element_dict(element_list: List[Element], node_dict: Dict[int, Point]) -> Dict[int, MultiLineString]: """A dictionary from an element list that turns an element number into a shapely MultiLineString""" d: Dict[int, MultiLineString] = dict() for e in element_list: if len(e) == 2: line_strings = (LineString((node_dict[e.i], node_dict[e.j])), ) else: lhs, rhs = e[:-1], e[1:] + e[:1] line_strings = tuple( LineString((node_dict[n1], node_dict[n2])) for n1, n2 in zip(lhs, rhs)) d[e.num] = MultiLineString(line_strings) return d
def test_with_known_mls_error(): """ Test branches_and_nodes with known mls error. """ linestrings = samples.mls_from_these_linestrings_list target_area = [box(*MultiLineString(linestrings).bounds)] branches, nodes = branches_and_nodes.branches_and_nodes( gpd.GeoSeries(linestrings), gpd.GeoSeries(target_area), Helpers.snap_threshold) for branch in branches.geometry: assert EE_branch not in str(branches[CONNECTION_COLUMN]) assert isinstance(branch, LineString) assert branch.is_simple assert not branch.is_empty for node in nodes.geometry: assert isinstance(node, Point) assert not node.is_empty
def contours_to_multiline(da_i, z_value, min_vertices=2): ''' Helper function to apply marching squares contour extraction to an array and return a data as a shapely MultiLineString. The `min_vertices` parameter allows you to drop small contours with less than X vertices. ''' # Extracts contours from array, and converts each discrete # contour into a Shapely LineString feature line_features = [ LineString(i[:, [1, 0]]) for i in find_contours(da_i, z_value) if i.shape[0] > min_vertices ] # Output resulting lines into a single combined MultiLineString return MultiLineString(line_features)
def rectilinear_optimal(shape, gap, angle): hlines, clines, theta, c, x = get_all(shape, gap) if not clines: return [], None, None, 0, 0, 0, 0 prob = LpProblem("Problema_Teste",LpMaximize) xf = [j for i in x for j in i] z = LpVariable.dicts("LC",xf,cat = 'Binary') prob += lpSum(z) '''restrição de não poder usar uma linha de conexão que não existe''' for i in range(len(x)): for j in range(len(x[i])-1): prob += z[x[i][j]] + z[x[i][j+1]] <= 1 prob += theta[i][j] + z[x[i][j]] <= 1 prob += theta[i][-1] + z[x[i][-1]] <= 1 ''' restrição não poder ir para duas linhas de conexão que possuem uma ligação''' for i in range(len(c)): for j in range(i+1,len(c[i])): for k in range(len(c[i][j])): if(c[i][j][k] != 0): prob += c[i][j][k] * (z[x[i][k]] + z[x[j][k]]) <= 1 prob.solve(PULP_CBC_CMD(msg=0)); tempo = prob.solutionTime num_restr = len(prob.constraints) num_var = len(prob._variables) vecSol = [v.varValue for v in prob.variables()] valor = value(prob.objective) selected_lines = [] for v in prob.variables(): if v.varValue == 1: i = int(v.name[4:].split(".")[0]) j = int(v.name[4:].split(".")[1]) selected_lines.append(clines[i][j]) selected_lines.extend(hlines) paths = linemerge(MultiLineString(selected_lines)) if type(paths) == LineString: return [paths] else: return list(paths)
def get_connections_by_heights(clines, polygon, gap): minx, miny, maxx, maxy = get_bounds(polygon) num_hlines = int((maxy - miny)/gap) + 1 edges = linemerge(MultiLineString(clines)) clines_by_heights = [] theta = np.ones((len(edges), num_hlines)) for i, edge in enumerate(edges): clines_by_heights.append([]) for j in range(num_hlines): clines_by_heights[i].append(None) for line in clines: if line.within(edge): j = int((line.bounds[1]-miny)/gap) clines_by_heights[i][j] = line theta[i][j] = 0 return clines_by_heights, theta
def test_projection(): given = GeometryCollection([ MultiPoint([(1, 2), (3, 4), (5, 6), (7, 8)]), MultiLineString([[(20, 30), (40, 50), (60, 70)]]), MultiPolygon([[[(1.1, 1.2), (1.3, 1.4), (1.5, 1.6), (1.7, 1.8), (1.1, 1.2)], []]]), ]) result = project(given, WGS84, MOLLWEIDE).wkt assert 'MULTIPOINT' in result assert 'GEOMETRYCOLLECTION' in result result = project(given).wkt assert 'MULTIPOINT' in result assert 'GEOMETRYCOLLECTION' in result expected = 'GEOMETRYCOLLECTION (MULTIPOINT (1 2, 3 4, 5 6, 7 8), MULTILINESTRING ((20 30, 40 50, 60 70)), MULTIPOLYGON (((1.1 1.2, 1.3 1.4, 1.5 1.6, 1.7 1.8, 1.1 1.2))))' assert project(given, WGS84, WGS84).wkt == expected
def generate_subpanel_bridges(dxf_outline_space, dxf_drill_space, area, cutout_width, count_x, count_y): __generate_bridges(dxf_outline_space, area, count_x, count_y) frame_lines = MultiLineString(cutout_lines) generate_mouse_bites(area, dxf_drill_space, frame_lines) for splitter in splitter_rectangles: frame_lines = frame_lines.difference(splitter) # Merge all lines, so the endpoints are not where lines cross frame_lines = ops.linemerge(frame_lines) inset_lines = [] for frame_line in frame_lines: line = frame_line.parallel_offset(2, 'left') # line2 = frame_line.parallel_offset(2, 'right') if frame_line.boundary and line.boundary: inset_line = LineString([frame_line.boundary[0], line.boundary[0]]) inset_lines.append(inset_line) inset_line = LineString([frame_line.boundary[1], line.boundary[1]]) inset_lines.append(inset_line) line = frame_line.parallel_offset(2, 'right') if frame_line.boundary and line.boundary: inset_line = LineString([frame_line.boundary[0], line.boundary[1]]) inset_lines.append(inset_line) inset_line = LineString([frame_line.boundary[1], line.boundary[0]]) inset_lines.append(inset_line) # frame_lines = frame_lines.union(inset_line) inset_lines = MultiLineString(inset_lines) dilated_insets = inset_lines.buffer(cutout_width / 3, join_style=1) # Remove outward bridges TODO: Needs better solution, this one is a quick hack TODO: Check if interior exterior # of polygon would work: https://gis.stackexchange.com/questions/341604/creating-shapely-polygons-with-holes # dilated_insets = clean_outer_perimeter(area, dilated_insets) # Merge cutouts and insets dilated = frame_lines.buffer(cutout_width / 2, cap_style=2, join_style=2) dilated = dilated.union(dilated_insets) # Round the corners of insets dilated = dilated.buffer(0.8, join_style=1).buffer(-0.8, join_style=1) for element in dilated: dxf_outline_space.add_lwpolyline(element.exterior.coords)
def generate(self, date, moduleid): # 尝试将一个相交线写入到文件 list = self.db.findAllLineByDateModuleid(date, moduleid) if len(list) == 0: return lineList = [] for wkt in list: lineList.append(loads(wkt[2])) lines = MultiLineString(lineList) # print(lines.bounds) # box = self.boxByModuleidDate(date, moduleid) startLon = lines.bounds[0] startLat = lines.bounds[1] polygonList = [] for geo in lines: # print("id=%d" % (geo[0])) box256 = self.createTileBox(startLon, startLat, geo.bounds[0], geo.bounds[1], geo.bounds[2], geo.bounds[3]) lonlat = box256[0] tileXy = box256[1] key = str(date) + "_" + str(moduleid) + "_" + str( tileXy[0]) + "_" + str(tileXy[1]) imgToPolygon = ImgToPolygon(startLon, startLat) if key not in self.keyList: filePath = self.getTrainTestImageByFileKey(key) if os.path.exists(filePath): self.keyList.append(key) list = imgToPolygon.toPolygon(tileXy, filePath) polygonList = polygonList + list # 尝试将一天的地块多边形写入到文件 polygonList = cascaded_union(polygonList) # for p in polygonList: # self.flowcountLine.wirteIntersection(moduleid, str(date), lineList, p) # self.flowcountLine.save() return polygonList
def plot(self, geometry, inverse = False): geometries = hasattr(geometry, 'geoms') and geometry.geoms or [geometry] res = [] # at first shift polygons #shifted = [] #for geom in geometries: # if isinstance(geom, Polygon): # shifted += self._shift_polygon(geom) # else: # shifted += [geom] for geom in geometries: if isinstance(geom, Polygon): res += self.plot_polygon(geom, inverse = inverse) elif isinstance(geom, LineString): rings = self.plot_linear_ring(geom, inverse = inverse) res += map(LineString, rings) elif isinstance(geom, Point): if self._visible(geom.x, geom.y) and inverse: x, y = self.project_inverse(geom.x, geom.y) res.append(Point(x, y)) elif self._visible(geom.x, geom.y): x, y = self.project(geom.x, geom.y) res.append(Point(x, y)) else: pass # raise KartographError('proj.plot(): unknown geometry type %s' % geom) if len(res) > 0: if isinstance(res[0], Polygon): if len(res) > 1: return MultiPolygon(res) else: return res[0] elif isinstance(res[0], LineString): if len(res) > 1: return MultiLineString(res) else: return LineString(res[0]) else: if len(res) > 1: return MultiPoint(res) else: return Point(res[0].x, res[0].y)
def create_multilinestring(self, route_json): """Converts original google route data to more suitable format for creating shapely and ogr geometry. Creates Shapely and Ogr LineString geometries from route coordinates. .. note:: Raw Google route data is in proprietary format, so GOOGLE_POLYLINE_DECODER is needed to convert it to more suitable format. Moreover, Raw google data may contain multiple routes, each route contains legs, each leg contains steps and each step contains encoded polyline points. :arg route_json: dictionary with google route data :type route_json: dictionary :returns: tuple consisted of route's shapely and ogr geometry :rtype: (shapely.geometry.linestring.LineString, osgeo.ogr.Geometry) """ # List of all polylines. polyline_list = [] route_distance = 0 duration = 0 for route in route_json['routes']: for leg in route['legs']: route_distance += leg['distance']['value'] / 1000.0 duration += leg['duration']['value'] for step in leg['steps']: # step is "edge" decoded_polyline = ( GOOGLE_POLYLINE_DECODER.decode_google_polyline( point_str=step['polyline']['points'])) polyline_list.append(decoded_polyline) # Create shapely multilinestring from list of polylines. route_multilinestring_shapely = MultiLineString(polyline_list) # Create ogr multilinestring route_multilinestring_ogr = ogr.CreateGeometryFromWkb( route_multilinestring_shapely.wkb) driving_time = {'sec': duration, 'hms': DT.timedelta(seconds=duration)} return (route_multilinestring_shapely, route_multilinestring_ogr, route_distance, driving_time)
def path_to_geom_dicts(path, skip_invalid=True): """ Converts a Path element into a list of geometry dictionaries, preserving all value dimensions. """ interface = path.interface.datatype if interface == 'geodataframe': return [row.to_dict() for _, row in path.data.iterrows()] elif interface == 'geom_dictionary': return path.data geoms = [] invalid = False xdim, ydim = path.kdims for i, path in enumerate(path.split(datatype='columns')): array = np.column_stack([path.pop(xdim.name), path.pop(ydim.name)]) splits = np.where(np.isnan( array[:, :2].astype('float')).sum(axis=1))[0] arrays = np.split(array, splits + 1) if len(splits) else [array] subpaths = [] for j, arr in enumerate(arrays): if j != (len(arrays) - 1): arr = arr[:-1] # Drop nan if len(arr) == 0: continue elif len(arr) == 1: if skip_invalid: continue g = Point(arr[0]) invalid = True else: g = LineString(arr) subpaths.append(g) if invalid: geoms += [dict(path, geometry=sp) for sp in subpaths] continue elif len(subpaths) == 1: geom = subpaths[0] elif subpaths: geom = MultiLineString(subpaths) path['geometry'] = geom geoms.append(path) return geoms
def route_to_line_string(graph): """ Method converts route representes as Directed Acyclic Graph to LineString :param graph: MultiDiGraph, DiGraph :return: LineString """ geometries = [e[2]['geometry'] for e in graph.edges(data=True)] # Some edges have list as geometry geometries = list(flatten(geometries)) # Form MultiLineString of given LineString geometries multi_line = MultiLineString(geometries) # Finally merge them merged_line = ops.linemerge(multi_line) # merged_line = ops.unary_union(geometries) if isinstance(merged_line, MultiLineString): print("Warning: route can`t be merged", file=sys.stderr) # merged_line = connect_lines(merged_line) return merged_line
def test_resample(): point = Point((0, 0)).resample() assert_array_equal(point.np, [[0, 0]]) assert_array_equal( LineString([(0, 0), (10, 0)]).resample(), np.column_stack((np.arange(11), np.zeros(11)))) assert_array_equal( LineString([(0, 0), (10, 0)]).resample(2), np.column_stack((np.arange(0, 11, 2), np.zeros(6)))) assert_array_equal( MultiLineString([[(0, 0), (10, 0)]]).resample().xy, np.column_stack((np.arange(11), np.zeros(11)))) assert_array_equal( Polygon([(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)]).resample().xy, np.array([[0., 0.], [0., 1.], [0., 2.], [1., 2.], [2., 2.], [2., 1.], [2., 0.], [1., 0.], [0., 0.]]))
def simplify_multiline(self, mline, threshold): # break MultiLineString into lines lineList = mline.geoms simpleLineList = [] # call simplify_line on each for line in lineList: simpleLine = self.simplify_line(line, threshold) #if not none append to list if simpleLine: simpleLineList.append(simpleLine) # check that line count > 0, otherwise return None if not simpleLineList: return None # put back into multilinestring return MultiLineString(simpleLineList)
def restore_geometry(self, lines, minArea=0): """ restores geometry from linear rings """ from shapely.geometry import LineString, MultiLineString linestrings = [] for line in lines: kept = [] for pt in line: if not pt.deleted: kept.append((pt[0], pt[1])) if len(kept) >= 2: linestrings.append(LineString(kept)) if len(linestrings) > 0: self.geometry = MultiLineString(linestrings) else: self.geometry = None
def main(): """Go Main, please""" pgconn = get_dbconn("postgis") cursor = pgconn.cursor() req = requests.get(URI, timeout=30) jobj = req.json() featureset = jobj["layers"][0]["featureSet"] for feat in featureset["features"]: props = feat["attributes"] # Geometry is [[pt]] and we only have single segments path = MultiLineString([LineString(feat["geometry"]["paths"][0])]) # segid is defined by the database insert major = props["ROUTE_NAME"] (typ, _num) = major.replace("-", " ").split() if typ not in ["I", "US", "IA"]: print("Totally unknown, abort") print(json.dumps(jobj, indent=4)) sys.exit() geom = ("ST_Transform(ST_SetSrid(ST_GeomFromText('%s'), 3857), 26915)" ) % (path.wkt) idot_id = props["SEGMENT_ID"] cursor.execute( """ SELECT st_length(geom), st_length(""" + geom + """), archive_begin from roads_base where idot_id = %s and archive_begin > '2017-01-01'::date ORDER by archive_begin DESC """, (idot_id, ), ) row = cursor.fetchone() if abs(row[1] - row[0]) > 1: print("%s, %s" % (str(row), cursor.rowcount)) cursor.execute( """ UPDATE roads_base SET geom = """ + geom + """ WHERE idot_id = %s and archive_begin > '2017-01-01'::date """, (idot_id, ), ) print("updated %s rows" % (cursor.rowcount, )) cursor.close() pgconn.commit()
def get(self,apnk,LatLongs): # Adding Geolocation gmaps = googlemaps.Client(key=apnk) val = [] geolocation = GoogleV3(api_key=apnk) RoadInfo = json.loads(LatLongs) directions = gmaps.directions(RoadInfo[0],RoadInfo[1],mode="driving",alternatives=True) polylines = [i["overview_polyline"]["points"] for i in directions] routeLengths = [j["distance"]["value"] for i in directions for j in i["legs"]] routeDurations = [j["duration"]["value"] for i in directions for j in i["legs"]] cur = mysql.connection.cursor() AccData = pd.read_sql('select Longitude, Latitude, RadiusInKM, PostcodeNo, AccidentCount,concat(RoadName,\' \', RoadType) as Road from AccidentCluster where AccidentCount > 1 ;',con=mysql.connection) LS = [LineString(PolylineCodec().decode(line)[1::2]) for line in polylines] MLS = MultiLineString(LS) bounds = MLS.bounds newData = AccData.loc[(AccData['Latitude'] >= bounds[0]) & (AccData['Latitude'] <= bounds[2]) & (AccData['Longitude'] >= bounds[1]) & (AccData['Longitude'] <= bounds[3])] def myFun(point,line): np = line.interpolate(line.project(point)) new_point = pnt(longitude=np.y,latitude=np.x) old_point = pnt(longitude=point.y,latitude=point.x) dist = distance(new_point,old_point).km return dist geo = geop.GeoDataFrame(newData,geometry=geop.points_from_xy(newData.Latitude,newData.Longitude)) for i in range(len(LS)): s=str(i) geo[s] = geo.apply(lambda val: myFun(val['geometry'],LS[i]),axis=1) for i in range(7,len(geo.columns)): j = str(i-7) newdf = geo.loc[(geo[j] <= 3.0)] dropList = [str(thing-7) for thing in range(7,len(geo.columns))] newdf = newdf.drop(columns=dropList) newdf = newdf.drop(columns=['geometry']) val.append({"RouteNo":int(j),"polyline": polylines[i-7], 'routeLengthInMeters':routeLengths[i-7], 'routeDurationInSeconds':routeDurations[i-7], 'data':newdf.to_dict(orient='records'), 'totalAccidents': str(newdf['AccidentCount'].sum()), 'bounds':bounds}) finalVal = {'routes':val} return jsonify(finalVal)
def load_poly_file(self, poly_file): points_basename = os.path.basename(poly_file).replace( 'POLY_', 'POINTS_', 1).replace('_shp', '_pts') print('Running %s...' % points_basename) points_path = os.path.join(self.analysis_folder, points_basename) if not os.path.isfile(points_path): print(points_path) print(poly_file) print('error: points not found for ', os.path.basename(poly_file)) else: self.poly_path_to_points_dic[poly_file] = points_path poly_poly = shp.Reader(poly_file) # 2. Load poly file # 2.1 check if it is an area or a line poly_check = True building_poly_coords = None building_poly = None try: building_poly_coords = poly_poly.shapeRecords( )[0].shape.__geo_interface__['coordinates'] building_string = LineString(building_poly_coords[0]) except ValueError: print('linelist') line_list = [] for a in poly_poly.shapeRecords(): building_poly_coords = a.shape.__geo_interface__[ 'coordinates'] line_list.append(building_poly_coords) building_string = MultiLineString(line_list) poly_check = False building_buff = building_string.buffer(5) building_interior = building_buff.interiors # what was this meant to achieve?????? try: building_poly = unary_union(building_string, building_interior) except Exception as e: # print('e3', e) print('unary exception') pass if poly_check: building_poly = Polygon(building_poly_coords[0]) else: building_poly = building_string self.poly_shp_dic[poly_file] = (building_poly, building_string)
def path_to_lines(path_str): """ Read SVG path string using svg.path and convert it to a shapely MultiLineString object. Parameters ---------- path_str : str Returns ------- shapely.geometry.MultiLineString """ # parse svg path path = svg.path.parse_path(path_str) # initiate lists and coords lines = [] points = [] x0 = 0 y0 = 0 x1 = 0 y1 = 0 for element in path: if isinstance(element, svg.path.Move) and not (x1 == 0 and y1 == 0): # element is a move (pen up): close line with previous element end point points.append((x1, y1)) lines.append(points) points = [] elif isinstance(element, svg.path.Line): # element is a line (pen down): add element start point x0 = element.start.real y0 = element.start.imag x1 = element.end.real y1 = element.end.imag points.append((x0, y0)) if points != []: # close line with previous element end point points.append((x1, y1)) lines.append(points) points = [] return MultiLineString(lines)
def reproject_records(records, inproj='epsg:4326', outproj='epsg:3857'): """ Reprojects a set of records from one projection to another Records can either be points, line strings, or multiline strings Args: records - list of records to reproject inproj - defaults to 4326 outproj - defaults to 3857 Returns: list of reprojected records """ results = [] inproj = pyproj.Proj(init=inproj) outproj = pyproj.Proj(init=outproj) for record in records: coords = record['geometry']['coordinates'] if record['geometry']['type'] == 'Point': re_point = pyproj.transform(inproj, outproj, coords[0], coords[1]) point = Point(re_point) results.append({'geometry': point, 'properties': record['properties']}) elif record['geometry']['type'] == 'MultiLineString': new_coords = [] for segment in coords: new_segment = [] for coord in segment: new_segment.append(pyproj.transform( inproj, outproj, coord[0], coord[1])) new_coords.append(new_segment) results.append({'geometry': MultiLineString(new_coords), 'properties': record['properties']}) elif record['geometry']['type'] == 'LineString': new_coords = [] for coord in coords: new_coords.append( pyproj.transform(inproj, outproj, coord[0], coord[1]) ) results.append({'geometry': LineString(new_coords), 'properties': record['properties']}) return results
def polygonizeFeatures(features, fields=None): lineList = [] for inFeat in features: inGeom = inFeat.geometry() if inGeom is None: pass elif inGeom.isMultipart(): lineList.extend(inGeom.asMultiPolyline()) else: lineList.append(inGeom.asPolyline()) allLines = MultiLineString(lineList) allLines = unary_union(allLines) polygons = list(polygonize([allLines])) outList = [] for polygon in polygons: outFeat = QgsFeature(fields) outFeat.setGeometry(QgsGeometry.fromWkt(polygon.wkt)) outList.append(outFeat) return outList
def sinewave(args): INTERVAL = 0.1 if args.numwaves == 2: TAIL_LEN, SINE_LEN = 7, 7 TAILS = np.zeros(int(TAIL_LEN / INTERVAL)) sin_range = np.arange(-pi / 4, SINE_LEN, INTERVAL) * pi / 2 amplitude = (np.sin(sin_range) + 1) else: TAIL_LEN, SINE_LEN = 3.5, 3.5 TAILS = np.zeros(int(TAIL_LEN / INTERVAL)) sin_range = np.arange(-pi / 4, SINE_LEN - pi / 8, INTERVAL) * pi / 2 amplitude = np.sin(sin_range) + 1 y = np.concatenate([TAILS, amplitude, TAILS]) x = np.arange(-TAIL_LEN - pi / 4, SINE_LEN + TAIL_LEN, INTERVAL) lines = LineString(zip(x * 10, y * 10)) geom = MultiLineString([lines]) write_file(args, geom)
def gdf(self): if not hasattr(self, '_gdf'): data = [] for i, (id, boundary) in enumerate(self._data.items()): front_face, back_face = list(zip(*self.indexes[i])) data.append({ 'geometry': MultiLineString([ LineString( self._mesh.coords.iloc[front_face, :].values), LineString( self._mesh.coords.iloc[back_face, :].values), ]), 'key': f'{boundary.get("ibtype")}:{id}', **boundary, }) self._gdf = gpd.GeoDataFrame(data, crs=self._mesh.crs) return self._gdf
def test_tri_grid_multilinestring_in_one_cell(rtree=True): # avoid test fail when shapely not available try: import shapely except: return gr = get_tri_grid() if gr == -1: return ix = GridIntersect(gr, rtree=rtree) result = ix.intersect( MultiLineString([ LineString([(1., 1), (9., 1.)]), LineString([(2., 2.), (9., 2.)]) ])) assert len(result) == 1 assert result.lengths == 15. assert result.cellids[0] == 4 return result
def walkshed(start, dist): visited = [] queued = [start] to_visit = [(start, 0)] # node, dist at node lines = [] while to_visit: node, cur_dist = to_visit.pop() edges = n.edges(nbunch=[node], data=True) for e in edges: if e[2]['type'] in ['1', '5', '6', '10']: if cur_dist + e[2]['len'] < dist: if e[2]['geo'] not in lines: lines.append(e[2]['geo']) if e[1] not in queued: to_visit.append((e[1], cur_dist + e[2]['len'])) queued.append(e[1]) return mapping(MultiLineString(lines))
def __init__(self,vehicles): # initialize some variables self.vehicles = vehicles self.confidence = None # average match confidence self.geom = MultiLineString() # multiline shapely geom self.error_radius = conf['error_radius'] self.use_times = False # whether times are sent to OSRM self.response = {} # python-parsed formerly-JSON object self.is_useable = True # good enough to be used elsewhere? self.num_attempts = 0 # send the query right away self.send() # validate the results - can we likely improve on them? self.validate() # output if self.is_useable: print '\tconf. is',self.confidence,'on',len(self.response['matchings']),'match(es) after',self.num_attempts,'tries' else: print '\tmatching failed'
def _overlay(self, layer2, method): assert method in ['union', 'intersection', 'identity'] idx1 = index.Index() idx2 = index.Index() # for fast lookup of geometry and properties after spatial index # advantage: don't have to reopen ds and seek on disk # disadvantage: have to keep everything in memory # {id: (shapely geom, properties dict) } # TODO just use the index as the id and just copy the fiona records? features1 = {} features2 = {} rings1 = [] rings2 = [] log.debug("gathering LinearRings") log.debug("\tself") for rec in self.collection(): geom = shape(rec['geometry']) rid = int(rec['id']) features1[rid] = (geom, rec['properties']) idx1.insert(rid, geom.bounds) if hasattr(geom, 'geoms'): for poly in geom.geoms: # if it's a multipolygon if not poly.is_valid: log.debug("\tgeom from self layer is not valid," + " attempting fix by buffer 0") poly = poly.buffer(0) rings1.append(poly.exterior) rings1.extend(poly.interiors) else: if not geom.is_valid: log.debug("\tgeom from self layer is not valid," + " attempting fix by buffer 0") geom = geom.buffer(0) rings1.append(geom.exterior) rings1.extend(geom.interiors) log.debug("\tlayer2") for rec in layer2.collection(): geom = shape(rec['geometry']) rid = int(rec['id']) features2[rid] = (geom, rec['properties']) idx2.insert(rid, geom.bounds) if hasattr(geom, 'geoms'): for poly in geom.geoms: # multipolygon if not poly.is_valid: log.debug("\t geom from layer2 is not valid," + " attempting fix by buffer 0") poly = poly.buffer(0) rings2.append(poly.exterior) rings2.extend(poly.interiors) else: if not geom.is_valid: log.debug("\t geom from layer2 is not valid," + " attempting fix by buffer 0") geom = geom.buffer(0) rings2.append(geom.exterior) rings2.extend(geom.interiors) #rings = [x for x in rings if x.is_valid] mls1 = MultiLineString(rings1) mls2 = MultiLineString(rings2) try: log.debug("calculating union (try the fast unary_union)") mm = unary_union([mls1, mls2]) except: log.exception("unary_union FAILED") log.debug("calculating union again (using the slow a.union(b))") mm = mls1.union(mls2) log.debug("polygonize rings") newpolys = polygonize(mm) log.debug("constructing new schema") out_schema = self.collection().schema.copy() # TODO polygon geomtype layer2_schema_map = {} # {old: new} for key, value in layer2.collection().schema['properties'].items(): if key not in out_schema['properties']: out_schema['properties'][key] = value layer2_schema_map[key] = key else: # try to rename it i = 2 while True: newkey = "%s_%d" % (key, i) if newkey not in out_schema['properties']: out_schema['properties'][newkey] = value layer2_schema_map[key] = newkey break i += 1 tempds = self.tempds(method) out_collection = fiona.collection( tempds, "w", "ESRI Shapefile", out_schema) log.debug("determine spatial relationship") for fid, newpoly in enumerate(newpolys): cent = newpoly.representative_point() # Test intersection with original polys layer1_hit = False layer2_hit = False prop1 = None prop2 = None candidates1 = list(idx1.intersection(cent.bounds)) candidates2 = list(idx2.intersection(cent.bounds)) for cand in candidates1: if cent.intersects(features1[cand][0]): layer1_hit = True prop1 = features1[cand][1] # properties break for cand in candidates2: if cent.intersects(features2[cand][0]): layer2_hit = True prop2 = features2[cand][1] # properties break # determine whether to output based on type of overlay hit = False if method == "intersection" and (layer1_hit and layer2_hit): hit = True elif method == "union" and (layer1_hit or layer2_hit): hit = True elif method == "identity" and ((layer1_hit and layer2_hit) or (layer1_hit and not layer2_hit)): hit = True if not hit: continue log.debug("write newpoly with attrs gathered from prop1 & prop2") if not prop1: prop1 = dict.fromkeys( self.collection().schema['properties'].keys(), None) if not prop2: prop2 = dict.fromkeys(layer2_schema_map.keys(), None) newprop = prop1 for key, value in prop2.items(): newkey = layer2_schema_map[key] newprop[newkey] = value out_feature = { 'id': fid, 'properties': newprop, 'geometry': mapping(newpoly)} out_collection.write(out_feature) out_collection.close() return Layer(tempds)
from shapely.geometry import LineString, MultiLineString, Polygon, Point from shapely.ops import polygonize, polygonize_full, transform, linemerge shapely_line_strings = [LineString(x) for x in snap_lines] # add bounding lines needed by polygonize (it's easy to do per pixel. TODO refactor to create only necessary lines) #for i in xrange(sz): # pair_lines.append(((0,i),(0,i+1))) # pair_lines.append(((sz,i),(sz,i+1))) # pair_lines.append(((i,0),(i+1,0))) # pair_lines.append(((i,sz),(i+1,sz))) #ps = list(polygonize(shapely_line_strings)) # simple polygonize doesn't work. it is a trick (see http://gis.stackexchange.com/questions/58245/generate-polygons-from-a-set-of-intersecting-lines) M = MultiLineString(shapely_line_strings) MB = M.buffer(0.001) P = Polygon([(0, 0), (0, sz), (sz, sz), (sz, 0)]) pso = P.difference(MB) # round vertices coords ps = [] for p in pso: pb = p.buffer(0.001) pbt = transform(lambda x, y, z=None: (int(round(x)), int(round(y))), pb) ps.append(pbt) # associate LineString_s with Polygon_s pt_polygon_index = dict(); for p in ps:
class match(object): """This object is responsible for coming up with a more spatially accurate version of the trip. We do this by first trying to map match the GPS track to the street/rail network using OSRM. If that doesn't work well for any reason, we try altering some parameters to improve the match. If it's still not great, we can see if there is a default route_geometry provided. Ultimately, we judge whether the match is sufficent to proceed. If we do use this match, this object also provides methods for associating points (vehicles, stops) with points along the route gemetry; these will be used for time interpolation inside the trip object.""" def __init__(self,trip_object): # initialize some variables self.trip = trip_object # trip object that this is a match for self.geometry = MultiLineString() # MultiLineString shapely geom self.OSRM_response = {} # python-parsed OSRM response object self.confidence = 0 # # error radius to use for map matching, same for all points self.error_radius = conf['error_radius'] self.default_route_used = False; # fire off a query to OSRM with the default parameters self.query_OSRM() if not self.OSRM_match_is_sufficient: # try again with a larger error radius self.error_radius *= 1.5 self.query_OSRM() # still no good? if not self.OSRM_match_is_sufficient: # Try a default geometry if self.get_default_route(): self.locate_vehicles_on_default_route() else: return # bad match, no default else: # have a workable OSRM match geometry self.parse_OSRM_geometry() self.locate_vehicles_on_OSRM_route() if len(self.trip.vehicles) > 2: self.locate_stops_on_route() # report on what happened self.print_outcome() @property def OSRM_match_is_sufficient(self): """Is this match good enough actually to be used?""" return self.confidence >= conf['min_OSRM_match_quality'] @property def is_useable(self): """Do we have everything we need to proceed with the match?""" if not (self.OSRM_match_is_sufficient or self.default_route_used): return False if not len(self.trip.vehicles) > 3: return False if self.trip.vehicles[0].measure == self.trip.vehicles[-1].measure: return False if not len(self.trip.timepoints) > 1: return False # and only if we make it past all those conditions: return True def query_OSRM(self): """Construct the request and send it to OSRM, retrying if necessary.""" # structure it as API requires, rounding coords to 6 decimals coords = ';'.join( [ format(v.lon,'.7g')+','+format(v.lat,'.7g') for v in self.trip.vehicles ] ) radii = ';'.join( [ str(self.error_radius) ] * len(self.trip.vehicles) ) # construct and send the request options = { 'radiuses':radii, 'steps':'false', 'geometries':'geojson', 'annotations':'false', 'overview':'full', 'gaps':'ignore', # don't split based on time gaps - shouldn't be any 'tidy':'true', 'generate_hints':'false' } # open a connection, configured to retry in case of errors with requests.Session() as session: retries = Retry( total=5, backoff_factor=1 ) session.mount( 'http://', HTTPAdapter(max_retries=retries) ) # make the request try: raw_response = session.get( conf['OSRMserver']['url']+'/match/v1/transit/'+coords, params=options, timeout=conf['OSRMserver']['timeout'] ) except: return db.ignore_trip(self.trip.trip_id,'connection issue') # parse the result to a python object self.OSRM_response = json.loads(raw_response.text) # how confident should we be in this response? if self.OSRM_response['code'] != 'Ok': self.confidence = 0 return else: # Get an average confidence value from the match result. confidence_values = [ m['confidence'] for m in self.OSRM_response['matchings'] ] self.confidence = mean( confidence_values ) def parse_OSRM_geometry(self): """Parse the OSRM match geometry into a more useable format. Specifically a simplified and projected MultiLineString.""" # get a list of lists of lat-lon coords which need to be reprojected lines = [asShape(matching['geometry']) for matching in self.OSRM_response['matchings']] multilines = MultiLineString(lines) # reproject to local local_multilines = reproject( conf['projection'], multilines ) # simplify slightly for speed (2 meter simplification) simple_local_multilines = local_multilines.simplify(2) # if the multi actually just had one line, this simplifies to a # linestring, which can cause problems down the road if simple_local_multilines.geom_type == 'LineString': simple_local_multilines = MultiLineString([simple_local_multilines]) self.geometry = simple_local_multilines def get_default_route(self): """Check if a default route geometry is available; if so, we'll need to parse things into the same format, just as though this had come from OSRM.""" # get the default if there is one route_geom = db.get_route_geom( self.trip.direction_id, self.trip.last_seen ) if route_geom: # default available self.default_route_used = True self.confidence = 1 self.geometry = MultiLineString([route_geom]) return True else: # no default return False def print_outcome(self): """Print the outcome of this match to stdout.""" if self.default_route_used and self.confidence == 1: print( '\tdefault route used for direction',self.trip.direction_id ) elif self.default_route_used and self.confidence == 0: print( '\tdefault route not found for',self.trip.direction_id ) elif not self.default_route_used and self.confidence > conf['min_OSRM_match_quality']: print( '\tOSRM match found with',round(self.confidence,3),'confidence' ) else: print( '\tmatching failed for trip',self.trip.trip_id ) # Below are functions associated with finding the measure of points along # the route geometry, either as given by OSRM or provided as the default. # These are called from inside the trip if the match is useable. def locate_vehicles_on_OSRM_route(self): """Find the measure of vehicles along the OSRM-supplied route. This is easy because OSRM provides the distance of an input coordinate along the match geometry.""" assert not self.default_route_used # these are the matched points of the input cordinates # null (None) entries indicate an omitted (outlier) point # true where not none drop_list = [ point is None for point in self.OSRM_response['tracepoints'] ] # drop vehicles that did not contribute to the match, # backwards to maintain order for i in reversed( range( 0, len(drop_list) ) ): if drop_list[i]: self.trip.ignore_vehicle( i ) # get cumulative distances of each vehicle along the match geom # This is based on the leg distances provided by OSRM. Each leg is just # the trip between matched points. Each match has one more vehicle record # associated with it than legs cummulative_distance = 0 v_i = 0 for matching in self.OSRM_response['matchings']: # the first point is at 0 per match self.trip.vehicles[v_i].set_measure( cummulative_distance ) v_i += 1 for leg in matching['legs']: cummulative_distance += leg['distance'] self.trip.vehicles[v_i].set_measure( cummulative_distance ) v_i += 1 # Because the line has been simplified, the distances will be # slightly off and need correcting adjust_factor = self.geometry.length / self.trip.vehicles[-1].measure for v in self.trip.vehicles: v.measure = v.measure * adjust_factor def locate_vehicles_on_default_route(self): """Find the measure of vehicles along the default route. First discard observations too far from the route geometry. Next, find the measure of the remaining vehicles in the order they were observed. If the vehicles progress monotonically down the line then all is good. Otherwise, we start dropping observations that are most severely out of order until we are left with an ordered list moving along the route in the correct direction. Wrong direction travel will generally result in a minimal ordered set: 1 remaining observation.""" assert self.default_route_used # match stops within a distance of the route geometry vehicles_to_ignore = [] for vehicle in self.trip.vehicles: # if the vehicle is close enough distance_from_route = self.geometry.distance( vehicle.geom ) if distance_from_route <= conf['stop_dist']: m = self.geometry.project(vehicle.geom) vehicle.set_measure(m) else: vehicles_to_ignore.append(vehicle) for vehicle in vehicles_to_ignore: self.trip.ignore_vehicle( vehicle ) # while the list is not fully sorted while self.trip.vehicles != sorted(self.trip.vehicles,key=lambda v: v.measure): correct_order = sorted(self.trip.vehicles,key=lambda v: v.measure) current_order = self.trip.vehicles transpositions = {} # compare all vehicles in both lists for i,v1 in enumerate(correct_order): for j,v2 in enumerate(current_order): if v1 == v2: if abs(i-j) > 0: # not in the same position # add these vehicles to the list with their distances as keys if abs(i-j) not in transpositions: transpositions[abs(i-j)] = [v1] else: transpositions[abs(i-j)].append(v1) else: # are in the same position continue max_dist = max(transpositions.keys()) # ignore vehicles associated with the max of the transposition distances for vehicle in transpositions[max_dist]: self.trip.ignore_vehicle(vehicle) # now we either have a sorted list or an essentially empty list if the # match happened to be bad def locate_stops_on_route(self): """Find the measure of stops along the route geometry for any arbitrary route. Stops must be within a given distance of the path, but can repeat if the route passes a stop two or more times. To check for this, the geometry is sliced up into segments and we check just a portion of the route at a time.""" assert len(self.trip.stops) > 0 assert self.geometry.length > 0 # list of timepoints potential_timepoints = [] # copy the geometry so we can slice it up it path = copy(self.geometry) traversed = 0 # while there is more than 750m of path remaining while path.length > 0: subpath, path = cut(path,750) # check for nearby stops for stop in self.trip.stops: # if the stop is close enough stop_dist = subpath.distance(stop.geom) if stop_dist <= conf['stop_dist']: # measure how far it is along the trip m = traversed + subpath.project(stop.geom) # add it to the list of measures potential_timepoints.append( TimePoint(stop,m,stop_dist) ) # note what we have already traversed traversed += 750 # Now some of these will be duplicates that are close to the cutpoint # and thus are added twice with similar measures # such points need to be removed final_timepoints = [] for pt in potential_timepoints: skip_this_timepoint = False for ft in final_timepoints: # if same stop and very close if pt.stop_id == ft.stop_id and abs(pt.measure-ft.measure) < 2*conf['stop_dist']: #choose the closer of the two to use if ft.dist <= pt.dist: skip_this_timepoint = True break else: ft = pt skip_this_timepoint = True break if not skip_this_timepoint: # we didn't have anything like that in the final set yet final_timepoints.append( pt ) # add terminal stops if they are anywhere near the GPS data # but not used yet if not self.default_route_used: # for first and last stops for terminal_stop in [self.trip.stops[0],self.trip.stops[-1]]: if not terminal_stop.id in [ t.stop.id for t in potential_timepoints ]: # if the terminal stop is less than 500m away from the route dist = self.geometry.distance(terminal_stop.geom) if dist < 500: m = self.geometry.project(terminal_stop.geom) final_timepoints.append( TimePoint( terminal_stop, m-dist if m < self.geometry.length/2 else m+dist, dist ) ) # for default geometries on the other hand, remove stops that are nowhere # near the actual GPS data else: final_timepoints = [ t for t in final_timepoints if t.measure > self.trip.vehicles[0].measure - 500 and t.measure < self.trip.vehicles[-1].measure + 500 ] # sort by measure ascending final_timepoints = sorted(final_timepoints,key=lambda timepoint: timepoint.measure) self.trip.timepoints = final_timepoints
poly = poly.buffer(0) rings2.append(poly.exterior) rings2.extend(poly.interiors) else: if not geom.is_valid: print "***** Geometry from layer2 is not valid, fixing by buffer 0" geom = geom.buffer(0) rings2.append(geom.exterior) rings2.extend(geom.interiors) #print "\t", len([x for x in rings if not x.is_valid]), "invalid rings" #rings = [x for x in rings if x.is_valid] from shapely.geometry import MultiLineString mls1 = MultiLineString(rings1) mls2 = MultiLineString(rings2) try: print "calculating union (try the fast unary_union)" mm = unary_union([mls1, mls2]) except: print "FAILED" print "calculating union again (using the slow a.union(b))" mm = mls1.union(mls2) print "polygonize rings" newpolys = polygonize(mm) print "determine spatial relationship and plot new polys" for newpoly in newpolys:
def overlay(df1, df2, how, use_sindex=True): """Perform spatial overlay between two polygons. Currently only supports data GeoDataFrames with polygons. Implements several methods that are all effectively subsets of the union. Parameters ---------- df1 : GeoDataFrame with MultiPolygon or Polygon geometry column df2 : GeoDataFrame with MultiPolygon or Polygon geometry column how : string Method of spatial overlay: 'intersection', 'union', 'identity', 'symmetric_difference' or 'difference'. use_sindex : boolean, default True Use the spatial index to speed up operation if available. Returns ------- df : GeoDataFrame GeoDataFrame with new set of polygons and attributes resulting from the overlay """ allowed_hows = [ 'intersection', 'union', 'identity', 'symmetric_difference', 'difference', # aka erase ] if how not in allowed_hows: raise ValueError("`how` was \"%s\" but is expected to be in %s" % \ (how, allowed_hows)) if isinstance(df1, GeoSeries) or isinstance(df2, GeoSeries): raise NotImplementedError("overlay currently only implemented for GeoDataFrames") # Collect the interior and exterior rings rings1 = _extract_rings(df1) rings2 = _extract_rings(df2) mls1 = MultiLineString(rings1) mls2 = MultiLineString(rings2) # Union and polygonize try: # calculating union (try the fast unary_union) mm = unary_union([mls1, mls2]) except: # unary_union FAILED # see https://github.com/Toblerity/Shapely/issues/47#issuecomment-18506767 # calculating union again (using the slow a.union(b)) mm = mls1.union(mls2) newpolys = polygonize(mm) # determine spatial relationship collection = [] for fid, newpoly in enumerate(newpolys): cent = newpoly.representative_point() # Test intersection with original polys # FIXME there should be a higher-level abstraction to search by bounds # and fall back in the case of no index? if use_sindex and df1.sindex is not None: candidates1 = [x.object for x in df1.sindex.intersection(newpoly.bounds, objects=True)] else: candidates1 = [i for i, x in df1.iterrows()] if use_sindex and df2.sindex is not None: candidates2 = [x.object for x in df2.sindex.intersection(newpoly.bounds, objects=True)] else: candidates2 = [i for i, x in df2.iterrows()] df1_hit = False df2_hit = False prop1 = None prop2 = None for cand_id in candidates1: cand = df1.ix[cand_id] if cent.intersects(cand[df1.geometry.name]): df1_hit = True prop1 = cand break # Take the first hit for cand_id in candidates2: cand = df2.ix[cand_id] if cent.intersects(cand[df2.geometry.name]): df2_hit = True prop2 = cand break # Take the first hit # determine spatial relationship based on type of overlay hit = False if how == "intersection" and (df1_hit and df2_hit): hit = True elif how == "union" and (df1_hit or df2_hit): hit = True elif how == "identity" and df1_hit: hit = True elif how == "symmetric_difference" and not (df1_hit and df2_hit): hit = True elif how == "difference" and (df1_hit and not df2_hit): hit = True if not hit: continue # gather properties if prop1 is None: prop1 = pd.Series(dict.fromkeys(df1.columns, None)) if prop2 is None: prop2 = pd.Series(dict.fromkeys(df2.columns, None)) # Concat but don't retain the original geometries out_series = pd.concat([prop1.drop(df1._geometry_column_name), prop2.drop(df2._geometry_column_name)]) out_series.index = _uniquify(out_series.index) # Create a geoseries and add it to the collection out_series['geometry'] = newpoly collection.append(out_series) # Return geodataframe with new indicies return GeoDataFrame(collection, index=range(len(collection)))