Beispiel #1
0
 def test_is_feature_collection(self):
     self.assertTrue(GeoJSON.is_feature_collection(dict(type='FeatureCollection',
                                                        features=[dict(type='Feature',
                                                                       geometry=dict(type='Point',
                                                                                     coordinates=[
                                                                                         2.13,
                                                                                         42.2]))])))
     self.assertFalse(GeoJSON.is_feature_collection(dict(type='Point', coordinates=[2.13, 42.2])))
Beispiel #2
0
 def test_get_type_name(self):
     self.assertEqual('Feature',
                      GeoJSON.get_type_name(dict(type='Feature')))
     self.assertEqual(None,
                      GeoJSON.get_type_name(dict(pype='Feature')))
     self.assertEqual(None,
                      GeoJSON.get_type_name(dict()))
     self.assertEqual(None,
                      GeoJSON.get_type_name(17))
Beispiel #3
0
def get_time_series_for_feature_collection(ctx: ServiceContext,
                                           ds_name: str,
                                           var_name: str,
                                           feature_collection: Dict,
                                           start_date: np.datetime64 = None,
                                           end_date: np.datetime64 = None,
                                           include_count: bool = False,
                                           include_stdev: bool = False,
                                           max_valids: int = None) -> Dict:
    """
    Get the time-series for the geometries of a given *feature_collection*.

    :param ctx: Service context object
    :param ds_name: The dataset identifier.
    :param var_name: The variable name.
    :param feature_collection: The feature collection.
    :param start_date: An optional start date.
    :param end_date: An optional end date.
    :param include_count: Whether to include the valid number of observations in the result.
    :param include_stdev: Whether to include the standard deviation in the result.
    :param max_valids: Optional number of valid points.
           If it is None (default), also missing values are returned as NaN;
           if it is -1 only valid values are returned;
           if it is a positive integer, the most recent valid values are returned.
    :return: Time-series data structure.
    """
    dataset = ctx.get_time_series_dataset(ds_name, var_name=var_name)
    features = GeoJSON.get_feature_collection_features(feature_collection)
    if features is None:
        raise ServiceBadRequestError("Invalid GeoJSON feature collection")
    shapes = []
    for feature in features:
        geometry = GeoJSON.get_feature_geometry(feature)
        try:
            geometry = shapely.geometry.shape(geometry)
        except (TypeError, ValueError) as e:
            raise ServiceBadRequestError(
                "Invalid GeoJSON feature collection") from e
        shapes.append(geometry)
    with measure_time() as time_result:
        result = _get_time_series_for_geometries(dataset,
                                                 var_name,
                                                 shapes,
                                                 start_date=start_date,
                                                 end_date=end_date,
                                                 include_count=include_count,
                                                 include_stdev=include_stdev,
                                                 max_valids=max_valids)
    if ctx.trace_perf:
        LOG.info(
            f'get_time_series_for_feature_collection: dataset id {ds_name}, variable {var_name},'
            f'size={len(result["results"])}, took {time_result.duration} seconds'
        )
    return result
Beispiel #4
0
 def test_get_feature_geometry(self):
     self.assertEqual(dict(type='Point', coordinates=[2.13, 42.2]),
                      GeoJSON.get_feature_geometry(dict(type='Feature',
                                                        geometry=dict(type='Point', coordinates=[2.13, 42.2]))))
     self.assertEqual(None,
                      GeoJSON.get_feature_geometry(dict(type='Pleature',
                                                        geometry=dict(type='Point', coordinates=[2.13, 42.2]))))
     self.assertEqual(None,
                      GeoJSON.get_feature_geometry(dict(type='Feature',
                                                        geometry=dict(type='Point'))))
     self.assertEqual(None,
                      GeoJSON.get_feature_geometry(dict(type='Feature',
                                                        geometry=17)))
Beispiel #5
0
def convert_geometry(
    geometry: Optional[Geometry]
) -> Optional[shapely.geometry.base.BaseGeometry]:
    if isinstance(geometry, shapely.geometry.base.BaseGeometry):
        return geometry

    if isinstance(geometry, dict):
        if not GeoJSON.is_geometry(geometry):
            raise ValueError(_INVALID_GEOMETRY_MSG)
        return shapely.geometry.shape(geometry)

    if isinstance(geometry, str):
        return shapely.wkt.loads(geometry)

    if geometry is None:
        return None

    # noinspection PyBroadException
    try:
        x1, y1, x2, y2 = geometry
        return shapely.geometry.shape(
            dict(type='Polygon',
                 coordinates=[[[x1, y1], [x2, y1], [x2, y2], [x1, y2],
                               [x1, y1]]]))
    except Exception:
        # noinspection PyBroadException
        try:
            x, y = geometry
            return shapely.geometry.shape(
                dict(type='Point', coordinates=[x, y]))
        except Exception:
            pass

    raise ValueError(_INVALID_GEOMETRY_MSG)
