Exemple #1
0
def test_transform_geom_gdal22():
    """Enabling `antimeridian_cutting` has no effect on GDAL 2.2.0 or newer
    where antimeridian cutting is always enabled.  This could produce
    unexpected geometries, so an exception is raised.
    """
    geom = {"type": "Point", "coordinates": [0, 0]}
    with pytest.raises(GDALVersionError):
        transform_geom("EPSG:4326", "EPSG:3857", geom, antimeridian_cutting=False)
Exemple #2
0
def test_transform_geom():
    geom = {
        'type': 'Polygon',
        'coordinates': (
            ((798842.3090855901, 6569056.500655151),
                (756688.2826828464, 6412397.888771972),
                (755571.0617232556, 6408461.009397383),
                (677605.2284582685, 6425600.39266733),
                (677605.2284582683, 6425600.392667332),
                (670873.3791649605, 6427248.603432341),
                (664882.1106069803, 6407585.48425362),
                (663675.8662823177, 6403676.990080649),
                (485120.71963574126, 6449787.167760638),
                (485065.55660851026, 6449802.826920689),
                (485957.03982722526, 6452708.625101285),
                (487541.24541826674, 6457883.292107048),
                (531008.5797472061, 6605816.560367976),
                (530943.7197027118, 6605834.9333479265),
                (531888.5010308184, 6608940.750411527),
                (533299.5981959199, 6613962.642851984),
                (533403.6388841148, 6613933.172096095),
                (576345.6064638699, 6761983.708069147),
                (577649.6721159086, 6766698.137844516),
                (578600.3589008929, 6770143.99782289),
                (578679.4732294685, 6770121.638265098),
                (655836.640492081, 6749376.357102599),
                (659913.0791150068, 6764770.1314677475),
                (661105.8478791204, 6769515.168134831),
                (661929.4670843681, 6772800.8565198565),
                (661929.4670843673, 6772800.856519875),
                (661975.1582566603, 6772983.354777632),
                (662054.7979028501, 6772962.86384242),
                (841909.6014891531, 6731793.200435557),
                (840726.455490463, 6727039.8672589315),
                (798842.3090855901, 6569056.500655151)),
            )
    }

    result = transform_geom('EPSG:3373', 'EPSG:4326', geom)
    assert result['type'] == 'Polygon'
    assert len(result['coordinates']) == 1

    result = transform_geom(
        'EPSG:3373', 'EPSG:4326', geom, antimeridian_cutting=True)
    assert result['type'] == 'MultiPolygon'
    assert len(result['coordinates']) == 2

    result = transform_geom(
        'EPSG:3373', 
        'EPSG:4326', 
        geom, 
        antimeridian_cutting=True, 
        antimeridian_offset=0)
    assert result['type'] == 'MultiPolygon'
    assert len(result['coordinates']) == 2

    result = transform_geom('EPSG:3373', 'EPSG:4326',  geom,  precision=1)
    assert int(result['coordinates'][0][0][0] * 10) == -1778
def test_transform_geom_gdal22():
    """Enabling `antimeridian_cutting` has no effect on GDAL 2.2.0 or newer
    where antimeridian cutting is always enabled.  This could produce
    unexpected geometries, so an exception is raised.
    """
    geom = {
        'type': 'Point',
        'coordinates': [0, 0]
    }
    with pytest.raises(GDALBehaviorChangeException):
        transform_geom(
            'EPSG:4326', 'EPSG:3857', geom, antimeridian_cutting=False)
Exemple #4
0
def test_transform_geom_polygon_offset(polygon_3373):
    geom = polygon_3373
    result = transform_geom(
        "EPSG:3373", "EPSG:4326", geom, antimeridian_cutting=True, antimeridian_offset=0
    )
    assert result["type"] == "MultiPolygon"
    assert len(result["coordinates"]) == 2
Exemple #5
0
def test_transform_geom_linearring_precision(polygon_3373):
    ring = polygon_3373["coordinates"][0]
    geom = {"type": "LinearRing", "coordinates": ring}
    result = transform_geom(
        "EPSG:3373", "EPSG:4326", geom, precision=1, antimeridian_cutting=True
    )
    assert all(round(x, 1) == x for x in flatten_coords(result["coordinates"]))
Exemple #6
0
def test_transform_geom_linestring_precision_z(polygon_3373):
    ring = polygon_3373['coordinates'][0]
    x, y = zip(*ring)
    ring = list(zip(x, y, [0.0 for i in range(len(x))]))
    geom = {'type': 'LineString', 'coordinates': ring}
    result = transform_geom('EPSG:3373', 'EPSG:3373', geom, precision=1)
    assert int(result['coordinates'][0][0] * 10) == 7988423
    assert int(result['coordinates'][0][2] * 10) == 0
Exemple #7
0
def test_transform_geom_linestring_precision_z(polygon_3373):
    ring = polygon_3373["coordinates"][0]
    x, y = zip(*ring)
    ring = list(zip(x, y, [0.0 for i in range(len(x))]))
    geom = {"type": "LineString", "coordinates": ring}
    result = transform_geom("EPSG:3373", "EPSG:3373", geom, precision=1)
    assert int(result["coordinates"][0][0] * 10) == 7988423
    assert int(result["coordinates"][0][2] * 10) == 0
Exemple #8
0
def test_issue_1446():
    """Confirm resolution of #1446"""
    g = transform_geom(
        CRS.from_epsg(4326),
        CRS.from_epsg(32610),
        {"type": "Point", "coordinates": (-122.51403808499907, 38.06106733107932)},
    )
    assert round(g["coordinates"][0], 1) == 542630.9
    assert round(g["coordinates"][1], 1) == 4212702.1
Exemple #9
0
def test_transform_geom_polygon_offset(polygon_3373):
    geom = polygon_3373
    result = transform_geom(
        'EPSG:3373',
        'EPSG:4326',
        geom,
        antimeridian_cutting=True,
        antimeridian_offset=0)
    assert result['type'] == 'MultiPolygon'
    assert len(result['coordinates']) == 2
Exemple #10
0
def geojson_tile_burn(tile, features, srid, ts, burn_value=1):
    """Burn tile with GeoJSON features."""

    crs = (CRS.from_epsg(srid), CRS.from_epsg(3857))
    shapes = ((transform_geom(*crs, feature["geometry"]), burn_value)
              for feature in features)

    try:
        return rasterize(shapes,
                         out_shape=ts,
                         transform=from_bounds(*tile_bbox(tile, mercator=True),
                                               *ts))
    except:
        return None
