Layer switching in OpenLayers 3 - javascript

I have an issue in where I need to change the map layer ( the displayed map tiles) in my web application.
I have this in my HTML
<div id="map"></div>
<select onchange="changeMap()">
<option value="BingSat">Bing Sat</option>
<option value="BingRoad">Bing Road</option>
<option value="OSM">OSM</option>
<option value="MapquestSat">Mapquest Sat</option>
<option value="MapQuestRoad">MapQuest Road</option>
</select>
and this is what I have in my JavaScript so far
$(document).ready(function() {
map = new ol.Map({
logo:'false',
target: 'map',
layers: [
new ol.layer.Group({
'title': 'Base maps',
layers: [
new ol.layer.Tile({
title: 'Water color',
type: 'base',
visible: false,
source: new ol.source.Stamen({
layer: 'watercolor'
})
}),
new ol.layer.Tile({
title: 'OSM',
type: 'base',
visible: true,
source: new ol.source.OSM()
}),
new ol.layer.Tile({
title: 'MapQuest Satellite',
type: 'base',
visible: false,
source: new ol.source.MapQuest({layer: 'sat'})
}),
new ol.layer.Tile({
title: 'MapQuest OSM',
type: 'base',
visible: false,
source: new ol.source.MapQuest({layer: 'osm'})
}),
new ol.layer.Tile({
title: 'Bing Maps aerial',
type: 'base',
visible: false,
source: new ol.source.BingMaps({
imagerySet: 'AerialWithLabels',
key: '123'
})
}),
new ol.layer.Tile({
title: 'Bing Maps road',
type: 'base',
visible: false,
source: new ol.source.BingMaps({
imagerySet: 'Road',
key: '123'
})
})
]
}),
],
view: new ol.View({
center: ol.proj.transform([17.813988, 43.342019], 'EPSG:4326', 'EPSG:3857'),
zoom: 3
})
});
}
function changeMap() {
//grab the selected ID
//change the map according to the selected ID
}
What would I need to do in order to change the displayed map? I have looked into https://github.com/walkermatt/ol3-layerswitcherdescription]1 but I cannot incorporate it entirely into my project because I need the dropdown list inside a toggable sidebar. I tried editing his code so it would add the generated HTML inside my sidebar but it did not work.
Is there a simpler way of changing the displayed map?

You need different layer groups. I do it this way:
var layersOSM = new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
]
});
var layersMQ = new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.MapQuest({layer: 'osm'})
})
]
});
function setMapType(newType) {
if(newType == 'OSM') {
map.setLayerGroup(layersOSM);
} else if (newType == 'MAPQUEST_OSM') {
map.setLayerGroup(layersMQ);
}
}
where setMapTypeis a function that I call with the necessary string attribute when I want to change the style. mapis my ol.Map variable.
You might arrange the layer groups in an array, and access them by name as you did already with the layers instead of the if/else construct I used. But I have only 2 different layer groups.

not directly an answer, but might be helpful for you or others:
to change the layer by using the name of the visible layer use:
var group = map.getLayerGroup();
var layers = group.getLayers();
var element = layers.item(0);
var name = element.get('name');
Here a JSfiddle to see what I mean.

