Leaflet custom coordinates on image - javascript

I have an image which size is 8576x8576px, and I want to make the coordinates match 1:1. Also I want the coordinates 0,0 in the center of the image (now the center is -128,128). And I want to show the coordinates too. I want to put a locate button for the user insert coordinates and then find them on the map.
Something like this: http://xero-hurtworld.com/map_steam.php
(I am using the same image but bigger). The tile size I made its 268px.
My code so far:
https://jsfiddle.net/ze62dte0/
<!DOCTYPE html>
<html>
<head>
<title>Map</title>
<meta charset="utf-8"/>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.ie.css" />
<![endif]-->
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js" charset="utf-8"></script>
<script>
function init() {
var mapMinZoom = 0;
var mapMaxZoom = 3;
var map = L.map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: L.CRS.Simple
}).setView([0, 0], mapMaxZoom);
window.latLngToPixels = function(latlng){
return window.map.project([latlng.lat,latlng.lng], window.map.getMaxZoom());
};
window.pixelsToLatLng = function(x,y){
return window.map.unproject([x,y], window.map.getMaxZoom());
};
var mapBounds = new L.LatLngBounds(
map.unproject([0, 8576], mapMaxZoom),
map.unproject([8576, 0], mapMaxZoom));
map.fitBounds(mapBounds);
L.tileLayer('{z}/{x}/{y}.jpg', {
minZoom: mapMinZoom, maxZoom: mapMaxZoom,
bounds: mapBounds,
noWrap: true,
tms: false
}).addTo(map);
L.marker([0, 0]).addTo(map).bindPopup("Zero");
L.marker([-128, 128]).addTo(map).bindPopup("center");
var popup = L.popup();
<!-- Click pop-up>
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked in " + e.latlng.toString ())
.openOn(map);
}
map.on('click', onMapClick);
}
</script>
<style>
html, body, #map { width:100%; height:100%; margin:0; padding:0; }
</style>
</head>
<body onload="init()">
<div id="map"></div>
</body>
</html>

If I understand correctly, you want a CRS similar to L.CRS.Simple that places tile 0/0/0 (tile size 268px, which is 8576 / 2⁵) so that:
Position [0, 0] is at the center of that tile.
The entire world (i.e. entire tile 0/0/0) goes from position [-8576/2, -8576/2] to [8576/2, 8576/2].
You would just need to adjust the L.CRS.Simple with the appropriate transformation, to account for this scale of 1/2⁵ = 1/32 (instead of just 1) and offset of 8576 * 1/32 / 2 = 268 / 2 = 134 (instead of 0.5).
L.CRS.MySimple = L.extend({}, L.CRS.Simple, {
transformation: new L.Transformation(1 / 32, 134, -1 / 32, 134)
});
var map = L.map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
crs: L.CRS.MySimple
}).setView([0, 0], mapMaxZoom);
Demo: http://plnkr.co/edit/5SQqp7SP4nf8muPM5iso?p=preview (I used Plunker instead of jsfiddle because you provided a full page code with HTML, whereas jsfiddle expects you to split your HTML, CSS and JavaScript codes into separate blocks).
As for showing the coordinates and a "locate" button, it would be quite easy to implement so that it is similar to the example you mention. Feel free to open new questions if you need help.
In the above demo, I used Leaflet.Coordinates plugin to implement quickly both functionalities (see the control on bottom left corner of the map; you have to start moving your mouse on the map for the coordinates to appear; click on that control to open the edition mode).
EDIT:
As for the Leaflet.Coordinates plugin, it wraps displayed coordinates longitude to stay within [-180; 180] degrees.
In your case where coordinates are not degrees, there is no point wrapping the longitude.
I think this is the cause for the discrepancy of coordinates between the click popup and the control.
Simply patch the plugin code to prevent wrapping:
// Patch first to avoid longitude wrapping.
L.Control.Coordinates.include({
_update: function(evt) {
var pos = evt.latlng,
opts = this.options;
if (pos) {
//pos = pos.wrap(); // Remove that instruction.
this._currentPos = pos;
this._inputY.value = L.NumberFormatter.round(pos.lat, opts.decimals, opts.decimalSeperator);
this._inputX.value = L.NumberFormatter.round(pos.lng, opts.decimals, opts.decimalSeperator);
this._label.innerHTML = this._createCoordinateLabel(pos);
}
}
});
Updated demo: http://plnkr.co/edit/M3Ru0xqn6AxAaSb4kIJU?p=preview

