def load_glyph(font_name: str, glyph: str, index: int = 0) -> Polygon: face = freetype.Face(font_manager.findfont(font_name), index=index) face.set_char_size(128 * 64) face.load_char(glyph, freetype.FT_LOAD_DEFAULT | freetype.FT_LOAD_NO_BITMAP) ctx = [] face.glyph.outline.decompose(ctx, move_to=move_to, line_to=line_to, conic_to=conic_to, cubic_to=cubic_to) bbox = face.glyph.outline.get_bbox() width = bbox.xMax - bbox.xMin height = bbox.yMax - bbox.yMin svg = f"""<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}"> <g transform="translate(0, {bbox.yMax - bbox.yMin}) scale(1, -1) translate({-bbox.xMin}, {-bbox.yMin})" transform-origin="0 0"> <path style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" d="{' '.join(ctx)}" /> <!-- rect x="{bbox.xMin}" y="{bbox.yMin}" width="{width}" height="{height}" /--> </g> </svg> """ svg_io = io.StringIO(svg) lc, w, h = vp.read_svg(svg_io, 1) mls = lc.as_mls() poly = Polygon(mls.geoms[0]) for geom in mls.geoms[1:]: poly = poly.symmetric_difference(Polygon(geom)) return poly
def make_geom_valid(polygon_geom): """ Given a polygon object, attempt to make it valid via the same logic provided from ST_MakeValid in PostGIS. """ if polygon_geom.is_valid: return polygon_geom elif polygon_geom.is_empty: logger.debug("Empty area!") return None else: logger.debug("Attempting to make polygon valid!") # cut_edges = polygon_geom.boundary.union(get_first_point(polygon_geom.boundary)) # Empty polygon to start off with. geos_area = Polygon() while hasattr(cut_edges, 'geoms'): new_area = build_area(cut_edges) if new_area.is_empty: logger.debug("No more rings can be built with these edges...") break new_area_bound = new_area.boundary symdif = geos_area.symmetric_difference(new_area) if not symdif: logger.warn("No symdif!") geos_area = symdif new_cut_edges = cut_edges.difference(new_area_bound) cut_edges = new_cut_edges if geos_area.is_empty: logger.warn("Failed to make valid!") return None return geos_area
def poly_outer_intersection(poly: Polygon, delete_polys: List[Polygon]) -> Polygon: for delete_poly in delete_polys: if poly.intersects(delete_poly) == True: # If they intersect, create a new polygon that is # essentially pol minus the intersection poly = poly.symmetric_difference(delete_poly).difference( delete_poly) return poly
def calcCurveErr(workspace,poly,mean,sigma,level): # see: http://toblerity.org/shapely/manual.html boundaryestimate = getLevelSet (workspace, mean, level) # GroundTruth = np.vstack((poly,poly[0])) GroundTruth=Polygon(GroundTruth) boundaryestimate=Polygon(boundaryestimate) mislabeled=boundaryestimate.symmetric_difference(GroundTruth) # mislabeled data () boundaryestimate.difference(GroundTruth) #mislabeled as tumor--extra that would be removed GroundTruth.difference(boundaryestimate) # mislbaled as not-tumor--would be missed and should be cut out correct=boundaryestimate.intersection(GroundTruth) #correctly labeled as tumor return correct.area, mislabeled.area
def get_poly_difference(bbox1, bbox2): poly1 = Polygon(bbox1) poly2 = Polygon(bbox2) diff = poly1.symmetric_difference(poly2) plt.switch_backend("TkAgg") if diff.area > 0.01 and showDiff: poly1points = np.array(list(poly1.exterior.coords)) poly2points = np.array(list(poly2.exterior.coords)) plt.plot(poly1points[:, 0], poly1points[:, 1], '--') plt.plot(poly2points[:, 0], poly2points[:, 1], '-.') for poly in diff: coords = poly.exterior.coords.xy plt.plot(coords[0], coords[1], '-') plt.show() return diff.area
def localSymDif(face, polygons, box): """ returns the symmetric difference between a dcel's face and his correspondent polygon of the list of voronoi's polygon (index by shared point) """ p = face.point #get the point to 'index' in polygons list found = False cont = 0 while not found: poly = polygons[cont] if poly.contains(Point(p[0], p[1])): polyFace = Polygon(face.coordsList()) polySelected = polygons[cont] polySelected = polySelected.intersection(box) dS = polyFace.symmetric_difference(polySelected).area found = True else: cont += 1 return dS
def generate_bounding_frame(df): ''' Generates a bounding frame arouund the extents of a shapefile Arguments: df: geodataframe of shapefile to create bounding frame around Output: Geometry of bounding frame (also saves shapefile) frame_df: geodataframe to the bounding frame (only one geometry) ''' # Calculate boundaries of the geodataframe using union of geometries # takes form (min_x, min_y, max_x, max_y) bounds = shp.ops.cascaded_union(list(df['geometry'])).bounds xmin = bounds[0] xmax = bounds[2] xlen = xmax - xmin ymin = bounds[1] ymax = bounds[3] ylen = ymax - ymin # Generate frame geometry. The multiplier of 10 is arbitrary. We just need # it to be large enough that when exporting in GIS over an image it is the # only color on the border of the image it is overlaid on top of in_frame = Polygon([(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]) out_frame = Polygon([(xmin - 10 * xlen, ymin - 10 * ylen), (xmax + 10 * xlen, ymin - 10 * ylen), (xmax + 10 * xlen, ymax + 10 * ylen), (xmin - 10 * xlen, ymax + 10 * ylen)]) frame = out_frame.symmetric_difference(in_frame) # Convert frame polygon into GeoDataFrame frame_df = gpd.GeoDataFrame() frame_df['geometry'] = [frame] return frame_df
def _shift_polygon(self, polygon): """ shifts a polygon according to the origin longitude """ if self.lon0 == 0.0: return [polygon] # no need to shift anything from shapely.geometry import Polygon # we need to split and join some polygons poly_coords = [] holes = [] for (lon, lat) in polygon.exterior.coords: poly_coords.append((lon - self.lon0, lat)) for hole in polygon.interiors: hole_coords = [] for (lon, lat) in hole.coords: hole_coords.append((lon - self.lon0, lat)) holes.append(hole_coords) poly = Polygon(poly_coords, holes) polygons = [] #print "shifted polygons", (time.time() - start) #start = time.time() try: p_in = poly.intersection(self.bounds) polygons += hasattr(p_in, 'geoms') and p_in.geoms or [p_in] except: pass #print "computed polygons inside bounds", (time.time() - start) #start = time.time() try: p_out = poly.symmetric_difference(self.bounds) out_geoms = hasattr(p_out, 'geoms') and p_out.geoms or [p_out] except: out_geoms = [] pass #print "computed polygons outside bounds", (time.time() - start) #start = time.time() for polygon in out_geoms: ext_pts = [] int_pts = [] s = 0 # at first we compute the avg longitude c = 0 for (lon, lat) in polygon.exterior.coords: s += lon c += 1 left = s / float(c) < -180 # and use it to decide where to shift the polygon for (lon, lat) in polygon.exterior.coords: ext_pts.append((lon + (-360, 360)[left], lat)) for interior in polygon.interiors: pts = [] for (lon, lat) in interior.coords: pts.append((lon + (-360, 360)[left], lat)) int_pts.append(pts) polygons.append(Polygon(ext_pts, int_pts)) # print "shifted outside polygons to inside", (time.time() - start) return polygons
def getProductBandTiles(self, geoPolygon, bandName, resolution, dateReference): """Downloads and returns file names with Sentinel2 tiles that best fit the polygon area at the desired date reference. It will perform up/downsampling if deriveResolutions is True and the desired resolution is not available for the required band.""" logger.debug("Getting contents. band=%s, resolution=%s, date=%s", bandName, resolution, dateReference) #find tiles that intercepts geoPolygon within date-tolerance and date+dateTolerance dateTolerance = timedelta(days=self.dateToleranceDays) dateObj = datetime.now() if dateReference != 'now': dateObj = datetime.strptime(dateReference, '%Y-%m-%d') dateFrom = dateObj - dateTolerance dateTo = dateObj dateL2A = datetime.strptime('2018-12-18', '%Y-%m-%d') productLevel = '2A' if dateObj < dateL2A: logger.debug( 'Reference date %s before 2018-12-18. Will use Level1C tiles (no atmospheric correction)' % (dateObj)) productLevel = '1C' resolutionDownload = resolution if self.deriveResolutions: if productLevel == '2A': if resolution == '10m': if bandName in ['B01', 'B09']: resolutionDownload = '60m' elif bandName in [ 'B05', 'B06', 'B07', 'B11', 'B12', 'B8A', 'SCL' ]: resolutionDownload = '20m' elif resolution == '20m': if bandName in ['B08']: resolutionDownload = '10m' elif bandName in ['B01', 'B09']: resolutionDownload = '60m' elif resolution == '60m': if bandName in ['B08']: resolutionDownload = '10m' elif productLevel == '1C': resolutionDownload = '10m' logger.debug("Querying API for candidate tiles") bbox = rasterio.features.bounds(geoPolygon) geoPolygon = [(bbox[0], bbox[3]), (bbox[0], bbox[1]), (bbox[2], bbox[1]), (bbox[2], bbox[3])] area = Polygon(geoPolygon).wkt #query cache key area_hash = hashlib.md5(area.encode()).hexdigest() apicache_file = self.dataPath + "/apiquery/Sentinel-2-S2MSI%s-%s-%s-%s-%s-%s.csv" % ( productLevel, area_hash, dateFrom.strftime("%Y%m%d"), dateTo.strftime("%Y%m%d"), self.cloudCoverage[0], self.cloudCoverage[1]) products_df = None if self.cacheApiCalls: if os.path.isfile(apicache_file): logger.debug("Using cached API query contents") products_df = pd.read_csv(apicache_file) os.system("touch -c %s" % apicache_file) else: logger.debug("Querying remote API") productType = 'S2MSI%s' % productLevel products = self.api.query( area, date=(dateFrom.strftime("%Y%m%d"), dateTo.strftime("%Y%m%d")), platformname='Sentinel-2', producttype=productType, cloudcoverpercentage=self.cloudCoverage) products_df = self.api.to_dataframe(products) logger.debug("Caching API query results for later usage") saveFile(apicache_file, products_df.to_csv(index=True)) logger.debug("Found %d products", len(products_df)) if len(products_df) == 0: raise Exception( 'Could not find any tiles for the specified parameters') products_df_sorted = products_df.sort_values( ['ingestiondate', 'cloudcoverpercentage'], ascending=[False, False]) #select the best product. if geoPolygon() spans multiple tiles, select the best of them missing = Polygon(geoPolygon) desiredRegion = Polygon(geoPolygon) selectedTiles = [] footprints = [desiredRegion] for index, pf in products_df_sorted.iterrows(): #osgeo.ogr.Geometry footprint = gmlToPolygon(pf['gmlfootprint']) if missing.area > 0: if missing.intersects(footprint) == True: missing = (missing.symmetric_difference(footprint) ).difference(footprint) selectedTiles.append(index) footprints.append(footprint) if missing.area > 0: raise Exception( 'Could not find tiles for the whole selected area at date range' ) logger.debug("Tiles selected for covering the entire desired area: %s", selectedTiles) # g = gpd.GeoSeries(footprints) # g.plot(cmap=plt.get_cmap('jet'), alpha=0.5) #download tiles data tileFiles = [] for index, sp in products_df.loc[selectedTiles].iterrows(): url = "https://apihub.copernicus.eu/apihub/odata/v1/Products('%s')/Nodes('%s.SAFE')/Nodes('MTD_MSIL%s.xml')/$value" % ( sp['uuid'], sp['title'], productLevel) meta_cache_file = self.dataPath + "/products/%s-MTD_MSIL%s.xml" % ( sp['uuid'], productLevel) mcontents = '' if self.cacheTilesData and os.path.isfile(meta_cache_file): logger.debug('Reusing cached metadata info for tile \'%s\'', sp['uuid']) mcontents = loadFile(meta_cache_file) os.system("touch -c %s" % meta_cache_file) else: logger.debug('Getting metadata info for tile \'%s\' remotelly', sp['uuid']) r = requests.get(url, auth=(self.user, self.password)) if r.status_code != 200: raise Exception("Could not get metadata info. status=%s" % r.status_code) mcontents = r.content.decode("utf-8") saveFile(meta_cache_file, mcontents) rexp = "<IMAGE_FILE>GRANULE\/([0-9A-Z_]+)\/IMG_DATA\/([0-9A-Z_]+_%s)<\/IMAGE_FILE>" % ( bandName) if productLevel == '2A': rexp = "<IMAGE_FILE>GRANULE\/([0-9A-Z_]+)\/IMG_DATA\/R%s\/([0-9A-Z_]+_%s_%s)<\/IMAGE_FILE>" % ( resolutionDownload, bandName, resolutionDownload) m = re.search(rexp, mcontents) if m == None: raise Exception( "Could not find image metadata. uuid=%s, resolution=%s, band=%s" % (sp['uuid'], resolutionDownload, bandName)) rexp1 = "<PRODUCT_START_TIME>([\-0-9]+)T[0-9\:\.]+Z<\/PRODUCT_START_TIME>" m1 = re.search(rexp1, mcontents) if m1 == None: raise Exception("Could not find product date from metadata") date1 = m1.group(1) downloadFilename = self.dataPath + "/products/%s/%s/%s.tiff" % ( date1, sp['uuid'], m.group(2)) if not os.path.exists(os.path.dirname(downloadFilename)): os.makedirs(os.path.dirname(downloadFilename)) if not self.cacheTilesData or not os.path.isfile(downloadFilename): tmp_tile_filejp2 = "%s/tmp/%s.jp2" % (self.dataPath, uuid.uuid4().hex) tmp_tile_filetiff = "%s/tmp/%s.tiff" % (self.dataPath, uuid.uuid4().hex) if not os.path.exists(os.path.dirname(tmp_tile_filejp2)): os.makedirs(os.path.dirname(tmp_tile_filejp2)) if productLevel == '2A': url = "https://apihub.copernicus.eu/apihub/odata/v1/Products('%s')/Nodes('%s.SAFE')/Nodes('GRANULE')/Nodes('%s')/Nodes('IMG_DATA')/Nodes('R%s')/Nodes('%s.jp2')/$value" % ( sp['uuid'], sp['title'], m.group(1), resolutionDownload, m.group(2)) elif productLevel == '1C': url = "https://apihub.copernicus.eu/apihub/odata/v1/Products('%s')/Nodes('%s.SAFE')/Nodes('GRANULE')/Nodes('%s')/Nodes('IMG_DATA')/Nodes('%s.jp2')/$value" % ( sp['uuid'], sp['title'], m.group(1), m.group(2)) logger.info( 'Downloading tile uuid=\'%s\', resolution=\'%s\', band=\'%s\', date=\'%s\'', sp['uuid'], resolutionDownload, bandName, date1) downloadFile(url, tmp_tile_filejp2, self.user, self.password) #remove near black features on image border due to compression artifacts. if not removed, some black pixels #will be present on final image, specially when there is an inclined crop in source images if bandName == 'TCI': logger.debug('Removing near black compression artifacts') ret = os.system("which nearblack") if ret != 0: raise Exception( "gdal nearblack utility was not found in the system. install it" ) ret = os.system("which gdal_translate") if ret != 0: raise Exception( "gdal gdal_translate utility was not found in the system. install it" ) ret = os.system("nearblack -o %s %s" % (tmp_tile_filetiff, tmp_tile_filejp2)) if ret != 0: raise Exception( "Error during 'nearblack' execution. code=%d" % ret) ret = os.system("gdal_translate %s %s" % (tmp_tile_filetiff, downloadFilename)) if ret != 0: raise Exception( "Error during 'gdal_translate' execution. code=%d" % ret) os.remove(tmp_tile_filetiff) else: os.system("gdal_translate %s %s" % (tmp_tile_filejp2, downloadFilename)) os.remove(tmp_tile_filejp2) else: logger.debug('Reusing tile data from cache') os.system("touch -c %s" % downloadFilename) filename = downloadFilename if resolution != resolutionDownload: filename = self.dataPath + "/products/%s/%s/%s-%s.tiff" % ( m1.group(1), sp['uuid'], m.group(2), resolution) logger.debug( "Resampling band %s originally in resolution %s to %s" % (bandName, resolutionDownload, resolution)) rexp = "([0-9]+).*" rnumber = re.search(rexp, resolution) if not self.cacheTilesData or not os.path.isfile(filename): os.system("gdalwarp -tr %s %s %s %s" % (rnumber.group(1), rnumber.group(1), downloadFilename, filename)) tileFiles.append(filename) return tileFiles
y, color='#6699cc', alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2) inter = poly3.intersection(poly) print(inter.area) x, y = poly2.exterior.xy ax.plot(x, y, color='#6699cc', alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2) diff = poly.symmetric_difference(poly3) print(diff.area) x, y = diff.exterior.xy ax.plot(x, y, color='red', alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2) ax.set_title('Polygon') plt.show()
def visible(sensor, obj_polygon): """ generate sensor visible region in obj_polygon :param sensor: :param obj_polygon: :return: visible region """ ext_ring = np.array(obj_polygon.exterior.coords) int_rings = [ np.array(int_ring.coords) for int_ring in obj_polygon.interiors ] config = sensor.get_config x0, y0 = config['x'], config['y'] ray_start = [x0, y0] min_angle, max_angle = sensor.get_FOV min_rho, max_rho = sensor.get_FOD if config['isOmini'] or min_rho > 0: ray_ends = [] else: ray_ends = [ray_start] angle = min_angle while True: if angle > max_angle: break x, y = x0 + max_rho * np.cos(angle), y0 + max_rho * np.sin(angle) # ray line segment ray_end = [x, y] ray = [ray_start, ray_end] length = max_rho if config['isThrough']: # if sensor can pass through objects pass else: # if sensor can not pass through objects # intersect with ext_ring for i in range(len(ext_ring) - 1): start = ext_ring[i] end = ext_ring[i + 1] line = [start, end] intersect = ray_casting(ray, line) if distance(ray_start, intersect) <= length: ray_end = intersect length = distance(ray_start, intersect) # intersect with int_rings for int_ring in int_rings: for i in range(len(int_ring) - 1): start = int_ring[i] end = int_ring[i + 1] line = [start, end] intersect = ray_casting(ray, line) if distance(ray_start, intersect) <= length: ray_end = intersect length = distance(ray_start, intersect) ray_ends.append(ray_end) angle += EPSILON visible_region = Polygon(ray_ends) if min_rho > 0: try: invisible_region = Point((x0, y0)).buffer(distance=min_rho) visible_region = visible_region.symmetric_difference( invisible_region) except: visible_region = None return visible_region
line_a = LineString(l_a) line_b = LineString(l_b) p = polygonize_full([line_a,line_b]) p0 = [(0,0),(10,0),(10,10),(0,10),(0,0)] p1 = [(0,10),(10,10),(10,20),(0,20),(0,10)] p2 = [(0,0),(20,20),(-5,20),(-5,17),(5,17),(5,12),(-5,12),(0,0)] pol1 = Polygon(p1) pol2 = Polygon(p2) pols = pol1.symmetric_difference(pol2) p1 = [(0,0),(10,0),(10,10),(0,10),(0,0)] p2 = [(2,2),(8,2),(8,8),(2,8),(2,2)] pol1 = Polygon(p1) pol2 = Polygon(p2) pols1 = pol1.symmetric_difference(pol2) pols2 = pol2.symmetric_difference(pol1) p1 = [(0,0),(7,0),(7,7),(0,7),(0,0)] p2 = p1 = [(0,10),(10,10),(10,20),(0,20),(0,10)] pol1 = Polygon(p1) pol2 = Polygon(p2)