OpenLayers not letting me add Popup - javascript

So I have (or had) a working version of OpenStreetMaps, but now that I want to add popups onto the map, the whole thing breaks. This is the code pertaining to the issue of the popup. The crazy thing is that I copy and pasted the code from the official wiki in order to just get a working example.
function init() {
map = new OpenLayers.Map( 'heatmapArea');
var query = new OpenLayers.LonLat(-122.2928337167, 37.5549570333).transform(
new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
var popup = new OpenLayers.Popup.FramedCloud("Popup", query, null, "Text", null, true);
map.addPopup(popup, false);
var lat = 39.3138895;
var lon = -98.2233523;
var zoom = 4;
var position = new OpenLayers.LonLat(lon, lat).transform( EPSG_WGS84, EPSG_900913);
map.setCenter(position, zoom );
}
The issue as it appears in my browser console is:
I have removed the code which I don't think is relevant to this issue but I could provide more if that is necessary. I have googled around extensively and all of the examples that I find work fine on the website I visit, but breaks my map and every StackOverflow answer to somebody else seems to work fine for the original poster, but once again, breaks my map.
Here's one of the website I tried to copy:
http://www.rigacci.org/openlayers/other_examples/markers.html
I am very eager to get this problem solved and any help would be greatly appreciated. Thanks!
-C.J.

Someone who really knows their way around the OL API will be able to explain this properly, but basically, your code is fine, but you need to reorder it. You need to add a map layer, and zoom to an extent, before you can call addPopup. I think this is because addPopup doesn't need an explicit layer of its own; it uses the map layer; and therefore you need a map layer on your map before trying to use it. That makes sense, but I am not sure why you need also to have called a zoom/zoomToExtent function.
Here's a fiddle, I've tried to leave your code as unchanged as possible:
http://jsfiddle.net/sifriday/u3j6h97d/3/
And here's the JS with some comments:
function init() {
var map = new OpenLayers.Map( 'heatmapArea');
// Add a map layer before trying to use addPopup
map.addLayer(new OpenLayers.Layer.OSM());
// Call the zoom function before trying to use addPopup
var lat = 39.3138895;
var lon = -98.2233523;
// I've changed the zoom to 1 so you can immediately see the popup in the small fiddle window
var zoom = 1;
var position = new OpenLayers.LonLat(lon, lat).transform(
"EPSG_WGS84", "EPSG_900913"
);
map.setCenter(position, zoom);
// Finally here's your addPopup code
var query = new OpenLayers.LonLat(
-122.2928337167, 37.5549570333
).transform(
new OpenLayers.Projection("EPSG:4326"),
map.getProjectionObject()
);
var popup = new OpenLayers.Popup.FramedCloud(
"Popup",
query,
// I added a size to make it fit in the small fiddle window
new OpenLayers.Size(100,100),
"Text",
null,
true
);
map.addPopup(popup);
}

Related

Finding vector source features in extent with wrapX

I currently have features on an ol.source.Vector that I would like to find by drawing a box (MultiPolygon) on the screen. I currently have this functionality working, however, if I move onto the next rendered world ("spin the globe" so to speak) when I draw my box over the same set of rendered features I get nothing back.
Example code:
var featureSource = new ol.source.Vector({
url: '/ShinyService/feature/geoJson',
format: new ol.format.GeoJSON()
});
var featureLayer = new ol.layer.Vector( {
source: featureSource
});
var myMap = new ol.Map({
layers: [ featureLayer],
view: new ol.View({
minZoom: 3,
maxZoom: 10
});
});
//Later within interaction event (draw end)
var boxExtent = box.getGeometry().getExtent();
vectorSource.forEachFeatureInExtent(boxExtent, function(feature){
foundFeature.push(feature.getId());
});
I'm currently thinking the only "solution" to this is to no longer allow the world to be rendered multiple times, but I don't think this is an option for the requirement I'm trying to fulfill.
I'm currently using Openlayers v3.18.2
Any help would be appreciated!
I was able to figure my problem out. It turned out I had to "wrap" the coordinates of my extent in order to get it to work properly as they were going past the -180 to 180 boundary. Once I did this every thing seemed to work.
However, this feels like something that should already be done in Openlayers so there could be something else I could be missing.

Animated heatmap with new data sets

I have created a simple heatmap (using google maps api v3), into which I would like to add an element of basic animation.
I have a number of collections of datapoints, each collection representing a time period (say 30 minutes).
I want to "replay" the heatmaps corresponding to one collection of data, followed by the next collection, and the next, and so on. Almost like replaying video frames.
I have tried doing this with an MVCArray, which I clear and update with the new datapoints. Something like this
data = new google.maps.MVCArray(datapoints);
then after a few seconds:
for (i=0;i<data.getLength();i++){
data.removeAt(i);
}
This does not work, because the new heatmap data will simply be added ontop of the old heatmap in the browser, even though the data array was succesfully cleared.
This problem, and a basic solution is described here:
Remove HeatmapLayer from google maps
The solution is to clear the heatmap using:
heatmap.setMap(null)
However, this does not work for me, since this will completely clear the heatmap, and leave the map empty for a second or two. That is to say, the animation effect is lost.
Any suggestions out there on what I could do?
Well, I don't know if it's possible to really animate one heatmap into the other but it is definitely possible to do something like this.
Which is basically doing the same thing you did, but at a quicker rate.
You can achieve something like that with some basic JavaScript.
For example:
<script type="text/javascript">
// initialise variables
var speed=5;
x=0;
var interval = setInterval(function(){},10000000);
var heatmap;
// function to change map
function changeSpeed(change) {
speed = speed+change;
if (speed<=0) speed=1;
if (speed>=10) speed=10;
// speed --> delay
if (speed>5) {
delay=1000/(speed-5);
} else {
delay=1000*(6-speed);
}
document.getElementById('speed').firstChild.nodeValue=speed;
// restarting the function
clearInterval(interval)
interval=setInterval(function(){
x++;
if (x>=heatmapFiles.length) x=0;
// document.getElementById('filename').firstChild.nodeValue=heatmapFiles[x];
var re = /[0-9]{8}/;
var datestr = re.exec(heatmapFiles[x])[0];
document.getElementById('date').firstChild.nodeValue=datestr.substring(6,8)+"."+datestr.substring(4,6)+"."+datestr.substring(0,4);
var req = new XMLHttpRequest();
req.open("GET", "./"+heatmapFiles[x], true);
req.onreadystatechange = function heatmapLoaded() {
if (req.readyState == 4) {
eval("map="+req.responseText);
heatmap.setData(map);
}
}
req.send(null);
// redraw
document.getElementById('boxed').style.display = 'none';
document.getElementById('boxed').style.display = 'inline-block';
},delay);
}
// this is important for you
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
center: new google.maps.LatLng(0,0),
zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
// creating a new heatmap
heatmap = new google.maps.visualization.HeatmapLayer({ radius: 50 });
heatmap.setMap(map);
// calling the 'change map' function
changeSpeed(0);
}
</script>
How does it work?
The heat map data are stored in JSON format in seperate Javascript files. A timer is used to load the next dataset. It will be parsed and assigned to the “data” property of the Google Maps HeatMapLayer object. After the property has been changed, the heatmap is redrawn automatically with the new data set. source
Alternatively
You could create the different heatmaps in different divs and then use jQuery to fadeIn the 'new' heatmap and fadeOut the 'old' heatmap at the same time. This would create a sort of 'animation'.
I hope this helps you out. Good luck!