Related

How do I add a thumbnail for users to select a satellite view?

I am very new to mapbox and leaflet. I am trying to extend the basic mapbox example here to let a user click on a small thumbnail satellite image that will take them to the satellite view. I have been through the examples of both mapbox and leaflet but see no way to do it. Is it possible? Something how google maps does with the satellite view in the lower left hand corner:
https://www.google.com/maps/place/New+York,+NY/#40.6971494,-74.2598655,10z/data=!3m1!4b1!4m5!3m4!1s0x89c24fa5d33f083b:0xc80b8f06e177fe62!8m2!3d40.7127753!4d-74.0059728?hl=en-US
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>A simple map</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
L.mapbox.accessToken = 'pk.eyJ1IjoiYndhZGFtc29uIiwiYSI6ImNqajZhNm1idDFzMjIza3A2Y3ZmdDV6YWYifQ.9NhptR7a9D0hzWXR51y_9w';
var map = L.mapbox.map('map', 'mapbox.streets')
.setView([40, -74.50], 9);
</script>
</body>
</html>
EDIT: Though this example is mapbox js I really don't care if it is mapbox gl or js. Can be either. ok thanks.
You can use a mapbox static api to get a preview of the satellite image:
<img src="https://api.mapbox.com/styles/v1/mapbox/satellite-v9/static/-74.50000,40.00000,9.0,0,0/300x300?access_token=pk.eyJ1IjoiYndhZGFtc29uIiwiYSI6ImNqajZhNm1idDFzMjIza3A2Y3ZmdDV6YWYifQ.9NhptR7a9D0hzWXR51y_9w"/>
[ https://www.mapbox.com/help/static-api-playground/ ]
Update:
You can use the mapbox/geo-viewport library to calculate centerpoint and zoom for preview, and render event to update preview:
map.on('render', function() {
setMapPreview()
})
function setMapPreview() {
var bounds = map.getBounds().toArray()
bounds = [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]]
// The size of the desired map.
var size = [100, 100];
// Calculate a zoom level and centerpoint for this map.
var vp = geoViewport.viewport(bounds, size, 0, 24, 512);
// Construct a static map url
// https://www.mapbox.com/developers/api/static/
document.getElementById('preview').src =
'https://api.mapbox.com/styles/v1/mapbox/satellite-v9/static/' +
vp.center.join(',') + ',' + vp.zoom + ',0,0/' +
size.join('x') + '?' +
'attribution=false&logo=false&access_token=' + mapboxgl.accessToken;
}
[ https://jsfiddle.net/btv9ogpc/ ]
It's not a problem to add an event click to the preview, and rotate styles:
document.getElementById('preview').addEventListener('click', function () {
map.setStyle('mapbox://styles/mapbox/satellite-v9')
})
[ https://jsfiddle.net/xh74rb83 ]
Sounds like you would be interested by some Leaflet plugins for Layer Switching Controls (mapbox.js is built onto Leaflet, so they should be compatible):
Leaflet.Basemaps: (ISC License)
A tile driven basemaps control for Leaflet.
It allows you to create a user interface control for choosing the basemap used on the map, based on a tile from the underlying tile service.
See the example.
With this plugin, you simply specify some constant tile coordinates to be used as "preview":
map.addControl(L.control.basemaps({
basemaps: basemaps, // Array of Tile Layers.
tileX: 0, // tile X coordinate
tileY: 0, // tile Y coordinate
tileZ: 1 // tile zoom level
}));
Leaflet-IconLayers: (MIT License)
Leaflet control that displays base layers as small icons (demo).
For this plugin, even though the documentation uses different images sized 80x80 pixels as preview icon, you can very well re-use tiles with specific coordinates, and the plugin will resize them to fit its icons:
var map = L.map('map').setView([48.86, 2.35], 5);
var OpenStreetMap_Mapnik = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
var OpenTopoMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
maxZoom: 17,
attribution: 'Map data: © OpenStreetMap, SRTM | Map style: © OpenTopoMap (CC-BY-SA)'
});
var layers = [{
layer: OpenStreetMap_Mapnik,
title: 'OSM Mapnik',
icon: 'https://a.tile.openstreetmap.org/1/0/0.png'
}, {
layer: OpenTopoMap,
title: 'OSM Topo',
icon: 'https://a.tile.opentopomap.org/1/0/0.png' // Re-use a tile
}];
L.control.iconLayers(layers).addTo(map);
html,
body,
#map {
height: 100%;
margin: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet-src.js" integrity="sha512-IkGU/uDhB9u9F8k+2OsA6XXoowIhOuQL1NTgNZHY1nkURnqEGlDZq3GsfmdJdKFe1k1zOc6YU2K7qY+hF9AodA==" crossorigin=""></script>
<link rel="stylesheet" href="https://cdn.rawgit.com/ScanEx/Leaflet-IconLayers/ea9af769/dist/iconLayers.css" />
<script src="https://cdn.rawgit.com/ScanEx/Leaflet-IconLayers/ea9af769/dist/iconLayers.js"></script>
<div id="map"></div>
If you wish you can also use images from mapbox static API as shown in stdob--'s answer.

