Related
I have a web app I am creating that maps 30k+ symbols, but my issue is that on initialization, the map.on('load') is way too slow. I've added timers all throughout my code and it is specifically this line of code that takes 10+ seconds to run.
I've looked through all of Mapbox's resources on performance but none of the suggestions work or are relevant, (e.g., minimize the amount of layers used even though my layers take 0.1 seconds to load, using vector tiles instead of GeoJSON data, changing the properties of the data source, minimizing features etc.).
Has anyone else had this issue and figured out how to resolve it? I've been trying for days to optimize this map but I can't figure it out!
// Set map properties with the predefined configuration
mapboxgl.accessToken = 'my access token';
const map = new mapboxgl.Map({
container: 'fullMap',
style: 'mapbox://styles/mapbox/light-v10',
center: [-100.83, 39.22],
zoom: 5,
});
startTime = performance.now();
map.on('load', () => {
endTime = performance.now(); // Ends up being 10+ seconds...
map.addSource('gc_offices', {
type: 'geojson',
data: geojsonData,
buffer: 0,
cluster: true,
clusterMaxZoom: 12, // Max zoom to cluster points on
clusterRadius: 50, // Radius of each cluster when clustering points
});
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'gc_offices',
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#2288f2',
5, '#3d97f5',
25, '#64abf5',
50, '#83bcf7',
100, '#a3ccf7',
250, '#cfe4fa',
500, '#eb4034'
],
'circle-radius': [
'step',
['get', 'point_count'],
13,
20, 18,
50, 25,
100, 30,
250, 40,
500, 50,
]
}
});
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'gc_offices',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12,
'text-allow-overlap': true
}
});
map.addLayer({
id: 'locationData',
type: 'symbol',
source: 'gc_offices',
minzoom: 10,
layout: gc_office_layout_properties,
paint: gc_office_paint_properties,
});
});
we are experimenting with harp gl to replace a custom building tool for geojson. I want to visualize my polygons with the extruded polygon technique. Coming from MapBox GL I was able to have two properties on my geojson feature.properties called height and base height. So I have Some GeoJson that I'm using for testing. I cannot seem to get the extruded polygon to show up. I am writing this in Javascript, using the https://unpkg.com/##here/harp.gl/dist/harp.js source. I am able to render points on my map. Just not these polygons. (ignore the walls for now, I want to see the floors first)
What I would like to achieve (This is how it looks in MapBox):
function for reseting building data:
resetBuildings: function (data) {
const dataProvider = new harp.GeoJsonDataProvider("buildings", data);
var geoJsonBuildingDataSource;
if (this.mapView.getDataSourceByName("buildings") == null) {
geoJsonBuildingDataSource = new harp.VectorTileDataSource({
dataProvider,
name: "buildings",
styleSetName: "geojson",
});
this.mapView.addDataSource(geoJsonBuildingDataSource);
}
else { geoJsonBuildingDataSource = this.mapView.getDataSourceByName("buildings") }
const theme = {
styles: {
geojson: this.getStyleSet()
},
};
geoJsonBuildingDataSource.setTheme(theme);
My StyleSet definition(?):
getStyleSet: function(){
return [
{
when: ["==", ["geometry-type"], "Polygon"],
technique: "extruded",
renderOrder: 1000,
constantHeight: true,
color: "#FF0000",
transparent: false,
opacity: 0.8,
lineWidth: 1,
lineColor: "#003344",
height: ["number", ["get", "base_height"], 10],
floorHeight: ["number", ["get", "base_height"], 0]
}
];
}
My test GeoJson:
{"type":"FeatureCollection","features":[{"id":"6223a3f0-2835-4ad8-8456-3ccbefc0d19c","type":"Feature","properties":{"level":1,"name":"base","height":0.2,"base_height":0,"color":"grey","message":"","coordinates":null},"geometry":{"type":"Polygon","coordinates":[[[174.81047388530345,-36.909499151794726],[174.81050337403076,-36.90965937993286],[174.8107788255581,-36.909634193592254],[174.8107567090126,-36.9094734295196],[174.81047388530345,-36.909499151794726]]]}},{"id":"a0583d91-f038-4d6e-8e32-99db806982bc","type":"Feature","properties":{"level":0,"name":"Wall","height":5,"base_height":0.2,"color":"blue","message":"","coordinates":null},"geometry":{"type":"LineString","coordinates":[[174.8104752256997,-36.909499687675165],[174.81075804940883,-36.90947396540121],[174.81078083615245,-36.90963526535126],[174.81050337403076,-36.909658844053574],[174.81049399125453,-36.909598825525144],[174.81050337403076,-36.909658844053574],[174.81078083615245,-36.90963526535126],[174.81075804940883,-36.90947396540121],[174.8104752256997,-36.909499687675165]]}},{"id":"ec219095-8891-440b-9fea-2db9cf74c7e0","type":"Feature","properties":{"level":0,"name":"Wall","height":5,"base_height":0.2,"color":"blue","message":"","coordinates":null},"geometry":{"type":"LineString","coordinates":[[174.8104752256997,-36.90950022355558],[174.81048594887454,-36.909558634520565],[174.8104752256997,-36.90950022355558]]}},{"id":"628a2754-c1b6-43a4-975b-eb45084d3853","type":"Feature","properties":{"level":1,"name":"base","height":0.2,"base_height":0,"color":"grey","message":"","coordinates":null},"geometry":{"type":"Polygon","coordinates":[[[174.8106367829821,-36.909648805405354],[174.81078191913713,-36.90963279956284],[174.81078129891483,-36.909646601064004],[174.81085540198825,-36.90961396183521],[174.81089173813234,-36.90967024159831],[174.81086324249378,-36.90968392941179],[174.8108926788683,-36.909721493301554],[174.8109264286474,-36.90971335389515],[174.81098518665493,-36.909814312925626],[174.81076392360183,-36.90990054860199],[174.81063578104482,-36.90969921056885],[174.8106354709525,-36.909651008673336],[174.8106367829821,-36.909648805405354]]]}},{"id":"d5a1c90d-295e-463b-9530-7643e411ca38","type":"Feature","properties":{"level":0,"name":"Wall","height":5,"base_height":0.2,"color":"blue","message":"","coordinates":null},"geometry":{"type":"LineString","coordinates":[[174.81063687126533,-36.909700434854784],[174.81076318373476,-36.90989889886059],[174.8109779359279,-36.90981381148965],[174.81076318373476,-36.90989889886059],[174.81063687126533,-36.909700434854784]]}}]}
Thank you in advance!
I figured it out. I was using the wrong kind of data source. I needed to use a FeaturesDataSource. My style set definition was also wrong, and I was not setting it to the map theme.
How I now initialize map:
this.styleSet = this.getStyleSet();
hereMapsHelper.geoJsonObj = JSON.parse(GeoJson);
const canvas = document.getElementById(mapContainerId);
var options = {
canvas,
theme: {
extends: "https://unpkg.com/#here/harp-map-theme#latest/resources/berlin_tilezen_base.json",
styles: {
geojson: this.styleSet,
}
}
}
var map = new harp.MapView(options);
function for buildings:
resetBuildings: function (data) {
var geoJsonBuildingDataSource;
if (this.mapView.getDataSourceByName("buildings") != null) {
var existingBuildingDataSource = this.mapView.getDataSourceByName("buildings")
this.mapView.removeDataSource(existingBuildingDataSource);
}
geoJsonBuildingDataSource = new harp.FeaturesDataSource({
geojson: data,
name: "buildings",
styleSetName: "geojson",
maxGeometryHeight: 30000
});
this.mapView.addDataSource(geoJsonBuildingDataSource);
},
Function to define style set:
getStyleSet: function () {
const color = new THREE.Color("blue");
const colorString = "#" + color.getHexString();
return [
{
description: "geoJson property-based style",
when: ["==", ["geometry-type"], "Polygon"],
technique: "extruded-polygon",
renderOrder: 1000,
height: ["number", ["get", "base_height"], 10],
floorHeight: ["number", ["get", "base_height"], 0],
attr: {
color: colorString,
transparent: true,
opacity: 0.8,
boundaryWalls: false,
constantHeight: true,
lineWidth: 1,
lineColor: "#003344",
emissive: colorString,
emissiveIntensity: 0.45
},
},
{
description: "geoJson property-based style",
when: ["==", ["geometry-type"], "Point"],
technique: "circles",
renderOrder: 2000,
color: "#00FF00",
size: 15,
}
];
},
This only renders floors, as I havent defined the style for the walls yet. But its progress!
When hovering over a line in billboardjs you can see a marker which follows the mouse (a tall vertical line). Is there a function for putting a marker on the x-line which can be used without triggering an automatic marker via onmousemove/hovering over data-points?
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
},
bindto: "#lineChart"
});
https://naver.github.io/billboard.js/demo/#Chart.LineChart
So to exemplify. I use an onclick (in the data object) in the chart which defocuses the view and I still want the marker to remain.
So the code would look something like:
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
onclick: function (d) {
focusElsewhere()
showMarker(d.x)
}
},
bindto: "#lineChart"
});
So the question is if there is a function for this, or an obvious fix?
I have looked through https://naver.github.io/billboard.js/release/latest/doc/Chart.html but I may of course have missed something.
I found that using xgrids did the trick. I don't think that the documentation gives a good example of how to use it. But basically you can use the "value" field to give which point the line should be on and add a class to show different kinds of lines.
var chart = bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
],
type: "line", // for ESM specify as: line()
onclick: function (d) {
focusElsewhere()
this.xgrids.add({ value: d.x, class: "hover-line" }); //showMarker(d.x)
}
},
bindto: "#lineChart"
});
To remove the line or reset the billboard for continued use so to say, you can use
xgrids․remove({}) and add an object with some parameters of what kind of lines you want to remove.
I'm trying to make a choropleth map, but how can I set the size of the map?
Now I've this map:
I would like expand the map to all the space, I read the documentations but I didn't find a solution.
This is my code:
var data = [{
type: 'choropleth',
locationmode: 'country names',
locations: unpack(output, 'label'),
z: unpack(output, 'nres'),
text: unpack(output, 'nres')
}];
var layout = {
geo: {
projection: {
type: 'equirectangular'
}
}
};
Plotly.plot(mapChoropleth, data, layout, {
showLink: false
});
Plotly tries to take all the available space without changing the image ratio. If you have a very wide div there will be a lot of empty space to left and right due but it will be filled from the top to the bottom.
You could change height and width in layout, change the margins and fine tune the color bar to get the desired result.
Plotly.d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv', function(err, rows) {
function unpack(rows, key) {
return rows.map(function(row) {
return row[key];
});
}
var data = [{
type: 'choropleth',
locations: unpack(rows, 'CODE'),
z: unpack(rows, 'GDP (BILLIONS)'),
text: unpack(rows, 'COUNTRY'),
colorscale: [
[0, 'rgb(5, 10, 172)'],
[0.35, 'rgb(40, 60, 190)'],
[0.5, 'rgb(70, 100, 245)'],
[0.6, 'rgb(90, 120, 245)'],
[0.7, 'rgb(106, 137, 247)'],
[1, 'rgb(220, 220, 220)']
],
autocolorscale: false,
reversescale: true,
marker: {
line: {
color: 'rgb(180,180,180)',
width: 0.5
}
},
tick0: 0,
zmin: 0,
dtick: 1000,
colorbar: {
autotic: false,
tickprefix: '$',
len: 0.8,
x: 1,
y: 0.6
}
}];
var layout = {
width: 300,
height: 300,
geo: {
showframe: false,
showcoastlines: false,
scope: 'europe',
projection: {
type: 'mercator',
},
},
margin: {
l: 0,
r: 0,
b: 0,
t: 0,
pad: 2
}
};
Plotly.plot(myDiv, data, layout, {
showLink: false
});
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"></div>
You can also change the ratio of the map directly, an ugly but working possibility.
var c = document.getElementsByClassName('countries')[0];
c.setAttribute('transform', 'translate(-300), scale(3, 1)');
c = document.getElementsByClassName('choropleth')[0];
c.setAttribute('transform', 'translate(-300), scale(3, 1)');
c = document.getElementsByClassName('clips')[0].firstChild.firstChild;
c.setAttribute('x', -300);
c.setAttribute('width', 900);
The map is first drawn normally and then resized when clicked on.
var myPlot = document.getElementById('myDiv');
var data = [];
var layout = {};
Plotly.d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv', function(err, rows) {
function unpack(rows, key) {
return rows.map(function(row) {
return row[key];
});
}
data = [{
type: 'choropleth',
locations: unpack(rows, 'CODE'),
z: unpack(rows, 'GDP (BILLIONS)'),
text: unpack(rows, 'COUNTRY'),
colorscale: [
[0, 'rgb(5, 10, 172)'],
[0.35, 'rgb(40, 60, 190)'],
[0.5, 'rgb(70, 100, 245)'],
[0.6, 'rgb(90, 120, 245)'],
[0.7, 'rgb(106, 137, 247)'],
[1, 'rgb(220, 220, 220)']
],
autocolorscale: false,
reversescale: true,
marker: {
line: {
color: 'rgb(180,180,180)',
width: 0.5
}
},
tick0: 0,
zmin: 0,
dtick: 1000,
colorbar: {
autotic: false,
tickprefix: '$',
len: 0.8,
x: 1,
y: 0.6
}
}];
layout = {
width: 1200,
height: 400,
geo: {
showframe: false,
showcoastlines: false,
scope: 'europe',
projection: {
type: 'mercator',
scale: 1
},
},
margin: {
l: 0,
r: 0,
b: 0,
t: 0,
pad: 2
}
};
Plotly.plot(myPlot, data, layout, {
showLink: false
});
myPlot.on('plotly_click', function(){
var c = document.getElementsByClassName('countries')[0];
c.setAttribute('transform', 'translate(-300), scale(3, 1)');
c = document.getElementsByClassName('choropleth')[0];
c.setAttribute('transform', 'translate(-300), scale(3, 1)');
c = document.getElementsByClassName('clips')[0].firstChild.firstChild;
c.setAttribute('x', -300);
c.setAttribute('width', 900);
})
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv" style="x: 0"></div>
As in example http://openlayers.org/dev/examples/graphic-name.html ...
I tried drawing my feature geometry as a flag but I realized that OpenLayers always uses the centroid of the geometry to place the feature at the specified point(latitude/longitude), whereas I wish the base of the flag to be placed at the specified point. Changing the value of Renderer.symbol did not make any effect neither did the graphicXOffset/graphicYOffset.
//my flag geometry
OpenLayers.Renderer.symbol.flag = [0, 0, 0, 140, 120, 140, 120, 60, 4, 60, 4, 0, 0, 0];
//create vector layer with default and select styles
myVectorLayer = new OpenLayers.Layer.Vector("Flag Geometry", {
renderers: renderer,
isBaseLayer: true,
graphicName: flag,
rotation: 180,
pointRadius: 15,
projection: new OpenLayers.Projection("EPSG:900913"),
styleMap: new OpenLayers.StyleMap({
"default": new OpenLayers.Style(OpenLayers.Util.applyDefaults({
fillColor: "red",
strokeColor: "gray",
label: "${label}",
fontColor: "${favColor}",
fontSize: "12px",
fontFamily: "Courier New, monospace",
fontWeight: "bold",
labelYOffset: 2 //for flag
}, OpenLayers.Feature.Vector.style["default"])),
"select": new OpenLayers.Style(OpenLayers.Util.applyDefaults({
}, OpenLayers.Feature.Vector.style["select"]))
})
});
var flag = new OpenLayers.Geometry.Point(latitude, longitude);
var flagFeature = new OpenLayers.Feature.Vector(flag);
myVectorLayer.addFeatures([flagFeature]);
To overcome this base point problem I changed the drawing method as follows:
myVectorLayer = new OpenLayers.Layer.Vector("Flag Geometry", {
renderers: renderer,
isBaseLayer: true,
projection: new OpenLayers.Projection("EPSG:900913"),
styleMap: new OpenLayers.StyleMap({
"default": new OpenLayers.Style(OpenLayers.Util.applyDefaults({
fillColor: "red",
strokeColor: "gray",
label: "${label}",
fontColor: "${favColor}",
fontSize: "12px",
fontFamily: "Courier New, monospace",
fontWeight: "bold",
labelYOffset: 2 //for flag
}, OpenLayers.Feature.Vector.style["default"])),
"select": new OpenLayers.Style(OpenLayers.Util.applyDefaults({
}, OpenLayers.Feature.Vector.style["select"]))
})
});
var pointList = [];
var xs = [0, 0, 240, 240, 8, 8, 0];
var ys = [0, 280, 280, 120, 120, 0, 0];
for(var p=0; p<6; ++p) {
var newPoint = new OpenLayers.Geometry.Point(camera.get('latitude') + xs[p],
camera.get('longitude') + ys[p]);
pointList.push(newPoint);
}
pointList.push(pointList[0]);
var linearRing = new OpenLayers.Geometry.LinearRing(pointList);
var flagVector = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.Polygon([linearRing]));
flagVector.attributes= {
label: 'label',
favColor: 'blue', //favorite color
align: "cm"
};
myVectorLayer.addFeatures([flagVector]);
This though will make the geometry zoom with map zoom not as the previous way.