def reproject(shapes, bounds):
    with rasterio.Env(OGR_ENABLE_PARTIAL_REPROJECTION=True):
        for g, val in shapes:
            # TODO this produces garbage on the left edge of the world
            g = warp.transform_geom(bounds.crs, WGS84_CRS, g)
            xs, ys = zip(*coords(g))
            yield {
                "type": "Feature",
                "properties": {
                    "value": val
                },
                "bbox": [min(xs), min(ys), max(xs),
                         max(ys)],
                "geometry": g,
            }
Exemple #12
0
def water_mask(case_study, crs, transform, width, height):
    """Water mask from OSM database (water bodies + seas & oceans)."""
    db = osm.OSMDatabase(DB_NAME, DB_USER, DB_PASS, DB_HOST)
    polygons = db.water(case_study.aoi_latlon)
    geoms = [feature['geometry'] for feature in polygons]
    geoms = [transform_geom(WGS84, crs, geom) for geom in geoms]
    if len(geoms) == 0:
        return np.zeros(shape=(height, width), dtype=np.bool)
    water = rasterize(
        shapes=geoms,
        out_shape=(height, width),
        transform=transform,
        dtype=np.uint8).astype(np.bool)
    db.connection.close()
    return water
Exemple #13
0
def create_cutline(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    geometry: Dict,
    geometry_crs: CRS = None,
) -> str:
    """
    Create WKT Polygon Cutline for GDALWarpOptions.

    Ref: https://gdal.org/api/gdalwarp_cpp.html?highlight=vrt#_CPPv415GDALWarpOptions

    Args:
        src_dst (rasterio.io.DatasetReader or rasterio.io.DatasetWriter or rasterio.vrt.WarpedVRT): Rasterio dataset.
        geometry (dict): GeoJSON feature or GeoJSON geometry. By default the coordinates are considered to be in the dataset CRS. Use `geometry_crs` to set a specific CRS.
        geometry_crs (rasterio.crs.CRS, optional): Input geometry Coordinate Reference System
    Returns:
        str: WKT geometry in form of `POLYGON ((x y, x y, ...)))

    """
    if "geometry" in geometry:
        geometry = geometry["geometry"]

    if not is_valid_geom(geometry):
        raise RioTilerError("Invalid geometry")

    geom_type = geometry["type"]
    if geom_type not in ["Polygon", "MultiPolygon"]:
        raise RioTilerError(
            "Invalid geometry type: {geom_type}. Should be Polygon or MultiPolygon"
        )

    if geometry_crs:
        geometry = transform_geom(geometry_crs, src_dst.crs, geometry)

    polys = []
    geom = ([geometry["coordinates"]]
            if geom_type == "Polygon" else geometry["coordinates"])
    for p in geom:
        xs, ys = zip(*coords(p))
        src_y, src_x = rowcol(src_dst.transform, xs, ys)
        src_x = [max(0, min(src_dst.width, x)) for x in src_x]
        src_y = [max(0, min(src_dst.height, y)) for y in src_y]
        poly = ", ".join([f"{x} {y}" for x, y in list(zip(src_x, src_y))])
        polys.append(f"(({poly}))")

    str_poly = ",".join(polys)

    return (f"POLYGON {str_poly}"
            if geom_type == "Polygon" else f"MULTIPOLYGON ({str_poly})")
Exemple #14
0
def test_transform_geom_wrap():
    geom = {
        'type':
        'Polygon',
        'coordinates':
        (((798842.3090855901, 6569056.500655151), (756688.2826828464,
                                                   6412397.888771972),
          (755571.0617232556, 6408461.009397383), (677605.2284582685,
                                                   6425600.39266733),
          (677605.2284582683, 6425600.392667332), (670873.3791649605,
                                                   6427248.603432341),
          (664882.1106069803, 6407585.48425362), (663675.8662823177,
                                                  6403676.990080649),
          (485120.71963574126, 6449787.167760638), (485065.55660851026,
                                                    6449802.826920689),
          (485957.03982722526, 6452708.625101285), (487541.24541826674,
                                                    6457883.292107048),
          (531008.5797472061, 6605816.560367976), (530943.7197027118,
                                                   6605834.9333479265),
          (531888.5010308184, 6608940.750411527), (533299.5981959199,
                                                   6613962.642851984),
          (533403.6388841148,
           6613933.172096095), (576345.6064638699,
                                6761983.708069147), (577649.6721159086,
                                                     6766698.137844516),
          (578600.3589008929,
           6770143.99782289), (578679.4732294685,
                               6770121.638265098), (655836.640492081,
                                                    6749376.357102599),
          (659913.0791150068,
           6764770.1314677475), (661105.8478791204,
                                 6769515.168134831), (661929.4670843681,
                                                      6772800.8565198565),
          (661929.4670843673,
           6772800.856519875), (661975.1582566603,
                                6772983.354777632), (662054.7979028501,
                                                     6772962.86384242),
          (841909.6014891531,
           6731793.200435557), (840726.455490463,
                                6727039.8672589315), (798842.3090855901,
                                                      6569056.500655151)), )
    }
    result = transform_geom('EPSG:3373',
                            'EPSG:4326',
                            geom,
                            antimeridian_cutting=True)
    assert result['type'] == 'MultiPolygon'
    assert len(result['coordinates']) == 2
    def _add_zones(self, source, name, uid, species, path, zone_field):
        with fiona.open(path, 'r') as shp:
            for feature in shp:
                try:
                    zone_id = feature['properties'][zone_field]
                except KeyError:
                    zone_id = feature['properties'][zone_field.lower()]

                if zone_id is None:
                    continue

                zone_id = int(zone_id)

                if zone_id == 0:
                    continue

                if hasattr(name, '__call__'):
                    object_id = feature['properties'].get('OBJECTID')
                    zone_name = name(zone_id, object_id)
                else:
                    zone_name = name.format(zone_id=zone_id, species=SPECIES_NAMES.get(species))

                geometry = transform_geom(shp.crs, {'init': 'EPSG:4326'}, feature['geometry'])

                if feature['geometry']['type'] == 'MultiPolygon':
                    geometry['coordinates'] = itertools.chain(*geometry['coordinates'])

                polygon = Polygon(*[LinearRing(x) for x in geometry['coordinates']])
                uid_suffix = 0

                while True:
                    zone_uid = uid.format(zone_id=zone_id)

                    if uid_suffix > 0:
                        zone_uid += '_{}'.format(uid_suffix)

                    try:
                        with transaction.atomic():
                            SeedZone.objects.create(
                                source=source, name=zone_name, species=species, zone_id=zone_id, zone_uid=zone_uid,
                                polygon=polygon
                            )
                        break
                    except IntegrityError:
                        if uid_suffix > 10:
                            raise

                        uid_suffix += 1
