Mapbox-gl line layer from source as fill layer - javascript

Mapbox GL JS w/ w3w grid
Hi, I'm just playing around with the What3Words grid on Mapbox code from the tutorial. (https://developer.what3words.com/tutorial/displaying-the-what3words-grid-on-a-mapbox-map)
I'm trying to make the tiles from the grid interactive, kind of like in the w3w website (clickable, hover effect, getting data from them, etc), but the grid doesn't seem to work when the data source is loaded as a 'fill' layer on Mapbox, it only works as a 'line' layer type. Every single example I find online uses Polygons (or MultiPolygons) from a fill layer type, but I can't see nothing around with bounding boxes.
(Basically trying to achieve something like this, but with every tile instead of the states: https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/)
I don't really know what's going on, why can't I add the source data as a fill layer? Is there a way to load the data as Polygons instead of bounding boxes?
Thanks.
Code (from the tutorial):
<html>
<head>
<script src="https://assets.what3words.com/sdk/v3.1/what3words.js?key=YOUR_API_KEY"></script>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.js"></script>
<link href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.0/mapbox-gl.css" rel="stylesheet" />
<style>
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// Create the Mapbox
mapboxgl.accessToken = "YOUR_MAPBOX_TOKEN";
let map = new mapboxgl.Map({
container: "map", // container id
style: "mapbox://styles/mapbox/streets-v9", // stylesheet location
center: [-0.195499, 51.52086], // starting position [lng, lat]
zoom: 18 // starting zoom
});
map.addControl(new mapboxgl.NavigationControl());
</script>
<script>
function drawGrid() {
const zoom = map.getZoom();
const loadFeatures = zoom > 17;
if (loadFeatures) { // Zoom level is high enough
var ne = map.getBounds().getNorthEast();
var sw = map.getBounds().getSouthWest();
// Call the what3words Grid API to obtain the grid squares within the current visble bounding box
what3words.api
.gridSectionGeoJson({
southwest: {
lat: sw.lat, lng: sw.lng
},
northeast: {
lat: ne.lat, lng: ne.lng
}
}).then(function(data) {
// Get the grid source from the map (it won't exist initally)
var grid = map.getSource('grid');
if (grid === undefined) {
// Create a source of type 'geojson' which loads the GeoJSON returned from the what3words API
map.addSource('grid', {
type: 'geojson',
data: data
});
// Create a new layer, which loads data from the newly created data source
map.addLayer({
id: 'grid_layer',
type: "line",
source: 'grid',
layout: {
"line-join": "round",
"line-cap": "round"
},
paint: {
"line-color": '#777',
"line-width": .5
}
});
} else {
// The source and map layer already exist, so just update the source data to be the new
// GeoJSON returned from the what3words API
map.getSource('grid').setData(data);
}
}).catch(console.error);
}
// If we have reached the required zoom level, set the 'grid_layer' to be visible
var grid_layer = map.getLayer('grid_layer');
if (typeof grid_layer !== 'undefined') {
map.setLayoutProperty('grid_layer', 'visibility', loadFeatures ? 'visible' : 'none');
}
}
// When the map is either loaded or moved, check to see if the grid should be draw
// if the appropriate zoom level has been met, and if so draw it on.
map
.on('load', drawGrid)
.on('move', drawGrid);
</script>
</body>
</html>

Related

Highlight feature with effects and blending in Arcgis JavaScript Api

