def test_buf_dst_crs(tmpdir): outfile = str(tmpdir.mkdir('out').join("buffered.geojson")) result = CliRunner().invoke(fio_buffer.core.buffer, [ 'tests/data/points.geojson', outfile, '--distance', '100', '--driver', 'GeoJSON', '--buf-crs', 'EPSG:3857', '--dst-crs', 'EPSG:900913' ]) assert result.exit_code == 0 with fio.open('tests/data/points.geojson') as points, fio.open(outfile) as actual: for pnt, buf in zip(points, actual): # Reproject, buffer, reroject to create an expected geometry geom = transform_geom('EPSG:4326', 'EPSG:3857', pnt['geometry']) buf_geom = mapping(shape(geom).buffer(100)) e_coords = transform_geom('EPSG:3857', 'EPSG:900913', buf_geom)['coordinates'][0] a_coords = buf['geometry']['coordinates'][0] for e_pair, a_pair in zip(e_coords, a_coords): # Multipolygons can creep in and mess up the test so just skip them if buf['geometry']['type'] == 'Polygon': e_x, e_y = e_pair a_x, a_y = a_pair assert round(e_x, 3) == round(a_x, 3) assert round(e_y, 3) == round(a_y, 3)
def _processor(args): """ Process a single feature Parameters ---------- args : dict feat - A GeoJSON feature to process. src_crs - The geometry's CRS. buf_crs - Apply buffer after reprojecting to this CRS. dst_crs - Reproject buffered geometry to this CRS before returning. skip_failures - If True then Exceptions don't stop processing. buf_args - Keyword arguments for the buffer operation. Returns ------- dict GeoJSON feature with updated geometry. """ feat = args['feat'] src_crs = args['src_crs'] buf_crs = args['buf_crs'] dst_crs = args['dst_crs'] skip_failures = args['skip_failures'] buf_args = args['buf_args'] try: # src_crs -> buf_crs reprojected = transform_geom( src_crs, buf_crs, feat['geometry'], antimeridian_cutting=True ) # buffering operation buffered = asShape(reprojected).buffer(**buf_args) # buf_crs -> dst_crs feat['geometry'] = transform_geom( buf_crs, dst_crs, mapping(buffered), antimeridian_cutting=True ) return feat except Exception: log.exception("Feature with ID %s failed" % feat.get('id')) if not skip_failures: raise
def bbox_copy(in_file, out_file, bbox, in_layer=0, out_layer=None, dst_crs=None): """Dump all features within the provided WGS84 bbox to a new file """ with fiona.drivers(): with fiona.open(in_file, layer=in_layer) as source: output_schema = source.schema.copy() # transform the provided bbox to the crs of source data bbox_proj = transform_bbox(bbox, from_epsg(4326), out_crs=source.meta['crs']) # use source crs if no reprojection specified if dst_crs: out_crs = dst_crs else: out_crs = source.crs with fiona.open(out_file, 'w', crs=out_crs, driver="ESRI Shapefile", schema=output_schema) as sink: for f in source.filter(bbox=bbox_proj): # transform only if dst_crs specified if dst_crs: g = transform_geom( source.crs, dst_crs, f['geometry'], antimeridian_cutting=True) f['geometry'] = g sink.write(f)
def build_mask(raster_path, shp_path, dest_path): # Read the GeoTIFF with rasterio with rasterio.open(raster_path) as src: profile = src.profile crs = profile["crs"].to_string() # Read the shapefile with fiona with fiona.open(shp_path, "r") as shapefile: shp_crs = shapefile.crs geoms = [ transform_geom(shapefile.crs, crs, feature["geometry"]) for feature in shapefile ] # Mask with the list of geometries; crop to the geoms extent dest_image, dest_transform = mask(src, geoms, crop=True) # Update the profile profile["height"] = dest_image.shape[1] profile["width"] = dest_image.shape[2] profile["transform"] = dest_transform # Write the masked data in the destination raster with rasterio.open(dest_path, "w", **profile) as dest: dest.write(dest_image)
def transform_shape_vector(infile): """Reads vector AOI bounds and reprojects to EPSG:4326. Returns bounds as shapely polygon. Parameters ---------- infile : str Path to vector AOI. Returns ------- shp : shapely object Infile AOI bounds as a shapely polygon object. Raises ------- ValueError If the input vector file is not a polygon. """ import fiona from fiona.transform import transform_geom with fiona.open(infile, encoding='utf-8') as c: shp_geom = c.schema['geometry'] if shp_geom != 'Polygon': raise ValueError( 'Shapefile not accepted. Only polygons may be used as input.') infile_4326 = transform_geom(c.crs.get("init"), 'epsg:4326', c[0]['geometry']) shp = shape(infile_4326) shp = shp.buffer(0.125) return shp
def rasterio_get_data(filepath, geojson): """ Returns the data rectangle surrounding the geometry over several channels from a raster. :param filepath: the path to the raster file :param geojson: the GeoJSON feature :return: the extracted data :rtype: np.ndarray """ try: crs_obj = geojson["properties"]["crs"] except KeyError: # Default coordinates reference system is WGS84 crs = "EPSG:4326" else: crs = CRS.from_user_input(crs_obj["properties"]["name"]).to_string() logging.info(f"GeoJSON CRS: {crs}") with rasterio.open(filepath) as img: img_crs = img.profile["crs"].to_string() logging.info(f"Raster CRS: {img_crs}") if crs != img_crs: geometry = transform_geom(crs, img_crs, geojson["geometry"]) else: geometry = geojson["geometry"] logging.info(f"GeoJSON geometry: {geometry}") array, _ = rasterio.mask.mask(img, shapes=[geometry], crop=True, filled=False) logging.info(f"Array extracted from raster. Shape={array.shape}") return array.data
def from_shapefile(self, zipped_shapefile_file, id_field=None): """ Load ShapeFile content provided into a zipped archive. zipped_shapefile_file -- a file-like object on the zipped content id_field -- the field name used a identifier """ with fiona.BytesCollection(zipped_shapefile_file.read()) as shape: # Extract source projection and compute if reprojection is required projection = self._fiona_shape_projection(shape) reproject = projection and not self.is_projection_allowed( projection.upper()) for feature in shape: properties = {} for prop, value in feature.get('properties', {}).items(): try: properties[prop] = json.loads(value) except (json.JSONDecodeError, TypeError): properties[prop] = value geometry = feature.get('geometry') if reproject: geometry = transform_geom( shape.crs, f'EPSG:{app_settings.INTERNAL_GEOMETRY_SRID}', geometry) identifier = properties.get(id_field, uuid.uuid4()) self.features.create( layer=self, identifier=identifier, properties=properties, geom=GEOSGeometry(json.dumps(geometry)), )
def cat(ctx, input, precision, indent, compact, ignore_errors, dst_crs, x_json_seq_rs): """Concatenate and print the features of input datasets as a sequence of GeoJSON features.""" verbosity = ctx.obj['verbosity'] logger = logging.getLogger('fio') sink = click.get_text_stream('stdout') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') item_sep = compact and ',' or ', ' try: with fiona.drivers(CPL_DEBUG=verbosity>2): for path in input: with fiona.open(path) as src: for feat in src: if dst_crs or precision > 0: g = transform_geom( src.crs, dst_crs, feat['geometry'], antimeridian_cutting=True, precision=precision) feat['geometry'] = g if x_json_seq_rs: sink.write(u'\u001e') json.dump(feat, sink, **dump_kwds) sink.write("\n") sys.exit(0) except Exception: logger.exception("Failed. Exception caught") sys.exit(1)
def test_transform_geom_null_dest(): failed_geom = { 'type': 'Polygon', 'coordinates': (( (81.2180196471443, 6.197141424988303), (80.34835696810447, 5.968369859232141), (79.87246870312859, 6.763463446474915), (79.69516686393516, 8.200843410673372), (80.14780073437967, 9.824077663609557), (80.83881798698664, 9.268426825391174), (81.3043192890718, 8.564206244333675), (81.78795901889143, 7.523055324733178), (81.63732221876066, 6.481775214051936), (81.2180196471443, 6.197141424988303) ),) } with pytest.warns(UserWarning): transformed_geom = transform.transform_geom( src_crs="epsg:4326", dst_crs="epsg:32628", geom=failed_geom, antimeridian_cutting=True, precision=2, ) assert transformed_geom is None
def reproject(f, srs_crs, dest_crs): f['geometry'] = transform_geom(srs_crs, dest_crs, f['geometry'], antimeridian_cutting=True, precision=-1) return f
def test_axis_ordering(crs): """ Test if transform uses traditional_axis_mapping """ expected = (-8427998.647958742, 4587905.27136252) t1 = transform.transform(crs, "epsg:3857", [-75.71], [38.06]) assert (t1[0][0], t1[1][0]) == pytest.approx(expected) geom = {"type": "Point", "coordinates": [-75.71, 38.06]} g1 = transform.transform_geom(crs, "epsg:3857", geom, precision=3) assert g1["coordinates"] == pytest.approx(expected) rev_expected = (-75.71, 38.06) t2 = transform.transform("epsg:3857", crs, [-8427998.647958742], [4587905.27136252]) assert (t2[0][0], t2[1][0]) == pytest.approx(rev_expected) geom = {"type": "Point", "coordinates": [-8427998.647958742, 4587905.27136252]} g2 = transform.transform_geom("epsg:3857", crs, geom, precision=3) assert g2["coordinates"] == pytest.approx(rev_expected)
def record_converter(record, desired_output, admin_level_info, country_info, available_fields, src_crs): name_field = findNameField(record, admin_level_info, available_fields) id_field = getIdField(admin_level_info, available_fields) feature_name = None if name_field != None: feature_name = record['properties'][name_field] if feature_name.isupper() or feature_name.islower(): feature_name = feature_name.title() else: print( f"Could not find name field: {country_info['countryIsoAlpha3Code']}, {admin_level_info['localName']}" ) return { 'geometry': transform_geom(src_crs, "EPSG:3832", record['geometry']), 'properties': OrderedDict([ (f"{desired_output['shortName']}_NAME", feature_name), (f"{desired_output['shortName']}_CODE", f"{country_info['countryIsoAlpha3Code']}-{int(record['properties'][id_field])}" ), ('CNTRY_ISO3', country_info['countryIsoAlpha3Code']), ('LC_AD_TYPE', admin_level_info['localName']), ('SRC_DS', 'popgis') ]) }
def shapefile_generator(filename): """ A generator that iterates over records in a shapefile """ with fiona.open(filename) as collection: for item in collection: item['geometry'] = transform_geom(collection.meta['crs'], 'epsg:4326', item['geometry']) yield item
def _reproject_geom(geometry, src_crs, dst_crs, validity_check=True): if geometry.is_empty or src_crs == dst_crs: return geometry.buffer(0) out_geom = to_shape( transform_geom(src_crs.to_dict(), dst_crs.to_dict(), mapping(geometry))).buffer(0) if validity_check and not out_geom.is_valid or out_geom.is_empty: raise TopologicalError("invalid geometry after reprojection") return out_geom
def _reproject_geom(geometry, src_crs, dst_crs): if geometry.is_empty or src_crs == dst_crs: return _repair(geometry) out_geom = _repair( to_shape( transform_geom(src_crs.to_dict(), dst_crs.to_dict(), mapping(geometry)))) if validity_check and (not out_geom.is_valid or out_geom.is_empty): raise TopologicalError("invalid geometry after reprojection") return out_geom
def __(geom: Dict[str, Any], src_srid: int, dst_srid: int, antimeridian_cutting: bool = False, antimeridian_offset: float = 10.0, precision: int = -1) -> Dict[str, Any]: """ """ return transform_geom(f"EPSG:{src_srid}", f"EPSG:{dst_srid}", geom, antimeridian_cutting, antimeridian_offset, precision)
def test_transform_issue971(): """ See https://github.com/Toblerity/Fiona/issues/971 """ source_crs = "epsg:25832" dest_src = "epsg:4326" geom = {'type': 'GeometryCollection', 'geometries': [{'type': 'LineString', 'coordinates': [(512381.8870945257, 5866313.311218272), (512371.23869999964, 5866322.282500001), (512364.6014999999, 5866328.260199999)]}]} geom_transformed = transform.transform_geom(source_crs, dest_src, geom, precision=3) assert geom_transformed['geometries'][0]['coordinates'][0] == pytest.approx((9.184, 52.946))
def test_transform_geom_array_z(geom): """Transforming a geom array with Z succeeds""" g2 = transform.transform_geom( "epsg:4326", "epsg:3857", [geom for _ in range(5)], precision=3, ) assert isinstance(g2, list) assert len(g2) == 5
def _reproject_geom(geometry, src_crs, dst_crs): if geometry.is_empty: return geometry else: out_geom = to_shape( transform_geom(src_crs.to_dict(), dst_crs.to_dict(), mapping(geometry), antimeridian_cutting=antimeridian_cutting)) return _repair(out_geom) if validity_check else out_geom
def main(): logging.basicConfig( stream=sys.stdout, level=logging.INFO, format='%(asctime)-15s %(name)s %(levelname)-8s %(message)s') parser = argparse.ArgumentParser( description='Import regions from shapefile') parser.add_argument('filename', type=str, help='Shapefile containing regions') args = parser.parse_args() session = get_session() session.execute("DELETE FROM t1_survey_region") with fiona.open(args.filename, encoding='Windows-1252') as shp: for index, feature in enumerate(tqdm(shp)): props = feature['properties'] geometry = shape( transform_geom(shp.crs, 'EPSG:4326', feature['geometry'])) geometry = geometry.buffer(0) session.execute( """INSERT INTO region (id, name, geometry, state, positional_accuracy_in_m) VALUES (:id, :name, ST_GeomFromWKB(_BINARY :geometry_wkb), :state, :positional_accuracy_in_m)""", { 'id': index, 'name': props['RegName'], 'geometry_wkb': shapely.wkb.dumps( to_multipolygon(geometry)), 'state': props['StateName'], 'positional_accuracy_in_m': int(props['Accuracy']) }) for geometry in subdivide_geometry(geometry): session.execute( """INSERT INTO region_subdiv (id, name, geometry) VALUES (:id, :name, ST_GeomFromWKB(_BINARY :geometry_wkb))""", { 'id': index, 'name': props['RegName'], 'geometry_wkb': shapely.wkb.dumps(to_multipolygon(geometry)) }) log.info("Updating t1_survey_region (this may take a while)") session.execute("CALL update_t1_survey_region(NULL)") session.commit()
def cat(ctx, files, precision, indent, compact, ignore_errors, dst_crs, use_rs, bbox, layer): """ Concatenate and print the features of input datasets as a sequence of GeoJSON features. When working with a multi-layer dataset the first layer is used by default. Use the '--layer' option to select a different layer. """ verbosity = (ctx.obj and ctx.obj['verbosity']) or 2 logger = logging.getLogger('fio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') # Validate file idexes provided in --layer option # (can't pass the files to option callback) if layer: options.validate_multilayer_file_index(files, layer) # first layer is the default for i in range(1, len(files) + 1): if str(i) not in layer.keys(): layer[str(i)] = [0] try: with fiona.drivers(CPL_DEBUG=verbosity > 2): for i, path in enumerate(files, 1): for lyr in layer[str(i)]: with fiona.open(path, layer=lyr) as src: if bbox: try: bbox = tuple(map(float, bbox.split(','))) except ValueError: bbox = json.loads(bbox) for i, feat in src.items(bbox=bbox): if dst_crs or precision >= 0: g = transform_geom( src.crs, dst_crs, feat['geometry'], antimeridian_cutting=True, precision=precision) feat['geometry'] = g feat['bbox'] = fiona.bounds(g) if use_rs: click.echo(u'\u001e', nl=False) click.echo(json.dumps(feat, **dump_kwds)) except Exception: logger.exception("Exception caught during processing") raise click.Abort()
def cat(ctx, files, precision, indent, compact, ignore_errors, dst_crs, use_rs, bbox, layer): """ Concatenate and print the features of input datasets as a sequence of GeoJSON features. When working with a multi-layer dataset the first layer is used by default. Use the '--layer' option to select a different layer. """ verbosity = (ctx.obj and ctx.obj['verbosity']) or 2 logger = logging.getLogger('fio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') item_sep = compact and ',' or ', ' # Validate file idexes provided in --layer option # (can't pass the files to option callback) if layer: options.validate_multilayer_file_index(files, layer) # first layer is the default for i in range(1, len(files) + 1): if str(i) not in layer.keys(): layer[str(i)] = [0] try: with fiona.drivers(CPL_DEBUG=verbosity > 2): for i, path in enumerate(files, 1): for lyr in layer[str(i)]: with fiona.open(path, layer=lyr) as src: if bbox: try: bbox = tuple(map(float, bbox.split(','))) except ValueError: bbox = json.loads(bbox) for i, feat in src.items(bbox=bbox): if dst_crs or precision >= 0: g = transform_geom( src.crs, dst_crs, feat['geometry'], antimeridian_cutting=True, precision=precision) feat['geometry'] = g feat['bbox'] = fiona.bounds(g) if use_rs: click.echo(u'\u001e', nl=False) click.echo(json.dumps(feat, **dump_kwds)) except Exception: logger.exception("Exception caught during processing") raise click.Abort()
def extract_pixels(self, raster_fn, ranch=None, pasture=None): if ranch is not None: ranch = ranch.replace(' ', '_') if pasture is not None: pasture = pasture.replace(' ', '_') if pasture is None: raise NotImplementedError("Cannot process request") ds = rasterio.open(raster_fn) ds_proj4 = ds.crs.to_proj4() loc_path = self.loc_path _d = self._d sf_fn = _join(loc_path, _d['sf_fn']) sf_feature_properties_key = _d['sf_feature_properties_key'] sf_fn = os.path.abspath(sf_fn) sf = fiona.open(sf_fn, 'r') features = [] for feature in sf: properties = feature['properties'] key = properties[sf_feature_properties_key].replace(' ', '_') _pasture, _ranch = key.split(self.key_delimiter) if self.reverse_key: _ranch, _pasture = _pasture, _ranch if ranch is not None: if _ranch.lower() != ranch.lower(): continue _features = transform_geom(sf.crs_wkt, ds_proj4, feature['geometry']) if pasture is None: features.append(_features) elif _pasture.lower() == pasture.lower(): features.append(_features) data = ds.read(1, masked=True) if 'biomass' in raster_fn: data = np.ma.masked_values(data, 0) pasture_mask, _, _ = raster_geometry_mask(ds, features) x = np.ma.MaskedArray(data, mask=pasture_mask) return x.compressed().tolist(), int(x.count())
def read_records(self, file_name, range_tracker): from fiona import BytesCollection from fiona.transform import transform_geom import json total_bytes = self.estimate_size() next_pos = range_tracker.start_position() def split_points_unclaimed(stop_pos): return 0 if stop_pos <= next_pos else iobase.RangeTracker.SPLIT_POINTS_UNKNOWN range_tracker.set_split_points_unclaimed_callback( split_points_unclaimed) with self.open_file(file_name) as f: if self.layer_name: collection = BytesCollection(f.read(), layer=self.layer_name) else: collection = BytesCollection(f.read()) src_crs = _GeoSourceUtils.validate_crs(collection.crs, self.in_epsg) num_features = len(collection) feature_bytes = math.floor(total_bytes / num_features) i = 0 logging.info( json.dumps({ 'msg': 'read_records', 'file_name': file_name, 'profile': collection.profile, 'num_features': num_features, 'total_bytes': total_bytes })) while range_tracker.try_claim(next_pos): i = math.ceil(next_pos / feature_bytes) if i >= num_features: break cur_feature = collection[i] geom = cur_feature['geometry'] props = cur_feature['properties'] if not self.skip_reproject: geom = transform_geom(src_crs, 'epsg:4326', geom) yield (props, geom) next_pos = next_pos + feature_bytes
def process(self, element: bytes): import json from fiona.transform import transform_geom parsed = json.loads(element.decode('utf-8')) geom = {"type": "Point", "coordinates": [parsed['x'], parsed['y']]} # reproject from the given proj str to 4326, aka WGS 84 used by BQ geom = transform_geom(self.in_crs, 'epsg:4326', geom) # ready to be inserted into BQ record = {**parsed, 'geom': json.dumps(geom)} logging.info(record) yield record
def output_poly_to_file(dst_file, polygons, src_crs, dst_crs): print('Writing polygon file...') with fiona.open(dst_file, 'w', crs=dst_crs, driver='ESRI Shapefile', schema={'geometry': 'Polygon', 'properties': {'id': 'int'}}) as dst: for counter, polygon in enumerate(polygons): # reproject if necessary new_polygon = transform.transform_geom(src_crs, dst_crs, mapping(polygon)) new_polygon = shape(new_polygon) prop = {'id': counter} dst.write({'geometry': mapping(new_polygon), 'properties': prop})
def read_records(self, file_name, range_tracker): from fiona import transform from fiona.io import ZipMemoryFile import json total_bytes = self.estimate_size() next_pos = range_tracker.start_position() def split_points_unclaimed(stop_pos): return 0 if stop_pos <= next_pos else iobase.RangeTracker.SPLIT_POINTS_UNKNOWN with self.open_file(file_name) as f, ZipMemoryFile(f.read()) as mem: collection = mem.open(self.gdb_name, layer=self.layer_name) src_crs = _GeoSourceUtils.validate_crs(collection.crs, self.in_epsg) num_features = len(collection) feature_bytes = math.floor(total_bytes / num_features) i = 0 # XXX workaround due to https://github.com/Toblerity/Fiona/issues/996 features = list(collection) logging.info( json.dumps({ 'msg': 'read_records', 'file_name': file_name, 'profile': collection.profile, 'num_features': num_features, 'total_bytes': total_bytes })) while range_tracker.try_claim(next_pos): i = math.ceil(next_pos / feature_bytes) if i >= num_features: break cur_feature = features[i] geom = cur_feature['geometry'] props = cur_feature['properties'] if not self.skip_reproject: geom = transform.transform_geom(src_crs, 'epsg:4326', geom) yield (props, geom) next_pos = next_pos + feature_bytes
def __antiMeridianCut(self, geom): """ Solving antimeridian problem. """ src_crs = '+proj=longlat +datum=WGS84 +no_defs' dst_crs = '+proj=longlat +datum=WGS84 +no_defs' am_offset = 360.0 line_t = transform_geom(src_crs, dst_crs, geom, antimeridian_cutting=self.__antimeridian, antimeridian_offset=am_offset, precision=-1) return line_t
def debug_dumpPoligons(polygons, inputfn, source_epsg): """Créé dans un nouveau dossier du même nom que le SHP d'entrée un nouveau SHP résultat de la découpe et autant de GeoJSON en EPSG:4326 que d'entitées créés """ schema = { 'geometry': 'Polygon', 'properties': { 'polygoneid': 'int', } } output_dir = './SPLITTED_' + os.path.splitext(inputfn)[0] if not os.path.exists(output_dir): os.mkdir(output_dir) print("Ecriture des fichiers dans", output_dir) i = 0 with fiona.collection(output_dir + '/SPLITTED_' + inputfn, 'w', 'ESRI Shapefile', schema) as output: for polygon in polygons: output.write({ 'properties': { 'polygoneid': i }, 'geometry': mapping(polygon) }) i += 1 i = 0 for polygon in polygons: geom_transform = transform_geom(source_epsg, "EPSG:4326", mapping(polygon)) with fiona.collection(output_dir + '/' + str(i) + '.geojson', 'w', 'GeoJSON', schema) as output: output.write({ 'properties': { 'polygoneid': i }, 'geometry': geom_transform }) i += 1 print(i, "fichiers GeoJSON créés")
def record_converter(record, desired_output, admin_level_info, country_info, available_fields, src_crs): id = convertGdamId( record['properties'][f"GID_{desired_output['levelCode']}"], desired_output['levelCode']) return { 'geometry': transform_geom(src_crs, "EPSG:3832", record['geometry']), 'properties': OrderedDict([ (f"{desired_output['shortName']}_NAME", record['properties'][f"NAME_{desired_output['levelCode']}"]), (f"{desired_output['shortName']}_CODE", f"{country_info['countryIsoAlpha3Code']}-{id}"), ('CNTRY_ISO3', country_info['countryIsoAlpha3Code']), ('LC_AD_TYPE', admin_level_info['localName']), ('SRC_DS', 'GADM') ]) }
def __antiMeridianCut(self, geom): """ Solving antimeridian problem. """ src_crs = '+proj=longlat +datum=WGS84 +no_defs' dst_crs = '+proj=longlat +datum=WGS84 +no_defs' am_offset = 360.0 line_t = transform_geom(src_crs, dst_crs, geom, antimeridian_cutting=self.__antimeridian, antimeridian_offset=am_offset, precision=-1) return line_t
def output_to_file(dst_file, geoms, src_crs, dst_crs): print('Writing point file...') with fiona.open(dst_file, 'w', crs=dst_crs, driver='ESRI Shapefile', schema={'geometry': 'Point', 'properties': {'POINT_NUM': 'int', 'X': 'float', 'Y': 'float'}}) as dst: for counter, point in enumerate(geoms): # reproject if necessary new_point = shape(transform.transform_geom(src_crs, dst_crs, mapping(point))) prop = {'POINT_NUM': counter + 1, 'X': new_point.x, 'Y': new_point.y} dst.write({'geometry': mapping(new_point), 'properties': prop})
def get_pasture_indx(self, raster_fn, pasture, ranch): if ranch is not None: ranch = ranch.replace(' ', '_') if pasture is not None: pasture = pasture.replace(' ', '_') ds = rasterio.open(raster_fn) ds_proj4 = ds.crs.to_proj4() target_ranch = ranch.replace(' ', '_').lower().strip() target_pasture = pasture.replace(' ', '_').lower().strip() loc_path = self.loc_path _d = self._d sf_fn = _join(loc_path, _d['sf_fn']) sf_feature_properties_key = _d['sf_feature_properties_key'] sf_fn = os.path.abspath(sf_fn) sf = fiona.open(sf_fn, 'r') features = [] for feature in sf: properties = feature['properties'] _key = properties[sf_feature_properties_key].replace(' ', '_') _pasture, _ranch = _key.split(self.key_delimiter) if self.reverse_key: _ranch, _pasture = _pasture, _ranch if _ranch.lower() == target_ranch and _pasture.lower( ) == target_pasture: _features = transform_geom(sf.crs_wkt, ds_proj4, feature['geometry']) features.append(_features) if len(features) == 0: raise KeyError((ranch, pasture)) pasture_mask, _, _ = raster_geometry_mask(ds, features) indx = np.where(pasture_mask == False) return indx
def extract_pixels_by_pasture(self, raster_fn, ranch=None): if ranch is not None: ranch = ranch.replace(' ', '_') ds = rasterio.open(raster_fn) ds_proj4 = ds.crs.to_proj4() loc_path = self.loc_path _d = self._d sf_fn = _join(loc_path, _d['sf_fn']) sf_feature_properties_key = _d['sf_feature_properties_key'] sf_fn = os.path.abspath(sf_fn) sf = fiona.open(sf_fn, 'r') data = ds.read(1, masked=True) # if 'biomass' in raster_fn: # data = np.ma.masked_values(data, 0) _data = {} for feature in sf: properties = feature['properties'] key = properties[sf_feature_properties_key].replace(' ', '_') _pasture, _ranch = key.split(self.key_delimiter) if self.reverse_key: _ranch, _pasture = _pasture, _ranch if ranch is not None: if _ranch.lower() != ranch.lower(): continue _features = transform_geom(sf.crs_wkt, ds_proj4, feature['geometry']) pasture_mask, _, _ = raster_geometry_mask(ds, [_features]) x = np.ma.MaskedArray(data, mask=pasture_mask) _data[(_ranch, _pasture)] = x.compressed().tolist(), int(x.count()) return _data
def transform_vector(shapefile, crs): """Reads vector AOI bounds and reprojects to desired crs. Returns bounds as shapely polygon. Parameters ---------- shapefile : Path Path to vector AOI. crs : rasterio CRS string CRS to transform to. Returns ------- shp : shapely object Infile AOI bounds as a shapely polygon object. Raises ------- ValueError If the input shapefile contains more than one geometry. ValueError If the input file is not of an accepted file format. """ vector_exts = ['.shp', '.geojson', '.json'] ext = pathlib.Path(shapefile).suffix if ext not in vector_exts: raise ValueError(f'File not accepted. Acceptable vector formats are {vector_exts}') with fiona.open(shapefile) as c: if len(c) > 1: raise ValueError('File contains multiple features. ' 'Only single feature files may be used as input.') transformed = transform_geom(c.crs.get("init"), str(crs), c[0]['geometry']) shp = shape(transformed) geom = [mapping(shp)] return geom
def main(infile, outfile): """ Reproject a layer to web mercator. """ # Cache stuff we need later bbox = Polygon([[-180, -85], [-180, 85], [180, 85], [180, -85]]) with fio.open(infile) as src: meta = src.meta.copy() meta['crs'] = 'EPSG:3857' meta['driver'] = 'GeoJSON' with fio.open(infile) as src, fio.open(outfile, 'w', **meta) as dst, click.progressbar(src) as features: for feat in features: geom = shape(feat['geometry'].copy()).intersection(bbox) feat['geometry'] = transform_geom(src.crs, dst.crs, mapping(geom)) dst.write(feat)
def cat(ctx, files, precision, indent, compact, ignore_errors, dst_crs, use_rs, bbox): """Concatenate and print the features of input datasets as a sequence of GeoJSON features.""" verbosity = (ctx.obj and ctx.obj['verbosity']) or 2 logger = logging.getLogger('fio') dump_kwds = {'sort_keys': True} if indent: dump_kwds['indent'] = indent if compact: dump_kwds['separators'] = (',', ':') item_sep = compact and ',' or ', ' try: with fiona.drivers(CPL_DEBUG=verbosity > 2): for path in files: with fiona.open(path) as src: if bbox: try: bbox = tuple(map(float, bbox.split(','))) except ValueError: bbox = json.loads(bbox) for i, feat in src.items(bbox=bbox): if dst_crs or precision > 0: g = transform_geom( src.crs, dst_crs, feat['geometry'], antimeridian_cutting=True, precision=precision) feat['geometry'] = g feat['bbox'] = fiona.bounds(g) if use_rs: click.echo(u'\u001e', nl=False) click.echo(json.dumps(feat, **dump_kwds)) except Exception: logger.exception("Exception caught during processing") raise click.Abort()
def _processor(args): """ Reproject a single feature's geometry. Parameters ---------- args : dict feat : dict A GeoJSON feature to process. src_crs : str The geometry's CRS. dst_crs : str Reproject buffered geometry to this CRS before returning. skip_failures : bool If True then Exceptions don't stop processing. Returns ------- dict GeoJSON feature with updated geometry. """ feat = args['feat'] src_crs = args['src_crs'] dst_crs = args['dst_crs'] skip_failures = args['skip_failures'] try: feat['geometry'] = transform_geom(src_crs, dst_crs, feat['geometry']) return feat except Exception: log.exception("Feature with ID %s failed" % feat.get('id')) if not skip_failures: raise
def test_transform_geom_with_z(geom): """Transforming a geom with Z succeeds""" g2 = transform.transform_geom("epsg:4326", "epsg:3857", geom, precision=3)
def rasterise_vector(vector_filename, shape=None, transform=None, crs=None, raster_filename=None, dtype='uint32'): """ Given a full file pathname to a vector file, rasterise the vectors geometry by either provding the raster shape and transform, or a raster file on disk. :param vector_filename: A full file pathname to an OGR compliant vector file. :param shape: Optional. If provided, then shape is a tuple containing the (height, width) of an array. If omitted, then `shape` will be retrieved via the raster provided in `raster_filename`. :param transform: Optional. If provided, then an `Affine` containing the transformation matrix parameters. If omitted, then `transform` will be retrieved via the `raster_filename`. :param crs: Optional. If provided, then WKT should be provided. If omitted, then `crs` will be retreived via the `raster_filename`. :param raster_filename: A full file pathname to a GDAL compliant raster file. :return: A `NumPy` array of the same dimensions provided by either the `shape` or `raster_filename`. :notes: The vector file will be re-projected as required in order to match the same crs as provided by `crs` or by `raster_filename`. """ with fiona.open(vector_filename, 'r') as v_src: if raster_filename is not None: with rasterio.open(raster_filename, 'r') as r_src: transform = r_src.affine shape = r_src.shape crs = r_src.crs r_bounds = tuple(r_src.bounds) else: ul = (0, 0) * transform lr = (shape[1], shape[0]) * transform min_x, max_x = min(ul[0], lr[0]), max(ul[0], lr[0]) min_y, max_y = min(ul[1], lr[1]), max(ul[1], lr[1]) r_bounds = (min_x, min_y, max_x, max_y) # convert the crs_wkt to a dict styled proj4 sr = osr.SpatialReference() sr.ImportFromWkt(crs) if CRS_CLASS: crs = CRS.from_string(sr.ExportToProj4()) else: crs = from_string(sr.ExportToProj4()) # rasterio has a CRS class that makes for easier crs comparison if CRS_CLASS: v_crs = CRS(v_src.crs) same_crs = v_crs == crs # fiona may update to the class crs in future, but for now... crs = crs.to_dict() else: same_crs = is_same_crs(v_src.crs, crs) # use rtree where possible for speedy shape reduction if RTREE: shapes = {} index = rtree.index.Index() # get crs, check if the same as the image and project as needed if not same_crs: for feat in v_src: new_geom = transform_geom(v_src.crs, crs, feat['geometry']) fid = int(feat['id']) shapes[fid] = (new_geom, fid + 1) index.insert(fid, shp(new_geom).bounds) else: for feat in v_src: fid = int(feat['id']) shapes[fid] = (feat['geometry'], fid + 1) index.insert(fid, shp(feat['geometry']).bounds) # we check the bounding box of each geometry object against # the image bounding box. Basic pre-selection to filter which # vectors are to be rasterised # bounding box intersection fids = index.intersection(r_bounds) selected_shapes = [shapes[fid] for fid in fids] else: selected_shapes = [] r_poly = box(*r_bounds) if not same_crs: for feat in v_src: new_geom = transform_geom(v_src.crs, crs, feat['geometry']) fid = int(feat['id']) geom = shp(new_geom) if geom.intersects(r_poly): selected_shapes.append((new_geom, fid + 1)) else: for feat in v_src: fid = int(feat['id']) geom = shp(feat['geometry']) if geom.intersects(r_poly): selected_shapes.append((feat['geometry'], fid + 1)) rasterised = rasterize(selected_shapes, out_shape=shape, fill=0, transform=transform, dtype=dtype) return rasterised
from shapely import speedups speedups.enable() with fiona.open("data/chko.shp", "r") as pas: with fiona.open("data/highways.geojson", "r") as hws: buffered_highways = [] wgs84 = "EPSG:4326" jtsk = pas.crs d8 = list(filter(lambda hw: hw["properties"]["ref"] == "D8", hws)) for hw in d8: geom_transformed = transform_geom(wgs84, jtsk, hw["geometry"]) g = shape(geom_transformed) buffered_highways.append(g.buffer(100)) intersections = [] for hw in buffered_highways: for pa in pas: pa_geom = shape(pa["geometry"]) if hw.intersects(pa_geom): out_geom = hw.intersection(pa_geom) intersections.append(out_geom)
def _processor(args): """ Process a single feature. Parameters ---------- args : dict feat : dict A GeoJSON feature to process. src_crs : str or dict The geometry's CRS. buf_crs : str or dict Apply buffer after reprojecting to this CRS. dst_crs : str or dict Reproject buffered geometry to this CRS before returning. skip_failures : bool If True then Exceptions don't stop processing. buf_args : dict Keyword arguments for the buffer operation. Returns ------- dict GeoJSON feature with updated geometry. """ feat = args['feat'] src_crs = args['src_crs'] buf_crs = args['buf_crs'] dst_crs = args['dst_crs'] skip_failures = args['skip_failures'] buf_args = args['buf_args'] # Support buffering by a field's value if not isinstance(buf_args['distance'], (float, int)): field_val = feat['properties'][buf_args['distance']] # Buffering according to a field but field is None so just return the feature if field_val is None: return feat else: buf_args['distance'] = field_val try: # src_crs -> buf_crs reprojected = transform_geom( src_crs, buf_crs, feat['geometry'], antimeridian_cutting=True ) # buffering operation buffered = shape(reprojected).buffer(**buf_args) # buf_crs -> dst_crs feat['geometry'] = transform_geom( buf_crs, dst_crs, mapping(buffered), antimeridian_cutting=True ) return feat except Exception: logger.exception("Feature with ID %s failed during buffering", feat.get('id')) if not skip_failures: raise