diff --git a/README.md b/README.md index 4ac4a07a..87c5fbc2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A Python library for fast, interactive geospatial vector data visualization in Jupyter. -By utilizing new technologies like [GeoArrow](https://github.com/geoarrow/geoarrow) and [GeoParquet](https://github.com/opengeospatial/geoparquet) in conjunction with [GPU-based map rendering](https://deck.gl/), lonboard aims to enable visualizing large geospatial datasets interactively through a simple interface. +Building on cutting-edge technologies like [GeoArrow](https://github.com/geoarrow/geoarrow) and [GeoParquet](https://github.com/opengeospatial/geoparquet) in conjunction with [GPU-based map rendering](https://deck.gl/), lonboard aims to enable visualizing large geospatial datasets interactively through a simple interface. ![](assets/hero-image.jpg) diff --git a/build.mjs b/build.mjs index 095b2737..e609eb65 100755 --- a/build.mjs +++ b/build.mjs @@ -12,4 +12,7 @@ esbuild.build({ define: { "define.amd": "false", }, + // Code splitting didn't work initially because it tried to load from a local + // relative path ./chunk.js + // splitting: true, }); diff --git a/docs/api/basemap.md b/docs/api/basemap.md new file mode 100644 index 00000000..cc19a8e6 --- /dev/null +++ b/docs/api/basemap.md @@ -0,0 +1,3 @@ +# lonboard.basemap + +::: lonboard.basemap.CartoBasemap diff --git a/lonboard/_map.py b/lonboard/_map.py index 21831d53..143923e0 100644 --- a/lonboard/_map.py +++ b/lonboard/_map.py @@ -11,6 +11,7 @@ from lonboard._environment import DEFAULT_HEIGHT from lonboard._layer import BaseLayer from lonboard._viewport import compute_view +from lonboard.basemap import CartoBasemap # bundler yields lonboard/static/{index.js,styles.css} bundler_output_dir = Path(__file__).parent / "static" @@ -108,6 +109,17 @@ class Map(BaseAnyWidget): - Default: `5` """ + basemap_style = traitlets.Unicode(CartoBasemap.PositronNoLabels).tag(sync=True) + """ + A MapLibre-compatible basemap style. + + Various styles are provided in [`lonboard.basemap`][lonboard.basemap]. + + - Type: `str` + - Default + [`lonboard.basemap.CartoBasemap.PositronNoLabels`][lonboard.basemap.CartoBasemap.PositronNoLabels] + """ + def to_html(self, filename: Union[str, Path]) -> None: """Save the current map as a standalone HTML file. diff --git a/lonboard/basemap.py b/lonboard/basemap.py new file mode 100644 index 00000000..b05ab378 --- /dev/null +++ b/lonboard/basemap.py @@ -0,0 +1,43 @@ +from enum import Enum + + +class CartoBasemap(str, Enum): + """Basemap styles provided by Carto. + + Refer to [Carto + documentation](https://docs.carto.com/carto-for-developers/carto-for-react/guides/basemaps) + for information on styles. + """ + + DarkMatter = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json" + """A dark map style with labels. + + ![](https://carto.com/help/images/building-maps/basemaps/dark_labels.png) + """ + + DarkMatterNoLabels = ( + "https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json" + ) + """A dark map style without labels.""" + + Positron = "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json" + """A light map style with labels. + + ![](https://carto.com/help/images/building-maps/basemaps/positron_labels.png) + """ + + PositronNoLabels = ( + "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json" + ) + """A light map style without labels.""" + + Voyager = "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json" + """A light, colored map style with labels. + + ![](https://carto.com/help/images/building-maps/basemaps/voyager_labels.png) + """ + + VoyagerNoLabels = ( + "https://basemaps.cartocdn.com/gl/voyager-nolabels-gl-style/style.json" + ) + """A light, colored map style without labels.""" diff --git a/mkdocs.yml b/mkdocs.yml index fe64216e..831bee1b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -39,6 +39,7 @@ nav: - api/layers/path-layer.md - api/layers/scatterplot-layer.md - api/layers/solid-polygon-layer.md + - api/basemap.md - api/colormap.md - api/traits.md - Experimental: diff --git a/package-lock.json b/package-lock.json index b47c9f90..6af0b3da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "devDependencies": { "@jupyter-widgets/base": "^6.0.6", "@types/react": "^18.2.35", + "@types/uuid": "^9.0.7", "esbuild": "^0.19.5", "nodemon": "^3.0.1", "prettier": "^3.1.0", @@ -1751,6 +1752,12 @@ "integrity": "sha512-beX81q12OQo809WJ/UYCvUDvJR3YQ4wtehYYQ6eNw5VLyd+KUNBRV4FgzZCHBmACbdPulH9F9ifhxzFFU9TWvQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, "node_modules/@vitest/expect": { "version": "0.34.6", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", diff --git a/package.json b/package.json index a4cef774..0c89b7af 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,11 @@ "react-map-gl": "^7.1.5", "uuid": "^9.0.1" }, + "type": "module", "devDependencies": { "@jupyter-widgets/base": "^6.0.6", "@types/react": "^18.2.35", + "@types/uuid": "^9.0.7", "esbuild": "^0.19.5", "nodemon": "^3.0.1", "prettier": "^3.1.0", diff --git a/src/accessor.ts b/src/accessor.ts index c5589f44..9a877b61 100644 --- a/src/accessor.ts +++ b/src/accessor.ts @@ -1,5 +1,5 @@ import * as arrow from "apache-arrow"; -import { parseParquetBuffers } from "./parquet"; +import { parseParquetBuffers } from "./parquet.js"; import { useState, useEffect } from "react"; export function useTableBufferState( diff --git a/src/index.tsx b/src/index.tsx index 0390385a..99e05ef9 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,11 +4,11 @@ import { createRender, useModelState, useModel } from "@anywidget/react"; import Map from "react-map-gl/maplibre"; import DeckGL from "@deck.gl/react/typed"; import type { Layer } from "@deck.gl/core/typed"; -import { BaseLayerModel, initializeLayer } from "./model"; +import { BaseLayerModel, initializeLayer } from "./model/index.js"; import type { WidgetModel } from "@jupyter-widgets/base"; -import { useParquetWasm } from "./parquet"; -import { getTooltip } from "./tooltip"; -import { loadChildModels } from "./util"; +import { useParquetWasm } from "./parquet.js"; +import { getTooltip } from "./tooltip/index.js"; +import { loadChildModels } from "./util.js"; import { v4 as uuidv4 } from "uuid"; const DEFAULT_INITIAL_VIEW_STATE = { @@ -19,7 +19,7 @@ const DEFAULT_INITIAL_VIEW_STATE = { pitch: 0, }; -const MAP_STYLE = +const DEFAULT_MAP_STYLE = "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json"; async function getChildModelState( @@ -59,6 +59,7 @@ async function getChildModelState( function App() { let [parquetWasmReady] = useParquetWasm(); let [initialViewState] = useModelState("_initial_view_state"); + let [mapStyle] = useModelState("basemap_style"); let [mapHeight] = useModelState("_height"); let [showTooltip] = useModelState("show_tooltip"); let [pickingRadius] = useModelState("picking_radius"); @@ -139,7 +140,7 @@ function App() { getTooltip={showTooltip && getTooltip} pickingRadius={pickingRadius} > - + ); diff --git a/src/model/base.ts b/src/model/base.ts index 2090bdeb..2660938a 100644 --- a/src/model/base.ts +++ b/src/model/base.ts @@ -1,5 +1,5 @@ import type { WidgetModel } from "@jupyter-widgets/base"; -import { parseAccessor } from "../accessor"; +import { parseAccessor } from "../accessor.js"; export abstract class BaseModel { protected model: WidgetModel; diff --git a/src/model/extension.ts b/src/model/extension.ts index a714d889..c3d3f3f4 100644 --- a/src/model/extension.ts +++ b/src/model/extension.ts @@ -6,7 +6,7 @@ import { CollisionFilterExtension as _CollisionFilterExtension, } from "@deck.gl/extensions/typed"; import type { WidgetModel } from "@jupyter-widgets/base"; -import { BaseModel } from "./base"; +import { BaseModel } from "./base.js"; export abstract class BaseExtensionModel extends BaseModel { static extensionType: string; diff --git a/src/model/index.ts b/src/model/index.ts index 659c7bec..212c6e33 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -1,14 +1,14 @@ -export { BaseModel } from "./base"; +export { BaseModel } from "./base.js"; export { BaseLayerModel, PathModel, ScatterplotModel, SolidPolygonModel, initializeLayer, -} from "./layer"; +} from "./layer.js"; export { BaseExtensionModel, BrushingExtension, CollisionFilterExtension, initializeExtension, -} from "./extension"; +} from "./extension.js"; diff --git a/src/model/layer.ts b/src/model/layer.ts index 97ff07e6..dfb90ac5 100644 --- a/src/model/layer.ts +++ b/src/model/layer.ts @@ -17,10 +17,10 @@ import { } from "@geoarrow/deck.gl-layers"; import type { WidgetModel } from "@jupyter-widgets/base"; import * as arrow from "apache-arrow"; -import { parseParquetBuffers } from "../parquet"; -import { loadChildModels } from "../util"; -import { BaseModel } from "./base"; -import { BaseExtensionModel, initializeExtension } from "./extension"; +import { parseParquetBuffers } from "../parquet.js"; +import { loadChildModels } from "../util.js"; +import { BaseModel } from "./base.js"; +import { BaseExtensionModel, initializeExtension } from "./extension.js"; export abstract class BaseLayerModel extends BaseModel { protected table: arrow.Table; diff --git a/src/tooltip/index.ts b/src/tooltip/index.ts index fae099e4..0a309eff 100644 --- a/src/tooltip/index.ts +++ b/src/tooltip/index.ts @@ -1,5 +1,5 @@ -import { TooltipContent } from "@deck.gl/core/typed/lib/tooltip"; -import { GeoArrowPickingInfo } from "@geoarrow/deck.gl-layers/dist/types"; +import { TooltipContent } from "@deck.gl/core/typed/lib/tooltip.js"; +import type { GeoArrowPickingInfo } from "@geoarrow/deck.gl-layers"; import "./index.css"; diff --git a/tsconfig.json b/tsconfig.json index d57c5a04..e72f7e13 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,9 @@ "include": ["src/**/*"], "exclude": ["node_modules"], "compilerOptions": { + // Note: setting these two to NodeNext this breaks typing for apache-arrow "moduleResolution": "node", + // "module": "NodeNext", "outDir": "./dist", "allowJs": true, "target": "ES2020",