I'm working on something similar using Bootstrap. This isn't "simpler", but it should be along the lines of what you're looking for.
So first in the HTML, I have this within my right sidebar for the basemaps:
...
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" href="#basemap-choice">
<i class="fa fa-list-alt"></i>
Basemaps
</a>
</h4>
</div>
<div id="basemap-choice" class="panel-collapse collapse in">
<div class="panel-body" id="basemaps">
</div>
<div id="baseopacity">
<label>Opacity</label>
<input id="baseopslider" type="text" data-slider-min="0" data-slider-max="100" data-slider-step="1" data-slider-value="100"/>
</div>
</div>
</div>
Under the div where id=basemaps is where I dynamically add my basemap radio buttons.
I declared my basemaps in a similar fashion yours with type: 'base'. Once I've completed the initializing the map and layers, I call a javascript function to add the layers to the sidebar panel and also bind a radio button change event so the basemap changes to what's selected:
function initBaseLayersList() {
// get all the map layers
var layers = map.getLayers().getArray();
// array to hold the basemap layers
var baseLayers = new Array();
// loop through the layers and save
// the ones with type = 'base'
for (var i=0; i<layers.length; i++) {
var lyrprop = layers[i].getProperties();
if(lyrprop.type =='base') {
baseLayers.push(layers[i]);
}
}
/* now that the basemap layers are separated, create appropriate HTML
for each layer's radio button which will be appended to the sidebar panel
body div */
$.each(baseLayers, function(index) {
var basecontent = '';
var lyrprop = this.getProperties();
basecontent += '<div class="input-group" id="radio-basemap-'+index+'">';
// first basemap in the list is selected, others are not
if(index==0) {
basecontent += '<span class="input-group-addon"><input type="radio" name="radio-basemap" checked="checked">'+lyrprop.title+'</span>';
}
else {
basecontent += '<span class="input-group-addon"><input type="radio" name="radio-basemap">'+lyrprop.title+'</span>';
}
basecontent += '</div>';
$('#basemaps').append($(basecontent));
});
/* lastly, bind the radio button group to the change event making the
selected basemap visible and hiding the one deselected
*/
$('#basemaps').bind( "change", function(event) {
var newbase = $('input[name=radio-basemap]:checked');
var btntext = newbase[0].nextSibling.nodeValue;
for (var i=0; i<baseLayers.length;i++) {
var lyrprop = baseLayers[i].getProperties();
if (lyrprop.title == btntext) {
baseLayers[i].setVisible(true);
var baseslider = $('#baseopslider').slider();
var baseopacity = baseslider.slider('getValue')/100;
baseLayers[i].setOpacity(baseopacity);
}
else {
baseLayers[i].setVisible(false);
}
}
});
}
As my code indicates, I also have a Bootstrap compatible slider which can adjust the basemap opacity.
If you can use what I've written, you should be able to modify it for a dropdown list in the sidebar vs using radio buttons. As I had developed a page previously using jQuery mobile with radio buttons for basemap selection, I just adapted that code for Bootstrap and OpenLayers 3. Just for info, OL3 version is 3.14.1, Bootstrap is 3.3.6, jQuery is 1.11.3.

Related

How to make a popup with GET/POST request ASP NET CORE

