I want to add a png icon to a mapbox map from any publicly available online source.
I'm trying to use this example here from mapbox.com, which uses a png from wikimedia:
https://docs.mapbox.com/mapbox-gl-js/example/add-image/
This is the png they use:
https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png
I can swap it out from anything at Wikimedia, but not anything else. For example, swap the link out with this one, and it doesn't appear:
https://www.thenittygritty.org/coronavirus/circle1.png
What am I missing here??
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add an icon to the map</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v1.8.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.8.1/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>
mapboxgl.accessToken = 'pk.eyJ1Ijoibml0dHlqZWUiLCJhIjoid1RmLXpycyJ9.NFk875-Fe6hoRCkGciG8yQ';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11'
});
map.on('load', function() {
map.loadImage(
'https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png',
function(error, image) {
if (error) throw error;
map.addImage('cat', image);
map.addSource('point', {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [0, 0]
}
}
]
}
});
map.addLayer({
'id': 'points',
'type': 'symbol',
'source': 'point',
'layout': {
'icon-image': 'cat',
'icon-size': 0.25
}
});
}
);
});
</script>
</body>
</html>
The problem lies with the CORS settings of your browser. When I allow CORS, the image loads just fine.
Error:
Access to fetch at
'https://www.thenittygritty.org/coronavirus/circle1.png' from origin
'null' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
What is CORS: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
You can try this Chrome extension to allow CORS:
https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en
Related
SO I am new to Mapbox but I am trying to make countries change color on hover. I have seen the hover tutorial on the Docs for states but I am having difficulty finding the right type, data, and source for countries. I have the Mapbox Countries v1 already added to the map from studio. When I try to run this code I get either that it can't find source:"country_boundaries" or "country-boundaries". I know it's something simple but appreciate the help. Thanks.
var hoveredStateId = null;
map.on('load', function () {
==>>map.addSource('states', {
==>>'type': 'geojson',
==>>'data': 'https://docs.mapbox.com/mapbox-gl-js/assets/us_states.geojson'
});
map.addLayer({
'id': 'state-fills',
==>>'type': 'fill',
==>>'source': 'states',
'layout': {},
'paint': {
'fill-color': '#627BC1',
'fill-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
1,
0.5
]
}
});
map.addLayer({
'id': 'state-borders',
==>>'type': 'line',
==>>'source': 'states',
'layout': {},
'paint': {
'line-color': '#627BC1',
'line-width': 2
}
});
Below you will find a complete example I've put together from findings at https://docs.mapbox.com/ and other places on the internet.
Please note that this uses Mapbox example token and http://geojson.xyz/ directly, you should download the data and host it yourself if used in an production environment.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Display a map on a webpage</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoid2l0dHJ1cCIsImEiOiJjbDc1NnFpaWIwYzZqM3VudGptdjUzNWZ1In0.R5FpTsM0CeaxLhq8i8Qmyg';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40], // starting position [lng, lat]
zoom: 0.31741259330687854
});
map.on('load', function () {
map.addSource('cbs', { // country-boundaries-simplified
'type': 'geojson',
'data': 'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_countries.geojson'
});
map.addLayer({
"id": "cf", // country-fills
"type": "fill",
"source": "cbs",
"layout": {},
"paint": {
"fill-color": "#627BC1",
"fill-opacity": 0.5
}
});
map.addLayer({
"id": "cb", // country borders
"type": "line",
"source": "cbs",
"layout": {},
"paint": {
"line-color": "#627BC1",
"line-width": 2
}
});
map.addLayer({
"id": "cfh", // country-fills-hover",
"type": "fill",
"source": "cbs",
"layout": {},
"paint": {
"fill-color": "#FFFFFF",
"fill-opacity": 1
},
"filter": ["==", "name", ""]
});
// When the user moves their mouse over the page, we look for features
// at the mouse position (e.point) and within the states layer (states-fill).
// If a feature is found, then we'll update the filter in the state-fills-hover
// layer to only show that state, thus making a hover effect.
map.on("mousemove", function(e) {
var features = map.queryRenderedFeatures(e.point, { layers: ["cf"] });
if (features.length) {
map.getCanvas().style.cursor = 'pointer';
map.setFilter("cfh", ["==", "name", features[0].properties.name]);
} else {
map.setFilter("cfh", ["==", "name", ""]);
map.getCanvas().style.cursor = '';
}
});
// Reset the state-fills-hover layer's filter when the mouse leaves the map
map.on("mouseout", function() {
map.getCanvas().style.cursor = 'auto';
map.setFilter("cfh", ["==", "name", ""]);
});
map.on("click", function(e) {
var features = map.queryRenderedFeatures(e.point, { layers: ["cf"] });
if (features.length) {
console.log(e, features[0].properties.name);
}
});
});
</script>
</body>
</html>
Hi I want to generate a heatmap from a .geojson file with this plugin
How can I manage that? I'm very new to json, leaflet and web development.
The geoJSON file I have looks something like this (with more points ofc):
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "testmarker",
"description": "This is a test description"
},
"geometry": {
"type": "Point",
"coordinates": [
-33.134766,
-60.326948
]
}
},
{
"type": "Feature",
"properties": {
"name": "testmarker2"
},
"geometry": {
"type": "Point",
"coordinates": [
-41.220703,
-62.552857
]
}
}
]
}
And here is what my index.html looks like right now.
<!DOCTYPE html>
<html = style="height: 100%;">
<head>
<title>Test Map</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="scripts/leaflet.css">
<script src="scripts/leaflet.js"></script>
<script src="scripts/leaflet.ajax.min.js"></script>
<script src="scripts/leaflet-heat.js"></script>
</head>
<body style="height: 100%;margin: 0;">
<div id="map" style="width: 100%; height: 100%; background: #000000;"></div>
<!-- Workaround for 1px lines appearing in some browsers due to fractional transforms
and resulting anti-aliasing.-->
<style>
.leaflet-tile-container img {
width: 256.5px !important;
height: 256.5px !important;
}
</style>
<script type="text/javascript">
// Creating the Map
var map = L.map('map').setView([58.602611, 50.350342], 4);
L.tileLayer('maps/mymap/{z}/{x}/{y}.png', {
continuousWorld: false,
noWrap: true,
minZoom: 2,
maxZoom: 6,
zoomControl: false,
minNativeZoom: 2,
maxNativeZoom: 6,
updateInterval: 200,
}).addTo(map);
// Remove old zoomControl and add new one in order to move it to bottomright
map.zoomControl.remove();
L.control.zoom({
position: 'bottomright'
}).addTo(map);
// Binds tooltip on all markers and also a popup on click if the markers has a description
function oef(feature, layer) {
if (feature.properties && feature.properties.name) {
layer.bindTooltip(feature.properties.name);
}
if (feature.properties && feature.properties.name && feature.properties.description) {
layer.bindPopup("<center><b> " + feature.properties.name + "</center></b><br>" + feature.properties.description);
}
}
var geojsonMarkers = new L.GeoJSON.AJAX(["https://raw.githubusercontent.com/***/test.geojson"],{onEachFeature:oef});
var mapOverlays={
"Test" : geojsonMarkers,
//"Test - Heat Map" : geojsonMarkersHeat,
}
var layerControl = L.control.layers(null, mapOverlays, {position:'topleft'}).addTo(map);
</script>
</body>
</html>
As you can see from above I want the heatmap to be toggleable via the mapOverlays but I don't really know how to proceed.
I am starting using Vega and I am now trying to stream my data to nicely refresh my graph. I followed this doc (https://vega.github.io/vega-lite/tutorials/streaming.html) with no success so far. The examples I found have values in arrays and mine are in a JSON file which is refreshed on a regular basis. I ended up with this code which properly shows my graph as I want but it does not refresh my data.
<html>
<head>
<title>A title</title>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/vega#5.8.1"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite#4.0.0-beta.12"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed#6.1.0"></script>
<style media="screen">
/* Add space between Vega-Embed links */
.vega-actions a {
margin-right: 5px;
}
</style>
</head>
<body>
<head>
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/vega#5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed#5"></script>
</head>
<body>
<div id="vis"></div>
<script>
var vlSpec = {
data: {"url": "http://localhost:8123/d.json", "name":"table"},
mark: "bar",
"width": 1240,
"encoding": {
"y": {"field": "task", "type": "ordinal", "sort":"x2"},
"x": {"field": "start", "type": "temporal", "aggregate":"sum"},
"x2": {"field": "end", "type": "temporal"},
"color": {"field": "status", "type": "nominal"}
}
};
// Embed visualization and save view as window.view:
vegaEmbed('#vis', vlSpec).then(function(res) {
window.setInterval(function() {
var changeSet = vega
.changeset()
.insert()
res.view.change('table', changeSet).run();
}, 1000);
});
</script>
</body>
</html>
My issue seems to be the insert() where in the example of the doc they insert the result of a function to generate data and I should insert here my JSON file -- I tried different variations with fetch but with no success.
Any help would be appreciated.
Thanks
Let me answer my own question as I figured it out.
First I used jquery:
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
I removed the url tag not to load the JSON file at first:
"data": {"name":'table'},
And the VEGA streaming data from my JSON file:
vegaEmbed('#vis', vlSpec).then(function(res) {
var newdata ;
window.setInterval(function() {
jQuery.getJSON("http://localhost:8123/d.json", function(data) {
newdata = data;
})
var changeSet = vega
.changeset()
.remove(vega.truthy)
.insert(newdata)
console.log (newdata)
res.view.change('table', changeSet).run();
}, 1000);
});
It works like a charm !
I have started playing around with Mapbox, and things are working as I would like, except that I have not been able to figure out how to disable clustering. Here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Add custom icons with Markers</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.0.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<style>
.marker {
/*display: block;*/
border: none;
/*border-radius: 50%;*/
cursor: pointer;
padding: 0;
background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Map_pin_icon.svg/176px-Map_pin_icon.svg.png');
background-size: cover;
width: 20px;
height: 27px;
/*border-radius: 50%;*/
/*cursor: pointer;*/
}
</style>
<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoibmFiZWxla3QiLCJhIjoiY2p4ZXVubnQwMGVmcTN6cGU0c3JpZmM2diJ9.peecDCcSljWhChxCknv7AQ';
var coordinates = [
[6.73579, 78.72300],
[2.70886, 11.51694],
[34.05482, -57.09742],
[-39.05019, 89.79126],
[17.44893, 35.57014],
[23.60105, 168.12674],
[-4.87631, 72.99334],
[39.7392, -104.9903],
[39.79905,-105.78118],
[39.80266,-105.78692],
[39.79758,-105.78061],
[39.80314,-105.78978],
[39.80313,-105.78999],
[39.80103,-105.78272],
[39.80096,-105.78259],
[39.80187,-105.78407],
[39.80283,-105.78593],
[39.79937,-105.78134],
[39.80023,-105.78164],
[39.80272,-105.78765],
[39.80263,-105.78673],
[39.80264,-105.78676],
[39.80125,-105.78326],
[39.7976,-105.78028],
[39.80316,-105.7905],
]
var num_coordinates = Object.keys(coordinates).length
var geojson = {
"type": "FeatureCollection",
"features": []
}
for (var coord_ind = 0; coord_ind < num_coordinates; coord_ind++) {
geojson.features.push({
"type": "Feature",
"properties": {
"message": "Bar",
"iconSize": [300, 300]
},
"geometry": {
"type": "Point",
"coordinates": [coordinates[coord_ind][1], coordinates[coord_ind][0]] // Longitude then Latitude
}
})
}
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location
// style: 'mapbox://styles/mapbox/satellite-v9', // stylesheet location
center: [-99.66, 38.46], // starting position [lng, lat]
zoom: 5.5 // starting zoom
});
map.on('load', function() {
map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Map_pin_icon.svg/176px-Map_pin_icon.svg.png', function(error, image) {
if (error) throw error;
map.addImage('pin', image);
map.addSource("photo_locations", {
type: "geojson",
data: geojson,
cluster: false,
clusterMaxZoom: 1, // Max zoom to cluster points on
clusterRadius: 1 // Radius of each cluster when clustering points (defaults to 50)
});
map.addLayer({
"id": "points",
"type": "symbol",
"source": "photo_locations",
"layout": {
"icon-image": "pin",
"icon-size": 0.12
}
});
});
});
</script>
</body>
</html>
You can see that I have cluster: false set for my source. However, if you display this in your browser, the pins are still clustered together. If you zoom in on the pin to the west of Denver, you will see it separate into multiple pins.
How can I get clustering to disable entirely so that each individual pin can be seen at any zoom level? Any thoughts are appreciated. Thanks!
As geografa suggested, adding "icon-allow-overlap": true does the trick. This needs to be added to the layout section of the layer. So I now have:
...
"layout": {
"icon-image": "pin",
"icon-size": 0.08,
"icon-allow-overlap": true
}
...
I am trying to use the following code to highlight features under the mouse pointer.
The difference between my geojson data and the geojson data used in the linked example is that the example is made up of polygons whereas my geojson is made up of polylines. I have tried to modify the code accordingly in order that lines are highlighted however it does not work.
My geojson is accessible here:
http://iskandarblue.github.io/mapbox/data/prototype2.geojson
Any advice on what needs to be changed?
Example:
https://www.mapbox.com/mapbox-gl-js/example/hover-styles/
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>HTML markers from geoJSON url</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.15.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.15.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>
mapboxgl.accessToken = 'pk.eyJ1IjoiaXNrYW5kYXJibHVlIiwiYSI6ImNpbHIxMXA3ejAwNWl2Zmx5aXl2MzRhbG4ifQ.qsQjbbm1A71QzVg8OcR7rQ';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v8',
center: [37.625224, 55.744537,],
zoom: 13
});
map.on('style.load', function () {
map.addSource("streets", {
"type": "geojson",
"data": "http://iskandarblue.github.io/mapbox/data/prototype2.geojson"
});
map.addLayer({
"id": "m_streets",
"type": "line",
"source": "streets",
"interactive": true,
"layout": {},
"paint": {
"line-color": "#627BC1",
"line-width": 2.5
}
});
map.addLayer({
"id": "route-hover",
"type": "line",
"source": "streets",
"layout": {},
"paint": {
"line-color": "#627BC1",
"line-width": 2.5
},
"filter": ["==", "rd_name", ""]
});
// When the user moves their mouse over the page, we look for features
// at the mouse position (e.point) and within the states layer (states-fill).
// If a feature is found, then we'll update the filter in the route-hover
// layer to only show that state, thus making a hover effect.
map.on("mousemove", function(e) {
map.featuresAt(e.point, {
radius: 5,
layer: ["m_streets"]
}, function (err, features) {
if (!err && features.length) {
map.setFilter("route-hover", ["==", "rd_name", features[0].properties.rd_name]);
} else {
map.setFilter("route-hover", ["==", "rd_name", ""]);
}
});
});
});
//.addTo(map);
</script>
</body>
</html>
The route-hover layer just needs to be styled differently right? Here's a live example of your code above with a slight adjustment: https://jsbin.com/loxoquwiye/