Exemple #16
0
def crop_to_box(image, gps_bbox):
    '''
    This differs from crop to shape in that:
    1 - the bbox coords are WGS84 lat lon (GPS)
    2 - we want the result to be a 'square' image (not a diamond)
    (we're assuming the image is more-or-less aligned N/S)
    '''
    # for some reason the gps_bbox cannot be a shapely object; it has to be a geojson dict
    crs_bbox = transform_geom('WGS84', image.crs, mapping(gps_bbox))
    pixel_bbox = [image.index(*xy) for xy in crs_bbox['coordinates'][0]]
    xs, ys = zip(*pixel_bbox)
    # maybe we should do something smarter here to ensure we always have data?
    # like take the miniumum of the maximal values, etc.
    pixel_bbox = box(min(xs), min(ys), max(xs), max(ys))
    crs_bbox = Polygon([image.xy(*xy) for xy in pixel_bbox.exterior.coords])
    return crop_to_shape(image, crs_bbox)
    def __enter__(self):
        """Support using with Context Managers."""
        self.scene_params = s2_sceneid_parser(self.sceneid)

        prefix = self._prefix.format(**self.scene_params)
        self.tileInfo = json.loads(
            get_object(self._hostname,
                       f"{prefix}/tileInfo.json",
                       request_pays=True))
        input_geom = self.tileInfo["tileDataGeometry"]
        input_crs = CRS.from_user_input(
            input_geom["crs"]["properties"]["name"])
        self.datageom = transform_geom(input_crs, constants.WGS84_CRS,
                                       input_geom)
        self.bounds = featureBounds(self.datageom)
        return self
Exemple #18
0
 def grid(ovr_level: int = Query(...)):
     """return geojson."""
     options = {"OVERVIEW_LEVEL": ovr_level - 1} if ovr_level else {}
     with rasterio.open(self.src_path, **options) as src_dst:
         feats = []
         for _, window in list(src_dst.block_windows(1)):
             geom = bbox_to_feature(src_dst.window_bounds(window))
             geom = transform_geom(
                 src_dst.crs,
                 WGS84_CRS,
                 geom.geometry.dict(),
             )
             feats.append(
                 {"type": "Feature", "geometry": geom, "properties": {}}
             )
     grids = {"type": "FeatureCollection", "features": feats}
     return grids
Exemple #19
0
def rasterize_geojson(geojson,
                      template_path,
                      out_path,
                      crs=None,
                      save_geojson=False):
    """
    Creates transition spatial multipliers from a GeoJSON dictionary or list of dictionaries.
    :param geojson: GeoJSON-formatted dictionary.
    :param template_path: Path to the template raster to constrain the shapes to.
    :param out_path: Path to the outputted raster with burned shapes into it.
    :param crs: CRS of the input geometry. Default is EPSG:4326.
    :param save_geojson: If True, save a copy of the rasterized GeoJSON next to the file.
    """

    if crs is None:
        crs = {'init': 'EPSG:4326'}

    # Handle single geometries as well as lists
    if type(geojson) is dict:
        geojson = [geojson]

    if save_geojson:
        ext = '.{}'.format(out_path.split('.')[-1])
        json_path = out_path.replace(ext, '.json')
        with open(json_path, 'w') as f:
            json.dump(geojson, f)

    with rasterio.open(template_path, 'r') as template:
        if not os.path.exists(os.path.dirname(out_path)):
            os.makedirs(os.path.dirname(out_path))

        with rasterio.open(out_path, 'w', **template.meta.copy()) as dest:
            image = features.rasterize(
                (
                    (transform_geom(crs, template.crs, g), 255.0
                     )  # Todo, should value be 255 or 100?
                    for g in [f['geometry'] for f in geojson]),
                out_shape=template.shape,
                transform=template.transform,
                dtype='float64')
            image[numpy.where(
                image == 0
            )] = 1.0  # Where a polygon wasn't drawn, set multiplier to 1.0
            dest.write(image, 1)
Exemple #20
0
    def get_zones(self):
        for file in self.config.files:
            src_filename = os.path.join(self.dir, file)

            with fiona.open(src_filename) as shp:
                reproject = True
                if shp.crs and "init" in shp.crs and shp.crs["init"].lower(
                ) == "epsg:4326":
                    reproject = False

                else:
                    warnings.warn(
                        "{} is not in WGS84 coordinates, it will be reprojected on the fly, which may be slow!"
                        .format(src_filename),
                        UserWarning,
                    )

                for feature in Bar(f"Processing {self.name}",
                                   max=len(shp)).iter(shp):
                    geometry = feature["geometry"]
                    if reproject:
                        geometry = transform_geom(shp.crs,
                                                  {"init": "EPSG:4326"},
                                                  geometry)

                    if feature["geometry"]["type"] == "MultiPolygon":
                        polygon = MultiPolygon(*[
                            Polygon(*[LinearRing(x) for x in g])
                            for g in geometry["coordinates"]
                        ])
                    else:
                        polygon = Polygon(
                            *[LinearRing(x) for x in geometry["coordinates"]])

                    info = self.config.get_zone_info(feature, file)

                    if info is not None:
                        yield polygon, info

                    else:
                        raise ValueError(
                            "Zone info is not valid for input feature",
                            feature["properties"])
    def geojson_parse_geometry(zoom, srid, feature_map, geometry, buffer):
        if buffer:
            geometry = transform_geom(CRS.from_epsg(srid), CRS.from_epsg(3857),
                                      geometry)  # be sure to be planar
            geometry = mapping(shape(geometry).buffer(buffer))
            srid = 3857

        if geometry["type"] == "Polygon":
            feature_map = geojson_parse_polygon(zoom, srid, feature_map,
                                                geometry)

        elif geometry["type"] == "MultiPolygon":
            for polygon in geometry["coordinates"]:
                feature_map = geojson_parse_polygon(zoom, srid, feature_map, {
                    "type": "Polygon",
                    "coordinates": polygon
                })

        return feature_map