Beispiel #6
0
    def test_is_geometry(self):
        self.assertTrue(GeoJSON.is_geometry(dict(type='Point', coordinates=[2.13, 42.2])))
        self.assertTrue(GeoJSON.is_geometry(dict(type='Point', coordinates=None)))
        self.assertFalse(GeoJSON.is_geometry(dict(type='Point')))

        self.assertTrue(GeoJSON.is_geometry(dict(type='GeometryCollection', geometries=None)))
        self.assertTrue(GeoJSON.is_geometry(dict(type='GeometryCollection', geometries=[])))
        self.assertFalse(GeoJSON.is_geometry(dict(type='GeometryCollection')))

        self.assertFalse(GeoJSON.is_geometry(dict(type='Feature', properties=None)))
Beispiel #7
0
def get_time_series_for_geometry(ctx: ServiceContext,
                                 ds_name: str,
                                 var_name: str,
                                 geometry: Dict,
                                 start_date: np.datetime64 = None,
                                 end_date: np.datetime64 = None,
                                 include_count: bool = False,
                                 include_stdev: bool = False,
                                 max_valids: int = None) -> Dict:
    """
    Get the time-series for a given *geometry*.

    :param ctx: Service context object
    :param ds_name: The dataset identifier.
    :param var_name: The variable name.
    :param geometry: The geometry, usually a point or polygon.
    :param start_date: An optional start date.
    :param end_date: An optional end date.
    :param include_count: Whether to include the valid number of observations in the result.
    :param include_stdev: Whether to include the standard deviation in the result.
    :param max_valids: Optional number of valid points.
           If it is None (default), also missing values are returned as NaN;
           if it is -1 only valid values are returned;
           if it is a positive integer, the most recent valid values are returned.
    :return: Time-series data structure.
    """
    dataset = ctx.get_time_series_dataset(ds_name, var_name=var_name)
    if not GeoJSON.is_geometry(geometry):
        raise ServiceBadRequestError("Invalid GeoJSON geometry")
    if isinstance(geometry, dict):
        geometry = shapely.geometry.shape(geometry)
    with measure_time() as time_result:
        result = _get_time_series_for_geometry(dataset,
                                               var_name,
                                               geometry,
                                               start_date=start_date,
                                               end_date=end_date,
                                               include_count=include_count,
                                               include_stdev=include_stdev,
                                               max_valids=max_valids)

    if ctx.trace_perf:
        LOG.info(
            f'get_time_series_for_geometry: dataset id {ds_name}, variable {var_name}, '
            f'geometry type {geometry},'
            f'size={len(result["results"])}, took {time_result.duration} seconds'
        )
    return result
Beispiel #8
0
def get_time_series_for_geometry_collection(ctx: ServiceContext,
                                            ds_name: str,
                                            var_name: str,
                                            geometry_collection: Dict,
                                            start_date: np.datetime64 = None,
                                            end_date: np.datetime64 = None,
                                            include_count: bool = False,
                                            include_stdev: bool = False,
                                            max_valids: int = None) -> Dict:
    """
    Get the time-series for a given *geometry_collection*.

    :param ctx: Service context object
    :param ds_name: The dataset identifier.
    :param var_name: The variable name.
    :param geometry_collection: The geometry collection.
    :param start_date: An optional start date.
    :param end_date: An optional end date.
    :param include_count: Whether to include the valid number of observations in the result.
    :param include_stdev: Whether to include the standard deviation in the result.
    :param max_valids: Optional number of valid points.
           If it is None (default), also missing values are returned as NaN;
           if it is -1 only valid values are returned;
           if it is a positive integer, the most recent valid values are returned.
    :return: Time-series data structure.
    """
    dataset = _get_time_series_dataset(ctx, ds_name, var_name)
    geometries = GeoJSON.get_geometry_collection_geometries(
        geometry_collection)
    if geometries is None:
        raise ServiceBadRequestError("Invalid GeoJSON geometry collection")
    shapes = []
    for geometry in geometries:
        try:
            geometry = shapely.geometry.shape(geometry)
        except (TypeError, ValueError) as e:
            raise ServiceBadRequestError(
                "Invalid GeoJSON geometry collection") from e
        shapes.append(geometry)
    return _get_time_series_for_geometries(dataset,
                                           var_name,
                                           shapes,
                                           start_date=start_date,
                                           end_date=end_date,
                                           include_count=include_count,
                                           include_stdev=include_stdev,
                                           max_valids=max_valids)
