Using the mapboxgl, I added a my custom map control, which can do some special functions,and I refered the office example with basic function. But When I completed codes, I found the event click of button did not work. I have add the stopPropagation function. So how to add click event in the mapbox control?
mapboxgl.accessToken = 'pk.eyJ1IjoieGlhb2thbmciLCJhIjoiY2lqc2d2NXlyMGhkbHU0bTVtcGNiOWxseCJ9.J5qsX13KKNT1slMGS-MOLg';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
center: [-74.50, 40], // starting position [lng, lat]
zoom: 9 // starting zoom
});
class ToggleControl {
onAdd(map){
this.map = map;
this.container = document.createElement('div');
this.container.className = 'my-custom-control';
const button = this._createButton('monitor_button')
this.container.appendChild(button);
return this.container;
}
onRemove(){
this.container.parentNode.removeChild(this.container);
this.map = undefined;
}
_createButton(className) {
const el = window.document.createElement('button')
el.className = className;
el.textContent = 'toggleControl';
el.addEventListener('click',(e)=>{
e.style.display = 'none'
console.log(e);
// e.preventDefault()
e.stopPropagation()
},false )
return el;
}
}
const toggleControl = new ToggleControl()
map.addControl(toggleControl,'top-left')
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Display a map</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.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>
</script>
</body>
</html>
The problem is that the mapbox container positioning style removes mouse events handling (pointer-events: none;):
.mapboxgl-ctrl-top-left,
.mapboxgl-ctrl-top-right,
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
position: absolute;
pointer-events: none;
z-index: 2;
}
So you need to add the class mapboxgl-ctrl (pointer-events: auto;) to the container for catching events:
this.container.className = 'mapboxgl-ctrl my-custom-control';
Or set this CSS-property manually:
.my-custom-control {
pointer-events: auto;
}
[ https://jsfiddle.net/Lkes0ch8/ ]
I have the same problem, stdob-- is correct,
by add 'mapboxgl-ctrl' to className solved my problem.
without this, user can not drag the slider bar.
this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group opacityDIV';
Top right corner slider is what I created.
There are 2 way to add mapbox control,
standard way is from mapbox example,
alternative way is works the same
Here is my working code:
Related
I have a JS variable controlling styling of the page. I want it to change depending on screen resolution. How can I make it depend on results of CSS media query.
I know that I can check screen resolution directly in JS but I want to keep all styling decisions within CSS rather than spread it across JS and CSS files.
to avoid XY problem: I am using Leaflet and JS variable decides whatever map control panel listing available layers is collapsed or not. It should be collapsed on small screens (mobile) and not collapsed on large screens (proper monitors).
The relevant code is available below
html,
body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
#media (min-width: 1000px) {
#map {
width: 1000px;
height: 900px;
}
}
<!DOCTYPE html>
<html>
<!--based on https://leafletjs.com/examples/layers-control/ example -->
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.3/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.3.3/dist/leaflet.js" integrity="sha512-tAGcCfR4Sc5ZP5ZoVz0quoZDYX5aCtEm/eu1KhSLj2c9eFrylXZknQYmxUssFaVJKvvc0dJQixhGjG2yXWiV9Q==" crossorigin=""></script>
<link rel="stylesheet" href="example.css" />
</head>
<body>
<div id='map'></div>
<script>
var positron = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
attribution: '#{openstreetmap_copyright_notice}, basemap: © CartoDB',
subdomains: 'abcd',
maxZoom: 19
}),
osmcarto = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
});
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
layers: [positron]
});
var baseLayers = {
"positron": positron,
"osm-carto": osmcarto
};
L.control.layers(baseLayers, {}, {
collapsed: false
}).addTo(map); //false/true should be specified by CSS
</script>
</body>
</html>
Pretty cheezy solution, but it would mean not hardcoding any width properties in JS.
Add a dummy, invisible element to the page.
<div id="dummy"></div>
#dummy::after {
display: none;
content: 'false';
}
#media (min-width: 1000px) {
#dummy::after {
content: 'true';
}
}
Then read the value of the content in JS:
var isBigScreen = window.getComputedStyle(
document.querySelector('#dummy'), ':after'
).getPropertyValue('content');
isBigScreen will be either "true" or "false". You might want to add some code to recheck that if the screen size changes.
if(isCollapsed === '"false"') {
isCollapsed = false;
} else {
isCollapsed = true;
}
may be used to convert it to a standard boolean.
You can use matchMedia to do this, either as a one-off check or (more usefully) with a callback (more on the callback option here):
window.matchMedia("(min-width: 1000px)").addListener(function(e) {
if (e.matches) {
// The window currently matches the query
} else {
// It doesn't
}
});
To avoid repeating the query text in the JavaScript, you can search through document.styleSheets and using their media property. May not be a lot of fun, but I can't imagine any other way to avoid duplicating the media query.
For example, this uses the first screen and rule it finds (jsFiddle):
// Find the rule
const rule = Array.from(document.styleSheets).reduce((found, sheet) => {
return found || Array.from(sheet.cssRules || sheet.rules).find(rule => {
return rule.conditionText && rule.conditionText.startsWith("screen and ");
});
}, null);
if (rule) {
const query = rule.conditionText.substring(11);
console.log("rule found, setting listener for: " + query);
window.matchMedia(query).addListener(function(e) {
console.log("matches? ", e.matches);
});
} else {
console.log("No rule found");
}
Naturally, you'd need to wait for any stylesheets loaded via link elements to be loaded first.
The only way I could think of is by setting a certain style in your CSS and then checking for that style in Javascript.
e.g. something like document.getElementById('map').style.width === '1000px'
I'm using leaflet to put some markers on a map. I have set that, clicking on a marker, a popup will be opened showing an image. Here's a brief example:
var map = L.map('map')
.addLayer(tile)
.setView([initLat, initLon], initZoom);
var m = L.marker([lat, lon])
.bindPopup('<img src="1.jpg"/>')
.addTo(map);
My objective is to load those images ("1.jpg" in the example above) using lazy load, so it's only loaded when I click on the marker.
Does anyone knows how to do this?
Thanks!
You could set the content of the popup when the popup is opened.
Let's create a custom popup with a lazyload option and without content :
var m = L.marker([0, 0])
.bindPopup(L.popup({
lazyload: '<img src="1.jpg"/>'
}))
.addTo(map);
You can then set a global handler to fill your popup when needed:
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
And a demo:
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup(L.popup({
lazyload: '<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />'
}))
.addTo(map);
map.on('popupopen', function(e) {
var popen = e.popup;
if (popen.options.lazyload) {
popen.setContent(popen.options.lazyload);
}
});
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>
Actually in the case where you fill your popup with a String content (like you did with .bindPopup('<img src="1.jpg"/>'), Leaflet converts it (through innerHTML) to DOM nodes only when the Popup is first opened on a map. Therefore your image will be loaded only at that moment, which is exactly the lazy loading behaviour you are looking for.
So you do not need to do anything extra from what you already have done in your question code:
(make sure you refresh your page / clear your cache to see the image loading pass in the browser network requests)
var map = L.map('map', {
center: [0, 0],
zoom: 1
});
var m = L.marker([-30, 0])
.bindPopup('<img src="https://i.stack.imgur.com/shfxy.jpg?s=32&g=1" />')
.addTo(map);
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>
<div id='map'></div>
I am trying to use the Google Maps API. I have gotten a authorization key and pasted in the sample code they gave us, but I can't make the map render even when they tell me to make sure I put a height of 100% on both my map div and the body.
I have tried and it won't show up. If I put a 500px height on the map div, and give the body a 100% height it works... but I want to know why height of 100% will not will not render. I am copying exactly what the Google documentation (https://developers.google.com/maps/documentation/javascript/examples/map-simple) is giving me and what the video tells me to double check. HELP, i am insanely curious on why 100% height won't render. I want to use percentages, so that it can be compatible on mobile devices!
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Ice Cream</title>
<!-- Google Docs-->
<!-- external CSS link -->
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/style.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
<!-- external CSS link -->
<!-- javascript -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDuDCh7Odrr4RUUiAHMyDtfwC2NtySy-64"></script>
<script src="js/index.js"></script>
</body>
</html>
body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
The browser needs to be able to calculate a non-zero height. You need to set the height of the html element to 100% also:
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
html, body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
The reason why 100% doesn't work is because the map needs to send you images at a specific ratio (fixed width & height). Now I might be guessing here, but that's what I think happens.
Since I had a similar problem, where my map was hidden inside a tab, I wrote this function to calculate the width & height or a ratio. Then whatever size of the container, the map was in, it would adjust. It uses jQuery so I just thought I'd post it to get the idea.
/**
* Calculate the dimensions
*/
calculateMap: function () {
var self = this,
parent = this.map.parent(), // jQuery obj
w, h, id, el;
// if the container of your map is visible and wrapped it has a certain width and height...
// if it also has 100% height, it could be collapsed (not wrapped)
// so you ask the google maps api to load images at 500px width and 0height?
// if the parent has 0height, the ratio helps to set it properly
// with debug tools (F12) you can see the parent if it lights up
if (parent.is(':visible')) {
w = parent.outerWidth(false);
h = w * this.settings.options.mapRatio; // 4/3 | 16/9
this.map.css({ width: w, height: h });
} else {
// if your map is invisible after load...
id = this.onParent.prop('id'); // this id references my tab
el = this.onParent.prevAll().find('a[href=#' + id + ']'); // trigger of the tab
// add click event once
el.one(this.settings.events.onParent, function() {
setTimeout(function() {
self.activate(); // goes back to calculateMap()
}, 200);
}.bind(this));
}
},
If you trigger calculateMap on the resize event, you'll have a responsive approach.
Also a quick google showed me this approach ... notice the fixed width and height on the iframe.
In short:
fixed width and height in css gives you the cleanest solution. you might be able to use a clearfix, padding in % or min-height in px to gain dimension if that works out.
For dynamic/event loading I suggest JavaScript or jQuery
or with an iframe and a fixed width and height
I want to call nokia.places.search.manager.findPlaces using a bounding box. I want the bounding box to correspond to the map being displayed on the page. I can't find the methods I would expect there to be at http://developer.here.com/maps_js.
The method you are after is Display.getViewBounds(). To quote from the API Reference:
This method retrieves the outer bounding box of the map view (the
smallest bounding box covering all visible points).
Here is an example using the boundingBox parameter in a places search (use your own app id and token of course). If you increase the zoomLevel of the map displayed, then the search is bounded to a smaller area. The important bit is right at the bottom.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>HERE Maps API Example: Search by bounding box</title>
<meta name="description" content="Search by category"/>
<meta name="keywords" content="search, services, places, category"/>
<!-- For scaling content for mobile devices, setting the viewport to the width of the device-->
<meta name=viewport content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- By default we add ?with=all to load every package available, it's better to change this parameter to your use case. Options ?with=maps|positioning|places|placesdata|directions|datarendering|all -->
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script>
<!-- JavaScript for example container (NoteContainer & Logger) -->
<style type="text/css">
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 80%;
height: 80%;
left: 0;
top: 0;
position: absolute;
}
#progress {
width: 80%;
height: 10%;
left: 0;
top: 80%;
position: absolute;
}
#buttons {
width: 80%;
height: 10%;
left: 0;
top: 90%;
position: absolute;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="progress"></div>
<script type="text/javascript" id="exampleJsSource">
nokia.Settings.set("appId", "YOUR APP ID");
nokia.Settings.set("authenticationToken", "YOUR TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [52.51, 13.4],
zoomLevel: 15,
components: [
new nokia.maps.map.component.Behavior()
]
});
var searchManager = nokia.places.search.manager,
resultSet;
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
if (requestStatus == "OK") {
// The function findPlaces() and reverseGeoCode() of return results in slightly different formats
locations = data.results ? data.results.items : [data.location];
// We check that at least one location has been found
if (locations.length > 0) {
// Remove results from previous search from the map
if (resultSet) map.objects.remove(resultSet);
// Convert all found locations into a set of markers
resultSet = new nokia.maps.map.Container();
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position, { text: i+1 });
resultSet.objects.add(marker);
}
// Next we add the marker(s) to the map's object collection so they will be rendered onto the map
map.objects.add(resultSet);
// We zoom the map to a view that encapsulates all the markers into map's viewport
//map.zoomTo(resultSet.getBoundingBox(), true);
} else {
//alert("Your search produced no results!");
}
searching = false;
} else {
alert("The search request failed");
searching = false;
}
};
// Binding of DOM elements to several variables so we can install event handlers.
var progressUiElt = document.getElementById("progress");
var searching = false;
search = function(boundingBox){
// Make a place search request
var category = "eat-drink"
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
boundingBox: boundingBox,
limit: 50,
});
}
search(map.getViewBounds());
</script>
</body>
</html>
Im using nokia maps javascript api, is posible place a grid in the map?
The easiest way to do this would be to use a transparent grid PNG as an overlay.
First create a 256x256 PNG file as shown below.
Then use that as your getTileUrl() function, so it is returned over all of the Map tiles on the map.
var getTileUrl = function (zoom, row, column) {
return "http://i.stack.imgur.com/M1ncK.png";
};
The result is something like this:
An example can be seen below, with your own PNG file, app id and token of course .
/* Set authentication token and appid
*
* please register on http://api.developer.nokia.com/
* and obtain your own developer's API key
*/
nokia.Settings.set("appId", "MY APP ID");
nokia.Settings.set("authenticationToken", "MY TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// initial center and zoom level of the map
center: [52.515, 13.405],
zoomLevel: 14,
components: [
// ZoomBar provides a UI to zoom the map in & out
new nokia.maps.map.component.ZoomBar(),
// We add the behavior component to allow panning / zooming of the map
new nokia.maps.map.component.Behavior()
]
});
var getTileUrl = function (zoom, row, column) {
return "http://i.stack.imgur.com/M1ncK.png";
};
tileProviderOptions = {
getUrl: getTileUrl, // Obligatory function
max:20, // The highest zoom level for the overlay.
min:1, // The lowest zoom level for the overlay.
opacity: 0.5, // Overlay opacity.0 is fully transparent, 1 is fully opaque.
alpha:true // This value tells the renderer to read the alpha channel; required if opacity is used.
},
// Create an overlay by calling the constructor for ImgTileProvider
gridOverlay = new nokia.maps.map.provider.ImgTileProvider(tileProviderOptions);
// Add the overlay to the map
map.overlays.add(gridOverlay);
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9; IE=EmulateIE10;"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps Example: Adding an overlay to the map</title>
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.4/jsl.js?with=all"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="uiContainer"></div>
</body>
</html>