Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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]))
Exemplo n.º 4
0
    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)
Exemplo n.º 6
0
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])
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
 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
Exemplo n.º 10
0
 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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
 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)
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
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,
    )
Exemplo n.º 17
0
 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
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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)
Exemplo n.º 21
0
    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)
Exemplo n.º 22
0
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')
Exemplo n.º 23
0
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))
Exemplo n.º 25
0
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
Exemplo n.º 26
0
    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]))
Exemplo n.º 27
0
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
Exemplo n.º 28
0
 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 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]))
Exemplo n.º 30
0
    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)
Exemplo n.º 31
0
    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
Exemplo n.º 32
0
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
Exemplo n.º 33
0
    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()
Exemplo n.º 34
0
    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 
Exemplo n.º 35
0
    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')
Exemplo n.º 36
0
    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)
Exemplo n.º 37
0
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)}")
Exemplo n.º 38
0
 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
Exemplo n.º 39
0
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
Exemplo n.º 40
0
    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
Exemplo n.º 41
0
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)
Exemplo n.º 42
0
    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]))
Exemplo n.º 43
0
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
Exemplo n.º 44
0
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
Exemplo n.º 46
0
    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
Exemplo n.º 47
0
 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)
Exemplo n.º 48
0
def _force_polygon_ccw(geometry):
    polygon = shape(geometry)
    return mapping(orient(polygon))
Exemplo n.º 49
0
    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})
Exemplo n.º 50
0
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)
Exemplo n.º 51
0
 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.'