How to add Images inside a custom container on Mapbox? - javascript

I want to add images to Mapbox in a similar style as follow:
I am not sure how to go about doing this since Mapbox doesn't really offer many options when adding an image to the map. I have tried adding a custom marker with an image inside it and that works but then I lose layer features such as hiding etc. Any ideas on how this can be done?

You can load an image and then use it as part of a symbol layer. You can see it in one of the official examples.
map.on('load', function () {
map.loadImage(
'https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.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
}
});
}
);
});

Related

How to add symbol on line center, and make the arrow aligns to line end point in Mapbox

I have created the map with Mapbox, but I had a few problems. When I try to Place the icon in the center of the line and add the arrow to the line endpoint, it always fails.
I added Markers on Jorge Chavez International Airport(LIM) & Alejandro Velasco Astete International Airport (CUZ)
const marker1 = new mapboxgl.Marker({ color: '#5C7F74',scale:'0.8'})
.setLngLat([-77.11174632225463, -12.02384281134897])
.addTo(map);
const marker3 = new mapboxgl.Marker({ color: '#8BAAA1',scale:'0.5'})
.setLngLat([-70.15810500000016,-15.469970999998012])
.addTo(map);
then add a line to map, So far it's okay but I don't know how to build new layers, add arrows aligns to the line endpoint, or add a symbol on line center
map.on('load', () => {
map.addSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'LineString',
'coordinates': [
[-70.15810500000016,-15.469970999998012],[-77.10670100000081,-12.030560999998116]
]
}
}
});
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round',
'line-miter-limit': 2
},
'paint': {
'line-color': '#888',
'line-width': 3,
'line-dasharray':[2,2]
}
});
});

mapbox passing coordinates as variable does not work

It tried to implement the solution to draw a polygon from external data as shown in https://jsfiddle.net/zxaktouy/1/ but I get the error:
Input data given to 'frag15' is not a valid GeoJSON object.
My JS-method:
drawFragment : function(pFRAGMENT) {
const wPolygon = pFRAGMENT.coordinates;
console.log("drawFragment: Coords="+wPolygon);
wSourceId = "frag"+pFRAGMENT.id;
wFillId = "fragfill"+pFRAGMENT.id;
wOutlineId = "fragoutline"+pFRAGMENT.id;
map.addSource(wSourceId,{
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [
wPolygon
]
}
}
});
// Add a new layer to visualize the polygon.
map.addLayer({
'id': wFillId,
'type': 'fill',
'source': wSourceId, // reference the data source
'layout': {},
'paint': {
'fill-color': '#00ff80', // green color fill
'fill-opacity': 0.5
}
});
// Add a black outline around the polygon.
map.addLayer({
'id': wOutlineId,
'type': 'line',
'source': wSourceId,
'layout': {},
'paint': {
'line-color': '#0d0',
'line-width': 2
}
});
},
And the data passed (perfectly shown via console.log) look like this:
[[8.543590974130666,47.377830192117756],
[8.543641551219707,47.37784384335191],
[8.543634914341965,47.37789513281288],
[8.543582309906242,47.37791046432616],
[8.543590974130666,47.377830192117756]]
when I replace the "wPolygon" just by copy-pasting the data into the code everything works fine.
After replacing:
const wPolygon = pFRAGMENT.coordinates;
with
var wPolygon = JSON.parse(pFRAGMENT.coordinates);
it works.

Change icon opacity when hover(symbol layer), in Mapbox GL JS

