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
示例#2
0
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
示例#3
0
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
示例#5
0
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
示例#6
0
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
示例#7
0
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
示例#9
0
    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
示例#11
0
        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
示例#13
0


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)