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