def remove_hidden(shapes: Sequence[Collection]): """Remove shapes from (nested) list if they are entirely covered. Used to optimize SVG file without altering appearance, e.g. when randomly placing objects to fill a region. Ignores opacity when determining overlap. Args: shapes: A list of shapes. """ def process_list(l, cover): for i, item in reversed(list(enumerate(l))): if isinstance(item, list): process_list(item, cover) elif type(item) is Group: if len(item.clip) > 0: keep_shapes_inside(item.members, shapes.clip) process_list(item.members, cover) else: shape = coverage(item) if shape.within(cover[0]): del l[i] else: cover[0] = cover[0].union(shape) # Pass list to recursive calls so coverage updates: cover = [SPoint((0, 0))] # Pack in list because 'shapes' can be single group: process_list([shapes], cover)
def coverage(obj: Collection) -> Union[SPolygon, SPoint, GeometryCollection]: """Create a shapely object. Used to calculate area/coverage. Args: obj: One or more shapes. Returns: A shapely object representing the union of coverage for all input shapes. """ if type(obj) is list: cover = coverage(obj[0]) for o in obj[1:]: cover = cover.union(coverage(o)) return cover elif type(obj) in [Polygon, Spline, Line]: return SPolygon([pt.state() for pt in obj.points]) elif type(obj) is Circle: c = obj.c.state() return SPoint(c[0], c[1]).buffer(obj.r.state()) else: print("Can't get coverage for:", obj)
def random_point(self): poly = wkb.loads(bytes(self.geom.data)) (min_x, min_y, max_x, max_y) = poly.bounds while True: p = SPoint(random.uniform(min_x, max_x), random.uniform(min_y, max_y)) if poly.contains(p): return p
def keep_points_inside(points: Sequence[Pnt], boundary: Collection): """Keep points that lie within a boundary. Args: points: A list of points. boundary: One or more shapes giving the boundary. """ # reverse so deleting items doesn't affect loop for i, point in reversed(list(enumerate(points))): if not coverage(boundary).intersects(SPoint(point)): del points[i]
def checkIfObjectInArea(objectPos: Point, field: Field): """Checks if object in area of map. Args: field: polygon defining the area objectPos (list): object x and y coordinates Returns: bool: True if object in area """ point = SPoint(objectPos.reprTuple()) (topLeft, topRight, bottomLeft, bottomRight) = field.reprTuple() polygon = SPolygon((bottomLeft, topLeft, topRight, bottomRight)) return polygon.contains(point)
def as_shapely(self) -> SPoint: """Use with caution! Convert this point to a shapely point.""" # Shapely Point construction is expensive! # Note that before python3.8, @cached_property was not thread safe, # nor can it be used in a NamedTuple (which doesn't have a __dict__). # (Points can be used by multi-threaded client code, even when # SMARTS is still single-threaded, so we want to be safe here.) # So we use the private global _shapely_points as a cache instead. # Here we are relying on CPython's implementation of dict # to be thread-safe. cached = _shapely_points.get(self) if cached: return cached spt = SPoint((self.x, self.y, self.z)) _shapely_points[self] = spt return spt
def sample_points_in_shape(shape: dict, n: int) -> List[Pnt]: """Sample random points inside a shape. Args: shape: A shape (currently works for polygons and splines). n: Number of points to sample. Returns: The sampled points. """ bound = bounding_box(shape) points = [] for i in range(n): while True: p = ( np.random.uniform(bound[0], bound[2]), np.random.uniform(bound[1], bound[3]), ) region = SPolygon([pt.state() for pt in shape.points]) if SPoint(p[0], p[1]).within(region): points.append(p) break return points
def shapely_contains(point): point = SPoint(*point) for shape in shapes: if shape.contains(point) or shape.boundary.contains(point): return True return False
for idx in correct_negatives: ax.add_patch(patches.Circle(points[idx], 3, color='xkcd:red')) for idx in false_negatives: ax.add_patch(patches.Circle(points[idx], 3, color='xkcd:red')) # ax.text(points[idx][0]+3, points[idx][1]-3 , axtext(points[idx][0], points[idx][1]), fontsize=5, zorder=5) for idx in false_positives: ax.add_patch(patches.Circle(points[idx], 3, color='xkcd:green')) # ax.text(points[idx][0]+3, points[idx][1]-3 , axtext(points[idx][0], points[idx][1]), fontsize=5, zorder=5) if len(false_negatives)> 0: test_point = points[false_negatives[0]] pt, spt = Point(*test_point), SPoint(*test_point) found = None for idx in range(len(obstacles.obss)): if obstacles.obss[idx].point_in_obstacle(pt) != shapes[idx].contains(spt): found = idx break if found is not None: idx = found print(idx) obstacle = obstacles.obss[idx] projection = Line(pt, Point(obstacle.x.max+5, pt.y)) print(projection) num_cross = 0 for l in obstacle.lines: print(l, l.intersects(projection), projection.intersects(l))
def endpoint(self): return SPoint(self.coords[-1])
def startpoint(self): return SPoint(self.coords[0])
def ConvertShapeToPlacemark(shape, geoid, aland, awater, kml): #if len(shape.parts) > 1: # print '----------geoid=%s aland=%s awater=%s' % (geoid, aland, awater) if shape.shapeType != 5: raise Exception('Unexpected shape type [%d] in file' % shape.shapeType) pm = KML.Placemark( KML.name('%s' % geoid), KML.styleUrl('#ts'), KML.ExtendedData( KML.Data( KML.displayName('ALAND'), KML.value(aland), name='string' ), KML.Data( KML.displayName('AWATER'), KML.value(awater), name='string' ) ), KML.MultiGeometry( KML.Polygon( KML.extrude(0), KML.altitudeMode('clampToGround') ) ) ) # The parentPoly will be used to append rings, and a # new Polygon will be appended for multiple rings in # a geography. parentPoly = pm.MultiGeometry.Polygon #if len(shape.parts) > 1: # print 'shape has %d parts' % len(shape.parts) for i in range(0, len(shape.parts)): lo = shape.parts[i] hi = len(shape.points) if i < len(shape.parts) - 1: hi = shape.parts[i + 1] #if len(shape.parts) > 1: # print 'shape has points in [%d, %d) of %d' % (lo, hi, len(shape.points)) if (shape.points[lo][0] != shape.points[hi-1][0] or shape.points[lo][1] != shape.points[hi-1][1]): raise Exception('Loop endpoints in [%d, %d) do not match' % (lo, hi)) coords = [] for j in reversed(range(lo, hi)): lng = shape.points[j][0] lat = shape.points[j][1] coords.append([lng, lat]) latlngCoords = [] for c in coords: latlngCoords.append('%f,%f,0' % (c[0], c[1])) coordinates = ' '.join(latlngCoords) # Note: need LinearRing to compute ccw. Need Polygon to compute contains(). spoly = SPolygon(coords) if i == 0: parentSpoly = spoly ring = polygon.LinearRing(coords) # Some sanity checks to make sure all rings are closed, non-empty, # and valid. if not ring.is_ring: raise Exception('Badly formatted non-ring : %s' % geoid) if ring.is_empty: raise Exception('Empty geometry found: %s' % geoid) if not ring.is_valid: raise Exception('Invalid ring: %s' % geoid) if not ring.is_ccw: # This ring is an internal (enclave) ring. rring = copy.deepcopy(ring) rring.coords = list(rring.coords)[::-1] # Shapely contains does not handle point-overlaps. This # means that enclaves which touch the containing ring # are not handled correctly. To cure this, we check two # points. if not (parentSpoly.contains(SPoint(rring.coords[0])) or parentSpoly.contains(SPoint(rring.coords[1]))): print 'Out-of-order enclave' # print 'ring %s does not contain %s' % (parentSpoly, ring) # print ring # print rring # Note: if this triggers, we will need to store the polys # to figure out which one is the enclosing one. Hopefully # the census files will not exhibit this, although it is # legal strictly according to the shapefule spec. raise Exception('Out-of-order enclave') coordinates = coordinates + ' 0,0,0' parentPoly.append(KML.innerBoundaryIs( KML.LinearRing( KML.coordinates(coordinates) ) )) else: # Find the containing poly... parentPoly.append(KML.innerBoundaryIs( KML.LinearRing( KML.coordinates(coordinates) ) )) else: if i > 0: # Set the new parent polygon ring. parentSpoly = spoly parentPoly = KML.Polygon( KML.extrude(0), KML.altitudeMode('clampToGround')) pm.MultiGeometry.append(parentPoly) parentPoly.append(KML.outerBoundaryIs( KML.LinearRing( KML.coordinates(coordinates) ) )) return pm
def CheckIfInsidePolygon(self, config, polygon): spoint = SPoint(point[0], point[1]) return polygon.contains(spoint)
def convert(self): return SPoint(self.x, self.y)
def filter_func(tree): """Inner function to filter tree by geo coordinates.""" return filter_poly.intersects(SPoint(tree.latitude, tree.longitude))