Esempio n. 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)
Esempio n. 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)
Esempio n. 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 = 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
Esempio n. 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 = 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]
Esempio n. 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.")
Esempio n. 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.")
Esempio n. 7
0
    def load_svg(self, path):
        with open(path) as file:
            svg = SVG(file.read())
            self.svg = svg
            self.geojson = GeoJSON(self.svg)
            self.layer_output = list(self.svg.get_layers())
            self.update_result()

        self.ui.load_preview(path)
        layers = self.svg.get_layers()
        self.ui.load_layers(layers)
Esempio n. 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.")
Esempio n. 9
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.")
Esempio n. 10
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)
def create_route(data):
    path = data.get('points', [])
    geo = GeoJSON()
    for i, _ in enumerate(path[:-1]):
        if path[i + 1] is None:
            path[i + 1] = path[i]
        elif path[i] is not None:
            route, _ = router.find_route(path[i], path[i + 1])
            if route:
                start, end = route[0], route[-1]
                geo.add_point(lon=start[0],
                              lat=start[1],
                              props={
                                  'title':
                                  path[i]['tags'].get('name', path[i]['id'])
                              })
                geo.add_point(lon=end[0],
                              lat=end[1],
                              props={
                                  'title':
                                  path[i + 1]['tags'].get(
                                      'name', path[i + 1]['id'])
                              })
                geo.add_line_string(route, {"style": {"color": next(color)}})

    return geo.data
Esempio n. 12
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)
Esempio n. 13
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)
Esempio n. 14
0
    def update_features(
        self,
        features: GeoJSON,  # must be a feature collection
        add_tags: Optional[List[str]] = None,
        remove_tags: Optional[List[str]] = None,
    ) -> GeoJSON:
        """
        Update GeoJSON features in this space.

        :param features: A JSON object describing one or more features to
            modify.
        :param add_tags: A list of strings describing tags to be added to
            the features.
        :param remove_tags: A list of strings describing tags to be removed
            from the features.
        :return: A GeoJSON representing a feature collection.
        """
        space_id = self.info["id"]
        res = self.api.post_space_features(
            space_id=space_id,
            data=features,
            addTags=add_tags,
            removeTags=remove_tags,
        )
        return GeoJSON(res)
Esempio n. 15
0
    def update_feature(
        self,
        feature_id: str,
        data: dict,
        add_tags: Optional[List[str]] = None,
        remove_tags: Optional[List[str]] = None,
    ) -> GeoJSON:
        """
        Update one GeoJSON feature with given ID in this space.

        :param feature_id: A string with the ID of the feature to be modified.
        :param data: A JSON object describing the feature to be changed.
        :param add_tags: A list of strings describing tags to be added to
            the feature.
        :param remove_tags: A list of strings describing tags to be removed
            from the feature.
        :return: A GeoJSON representing a feature.
        """
        res = self.api.patch_space_feature(
            space_id=self.info["id"],
            feature_id=feature_id,
            data=data,
            addTags=add_tags,
            removeTags=remove_tags,
        )
        return GeoJSON(res)
Esempio n. 16
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
Esempio n. 17
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
Esempio n. 18
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)
Esempio n. 19
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)
Esempio n. 20
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)
Esempio n. 21
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)
Esempio n. 22
0
    def add_features_csv(
        self,
        path: str,
        lon_col: str,
        lat_col: str,
        id_col: str,
        alt_col: str = "",
        delimiter: str = ",",
    ):
        """
        Add features in space from a csv file.

        :param path: Path to csv file.
        :param lon_col: Name of the column for longitude coordinates.
        :param lat_col: Name of the column for latitude coordinates.
        :param id_col: Name of the column for feature id's.
        :param alt_col: Name of the column for altitude, if not provided
            altitudes with default value 0.0 will be added.
        :param delimiter: delimiter which should be used to process the csv file.
        :raises Exception: If values of params `lat_col`, `lon_col`, `id_col`
             do not match with column names in csv file.
        """
        feature: dict = {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [0, 0, 0]
            },
            "properties": {},
        }
        with open(path) as f:
            input_file = csv.DictReader(f, delimiter=delimiter)
            columns = input_file.fieldnames
            if (lon_col not in columns  # type: ignore
                    or lat_col not in columns  # type: ignore
                    or id_col not in columns  # type: ignore
                    or
                (alt_col not in columns if alt_col else False)  # type: ignore
                ):
                raise Exception(
                    "The longitude, latitude coordinates and id column name "
                    "should match with `lon_col`, `lat_col`,"
                    " `id_col` and `alt_col` parameter value")
            for row in input_file:
                feature["geometry"]["coordinates"] = [
                    row[lon_col],
                    row[lat_col],
                    row[alt_col] if alt_col else 0.0,
                ]
                for field in columns:  # type: ignore
                    if (field != lon_col and field != lat_col
                            and field != id_col):
                        feature["properties"][field] = row[field]
                self.add_feature(feature_id=row[id_col], data=GeoJSON(feature))
