Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
underbluewaters committed Sep 15, 2023
1 parent 9c99f8a commit b648672
Show file tree
Hide file tree
Showing 48 changed files with 3,895 additions and 120 deletions.
21 changes: 21 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@
"problemMatcher": [],
"label": "Start Database & Redis",
"detail": "docker-compose up -d"
},
{
"type": "npm",
"script": "watch",
"path": "packages/mapbox-gl-esri-sources/",
"problemMatcher": [],
"label": "Rollup: mapbox-gl-esri-sources",
"runOptions": {
"runOn": "folderOpen"
}
},
{
"type": "typescript",
"runOptions": {
"runOn": "folderOpen"
},
"tsconfig": "packages/mapbox-gl-esri-sources/tsconfig.json",
"option": "watch",
"problemMatcher": ["$tsc-watch"],
"group": "build",
"label": "TypeScript: watch mapbox-gl-esri-sources"
}
]
}
11 changes: 11 additions & 0 deletions packages/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"lodash.setwith": "^4.3.2",
"lodash.sortby": "^4.7.0",
"lru-cache": "^6.0.0",
"mapbox-expression": "^0.0.3",
"mapbox-gl": "2.15",
"mapbox-gl-arcgis-featureserver": "file://Users/cburt/src/mapbox-gl-arcgis-featureserver",
"mapbox-gl-draw-rectangle-mode": "^1.0.4",
Expand Down
34 changes: 33 additions & 1 deletion packages/client/src/admin/data/DataSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from "react";
import React, { useCallback, useEffect, useState } from "react";
import {
Link,
Route,
Expand All @@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next";
import DataUploadDropzone from "../uploads/DataUploadDropzone";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import Legend, { LegendItem } from "../../dataLayers/Legend";

const LazyArcGISBrowser = React.lazy(
() =>
Expand All @@ -35,6 +36,28 @@ export default function DataSettings() {
},
});

const [legendState, setLegendState] = useState<{ items: LegendItem[] }>({
items: [],
});

useEffect(() => {
if (mapContext.legends) {
// TODO: this does't really handle WMS or dynamic map services
const visibleLegends: LegendItem[] = [];
for (const id in mapContext.layerStatesByTocStaticId) {
if (mapContext.layerStatesByTocStaticId[id].visible) {
const legend = mapContext.legends[id];
if (legend) {
visibleLegends.push(legend);
}
}
}
setLegendState({
items: visibleLegends,
});
}
}, [mapContext.legends, mapContext.layerStatesByTocStaticId]);