Exemple #22
0
    def handle(self, name, file, *args, **options):
        name = name[0]
        file = file[0]

        if Region.objects.filter(name__iexact=name).exists():
            message = (
                'WARNING: This will replace an existing region with the same name: {}. Do you want to continue? [y/n]'
            ).format(name)
            if input(message).lower() not in {'y', 'yes'}:
                return

        temp_dir = None

        try:
            if file.endswith('.zip'):
                temp_dir = mkdtemp()

                with ZipFile(file) as zf:
                    zf.extractall(temp_dir)

                    try:
                        file = glob.glob(os.path.join(temp_dir, '*.shp'))[0]
                    except IndexError:
                        raise ValueError('No shapefile in zip archive')

            polygons = []

            with fiona.open(file, 'r') as shp:
                for feature in shp:
                    geometry = transform_geom(shp.crs, {'init': 'EPSG:4326'}, feature['geometry'])
                    polygons.append(Polygon(*[LinearRing(x) for x in geometry['coordinates']]))

            with transaction.atomic():
                Region.objects.filter(name__iexact=name).delete()
                Region.objects.create(name=name, polygons=MultiPolygon(polygons))

        finally:
            if temp_dir is not None:
                try:
                    shutil.rmtree(temp_dir)
                except OSError:
                    pass
Exemple #23
0
def create_cutline(src_dst: DataSet,
                   geometry: Dict,
                   geometry_crs: CRS = None) -> str:
    """
    Create WKT Polygon Cutline for GDALWarpOptions.

    Ref: https://gdal.org/api/gdalwarp_cpp.html?highlight=vrt#_CPPv415GDALWarpOptions

    Attributes
    ----------
    src_dst: rasterio.io.DatasetReader
        rasterio.io.DatasetReader object
    geometry: dict
        GeoJSON feature or GeoJSON geometry
    geometry_crs: CRS or str, optional
            Specify bounds coordinate reference system, default is same as input dataset.

    Returns
    -------
    wkt: str
        Cutline WKT geometry in form of `POLYGON ((x y, x y, ...)))

    """
    if "geometry" in geometry:
        geometry = geometry["geometry"]

    geom_type = geometry["type"]
    if not geom_type == "Polygon":
        raise RioTilerError(
            "Invalid geometry type: {geom_type}. Should be Polygon")

    if geometry_crs:
        geometry = transform_geom(geometry_crs, src_dst.crs, geometry)

    xs, ys = zip(*coords(geometry))
    src_y, src_x = rowcol(src_dst.transform, xs, ys)

    src_x = [max(0, min(src_dst.width, x)) for x in src_x]
    src_y = [max(0, min(src_dst.height, y)) for y in src_y]

    poly = ", ".join([f"{x} {y}" for x, y in list(zip(src_x, src_y))])
    return f"POLYGON (({poly}))"
Exemple #24
0
def test_issue_1446_b():
    """Confirm that lines aren't thrown as reported in #1446"""
    src_crs = CRS.from_epsg(4326)
    dst_crs = CRS(
        {
            "proj": "sinu",
            "lon_0": 350.85607029556,
            "x_0": 0,
            "y_0": 0,
            "a": 3396190,
            "b": 3396190,
            "units": "m",
            "no_defs": True,
        }
    )
    collection = json.load(open("tests/data/issue1446.geojson"))
    geoms = {f["properties"]["fid"]: f["geometry"] for f in collection["features"]}
    transformed_geoms = {
        k: transform_geom(src_crs, dst_crs, g) for k, g in geoms.items()
    }
    # Before the fix, this geometry was thrown eastward of 0.0. It should be between -350 and -250.
    assert all([-350 < x < -150 for x, y in transformed_geoms[183519]["coordinates"]])
Exemple #25
0
def transform(shape,
              source_crs,
              destination_crs=None,
              src_affine=None,
              dst_affine=None):
    """Transforms shape from one CRS to another.

    Parameters
    ----------
    shape : shapely.geometry.base.BaseGeometry
        Shape to transform.
    source_crs : dict or str
        Source CRS in the form of key/value pairs or proj4 string.
    destination_crs : dict or str, optional
        Destination CRS, EPSG:4326 if not given.
    src_affine: Affine, optional.
        input shape in relative to this affine
    dst_affine: Affine, optional.
        output shape in relative to this affine

    Returns
    -------
    shapely.geometry.base.BaseGeometry
        Transformed shape.

    """
    if destination_crs is None:
        destination_crs = WGS84_CRS

    if src_affine is not None:
        shape = ops.transform(lambda r, q: ~src_affine * (r, q), shape)

    if source_crs != destination_crs:
        shape = make_shape(transform_geom(source_crs, destination_crs, shape))

    if dst_affine is not None:
        shape = ops.transform(lambda r, q: dst_affine * (r, q), shape)

    return shape
Exemple #26
0
def test_issue_1446_b():
    """Confirm that lines aren't thrown as reported in #1446"""
    src_crs = CRS({"init": "epsg:4326"})
    dst_crs = CRS(
        {
            "proj": "sinu",
            "lon_0": 350.85607029556,
            "x_0": 0,
            "y_0": 0,
            "a": 3396190,
            "b": 3396190,
            "units": "m",
            "no_defs": True,
        }
    )
    collection = json.load(open("tests/data/issue1446.geojson"))
    geoms = {f["properties"]["fid"]: f["geometry"] for f in collection["features"]}
    transformed_geoms = {
        k: transform_geom(src_crs, dst_crs, g) for k, g in geoms.items()
    }
    # Before the fix, this geometry was thrown eastward of 0.0. It should be between -350 and -250.
    assert all([-350 < x < -150 for x, y in transformed_geoms[183519]["coordinates"]])
Exemple #27
0
    def geojson(self, crs=_GEOJSON_EPSG_4326_STRING):
        """ Return this Tile's geometry as GeoJSON

        Parameters
        ----------
        crs : rasterio.crs.CRS
            Coordinate reference system of output. Defaults to EPSG:4326 per
            GeoJSON standard (RFC7946). If ``None``, will return
            geometries in Tile's CRS

        Returns
        -------
        dict
            This tile's geometry and crs represented as GeoJSON

        References
        ----------
        .. [1] https://tools.ietf.org/html/rfc7946#page-12
        """
        gj = shapely.geometry.mapping(self.bbox)

        if crs is not None:
            from rasterio.warp import transform_geom
            crs_ = convert.to_crs(crs)
            if crs_ != self.crs:
                gj = transform_geom(self.crs, crs_, gj)
            else:
                logger.debug('Not reprojecting GeoJSON since output CRS '
                             'is the same as the Tile CRS')

        return {
            'type': 'Feature',
            'properties': {
                'horizontal': self.horizontal,
                'vertical': self.vertical
            },
            'geometry': gj
        }
