@devgateway/dvz-ui-react
Version:
A modular, embeddable React component library for data visualization and UI, built with TypeScript. Provides reusable components for charts, maps, dashboards, and more, with built-in support for internationalization and Redux integration.
1,049 lines (1,048 loc) • 43.6 kB
JavaScript
var X = Object.defineProperty;
var Y = (w, F, e) => F in w ? X(w, F, { enumerable: !0, configurable: !0, writable: !0, value: e }) : w[F] = e;
var H = (w, F, e) => Y(w, typeof F != "symbol" ? F + "" : F, e);
import { jsx as b, jsxs as M, Fragment as R } from "react/jsx-runtime";
import { injectIntl as q, FormattedMessage as K } from "react-intl";
import * as g from "d3";
import { Container as U, Segment as Q, Dimmer as ee, Loader as te, Message as N, Icon as E, Popup as se, Grid as S } from "semantic-ui-react";
import V from "react";
import * as ie from "topojson-client";
import oe from "./legend.js";
import { formatContent as k } from "../common/MapTooltip.js";
import T from "../../utils/deviceType.js";
import ae from "geostats";
import { Config as re } from "../../conf/index.js";
function O(w, F, e) {
var l;
if (!(w != null && w.length)) return F;
const s = ((l = w[0]) == null ? void 0 : l.items) ?? [], i = (a) => (a == null ? void 0 : a.trim().toLowerCase()) ?? "", o = i(F), t = i(e).toUpperCase(), r = s.find(
(a) => i(a.value) === o || a.code.trim().toLowerCase() === o
) ?? null;
if (!r) return F;
const n = t && r.labels && r.labels[t] ? r.labels[t] : r.value;
return (n == null ? void 0 : n.trim()) || r.value.trim();
}
const le = "_Color_", G = "location", j = "showAll", B = "ifUnitHasData", ne = 10, pe = {
mobile: 4,
tablet: 4,
midTablet: 2,
laptop: 2,
desktop: 2,
wide: 2
}, ce = {
mobile: 330,
tablet: 250,
midTablet: 250,
laptop: 200,
desktop: 100,
wide: 100
}, Z = {
mobile: 250,
tablet: 250,
midTablet: 250,
laptop: 0,
desktop: 0,
wide: 0
}, he = {
greens: [
"#ccffdd",
"#b3ffcc",
"#99ffbb",
"#80ffaa",
"#66ff99",
"#4dff88",
"#33ff77",
"#1aff66",
"#00ff55",
"#00e64d"
],
greys: [
"#f2f2f2",
"#e6e6e6",
"#d9d9d9",
"#cccccc",
"#bfbfbf",
"#b3b3b3",
"#a6a6a6",
"#999999",
"#8c8c8c",
"#808080"
],
oranges: [
"#fff0e6",
"#ffe0cc",
"#ffd1b3",
"#ffc299",
"#ffb380",
"#ffa366",
"#ff944d",
"#ff8533",
"#ff751a",
"#ff6600"
],
purples: [
"#ffe6ff",
"#ffccff",
"#ffb3ff",
"#ff99ff",
"#ff80ff",
"#ff66ff",
"#ff4dff",
"#ff33ff",
"#ff1aff",
"#ff00ff"
],
reds: [
"#ffe6e6",
"#ffcccc",
"#ffb3b3",
"#ff9999",
"#ff8080",
"#ff6666",
"#ff4d4d",
"#ff3333",
"#ff1a1a",
"#ff0000"
],
blues: [
"#e6eeff",
"#ccddff",
"#b3ccff",
"#99bbff",
"#80aaff",
"#6699ff",
"#4d88ff",
"#3377ff",
"#1a66ff",
"#0055ff"
]
}, de = ["mobile", "tablet", "midTablet"].includes(
T()
), $ = ["mobile", "tablet"].includes(T());
class ue extends V.Component {
constructor(e) {
var s;
super(e);
H(this, "handleScroll", () => {
let e = null;
clearTimeout(e), e = setTimeout(() => {
g.select(this.getMapId()).selectAll(".map-labels-container").size() > 0 || this.updateFeatures(this.getFeatures(), !1);
}, 300);
});
this.mapContainer = V.createRef(), this.state = { mainLayer: null, layers: null }, this.classColor = this.classColor.bind(this), this.featuresZoom = this.featuresZoom.bind(this), this.fullView = this.fullView.bind(this), this.onZoomIn = this.onZoomIn.bind(this), this.onZoomOut = this.onZoomOut.bind(this), this.onReset = this.onReset.bind(this), this.onClick = this.onClick.bind(this), this.showTooltip = this.showTooltip.bind(this), this.mousemove = this.mousemove.bind(this), this.mouseout = this.mouseout.bind(this), this.updateFeatures = this.updateFeatures.bind(this), this.d3Map = this.d3Map.bind(this), this.getFeatures = this.getFeatures.bind(this), this.boundingExtent = this.boundingExtent.bind(this), this.getMapId = this.getMapId.bind(this), this.zoomed = this.zoomed.bind(this), this.zoomEnd = this.zoomEnd.bind(this), this.drawPoints = this.drawPoints.bind(this), this.extractFeatures = this.extractFeatures.bind(this), this.getLayers = this.getLayers.bind(this), this.onPointClick = this.onPointClick.bind(this), this.onPolygonClick = this.onPolygonClick.bind(this), this.getCenter = this.getCenter.bind(this), this.mapPosition = null, this.zooming = !1, this.translateValue = pe[T()], this.projection = g.geoMercator().scale(e.scale).center(e.center).translate([this.getWidth() / this.translateValue, this.getHeight() / 2]), this.path = g.geoPath().projection(this.projection), this.zoom = g.zoom().scaleExtent([1, 16]).on("zoom", this.zoomed).on("end", this.zoomEnd), this.centered = null, this.state = {
selectedMeasure: e.transformedData && e.transformedData.measures && e.transformedData.measures.length > 1 ? e.transformedData.measures[0] : null,
generatedBreaks: [],
selectedPolygon: null,
layersLoading: !1
}, this.metadataTypes = ((s = e.transformedData) == null ? void 0 : s.types) || [];
}
componentDidMount() {
window.addEventListener("scroll", this.handleScroll, { passive: !0 }), window.addEventListener("touchmove", this.handleScroll, { passive: !0 }), this.loadLayers(), this.tooltip = g.select("body").append("div").style("position", "absolute").style("visibility", "hidden"), console.log("Map props", this.metadataTypes);
}
componentDidCatch(e, s) {
console.log(e);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScroll);
}
loadLayers() {
const { source: e, mainLayerId: s, enabledLayers: i } = this.props;
if (this.setState({
layers: [],
layersLoading: !0
}), i && i.length > 0) {
const o = [];
i.forEach((t) => {
o.push(
new Promise((r, n) => {
g.json(re.REACT_APP_WP_API + "/wp/v2/media/" + t.id).then((l) => {
r({
id: t.id,
url: l.source_url,
index: t.index,
layerMappingField: t.layerMappingField,
layerDatasource: t.datasource,
layerApiField: t.apiField,
layerLocale: t.locale,
displayLayerLabels: t.displayLayerLabels
});
}).catch(function(l) {
r({
id: t.id,
url: null,
index: t.index,
layerMappingField: t.layerMappingField,
layerDatasource: t.datasource,
layerApiField: t.apiField,
layerLocale: t.locale,
displayLayerLabels: t.displayLayerLabels
});
});
})
);
}), Promise.all(o).then((t) => {
const r = [];
t.forEach((n) => {
n.url && r.push(
new Promise((l, a) => {
g.json(n.url).then((h) => {
l({
id: n.id,
data: h,
index: n.index,
layerMappingField: n.layerMappingField,
layerDatasource: n.layerDatasource,
layerApiField: n.layerApiField,
layerLocale: n.layerLocale,
displayLayerLabels: n.displayLayerLabels
});
});
})
);
}), Promise.all(r).then((n) => {
this.setState({
layers: n,
layersLoading: !1
});
});
});
} else
g.json(e).then((o) => {
this.setState({
layers: [
{
id: null,
url: e,
data: o,
index: 0,
layerMappingField: null,
layerDatasource: null,
layerApiField: null,
layerLocale: null,
displayLayerLabels: !1
}
],
layersLoading: !1
});
});
}
getMainLayer() {
const e = this.getLayers(), { mainLayerId: s, enabledLayers: i } = this.props;
let o;
return e && (o = e.filter(
(t) => t.id == s || t.id == null
)[0] || e[0]), o ? o.data : null;
}
componentDidUpdate(e, s, i) {
const { selectedMeasure: o, layers: t, selectedPolygon: r } = this.state, n = this.getMainLayer(), { transformedData: l, intl: a, zoomOnFilterField: h, appliedFilters: p } = this.props, { appliedFilters: d } = e;
if (h) {
const v = [], c = [];
d && Object.keys(d).forEach((f) => {
d[f] != null && d[f] instanceof Array && v.push(
...d[f].filter(
(y) => y != Number.MIN_SAFE_INTEGER
)
);
}), p && Object.keys(p).forEach((f) => {
p[f] != null && p[f] instanceof Array && c.push(
...p[f].filter((y) => y != Number.MIN_SAFE_INTEGER)
);
}), v.length > 0 && c.length == 0 && this.onReset();
}
this.tooltip.style("visibility", "hidden"), e.enabledLayers.length != this.props.enabledLayers.length && this.loadLayers();
const u = this.getFeatures();
e.center !== this.props.center && (this.mapPosition = null, this.projection.scale(this.props.scale).center(this.props.center).translate([this.getWidth() / 2, this.getHeight() / 2]));
const m = this.filterUpdated(e, s);
this.d3Map(u, m), t && l && (l != e.transformedData || t != s.layers || o != s.selectedMeasure || r != s.selectedPolygon || n != s.mainLayer || e.mainLayerId !== this.props.mainLayerId || JSON.stringify(e.enabledLayers) != JSON.stringify(this.props.enabledLayers)) && this.updateFeatures(this.getFeatures(), m);
}
getHeight() {
return this.props.height;
}
getWidth() {
return this.mapContainer.current ? this.mapContainer.current.offsetWidth : this.props.width;
}
boundingExtent(e) {
let s, i, o, t;
for (const r in e) {
const [[n, l], [a, h]] = this.path.bounds(e[r]);
(n < s || s == null) && (s = n), (a > i || i == null) && (i = a), (l < o || o == null) && (o = l), (h > t || t == null) && (t = h);
}
return [
[s, o],
[i, t]
];
}
onReset() {
this.mapPosition = null, this.tooltip.style("visibility", "hidden"), this.fullView();
}
resizeLabels(e) {
const { labelFontSize: s, mapLabelField: i } = this.props;
g.select(this.getMapId()).selectAll(".map-labels-container").each((o, t, r) => {
const n = g.select(r[t]), l = n.select("div"), a = e.k > 1 ? e.k : 1, h = s / a;
l.style("font-size", `${h}px`);
const p = this.getLabelPosition(o), d = this.getLabelBoxWidth(o) / a, u = p[0] - d / 2, m = e.k > 1 ? 10 / e.k : 10, v = p[1] - m;
n.attr("x", u).attr("y", v).attr("width", this.getLabelBoxWidth(o) / a).attr("height", this.getLabelBoxHeight(o) / a);
});
}
resizePointLabels(e) {
const { labelFontSize: s } = this.props;
g.select(this.getMapId()).selectAll(".point-labels-container").each((i, o, t) => {
const r = g.select(t[o]), n = r.select("div"), l = e.k > 1 ? e.k : 1, a = s / l;
n.style("font-size", `${a}px`);
const h = this.projection([
i.geometry.coordinates[1],
i.geometry.coordinates[0]
]), p = (this.getLabelBoxWidth(i) + 20) / l, d = this.getLabelBoxHeight(i) / l, u = h[0] - p / 2, m = h[1] - d / 2;
r.attr("x", u).attr("y", m).attr("width", p).attr("height", d);
});
}
resizeCircles(e) {
g.select(this.getMapId()).select("svg").selectAll("circle").attr("r", e.k > 1 ? 6 / e.k : 6);
}
zoomed(e) {
this.tooltip.style("visibility", "hidden");
const s = e.transform;
g.select(this.getMapId()).select("svg").select("g").attr("transform", s), this.resizeCircles(s), this.resizeLabels(s), this.resizePointLabels(s);
}
zoomEnd(e) {
const { editing: s } = this.props, i = e.transform;
this.mapPosition = { k: i.k, x: i.x, y: i.y }, s && window.parent.postMessage(
{ type: "map", value: JSON.stringify(this.mapPosition) },
"*"
);
}
classColor(e) {
let { zoomEnabled: s } = this.props;
return s || (s = !!["mobile", "tablet", "midTablet"].includes(
T()
)), s ? "active zoom-enabled" : "active";
}
generateBreaks(e) {
const { autoGenerateBreaks: s, numberOfBreaks: i, colorScheme: o } = this.props, t = [];
if (s && e && e.length > 0) {
const r = e.filter((a) => a.properties && a.properties.value != null).map((a) => a.properties.value.toFixed(2)), n = [];
r.forEach((a) => {
if (a > 0) {
const h = a * 0.99, p = a * 1.01;
n.indexOf(h) === -1 && n.push(h), n.indexOf(p) === -1 && n.push(p);
}
});
const l = he[o];
if (n.length > 0) {
const a = new ae(n);
a.setPrecision(2);
const h = n.length > 1 ? n.length - 1 : n.length;
return a.getJenks(Math.min(i, h)), a.ranges.forEach((p, d) => {
const u = {};
u.min = parseFloat(p.substr(0, p.indexOf("-") - 1)) + (d > 0 ? 0.01 : 0), u.max = parseFloat(
p.substr(p.indexOf("-") + 2, p.length)
), u.color = l[d], t.push(u);
}), t;
}
}
return t;
}
getBreaks() {
const { legendBreaks: e, autoGenerateBreaks: s } = this.props;
if (s) {
const i = this.getFeatures();
return this.generateBreaks(i);
} else {
let i = e;
return this.getSelectedMeasure() && (i = e.filter((o) => o.measure === this.getSelectedMeasure()).filter((o) => {
if (o.filters && o.filters.length > 0 && this.props.appliedFilters && JSON.stringify(this.props.appliedFilters) !== "{}") {
const r = Object.keys(this.props.appliedFilters);
return o.filters.filter((l) => {
if (r.indexOf(l.field) != -1) {
const a = this.props.appliedFilters[l.field], h = l.values;
return a.join(",").indexOf(h) != -1;
}
return !1;
}).length > 0;
}
return !0;
})), i;
}
}
fillColor(e, s) {
const { mapNoDataColor: i, mainLayerId: o } = this.props;
let t;
if (e.properties && e.properties.variables && this.state.selectedMeasure && e.properties.value != null) {
const n = le + this.state.selectedMeasure;
if (t = e.properties.variables[n.trim()], t)
return t;
}
if (e.properties.value != null && (o && e.properties.layerId === o || !o)) {
const n = s.find((l) => {
if (l.min != null && l.max != null)
return e.properties.value >= l.min && e.properties.value <= l.max;
if (l.min != null)
return e.properties.value >= l.min;
if (l.max != null)
return e.properties.value <= l.max;
});
return n && n.color ? n.color : i;
}
const r = this.props.enabledLayers.filter(
(n) => n.id === e.properties.layerId
)[0];
return r && r.bgColor && r.bgColor != "undefined" ? r.bgColor : i;
}
setValues() {
const e = this.getFeatures();
g.select(this.getMapId()).select("svg").select("g").selectAll("path").data(e).join("path").attr("d", this.path);
}
getLabelPosition(e) {
return e.properties.LABEL_LATITUDE && e.properties.LABEL_LONGITUDE ? this.projection([
e.properties.LABEL_LONGITUDE,
e.properties.LABEL_LATITUDE
]) : this.path.centroid(e);
}
updateFeatures(e, s) {
const { mapLabelField: i, symbols: o, highlightedLocation: t } = this.props, r = [
...e.filter((n) => t != n.properties[i]),
...e.filter((n) => t == n.properties[i])
];
this.drawPolygons(r), this.drawLabels(r), this.drawPoints(r, s), o.length > 0 && this.addSymbols(o, r);
}
drawLabels(e) {
const {
mapLabelField: s,
mapLabelShowValue: i,
intl: o,
valueFormat: t,
showNoDataLabel: r,
labelFontColor: n,
labelFontWeight: l,
labelFontSize: a,
showAdminUnitLabel: h,
mapType: p,
noDataText: d,
labelsExclusionList: u
} = this.props, m = g.select(this.getMapId()).select("svg").select("g");
if (m.selectAll(".map-labels-container").size() > 0) {
console.log("Labels already exist, skipping redraw...");
return;
}
m.selectAll(".map-labels").data(
e.filter((c) => u && u.length > 0 ? !u.includes(c.properties[s]) : !0)
).enter().append("foreignObject").attr("class", "map-labels-container").attr("x", (c) => {
const f = this.getLabelPosition(c);
if (c.properties[s]) {
const y = this.getLabelBoxWidth(c);
return f[0] - y / 2;
}
return f[0];
}).attr("y", (c) => this.getLabelPosition(c)[1] - 10).attr("width", (c) => this.getLabelBoxWidth(c)).attr("height", (c) => this.getLabelBoxHeight(c)).attr("font-size", (c, f) => `${a}px`).attr("overflow", "visible").attr("opacity", 1).style("display", (c) => h === j || h === B && c.properties.hasDataRow || c.properties.displayLayerLabels ? "block" : "none").attr("pointer-events", p == "POINTS_MAP" ? "none" : "all").on("mouseover", this.showTooltip).on("mousemove", this.mousemove).on("mouseout", this.mouseout).append("xhtml:div").style("font-size", (c) => `${a}px`).style("color", (c, f) => n).style("font-weight", (c) => l).style("background-color", (c) => c.properties.hasDataRow && i && (c.properties.value != null || c.properties.value == null && r) ? "#fff6e1" : "none").style("border-radius", (c) => "4px").style("line-height", "95%").style("text-align", "center").html((c, f) => this.createLabel(c));
}
getTranslatedLocationName(e, s, i) {
if (e.properties.displayLayerLabels && e.properties.layerLocale) {
const o = e.properties.layerMappingField || s, t = e.properties[o];
return O(this.metadataTypes, t, e.properties.layerLocale);
}
return e.properties[s];
}
createLabel(e) {
const {
mapLabelField: s,
mapLabelShowValue: i,
intl: o,
valueFormat: t,
showNoDataLabel: r,
showAdminUnitLabel: n,
noDataText: l
} = this.props;
let a = "";
if (n == j || n == B && e.properties.hasDataRow || e.properties.displayLayerLabels) {
const h = e.properties.displayLayerLabels && e.properties.layerMappingField ? e.properties.layerMappingField : s, p = e.properties[h], d = e.properties.layerLocale || (o == null ? void 0 : o.locale);
a = O(this.metadataTypes, p, d);
const u = e.properties.abbrev;
if (a && a.length > ne && u && (a = u), i)
if (e.properties.value != null) {
const m = e.properties.variables || {};
a += "<br><span class='map-label-value'>" + k(
t,
{
value: e.properties.value,
measure: this.getSelectedMeasure(),
...m
},
o
) + "</span>";
} else
r == !0 && e.properties.value == null && e.properties.hasDataRow && (a += "<br><span class='map-label-value'>" + l + "</span>");
}
return a;
}
drawPolygons(e) {
const {
mapLabelField: s,
mapBoundaryColor: i,
mapFocusBoundaryColor: o,
highlightedLocation: t
} = this.props, r = this.getBreaks(), n = g.select(this.getMapId()).select("svg").select("g"), l = e.filter(
(a) => a.geometry && a.geometry && (a.geometry.type == "Polygon" || a.geometry.type == "MultiPolygon")
);
l.length > 0 && n.selectAll("path").data(l).join("path").attr("d", this.path).attr("fill", (a) => this.fillColor(a, r)).attr("stroke-width", (a) => t == a.properties[s] ? 1.2 : 0.4).attr("stroke", (a) => t == a.properties[s] ? o : i).on("click", this.onPolygonClick);
}
drawPoints(e, s) {
const {
intl: i,
pointLabelColor: o,
pointLabelFormat: t,
transformedData: r,
defaultPointColor: n,
appliedFilters: l,
zoomOnFilterField: a,
noDataText: h,
showShadingLayerLabels: p
} = this.props, d = g.select(this.getMapId()).select("svg").select("g");
let u = [];
if (r.pointsData) {
let c = this.state.selectedPolygon;
s && l && l[a] && (c = l[a]), u = r.pointsData.filter((f) => f.lat && f.lng && f.label == c).map((f) => ({
properties: {
label: f.label,
lat: f.lat,
lng: f.lng,
value: f.value,
variables: f.variables
}
})), d.selectAll(".circle").data(u).enter().append("circle").attr("id", (f, y) => "circle" + y).attr("cx", (f) => this.projection([
f.properties.lng,
f.properties.lat
])[0]).attr("cy", (f) => this.projection([
f.properties.lng,
f.properties.lat
])[1]).attr("r", (f, y) => 2).style("stroke-width", 0.5).style("fill", (f, y) => n).on("mouseover", (f, y) => this.onPointClick(f, y)).on("mouseout", this.mouseout);
}
const m = this.getBreaks();
let v = [];
p == j ? v = e.filter(
(c) => c.geometry && c.geometry.type == "Point"
) : p == B && (v = e.filter(
(c) => c.geometry && c.geometry.type == "Point" && c.properties.hasDataRow
)), v.length > 0 && d.selectAll(".point-labels").data(v).enter().append("foreignObject").attr("id", (c, f) => "point-label" + f).attr("class", "point-labels-container").attr("x", (c) => {
const f = this.getLabelBoxWidth(c) + 20;
return this.projection([
c.geometry.coordinates[1],
c.geometry.coordinates[0]
])[0] - f / 2;
}).attr("y", (c) => this.projection([
c.geometry.coordinates[1],
c.geometry.coordinates[0]
])[1] - this.getLabelBoxHeight(c) / 2).attr("width", (c) => this.getLabelBoxWidth(c) + 20).attr("height", (c) => "1px").attr("overflow", "visible").attr("font-size", "12px").style("opacity", 1).append("xhtml:div").style("color", (c, f) => o).style("font-weight", (c) => "bold").style("background-color", (c) => this.fillColor(c, m)).style("padding", (c) => "5px 3px 5px 3px").style("border-radius", (c) => "4px").style("line-height", "100%").style("text-align", "center").html((c, f) => k(
t,
{
value: c.properties.value,
locationName: c.properties[this.props.mapLabelField]
},
i
)).on("mouseover", (c, f, y) => {
g.select(this.getMapId()).select("svg").select("g").select("#point-label" + y).raise(), this.showTooltip(c, f);
}).on("mousemove", this.mousemove).on("mouseout", this.mouseout);
}
addSymbols(e, s) {
const { mappingField: i } = this.props, o = g.select(this.getMapId()).select("svg").select("g");
e.forEach((t) => {
if (t.field && t.image && t.values) {
const r = s.filter((l) => {
const a = G == t.field ? i : "value", h = (l.properties[a] || (l.properties.variables ? l.properties.variables[a] : "")) + "", d = (t.values + "").split(",");
return l.properties.LATITUDE && l.properties.LONGITUDE && d.filter(
(u) => u.trim().toLowerCase() == h.trim().toLowerCase()
).length > 0;
}), n = s.filter((l) => {
const a = G == t.field ? i : "value", h = (l.properties[a] || (l.properties.variables ? l.properties.variables[a] : "")) + "", d = (t.values + "").split(",");
return !l.properties.LATITUDE && !l.properties.LONGITUDE && d.filter(
(u) => u.trim().toLowerCase() == h.trim().toLowerCase()
).length > 0;
});
o.selectAll("image").data(r).enter().append("image").attr("width", 40).attr("height", 40).attr("class", "map-symbol").attr("xlink:href", "/" + t.image).attr("transform", (l) => "translate(" + this.projection([l.properties.LONGITUDE, l.properties.LATITUDE]) + ")").on("mouseover", this.showTooltip).on("mousemove", this.mousemove).on("mouseout", this.mouseout), o.selectAll("image").data(n).enter().append("image").attr("width", 40).attr("height", 40).attr("class", "map-symbol").attr("xlink:href", "/" + t.image).attr("x", (l) => this.path.centroid(l)[0] - 20).attr("y", (l) => this.path.centroid(l)[1]).on("mouseover", this.showTooltip).on("mousemove", this.mousemove).on("mouseout", this.mouseout);
}
});
}
getLabelBoxHeight() {
const { mapLabelShowValue: e } = this.props;
return e ? 30 : 25;
}
getLabelBoxWidth(e) {
const { mapLabelField: s } = this.props, i = 80;
if (e.properties[s]) {
const o = e.properties[s].length;
return o < 10 ? i : o * 8;
}
return 0;
}
featuresZoom(e, s, i) {
const o = g.select(this.getMapId()).select("svg"), t = this.boundingExtent(e), [[r, n], [l, a]] = t, h = this.getWidth(), p = this.getHeight(), d = Math.min(
8,
0.9 / Math.max((l - r) / h, (a - n) / p)
), u = [h / 2 - (r + l) / 2, p / 2 - (n + a) / 2];
s ? o.call(
this.zoom.transform,
g.zoomIdentity.translate(u[0], u[1]).scale(d)
) : o.transition().duration(450).call(
this.zoom.transform,
g.zoomIdentity.translate(u[0], u[1]).scale(d)
).on("end", i);
}
fullView() {
const { mapPosition: e, editing: s } = this.props, i = g.select(this.getMapId()).select("svg");
i.select("g").selectAll(".active").attr("class", function() {
return g.select(this).attr("class").replace(/background/gi, "");
});
let t = g.zoomIdentity;
e && !s ? t = t.translate(e.x, e.y).scale(e.k) : t = t.translate(0, 0).scale(1), i.transition().duration(300).call(this.zoom.transform, t);
}
showTooltip(e, s) {
let {
showTooltip: i,
zoomEnabled: o,
tooltipTheme: t,
customTooltips: r,
tooltipFontSize: n,
tooltipFormat: l,
intl: a,
mappingField: h,
showNoDataTooltip: p,
fields: d,
mapType: u,
noDataText: m
} = this.props;
if (o = !!["mobile", "tablet", "midTablet"].includes(
T()
), i && s.properties.value != null || i && p) {
g.select(this.getMapId()).select("svg").select("g").selectAll(".active").attr("class", (A) => A.properties[h] === s.properties[h] ? "focus" + (o ? " zoom-enabled" : "") : "active" + (o ? " zoom-enabled" : ""));
const f = l || `{locationName} %({value},2)
{label}: %({value},2)`, y = s.properties.variables || {}, I = {
...s.properties,
value: s.properties.value,
measure: this.getSelectedMeasure(),
measureLabel: s.properties.measureLabel,
locationName: this.getTranslatedLocationName(s, h, a),
label: this.getTranslatedLocationName(s, h, a),
...y
};
this.tooltip.attr("class", t).style("position", "absolute").style("visibility", "hidden").style("visibility", "visible").html((A) => {
let L = `<div style='font-size:${n}px;' class='tooltip-content' >`;
if (s.properties.value != null) {
const x = f.split(`
`), D = x[0], W = x.length > 1 ? x[1] : null;
let z = 1, P;
d.length > 1 && u != "POINTS_MAP" ? (z = x.length > 2 ? 2 : 1, P = x[z]) : P = null, D && (L += k(D, I, a)), W && (L.endsWith("<hr>") || (L += "<hr>"), L += k(W, I, a)), P && s.properties.children && s.properties.children.forEach((C, _) => {
const J = {
value: C.value,
label: C.label,
measure: this.getSelectedMeasure(),
measureLabel: s.properties.measureLabel,
...y
};
L.endsWith("<hr>") || (L += "<hr>"), L += k(
P,
J,
a
);
}), x.length > z + 1 && (L.endsWith("<hr>") || (L += "<hr>"), x.forEach((C, _) => {
_ > z && (L.endsWith("<hr>") || (L += "<hr>"), L += k(C, I, a));
})), r.filter(
(C) => C.location === s.properties[h]
).forEach((C) => {
L.endsWith("<hr>") || (L += "<hr>"), L += C.tooltip;
});
} else {
const x = l || `{locationName} %({value},2)
{label}: %({value},2)`, D = {
value: null,
measure: this.getSelectedMeasure(),
measureLabel: s.properties.measureLabel,
locationName: this.getTranslatedLocationName(s, h, a),
...y
};
L += k(x, D, a), L += "</div>";
}
return L;
});
}
}
mousemove(e, s) {
this.tooltip.style("top", e.pageY + "px").style("left", e.pageX + 5 + "px");
}
mouseout(e, s) {
const { showTooltip: i } = this.props;
i && (g.select(this.getMapId()).select("svg").select("g").selectAll(".focus").attr("class", "active"), this.tooltip.style("visibility", "hidden"));
}
onClick(e, s) {
s.properties && this.tooltip.style("visibility", "visible").style("top", e.pageY + "px").style("left", e.pageX + 5 + "px"), e.stopPropagation(), e.preventDefault();
}
onPointClick(e, s, i) {
this.showTooltip(e, s), this.tooltip.style("visibility", "visible").style("top", e.pageY + "px").style("left", e.pageX + 5 + "px");
const o = g.select(this.getMapId()).select("svg").select("g");
o.selectAll("circle").style("fill", this.props.defaultPointColor).style("stroke", "none"), o.select("#circle" + i).raise().style("fill", "#fff");
}
onPolygonClick(e, s) {
const { mappingField: i } = this.props;
this.state.selectedPolygon !== s.properties[i] && s.properties.value !== null && this.setState({ selectedPolygon: s.properties[i] });
}
onZoomIn(e) {
g.select(this.getMapId()).select("svg").transition().call(this.zoom.scaleBy, 1.5);
}
onZoomOut() {
g.select(this.getMapId()).select("svg").transition().call(this.zoom.scaleBy, 0.6667);
}
getSelectedMeasure() {
let e = this.state.selectedMeasure;
return !e && this.props.transformedData && this.props.transformedData.measures && this.props.transformedData.measures.length > 1 && (e = this.props.transformedData.measures[0]), e;
}
getCollectionField(e) {
const { topoJSONField: s } = this.props;
if (e && e.objects) {
const i = Object.keys(e.objects);
for (const o in i) {
const t = i[o];
if (e.objects[t].type == "GeometryCollection")
return t;
}
}
return s;
}
extractFeatures(e) {
const s = this.getCollectionField(e);
return e && e.objects && e.objects[s] ? ie.feature(e, e.objects[s]).features : e && e.features ? e.features : [];
}
getLayers() {
const { layers: e } = this.state, { enabledLayers: s } = this.props;
return e && e.length > 0 ? e.map((o) => {
const t = s.find((r) => r.id == o.id);
return o.index = t ? t.index : 0, o;
}).sort((o, t) => parseInt(o.index) < parseInt(t.index) ? 1 : parseInt(o.index) > parseInt(t.index) ? -1 : 0) : [];
}
getFeatures() {
const e = this.getMainLayer(), s = this.getLayers();
if (e) {
const { transformedData: i, mappingField: o, app: t, mainLayerId: r, enabledLayers: n } = this.props;
let l = [];
try {
l = this.extractFeatures(e), l.map((p) => {
p.properties.layerId = r;
const d = s.find((u) => String(u.id) === String(r));
return d && (p.properties.layerMappingField = d.layerMappingField, p.properties.layerDatasource = d.layerDatasource, p.properties.layerApiField = d.layerApiField, p.properties.layerLocale = d.layerLocale, p.properties.displayLayerLabels = d.displayLayerLabels), n.forEach((u) => {
String(u.id) == r && Object.keys(u || {}).forEach((m) => {
p.properties[m] = u[m];
});
}), p;
}), s && s.forEach((p) => {
if (p.id != r) {
let d = this.extractFeatures(p.data);
d = d.map((u) => (u.properties.layerId = p.id, u.properties.layerMappingField = p.layerMappingField, u.properties.layerDatasource = p.layerDatasource, u.properties.layerApiField = p.layerApiField, u.properties.layerLocale = p.layerLocale, u.properties.displayLayerLabels = p.displayLayerLabels, u)), l = [...d, ...l];
}
});
} catch (p) {
console.log("error updating features .." + p);
}
const a = l.filter((p) => p.properties != null);
let h = i.locationsData;
return i.measures && i.measures.length > 1 && (h = i.locationsData.filter(
(p) => p.measure === this.getSelectedMeasure()
)), a.map((p) => {
if (h) {
const d = h.find((u) => {
const m = u.label ? ("" + u.label).toLowerCase() : "", v = p.properties[o] ? p.properties[o].toLowerCase() : "";
return m === v;
});
if (d) {
let u = d.measure;
i.measureLabelMap && d.measure && i.measureLabelMap[d.measure] && (u = i.measureLabelMap[d.measure]), p.properties.value = d.value, p.properties.measure = d.measure, p.properties.measureLabel = u, p.properties.children = d.children, p.properties.variables = d.variables, p.properties.hasDataRow = !0, Object.keys(d).forEach((m) => {
p.properties[m] = d[m];
});
} else
p.properties.value = null, p.properties.measure = null, p.properties.children = null, p.properties.measureLabel = null, p.properties.hasDataRow = !1;
}
}), a;
}
return [];
}
getMapId() {
const { unique: e } = this.props;
return ".map.wrapper." + e;
}
filterUpdated(e, s) {
const { zoomOnFilterField: i } = this.props, o = e && e.appliedFilters || {}, t = this.props.appliedFilters || {};
let r = !1;
return o[i] != t[i] && (r = !0), r;
}
getCenter(e, s) {
const { zoomOnFilter: i, zoomOnFilterField: o, mappingField: t, appliedFilters: r } = this.props;
let n = null;
if (i && o) {
let l = this.state.selectedPolygon;
s && r && r[o] && (l = r[o]);
const a = e.filter(
(h) => h.properties != null && h.properties[t] == l
)[0];
a && a.properties != null && a.properties.value && (n = a);
}
return n;
}
area(e) {
let s = 0;
const i = e.coordinates.length > 1 ? e.coordinates[0][0] : e.coordinates[0];
for (let o = 0; o < i.length - 1; o++)
s += i[o][0] * i[o + 1][1] - i[o + 1][0] * i[o][1];
return 0.5 * s;
}
centroid(e) {
const s = [0, 0], i = e.coordinates.length > 1 ? e.coordinates[0][0] : e.coordinates[0];
for (let t = 0; t < i.length - 1; t++)
s[0] += (i[t][0] + i[t + 1][0]) * (i[t][0] * i[t + 1][1] - i[t + 1][0] * i[t][1]), s[1] += (i[t][1] + i[t + 1][1]) * (i[t][0] * i[t + 1][1] - i[t + 1][0] * i[t][1]);
const o = this.area(e);
return s[0] /= o * 6, s[1] /= o * 6, s;
}
d3Map(e, s) {
let { zoomEnabled: i, mapContainerBgColor: o, mapPosition: t, editing: r, mapType: n } = this.props;
i || (i = !!["mobile", "tablet"].includes(T()));
const l = this.getBreaks(), a = g.select(this.getMapId());
let h = a.select("svg"), p = this.getWidth();
p === 0 ? p = window.innerWidth + Z[T()] : p += Z[T()];
const d = this.getHeight() - 100;
if (h.empty() ? h = a.append("svg") : h.selectAll("*").remove(), h.attr("style", `background-color:${o};`).attr("viewBox", `0 0 ${p} ${d}`).attr("preserveAspectRatio", "xMidYMid meet"), h.append("g").selectAll("path").data(e).enter().append("path").attr("fill", (m) => this.fillColor(m, l)).attr("d", g.geoPath().projection(this.projection)).attr("class", (m) => this.classColor(m)).on("mouseover", n !== "POINTS_MAP" ? this.showTooltip : null).on("mousemove", n !== "POINTS_MAP" ? this.mousemove : null).on("mouseout", n !== "POINTS_MAP" ? this.mouseout : null), this.mapPosition && h.transition().duration(300).call(
this.zoom.transform,
g.zoomIdentity.translate(this.mapPosition.x, this.mapPosition.y).scale(this.mapPosition.k)
), !this.mapPosition && t && t.x && t.y && t.k && (h.transition().duration(300).call(
this.zoom.transform,
g.zoomIdentity.translate(t.x, t.y).scale(t.k)
), n === "POINTS_MAP")) {
const v = {
mobile: 100,
tablet: 0,
midTablet: 0,
desktop: 0,
laptop: 0,
wide: 0
}[T()];
h.transition().duration(300).call(
this.zoom.transform,
g.zoomIdentity.translate(t.x + v, t.y).scale(t.k)
);
}
i || r ? h.call(this.zoom) : h.on("dblclick.zoom", null);
const u = this.getCenter(e, s);
if (u) {
const m = this.path.bounds(u), v = [
(m[0][0] + m[1][0]) / 2,
(m[0][1] + m[1][1]) / 2
];
h.transition().duration(750).call(
this.zoom.transform,
g.zoomIdentity.translate(p / 2, d / 2).scale(12).translate(-v[0], -v[1])
);
}
}
getAvg() {
const { transformedData: e } = this.props;
return e.nationalData.value;
}
selectedMeasureChanged(e) {
this.state.selectedMeasure != e && this.setState({ selectedMeasure: e });
}
getFilters() {
const { appliedFilters: e } = this.props, s = {};
return e && Object.keys(e).forEach((o) => {
const t = e[o];
t && (s[o] = Array.isArray(t) ? t.join(" ,") : t);
}), s;
}
getHighlightedLocationData() {
const { highlightedLocation: e, transformedData: s } = this.props;
let i = s.locationsData;
return s.measures && s.measures.length > 1 && (i = s.locationsData.filter(
(t) => t.measure === this.getSelectedMeasure()
)), i.find(
(t) => t.label === e
);
}
getTranslatedHighlightedLocationName() {
const { highlightedLocation: e, mapLabelField: s, intl: i } = this.props;
if (!e)
return null;
const t = this.getFeatures().find(
(r) => r.properties[s] === e
);
if (t && t.properties.displayLayerLabels && t.properties.layerLocale) {
const r = t.properties.layerMappingField || s, n = t.properties[r];
return O(this.metadataTypes, n, t.properties.layerLocale);
}
return e;
}
getHighlightedLocationColor(e) {
const s = this.getBreaks(), { mapNoDataColor: i } = this.props, o = e ? e.value : null;
if (o != null) {
const t = s.find((r) => {
if (r.min != null && r.max != null)
return o >= r.min && o <= r.max;
if (r.min != null)
return o >= r.min;
if (r.max != null)
return o <= r.max;
});
return t && t.color ? t.color : i;
}
return i;
}
renderLoader() {
return /* @__PURE__ */ b(U, { className: "loading", children: /* @__PURE__ */ b(
Q,
{
basic: !0,
padded: !0,
textAlign: "center",
style: { margin: "30px" },
children: /* @__PURE__ */ b(ee, { active: !0, inverted: !0, children: /* @__PURE__ */ b(te, { size: "medium" }) })
}
) });
}
noMapSelected() {
return /* @__PURE__ */ M(N, { icon: !0, warning: !0, children: [
/* @__PURE__ */ b(E, { name: "map outline" }),
/* @__PURE__ */ M(N.Content, { children: [
/* @__PURE__ */ b(N.Header, { children: "No map selected" }),
"Pick one from the list in the ",
/* @__PURE__ */ b("strong", { children: "Map Layers" }),
" section."
] })
] });
}
render() {
let {
app: e,
legendTitle: s,
nationalAverageLabel: i,
intl: o,
zoomEnabled: t,
transformedData: r,
measureSelectorLabel: n,
valueFormat: l,
showOverallValue: a,
unique: h,
highlightedLocation: p,
labelFontColor: d,
legendFontSize: u,
editing: m,
highlightedLocLabelFormat: v,
noDataText: c
} = this.props;
t || (t = !!["mobile", "tablet", "midTablet"].includes(
T()
));
const f = this.getAvg(), y = this.getFilters(), I = this.getHighlightedLocationData(), A = {
backgroundColor: this.getHighlightedLocationColor(I),
color: d,
fontSize: u + "px"
};
m && (A.marginTop = "25px");
const L = () => /* @__PURE__ */ M(U, { fluid: !0, className: "footnote ", children: [
/* @__PURE__ */ M(S, { columns: 2, children: [
e !== "csv" && a && /* @__PURE__ */ b(S.Column, { textAlign: "left", width: 4, children: /* @__PURE__ */ M("div", { className: "national-average-div", children: [
/* @__PURE__ */ b("span", { className: "national-avg-label", children: i }),
/* @__PURE__ */ b("span", { className: "national-avg-value", children: k(
l,
{ value: f },
o
) })
] }) }),
/* @__PURE__ */ b(
S.Column,
{
textAlign: "right",
width: e !== "csv" && a ? 12 : 16,
children: /* @__PURE__ */ b(
oe,
{
filteredBreaks: this.getBreaks(),
formattedLegendTitle: k(
s,
{ ...y },
o
),
selectedMeasure: this.state.selectedMeasure,
...this.props
}
)
}
)
] }),
/* @__PURE__ */ b("div", { className: "measure-selector", children: /* @__PURE__ */ M("ul", { children: [
n && /* @__PURE__ */ b("li", { children: /* @__PURE__ */ b("span", { className: "label", children: n }) }),
r && r.measures && r.measures.length > 1 && r.measures.map((x) => /* @__PURE__ */ M("li", { onClick: this.selectedMeasureChanged.bind(this, x), children: [
/* @__PURE__ */ b(
"input",
{
checked: this.getSelectedMeasure() === x,
type: "radio",
value: x
}
),
/* @__PURE__ */ b("label", { children: r.measureLabelMap[x] || x })
] }))
] }) })
] });
return /* @__PURE__ */ M("div", { className: "map component wp-data-viz-map", ref: this.mapContainer, children: [
this.state.layersLoading && (m ? this.noMapSelected() : this.renderLoader()),
!this.state.layersLoading && /* @__PURE__ */ M(R, { children: [
!$ && /* @__PURE__ */ b(L, {}),
/* @__PURE__ */ M(
"div",
{
className: "map wrapper scaling-svg-container " + h,
style: {
height: this.props.height - ce[T()] + "px"
},
children: [
I ? /* @__PURE__ */ b(
"div",
{
className: "highlighted-loc-info",
style: A,
children: /* @__PURE__ */ M("span", { children: [
" ",
k(
v,
{
value: I.value,
locationName: this.getTranslatedHighlightedLocationName() || I.label,
measureName: I.measure
},
o
)
] })
}
) : /* @__PURE__ */ b(R, { children: " " }),
(m || t) && !de && /* @__PURE__ */ M("div", { className: "control panel ignore", children: [
/* @__PURE__ */ b("div", { className: "zoom plus", onClick: this.onZoomIn, children: /* @__PURE__ */ b(E, { name: "plus", size: "large" }) }),
/* @__PURE__ */ b("div", { className: "zoom minus", onClick: this.onZoomOut, children: /* @__PURE__ */ b(E, { name: "minus", size: "large" }) }),
/* @__PURE__ */ b(
se,
{
content: /* @__PURE__ */ b(
K,
{
id: "map.reset.tooltip",
defaultMessage: "Reset zoom"
}
),
trigger: /* @__PURE__ */ b("div", { className: "reset", onClick: this.onReset, children: /* @__PURE__ */ b(E, { name: "repeat", size: "large" }) })
}
)
] })
]
}
),
$ && /* @__PURE__ */ b(L, {})
] })
] });
}
}
const Te = q(ue);
export {
Te as default,
O as getTranslatedItemLabel
};