def _get_boundaries(self, src): self.output("Getting boundaries", normal=True, arrow=True) output = {'ul': {'x': [0, 0], 'y': [0, 0]}, # ul: upper left 'ur': {'x': [0, 0], 'y': [0, 0]}, # ur: upper right 'll': {'x': [0, 0], 'y': [0, 0]}, # ll: lower left 'lr': {'x': [0, 0], 'y': [0, 0]}} # lr: lower right output['ul']['x'][0] = src['affine'][2] output['ul']['y'][0] = src['affine'][5] output['ur']['x'][0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['ur']['y'][0] = output['ul']['y'][0] output['ll']['x'][0] = output['ul']['x'][0] output['ll']['y'][0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['lr']['x'][0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['lr']['y'][0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['ul']['x'][1], output['ul']['y'][1] = transform(src['crs'], self.projection, [output['ul']['x'][0]], [output['ul']['y'][0]]) output['ur']['x'][1], output['ur']['y'][1] = transform(src['crs'], self.projection, [output['ur']['x'][0]], [output['ur']['y'][0]]) output['ll']['x'][1], output['ll']['y'][1] = transform(src['crs'], self.projection, [output['ll']['x'][0]], [output['ll']['y'][0]]) output['lr']['x'][1], output['lr']['y'][1] = transform(src['crs'], self.projection, [output['lr']['x'][0]], [output['lr']['y'][0]]) return output
def point(scene, coord): scene_params = utils.landsat_parse_scene_id(scene) meta_data = utils.landsat_get_mtl(scene) landsat_address = f's3://landsat-pds/{scene_params["key"]}' band_address = f'{landsat_address}_B4.TIF' with rio.open(band_address) as band: lon_srs, lat_srs = warp.transform('EPSG:4326', band.crs, [coord[0]], [coord[1]]) b4 = list(band.sample([(lon_srs[0], lat_srs[0])]))[0] b4 = float(utils.landsat_to_toa(b4, 4, meta_data)[0]) band_address = f'{landsat_address}_B5.TIF' with rio.open(band_address) as band: lon_srs, lat_srs = warp.transform('EPSG:4326', band.crs, [coord[0]], [coord[1]]) b5 = list(band.sample([(lon_srs[0], lat_srs[0])]))[0] b5 = float(utils.landsat_to_toa(b5, 5, meta_data)[0]) if b4 * b5 > 0: ratio = (b5 - b4) / (b5 + b4) else: ratio = 0 out = { 'ndvi': ratio, 'date': scene_params['date'], 'cloud': float(utils.landsat_mtl_extract(meta_data, 'CLOUD_COVER')) } return out
def _get_boundaries(self, src, shape): self.output("Getting boundaries", normal=True, arrow=True) output = { 'ul': { 'x': [0, 0], 'y': [0, 0] }, # ul: upper left 'ur': { 'x': [0, 0], 'y': [0, 0] }, # ur: upper right 'll': { 'x': [0, 0], 'y': [0, 0] }, # ll: lower left 'lr': { 'x': [0, 0], 'y': [0, 0] } } # lr: lower right output['ul']['x'][0] = src['affine'][2] output['ul']['y'][0] = src['affine'][5] output['ur']['x'][ 0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['ur']['y'][0] = output['ul']['y'][0] output['ll']['x'][0] = output['ul']['x'][0] output['ll']['y'][ 0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['lr']['x'][ 0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['lr']['y'][ 0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['ul']['x'][1], output['ul']['y'][1] = transform( src['crs'], self.projection, [output['ul']['x'][0]], [output['ul']['y'][0]]) output['ur']['x'][1], output['ur']['y'][1] = transform( src['crs'], self.projection, [output['ur']['x'][0]], [output['ur']['y'][0]]) output['ll']['x'][1], output['ll']['y'][1] = transform( src['crs'], self.projection, [output['ll']['x'][0]], [output['ll']['y'][0]]) output['lr']['x'][1], output['lr']['y'][1] = transform( src['crs'], self.projection, [output['lr']['x'][0]], [output['lr']['y'][0]]) dst_corner_ys = [output[k]['y'][1][0] for k in output.keys()] dst_corner_xs = [output[k]['x'][1][0] for k in output.keys()] y_pixel = abs(max(dst_corner_ys) - min(dst_corner_ys)) / shape[0] x_pixel = abs(max(dst_corner_xs) - min(dst_corner_xs)) / shape[1] return (min(dst_corner_xs), x_pixel, 0.0, max(dst_corner_ys), 0.0, -y_pixel)
def get_tile_tif(tile, imagery, folder, kwargs): """ Read a GeoTIFF with a window corresponding to a TMS tile The TMS tile bounds are converted to the GeoTIFF source CRS. That bounding box is converted to a pixel window which is read from the GeoTIFF. For remote files which are internally tiled, this will take advantage of HTTP GET Range Requests to avoid downloading the entire file. See more info at: http://www.cogeo.org/in-depth.html """ bound = bounds(*[int(t) for t in tile.split('-')]) imagery_offset = kwargs.get('imagery_offset') or [0, 0] with rasterio.open(imagery) as src: x_res, y_res = src.transform[0], src.transform[4] # offset our imagery in the "destination pixel" space offset_bound = dict() deg_per_pix_x = (bound.west - bound.east) / 256 deg_per_pix_y = (bound.north - bound.south) / 256 offset_bound['west'] = bound.west + imagery_offset[0] * deg_per_pix_x offset_bound['east'] = bound.east + imagery_offset[0] * deg_per_pix_x offset_bound['north'] = bound.north + imagery_offset[1] * deg_per_pix_y offset_bound['south'] = bound.south + imagery_offset[1] * deg_per_pix_y # project tile boundaries from lat/lng to source CRS x, y = transform(WGS84_CRS, src.crs, [offset_bound['west']], [offset_bound['north']]) tile_ul_proj = x[0], y[0] x, y = transform(WGS84_CRS, src.crs, [offset_bound['east']], [offset_bound['south']]) tile_lr_proj = x[0], y[0] # get origin point from the TIF tif_ul_proj = (src.bounds.left, src.bounds.top) # use the above information to calculate the pixel indices of the window top = int((tile_ul_proj[1] - tif_ul_proj[1]) / y_res) left = int((tile_ul_proj[0] - tif_ul_proj[0]) / x_res) bottom = int((tile_lr_proj[1] - tif_ul_proj[1]) / y_res) right = int((tile_lr_proj[0] - tif_ul_proj[0]) / x_res) window = ((top, bottom), (left, right)) # read the first three bands (assumed RGB) of the TIF into an array data = np.empty(shape=(3, 256, 256)).astype(src.profile['dtype']) for k in (1, 2, 3): src.read(k, window=window, out=data[k - 1], boundless=True) # save tile_img = op.join(folder, '{}{}'.format(tile, '.jpg')) img = Image.fromarray(np.moveaxis(data, 0, -1), mode='RGB') img.save(tile_img) return tile_img
def test_transform(): """2D and 3D.""" WGS84_crs = CRS.from_epsg(4326) WGS84_points = ([12.492269], [41.890169], [48.]) ECEF_crs = CRS.from_epsg(4978) ECEF_points = ([4642610.], [1028584.], [4236562.]) ECEF_result = transform(WGS84_crs, ECEF_crs, *WGS84_points) assert np.allclose(np.array(ECEF_result), np.array(ECEF_points)) UTM33_crs = CRS.from_epsg(32633) UTM33_points = ([291952], [4640623]) UTM33_result = transform(WGS84_crs, UTM33_crs, *WGS84_points[:2]) assert np.allclose(np.array(UTM33_result), np.array(UTM33_points))
def test_transform(): """2D and 3D""" WGS84_crs = {'init': 'EPSG:4326'} WGS84_points = ([12.492269], [41.890169], [48.]) ECEF_crs = {'init': 'EPSG:4978'} ECEF_points = ([4642610.], [1028584.], [4236562.]) ECEF_result = transform(WGS84_crs, ECEF_crs, *WGS84_points) assert numpy.allclose(numpy.array(ECEF_result), numpy.array(ECEF_points)) UTM33_crs = {'init': 'EPSG:32633'} UTM33_points = ([291952], [4640623]) UTM33_result = transform(WGS84_crs, UTM33_crs, *WGS84_points[:2]) assert numpy.allclose(numpy.array(UTM33_result), numpy.array(UTM33_points))
def test_transform(): """2D and 3D.""" WGS84_crs = {"init": "epsg:4326"} WGS84_points = ([12.492269], [41.890169], [48.]) ECEF_crs = {"init": "epsg:4978"} ECEF_points = ([4642610.], [1028584.], [4236562.]) ECEF_result = transform(WGS84_crs, ECEF_crs, *WGS84_points) assert np.allclose(np.array(ECEF_result), np.array(ECEF_points)) UTM33_crs = {"init": "epsg:32633"} UTM33_points = ([291952], [4640623]) UTM33_result = transform(WGS84_crs, UTM33_crs, *WGS84_points[:2]) assert np.allclose(np.array(UTM33_result), np.array(UTM33_points))
def test_transform(): """2D and 3D.""" WGS84_crs = {'init': 'EPSG:4326'} WGS84_points = ([12.492269], [41.890169], [48.]) ECEF_crs = {'init': 'EPSG:4978'} ECEF_points = ([4642610.], [1028584.], [4236562.]) ECEF_result = transform(WGS84_crs, ECEF_crs, *WGS84_points) assert np.allclose(np.array(ECEF_result), np.array(ECEF_points)) UTM33_crs = {'init': 'EPSG:32633'} UTM33_points = ([291952], [4640623]) UTM33_result = transform(WGS84_crs, UTM33_crs, *WGS84_points[:2]) assert np.allclose(np.array(UTM33_result), np.array(UTM33_points))
def point( self, coordinates: Tuple[float, float], coord_crs: CRS = WGS84_CRS, **kwargs: Any, ) -> List: """ Read point value from a COG. Attributes ---------- coordinates : tuple (X, Y) coordinates. coord_crs : rasterio.crs.CRS, optional (X, Y) coordinate system. Default is WGS84/EPSG:4326. kwargs : Any, optional Additional options to forward to src_dst.sample() Returns ------- list : List """ lon, lat = transform(coord_crs, self.src_dst.crs, [coordinates[0]], [coordinates[1]]) return list(self.src_dst.sample([(lon[0], lat[0])], **kwargs))[0].tolist()
def get_bounding_box_from_draw(raster, dc): """ returns bounding box in a image :param raster: displayed raster (rasterio image) :param dc: drawcontrol ipyleaflet """ try: # Get last draw from the map coordinates = np.array(dc.last_draw['geometry']['coordinates'][0]) # lon, lat to Sentinel-2 coordinate reference system lon_min_max = [np.amin(coordinates[:, 0]), np.amax(coordinates[:, 0])] lat_min_max = [np.amin(coordinates[:, 1]), np.amax(coordinates[:, 1])] lons, lats = np.meshgrid(lon_min_max, lat_min_max) xs, ys = transform({'init': 'EPSG:4326'}, raster.crs, lons.flatten(), lats.flatten()) # Get the region of interest in the image rows, cols = [], [] for x, y in zip(xs, ys): row, col = ~raster.affine * (x, y) rows.append(row) cols.append(col) row_min, row_max = np.amin(rows), np.amax(rows) col_min, col_max = np.amin(cols), np.amax(cols) startx, starty = map(lambda v: int(np.floor(v)), [col_min, row_min]) endx, endy = map(lambda v: int(np.ceil(v)), [col_max, row_max]) print("Bounding box computed") except: print("Draw a polygon in the displayed map") startx, starty, endx, endy = None, None, None, None return startx, starty, endx, endy
def point_handler( url: str, lon: float, lat: float, indexes: Union[str, Tuple[int]] = None, ) -> Tuple[str, str, str]: """Handle /point requests.""" if isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) if isinstance(lon, str): lon = float(lon) if isinstance(lat, str): lat = float(lat) with rasterio.Env(aws_session): with rasterio.open(url) as src_dst: lon_srs, lat_srs = warp.transform("epsg:4326", src_dst.crs, [lon], [lat]) if not ((src_dst.bounds[0] < lon_srs[0] < src_dst.bounds[2]) and (src_dst.bounds[1] < lat_srs[0] < src_dst.bounds[3])): raise TilerError("Point is outside the raster bounds") indexes = indexes if indexes is not None else src_dst.indexes values = list( src_dst.sample([(lon_srs[0], lat_srs[0])], indexes=indexes))[0].tolist() meta = {"coordinates": [lon, lat], "values": values} return ("OK", "application/json", json.dumps(meta))
def load_dem(self, dem_file): # install using `conda install -c conda-forge rastrio` wgs84 = CRS.from_epsg(4326) with rs.open(dem_file) as ds: top, left, bottom, right = self.bounds top, bottom = int(ds.height * top), int(ds.height * bottom) left, right = int(ds.width * left), int(ds.width * right) xx, yy = np.meshgrid(np.arange(left, right), np.arange(top, bottom)) alt = ds.read(1)[top:bottom, left:right] lon, lat = ds.xy(yy, xx) lon, lat, alt = transform( ds.crs, wgs84, *map(lambda x: np.array(x).flatten(), (lon, lat, alt))) # from https://proj.org/operations/conversions/topocentric.html # should work since proj version 8, gives errors here though # # lc = CRS.from_proj4(('cct -d 3 +proj=pipeline +step +proj=cart +ellps=WGS84 ' # + '+step +proj=topocentric +ellps=WGS84 +lat_0=%f +lon_0=%f +h_0=%f') % self.coord0) # now going first to wgs84 and then using pygeodesy, which is very slow but luckily need to do only once lc = LocalCartesian(*self.coord0) dem = np.zeros((len(lon), 3), dtype=np.float32) for i, (lat_, lon_, alt_) in enumerate(zip(lat, lon, alt)): xyz = lc.forward(lat_, lon_, alt_) dem[i, :] = xyz.xyz # transform to correct coordinate frame (now x east, y north, z up => x east, y south, z down) dem = tools.q_times_mx(self.w2c.quat.conj(), dem) dem = dem.reshape((bottom - top, right - left, 3)) return dem
def createGeoJSONtoExport(self): if self.gj_loaded != "": return nodes_gj = [] # supported format to create geojson (epsg:4326) for point in self.roi_coords: xy = self.raster.xy( self.roi_coords[self.roi_coords.index(point)].y() - 0.5, self.roi_coords[self.roi_coords.index(point)].x() - 0.5) nodes_gj.append(xy[0]) nodes_gj.append(xy[1]) nodes_gj.append(nodes_gj[0]) nodes_gj.append(nodes_gj[1]) ys, xs = warp.transform(self.raster.crs, {'init': 'epsg:4326'}, nodes_gj[::2], nodes_gj[1::2]) nodes_proj = [] for i in range(0, len(ys)): nodes_proj.append([ys[i], xs[i]]) geojson = """{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {}, "geometry": {"type": "Polygon", "coordinates": [""" + str( nodes_proj) + """]}}]}""" if self.createGeoJSONDir(): with open( Control.dirname + "/Output_data/GeoJSON/" + str(self.roi_id) + ".geojson", "w") as gj: gj.write(geojson) gj.close()
def read_raster(filename, out_crs="EPSG:3857", use_z=False): """Read a raster to a ``pyvista.StructuredGrid``. This will handle coordinate transformations. """ # Read in the data data = xr.open_rasterio(filename) values = np.asarray(data) nans = values == data.nodatavals if np.any(nans): # values = np.ma.masked_where(nans, values) values[nans] = np.nan # Make a mesh xx, yy = np.meshgrid(data["x"], data["y"]) if use_z and values.shape[0] == 1: # will make z-comp the values in the file zz = values.reshape(xx.shape) else: # or this will make it flat zz = np.zeros_like(xx) mesh = pv.StructuredGrid(xx, yy, zz) pts = mesh.points lon, lat = transform(data.crs, out_crs, pts[:, 0], pts[:, 1]) mesh.points[:, 0] = lon mesh.points[:, 1] = lat mesh["data"] = values.reshape(mesh.n_points, -1, order="F") return mesh
def _get_point(self, src_path: str, coordinates: Tuple[float, float]) -> dict: with rasterio.open(src_path) as src_dst: lon_srs, lat_srs = transform( "EPSG:4326", src_dst.crs, [coordinates[0]], [coordinates[1]] ) return list( src_dst.sample([(lon_srs[0], lat_srs[0])], indexes=src_dst.indexes) )[0].tolist()
def worker(address): """Worker.""" with rasterio.open(address) as band: lon_srs, lat_srs = warp.transform("EPSG:4326", band.crs, [coordinates[0]], [coordinates[1]]) point = list(band.sample([(lon_srs[0], lat_srs[0])]))[0] return point[0]
def point_value(url, coordinates, indexes=None): """ Handle /point requests. Note: All the querystring parameters are translated to function keywords and passed as string value by lambda_proxy Attributes ---------- url : str, required Dataset url to read from. coordinates : str, required Comma separated longitude,latitude values. indexes : str, optional, (defaults: None) Comma separated band index number (e.g "1,2,3"). Returns ------- status : str Status of the request (e.g. OK, NOK). MIME type : str response body MIME type (e.g. image/jpeg). body : bytes String encoded json point value """ if indexes is not None and isinstance(indexes, str): indexes = tuple(int(s) for s in re.findall(r"\d+", indexes)) coordinates = list(map(float, coordinates.split(","))) with rasterio.open(url) as src_dst: indexes = indexes if indexes is not None else src_dst.indexes lon_srs, lat_srs = warp.transform("EPSG:4326", src_dst.crs, [coordinates[0]], [coordinates[1]]) results = list( src_dst.sample([(lon_srs[0], lat_srs[0])], indexes=indexes))[0] def _get_name(idx): name = src_dst.descriptions[idx - 1] if not name: name = f"band{idx}" return name band_descriptions = [(ix, _get_name(ix)) for ix in indexes] return ( "OK", "application/json", json.dumps({ "address": url, "coordinates": coordinates, "band_descriptions": band_descriptions, "values": {b[0]: r for b, r in zip(band_descriptions, results.tolist())} }), )
def point( src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT], coordinates: Tuple[float, float], indexes: Optional[Union[Sequence[int], int]] = None, coord_crs: CRS = constants.WGS84_CRS, unscale: bool = False, ) -> List: """ Read point value Attributes ---------- src_dst : rasterio.io.DatasetReader rasterio.io.DatasetReader object coordinates : tuple (X, Y) coordinates. indexes : list of ints or a single int, optional Band indexes coord_crs : rasterio.crs.CRS, optional (X, Y) coordinate system. Default is WGS84/EPSG:4326. unscale, bool, optional If True, apply scale and offset to the data. Default is set to False. kwargs : Any, optional Additional options to forward to src_dst.sample() Returns ------- point : list List of pixel values per bands indexes. """ if isinstance(indexes, int): indexes = (indexes, ) lon, lat = transform(coord_crs, src_dst.crs, [coordinates[0]], [coordinates[1]]) if not ((src_dst.bounds[0] < lon[0] < src_dst.bounds[2]) and (src_dst.bounds[1] < lat[0] < src_dst.bounds[3])): raise Exception("Point is outside dataset bounds") indexes = indexes if indexes is not None else src_dst.indexes point_values = list(src_dst.sample([(lon[0], lat[0])], indexes=indexes))[0] if unscale: point_values = point_values.astype("float32", casting="unsafe") numpy.multiply(point_values, src_dst.scales[0], out=point_values, casting="unsafe") numpy.add(point_values, src_dst.offsets[0], out=point_values, casting="unsafe") return point_values.tolist()
def convertPointCrs(lon, lat, orgCrs=CRS.from_epsg(4326), destCrs=CRS.from_epsg(4326)): """Do the crs convertion of a point""" lonConv, latConv = transform(orgCrs, destCrs, [lon], [lat]) lonConv = lonConv[0] latConv = latConv[0] return lonConv, latConv
def geojson_reproject(feature, srid_in, srid_out): """Reproject GeoJSON Polygon feature coords Inspired by: https://gist.github.com/dnomadb/5cbc116aacc352c7126e779c29ab7abe """ if feature["geometry"]["type"] == "Polygon": xys = (zip(*ring) for ring in feature["geometry"]["coordinates"]) xys = (list(zip(*transform(CRS.from_epsg(srid_in), CRS.from_epsg(srid_out), *xy))) for xy in xys) yield {"coordinates": list(xys), "type": "Polygon"}
def get_elevation(lat_lon, image): results = [] lat = lat_lon[0] lon = lat_lon[1] new_coo = transform(WGS84, epsg_27700, xs=[lon], ys=[lat]) vals = image.sample(((new_coo[0][0], new_coo[1][0]), )) for val in vals: results.append({"lat": lat, "lon": lon, "elevation": val[0]}) return results
def geojson_to_mercator(feature): """Convert GeoJSON Polygon feature coords to Mercator (i.e EPSG:3857). Inspired by: https://gist.github.com/dnomadb/5cbc116aacc352c7126e779c29ab7abe """ # FIXME: We assume that GeoJSON input coordinates can't be anything else than EPSG:4326 if feature["geometry"]["type"] == "Polygon": xys = (zip(*ring) for ring in feature["geometry"]["coordinates"]) xys = (list(zip(*transform(CRS.from_epsg(4326), CRS.from_epsg(3857), *xy))) for xy in xys) yield {"coordinates": list(xys), "type": "Polygon"}
def transform_bounds(boundsArr, crs): if not crs: raise ValueError('Input raster must have a CRS') if 'init' in crs and crs['init'] == 'epsg:4326': boundsArr[:, 0] = _wrap_x_coord(boundsArr[:, 0]) else: boundsArr = np.dstack( warp.transform(crs, {'init': 'epsg:4326'}, boundsArr[:, 0], boundsArr[:, 1]))[0] return boundsArr
def dest_affine(affine, src_crs, shape): from rasterio.warp import transform corners = [(0, 0), (0, shape[0]), (shape[1], shape[0]), (shape[1], 0)] corners = [affine * p for p in corners] crn = transform(src_crs, dst_crs, [p[0] for p in corners], [p[1] for p in corners]) y_pixel = abs(max(crn[1]) - min(crn[1])) / shape[0] x_pixel = abs(max(crn[0]) - min(crn[0])) / shape[1] dst_transform = (x_pixel, 0.0, min(crn[0]), 0.0, -y_pixel, max(crn[1])) return dst_transform
def get_tiles(zoom, input, dst_crs="EPSG:3857"): print("getting tiles for", input) input = input.replace("s3://", "/vsicurl/http://s3.amazonaws.com/") with rasterio.drivers(): with rasterio.open(input) as src: # Compute the geographic bounding box of the dataset. (west, east), (south, north) = transform( src.crs, "EPSG:4326", src.bounds[::2], src.bounds[1::2]) # Initialize an iterator over output tiles. return mercantile.tiles( west, south, east, north, range(zoom, zoom + 1))
def geojson_to_mercator(feature, epsg=4326): """Convert GeoJSON Polygon feature coords to Mercator (i.e EPSG:3857). Inspired by: https://gist.github.com/dnomadb/5cbc116aacc352c7126e779c29ab7abe """ if feature["geometry"]["type"] == "Polygon": xys = (zip(*ring) for ring in feature["geometry"]["coordinates"]) xys = (list( zip(*transform(CRS.from_epsg(int(epsg)), CRS.from_epsg(3857), * xy))) for xy in xys) yield {"coordinates": list(xys), "type": "Polygon"}
def convert_cols_rows(fn, cols, rows, dst_crs=rasterio.crs.CRS.from_epsg(4326)): with rasterio.open(fn) as ds: xs, ys = rasterio.transform.xy(ds.transform, rows, cols) xs, ys = np.array(xs), np.array(ys) lons, lats = warp.transform(ds.crs, dst_crs, xs.flatten(), ys.flatten()) lons, lats = ( np.array(lons).reshape(xs.shape), np.array(lats).reshape(ys.shape), ) return xs, ys, lons, lats
def read_SWISS_SM( basepath='/mnt/CEPH_PROJECTS/ADO/SWI/reference_data/swiss_model/', format='tif'): # multidataset method # get file paths sm_files = list() date_list = list() if format == 'tif': for path in Path(basepath).rglob('*.tif'): sm_files.append(path) date_list.append(dt.datetime.strptime(path.name[4:12], '%Y%m%d')) elif format == 'asc': for path in Path(basepath).rglob('*.asc'): sm_files.append(path) date_list.append(dt.datetime.strptime(path.name[4:12], '%Y%m%d')) # initiate sm stack tmp = xr.open_rasterio(basepath + 'fcp_20190101.tif') #del tmp['band'] # coordinate transformation tmp.attrs['crs'] = 'EPSG:21781' ny, nx = len(tmp['y']), len(tmp['x']) x, y = np.meshgrid(tmp['x'], tmp['y']) lon, lat = transform(tmp.crs, {'init': 'EPSG:4326'}, x.flatten(), y.flatten()) lon = np.asarray(lon).reshape((ny, nx)) lat = np.asarray(lat).reshape((ny, nx)) tmp.coords['lon'] = (('y', 'x'), lon) tmp.coords['lat'] = (('y', 'x'), lat) stack = xr.DataArray(np.full((365, len(lat[:, 0]), len(lon[0, :])), -9999.0), coords=[('time', date_list), ('lat', lat[:, 0]), ('lon', lon[0, :])], name='SM_2019') tmp.close() tmp = None # read all sm files sm_data_list = list() for (path, date) in zip(sm_files, date_list): if format == 'tif': tmp = xr.open_rasterio(path) stack.loc[dict(time=date)] = tmp.values.squeeze() tmp.close() elif format == 'asc': tmp = pd.read_csv(path, sep=" ", header=None, skiprows=6) stack.loc[dict(time=date)] = tmp.values return stack
def get_point_data(lat, lng, path): with rasterio.open(path) as src_dst: # Concert to source CRS. x, y = warp.transform("epsg:4326", src_dst.crs, [lng], [lat]) # Check bounds. x_inside = min(src_dst.bounds.left, src_dst.bounds.right) < x[0] < \ max(src_dst.bounds.left, src_dst.bounds.right) y_inside = min(src_dst.bounds.bottom, src_dst.bounds.top) < y[0] < \ max(src_dst.bounds.bottom, src_dst.bounds.top) if not (x_inside and y_inside): raise InvalidArgumentsError('requested lat lon outside bounds') # Sample value. return list(src_dst.sample([(x[0], y[0])]))[0].tolist()[0]
def lnglat(self, x: float, y: float, truncate=False) -> Coords: """Transform point(x,y) to longitude and latitude.""" inside = point_in_bbox(Coords(x, y), self.xy_bbox) if not inside: warnings.warn("Point is outside TMS bounds.", UserWarning) xs, ys = transform(self.crs, WGS84_CRS, [x], [y]) lng, lat = xs[0], ys[0] if truncate: lng, lat = truncate_lnglat(lng, lat) return Coords(lng, lat)
def add_lat_lon(da_in): ny, nx = len(da_in['y']), len(da_in['x']) x, y = np.meshgrid(da_in['x'], da_in['y']) # Rasterio works with 1D arrays lon, lat = transform(da_in.crs, {'init': 'EPSG:4326'}, x.flatten(), y.flatten()) lon = np.asarray(lon).reshape((ny, nx)) lat = np.asarray(lat).reshape((ny, nx)) da_in.coords['lon'] = (('y', 'x'), lon) da_in.coords['lat'] = (('y', 'x'), lat) return da_in
def _get_boundaries(self, src, shape): self.output("Getting boundaries", normal=True, arrow=True) output = {'ul': {'x': [0, 0], 'y': [0, 0]}, # ul: upper left 'ur': {'x': [0, 0], 'y': [0, 0]}, # ur: upper right 'll': {'x': [0, 0], 'y': [0, 0]}, # ll: lower left 'lr': {'x': [0, 0], 'y': [0, 0]}} # lr: lower right output['ul']['x'][0] = src['affine'][2] output['ul']['y'][0] = src['affine'][5] output['ur']['x'][0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['ur']['y'][0] = output['ul']['y'][0] output['ll']['x'][0] = output['ul']['x'][0] output['ll']['y'][0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['lr']['x'][0] = output['ul']['x'][0] + self.pixel * src['shape'][1] output['lr']['y'][0] = output['ul']['y'][0] - self.pixel * src['shape'][0] output['ul']['x'][1], output['ul']['y'][1] = transform(src['crs'], self.projection, [output['ul']['x'][0]], [output['ul']['y'][0]]) output['ur']['x'][1], output['ur']['y'][1] = transform(src['crs'], self.projection, [output['ur']['x'][0]], [output['ur']['y'][0]]) output['ll']['x'][1], output['ll']['y'][1] = transform(src['crs'], self.projection, [output['ll']['x'][0]], [output['ll']['y'][0]]) output['lr']['x'][1], output['lr']['y'][1] = transform(src['crs'], self.projection, [output['lr']['x'][0]], [output['lr']['y'][0]]) dst_corner_ys = [output[k]['y'][1][0] for k in output.keys()] dst_corner_xs = [output[k]['x'][1][0] for k in output.keys()] y_pixel = abs(max(dst_corner_ys) - min(dst_corner_ys)) / shape[0] x_pixel = abs(max(dst_corner_xs) - min(dst_corner_xs)) / shape[1] return (min(dst_corner_xs), x_pixel, 0.0, max(dst_corner_ys), 0.0, -y_pixel)
def get_elevation(lat_lon, IMAGE_PATH): WGS84 = CRS.from_epsg(4326) epsg_27700 = CRS.from_epsg(27700) image = rasterio.open(IMAGE_PATH) results = [] lat = lat_lon[0] lon = lat_lon[1] new_coo = transform(WGS84, epsg_27700, xs=[lon], ys=[lat]) vals = image.sample(((new_coo[0][0], new_coo[1][0]), )) for val in vals: results.append({"lat": lat, "lon": lon, "elevation": val[0]}) return results
def get_latlon(da): """Calculates lat, lon for grid""" ny, nx = len(da['y']), len(da['x']) x, y = np.meshgrid(da['x'], da['y']) lon, lat = transform(da.crs, {'init': 'EPSG:4326'}, x.flatten(), y.flatten()) lon = np.asarray(lon).reshape((ny, nx)) lat = np.asarray(lat).reshape((ny, nx)) da.coords['lon'] = (('y', 'x'), lon) da.coords['lat'] = (('y', 'x'), lat) return
def _get_boundaries(self, src): self.output("Getting boundaries", normal=True, arrow=True) output = { "ul": {"x": [0, 0], "y": [0, 0]}, # ul: upper left "ur": {"x": [0, 0], "y": [0, 0]}, # ur: upper right "ll": {"x": [0, 0], "y": [0, 0]}, # ll: lower left "lr": {"x": [0, 0], "y": [0, 0]}, } # lr: lower right output["ul"]["x"][0] = src["affine"][2] output["ul"]["y"][0] = src["affine"][5] output["ur"]["x"][0] = output["ul"]["x"][0] + self.pixel * src["shape"][1] output["ur"]["y"][0] = output["ul"]["y"][0] output["ll"]["x"][0] = output["ul"]["x"][0] output["ll"]["y"][0] = output["ul"]["y"][0] - self.pixel * src["shape"][0] output["lr"]["x"][0] = output["ul"]["x"][0] + self.pixel * src["shape"][1] output["lr"]["y"][0] = output["ul"]["y"][0] - self.pixel * src["shape"][0] output["ul"]["x"][1], output["ul"]["y"][1] = transform( src["crs"], self.projection, [output["ul"]["x"][0]], [output["ul"]["y"][0]] ) output["ur"]["x"][1], output["ur"]["y"][1] = transform( src["crs"], self.projection, [output["ur"]["x"][0]], [output["ur"]["y"][0]] ) output["ll"]["x"][1], output["ll"]["y"][1] = transform( src["crs"], self.projection, [output["ll"]["x"][0]], [output["ll"]["y"][0]] ) output["lr"]["x"][1], output["lr"]["y"][1] = transform( src["crs"], self.projection, [output["lr"]["x"][0]], [output["lr"]["y"][0]] ) return output
def feature_to_mercator(feature): '''Normalize feature and converts coords to 3857. Args: feature: geojson feature to convert to mercator geometry. ''' # Ref: https://gist.github.com/dnomadb/5cbc116aacc352c7126e779c29ab7abe src_crs = CRS.from_epsg(4326) dst_crs = CRS.from_epsg(3857) geometry = feature['geometry'] if geometry['type'] == 'Polygon': xys = (zip(*part) for part in geometry['coordinates']) xys = (list(zip(*transform(src_crs, dst_crs, *xy))) for xy in xys) yield {'coordinates': list(xys), 'type': 'Polygon'} elif geometry['type'] == 'MultiPolygon': for component in geometry['coordinates']: xys = (zip(*part) for part in component) xys = (list(zip(*transform(src_crs, dst_crs, *xy))) for xy in xys) yield {'coordinates': list(xys), 'type': 'Polygon'}
def get_zoom(input, dst_crs="EPSG:3857"): input = input.replace("s3://", "/vsicurl/http://s3.amazonaws.com/") with rasterio.drivers(): with rasterio.open(input) as src: # Compute the geographic bounding box of the dataset. (west, east), (south, north) = transform( src.crs, "EPSG:4326", src.bounds[::2], src.bounds[1::2]) affine, _, _ = calculate_default_transform(src.crs, dst_crs, src.width, src.height, *src.bounds, resolution=None) # grab the lowest resolution dimension resolution = max(abs(affine[0]), abs(affine[4])) return int(round(math.log((2 * math.pi * 6378137) / (resolution * CHUNK_SIZE)) / math.log(2)))
def get_corners(src_crs, affine, shape, dst_crs): corners = [affine * p for p in corners_from_shape(shape)] return transform(src_crs, dst_crs, [p[0] for p in corners], [p[1] for p in corners])
def mbtiles(ctx, files, output_opt, title, description, layer_type, img_format, zoom_levels, image_dump, num_workers): """Export a dataset to MBTiles (version 1.1) in a SQLite file. The input dataset may have any coordinate reference system. It must have at least three bands, which will be become the red, blue, and green bands of the output image tiles. If no zoom levels are specified, the defaults are the zoom levels nearest to the one at which one tile may contain the entire source dataset. If a title or description for the output file are not provided, they will be taken from the input dataset's filename. This command is suited for small to medium (~1 GB) sized sources. Python package: rio-mbtiles (https://github.com/mapbox/rio-mbtiles). """ verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 logger = logging.getLogger('rio') output, files = resolve_inout(files=files, output=output_opt) inputfile = files[0] with rasterio.drivers(CPL_DEBUG=verbosity > 2): # Read metadata from the source dataset. with rasterio.open(inputfile) as src: # Name and description. title = title or os.path.basename(src.name) description = description or src.name # Compute the geographic bounding box of the dataset. (west, east), (south, north) = transform( src.crs, 'EPSG:4326', src.bounds[::2], src.bounds[1::2]) # Resolve the minimum and maximum zoom levels for export. if zoom_levels: minzoom, maxzoom = map(int, zoom_levels.split('..')) else: zw = int(round(math.log(360.0/(east-west), 2.0))) zh = int(round(math.log(170.1022/(north-south), 2.0))) minzoom = min(zw, zh) maxzoom = max(zw, zh) logger.debug("Zoom range: %d..%d", minzoom, maxzoom) # Parameters for creation of tile images. base_kwds = { 'driver': img_format.upper(), 'dtype': 'uint8', 'nodata': 0, 'height': 256, 'width': 256, 'count': 3, 'crs': 'EPSG:3857'} img_ext = 'jpg' if img_format.lower() == 'jpeg' else 'png' # Initialize the sqlite db. if os.path.exists(output): os.unlink(output) conn = sqlite3.connect(output) cur = conn.cursor() cur.execute( "CREATE TABLE tiles " "(zoom_level integer, tile_column integer, " "tile_row integer, tile_data blob);") cur.execute( "CREATE TABLE metadata (name text, value text);") # Insert mbtiles metadata into db. cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("name", title)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("type", layer_type)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("version", "1.1")) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("description", description)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("format", img_ext)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("bounds", "%f,%f,%f,%f" % (west, south, east, north))) conn.commit() # Create a pool of workers to process tile tasks. pool = Pool(num_workers, init_worker, (inputfile, base_kwds), 100) # Constrain bounds. EPS = 1.0e-10 west = max(-180+EPS, west) south = max(-85.051129, south) east = min(180-EPS, east) north = min(85.051129, north) # Initialize iterator over output tiles. tiles = mercantile.tiles( west, south, east, north, range(minzoom, maxzoom+1)) for tile, contents in pool.imap_unordered(process_tile, tiles): # MBTiles has a different origin than Mercantile/tilebelt. tiley = int(math.pow(2, tile.z)) - tile.y - 1 # Optional image dump. if image_dump: img_name = '%d-%d-%d.%s' % ( tile.x, tiley, tile.z, img_ext) img_path = os.path.join(image_dump, img_name) with open(img_path, 'wb') as img: img.write(contents) # Insert tile into db. cur.execute( "INSERT INTO tiles " "(zoom_level, tile_column, tile_row, tile_data) " "VALUES (?, ?, ?, ?);", (tile.z, tile.x, tiley, buffer(contents))) conn.commit() conn.close()
def transform_coordinates(source_srs, target_srs, x, y): return tuple(i[0] for i in transform(source_srs, target_srs, x, y))
def get_corners(affine, src_crs, shape, dst_crs): corners = [(0,0), (0, shape[0]), (shape[1], shape[0]), (shape[1], 0)] corners = [affine * p for p in corners] return transform(src_crs, dst_crs, [p[0] for p in corners], [p[1] for p in corners])
import matplotlib.pyplot as plt import numpy as np from rasterio.warp import transform import xarray as xr # Read the data url = 'https://github.com/mapbox/rasterio/raw/master/tests/data/RGB.byte.tif' da = xr.open_rasterio(url) # Compute the lon/lat coordinates with rasterio.warp.transform ny, nx = len(da['y']), len(da['x']) x, y = np.meshgrid(da['x'], da['y']) # Rasterio works with 1D arrays lon, lat = transform(da.crs, {'init': 'EPSG:4326'}, x.flatten(), y.flatten()) lon = np.asarray(lon).reshape((ny, nx)) lat = np.asarray(lat).reshape((ny, nx)) da.coords['lon'] = (('y', 'x'), lon) da.coords['lat'] = (('y', 'x'), lat) # Compute a greyscale out of the rgb image greyscale = da.mean(dim='band') # Plot on a map ax = plt.subplot(projection=ccrs.PlateCarree()) greyscale.plot(ax=ax, x='lon', y='lat', transform=ccrs.PlateCarree(), cmap='Greys_r', add_colorbar=False) ax.coastlines('10m', color='r') plt.show()
def mbtiles(ctx, files, output, overwrite, title, description, layer_type, img_format, tile_size, zoom_levels, image_dump, num_workers, src_nodata, dst_nodata, resampling, rgba): """Export a dataset to MBTiles (version 1.1) in a SQLite file. The input dataset may have any coordinate reference system. It must have at least three bands, which will be become the red, blue, and green bands of the output image tiles. An optional fourth alpha band may be copied to the output tiles by using the --rgba option in combination with the PNG format. This option requires that the input dataset has at least 4 bands. If no zoom levels are specified, the defaults are the zoom levels nearest to the one at which one tile may contain the entire source dataset. If a title or description for the output file are not provided, they will be taken from the input dataset's filename. This command is suited for small to medium (~1 GB) sized sources. Python package: rio-mbtiles (https://github.com/mapbox/rio-mbtiles). """ output, files = resolve_inout(files=files, output=output, overwrite=overwrite) inputfile = files[0] log = logging.getLogger(__name__) with ctx.obj['env']: # Read metadata from the source dataset. with rasterio.open(inputfile) as src: validate_nodata(dst_nodata, src_nodata, src.profile.get('nodata')) base_kwds = {'dst_nodata': dst_nodata, 'src_nodata': src_nodata} if src_nodata is not None: base_kwds.update(nodata=src_nodata) if dst_nodata is not None: base_kwds.update(nodata=dst_nodata) # Name and description. title = title or os.path.basename(src.name) description = description or src.name # Compute the geographic bounding box of the dataset. (west, east), (south, north) = transform( src.crs, 'EPSG:4326', src.bounds[::2], src.bounds[1::2]) # Resolve the minimum and maximum zoom levels for export. if zoom_levels: minzoom, maxzoom = map(int, zoom_levels.split('..')) else: zw = int(round(math.log(360.0 / (east - west), 2.0))) zh = int(round(math.log(170.1022 / (north - south), 2.0))) minzoom = min(zw, zh) maxzoom = max(zw, zh) log.debug("Zoom range: %d..%d", minzoom, maxzoom) if rgba: if img_format == 'JPEG': raise click.BadParameter("RGBA output is not possible with JPEG format.") else: count = 4 else: count = 3 # Parameters for creation of tile images. base_kwds.update({ 'driver': img_format.upper(), 'dtype': 'uint8', 'nodata': 0, 'height': tile_size, 'width': tile_size, 'count': count, 'crs': TILES_CRS}) img_ext = 'jpg' if img_format.lower() == 'jpeg' else 'png' # Initialize the sqlite db. if os.path.exists(output): os.unlink(output) # workaround for bug here: https://bugs.python.org/issue27126 sqlite3.connect(':memory:').close() conn = sqlite3.connect(output) cur = conn.cursor() cur.execute( "CREATE TABLE tiles " "(zoom_level integer, tile_column integer, " "tile_row integer, tile_data blob);") cur.execute( "CREATE TABLE metadata (name text, value text);") # Insert mbtiles metadata into db. cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("name", title)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("type", layer_type)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("version", "1.1")) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("description", description)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("format", img_ext)) cur.execute( "INSERT INTO metadata (name, value) VALUES (?, ?);", ("bounds", "%f,%f,%f,%f" % (west, south, east, north))) conn.commit() # Create a pool of workers to process tile tasks. pool = Pool(num_workers, init_worker, (inputfile, base_kwds, resampling), 100) # Constrain bounds. EPS = 1.0e-10 west = max(-180 + EPS, west) south = max(-85.051129, south) east = min(180 - EPS, east) north = min(85.051129, north) # Initialize iterator over output tiles. tiles = mercantile.tiles( west, south, east, north, range(minzoom, maxzoom + 1)) for tile, contents in pool.imap_unordered(process_tile, tiles): if contents is None: log.info("Tile %r is empty and will be skipped", tile) continue # MBTiles have a different origin than Mercantile/tilebelt. tiley = int(math.pow(2, tile.z)) - tile.y - 1 # Optional image dump. if image_dump: img_name = '%d-%d-%d.%s' % ( tile.x, tiley, tile.z, img_ext) img_path = os.path.join(image_dump, img_name) with open(img_path, 'wb') as img: img.write(contents) # Insert tile into db. cur.execute( "INSERT INTO tiles " "(zoom_level, tile_column, tile_row, tile_data) " "VALUES (?, ?, ?, ?);", (tile.z, tile.x, tiley, sqlite3.Binary(contents))) conn.commit() conn.close()
def test_transform_src_crs_none(): with pytest.raises(CRSError): transform(None, WGS84_crs, [], [])
def test_transform_dst_crs_none(): with pytest.raises(CRSError): transform(WGS84_crs, None, [], [])