def geom_to_bbox(geom, min_area=0): from kartograph.geometry import BBox from shapely.geometry import MultiPolygon if True or min_area == 0 or not isinstance(geom, MultiPolygon): # if no minimum area ratio is set or the geometry # is not a multipart geometry, we simply use the # full bbox minx, miny, maxx, maxy = geom.bounds return BBox(width=maxx - minx, height=maxy - miny, left=minx, top=miny) else: # for multipart geometry we use only the bbox of # the 'biggest' sub-geometries, depending on min_area bbox = BBox() areas = [] bb = [] for polygon in geom.geoms: areas.append(polygon.area) max_a = max(areas) for i in range(len(geom.geoms)): a = areas[i] if a < max_a * min_area: # ignore this sub polygon since it is too small continue bb.append(geom.geoms[i].bounds) for b in bb: bbox.update((b[0], b[2])) bbox.update((b[1], b[2])) bbox.update((b[0], b[3])) bbox.update((b[1], b[3])) return bbox
def shape2polygon(shp, ignore_holes=False, min_area=False, proj=None): """ converts a shapefile polygon to geometry.MultiPolygon """ # from kartograph.geometry import MultiPolygon from shapely.geometry import Polygon, MultiPolygon from kartograph.geometry.utils import is_clockwise parts = shp.parts[:] parts.append(len(shp.points)) exteriors = [] holes = [] for j in range(len(parts) - 1): pts = shp.points[parts[j]:parts[j + 1]] if shp.shapeType == 15: # remove z-coordinate from PolygonZ contours (not supported) for k in range(len(pts)): pts[k] = pts[k][:2] if proj: project_coords(pts, proj) cw = is_clockwise(pts) if cw: exteriors.append(pts) else: holes.append(pts) if ignore_holes: holes = None if len(exteriors) == 1: poly = Polygon(exteriors[0], holes) elif len(exteriors) > 1: # use multipolygon, but we need to assign the holes to the right # exteriors from kartograph.geometry import BBox used_holes = set() polygons = [] for ext in exteriors: bbox = BBox() my_holes = [] for pt in ext: bbox.update(pt) for h in range(len(holes)): if h not in used_holes: hole = holes[h] if bbox.check_point(hole[0]): # this is a very weak test but it should be sufficient used_holes.add(h) my_holes.append(hole) polygons.append(Polygon(ext, my_holes)) if min_area: # compute maximum area max_area = 0 for poly in polygons: max_area = max(max_area, poly.area) # filter out polygons that are below min_area * max_area polygons = [poly for poly in polygons if poly.area >= min_area * max_area] poly = MultiPolygon(polygons) else: raise KartographError('shapefile import failed - no outer polygon found') return poly
def shape2geometry(shp, ignore_holes=False, min_area=False, bbox=False, proj=None): if shp is None: return None if bbox and shp.shapeType != 1: if proj: left, top = proj(shp.bbox[0], shp.bbox[1], inverse=True) right, btm = proj(shp.bbox[2], shp.bbox[3], inverse=True) else: left, top, right, btm = shp.bbox sbbox = BBox(left=left, top=top, width=right - left, height=btm - top) if not bbox.intersects(sbbox): # ignore the shape if it's not within the bbox return None if shp.shapeType in (5, 15): # multi-polygon geom = shape2polygon(shp, ignore_holes=ignore_holes, min_area=min_area, proj=proj) elif shp.shapeType in (3, 13): # line geom = shape2line(shp, proj=proj) elif shp.shapeType == 1: # point geom = shape2point(shp, proj=proj) else: raise KartographError('unknown shape type (%d)' % shp.shapeType) return geom
def get_features(self, filter=None, bbox=None, ignore_holes=False, charset='utf-8', min_area=0): # Eventually we convert the bbox list into a proper BBox instance if bbox is not None and not isinstance(bbox, BBox): bbox = BBox(bbox[2] - bbox[0], bbox[3] - bbox[1], bbox[0], bbox[1]) mode = self.mode if mode in ('line', 'polygon'): coords = [] features = [] for row in self.cr: attrs = dict() for i in range(len(row)): key = self.header[i] if key == self.xfield: x = float(row[i]) elif key == self.yfield: y = float(row[i]) else: attrs[key] = row[i] if self.proj is not None: # inverse project coord x, y = self.proj(x, y, inverse=True) if mode == 'points': features.append(create_feature(Point(x, y), attrs)) else: coords.append((x, y)) if mode == 'line': features.append(create_feature(LineString(coords), dict())) elif mode == 'polygon': features.append(create_feature(Polygon(coords), dict())) return features
def get_features(self, attr=None, filter=None, bbox=None, ignore_holes=False, min_area=False, charset='utf-8'): """ ### Get features """ res = [] # We will try these encodings.. known_encodings = ['utf-8', 'latin-1', 'iso-8859-2', 'iso-8859-15'] try_encodings = [charset] for enc in known_encodings: if enc != charset: try_encodings.append(enc) # Eventually we convert the bbox list into a proper BBox instance if bbox is not None and not isinstance(bbox, BBox): bbox = BBox(bbox[2] - bbox[0], bbox[3] - bbox[1], bbox[0], bbox[1]) ignored = 0 for i in range(0, len(self.recs)): # Read all record attributes drec = {} for j in range(len(self.attributes)): drec[self.attributes[j]] = self.recs[i][j] # For each record that is not filtered.. if filter is None or filter(drec): props = {} # ..we try to decode the attributes (shapefile charsets are arbitrary) for j in range(len(self.attributes)): val = self.recs[i][j] decoded = False if isinstance(val, str): for enc in try_encodings: try: val = val.decode(enc) decoded = True break except: if verbose: print 'warning: could not decode "%s" to %s' % (val, enc) if not decoded: raise KartographError('having problems to decode the input data "%s"' % val) if isinstance(val, (str, unicode)): val = val.strip() props[self.attributes[j]] = val # Read the shape from the shapefile (can take some time..).. shp = self.get_shape(i) # ..and convert the raw shape into a shapely.geometry geom = shape2geometry(shp, ignore_holes=ignore_holes, min_area=min_area, bbox=bbox, proj=self.proj) if geom is None: ignored += 1 self.forget_shape(i) continue # Finally we construct the map feature and append it to the # result list feature = create_feature(geom, props) res.append(feature) if bbox is not None and ignored > 0 and verbose: print "-ignoring %d shapes (not in bounds %s )" % (ignored, bbox) return res
def get_features(self, attr=None, filter=None, bbox=None, verbose=False): """ returns a list of features matching to the attr -> value pair """ from kartograph.geometry import Feature, BBox res = [] if bbox is not None: bbox = BBox(bbox[2] - bbox[0], bbox[3] - bbox[1], bbox[0], bbox[1]) ignored = 0 for i in range(0, len(self.recs)): drec = {} for j in range(len(self.attributes)): drec[self.attributes[j]] = self.recs[i][j] if filter is None or filter(drec): props = {} for j in range(len(self.attributes)): val = self.recs[i][j] if isinstance(val, (str, unicode)): val = val.strip() props[self.attributes[j]] = val shp = self.get_shape(i) if shp.shapeType in (5, 15): # multi-polygon geom = points2polygon(shp) elif shp.shapeType == 3: # line geom = points2line(shp) else: raise KartographError( 'unknown shape type (%d) in shapefile %s' % (shp.shapeType, self.shpSrc)) if bbox is not None and not bbox.intersects(geom.bbox()): ignored += 1 continue # ignore if not within bounds feature = Feature(geom, props) res.append(feature) if bbox is not None and ignored > 0 and verbose: print "[%s] ignored %d shapes (not in bounds)" % (basename( self.shpSrc), ignored) return res
def get_features(self, attr=None, filter=None, bbox=None, verbose=False): """ returns a list of features matching to the attr -> value pair """ from kartograph.geometry import Feature, BBox res = [] if bbox is not None: bbox = BBox(bbox[2] - bbox[0], bbox[3] - bbox[1], bbox[0], bbox[1]) ignored = 0 for i in range(0, len(self.recs)): drec = {} for j in range(len(self.attributes)): drec[self.attributes[j]] = self.recs[i][j] if filter is None or filter(drec): props = {} for j in range(len(self.attributes)): val = self.recs[i][j] if isinstance(val, (str, unicode)): val = val.strip() props[self.attributes[j]] = val shp = self.get_shape(i) if shp.shapeType in (5, 15): # multi-polygon geom = points2polygon(shp) elif shp.shapeType == 3: # line geom = points2line(shp) else: raise KartographError("unknown shape type (%d) in shapefile %s" % (shp.shapeType, self.shpSrc)) if bbox is not None and not bbox.intersects(geom.bbox()): ignored += 1 continue # ignore if not within bounds feature = Feature(geom, props) res.append(feature) if bbox is not None and ignored > 0 and verbose: print "[%s] ignored %d shapes (not in bounds)" % (basename(self.shpSrc), ignored) return res
return (x, y) for pjname in projections: projections[pjname].name = pjname if __name__ == '__main__': import sys # some class testing #p = LAEA(52.0,10.0) #x,y = p.project(50,5) #assert (round(x,2),round(y,2)) == (3962799.45, -2999718.85), 'LAEA proj error' from kartograph.geometry import BBox print Proj.fromXML(Robinson(lat0=3, lon0=4).toXML(), projections) Robinson(lat0=3, lon0=4) for pj in projections: Proj = projections[pj] bbox = BBox() try: proj = Proj(lon0=60) print proj.project(0, 0) print proj.world_bounds(bbox) print proj.toXML() except: print 'Error', pj print sys.exc_info()[0] raise
def shape2polygon(shp, ignore_holes=False, min_area=False, proj=None): """ converts a shapefile polygon to geometry.MultiPolygon """ # ignore_holes=True # from kartograph.geometry import MultiPolygon from shapely.geometry import Polygon, MultiPolygon from kartograph.geometry.utils import is_clockwise parts = shp.parts[:] parts.append(len(shp.points)) exteriors = [] rep_point = None holes = [] # print 'shp.the_feat_name={0}'.format(shp.the_feat_name) # print 'shp.represenative_points={0}'.format(shp.representative_point()) for j in range(len(parts) - 1): pts = shp.points[parts[j]:parts[j + 1]] if shp.shapeType == 15: # remove z-coordinate from PolygonZ contours (not supported) for k in range(len(pts)): pts[k] = pts[k][:2] if proj and shp.alreadyProj is False: project_coords(pts, proj, rep_point=rep_point) if shp.bounding: # print 'Already proj, proj exists' shp.alreadyProj = True elif shp.alreadyProj is False: if shp.bounding: shp.alreadyProj = True #else: # print 'shp.bounding={0}'.format(shp.bounding) # print 'Already proj, no proj exists' cw = is_clockwise(pts) if cw: exteriors.append(pts) else: holes.append(pts) if ignore_holes: print 'ignoring holes' holes = None # if len(holes) > 0: # print '\tThere are {0} holes'.format(len(holes)) if len(exteriors) == 1: # print 'Single polygon, {0}'.format(shp.the_feat_name) poly = Polygon(exteriors[0], holes) elif len(exteriors) > 1: # print 'Multipolygon, {0}'.format(shp.the_feat_name) # use multipolygon, but we need to assign the holes to the right # exteriors from kartograph.geometry import BBox used_holes = set() polygons = [] for ext in exteriors: bbox = BBox() my_holes = [] for pt in ext: bbox.update(pt) for h in range(len(holes)): if h not in used_holes: hole = holes[h] if bbox.check_point(hole[0]): # this is a very weak test but it should be sufficient used_holes.add(h) my_holes.append(hole) polygons.append(Polygon(ext, my_holes)) if min_area: # compute maximum area max_area = 0 for poly in polygons: max_area = max(max_area, poly.area) # filter out polygons that are below min_area * max_area polygons = [ poly for poly in polygons if poly.area >= min_area * max_area ] poly = MultiPolygon(polygons) else: # return None raise KartographError( 'shapefile import failed - no outer polygon found') # print 'poly={0}'.format(poly) return poly
def get_features(self, attr=None, filter=None, bbox=None, ignore_holes=False, min_area=False, charset='utf-8', bounding=False, bounding_geom=None, contained_geom=None): """ ### Get features """ res = [] max_intersect = 0 #if contained_geom is None: # print '\t\tcontained_geom is None' #if bounding_geom is None: # print '\t\tbounding_geom is None' # We will try these encodings.. known_encodings = ['utf-8', 'latin-1', 'iso-8859-2', 'iso-8859-15'] try_encodings = [charset] for enc in known_encodings: if enc != charset: try_encodings.append(enc) # Eventually we convert the bbox list into a proper BBox instance if bbox is not None and not isinstance(bbox, BBox): bbox = BBox(bbox[2] - bbox[0], bbox[3] - bbox[1], bbox[0], bbox[1]) ignored = 0 #print 'len(self.recs)={0}'.format(len(self.recs)) for i in range(0, len(self.recs)): # Read all record attributes drec = {} for j in range(len(self.attributes)): drec[self.attributes[j]] = self.recs[i][j] # For each record that is not filtered.. is_nameless = True the_feat_name = '' # print drec if 'NAME' in drec: the_feat_name = drec['NAME'] elif 'FULLNAME' in drec: the_feat_name = drec['FULLNAME'] if len(the_feat_name.strip()) > 0: is_nameless = False desired_geom = False drec['DESIRED_GEOM'] = False if bounding_geom is not None: # Check if we want it to intersect shp = self.get_shape(i) shp.bounding = bounding shp.the_feat_name = the_feat_name geom = self.get_geom(i, ignore_holes=ignore_holes, min_area=min_area, bbox=bbox, proj=self.proj) #(shape2geometry(shp, ignore_holes=ignore_holes, min_area=min_area, bbox=bbox, proj=self.proj) if geom is None: ignored += 1 continue intersect_geom = bounding_geom.intersection(geom) # if intersect_geom.area>0: # print 'intersect_geom.area={0}'.format(intersect_geom.area) if intersect_geom.area >= self.intersect_tol * geom.area: desired_geom = True # print 'Found intersecting feature {0}'.format(the_feat_name) # Check for sufficient intersection to add places automatically drec['DESIRED_GEOM'] = desired_geom if filter is None or filter(drec): #if contained_geom is not None: # print '\tIn for the_feat_name {0}'.format(drec['NAME']) props = {} # ..we try to decode the attributes (shapefile charsets are arbitrary) for j in range(len(self.attributes)): val = self.recs[i][j] decoded = False if isinstance(val, str): for enc in try_encodings: try: val = val.decode(enc) decoded = True break except: if verbose: print 'warning: could not decode "%s" to %s' % ( val, enc) if not decoded: raise KartographError( 'having problems to decode the input data "%s"' % val) if isinstance(val, (str, unicode)): val = val.strip() props[self.attributes[j]] = val if bounding_geom is not None: # print 'type(bounding_geom)={0}'.format(type(bounding_geom)) x = bounding_geom.intersection(geom) if x.area < self.intersect_tol * geom.area: #print 'Name: {0} does not intersect'.format(shp.the_feat_name) ignored += 1 self.forget_shape(i) continue else: ignored += 0 # print 'Name: {0} intersects'.format(shp.the_feat_name) else: # If we didn't already set the shape and geom above, we set it here instead shp = self.get_shape(i) shp.bounding = bounding shp.the_feat_name = the_feat_name # ..and convert the raw shape into a shapely.geometry geom = self.get_geom(i, ignore_holes=ignore_holes, min_area=min_area, bbox=bbox, proj=self.proj) #shape2geometry(shp, ignore_holes=ignore_holes, min_area=min_area, bbox=bbox, proj=self.proj) if geom is None: ignored += 1 continue if contained_geom is not None: # Add a circle if no good at the end after we get all the good features #print 'Checking county {0}'.format(drec['NAME']) # Find if it's the most intersecting of the geometries with # contained_geom (which should really be contained_geom but haven't # changed yet) curr_intersect = contained_geom.intersection(geom) if curr_intersect.area == 0: # print '\tfail: curr_intersect.area={0}'.format(curr_intersect.area) ignored += 1 self.forget_shape(i) #continue else: # Set this to be the new intersection level #print '\tNew largest area intersection, area={0}'.format(curr_intersect.area) max_intersect = curr_intersect.area feature = create_feature(geom, props) res.append(feature) #continue else: #print 'Constructing feature {0}'.format(drec['NAME']) feature = create_feature(geom, props) self.feature = feature res.append(feature) if bbox is not None and ignored > 0 and verbose: print "-ignoring %d shapes (not in bounds %s )" % (ignored, bbox) #self.proj=None # print 'res={0}'.format(res) # Add a feature consisting of a circle around the contained_geom if it's too small # if contained_geom is not None and bounding_geom is None: # highlight_circ=self.get_highlight_circle(res, contained_geom) # if highlight_circ is not None: # #print('\tAdding a highlight_circ') # # Create and append feature # curr_props={'STATEFP':'00', 'COUNTYFP': '000', 'NAME': 'HighlightThePlace'} # feature=create_feature(highlight_circ, curr_props) # res.append(feature) return res