Return canvas (image) in openlayers 4 - javascript

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.

Related

change openlayers map color (dark and light style)

I like to use openlayers map with dark and light styles. so how can I change map color or map style?
My friend(Dear morteza) found a simple way that I answered in this post.
my html file is:
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.1.1/css/ol.css" type="text/css">
<style>
.map {
height: 400px;
width: 50%;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.1.1/build/ol.js"></script>
<title>OpenLayers example</title>
</head>
<body>
<h2>My Map</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
})
});
// function applies greyscale to every pixel in canvas
</script>
</body>
</html>
openlayes shows maps in <canvas>. and <canvas> will be add to <div> container with openlayers library. So add bellow codes to add map and change it's color:
var map = new ol.Map({
target: 'map',//div with map id
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([61.2135, 28.2331]),
zoom: 13
})
});
//change map color
map.on('postcompose',function(e){
document.querySelector('canvas').style.filter="invert(90%)";
});
you can also test other filters
The ol-ext library lets you set filters on openlayers layers. It uses canvas composite operations to achieve the effects.
See code sample online: https://viglino.github.io/ol-ext/examples/filter/map.filter.colorize.html
const tile = new TileLayer({
source: new OSM()
});
tile.on('prerender', (evt) => {
// return
if (evt.context) {
const context = evt.context as CanvasRenderingContext2D;
context.filter = 'grayscale(80%) invert(100%) ';
context.globalCompositeOperation = 'source-over';
}
});
tile.on('postrender', (evt) => {
if (evt.context) {
const context = evt.context as CanvasRenderingContext2D;
context.filter = 'none';
}
});
Before the tile layer rendered set the canvas filter and reset back to none after the rendering, by doing this, the following layers will not be affected in any way, Here is the effect:

how to rationalise code in order to add style to openlayers code mish-mash

(I considered posting this to codereview.stackexchange.com, but openlayers.org's "Ask a question" link leads here, so I'm posting here.)
My code successfully draws a map, puts some SVG markers on it and overlays a GPX track. Now I would like to change the GPX track colour to make it more distinguishable from the map but all attempts (based on googled examples) are failing; there's no error message - though I wouldn't know where it would appear -, it's just that all attempts to style the track are resulting in the track not being displayed.
Here's the code:
<html lang="en">
<head>
<meta charset="utf-8">
<title>picrenamer3 - SAM_5772.JPG</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
<script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script>
<style>
svg#bullet {
max-height: 22px;
max-width: 22px;
}
</style>
<script>
var map;
function updateClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
/* clipboard successfully set */
}, function() {
/* clipboard write failed */
alert('failed to write to clipboard (hint: are you serving this file over https?)');
});
}
function initialize_map(lat, lon, zoom, trackfile) {
map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM({
url: "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
})
})
],
view: new ol.View({
center: ol.proj.fromLonLat([lon, lat]),
zoom: zoom
})
});
map.on('singleclick', function(evt) {
var coordinates = evt.coordinate;
var p = map.getView().getProjection();
var cord = ol.proj.toLonLat(coordinates, p);
updateClipboard(cord[1].toFixed(6) + ',' + cord[0].toFixed(6));
});
if (trackfile != '') {
gpxTrackVector = new ol.source.Vector({
url: trackfile,
format: new ol.format.GPX()
});
var gpxTrackLayer = new ol.layer.Vector({
source: gpxTrackVector,
});
map.addLayer(gpxTrackLayer);
}
}
function add_layer_with_marker(lat, lng, col, cx, cy) {
var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="40" height="40" id="svg2"><circle cx="' + cx + '" cy="' + cy + '" r="7.5" id="c2" style="fill:' + col + ';stroke:#000000;stroke-width:2.5" /></svg>'
var mysvg = new Image();
mysvg.src = 'data:image/svg+xml,' + escape(svg);
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([parseFloat(lng), parseFloat(lat)], 'EPSG:4326', 'EPSG:3857')),
})]
}),
style: new ol.style.Style({
image: new ol.style.Icon({
img: mysvg,
imgSize:[40, 40]
})
}),
/*
* Stop markers totally obscuring markers at same place.
*/
opacity: 0.6
});
map.addLayer(vectorLayer);
}
</script>
</head>
<body onload="
initialize_map(50.125819, 12.528039, 10.000000, '/home/alexis/sieben-fluesse-simplified.gpx');
add_layer_with_marker(50.125819,12.528039,'#0000ff', 23, 17);
add_layer_with_marker(50.125819,12.528039,'#ffffff', 23, 23);
">
<div id="map" style="width: 100%; height: 80vh;"></div>
</body>
</html>
Can anybody advise me about how to rationalise the OpenLayers code here? Am I making any obvious errors regarding mixing OpenLayer versions? If I can correct those points, then I think I can probably google how to do the track styling or find an example that works on my code. Many thanks!
The rest of this post is some notes about the above code.
The above code is identical to what I'm running. I'm serving it using Python's SimpleHTTPServer (which I switched to when I realised file://-loaded JS can't write to the clipboard) so the path the GPX file in the initialize_map() call ('/home/alexis/') works for me and is shown on the map as a pale thin turquoise line, which is hard to distinguish from the map beneath it. Hence wanting to change the track's colour.
The above code is mashed together googled examples (one for displaying markers, one for displaying tracks, one for getting the location, one for writing text to the clipboard, etc), which - for all I know - might have been based on difference OpenLayers versions or used different approaches to programming with OpenLayers. But the result is that - due to my own lack of knowledge about Javascript and OpenLayers - I've painted myself into a corner and am unable to successfully do my next bit of development (the track colour). Hence coming here for some advice regarding rationalising the code, which I think is a prerequisite to carrying on code development and carrying on learning OpenLayers.
Unfortunately, for the purposes of writing this question, changing the path of the GPX to a URL doesn't show the track. In case it helps, the GPX file is available at https://www.pasta.freemyip.com/~alexis/sieben-fluesse-simplified.gpx.
Currently I'm bundling the SVG, CSS, HTML and JS all into one file. I'm doing this because the entire output is generated by a Python script, but I'm open to fanning it out into separate SVG, CSS, HTML and JS files.
If OpenLayers is displaying the track in default style it should display any style as long as it is specified correctly, for example
var gpxTrackLayer = new ol.layer.Vector({
source: gpxTrackVector,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'black',
width: 8
})
})
});

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.

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.

