Ejemplo n.º 1
0
    def coordinate_system(self, domain: GeoPolygon) -> str:
        raster_files = self.directory.glob("*.tif")
        for filepath in raster_files:
            geo_polygon = GeoPolygon.from_raster_file(filepath=filepath)
            if domain.intersects(geo_polygon):
                return geo_polygon.crs.to_proj4()

        raise RuntimeError(
            "Defining polygon does not intersect with dem raster data.")
def test_get():
    x0 = 8.54758671814368
    y0 = 60.898468
    repo = RasterRepository(directory=Path(data_dir) / "dem_archive")
    input_crs = pyproj.CRS.from_string("+init=EPSG:4326")
    polygon = Polygon.from_bounds(xmin=x0 - 0.1, xmax=x0 + 0.1, ymin=y0 - 0.1, ymax=y0 + 0.1)
    geo_polygon = GeoPolygon(crs=input_crs, polygon=polygon)
    result = repo.read(domain=geo_polygon)
    assert result
Ejemplo n.º 3
0
def polygon():
    # Create polygonal approximation to a circle
    N = 117
    x, y = array([0.51, 0.5])
    r = 0.4

    epsg_id = 32633

    polygon = Polygon([(x + r * cos(t), y + r * sin(t))
                       for t in linspace(0, 2 * pi, N + 1)[:-1]])

    return GeoPolygon(polygon=polygon, crs=pyproj.CRS.from_epsg(epsg_id))
Ejemplo n.º 4
0
    def land_cover(self, *, land_types: Optional[List[LandCoverType]],
                   geo_points: GeoPoints, domain: GeoPolygon) -> np.ndarray:
        tree = etree.parse(str(self.fn), self.parser)
        root = tree.getroot()
        self._assign_data_crs(root)
        pts_crs = geo_points.crs
        if pts_crs.to_authority() != self.data_crs.to_authority():
            xy = np.dstack(
                Transformer.from_crs(pts_crs, self.data_crs).transform(
                    geo_points.xy[:, 0], geo_points.xy[:, 1])).reshape((-1, 2))
        else:
            xy = geo_points.xy
        # Add rel_buffer_size buffer on domain to make sure we hit all geo_points in data projection
        rel_buffer_size = 0.02
        domain = domain.transform(target_crs=self.data_crs)
        bbox = domain.polygon.bounds
        buffer_size = (bbox[2] - bbox[0]) * rel_buffer_size
        domain = domain.buffer(buffer_size)

        # At this point, we have a consistent coordinate system for domain and xy all in the self.data_crs, so
        # omit checks for speed.

        land_cover_types = self.read(domain)

        # TODO: Move to C++ for speed!
        faces = np.zeros(len(xy), dtype="int")
        for i, _pt in enumerate(xy):
            pt = Point(_pt)
            found = False
            for key, p_list in land_cover_types.items():
                for p in p_list:
                    if p.intersects(pt):
                        faces[i] = key.value
                        found = True
                        break
                if found:
                    break
            if not found:
                raise RuntimeError(f"Illegal point {pt}")
        return faces
Ejemplo n.º 5
0
def test_gml_repository():
    if "RASPUTIN_DATA_DIR" not in os.environ:
        raise RuntimeError("Please set RASPUTIN_DATA_DIR")
    path = Path(os.environ["RASPUTIN_DATA_DIR"]) / "corine"
    input_crs = pyproj.CRS.from_string("+init=EPSG:4326")
    target_crs = pyproj.CRS.from_epsg(32633)

    x = np.array([8.5, 8.52, 8.52, 8.5])
    y = np.array([60.55, 60.55, 60.50, 60.50])

    x, y = pyproj.Transformer.from_crs(input_crs, target_crs).transform(x, y)
    domain = GeoPolygon(polygon=Polygon(shell=list(zip(x, y))), crs=target_crs)

    repos = GMLRepository(path=path)
    plot = False
    if plot:
        response = repos.read(domain=domain)
        fig = plt.figure()
        ax = fig.gca()
        for key in response:
            color = [c / 255 for c in LandCoverType.color(land_cover_type=key)]
            for p in response[key]:
                ax.add_patch(PolygonPatch(p, fc=color, alpha=0.5))
        ax.set_xbound(min(x), max(x))
        ax.set_ybound(min(y), max(y))
        plt.show()

    dem_archive = Path(os.environ["RASPUTIN_DATA_DIR"]) / "dem_archive"
    rr = RasterRepository(directory=dem_archive)
    raster_domain = domain.transform(
        target_crs=pyproj.CRS.from_proj4(rr.coordinate_system(domain=domain)))
    raster_data_list = rr.read(domain=raster_domain)
    mesh = Mesh.from_raster(data=raster_data_list, domain=raster_domain)

    cmesh = mesh.simplify(ratio=0.1)
    geo_cell_centers = GeoPoints(xy=cmesh.cell_centers[:, :2], crs=target_crs)
    terrain_cover = repos.land_cover(land_types=None,
                                     geo_points=geo_cell_centers,
                                     domain=domain)
    assert terrain_cover is not None