Move marker dynamically

I am using Openlayers.Layer and OpenLayers.Marker to display a marker on the map.
It is positioned correctly and I can successfuly show it and hide it doing:
marker.display(boolean);
But I try to change its position before displaying it but with no success. I already tried this:
var projections = {
g: new OpenLayers.Projection("EPSG:4326"),
p: new OpenLayers.Projection("EPSG:900913")
};
var newlonlat = new OpenLayers.LonLat(newlon, newlat).transform(projections.g, projections.p);
marker.lonlat = newlonlat
layer.redraw();
(no errors triggered but position does not change)
and also tried this:
var px = map.getPixelFromLonLat(newlonlat);
marker.moveTo(px);
layer.redraw();
(it throws an error inside getPixelFromLonLat function. Error: c is null)
Why can't I move markers dynamically and what is the best way to do it?
Edit:
Maybe the problem resides in my position projection transformation when using second option:
new OpenLayers.LonLat(newlon, newlat).transform(projections.g, projections.p);
Edit 2
So, going deeper I found that marker.map property was null, so after its initialization I did:
var marker = new OpenLayers.Marker(lonlat, icon);
marker.map = map;
where map is an OpenLayers.Map object and now its working fine. Don't know why but it fixed it.
marker.moveTo works fine for me:
var px = map.getPixelFromLonLat(new OpenLayers.LonLat(newLon,newLat));
marker.moveTo(px);
Hope this helps :-)
To workaround the problem I did:
marker.lonlat = new OpenLayers.LonLat(newlon, newlat);
layer.removeMarker(marker);
layer.addMarker(marker);
layer.redraw();
It seems stupid removing and adding the same marker to just update its position but it works. It may be slower when doing this to a large group of markers though.
Try:
marker.lonlat = new OpenLayers.LonLat(newlon, newlat);
layer.drawMarker(marker);
(not testet)

js: the environment was captured during function creation. How can I overcome this?

I'm running the following script that does geocoding:
function find_callback(response){
var map = g_waze_map.map;
var first_result = response[0];
var lonlat = new OpenLayers.LonLat(first_result.location.lon,first_result.location.lat);
g_waze_map.map.setCenter(lonlat);
var size = new OpenLayers.Size(36,47);
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
var icon = new OpenLayers.Icon('http://www.waze.co.il/images/home.png',size,offset);
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(lonlat,icon));
map.addPopup(new OpenLayers.Popup.FramedCloud("test",lonlat,null,
"<div style='font-family:Arial,sans-serif;font-size:0.8em;'>"+first_result.name+"<div>",
anchor=null,true,null));
};
When I invoke the 3 calls to "find" in a batch (as part of my JS) I get a map with only the 3rd piont on it. When I invoke them via chrome console (one by one) I get a map with all 3 point on it. Is it the call_back that holds its environment? If so, how can I overcome this?
function onInit(){
g_waze_map.find('<%#Locations[2]%>','find_callback');
g_waze_map.find('<%#Locations[3]%>','find_callback');
g_waze_map.find('<%#Locations[5]%>','find_callback');
}
This script looks very strange to me. I'm assuming you only have one map object (g_waze_map.map) but for each callback you do g_waze_map.map.setCenter(lonlat); where lonlat is the latitude longitude of the first result. That doesn't make any sense.
If you calls to g_waze_map.find are asynchronous then you don't know what order they will finish in so I have no idea why you'd want to setCenter.
Also, why not just:
g_waze_map.find('<%#Locations[2]%>', find_callback);
g_waze_map.find('<%#Locations[3]%>', find_callback);
g_waze_map.find('<%#Locations[5]%>', find_callback);
// without the quotes, find_callback is a function afterall
My work-around was: sending an array of location to the g_waze_map.find();
That achived my goal, but I yet don't understand why it didn't work in the original way.

How can I overlay SVG diagrams on Google Maps?

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!

Categories