Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
underbluewaters committed Oct 9, 2023
1 parent ca84c1d commit 51094c5
Show file tree
Hide file tree
Showing 21 changed files with 1,276 additions and 952 deletions.
18 changes: 9 additions & 9 deletions packages/client/src/admin/data/arcgis/ArcGISCartLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import {
import * as Accordion from "@radix-ui/react-accordion";
import Spinner from "../../../components/Spinner";
import { Layer, Map } from "mapbox-gl";
import { compileLegendFromGLStyleLayers } from "../../../dataLayers/legends/compileLegend";
import { memo } from "react";
import SimpleSymbol from "../../../dataLayers/legends/SimpleSymbol";
import { LegendForGLLayers } from "../../../dataLayers/legends/LegendDataModel";
import {
LegendForGLLayers,
buildLegendForGLStyleLayers,
hasGetExpression,
isExpression,
} from "../../../dataLayers/legends/glLegends";
import { memo } from "react";
import SimpleSymbol from "../../../dataLayers/legends/SimpleSymbol";
} from "../../../dataLayers/legends/utils";
require("./Accordion.css");

export default function ArcGISCartLegend({
Expand Down Expand Up @@ -269,19 +269,19 @@ export function styleHasDataExpression(style: Layer[]) {
style.length > 1 &&
layer.filter &&
isExpression(layer.filter) &&
hasGetExpression(layer.filter)
hasGetExpression(layer.filter, true)
) {
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]);
return hasGetExpression((layer.paint as any)[key], key === "filter");
}
}
} 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 hasGetExpression((layer.layout as any)[key], key === "filter");
}
}
}
Expand All @@ -301,7 +301,7 @@ const SimpleLegendIconFromStyle = memo(
}) {
let data: LegendForGLLayers | undefined;
try {
data = buildLegendForGLStyleLayers(props.style.layers, "vector");
data = compileLegendFromGLStyleLayers(props.style.layers, "vector");
} catch (e) {
// Do nothing
}
Expand Down
3 changes: 1 addition & 2 deletions packages/client/src/dataLayers/Legend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
DynamicRenderingSupportOptions,
LegendItem as LegendSymbolItem,
} from "@seasketch/mapbox-gl-esri-sources";
import { LegendForGLLayers } from "./legends/glLegends";
import { LegendForGLLayers } from "./legends/LegendDataModel";
import * as Accordion from "@radix-ui/react-accordion";
import {
CaretDownIcon,
Expand All @@ -15,7 +15,6 @@ import SimpleSymbol from "./legends/SimpleSymbol";
import { Map } from "mapbox-gl";
import LegendBubblePanel from "./legends/LegendBubblePanel";
import LegendGradientPanel from "./legends/LegendGradientPanel";
import { stopsToLinearGradient } from "./legends/utils";
import LegendHeatmapPanel from "./legends/LegendHeatmapPanel";
import LegendListPanel from "./legends/LegendListPanel";
import LegendMarkerSizePanel from "./legends/LegendMarkerSizePanel";
Expand Down
14 changes: 12 additions & 2 deletions packages/client/src/dataLayers/MapContextManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ import LRU from "lru-cache";
import debounce from "lodash.debounce";
import { currentSidebarState } from "../projects/ProjectAppSidebar";
import { ApolloClient, NormalizedCacheObject } from "@apollo/client";
import { buildLegendForGLStyleLayers } from "./legends/glLegends";
import {
compileLegendFromGLStyleLayers,
compileLegendFromGLStyleLayers2,
} from "./legends/compileLegend";
import { LegendItem } from "./Legend";

const rejectAfter = (duration: number) =>
Expand Down Expand Up @@ -2242,11 +2245,18 @@ class MapContextManager {
try {
// TODO: get geostats here from graphql api before requesting
// legend
const legend = buildLegendForGLStyleLayers(
const legend = compileLegendFromGLStyleLayers(
layer.mapboxGlStyles,
sourceType
);

console.log(
compileLegendFromGLStyleLayers2(
layer.mapboxGlStyles,
sourceType
)
);

if (legend) {
newLegendState[id] = {
id,
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/dataLayers/legends/CircleSymbol.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GLLegendCircleSymbol } from "./glLegends";
import { GLLegendCircleSymbol } from "./LegendDataModel";
import { colord, extend } from "colord";
import namesPlugin from "colord/plugins/names";
extend([namesPlugin]);
Expand Down
69 changes: 69 additions & 0 deletions packages/client/src/dataLayers/legends/ExpressionEvaluator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { expression } from "mapbox-gl/dist/style-spec/index.es.js";
import { Expression as MapboxExpression, StyleFunction } from "mapbox-gl";
import { Feature } from "geojson";

const expressionGlobals = {
zoom: 14,
};

/** A color as returned by a Mapbox style expression. All values are in [0, 1] */
export interface RGBA {
r: number;
g: number;
b: number;
a: number;
}

interface TypeMap {
string: string;
number: number;
color: RGBA;
boolean: boolean;
[other: string]: any;
}

// Copied from https://gist.github.com/danvk/4378b6936f9cd634fc8c9f69c4f18b81
/**
* Class for working with Mapbox style expressions.
*
* See https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions
*/
export class ExpressionEvaluator<T> {
/**
* Parse a Mapbox style expression.
*
* Pass an expected type to get tigher error checking and more precise types.
*/
static parse<T extends expression.StylePropertyType>(
expr:
| number
| string
| Readonly<StyleFunction>
| Readonly<MapboxExpression>
| undefined,
expectedType?: T
): ExpressionEvaluator<TypeMap[T]> {
// For details on use of this private API and plans to publicize it, see
// https://github.com/mapbox/mapbox-gl-js/issues/7670
let parseResult: expression.ParseResult;
if (expectedType) {
parseResult = expression.createExpression(expr, { type: expectedType });
if (parseResult.result === "success") {
return new ExpressionEvaluator<TypeMap[T]>(parseResult.value);
}
} else {
parseResult = expression.createExpression(expr);
if (parseResult.result === "success") {
return new ExpressionEvaluator<any>(parseResult.value);
}
}

throw parseResult.value[0];
}

constructor(public parsedExpression: expression.StyleExpression) {}

evaluate(feature: Feature): T {
return this.parsedExpression.evaluate(expressionGlobals, feature);
}
}
2 changes: 1 addition & 1 deletion packages/client/src/dataLayers/legends/FillSymbol.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { GLLegendFillSymbol } from "./glLegends";
import { GLLegendFillSymbol } from "./LegendDataModel";
import { LegendResolvedImage, getImage } from "./MarkerSymbol";
import { Map } from "mapbox-gl";
import { colord, extend } from "colord";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GLLegendBubblePanel } from "./glLegends";
import { GLLegendBubblePanel } from "./LegendDataModel";

export default function LegendBubblePanel({
panel,
Expand Down
155 changes: 155 additions & 0 deletions packages/client/src/dataLayers/legends/LegendDataModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
export interface GLLegendFillSymbol {
type: "fill";
color: string;
extruded?: boolean;
patternImageId?: string;
/** 0-1 */
fillOpacity: number;
strokeWidth: number;
strokeOpacity?: number;
strokeColor?: string;
dashed?: boolean;
}

export interface GLLegendLineSymbol {
type: "line";
color: string;
strokeWidth: number;
patternImageId?: string;
dashed?: boolean;
opacity?: number;
}

export interface GLLegendCircleSymbol {
type: "circle";
color: string;
strokeWidth: number;
strokeColor?: string;
/** 0-1 */
fillOpacity: number;
strokeOpacity: number;
radius: number;
}

// TODO: icon-color
export interface GLLegendMarkerSymbol {
type: "marker";
imageId: string;
haloColor?: string;
haloWidth?: number;
rotation?: number;
/** multiple of width & height to display */
iconSize: number;
}

export interface GLLegendRasterSymbol {
type: "raster";
}

export interface GLLegendVideoSymbol {
type: "video";
}

export interface GLLegendTextSymbol {
type: "text";
color: string;
fontFamily: string;
fontWeight: "normal" | "bold";
fontStyle: "normal" | "italic";
haloColor?: string;
haloWidth?: number;
}

export type GLLegendSymbol =
| GLLegendFillSymbol
| GLLegendCircleSymbol
| GLLegendMarkerSymbol
| GLLegendTextSymbol
| GLLegendRasterSymbol
| GLLegendLineSymbol
| GLLegendVideoSymbol;

export type GLLegendListPanel = {
id: string;
type: "GLLegendListPanel";
label?: string;
items: { id: string; label: string; symbol: GLLegendSymbol }[];
};

/**
* Display should be stacked if bubbles are big and can nest together, otherwise
* display as a list.
*
* Note that a BubblePanel may be paired with a ListPanel for a common case of
* a bubble chart with a categorical variable controlling the color of the bubbles.
*/
export type GLLegendBubblePanel = {
id: string;
type: "GLLegendBubblePanel";
label?: string;
stops: {
value: number;
radius: number;
fill: string;
stroke: string;
strokeWidth: number;
}[];
};

export type GLMarkerSizePanel = {
id: string;
type: "GLMarkerSizePanel";
label?: string;
stops: {
id: string;
imageId: string;
value: number;
iconSize: number;
color?: string;
haloColor?: string;
haloWidth?: number;
rotation?: number;
}[];
};

export type GLLegendStepPanel = {
id: string;
type: "GLLegendStepPanel";
label?: string;
steps: { id: string; label: string; symbol: GLLegendSymbol }[];
};

export type GLLegendHeatmapPanel = {
id: string;
type: "GLLegendHeatmapPanel";
stops: { value: number; color: string }[];
};

export type GLLegendGradientPanel = {
id: string;
type: "GLLegendGradientPanel";
label?: string;
stops: { value: number; label: string; color: string }[];
};

export type GLLegendPanel =
| GLLegendListPanel
| GLLegendBubblePanel
| GLLegendHeatmapPanel
| GLLegendGradientPanel
| GLMarkerSizePanel
| GLLegendStepPanel;

export type SimpleLegendForGLLayers = {
type: "SimpleGLLegend";
symbol: GLLegendSymbol;
};

export type MultipleSymbolLegendForGLLayers = {
type: "MultipleSymbolGLLegend";
panels: GLLegendPanel[];
};

export type LegendForGLLayers =
| SimpleLegendForGLLayers
| MultipleSymbolLegendForGLLayers;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GLLegendGradientPanel } from "./glLegends";
import { GLLegendGradientPanel } from "./LegendDataModel";
import { stopsToLinearGradient } from "./utils";

export default function LegendGradientPanel({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GLLegendHeatmapPanel } from "./glLegends";
import { GLLegendHeatmapPanel } from "./LegendDataModel";
import { stopsToLinearGradient } from "./utils";

export default function LegendHeatmapPanel({
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/dataLayers/legends/LegendListPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SimpleSymbol from "./SimpleSymbol";
import { GLLegendListPanel } from "./glLegends";
import { GLLegendListPanel } from "./LegendDataModel";
import { Map } from "mapbox-gl";

export default function LegendListPanel({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import MarkerSymbol from "./MarkerSymbol";
import SimpleSymbol from "./SimpleSymbol";
import { GLLegendListPanel, GLMarkerSizePanel } from "./glLegends";
import { GLLegendListPanel, GLMarkerSizePanel } from "./LegendDataModel";
import { Map } from "mapbox-gl";

export default function LegendMarkerSizePanel({
Expand Down
3 changes: 1 addition & 2 deletions packages/client/src/dataLayers/legends/LegendStepPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import MarkerSymbol from "./MarkerSymbol";
import { Map } from "mapbox-gl";
import { GLLegendStepPanel } from "./glLegends";
import { GLLegendStepPanel } from "./LegendDataModel";
import SimpleSymbol from "./SimpleSymbol";

export default function LegendStepPanel({
Expand Down
5 changes: 1 addition & 4 deletions packages/client/src/dataLayers/legends/LineSymbol.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { GLLegendLineSymbol } from "./glLegends";
import { colord, extend } from "colord";
import namesPlugin from "colord/plugins/names";
extend([namesPlugin]);
import { GLLegendLineSymbol } from "./LegendDataModel";

// TODO: support line patterns
// but how??
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/dataLayers/legends/SimpleSymbol.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VideoCameraIcon } from "@heroicons/react/solid";
import { GLLegendSymbol } from "./glLegends";
import { GLLegendSymbol } from "./LegendDataModel";
import FillSymbol from "./FillSymbol";
import LineSymbol from "./LineSymbol";
import MarkerSymbol from "./MarkerSymbol";
Expand Down
Loading

0 comments on commit 51094c5

Please sign in to comment.