@teamimpact/veda-ui-blocks 0.1.0-beta.11 → 0.1.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +33 -11
  2. package/dist/index.js +212 -188
  3. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -318,9 +318,6 @@ type TagProps = {
318
318
  */
319
319
  declare function Tag({ variant, size, color, bgColor, borderColor, textColor, icon, onClose, children, className, }: TagProps): react_jsx_runtime.JSX.Element;
320
320
 
321
- /** NASA Blue Marble imagery basemap */
322
- declare const NASA_BLUE_MARBLE_BASEMAP_STYLE: StyleSpecification;
323
-
324
321
  interface GeoConfigProviderProps {
325
322
  stacApiUrl?: string;
326
323
  titilerBaseUrl?: string;
@@ -337,20 +334,22 @@ type TileMatrixSet = "WebMercatorQuad" | "GDALWebMercatorQuad" | "WorldCRS84Quad
337
334
  type StacRasterLayerConfig = {
338
335
  type: "raster";
339
336
  collectionId: string;
337
+ collectionAssetId: string;
340
338
  dateRange: DateRange;
341
339
  tileMatrixSet?: TileMatrixSet;
340
+ hideLegend?: boolean;
342
341
  };
343
342
  interface GradientLegendProps {
344
343
  type: "gradient";
345
- colorStops: string[];
344
+ colorStops?: string[];
346
345
  title?: string;
347
346
  description?: string;
348
347
  provider?: string;
349
348
  spatialExtent?: string;
350
349
  timeDensity?: string;
351
350
  unit?: string;
352
- min: number;
353
- max: number;
351
+ min?: number;
352
+ max?: number;
354
353
  }
355
354
  type LegendProps = GradientLegendProps & {
356
355
  initialExpanded?: boolean;
@@ -358,9 +357,32 @@ type LegendProps = GradientLegendProps & {
358
357
 
359
358
  declare function Legend({ initialExpanded, ...props }: LegendProps): react_jsx_runtime.JSX.Element;
360
359
 
360
+ /** NASA Blue Marble imagery basemap */
361
+ declare const NASA_BLUE_MARBLE_BASEMAP_STYLE: StyleSpecification;
362
+ /** Carto Dark Matter raster basemap without labels */
363
+ declare const CARTO_DARK_BASEMAP_STYLE: StyleSpecification;
364
+ /**
365
+ * Carto Dark Matter vector style with labels.
366
+ * Data layers should render beneath `waterway_label`.
367
+ */
368
+ declare const CARTO_DARK_WITH_LABELS_BASEMAP_STYLE = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
369
+ declare const CARTO_LABELS_LAYER_ID = "waterway_label";
370
+ /**
371
+ * OpenFreeMap Dark vector style.
372
+ * Data layers should render beneath `highway_name_other`.
373
+ */
374
+ declare const OPEN_FREE_MAP_DARK_BASEMAP_STYLE = "https://tiles.openfreemap.org/styles/dark";
375
+ declare const OPEN_FREE_MAP_LABELS_LAYER_ID = "highway_name_other";
376
+ /**
377
+ * Maps vector basemap styles to the first label layer that should render
378
+ * above data overlays.
379
+ */
380
+ declare const BASEMAP_LABEL_LAYER_IDS: Record<string, string>;
381
+ type BaseMaps = keyof typeof BASEMAP_LABEL_LAYER_IDS;
382
+
361
383
  type StacCompareMapProps = {
362
- /** MapLibre base map style URL or style object. */
363
- baseMapStyle?: string | StyleSpecification;
384
+ /** MapLibre base map style URL or style object. Defaults to Carto Dark with labels. For known vector styles the label layer is derived automatically so STAC data renders beneath labels. */
385
+ baseMapStyle?: BaseMaps | StyleSpecification;
364
386
  /** Initial map center (longitude/latitude), zoom level, and optional bearing/pitch. MapLibre applies defaults for any properties not provided. */
365
387
  initialViewState?: Partial<ViewState>;
366
388
  /** Layer config for the left panel. */
@@ -394,8 +416,8 @@ type StacCompareMapProps = {
394
416
  declare function StacCompareMap({ baseMapStyle, initialViewState, leftLayerConfig, rightLayerConfig, className, }: StacCompareMapProps): react_jsx_runtime.JSX.Element;
395
417
 
396
418
  type StacSingleLayerMapProps = {
397
- /** MapLibre base map style URL or style object. Defaults to NASA Blue Marble imagery. */
398
- baseMapStyle?: string | StyleSpecification;
419
+ /** MapLibre base map style URL or style object. Defaults to Carto Dark with labels. For known vector styles the label layer is derived automatically so STAC data renders beneath labels. */
420
+ baseMapStyle?: BaseMaps | StyleSpecification;
399
421
  /** Initial map center (longitude/latitude), zoom level, and optional bearing/pitch. MapLibre applies defaults for any properties not provided. */
400
422
  initialViewState?: Partial<ViewState>;
401
423
  /** STAC collection and date range to fetch and display as a raster tile layer. */
@@ -420,4 +442,4 @@ type StacSingleLayerMapProps = {
420
442
  */
421
443
  declare function StacSingleLayerMap({ baseMapStyle, initialViewState, layerConfig, }: StacSingleLayerMapProps): react_jsx_runtime.JSX.Element;
422
444
 
423
- export { Banner, type BannerProps, Card, CardCTA, type CardCTAProps, CardDetailed, type CardDetailedProps, CardMini, type CardMiniProps, type CardProps, CardSimple, type CardSimpleProps, Footer, type FooterProps, GeoConfigProvider, type GeoConfigProviderProps, Header, type HeaderProps, Legend, type LegendProps, Link, type LinkProps, NASA_BLUE_MARBLE_BASEMAP_STYLE, StacCompareMap, type StacCompareMapProps, StacSingleLayerMap, type StacSingleLayerMapProps, Tag, type TagProps };
445
+ export { Banner, type BannerProps, CARTO_DARK_BASEMAP_STYLE, CARTO_DARK_WITH_LABELS_BASEMAP_STYLE, CARTO_LABELS_LAYER_ID, Card, CardCTA, type CardCTAProps, CardDetailed, type CardDetailedProps, CardMini, type CardMiniProps, type CardProps, CardSimple, type CardSimpleProps, Footer, type FooterProps, GeoConfigProvider, type GeoConfigProviderProps, Header, type HeaderProps, Legend, type LegendProps, Link, type LinkProps, NASA_BLUE_MARBLE_BASEMAP_STYLE, OPEN_FREE_MAP_DARK_BASEMAP_STYLE, OPEN_FREE_MAP_LABELS_LAYER_ID, StacCompareMap, type StacCompareMapProps, StacSingleLayerMap, type StacSingleLayerMapProps, Tag, type TagProps };
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ var isArray = (as) => Array.isArray(as);
26
26
  var isEmptyArray = (as) => isArray(as) && as.length === 0;
27
27
  var isNonEmptyArray = (as) => isArray(as) && as.length > 0;
28
28
  var getTypedValues = Object.values;
29
+ var getTypedEntries = Object.entries;
29
30
 
30
31
  // src/utils/component-utils.tsx
31
32
  var get_external_anchor_props = (isExternal) => isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {};
@@ -737,43 +738,17 @@ function Tag({
737
738
  ] });
738
739
  }
739
740
 
741
+ // src/geo/GeoConfigProvider.tsx
742
+ import { StacApiProvider } from "@developmentseed/stac-react";
743
+ import { createContext, useContext } from "react";
744
+
740
745
  // src/geo/constants.ts
741
- var NASA_BLUE_MARBLE_BASEMAP_STYLE = {
742
- version: 8,
743
- sources: {
744
- "nasa-blue-marble": {
745
- type: "raster",
746
- tiles: [
747
- "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpeg"
748
- ],
749
- tileSize: 256,
750
- maxzoom: 8,
751
- attribution: "Imagery courtesy NASA EOSDIS/GIBS"
752
- }
753
- },
754
- layers: [
755
- {
756
- id: "nasa-blue-marble",
757
- type: "raster",
758
- source: "nasa-blue-marble"
759
- }
760
- ]
761
- };
762
746
  var STAC_API_URL = "https://openveda.cloud/api/stac";
763
747
  var TITILER_BASE_URL = "https://openveda.cloud/api/raster";
764
- var DEFAULT_RENDER_PARAMS = {
765
- assets: ["cog_default"],
766
- rescale: "0,0.0001",
767
- resampling: "bilinear",
768
- colormap_name: "viridis",
769
- bidx: [1]
770
- };
771
748
  var DEFAULT_TILE_MATRIX_SET = "WebMercatorQuad";
772
749
  var STAC_COLORSTOP_RGBA_ARG_COUNT = 4;
773
750
 
774
751
  // src/geo/GeoConfigProvider.tsx
775
- import { StacApiProvider } from "@developmentseed/stac-react";
776
- import { createContext, useContext } from "react";
777
752
  import { jsx as jsx14 } from "react/jsx-runtime";
778
753
  var GeoConfigContext = createContext(null);
779
754
  function useGeoConfig() {
@@ -792,7 +767,7 @@ function GeoConfigProvider({
792
767
  // src/geo/Legend/Legend.tsx
793
768
  import { Icon as Icon3 } from "@trussworks/react-uswds";
794
769
  import { useState as useState3 } from "react";
795
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
770
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
796
771
  function Legend({ initialExpanded = false, ...props }) {
797
772
  const { title, description, unit, min, max, colorStops, provider, spatialExtent, timeDensity } = props;
798
773
  const [isExpanded, setIsExpanded] = useState3(initialExpanded);
@@ -815,19 +790,21 @@ function Legend({ initialExpanded = false, ...props }) {
815
790
  }
816
791
  )
817
792
  ] }),
818
- /* @__PURE__ */ jsx15("div", { className: "blocks-legend__gradient-bar", children: /* @__PURE__ */ jsx15(
819
- "div",
820
- {
821
- className: "blocks-legend__gradient",
822
- style: {
823
- background: `linear-gradient(to right, ${colorStops.join(", ")})`
793
+ colorStops && /* @__PURE__ */ jsxs13(Fragment3, { children: [
794
+ /* @__PURE__ */ jsx15("div", { className: "blocks-legend__gradient-bar", children: /* @__PURE__ */ jsx15(
795
+ "div",
796
+ {
797
+ className: "blocks-legend__gradient",
798
+ style: {
799
+ background: `linear-gradient(to right, ${colorStops.join(", ")})`
800
+ }
824
801
  }
825
- }
826
- ) }),
827
- /* @__PURE__ */ jsxs13("div", { className: "blocks-legend__labels", children: [
828
- /* @__PURE__ */ jsx15("span", { className: "blocks-legend__min", children: min }),
829
- /* @__PURE__ */ jsx15("span", { className: "blocks-legend__unit", children: unit }),
830
- /* @__PURE__ */ jsx15("span", { className: "blocks-legend__max", children: max })
802
+ ) }),
803
+ isDefined(min) && isDefined(max) && /* @__PURE__ */ jsxs13("div", { className: "blocks-legend__labels", children: [
804
+ /* @__PURE__ */ jsx15("span", { className: "blocks-legend__min", children: min }),
805
+ /* @__PURE__ */ jsx15("span", { className: "blocks-legend__unit", children: unit }),
806
+ /* @__PURE__ */ jsx15("span", { className: "blocks-legend__max", children: max })
807
+ ] })
831
808
  ] }),
832
809
  isExpanded && description && /* @__PURE__ */ jsx15("div", { className: "blocks-legend__description", children: /* @__PURE__ */ jsx15("div", { className: "blocks-legend__description-inner", children: description }) })
833
810
  ] });
@@ -850,7 +827,17 @@ function AttributionMapControl({ position = "top-right" }) {
850
827
  return null;
851
828
  }
852
829
 
853
- // src/geo/utils/formatDateRange.ts
830
+ // src/geo/utils/stac.formatters.ts
831
+ var formatSpatialExtent = (spatialExtent) => {
832
+ const [minX, minY, maxX, maxY] = spatialExtent?.bbox?.[0] ?? [];
833
+ if (minX === void 0 || minY === void 0 || maxX === void 0 || maxY === void 0)
834
+ return void 0;
835
+ return maxX - minX >= 359 && maxY - minY >= 179 ? "global" : "regional";
836
+ };
837
+ function formatDateToISODatetime(date, isEndOfDay) {
838
+ const datetime = /* @__PURE__ */ new Date(`${date}${isEndOfDay ? "T23:59:59Z" : "T00:00:00Z"}`);
839
+ return datetime.toISOString();
840
+ }
854
841
  function formatDateRange(dateRange) {
855
842
  const from = /* @__PURE__ */ new Date(`${dateRange.from}T00:00:00Z`);
856
843
  const to = /* @__PURE__ */ new Date(`${dateRange.to}T00:00:00Z`);
@@ -875,6 +862,18 @@ function formatDateRange(dateRange) {
875
862
  });
876
863
  return `${fromStr} \u2013 ${toStr}`;
877
864
  }
865
+ var formatRenderPropertiesToQuery = (renderProperties) => {
866
+ const query = new URLSearchParams();
867
+ getTypedEntries(renderProperties).forEach(([key, value]) => {
868
+ if (!isDefined(value)) return;
869
+ if (isArray(value)) {
870
+ value.map((v) => query.append(key, String(v)));
871
+ } else {
872
+ query.append(key, String(value));
873
+ }
874
+ });
875
+ return query.toString();
876
+ };
878
877
 
879
878
  // src/geo/DateChip/DateChip.tsx
880
879
  import { jsx as jsx16 } from "react/jsx-runtime";
@@ -886,43 +885,11 @@ function DateChip({ left, right }) {
886
885
  // src/geo/hooks/useStacRasterLayer.ts
887
886
  import { useCollection } from "@developmentseed/stac-react";
888
887
 
889
- // src/geo/hooks/useMosaicTiles.ts
888
+ // src/geo/hooks/useRasterTiles.ts
890
889
  import { useQuery } from "@tanstack/react-query";
891
890
 
892
- // src/geo/api/titiler/fetchTilejson.ts
893
- async function fetchTilejson(tilejsonUrl, queryString) {
894
- const response = await fetch(`${tilejsonUrl}?${queryString}`);
895
- if (!response.ok) {
896
- const error = { detail: `Failed to fetch tilejson: ${response.statusText}` };
897
- throw error;
898
- }
899
- const tilejson = await response.json();
900
- if (!tilejson.tiles?.[0]) {
901
- const error = { detail: "No tile URL found in response" };
902
- throw error;
903
- }
904
- return tilejson.tiles[0];
905
- }
906
-
907
- // src/geo/api/titiler/mosaicLinks.ts
908
- function findTileLink(links) {
909
- return links.find((link) => link.rel === "tiles") ?? links.at(1) ?? links.at(0) ?? null;
910
- }
911
- function buildTilejsonUrl(tileLinkHref, tileMatrixSet) {
912
- return tileLinkHref.replace("/{tileMatrixSetId}", `/${tileMatrixSet}`);
913
- }
914
- function extractTileUrl(registrationResponse, tileMatrixSet) {
915
- const tileLink = findTileLink(registrationResponse.links);
916
- if (!tileLink?.href) {
917
- const error = { detail: "No tile URL found in registration response" };
918
- throw error;
919
- }
920
- const tilejsonUrl = buildTilejsonUrl(tileLink.href, tileMatrixSet);
921
- return tilejsonUrl;
922
- }
923
-
924
- // src/geo/api/titiler/registerMosaic.ts
925
- async function registerMosaic(request) {
891
+ // src/geo/api/titiler/rasterSearchRegister.ts
892
+ async function rasterSearchRegister(request) {
926
893
  const { rasterApiUrl, collectionId, datetime } = request;
927
894
  const response = await fetch(`${rasterApiUrl}/searches/register`, {
928
895
  method: "POST",
@@ -939,93 +906,70 @@ async function registerMosaic(request) {
939
906
  };
940
907
  throw error;
941
908
  }
942
- return response.json();
909
+ const responseJson = await response.json();
910
+ return responseJson;
943
911
  }
944
912
 
945
- // src/geo/api/titiler/fetchMosaicTileUrl.ts
946
- var TILE_PARAMS = {
947
- ASSETS: "assets",
948
- RESCALE: "rescale",
949
- RESAMPLING: "resampling",
950
- COLORMAP_NAME: "colormap_name",
951
- BIDX: "bidx"
952
- };
953
- function buildQueryParams(sourceParams) {
954
- const params = new URLSearchParams();
955
- if (sourceParams.assets) params.append(TILE_PARAMS.ASSETS, sourceParams.assets.join(","));
956
- if (sourceParams.rescale) params.append(TILE_PARAMS.RESCALE, sourceParams.rescale);
957
- if (sourceParams.resampling) params.append(TILE_PARAMS.RESAMPLING, sourceParams.resampling);
958
- if (sourceParams.colormap_name)
959
- params.append(TILE_PARAMS.COLORMAP_NAME, sourceParams.colormap_name);
960
- if (sourceParams.bidx) params.append(TILE_PARAMS.BIDX, sourceParams.bidx.join(","));
961
- return params;
962
- }
963
- async function fetchMosaicTileUrl(request) {
964
- const { sourceParams, tileMatrixSet } = request;
965
- const queryParams = buildQueryParams(sourceParams);
966
- const queryString = queryParams.toString();
967
- const registrationResponse = await registerMosaic(request);
968
- const tilejsonUrl = extractTileUrl(registrationResponse, tileMatrixSet);
969
- return fetchTilejson(tilejsonUrl, queryString);
913
+ // src/geo/api/titiler/rasterSearchTileJson.ts
914
+ async function rasterSearchTileJson({
915
+ rasterApiUrl,
916
+ searchId,
917
+ renderProperties,
918
+ tileMatrixSet
919
+ }) {
920
+ const tilejsonUrl = `${rasterApiUrl}/searches/${searchId}/${tileMatrixSet}/tilejson.json`;
921
+ const queryString = formatRenderPropertiesToQuery(renderProperties);
922
+ const response = await fetch(`${tilejsonUrl}?${queryString}`);
923
+ if (!response.ok) {
924
+ const error = { detail: `Failed to fetch tilejson: ${response.statusText}` };
925
+ throw error;
926
+ }
927
+ const responseJson = await response.json();
928
+ return responseJson;
970
929
  }
971
930
 
972
- // src/geo/utils/resolveAssetName.ts
973
- function resolveAssetName(collection) {
974
- if (collection.renders?.dashboard?.assets?.[0]) {
975
- return collection.renders.dashboard.assets[0];
976
- }
977
- if (collection.item_assets?.cog_default) {
978
- return "cog_default";
979
- }
980
- if (collection.item_assets) {
981
- const firstKey = Object.keys(collection.item_assets)[0];
982
- if (firstKey) return firstKey;
983
- }
984
- return "cog_default";
931
+ // src/geo/api/titiler/fetchRasterTileUrl.ts
932
+ async function fetchRasterTileUrl(request) {
933
+ const { collectionId, datetime, rasterApiUrl, renderProperties, tileMatrixSet } = request;
934
+ const { id: searchId } = await rasterSearchRegister({ collectionId, datetime, rasterApiUrl });
935
+ const { tiles } = await rasterSearchTileJson({
936
+ rasterApiUrl,
937
+ searchId,
938
+ renderProperties,
939
+ tileMatrixSet
940
+ });
941
+ return tiles[0];
985
942
  }
986
943
 
987
- // src/geo/hooks/useMosaicTiles.ts
988
- function useMosaicTiles(rasterLayerConfig, collection) {
944
+ // src/geo/hooks/useRasterTiles.ts
945
+ function useRasterTiles(rasterLayerConfig, collection) {
989
946
  const { titilerBaseUrl } = useGeoConfig();
990
- const { collectionId, dateRange, tileMatrixSet = DEFAULT_TILE_MATRIX_SET } = rasterLayerConfig;
991
- const datetime = dateRange.from === dateRange.to ? dateRange.from : `${dateRange.from}/${dateRange.to}`;
992
- const assetName = collection ? resolveAssetName(collection) : DEFAULT_RENDER_PARAMS.assets[0];
993
- const colormapName = collection?.renders?.dashboard?.colormap_name ?? DEFAULT_RENDER_PARAMS.colormap_name;
994
- const rescale = collection?.renders?.dashboard?.rescale?.join(",") ?? DEFAULT_RENDER_PARAMS.rescale;
995
- const resampling = collection?.renders?.dashboard?.resampling ?? DEFAULT_RENDER_PARAMS.resampling;
996
- const request = {
997
- rasterApiUrl: titilerBaseUrl,
947
+ const {
998
948
  collectionId,
999
- datetime,
1000
- sourceParams: {
1001
- ...DEFAULT_RENDER_PARAMS,
1002
- assets: [assetName],
1003
- colormap_name: colormapName,
1004
- rescale,
1005
- resampling
1006
- },
1007
- tileMatrixSet
1008
- };
949
+ collectionAssetId,
950
+ dateRange,
951
+ tileMatrixSet = DEFAULT_TILE_MATRIX_SET
952
+ } = rasterLayerConfig;
953
+ const datetime = `${formatDateToISODatetime(dateRange.from)}/${formatDateToISODatetime(dateRange.to, true)}`;
954
+ const renderProperties = collection?.renders?.[collectionAssetId] ?? {};
1009
955
  return useQuery({
1010
- queryKey: [collectionId, dateRange, titilerBaseUrl, assetName],
1011
- queryFn: () => fetchMosaicTileUrl(request),
956
+ queryKey: [collectionId, collectionAssetId, datetime, titilerBaseUrl],
957
+ queryFn: () => fetchRasterTileUrl({
958
+ rasterApiUrl: titilerBaseUrl,
959
+ collectionId,
960
+ datetime,
961
+ renderProperties,
962
+ tileMatrixSet
963
+ }),
1012
964
  staleTime: 30 * 60 * 1e3,
1013
965
  gcTime: 60 * 60 * 1e3,
1014
- enabled: !!collectionId && !!dateRange?.from && !!dateRange?.to
966
+ enabled: isDefined(collection) && !isEmptyObject(collection) && !isEmptyObject(renderProperties)
1015
967
  });
1016
968
  }
1017
969
 
1018
970
  // src/geo/hooks/useStacRasterLegend.ts
1019
971
  import { useQuery as useQuery2 } from "@tanstack/react-query";
1020
972
 
1021
- // src/geo/utils/stac.formatters.ts
1022
- var formatSpatialExtent = (spatialExtent) => {
1023
- const [minX, minY, maxX, maxY] = spatialExtent?.bbox?.[0] ?? [];
1024
- if (minX === void 0 || minY === void 0 || maxX === void 0 || maxY === void 0)
1025
- return void 0;
1026
- return maxX - minX >= 359 && maxY - minY >= 179 ? "global" : "regional";
1027
- };
1028
-
1029
973
  // src/geo/utils/stac.typeguards.ts
1030
974
  var isRgbaColorStopMap = (value) => {
1031
975
  return !isNil(value) && isObject(value) && !isEmptyObject(value) && getTypedValues(value).every(
@@ -1034,53 +978,58 @@ var isRgbaColorStopMap = (value) => {
1034
978
  };
1035
979
 
1036
980
  // src/geo/utils/extractLegendProps.ts
1037
- function extractLegendProps(collection, colorStopMap) {
1038
- const rescale = collection.renders?.dashboard?.rescale?.[0];
1039
- if (!rescale) return null;
1040
- if (!isRgbaColorStopMap(colorStopMap)) return null;
1041
- const colorStops = getTypedValues(colorStopMap).map((rgba) => `rgba(${rgba.join(",")})`);
981
+ function extractLegendProps(collection, collectionAssetId, colorStopMap) {
982
+ const rescale = collection.renders?.[collectionAssetId]?.rescale?.[0];
983
+ let colorStops;
984
+ if (isRgbaColorStopMap(colorStopMap)) {
985
+ colorStops = getTypedValues(colorStopMap).map((rgba) => `rgba(${rgba.join(",")})`);
986
+ }
1042
987
  const title = collection.title;
1043
988
  const description = collection.description;
1044
989
  const provider = collection.providers?.[0]?.name;
1045
990
  const timeDensity = collection.properties?.["dashboard:time_density"];
1046
991
  const spatialExtent = formatSpatialExtent(collection.extent?.spatial);
1047
- const assetName = resolveAssetName(collection);
1048
- const unit = collection.item_assets?.[assetName]?.["raster:bands"]?.[0]?.unit;
992
+ const unit = collection.item_assets?.[collectionAssetId]?.["raster:bands"]?.[0]?.unit;
1049
993
  return {
1050
994
  type: "gradient",
1051
- ...title && { title },
1052
- ...description && { description },
1053
- min: rescale[0],
1054
- max: rescale[1],
1055
- colorStops,
1056
- ...provider && { provider },
1057
- ...timeDensity && { timeDensity },
1058
- ...spatialExtent && { spatialExtent },
1059
- ...unit && { unit }
995
+ ...{ title },
996
+ ...{ description },
997
+ // TODO 271: existing datasets for use do not include rescale, are we certain this is required
998
+ // is there another way to determine min and max
999
+ ...isDefined(rescale) ? { min: rescale[0] } : {},
1000
+ ...isDefined(rescale) ? { max: rescale[1] } : {},
1001
+ ...{ colorStops },
1002
+ ...{ provider },
1003
+ ...{ timeDensity },
1004
+ ...{ spatialExtent },
1005
+ ...{ unit }
1060
1006
  };
1061
1007
  }
1062
1008
 
1063
1009
  // src/geo/hooks/useStacRasterLegend.ts
1064
- function useStacRasterLegend(collection) {
1010
+ function useStacRasterLegend(collectionAssetId, collection, options) {
1065
1011
  const { titilerBaseUrl } = useGeoConfig();
1012
+ const { hideLegend } = options ?? {};
1066
1013
  const query = useQuery2({
1067
- queryKey: ["legend", collection, titilerBaseUrl],
1014
+ queryKey: ["legend", collection, collectionAssetId, titilerBaseUrl],
1068
1015
  queryFn: async () => {
1069
1016
  if (!collection) return null;
1070
- const colormapName = collection?.renders?.dashboard?.colormap_name;
1071
- if (!colormapName) return null;
1072
- const response = await fetch(`${titilerBaseUrl}/colorMaps/${colormapName}`);
1073
- if (!response.ok) {
1074
- const error = {
1075
- detail: `Colormap "${colormapName}" not found (HTTP ${response.status})`,
1076
- status: response.status
1077
- };
1078
- throw error;
1017
+ let colormap;
1018
+ const colormapName = collection?.renders?.[collectionAssetId]?.colormap_name;
1019
+ if (colormapName) {
1020
+ const response = await fetch(`${titilerBaseUrl}/colorMaps/${colormapName}`);
1021
+ if (!response.ok) {
1022
+ const error = {
1023
+ detail: `Colormap "${colormapName}" not found (HTTP ${response.status})`,
1024
+ status: response.status
1025
+ };
1026
+ throw error;
1027
+ }
1028
+ colormap = await response.json();
1079
1029
  }
1080
- const colorMap = await response.json();
1081
- return extractLegendProps(collection, colorMap);
1030
+ return extractLegendProps(collection, collectionAssetId, colormap);
1082
1031
  },
1083
- enabled: !!collection
1032
+ enabled: !!collection && !hideLegend
1084
1033
  });
1085
1034
  return {
1086
1035
  legend: query.data ?? null,
@@ -1091,7 +1040,7 @@ function useStacRasterLegend(collection) {
1091
1040
 
1092
1041
  // src/geo/hooks/useStacRasterLayer.ts
1093
1042
  function useStacRasterLayer(rasterLayerConfig) {
1094
- const { collectionId } = rasterLayerConfig;
1043
+ const { collectionId, collectionAssetId, hideLegend } = rasterLayerConfig;
1095
1044
  const {
1096
1045
  collection,
1097
1046
  isLoading: isLoadingCollection,
@@ -1102,12 +1051,12 @@ function useStacRasterLayer(rasterLayerConfig) {
1102
1051
  data: tiles,
1103
1052
  isLoading: tilesetLoading,
1104
1053
  error: tilesetError
1105
- } = useMosaicTiles(rasterLayerConfig, extCollection);
1054
+ } = useRasterTiles(rasterLayerConfig, extCollection);
1106
1055
  const {
1107
1056
  legend,
1108
1057
  isLoading: isLoadingLegend,
1109
1058
  error: legendError
1110
- } = useStacRasterLegend(extCollection);
1059
+ } = useStacRasterLegend(collectionAssetId, extCollection, { hideLegend });
1111
1060
  return {
1112
1061
  mapSource: {
1113
1062
  tiles: tiles ?? null
@@ -1118,6 +1067,53 @@ function useStacRasterLayer(rasterLayerConfig) {
1118
1067
  };
1119
1068
  }
1120
1069
 
1070
+ // src/geo/stac.basemaps.ts
1071
+ var createRasterBasemapStyle = ({
1072
+ id,
1073
+ tiles,
1074
+ maxzoom,
1075
+ attribution
1076
+ }) => ({
1077
+ version: 8,
1078
+ sources: {
1079
+ [id]: {
1080
+ type: "raster",
1081
+ tiles,
1082
+ tileSize: 256,
1083
+ maxzoom,
1084
+ attribution
1085
+ }
1086
+ },
1087
+ layers: [{ id, type: "raster", source: id }]
1088
+ });
1089
+ var NASA_BLUE_MARBLE_BASEMAP_STYLE = createRasterBasemapStyle({
1090
+ id: "nasa-blue-marble",
1091
+ tiles: [
1092
+ "https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/BlueMarble_NextGeneration/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpeg"
1093
+ ],
1094
+ maxzoom: 8,
1095
+ attribution: "Imagery courtesy NASA EOSDIS/GIBS"
1096
+ });
1097
+ var CARTO_DARK_BASEMAP_STYLE = createRasterBasemapStyle({
1098
+ id: "carto-dark",
1099
+ tiles: [
1100
+ "https://a.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
1101
+ "https://b.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
1102
+ "https://c.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
1103
+ "https://d.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png"
1104
+ ],
1105
+ maxzoom: 19,
1106
+ attribution: "Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL."
1107
+ });
1108
+ var CARTO_DARK_WITH_LABELS_BASEMAP_STYLE = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
1109
+ var CARTO_LABELS_LAYER_ID = "waterway_label";
1110
+ var OPEN_FREE_MAP_DARK_BASEMAP_STYLE = "https://tiles.openfreemap.org/styles/dark";
1111
+ var OPEN_FREE_MAP_LABELS_LAYER_ID = "highway_name_other";
1112
+ var BASEMAP_LABEL_LAYER_IDS = {
1113
+ [CARTO_DARK_WITH_LABELS_BASEMAP_STYLE]: CARTO_LABELS_LAYER_ID,
1114
+ [OPEN_FREE_MAP_DARK_BASEMAP_STYLE]: OPEN_FREE_MAP_LABELS_LAYER_ID
1115
+ };
1116
+
1121
1117
  // src/geo/StacCompareMap/StacCompareMap.tsx
1122
1118
  import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1123
1119
  var MAP_PANEL_STYLE = {
@@ -1127,12 +1123,13 @@ var MAP_PANEL_STYLE = {
1127
1123
  width: "100%"
1128
1124
  };
1129
1125
  function StacCompareMap({
1130
- baseMapStyle,
1126
+ baseMapStyle = CARTO_DARK_WITH_LABELS_BASEMAP_STYLE,
1131
1127
  initialViewState,
1132
1128
  leftLayerConfig,
1133
1129
  rightLayerConfig,
1134
1130
  className
1135
1131
  }) {
1132
+ const resolvedBeforeLayerId = typeof baseMapStyle === "string" ? BASEMAP_LABEL_LAYER_IDS[baseMapStyle] : void 0;
1136
1133
  const instanceId = useId();
1137
1134
  const leftMapRef = useRef3(null);
1138
1135
  const rightMapRef = useRef3(null);
@@ -1174,7 +1171,7 @@ function StacCompareMap({
1174
1171
  };
1175
1172
  }, []);
1176
1173
  const mapProps = {
1177
- ...baseMapStyle && { mapStyle: baseMapStyle },
1174
+ mapStyle: baseMapStyle,
1178
1175
  ...initialViewState && { initialViewState },
1179
1176
  style: MAP_PANEL_STYLE
1180
1177
  };
@@ -1189,7 +1186,14 @@ function StacCompareMap({
1189
1186
  children: [
1190
1187
  /* @__PURE__ */ jsx17(AttributionMapControl, {}),
1191
1188
  /* @__PURE__ */ jsx17(NavigationControl, { position: "top-left", showCompass: false }),
1192
- left.mapSource.tiles && /* @__PURE__ */ jsx17(Source, { id: leftSourceId, type: "raster", tiles: [left.mapSource.tiles], children: /* @__PURE__ */ jsx17(Layer, { id: leftLayerId, type: "raster" }) })
1189
+ left.mapSource.tiles && /* @__PURE__ */ jsx17(Source, { id: leftSourceId, type: "raster", tiles: [left.mapSource.tiles], children: /* @__PURE__ */ jsx17(
1190
+ Layer,
1191
+ {
1192
+ id: leftLayerId,
1193
+ type: "raster",
1194
+ ...resolvedBeforeLayerId && { beforeId: resolvedBeforeLayerId }
1195
+ }
1196
+ ) })
1193
1197
  ]
1194
1198
  }
1195
1199
  ),
@@ -1202,7 +1206,14 @@ function StacCompareMap({
1202
1206
  onLoad: () => setRightMapLoaded(true),
1203
1207
  children: [
1204
1208
  /* @__PURE__ */ jsx17(AttributionMapControl, {}),
1205
- right.mapSource.tiles && /* @__PURE__ */ jsx17(Source, { id: rightSourceId, type: "raster", tiles: [right.mapSource.tiles], children: /* @__PURE__ */ jsx17(Layer, { id: rightLayerId, type: "raster" }) })
1209
+ right.mapSource.tiles && /* @__PURE__ */ jsx17(Source, { id: rightSourceId, type: "raster", tiles: [right.mapSource.tiles], children: /* @__PURE__ */ jsx17(
1210
+ Layer,
1211
+ {
1212
+ id: rightLayerId,
1213
+ type: "raster",
1214
+ ...resolvedBeforeLayerId && { beforeId: resolvedBeforeLayerId }
1215
+ }
1216
+ ) })
1206
1217
  ]
1207
1218
  }
1208
1219
  ),
@@ -1222,10 +1233,11 @@ import {
1222
1233
  } from "react-map-gl/maplibre";
1223
1234
  import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1224
1235
  function StacSingleLayerMap({
1225
- baseMapStyle = NASA_BLUE_MARBLE_BASEMAP_STYLE,
1236
+ baseMapStyle = CARTO_DARK_WITH_LABELS_BASEMAP_STYLE,
1226
1237
  initialViewState,
1227
1238
  layerConfig
1228
1239
  }) {
1240
+ const resolvedBeforeLayerId = typeof baseMapStyle === "string" ? BASEMAP_LABEL_LAYER_IDS[baseMapStyle] : void 0;
1229
1241
  const instanceId = useId2();
1230
1242
  const sourceId = `${layerConfig.collectionId}-raster-source-${instanceId}`;
1231
1243
  const layerId = `${layerConfig.collectionId}-raster-layer-${instanceId}`;
@@ -1241,7 +1253,14 @@ function StacSingleLayerMap({
1241
1253
  children: [
1242
1254
  /* @__PURE__ */ jsx18(AttributionMapControl, {}),
1243
1255
  /* @__PURE__ */ jsx18(NavigationControl2, { position: "top-left", showCompass: false }),
1244
- mapSource.tiles && /* @__PURE__ */ jsx18(Source2, { id: sourceId, type: "raster", tiles: [mapSource.tiles], children: /* @__PURE__ */ jsx18(Layer2, { id: layerId, type: "raster" }) })
1256
+ mapSource.tiles && /* @__PURE__ */ jsx18(Source2, { id: sourceId, type: "raster", tiles: [mapSource.tiles], children: /* @__PURE__ */ jsx18(
1257
+ Layer2,
1258
+ {
1259
+ id: layerId,
1260
+ type: "raster",
1261
+ ...resolvedBeforeLayerId && { beforeId: resolvedBeforeLayerId }
1262
+ }
1263
+ ) })
1245
1264
  ]
1246
1265
  }
1247
1266
  ),
@@ -1251,6 +1270,9 @@ function StacSingleLayerMap({
1251
1270
  }
1252
1271
  export {
1253
1272
  Banner,
1273
+ CARTO_DARK_BASEMAP_STYLE,
1274
+ CARTO_DARK_WITH_LABELS_BASEMAP_STYLE,
1275
+ CARTO_LABELS_LAYER_ID,
1254
1276
  Card,
1255
1277
  CardCTA,
1256
1278
  CardDetailed,
@@ -1262,6 +1284,8 @@ export {
1262
1284
  Legend,
1263
1285
  Link3 as Link,
1264
1286
  NASA_BLUE_MARBLE_BASEMAP_STYLE,
1287
+ OPEN_FREE_MAP_DARK_BASEMAP_STYLE,
1288
+ OPEN_FREE_MAP_LABELS_LAYER_ID,
1265
1289
  StacCompareMap,
1266
1290
  StacSingleLayerMap,
1267
1291
  Tag
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamimpact/veda-ui-blocks",
3
- "version": "0.1.0-beta.11",
3
+ "version": "0.1.0-beta.12",
4
4
  "description": "UI component library for VEDA-based portals, built on USWDS and React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -67,7 +67,7 @@
67
67
  "storybook": "^10.2.17",
68
68
  "tsup": "^8.5.1",
69
69
  "typescript": "5.9.3",
70
- "vitest": "^4.0.18"
70
+ "vitest": "^4.1.0"
71
71
  },
72
72
  "scripts": {
73
73
  "build": "tsup src/index.ts --format esm --dts && pnpm run build:css",