def setGeospatial(self, item, geospatial, params): for k, v in six.viewitems(geospatial): if '.' in k or k[0] == '$': raise RestException('Geospatial key name %s must not contain a' ' period or begin with a dollar sign.' % k) if v: try: GeoJSON.to_instance(v, strict=True) except ValueError: raise RestException('Geospatial field with key %s does not' ' contain valid GeoJSON: %s' % (k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = dict() item[GEOSPATIAL_FIELD].update(six.viewitems(geospatial)) keys = [ k for k, v in six.viewitems(item[GEOSPATIAL_FIELD]) if v is None ] for key in keys: del item[GEOSPATIAL_FIELD][key] item = self.model('item').updateItem(item) return self._filter(item)
def intersects(self, params): """ Search for items that intersects with a GeoJSON object. :param params: parameters to the API call, including 'field' and 'geometry'. :type params: dict[str, unknown] :returns: filtered fields of the matching items with geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed API call. """ self.requireParams(('field', 'geometry'), params) try: geometry = bson.json_util.loads(params['geometry']) GeoJSON.to_instance(geometry, strict=True) except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry'" " parameter.") if params['field'][:3] == '%s.' % GEOSPATIAL_FIELD: field = params['field'].strip() else: field = '%s.%s' % (GEOSPATIAL_FIELD, params['field'].strip()) query = {field: {'$geoIntersects': {'$geometry': geometry}}} limit, offset, sort = self.getPagingParameters(params, 'lowerName') return self._find(query, limit, offset, sort)
def create(self, folder, geoJSON): try: GeoJSON.to_instance(geoJSON, strict=True) except ValueError: raise RestException('Invalid GeoJSON passed in request body.') if geoJSON['type'] == 'Feature': features = [geoJSON] elif geoJSON['type'] == 'FeatureCollection': features = geoJSON['features'] else: raise RestException('GeoJSON feature or feature collection must be ' 'passed in request body.') data = [] for feature in features: properties = feature['properties'] if 'name' not in properties: raise RestException("All GeoJSON features must contain a" " property named 'name'.") name = properties['name'] del properties['name'] if 'description' in properties: description = properties['description'] del properties['description'] else: description = '' for key in properties: if not len(key): raise RestException('Property names must be at least one' ' character long.') if '.' in key or key[0] == '$': raise RestException('The property name %s must not contain' ' a period or begin with a dollar sign.' % key) data.append({'name': name, 'description': description, 'metadata': properties, 'geometry': feature['geometry']}) user = self.getCurrentUser() items = [] for datum in data: newItem = self.model('item').createItem( folder=folder, name=datum['name'], creator=user, description=datum['description']) self.model('item').setMetadata(newItem, datum['metadata']) newItem[GEOSPATIAL_FIELD] = {'geometry': datum['geometry']} newItem = self.model('item').updateItem(newItem) items.append(newItem) return [self._filter(item) for item in items]
def create(self, folder, geoJSON): try: GeoJSON.to_instance(geoJSON, strict=True) except ValueError: raise RestException('Invalid GeoJSON passed in request body.') if geoJSON['type'] == 'Feature': features = [geoJSON] elif geoJSON['type'] == 'FeatureCollection': features = geoJSON['features'] else: raise RestException('GeoJSON feature or feature collection must be ' 'passed in request body.') data = [] for feature in features: properties = feature['properties'] if 'name' not in properties: raise RestException("All GeoJSON features must contain a" " property named 'name'.") name = properties['name'] del properties['name'] if 'description' in properties: description = properties['description'] del properties['description'] else: description = '' for key in properties: if not len(key): raise RestException('Property names must be at least one' ' character long.') if '.' in key or key[0] == '$': raise RestException('The property name %s must not contain' ' a period or begin with a dollar sign.' % key) data.append({'name': name, 'description': description, 'metadata': properties, 'geometry': feature['geometry']}) user = self.getCurrentUser() items = [] for datum in data: newItem = Item().createItem( folder=folder, name=datum['name'], creator=user, description=datum['description']) Item().setMetadata(newItem, datum['metadata']) newItem[GEOSPATIAL_FIELD] = {'geometry': datum['geometry']} newItem = Item().updateItem(newItem) items.append(newItem) return items
def _getGeometry(self, geometry): try: GeoJSON.to_instance(geometry, strict=True) if geometry['type'] != 'Point': raise ValueError return geometry except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry' parameter.")
def _getGeometry(self, params): try: geometry = bson.json_util.loads(params['geometry']) GeoJSON.to_instance(geometry, strict=True) if geometry['type'] != 'Point': raise ValueError return geometry except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry'" " parameter.")
def intersects(self, field, geometry, limit, offset, sort): try: GeoJSON.to_instance(geometry, strict=True) except (TypeError, ValueError): raise RestException( "Invalid GeoJSON passed as 'geometry' parameter.") if field[:3] != '%s.' % GEOSPATIAL_FIELD: field = '%s.%s' % (GEOSPATIAL_FIELD, field) query = {field: {'$geoIntersects': {'$geometry': geometry}}} return self._find(query, limit, offset, sort)
def within(self, field, geometry, center, radius, limit, offset, sort): if geometry is not None: try: GeoJSON.to_instance(geometry, strict=True) if geometry['type'] != 'Polygon': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry' parameter.") condition = { '$geometry': geometry } elif center is not None and radius is not None: try: radius /= self._RADIUS_OF_EARTH if radius < 0.0: raise ValueError except ValueError: raise RestException("Parameter 'radius' must be a number.") try: GeoJSON.to_instance(center, strict=True) if center['type'] != 'Point': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'center' parameter.") condition = { '$centerSphere': [center['coordinates'], radius] } else: raise RestException("Either parameter 'geometry' or both parameters" " 'center' and 'radius' are required.") if field[:3] != '%s.' % GEOSPATIAL_FIELD: field = '%s.%s' % (GEOSPATIAL_FIELD, field) query = { field: { '$geoWithin': condition } } return self._find(query, limit, offset, sort)
def update(self, request, response, id): """ Read the GeoJSON feature from the request body and update the corresponding object in the database. """ if self.readonly: abort(403) obj = self.Session.query(self.mapped_class).get(id) if obj is None: abort(404) content = request.environ['wsgi.input'].read( int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) feature = loads(content, object_hook=factory) if not isinstance(feature, Feature): abort(400) if self.before_update is not None: self.before_update(request, feature, obj) self.__copy_attributes(feature, obj) # We call flush, create the feature, and then commit. Commit expires # the session, so we create the feature before commit to avoid SELECT # queries in toFeature. self.Session.flush() feature = obj.toFeature() self.Session.commit() response.status = 201 return feature
def create(self, request, response): """ Read the GeoJSON feature collection from the request body and create new objects in the database. """ if self.readonly: response.status_code = 403 return content = request.environ['wsgi.input'].read(int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) collection = loads(content, object_hook=factory) if not isinstance(collection, FeatureCollection): response.status_code = 400 return objects = [] for feature in collection.features: create = False obj = None if self.before_create is not None: self.before_create(request, feature) if isinstance(feature.id, int): obj = self.Session.query(self.mapped_class).get(feature.id) if obj is None: obj = self.mapped_class() obj.geometry = asShape(feature.geometry) create = True for key in feature.properties: obj[key] = feature.properties[key] if create: self.Session.save(obj) objects.append(obj) self.Session.commit() response.status_code = 201 if len(objects) > 0: return dumps(FeatureCollection([o.toFeature() for o in objects])) return
def extract_geojson_obj(self, json_dict): """Extract geojson from a defined json key""" try: if isinstance(json_dict, dict): val_inst = GeoJSON.to_instance(json_dict) return val_inst except KeyError as e: raise Exception(e)
def intersects(self, field, geometry, limit, offset, sort): try: GeoJSON.to_instance(geometry, strict=True) except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry' parameter.") if field[:3] != '%s.' % GEOSPATIAL_FIELD: field = '%s.%s' % (GEOSPATIAL_FIELD, field) query = { field: { '$geoIntersects': { '$geometry': geometry } } } return self._find(query, limit, offset, sort)
def setGeospatial(self, item, params): """ Set geospatial data on an item. :param item: item on which to set geospatial data. :type item: dict[str, unknown] :param params: parameters to the API call, unused. :type params: dict[str, unknown] :return: filtered fields of the item with geospatial data appended to its 'geo' field. :rtype : dict[str, unknown] :raise RestException: on malformed, forbidden, or unauthorized API call. """ try: geospatial = json.load(cherrypy.request.body) except ValueError: raise RestException('Invalid JSON passed in request body.') for k, v in geospatial.items(): if '.' in k or k[0] == '$': raise RestException('Geospatial key name {} must not contain a' ' period or begin with a dollar sign.' .format(k)) if v: try: GeoJSON.to_instance(v, strict=True) except ValueError: raise RestException('Geospatial field with key {} does not' ' contain valid GeoJSON: {}' .format(k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = dict() item[GEOSPATIAL_FIELD].update(geospatial.items()) keys = [k for k, v in item[GEOSPATIAL_FIELD].iteritems() if v is None] for key in keys: del item[GEOSPATIAL_FIELD][key] item = self.model('item').updateItem(item) return self._filter(item)
def setGeospatial(self, item, params): """ Set geospatial data on an item. :param item: item on which to set geospatial data. :type item: dict[str, unknown] :param params: parameters to the API call, unused. :type params: dict[str, unknown] :return: filtered fields of the item with geospatial data appended to its 'geo' field. :rtype : dict[str, unknown] :raise RestException: on malformed, forbidden, or unauthorized API call. """ try: geospatial = json.load(cherrypy.request.body) except ValueError: raise RestException('Invalid JSON passed in request body.') for k, v in geospatial.items(): if '.' in k or k[0] == '$': raise RestException( 'Geospatial key name {} must not contain a' ' period or begin with a dollar sign.'.format(k)) if v: try: GeoJSON.to_instance(v, strict=True) except ValueError: raise RestException('Geospatial field with key {} does not' ' contain valid GeoJSON: {}'.format( k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = dict() item[GEOSPATIAL_FIELD].update(geospatial.items()) keys = [k for k, v in item[GEOSPATIAL_FIELD].iteritems() if v is None] for key in keys: del item[GEOSPATIAL_FIELD][key] item = self.model('item').updateItem(item) return self._filter(item)
def setGeospatial(self, item, params): """ Set geospatial data on an item. :param item: item on which to set geospatial data. :type item: dict[str, unknown] :param params: parameters to the API call, unused. :type params: dict[str, unknown] :returns: filtered fields of the item with geospatial data appended to its 'geo' field. :rtype : dict[str, unknown] :raise RestException: on malformed, forbidden, or unauthorized API call. """ geospatial = self.getBodyJson() for k, v in six.viewitems(geospatial): if '.' in k or k[0] == '$': raise RestException('Geospatial key name %s must not contain a' ' period or begin with a dollar sign.' % k) if v: try: GeoJSON.to_instance(v, strict=True) except ValueError: raise RestException('Geospatial field with key %s does not' ' contain valid GeoJSON: %s' % (k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = dict() item[GEOSPATIAL_FIELD].update(six.viewitems(geospatial)) keys = [ k for k, v in six.viewitems(item[GEOSPATIAL_FIELD]) if v is None ] for key in keys: del item[GEOSPATIAL_FIELD][key] item = self.model('item').updateItem(item) return self._filter(item)
def setGeospatial(self, item, params): """ Set geospatial data on an item. :param item: item on which to set geospatial data. :type item: dict[str, unknown] :param params: parameters to the API call, unused. :type params: dict[str, unknown] :returns: filtered fields of the item with geospatial data appended to its 'geo' field. :rtype : dict[str, unknown] :raise RestException: on malformed, forbidden, or unauthorized API call. """ geospatial = self.getBodyJson() for k, v in six.viewitems(geospatial): if '.' in k or k[0] == '$': raise RestException('Geospatial key name %s must not contain a' ' period or begin with a dollar sign.' % k) if v: try: GeoJSON.to_instance(v, strict=True) except ValueError: raise RestException('Geospatial field with key %s does not' ' contain valid GeoJSON: %s' % (k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = dict() item[GEOSPATIAL_FIELD].update(six.viewitems(geospatial)) keys = [k for k, v in six.viewitems(item[GEOSPATIAL_FIELD]) if v is None] for key in keys: del item[GEOSPATIAL_FIELD][key] item = self.model('item').updateItem(item) return self._filter(item)
def setGeospatial(self, item, geospatial): for k, v in six.viewitems(geospatial): if '.' in k or k[0] == '$': raise RestException('Geospatial key name %s must not contain a' ' period or begin with a dollar sign.' % k) if v: try: GeoJSON.to_instance(v, strict=True) except (ValueError, TypeError): raise RestException('Geospatial field with key %s does not' ' contain valid GeoJSON: %s' % (k, v)) if GEOSPATIAL_FIELD not in item: item[GEOSPATIAL_FIELD] = {} item[GEOSPATIAL_FIELD].update(six.viewitems(geospatial)) keys = [k for k, v in six.viewitems(item[GEOSPATIAL_FIELD]) if v is None] for key in keys: del item[GEOSPATIAL_FIELD][key] return Item().updateItem(item)
def _as_geojson_instance(cls, geojson_dict): try: geojson = GeoJSON.to_instance(geojson_dict, strict=True) except (TypeError, KeyError, UnicodeEncodeError) as ex: raise ValueError( "geometry not recognized as valid GeoJSON ({}): {}".format( str(ex), geojson_dict)) # Shapely cannot handle GeoJSON Features or FeatureCollections if isinstance(geojson, Feature): geojson = geojson.geometry elif isinstance(geojson, FeatureCollection): features = [] for feature in geojson.features: try: features.append( GeoJSON.to_instance(feature, strict=True).geometry) except (TypeError, KeyError, UnicodeEncodeError) as ex: raise ValueError( "feature in FeatureCollection not recognized as valid ({}): {}" .format(str(ex), feature)) geojson = GeometryCollection(features) return geojson
def intersects(self, params): """ Search for items that intersects with a GeoJSON object. :param params: parameters to the API call, including 'field' and 'geometry'. :type params: dict[str, unknown] :returns: filtered fields of the matching items with geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed API call. """ self.requireParams(('field', 'geometry'), params) try: geometry = bson.json_util.loads(params['geometry']) GeoJSON.to_instance(geometry, strict=True) except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry'" " parameter.") if params['field'][:3] == '{}.'.format(GEOSPATIAL_FIELD): field = params['field'].strip() else: field = '{}.{}'.format(GEOSPATIAL_FIELD, params['field'].strip()) query = { field: { '$geoIntersects': { '$geometry': geometry } } } limit, offset, sort = self.getPagingParameters(params, 'lowerName') return self._find(query, limit, offset, sort)
def create_geom_filter(request, mapped_class, **kwargs): """Create MapFish geometry filter based on the request params. Either a box or within or geometry filter, depending on the request params. Additional named arguments are passed to the spatial filter.""" tolerance = 0 if 'tolerance' in request.params: tolerance = float(request.params['tolerance']) epsg = None if 'epsg' in request.params: epsg = int(request.params['epsg']) box = None if 'bbox' in request.params: box = request.params['bbox'] geometry = None if box is not None: box = map(float, box.split(',')) geometry = Polygon( ((box[0], box[1]), (box[0], box[3]), (box[2], box[3]), (box[2], box[1]), (box[0], box[1]))) elif 'lon' and 'lat' in request.params: geometry = Point(float(request.params['lon']), float(request.params['lat'])) elif 'geometry' in request.params: factory = lambda ob: GeoJSON.to_instance(ob) geometry = loads(request.params['geometry'], object_hook=factory) geometry = asShape(geometry) if geometry is None: return None geom_column = mapped_class.geometry_column() epsg = geom_column.type.srid if epsg is None else epsg if epsg != geom_column.type.srid: geom_column = functions.transform(geom_column, epsg) wkb_geometry = WKBSpatialElement(buffer(geometry.wkb), epsg) if 'additional_params' in kwargs: return functions._within_distance(geom_column, wkb_geometry, tolerance, kwargs['additional_params']) else: return functions._within_distance(geom_column, wkb_geometry, tolerance)
def create_geom_filter(request, mapped_class, **kwargs): """Create MapFish geometry filter based on the request params. Either a box or within or geometry filter, depending on the request params. Additional named arguments are passed to the spatial filter.""" tolerance = 0 if 'tolerance' in request.params: tolerance = float(request.params['tolerance']) epsg = None if 'epsg' in request.params: epsg = int(request.params['epsg']) box = None if 'bbox' in request.params: box = request.params['bbox'] geometry = None if box is not None: box = map(float, box.split(',')) geometry = Polygon(((box[0], box[1]), (box[0], box[3]), (box[2], box[3]), (box[2], box[1]), (box[0], box[1]))) elif 'lon' and 'lat' in request.params: geometry = Point(float(request.params['lon']), float(request.params['lat'])) elif 'geometry' in request.params: factory = lambda ob: GeoJSON.to_instance(ob) geometry = loads(request.params['geometry'], object_hook=factory) geometry = asShape(geometry) if geometry is None: return None geom_column = mapped_class.geometry_column() epsg = geom_column.type.srid if epsg is None else epsg if epsg != geom_column.type.srid: geom_column = functions.transform(geom_column, epsg) wkb_geometry = WKBSpatialElement(buffer(geometry.wkb), epsg) if 'additional_params' in kwargs: return functions._within_distance(geom_column, wkb_geometry, tolerance, kwargs['additional_params']) else: return functions._within_distance(geom_column, wkb_geometry, tolerance)
def to_sql_expr(self): if self.type == self.BOX: geometry = self.__box_to_geometry() if self.type == self.WITHIN: geometry = Point(self.values['lon'], self.values['lat']) if self.type == self.GEOMETRY: factory = lambda ob: GeoJSON.to_instance(ob) geometry = loads(self.values['geometry'], object_hook=factory) geometry = asShape(geometry) if self.epsg != self.geom_column.type.srid: geom_column = func.transform(self.geom_column, self.epsg) else: geom_column = self.geom_column tolerance = self.values['tolerance'] pg_geometry = func.geomfromtext(geometry.wkt, self.epsg) return and_(func.expand(pg_geometry, tolerance).op('&&')(geom_column), func.distance(geom_column, pg_geometry) <= tolerance)
def create(self, request, response, execute=True): """ Read the GeoJSON feature collection from the request body and create new objects in the database. """ if self.readonly: abort(403) content = request.environ['wsgi.input'].read( int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) collection = loads(content, object_hook=factory) if not isinstance(collection, FeatureCollection): abort(400) objects = [] for feature in collection.features: create = False obj = None if feature.id is not None: obj = self.Session.query(self.mapped_class).get(feature.id) if self.before_create is not None: self.before_create(request, feature, obj) if obj is None: obj = self.mapped_class() create = True self.__copy_attributes(feature, obj) if create: self.Session.add(obj) objects.append(obj) # We call flush, create the feature collection, and then commit. Commit # expires the session, so we create the feature collection before # commit to avoid SELECT queries in toFeature. if execute: self.Session.flush() collection = None if len(objects) > 0: collection = FeatureCollection([o.toFeature() for o in objects]) if execute: self.Session.commit() response.status = 201 return collection
def create(self, request, response, execute=True): """ Read the GeoJSON feature collection from the request body and create new objects in the database. """ if self.readonly: abort(403) content = request.environ['wsgi.input'].read(int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) collection = loads(content, object_hook=factory) if not isinstance(collection, FeatureCollection): abort(400) objects = [] for feature in collection.features: create = False obj = None if feature.id is not None: obj = self.Session.query(self.mapped_class).get(feature.id) if self.before_create is not None: self.before_create(request, feature, obj) if obj is None: obj = self.mapped_class() create = True self.__copy_attributes(feature, obj) if create: self.Session.add(obj) objects.append(obj) # We call flush, create the feature collection, and then commit. Commit # expires the session, so we create the feature collection before # commit to avoid SELECT queries in toFeature. if execute: self.Session.flush() collection = None if len(objects) > 0: collection = FeatureCollection([o.toFeature() for o in objects]) if execute: self.Session.commit() response.status = 201 return collection
def update(self, request, response, id): """ Read the GeoJSON feature from the request body and update the corresponding object in the database. """ if self.readonly: response.status_code = 403 return obj = self.Session.query(self.mapped_class).get(id) if obj is None: response.status_code = 404 return content = request.environ['wsgi.input'].read(int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) feature = loads(content, object_hook=factory) if not isinstance(feature, Feature): response.status_code = 400 return response if self.before_update is not None: self.before_update(request, feature) obj.geometry = asShape(feature.geometry) for key in feature.properties: obj[key] = feature.properties[key] self.Session.commit() response.status_code = 201 return dumps(obj.toFeature())
def update(self, request, response, id): """ Read the GeoJSON feature from the request body and update the corresponding object in the database. """ if self.readonly: abort(403) obj = self.Session.query(self.mapped_class).get(id) if obj is None: abort(404) content = request.environ['wsgi.input'].read(int(request.environ['CONTENT_LENGTH'])) factory = lambda ob: GeoJSON.to_instance(ob) feature = loads(content, object_hook=factory) if not isinstance(feature, Feature): abort(400) if self.before_update is not None: self.before_update(request, feature, obj) self.__copy_attributes(feature, obj) # We call flush, create the feature, and then commit. Commit expires # the session, so we create the feature before commit to avoid SELECT # queries in toFeature. self.Session.flush() feature = obj.toFeature() self.Session.commit() response.status = 201 return feature
def create(self, params): """ Create new items from a GeoJSON feature or feature collection. All GeoJSON features must contain a property named 'name' from which the name of each created item is taken. :param params: parameters to the API call, including 'folderId' and 'geoJSON'. :type params: dict[str, unknown] :returns: filtered fields of the created items with properties appended to the 'meta' field and geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed, forbidden, or unauthorized API call. """ self.requireParams(('folderId', 'geoJSON'), params) try: geospatial = bson.json_util.loads(params['geoJSON']) GeoJSON.to_instance(geospatial, strict=True) except ValueError: raise RestException('Invalid GeoJSON passed in request body.') if geospatial['type'] == 'Feature': features = [geospatial] elif geospatial['type'] == 'FeatureCollection': features = geospatial['features'] else: raise RestException('GeoJSON feature or feature collection must be ' 'passed in request body.') data = [] for feature in features: properties = feature['properties'] if 'name' not in properties: raise RestException("All GeoJSON features must contain a" " property named 'name'.") name = properties['name'] del properties['name'] if 'description' in properties: description = properties['description'] del properties['description'] else: description = '' for key in properties: if not len(key): raise RestException('Property names must be at least one' ' character long.') if '.' in key or key[0] == '$': raise RestException('The property name {} must not contain' ' a period or begin with a dollar sign.' .format(key)) data.append({'name': name, 'description': description, 'metadata': properties, 'geometry': feature['geometry']}) user = self.getCurrentUser() folder = self.model('folder').load( id=params['folderId'], user=user, level=AccessType.WRITE, exc=True) items = [] for datum in data: newItem = self.model('item').createItem( folder=folder, name=datum['name'], creator=user, description=datum['description']) self.model('item').setMetadata(newItem, datum['metadata']) newItem[GEOSPATIAL_FIELD] = {'geometry': datum['geometry']} newItem = self.model('item').updateItem(newItem) items.append(newItem) return [self._filter(item) for item in items]
def within(self, params): """ Search for items that are entirely within either a GeoJSON polygon or a circular region. Either parameter 'geometry' or both parameters 'center' and 'radius' are required. :param params: parameters to the API call, including 'field' and either 'geometry' or both 'center' and 'radius'. :type params: dict[str, unknown] :returns: filtered fields of the matching items with geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed API call. """ self.requireParams(('field',), params) if 'geometry' in params: try: geometry = bson.json_util.loads(params['geometry']) GeoJSON.to_instance(geometry, strict=True) if geometry['type'] != 'Polygon': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry'" " parameter.") condition = { '$geometry': geometry } elif 'center' in params and 'radius' in params: try: radius = float(params['radius']) / self._RADIUS_OF_EARTH if radius < 0.0: raise ValueError except ValueError: raise RestException("Parameter 'radius' must be a number.") try: center = bson.json_util.loads(params['center']) GeoJSON.to_instance(center, strict=True) if center['type'] != 'Point': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'center'" " parameter.") condition = { '$centerSphere': [center['coordinates'], radius] } else: raise RestException("Either parameter 'geometry' or both parameters" " 'center' and 'radius' are required.") if params['field'][:3] == '{}.'.format(GEOSPATIAL_FIELD): field = params['field'].strip() else: field = '{}.{}'.format(GEOSPATIAL_FIELD, params['field'].strip()) limit, offset, sort = self.getPagingParameters(params, 'lowerName') query = { field: { '$geoWithin': condition } } return self._find(query, limit, offset, sort)
def create(self, params): """ Create new items from a GeoJSON feature or feature collection. All GeoJSON features must contain a property named 'name' from which the name of each created item is taken. :param params: parameters to the API call, including 'folderId' and 'geoJSON'. :type params: dict[str, unknown] :returns: filtered fields of the created items with properties appended to the 'meta' field and geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed, forbidden, or unauthorized API call. """ self.requireParams(('folderId', 'geoJSON'), params) try: geospatial = bson.json_util.loads(params['geoJSON']) GeoJSON.to_instance(geospatial, strict=True) except ValueError: raise RestException('Invalid GeoJSON passed in request body.') if geospatial['type'] == 'Feature': features = [geospatial] elif geospatial['type'] == 'FeatureCollection': features = geospatial['features'] else: raise RestException( 'GeoJSON feature or feature collection must be ' 'passed in request body.') data = [] for feature in features: properties = feature['properties'] if 'name' not in properties: raise RestException("All GeoJSON features must contain a" " property named 'name'.") name = properties['name'] del properties['name'] if 'description' in properties: description = properties['description'] del properties['description'] else: description = '' for key in properties: if not len(key): raise RestException('Property names must be at least one' ' character long.') if '.' in key or key[0] == '$': raise RestException('The property name %s must not contain' ' a period or begin with a dollar' ' sign.' % key) data.append({ 'name': name, 'description': description, 'metadata': properties, 'geometry': feature['geometry'] }) user = self.getCurrentUser() folder = self.model('folder').load(id=params['folderId'], user=user, level=AccessType.WRITE, exc=True) items = [] for datum in data: newItem = self.model('item').createItem( folder=folder, name=datum['name'], creator=user, description=datum['description']) self.model('item').setMetadata(newItem, datum['metadata']) newItem[GEOSPATIAL_FIELD] = {'geometry': datum['geometry']} newItem = self.model('item').updateItem(newItem) items.append(newItem) return [self._filter(item) for item in items]
def within(self, params): """ Search for items that are entirely within either a GeoJSON polygon or a circular region. Either parameter 'geometry' or both parameters 'center' and 'radius' are required. :param params: parameters to the API call, including 'field' and either 'geometry' or both 'center' and 'radius'. :type params: dict[str, unknown] :returns: filtered fields of the matching items with geospatial data appended to the 'geo' field of each item. :rtype: list[dict[str, unknown]] :raise RestException: on malformed API call. """ self.requireParams(('field', ), params) if 'geometry' in params: try: geometry = bson.json_util.loads(params['geometry']) GeoJSON.to_instance(geometry, strict=True) if geometry['type'] != 'Polygon': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'geometry'" " parameter.") condition = {'$geometry': geometry} elif 'center' in params and 'radius' in params: try: radius = float(params['radius']) / self._RADIUS_OF_EARTH if radius < 0.0: raise ValueError except ValueError: raise RestException("Parameter 'radius' must be a number.") try: center = bson.json_util.loads(params['center']) GeoJSON.to_instance(center, strict=True) if center['type'] != 'Point': raise ValueError except (TypeError, ValueError): raise RestException("Invalid GeoJSON passed as 'center'" " parameter.") condition = {'$centerSphere': [center['coordinates'], radius]} else: raise RestException( "Either parameter 'geometry' or both parameters" " 'center' and 'radius' are required.") if params['field'][:3] == '%s.' % GEOSPATIAL_FIELD: field = params['field'].strip() else: field = '%s.%s' % (GEOSPATIAL_FIELD, params['field'].strip()) limit, offset, sort = self.getPagingParameters(params, 'lowerName') query = {field: {'$geoWithin': condition}} return self._find(query, limit, offset, sort)