def parse(self, value): if type(value) in [list, tuple]: if all([isinstance(element, dict) for element in value]): for ii, element in enumerate(value, start=1): if 'geom' not in element: ocgis_lh(exc=DefinitionValidationError(self, 'Geometry dictionaries must have a "geom" key.')) if 'properties' not in element: element['properties'] = {self._ugid_key: ii} crs = element.get('crs', constants.UNINITIALIZED) if 'crs' not in element: ocgis_lh(msg='No CRS in geometry dictionary - assuming WGS84.', level=logging.WARN) ret = Field.from_records(value, crs=crs, uid=self.geom_uid, union=self.union, data_model=self.data_model) else: if len(value) == 2: geom = Point(value[0], value[1]) elif len(value) == 4: minx, miny, maxx, maxy = value geom = Polygon(((minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny))) if not geom.is_valid: raise DefinitionValidationError(self, 'Parsed geometry is not valid.') ret = [{'geom': geom, 'properties': {self._ugid_key: 1}}] ret = Field.from_records(ret, uid=self.geom_uid, union=self.union, data_model=self.data_model) self._bounds = geom.bounds elif isinstance(value, GeomCabinetIterator): self._shp_key = value.key or value.path # Always yield fields. value.as_field = True ret = value elif isinstance(value, BaseGeometry): ret = [{'geom': value, 'properties': {self._ugid_key: 1}}] ret = Field.from_records(ret, uid=self.geom_uid, union=self.union, data_model=self.data_model) elif value is None: ret = value elif isinstance(value, Field): ret = value elif isinstance(value, GeometryVariable): if value.ugid is None: msg = 'Geometry variables must have an associated "UGID".' raise DefinitionValidationError(self, msg) ret = Field(geom=value, crs=value.crs) else: raise NotImplementedError(type(value)) # Convert to singular field if this is a field object. if isinstance(ret, Field): ret = tuple(self._iter_singular_fields_(ret)) return ret
def test_from_records(self): gci = GeomCabinetIterator(path=self.path_state_boundaries) actual = Field.from_records(gci, data_model='NETCDF3_CLASSIC') desired = {'UGID': np.int32, 'ID': np.int32} for v in desired.keys(): self.assertEqual(actual[v].get_value().dtype, desired[v])
def _run_(): env.SUPPRESS_WARNINGS = False ocgis_lh.configure(to_stream=True) records = [{ 'geom': Point(1, 2), 'properties': { 'a_list': [1, 2, 3] } }] actual = Field.from_records(records) self.assertNotIn("a_list", actual.keys()) env.SUPPRESS_WARNINGS = True
def test_from_records(self): gci = GeomCabinetIterator(path=self.path_state_boundaries) actual = Field.from_records(gci, data_model='NETCDF3_CLASSIC') desired = {'UGID': np.int32, 'ID': np.int32} for v in desired.keys(): self.assertEqual(actual[v].get_value().dtype, desired[v]) # Test strictness with list/tuple def _run_(): env.SUPPRESS_WARNINGS = False ocgis_lh.configure(to_stream=True) records = [{ 'geom': Point(1, 2), 'properties': { 'a_list': [1, 2, 3] } }] actual = Field.from_records(records) self.assertNotIn("a_list", actual.keys()) env.SUPPRESS_WARNINGS = True self.assertWarns(OcgWarning, _run_)
def iter_geoms(self, key=None, select_uid=None, path=None, load_geoms=True, as_field=False, uid=None, select_sql_where=None, slc=None, union=False, data_model=None, driver_kwargs=None): """ See documentation for :class:`~ocgis.GeomCabinetIterator`. """ # Get the path to the output shapefile. shp_path = self._get_path_by_key_or_direct_path_(key=key, path=path) # Get the source metadata. meta = self.get_meta(path=shp_path, driver_kwargs=driver_kwargs) if union: gic = GeomCabinetIterator(key=key, select_uid=select_uid, path=path, load_geoms=load_geoms, as_field=False, uid=uid, select_sql_where=select_sql_where, slc=slc, union=False, data_model=data_model, driver_kwargs=driver_kwargs) yld = Field.from_records(gic, meta['schema'], crs=meta['crs'], uid=uid, union=True, data_model=data_model) yield yld else: if slc is not None and (select_uid is not None or select_sql_where is not None): exc = ValueError( 'Slice is not allowed with other select statements.') ocgis_lh(exc=exc, logger='geom_cabinet') # Format the slice for iteration. We will get the features by index if a slice is provided. if slc is not None: slc = get_index_slice_for_iteration(slc) # Open the target geometry file. ds = ogr.Open(shp_path) try: # Return the features iterator. features = self._get_features_object_( ds, uid=uid, select_uid=select_uid, select_sql_where=select_sql_where, driver_kwargs=driver_kwargs) # Using slicing, we will select the features individually from the object. if slc is None: itr = features else: # The geodatabase API requires iterations to get the given location. if self.get_gdal_driver( shp_path) == 'OpenFileGDB' or isinstance( slc, slice): def _o_itr_(features_object, slice_start, slice_stop): for ctr2, fb in enumerate(features_object): # ... iterate until start is reached. if ctr2 < slice_start: continue # ... stop if we have reached the stop. elif ctr2 == slice_stop: return yield fb itr = _o_itr_(features, slc.start, slc.stop) else: # Convert the slice index to an integer to avoid type conflict in GDAL layer. itr = (features.GetFeature(int(idx)) for idx in slc) # Convert feature objects to record dictionaries. for ctr, feature in enumerate(itr): if load_geoms: yld = { 'geom': wkb.loads(feature.geometry().ExportToWkb()) } else: yld = {} items = feature.items() properties = OrderedDict([(key, items[key]) for key in feature.keys()]) yld.update({'properties': properties, 'meta': meta}) if ctr == 0: uid, add_uid = get_uid_from_properties(properties, uid) # The properties schema needs to be updated to account for the adding of a unique identifier. if add_uid: meta['schema']['properties'][uid] = 'int' # Add the unique identifier if required if add_uid: properties[uid] = feature.GetFID() # Ensure the unique identifier is an integer else: properties[uid] = int(properties[uid]) if as_field: yld = Field.from_records([yld], schema=meta['schema'], crs=yld['meta']['crs'], uid=uid, data_model=data_model) yield yld try: assert ctr >= 0 except UnboundLocalError: # occurs if there were not feature returned by the iterator. raise a more clear exception. msg = 'No features returned from target data source. Were features appropriately selected?' raise ValueError(msg) finally: # Close or destroy the data source object if it actually exists. if ds is not None: ds.Destroy() ds = None
def parse(self, value): if type(value) in [list, tuple]: if all([isinstance(element, dict) for element in value]): for ii, element in enumerate(value, start=1): if 'geom' not in element: ocgis_lh(exc=DefinitionValidationError( self, 'Geometry dictionaries must have a "geom" key.')) if 'properties' not in element: element['properties'] = {self._ugid_key: ii} crs = element.get('crs', constants.UNINITIALIZED) if 'crs' not in element: ocgis_lh( msg= 'No CRS in geometry dictionary - assuming WGS84.', level=logging.WARN) ret = Field.from_records(value, crs=crs, uid=self.geom_uid, union=self.union, data_model=self.data_model) else: if len(value) == 2: geom = Point(value[0], value[1]) elif len(value) == 4: minx, miny, maxx, maxy = value geom = Polygon(((minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny))) if not geom.is_valid: raise DefinitionValidationError( self, 'Parsed geometry is not valid.') ret = [{'geom': geom, 'properties': {self._ugid_key: 1}}] ret = Field.from_records(ret, uid=self.geom_uid, union=self.union, data_model=self.data_model) self._bounds = geom.bounds elif isinstance(value, GeomCabinetIterator): self._shp_key = value.key or value.path # Always yield fields. value.as_field = True ret = value elif isinstance(value, BaseGeometry): ret = [{'geom': value, 'properties': {self._ugid_key: 1}}] ret = Field.from_records(ret, uid=self.geom_uid, union=self.union, data_model=self.data_model) elif value is None: ret = value elif isinstance(value, Field): ret = value elif isinstance(value, GeometryVariable): if value.ugid is None: msg = 'Geometry variables must have an associated "UGID".' raise DefinitionValidationError(self, msg) ret = Field(geom=value, crs=value.crs) else: raise NotImplementedError(type(value)) # Convert to singular field if this is a field object. if isinstance(ret, Field): ret = tuple(self._iter_singular_fields_(ret)) return ret