Ejemplo n.º 6
0
    def get_intersections(self, *,
                          target_polygon: GeoPolygon) -> List[Rasterdata]:

        parts = []

        raster_files = self.directory.glob("*.tif")
        logger = getLogger()
        for filepath in raster_files:
            geo_polygon = GeoPolygon.from_raster_file(filepath=filepath)

            if target_polygon.intersects(geo_polygon):
                logger.info(f"Using file: {filepath}")
                polygon = target_polygon.transform(
                    target_crs=geo_polygon.crs).polygon
                part = read_raster_file(filepath=filepath, polygon=polygon)
                parts.append(part)

                target_polygon = target_polygon.difference(geo_polygon)

                if target_polygon.polygon.area < 1e-10:
                    break
        return parts
Ejemplo n.º 7
0
def polygon_w_hole():
    # Create polygonal approximation to a circle
    N1, N2 = 117, 77
    x, y = array([0.51, 0.5])
    r1, r2 = 0.4, 0.25

    epsg_id = 32633

    polygon_1 = Polygon((x + r1 * cos(t), y + r1 * sin(t))
                        for t in linspace(0, 2 * pi, N1 + 1)[:-1])

    polygon_2 = Polygon((x + r2 * cos(t), y + r2 * sin(t))
                        for t in linspace(0, 2 * pi, N2 + 1)[:-1])

    return GeoPolygon(polygon=polygon_1.difference(polygon_2),
                      crs=pyproj.CRS.from_epsg(epsg_id))
Ejemplo n.º 8
0
 def read(self, domain: GeoPolygon) -> Dict[LandCoverType, List[Polygon]]:
     tree = etree.parse(str(self.fn), self.parser)
     root = tree.getroot()
     self._assign_data_crs(root)
     if domain.crs.to_authority() != self.data_crs.to_authority():
         domain = domain.transform(target_crs=self.data_crs)
     assert "gml" in root.nsmap, "Not a GML file!"
     assert "ogr" in root.nsmap, "Can not find land cover types!"
     gml = f"{{{root.nsmap['gml']}}}"
     ogr = f"{{{root.nsmap['ogr']}}}"
     result = {}
     for elm in root.iter(f"{gml}featureMember"):
         code = LandCoverType(int(next(elm.iter(f"{ogr}clc18_kode")).text))
         polygon = self._parse_polygon(next(elm.iter(f"{gml}Polygon")),
                                       root.nsmap)
         if polygon.intersects(domain.polygon):
             if code not in result:
                 result[code] = []
             result[code].append(polygon.intersection(domain.polygon))
     return result
Ejemplo n.º 9
0
 def polygon(self):
     return GeoPolygon(polygon=Polygon.from_bounds(*self.box),
                       crs=pyproj.CRS.from_proj4(self.coordinate_system))