Return canvas (image) in openlayers 4

the example shows how to bind a button click event to the canvas and then it returns the image Example. How can I change it, that when I use a call openlayers with a permalink, that it automatically returns me that image? I would like to use a simple get request from an c++ programm to get the image. I have the
e.g. "#map=12/1085115.28/6035092.46/0" as parsing parameters. Any ideas?
Thanks and Greetings
Melina
So far I have the parameter parsing
<!DOCTYPE html>
<html>
<head>
<title>OpenStreetMap</title>
<link rel="stylesheet" href="https://openlayers.org/en/v4.2.0/css/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://openlayers.org/en/v4.2.0/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script>
var center = [0,0];
var zoom = 0;
var rotation = 0;
if (window.location.has !== '')
{
var hash = window.location.hash.replace('#map=', '');
var parts = hash.split('/');
console.log (parts);
if (parts.length === 4)
{
zoom = parseInt(parts[0],10);
center = [
parseFloat(parts[1]),
parseFloat(parts[2])
];
rotation = parseFloat(parts[3]);
var rotation = 0;
}
}
var openStreetMapLayer = new ol.layer.Tile({
source: new ol.source.OSM({
attributions: [
'All maps © openStreetMapLayer',
ol.source.OSM.ATTRIBUTION
],
opaque: false,
// url: '<myosmserver>/hot/{z}/{x}/{y}.png'
})
});
var map = new ol.Map({
layers: [
openStreetMapLayer
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** #type {olx.control.AttributionOptions} */ ({
collapsible: false
})
}),
view: new ol.View({
maxZoom: 20,
center: center,
zoom: zoom
})
});
</script>
</body>
</html>
You cannot add a link that will somehow download the map as an image. You will need to render it somewhere.
This is how it works. When Openlayers renders the map, it renders it in a HTML canvas element. The download feature is not a Openlayers feature but a HTML canvas feature. The canvas has API to take a snapshot of the current canvas. You can download it as an image.
You can either render the map in a browser or render it server-side. I have not tried rendering the Openlayers map on the server but it should be possible.

Polygon from LeafletDraw to GeoJSON

This is my first attempt working with javascript and GeoJSON by using leaflet.
So far, I got the desired map and the leaflet.draw plugin working in the way that I can draw a shape and it appears on my screen.
I tried to write this shape to a GeoJSON that I want to use in R.
Therfore I used the ideas presented here to create the GeoJSON string. I think the desired information for me is stored in the variable shape_for_db.
However, using Firebug in Firefox I am not able to find this variable.
Do I get something wrong here?
This is the script I am using:
<html>
<head>
<title>A Leaflet map!</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.css" />
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css" />
<script src="http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.js"></script>
<script src="jquery-2.1.1.min.js"></script>
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script>
<style>
#map{ width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// base map
var map = L.map('map').setView([51.25,10.57], 8);
// load a tile layer
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png',
{
attribution: 'Tiles by: OpenStreetMaps',
maxZoom: 17,
minZoom: 5
}).addTo(map);
// Initialise the FeatureGroup to store editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Initialise the draw control and pass it the FeatureGroup of editable layers
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
drawnItems.addLayer(layer);
});
// Shape to GeoJSON
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var shape = layer.toGeoJSON()
var shape_for_db = JSON.stringify(shape);
});
</script>
</body>
</html>
The scope for your shape_for_db is inside your second listener for draw-created. You can place on window.shape_for_db if you are doing this for a one-off experimental/playing around approach and want to use your dev console/Firebug. Or set up var shape_for_db outside the listener.

