def test_shape(self): """Is the transformation doing well?""" from salem import read_shapefile so = read_shapefile(get_demo_file('Hintereisferner.shp')) sref = read_shapefile(get_demo_file('Hintereisferner_UTM.shp')) st = gis.transform_geopandas(so, to_crs=sref.crs) self.assertFalse(st is so) assert_allclose(st.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) sti = gis.transform_geopandas(so, to_crs=sref.crs, inplace=True) self.assertTrue(sti is so) assert_allclose(so.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) assert_allclose(sti.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) g = Grid(nxny=(1, 1), dxdy=(1, 1), x0y0=(10., 46.), proj=wgs84) so = read_shapefile(get_demo_file('Hintereisferner.shp')) st = gis.transform_geopandas(so, to_crs=g) ref = np.array(so.geometry[0].exterior.coords) ref = ref - np.floor(ref) assert_allclose(ref, st.geometry[0].exterior.coords) # round trip so_back = gis.transform_geopandas(st, to_crs=so.crs) assert_allclose(so_back.geometry[0].exterior.coords, so.geometry[0].exterior.coords)
def set_roi(self, shape=None, geometry=None, crs=wgs84, grid=None, corners=None, noerase=False): """Set a region of interest for the dataset. If set succesfully, a ROI is simply a mask of the same size as the dataset's grid, obtained with the .roi attribute. I haven't decided yet if the data should be masekd out when a ROI has been set. Parameters ---------- shape: path to a shapefile geometry: a shapely geometry crs: the crs of the geometry grid: a Grid object corners: a ((x0, y0), (x1, y1)) tuple of the corners of the square to subset the dataset to. The coordinates are not expressed in wgs84, set the crs keyword noerase: set to true in order to add the new ROI to the previous one """ # The rois are always defined on the original grids, but of course # we take that into account when a subset is set (see roi # decorator below) ogrid = self._ogrid # Initial mask if noerase and (self.roi is not None): mask = self.roi else: mask = np.zeros((ogrid.ny, ogrid.nx), dtype=np.int16) # Several cases if shape is not None: gdf = sio.read_shapefile(shape) gis.transform_geopandas(gdf, to_crs=ogrid.corner_grid, inplace=True) if rasterio is None: raise ImportError('This feature needs rasterio') from rasterio.features import rasterize with rasterio.Env(): mask = rasterize(gdf.geometry, out=mask) if geometry is not None: geom = gis.transform_geometry(geometry, crs=crs, to_crs=ogrid.corner_grid) if rasterio is None: raise ImportError('This feature needs rasterio') from rasterio.features import rasterize with rasterio.Env(): mask = rasterize(np.atleast_1d(geom), out=mask) if grid is not None: _tmp = np.ones((grid.ny, grid.nx), dtype=np.int16) mask = ogrid.map_gridded_data(_tmp, grid, out=mask).filled(0) if corners is not None: cgrid = self._ogrid.center_grid xy0, xy1 = corners x0, y0 = cgrid.transform(*xy0, crs=crs, nearest=True) x1, y1 = cgrid.transform(*xy1, crs=crs, nearest=True) mask[np.min([y0, y1]):np.max([y0, y1])+1, np.min([x0, x1]):np.max([x0, x1])+1] = 1 self.roi = mask
def test_shape(self): """Is the transformation doing well?""" from salem import read_shapefile so = read_shapefile(get_demo_file('Hintereisferner.shp')) sref = read_shapefile(get_demo_file('Hintereisferner_UTM.shp')) st = gis.transform_geopandas(so, to_crs=sref.crs) self.assertFalse(st is so) assert_allclose(st.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) sti = gis.transform_geopandas(so, to_crs=sref.crs, inplace=True) self.assertTrue(sti is so) assert_allclose(so.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) assert_allclose(sti.geometry[0].exterior.coords, sref.geometry[0].exterior.coords) g = Grid(nxny=(1, 1), dxdy=(1, 1), ll_corner=(10., 46.), proj=wgs84) so = read_shapefile(get_demo_file('Hintereisferner.shp')) st = gis.transform_geopandas(so, to_crs=g) ref = np.array(so.geometry[0].exterior.coords) ref = ref - np.floor(ref) assert_allclose(ref, st.geometry[0].exterior.coords)
def set_roi(self, shape=None, geometry=None, crs=wgs84, grid=None, corners=None, noerase=False): """Set a region of interest for the dataset. If set succesfully, a ROI is simply a mask of the same size as the dataset's grid, obtained with the .roi attribute. I haven't decided yet if the data should be masekd out when a ROI has been set. Parameters ---------- shape: path to a shapefile geometry: a shapely geometry crs: the crs of the geometry grid: a Grid object corners: a ((x0, y0), (x1, y1)) tuple of the corners of the square to subset the dataset to. The coordinates are not expressed in wgs84, set the crs keyword noerase: set to true in order to add the new ROI to the previous one """ # The rois are always defined on the original grids, but of course # we take that into account when a subset is set (see roi # decorator below) ogrid = self._ogrid # Initial mask if noerase and (self.roi is not None): mask = self.roi else: mask = np.zeros((ogrid.ny, ogrid.nx), dtype=np.int16) # Several cases if shape is not None: gdf = sio.read_shapefile(shape) gis.transform_geopandas(gdf, to_crs=ogrid.corner_grid, inplace=True) with rasterio.Env(): mask = features.rasterize(gdf.geometry, out=mask) if geometry is not None: geom = gis.transform_geometry(geometry, crs=crs, to_crs=ogrid.corner_grid) with rasterio.Env(): mask = features.rasterize(np.atleast_1d(geom), out=mask) if grid is not None: _tmp = np.ones((grid.ny, grid.nx), dtype=np.int16) mask = ogrid.map_gridded_data(_tmp, grid, out=mask).filled(0) if corners is not None: cgrid = self._ogrid.center_grid xy0, xy1 = corners x0, y0 = cgrid.transform(*xy0, crs=crs, nearest=True) x1, y1 = cgrid.transform(*xy1, crs=crs, nearest=True) mask[np.min([y0, y1]):np.max([y0, y1])+1, np.min([x0, x1]):np.max([x0, x1])+1] = 1 self.roi = mask
def _memory_transform(shape_cpath, grid=None, grid_str=None): """Quick solution using joblib in order to not transform many times the same shape (useful for maps). Since grid is a complex object joblib seemed to have trouble with it, so joblib is checking its cache according to grid_str while the job is done with grid. """ shape = read_shapefile(shape_cpath, cached=True) e = grid.extent_in_crs(crs=shape.crs) p = np.nonzero(~((shape['min_x'] > e[1]) | (shape['max_x'] < e[0]) | (shape['min_y'] > e[3]) | (shape['max_y'] < e[2]))) shape = shape.iloc[p] shape = gis.transform_geopandas(shape, to_crs=grid, inplace=True) return shape
def _memory_shapefile_to_grid(shape_cpath, grid=None, nxny=None, pixel_ref=None, x0y0=None, dxdy=None, proj=None): """Quick solution using joblib in order to not transform many times the same shape (useful for maps). Since grid is a complex object, joblib seems to have trouble with it. So joblib is checking its cache according to the grid params while the job is done with grid. """ shape = read_shapefile(shape_cpath, cached=True) e = grid.extent_in_crs(crs=shape.crs) p = np.nonzero(~( (shape['min_x'].to_numpy() > e[1]) | (shape['max_x'].to_numpy() < e[0]) | (shape['min_y'].to_numpy() > e[3]) | (shape['max_y'].to_numpy() < e[2]))) shape = shape.iloc[p] shape = gis.transform_geopandas(shape, to_crs=grid, inplace=True) return shape
def set_shapefile(self, shape=None, countries=False, oceans=False, rivers=False, lakes=False, **kwargs): """Add a shapefile to the plot. Salem is shipped with a few default settings for country borders, oceans and rivers (set one at the time!) set_shapefile() without argument will reset the map to zero shapefiles. Parameters ---------- shape: the path to the shapefile to read countries: if True, add country borders oceans: if True, add oceans rivers: if True, add rivers lakes: if True, add lakes kwargs: all keywords accepted by the corresponding collection. For LineStrings:: linewidths, colors, linestyles, ... For Polygons:: alpha, edgecolor, facecolor, fill, linestyle, linewidth, color, ... """ # See if the user wanted defaults settings if oceans: kwargs.setdefault('facecolor', (0.36862745, 0.64313725, 0.8)) kwargs.setdefault('edgecolor', 'none') kwargs.setdefault('alpha', 1) return self.set_shapefile(shapefiles['oceans'], **kwargs) if rivers: kwargs.setdefault('color', (0.08984375, 0.65625, 0.8515625)) return self.set_shapefile(shapefiles['rivers'], **kwargs) if lakes: kwargs.setdefault('color', (0.08984375, 0.65625, 0.8515625)) return self.set_shapefile(shapefiles['lakes'], **kwargs) if countries: kwargs.setdefault('zorder', 1.5) return self.set_shapefile(shapefiles['world_borders'], **kwargs) # Defaults if not all(k in kwargs for k in ('facecolor', 'edgecolor')): kwargs.setdefault('color', 'k') # Reset? if shape is None: self._collections = [] return # Transform if isinstance(shape, pd.DataFrame): shape = gis.transform_geopandas(shape, to_crs=self.grid) else: shape = sio.read_shapefile_to_grid(shape, grid=self.grid) if len(shape) == 0: return # Different collection for each type geomtype = shape.iloc[0].geometry.type if 'Polygon' in geomtype: patches = [] for g in shape.geometry: if 'Multi' in g.type: for gg in g: patches.append(PolygonPatch(gg)) else: patches.append(PolygonPatch(g)) kwargs.setdefault('facecolor', 'none') if 'color' in kwargs: kwargs.setdefault('edgecolor', kwargs['color']) del kwargs['color'] self._collections.append(PatchCollection(patches, **kwargs)) elif 'LineString' in geomtype: lines = [] for g in shape.geometry: if 'Multi' in g.type: for gg in g: lines.append(np.array(gg)) else: lines.append(np.array(g)) self._collections.append(LineCollection(lines, **kwargs)) else: raise NotImplementedError(geomtype)
def set_shapefile(self, shape=None, countries=False, oceans=False, rivers=False, **kwargs): """Add a shapefile to the plot. Salem is shipped with a few default settings for country borders, oceans and rivers (set one at the time!) set_shapefile() without argument will reset the map to zero shapefiles. Parameters ---------- shape: the path to the shapefile to read countries: if True, add country borders oceans: if True, add oceans rivers: if True, add rivers kwargs: all keywords accepted by the corresponding collection. For LineStrings:: linewidths, colors, linestyles, ... For Polygons:: alpha, edgecolor, facecolor, fill, linestyle, linewidth, color, ... """ # See if the user wanted defaults settings if oceans: kwargs.setdefault('facecolor', (0.36862745, 0.64313725, 0.8)) kwargs.setdefault('edgecolor', 'none') kwargs.setdefault('alpha', 1) return self.set_shapefile(shapefiles['oceans'], **kwargs) if rivers: kwargs.setdefault('colors', (0.08984375, 0.65625, 0.8515625)) return self.set_shapefile(shapefiles['rivers'], **kwargs) if countries: kwargs.setdefault('zorder', 50) return self.set_shapefile(shapefiles['world_borders'], **kwargs) # Reset? if shape is None: self._collections = [] return # Transform if isinstance(shape, pd.DataFrame): shape = gis.transform_geopandas(shape, to_crs=self.grid) else: shape = sio.read_shapefile_to_grid(shape, grid=self.grid) if len(shape) == 0: return # Different collection for each type geomtype = shape.iloc[0].geometry.type if 'Polygon' in geomtype: patches = [] for g in shape.geometry: if 'Multi' in g.type: for gg in g: patches.append(PolygonPatch(gg)) else: patches.append(PolygonPatch(g)) kwargs.setdefault('facecolor', 'none') if 'color' in kwargs: kwargs.setdefault('edgecolor', kwargs['color']) del kwargs['color'] self._collections.append(PatchCollection(patches, **kwargs)) elif 'LineString' in geomtype: lines = [] for g in shape.geometry: if 'Multi' in g.type: for gg in g: lines.append(np.array(gg)) else: lines.append(np.array(g)) self._collections.append(LineCollection(lines, **kwargs)) else: raise NotImplementedError(geomtype)