Multiple info boxes showing up when I click on map (GeoServer layers)

I have created a map using layers I added in GeoServer.
I created GetFeatureInfoUrl function to get the attribute table when clicking on layer.
But when I click on the map, all the info boxes of all the layers show up. Even if a layer (which is on top of another layer) is turned off, its attribute information comes up.
How can I make it so that only one info box shows up at a time? (So if two layers are on top of each other and user click on the map, the attribute information of the layer which is on top of the other will show up.)
One user online explained me how to do it but did not provide code. He offered the following explanation:
loop over the layers list
call get("visible") on each layer to get the visibility status set by the layer switcher
for each visible layer, append its name to a list of visible layer names
join the list of visible layer names into a single string containing comma-separated layer names
pass the string of comma-separated visible layer names as an additional parameter QUERY_LAYERS in the map in the last argument of testSource.getGetFeatureInfoUrl"
How can I create this code?
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Map</title>
<link rel="stylesheet" href="http://openlayers.org/en/v3.12.1/css/ol.css">
<link rel="stylesheet" href="ol3-layerswitcher.css">
<script src="http://openlayers.org/en/v3.12.1/build/ol.js"></script>
<script src="ol3-layerswitcher.js"></script>
</head>
<body>
<div id="map" style="width:100%;"></div>
<script src="javascript4.js"></script>
<div id="info2"></div>
<div id="info3"></div>
</body>
</html>
JavaScript:
var testSource2 = new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/wms',
params: {'LAYERS': 'Marine:Great_Britain', 'TILED': true},
serverType: 'geoserver'
});
var testSource3 = new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/wms',
params: {'LAYERS': 'Marine:Bedrock_Geology', 'TILED': true},
serverType: 'geoserver'
});
var layers = [
new ol.layer.Tile({
source: new ol.source.MapQuest({layer: 'osm'})
}),
new ol.layer.Group({
title: 'Layers',
layers: [
//Implementing layers
new ol.layer.Tile({
title: 'Great Britain',
source: testSource2
}),
new ol.layer.Tile({
title: 'Geology - Bedrock',
source: testSource3
}),
]
})
];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: [51480.6, 7216744.2], //UK
zoom: 5
})
});
//Function to get features from layer
map.on('singleclick', function(evt) {
document.getElementById('info2').innerHTML = '';
viewResolution = map.getView().getResolution();
var url = testSource2.getGetFeatureInfoUrl(
evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'});
if (url) {
document.getElementById('info2').innerHTML =
'<iframe seamless src="' + url + '"></iframe>';
}
});
//Function to get features from layer
map.on('singleclick', function(evt) {
document.getElementById('info3').innerHTML = '';
viewResolution = map.getView().getResolution();
var url = testSource3.getGetFeatureInfoUrl(
evt.coordinate, viewResolution, 'EPSG:3857',
{'INFO_FORMAT': 'text/html'});
if (url) {
document.getElementById('info3').innerHTML =
'<iframe seamless src="' + url + '"></iframe>';
}
});
//Layer switcher to turn layers on and off
var layerSwitcher = new ol.control.LayerSwitcher({
tipLabel: 'Legend'
});
map.addControl(layerSwitcher);
I believe the issue lies that you do not check in your map.on('singleclick') for what layer is being clicked on. Create a single map.on('singleclick') handler, since your code in both listeners are exactly the same.
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
var source = layer.getSource();
...
});
Now you will get the source for the layer that you have clicked on and be able to use that with your mentioned code.

Categories