return (
<>
<DndProvider backend={HTML5Backend}>
Expand All @@ -49,6 +72,15 @@ export default function DataSettings() {
<LayerAdminSidebar />
</div>
<div className="flex-1 h-full">
<Legend
maxHeight={800}
className="absolute ml-5 top-5 z-10"
items={legendState.items}
hiddenItems={[]}
opacity={{}}
zOrder={{}}
map={mapContext.manager?.map}
/>
{data?.projectBySlug && (
<MapboxMap
bounds={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,7 @@ export default function LayerTableOfContentsItemEditor(
},
});
setStyle(newStyle);
mapContext.manager?.updateLegends(true);
}}
bounds={
item.bounds
Expand Down
146 changes: 134 additions & 12 deletions packages/client/src/admin/data/arcgis/ArcGISCartLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@ import {
import {
DataTableOfContentsItem,
FolderTableOfContentsItem,
ImageList,
LegendItem,
} from "@seasketch/mapbox-gl-esri-sources";
import * as Accordion from "@radix-ui/react-accordion";
import Spinner from "../../../components/Spinner";
import { Layer, Map } from "mapbox-gl";
import {
LegendForGLLayers,
getLegendForGLStyleLayers,
hasGetExpression,
isExpression,
} from "../../../dataLayers/legends/glLegends";
import { memo } from "react";
import SimpleSymbol from "../../../dataLayers/legends/SimpleSymbol";
require("./Accordion.css");

export default function ArcGISCartLegend({
Expand All @@ -19,12 +29,14 @@ export default function ArcGISCartLegend({
loading,
visibleLayerIds,
toggleLayer,
map,
}: {
className?: string;
items: (FolderTableOfContentsItem | DataTableOfContentsItem)[];
loading?: boolean;
visibleLayerIds?: string[];
toggleLayer?: (id: string) => void;
map: Map;
}) {
function onChangeVisibility(id: string) {
if (toggleLayer) {
Expand All @@ -45,7 +57,7 @@ export default function ArcGISCartLegend({
}}
className={`${className} shadow rounded bg-white bg-opacity-90 w-64 text-sm flex flex-col overflow-hidden`}
>
<Accordion.Root type="single" collapsible>
<Accordion.Root type="single" collapsible defaultValue="legend">
<Accordion.Item value="legend">
<Accordion.Header className="flex-none flex p-2">
<Accordion.Trigger className="flex w-full AccordionTrigger">
Expand Down Expand Up @@ -78,22 +90,78 @@ export default function ArcGISCartLegend({
);
} else {
if (!item.legend) {
return (
<li
className={`${
visible ? "text-black" : "text-gray-400"
}`}
>
{item.label}
</li>
);
if (item.glStyle) {
if (styleHasDataExpression(item.glStyle.layers)) {
return (
<li
key={item.id}
className={`max-w-full ${
visible ? "opacity-100" : "opacity-50"
}`}
>
<div className="flex items-center space-x-1 mb-0.5">
<span className="truncate flex-1">
{item.label}
</span>
<Toggle
onChange={onChangeVisibility(item.id)}
visible={visible}
/>
</div>
<ul className="pl-2 text-sm mb-1">
<li
className="flex items-center space-x-2"
// key={legendItem.id}
></li>
</ul>
</li>
);
} else {
return (
<li
className={`flex items-center space-x-2 max-w-full ${
visible ? "opacity-100" : "opacity-50"
}`}
>
<SimpleLegendIconFromStyle
style={item.glStyle}
map={map}
/>
<span className="truncate flex-1">
{item.label}
</span>
<Toggle
onChange={onChangeVisibility(item.id)}
visible={visible}
/>
</li>
);
}
} else {
return (
<li
key={item.id}
className={`flex items-center space-x-2 max-w-full ${
visible ? "opacity-100" : "opacity-50"
}`}
>
<span className="truncate flex-1">
{item.label}
</span>
<Toggle
onChange={onChangeVisibility(item.id)}
visible={visible}
/>
</li>
);
}
} else if (item.legend && item.legend.length === 1) {
const legendItem = item.legend[0];
return (
<li
key={item.id}
className={`flex items-center space-x-2 max-w-full ${
visible ? "text-black" : "text-gray-400"
visible ? "opacity-100" : "opacity-50"
}`}
>
<LegendImage item={legendItem} />
Expand All @@ -109,7 +177,7 @@ export default function ArcGISCartLegend({
<li
key={item.id}
className={`max-w-full ${
visible ? "text-black" : "text-gray-400"
visible ? "opacity-100" : "opacity-50"
}`}
>
<div className="flex items-center space-x-1 mb-0.5">
Expand Down Expand Up @@ -162,6 +230,7 @@ function LegendImage({
return (
<img
className={`${className}`}
alt={item.label}
src={item.imageUrl}
width={
(item.imageWidth || 20) /
Expand Down Expand Up @@ -193,3 +262,56 @@ function Toggle({
</button>
);
}

export function styleHasDataExpression(style: Layer[]) {
for (const layer of style) {
if (
style.length > 1 &&
layer.filter &&
isExpression(layer.filter) &&
hasGetExpression(layer.filter)
) {
return true;
} else if (layer.paint) {
for (const key in layer.paint) {
if (isExpression((layer.paint as any)[key])) {
return hasGetExpression((layer.paint as any)[key]);
}
}
} else if (layer.layout) {
for (const key in layer.layout) {
if (isExpression((layer.layout as any)[key])) {
return hasGetExpression((layer.layout as any)[key]);
}
}
}
}
return false;
}

type Expression = [string, Expression | string | number | boolean | null];

const SimpleLegendIconFromStyle = memo(
function _SimpleLegendIconFromStyle(props: {
style: {
layers: Layer[];
imageList?: ImageList;
};
map: Map;
}) {
let data: LegendForGLLayers | undefined;
try {
data = getLegendForGLStyleLayers(props.style.layers, "vector");
} catch (e) {
// Do nothing
}
const simpleSymbol = data?.type === "SimpleGLLegend" ? data.symbol : null;
return (
<div className="w-5 h-5 flex items-center justify-center bg-transparent">
{simpleSymbol ? (
<SimpleSymbol map={props.map} data={simpleSymbol} />
) : null}
</div>
);
}
);
Loading

0 comments on commit b648672

Please sign in to comment.