Skip to content

Commit

Permalink
Merge 0f6046f into 6ddaeb8
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy committed Apr 16, 2024
2 parents 6ddaeb8 + 0f6046f commit 27619df
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 73 deletions.
21 changes: 12 additions & 9 deletions bindings/pydeck-carto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ from carto_auth import CartoAuth
# Authentication with CARTO
carto_auth = CartoAuth.from_oauth()

# Register CartoLayer in pydeck
pdkc.register_carto_layer()

# Render CartoLayer in pydeck
# Register new layer types in pydeck
pdkc.register_layers()

# Render CARTO layer in pydeck
data = pdkc.sources.vector_query_source(
access_token=carto_auth.get_access_token(),
api_base_url=carto_auth.get_api_base_url(),
connection_name="carto_dw",
sql_query="SELECT geom, name FROM carto-demo-data.demo_tables.world_airports",
)
layer = pdk.Layer(
"CartoLayer",
data="SELECT geom, name FROM carto-demo-data.demo_tables.airports",
type_=pdkc.MapType.QUERY,
connection=pdkc.CartoConnection.CARTO_DW,
credentials=pdkc.get_layer_credentials(carto_auth),
"VectorTileLayer",
data=data,
get_fill_color=[238, 77, 90],
point_radius_min_pixels=2.5,
pickable=True,
Expand Down
16 changes: 10 additions & 6 deletions bindings/pydeck-carto/examples/scripts/carto_layer_geo_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@

carto_auth = CartoAuth.from_oauth()

pdkc.register_carto_layer()
pdkc.register_layers()

data = pdkc.sources.vector_query_source(
access_token=carto_auth.get_access_token(),
api_base_url=carto_auth.get_api_base_url(),
connection_name="carto_dw",
sql_query="SELECT geom, name FROM carto-demo-data.demo_tables.world_airports",
)

layer = pdk.Layer(
"CartoLayer",
data="SELECT geom, name FROM carto-demo-data.demo_tables.airports",
type_=pdkc.MapType.QUERY,
connection=pdkc.CartoConnection.CARTO_DW,
credentials=pdkc.get_layer_credentials(carto_auth),
"VectorTileLayer",
data=data.serialize(), # TODO(donmccurdy): How to automate this?
get_fill_color=[238, 77, 90],
point_radius_min_pixels=2.5,
pickable=True,
Expand Down
244 changes: 215 additions & 29 deletions bindings/pydeck-carto/pydeck_carto/sources.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,235 @@
import pydeck as pdk
from typing import Any, TypedDict, List
from typing_extensions import NotRequired, Unpack, assert_type

# TYPES

def base_source(connection_name: str, access_token: str, api_base_url: str):

class Options(TypedDict):
pass


class BaseSourceOptions(Options):
connection_name: str
access_token: str
api_base_url: str


class TableSourceOptions(BaseSourceOptions):
table_name: str
spatial_data_column: NotRequired[str]


class QuerySourceOptions(BaseSourceOptions):
sql_query: str
spatial_data_column: NotRequired[str]


class TilesetSourceOptions(BaseSourceOptions):
table_name: str


class ColumnOptions(Options):
columns: NotRequired[List[str]]


class AggregationOptions(Options):
aggregation_exp: str
aggregation_res_level: NotRequired[int]


# VALIDATORS

# The 'interface' arguments are unused, but could be used with
# 'get_type_hints' to provide type hints in the future.


def validate_str(
interface: Any, args: Options, arg: str, required: bool = True
):
"""Validates given key on an options object is a string."""
if arg not in args and required:
raise AssertionError('Missing argument "{}".'.format(arg))
elif arg in args:
assert type(args[arg]) is str, "Argument {} must be of type str".format(arg)


def validate_int(
interface: Any, args: Options, arg: str, required: bool = True
):
"""Validates given key on an options object is an int."""
if arg not in args and required:
raise AssertionError('Missing argument "{}".'.format(arg))
elif arg in args:
assert type(args[arg]) is int, "Argument {} must be of type int".format(arg)


# BASE


def base_options(**kwargs: Unpack[BaseSourceOptions]):
assert_type(kwargs, BaseSourceOptions)
validate_str(BaseSourceOptions, kwargs, "connection_name")
validate_str(BaseSourceOptions, kwargs, "access_token")
validate_str(BaseSourceOptions, kwargs, "api_base_url")
return {
"connectionName": connection_name,
"accessToken": access_token,
"apiBaseUrl": api_base_url,
"connectionName": kwargs["connection_name"],
"accessToken": kwargs["access_token"],
"apiBaseUrl": kwargs["api_base_url"],
"clientId": "pydeck-carto",
}


def vector_table_source(table_name: str, spatial_data_column: str, **kwargs):
def table_options(**kwargs: Unpack[TableSourceOptions]):
assert_type(kwargs, TableSourceOptions)
validate_str(TableSourceOptions, kwargs, "table_name")
validate_str(TableSourceOptions, kwargs, "spatial_data_column", False)
return {
"tableName": kwargs.get("table_name"),
**({"spatialDataColumn": kwargs["spatial_data_column"]} if "spatial_data_column" in kwargs else {}),
**base_options(**kwargs),
}


def query_options(**kwargs: Unpack[QuerySourceOptions]):
assert_type(kwargs, QuerySourceOptions)
validate_str(TableSourceOptions, kwargs, "sql_query")
validate_str(TableSourceOptions, kwargs, "spatial_data_column", False)
return {
"sqlQuery": kwargs.get("sql_query"),
**({"spatialDataColumn": kwargs["spatial_data_column"]} if "spatial_data_column" in kwargs else {}),
**base_options(**kwargs),
}


def tileset_options(**kwargs: Unpack[TilesetSourceOptions]):
assert_type(kwargs, TilesetSourceOptions)
validate_str(TableSourceOptions, kwargs, "table_name")
return {
"tableName": kwargs["table_name"],
**base_options(**kwargs),
}


def column_options(**kwargs: Unpack[ColumnOptions]):
assert_type(kwargs, ColumnOptions)
return {"columns": kwargs["columns"]} if "columns" in kwargs else {}


def aggregation_options(**kwargs: Unpack[AggregationOptions]):
assert_type(kwargs, AggregationOptions)
validate_str(AggregationOptions, kwargs, "aggregation_exp")
validate_int(AggregationOptions, kwargs, "aggregation_res_level", False)
return {
"aggregationExp": kwargs["aggregation_exp"],
**({"aggregationResLevel": kwargs["aggregation_res_level"]} if "aggregation_res_level" in kwargs else {}),
}


# VECTOR


class VectorTableSourceOptions(TableSourceOptions, ColumnOptions):
pass


class VectorQuerySourceOptions(QuerySourceOptions, ColumnOptions):
pass


class VectorTilesetSourceOptions(TilesetSourceOptions):
pass


def vector_table_source(**kwargs: Unpack[VectorTableSourceOptions]):
return pdk.types.Function(
"vectorTableSource", **{**column_options(**kwargs), **table_options(**kwargs)}
)


def vector_query_source(**kwargs: Unpack[VectorQuerySourceOptions]):
return pdk.types.Function(
"vectorQuerySource", **{**column_options(**kwargs), **query_options(**kwargs)}
)


def vector_tileset_source(**kwargs: Unpack[VectorTilesetSourceOptions]):
return pdk.types.Function("vectorTilesetSource", **tileset_options(**kwargs))


# H3


class H3TableSourceOptions(TableSourceOptions, AggregationOptions):
pass


class H3QuerySourceOptions(QuerySourceOptions, AggregationOptions):
pass


class H3TilesetSourceOptions(TilesetSourceOptions, AggregationOptions):
pass


def h3_table_source(**kwargs: Unpack[H3TableSourceOptions]):
return pdk.types.Function(
"vectorTableSource",
**{
"tableName": table_name,
"spatialDataColumn": spatial_data_column,
**base_source(**kwargs),
}
"h3TableSource", **{**aggregation_options(**kwargs), **table_options(**kwargs)}
)


def vector_query_source(sql_query: str, spatial_data_column: str, **kwargs):
def h3_query_source(**kwargs: Unpack[H3QuerySourceOptions]):
return pdk.types.Function(
"vectorQuerySource",
**{
"sqlQuery": sql_query,
"spatialDataColumn": spatial_data_column,
**base_source(**kwargs),
}
"h3QuerySource", **{**aggregation_options(**kwargs), **query_options(**kwargs)}
)


def vector_tileset_source(table_name: str, **kwargs):
def h3_tileset_source(**kwargs: Unpack[H3TilesetSourceOptions]):
return pdk.types.Function(
"vectorTilesetSource", **{"tableName": table_name, **base_source(**kwargs)}
"h3TilesetSource",
**tileset_options(**kwargs)
)


# TODO: Implement all the source functions and parameters
# https://felixpalmer.github.io/deck.gl/docs/api-reference/carto/data-sources
# h3_table_source
# h3_query_source
# h3_tileset_source
# quadbin_table_source
# quadbin_query_source
# quadbin_tileset_source
# raster_tileset_source (experimental)
# QUADBIN


class QuadbinTableSourceOptions(TableSourceOptions, AggregationOptions):
pass


class QuadbinQuerySourceOptions(QuerySourceOptions, AggregationOptions):
pass


class QuadbinTilesetSourceOptions(TilesetSourceOptions, AggregationOptions):
pass


def quadbin_table_source(**kwargs: Unpack[QuadbinTableSourceOptions]):
return pdk.types.Function(
"quadbinTableSource",
**{**aggregation_options(**kwargs), **table_options(**kwargs)}
)


def quadbin_query_source(**kwargs: Unpack[QuadbinQuerySourceOptions]):
return pdk.types.Function(
"quadbinQuerySource",
**{**aggregation_options(**kwargs), **query_options(**kwargs)}
)


def quadbin_tileset_source(**kwargs: Unpack[QuadbinTilesetSourceOptions]):
return pdk.types.Function(
"quadbinTilesetSource",
**tileset_options(**kwargs)
)


# RASTER (EXPERIMENTAL)


def raster_tileset_source(**kwargs):
"""EXPERIMENTAL."""
raise RuntimeError("not implemented")
14 changes: 8 additions & 6 deletions bindings/pydeck-carto/requirements/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
wheel==0.37.1
flake8==4.0.1
black==22.3.0
carto-auth>=0.2.0
flake8==4.0.1
ipython>=7.8.0
tokenize-rt>=3.2.0
twine==4.0.0
pytest-cov==3.0.0
pytest-mock==3.8.2
pytest==7.1.2
requests-mock==1.9.3
pytest-mock==3.8.2
pytest-cov==3.0.0
tokenize-rt>=3.2.0
twine==4.0.0
typing-extensions>=4.0.0
wheel==0.37.1
3 changes: 2 additions & 1 deletion bindings/pydeck-carto/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
python_requires=">=3.7",
install_requires=[
"pydeck>=0.8.0",
"carto-auth>=0.1.0",
"carto-auth>=0.2.0",
"typing-extensions>=4.0.0",
],
classifiers=[
"Development Status :: 5 - Production/Stable",
Expand Down

0 comments on commit 27619df

Please sign in to comment.