How can I add an infoTemplate to an existing Graphic instance?
I'm doing this graphic instances without infoTemplate for every point that I have:
const layerMarkers = new GraphicsLayer({id: layerId});
this.map.addLayer(layerMarkers);
// Some code
const graphic = new Graphic(point, imageSymbol, null, null );
layerMarkers.add(graphic);
Then I want to call some API when I click the graphic:
layerMarkers.on('click', function(e) {
console.log(e);
console.log(this);
//Here I can see the object, I need to do something here with it
});
What I need is to set:
infoTemplate.setTitle(result.poi.nombre);
infoTemplate.setContent(this.getTooltip(result.poi));
And then I need to update my graph object with the infoTemplate
I can't do:
const graphic = new Graphic(point, imageSymbol, null, infoTemplate);
Or I will overwrite my object.
Any tips?
Just use the setInfoTemplate method on the graphic layer to set the template after the graphics layer is already instantiated.
If you just want to show the info window when a user clicks on the graphic layer then just use the show method on the maps InfoWindow instance.
this.map.infoWindow.show(e.screenPoint,e.getInfoWindowAnchor(e.screenPoint));
after you set the content.
You can also use setFeatures which will take a deferred (that eventually returns an array of graphics)
There's a bunch of ways to do what you want depending on the order of the user interactions and whether you are getting data for the popup over the network or not.
we are using OpenLayers 4.6.4 with the ol-ext extension in order to show beautiful looking clusters. That is working fine so far, but we need to link each cluster with custom informations (like putting a simple field into the cluster object) and once the cluster is clicked on the map, i want to retrieve it with the arguments delivered in the callback.
I was not able to find a simple example on putting custom fields on a cluster and retrieving them once i click them on the map.
The event i add my listener is handled by ol.interaction.SelectCluster from ol-ext
Any ideas?
You cannot change the cluster features so easily, but that's actually not necessary to get information about it.
Cluster is just a layer source which clusters (who would have guessed) an underlaying source. It creates Features, which in turn have the represented Features stored in a property features.
The ol-ext example demonstrates how to read the contained features:
var selectCluster = new ol.interaction.SelectCluster(.....);
selectCluster.getFeatures().on(['add'], function (e)
{ var c = e.element.get('features');
if (c.length==1)
{ var feature = c[0];
$(".infos").html("One feature selected...<br/>(id="+feature.get('id')+")");
}
else
{ $(".infos").text("Cluster ("+c.length+" features)");
}
})
Without any interaction, you could do this:
map.on('singleclick', function(evt) {
const feature = map.forEachFeatureAtPixel(
evt.pixel,
function(someFeature){ return someFeature; }, // stop at the very first feature
);
const containedFeatures = feature.get('features');
});
I am setting up the mxGraph like this:
model = new mxGraphModel();
// Tell the model to manage cell ids for us
model.isCreateIds();
graph = new mxGraph(container, model);
var graphMgr = new GraphManager(graph);
var edgeMgr = new EdgeManager();
// Configure mxGraph properties, methods, and event handlers
graphMgr.configure();
// Create style sheet
graphMgr.defineStyles();
// Initialize the layout managers
graphMgr.defineLayout(model);
// Get the main parent for the graph
var parent = graph.getDefaultParent();
var parallelEdges = new mxParallelEdgeLayout(graph);
parallelEdges.execute(parent);
That is the initial setup. I just added the mxParallelEdgeLayout thing because I have found that that is supposed to prevent edges from running on top of each other and overlap. What I mean by overlapping and running on top of each other:
I am using Orthogonal edge styling.
If this is not how you prevent this (using mxParallelEdgeLayout), then is there a way that mxGraph prevents this kind of thing? And if mxParallelEdgeLayout is the way to go, does anyone know why it is not accepting my execution of the layout?
Have you tried using the spacing variable?
var parallelEdges = new mxParallelEdgeLayout(graph);
parallelEdges.spacing = 20;
I have a page to display all markers on a single google map and use map.data.loadGeoJson to achieve this.
The markers link to their respective details page with the following:
map.data.addListener('click', function(event) {
var id = event.feature.getProperty('name');
window.location.href = 'submission-details.php?submission_id='+id;
});
(The property 'name' is a json file property and the ID for each submission).
I would like the details page to also show a map, but only with one marker relevant to that specific details page.
My json type is feature collection, so I think I need a way to target each feature from the collection, rather than just using map.data.loadGeoJson which displays them all.
Not sure where to go from here and struggled to find someone else asking the same question so any help appreciated!
UPDATE
Thanks Duncan.
I added:
map.data.loadGeoJson('../photo_share/upload/file.json', {},function() {
map.data.getFeatureById(53);
map.data.getFeatureById(53).getProperty('name');
console.log(map.data.getFeatureById(53));
console.log(map.data.getFeatureById(53).getProperty('name'));
});
google.maps.event.addDomListener(window, "load", initMap3);
And it does display in console log, but how do I now use this to only display the one marker, because it currently displays all of them still.
The google.maps.Data class has a getFeatureById method. Given you're passing that id into the submission-details.php page, you should be able to use that to get just the single feature.
You still need to use loadGeoJson, and wait until that's loaded before trying to access it; see Google Maps API: getFeatureById for feature collection not working
Update: And then to stop the whole collection displaying, maybe something like this (completely untested)?
map.data.loadGeoJson('../photo_share/upload/file.json', {}, function(features) {
// get the feature you want:
var feature = map.data.getFeatureById(53);
// hide the current set:
map.data.setMap(null);
// wipe out all the current features:
for (var i = 0; i < features.length; i++) {
map.data.remove(features[i]);
}
// add the feature you want back in:
map.data.add(feature);
// display it:
map.data.setMap(map);
});
I would like to add an overlay image on a Google Map. The image is a SVG file I have generated (Python with SVGFig).
I am using the following code:
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(48.8, 2.4), 12);
// ground overlay
var boundaries = new GLatLngBounds(new GLatLng(48.283188032632829, 1.9675270369830129), new GLatLng(49.187215000000002, 2.7771877478303999));
var oldmap = new GGroundOverlay("test.svg", boundaries);
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
map.addOverlay(oldmap);
}
Surprisingly, it works with Safari 4, but it doesn't work with Firefox (with Safari 3, the background is not transparent).
Does anyone have an idea on how I could overlay an SVG?
PS1: I read some works like this or the source code of swa.ethz.ch/googlemaps, but it seems that they have to use JavaScript code to parse the SVG and add one by one all the elements (but I did not understand all the source...).
PS2: The SVG is composed of different filled paths and circles, with transparency.
If there is no solution to overlay my SVG, I can use 2 alternative solutions:
rasterize the SVG
convert the paths and circles in GPolygons
But I do not really like the 1st solution because of the poor quality of the bitmap and the time to generate it with antialiasing.
And for the 2nd solution, the arcs, ellipses and circles will have to be decomposed into small polylines. A lot of them will be necessary for a good result. But I have around 3000 arcs and circles to draw, so...
Here are some news (I hope it's better to put them here in an answer, instead of editing my questions or to create a new question. Please feel free to move it if needed, or to tell me, so as I can rectify):
My problem was the following:
var oldmap = new GGroundOverlay("test.svg", boundaries);
map.addOverlay(oldmap);
did not work on Safari 3, Firefox and Opera (IE is not enable to draw SVG).
In fact, this code produce the insertion (in a <div>) of the following element
<img src="test.svg" style=".....">
And Safari 4 is able to draw a SVG file as an image, but this is not the way to do for the other browser. So the idea is now to create a custom overlay for the SVG, as explained here.
That's the reason why I asked for this question (I am sorry, but HTML/javascript are not my strongest points).
And since there is a small bug with Webkit for rendering a SVG with transparent background with <object>element, I need to use <object> or <img> accordingly to the browser (I don't like this, but... for the moment, it's still the quick-and-dirty experiments)
So I started with this code (still work in progress):
// create the object
function myOverlay(SVGurl, bounds)
{
this.url_ = SVGurl;
this.bounds_ = bounds;
}
// prototype
myOverlay.prototype = new GOverlay();
// initialize
myOverlay.prototype.initialize = function(map)
{
// create the div
var div = document.createElement("div");
div.style.position = "absolute";
div.setAttribute('id',"SVGdiv");
div.setAttribute('width',"900px");
div.setAttribute('height',"900px");
// add it with the same z-index as the map
this.map_ = map;
this.div_ = div;
//create new svg root element and set attributes
var svgRoot;
if (BrowserDetect.browser=='Safari')
{
// Bug in webkit: with <objec> element, Safari put a white background... :-(
svgRoot = document.createElement("img");
svgRoot.setAttribute("id", "SVGelement");
svgRoot.setAttribute("type", "image/svg+xml");
svgRoot.setAttribute("style","width:900px;height:900px");
svgRoot.setAttribute("src", "test.svg");
}
else //if (BrowserDetect.browser=='Firefox')
{
svgRoot = document.createElement("object");
svgRoot.setAttribute("id", "SVGelement");
svgRoot.setAttribute("type", "image/svg+xml");
svgRoot.setAttribute("style","width:900px;height:900px;");
svgRoot.setAttribute("data", "test.svg");
}
div.appendChild(svgRoot);
map.getPane(G_MAP_MAP_PANE).appendChild(div);
//this.redraw(true);
}
...
The draw function is not yet written.
I still have a problem (I progress slowly, thanks to what I read/learn everywhere, and also thanks to people who answer my questions).
Now, the problem is the following : with the <object> tag, the map is not draggable. All over the <object> element, the mouse pointer is not "the hand icon" to drag the map, but just the normal pointer.
And I did not find how to correct this. Should I add a new mouse event (I just saw mouse event when a click or a double-click append, but not for dragging the map...) ?
Or is there another way to add this layer so as to preserve the drag-ability ?
Thank you for your comments and answers.
PS: I also try to add one by one the elements of my SVG, but... in fact... I don't know how to add them in the DOM tree. In this example, the SVG is read and parsed with GXml.parse(), and all the elements with a given tag name are obtained (xml.documentElement.getElementsByTagName) and added to the SVG node (svgNode.appendChild(node)). But in my case, I need to add directly the SVG/XML tree (add all its elements), and there are different tags (<defs>, <g>, <circle>, <path>, etc.). It is may be simpler, but I don't know how to do.. :(
I spend the last evening on this problem, and I finally found the solution to my problem.
It was not so difficult.
The idea is, as Chris B. said, to load the SVG file with GDownloadUrl, parse it with GXml.parse() and add in the DOM tree every SVG elements I need
To simplify, I have supposed that all the SVG elements was put in a big group called "mainGroup". I have also supposed that some elements can be in the file.
So here is the library, based on the Google Maps Custom Overlays:
// create the object
function overlaySVG( svgUrl, bounds)
{
this.svgUrl_ = svgUrl;
this.bounds_ = bounds;
}
// prototype
overlaySVG.prototype = new GOverlay();
// initialize
overlaySVG.prototype.initialize = function( map)
{
//create new div node
var svgDiv = document.createElement("div");
svgDiv.setAttribute( "id", "svgDivison");
//svgDiv.setAttribute( "style", "position:absolute");
svgDiv.style.position = "absolute";
svgDiv.style.top = 0;
svgDiv.style.left = 0;
svgDiv.style.height = 0;
svgDiv.style.width = 0;
map.getPane(G_MAP_MAP_PANE).appendChild(svgDiv);
// create new svg element and set attributes
var svgRoot = document.createElementNS( "http://www.w3.org/2000/svg", "svg");
svgRoot.setAttribute( "id", "svgRoot");
svgRoot.setAttribute( "width", "100%");
svgRoot.setAttribute( "height","100%");
svgDiv.appendChild( svgRoot);
// load the SVG file
GDownloadUrl( this.svgUrl_, function( data, responseCode)
{
var xml = GXml.parse(data);
// specify the svg attributes
svgRoot.setAttribute("viewBox", xml.documentElement.getAttribute("viewBox"));
// append the defs
var def = xml.documentElement.getElementsByTagName("defs");
//for( var int=0; i<def.length; i++)
svgRoot.appendChild(def[0].cloneNode(true));
//append the main group
var nodes = xml.documentElement.getElementsByTagName("g");
for (var i = 0; i < nodes.length; i++)
if (nodes[i].id=="mainGroup")
svgRoot.appendChild(nodes[i].cloneNode(true));
});
// keep interesting datas
this.svgDiv_ = svgDiv;
this.map_ = map;
// set position and zoom
this.redraw(true);
}
// remove from the map pane
overlaySVG.prototype.remove = function()
{
this.div_.parentNode.removeChild( this.div_);
}
// Copy our data to a new overlaySVG...
overlaySVG.prototype.copy = function()
{
return new overlaySVG( this.url_, this.bounds_, this.center_);
}
// Redraw based on the current projection and zoom level...
overlaySVG.prototype.redraw = function( force)
{
// We only need to redraw if the coordinate system has changed
if (!force) return;
// get the position in pixels of the bound
posNE = map.fromLatLngToDivPixel(this.bounds_.getNorthEast());
posSW = map.fromLatLngToDivPixel(this.bounds_.getSouthWest());
// compute the absolute position (in pixels) of the div ...
this.svgDiv_.style.left = Math.min(posNE.x,posSW.x) + "px";
this.svgDiv_.style.top = Math.min(posSW.y,posNE.y) + "px";
// ... and its size
this.svgDiv_.style.width = Math.abs(posSW.x - posNE.x) + "px";
this.svgDiv_.style.height = Math.abs(posSW.y - posNE.y) + "px";
}
And, you can use it with the following code:
if (GBrowserIsCompatible())
{
//load map
map = new GMap2(document.getElementById("map"), G_NORMAL_MAP);
// create overlay
var boundaries = new GLatLngBounds( new GLatLng(48.2831, 1.9675), new GLatLng(49.1872, 2.7774));
map.addOverlay( new overlaySVG( "test.svg", boundaries ));
//add control and set map center
map.addControl(new GLargeMapControl());
map.setCenter(new GLatLng(48.8, 2.4), 12);
}
So, you can use it exactly as you use the GGroundOverlay function, except that your SVG file should be created with the Mercator projection (but if you apply it on small area, like one city or smaller, you will not see the difference).
This should work with Safari, Firefox and Opera. You can try my small example here
Tell me what do you think about it.
This question was briefly discussed on the Google Maps API Group. Here's what they said:
I've not tried it, but SVG is a subset
of XML, so you can read them with
GDownloadUrl() and analyse them with
GXml.parse(). On some wonky webservers
you may have to change the file
extension to XML.
You then have to crawl through the XML
DOM, writing the SVG that you find
with document.createElementNS() and
.setAttribute() calls...
There are also a some Google Maps SVG examples here and here.
Good luck!