Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GTC-2659: "include_attribute" query param does not behave as expected #130

Merged
merged 28 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fe47b54
feat(supported attributes): use enum in col_dict
gtempus Jan 10, 2024
9ed2998
feat(include attributes): get the fields from Data API
gtempus Jan 10, 2024
c89a9ca
refactor: move SCHEMA back to module scope
gtempus Jan 10, 2024
f43e175
chore: add logging of query
gtempus Jan 10, 2024
8e23759
chore: add logging
gtempus Jan 10, 2024
00c8587
chore: log the tile
gtempus Jan 10, 2024
d54c27a
chore: clean up unused imports and module level code
gtempus Jan 10, 2024
f5602ac
feat: add umd tc thresholds
gtempus Jan 10, 2024
28ed4a1
refactor: rename SupportedAttribute
gtempus Jan 10, 2024
870290e
refactor: return a list of Attribute enums from the api call
gtempus Jan 10, 2024
aa60d5c
refactor: use aenum to store the aggregation rule close to the enum
gtempus Jan 10, 2024
b343a4a
refactor: try init param
gtempus Jan 10, 2024
62f012a
refactor(SupportedAttributes): rename formal parameter to mention agg…
gtempus Jan 11, 2024
6e1d738
style: removing logging statements
gtempus Jan 11, 2024
a81b273
refactor: Return a list of string from the api
gtempus Jan 11, 2024
daa015e
refactor: delete nasa viirs fire alerts attributes enum
gtempus Jan 11, 2024
552214f
chore: add these back
gtempus Jan 11, 2024
92b97f6
refactor(data_api): make call to Data API async
gtempus Jan 18, 2024
2e476cc
refactor: make api call async
gtempus Jan 18, 2024
8490bf0
test: disable a few tests that require some refactoring work before t…
gtempus Jan 18, 2024
603e547
refactor(tile cache assets): remove `fields` from the SQL query.
gtempus Jan 18, 2024
f2b4d76
refactor: remove unused `get_attributes` function
gtempus Jan 18, 2024
563b71a
refactor(Attributes): tile cache type is no longer needed.
gtempus Jan 18, 2024
5374f35
refactor: do not log the tile contents
gtempus Jan 18, 2024
5295fb8
refactor: log the query at `debug` level
gtempus Jan 18, 2024
c9802fd
refactor(DataAPI): DRY up the two functions to remove duplication
gtempus Jan 18, 2024
d143171
docs: Minor update to `include_attributes`
gtempus Jan 19, 2024
8071e29
fix(sync_db): ensure rows returned from tile cache query is iterable
gtempus Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ repos:
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline'] # run: `pip install detect-secrets` to establish baseline
exclude: Pipfile.lock
exclude: Pipfile.lock
21 changes: 7 additions & 14 deletions app/crud/async_db/vector_tiles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ def get_mvt_table(


async def get_tile(query: Select, name: str, extent: int) -> VectorTileResponse:
"""
Make SQL query to PostgreSQL and return vector tile in PBF format.
"""
"""Make SQL query to PostgreSQL and return vector tile in PBF format."""
query = _as_vector_tile(query, name, extent)
return await _get_tile(query)

Expand All @@ -44,9 +42,10 @@ async def get_aggregated_tile(
name: str,
extent: int,
) -> VectorTileResponse:
"""
Make SQL query to PostgreSQL and return vector tile in PBF format.
This function makes a SQL query that aggregates point features based on proximity.
"""Make SQL query to PostgreSQL and return vector tile in PBF format.

This function makes a SQL query that aggregates point features based
on proximity.
"""
query = _group_mvt_table(query, columns, group_by_columns).alias(
"grouped_mvt_table"
Expand All @@ -56,17 +55,13 @@ async def get_aggregated_tile(


async def _get_tile(query: Select) -> VectorTileResponse:

logger.debug(query)
tile = await db.scalar(query)

return VectorTileResponse(content=tile, status_code=200)


def _get_bounds(left: float, bottom: float, right: float, top: float) -> Select:
"""
Create bounds query
"""
"""Create bounds query."""
geom = db.text(
"ST_MakeEnvelope(:left, :bottom, :right, :top, 3857) AS geom"
# "ST_SetSRID(ST_MakeBox2D(ST_Point(:left, :bottom), ST_Point(:right, :top)),3857) AS geom"
Expand All @@ -87,9 +82,7 @@ def _get_mvt_table(
columns: List[ColumnClause],
order_by: List[ColumnClause] = [],
) -> Select:
"""
Create MVT Geom query
"""
"""Create MVT Geom query."""

mvt_geom = db.literal_column(
f"ST_AsMVTGeom(t.geom_wm, bounds.geom::box2d, {extent}, 0,false)"
Expand Down
67 changes: 16 additions & 51 deletions app/crud/async_db/vector_tiles/nasa_viirs_fire_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,79 +3,44 @@
from sqlalchemy.sql.elements import ColumnClause, TextClause

from ....application import db
from ....models.enumerators.tile_caches import TileCacheType
from ....models.enumerators.nasa_viirs_fire_alerts.supported_attributes import (
SupportedAttribute,
)
from ....models.types import Bounds
from ....responses import VectorTileResponse
from ...async_db import vector_tiles
from ...sync_db.tile_cache_assets import get_attributes, get_latest_version
from ...sync_db.tile_cache_assets import get_attributes
from . import get_mvt_table

SCHEMA = "nasa_viirs_fire_alerts"

COLUMNS: List[ColumnClause] = list()
latest_version: Optional[str] = get_latest_version(
SCHEMA, TileCacheType.dynamic_vector_tile_cache
)
if latest_version:
fields = get_attributes(
SCHEMA, latest_version, TileCacheType.dynamic_vector_tile_cache
)
for field in fields:
if field["is_feature_info"]:
COLUMNS.append(db.column(field["field_name"]))


async def get_tile(
version: str, bbox: Bounds, extent: int, filters: List[TextClause]
) -> VectorTileResponse:
"""
Make SQL query to PostgreSQL and return vector tile in PBF format.
"""
query = get_mvt_table(SCHEMA, version, bbox, extent, COLUMNS, filters)
return await vector_tiles.get_tile(query, SCHEMA, extent)


async def get_aggregated_tile(
version: str,
bbox: Bounds,
extent: int,
attributes: List[str],
supported_attributes: List[SupportedAttribute],
filters: List[TextClause],
) -> VectorTileResponse:
"""
Make SQL query to PostgreSQL and return vector tile in PBF format.
This function makes a SQL query that aggregates point features based on proximity.
"""Make SQL query to PostgreSQL and return vector tile in PBF format.

This function makes a SQL query that aggregates point features based
on proximity.
"""

col_dict = {
"latitude": db.literal_column("round(avg(latitude),4)").label("latitude"),
"longitude": db.literal_column("round(avg(longitude),4)").label("longitude"),
"alert__date": db.literal_column(
"mode() WITHIN GROUP (ORDER BY alert__date)"
).label("alert__date"),
"alert__time_utc": db.literal_column(
"mode() WITHIN GROUP (ORDER BY alert__time_utc)"
).label("alert__time_utc"),
"confidence__cat": db.literal_column(
"mode() WITHIN GROUP (ORDER BY confidence__cat)"
).label("confidence__cat"),
"bright_ti4__K": db.literal_column('round(avg("bright_ti4__K"),3)').label(
"bright_ti4__K"
),
"bright_ti5__K": db.literal_column('round(avg("bright_ti5__k"),3)').label(
"bright_ti5__K"
),
"frp__MW": db.literal_column('sum("frp__MW")').label("frp__MW"),
}
columns: List[ColumnClause] = list()
attributes: List[str] = await get_attributes(SCHEMA, version)
for attribute in attributes:
columns.append(db.column(attribute))

query = get_mvt_table(SCHEMA, version, bbox, extent, COLUMNS, filters)
query = get_mvt_table(SCHEMA, version, bbox, extent, columns, filters)
columns = [
db.column("geom"),
db.literal_column("count(*)").label("count"),
]

for attribute in attributes:
columns.append(col_dict[attribute])
for attribute in supported_attributes:
columns.append(attribute.aggregation_rule)

group_by_columns = [db.column("geom")]

Expand Down
37 changes: 13 additions & 24 deletions app/crud/sync_db/tile_cache_assets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional
from typing import Dict, List, Optional

import pendulum
from cachetools import TTLCache, cached
Expand All @@ -7,6 +7,7 @@

from ...application import get_synchronous_db
from ...models.enumerators.tile_caches import TileCacheType
from ...utils.data_api import get_version_fields


@cached(cache=TTLCache(maxsize=1, ttl=900))
Expand All @@ -23,7 +24,6 @@ def get_all_tile_caches():
versions.version as version,
assets.creation_options->'implementation' as implementation,
versions.is_latest as is_latest,
assets.fields as fields,
assets.creation_options->'min_zoom' as min_zoom,
assets.creation_options->'max_zoom' as max_zoom,
version_metadata.content_start_date as min_date,
Expand All @@ -38,11 +38,12 @@ def get_all_tile_caches():
WHERE assets.asset_type IN {str(tuple([e.value for e in list(TileCacheType)])).replace('"', "'")}
AND assets.status = 'saved'"""
).fetchall()
if not rows:
# tile_caches = []
logger.warning("There are no tile caches registered with the API.")
# else:
# tile_caches = rows

if rows is None or len(rows) == 0:
logger.warning(
"No rows returned. There are no tile caches registered with the API"
)
rows = []

for row in rows:
tile_caches[row.asset_type].append(
Expand All @@ -51,7 +52,6 @@ def get_all_tile_caches():
"version": row.version,
"implementation": row.implementation,
"is_latest": row.is_latest,
"fields": row.fields,
"min_zoom": row.min_zoom,
"max_zoom": row.max_zoom,
"min_date": row.min_date.strftime("%Y-%m-%d")
Expand Down Expand Up @@ -124,22 +124,11 @@ def get_latest_versions() -> List[Dict[str, str]]:
return latest_versions


@cached(cache=TTLCache(maxsize=15, ttl=900))
def get_attributes(dataset: str, version: str, asset_type: str) -> List[Dict[str, Any]]:
tile_caches = get_all_tile_caches()

for tile_cache in tile_caches[asset_type]:
# pick the first one that matchs
# TODO: fetch the correct one for the current implementation
# needs changes to data-api to assure dynamic vector tile caches
# also have the implementation parameter in the creation options
if tile_cache["dataset"] == dataset and tile_cache["version"] == version:
return tile_cache["fields"]

logger.warning(
f"Did not find any fields in metadata for {asset_type} of {dataset}.{version}."
)
return list()
async def get_attributes(dataset, version):
# TODO: fetch the correct one for the current implementation
# needs changes to data-api to assure dynamic vector tile caches
# also have the implementation parameter in the creation options
return await get_version_fields(dataset, version)


@cached(cache=TTLCache(maxsize=100, ttl=900))
Expand Down
22 changes: 0 additions & 22 deletions app/models/enumerators/nasa_viirs_fire_alerts/attributes.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from aenum import Enum

from ....application import db


def rule(aggregation_expression, label):
return db.literal_column(aggregation_expression).label(label)


class SupportedAttribute(Enum, init="value aggregation_rule"): # type: ignore

LATITUDE = ("latitude", rule("round(avg(latitude),4)", "latitude"))
LONGITUDE = ("longitude", rule("round(avg(longitude),4)", "longitude"))
ALERT_DATE = (
"alert__date",
rule("mode() WITHIN GROUP (ORDER BY alert__date)", "alert__date"),
)
ALERT_TIME_UTC = (
"alert__time_utc",
rule("mode() WITHIN GROUP (ORDER BY alert__time_utc)", "alert__time_utc"),
)
CONFIDENCE_CAT = (
"confidence__cat",
rule("mode() WITHIN GROUP (ORDER BY confidence__cat)", "confidence__cat"),
)
BRIGHT_TI4_K = (
"bright_ti4__K",
rule('round(avg("bright_ti4__K"),3)', "bright_ti4__K"),
)
BRIGHT_TI5_K = (
"bright_ti5__K",
rule('round(avg("bright_ti5__k"),3)', "bright_ti5__K"),
)
FRP_MW = ("frp__MW", rule('sum("frp__MW")', "frp__MW"))
UMD_TREE_COVER_DENSITY_2000__THRESHOLD = (
"umd_tree_cover_density_2000__threshold",
rule(
'max("umd_tree_cover_density_2000__threshold")',
"umd_tree_cover_density_2000__threshold",
),
)
UMD_TREE_COVER_DENSITY__THRESHOLD = (
"umd_tree_cover_density__threshold",
rule(
'max("umd_tree_cover_density__threshold")',
"umd_tree_cover_density__threshold",
),
)

def __str__(self):
return self.value


default_attributes = [SupportedAttribute.FRP_MW]
33 changes: 12 additions & 21 deletions app/routes/dynamic_vector_tiles.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Dynamic vector tiles are generated on the fly.

The dynamic nature of the service allows users to apply filters using
query parameters or to change tile resolution using the `@` operator
after the `y` index
"""
Dynamic vector tiles are generated on the fly.
The dynamic nature of the service allows users to apply filters using query parameters
or to change tile resolution using the `@` operator after the `y` index
"""
from typing import Dict, List, Optional, Tuple
from typing import List, Optional, Tuple
from uuid import UUID

from asyncpg.exceptions import QueryCanceledError
Expand All @@ -16,7 +17,6 @@
from ..crud.async_db.vector_tiles.filters import geometry_filter
from ..crud.sync_db.tile_cache_assets import get_attributes
from ..models.enumerators.geostore import GeostoreOrigin
from ..models.enumerators.tile_caches import TileCacheType
from ..models.types import Bounds
from ..responses import VectorTileResponse
from . import dynamic_vector_tile_cache_version_dependency, vector_xyz
Expand Down Expand Up @@ -49,9 +49,7 @@ async def dynamic_vector_tile(
"If not specified, all attributes will be shown.",
),
) -> VectorTileResponse:
"""
Generic dynamic vector tile
"""
"""Generic dynamic vector tile."""
dataset, version = dv
bbox, _, extent = bbox_z

Expand All @@ -62,24 +60,17 @@ async def dynamic_vector_tile(
if geom_filter is not None:
filters.append(geom_filter)

fields: List[Dict] = get_attributes(
dataset, version, TileCacheType.dynamic_vector_tile_cache
)
attributes: List[str] = await get_attributes(dataset, version)

# if no attributes specified get all feature info fields
if not include_attribute:
columns: List[ColumnClause] = [
db.column(field["field_name"])
for field in fields
if field["is_feature_info"]
]

columns: List[ColumnClause] = [db.column(attribute) for attribute in attributes]
# otherwise run provided list against feature info list and keep common elements
else:
columns = [
db.column(field["field_name"])
for field in fields
if field["is_feature_info"] and field["field_name"] in include_attribute
db.column(attribute)
for attribute in attributes
if attribute in include_attribute
]

query: Select = get_mvt_table(dataset, version, bbox, extent, columns, filters)
Expand Down
Loading
Loading