I have a map, with n numbers of markers with a unique ID. When I click on them a custom popup for that marker appears (using JS and CSS). Every time I click on one of the markers, I want it to send a POST request that will change the URL from ~/reports/index to ~report/index/custom_Id. I need it to be with a custom id in the URL because I need to load some popups from another page using GET request.
My code:
Load map:
<script type="text/javascript">
var layers = [new ol.layer.Tile({ source: new ol.source.OSM() })];
var map = new ol.Map({
target: 'map',
view: new ol.View({
zoom: 5,
center: [166326, 5992663]
}),
interactions: ol.interaction.defaults({ altShiftDragRotate: false, pinchRotate: false }),
layers: layers
});
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
function togglePopup(id) { //The function that revals the popup
document.getElementById("overlay-container-" + id).classList.toggle("active");
}
// Vector Feature Popup logic
map.on('click', function (e) { //The "sensor" that detects if a marker has been clicked.
map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
togglePopup(feature.getId());
resetCurrent();
handleImages(feature.getId());
})
})
Add markers (this is inside a bigger for loop):
<script type="text/javascript">
markers = new ol.layer.Vector({
source: new ol.source.Vector(),
style: new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: 'https://ucarecdn.com/4b516de9-d43d-4b75-9f0f-ab0916bd85eb/marker.png' // => https://app.uploadcare.com/projects/c05bbeb5e1a61e862903/files/7110cd57-b0ee-4833-bcd1-ff0343dd01cc/?limit=100&ordering=-datetime_uploaded
})
})
});
map.addLayer(markers);
var marker = new ol.Feature(new ol.geom.Point([parseFloat(#_Data.coordLat), parseFloat(#_Data.coordLong)]));
marker.setId(#_Data.Id);
markers.getSource().addFeature(marker);
</script>
I know that if it was a HTML button for something like that I could have add asp-route-id=#_Data.Id or something but this is JS. How can I make it?

Pass json data to JavaScript without creating new object

I am querying json data from database then performing some filtering and further procedures. After that I want to display that data on map, so I am passing it through JSInterop to JavaScript. Everything seems to be working except that on each method execution I am getting new map on my webpage. So after executing method 4 times, I am having 4 maps on web page.
Please comment, if additional code is necessary...
Index.razor:
<div>
<select id="layer-select" style="visibility:hidden;">
<option value="osm" selected>Open Street Map</option>
</select>
<div class="card-body p-0">
<div id="map"></div>
<div id="popup" class="ol-popup">
<div id="popup-content"></div>
</div>
</div>
</div>
Index.razor.cs:
[Inject] IJSRuntime JSRuntime { get; set; }
...
private async Task UpdateData()
{
this.SelectedDate = this.SelectedDate.Value.AddDays(1);
this.FilteredDataRecords = this.DataRecords
.Where(w => w.TimeNow.Date == SelectedDate.Value.Date && (w.Latitude != 0 || w.Longitude != 0))
.OrderBy(o => o.TimeNow)
.ToList();
string jsonData = JsonSerializer.Serialize(this.FilteredDataRecords);
await JSRuntime.InvokeAsync<object>("olMap.showMap", jsonData);
}
map.js:
window.olMap = {
showMap: function (jsonData) {
var HereLayers = [
{
base: 'base',
type: 'maptile',
scheme: 'osm',
},
];
...
var vectorLayer = new ol.layer.Vector({
source: loadMarineData(JSON.parse(jsonData)), //JSON.parse(jsonData)
visible: true,
style: new ol.style.Style({
stroke: new ol.style.Stroke({ color: '#d12710', width: 4 })
})
});
var map = new ol.Map({
overlays: [overlay],
target: 'map',
layers: layers,
view: new ol.View({
center: ol.proj.fromLonLat([25.2849, 60.0917]),
zoom: 8
})
});
map.addLayer(vectorLayer);
map.addLayer(nauticLayer);
....
var select = document.getElementById('layer-select');
function onChange() {
var scheme = select.value;
for (var i = 0, ii = layers.length; i < ii; ++i) {
layers[i].setVisible(HereLayers[i].scheme === scheme);
}
}
select.addEventListener('change', onChange);
onChange();
}
};
i've not used this map engine before but i would suggest
var map = new ol.Map({
overlays: [overlay],
target: 'map',
layers: layers,
view: new ol.View({
center: ol.proj.fromLonLat([25.2849, 60.0917]),
zoom: 8
})
});
should be
if(! document.map){
document.map = new ol.Map({
overlays: [overlay],
target: 'map',
layers: layers,
view: new ol.View({
center: ol.proj.fromLonLat([25.2849, 60.0917]),
zoom: 8
})
});
}
const map = document.map
this basically says get the already created map and only create a new map it if doesn't exist
just remember that because you are using the same map over and over again you will need to clean up the previous runs yourself
ie before
map.addLayer(vectorLayer);
map.addLayer(nauticLayer
do
for(const layer of map.getLayers())
{
map.removeLayer(layer)
}
(note this is example code to demonstrate the idea, not functioning code)
you should also avoid the var keyword instead you should use const and let var is a hold over from the early days of JS before the introduction of scoping and it has some nasty side effects if you use it in the wrong way

With two different js both generating an openlayers map each does not replace the other in the same div

Having a simple div with id='map'
and an JS:
var MapPlayOL = {
myinfo: "MapObjectOL",
simpleMap : function() {
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([12.82, 50.41]),
zoom: 7
})
});
},
mainMap : function() {
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
})
});
},
clearMap : function() {
$( "#map" ).html("");
}
Executing MapPlayOL.simpleMap() fills the map. Afterwards executing MapPlayOL.mainMap() does not replace the content of the div: no new map appears.
Same happens vice versa. Only executing MapPlayOL.clearMap() inbetween allows the second call to replace the content of the div.
Why?
It does add the map, but it does not superimpose them. If you set the map div height to 100% you will see the second map, you need to scroll down to see it below the first.
To superimpose maps you would need 2 divs inside a container
<div id="container" class="container">
<div id="map1" class="map"></div>
<div id="map2" class="map"></div>
</div>
and position them in the css
.map {
position: absolute;
}