Exemple #28
0
def reproject_features(features, src_crs, dst_crs):
    """Reproject a list of GeoJSON-like features.

    Parameters
    ----------
    features : iterable of dict
        An iterable of GeoJSON-like features.
    src_crs : CRS
        Source CRS.
    dst_crs : CRS
        Target CRS.

    Returns
    -------
    out_features : iterable of dict
        Iterable of reprojected GeoJSON-like features.
    """
    out_features = []
    for feature in features:
        out_feature = feature.copy()
        out_geom = transform_geom(src_crs, dst_crs, feature['geometry'])
        out_feature['geometry'] = out_geom
        out_features.append(out_feature)
    return out_features
Exemple #29
0
def test_transform_geom_polygon_precision(polygon_3373):
    geom = polygon_3373
    result = transform_geom(
        "EPSG:3373", "EPSG:4326", geom, precision=1, antimeridian_cutting=True
    )
    assert all(round(x, 1) == x for x in flatten_coords(result["coordinates"]))
Exemple #30
0
def test_transform_geom_linestring_precision_iso(polygon_3373):
    ring = polygon_3373["coordinates"][0]
    geom = {"type": "LineString", "coordinates": ring}
    result = transform_geom("EPSG:3373", "EPSG:3373", geom, precision=1)
    assert int(result["coordinates"][0][0] * 10) == 7988423
Exemple #31
0
from tilezilla import geoutils, products, stores, tilespec

# Get tilespec, find product, retrieve intersecting tiles
weld_conus = tilespec.TILESPECS['WELD_CONUS']
product = products.ESPALandsat('../tests/data/LT50120312002300-SC20151009172149/')

tile = list(weld_conus.bounds_to_tiles(product.bounding_box(weld_conus.crs)))[0]

crs.to_string(weld_conus.crs)
weld_conus.crs


geoutils.bounds_to_polygon(tile.bounds)

# TODO: geom_geojson looks a little off
warp.transform_geom('EPSG:4326', 'EPSG:5070', json.dumps(tile.geom_geojson))


# Init the data store
# TODO: switch on some storage format metadata configuration values
store = stores.GeoTIFFStore('tests/data/stores/GeoTIFF', tile, product)

# TODO: allow override driver metadata options (section "creation_options:")
store.meta_options

# TODO: user input / configuration to define desired variables
to_store = _util.multiple_filter(
    [b.long_name for b in product.bands],
    ('.*surface reflectance.*', '.*brightness temperature.*', '^cfmask_band$',),
    True)
to_store
Exemple #32
0
def test_transform_geom_multipolygon(polygon_3373):
    geom = {
        'type': 'MultiPolygon', 'coordinates': [polygon_3373['coordinates']]}
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1)
    assert int(result['coordinates'][0][0][0][0] * 10) == -1778
Exemple #33
0
def test_transform_geom_polygon(polygon_3373):
    geom = polygon_3373
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom)
    assert result['type'] == 'Polygon'
    assert len(result['coordinates']) == 1
Exemple #34
0
def dataset_features(
        src,
        bidx=None,
        sampling=1,
        band=True,
        as_mask=False,
        with_nodata=False,
        geographic=True,
        precision=-1):
    """Yield GeoJSON features for the dataset

    The geometries are polygons bounding contiguous regions of the same raster value.

    Parameters
    ----------
    src: Rasterio Dataset

    bidx: int
        band index

    sampling: int (DEFAULT: 1)
        Inverse of the sampling fraction; a value of 10 decimates

    band: boolean (DEFAULT: True)
        extract features from a band (True) or a mask (False)

    as_mask: boolean (DEFAULT: False)
        Interpret band as a mask and output only one class of valid data shapes?

    with_nodata: boolean (DEFAULT: False)
        Include nodata regions?

    geographic: str (DEFAULT: True)
        Output shapes in EPSG:4326? Otherwise use the native CRS.

    precision: int (DEFAULT: -1)
        Decimal precision of coordinates. -1 for full float precision output

    Yields
    ------
    GeoJSON-like Feature dictionaries for shapes found in the given band
    """
    if bidx is not None and bidx > src.count:
        raise ValueError('bidx is out of range for raster')

    img = None
    msk = None

    # Adjust transforms.
    transform = src.transform
    if sampling > 1:
        # Determine the target shape (to decimate)
        shape = (int(math.ceil(src.height / sampling)),
                 int(math.ceil(src.width / sampling)))

        # Calculate independent sampling factors
        x_sampling = src.width / shape[1]
        y_sampling = src.height / shape[0]

        # Decimation of the raster produces a georeferencing
        # shift that we correct with a translation.
        transform *= Affine.translation(
            src.width % x_sampling, src.height % y_sampling)

        # And follow by scaling.
        transform *= Affine.scale(x_sampling, y_sampling)

    # Most of the time, we'll use the valid data mask.
    # We skip reading it if we're extracting every possible
    # feature (even invalid data features) from a band.
    if not band or (band and not as_mask and not with_nodata):
        if sampling == 1:
            msk = src.read_masks(bidx)
        else:
            msk_shape = shape
            if bidx is None:
                msk = np.zeros(
                    (src.count,) + msk_shape, 'uint8')
            else:
                msk = np.zeros(msk_shape, 'uint8')
            msk = src.read_masks(bidx, msk)

        if bidx is None:
            msk = np.logical_or.reduce(msk).astype('uint8')

        # Possibly overridden below.
        img = msk

    # Read the band data unless the --mask option is given.
    if band:
        if sampling == 1:
            img = src.read(bidx, masked=False)
        else:
            img = np.zeros(
                shape,
                dtype=src.dtypes[src.indexes.index(bidx)])
            img = src.read(bidx, img, masked=False)

    # If as_mask option was given, convert the image
    # to a binary image. This reduces the number of shape
    # categories to 2 and likely reduces the number of
    # shapes.
    if as_mask:
        tmp = np.ones_like(img, 'uint8') * 255
        tmp[img == 0] = 0
        img = tmp
        if not with_nodata:
            msk = tmp

    # Prepare keyword arguments for shapes().
    kwargs = {'transform': transform}
    if not with_nodata:
        kwargs['mask'] = msk

    src_basename = os.path.basename(src.name)

    # Yield GeoJSON features.
    for i, (g, val) in enumerate(
            rasterio.features.shapes(img, **kwargs)):
        if geographic:
            g = warp.transform_geom(
                src.crs, 'EPSG:4326', g,
                antimeridian_cutting=True, precision=precision)
        xs, ys = zip(*coords(g))
        yield {
            'type': 'Feature',
            'id': "{0}:{1}".format(src_basename, i),
            'properties': {
                'val': val,
                'filename': src_basename
            },
            'bbox': [min(xs), min(ys), max(xs), max(ys)],
            'geometry': g
        }
