diff --git a/client/app/assets/less/inc/visualizations/map.less b/client/app/assets/less/inc/visualizations/map.less
index 85d6c49d32..58407e4964 100644
--- a/client/app/assets/less/inc/visualizations/map.less
+++ b/client/app/assets/less/inc/visualizations/map.less
@@ -7,3 +7,8 @@
z-index: 0;
}
}
+
+.leaflet-popup-content img {
+ max-width: 100%;
+ height: auto;
+}
diff --git a/client/app/visualizations/map/Editor/FormatSettings.jsx b/client/app/visualizations/map/Editor/FormatSettings.jsx
new file mode 100644
index 0000000000..2e53da8fd5
--- /dev/null
+++ b/client/app/visualizations/map/Editor/FormatSettings.jsx
@@ -0,0 +1,71 @@
+import React from "react";
+import { useDebouncedCallback } from "use-debounce";
+import { Section, Input, Checkbox, TextArea, ContextHelp } from "@/components/visualizations/editor";
+import { EditorPropTypes } from "@/visualizations";
+
+function TemplateFormatHint() {
+ // eslint-disable-line react/prop-types
+ return (
+
+
+ All query result columns can be referenced using {"{{ column_name }}"}
syntax.
+
+ Leave this field empty to use default template.
+
+ );
+}
+
+export default function FormatSettings({ options, onOptionsChange }) {
+ const [onOptionsChangeDebounced] = useDebouncedCallback(onOptionsChange, 200);
+
+ const templateFormatHint = ;
+
+ return (
+
+
+ onOptionsChange({ tooltip: { enabled: event.target.checked } })}>
+ Show tooltip
+
+
+
+
+
+
+ onOptionsChange({ popup: { enabled: event.target.checked } })}>
+ Show popup
+
+
+
+
+
+ );
+}
+
+FormatSettings.propTypes = EditorPropTypes;
diff --git a/client/app/visualizations/map/Editor/index.js b/client/app/visualizations/map/Editor/index.js
index 58334854d9..5c61e22832 100644
--- a/client/app/visualizations/map/Editor/index.js
+++ b/client/app/visualizations/map/Editor/index.js
@@ -2,10 +2,12 @@ import createTabbedEditor from "@/components/visualizations/editor/createTabbedE
import GeneralSettings from "./GeneralSettings";
import GroupsSettings from "./GroupsSettings";
+import FormatSettings from "./FormatSettings";
import StyleSettings from "./StyleSettings";
export default createTabbedEditor([
{ key: "General", title: "General", component: GeneralSettings },
{ key: "Groups", title: "Groups", component: GroupsSettings },
+ { key: "Format", title: "Format", component: FormatSettings },
{ key: "Style", title: "Style", component: StyleSettings },
]);
diff --git a/client/app/visualizations/map/getOptions.js b/client/app/visualizations/map/getOptions.js
index cb8e499e58..a87209ac43 100644
--- a/client/app/visualizations/map/getOptions.js
+++ b/client/app/visualizations/map/getOptions.js
@@ -14,6 +14,14 @@ const DEFAULT_OPTIONS = {
backgroundColor: "#356AFF",
borderColor: "#356AFF",
bounds: null,
+ tooltip: {
+ enabled: false,
+ template: "",
+ },
+ popup: {
+ enabled: true,
+ template: "",
+ },
};
export default function getOptions(options) {
diff --git a/client/app/visualizations/map/initMap.js b/client/app/visualizations/map/initMap.js
index 954b91d60d..5733367d0b 100644
--- a/client/app/visualizations/map/initMap.js
+++ b/client/app/visualizations/map/initMap.js
@@ -1,4 +1,4 @@
-import { isFunction, each, map, toString } from "lodash";
+import { isFunction, each, map, toString, clone } from "lodash";
import chroma from "chroma-js";
import L from "leaflet";
import "leaflet.markercluster";
@@ -12,6 +12,8 @@ import markerIconRetina from "leaflet/dist/images/marker-icon-2x.png";
import markerShadow from "leaflet/dist/images/marker-shadow.png";
import "leaflet-fullscreen";
import "leaflet-fullscreen/dist/leaflet.fullscreen.css";
+import { formatSimpleTemplate } from "@/lib/value-format";
+import { $sanitize } from "@/services/ng";
import resizeObserver from "@/services/resizeObserver";
import chooseTextColorForBackground from "@/lib/chooseTextColorForBackground";
@@ -96,6 +98,10 @@ function createMarkersLayer(options, { color, points }) {
// create markers
each(points, ({ lat, lon, row }) => {
+ const rowCopy = clone(row);
+ rowCopy[options.latColName] = lat;
+ rowCopy[options.lonColName] = lon;
+
let marker;
if (classify) {
marker = createHeatpointMarker(lat, lon, color);
@@ -107,12 +113,28 @@ function createMarkersLayer(options, { color, points }) {
}
}
- marker.bindPopup(`
-
- - ${lat}, ${lon}
- ${map(row, (v, k) => `
- ${k}: ${v}
`).join("")}
-
- `);
+ if (options.tooltip.enabled) {
+ if (options.tooltip.template !== "") {
+ marker.bindTooltip($sanitize(formatSimpleTemplate(options.tooltip.template, rowCopy)));
+ } else {
+ marker.bindTooltip(`
+ ${lat}, ${lon}
+ `);
+ }
+ }
+
+ if (options.popup.enabled) {
+ if (options.popup.template !== "") {
+ marker.bindPopup($sanitize(formatSimpleTemplate(options.popup.template, rowCopy)));
+ } else {
+ marker.bindPopup(`
+
+ - ${lat}, ${lon}
+ ${map(row, (v, k) => `
- ${k}: ${v}
`).join("")}
+
+ `);
+ }
+ }
result.addLayer(marker);
});