google maps MarkerClusterer how to personalize the renderer - javascript

I'm using MarkerClusterer with google maps api, I'm trying to change the behavior of the markers with the renderer inerface, as they explain in the doc : https://googlemaps.github.io/js-markerclusterer
problem is, I don't understand the doc... it says that the MarkerClusterer class can accept parameters :
{
algorithm?: Algorithm;
map?: google.maps.Map;
markers?: google.maps.Marker[];
renderer?: Renderer;
onClusterClick?: onClusterClickHandler;
}
and indeed I can see it in the source code : https://github.com/googlemaps/js-markerclusterer/blob/1ee6469fa3c62a30c39cf509b379847741a7ebb9/src/markerclusterer.ts
and I can see here the implementation of DefaultRenderer, the default value for the renderer parameter : https://github.com/googlemaps/js-markerclusterer/blob/1ee6469fa3c62a30c39cf509b379847741a7ebb9/src/renderer.ts
so in my code, I thought I should create an object with a method called render that returns a new google.maps.Marker. I tried lots of different variations, I show you one there, in which I took the source code with few modifications (colors of markerclusters), I don't know what I should actually do to make it works :
function init_map() {
let map = new google.maps.Map(document.getElementById("ljdp_map"), { zoom: 2, center: {lat:46.227638, lng:2.213749} });
let markers = [];
for (ev of events) // events is defined outside this function
markers.push( new google.maps.Marker({ position: ev.coordinates, map: map }) );
// so, this is where I try to modify the cluster appearance, without luck
// maybe "my_renderer" need to be a class inheriting from DefaultRenderer ?
// class my_renderer extends markerClusterer.DefaultRenderer {
// it didn't work
let my_renderer = {
render({ count, position }, stats) {
const svg = window.btoa(`
<svg fill="#00ff00" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<circle cx="120" cy="120" opacity=".6" r="70" /></svg>`);
return new google.maps.Marker({
position,
icon: {
url: `data:image/svg+xml;base64,${svg}`,
scaledSize: new google.maps.Size(45, 45),
},
title: `Cluster of ${count} markers`,
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
// since the render argument is the fourth, after an algorithm argument, in the constructor,
// maybe I need to create an algorithm object to place the renderer at the right position ?
// let algorithm = new markerClusterer.SuperClusterAlgorithm({});
// new markerClusterer.MarkerClusterer({ map, markers, algorithm, my_renderer });
// it didn't work
new markerClusterer.MarkerClusterer({ map, markers, my_renderer });
}
the init function is called with :
<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=ini_map"></script>
and the marker clustering library is added by this script :
<script src="https://unpkg.com/#googlemaps/markerclusterer/dist/index.min.js"></script>
and the output is as if I didn't add my_renderer, cluster are not personalized. I don't know if I'm on the right way but making mistakes, or if it's not the way intended to modify the clusters ?

so, looking at this other SO post : Google Maps markerClusterer: how to combine Interface Renderer and GridOptions gridSize in configuration object?
I found what didn't work : the parameter name has to be exactly renderer and not my_renderer (that explains why the order in parameters didn't matters)
but I don't know why, maybe it's a typescript thing (I don't know typescript yet) ? I don't see it when I look at the source code, but there are lots of things I don't understand there anyway
the working code is the same, except for the object name :
function init_map() {
let map = new google.maps.Map(document.getElementById("ljdp_map"), { zoom: 2, center: {lat:46.227638, lng:2.213749} });
let markers = [];
for (ev of events) // events is defined outside this function
markers.push( new google.maps.Marker({ position: ev.coordinates, map: map }) );
// this is where the error was : the object has to be named exactly 'renderer'
let renderer = {
render({ count, position }, stats) {
const svg = window.btoa(`
<svg fill="#00ff00" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<circle cx="120" cy="120" opacity=".6" r="70" /></svg>`);
return new google.maps.Marker({
position,
icon: {
url: `data:image/svg+xml;base64,${svg}`,
scaledSize: new google.maps.Size(45, 45),
},
title: `Cluster of ${count} markers`,
zIndex: Number(google.maps.Marker.MAX_ZINDEX) + count,
});
}
}
new markerClusterer.MarkerClusterer({ map, markers, renderer });
}
I don't know why :/
(I'll mark my answer as the solution for the moment, since it works, even though I don't have the explanation why)

Related

OpenLayers overlapping LineStrings issue

I have one set of coordinates from which I create 2 LineStrings
// global variables, I have previously defined map
let route = null;
let routeFeature = null;
let routeOverlay = null;
let routeOverlayFeatur = null;
My main LineString
function createMainRoute(coordinates) {
route = new LineString(coordinates);
routeFeature = new Feature({ geometry: route });
routeFeature.setStyle(styles.mainRoute);
const routeLayer = new VectorLayer({
source: new VectorSource({
features: [routeFeature],
}),
});
map.addLayer(routeLayer);
}
My second LineString with the exact coordinates
function createMainRouteOverlay(coordinates) {
routeOverlay = new LineString(coordinates);
routeOverlayFeature = new Feature({ geometry: routeOverlay });
routeOverlayFeature.setStyle(styles.routeOverlay);
const routeOverlayLayer = new VectorLayer({
source: new VectorSource({
features: [routeOverlayFeature],
}),
});
map.addLayer(routeOverlayLayer );
}
Now I have a function that draws these 2 lines, this function is called when I get initial data from server
function init(coordinates) {
createMainRoute(coordinates);
createMainRouteOverlay(coordinates);
}
Goal is that I want to modify coordinates on the overlay line like the coordinates, style and so on without loosing track of the main route, but they are at the start the same.
Now I have a function that will dynamically change coordinates set of the overlay line. This function is called when the external data is changed.
function readjust(newCoordinates) {
routeOverlayFeature.getGeomety().setCoordinates(newCoordinates)
}
The problem here is that when I call readjust(), nothing happens on the map, line stays the same with these new coordinates, but when I comment out createMainRoute() from the init function, meaning i only have one line and just call readjust(), line is updated with the new data. Does anyone knows what is the issue here? Is something like this not supported or do I need to set some property to allow this? I don't get why they are in conflict when they are 2 separated variables and only one is updated. Any help is appreciated
Your features are sharing the same geometry
routeOverlayFeature = new Feature({ geometry: route });
should be
routeOverlayFeature = new Feature({ geometry: routeOverlay });

Creating MapKit JS with origin, destination and waypoints

I'm migrating from Google Maps API to Apple MapKit JS for the simple reason I have a developer account with them and they offer more free hits.
Anyway, actual examples of MapKit JS are a bit thin (or at least Google isn't finding them - draw what conspiracy theories you will), so although I've got the basics going of displaying an embeded map, I can't seem to do the next step which is route between two points (Apple's documentation also seems impenetrable as they don't show examples).
Here's my script for a basic map:
<script>
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
var MarkerAnnotation = mapkit.MarkerAnnotation
var myMarker = new mapkit.Coordinate(55.9496320, -3.1866360)
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.003, 0.003)
);
var map = new mapkit.Map("map");
var myAnnotation = new MarkerAnnotation(myMarker, { color: "#9b6bcc", title: "theSpace On The Mile"});
map.showItems([myAnnotation]);
map.region = myRegion;
</script>
Now I want to:
• Show a walking route between two points
• Include waypoints on the route
Could someone show the code that would achieve this? Once I can see an example I know I'll get it ;-)
Ok, so I've found a solution to this so sharing it here for the benefit of others.
Let's start by saying Apple's MapKit JS doesn't appear to have a waypoints option as offered by Google Maps API - so the way around that is to create a map that stores the markers in an array and then routes from one to the next. The code stores the location of the last waypoint in a variable, and doesn't bother to draw a route to the last waypoint if this is the first one in the array (obviously).
<script>
// Initiallise MapKit - you'll need your own long-lived token for this
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
// Function to draw the route once MapKit has returned a response
function directionHandler(error, data) {
data["routes"].forEach(function(route, routeIdx) {
if (routeIdx !== 0) { return; }
overlays = [];
route['path'].forEach(function(path) {
// This styles the line drawn on the map
let overlayStyle = new mapkit.Style({
lineWidth: 3,
strokeColor: "#9b6bcc"
});
let overlay = new mapkit.PolylineOverlay(path, {
style: overlayStyle
});
overlays.push(overlay);
});
map.addOverlays(overlays);
});
}
// This asks MapKit for directions and when it gets a response sends it to directionHandler
function computeDirections(origin,destination) {
let directionsOptions = {
origin: origin,
destination: destination,
transportType: mapkit.Directions.Transport.Walking
};
directions.route(directionsOptions, directionHandler);
}
// This sets the initial region, but is overridden when all points have been potted to automatically set the bounds
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.05, 0.05)
);
var map = new mapkit.Map("map");
map.region = myRegion;
var myAnnotations = [];
// lastWaypoint variable is 'unset' initially so the map doesn't try and find a route to the lastWaypoint for the first point of the route
var lastWaypoint = "unset";
var directions = new mapkit.Directions();
// Array of co-ordinates and label for marker
waypoints = [
{name:'Sofi’s Bar',lat:55.9746308,lon:-3.1722282},
{name:'TThe Roseleaf Cafe',lat:55.975992,lon:-3.173474},
{name:'Hemingway’s',lat:55.9763631,lon:-3.1706564},
{name:'Teuchter’s Landing',lat:55.9774693,lon:-3.1713826},
{name:'The King’s Wark',lat:55.9761425,lon:-3.1695419},
{name:'Malt and Hops',lat:55.975885,lon:-3.1698957},
{name:'The Carrier’s Quarters',lat:55.9760813,lon:-3.1685323},
{name:'Noble’s',lat:55.974905,lon:-3.16714},
{name:'The Fly Half',lat:55.9747906,lon:-3.1674496},
{name:'Port O’ Leith',lat:55.974596,lon:-3.167525}
];
// Loop through the array and create marker for each
waypoints.forEach(function(data) {
var myAnnotation = new mapkit.MarkerAnnotation(new mapkit.Coordinate(data['lat'],data['lon']), {
color: "#9b6bcc",
title: data['name']
});
myAnnotations.push(myAnnotation);
// As long as this isn't the first point on the route, draw a route back to the last point
if(lastWaypoint!="unset") {
computeDirections(lastWaypoint,new mapkit.Coordinate(data['lat'],data['lon']));
}
lastWaypoint = new mapkit.Coordinate(data['lat'],data['lon']);
});
map.showItems(myAnnotations);
</script>
This map is for a pub crawl around Leith, so the trasportType is 'Walking', but change that to 'Automobile' if you so wish.
With credit to Vasile whose MapKit JS Demo (https://github.com/vasile/mapkit-js-demo) helped me understand a lot more about the options.

Google Maps API -- Setting Icon for Marker Results in Error: "constructor Error cannot be invoked without 'new'"

I'm trying to create some markers to add using the Google Maps API, but if I try to specify an icon, I get the error: "constructor Error cannot be invoked without 'new'".
I'm running this via locahost.
Things I have tried:
Creating the marker, then setting the icon afterwards.
Different versions of the API.
Plugging my API key into the JSFiddle. (No errors.)
Removing/commenting out the icon lines. (Runs fine with the default icon.)
The code is pretty much verbatim from the Google-supplied JSFiddle for this, and it works there, but not on the page I'm working on.
const latlng = new google.maps.LatLng(this.location[0], this.location[1]);
this.marker = new google.maps.Marker({
position: latlng,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 10
},
draggable: false
});
I should have a circle icon at the specified position, but instead I get the following:
js?v=weekly&key=(APIKEY):53 Uncaught TypeError: Class constructor Error cannot be invoked without 'new'
at new Yc (:53)
at Object._.$c (:53)
at :56
at :56
at :56
at _.nf.setIcon (:66)
at .nf..T.setValues (:165)
at _.nf.mf [as constructor] (:79)
at new _.nf (:79)
at User.createMarker (classes.js:329)
It looks to me like there's a problem in the API's code that's making it difficult to debug the actual problem that's triggering the error.
Any suggestions would be appreciated.
Did you place the place in a class? I changed the official JSFiddle to use a class and it works.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {
lat: -25.363882,
lng: 131.044922
}
});
var Location = class Location {
constructor(lat, lng) {
this.location = [lat, lng]
const latlng = new google.maps.LatLng(this.location[0], this.location[1]);
this.marker = new google.maps.Marker({
position: latlng,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 10
},
draggable: false
});
this.marker.setMap(map);
}
}
var location = new Location(-25.363882, 131.044922);
}
What's happening is that the API is calling on a global Error object within the setIcon chain of functions (despite, apparently, a total lack of errors), which was causing an error due to me having overridden it with a custom Error class I had naively tried to add to the project.
Info on Error:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error