Exemple #35
0
def test_transform_geom_linestring_precision_iso(polygon_3373):
    ring = polygon_3373['coordinates'][0]
    geom = {'type': 'LineString', 'coordinates': ring}
    result = transform_geom('EPSG:3373', 'EPSG:3373', geom, precision=1)
    assert int(result['coordinates'][0][0] * 10) == 7988423
Exemple #36
0
def test_transform_geom_multipolygon(polygon_3373):
    geom = {"type": "MultiPolygon", "coordinates": [polygon_3373["coordinates"]]}
    result = transform_geom("EPSG:3373", "EPSG:4326", geom, precision=1)
    assert all(round(x, 1) == x for x in flatten_coords(result["coordinates"]))
Exemple #37
0
    def _compute_image_stats_chunked(
            dataset: 'DatasetReader') -> Optional[Dict[str, Any]]:
        """Compute statistics for the given rasterio dataset by looping over chunks."""
        from rasterio import features, warp, windows
        from shapely import geometry

        total_count = valid_data_count = 0
        tdigest = TDigest()
        sstats = SummaryStats()
        convex_hull = geometry.Polygon()

        block_windows = [w for _, w in dataset.block_windows(1)]

        for w in block_windows:
            with warnings.catch_warnings():
                warnings.filterwarnings('ignore',
                                        message='invalid value encountered.*')
                block_data = dataset.read(1, window=w, masked=True)

            total_count += int(block_data.size)
            valid_data = block_data.compressed()

            if valid_data.size == 0:
                continue

            valid_data_count += int(valid_data.size)

            if np.any(block_data.mask):
                hull_candidates = RasterDriver._hull_candidate_mask(
                    ~block_data.mask)
                hull_shapes = [
                    geometry.shape(s) for s, _ in features.shapes(
                        np.ones(hull_candidates.shape, 'uint8'),
                        mask=hull_candidates,
                        transform=windows.transform(w, dataset.transform))
                ]
            else:
                w, s, e, n = windows.bounds(w, dataset.transform)
                hull_shapes = [
                    geometry.Polygon([(w, s), (e, s), (e, n), (w, n)])
                ]
            convex_hull = geometry.MultiPolygon([convex_hull,
                                                 *hull_shapes]).convex_hull

            tdigest.update(valid_data)
            sstats.update(valid_data)

        if sstats.count() == 0:
            return None

        convex_hull_wgs = warp.transform_geom(dataset.crs, 'epsg:4326',
                                              geometry.mapping(convex_hull))

        return {
            'valid_percentage': valid_data_count / total_count * 100,
            'range': (sstats.min(), sstats.max()),
            'mean': sstats.mean(),
            'stdev': sstats.std(),
            'percentiles': tdigest.quantile(np.arange(0.01, 1, 0.01)),
            'convex_hull': convex_hull_wgs
        }
Exemple #38
0
def mask(
    input,
    output,
    variable,
    like,
    netcdf3,
    all_touched,
    invert,
    zip):

    """
    Create a NetCDF mask from a shapefile.

    Values are equivalent to a numpy mask: 0 for unmasked areas, and 1 for masked areas.

    Template NetCDF dataset must have a valid projection defined or be inferred from dimensions (e.g., lat / long)
    """

    with Dataset(like) as template_ds:
        template_varname = data_variables(template_ds).keys()[0]
        template_variable = template_ds.variables[template_varname]
        template_crs = get_crs(template_ds, template_varname)

        if template_crs:
            template_crs = CRS.from_string(template_crs)
        elif is_geographic(template_ds, template_varname):
            template_crs = CRS({'init': 'EPSG:4326'})
        else:
            raise click.UsageError('template dataset must have a valid projection defined')

        spatial_dimensions = template_variable.dimensions[-2:]
        mask_shape = template_variable.shape[-2:]

        template_y_name, template_x_name = spatial_dimensions
        coords = SpatialCoordinateVariables.from_dataset(
            template_ds,
            x_name=template_x_name,
            y_name=template_y_name,
            projection=Proj(**template_crs.to_dict())
        )


    with fiona.open(input, 'r') as shp:
        transform_required = CRS(shp.crs) != template_crs

        # Project bbox for filtering
        bbox = coords.bbox
        if transform_required:
            bbox = bbox.project(Proj(**shp.crs), edge_points=21)

        geometries = []
        for f in shp.filter(bbox=bbox.as_list()):
            geom = f['geometry']
            if transform_required:
                geom = transform_geom(shp.crs, template_crs, geom)

            geometries.append(geom)

    click.echo('Converting {0} features to mask'.format(len(geometries)))

    if invert:
        fill_value = 0
        default_value = 1
    else:
        fill_value = 1
        default_value = 0

    with rasterio.Env():
        # Rasterize features to 0, leaving background as 1
        mask = rasterize(
            geometries,
            out_shape=mask_shape,
            transform=coords.affine,
            all_touched=all_touched,
            fill=fill_value,
            default_value=default_value,
            dtype=numpy.uint8
        )

    format = 'NETCDF3_CLASSIC' if netcdf3 else 'NETCDF4'
    dtype = 'int8' if netcdf3 else 'uint8'

    with Dataset(output, 'w', format=format) as out:
        coords.add_to_dataset(out, template_x_name, template_y_name)
        out_var = out.createVariable(variable, dtype, dimensions=spatial_dimensions, zlib=zip,
                                     fill_value=get_fill_value(dtype))
        out_var[:] = mask
Exemple #39
0
def test_transform_geom_multipolygon(polygon_3373):
    geom = {
        'type': 'MultiPolygon', 'coordinates': [polygon_3373['coordinates']]}
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1)
    assert int(result['coordinates'][0][0][0][0] * 10) == -1778
Exemple #40
0
def test_transform_geom_polygon_precision(polygon_3373):
    geom = polygon_3373
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1)
    assert int(result['coordinates'][0][0][0] * 10) == -1778
Exemple #41
0
def test_transform_geom_polygon_cutting(polygon_3373):
    geom = polygon_3373
    result = transform_geom(
        'EPSG:3373', 'EPSG:4326', geom, antimeridian_cutting=True)
    assert result['type'] == 'MultiPolygon'
    assert len(result['coordinates']) == 2