Esempio n. 23
0
    def get_feature(self, feature_id: str) -> GeoJSON:
        """
        Retrieve one GeoJSON feature with given ID from this space.

        :param feature_id: Feature id which is to fetched.
        :return: A GeoJSON representing a feature with the specified feature
             ID inside the space.
        """
        res = self.api.get_space_feature(space_id=self.info["id"],
                                         feature_id=feature_id)
        return GeoJSON(res)
Esempio n. 24
0
    def get_features(self, feature_ids: List[str]) -> GeoJSON:
        """
        Retrieve one GeoJSON feature with given ID from this space.

        :param feature_ids: A list of feature_ids.
        :return: A feature collection with all features inside the specified
            space.
        """
        res = self.api.get_space_features(space_id=self.info["id"],
                                          feature_ids=feature_ids)
        return GeoJSON(res)
Esempio n. 25
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)
Esempio n. 26
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)
Esempio n. 27
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
Esempio n. 29
0
    def add_features(
        self,
        features: GeoJSON,
        add_tags: Optional[List[str]] = None,
        remove_tags: Optional[List[str]] = None,
        features_size: int = 2000,
        chunk_size: int = 1,
    ) -> GeoJSON:
        """
        Add GeoJSON features to this space.

        As API has a limitation on the size of features, features are divided into chunks,
        and multiple processes will process those chunks.
        Each chunk has a number of features based on the value of ``features_size``.
        Each process handles chunks based on the value of ``chunk_size``.

        :param features: A JSON object describing one or more features to add.
        :param add_tags: A list of strings describing tags to be added to
            the features.
        :param remove_tags: A list of strings describing tags to be removed
            from the features.
        :param features_size: An int representing a number of features to upload at a
            time.
        :param chunk_size: Number of chunks each process to handle. The default value is
            1, for a large number of features please use `chunk_size` greater than 1
            to get better results in terms of performance.
        :return: A GeoJSON representing a feature collection.
        """
        space_id = self.info["id"]
        total = 0
        if len(features["features"]) > features_size:
            groups = grouper(features_size, features["features"])
            part_func = partial(
                self._upload_features,
                add_tags=add_tags,
                remove_tags=remove_tags,
            )
            with concurrent.futures.ProcessPoolExecutor() as executor:
                for ft in executor.map(part_func, groups,
                                       chunksize=chunk_size):
                    logger.info(f"features processed: {ft}")
                    total += ft
            logger.info(f"{total} features are uploaded on space: {space_id}")
        else:
            res = self.api.put_space_features(
                space_id=space_id,
                data=features,
                addTags=add_tags,
                removeTags=remove_tags,
            )
            return GeoJSON(res)
Esempio n. 30
0
def main(start, end, coords, coorde):
    router = Router('./map.osm')
    print('Would you like to search by name or by coordinates?')
    print('(1) Name')
    print('(2) Coordinates')
    index = click.prompt('', type=int)
    start_point, end_point = None, None
    if index == 1:
        if not start:
            start = click.prompt('What is your starting point?')
        start_point = router.find_point_by_name_cli(start)
        if not start_point:
            return print('Starting point not set')
        if not end:
            end = click.prompt('What is your destination?')
        end_point = router.find_point_by_name_cli(end)
        if not end_point:
            return print('Destination not set')
    elif index == 2:
        if not coords:
            coords = get_float_tuple('What is your starting point? [format="lat lon"]')
        start_point = router.find_point_by_coords(dict(lat=coords[0], lon=coords[1]))
        if not coorde:
            coorde = get_float_tuple('What is your destination? [format="lat lon"]').split(' ')
        end_point = router.find_point_by_coords(dict(lat=coorde[1], lon=coorde[1]))
    else:
        print('Invalid input')
        return
    route, _ = router.find_route(start_point, end_point)
    if route:
        print("Route found")
        geo = GeoJSON()
        geo.add_line_string(route)
        geo.add_point(lon=route[0][0], lat=route[0][1], props={
            "title": "Start"
        })
        geo.add_point(lon=route[-1][0], lat=route[-1][1], props={
            "title": "End"
        })
        js_file = ''
        with open('./map.template.js',encoding='utf-8') as f:
            js_file = Template(f.read()).safe_substitute(geojson=geo.data)
            f.close()
        with open('./map_client/index.js', 'w', encoding='utf-8') as f:
            f.write(js_file)
            f.close()

        webbrowser.open_new_tab(Path('./map_client/index.html').resolve().as_uri())
