I have a leaflet map. The map container is very big compared to the tile in first zoom level (256px X 256px). I want to get the bounds of tile and fit the map bounds to it. I am able to fitbound() with multiple markers. I adopted the code from here . I tried to achieve fit to tile also with same idea. unfortunately it doesn't seem working. Here is my code
var fitToBounds = function () {
//getting bound of tile 256X256 pixel
var maxLat = map.unproject(new L.Point(0,0)).lat;
var maxLat = map.unproject(new L.Point(0,256)).lat;
var maxLng = map.unproject(new L.Point(256,0)).lng;
var minLng = map.unproject(new L.Point(256,256)).lng;
var southWest = new L.LatLng(minLat, minLng);
var northEast = new L.LatLng(maxLat, maxLng);
map.fitBounds(new L.LatLngBounds(southWest, northEast));
};
Group your markers in a L.mapbox.featureGroup() and then on your map use
map.fitBounds(featureGroup.getBounds());
Feature groups are like a layerGroup but also has a .getBounds() method that can be used to set your fit. See the docs.
maybe not the best solution, but at least working:
Add an other layer and make it invisible. Zoom to this one. Advantage is, you might also add onEachFeature.
var pointsWithBounds=[]
_.each(layerWithoutBounds, function (element) {
pointsWithBounds.push(
{
"type": "Feature",
"geometry": {
"type": "CircleMarker",
"coordinates": [element.lon, element.lat]
});
});
var layerWithBounds = L.geoJSON(pointsWithBounds, {
style: {
fillColor: 'transparent',
color: 'transparent'
}
});
layerWithBounds.addTo(map)
map.setView(layerWithBounds.getBounds().getCenter());
map.fitBounds(layerWithBounds.getBounds());
Related
I want to add a marker in the middle of a polygon that is made form geojson data. The polygon is connected a control where the layer can be turned on and off. This marker should only be displayed when the layer is active. I have the following code:
var geoJsonLayer = L.geoJSON(Locations, {
onEachFeature: function (feature, layer) {
if (feature.geometry.type === "Polygon") {
var bounds = layer.getBounds();
var center = bounds.getCenter();
var markerTitle = feature.properties.ItemId;
layer.id = markerTitle;
var popUpFormat = dataPopUp(feature);
layer.bindPopup(popUpFormat, customPopUpOptions);
}
},
});
Thanks for your interest and I hope someone can help me :D
You want to group a L.Polygon and a L.Marker together, and treat them as the same entity. This is a textbook scenario for using L.LayerGroups, e.g.
var geoJsonLayer = L.geoJSON(Locations, {
onEachFeature: function (feature, layer) {
if (feature.geometry.type === "Polygon") {
var center = layer.getBounds().getCenter();
var marker = L.marker(center);
var polygonAndItsCenter = L.layerGroup([layer, marker]);
}
},
});
Now polygonAndItsCenter is a L.LayerGroup with the polygon and its center (so adding/removing to/from the map will apply to both), but geoJsonLayer will contain only the polygons. How you handle that is up to you, but I guess you might want to not add geoJson to the map (using only for parsing and instantiating the polygons), and keep track of your polygon+marker LayerGroups separately, e.g.
var polygonsWithCenters = L.layerGroup();
var geoJsonLayer = L.geoJSON(Locations, {
onEachFeature: function (feature, layer) {
if (feature.geometry.type === "Polygon") {
var center = layer.getBounds().getCenter();
var marker = L.marker(center);
var polygonAndItsCenter = L.layerGroup([layer, marker]);
polygonAndItsCenter.addTo(polygonsWithCenters);
}
},
});
// geoJsonLayer.addTo(map); // No!!
polygonsWithCenters.addTo(map);
// Do something with a polygon+marker, e.g. remove the first one from the map
polygonsWithCenters.getLayers()[0].remove();
There are a few secondary problems that can spawn for this, so think about what you want to do with each polygon/layergroup/marker before writing code, keep the Leaflet documentation at hand, and remember:
You can not attach events or bind popups to LayerGroups, but you can do that to L.FeatureGroups
The center of a polygon's bounding box is different from its centroid which is different from the point inside the polygon which is furthest away from any of its edges. Only the third option is guaranteed to be inside the polygon.
This question is related to this question: Updating Leaflet Marker Position Every x Seconds with JSON. Only I want to project the map created with Leaflet onto a 3D globe with WebGLEarth. Normally you can combine Leaflet and WebGLEarth by replacing L.map, L.marker and so on with WE.map, WE.marker in your original Leaflet code.
I would like to have the current location of the International Space Station projected on my 3D globe, so I replaced the L.marker in the update_position function of this code...
var cloudmadeUrl = 'http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg';
var subDomains = ['otile1','otile2','otile3','otile4'];
var cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 11, subdomains: subDomains});
var map = new L.Map('map', {layers : [cloudmade]}).fitWorld();
var iss;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss = L.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss.setLatLng([latitude,longitude]).update();
setTimeout(update_position, 1000);
});
}
update_position();
DEMO
...by WE.marker. Unfortunately, the updating of the position doesn't work anymore on my 3D globe, whereas it did work on the 2D map.
I tried adding
setInterval(update_position,2000);
just above update_position();, then the marker gets updated a few times (~5) and then suddenly stops. If I interact with my mouse on the globe by panning, the marker gets updated to its current position and gets updated a few times afterwards, and eventually stops again.
Main Question:
Does anyone know how to fix this so that I have a continuous marker update?
Additional Question:
Ideally I would like the International Space Station track to look like https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=CZML.html&label=Showcases, with a colored path the station travelled in its last orbit around the earth. Any tips on how to do this?
Thanks in advance!
[Edit 23-1-2015:
I managed to update it continuously by making a new variable for iis, namely iis2. Why this works is not clear to my. Unfortunately, all 'old' markerpositions don't dissapear, so I get a blurred marker track.
var iss;
var iis2;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss2 = WE.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss2.setLatLng([latitude,longitude]).update();
});
}
update_position();
setInterval(update_position,1000);
]
remove the marker
var iss;
var iis2;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
map.removeLayer(iss2);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss2 = WE.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss2.setLatLng([latitude,longitude]).update();
});
}
update_position();
setInterval(update_position,1000);
I use the following block of JavaScript to try to show a WMS layer. I'm using OpenLayers 2.8.
The map's base layer (Openstreetmap) shows correctly, it zooms to the correct area, the "pyramid" layer is shown in the layer switcher, but no request to its WMS service is ever made (so the fact that the URL, styles and params are dummies shouldn't matter -- it never even attempts to get them).
OpenLayers does try to get a WMS layer once I pan or zoom far enough so that the Gulf of Guinea is in view (but all my data is in the Netherlands). This suggests a projection problem (WGS84's (0, 0) point is there), but I don't understand why OpenLayers doesn't even try to fetch a map layer elsewhere. My data is in EPSG:3857 (Web Mercator) projection.
/*global $, OpenLayers */
(function () {
"use strict";
$(function () {
$(".map").each(function () {
var div = $(this);
var data_bounds = div.attr("data-bounds");
console.log("data_bounds: " + data_bounds);
if (data_bounds !== "") {
var map = new OpenLayers.Map(div.attr("id"), {
projection: "EPSG:3857"});
var extent = JSON.parse(data_bounds);
var bounds = new OpenLayers.Bounds(
extent.minx, extent.miny,
extent.maxx, extent.maxy);
map.addLayer(
new OpenLayers.Layer.OSM(
"OpenStreetMap NL",
"http://tile.openstreetmap.nl/tiles/${z}/${x}/${y}.png",
{buffer: 0}));
map.addLayer(
new OpenLayers.Layer.WMS(
"pyramid", "http://rasterserver.local:5000/wms", {
layers: "test",
styles: "test"
}, {
singleTile: true,
isBaseLayer: false,
displayInLayerSwitcher: true,
units: 'm'
}));
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.zoomToExtent(bounds);
}
});
});
})();
Edit: the 'data_bounds' console print prints (with some added formatting):
data_bounds: {
"minx": 582918.5701295201,
"miny": 6923595.841021758,
"maxx": 821926.9006116659,
"maxy": 7079960.166533174
}
It zooms to the correct region in the north of the Netherlands, so I don't think the problem is there.
Since posting, I found out that if I don't use the OSM layer, and instead use the WMS layer as baselayer, it works. So perhaps there's some incompatibility with a OSM baselayer and a WMS layer added to it? But then I don't get that it does seem to do something near WGS84 (0, 0).
I eventually managed to fix this by giving the map an explicit maxExtent:
var extent = JSON.parse(data_bounds);
var bounds = new OpenLayers.Bounds(
extent.minx, extent.miny,
extent.maxx, extent.maxy);
var map = new OpenLayers.Map(div.attr("id"), {
projection: "EPSG:3857",
maxExtent: bounds
});
Oddly enough this doesn't limit the user's ability to pan and zoom around the world, but it does make the overlay work...
I'm using Google Maps API and jquery-ui-maps (this questions has nothing to do with the plugin which is working great).
I've created a FusionTablesLayer with all countries except Mozambique. The user could place a marker and reposition it. I'm trying to find a way to block the drag (or alert the user, it doesn't matter now) if he tries to place the marker outside Mozambique (over the FusionTablesLayer).
After some research I discover this method: containsLocation(point:LatLng, polygon:Polygon), which computes whether the given point lies inside the specified polygon.
It should receive a Polygon and I've got a FusionTablesLayer. Any clue how to solve this?
Here's my code:FIDDLE
Try to place a marker and drag it...
//Initialize the map
var mapa = $('#map_canvas').gmap({'center': '-18.646245,35.815918'});
$('#map_canvas').gmap('option', 'zoom', 7);
//create the layer (all countries except Mozambique)
var world_geometry;
$('#map_canvas').gmap().bind('init', function(event, map) {
world_geometry = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1N2LBk4JHwWpOY4d9fobIn27lfnZ5MDy-NoqqRpk',
where: "ISO_2DIGIT NOT EQUAL TO 'MZ'"
},
styles: [{
polygonOptions: {
fillColor: "#333333",
fillOpacity: 0.3
}
}],
map: map,
suppressInfoWindows: true
});
});
$('#map_canvas').gmap().bind('init', function(event, map) {
$(map).click(function(event) {
$('#map_canvas').gmap('clear', 'markers');
$('#map_canvas').gmap('addMarker', {
'position': event.latLng,
'draggable': true,
'bounds': false
}, function(map, marker) {
}).dragend(function(event) {
//I need to check if the marker is over the FusionTablesLayer and block the drag.
//var test = google.maps.geometry.poly.containsLocation(event.latLng, world_geometry);
}).click(function() {
})
});
});
Since there is no containsLocation in FusionTablesLayer, and since no mouseevents but click is supported (that would have made it a lot easier) - there is no other way round than to check if there is being dragged outside the area itself, Mozambique - not into the FusionTablesLayer. The solution is to create an invisible polygon for Mozambique, and use that polygon to check for containsLocation when dragging is finished.
The polygon can be based on the KML from the row you are excluding, MZ. That can be done using google.visualization.Query.
1) include the Google API loader in your project :
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
2) initialize Visualization :
google.load('visualization', '1.0');
3) define a variable for the polygon holding the Mozambique borders :
var mozambique;
The following is a function that loads the geometry data for Mozambique, and then creates an invisible polygon on the map; google.visualization.Query is used instead of the automated FusionTablesLayer so we can extract the <coordinates> from the KML and use them as base for the polygon.
In basic, this is how to convert KML-data from a FusionTable to a polygon :
function initMozambique(map) {
//init the query string, select mozambique borders
var sql = encodeURIComponent("SELECT 'geometry' FROM 1N2LBk4JHwWpOY4d9fobIn27lfnZ5MDy-NoqqRpk WHERE ISO_2DIGIT ='MZ'");
var query = new google.visualization.Query('http://www.google.com/fusiontables/gvizdata?tq=' + sql);
query.send(function (response) {
var data = response.getDataTable().getValue(0, 0);
//create a XML parser
if (window.DOMParser) {
var parser = new DOMParser();
var kml = parser.parseFromString(data, "text/xml");
} else { // Internet Explorer
var kml = new ActiveXObject("Microsoft.XMLDOM");
kml.loadXML(data);
}
//get the coordinates of Mozambique
var latLngs = kml.getElementsByTagName("coordinates")[0].childNodes[0].nodeValue.split(' ');
//create an array of LatLngs
var mzLatLngs = [];
for (var i = 0; i < latLngs.length; i++) {
var latLng = latLngs[i].split(',');
//<coordinates> for this FusionTable comes in lng,lat format
mzLatLngs.push(new google.maps.LatLng(latLng[1], latLng[0]));
}
//initialize the mozambique polygon
mozambique = new google.maps.Polygon({
paths: mzLatLngs,
fillColor: 'transparent',
strokeColor : 'transparent',
map: map
});
//make the mozambique polygon "transparent" for clicks (pass clicks to map)
google.maps.event.addListener(mozambique, 'click', function(event) {
google.maps.event.trigger(map, 'click', event);
});
});
}
Call the above initMozambique function in your second gmap().bind('init'... :
$('#map_canvas').gmap().bind('init', function(event, map) {
initMozambique(map);
...
Now you can check the mozambique-polygon for containsLocation after dragging
...
}).dragend(function(event) {
if (!google.maps.geometry.poly.containsLocation(event.latLng, mozambique)) {
alert('You are not allowed to drag the marker outside Mozambique');
}
//I need to check if the marker is over the FusionTablesLayer and block the drag.
//var test = google.maps.geometry.poly.containsLocation(event.latLng, world_geometry);
}).click(function() {
})
...
See forked fiddle, working demo with the code above -> http://jsfiddle.net/yb5t6cw6/
Tested in Chrome, FF and IE, ubuntu and windows.
First I'm pretty new to Javascript, so sorry if my question comes across poorly.
I'm creating an application in Flash to help users calculate their electrical costs. Then I'm taking this figure and write it to an xml file.
Now I'm looking to open a webpage and show a google map, and there is a rectangle drawn over the map which is generated dynamically from the number generated earlier and stored in the xml file.
I'm completely lost as to places to turn on how to achieve this. I've gotten my map on to my page, and it scales 100% as I want it to, but I can't figure out the dynamic rectangle part at all. Any ideas or pointers in the right direction greatly appreciated.
In this latest version, the XML file
<countries>
<country name="USA" lat="40.0" lng="-100.0" width="30.0"/>
<country name="France" lat="46.6" lng="2.7" width="10"/>
<country name="Germany" lat="51.1" lng="10.1" width="20"/>
</countries>
is loaded as soon as the map tiles finish loading. I could not get the getProjection to be called correctly if I did not wait for tile loading to finish. The docs state that getting the projection needs the map to be initialized, and recommends listening for projection_changed. Both ways work yet I still feel listening to tiles_loaded is safer and if something goes wrong with the xml loading it will get called again if the map is zoomed or panned a noticeable amount.
var map;
var xmlLoaded = false;
function initialize() {
var mapOptions = { center: new google.maps.LatLng(30.0, 0.0), zoom: 2,
mapTypeId: google.maps.MapTypeId.ROADMAP };
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
google.maps.event.addListener(map, 'tilesloaded', loadData);
}
function loadData() {
if(!xmlLoaded) {
$.ajax({
type: "GET",
url: "co2data.xml",
dataType: "xml",
success: function(xml) {
var countries = xml.documentElement.getElementsByTagName("country");
for(var i = 0, country; country = countries[i]; i++) {
var name = country.getAttribute("name");
var lat = parseFloat(country.getAttribute("lat"));
var lng = parseFloat(country.getAttribute("lng"));
var point = map.getProjection().fromLatLngToPoint(new google.maps.LatLng(lat,lng));
// width is really an arbitrary unit, relative to CO2 tonnage.
// equals the side of the drawn square.
// it is measured in google maps points units.
var width = parseFloat(country.getAttribute("width"));
makeCO2Rect(name, point, width);
}
xmlLoaded = true;
}
});
}
}
The rectangle is defined by width in points (the whole world is 256x256 points), so some conversion is needed when assigning their centers to the more conventional LatLng.
function rectParamsToBounds(point, width) {
var ctrX = point.x;
var ctrY = point.y;
var swX = ctrX - (width/2);
var swY = ctrY - (width/2);
var neX = ctrX + (width/2);
var neY = ctrY + (width/2);
return new google.maps.LatLngBounds(
map.getProjection().fromPointToLatLng(new google.maps.Point(swX, swY)),
map.getProjection().fromPointToLatLng(new google.maps.Point(neX, neY)));
}
Finally, a rectangle is created with a country name that goes into a MarkerWithLabel (using v1.1.5 here, you can hotlink to http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.1.5/src/markerwithlabel_packed.js though I prefer saving a local copy)
Since dragging a rectangle appears impossible, a marker in its center works as a handle. When it's dragged, the associated rectangle moves with it.
function makeCO2Rect(name, point, width) {
var rect = new google.maps.Rectangle({
map: map,
bounds: rectParamsToBounds(point, width)
});
var marker = new MarkerWithLabel({
map: map,
position: map.getProjection().fromPointToLatLng(new google.maps.Point(point.x, point.y)),
draggable: true,
raiseOnDrag: false,
labelContent: name,
labelAnchor: new google.maps.Point(30, 0),
labelClass: "labels", // the CSS class for the label
labelStyle: {opacity: 1.0}
});
google.maps.event.addListener(marker, 'drag', function(event) {
var newLatLng = event.latLng;
var newPoint = map.getProjection().fromLatLngToPoint(newLatLng);
rect.setBounds(rectParamsToBounds(newPoint, width));
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Styling the labels need to be done both in the .labels CSS class and the constructor, and rectangles have options like stroke color, thickness, opacity, and fill color.
If you just want to place a rectangular shape on the map, you can create a google.maps.Rectangleapi-doc. If you want to create a rectangular label on the map, you may be more interested in the InfoBox Utility Librarywiki-page.