def convert_polygons(feature): if getattr(feature, 'geometry', None): if isinstance(feature.geometry, Polygon): feature.geometry = orient(feature.geometry, 1.0) elif isinstance(feature.geometry, MultiPolygon): polys = [] for p in feature.geometry.geoms: polys.append(orient(p, 1.0)) mp = MultiPolygon(polys) feature.geometry = mp if getattr(feature, 'features', None): for f in feature.features(): convert_polygons(f)
def get_as_3d_object(self, z=0, extrude_height=0, include_bottom_face=False, material_param=None, extrude_height_param=None, extrude_height_param_transform=None): """ Generates an Object3D from the junction. :param z: z coordinate of junction :param extrude_height: distance by which to extrude the junction :param include_bottom_face: whether to include the bottom face of the extruded geometry. :param material_param: generic parameter to use to override material, if present :param extrude_height_param: generic parameter to use to override extrude height, if present :param extrude_height_param_transform: function to apply to extrude_height_param values. Defaults to str->float conversion. :return: Object3D :type z: float :type extrude_height: float :type include_bottom_face: bool :type material_param: str :type extrude_height_param: str """ if self.shape.is_empty: return None if extrude_height_param is not None and extrude_height_param in self.params: if extrude_height_param_transform is None: extrude_height_param_transform = lambda x: float(x) if x is not None else extrude_height extrude_height = extrude_height_param_transform(self.params[extrude_height_param]) material = self.params.get(material_param, "junction") return _Utils.Object3D.from_shape(orient(self.shape), self.id, "junction", z=z, extrude_height=extrude_height, include_bottom_face=include_bottom_face)
def _gen_frame(self): ''' generate the frame by intersecting the contacts with the body ''' for layer in self.ps.keys(): if layer == '0': self.body = Polygon(self.ps[layer][0]) else: for poly in self.ps[layer]: ohm = Polygon(poly) inter = self.body.intersection(ohm) self.body = self.body.union(inter) self.body = orient(self.body, -1) # order clockwise xs, ys = self.body.exterior.coords.xy layers = [0] * (len(xs) - 1) for layer in self.ps.keys(): if layer == '0': continue for poly in self.ps[layer]: ohm = Polygon(poly) for i, (x, y) in enumerate(zip(xs[:-1], ys[:-1])): if ohm.contains( LineString([(x, y), (xs[i + 1], ys[i + 1])])): layers[i] = int(layer) self.edges = [] for i, (x, y) in enumerate(zip(xs[:-1], ys[:-1])): self.edges.append(Edge(x, y, xs[i + 1], ys[i + 1], layers[i]))
def add_airspace(self, country_code, airspace_class, name, base, top, geom_str): try: geom = loads(geom_str) except ReadingError: print name + "(" + airspace_class + ") is not a polygon (maybe not enough points?)" return False # orient polygon clockwise geom = polygon.orient(geom, sign=-1) if not airspace_class: print name + " has no airspace class" return False base = self.normalise_height(base, name) top = self.normalise_height(top, name) flightlevel_re = re.compile(r'^FL (\d+)$') match = flightlevel_re.match(base) if match and int(match.group(1)) >= 200: print name + " has it's base above FL 200 and is therefore disregarded" return False airspace = Airspace() airspace.country_code = country_code airspace.airspace_class = airspace_class airspace.name = name airspace.base = base airspace.top = top airspace.the_geom = from_shape(geom, srid=4326) db.session.add(airspace) return True
def test_ccw(self): """Test polygons can be oriented counter-clockwise.""" path = '/home/benkoziol/data/pmesh/catchment_shapefiles/NHDPlusTX/NHDPlus12/NHDPlusCatchment/Catchment.shp' path_out = os.path.join(tempfile.mkdtemp(), 'ccw.shp') with fiona.open(path, 'r') as source: with fiona.open(path_out, 'w', **source.meta) as sink: for record in source: geom = shape(record['geometry']) if isinstance(geom, MultiPolygon): itr = geom else: itr = [geom] for polygon in itr: if not polygon.exterior.is_ccw: polygon = orient(polygon) self.assertTrue(polygon.exterior.is_ccw) record['goemetry'] = mapping(geom) sink.write(record) with fiona.open(path_out, 'r') as source: for record in source: geom = shape(record['geometry']) if isinstance(geom, MultiPolygon): itr = geom else: itr = [geom] for polygon in itr: self.assertTrue(polygon.exterior.is_ccw) record['goemetry'] = mapping(geom)
def get_arc_footprint(ship, weapon): """ Get a shape representing the arc footprint from the base shape, up to AT LEAST range 3 (maybe more) ship: The ship to measure arc from weapon: The weapon to measure arc of return: The geometric object representing arc footprint from the base shape """ theta = ship.orientation + weapon.firing_direction start_angle = theta - weapon.firing_arc / 2 end_angle = theta + weapon.firing_arc / 2 angle_step = 1 # degree bx, by = ship.base_size # To be at least range 3, we need to add at least the centre-to-corner distance for the base template min_dist_from_centre = 0.5 * math.sqrt(bx * bx + by * by) dist = min_dist_from_centre + RANGE_ONE * 3 # Generate sector over the angle range angles = np.radians( np.linspace(start_angle, end_angle, (end_angle - start_angle) / angle_step)) points = [(0, 0)] + list(zip(np.sin(angles) * dist, np.cos(angles) * dist)) sector = polygon.orient(Polygon(points)) # Translate the result to be centred on the ship return translate(sector, ship.position[0], ship.position[1])
def get_data(self) -> Dict[str, Airspace]: features = [elt for elt in self.json_contents()["features"]] airspaces: Dict[str, Airspace] = dict() for feat in features: name = feat["properties"]["NAME"].strip() airspace = Airspace( name=name, elements=[ ExtrudedPolygon( polygon.orient(shape(feat["geometry"]), -1), feat["properties"]["LOWER_VAL"] if feat["properties"]["LOWER_VAL"] != -9998 else 0, feat["properties"]["UPPER_VAL"] if feat["properties"]["UPPER_VAL"] != -9998 else float("inf"), ) ], type_=feat["properties"]["TYPE_CODE"], designator=feat["properties"]["IDENT"], properties=feat["properties"], ) if not airspace.shape.is_valid: logging.warning(f"Invalid shape part {name}, skipping...") continue if name in airspaces: airspaces[name] += airspace else: airspaces[name] = airspace return airspaces
def add_airspace(country_code, airspace_class, name, base, top, geom_str): try: geom = loads(geom_str) except ReadingError: print name + "(" + airspace_class + ") is not a polygon (maybe not enough points?)" return False # orient polygon clockwise geom = polygon.orient(geom, sign=-1) if not airspace_class: print name + " has no airspace class" return False base = normalise_height(base, name) top = normalise_height(top, name) flightlevel_re = re.compile(r"^FL (\d+)$") match = flightlevel_re.match(base) if match and int(match.group(1)) >= 200: print name + " has it's base above FL 200 and is therefore disregarded" return False airspace = Airspace() airspace.country_code = country_code airspace.airspace_class = airspace_class airspace.name = name airspace.base = base airspace.top = top airspace.the_geom = from_shape(geom, srid=4326) db.session.add(airspace) return True
def __init__(self, elem_num, node1, node2, node3, node5, node6, node7, layer_num, autocorrect=True): _Element.__init__(self, elem_num, layer_num) # create a polygon representation p = Polygon([ node1.coords, node5.coords, node2.coords, node6.coords, node3.coords, node7.coords ]) if autocorrect: p_ccw = orient(p) self.polygon = p_ccw # find each node that matches each polygon point node_list = [] for point in p_ccw.exterior.coords[:-1]: if point == node1.coords: node_list.append(node1) elif point == node2.coords: node_list.append(node2) elif point == node3.coords: node_list.append(node3) elif point == node5.coords: node_list.append(node5) elif point == node6.coords: node_list.append(node6) elif point == node7.coords: node_list.append(node7) else: raise Warning("No point match found!") # assign the nodes in a proper CCW-orientation self.node1 = node_list[0] self.node5 = node_list[1] self.node2 = node_list[2] self.node6 = node_list[3] self.node3 = node_list[4] self.node7 = node_list[5] else: self.polygon = p self.node1 = node1 self.node2 = node2 self.node3 = node3 self.node5 = node5 self.node6 = node6 self.node7 = node7 # assign node_num=0 for nodes that are not present self.node4 = Node(0, 0.0, 0.0) self.node8 = Node(0, 0.0, 0.0) self.node9 = Node(0, 0.0, 0.0) self.nodes = (self.node1, self.node2, self.node3, self.node5, self.node6, self.node7) for node in self.nodes: node.parent_element = self
def test_holes(self): polygon = Polygon([(0,0),(0,1),(1,0)], [[(0.5,0.25),(0.25,0.5),(0.25,0.25)]]) self.failIf(polygon.exterior.is_ccw) self.failUnless(polygon.interiors[0].is_ccw) polygon = orient(polygon, 1) self.failUnless(polygon.exterior.is_ccw) self.failIf(polygon.interiors[0].is_ccw)
def test_holes(self): polygon = Polygon([(0, 0), (0, 1), (1, 0)], [[(0.5, 0.25), (0.25, 0.5), (0.25, 0.25)]]) self.assertFalse(polygon.exterior.is_ccw) self.assertTrue(polygon.interiors[0].is_ccw) polygon = orient(polygon, 1) self.assertTrue(polygon.exterior.is_ccw) self.assertFalse(polygon.interiors[0].is_ccw)
def _orient(shape): """ The Shapely version of the orient function appears to only work on Polygons, and fails on MultiPolygons. This is a quick wrapper to allow orienting of either. """ assert shape.geom_type in ('Polygon', 'MultiPolygon') if shape.geom_type == 'Polygon': return orient(shape) else: polys = [] for geom in shape.geoms: polys.append(orient(geom)) return MultiPolygon(polys)
def test_holes(self): polygon = Polygon([(0, 0), (0, 1), (1, 0)], [[(0.5, 0.25), (0.25, 0.5), (0.25, 0.25)]]) self.failIf(polygon.exterior.is_ccw) self.failUnless(polygon.interiors[0].is_ccw) polygon = orient(polygon, 1) self.failUnless(polygon.exterior.is_ccw) self.failIf(polygon.interiors[0].is_ccw)
def to_cpp(self) -> Union[td.simple_polygon, td.polygon]: # Note: Shapely polygons have one vertex repeated and orientation dos not matter # CGAL polygons have no vertex repeated and orientation mattters from shapely.geometry.polygon import orient # Get point sequences as arrays exterior = np.asarray(orient(self.polygon).exterior)[:-1] interiors = [ np.asarray(hole)[:-1] for hole in orient(self.polygon, -1).interiors ] cgal_polygon = td.simple_polygon(exterior) for interior in interiors: cgal_polygon = cgal_polygon.difference(td.simple_polygon(interior)) # Intersection result is MultiPolygon if cgal_polygon.num_parts() == 1: cgal_polygon = cgal_polygon[0] return cgal_polygon
def test_geometry_area_perimeter__polygon__holes(): geod = Geod(ellps="WGS84") polygon = Polygon( LineString([Point(1, 1), Point(1, 10), Point(10, 10), Point(10, 1)]), holes=[LineString([Point(1, 2), Point(3, 4), Point(5, 2)])], ) assert_almost_equal( geod.geometry_area_perimeter(orient(polygon, 1)), (944373881400.3394, 3979008.0359657984), decimal=2, ) assert_almost_equal( geod.geometry_area_perimeter(orient(polygon, -1)), (-944373881400.3394, 3979008.0359657984), decimal=2, )
def __init__(self, elem_num, node1, node2, node3, node5, node6, node7, layer_num, autocorrect=True): _Element.__init__(self, elem_num, layer_num) # create a polygon representation p = Polygon([ node1.coords, node5.coords, node2.coords, node6.coords, node3.coords, node7.coords ]) if autocorrect: p_ccw = orient(p) self.polygon = p_ccw # find each node that matches each polygon point node_list = [] for point in p_ccw.exterior.coords[:-1]: if point == node1.coords: node_list.append(node1) elif point == node2.coords: node_list.append(node2) elif point == node3.coords: node_list.append(node3) elif point == node5.coords: node_list.append(node5) elif point == node6.coords: node_list.append(node6) elif point == node7.coords: node_list.append(node7) else: raise Warning("No point match found!") # assign the nodes in a proper CCW-orientation self.node1 = node_list[0] self.node5 = node_list[1] self.node2 = node_list[2] self.node6 = node_list[3] self.node3 = node_list[4] self.node7 = node_list[5] else: self.polygon = p self.node1 = node1 self.node2 = node2 self.node3 = node3 self.node5 = node5 self.node6 = node6 self.node7 = node7 # assign node_num=0 for nodes that are not present self.node4 = Node(0, 0.0, 0.0) self.node8 = Node(0, 0.0, 0.0) self.node9 = Node(0, 0.0, 0.0) self.nodes = (self.node1,self.node2,self.node3,self.node5,self.node6, self.node7) for node in self.nodes: node.parent_element = self
def enforce_multipolygon_winding_order(self, shape): assert shape.type == 'MultiPolygon' parts = [] for part in shape.geoms: # see comment in shape.type == 'Polygon' above about why # the sign here has to be -1. part = orient(part, sign=-1.0) parts.append(part) oriented_shape = MultiPolygon(parts) return oriented_shape
def buffer(self, distance=None, join_style=2): # type: (Optional[float], Optional[int]) -> Polygon2D """Returns a representation of all points within a given distance of the polygon. :param join_style: The styles of joins between offset segments: 1 (round), 2 (mitre), and 3 (bevel). """ s_poly = SPoly(self.vertices) core = orient(s_poly.buffer(distance=distance, join_style=join_style), sign=1.0) return Polygon2D(core.boundary.coords)
def addFeatures(self, features, layer_name='', quantize_bounds=None, y_coord_down=False): self.layer = self.tile.layers.add() self.layer.name = layer_name self.layer.version = 2 self.layer.extent = self.extents self.key_idx = 0 self.val_idx = 0 self.seen_keys_idx = {} self.seen_values_idx = {} for feature in features: # skip missing or empty geometries geometry_spec = feature.get('geometry') if geometry_spec is None: continue shape = self._load_geometry(geometry_spec) if shape is None: raise NotImplementedError( 'Can\'t do geometries that are not wkt, wkb, or shapely ' 'geometries') if shape.is_empty: continue if shape.type == 'MultiPolygon': # If we are a multipolygon, we need to ensure that the # winding orders of the consituent polygons are # correct. In particular, the winding order of the # interior rings need to be the opposite of the # exterior ones, and all interior rings need to follow # the exterior one. This is how the end of one polygon # and the beginning of another are signaled. shape = self.enforce_multipolygon_winding_order(shape) elif shape.type == 'Polygon': # Ensure that polygons are also oriented with the # appropriate winding order. Their exterior rings must # have a clockwise order, which is translated into a # clockwise order in MVT's tile-local coordinates with # the Y axis in "screen" (i.e: +ve down) configuration. # Note that while the Y axis flips, we also invert the # Y coordinate to get the tile-local value, which means # the clockwise orientation is unchanged. shape = orient(shape, sign=-1.0) if quantize_bounds: shape = self.quantize(shape, quantize_bounds) self.addFeature(feature, shape, y_coord_down)
def orient_polygons(shp): if shp.geom_type == 'Polygon': return orient(shp) elif shp.geom_type == 'MultiPolygon': oriented_polygons = [] for polygon in shp: oriented = orient_polygons(polygon) oriented_polygons.append(oriented) return MultiPolygon(oriented_polygons) else: print('shp is not a Polygon or a MultiPolygon')
def get_oriented_and_valid_geometry(geom): try: assert geom.is_valid except AssertionError: geom = geom.buffer(0) assert geom.is_valid if not geom.exterior.is_ccw: geom = orient(geom) return geom
def test_from_shapely_polygon(self): for d in ['2d', '3d']: poly1 = wkt.loads(self.fixture_wkt[d]['polygon']) poly2 = wkt.loads(self.fixture_wkt[d]['polygon_hole']) geoms = [orient(poly1, sign=-1.0), poly2, MultiPolygon([poly1, poly2])] res = cf.from_shapely('polygon', geoms) # res.describe() self.assertEqual(res.outer_ring_order, OuterRingOrder.CCW) self.assertEqual(res.closure_convention, ClosureConvention.CLOSED) self.assertIsInstance(res, CFGeometryCollection) for ctr, c in enumerate(res.cindex): loaded = cf.to_shapely('polygon', c, res.x, res.y, z=res.z, multipart_break=res.multipart_break, hole_break=res.hole_break) if ctr == 0: desired = orient(geoms[0]) else: desired = geoms[ctr] self.assertTrue(loaded.almost_equals(desired))
def test_from_shapely_polygon(self): for d in ['2d', '3d']: poly1 = wkt.loads(self.fixture_wkt[d]['polygon']) poly2 = wkt.loads(self.fixture_wkt[d]['polygon_hole']) geoms = [orient(poly1, sign=-1.0), poly2] res = shapely_to_container(geoms) self.assertIsInstance(res, GeometryContainer) for ctr, geom in enumerate(res.geoms): loaded = geom.to_shapely() self.assertTrue(loaded.almost_equals(geoms[ctr]))
def format_polygon(poly): '''Return serialized polygon string for e.g. 'polygon' api parameter''' # Orient counter-clockwise poly = orient(poly, sign=1.0) # Format dictionary to polygon coordinate pairs for CMR polygon filtering formatted = ','.join([ '{0:.5f}'.format(c) for xy in zip(*poly.exterior.coords.xy) for c in xy ]) return formatted
def from_json(cls, json: Dict[str, Any]) -> "Airspace": return cls( name=json["name"], type_=json["type"], elements=[ ExtrudedPolygon( polygon=polygon.orient(shape(layer["polygon"]), -1), upper=layer["upper"], lower=layer["lower"], ) for layer in json["shapes"] ], )
def get_as_3d_object(self, z=0.001, extrude_height=0, include_bottom_face=False): """ Generates an Object3D from the marking. :param z: z coordinate of marking :param extrude_height: distance by which to extrude the marking :param include_bottom_face: whether to include the bottom face of the extruded geometry. :return: Object3D :type z: float :type extrude_height: float :type include_bottom_face: bool """ shape = self.get_as_shape() if shape.geometryType() == "Polygon": oriented_shape = MultiPolygon([orient(shape)]) elif shape.geometryType() in ["MultiPolygon", "GeometryCollection"]: oriented_geoms = [] for geom in shape: if geom.geometryType() == "Polygon": oriented_geoms.append(orient(geom)) oriented_shape = MultiPolygon(oriented_geoms) return _Utils.Object3D.from_shape(oriented_shape, self.purpose+"_marking", self.color+"_marking", z=z, extrude_height=extrude_height, include_bottom_face=include_bottom_face)
def __init__(self, turn_target: np.ndarray, agent_id: int, frame: Dict[int, ip.AgentState], scenario_map: ip.Map, open_loop: bool = True): self.turn_target = turn_target super(Exit, self).__init__(agent_id, frame, scenario_map, open_loop) # Calculate the orientation of the turn. If the returned value is less than 0 then the turn is clockwise (right) # If it is larger than 0 it is oriented counter-clockwise (left). # If it is zero, the turn is a straight line. trajectory = LineString(self.get_trajectory().path) if isinstance(trajectory.convex_hull, LineString): self.orientation = 0 else: self.orientation = orient(trajectory.convex_hull).area / trajectory.length
def region_from_polygon(polygon, variables): """Compute formula representing polygon (Polygon, LineString or LinearRing). Area will be considered at the rhs (ccw) of line segments. :param polygon: Polygon, LineString or LinearRing, must be convex :return: Formula s.t. points in polygon are satisfied. :rtype: pycarl.Formula or pycarl.Constraint """ from shapely.geometry.polygon import LinearRing, Polygon, orient if len(variables) > 2: raise ValueError( "The method is only available for two dimensional polygons.") if isinstance(polygon, LinearRing): # Convert to polygon so it can be oriented polygon = Polygon(polygon) if isinstance(polygon, Polygon): assert len(list(polygon.interiors)) == 0 polygon = orient(polygon, sign=1.0) polygon = polygon.exterior points = [(Rational(c1), Rational(c2)) for c1, c2 in list(polygon.coords)] assert len(points) >= 2 constraint = None # Calculate half-plane for each pair of points # http://math.stackexchange.com/questions/82151/find-the-equation-of-the-plane-passing-through-a-point-and-a-vector-orthogonal for p1, p2 in zip(points[:-1], points[1:]): # Get vector direction (parallel to hyperplane) dvec = tuple([c2 - c1 for c1, c2 in zip(p1, p2)]) # Make vector orthogonal to hyperplane # NOTE: rotate clockwise dvec = (dvec[1], -dvec[0]) # Constant is dot-product of directional vector and origin c = sum([c1 * c2 for c1, c2 in zip(dvec, p1)]) # Construct polynomial for line poly = Polynomial(-Rational(c)) for variable, coefficient in zip(variables, dvec): if coefficient != 0: poly = poly + Polynomial(variable) * coefficient new_constraint = pc.Constraint(poly, pc.Relation.LEQ) if constraint is None: constraint = new_constraint else: constraint = constraint & new_constraint return constraint
def draw_coords(coords, close, ccw=True): # Force proper orientation of lines if close: coords = orient(Polygon(coords), sign=1 if ccw else -1).exterior.coords for i, p in enumerate(coords): if i == 0: cairo_context.move_to(*p) else: cairo_context.line_to(*p) if close: cairo_context.close_path()
def toPolygon(self): """ return a shapely polygon using points!!! points="375.12,98.88,924.0,101.52,924.0,113.52,375.12,110.88" """ if self._poly is not None: return self._poly x = [float(x) for x in self.getAttribute("points").replace(" ",",").split(',')] if len(x) <3*2: return LineString(list(zip(*[iter(x)]*2))) self._poly = polygon.orient(Polygon(list(zip(*[iter(x)]*2)))) if not self._poly.is_valid:self._poly= self._poly.convex_hull return self._poly
def read(self, fpath): """ Read polygons from a Shapefile and return a list of SchismPolygons. Parameters ---------- fpath: str Filename of a Shapefile containing Polygons with data columns such as name, type, and attribute. Returns ------- list list of SchismPolygons """ if os.path.exists(fpath): datasource = Open(fpath) layer = datasource.GetLayer(0) feat = layer.GetFeature(0) field_names = [ feat.GetFieldDefnRef(i).GetName() for i in range(feat.GetFieldCount()) ] # fld_idx = [field_names.index(x) # for x in ('name', 'type', 'attribute')] polygons = [] for feature in layer: geom = feature.GetGeometryRef() name_geom = geom.GetGeometryName() if name_geom in ('POLYGON', 'MULTIPOLYGON'): if name_geom == 'MULTIPOLYGON': geoms = [g for g in geom] else: geoms = [ geom, ] for g in geoms: shell = [ point for point in g.GetGeometryRef(0).GetPoints() ] prop = {} for i, field_name in enumerate(field_names): prop[field_name] = feature.GetFieldAsString(i) polygons.append( SchismPolygon(orient(Polygon(shell=shell)), prop=prop)) return polygons # return SchismPolygonDictConverter().read({'polygons': polygons}) else: raise ValueError('File not found')
def addFeatures(self, features, layer_name=""): self.layer = self.tile.layers.add() self.layer.name = layer_name self.layer.version = 2 self.layer.extent = self.extents self.key_idx = 0 self.val_idx = 0 self.seen_keys_idx = {} self.seen_values_idx = {} for feature in features: # skip missing or empty geometries geometry_spec = feature.get('geometry') if geometry_spec is None: continue shape = self._load_geometry(geometry_spec) if shape is None: raise NotImplementedError( 'Can\'t do geometries that are not wkt, wkb, or shapely ' 'geometries') if shape.is_empty: continue if shape.type == 'MultiPolygon': # If we are a multipolygon, we need to ensure that the # winding orders of the consituent polygons are # correct. In particular, the winding order of the # interior rings need to be the opposite of the # exterior ones, and all interior rings need to follow # the exterior one. This is how the end of one polygon # and the beginning of another are signaled. shape = self.enforce_multipolygon_winding_order(shape) elif shape.type == 'Polygon': # Ensure that polygons are also oriented with the # appropriate winding order. Their exterior rings must # have a clockwise order, which is translated into a # clockwise order in MVT's tile-local coordinates with # the Y axis in "screen" (i.e: +ve down) configuration. # Note that while the Y axis flips, we also invert the # Y coordinate to get the tile-local value, which means # the clockwise orientation is unchanged. shape = orient(shape, sign=-1.0) self.addFeature(feature, shape)
def orient_geometry_rfc7946(geometry: Polygon | MultiPolygon) -> Polygon | MultiPolygon: """ Converts orientation of geometry according to the GeoJSON RFC7946 Parameters ---------- geometry : Polygon | MultiPolygon The geometry to orient Returns ------- Polygon | MultiPolygon The oriented geometry Raises ------ ValueError If not a Polygon or MultiPolygon was passed """ if isinstance(geometry, Polygon): return orient(geometry, sign=1.0) # type: ignore elif isinstance(geometry, MultiPolygon): # type: ignore return MultiPolygon([orient(geom, sign=1.0) for geom in geometry.geoms]) # type: ignore raise ValueError(f"Only Polygon or MultiPolygon types are supported, not {type(geometry)}")
def read(self, data): polygons = [] data = data.get('polygons') if data is not None: for item in data: prop = {} for k in item: if k == 'vertices': vertices = [[float(e) for e in row] for row in item['vertices']] else: prop[k] = item[k] polygons.append(SchismPolygon(orient( Polygon(vertices)), prop=prop)) return polygons
def osm_groups_items_areas_leisure_swimming_pool(obj, root, osm): """ """ pool = obj.copy() #obj = obj.material(ddd.mats.water) #obj = obj.material(ddd.mats.pavement) pool.name = "Swimming Pool: %s" % pool.name #obj.extra['ddd:area:water'] = 'ignore' # Water is created by the fountain object, but the riverbank still requires #obj.extra['ddd:item:type'] = "area" #obj.extra['osm:natural'] pool.extra["ddd:elevation"] = "max" root.find("/ItemsAreas").append(pool) # ItemsAreas # TODO: Use normal alignment of nodes pool_outline = pool.copy() pool_outline.geom = polygon.orient(pool_outline.geom, 1) pool_outline = pool_outline.outline() ladder_pos_d = random.uniform(0, pool_outline.length()) ladder_pos, segment_idx, segment_coords_a, segment_coords_b = pool_outline.interpolate_segment( ladder_pos_d) ladder = obj.copy(name="Swimming Pool Ladder") ladder.children = [] ladder.geom = ddd.point(ladder_pos).geom angle = math.atan2(segment_coords_b[1] - segment_coords_a[1], segment_coords_b[0] - segment_coords_a[0]) ladder.set('ddd:ladder', 'swimming_pool') ladder.set('ddd:angle', angle + math.pi) ladder.set('ddd:height:base', 0.5) #pool.extra["ddd:elevation"] = "max" root.find("/ItemsNodes").append(ladder) # Define terrain/area below pool as void area = pool.copy() area.set('ddd:area:type', 'void') # 'void' # will be constructed by the pool areaitem area.set('ddd:area:height', -2.0) # 'void' #area.set('ddd:area:weight', 200) # Higher than default priority (100) ? area.set('ddd:area:area', area.geom.area) # Needed for area to be processed in s40 area.set('ddd:layer', '0') area = area.material( ddd.mats.water ) # Better, use fountain:base (terrain vs base) leave void and construct fopuntain base base, get materia from surrounding possibly #area.set["ddd:elevation:"] = "min" # Make min+raise-height root.find("/Areas").append(area) # ItemsAreas
def enforce_polygon_winding_order(self, shape, n_try): assert shape.type == 'Polygon' def fn(point): x, y = point return self._round(x), self._round(y) exterior = apply_map(fn, shape.exterior.coords) rings = None if len(shape.interiors) > 0: rings = [apply_map(fn, ring.coords) for ring in shape.interiors] oriented_shape = orient(Polygon(exterior, rings), sign=-1.0) oriented_shape = self.handle_shape_validity(oriented_shape, n_try) return oriented_shape
def placekey_to_polygon(placekey, geo_json=False): """ Get the boundary shapely Polygon for a Placekey. :param place_key: Placekey (string) :param geo_json: If True return the coordinates in GeoJSON format: (long, lat)-tuples and with the first and last tuples identical, and in counter-clockwise orientation. If False (default) tuples will be (lat, long), the last tuple will not equal the first, and the orientation will be clockwise. :return: A shapely Polygon object """ return polygon.orient(Polygon( placekey_to_hex_boundary(placekey, geo_json=geo_json)), sign=1)
def buildGeometry(self, coordDict, nodeDict): self.coords = nodesToCoords(self.nodes, coordDict) self.geometry = LineString(self.coords) self.leftEdgeLine = self.geometry.parallel_offset(metersToDeg(self.width / 2.0 - shoulderWidth), 'left', 2) self.rightEdgeLine = self.geometry.parallel_offset(metersToDeg(self.width / 2.0 - shoulderWidth), 'right', 2) self.leftDotted = copy.deepcopy(self.leftEdgeLine) self.rightDotted = copy.deepcopy(self.rightEdgeLine) self.concreteGeometry = orient(self.geometry.buffer(metersToDeg(self.width)/2.0, 2)) # Loop over all the nodes that make up the taxiway and make a list of # any that are holding positions. # TODO: Currently only checks normal holds, should check for ILS holds as well. for node in self.nodes: if node in nodeDict: nodeProps = nodeDict[node] if 'aeroway' in nodeProps[0]: if nodeProps[0]['aeroway'] == 'holding_position': self.holdingPositions.append((node, nodeProps[0], nodeProps[1]))
def get_update_feature(fid, feature): # todo: doc # create the geometry object obj = shape(feature['geometry']) # only polygons are acceptable geometry types try: assert not isinstance(obj, BaseMultipartGeometry) except AssertionError: msg = 'Only singlepart geometries allowed. Perhaps "ugrid.convert_multipart_to_singlepart" would be useful?' raise ValueError(msg) # if the coordinates are not counterclockwise, reverse the orientation if not obj.exterior.is_ccw: obj = orient(obj) feature['geometry'] = mapping(obj) # add this to feature dictionary feature['geometry']['object'] = obj # add a custom feature identifier feature['fid'] = fid return feature
def get_arc_footprint(ship, weapon): """ Get a shape representing the arc footprint from the base shape, up to AT LEAST range 3 (maybe more) ship: The ship to measure arc from weapon: The weapon to measure arc of return: The geometric object representing arc footprint from the base shape """ theta = ship.orientation + weapon.firing_direction start_angle = theta - weapon.firing_arc / 2 end_angle = theta + weapon.firing_arc / 2 angle_step = 1 # degree bx, by = ship.base_size # To be at least range 3, we need to add at least the centre-to-corner distance for the base template min_dist_from_centre = 0.5 * math.sqrt(bx * bx + by * by) dist = min_dist_from_centre + RANGE_ONE * 3 # Generate sector over the angle range angles = np.radians(np.linspace(start_angle, end_angle, (end_angle - start_angle) / angle_step)) points = [(0, 0)] + list(zip(np.sin(angles) * dist, np.cos(angles) * dist)) sector = polygon.orient(Polygon(points)) # Translate the result to be centred on the ship return translate(sector, ship.position[0], ship.position[1])
def LineGroupingFromSHP(abs_file_path, MINIMALDIST): sf = shapefile.Reader(abs_file_path) #------------------------ read lines from shapefile --------------------------- chains=[] for geom in sf.shapeRecords(): chain=[(geom.shape.points[0][0],geom.shape.points[0][1]),(geom.shape.points[1][0],geom.shape.points[1][1])] chains.append(chain) #------------------------------------------------------------------------------ #------------------ group lines & fix unconnected vertices -------------------- closed_chains=[] k=1 RADIUS=MINIMALDIST print '#-----------------------------' print 'k=', k, 'RADIUS=', RADIUS print 'len(chains)', len(chains) print 'len(closed_chains)', len(closed_chains) while (k<=5 and len(chains)>0): if len(chains)==1: pt11=Point(chains[0][0][0], chains[0][0][1]) pt12=Point(chains[0][-1][0],chains[0][-1][1]) if pt11.distance(pt12)<=RADIUS: chains.pop(0) l1=LineString([chain1[0], chain1[1]]) l2=LineString([chain1[-1], chain1[-2]]) new_pts1=fix_disjoint_vertices(l1, l2) new_chain=chain1[1:-1]+new_pts1 closed_chains.append(new_chain) break else: print 'wawawa' k=k+1 RADIUS=RADIUS+MINIMALDIST print '#-----------------------------' print 'k=', k, 'RADIUS=', RADIUS print 'len(chains)', len(chains) print 'len(closed_chains)', len(closed_chains) break L=len(chains) for i in range(0, len(chains)-1): chain1=chains[i] pt11=Point(chain1[0][0], chain1[0][1]) pt12=Point(chain1[-1][0],chain1[-1][1]) if pt11.distance(pt12)<=RADIUS: chains.pop(i) l1=LineString([chain1[0], chain1[1]]) l2=LineString([chain1[-1], chain1[-2]]) new_pts1=fix_disjoint_vertices(l1, l2) new_chain=chain1[1:-1]+new_pts1 closed_chains.append(new_chain) break for j in range(i+1, len(chains)+1): if j==len(chains): break chain2=chains[j] pt21=Point(chain2[0][0], chain2[0][1]) pt22=Point(chain2[-1][0],chain2[-1][1]) if pt11.distance(pt21)<=RADIUS: l1=LineString([chain1[0], chain1[1]]) l2=LineString([chain2[0], chain2[1]]) new_pts1=fix_disjoint_vertices(l1, l2) if pt12.distance(pt22)<=RADIUS: # closed l1=LineString([chain1[-1], chain1[-2]]) l2=LineString([chain2[-1], chain2[-2]]) new_pts2=fix_disjoint_vertices(l1, l2) chains.pop(j) chains.pop(i) chain1.reverse() new_chain=new_pts2+chain1[1:-1]+new_pts1+chain2[1:-1] closed_chains.append(new_chain) break else: chains.pop(j) chains.pop(i) chain1.reverse() new_chain=chain1[0:-1]+new_pts1+chain2[1:] chains.append(new_chain) break elif pt11.distance(pt22)<=RADIUS: l1=LineString([chain1[0], chain1[1]]) l2=LineString([chain2[-1], chain2[-2]]) new_pts1=fix_disjoint_vertices(l1, l2) if pt12.distance(pt21)<=RADIUS: # closed l1=LineString([chain1[-1], chain1[-2]]) l2=LineString([chain2[0], chain2[1]]) new_pts2=fix_disjoint_vertices(l1, l2) chains.pop(j) chains.pop(i) new_chain=new_pts1+chain1[1:-1]+new_pts2+chain2[1:-1] closed_chains.append(new_chain) break else: chains.pop(j) chains.pop(i) new_chain=chain2[0:-1]+new_pts1+chain1[1:] chains.append(new_chain) break elif pt12.distance(pt21)<=RADIUS: l1=LineString([chain1[-1], chain1[-2]]) l2=LineString([chain2[0], chain2[1]]) new_pts1=fix_disjoint_vertices(l1, l2) chains.pop(j) chains.pop(i) new_chain=chain1[0:-1]+new_pts1+chain2[1:] chains.append(new_chain) break elif pt12.distance(pt22)<=RADIUS: l1=LineString([chain1[-1], chain1[-2]]) l2=LineString([chain2[-1], chain2[-2]]) new_pts1=fix_disjoint_vertices(l1, l2) chains.pop(j) chains.pop(i) chain2.reverse() new_chain=chain1[0:-1]+new_pts1+chain2[1:] chains.append(new_chain) break else: continue if j==L: continue else: break if i==L-2 and j==L: print 'hahaha' k=k+1 RADIUS=RADIUS+MINIMALDIST print '#-----------------------------' print 'k=', k, 'RADIUS=', RADIUS print 'len(chains)', len(chains) print 'len(closed_chains)', len(closed_chains) else: continue print '#-----------------------------' print 'len(chains)', len(chains) print 'len(closed_chains)', len(closed_chains) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ groups_P=[] for i in range(0, len(closed_chains)): if len(closed_chains[i])>2: if Polygon(closed_chains[i]).is_valid==True: ply=Polygon(closed_chains[i]) new_ply=polygon.orient(ply, sign=1.0) groups_P.append(list(new_ply.exterior.coords)[0:-1]) else: ply=Polygon(closed_chains[i]).buffer(0).exterior.coords new_ply=polygon.orient(ply, sign=1.0) groups_P.append(list(new_ply.exterior.coords)[0:-1]) else: print 'closed_chains ',i,' only has two points' return groups_P
def add_airspace(self, country_code, airspace_class, name, base, top, geom_str): try: geom = loads(geom_str) except ReadingError: print name + "(" + airspace_class + ") is not a polygon (maybe not enough points?)" return False # orient polygon clockwise geom = polygon.orient(geom, sign=-1) if not airspace_class: print name + " has no airspace class" return False base = self.normalise_height(base, name) top = self.normalise_height(top, name) flightlevel_re = re.compile(r'^FL (\d+)$') match = flightlevel_re.match(base) if match and int(match.group(1)) >= 200: print name + " has it's base above FL 200 and is therefore disregarded" return False airspace = Airspace() airspace.country_code = country_code airspace.airspace_class = airspace_class airspace.name = name airspace.base = base airspace.top = top # Check geometry type, disregard everything except POLYGON if geom.geom_type != 'Polygon': print name + " is not a polygon (it's a " + geom.geom_type + ")" return False wkb = from_shape(geom, srid=4326) # Try to fix invalid (self-intersecting) geometries valid_dump = (func.ST_Dump(func.ST_MakeValid(wkb))).geom valid_query = db.session.query(func.ST_SetSRID(valid_dump, 4326)).order_by(func.ST_Area(valid_dump).desc()).first() if not valid_query: print 'Error importing ' + name print 'Could not validate geometry' return False else: wkb = valid_query[0] geom_type = db.session.query(func.ST_GeometryType(wkb)).first()[0] if geom_type != 'ST_Polygon': print name + " got some errors makeing it valid..." return False tolerance = 0.0000001 simplify = lambda x: func.ST_SimplifyPreserveTopology(x, tolerance) airspace.the_geom = case( [ (func.ST_IsValid(wkb), wkb), (func.ST_IsValid(simplify(wkb)), simplify(wkb)), ], else_=None) db.session.add(airspace) return True
def test_no_holes(self): ring = LinearRing([(0,0),(0,1),(1,0)]) polygon = Polygon(ring) self.failIf(polygon.exterior.is_ccw) polygon = orient(polygon, 1) self.failUnless(polygon.exterior.is_ccw)
def _force_polygon_ccw(geometry): polygon = shape(geometry) return mapping(orient(polygon))
def recalculate(cls): # collect location areas all_areas = [] all_ramps = [] space_areas = {} spaces = {} levels = Level.objects.prefetch_related('buildings', 'doors', 'spaces', 'spaces__columns', 'spaces__obstacles', 'spaces__lineobstacles', 'spaces__holes', 'spaces__stairs', 'spaces__ramps', 'spaces__altitudemarkers') logger = logging.getLogger('c3nav') for level in levels: areas = [] ramps = [] stairs = [] # collect all accessible areas on this level buildings_geom = unary_union(tuple(building.geometry for building in level.buildings.all())) for space in level.spaces.all(): spaces[space.pk] = space space.orig_geometry = space.geometry if space.outside: space.geometry = space.geometry.difference(buildings_geom) space_accessible = space.geometry.difference( unary_union(tuple(c.geometry for c in space.columns.all() if c.access_restriction_id is None) + tuple(o.geometry for o in space.obstacles.all()) + tuple(o.buffered_geometry for o in space.lineobstacles.all()) + tuple(h.geometry for h in space.holes.all())) ) space_ramps = unary_union(tuple(r.geometry for r in space.ramps.all())) areas.append(space_accessible.difference(space_ramps)) for geometry in assert_multipolygon(space_accessible.intersection(space_ramps)): ramp = AltitudeArea(geometry=geometry, level=level) ramp.geometry_prep = prepared.prep(geometry) ramp.space = space.pk ramps.append(ramp) areas = tuple(orient(polygon) for polygon in assert_multipolygon( unary_union(areas+list(door.geometry for door in level.doors.all())) )) # collect all stairs on this level for space in level.spaces.all(): space_buffer = space.geometry.buffer(0.001, join_style=JOIN_STYLE.mitre) for stair in space.stairs.all(): stairs.extend(assert_multilinestring( stair.geometry.intersection(space_buffer) )) # divide areas using stairs for stair in stairs: areas = cut_polygon_with_line(areas, stair) # create altitudearea objects areas = [AltitudeArea(geometry=clean_cut_polygon(area), level=level) for area in areas] # prepare area geometries for area in areas: area.geometry_prep = prepared.prep(area.geometry) # assign spaces to areas space_areas.update({space.pk: [] for space in level.spaces.all()}) for area in areas: area.spaces = set() area.geometry_prep = prepared.prep(area.geometry) for space in level.spaces.all(): if area.geometry_prep.intersects(space.geometry): area.spaces.add(space.pk) space_areas[space.pk].append(area) # give altitudes to areas for space in level.spaces.all(): for altitudemarker in space.altitudemarkers.all(): for area in space_areas[space.pk]: if area.geometry_prep.contains(altitudemarker.geometry): area.altitude = altitudemarker.altitude break else: logger.error( _('AltitudeMarker #%(marker_id)d in Space #%(space_id)d on Level %(level_label)s ' 'is not placed in an accessible area') % {'marker_id': altitudemarker.pk, 'space_id': space.pk, 'level_label': level.short_label}) # determine altitude area connections for area in areas: area.connected_to = [] for area, other_area in combinations(areas, 2): if area.geometry_prep.intersects(other_area.geometry): area.connected_to.append(other_area) other_area.connected_to.append(area) # determine ramp connections for ramp in ramps: ramp.connected_to = [] buffered = ramp.geometry.buffer(0.001) for area in areas: if area.geometry_prep.intersects(buffered): intersection = area.geometry.intersection(buffered) ramp.connected_to.append((area, intersection)) if len(ramp.connected_to) != 2: if len(ramp.connected_to) == 0: logger.warning('A ramp in space #%d has no connections!' % ramp.space) elif len(ramp.connected_to) == 1: logger.warning('A ramp in space #%d has only one connection!' % ramp.space) else: logger.warning('A ramp in space #%d has more than two connections!' % ramp.space) # add areas to global areas all_areas.extend(areas) all_ramps.extend(ramps) # give temporary ids to all areas areas = all_areas ramps = all_ramps for i, area in enumerate(areas): area.tmpid = i for area in areas: area.connected_to = set(area.tmpid for area in area.connected_to) for space in space_areas.keys(): space_areas[space] = set(area.tmpid for area in space_areas[space]) areas_without_altitude = set(area.tmpid for area in areas if area.altitude is None) # interpolate altitudes areas_with_altitude = [i for i in range(len(areas)) if i not in areas_without_altitude] for i, tmpid in enumerate(areas_with_altitude): areas[tmpid].i = i csgraph = np.zeros((len(areas), len(areas)), dtype=bool) for area in areas: for connected_tmpid in area.connected_to: csgraph[area.tmpid, connected_tmpid] = True repeat = True while repeat: repeat = False distances, predecessors = dijkstra(csgraph, directed=False, return_predecessors=True, unweighted=True) np_areas_with_altitude = np.array(areas_with_altitude, dtype=np.uint32) relevant_distances = distances[np_areas_with_altitude[:, None], np_areas_with_altitude] # noinspection PyTypeChecker for from_i, to_i in np.argwhere(np.logical_and(relevant_distances < np.inf, relevant_distances > 1)): from_area = areas[areas_with_altitude[from_i]] to_area = areas[areas_with_altitude[to_i]] if from_area.altitude == to_area.altitude: continue path = [to_area.tmpid] while path[-1] != from_area.tmpid: path.append(predecessors[from_area.tmpid, path[-1]]) from_altitude = from_area.altitude delta_altitude = (to_area.altitude-from_altitude)/(len(path)-1) if set(path[1:-1]).difference(areas_without_altitude): continue for i, tmpid in enumerate(reversed(path[1:-1]), start=1): area = areas[tmpid] area.altitude = Decimal(from_altitude+delta_altitude*i).quantize(Decimal('1.00')) areas_without_altitude.discard(tmpid) area.i = len(areas_with_altitude) areas_with_altitude.append(tmpid) for from_tmpid, to_tmpid in zip(path[:-1], path[1:]): csgraph[from_tmpid, to_tmpid] = False csgraph[to_tmpid, from_tmpid] = False repeat = True # remaining areas: copy altitude from connected areas if any repeat = True while repeat: repeat = False for tmpid in tuple(areas_without_altitude): area = areas[tmpid] connected_with_altitude = area.connected_to-areas_without_altitude if connected_with_altitude: area.altitude = areas[next(iter(connected_with_altitude))].altitude areas_without_altitude.discard(tmpid) repeat = True # remaining areas which belong to a room that has an altitude somewhere for contained_areas in space_areas.values(): contained_areas_with_altitude = contained_areas - areas_without_altitude contained_areas_without_altitude = contained_areas - contained_areas_with_altitude if contained_areas_with_altitude and contained_areas_without_altitude: altitude_areas = {} for tmpid in contained_areas_with_altitude: area = areas[tmpid] altitude_areas.setdefault(area.altitude, []).append(area.geometry) for altitude in altitude_areas.keys(): altitude_areas[altitude] = unary_union(altitude_areas[altitude]) for tmpid in contained_areas_without_altitude: area = areas[tmpid] area.altitude = min(altitude_areas.items(), key=lambda aa: aa[1].distance(area.geometry))[0] areas_without_altitude.difference_update(contained_areas_without_altitude) # last fallback: level base_altitude for tmpid in areas_without_altitude: area = areas[tmpid] area.altitude = area.level.base_altitude # prepare per-level operations level_areas = {} for area in areas: level_areas.setdefault(area.level, set()).add(area.tmpid) # make sure there is only one altitude area per altitude per level for level in levels: areas_by_altitude = {} for tmpid in level_areas.get(level, []): area = areas[tmpid] areas_by_altitude.setdefault(area.altitude, []).append(area.geometry) level_areas[level] = [AltitudeArea(level=level, geometry=unary_union(geometries), altitude=altitude) for altitude, geometries in areas_by_altitude.items()] # renumber joined areas areas = list(chain(*(a for a in level_areas.values()))) for i, area in enumerate(areas): area.tmpid = i # finalize ramps for ramp in ramps: if not ramp.connected_to: for area in space_areas[ramp.space]: ramp.altitude = areas[area].altitude break else: ramp.altitude = ramp.level.base_altitude continue if len(ramp.connected_to) == 1: ramp.altitude = ramp.connected_to[0][0].altitude continue if len(ramp.connected_to) > 2: ramp.connected_to = sorted(ramp.connected_to, key=lambda item: item[1].area)[-2:] ramp.point1 = ramp.connected_to[0][1].centroid ramp.point2 = ramp.connected_to[1][1].centroid ramp.altitude = ramp.connected_to[0][0].altitude ramp.altitude2 = ramp.connected_to[1][0].altitude ramp.tmpid = len(areas) areas.append(ramp) level_areas[ramp.level].append(ramp) # # now fill in the obstacles and so on # for level in levels: for space in level.spaces.all(): space.geometry = space.orig_geometry buildings_geom = unary_union(tuple(b.geometry for b in level.buildings.all())) doors_geom = unary_union(tuple(d.geometry for d in level.doors.all())) space_geom = unary_union(tuple((s.geometry if not s.outside else s.geometry.difference(buildings_geom)) for s in level.spaces.all())) accessible_area = unary_union((doors_geom, space_geom)) for space in level.spaces.all(): accessible_area = accessible_area.difference(space.geometry.intersection( unary_union(tuple(h.geometry for h in space.holes.all())) )) our_areas = level_areas.get(level, []) for area in our_areas: area.orig_geometry = area.geometry area.orig_geometry_prep = prepared.prep(area.geometry) stairs = [] for space in level.spaces.all(): space_geom = space.geometry if space.outside: space_geom = space_geom.difference(buildings_geom) space_geom_prep = prepared.prep(space_geom) holes_geom = unary_union(tuple(h.geometry for h in space.holes.all())) remaining_space = ( tuple(o.geometry for o in space.obstacles.all()) + tuple(o.buffered_geometry for o in space.lineobstacles.all()) ) remaining_space = tuple(g.intersection(space_geom).difference(holes_geom) for g in remaining_space if space_geom_prep.intersects(g)) remaining_space = tuple(chain(*( assert_multipolygon(g) for g in remaining_space if not g.is_empty ))) if not remaining_space: continue cuts = [] for cut in chain(*(assert_multilinestring(stair.geometry) for stair in space.stairs.all()), (ramp.geometry.exterior for ramp in space.ramps.all())): for coord1, coord2 in zip(tuple(cut.coords)[:-1], tuple(cut.coords)[1:]): line = space_geom.intersection(LineString([coord1, coord2])) if line.is_empty: continue factor = (line.length + 2) / line.length line = scale(line, xfact=factor, yfact=factor) centroid = line.centroid line = min(assert_multilinestring(space_geom.intersection(line)), key=lambda l: l.centroid.distance(centroid), default=None) cuts.append(scale(line, xfact=1.01, yfact=1.01)) remaining_space = tuple( orient(polygon) for polygon in remaining_space ) for cut in cuts: remaining_space = tuple(chain(*(cut_polygon_with_line(geom, cut) for geom in remaining_space))) remaining_space = MultiPolygon(remaining_space) for polygon in assert_multipolygon(remaining_space): polygon = clean_cut_polygon(polygon).buffer(0) buffered = polygon.buffer(0.001) center = polygon.centroid touches = tuple((area, buffered.intersection(area.orig_geometry).area) for area in our_areas if area.orig_geometry_prep.intersects(buffered)) if touches: min_touches = sum((t[1] for t in touches), 0)/4 area = max(touches, key=lambda item: (item[1] > min_touches, item[0].altitude2 is not None, item[0].altitude, item[1]))[0] else: area = min(our_areas, key=lambda a: a.orig_geometry.distance(center)-(0 if a.altitude2 is None else 0.6)) area.geometry = area.geometry.buffer(0).union(polygon) for level in levels: level_areas[level] = set(area.tmpid for area in level_areas.get(level, [])) # save to database areas_to_save = set(range(len(areas))) all_candidates = AltitudeArea.objects.select_related('level') for candidate in all_candidates: candidate.area = candidate.geometry.area candidate.geometry_prep = prepared.prep(candidate.geometry) all_candidates = sorted(all_candidates, key=attrgetter('area'), reverse=True) num_modified = 0 num_deleted = 0 num_created = 0 field = AltitudeArea._meta.get_field('geometry') for candidate in all_candidates: new_area = None if candidate.altitude2 is None: for tmpid in level_areas.get(candidate.level, set()): area = areas[tmpid] if area.altitude2 is None and area.altitude == candidate.altitude: new_area = area break else: potential_areas = [areas[tmpid] for tmpid in level_areas.get(candidate.level, set())] potential_areas = [area for area in potential_areas if (candidate.altitude, candidate.altitude2) in ((area.altitude, area.altitude2), (area.altitude2, area.altitude))] potential_areas = [(area, area.geometry.intersection(candidate.geometry).area) for area in potential_areas if candidate.geometry_prep.intersects(area.geometry)] if potential_areas: new_area = max(potential_areas, key=itemgetter(1))[0] if new_area is None: candidate.delete() num_deleted += 1 continue if not field.get_final_value(new_area.geometry).almost_equals(candidate.geometry): num_modified += 1 candidate.geometry = new_area.geometry candidate.altitude = new_area.altitude candidate.altitude2 = new_area.altitude2 candidate.point1 = new_area.point1 candidate.point2 = new_area.point2 candidate.save() areas_to_save.discard(new_area.tmpid) level_areas[new_area.level].discard(new_area.tmpid) for tmpid in areas_to_save: num_created += 1 areas[tmpid].save() logger = logging.getLogger('c3nav') logger.info(_('%d altitude areas built.') % len(areas)) logger.info(_('%(num_modified)d modified, %(num_deleted)d deleted, %(num_created)d created.') % {'num_modified': num_modified, 'num_deleted': num_deleted, 'num_created': num_created})
def from_shapely(geom_type, geoms, start_index=0, multipart_break=BreakValue.MULTIPART, hole_break=BreakValue.HOLE, string_id=None): """ Create a CF geometry collection from a sequence of geometries. :param str geom_type: The destination geometry type. Valid values are ``"point"``, ``"linestring"``, ``"polygon"``, and multipart types `"multipoint"``, ``"multilinestring"``, ``"multipolygon"``. :param sequence geoms: A sequence of geometry objects to convert. :param int start_index: The starting index value. The default is Python zero-based indexing. Valid values are ``0`` or ``1``. :param int multipart_break: A break value indicating a multipart geometry split. Must be a negative integer value. If ``None``, there will be no attempt to search for multipart splits. :param int hole_break: A break value indicating a hole in the previous polygon. Setting to ``None`` tells the function to not search for holes. Must be a negative integer value. This argument is valid only when decoding polygon geometry objects. All holes (interiors) must be placed after the coordinate index sequence defining the polygon's exterior. :return: :class:`~ncsg.CFGeometryCollection` """ # Only interested in lower-case geometry types. geom_type = geom_type.lower() # Allow a singleton geometry object to be passed. if isinstance(geoms, BaseGeometry): itr = [geoms] else: itr = geoms # Holds coordinate index arrays for each geometry. cindex_all = [] # Holds all the x coordinate values. x = [] # Holds all the y coordinate values. y = [] # May hold z coordinate values. z = None # Flag to indicate if there are z values in our geometries. has_z = False # Track the current node index value when creating coordinate index arrays. node_index = start_index # Convert each geometry to its coordinate index representation. for ctr_geom_element, geom_element in enumerate(itr): # Coordinate index for the current geometry. cindex = [] # Allows us to iterate over single-part and multi-part geometries. for ctr_geom, geom in enumerate(_get_geometry_iter_(geom_element)): # Determine if we have z coordinates in our geometry. if ctr_geom == 0 and ctr_geom_element == 0 and geom.has_z: z = [] has_z = True # Add a multi-part break value if we are on the second component of a geometry. if ctr_geom > 0: cindex += [multipart_break] # Get the geometry nodes' coordinates. if 'polygon' in geom_type: exterior = geom.exterior # Always orient the polygon CCW. if not exterior.is_ccw: exterior = orient(geom).exterior coords = np.array(exterior.coords) else: coords = np.array(geom) # Extend coordinate containers with additional coordinates for the geometry. x_coords = _get_coordinates_as_list_(coords, 0) x += x_coords y += _get_coordinates_as_list_(coords, 1) if has_z: z += _get_coordinates_as_list_(coords, 2) # Extend the coordinate index array. len_x_coords = len(x_coords) cindex += range(node_index, node_index + len_x_coords) # Increment the node index accordingly. node_index += len_x_coords # Check for polygon interiors. try: # Add interiors/holes if the polygon objects contains them. if len(geom.interiors) > 0: for ii in geom.interiors: # Always orient holes CW. if ii.is_ccw: ii = orient(ii, sign=-1.0) # Add a hole to the coordinate index. cindex += [hole_break] # Convert exterior to a coordinate series and add these values to the coordinate arrays. coords = np.array(ii) x += coords[:, 0].tolist() y += coords[:, 1].tolist() if has_z: z += coords[:, 2].tolist() # Add coordinate indices. cindex += range(node_index, node_index + coords.shape[0]) # Increment the node index. node_index += coords.shape[0] except AttributeError: # If this is not a polygon, we are not worried about interiors. if 'polygon' in geom_type: raise # Add the geometries coordinate index array to the collection of index arrays. cindex_all.append(cindex) if 'polygon' in geom_type: outer_ring_order = OuterRingOrder.CCW closure_convention = ClosureConvention.CLOSED else: outer_ring_order = None closure_convention = None return CFGeometryCollection(geom_type, cindex_all, x, y, z=z, start_index=start_index, multipart_break=multipart_break, hole_break=hole_break, outer_ring_order=outer_ring_order, closure_convention=closure_convention, string_id=string_id)
def buildGeometry(self, coordDict, nodeDict): if checkNodesFormClosedWay(self.nodes): # TODO: Need to make sure the coords in the resulting geometry for a counter clockwise ring. self.geometry = orient(Polygon(nodesToCoords(self.nodes, coordDict))) else: print 'Not building geometry for apron since it is not closed.'