I am using this sample, which uses layer blending and effect on multiple layers to create a more artistic way of highlighting features selected by the user. When the application loads, the basemap is displayed over all other layers in the map. When a user clicks on a country, the country will be highlighted.
The light gray canvas vector tiles layer and a countries feature layer are added to a group layer and the destination-over blendMode is set on the group layer. With the destination-over blendMode, the background layer covers, or is placed over, the top layer. The content of the top layer is visible where both layers do not overlap. When the app loads, you see the modern antique vector tiles basemap on top of the groupLayer.
My question is, is there a way to do the same with Feature Access instead of using portal ID.
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>
Highlight feature with effects and blending | Sample | ArcGIS API for
JavaScript 4.21
</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#messageDiv {
padding-left: 10px;
padding-right: 10px;
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.21/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.21/"></script>
<script>
require([
"esri/WebMap",
"esri/views/MapView",
"esri/layers/VectorTileLayer",
"esri/Graphic",
"esri/layers/FeatureLayer",
"esri/layers/GraphicsLayer",
"esri/layers/GroupLayer"
], (
WebMap,
MapView,
VectorTileLayer,
Graphic,
FeatureLayer,
GraphicsLayer,
GroupLayer
) => {
const map = new WebMap({
basemap: {
portalItem: {
id: "f35ef07c9ed24020aadd65c8a65d3754" // modern antique vector tiles
}
}
});
const vtLayer = new VectorTileLayer({
portalItem: {
id: "2afe5b807fa74006be6363fd243ffb30" // gray vector tiles canvas
}
});
const countries = new FeatureLayer({
portalItem: {
id: "53a1e68de7e4499cad77c80daba46a94" // boundaries of countries
}
});
// This group layer groups the gray canvas vector tiles and
// countries feature layer.
// With destination-over blendMode, the background layer covers
// the top layer. The top layer is put behind the destination layer.
// So when the app starts, the basemap layer will be shown over this layer
const groupLayer = new GroupLayer({
layers: [vtLayer, countries],
blendMode: "destination-over"
});
map.add(groupLayer);
const view = new MapView({
container: "viewDiv",
map: map,
zoom: 6,
center: [2, 46],
constraints: {
snapToZoom: false,
minScale: 147914381
}
});
let layerView, animation;
// countryGraphicsLayer is added to the view's basemap.
// It will contain black polygon covering the extent of the world
// the country graphic will also be added to this layer when user clicks a country.
// With destination-in blend mode, the contents of background layer is
// kept where it overlaps with top layer. Everything else is made transparent.
// In this case, the countryGraphicsLayer will be displayed underneath
// modern antique vector tiles basemap.
// The bloom effect will add a glow around the selected country.
const countryGraphicsLayer = new GraphicsLayer({
blendMode: "destination-in",
effect: "bloom(200%)"
});
map.loadAll().then(async () => {
addWorld();
map.basemap.baseLayers.getItemAt(1).blendMode = "multiply";
// add the buffer graphicslayer to the basemap
map.basemap.baseLayers.add(countryGraphicsLayer);
// get a reference ot the countries featurelayer's layerview
// layerview will be queried to get the intersecting country
// when user clicks on the map
layerView = await view.whenLayerView(countries);
});
view.ui.add("messageDiv", "top-right");
const symbol = {
type: "simple-fill",
color: "white",
outline: null
};
// listen to the view's click event
view.on("click", async (event) => {
// query the countries featurelayer for a country that intersects the point
// user clicked on
const {
features: [feature]
} = await layerView.queryFeatures({
geometry: view.toMap(event),
returnGeometry: true,
maxAllowableOffset: 10000,
outFields: ["*"]
});
countryGraphicsLayer.graphics.removeAll();
animation && animation.remove();
let world = addWorld();
// add the clicked country feature to the graphicslayer
if (feature) {
feature.symbol = symbol;
countryGraphicsLayer.graphics.add(feature);
// add a fade animation to show the highlight effect
// for the selected country
animation = fadeWorld(world);
// zoom to the highlighted country
view.goTo(
{
target: view.toMap(event),
extent: feature.geometry.extent.clone().expand(1.8)
},
{ duration: 1000 }
);
}
});
function addWorld(world) {
world = new Graphic({
geometry: {
type: "extent",
xmin: -180,
xmax: 180,
ymin: -90,
ymax: 90
},
symbol: {
type: "simple-fill",
color: "rgba(0, 0, 0, 1)",
outline: null
}
});
countryGraphicsLayer.graphics.add(world);
return world;
}
// add a fading animation when user clicks on a country
function fadeWorld(world) {
let timer;
// requestAnimationFrame method specifies "frame" function
// to perform an animation where the opacity of the world polygon graphic
// decreased by 0.1 until it is 0 or completely transparent
// then the animation is cancelled
function frame() {
const symbol = world.symbol.clone();
symbol.color.a = Math.max(0, symbol.color.a - 0.1);
world.symbol = symbol;
if (symbol.color.a > 0) {
timer = requestAnimationFrame(frame);
}
}
frame();
return {
remove() {
cancelAnimationFrame(timer);
}
};
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="messageDiv" class="esri-widget esri-heading">
<h4 class="esri-heading">Click on a country</h4>
</div>
</body>
</html>

Azure Maps SDK popup does not open after initial load

I am using Azure Maps SDK in a Blazor Server app which uses JavaScript interop. The map works well and renders perfectly.
On initial load of the map component the popup displays correctly when clicking a pin on the map. However, when the data source changes via an API call datasource.importDataFromUrl(organisationDataFeed); it correctly brings back the filtered data, but when I click on one of the pins again the popup does not display. The click event handler (for the pin) is being called, but the popup does not open.
(function () {
var map, datasource, popup;
// Global export
window.azuremaps = {
initMap: function (organisationDataFeed) {
// Create an instance of the map control and set some options.
map = new atlas.Map('map', {
// center: [-97, 39],
// center: [18.424095, -33.925000],
center: [26.157981, -29.083937],
zoom: 4,
pitch: 50,
style: 'night',
view: 'Auto',
// Add your Azure Maps subscription key to the map SDK. Get an Azure Maps key at https://azure.com/maps
authOptions: {
authType: 'subscriptionKey',
subscriptionKey: ''
}
});
map.controls.add([
new atlas.control.ZoomControl(),
new atlas.control.CompassControl(),
new atlas.control.PitchControl(),
new atlas.control.StyleControl()
], {
position: "top-right"
});
// Wait until the map resources are ready.
map.events.add('ready', function () {
// Create a data source and add it to the map.
datasource = new atlas.source.DataSource(null, {
//Tell the data source to cluster point data.
cluster: true,
//The radius in pixels to cluster points together.
clusterRadius: 45,
// The maximium zoom level in which clustering occurs.
// If you zoom in more than this, all points are rendered as symbols.
clusterMaxZoom: 15
});
map.sources.add(datasource);
//Create a bubble layer for rendering clustered data points.
var clusterBubbleLayer = new atlas.layer.BubbleLayer(datasource, null, {
//Scale the size of the clustered bubble based on the number of points inthe cluster.
radius: [
'step',
['get', 'point_count'],
20, //Default of 20 pixel radius.
100, 30, //If point_count >= 100, radius is 30 pixels.
750, 40 //If point_count >= 750, radius is 40 pixels.
],
//Change the color of the cluster based on the value on the point_cluster property of the cluster.
color: [
'step',
['get', 'point_count'],
'rgba(0,255,0,0.8)', //Default to green.
100, 'rgba(255,255,0,0.8)', //If the point_count >= 100, color is yellow.
750, 'rgba(255,0,0,0.8)' //If the point_count >= 100, color is red.
],
strokeWidth: 0,
filter: ['has', 'point_count'] //Only rendered data points which have a point_count property, which clusters do.
});
//Add a click event to the layer so we can zoom in when a user clicks a cluster.
map.events.add('click', clusterBubbleLayer, clusterClicked);
//Add mouse events to change the mouse cursor when hovering over a cluster.
map.events.add('mouseenter', clusterBubbleLayer, function () {
map.getCanvasContainer().style.cursor = 'pointer';
});
map.events.add('mouseleave', clusterBubbleLayer, function () {
map.getCanvasContainer().style.cursor = 'grab';
});
// Create a layer to render the individual locations.
var individualSymbolLayer = new atlas.layer.SymbolLayer(datasource, null, {
filter: ['!', ['has', 'point_count']], //Filter out clustered points from this layer.
textOptions: {
textField: ['get', 'name'],
color: "#FFFFFF",
offset: [0, -2.2]
},
});
map.events.add('click', individualSymbolLayer, symbolClicked);
//Add the clusterBubbleLayer and two additional layers to the map.
map.layers.add([
clusterBubbleLayer,
// Create a symbol layer to render the count of locations in a cluster.
new atlas.layer.SymbolLayer(datasource, null, {
iconOptions: {
image: 'none' //Hide the icon image.
},
textOptions: {
textField: ['get', 'point_count_abbreviated'],
offset: [0, 0.4]
}
}),
individualSymbolLayer
]);
// Create a popup but leave it closed so we can update it and display it later.
popup = new atlas.Popup({
pixelOffset: [0, -18],
closeButton: true
});
// Retrieve a GeoJSON data set and add it to the data source.
datasource.importDataFromUrl(organisationDataFeed);
});
},
};
function clusterClicked(e) {
if (e && e.shapes && e.shapes.length > 0 && e.shapes[0].properties.cluster) {
// Get the clustered point from the event.
var cluster = e.shapes[0];
// Get the cluster expansion zoom level. This is the zoom level at which the cluster starts to break apart.
datasource.getClusterExpansionZoom(cluster.properties.cluster_id).then(function (zoom) {
//Update the map camera to be centered over the cluster.
map.setCamera({
center: cluster.geometry.coordinates,
zoom: zoom + 2,
type: 'ease',
duration: 200
});
});
}
}
function symbolClicked(e) {
// Make sure the event occured on a point feature.
var popupTemplate = '<div class="card border-success" style="visibility: visible"><div class="card-header" style="visibility: visible">{name}</div><div class="card-body text-success" style="visibility: visible"><h5 class="card-title" style="visibility: visible">{description}</h5><p class="card-text" style="visibility: visible">Contact: {contact}</p><p class="card-text">Web: {website}</p></div></div>';
if (e.shapes && e.shapes.length > 0) {
var content, coordinate;
// Check to see if the first value in the shapes array is a Point Shape.
if (e.shapes[0] instanceof atlas.Shape && e.shapes[0].getType() === 'Point') {
var properties = e.shapes[0].getProperties();
content = popupTemplate.replace(/{name}/g, properties.name).replace(/{description}/g, properties.description).replace(/{contact}/g, properties.contact).replace(/{website}/g, properties.website);
coordinate = e.shapes[0].getCoordinates();
}
else if (e.shapes[0].type === 'Feature' && e.shapes[0].geometry.type === 'Point') {
// Check to see if the feature is a cluster.
if (e.shapes[0].properties.cluster) {
content = '<div style="padding:10px;">Cluster of ' + e.shapes[0].properties.point_count + ' symbols</div>';
} else {
// Feature is likely from a VectorTileSource.
content = popupTemplate.replace(/{name}/g, properties.name).replace(/{description}/g, properties.description).replace(/{contact}/g, properties.contact).replace(/{website}/g, properties.website);
}
coordinate = e.shapes[0].geometry.coordinates;
}
if (content && coordinate) {
// Populate the popupTemplate with data from the clicked point feature.
console.log("JB content");
console.log(content);
console.log("JB coordinate");
console.log(coordinate);
popup.setOptions({
//Update the content of the popup.
content: content,
//Update the position of the popup with the symbols coordinate.
position: coordinate
});
console.log("JB: logging map variable");
console.log(map);
popup.open(map);
}
}
}
})();
content and coordinate are populated with values and evaluate to true, the options are correctly set, but just the last line: popup.open(map); does not work if the data source changes bringing back new data into the map. It works perfectly on initial load.
Any ideas what I could do to get this working? Thanks
A couple of things to try:
Double check the values of coordinate and content. The coordinates should be an array with [longitude,latitude] numbers (make sure they aren't string values of numbers). The content should either be a DOMElement (i.e. div), or a string. If it is anything else, it may not display anything.
Double check the "map" variable is a map the second time around. If the reference is lost for some reason, that function won't work.
It looks like you are create a new popup all the time. Often apps only want to display a single popup at a time. In that case it is more efficient to create a single popup and reuse it as shown in this example: https://azuremapscodesamples.azurewebsites.net/index.html?sample=Reusing%20Popup%20with%20Multiple%20Pins

ArcGIS highlight a graphic

in my program I have created polygons on a graphicsLayer from coordinates on my mapView. When I hover over a polygon, I want to highlight that polygon with orange color.
require([
"esri/Map",
"esri/views/MapView",
"esri/Graphic",
"esri/layers/GraphicsLayer",
"esri/widgets/Search",
"dojo/dom",
"esri/renderers/UniqueValueRenderer",
"esri/symbols/SimpleLineSymbol"
], function (Map, MapView, Graphic, GraphicsLayer, Search, dom, UniqueValueRenderer, SimpleLineSymbol) {
map = new Map({
basemap: "streets-navigation-vector"
});
var center = app.addresses[0].center;
view = new MapView({
container: "viewDiv",
map: map,
center: center, // longitude, latitude
zoom: 15,
highlightOptions: {
color: "orange"
}
});
var search = new Search({
view: view,
container: "search"
});
graphicsLayer = new GraphicsLayer();
map.add(graphicsLayer);
var i = 0;
app.addresses.forEach(function (address) {
var polygon = {
type: "polygon",
rings: address.polygon_rings
};
address.polygonGraphic = new Graphic({
geometry: polygon,
symbol: address.parking_zone
});
graphicsLayer.add(address.polygonGraphic);
i++;
});
view.on("click", function(event){
var screenPoint = {
x: event.x,
y: event.y
};
view.hitTest(screenPoint).then(function (response) {
if (response.results.length) {
var graphic = response.results.filter(function (result) {
// check if the graphic belongs to the layer of interest
return result.graphic.layer === graphicsLayer;
})[0].graphic;
// do something with the result graphic
ToggleSelectedAddress(parseInt(graphic.uid));
}
});
});
view
.when()
.then(function() {
return graphicsLayer.when();
})
.then(function(layer) {
// Set up an event handler for pointer-down (mobile)
// and pointer-move events (mouse)
// and retrieve the screen x, y coordinates
return view.whenLayerView(layer);
})
.then(function(layerView) {
view.on("pointer-move", eventHandler);
view.on("pointer-down", eventHandler);
function eventHandler(event) {
// the hitTest() checks to see if any graphics in the view
// intersect the x, y coordinates of the pointer
view.hitTest(event).then(getGraphics);
}
let highlight;
function getGraphics(response) {
// the topmost graphic from the hurricanesLayer
// and display select attribute values from the
// graphic to the user
if (response.results.length) {
const graphic = response.results.filter(function(result) {
if(result.graphic.layer === graphicsLayer){
layerView.highlight(result.graphic);
}
return result.graphic.layer === graphicsLayer;
})[0].graphic;
if(highlight) {
console.log("highlighted");
highlight.remove();
}
// highlight all features belonging to the same hurricane as the feature
// returned from the hitTest
highlight = layerView.highlight(graphic);
} else {
// remove the highlight if no features are
// returned from the hitTest
highlight.remove();
highlight = null;
}
}
});
});
I even tried creating functions that change the symbol on those polygon graphics, but to no success.
I think, that the else statement at the end is never executed. Which means, that the hitTest almost always returns a result. I debugged this a little and I saw, that the results are streets, buildings and such. After filtering those results I get the correct graphics I wish to highlight. However, layerView.highlight(graphic) doesn't work. This is how I create my mapView:
view = new MapView({
container: "viewDiv",
map: map,
center: center, // longitude, latitude
zoom: 15,
highlightOptions: {
color: "orange"
}
});
When I take the hovered graphic and change its' symbol to orange, it gets highlighted, but doesn't get unhighlighted once I unhover the element (since the else{} statement is almost never executed). Can anyone explain why hitTest always returns a result and why .highlight() doesn't work?
You problem is that you are highlighting when highlight is null, that is the first hit at start or after no feature is hit. You need to change,
if (highlight) {
return;
}
for
if (highlight) {
highlight.remove();
}
That means, if there is a feature highlighted, then remove it. Then highlight the new hit feature.
hitTest
returns the topmost feature from each layer that intersects the
specified screen coordinates
(ArcGIS Doc - MapView hitTest).
Here is the code modification from the ArcGIS example,
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>Access features with pointer events - 4.14</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.14/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.14/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer"
], function(Map, MapView, FeatureLayer) {
const hurricanesLayer = new FeatureLayer({
url:
"https://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/1",
outFields: ["*"]
});
const map = new Map({
basemap: "dark-gray",
layers: [hurricanesLayer]
});
const view = new MapView({
container: "viewDiv",
map: map,
center: [-61.125537, 35.863534],
zoom: 4,
highlightOptions: {
color: "orange"
}
});
view
.when()
.then(function() {
return hurricanesLayer.when();
})
.then(function(layer) {
const renderer = layer.renderer.clone();
renderer.symbol.width = 4;
renderer.symbol.color = [128, 128, 128, 0.8];
layer.renderer = renderer;
// Set up an event handler for pointer-down (mobile)
// and pointer-move events (mouse)
// and retrieve the screen x, y coordinates
return view.whenLayerView(layer);
})
.then(function(layerView) {
view.on("pointer-move", eventHandler);
view.on("pointer-down", eventHandler);
function eventHandler(event) {
// the hitTest() checks to see if any graphics in the view
// intersect the x, y coordinates of the pointer
view.hitTest(event).then(getGraphics);
}
let highlight, currentYear, currentName;
function getGraphics(response) {
// the topmost graphic from the hurricanesLayer
if (response.results.length) {
const features = response.results.filter(function(result) {
return result.graphic.layer === hurricanesLayer;
});
if (features.length) {
const graphic = features[0].graphic;
if (highlight) {
highlight.remove();
}
highlight = layerView.highlight(graphic);
return;
}
}
// remove the highlight if no features, of the layer, are
// returned from the hitTest
highlight.remove();
highlight = null;
}
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>

MapBox draw Polygon of Feature on Click

So my idea seems pretty straight forward to me but I struggle nevertheless. What I want to do is basically click on any point of my map and draw a polygon on the main feature, i.e. if I click on a park or a building that specific polygon is displayed and highlighted.
I used a lot of this code: https://www.mapbox.com/mapbox-gl-js/example/queryrenderedfeatures-around-point/
But instead of giving it a set of geojson I want my javascript to select to needed geojson data on mousover (eventhough i am not sure whether that works in general). Right now my code snipped compiles but doesn't show anything.
In a later step I want to collect all polygons of the same feature, i.e. all parks, and display them as highlighted polygons and then export them as a svg file which only consists of the map representations of the feature clicked on. Maybe someone has an idea for that as well?
Thanks in regard :)
This is my javascript as of now:
//Set AccessToken from MapBox
mapboxgl.accessToken = 'pk.eyJ1IjoidG1pbGRuZXIiLCJhIjoiY2o1NmlmNWVnMG5rNzMzcjB5bnV3YTlnbiJ9.r0BCga0qhRaHh0CnDdcGBQ';
//Setup starting view point at Uni-Bremen campus
var map = new mapboxgl.Map({
container: 'content-map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [8.85307, 53.10810],
zoom: 16
});
//Add a search bar -> hidden for presentation
/*map.addControl(new MapboxGeocoder({
accessToken: mapboxgl.accessToken
}));*/
//Function to show all Features of a certian point
map.on('mousemove', function (e) {
var features = map.queryRenderedFeatures(e.point);
document.getElementById('features').innerHTML = JSON.stringify(features, null, 2);
console.log(JSON.stringify(features, null, 2));
drawPolygon();
});
//Draw a Polygon
function drawPolygon () {
//set boundary box as 5px rectangle area around clicked point
var bbox = [[e.point.x - 5, e.point.y - 5], [e.pont.x + 5, e.point.y + 5]];
//set the data on pointer using the bbox
var data = map.queryRenderedFeatures(bbox);
map.on('load', function() {
var dataSource = 'school';
//set school to the feature and use 'setJsonData' as data source.
map.addSource(dataSource, {
'type': 'geojson',
'data': data
});
//adding a new layer for the general display
map.addLayer({
'id': 'dataSet',
'type': 'fill',
'source': dataSource,
'source-layer': 'original',
'paint': {
'fill-outline-color': 'rgba(0,0,0,0.1)',
'fill-color': 'rgba(0,0,0,0.1)'
}
}, 'place-city-sm' ); //place polygon under these labels
//adding a new layer for the polygon to be drawn
map.addLeyer({
'id': 'dataSet-highlighted',
'type': 'fill',
'source': dataSource,
'source-layer': 'original',
'paint': {
'fill-outline-color': '#484896',
'fill-color': '#6e599f',
'fill-opacity': 0.75
},
'filter': ['in', 'FIPS', '']
}, 'place-city-sm'); //place polygon under these labels
//action on click to show the polygon and change their color
map.on('click', function (e) {
//retrieve data from 'dataSource'
var dataFromSource = map.queryRenderedFeatures(bbox, {layers: ['dataSource'] });
// Run through the selected features and set a filter
// to match features with unique FIPS codes to activate
// the `counties-highlighted` layer.
var filter = dataSource.reduce(function(memo, dataSource) {
memo.push(dataSource, properties.FIPS);
return memo;
} ['in', 'FIPS'] );
map.setFilter('dataSet-highlighted', filter);
});
});
}
I'm not 100% sure what you're asking, but my interpretation is that you'd like to specifically style certain types of geometry when you hover over them, such as "parks". You're on the right path, where using map.queryRenderedFeatures() is great. I've put together an example using the same Mapbox Streets style that queries only the building layer and looks for type university on mouseover.
When the interaction encounters a proper feature, it updates the source data with the new feature, which then updates the school-hover layer.
Check out the pen here: https://codepen.io/mapsam/pen/oemqKb
In a later step I want to collect all polygons of the same feature, i.e. all parks, and display them as highlighted polygons and then export them as a svg file which only consists of the map representations of the feature clicked on.
I won't go into file exports, but just remember that all results returned from map.queryRenderedFeatures are specific to the single vector tile you are querying, which can lead to issues on tile boundaries where a polygon isn't fully covered by your current query.
Check out this example where we are highlighting features with similar data, which should allow you get all of the necessary geometries and export to SVG.
Cheers!

How to draw circle or rectangle on tile images leaflet.js

i know it is possible to draw circle or rectangle on tile images when using leaflet.js. here is one link http://jsfiddle.net/tridip/p6ssbvqj/
leaflet has function called circle() polygon() etc
my interface is like that i have 4 button one is circle,rectangle,load image, save image.
when page will load first time then i will show a image by leaflet.js and when user click on circle or rectangle button then i have to allow user to draw a circle or rectangle on image. the question which jquery or any javascript library i should use which will allow to draw a circle or rectangle on image ?
next i need to store those coordinate of circle or rectangle at client side because later i could save those info in db.
3rd one when i will reload images again i need to show drawn circle or rectangle on same image and in same location where user has drawn.
please guide me how to achieve it. i have never done before so no idea i have. please help me. thanks
EDIT 1
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
1) What is the meaning of L.FeatureGroup()?
What does L.FeatureGroup() do?
2) What does the code below do?
var drawControl = new L.Control.Draw({
draw: {
position: 'topleft',
polygon: {
allowIntersection: false,
drawError: {
color: '#b00b00',
timeout: 1000
},
shapeOptions: {
color: '#bada55'
},
showArea: true
},
polyline: {
metric: false
},
circle: {
shapeOptions: {
color: '#662d91'
}
}
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
what the above code is doing. it seems that the above code is trying to draw control on map programmatically. may be i am not right.
because if the above line is related to draw control on map programmatically then there should be coordinate or something relavent should be there but i have not
found anything in the above code. so please tell me what the above code is doing ?
3) if i need to draw any shape on map then do i need to first add any layer on map because as per your code you first add layer by this line
map.addLayer(drawnItems);
4) the below code is clear
if (type === 'marker') {
coords = JSON.stringify(layer._latlng);
}
the above code storing lat and lang as coordinate in variable but you have missded to show another set of code where i will provide
lat and lang details as coordinate then code will draw same shape in right position as per lat & lang value.
please read my all 4 point and please write answer to drive out my confusion. specially point 1 & 2 related code is not clear to me
and next give me code where i will pass shape name and latlang then leaflet api will draw shape accordingly.
thanks
As gusper noted, Leaflet.draw is a ready-made library for interactive drawing on Leaflet maps. Here's their demo slightly modified to display the coordinates of shapes drawn on the map.
If necessary, you can store these coordinates in a DB, and then use the native Leaflet functions to re-draw these shapes from the list of coordinates.
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© OpenStreetMap contributors',
osm = L.tileLayer(osmUrl, {
maxZoom: 18,
attribution: osmAttrib
}),
map = new L.Map('map', {
layers: [osm],
center: new L.LatLng(-37.7772, 175.2756),
zoom: 15
});
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
draw: {
position: 'topleft',
polygon: {
allowIntersection: false,
drawError: {
color: '#b00b00',
timeout: 1000
},
shapeOptions: {
color: '#bada55'
},
showArea: true
},
polyline: {
metric: false
},
circle: {
shapeOptions: {
color: '#662d91'
}
}
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function(e) {
var type = e.layerType;
var layer = e.layer;
var coords;
console.log(e);
if (type === 'marker') {
coords = JSON.stringify(layer._latlng);
}
if (type === 'circle') {
coords = JSON.stringify(layer._latlng) + " " + layer._mRadius;
}
if (type === 'rectangle') {
coords = JSON.stringify(layer._latlngs);
}
if (type === 'polygon') {
coords = JSON.stringify(layer._latlngs);
}
if (type === 'polyline') {
coords = JSON.stringify(layer._latlngs);
}
document.getElementById("coords").innerHTML = coords;
drawnItems.addLayer(layer);
});
<head>
<title>Leaflet Draw</title>
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/lib/leaflet/leaflet.css" />
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="lib/leaflet/leaflet.ie.css" />
<link rel="stylesheet" href="leaflet.draw.ie.css" />
<![endif]-->
<script src="http://leaflet.github.io/Leaflet.draw/lib/leaflet/leaflet.js"></script>
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script>
</head>
<body>
<div id="map" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>
<div id="coords" style="position: fixed; bottom: 0; right: 0; width: 50%; height: 20%; z-index: 99; background-color: white; text-wrap: "></div>

Categories

Resources