I'm using Google Maps in React and my markers don't always load?

So as the title says, I'm using React and trying to implement Google Maps with list of places fetched from Foursquare and display their markers. I'm not using any packages such as react-google-maps etc, just vanilla JS. The map loads just fine, the places are fetched BUT the markers show every now and then. When they do load, they point to correct location but they load rarely. I'm not sure what's wrong with my createMarkers function here:
// Initialize map
initMap = () => {
let map = new window.google.maps.Map(document.getElementById("map"), {
zoom: 14,
center: {lat: 45.5616873, lng: 18.6770196 }
});
this.createMarkers(map);
}
// The following function uses the places array to create an array of markers on initialize.
createMarkers = (map) => {
for (let i = 0; i < this.state.places.length; i++) {
// Get the position from the location array
let position = this.state.places[i].location;
let name = this.state.places[i].name;
let venueID = this.state.places[i].venueID;
// Create a marker per location, and put into markers array.
let marker = new window.google.maps.Marker({
position: position,
name: name,
animation: window.google.maps.Animation.DROP,
icon: this.state.defaultIcon,
venueID: venueID,
map: map
});
// Set state to save the marker to our array of markers.
this.setState((state) => ({
markers: [...state.markers, marker]
}))
}
}
// Create default marker icon
makeMarkerIcon(markerColor) {
const markerImage = new window.google.maps.Icon(
'http://chart.googleapis.com/chart?chst=d_map_spin&chld=1.15|0|'+ markerColor +
'|40|_|%E2%80%A2',
new window.google.maps.Size(21, 34),
new window.google.maps.Point(0, 0),
new window.google.maps.Point(10, 34),
new window.google.maps.Size(21,34)
);
return markerImage;
};
Any help would be much appreciated, as I'm still new to React and prone to beginner's mistakes.