Exemple #42
0
def test_transform_geom_polygon_precision(polygon_3373):
    geom = polygon_3373
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1)
    assert int(result['coordinates'][0][0][0] * 10) == -1778
Exemple #43
0
def test_transform_geom_linestring_precision_iso(polygon_3373):
    ring = polygon_3373["coordinates"][0]
    geom = {"type": "LineString", "coordinates": ring}
    result = transform_geom("EPSG:3373", "EPSG:3373", geom, precision=1)
    assert int(result["coordinates"][0][0] * 10) == 7988423
Exemple #44
0
def test_transform_geom_polygon(polygon_3373):
    geom = polygon_3373
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom)
    assert result['type'] == 'Polygon'
    assert len(result['coordinates']) == 1
Exemple #45
0
def test_transform_geom_array(polygon_3373):
    geom = [polygon_3373 for _ in range(10)]
    result = transform_geom("EPSG:3373", "EPSG:4326", geom, precision=1)
    assert isinstance(result, list)
    assert len(result) == 10
Exemple #46
0
def dataset_features(src,
                     bidx=None,
                     sampling=1,
                     band=True,
                     as_mask=False,
                     with_nodata=False,
                     geographic=True,
                     precision=-1):
    """Yield GeoJSON features for the dataset

    The geometries are polygons bounding contiguous regions of the same raster value.

    Parameters
    ----------
    src: Rasterio Dataset

    bidx: int
        band index

    sampling: int (DEFAULT: 1)
        Inverse of the sampling fraction; a value of 10 decimates

    band: boolean (DEFAULT: True)
        extract features from a band (True) or a mask (False)

    as_mask: boolean (DEFAULT: False)
        Interpret band as a mask and output only one class of valid data shapes?

    with_nodata: boolean (DEFAULT: False)
        Include nodata regions?

    geographic: str (DEFAULT: True)
        Output shapes in EPSG:4326? Otherwise use the native CRS.

    precision: int (DEFAULT: -1)
        Decimal precision of coordinates. -1 for full float precision output

    Yields
    ------
    GeoJSON-like Feature dictionaries for shapes found in the given band
    """
    if bidx is not None and bidx > src.count:
        raise ValueError('bidx is out of range for raster')

    img = None
    msk = None

    # Adjust transforms.
    transform = src.transform
    if sampling > 1:
        # Determine the target shape (to decimate)
        shape = (int(math.ceil(src.height / sampling)),
                 int(math.ceil(src.width / sampling)))

        # Calculate independent sampling factors
        x_sampling = src.width / shape[1]
        y_sampling = src.height / shape[0]

        # Decimation of the raster produces a georeferencing
        # shift that we correct with a translation.
        transform *= Affine.translation(src.width % x_sampling,
                                        src.height % y_sampling)

        # And follow by scaling.
        transform *= Affine.scale(x_sampling, y_sampling)

    # Most of the time, we'll use the valid data mask.
    # We skip reading it if we're extracting every possible
    # feature (even invalid data features) from a band.
    if not band or (band and not as_mask and not with_nodata):
        if sampling == 1:
            msk = src.read_masks(bidx)
        else:
            msk_shape = shape
            if bidx is None:
                msk = np.zeros((src.count, ) + msk_shape, 'uint8')
            else:
                msk = np.zeros(msk_shape, 'uint8')
            msk = src.read_masks(bidx, msk)

        if bidx is None:
            msk = np.logical_or.reduce(msk).astype('uint8')

        # Possibly overridden below.
        img = msk

    # Read the band data unless the --mask option is given.
    if band:
        if sampling == 1:
            img = src.read(bidx, masked=False)
        else:
            img = np.zeros(shape, dtype=src.dtypes[src.indexes.index(bidx)])
            img = src.read(bidx, img, masked=False)

    # If as_mask option was given, convert the image
    # to a binary image. This reduces the number of shape
    # categories to 2 and likely reduces the number of
    # shapes.
    if as_mask:
        tmp = np.ones_like(img, 'uint8') * 255
        tmp[img == 0] = 0
        img = tmp
        if not with_nodata:
            msk = tmp

    # Prepare keyword arguments for shapes().
    kwargs = {'transform': transform}
    if not with_nodata:
        kwargs['mask'] = msk

    src_basename = os.path.basename(src.name)

    # Yield GeoJSON features.
    for i, (g, val) in enumerate(rasterio.features.shapes(img, **kwargs)):
        if geographic:
            g = warp.transform_geom(src.crs,
                                    'EPSG:4326',
                                    g,
                                    antimeridian_cutting=True,
                                    precision=precision)
        xs, ys = zip(*coords(g))
        yield {
            'type': 'Feature',
            'id': "{0}:{1}".format(src_basename, i),
            'properties': {
                'val': val,
                'filename': src_basename
            },
            'bbox': [min(xs), min(ys), max(xs),
                     max(ys)],
            'geometry': g
        }
