I have an application where I use React to render an OpenLayers map (using version 3) in order to display a .mbtiles file served from Tilserver-gl-light. The app functions and does what I want it to, but it will be running on an old browser that is not very powerful. When I run the app in my dev environment (latest version of Chrome), it works well. I am looking for any other optimizations that you may recommend for me to try.
MapComponent.jsx:
import React, { useEffect, useState, useRef } from 'react'
import ol from 'openlayers'
import InfoCard from './InfoCard/InfoCard'
const center = [-9735948.897, 3526370.974, -9695948.897, 3566370.974]
const MapComponent = () => {
const [error, setError] = useState(null)
const [infoCardOpen, setInfoCardOpen] = useState(false)
const [infoCardInfo, setInfoCardInfo] = useState(null)
const styleFunction = (feature) => {
const layerName = feature.get('layer');
if (layerName === 'DEPARE' || layerName === "SLCONS" || layerName === "FAIRWY" || layerName === "NAVAIDS"){
return null
}
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'black',
width: 1,
}),
});
};
const vectorTileSource = new ol.source.VectorTile({
format: new ol.format.MVT(),
tileGrid: new ol.tilegrid.createXYZ(),
buffer: 1,
tilePixelRatio: 14,
url: `http://${location.host}:8080/data/my_tiles/{z}/{x}/{y}.pbf`,
})
const vectorTileLayer = new ol.layer.VectorTile({
preload: Infinity,
source: vectorTileSource,
style: styleFunction,
renderBuffer: 512,
updateWhileAnimating: false,
updateWhileInteracting: false,
renderMode: 'hybrid'
})
useEffect(() => {
try{
const controls = ol.control.defaults({
attributionOptions: ({
collapsible: false
})
}).extend([
new ol.control.ZoomToExtent({
extent: [center]
})
])
const view = new ol.View({
center: PensacolaFlorida,
zoom: 12,
projection: 'EPSG:3857',
})
const map = new ol.Map({
cacheSize: 2000,
layers: [
vectorTileLayer
],
controls: controls,
target: 'map',
view: view,
loadTilesWhileInteracting: true,
loadTilesWhileAnimating: true
})
map.on('movestart', () => {
vectorTileLayer.setExtent(map.getView().calculateExtent())
});
map.on('moveend', () => {
vectorTileLayer.setExtent(undefined)
});
map.on('click', (event) => {
map.forEachFeatureAtPixel(event.pixel, (feature, layer) => {
const properties = feature.getProperties();
displayFeatureInfo(properties);
return true;
});
})
const displayFeatureInfo = (properties) => {
setInfoCardOpen(true)
setInfoCardInfo(properties)
console.log(properties)
setTimeout(() => {
setInfoCardOpen(false)
}, 3000)
}
return () => {
map.setTarget(null)
}
} catch(err){
const errorMessage = `Error initializing map: ${err.message}`;
}
}, [])
return (
<div className='map-container'>
{error ? <h1>{error}</h1> : <div id='map'></div>}
{infoCardOpen && <InfoCard info={infoCardInfo}/>}
</div>
)
}
export default MapComponent
I have utilized various different approaches, some of which did indeed help. Those that have helped so far: excluding certain feature layers using a style function, map.on('movestart' || 'moveend')
functionality, loadTilesWhileInteracting
and loadTilesWhileAnimating
.
Something I wanted to do was use OpenLayers’ Cluster
, but this does not work for VectorTiles
to my knowledge. I also heard about OffscreenCanvas
which sounds incredible, but this is not supported on the browser the app will run on.
Please let me know any other optimizations to increase the pan/zoom/rotate speed of the map if any come to mind.
Thanks,
Wade
wade is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.