geowatch.cli.crop_sites_to_regions module¶
- class geowatch.cli.crop_sites_to_regions.SiteFilterConfig(*args, **kwargs)[source]¶
Bases:
DataConfig
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'apply_bounds_filter_to': <Value('multipolygon')>, 'apply_clip_to': <Value('polygon')>, 'in_bounds_thresh': <Value(0.6)>, 'max_area_square_meters': <Value(None)>, 'min_area_square_meters': <Value(None)>}¶
- class geowatch.cli.crop_sites_to_regions.CropSitesToRegionsConfig(*args, **kwargs)[source]¶
Bases:
SiteFilterConfig
Crops site models to the bounds of a region model.
Todo
[ ] Rename this to ClipSitesToRegions?
Example
DVC_DPATH=$(GEOWATCH_PREIMPORT=none python -m geowatch.cli.find_dvc) GEOWATCH_PREIMPORT=none python -m geowatch.cli.crop_sites_to_regions
–site_models “$DVC_DPATH/annotations/site_models/KR_R002_*.geojson” –region_models “$DVC_DPATH/annotations/region_models/KR_R002.geojson” –new_site_dpath ./cropped_sites
Valid options: []
- Parameters:
*args – positional arguments for this data config
**kwargs – keyword arguments for this data config
- default = {'apply_bounds_filter_to': <Value('multipolygon')>, 'apply_clip_to': <Value('polygon')>, 'force_multipolygon': <Value(True)>, 'in_bounds_thresh': <Value(0.6)>, 'io_workers': <Value(0)>, 'max_area_square_meters': <Value(None)>, 'min_area_square_meters': <Value(None)>, 'new_region_dpath': <Value(None)>, 'new_site_dpath': <Value(None)>, 'region_models': <Value(None)>, 'site_models': <Value(None)>}¶
- geowatch.cli.crop_sites_to_regions.main(cmdline=False, **kwargs)[source]¶
CommandLine
xdoctest -m geowatch.cli.crop_sites_to_regions main:0 xdoctest -m geowatch.cli.crop_sites_to_regions main:1
Example
>>> from geowatch.geoannots import geomodels >>> import kwimage >>> region = geomodels.RegionModel.random(num_sites=0) >>> # Create several clipping cases >>> region_poly = kwimage.Polygon.coerce(region.geometry) >>> width = region_poly.to_box().width >>> height = region_poly.to_box().height >>> geoms = {} >>> geoms['in_bounds'] = region_poly.scale(0.1, about='centroid') >>> geoms['half_oob'] = region_poly.translate((width / 2, 0)).scale(0.5, about='centroid') >>> geoms['some_oob'] = region_poly.translate((-width / 2, -height / 2)).scale(0.5, about='centroid').translate(width / 4, height / 4) >>> geoms['fully_oob'] = region_poly.translate((width * 2, 0)) >>> sites = {} >>> for key, poly in geoms.items(): >>> sites[key] = geomodels.SiteModel.random(region=region, site_poly=poly) >>> region.add_site_summary(sites[key].as_summary()) >>> # Write demo data to disk >>> dpath = ub.Path.appdir('geowatch/tests/cli/crop_sites_to_regions/doctest0') >>> dpath.delete().ensuredir() >>> region_dpath = (dpath / 'region_models').ensuredir() >>> site_dpath = (dpath / 'site_models').ensuredir() >>> region_fpath = region_dpath / 'region.geojson' >>> region_fpath.write_text(region.dumps()) >>> for k, site in sites.items(): >>> site_fpath = site_dpath / f'{k}.geojson' >>> site_fpath.write_text(site.dumps()) >>> kwargs = { >>> 'site_models': site_dpath, >>> 'region_models': region_dpath, >>> 'new_site_dpath': dpath / 'new_site_models', >>> 'new_region_dpath': dpath / 'new_region_models', >>> } >>> from geowatch.cli import crop_sites_to_regions >>> cmdline = 0 >>> crop_sites_to_regions.main(cmdline=cmdline, **kwargs) >>> new_region = geomodels.RegionModel.coerce(dpath / 'new_region_models') >>> new_sites = list(geomodels.SiteModel.coerce_multiple(dpath / 'new_site_models')) >>> # xdoctest: +REQUIRES(--show) >>> # xdoctest: +REQUIRES(module:kwplot) >>> import kwplot >>> kwplot.plt.ion() >>> ax = kwplot.figure(doclf=True, fnum=2, pnum=(2, 2, 1), title='Sites Before Clip').gca() >>> df = region.pandas_region() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> for site in sites.values(): >>> df = site.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.1, 0.8, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 2), title='Sites After Clip').gca() >>> df = new_region.pandas_region() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> for site in new_sites: >>> df = site.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.1, 0.8, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 3), title='Region Before Clip').gca() >>> df = region.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 4), title='Region After Clip').gca() >>> df = new_region.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax)
Example
>>> # Convex clipping case >>> from geowatch.geoannots import geomodels >>> import kwimage >>> star = kwimage.Polygon.star() >>> p1 = kwimage.Polygon.circle(xy=(0, 0), r=1) >>> p2 = kwimage.Polygon.circle(xy=(0.2, 0), r=1) >>> p3 = p1.difference(p2).translate(0.3) >>> box = kwimage.Box.coerce([-1, .3, 5, 5], format='xywh').to_polygon() >>> p3 = p3.difference(box) >>> box = kwimage.Box.coerce([-.1, -1, 10, 10], format='xywh').to_polygon() >>> p3 = p3.difference(box) >>> p3 = p3.difference(kwimage.Polygon.circle(xy=(-.6, -.2), r=0.15)) >>> region = geomodels.RegionModel.random(region_poly=star, num_sites=0, rng=21) >>> region_poly = kwimage.Polygon.coerce(region.geometry) >>> width = region_poly.to_box().width >>> height = region_poly.to_box().height >>> geoms = {} >>> geoms['in_bounds'] = region_poly.scale(0.1, about='centroid') >>> geoms['half_oob'] = region_poly.translate((width / 2, 0)) >>> geoms['some_oob'] = region_poly.translate((width / 2, height / 2)).scale(0.5, about='centroid').translate(-width / 3, -height / 3) >>> geoms['fully_oob'] = region_poly.translate((width * 2, 0)) >>> geoms['tiny_oob'] = kwimage.Polygon.circle(xy=(-.20, .27), r=0.1) >>> geoms['sliver'] = p3 >>> sites = {} >>> for key, poly in geoms.items(): >>> sites[key] = geomodels.SiteModel.random(region=region, site_poly=poly) >>> region.add_site_summary(sites[key].as_summary()) >>> # Write demo data to disk >>> dpath = ub.Path.appdir('geowatch/tests/cli/crop_sites_to_regions/doctest0') >>> dpath.delete().ensuredir() >>> region_dpath = (dpath / 'region_models').ensuredir() >>> site_dpath = (dpath / 'site_models').ensuredir() >>> region_fpath = region_dpath / 'region.geojson' >>> region_fpath.write_text(region.dumps()) >>> for k, site in sites.items(): >>> site_fpath = site_dpath / f'{k}.geojson' >>> site_fpath.write_text(site.dumps()) >>> kwargs = { >>> 'site_models': site_dpath, >>> 'region_models': region_dpath, >>> 'new_site_dpath': dpath / 'new_site_models', >>> 'new_region_dpath': dpath / 'new_region_models', >>> 'min_area_square_meters': 5e8, >>> } >>> from geowatch.cli import crop_sites_to_regions >>> cmdline = 0 >>> crop_sites_to_regions.main(cmdline=cmdline, **kwargs) >>> new_region = geomodels.RegionModel.coerce(dpath / 'new_region_models') >>> new_sites = list(geomodels.SiteModel.coerce_multiple(dpath / 'new_site_models')) >>> assert len(new_sites) == 2 >>> assert len(sites) == 6 >>> # xdoctest: +REQUIRES(--show) >>> # xdoctest: +REQUIRES(module:kwplot) >>> import kwplot >>> kwplot.plt.ion() >>> ax = kwplot.figure(doclf=True, fnum=2, pnum=(2, 2, 1), title='Observations Before Clip').gca() >>> df = region.pandas_region() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> for site in sites.values(): >>> df = site.pandas_observations() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.1, 0.8, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 2), title='Observations After Clip').gca() >>> df = new_region.pandas_region() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> for site in new_sites: >>> df = site.pandas_observations() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.1, 0.8, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 3), title='Site Summary Before Clip').gca() >>> df = region.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax) >>> ax = kwplot.figure(fnum=2, pnum=(2, 2, 4), title='Site Summary After Clip').gca() >>> df = new_region.pandas() >>> ax = df.plot(edgecolor='black', facecolor=(0.1, 0.8, 0.1, 0.5), ax=ax)
- geowatch.cli.crop_sites_to_regions.filter_sites(region_gdf_crs84, sites, filter_config=None)[source]¶
- Parameters:
region_gdf_crs84 (GeoDataFrame) – the region GDF containing the region geom to crop to and the site summary geometry
sites (Iterable[Dict]) – List of the loaded geo data frames with a ‘data’ key and the file path in the ‘fpath’ key.
filter_config (SiteFilterConfig | None) – modifies filter behavior.
- Returns:
Region model with cropped site summaries and a list of site info dictionaries containing the new cropped data field.
- Return type:
Tuple[GeoDataFrame, Iterable[Dict]]
Example
>>> from geowatch.cli.crop_sites_to_regions import * # NOQA >>> import geopandas as gpd >>> import kwimage >>> from kwgis.utils import util_gis >>> crs84 = util_gis._get_crs84() >>> region_poly = kwimage.Polygon.random(rng=0).translate((42, 72)) >>> site_poly1 = region_poly.translate((0.0001, 0.0001)) >>> # >>> def demo_site_summary(site_id, site_poly): >>> return { >>> 'type': 'site_summary', >>> 'region_id': None, >>> 'site_id': site_id, >>> 'start_date': '2020-01-01', >>> 'end_date': '2020-01-03', >>> 'geometry': site_poly.to_shapely() >>> } >>> def demo_site(site_id, site_poly): >>> sh_poly = site_poly.to_shapely() >>> site = gpd.GeoDataFrame([ >>> {'type': 'site', 'region_id': 'DemoRegion', 'site_id': site_id, 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-01', 'current_phase': 'phase1', 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-02', 'current_phase': 'phase2', 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-03', 'current_phase': 'phase3', 'geometry': sh_poly}, >>> ], crs=crs84) >>> return {'fpath': None, 'data': site} >>> region_gdf_crs84 = gpd.GeoDataFrame([ >>> { >>> 'type': 'region', >>> 'region_id': 'DemoRegion', >>> 'geometry': region_poly.to_shapely(), >>> }, >>> demo_site_summary('DemoRegion_0001', site_poly1), >>> ], crs=crs84) >>> sites = [ >>> demo_site('DemoRegion_0001', site_poly1), >>> ] >>> cropped_region, cropped_sites = filter_sites(region_gdf_crs84, sites) >>> cropped_sites = list(cropped_sites) >>> assert len(cropped_sites) == len(sites) >>> assert len(cropped_region) == 2
Example
>>> # xdoctest: +REQUIRES(--slow) >>> from geowatch.cli.crop_sites_to_regions import * # NOQA >>> import geopandas as gpd >>> import kwimage >>> from kwgis.utils import util_gis >>> crs84 = util_gis._get_crs84() >>> region_poly = kwimage.Polygon.random(rng=0).translate((42, 72)) >>> site_poly0 = region_poly >>> site_poly1 = region_poly.translate((0.0001, 0.0001)) >>> site_poly2 = region_poly.translate((3.0, 3.0)) >>> site_poly3 = kwimage.Polygon.random().translate((42, 72)) >>> site_poly4 = kwimage.Polygon.random().translate((43, 73)) >>> site_poly5 = kwimage.Polygon.random().translate((42.1, 72.1)) >>> # >>> def demo_site_summary(site_id, site_poly): >>> return { >>> 'type': 'site_summary', >>> 'region_id': None, >>> 'site_id': site_id, >>> 'start_date': '2020-01-01', >>> 'end_date': '2020-01-03', >>> 'geometry': site_poly.to_shapely() >>> } >>> def demo_site(site_id, site_poly): >>> sh_poly = site_poly.to_shapely() >>> site = gpd.GeoDataFrame([ >>> {'type': 'site', 'region_id': 'DemoRegion', 'site_id': site_id, 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-01', 'current_phase': 'phase1', 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-02', 'current_phase': 'phase2', 'geometry': sh_poly}, >>> {'type': 'observation', 'observation_date': '2020-01-03', 'current_phase': 'phase3', 'geometry': sh_poly}, >>> ], crs=crs84) >>> return {'fpath': None, 'data': site} >>> region_gdf_crs84 = gpd.GeoDataFrame([ >>> { >>> 'type': 'region', >>> 'region_id': 'DemoRegion', >>> 'geometry': region_poly.to_shapely(), >>> }, >>> demo_site_summary('DemoRegion_0001', site_poly1), >>> demo_site_summary('DemoRegion_0002', site_poly2), >>> ], crs=crs84) >>> sites = [ >>> demo_site('DemoRegion_0000', site_poly0), >>> demo_site('DemoRegion_0001', site_poly1), >>> demo_site('DemoRegion_0002', site_poly2), >>> demo_site('DemoRegion_0003', site_poly3), >>> demo_site('DemoRegion_0004', site_poly4), >>> demo_site('DemoRegion_0005', site_poly5), >>> ] >>> cropped_region, cropped_sites = filter_sites(region_gdf_crs84, sites) >>> cropped_sites = list(cropped_sites) >>> assert len(cropped_sites) == len(sites) >>> assert len(cropped_sites[0]['data']) == len(sites[0]['data']) >>> assert len(cropped_sites[1]['data']) == len(sites[1]['data']) >>> assert len(cropped_sites[2]['data']) == 0 >>> assert len(cropped_region) == 2
- geowatch.cli.crop_sites_to_regions.filter_gdf_in_utm(gdf, crop_geom_utm, utm_epsg, output_crs, main_type=None, filter_config=None)[source]¶
Crop geometry in a geopandas data frame to specified bounds in UTM space. Filter out any rows where the cropped geometry is null or invalid.
- Parameters:
gdf (geopandas.GeoDataFrame) – The data to crop (in CRS84)
crop_geom_utm (shapely.geometry.polygon.Polygon) – The UTM polygon to crop to.
utm_epsg (int) – The UTM zone to work in
output_crs (pyproj.crs.crs.CRS) – The output CRS to wrap back into (should be CRS84)
main_type (str) – “site_summary” for region models, and “site” for site models.
filter_config (SiteFilterConfig | None) – modifies filter behavior.
- Returns:
GeoDataFrame