Exemple #47
0
def zones(
    input,
    output,
    variable,
    attribute,
    like,
    netcdf3,
    zip):

    """
    Create zones in a NetCDF from features in a shapefile.  This is intended
    to be used as input to zonal statistics functions; it is not intended
    as a direct replacement for rasterizing geometries into NetCDF.

    Only handles < 65,535 features for now.

    If --attribute is provided, any features that do not have this will not be
    assigned to zones.

    A values lookup will be used to store values.  The zones are indices of
    the unique values encountered when extracting features.
    The original values are stored in an additional variable with the name of
    the zones variable plus '_values'.

    Template NetCDF dataset must have a valid projection defined or be inferred
    from dimensions (e.g., lat / long).
    """

    with Dataset(like) as template_ds:
        template_varname = list(data_variables(template_ds).keys())[0]
        template_variable = template_ds.variables[template_varname]
        template_crs = get_crs(template_ds, template_varname)

        if template_crs:
            template_crs = CRS.from_string(template_crs)
        elif is_geographic(template_ds, template_varname):
            template_crs = CRS({'init': 'EPSG:4326'})
        else:
            raise click.UsageError('template dataset must have a valid projection defined')

        spatial_dimensions = template_variable.dimensions[-2:]
        out_shape = template_variable.shape[-2:]

        template_y_name, template_x_name = spatial_dimensions
        coords = SpatialCoordinateVariables.from_dataset(
            template_ds,
            x_name=template_x_name,
            y_name=template_y_name,
            projection=Proj(**template_crs.to_dict())
        )


    with fiona.open(input, 'r') as shp:
        if attribute:
            if not attribute in shp.meta['schema']['properties']:
                raise click.BadParameter('{0} not found in dataset'.format(attribute),
                                         param='--attribute', param_hint='--attribute')

            att_dtype = shp.meta['schema']['properties'][attribute].split(':')[0]
            if not att_dtype in ('int', 'str'):
                raise click.BadParameter('integer or string attribute required'.format(attribute),
                                         param='--attribute', param_hint='--attribute')

        transform_required = CRS(shp.crs) != template_crs
        geometries = []
        values = set()
        values_lookup = {}

        # Project bbox for filtering
        bbox = coords.bbox
        if transform_required:
            bbox = bbox.project(Proj(**shp.crs), edge_points=21)

        index = 0
        for f in shp.filter(bbox=bbox.as_list()):
            value = f['properties'].get(attribute) if attribute else int(f['id'])
            if value is not None:
                geom = f['geometry']
                if transform_required:
                    geom = transform_geom(shp.crs, template_crs, geom)

                geometries.append((geom, index))

                if not value in values:
                    values.add(value)
                    values_lookup[index] = value
                    index += 1

            # Otherwise, these will not be rasterized

        num_geometries = len(geometries)
        # Save a slot at the end for nodata
        if num_geometries < 255:
            dtype = numpy.dtype('uint8')
        elif num_geometries < 65535:
            dtype = numpy.dtype('uint16')
        else:
            raise click.UsageError('Too many features to rasterize: {0}, Exceptioning...'.format(num_geometries))

        fill_value = get_fill_value(dtype)

        click.echo('Rasterizing {0} features into zones'.format(num_geometries))

    with rasterio.Env():
        zones = rasterize(
            geometries,
            out_shape=out_shape,
            transform=coords.affine,
            all_touched=False, # True produces undesirable results for adjacent polygons
            fill=fill_value,
            dtype=dtype
        )

    format = 'NETCDF4'
    out_dtype = dtype
    if netcdf3:
        format = 'NETCDF3_CLASSIC'
        if dtype == numpy.uint8:
            out_dtype = numpy.dtype('int16')
        elif dtype == numpy.uint16:
            out_dtype = numpy.dtype('int32')

        # Have to convert fill_value to mask since we changed data type
        zones = numpy.ma.masked_array(zones, mask=(zones == fill_value))


    with Dataset(output, 'w', format=format) as out:
        values_varname = '{0}_values'.format(variable)
        coords.add_to_dataset(out, template_x_name, template_y_name)
        out_var = out.createVariable(variable, out_dtype,
                                     dimensions=spatial_dimensions,
                                     zlib=zip,
                                     fill_value=get_fill_value(out_dtype))
        out_var.setncattr('values', values_varname)
        out_var[:] = zones

        out_values = numpy.array([values_lookup[k] for k in range(0, len(values_lookup))])
        if netcdf3 and out_values.dtype == numpy.int64:
            out_values = out_values.astype('int32')

        out.createDimension(values_varname, len(out_values))
        values_var = out.createVariable(values_varname, out_values.dtype,
                                        dimensions=(values_varname, ),
                                        zlib=zip)
        values_var[:] = out_values
    easting_list = []
    northing_list = []

    for i in highest_east:
        easting_list.append(i)
    for i in highest_north:
        northing_list.append(i)
    buffer_coordinates = generate_coordinates(
        easting_list,
        northing_list)

    # Warp the coordinates
    warped_elevation_coordinates = warp.transform_geom(
        {'init': 'EPSG:27700'},
        elevation.crs,
        {"type": "Polygon",
         "coordinates": [buffer_coordinates]})

    # create an 3d array containing the elevation data masked to the buffer zone
    elevation_mask, out_transform = mask.mask(elevation,
                                              [warped_elevation_coordinates],
                                              crop=False)

    # Search for the highest point in the buffer zone
    highest_point = np.amax(elevation_mask)

    # Extract the index of the highest point in pixel coordinates
    z, highest_east, highest_north = np.where(highest_point == elevation_mask)

    # Isolate the first value from the list
Exemple #49
0
def test_transform_geom_multipolygon(polygon_3373):
    geom = {
        'type': 'MultiPolygon', 'coordinates': [polygon_3373['coordinates']]}
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1)
    assert all(round(x, 1) == x for x in flatten_coords(result['coordinates']))
Exemple #50
0
def test_transform_geom_polygon_precision(polygon_3373):
    geom = polygon_3373
    result = transform_geom(
        "EPSG:3373", "EPSG:4326", geom, precision=1, antimeridian_cutting=True
    )
    assert all(round(x, 1) == x for x in flatten_coords(result["coordinates"]))
Exemple #51
0
def test_transform_geom_dst_crs_none():
    with pytest.raises(CRSError):
        transform_geom(WGS84_crs, None, None)
Exemple #52
0
def test_transform_geom_multipolygon(polygon_3373):
    geom = {"type": "MultiPolygon", "coordinates": [polygon_3373["coordinates"]]}
    result = transform_geom("EPSG:3373", "EPSG:4326", geom, precision=1)
    assert all(round(x, 1) == x for x in flatten_coords(result["coordinates"]))
Exemple #53
0
def test_transform_geom_linestring_precision_iso(polygon_3373):
    ring = polygon_3373['coordinates'][0]
    geom = {'type': 'LineString', 'coordinates': ring}
    result = transform_geom('EPSG:3373', 'EPSG:3373', geom, precision=1)
    assert int(result['coordinates'][0][0] * 10) == 7988423
Exemple #54
0
def test_transform_geom_dst_crs_none():
    with pytest.raises(CRSError):
        transform_geom(WGS84_crs, None, None)
Exemple #55
0
def test_transform_geom_linestring_precision(polygon_3373):
    ring = polygon_3373['coordinates'][0]
    geom = {'type': 'LineString', 'coordinates': ring}
    result = transform_geom('EPSG:3373', 'EPSG:4326', geom, precision=1, antimeridian_cutting=True)
    assert all(round(x, 1) == x for x in flatten_coords(result['coordinates']))
Exemple #56
0
def test_transform_geom_polygon_cutting(polygon_3373):
    geom = polygon_3373
    result = transform_geom("EPSG:3373", "EPSG:4326", geom, antimeridian_cutting=True)
    assert result["type"] == "MultiPolygon"
    assert len(result["coordinates"]) == 2
Exemple #57
0
 def transform(feat):
     dst_crs = 'epsg:4326' if projection == 'geographic' else crs
     geom = transform_geom(crs, dst_crs, feat['geometry'],
                           precision=precision)
     feat['geometry'] = geom
     return feat