Pop-ups are not working on hover or mouse click

I am trying to display popups for open layers as shown LAYER1, I want to display that when user hovers over a particular polygon of the layer it displays fields information from attribute table which are Name and Area. There is no error and no feature is returned in console as well.Please suggest me solution for this.
Javascript:
//Layers for which popups have to be displayed
var LAYERNAME = new ol.layer.Tile({
source: new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/Workspace/wms',
params: { 'LAYERS': 'Workspace:LAYERNAME', 'TILED': false },
serverType: 'geoserver',
// Countries have transparency, so do not fade tiles:
transition: 0
}),
visible:false,
title: 'LAYER1'
})
var view = new ol.View({
projection: 'EPSG:4326',
center: [75,31],
zoom: 8.5,
// minZoom: 14,
})
var map = new ol.Map({
controls: ol.control.defaults({
attributionOptions: {
collapsible: false
}
}).extend([
new app.drawFeatures(), new app.deleteFeatures()
]),
overlays: [overlay],
target: 'map',
view: view,
layers: [basemap, LAYERNAME],
//Overlay container for layer
var OverlayElement=document.querySelector('.overlay-container');
var Overlay=new ol.Overlay({
element: OverlayElement,
positioning: 'center-left'
})
map.addOverlay(Overlay);
//POPUP using fiels name from attribute table
const Name=document.getElementById('Name');
const Area=document.getElementById('Area');
map.on('click',function(e){
Overlay.setPosition(undefined);
//var info = [];
map.forEachFeatureAtPixel(e.pixel,function(feature,layer){
//info.push(feature.get('Name'));
const coordinate=e.coordinate;
var hdms = ol.coordinate.toStringHDMS(new ol.proj.toLonLat(coordinate));
content.innerHTML = '<p>You clicked here:</p><code>' + coordinate + '</code>';
overlay.setPosition(coordinate);
console.log(e.coordinate);
let clickedFeatureName=feature.get('Name');
let clickedFeatureArea=feature.get('Area');
Overlay.setPosition(ClickedCoordinate);
Name.innerHTML="Name:"+clickedFeatureName;
Area.innerHTML="Area:"+clickedFeatureArea;
}),
})
HTML:
<div class="grid-1">
<div id='map' class='map'></div>
<div id="popup" style='width:180px' class="ol-popup">
<div id="popup-content"></div>
</div>
</div>
</div>
<div class="overlay-container">
<span class='Overlay-text' id='Name'></span><br>
<span class='Overlay-text' id='Area'></span><br>
</div>

how to change the default marker OPENLAYERS 5

I am trying to display some markers on the OSM. This works so far, but i just wanted to change the default marker against another one from a local path, but i didnt know how to add this to my code, i have tried the setStyle but i didnt know how to apply it correctly. It would be also nice if am able to style the icon too.
I also want markers to be dynamically displayed, because i am doing this manually each time through
var layer = new ol.layer.Vector({source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([4.35247, 50.84673]))
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([4.35247, 52.84673]))
}),
]
});
Here is a snippet of the full code that i use
/* open street map newest version */
var map = new ol.Map({
target: 'map', // the div id
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([4.35247, 52.520008]),
zoom: 4
})
});
// add a marker to the map
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([4.35247, 50.84673]))
}),
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([4.35247, 52.84673]))
})
]
})
});
map.addLayer(layer);
//initialize the popup
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var overlay = new ol.Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250
}
});
map.addOverlay(overlay);
//display the pop with on mouse over
map.on('pointermove', function (event) {
if (map.hasFeatureAtPixel(event.pixel) === true) {
var coordinate = event.coordinate;
content.innerHTML = '<b>Hello world!</b><br />I am a popup.';
overlay.setPosition(coordinate);
}
else {
overlay.setPosition(undefined);
closer.blur();
}
});
Fixed this, the issue was from the local path. If this may help anyone i added this to my code
layer.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: 'https://wiki.openstreetmap.org/w/images/0/0c/Hgv.png'
})
}));

Categories

Resources