Ejemplo n.º 10
0
def store_tin():
    """
    Avalance Forecast Visualization Example.

    Items to develop:
     * Construct several color maps for each avalanche danger, and make the web app offer selections
     * Alternative approach is to partition the whole area into disjoint topologies and switch between these
     * Use different textures for different terrain types (under development)
     * Use more of the information from varsom.no (and perhaps alpha blending) to better display avalanche dangers

    """
    logging.basicConfig(level=logging.CRITICAL, format='Rasputin[%(levelname)s]: %(message)s')
    logger = logging.getLogger()


    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument("-x", nargs="+", type=float, help="x-coordinates of polygon", default=None)
    arg_parser.add_argument("-y", nargs="+", type=float, help="y-coordinates of polygon", default=None)
    arg_parser.add_argument("-polyfile", type=str, help="Polygon definition in WKT or WKB format", default="")
    arg_parser.add_argument("-target-coordinate-system", type=str, default="EPSG:32633", help="Target coordinate system")
    arg_parser.add_argument("-ratio", type=float, default=0.4, help="Mesh coarsening factor in [0, 1]")

    arg_parser.add_argument("-override", action="store_true", help="Replace existing archive entry")
    arg_parser.add_argument("-land-type-partition",
                            type=str,
                            default="",
                            choices=["corine", "globcov"],
                            help="Partition mesh by land type")
    arg_parser.add_argument("uid", type=str, help="Unique ID for the result TIN")
    arg_parser.add_argument("-silent", action="store_true", help="Run in silent mode")
    res = arg_parser.parse_args(sys.argv[1:])
    if not res.silent:
        logger.setLevel(logging.INFO)

    if "RASPUTIN_DATA_DIR" in os.environ:
        dem_archive = Path(os.environ["RASPUTIN_DATA_DIR"]) / "dem_archive"
        tin_archive = Path(os.environ["RASPUTIN_DATA_DIR"]) / "tin_archive"
        gc_archive = Path(os.environ["RASPUTIN_DATA_DIR"]) / "globcov"
        corine_archive = Path(os.environ["RASPUTIN_DATA_DIR"]) / "corine"
    else:
        #  data_dir = Path(os.environ["HOME"]) /"projects" / "rasputin_data" / "dem_archive"
        dem_archive = Path(".") / "dem_archive"
        tin_archive = Path(".") / "tin_archive"
        gc_archive = Path(".") / "globcov"
        corine_archive = Path(".") / "corine"
        logger.critical(f"WARNING: No data directory specified, assuming dem_archive {dem_archive.absolute()}")
        logger.critical(f"WARNING: No data directory specified, assuming tin_archive {tin_archive.absolute()}")
        logger.critical(f"WARNING: No data directory specified, assuming globcov_archive {gc_archive.absolute()}")
        logger.critical(f"WARNING: No data directory specified, assuming corine_archive {corine_archive.absolute()}")
    # Some sanity checks
    try:
        next(dem_archive.glob("*.tif"))
    except StopIteration as si:
        raise RuntimeError(f"No GeoTIFF files found in {dem_archive.absolute()}, giving up.")
    if not tin_archive.exists():
        tin_archive.mkdir(parents=True)
    elif tin_archive.exists() and not tin_archive.is_dir():
        raise RuntimeError(f"{tin_archive} exists and is not a directory, giving up.")

    tr = TinRepository(path=tin_archive)
    if res.uid in tr.content:
        if not res.override:
            raise RuntimeError(f"Tin archive {tin_archive.absolute()} already contains uid {res.uid}.")
        else:
            tr.delete(res.uid)

    # Determine region of interest
    if res.polyfile:
        input_domain = GeoPolygon.from_polygon_file(filepath=Path(res.polyfile),
                                                    crs=pyproj.CRS.from_string("+init=EPSG:4326"))

    elif (res.x and res.y):
        assert 3 <= len(res.x) == len(res.y), "x and y coordinates must have equal length greater or equal to 3"
        source_polygon = Polygon((x, y) for (x, y) in zip(res.x, res.y))
        input_domain = GeoPolygon(polygon=source_polygon,
                                  crs=pyproj.CRS.from_string("+init=EPSG:4326"))

    else:
        raise RuntimeError("A constraining polygon is needed")

    target_crs = pyproj.CRS.from_string(f"+init={res.target_coordinate_system}")
    target_domain = input_domain.transform(target_crs=target_crs)

    raster_repo = RasterRepository(directory=dem_archive)
    raster_crs = pyproj.CRS.from_string(raster_repo.coordinate_system(domain=target_domain))
    raster_domain = input_domain.transform(target_crs=raster_crs)

    raster_data_list = raster_repo.read(domain=raster_domain)

    mesh = (Mesh.from_raster(data=raster_data_list,
                             domain=raster_domain)
            .simplify(ratio=res.ratio))


    assert len(mesh.points), "No tin extracted, something went wrong..."
    if raster_crs.to_authority() != target_crs.to_authority():
        points, faces = mesh.points, mesh.faces
        proj = pyproj.Transformer.from_crs(raster_crs, target_crs)
        x, y, z = proj.transform(points[:, 0],
                                 points[:, 1],
                                 points[:, 2])
        points = np.dstack([x, y, z]).reshape(-1, 3)
        mesh = Mesh.from_points_and_faces(points=points, faces=faces, proj4_str=target_crs.to_proj4())

    if res.land_type_partition:
        if res.land_type_partition == "corine":
            lt_repo = gml_repository.GMLRepository(path=corine_archive)
        else:
            lt_repo = globcov_repository.GlobCovRepository(path=gc_archive)
        geo_cell_centers = GeoPoints(xy=mesh.cell_centers[:, :2],
                                     crs=target_crs)
        terrain_cover = lt_repo.land_cover(land_types=None,
                                           geo_points=geo_cell_centers,
                                           domain=target_domain)
        terrain_colors = np.empty((terrain_cover.shape[0], 3), dtype='d')
        extracted_terrain_types = set()
        for i, cell in enumerate(terrain_cover):
            if cell not in extracted_terrain_types:
                extracted_terrain_types.add(cell)
        meta_info = lt_repo.land_cover_meta_info_type
        for tt in extracted_terrain_types:
            cover = lt_repo.land_cover_type(tt)
            color = [c/255 for c in meta_info.color(land_cover_type=cover)]
            terrain_colors[terrain_cover == tt] = color
        tr.save(uid=res.uid,
                geometry=Geometry(mesh=mesh, crs=target_crs),
                land_cover_repository=lt_repo,
                face_fields={"cover_type": terrain_cover, "cover_color": terrain_colors})
    else:
        tr.save(uid=res.uid,
                geometry=Geometry(mesh=mesh, crs=target_crs)
                )

    meta = tr.content[res.uid]
    logger.info(f"Successfully added uid='{res.uid}' to the tin archive {tin_archive.absolute()}, with meta info:")
    pprint.PrettyPrinter(indent=4).pprint(meta)