I see in the documentation that icon-opacity supports feature-state,
https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-opacity
Therefore I use this code for making a change of opacity when user hovers over a icon:
map.addLayer({
type: 'symbol',
layout: {
'icon-image': 'point',
...
},
paint: {
'icon-opacity':
['case', ['boolean', ['feature-state','hover'], false], 1, 0.3]
}
});
The problem is that it takes the original opacity right(0.3) but does not change on hover.
Any idea?
Thanks.
The code you´re posting is right, but it's only half of the solution.
You already implemented the definition of the layer paint behavior for any feature (by default 0.3 as you say), the second part is to change the feature-state hover to true on mouseover.
The state of the features on 'hover' in your symbol layer don´t change automatically, you need to change it to true in a map.on('mousemove', 'youLayerId'... method, and again to false in a map.on('mouseout', 'youLayerId'... .
Check out this fiddle I have created for you on how to change opacity of an icon.
The relevant code is below
mapboxgl.accessToken = 'PUT HERE YOUR TOKEN';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11'
});
let fHover = null;
map.on('load', function() {
map.loadImage(
'https://upload.wikimedia.org/wikipedia/commons/7/7c/201408_cat.png',
function(error, image) {
if (error) throw error;
map.addImage('cat', image);
map.addSource('point', {
'type': 'geojson',
'generateId': true,
'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,
},
'paint': {
'icon-opacity': [
'case',
['boolean', ['feature-state', 'hover'], false],
1,
0.3
]
}
});
}
);
map.on('mousemove', 'points', function(e) {
if (e.features[0]) {
mouseover(e.features[0]);
} else {
mouseout();
}
});
map.on('mouseout', 'points', function(e) {
mouseout();
});
function mouseover(feature) {
fHover = feature;
map.getCanvasContainer().style.cursor = 'pointer';
map.setFeatureState({
source: 'point',
id: fHover.id
}, {
hover: true
});
}
function mouseout() {
if (!fHover) return;
map.getCanvasContainer().style.cursor = 'default';
map.setFeatureState({
source: 'point',
id: fHover.id
}, {
hover: false
});
fHover = null;
}
});
Important, to change a feature state every feature must have an id in the source, so I strongly recommend to set always 'generateId': true in the addSource method.
PS.- If this answer solves your question, please mark it as answer accepted, in that way it will also help other users to know it was the right solution.

Style is not done loading error: Mapbox GL JS

