This is my first post here, hope someone can help.
I am building a mapping website using google maps.
The map shows upto 14 different icons based on a type category
I understand the theory of sprite images and the reduction in server calls these have so I have one image conating all the markers.
Question:
How do I set the position of the sprite, but only call the image once?
I have seen this post
Google map marker sprite image position
but that only shows changing the image for one marker
This is the code
//set the image once
var img = {
url:'images/site/type_sprite.png',
size: new google.maps.Size(32,32),
origin: new google.maps.Point(0,0)
};
idx = 0;
for (var i = 0; i < l; i++) {
lat = parseFloat(markers[i].getAttribute("lt"));
lng = parseFloat(markers[i].getAttribute("ln"));
var type = markers[i].getAttribute("typ");
if ((lat < latUpper && lat > latLower) && (lng < lngUpper && lng > lngLower) && (jQuery.inArray(type,filter_arr)>=0)) {
//change the position of the icon sprite depending on type
switch(type) {
case 'AA':
img(origin, new google.maps.Point(0,0));
break;
case 'AC':
img(origin = new google.maps.Point(0, 42));
break;
case 'ACF':
img(origin= new google.maps.Point(0,84));
break;
default:
img(origin= new google.maps.Point(0,0));
}
var id = markers[i].getAttribute("id");
var name = markers[i].getAttribute("nme");
//var icon = 'images/pins/' + type + '.png';
var point = new google.maps.LatLng(lat, lng);
var marker = new google.maps.Marker({
map: map,
position: point,
title: name,
icon: img
});
//add to valid array
markersArray.push(marker);
//build infowindow string
str = '<div class="iw_h">' + id + ' ' + name + '</div>';
bindInfoWindow(marker, map, infoWindow, str);
bounds.extend(point);
map.fitBounds(bounds);
idx++;
}
} //end of loop
}
If I put the code
var img = {
url:'images/site/type_sprite.png',
size: new google.maps.Size(32,32),
origin: new google.maps.Point(0,0)
};
inside the loop, doesn't that mean the image is repeatedly downloaded (and it will be a bigger image than an individual icon) thus defeating the object of using a sprite?
This is the site btw
http://www.searchforsites.co.uk/full_screen.php
Related
I've got a Google Maps API on my web site like this:
map = new google.maps.Map(document.getElementById("map"),
{
tilt:0
,mapTypeId: google.maps.MapTypeId.SATELLITE
,mapTypeControlOptions: {
mapTypeIds: ["satellite", "terrain","roadmap"],
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
position: google.maps.ControlPosition.TOP_CENTER
}
}
);
I would like to add a new raster overlay to this map.
This is the external overlay that I would like to add:
external jpeg
As you can see, there are 6 parameters that compose the URL to get the JPEG that I would like to overlay to Google Maps:
https://it-it.topographic-map.com/
?_path=api.maps.getOverlay
&southLatitude=43.40402777777778
&westLongitude=12.41375
&northLatitude=43.47263888888889
&eastLongitude=12.613194444444446
&zoom=13
&version=202211041528
4 parameters (Southlat, Westlng, Nordlat and Eastlng) that are the bounds of the image, the zoom level and version.
I would like to have some suggestions to write a JS function to add this layer to my Google Maps instance.
I tried looking at maptype image overlay example but I can see that there are only 2 coordinates and not 4, and I cannot understand how tiles works, and if this is compatible with my external image source.
The Topographic-map.com service indeed needs the 4 lat/lng coordinates and the zoom level so you can get an image of whatever dimensions you need.
Therefore the easiest method would be to use a Custom Overlay.
Based off the example linked above, you can create a map, get its bounds, calculate the 4 coordinates (and the zoom level) you need to request the image.
The idea is that you need to recreate the overlay with the new bounds and zoom level every time the user pans the map or zooms in/out, which is why I have used the idle map event listener in the below example.
Also with such a solution, it will work whatever the map size / aspect ratio.
let map;
let overlay = {};
function initMap() {
const mapOptions = {
center: new google.maps.LatLng(46.102284, 7.357985),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
// Listen for idle map event
google.maps.event.addListener(map, "idle", function() {
// Check if overlay already exists
if (overlay.hasOwnProperty('map')) {
// Remove overlay
overlay.setMap(null)
}
// Get the map bounds and needed coordinates and zoom level
let bounds = map.getBounds();
let southLat = bounds.getSouthWest().lat();
let northLat = bounds.getNorthEast().lat();
let eastLng = bounds.getNorthEast().lng();
let westLng = bounds.getSouthWest().lng();
let zoom = map.getZoom();
// Build the image URL
let srcImg = `https://it-it.topographic-map.com/?_path=api.maps.getOverlay&southLatitude=${southLat}&westLongitude=${westLng}&northLatitude=${northLat}&eastLongitude=${eastLng}&zoom=${zoom}&version=202211041528`;
// Create the overlay
overlay = new TopoOverlay(bounds, srcImg, map);
});
TopoOverlay.prototype = new google.maps.OverlayView();
}
/** #constructor */
function TopoOverlay(bounds, image, map) {
// Initialize all properties.
this.bounds_ = bounds;
this.image_ = image;
this.map_ = map;
// Define a property to hold the image's div. We'll
// actually create this div upon receipt of the onAdd()
// method so we'll leave it null for now.
this.div_ = null;
// Explicitly call setMap on this overlay.
this.setMap(map);
}
/**
* onAdd is called when the map's panes are ready and the overlay has been
* added to the map.
*/
TopoOverlay.prototype.onAdd = function() {
let div = document.createElement('div');
div.style.borderStyle = 'none';
div.style.borderWidth = '0px';
div.style.position = 'absolute';
// Create the img element and attach it to the div.
let img = document.createElement('img');
img.src = this.image_;
img.style.width = '100%';
img.style.height = '100%';
img.style.position = 'absolute';
div.appendChild(img);
this.div_ = div;
// Add the element to the "overlayLayer" pane.
let panes = this.getPanes();
//panes.overlayLayer.appendChild(div);
this.getPanes().overlayMouseTarget.appendChild(div);
};
TopoOverlay.prototype.draw = function() {
// We use the south-west and north-east
// coordinates of the overlay to peg it to the correct position and size.
// To do this, we need to retrieve the projection from the overlay.
let overlayProjection = this.getProjection();
// Retrieve the south-west and north-east coordinates of this overlay
// in LatLngs and convert them to pixel coordinates.
// We'll use these coordinates to resize the div.
let sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
let ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Resize the image's div to fit the indicated dimensions.
let div = this.div_;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
div.style.opacity = 0.75;
};
// The onRemove() method will be called automatically from the API if
// we ever set the overlay's map property to 'null'.
TopoOverlay.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
};
#map-canvas {
height: 180px;
}
<div id="map-canvas"></div>
<script defer src="//maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap"></script>
It doesn't work in the snippet because of Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
Not sure why I get this.
Working code here in a JSFiddle.
I am trying to switch a project from here maps 2.5.4 to 3.0.5.
I have a map with a custom animated image overlay. In 2.5.4 it is realized via ImageProvider:
var imageProvider = new nokia.maps.map.provider.ImageProvider({
opacity: 0.8,
getBoundingBox: function() {
return new nokia.maps.geo.BoundingBox(
new nokia.maps.geo.Coordinate(55.599073, 3.550307),
new nokia.maps.geo.Coordinate(47.27036232672, 15.434621365086)
)},
getUrl: function() {
return images[index]; // return the current image
},
updateCycle: 86400,
cache: new nokia.maps.util.Cache(100)
});
//add listener to show next image
imageProvider.addListener("update", function(evt) {
index++;
}
In v3.0.5 there is no ImageProvider and I didn't find another solution in the api documentation. Does anyone know how to realize it in v3 ?
Yup, there seems to be no ImageProvider (yet?).
You can hack it in, though:
// assuming you have a H.Map instance call "map"
// that's where we want the image
var rect = new H.geo.Rect(51, 12, 54, 15),
// that's the images we want
images = [
'http://newnation.sg/wp-content/uploads/random-pic-internet-06.jpg',
'http://newnation.sg/wp-content/uploads/random-pic-internet-04.jpg',
'http://newnation.sg/wp-content/uploads/random-pic-internet-06.jpg'
],
current = 0,
// that the image node we'll use
image = document.createElement('img');
// you could probably use CSS3 matrix transforms to improve performance
// but for demo purposes, I'lluse position:absolute and top/left for the image
image.style.position = "absolute";
image.style.opacity = "0.8";
// this function updates the image whenever something changes
var update = function() {
// project the rectangle's geo-coords to screen space
var topLeft = map.geoToScreen(rect.getTopLeft());
var bottomRight = map.geoToScreen(rect.getBottomRight());
// calculate top/left and width/height
var offsetX = topLeft.x;
var offsetY = topLeft.y;
var width = bottomRight.x - topLeft.x;
var height = bottomRight.y - topLeft.y;
// set image source (update is also called, when we choose another image)
image.src = images[current];
// set image position and size
image.style.top = offsetY + "px";
image.style.left = offsetX + "px";
image.style.width = width + "px";
image.style.height = height + "px";
};
// append the image
map.getViewPort().element.appendChild(image);
// set initial values
update();
// update whenever viewport or viewmodel changes
map.getViewPort().addEventListener('sync', function() {
update();
});
map.getViewModel().addEventListener('sync', function() {
update();
});
// zoom to rectangle (just to get the images nicely in view)
map.setViewBounds(rect);
// start the image change interval
setInterval(function() {
current = (current + 1) % 3;
update();
}, 3000);
I have been trying to create a Placemark that I can hide and show (like turning visibility on and off) on demand (on click)... I am using this to make the placemark:
function placemark(lat, long, name, url, iconsrc){
var placemark = ge.createPlacemark(name);
ge.getFeatures().appendChild(placemark);
placemark.setName(name);
// Create style map for placemark
var icon = ge.createIcon('');
if(iconsrc == "0")
icon.setHref('http://maps.google.com/mapfiles/kml/paddle/red-circle.png');
else{
icon.setHref(iconsrc);
}
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
if(iconsrc != "0")
style.getIconStyle().setScale(2.5);
placemark.setStyleSelector(style);
// Create point
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(long);
//point.setAltitudeMode(1500);
placemark.setGeometry(point);
google.earth.addEventListener(placemark, 'click', function(event) {
// Prevent the default balloon from popping up.
event.preventDefault();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark); // optional
balloon.setContentString(
'<iframe src="'+ url +'" frameborder="0"></iframe>');
ge.setBalloon(balloon);
});
}
I have tried everything... from this:
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
to using this ge.getFeatures().removeChild(child);
can anyone point me to the right direction on creating a function that will allow me to turn the visibility on/off on demand please.
Your hidePlacemark function is missing some {} in your final IF statement
if(child.getId()== name)
you have
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
make it
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name) {
child.setVisibility(false);
}
}
}
}
HOWEVER ------- you are better off doing this as it is much faster as you don't need to loop through ALL your placemarks
function hidePlacemark(name) {
var placemark = ge.getElementById(name);
placemark.setVisibility(false);
}
I think the plain ge.getFeatures().removeChild(placemark); works.
I played with this GooglePlayground, and just added the following code to line 8 (that is empty in this GooglePlayground Sample):
addSampleButton('Hide Placemark', function(){
ge.getFeatures().removeChild(placemark);
});
Clicking the button Hide Placemark hides the placemark like a charm here. Any chances your problem is somewhere else in your code?
I have a map in OpenLayers with a simple layer and a marker layer.
The markers in the marker layer are generated from data.rows. For every marker, I want to register a function to the "mousedown" (or "click") event, but that does not seem to work. The markers got added to the map, but upon clicking, the function registered to the event is not entered.
// Marker-Layer
var markers = new OpenLayers.Layer.Markers("SABA")
// Icon
var size = new OpenLayers.Size(21, 25);
var offset = new OpenLayers.Pixel(-(size.w / 2), -size.h);
var iconPath = document.location.protocol + '//' + document.location.hostname + '/saba/modulesinst/sa/icons/pin.png';
var icon = new OpenLayers.Icon(iconPath, size, offset);
// Rows durchgehen
Array.each(data.rows, function(item, index) {
if (item.x != null && item.y != null) {
var newmarker = new OpenLayers.Marker(new OpenLayers.LonLat(item.x, item.y), icon.clone())
newmarker.events.register('mousedown', newmarker, function(evt) {
alert(item.name);
OpenLayers.Event.stop(evt);
});
markers.addMarker(newmarker);
}
});
this.listMap.addLayer(markers);
The Openlayers Documentation states that you should use a vector layer for this purpose instead of a marker layer: "Markers are the ‘older’ way to interact with geographic data in the browser. Most new code should, where possible, use vector layers in place of marker layers".
When you use a vector layer you can add markers like this:
var marker = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lon,lat), attributes);
vectorLayer.addFeatures(marker);
and you can use this code to initialize the vector layer:
function selected (evt) {
alert(evt.feature.id + " selected on " + this.name);
}
var layer = new OpenLayes.Layer.Vector("VLayer");
layer.events.register("featureselected", layer, selected);
and finally this to add the select feature control to your map:
var control = new OpenLayers.Control.SelectFeature(layer);
map.addControl(control);
control.activate();
Refer to this Openlayers Documentation
I have a some UI elements on the right of my map (sometimes), and I'd like to offset my panTo() calls (sometimes).
So I figured:
get the original latlng
convert it to screen pixels
add an offset
convert it back to latlng.
But I must misunderstand what Google Maps API refers to as the "Point Plane":
http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection
Here is my code that seems to offset by lat-long:
function getCentreOffset( alatlng ) {
var PIXEL_OFFSET= 100;
var aPoint = me.gmap.getProjection().fromLatLngToPoint(alatlng);
aPoint.x=aPoint.x + OFFSET;
return me.gmap.getProjection().fromPointToLatLng(aPoint);
}
Here's a simpler version of Ashley's solution:
google.maps.Map.prototype.panToWithOffset = function(latlng, offsetX, offsetY) {
var map = this;
var ov = new google.maps.OverlayView();
ov.onAdd = function() {
var proj = this.getProjection();
var aPoint = proj.fromLatLngToContainerPixel(latlng);
aPoint.x = aPoint.x+offsetX;
aPoint.y = aPoint.y+offsetY;
map.panTo(proj.fromContainerPixelToLatLng(aPoint));
};
ov.draw = function() {};
ov.setMap(this);
};
You can then use it like this:
var latlng = new google.maps.LatLng(-34.397, 150.644);
var map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP,
center: latlng
});
setTimeout(function() { map.panToWithOffset(latlng, 0, 150); }, 1000);
Here is a working example.
Let me explain in detail. This extends the Map object itself. So you can use it just like panTo() with extra parameters for offsets. This uses the fromLatLngToContainerPixel() and fromContainerPixelToLatLng() methods of the MapCanvasProjecton class. This object has no contructor and has to be gotten from the getProjection() method of the OverlayView class; the OverlayView class is used for the creation of custom overlays by implementing its interface, but here we just use it directly. Because getProjection() is only available after onAdd() has been called. The draw() method is called after onAdd() and is defined for our instance of OverlayView to be a function that does nothing. Not doing so will otherwise cause an error.
Answer by Dean looks a lot cleaner as said in some comments, but was looking a little complicated to me. This single line solution is looking more elegant to me.
var map = $('#map_canvas').gmap3("get")
map.panBy(-500,-500); // (x,y)
Set center of map first. Then panyBy will shift the center in (x,y) direction. The more negative x, map will shift right. The more negative y, map will shift down.
Ok I found the answer here: How to call fromLatLngToDivPixel in Google Maps API V3?
First create function/prototpe to access the map's projection (difficult in V3)
//declare function/prototpe
function CanvasProjectionOverlay() {}
//define..
CanvasProjectionOverlay.prototype = new google.maps.OverlayView();
CanvasProjectionOverlay.prototype.constructor = CanvasProjectionOverlay;
CanvasProjectionOverlay.prototype.onAdd = function(){};
CanvasProjectionOverlay.prototype.draw = function(){};
CanvasProjectionOverlay.prototype.onRemove = function(){};
var gmap;
var canvasProjectionOverlay;
var PIXEL_OFFSET= 100;
function showUluru(isOffset=false){
//create map
var gmap = new google.maps.Map($('#map_canvas', {});
//create projection
canvasProjectionOverlay = new CanvasProjectionOverlay();
canvasProjectionOverlay.setMap(gmap);
var uluruRock = new google.maps.LatLng(-25.335448,135.745076);
if (isOffset)
uluruRock = getCentreOffset(uluruRock);
gmap.panTo( uluruRock )
}
//Use this function on LatLng you want to PanTo();
function getCentreOffset( alatlng ) {
var proj = canvasProjectionOverlay.getProjection();
var aPoint = proj.fromLatLngToContainerPixel(alatlng);
aPoint.x=aPoint.x+PIXEL_OFFSET;
return proj.fromContainerPixelToLatLng(aPoint);
}