Skip to content

Commit

Permalink
Map Widget Improvements (#53)
Browse files Browse the repository at this point in the history
Map Widget Improvements

Changed tile source so shows valid tiles up to max zoom
Selected-row's marker is highlighted green
Clustering behavior improved
No longer jumps around on zoom
Selecting a row zooms to it, instead of spiderfying
Minor other fixes
  • Loading branch information
jvorob authored Aug 1, 2023
1 parent 459a159 commit a3eaa73
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 21 deletions.
Binary file added map/marker-icon-2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added map/marker-icon-green-2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added map/marker-icon-green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added map/marker-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added map/marker-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
145 changes: 125 additions & 20 deletions map/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,78 @@ const Latitude = "Latitude";
const Geocode = 'Geocode';
// Optional - but required for geocoding. Field with address to find (might be formula)
const Address = 'Address';
// Optional - but
// Optional - but useful for geocoding. Blank field which map uses
// to store last geocoded Address. Enables map widget
// to automatically update the geocoding if Address is changed
const GeocodedAddress = 'GeocodedAddress';
let lastRecord;
let lastRecords;


//Color markers downloaded from leaflet repo, color-shifted to green
//Used to show currently selected pin
const selectedIcon = new L.Icon({
iconUrl: 'marker-icon-green.png',
iconRetinaUrl: 'marker-icon-green-2x.png',
shadowUrl: 'marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
const defaultIcon = new L.Icon.Default();



// Creates clusterIcons that highlight if they contain selected row
// Given a function `() => selectedMarker`, return a cluster icon create function
// that can be passed to MarkerClusterGroup({iconCreateFunction: ... } )
//
// Cluster with selected record gets the '.marker-cluster-selected' class
// (defined in screen.css)
//
// Copied from _defaultIconCreateFunction in ClusterMarkerGroup
// https://github.com/Leaflet/Leaflet.markercluster/blob/master/src/MarkerClusterGroup.js
const selectedRowClusterIconFactory = function (selectedMarkerGetter) {
return function(cluster) {
var childCount = cluster.getChildCount();

let isSelected = false;
try {
const selectedMarker = selectedMarkerGetter();

// hmm I think this is n log(n) to build all the clusters for the whole map.
// It's probably fine though, it only fires once when map markers
// are set up or when selectedRow changes
isSelected = cluster.getAllChildMarkers().filter((m) => m == selectedMarker).length > 0;
} catch (e) {
console.error("WARNING: Error in clusterIconFactory in map widget");
console.error(e);
}

var c = ' marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 100) {
c += 'medium';
} else {
c += 'large';
}

return new L.DivIcon({
html: '<div><span>'
+ childCount
+ ' <span aria-label="markers"></span>'
+ '</span></div>',
className: 'marker-cluster' + c + (isSelected ? ' marker-cluster-selected' : ''),
iconSize: new L.Point(40, 40)
});
}
};




const geocoder = L.Control.Geocoder && L.Control.Geocoder.nominatim();
if (URLSearchParams && location.search && geocoder) {
const c = new URLSearchParams(location.search).get('geocoder');
Expand Down Expand Up @@ -138,10 +205,19 @@ function updateMap(data) {
" in the Creator Panel.");
return;
}
const tiles = L.tileLayer('//server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}', {
maxZoom: 18,
attribution: 'Tiles &copy; Esri &mdash; National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'


// Map tile source:
// https://leaflet-extras.github.io/leaflet-providers/preview/
// Old source was natgeo world map, but that only has data up to zoom 16
// (can't zoom in tighter than about 10 city blocks across)
//
const tiles = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, DeLorme, NAVTEQ, USGS, Intermap, iPC, NRCAN, Esri Japan, METI, Esri China (Hong Kong), Esri (Thailand), TomTom, 2012'
});



const error = document.querySelector('.error');
if (error) { error.remove(); }
if (amap) {
Expand All @@ -153,10 +229,36 @@ function updateMap(data) {
console.warn(e);
}
}
const map = L.map('map', {layers: [tiles]});
const markers = L.markerClusterGroup();
const points = [];
popups = {};
const map = L.map('map', {
layers: [tiles],
wheelPxPerZoomLevel: 90, //px, default 60, slows scrollwheel zoom
});

//Make sure clusters always show up above points
//Default z-index for markers is 600, 650 is where tooltipPane z-index starts
map.createPane('selectedMarker').style.zIndex = 620;
map.createPane('clusters' ).style.zIndex = 610;
map.createPane('otherMarkers' ).style.zIndex = 600;

const points = []; //L.LatLng[], used for zooming to bounds of all markers

popups = {}; // Map: {[rowid]: L.marker}
// Make this before markerClusterGroup so iconCreateFunction
// can fetch the currently selected marker from popups by function closure

const markers = L.markerClusterGroup({
disableClusteringAtZoom: 18,
//If markers are very close together, they'd stay clustered even at max zoom
//This disables that behavior explicitly for max zoom (18)
maxClusterRadius: 30, //px, default 80
// default behavior clusters too aggressively. It's nice to see individual markers
showCoverageOnHover: true,

clusterPane: 'clusters', //lets us specify z-index, so cluster icons can be on top
iconCreateFunction: selectedRowClusterIconFactory(() => popups[selectedRowId]),
});


for (const rec of data) {
const {id, name, lng, lat} = getInfo(rec);
if (String(lng) === '...') { continue; }
Expand All @@ -165,35 +267,38 @@ function updateMap(data) {
continue;
}
const pt = new L.LatLng(lat, lng);
const title = name;
const marker = L.marker(pt, { title });
points.push(pt);
marker.bindPopup(title);

const marker = L.marker(pt, {
title: name,
icon: (id == selectedRowId) ? selectedIcon : defaultIcon,
pane: (id == selectedRowId) ? "selectedMarker" : "otherMarkers",
});

marker.bindPopup(name);
markers.addLayer(marker);

popups[id] = marker;
}
map.addLayer(markers);

try {
map.fitBounds(new L.LatLngBounds(points), {maxZoom: 12, padding: [0, 0]});
map.fitBounds(new L.LatLngBounds(points), {maxZoom: 15, padding: [0, 0]});
} catch (err) {
console.warn('cannot fit bounds');
}
function makeSureSelectedMarkerIsShown() {
const rowId = selectedRowId;

if (rowId && popups[rowId]) {
var marker = popups[rowId];
if (!marker._icon) { marker.__parent.spiderfy(); }
if (!marker._icon) { markers.zoomToShowLayer(marker); }
marker.openPopup();
}
}
map.on('zoomend', () => {
// Should reshow marker if it has been lost, but I didn't find a good
// event to trigger that exactly. A small timeout seems to work :-(
// TODO: find a better way; also, if user has changed selection within
// the map we should respect that.
setTimeout(makeSureSelectedMarkerIsShown, 500);
});

amap = map;

makeSureSelectedMarkerIsShown();
}

Expand Down
7 changes: 6 additions & 1 deletion map/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@ html, body, #map {
#lblText {
min-width: 100px;
display: inline-block;
}
}

/* JV: Needed to highlight selected cluster */
.marker-cluster-selected {
border: 3px solid #16b378;
}

0 comments on commit a3eaa73

Please sign in to comment.