def overlap_summary(groupbydata, valuedata, fieldmapping=[]): # prep data1,data2 = groupbydata,valuedata if fieldmapping: aggfields,aggtypes = zip(*fieldmapping) aggfunctions = dict([("count",len), ("sum",sum), ("max",max), ("min",min), ("average",lambda seq: sum(seq)/float(len(seq)) ) ]) # create spatial index if not hasattr(data1, "spindex"): data1.create_spatial_index() if not hasattr(data2, "spindex"): data2.create_spatial_index() # create new new = GeoTable() new.fields = list(data1.fields) if fieldmapping: for aggfield,aggtype in fieldmapping: new.fields.append(aggfield) # for each groupby feature for i,feat in enumerate(data1.quick_overlap(data2.bbox)): geom = geoj2shapely(feat.geometry) geom = supershapely(geom) matches = [] # get all value features that intersect for otherfeat in data2.quick_overlap(feat.bbox): othergeom = geoj2shapely(otherfeat.geometry) if geom.intersects(othergeom): matches.append(otherfeat) # make newrow from original row newrow = list(feat.row) # if any matches if matches: def make_number(value): try: return float(value) except: return None # add summary values to newrow based on fieldmapping for aggfield,aggtype in fieldmapping: values = [otherfeat[aggfield] for otherfeat in matches] if aggtype in ("sum","max","min","average"): # only consider number values if numeric stats values = [make_number(value) for value in values if make_number(value) != None] aggregatefunc = aggfunctions[aggtype] summaryvalue = aggregatefunc(values) newrow.append(summaryvalue) # otherwise, add empty values else: newrow.extend(("" for _ in fieldmapping)) # write feature to output new.add_feature(newrow, feat.geometry) return new
def vector_clean(data, tolerance=0): """Cleans the vector data of unnecessary clutter such as repeat points or closely related points within the distance specified in the 'tolerance' parameter. Also tries to fix any broken geometries, dropping any unfixable ones. Adds the resulting cleaned data to the layers list. """ # create new file outfile = GeoTable() outfile.fields = list(data.fields) # clean for feat in data: shapelyobj = geoj2shapely(feat.geometry) # try fixing invalid geoms if not shapelyobj.is_valid: if "Polygon" in shapelyobj.type: # fix bowtie polygons shapelyobj = shapelyobj.buffer(0.0) # remove repeat points (tolerance=0) # (and optionally smooth out complex shapes, tolerance >= 1) shapelyobj = shapelyobj.simplify(tolerance) # if still invalid, do not add to output if not shapelyobj.is_valid: continue # write to file geoj = shapely2geoj(shapelyobj) outfile.add_feature(feat.row, geoj) return outfile
def vector_spatialjoin(data1, data2, joincondition, radius=None): """Possible joinconditions: intersects, within, contains, crosses, touches, equals Also: distance """ # create spatial index if not hasattr(data1, "spindex"): data1.create_spatial_index() if not hasattr(data2, "spindex"): data2.create_spatial_index() # create new new = GeoTable() new.fields = list(data1.fields) new.fields.extend(data2.fields) # intersect each feat in first with all others if joincondition in ("distance", ): new.fields.append("DISTANCE") if joincondition == "distance" and radius is None: raise Exception("radius arg must be set when using distance mode") maxnum = 9223372036854775807 for feat in data1.quick_nearest(data2.bbox, n=maxnum): geom = geoj2shapely(feat.geometry) matchtest = getattr(geom, "distance") for otherfeat in data2.quick_nearest(feat.bbox, n=maxnum): othergeom = geoj2shapely(otherfeat.geometry) match = matchtest(othergeom) if match <= radius: joined = list(feat.row) joined.extend(otherfeat.row) joined.append(match) print "match", match new.add_feature(joined, feat.geometry) else: for feat in data1.quick_overlap(data2.bbox): geom = geoj2shapely(feat.geometry) matchtest = getattr(geom, joincondition) for otherfeat in data2.quick_overlap(feat.bbox): othergeom = geoj2shapely(otherfeat.geometry) match = matchtest(othergeom) if match: joined = list(feat.row) joined.extend(otherfeat.row) print "match", len(joined) new.add_feature(joined, feat.geometry) return new
def vector_spatialjoin(data1, data2, joincondition, radius=None): """Possible joinconditions: intersects, within, contains, crosses, touches, equals Also: distance """ # create spatial index if not hasattr(data1, "spindex"): data1.create_spatial_index() if not hasattr(data2, "spindex"): data2.create_spatial_index() # create new new = GeoTable() new.fields = list(data1.fields) new.fields.extend(data2.fields) # intersect each feat in first with all others if joincondition in ("distance",): new.fields.append("DISTANCE") if joincondition == "distance" and radius is None: raise Exception("radius arg must be set when using distance mode") maxnum = 9223372036854775807 for feat in data1.quick_nearest(data2.bbox, n=maxnum): geom = geoj2shapely(feat.geometry) matchtest = getattr(geom, "distance") for otherfeat in data2.quick_nearest(feat.bbox, n=maxnum): othergeom = geoj2shapely(otherfeat.geometry) match = matchtest(othergeom) if match <= radius: joined = list(feat.row) joined.extend(otherfeat.row) joined.append(match) print "match", match new.add_feature(joined, feat.geometry) else: for feat in data1.quick_overlap(data2.bbox): geom = geoj2shapely(feat.geometry) matchtest = getattr(geom, joincondition) for otherfeat in data2.quick_overlap(feat.bbox): othergeom = geoj2shapely(otherfeat.geometry) match = matchtest(othergeom) if match: joined = list(feat.row) joined.extend(otherfeat.row) print "match", len(joined) new.add_feature(joined, feat.geometry) return new
def _to_centroids(data): """create one centroid point for each multi geometry part""" # create new file outfile = GeoTable() outfile.fields = list(data.fields) # loop features for feat in data: if feat.geometry["type"] != "Point": shapelypoint = geoj2shapely(feat.geometry).centroid geoj = shapely2geoj(shapelypoint) outfile.add_feature(feat.row, geoj) return outfile
def vector_buffer(data, dist_expression): # buffer and change each geojson dict in-place new = GeoTable() new.fields = list(data.fields) for feat in data: geom = geoj2shapely(feat.geometry) dist = eval(dist_expression) buffered = geom.buffer(dist) if not buffered.is_empty: geoj = shapely2geoj(buffered) geoj["type"] = buffered.type new.add_feature(feat.row, geoj) # change data type to polygon new.type = "Polygon" return new
def _to_multicentroids(data): """create multiple centroid points for each multi geometry part""" # create new file outfile = GeoTable() outfile.fields = list(data.fields) # loop features if "LineString" in data.type or "Polygon" in data.type: for feat in data: if "Multi" in feat.geometry["type"]: multishape = geoj2shapely(feat.geometry) for geom in multishape.geoms: shapelypoint = geom.centroid geoj = shapely2geoj(shapelypoint) outfile.add_feature(feat.row, geoj) else: shapelypoint = geoj2shapely(feat.geometry).centroid geoj = shapely2geoj(shapelypoint) outfile.add_feature(feat.row, geoj) return outfile else: return data.copy()
def overlap_summary(groupbydata, valuedata, fieldmapping=[]): # prep data1, data2 = groupbydata, valuedata if fieldmapping: aggfields, aggtypes = zip(*fieldmapping) aggfunctions = dict([("count", len), ("sum", sum), ("max", max), ("min", min), ("average", lambda seq: sum(seq) / float(len(seq)))]) # create spatial index if not hasattr(data1, "spindex"): data1.create_spatial_index() if not hasattr(data2, "spindex"): data2.create_spatial_index() # create new new = GeoTable() new.fields = list(data1.fields) if fieldmapping: for aggfield, aggtype in fieldmapping: new.fields.append(aggfield) # for each groupby feature for i, feat in enumerate(data1.quick_overlap(data2.bbox)): geom = geoj2shapely(feat.geometry) geom = supershapely(geom) matches = [] # get all value features that intersect for otherfeat in data2.quick_overlap(feat.bbox): othergeom = geoj2shapely(otherfeat.geometry) if geom.intersects(othergeom): matches.append(otherfeat) # make newrow from original row newrow = list(feat.row) # if any matches if matches: def make_number(value): try: return float(value) except: return None # add summary values to newrow based on fieldmapping for aggfield, aggtype in fieldmapping: values = [otherfeat[aggfield] for otherfeat in matches] if aggtype in ("sum", "max", "min", "average"): # only consider number values if numeric stats values = [ make_number(value) for value in values if make_number(value) != None ] aggregatefunc = aggfunctions[aggtype] summaryvalue = aggregatefunc(values) newrow.append(summaryvalue) # otherwise, add empty values else: newrow.extend(("" for _ in fieldmapping)) # write feature to output new.add_feature(newrow, feat.geometry) return new
def vector_to_polygons(data, polytype="convex hull"): # create new file outfile = GeoTable() outfile.fields = list(data.fields) if polytype == "convex hull": for feat in data: shapelygeom = geoj2shapely(feat.geometry) convex = shapelygeom.convex_hull geoj = shapely2geoj(convex) # sometimes the convex hull is only a line or point # but we cannot mix shapetypes, so exclude those if geoj["type"] == "Polygon": outfile.add_feature(feat.row, geoj) return outfile elif polytype == "delauney triangles": if "Point" in data.type: def get_flattened_points(): for feat in data: if "Multi" in feat.geometry["type"]: for point in feat.geometry["coordinates"]: yield point else: yield feat.geometry["coordinates"] triangles = pytess.triangulate(get_flattened_points()) # triangle polygons are between multiple existing points, # so do not inherit any attributes for tri in triangles: geoj = {"type": "Polygon", "coordinates": [tri] } row = ["" for field in outfile.fields] outfile.add_feature(row, geoj) return outfile else: raise Exception("Delauney triangles can only be made from point data") elif polytype == "voronoi polygons": if "Point" in data.type: def get_flattened_points(): for feat in data: if "Multi" in feat.geometry["type"]: for point in feat.geometry["coordinates"]: yield point else: yield feat.geometry["coordinates"] results = pytess.voronoi(get_flattened_points()) # return new file with polygon geometries for midpoint,polygon in results: geoj = {"type": "Polygon", "coordinates": [polygon] } row = ["" for field in outfile.fields] outfile.add_feature(row, geoj) return outfile else: raise Exception("Voronoi polygons can only be made from point data") elif polytype == "enclose lines": if "LineString" in data.type: shapelylines = (geoj2shapely(feat.geometry) for feat in data) for polygon in shapely.ops.polygonize(shapelylines): geoj = shapely2geoj(polygon) row = ["" for field in outfile.fields] outfile.add_feature(row, geoj) return outfile else: raise Exception("Enclose lines can only be done on line data")