Beispiel #9
0
def _to_geo_json_geometries(
        geo_json: GeoJsonObj) -> Tuple[List[GeoJsonGeometry], bool]:
    is_collection = False
    if GeoJSON.is_feature(geo_json):
        geometry = _get_feature_geometry(geo_json)
        geometries = [geometry]
    elif GeoJSON.is_feature_collection(geo_json):
        is_collection = True
        features = GeoJSON.get_feature_collection_features(geo_json)
        geometries = [_get_feature_geometry(feature)
                      for feature in features] if features else []
    elif GeoJSON.is_geometry_collection(geo_json):
        is_collection = True
        geometries = GeoJSON.get_geometry_collection_geometries(geo_json)
    elif GeoJSON.is_geometry(geo_json):
        geometries = [geo_json]
    else:
        raise ServiceBadRequestError("GeoJSON object expected")
    return geometries, is_collection
Beispiel #10
0
def _get_feature_geometry(feature: GeoJsonFeature) -> GeoJsonGeometry:
    geometry = GeoJSON.get_feature_geometry(feature)
    if geometry is None or not GeoJSON.is_geometry(geometry):
        raise ServiceBadRequestError("GeoJSON feature without geometry")
    return geometry
Beispiel #11
0
def convert_geometry(
    geometry: Optional[GeometryLike]
) -> Optional[shapely.geometry.base.BaseGeometry]:
    """
    Convert a geometry-like object into a shapely geometry object (``shapely.geometry.BaseGeometry``).

    A geometry-like object is may be any shapely geometry object,
    * a dictionary that can be serialized to valid GeoJSON,
    * a WKT string,
    * a box given by a string of the form "<x1>,<y1>,<x2>,<y2>"
      or by a sequence of four numbers x1, y1, x2, y2,
    * a point by a string of the form "<x>,<y>"
      or by a sequence of two numbers x, y.

    Handling of geometries crossing the antimeridian:

    * If box coordinates are given, it is allowed to pass x1, x2 where x1 > x2,
      which is interpreted as a box crossing the antimeridian. In this case the function
      splits the box along the antimeridian and returns a multi-polygon.
    * In all other cases, 2D geometries are assumed to _not cross the antimeridian at all_.

    :param geometry: A geometry-like object
    :return:  Shapely geometry object or None.
    """

    if isinstance(geometry, shapely.geometry.base.BaseGeometry):
        return geometry

    if isinstance(geometry, dict):
        if GeoJSON.is_geometry(geometry):
            return shapely.geometry.shape(geometry)
        elif GeoJSON.is_feature(geometry):
            geometry = GeoJSON.get_feature_geometry(geometry)
            if geometry is not None:
                return shapely.geometry.shape(geometry)
        elif GeoJSON.is_feature_collection(geometry):
            features = GeoJSON.get_feature_collection_features(geometry)
            if features is not None:
                geometries = [
                    f2 for f2 in
                    [GeoJSON.get_feature_geometry(f1) for f1 in features]
                    if f2 is not None
                ]
                if geometries:
                    geometry = dict(type='GeometryCollection',
                                    geometries=geometries)
                    return shapely.geometry.shape(geometry)
        raise ValueError(_INVALID_GEOMETRY_MSG)

    if isinstance(geometry, str):
        return shapely.wkt.loads(geometry)

    if geometry is None:
        return None

    invalid_box_coords = False
    # noinspection PyBroadException
    try:
        x1, y1, x2, y2 = geometry
        is_point = x1 == x2 and y1 == y2
        if is_point:
            return shapely.geometry.Point(x1, y1)
        invalid_box_coords = x1 == x2 or y1 >= y2
        if not invalid_box_coords:
            return get_box_split_bounds_geometry(x1, y1, x2, y2)
    except Exception:
        # noinspection PyBroadException
        try:
            x, y = geometry
            return shapely.geometry.Point(x, y)
        except Exception:
            pass

    if invalid_box_coords:
        raise ValueError(_INVALID_BOX_COORDS_MSG)
    raise ValueError(_INVALID_GEOMETRY_MSG)
Beispiel #12
0
 def test_is_point(self):
     self.assertTrue(GeoJSON.is_point(dict(type='Point', coordinates=[2.13, 42.2])))
     self.assertFalse(GeoJSON.is_point(dict(type='Feature', properties=None)))
Beispiel #13
0
 def test_is_feature(self):
     self.assertTrue(GeoJSON.is_feature(dict(type='Feature',
                                             geometry=dict(type='Point',
                                                           coordinates=[2.13, 42.2]))))
     self.assertFalse(GeoJSON.is_feature(dict(type='Point', coordinates=[2.13, 42.2])))