示例#1
0
    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)
示例#2
0
    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)
示例#3
0
    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]
示例#4
0
    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
示例#5
0
    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.")
示例#6
0
    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.")
示例#7
0
    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.")
示例#8
0
    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.")
示例#9
0
    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)
示例#10
0
    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)
示例#11
0
    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)
示例#12
0
 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
示例#13
0
 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
示例#14
0
 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)
示例#15
0
    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)
示例#16
0
    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)
示例#17
0
    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)
示例#18
0
    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)
示例#19
0
    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)
示例#20
0
    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
示例#22
0
    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)
示例#23
0
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)
示例#24
0
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)
示例#25
0
    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)
示例#26
0
 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
示例#27
0
 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
示例#28
0
 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())
示例#29
0
 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
示例#30
0
    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]
示例#31
0
    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)
示例#32
0
    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]
示例#33
0
    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)