def test_init(self): # Test empty. gvar = GeometryVariable() self.assertEqual(gvar.dtype, object) gvar = self.get_geometryvariable() self.assertIsInstance(gvar.get_masked_value(), MaskedArray) self.assertEqual(gvar.ndim, 1) # Test passing a "crs". gvar = self.get_geometryvariable(crs=WGS84(), name='my_geom', dimensions='ngeom') self.assertEqual(gvar.crs, WGS84()) # Test using lines. line1 = LineString([(0, 0), (1, 1)]) line2 = LineString([(1, 1), (2, 2)]) gvar = GeometryVariable(value=[line1, line2], dimensions='two') self.assertTrue(gvar.get_value()[1].almost_equals(line2)) self.assertEqual(gvar.geom_type, line1.geom_type) lines = MultiLineString([line1, line2]) lines2 = [lines, lines] for actual in [lines, lines2, lines]: gvar2 = GeometryVariable(value=actual, dimensions='ngeom') self.assertTrue(gvar2.get_value()[0].almost_equals(lines)) self.assertEqual(gvar2.geom_type, lines.geom_type) self.assertTrue(gvar2.shape[0] > 0) self.assertIsNone(gvar2.get_mask())
def to_python(self, value): """ This assumes the value has been preprocessed into a dictionary of the form: {'type': <geometry_type>, 'geometry': <raw_geometry>} """ if not value or isinstance(value, BaseGeometry): return value geometry_type = value['type'] geometry = value['geometry'] try: if geometry_type == 'esriGeometryPoint': if 'x' in geometry: data = json.loads(geometry) x, y = data['x'], data['y'] else: x, y = [float(val) for val in geometry.split(',')] return Point(x, y) elif geometry_type == 'esriGeometryMultipoint': data = json.loads(geometry) return MultiPoint([(p['0'], p['1']) for p in data['points']]) elif geometry_type == 'esriGeometryPolyline': data = json.loads(geometry) return MultiLineString([((l[0][0], l[0][1]), (l[1][0], l[1][1])) for l in data['paths']]) elif geometry_type == 'esriGeometryPolygon': data = json.loads(geometry) rings = [ LinearRing([(p[0], p[1]) for p in r]) for r in data['rings'] ] return Polygon([r for r in rings if not r.is_ccw], interiors=[r for r in rings if r.is_ccw]) elif geometry_type == 'esriGeometryEnvelope': if 'xmin' in geometry: data = json.loads(geometry) xmin, ymin, xmax, ymax = [ data[k] for k in ('xmin', 'ymin', 'xmax', 'ymax') ] else: xmin, ymin, xmax, ymax = [ float(val) for val in geometry.split(',') ] return MultiPoint([(xmin, ymin), (xmax, ymax)]).envelope else: raise ValueError except ValueError: raise ValidationError('Invalid geometry')
def fix_geom_type(row, expected_geom_type): # ESRI mixes geom_types within a given file. GC2 doesn't accept geojson with mixed types. When expecting multi features this ensures all features in a file are represented as Multi. if 'Multi' not in str( (row.geometry).geom_type) and expected_geom_type == 'MultiPolygon': geom = MultiPolygon([wkt.loads(str(row.geometry))]) elif 'Multi' not in str( (row.geometry).geom_type) and expected_geom_type == 'MultiLineString': geom = MultiLineString([wkt.loads(str(row.geometry))]) elif 'Multi' not in str( (row.geometry).geom_type) and expected_geom_type == 'MultiPoint': geom = MultiPoint([wkt.loads(str(row.geometry))]) else: geom = row.geometry return geom
def _get_multigeometry(self, element): # MultiTrack geoms = [] if element.tag == ('%sMultiTrack' % self.ns): tracks = element.findall("%sTrack" % self.ns) for track in tracks: self._get_geometry_spec(track) geoms.append(LineString(self._get_coordinates(track))) geom_types = {geom.geom_type for geom in geoms} if len(geom_types) > 1: return GeometryCollection(geoms) if 'LineString' in geom_types: return MultiLineString(geoms)
def test_init(self): # Test empty. gvar = GeometryVariable() self.assertEqual(gvar.dtype, object) gvar = self.get_geometryvariable() self.assertIsInstance(gvar.get_masked_value(), MaskedArray) self.assertEqual(gvar.ndim, 1) # The geometry variable should set itself as the representative geometry on its parent field if that parent does # not have a representative geometry set. self.assertIsNotNone(gvar.parent.geom) # Test with a parent that already has a geometry. field = Field() field.set_geom(GeometryVariable(name='empty')) gvar = self.get_geometryvariable(parent=field) self.assertEqual(field.geom.name, 'empty') self.assertIn(gvar.name, field) # Test passing a "crs". gvar = self.get_geometryvariable(crs=WGS84(), name='my_geom', dimensions='ngeom') self.assertEqual(gvar.crs, WGS84()) # Test using lines. line1 = LineString([(0, 0), (1, 1)]) line2 = LineString([(1, 1), (2, 2)]) gvar = GeometryVariable(value=[line1, line2], dimensions='two') self.assertTrue(gvar.get_value()[1].almost_equals(line2)) self.assertEqual(gvar.geom_type, line1.geom_type) lines = MultiLineString([line1, line2]) lines2 = [lines, lines] for actual in [lines, lines2, lines]: gvar2 = GeometryVariable(value=actual, dimensions='ngeom') self.assertTrue(gvar2.get_value()[0].almost_equals(lines)) self.assertEqual(gvar2.geom_type, lines.geom_type) self.assertTrue(gvar2.shape[0] > 0) self.assertIsNone(gvar2.get_mask())
def path_to_geos(path): """ """ import time start_time = time.time() log = [] DEBUG = False # path_verts, path_codes = zip(*list(path.iter_segments(curves=False))) path_verts, path_codes = path_segments(path, curves=False) path_verts = np.array(path_verts) path_codes = np.array(path_codes) if DEBUG: print 'codes:', path_codes verts_split_inds = np.where(path_codes == Path.MOVETO)[0] verts_split = np.split(path_verts, verts_split_inds, 0) codes_split = np.split(path_codes, verts_split_inds, 0) if DEBUG: print 'vs: ', `verts_split` if DEBUG: print 'cs: ', `codes_split` log.append('split done %s' % (time.time() - start_time)) collection = [] for path_verts, path_codes in zip(verts_split, codes_split): if len(path_verts) == 0: continue # XXX A path can be given which does not end with close poly, in that situation, we have to guess? if DEBUG: print 'pv: ', path_verts # XXX Implement a point if path_verts.shape[0] > 2 and (path_codes[-1] == Path.CLOSEPOLY or all(path_verts[0, :] == path_verts[-1, :])): if path_codes[-1] == Path.CLOSEPOLY: ipath2 = polygon.Polygon(path_verts[:-1, :]) else: ipath2 = polygon.Polygon(path_verts) else: ipath2 = linestring.LineString(path_verts) if (len(collection) > 0 and isinstance(collection[-1][0], polygon.Polygon) and isinstance(ipath2, polygon.Polygon) and collection[-1][0].contains(ipath2.exterior)): collection[-1][1].append(ipath2.exterior) else: # collection is a list of [exernal_poly, list_of_internal_polys] collection.append([ipath2, []]) log.append('collection done before while %s.' % (time.time() - start_time)) log.append('Len of collection %s.' % (len(collection))) res = [] for external_poly, internal_polys in collection: # print external_poly if len(internal_polys) > 0: # print internal_polys # XXX worry about islands within lakes poly = polygon.Polygon(external_poly.exterior, internal_polys) else: poly = external_poly res.append(poly) collection = res # if len(collection) > 1: # i = 0 # while i<len(collection)-1: # poly = collection[i] # poly2 = collection[i+1] # # # TODO Worry about islands within lakes # if isinstance(poly, polygon.Polygon) and isinstance(poly2, polygon.Polygon): # if poly.contains(poly2): # # XXX This is the slow bit! ## collection[i] = polygon.Polygon(poly.exterior, list(poly.interiors) + [poly2.exterior]) # collection.pop(i+1) # continue # else: # res.append([poly]) # i+=1 # # log.append('Post len of collection %s.' % (len(collection))) # log.append('collection done after while %s' % (time.time() - start_time)) if len(collection) == 1: result = collection else: if all([isinstance(geom, linestring.LineString) for geom in collection]): result = [MultiLineString(collection)] else: result = collection # if DEBUG: print 'geom: ', collection, type(collection) # raise NotImplementedError('The path given was not a collection of line strings, ' # 'nor a single polygon with interiors.') if (time.time() - start_time) > 1: print 'geos time %s' % (time.time() - start_time) print '\n'.join(log) # remove any zero area polygons result = filter(lambda geom: (isinstance(geom, polygon.Polygon) and geom.area != 0) or \ not isinstance(geom, polygon.Polygon), result) return result
def path_to_geos(path, force_ccw=False): """ Creates a list of Shapely geometric objects from a :class:`matplotlib.path.Path`. Args: * path A :class:`matplotlib.path.Path` instance. Kwargs: * force_ccw Boolean flag determining whether the path can be inverted to enforce ccw. Returns: A list of :class:`shapely.geometry.polygon.Polygon`, :class:`shapely.geometry.linestring.LineString` and/or :class:`shapely.geometry.multilinestring.MultiLineString` instances. """ # Convert path into numpy array of vertices (and associated codes) path_verts, path_codes = path_segments(path, curves=False) # Split into subarrays such that each subarray consists of connected # line segments based on the start of each one being marked by a # matplotlib MOVETO code. verts_split_inds = np.where(path_codes == Path.MOVETO)[0] verts_split = np.split(path_verts, verts_split_inds) codes_split = np.split(path_codes, verts_split_inds) # Iterate through the vertices generating a list of # (external_geom, [internal_polygons]) tuples. collection = [] for path_verts, path_codes in zip(verts_split, codes_split): if len(path_verts) == 0: continue # XXX A path can be given which does not end with close poly, in that # situation, we have to guess? # XXX Implement a point if (path_verts.shape[0] > 2 and (path_codes[-1] == Path.CLOSEPOLY or all(path_verts[0, :] == path_verts[-1, :]))): if path_codes[-1] == Path.CLOSEPOLY: geom = Polygon(path_verts[:-1, :]) else: geom = Polygon(path_verts) else: geom = LineString(path_verts) # If geom is a Polygon and is contained within the last geom in # collection, add it to its list of internal polygons, otherwise # simple append it as a new external geom. if (len(collection) > 0 and isinstance(collection[-1][0], Polygon) and isinstance(geom, Polygon) and collection[-1][0].contains(geom.exterior)): collection[-1][1].append(geom.exterior) else: collection.append((geom, [])) # Convert each (external_geom, [internal_polygons]) pair into a # a shapely Polygon that encapsulates the internal polygons, if the # external geom is a LineSting leave it alone. geom_collection = [] for external_geom, internal_polys in collection: if internal_polys: # XXX worry about islands within lakes geom = Polygon(external_geom.exterior, internal_polys) else: geom = external_geom # Correctly orientate the polygon (ccw) if force_ccw and not geom.exterior.is_ccw: geom = shapely.geometry.polygon.orient(geom) geom_collection.append(geom) # If the geom_collection only contains LineStrings combine them # into a single MultiLinestring. if geom_collection and all( isinstance(geom, LineString) for geom in geom_collection): geom_collection = [MultiLineString(geom_collection)] # Remove any zero area Polygons not_zero_poly = lambda geom: ( (isinstance(geom, Polygon) and not geom._is_empty and geom.area != 0 ) or not isinstance(geom, Polygon)) result = filter(not_zero_poly, geom_collection) return result
def calc_gz(GL_FILE='', WIDTH_FILE='', BASIN_FILE='', region='', N=0): #-- read the grounding lines and widths df_gl = gpd.read_file(GL_FILE) df_w = gpd.read_file(WIDTH_FILE) #-- read the basin file basins = gpd.read_file(BASIN_FILE) idx = basins.index[basins['NAME'] == region] #-- get polygon poly = basins['geometry'][idx[0]] #-- add a 5km buffer to find the corresponding GLs region_poly = poly.buffer(5e3) lines = [] dates = [] for i in range(len(df_gl)): #-- extract geometry to see if it's in region of interest ll = df_gl['geometry'][i] if ll.intersects(region_poly): lines.append(ll) dates.append(df_gl['FILENAME'][i].split("_")[2]) #-- get width lines ws = [] for i in range(len(df_w)): ws.append(df_w['geometry'][i]) widths = MultiLineString(ws) #-- get middle coordinates xlist = np.zeros(N) ylist = np.zeros(N) gz = np.zeros(N) date1_list = [None] * N date2_list = [None] * N ind_list = np.zeros(N, dtype=int) random.seed(11) for i in range(N): ind_list[i] = random.randrange(0, len(widths)) mid_pt = widths[ind_list[i]].interpolate(0.5, normalized=True) xx, yy = mid_pt.coords.xy xlist[i] = float(xx[0]) ylist[i] = float(yy[0]) gz[i] = widths[ind_list[i]].length #-- also get teh corresponding dates pt0 = widths[ind_list[i]].interpolate(0, normalized=True) pt1 = widths[ind_list[i]].interpolate(1, normalized=True) for l in range(len(lines)): if lines[l].distance(pt1) < 0.2: date1_list[i] = dates[l] elif lines[l].distance(pt0) < 0.2: date2_list[i] = dates[l] #-- write grounding zone widths to file outfile = os.path.join(os.path.dirname(GL_FILE), 'GZ_retreived-widths_{0}.csv'.format(region)) outfid = open(outfile, 'w') outfid.write('X (m),Y (m),width (km),date1,date2\n') for i in range(N): outfid.write('{0:.6f},{1:.6f},{2:.3f},{3},{4}\n'.\ format(xlist[i],ylist[i],gz[i]/1e3,date1_list[i],date2_list[i])) outfid.close() #-- plot a sample of points to check the grounding zones fig = plt.figure(1, figsize=(10, 8)) ax = fig.add_subplot(111) pp = PolygonPatch(poly, alpha=0.3, fc='lawngreen', ec='lawngreen', zorder=1) ax.add_patch(pp) for il in lines: xs, ys = il.coords.xy ax.plot(xs, ys, linewidth=0.4, alpha=0.8, color='k', zorder=2) for i in range(35): ip = random.randrange(0, N) #-- while distance to any of the previous points is less than 20km, #-- keep trying new indices (doesn't apply to 1st point) if i == 0: plot_pts = [Point(xlist[ip], ylist[ip])] else: pt = Point(xlist[ip], ylist[ip]) while (pt.distance(MultiPoint(plot_pts)) < 10e3): ip = random.randrange(0, N) pt = Point(xlist[ip], ylist[ip]) #-- now we can ensure the points aren't overlapping print("minimum distance to previous points: ", pt.distance(MultiPoint(plot_pts))) plot_pts.append(pt) #-- Now plot the transect for the given index lx, ly = widths[ind_list[ip]].coords.xy ax.plot(lx, ly, linewidth=2.0, alpha=1.0, color='red', zorder=3) ax.text(xlist[ip]+5e3,ylist[ip]+5e3,'{0:.1f}km'.format(gz[ip]/1e3),color='darkred',\ fontsize=6,fontweight='bold',bbox=dict(facecolor='mistyrose', alpha=0.5)) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) ax.set_title("Grounding Zone Width for {0}".format(region)) plt.tight_layout() plt.savefig(outfile.replace('.csv', '.pdf'), format='PDF') plt.close(fig)
def quarter(polygon): """Split the polygon into 4 line strings, classify the lines by their direction to determine cardinality. For each of the 4 lines split at the midpoint while preserving the original coordinates. Generate 4 polygons based on the diagram below. Eg, the NW polygon is the convex_hull of the following lines - first half of the north line - first half of the center vertical line - first half of the center horizontal line - second half of the west line 1 2 --------------------------- | | | 8 | NW | NE | 3 | |9 | | 11 | 12 | --------------------------- | | | | |10 | 7 | SW | SE | 4 | | | --------------------------- 6 5 :param polygon: :type polygon: LabeledPolygon :return: list of LabeledPolygons :rtype: []LabeledPolygon """ label, polygon = polygon.label, polygon.polygon try: if polygon.type == 'MultiPolygon': logging.debug( "Multipolygon found, attemping to convert to polygon...") polygon = unary_union(polygon) if polygon.type == 'MultiPolygon': raise Exception( "Unable to convert multipolygon to polygon (disjoint)") polygon = orient(polygon, sign=-1.0) # set orientation to clockwise lines = split_to_lines(polygon) labeled_lines = classify_lines(lines) north_lines = split_line( linemerge( MultiLineString( [l.line for l in labeled_lines if l.label == "N"]))) east_lines = split_line( linemerge( MultiLineString( [l.line for l in labeled_lines if l.label == "E"]))) south_lines = split_line( linemerge( MultiLineString( [l.line for l in labeled_lines if l.label == "S"]))) west_lines = split_line( linemerge( MultiLineString( [l.line for l in labeled_lines if l.label == "W"]))) # split the inner lines at their intersection center_vertical = LineString( (north_lines[0].coords[-1], south_lines[0].coords[-1]) ) # straight line between midpoint of north line and midpoint of south line center_horizontal = LineString( (west_lines[0].coords[-1], east_lines[0].coords[-1]) ) # straight line between midpoint of west line and midpoint of east line center_verticals, center_horizontals = split_intersection( center_vertical, center_horizontal) nw_polygon = LabeledPolygon(label="NW" + label, polygon=MultiLineString( (north_lines[0], center_verticals[0], center_horizontals[0], west_lines[1])).convex_hull) ne_polygon = LabeledPolygon(label="NE" + label, polygon=MultiLineString( (north_lines[1], center_verticals[0], center_horizontals[1], east_lines[0])).convex_hull) sw_polygon = LabeledPolygon(label="SW" + label, polygon=MultiLineString( (south_lines[1], center_verticals[1], center_horizontals[0], west_lines[0])).convex_hull) se_polygon = LabeledPolygon(label="SE" + label, polygon=MultiLineString( (south_lines[0], center_verticals[1], center_horizontals[1], east_lines[1])).convex_hull) return [nw_polygon, ne_polygon, sw_polygon, se_polygon] except Exception as e: logging.error("Unable to process polygon! {}; {}".format(polygon, e))
def intersectionsGridFrontiere(P, X, Y): """Calcule la trace de la grille cartésienne définie par X, Y sur le Polyligne P autrement dit les intersections de P avec les droites - x = X[i] verticales et - y = Y[j] horizontales qui représentent la grille. :param P : un polyligne np.ndarray de shape (n,3) ou (n,2). La 3-eme dimension est ignorée. :param X, Y : la grille cartésienne. X et Y sont des np.ndarray de shape (nx,1) et (ny,1) qui représentent les abscisses et les ordonnées de la grille - On suppose que X et Y sont croissants (i) - On suppose également que la grille recouvre entièrement P, et déborde, i.e. min(X) < xmin(P) <= xmax(P) < max(X) min(Y) < ymin(P) <= ymax(P) < max(Y) :return PD: np.ndarray((npd,2)) contenant les points """ # nx, ny = len(X), len(Y) # for x in X : plt.plot(ny*[x], Y, 'y-', linewidth=0.3,) # for y in Y : plt.plot(X, nx*[y], 'y-', linewidth=0.3) # plt.plot(P[:,0],P[:,1], 'r.') # P = LineString(P) P = LinearRing(P) # x,y = P.xy # plt.plot(x,y,'g-') # plt.axes().set_aspect('equal') # debug(P) (minx, miny, maxx, maxy) = P.bounds # debug(P.bounds) #Les numeros de droites verticales intersectant P iX = np.where(np.logical_and(minx < X, X < maxx))[0] # px = X[iX]#une croix pour marquer les droite concernées (graphique) # plt.plot(px, len(px)*[min(Y)], 'rx') #les droites verticales ms = [((X[i], Y[0]), (X[i], Y[-1])) for i in iX] #Les numeros de droites horizontales intersectant P iY = np.where(np.logical_and(miny < Y, Y < maxy))[0] # px = Y[iY]#une croix pour marquer les droite concernées # plt.plot(len(px)*[max(X)], px, 'rx') # plt.legend() # plt.show() #Les droites horizontales concernées par P ms.extend([((X[0], Y[i]), (X[-1], Y[i])) for i in iY]) D = MultiLineString(ms) #La famille des droites de la grille # debug(len(D)) #convex_hull pour réordonner les points, en faire un polygone #array_interface pour recuperer les data pures numpy PD = P.intersection(D) #.array_interface() # debug(type(PD)) D = [P.project(pd) for pd in PD] #abscisse curviligne des points d'intersection D.sort() PD = [P.interpolate(d).xy for d in D] #Les points dans l'ordre # PD = PD.array_interface() # exit() #.convex_hull.exterior # shape = PD['shape'] #PD.reshape(...) : si le tableau PD doit être recopié => en silence #PD = array(PD['data']).reshape(shape) # PD = array(PD['data']) PD = array(PD) #PD.shape=... : si le tableau PD doit être recopié => erreur # PD.shape = shape return PD