Ejemplo n.º 11
0
def geo_tiff_reader():
    logger = getLogger()
    arg_parser = argparse.ArgumentParser()
    arg_parser.add_argument("input", type=str, metavar="FILENAME")
    arg_parser.add_argument("-output",
                            type=str,
                            default="output.off",
                            help="Surface mesh file name")
    arg_parser.add_argument("-x",
                            nargs="+",
                            type=float,
                            help="x-coordinates of polygon",
                            default=None)
    arg_parser.add_argument("-y",
                            nargs="+",
                            type=float,
                            help="y-coordinates of polygon",
                            default=None)
    arg_parser.add_argument("-sun_x", type=float, help="Sun ray x component")
    arg_parser.add_argument("-sun_y", type=float, help="Sun ray y component")
    arg_parser.add_argument("-sun_z", type=float, help="Sun ray z component")
    arg_parser.add_argument("-n",
                            action="store_true",
                            help="Compute surface normals")
    arg_parser.add_argument("-slope",
                            action="store_true",
                            help="Compute slope")
    arg_parser.add_argument("-loglevel", type=int, default=1, help="Verbosity")
    group = arg_parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "-ratio",
        type=float,
        help="Edge ratio between original meshed raster and result")
    group.add_argument("-size",
                       type=int,
                       help="Number of edges in the generated surface mesh")

    res = arg_parser.parse_args(sys.argv[1:])
    logger.setLevel(res.loglevel)

    # Determine region of interest
    x_coords = res.x if res.x else []
    y_coords = res.y if res.y else []

    if len(x_coords) == len(y_coords) == 0:
        polygon = None

    elif 3 <= len(x_coords) == len(y_coords):
        polygon = Polygon((x, y) for (x, y) in zip(x_coords, y_coords))

    else:
        raise ValueError(
            "x and y coordinates must have equal length greater or equal to 3")

    # Read from raster
    rasterdata = read_raster_file(filepath=Path(res.input), polygon=polygon)
    m, n = rasterdata.array.shape
    logger.debug(f"Original: {m * n}")
    logger.critical(rasterdata.info)

    domain = GeoPolygon(polygon=polygon, proj=None)

    mesh = (Mesh.from_raster(rasterda=rasterdata,
                             domain=domain).simplify(ratio=res.ratio,
                                                     max_size=res.max_size))

    pts, faces, normals = mesh.points, mesh.faces, mesh.face_normals

    logger.debug(f"Result: {len(pts)}")

    # Compute additional fields
    fields = {}

    if res.sun_x is not None or res.sun_y is not None or res.sun_z is not None:
        assert res.sun_x is not None
        assert res.sun_y is not None
        assert res.sun_z is not None
        fields["shade"] = compute_shade(
            pts=pts, faces=faces, sun_ray=[res.sun_x, res.sun_y, res.sun_z])

    if res.n:
        fields["surface_normal"] = normals

    if res.slope:
        slopes = compute_slopes(normals)
        fields["slope"] = slopes

    output_path = Path(res.output)
    write(pts=pts, faces=faces, filepath=output_path, fields=fields)