I have built a website using Mapbox GL JS to display a number of layered routes and points on a map to track different teams progress on a route. However when testing on a large number of page reloads the tracks on the map along with a number of other page elements sometimes don't load and I get a Style is not done loading error.
Code Extract:
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/outdoors-v11',
center: [15, 50],
zoom: 4 // zoom range is 0 (the whole world) to 22 (street level)
});
var distanceContainer = document.getElementById('distance');
// GeoJSON object to hold our measurement features
var geojson = {
'type': 'FeatureCollection',
'features': []
};
var finishPoint = {
'type': 'FeatureCollection',
'features': []
};
var progressLine = {
'type': 'FeatureCollection',
'features': []
};
map.on('load', function () {
var routeLineString = getRoute();
var line = routeLineString;
var options = {units: 'kilometers'};
var endPoint = turf.along(line, turf.length(line), options);
map.addSource('route-source', {
'type': 'geojson',
'data': line
});
// Add styles to the map. Style layer: A style layer ties together the source and image and specifies how they are displayed on the map.
// 'line-color': '#0C7CBB', // dark cyan
// NHS blue: 005EB8
map.addLayer({
'id': 'route',
'type': 'line',
'source': 'route-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#000000',
'line-width': 3
}
},"team1RouteProgress"); // placement of this line below the progress line layer
// destination marker
map.loadImage("https://i.imgur.com/MK4NUzI.png", function (error, image) {
if (error) throw error;
map.addImage("finish-flag", image);
map.addSource('finish-source', {
'type': 'geojson',
'data': finishPoint
});
map.addLayer({
id: 'finish',
type: 'symbol',
source: 'finish-source',
layout: {
"icon-image": "finish-flag",
'icon-anchor': "bottom"
}
});
});
finishPoint.features.push(endPoint);
//map.getSource('geojson').setData(geojson);
});
$(document).ready(function() {
// Clear the Distance container to populate it with a new value
distanceContainer.innerHTML = '';
var line = getRoute();
var options = {units: 'kilometers'};
$.ajax({ //create an ajax request to getProgress.php
type: "GET",
url: "./php/getTeamProgress.php",
dataType: "json", //expect json to be returned
data: {
//access_key: access_key,
},
success: function (response) {
var Teams_arr = [];
var dist_arr = [];
var colour_arr = [];
response.data.forEach(function (dat) {
Teams_arr.push(dat.team);
dist_arr.push(dat.distance);
//Colour Setting
if(dat.team === "Typhoon Squadron"){
colour_arr.push("purple");
}else if (dat.team === "Gloucester Penguins; for Ben"){
colour_arr.push("red");
}else if (dat.team === "Community"){
colour_arr.push("yellow");
}else if(dat.team === "HMS Grimsby"){
colour_arr.push("navy");
}else if(dat.team === "Thunderer Squadron"){
colour_arr.push("blue");
}else{
colour_arr.push("grey");
}
});
//alert(response.totaldist);
var distStart = 0;
//Team 1
var team1DistAlongRoute = dist_arr[0]
var team1Along = turf.along(line, team1DistAlongRoute, options);
if (team1DistAlongRoute<0.1) {
team1DistAlongRoute = 0.1; // prevent error in lineSliceAlong if dist = 0
}
var team1SliceLine = turf.lineSliceAlong(line, distStart, team1DistAlongRoute, {units: 'kilometers'});
map.addSource('team1progress-source', {
'type': 'geojson',
'data': team1SliceLine
});
map.addLayer({
'id': 'team1RouteProgress',
'type': 'line',
'source': 'team1progress-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': colour_arr[0],
'line-width': 5
}
});
// Progress marker- Purple
// Image: An image is loaded and added to the map.
map.loadImage("./assets/black.png", function (error, image) {
if (error) throw error;
map.addImage("team1custom-marker", image);
map.addSource('geojson', {
'type': 'geojson',
'data': geojson
});
map.addLayer({
id: 'team1Progress',
type: 'symbol',
source: 'geojson',
layout: {
"icon-image": "team1custom-marker",
'icon-anchor': "bottom"
}
});
});
geojson.features.push(team1Along);
progressLine.features.push(team1SliceLine);
//Team 2
var team2DistAlongRoute = dist_arr[1]
var team2Along = turf.along(line, team2DistAlongRoute, options);
if (team2DistAlongRoute<0.1) {
team2DistAlongRoute = 0.1; // prevent error in lineSliceAlong if dist = 0
}
var team2SliceLine = turf.lineSliceAlong(line, distStart, team2DistAlongRoute, {units: 'kilometers'});
map.addSource('team2progress-source', {
'type': 'geojson',
'data': team2SliceLine
});
map.addLayer({
'id': 'team2RouteProgress',
'type': 'line',
'source': 'team2progress-source',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': colour_arr[1],
'line-width': 5
}
});
geojson.features.push(team2Along);
progressLine.features.push(team2SliceLine);
etc etc
The quick fix:
map = new Mapboxgl.map({ ... })
map.on('load', () => {
...add all your `addSource()`, `addLayer()` etc here.
});
The 'Style is not done loading' error is thrown by the _checkLoaded method in Mapbox GL JS's style.js. This method is called each time a modification is made to the style -- for example when the Map#addSource and Map#addLayer methods are called. In addition to making use of the map#on('load', function() {}) listener to ensure that all necessary map resources are loaded before attempting to make a modification to the map's style, you could also take a look at some of these examples from our documentation, which demonstrate strategies for updating map sources and layers dynamically:
Update a feature in realtime.
Add live realtime data.
Using the GeoJSONSource#setData method to update an existing source's data and re-render the map.
Change a layer's color with buttons.

How to display database values on mapbox?

I am using Mapbox and I created a database with values like (longitude and latitude), I want to display the data in the form of polygon on my map.
As you can see I want to put the the longitude and latitude from my database to the coordinates on my code but I don't know how to retrieve it from my database.
map.on('load', function() {
map.addSource('maine', {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [
[]
]
}
}
});
map.addLayer({
'id': 'maine',
'type': 'fill',
'source': 'maine',
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.8
}
});
});
this is my database which is in the file api.php:
I don't know how to retrieve it from your database either, because you haven't provided any details. But generally speaking, your approach will be:
provide an API that provides access to the data in some form (such as CSV) via URL (example.com/mydatabase/points.csv)
fetch the CSV in the client, and transform it to GeoJSON. (If your back end can generate GeoJSON directly, you can save this step)
add it to the Mapbox map as a GeoJSON source layer

Categories