How to store e.latlng into array : leaflet

I am working with leaflet api where user can put markers on map. I have made a custom button for putting marker.
I am willing to draw line between those markers i.e. using
L.polylines() but as I am new to javascript and leaflet I can't
understand how to pass those latlng point to array which will later be
used in these functions. For initial working I have passed static
coordinates(working as req).
L.easyButton('fa-link', function () {
var secureThisArea = [[-81, 100.75], [-76.50, 245.75], [-145.50, 184.25], [-128, 311.75]];
map.on('click', function fencePlace(e) {
L.marker([-81, 100.75], { icon: fenceIcon, draggable: true }).bindPopup("this is first").addTo(map);
L.marker([-76.50, 245.75], { icon: fenceIcon, draggable: true }).bindPopup("this is second").addTo(map);
L.marker([-145.50, 184.25], { icon: fenceIcon, draggable: true }).bindPopup("this is third").addTo(map);
L.marker([-128, 311.75], { icon: fenceIcon, draggable: true }).bindPopup("this is fourth").addTo(map);
L.polyline(secureThisArea).addTo(map);
});
}).addTo(map);
Adding another value to an array is easy, e.g.:
secureThisArea.push([-81, 100.75]);
You can find more details (also about anything else JavaScript related) at Mozilla Developer Network.
If you want to use the coordinates from the marker objects, you can get those with:
var myMarker = L.marker([-81, 100.75], { icon: fenceIcon, draggable: true }),
latLng = null;
latLng = myMarker.getLatLng();
Also take a look at the Leaflet documentation.
If i understand you correctly you want to create markers on click and connect them via polylines. That's easy to do, in code with comments to explain:
// Create new empty polyline and add it to the map
var polyline = new L.Polyline([]).addTo(map);
// Handle map click event
map.on('click', function(event) {
// New marker on coordinate, add it to the map
new L.Marker(event.latlng).addTo(map);
// Add coordinate to the polyline
polyline.addLatLng(event.latlng);
});
Now afterwards if you want to get all the coordinates added to the polyline you can use the getLatLngs method of L.Polyline which returns an array of L.LatLng objects.
Reference: http://leafletjs.com/reference.html#polyline
Example: http://plnkr.co/edit/h7aMwc?p=preview

Categories

Resources