@ugrc/layer-selector
Version:
This is a react component for adding a quick base map selector with a happy path for using [UGRC's Discover Service](https://gis.utah.gov/discover).
406 lines (405 loc) • 13.4 kB
JavaScript
import { jsxs as k, jsx as u, Fragment as F } from "react/jsx-runtime";
import W from "@arcgis/core/Basemap.js";
import j from "@arcgis/core/layers/FeatureLayer.js";
import S from "@arcgis/core/layers/support/LOD.js";
import N from "@arcgis/core/layers/support/TileInfo.js";
import z from "@arcgis/core/layers/TileLayer.js";
import T from "@arcgis/core/layers/WebTileLayer.js";
import C from "clsx";
import t from "prop-types";
import { useState as w, useRef as _, useEffect as L } from "react";
const $ = (e, o, l) => {
const f = o.lods, g = f.slice(0, 20), h = f.slice(0, 18), s = f.slice(0, 19);
e.Imagery.tileInfo = new l(o), e.Hybrid.tileInfo = new l(o);
let d = Object.assign({}, o);
return d.lods = s, e["Color IR"].tileInfo = new l(d), d = Object.assign({}, o), d.lods = h, e.Topo.tileInfo = new l(d), d = Object.assign({}, o), d.lods = g, e.Lite.tileInfo = new l(d), e.Overlay.tileInfo = new l(d), e;
}, A = (e) => {
const l = 40075016685568e-6, f = 39.37, g = l / 256, h = 96, s = 20, d = 2, v = [];
for (let p = 0; p <= s; p++) {
const a = g / Math.pow(d, p), y = a * h * f;
v.push(
new e({
level: p,
scale: y,
resolution: a
})
);
}
return {
dpi: h,
size: 256,
origin: {
x: -20037508342787e-6,
y: 20037508342787e-6
},
spatialReference: {
wkid: 3857
},
lods: v
};
}, M = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='26'%20height='26'%20viewBox='0%200%2026%2026'%3e%3cg%20fill='none'%20stroke='%23555'%20stroke-width='3'%20stroke-miterlimit='9'%3e%3cpath%20transform='matrix(.95342%20.30165%20-.95342%20.30165%200%20-1026)'%20d='M1715%201701h10v10h-10zM1734%201709v11.2h-11.2M1744%201719v11.2h-11.2'/%3e%3c/g%3e%3c/svg%3e", U = {
FeatureLayer: j,
WebTileLayer: T,
TileLayer: z
}, E = (e) => {
const [o, l] = w(e.expanded), f = C("layer-selector__toggle", {
"layer-selector--hidden": o
}), g = C({ "layer-selector--hidden": !o });
return /* @__PURE__ */ k(
"div",
{
className: "layer-selector esri-component esri-widget",
onMouseOver: () => l(!0),
onMouseOut: () => l(!1),
"aria-haspopup": "true",
children: [
/* @__PURE__ */ u("input", { type: "image", className: f, src: M, alt: "layers" }),
/* @__PURE__ */ u("form", { className: g, children: e.children })
]
}
);
};
E.propTypes = {
children: t.object,
expanded: t.bool
};
const q = (e) => {
const o = {
type: e.layerType === "baselayer" ? "radio" : "checkbox",
name: e.layerType,
value: e.id
};
return /* @__PURE__ */ u("div", { className: "layer-selector-item radio checkbox", children: /* @__PURE__ */ k("label", { className: "layer-selector--item", children: [
/* @__PURE__ */ u(
"input",
{
...o,
checked: e.selected,
onChange: (l) => e.onChange(l, e)
}
),
/* @__PURE__ */ u("span", { className: "layer-selector-item--text", children: o.value })
] }) });
}, R = "https://mapserv.utah.gov/cdn/attribution/imagery.json", P = (e, o, l, f, g) => {
const h = [];
return o.forEach((s) => {
if (typeof s == "string" || s instanceof String || s.token || typeof s.token == "string" || s.token instanceof String) {
const v = s.token || s;
if (!f)
return console.warn(
"layer-selector::You chose to use a layer token `" + v + "` without setting your `quadWord` from Discover. The requests for tiles will fail to authenticate. Pass `quadWord` into the constructor of this widget."
), !1;
var d = g[v];
if (!d)
return console.warn(
"layer-selector::The layer token `" + v + "` was not found. Please use one of the supported tokens (" + Object.keys(g).join(", ") + ") or pass in the information on how to create your custom layer (`{Factory, url, id}`)."
), !1;
const p = [d.linked, s.linked].reduce((a, y, r) => (y && (a = a.concat(y)), r === 1 && a.length === 0 ? null : a), []);
h.push({
Factory: l,
urlTemplate: d.urlPattern,
linked: p,
id: v,
selected: !!s.selected,
copyright: d.copyright,
layerType: e
// TODO: not supported in 4.x WebTileLayer copyright
// hasAttributionData: layer.hasAttributionData,
// attributionDataUrl: layer.attributionDataUrl
});
} else {
if (Object.prototype.hasOwnProperty.call(s, "layerType") || (s.layerType = e), s.selected || (s.selected = !1), typeof s.Factory == "string" && (s.Factory = U[s.Factory], !s.Factory))
throw new Error(`Unknown layer factory: ${s.Factory}`);
h.push(s);
}
}), h;
}, D = ({ condition: e, wrapper: o, children: l }) => e ? o(l) : l, H = {
makeExpandable: !0,
position: "top-right",
showOpacitySlider: !1
}, G = (e) => {
e = {
...H,
...e
};
const [o, l] = w({
baseLayers: [],
overlays: []
}), [f, g] = w([]), [h, s] = w({}), d = _();
function v(a) {
const y = parseFloat(a.target.value) / 100;
if (e.showOpacitySlider)
for (const r in h) {
const n = h[r];
if (n.layer) {
const i = (n.layer.originalOpacity ?? 1) - (1 - y);
n.layer.opacity = i < 0 ? 0 : i;
}
}
}
L(() => {
const a = { ...h };
o.baseLayers.concat(o.overlays).forEach((r) => {
var i;
let n = null;
switch (r.layerType) {
case "baselayer":
(i = e.view.map.basemap) != null && i.baseLayers || (e.view.map.basemap = {
baseLayers: [],
id: "layer-selector",
title: "layer-selector"
}), n = e.view.map.basemap.baseLayers;
break;
case "overlay":
n = e.view.map.layers;
break;
default:
throw new Error(`unknown layerType: ${r.layerType}`);
}
if (r.selected === !1) {
var c = a[r.id] || {};
c.layer || (c.layer = n.getItemAt(
n.indexOf(r.layer)
)), c.layer && n.remove(c.layer);
return;
}
Object.keys(a).indexOf(r.id) < 0 && (a[r.id] = {
layerType: r.layerType
}), a[r.id].layer || (a[r.id].layer = new r.Factory({
originalOpacity: r.opacity,
...r
})), r.selected === !0 ? n.includes(a[r.id].layer) || n.add(a[r.id].layer) : n.remove(a[r.id].layer), a[r.id].layer.when("loaded", () => {
const b = a[r.id].layer.tileInfo.lods[e.view.zoom].scale;
e.view.zoom > -1 && e.view.scale !== b && (e.view.zoom = e.view.zoom);
}), s(a);
});
}, [o]), L(() => {
if (!e.baseLayers || e.baseLayers.length < 1) {
console.warn(
"layer-selector::`baseLayers` is null or empty. Make sure you have spelled it correctly and are passing it into the constructor of this widget."
);
return;
}
}, [e.baseLayers]), L(() => {
var x;
const a = {
Imagery: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/utah/{level}/{col}/{row}`,
hasAttributionData: !0,
copyright: "Hexagon",
attributionDataUrl: R
},
Topo: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/topo_basemap/{level}/{col}/{row}`,
copyright: "UGRC"
},
Terrain: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/terrain_basemap/{level}/{col}/{row}`,
copyright: "UGRC"
},
Lite: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/lite_basemap/{level}/{col}/{row}`,
copyright: "UGRC"
},
"Color IR": {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/naip_2021_nrg/{level}/{col}/{row}`,
copyright: "UGRC"
},
Hybrid: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/utah/{level}/{col}/{row}`,
linked: ["Overlay"],
copyright: "Hexagon, UGRC",
hasAttributionData: !0,
attributionDataUrl: R
},
Overlay: {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/overlay_basemap/{level}/{col}/{row}`
// no attribution for overlay layers since it just duplicates the base map attribution
},
"Address Points": {
urlPattern: `https://discover.agrc.utah.gov/login/path/${e.quadWord}/tiles/address_points_basemap/{level}/{col}/{row}`
}
};
try {
e.view.map.basemap = new W();
} catch {
console.warn(
"layer-selector::You must pass a view with a map to the constructor of this widget."
);
}
const y = A(S), r = $(
a,
y,
N
), n = P(
"baselayer",
e.baseLayers,
T,
e.quadWord,
r
) || [];
let c = e.overlays || [], i = null, b = !1, O = [];
n.forEach((m) => {
m.selected === !0 && (i = m), (m.id || m.token) === "Hybrid" && (b = !0), m.linked && (O = O.concat(m.linked));
}), g(O), !i && n.length > 0 && (n[0].selected = !0, i = n[0]), b && c[0] !== "Overlay" && ((x = c[0]) == null ? void 0 : x.id) !== "Overlay" && c.splice(0, 0, "Overlay"), c = P(
"overlay",
c,
T,
e.quadWord,
r
) || [], i != null && i.linked && i.linked.length > 0 && c.forEach((m) => {
i.linked.includes(m.id) && (m.selected = !0);
}), l({
baseLayers: n,
overlays: c
}), e.view.ui.add(d.current, e.position);
}, [
e.baseLayers,
e.overlays,
e.position,
e.quadWord,
e.view.map,
e.view.ui
]);
const p = (a, y) => {
console.log("LayerSelector.onItemChanged", y);
const r = o.overlays, n = o.baseLayers;
if (y.layerType === "baselayer") {
n.forEach((i) => {
i.selected = i.id === y.id ? a.target.checked : !1;
});
const c = n.filter((i) => i.selected)[0];
r.forEach((i) => {
f.includes(i.id) && (i.selected = !1);
}), c.linked && c.linked.length > 0 && r.forEach((i) => (c.linked.includes(i.id) && (i.selected = !0), i));
} else y.layerType === "overlay" && r.forEach((c) => {
c.id === y.id && (c.selected = a.target.checked);
});
l({
overlays: r,
baseLayers: n
});
};
return /* @__PURE__ */ u("div", { ref: d, children: /* @__PURE__ */ u(
D,
{
condition: e.makeExpandable,
wrapper: (a) => /* @__PURE__ */ u(E, { children: a }),
children: /* @__PURE__ */ k("div", { className: "layer-selector--layers", children: [
o.baseLayers.map((a, y) => /* @__PURE__ */ u(
q,
{
id: a.name || a.id || "unknown",
layerType: "baselayer",
selected: a.selected,
onChange: p
},
y
)),
o.overlays.length ? /* @__PURE__ */ u("hr", { className: "layer-selector-separator" }) : null,
o.overlays.map((a) => /* @__PURE__ */ u(
q,
{
id: a.name || a.id || "unknown",
layerType: "overlay",
selected: a.selected,
onChange: p
},
a.id || a
)),
e.showOpacitySlider ? /* @__PURE__ */ k(F, { children: [
/* @__PURE__ */ u("hr", { className: "layer-selector-separator" }),
/* @__PURE__ */ u(
"input",
{
type: "range",
min: "0",
max: "100",
step: "1",
defaultValue: "100",
onChange: v
}
)
] }) : null
] })
}
) });
};
G.propTypes = {
view: t.object.isRequired,
quadWord: t.string,
baseLayers: t.arrayOf(
t.oneOfType([
t.oneOf([
"Hybrid",
"Lite",
"Terrain",
"Topo",
"Color IR",
"Address Points",
"Overlay",
"Imagery"
]),
t.shape({
Factory: t.oneOfType([t.func, t.string]).isRequired,
urlTemplate: t.string,
url: t.string,
id: t.string.isRequired,
tileInfo: t.object,
linked: t.arrayOf(t.string)
}),
t.shape({
token: t.oneOf([
"Hybrid",
"Lite",
"Terrain",
"Topo",
"Color IR",
"Address Points",
"Overlay"
]).isRequired,
selected: t.bool,
linked: t.arrayOf(t.string)
})
])
).isRequired,
overlays: t.arrayOf(
t.oneOfType([
t.oneOf(["Address Points", "Overlay"]),
t.shape({
Factory: t.oneOfType([t.func, t.string]).isRequired,
urlTemplate: t.string,
url: t.string,
id: t.string.isRequired,
tileInfo: t.object,
linked: t.arrayOf(t.string)
})
])
),
position: t.oneOf([
"bottom-leading",
"bottom-left",
"bottom-right",
"bottom-trailing",
"top-leading",
"top-left",
"top-right",
"top-trailing"
]),
makeExpandable: t.bool,
layerType: t.string,
id: t.string,
showOpacitySlider: t.bool
};
q.propTypes = {
onChange: t.func.isRequired,
selected: t.bool.isRequired,
layerType: t.oneOf(["baselayer", "overlay"]).isRequired,
id: t.string.isRequired
};
export {
E as ExpandableContainer,
q as LayerSelectorItem,
G as default
};
//# sourceMappingURL=index.es.js.map