I have a Google Maps canvas that stretches the full width and height of the page. Overlaid on top of it is a header which is fixed height (100 pixels) and a sidebar which is a responsive width (20% + 5% margin).
Fiddle for demonstration: http://jsfiddle.net/L9yjvdLv/1/
The problem I'm facing is making sure that all the markers on the map are visible.
I tried playing around with fitBounds, but the problem is that the map doesn't take into account the overlaid elements, meaning markers will be behind the sidebar or header elements, or very close to them.
How do I zoom and center the map so that all markers are visible in the "usable" area of the map?
You will need to use the map projection and fromLatLngToPoint to translate between coordinates and points to be able to take into account your different overlay elements.
For a full explanation, please check this answer.
function fromLatLngToPoint(latLng) {
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng());
var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
var worldCoordinate = map.getProjection().fromLatLngToPoint(latLng);
return new google.maps.Point(Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale), Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale));
}
My example only has a left sidebar overlay but you should be able to adapt the functions to your needs.
JSFiddle demo
Hope this helps.
Related
I am using jVectorMap, everything works fine without zooming.
But when a user zoomed in the page I need to allow the user to scroll the the map using a vertical and horizontal scroll bar.
I have tried to add overflow-y: scroll; And other many options to do the scrolling but nothing works perfectly.
I can set the width and height of div to get the scroll bar but it is not related with map zoom in and zoom out.
So I am expecting a scroll bar horizontally and vertically which using that user can see the full map if even it is zoomed.
I have seen a map with below image in the internet
But No idea how can I add a scroll button control like this in jVector map.
Can someone help me to resolve this issue.?
You need two steps:
To understand how the map is translated inside the container, initialize the Map with the onViewportChange event:
$("#map").vectorMap({
map: "world_mill",
// set map properties, series, and so on
//...
onViewportChange: function(event, scaleFactor,transX,transY){
// look at the values here:
console.log("Viewport changed",scaleFactor,transX,transY);
}
});
To the point:
to apply a map translation, set your desired X and Y panning, at the end invoke the applyTransform function:
Example:
var worldMap = $("#map").vectorMap("get", "mapObject");
worldMap.transX = -100;
worldMap.applyTransform();
Additional information:
Luckily, jVectorMap will do the range checking for you, so for your pan buttons you can also simply use somethng like:
worldMap.transX -= (10 * worldMap.scale); // move left
worldMap.transX += (10 * worldMap.scale); // move right
worldMap.transY -= (10 * worldMap.scale); // move up
worldMap.transY += (10 * worldMap.scale); // move down
You will find the range check in the applyTransform function in jVectorMap source code.
Credits: Kirill Lebedev, the great author of jVectorMap.
Lastly, the re-center button:
You can get the center of the map as follows:
var mapCX = (worldMap.width / 2) * worldMap.scale + worldMap.transX * worldMap.scale;
var mapCY = (worldMap.height / 2) * worldMap.scale + worldMap.transY * worldMap.scale;
As you haven't provide any source code, I can't help further, but if you have understand the concept, the transformation between your scrollbar range and the map translation is trivial easy.
I have a map that fills the screen, and a horizontal overlay of non-map content displayed in the bottom portion of the screen. I want to display a polyline on the map so that it as large as possible within the map view but not hidden below the overlaid content.
Below is what I am trying and it nearly works but gives different results depending on the zoom / position of the map's current view. I need something independent of the current view of the map.
// `map` is the leaflet map
// `polyline` is a leaflet polyline
function fitBounds (latLngBounds, offsetY) { // offsetY in pixels
var zoom, southeast, southeastOffset, newBounds;
if (offsetY) {
zoom = map.getBoundsZoom(latLngBounds);
southeast = map.project(latLngBounds.getSouthEast(), zoom);
southeastOffset = new L.Point(southeast.x, southeast.y + offsetY);
newBounds = latLngBounds.extend(map.unproject(southeastOffset, zoom));
}
map.fitBounds(newBounds || latLngBounds);
}
var contentHeight = 350;
// this will be calculated and is the distance from the top of the
// overlaid content to the bottom of the map
fitBounds(polyline.getBounds(), contentHeight);
The map.getBoundsZoom(latLngBounds) and project/unproject seem to return different values when the map is panned or zoomed differently. I understood from the docs that they'd be independent of the current map view.
Am I using the methods wrong, or is there a different strategy to achieve what I need? Thanks.
getBoundsZoom is dependent on the current map view port size. Therefore if you test with different sizes (e.g. your map container fills the whole page width and you have varying browser window width, possibly because of the console), the results will be different.
If you are sure the map container size has not changed, and you can reproduce the problem on JSBin / Plunker / JSFiddle, then there might be a bug in Leaflet; feel free to report it in the issue tracker.
I want to display objects on a map in their exact dimensions.
I have reference data about these objects, containing the length and width of the object in meters, now I need to convert the meters into pixels for the leaflet icon size based on the zoom level.
var meterlength = 50;
var meterwidth = 40;
//convert meters to pixels
var icon = L.divIcon({html:"<svg>..</svg>",iconSize:[xx,yy]});
I found this https://github.com/makinacorpus/Leaflet.GeometryUtil/
GeometryUtils with the length and distance function, but could not get it working.
Any ideas ? Thank you very much !
EDIT:
This answer solved my original problem:
https://gis.stackexchange.com/a/198444
But I'm sacling the icons on map zoom using the "zoomanim" event - unfortunately the map.containerPointToLatLng() Methode applies to the old zoom level not to the new one.
Is there a Workaround ?
If you want your image to represent the object real size, then you should probably better use an L.imageOverlay instead of a marker and trying to adjust its size based on zoom level.
See https://gis.stackexchange.com/questions/171609/resize-divicons-svgs-at-zoom-levels-leaflet
Then you would need to find the appropriate coordinates for your Image Overlay bounds, not pixels.
Now if you really want to change your marker icon size depending on the zoom level, you have several posts on SO and GIS SE that cover this topic.
E.g. Best way to make marker resizable in leaflet
I want center my marker on popup open.. and centering map not in marker latlng, but on center of marker and popup!
The problem is that popup has dinamic content(loaded on click).
The map size is full display size in a mobile device!
I'm just used autoPanPadding option in popup but not sufficient
Refer to follow picture:
Using fitzpaddy's answer I was able to make this code which works and is much more flexible.
map.on('popupopen', function(e) {
var px = map.project(e.target._popup._latlng); // find the pixel location on the map where the popup anchor is
px.y -= e.target._popup._container.clientHeight/2; // find the height of the popup container, divide by 2, subtract from the Y axis of marker location
map.panTo(map.unproject(px),{animate: true}); // pan to new center
});
Ciao Stefano,
This is untested pseudocode, but Leaflet project/unproject functions should provide assistance.
i.e;
// Obtain latlng from mouse event
var latlng;
// Convert latlng to pixels
var px = project(latlng);
// Add pixel height offset to converted pixels (screen origin is top left)
px.y -= mypopup.height/2
// Convert back to coordinates
latlng = unproject(px);
// Pan map
map.panTo(latlng,{animate: true});
This depends on zoom scale being constant during calculation, so you might be required to pan the map and then calculate the pan offset to update correctly (using animation, this will only be a gentle transition).
Good luck!
Here's an easy solution:
First you center the map to your marker.
map.setView(marker.latLng);
Then you open the popup.
var popup.openOn(map); = L.popup()
.setLatLng(marker.latLng)
.setContent(dynamic-content)
.openOn(map);
Leaflet will automatically pan the map so the popup fits on the map. To make it look more beautiful you can add a margin-top to the popup with CSS.
My very simple solution keeps the current zoom level as well for better usability.
map.on('popupopen', function (e) {
map.setView(e.target._popup._latlng, e.target._zoom);
});
I can't seem to find andthing in the Google Maps V3 docs about creating custom content boxes for Google maps.
Below is an example of a custom content box:
http://www.apple.com/retail/alamoana/map/
My question is... how do you go about changing the default white balloon popup?
Cheers!
Look at the InfoBox toolkit in the v3 utility libraries. I'm using them on dev.mapfaire.com, if you want to take a look.
Personally, i don't use any of google's custom overlay scripts and such. I made a custom php page which hold the iframe, where I can load custom css files, and also I create custom DIVs that go over top of the map, which are then repositioned while dragging when opened.
You can use the "Drag,Dragstart,Dragend" events to help you reposition elements that you have created.
If you have custom markers set onto you page you can get their pixel position with this function:
getPosition: function (marker) {
var map = this.map /* Your map, in my case is part of my controller object linked to this.map */;
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(
map.getBounds().getNorthEast().lat(),
map.getBounds().getSouthWest().lng()
);
var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
var worldCoordinate = map.getProjection().fromLatLngToPoint(marker.getPosition());
var pixelOffset = new google.maps.Point(
Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale),
Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale)
);
return pixelOffset; /* Returns the top left pixel of the specified marker on the specified map */
}
And then I use a setPosition function which is used when the window is dragging, it sets your custom element's position to the marker's position.
The dragging event can be set in such manner:
google.maps.addEventListener(map,'drag',function () { setPosition(marker,element); });
The setPosition Function simply gathers the width,height of the element, and the pixel offset associated to the marker using the getPosition(marker) function:
setPosition: function (marker,element) {
var p = this.getPosition(marker),
s = {width:element.offsetWidth,height:element.offsetHeight},
markerOffset = {width:58,height:58};
element.style.left = p.x - (s.width/2) + (markerOffset.width/2) + "px"; /* We set the element's left position to the marker's position + half of our element's width + half of the marker's width's so it is centered */
element.style.top = p.y - s.height - (markerOffset.height) + 10 + "px"; /* We set the element's top position to the marker's position + our element's height + markers height so it is 10px above our marker vertically */
},
Sometimes you just have to think a bit outside the box
That example is using the second version of google maps. That features might not be available in the 3rd one.
But you can add any html code in the infowindows and personalize them. I'm not sure if you can change how they look directly, but you can definitely change how the content looks like.
Edit: I found some sample code: http://gmaps-samples-v3.googlecode.com/svn/trunk/infowindow_custom/infowindow-custom.html
Take a look at gmaps-utility-library-dev and more specific in the ExtInfoWindow utility and PopupMarker utility
As Sudhir pointed out, the Infobox Plugin is one way to do what you want. I've recently answered a similar question about using the infobox plugin for google maps api 3 and provided a complete example.