Esempio n. 31
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)
Esempio n. 32
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)
Esempio n. 33
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)
Esempio n. 34
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)
Esempio n. 35
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
Esempio n. 36
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
Esempio n. 37
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
Esempio n. 38
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())
Esempio n. 39
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]
Esempio n. 40
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)
Esempio n. 41
0
def bbdb2geo(db, geocoder=None):
    return GeoJSON(FeatureCollection(list(features(db, geocoder))))
Esempio n. 42
0
    input_text = ""
    input_path = "json/" + input_file_2 + ".json"
    file_size = os.path.getsize(input_path)
    progress = Progress(file_size)
    progress.progressBar("File2 Loading")
    with open(input_path, 'r') as f:
        line = f.readline()
        while line:
            progress.tick( len(line) )
            input_text += line
            line = f.readline()

    dataset_2 = json.loads(input_text)

    geojson = GeoJSON()
    output = geojson.getFormat()

    progress = Progress( len(dataset_1) )
    progress.progressBar("Conversioning")
    for i in range( len(dataset_1) ):
        if dataset_1[i][5] > threshold_1 and dataset_2[i][5] > threshold_2:
            red = int( ( log(dataset_1[i][5]) / log(max_1) ) * 255 )
            blue = int( ( log(dataset_2[i][5]) / log(max_2) ) * 255 )
            color = "rgba(" + str(red) + ", 0, " + str(blue) + ", 0.5)"
            output["features"].append( geojson.getFeature("Polygon",
                                                          [[ [dataset_1[i][2], dataset_1[i][1]],
                                                             [dataset_1[i][2], dataset_1[i][3]],
                                                             [dataset_1[i][4], dataset_1[i][3]],
                                                             [dataset_1[i][4], dataset_1[i][1]] ]],
                                                             { "color": color,
Esempio n. 43
0
    data_source = input()

    input_text = ""
    input_path = 'json/' + input_file + '.json'

    file_size = os.path.getsize(input_path)
    progress = Progress(file_size)
    progress.progressBar("File Loading")
    with open(input_path, 'r') as f:
        line = f.readline()
        while line:
            progress.tick( len(line) )
            input_text += line
            line = f.readline()

    geojson = GeoJSON()
    output = geojson.getFormat()

    dataset = json.loads(input_text)
    progress = Progress( len(dataset) )
    progress.progressBar("Conversioning")
    for data in dataset:
        if data[5] > threshold:
            alpha = log(data[5]) / log(max_value)
            if (data_source == "twitter"):
                color = "rgba(255, 0, 0, " + str(alpha) + ")"
            elif (data_source == "flickr"):
                color = "rgba(0, 0, 255, " + str(alpha) + ")"

            output["features"].append( geojson.getFeature("Polygon",
                                                          [[ [data[2], data[1]], [data[2], data[3]],
Esempio n. 44
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)
Esempio n. 45
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]
Esempio n. 46
0
class Controller:
    def __init__(self):
        self.ui = UserInterface(self)
        self.svg = None
        self.geojson = None
        self.layer_output = []
        self.bounds = [0.0, 0.0, 0.0, 0.0]
        self.rotation = 0.0
        self.last_click = None
        self.ui.run()

    def load_svg(self, path):
        with open(path) as file:
            svg = SVG(file.read())
            self.svg = svg
            self.geojson = GeoJSON(self.svg)
            self.layer_output = list(self.svg.get_layers())
            self.update_result()

        self.ui.load_preview(path)
        layers = self.svg.get_layers()
        self.ui.load_layers(layers)

    def set_layer_output(self, layer: str, enable: bool):
        if enable:
            self.layer_output.append(layer)
        else:
            self.layer_output.remove(layer)

    def update_boundaries(self, direction: chr, value, update_ui=True):
        if direction == 'N':
            self.bounds[0] = value
        elif direction == 'S':
            self.bounds[1] = value
        elif direction == 'E':
            self.bounds[2] = value
        elif direction == 'W':
            self.bounds[3] = value

        if update_ui:
            self.ui.set_boundaries(self.bounds)
        self.update_result()

    def update_rotation(self, rotation):
        self.rotation = rotation
        self.update_result()

    def update_result(self):
        print(
            f'Updating to match the bounds {self.bounds} with {self.rotation}º of rotation.'
        )
        geojson = self.geojson.calculate(self.layer_output, self.bounds,
                                         self.rotation)
        self.ui.set_output(geojson)
        self.ui.draw_polygons(self.geojson.polygons)

    def record_click(self, lon: float, lat: float):
        self.last_click = lon, lat
        self.ui.set_last_click(lon, lat)

    def replace_lim(self, direction: chr):
        if direction in ('N', 'S'):
            self.update_boundaries(direction, self.last_click[1])
        else:
            self.update_boundaries(direction, self.last_click[0])