Google Maps Api v3 DrawingManager - Start drawing without using drawingControl

I'm building a geofencing api with google maps api v3. I started from MapToolbar code :
http://nettique.free.fr/gmap/toolbar.html
I like it because it's really easy to customize like changing the icons and all the way the functions are called. I only use the polygon tool and I don't like the fact that you can't make convex angle when you add a point (only when you edit it after).
You can do it if you use drawingManager tools because the polygon is created only after you have completed the drawing :
https://developers.google.com/maps/documentation/javascript/examples/drawing-tools
But I don't like the default icons and I think the way the functions are called is not really user friendly. The thing is that I'm not able to call the functions by myself.
So, I'm looking for a way to start drawing without using the default drawing control as simple as calling the right function but I can't find it! Or maybe I can create polygons the same way without using drawingManager at all. Please help me!
I found it! If you want to hide the control menu, use drawingControl: false and if you want to change the drawing mode, use setDrawingMode() as explained here:
https://developers.google.com/maps/documentation/javascript/reference#DrawingManager
So, this is the new code starting in polygon drawing mode:
<!DOCTYPE html>
<html>
<head>
<title>Drawing tools</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=drawing"></script>
<script>
// ColorLuminance() is a little extra to have your strokeColor
// darker than your fillColor
function ColorLuminance(hex, lum) {
// validate hex string
hex = String(hex).replace(/[^0-9a-f]/gi, '');
if (hex.length < 6) {
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
}
lum = lum || 0;
// convert to decimal and change luminosity
var rgb = "#", c, i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substr(i*2,2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb += ("00"+c).substr(c.length);
}
return rgb;
}
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.POLYGON,
drawingControl: false,
polygonOptions: {
strokeWeight: 2,
strokeColor: ColorLuminance("#FF0000", -0.6),
strokeOpacity: 0.9,
fillColor: "#FF0000",
fillOpacity: 0.3
}
});
drawingManager.setMap(map);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>

How can I get the smallest LatLngBounds that still contains a set of Lat/Long Coordinates in Google Maps JS API?

I need to plot a set of coordinates on the map in response to a user selection, and when it happens, I'd like to pan the map to focus on that set of points. How can I find the smallest bounding box (LatLngBounds) that contains all of the coordinates?
In addition to the Stack Overflow post which #Crescent Fresh pointed to above (which is using the v2 API), the method you'd want to use is the LatLngBounds.extend().
Here's a complete example, using the v3 API:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps LatLngBounds.extend() Demo</title>
<script src="http://maps.google.com/maps/api/js?sensor=false"
type="text/javascript"></script>
</head>
<body>
<div id="map" style="width: 400px; height: 300px;"></div>
<script type="text/javascript">
var map = new google.maps.Map(document.getElementById('map'), {
mapTypeId: google.maps.MapTypeId.TERRAIN
});
var markerBounds = new google.maps.LatLngBounds();
var randomPoint, i;
for (i = 0; i < 10; i++) {
// Generate 10 random points within North East America
randomPoint = new google.maps.LatLng( 39.00 + (Math.random() - 0.5) * 20,
-77.00 + (Math.random() - 0.5) * 20);
// Draw a marker for each random point
new google.maps.Marker({
position: randomPoint,
map: map
});
// Extend markerBounds with each random point.
markerBounds.extend(randomPoint);
}
// At the end markerBounds will be the smallest bounding box to contain
// our 10 random points
// Finally we can call the Map.fitBounds() method to set the map to fit
// our markerBounds
map.fitBounds(markerBounds);
</script>
</body>
</html>
Screenshot:

Categories