Displaying TIFF data on React-Leaflet Map - leaflet

I have basic javascript code that uses a simple Leaflet map; I have TIFF files that I parse through, and then I can display these on the map though Layer Control, in a simple index.html file. I can't figure out how to show the TIFF data in react-leaflet though. I can parse the data properly, but react-leaflet only seems to take PNG or PNG url's, and I can't find any documentation or anything that shows how to show TIFF files. I'm sure I'm missing something simple, but it's super frustrating :(
This is my code with react, and react-leaflet. It's working + parsing the TIFF data, I just don't know how to add the data to the layerscontrol baselayer TileLayer...
import React, { useMemo, useState } from "react";
import {
TileLayer,
Marker,
Popup,
MapContainer,
LayersControl,
LayerGroup,
FeatureGroup,
Circle,
useMap,
} from 'react-leaflet';
import chroma from 'chroma-js';
import parseGeoraster from 'georaster';
import GeoRasterLayer from 'georaster-layer-for-leaflet';
import AddLocate from "../components/AddLocate";
import { Helmet } from 'react-helmet';
import sample from '../data/tmin.tiff';
const MapBody = () => {
const center = [0, 0];
let tmin_layer = '';
fetch(sample)
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
parseGeoraster(arrayBuffer).then(georaster => {
const min = georaster.mins[0];
const max = georaster.maxs[0];
const range = georaster.ranges[0];
const scale = chroma.scale('Spectral').domain([1, 0]);
tmin_layer = new GeoRasterLayer({
georaster: georaster,
opacity: 0.7,
pixelValuesToColorFn: function (pixelValues) {
var pixelValue = pixelValues[0];
if (pixelValue === 0) return null;
const scaledPixelValue = (pixelValue - min) / range;
const color = scale(scaledPixelValue).hex();
return color;
},
resolution: 256
});
console.log("layer:", tmin_layer);
})
})
return (
<>
<Helmet>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
/>
</Helmet>
<MapContainer className="map" center={center} zoom={2} scrollWheelZoom={false} doubleClickZoom={true}>
<TileLayer
className="tile"
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
//prevents map duplicating
noWrap={true} />
<LayersControl position="topright">
<LayersControl.BaseLayer name="TOPO TESTING">
<TileLayer className="tiles" url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"/>
</LayersControl.BaseLayer>
<LayersControl.BaseLayer name="TIFF TEST">
<TileLayer className="tiffy" url={tmin_layer}/>
</LayersControl.BaseLayer>
</LayersControl>
<AddLocate/>
</MapContainer>
</>
)
}
export default MapBody;

Related

react-mapbox-gl markers are not displayed correctly

I have a site with mapbox, the map itself works fine, but I can't add markers to it.
I copied the code from one source on which everything works, but when I added it to my project, the markers shifted from the correct coordinates on the map and got even more shifted when approaching.
here's my code
import React, { useState } from "react";
import ReactDOM from "react-dom";
import ReactMapboxGl, { Layer, Marker } from "react-mapbox-gl";
import { observer } from "mobx-react-lite";
import state from "../../state/state";
const Map = ReactMapboxGl({
accessToken:
"pk.eyJ1IjoibmFnaHQiLCJhIjoiY2wyYTJrazZxMDFlbzNpbnp0eTNnOG44aCJ9.i3nyiAJBTDyWviIWhsX-Zg",
});
const IndexMap = observer(({ coordinats }) => {
return (
<div style={{ height: "100vh", width: "100%", overflow: "hidden" }}>
<Map
style="mapbox://styles/mapbox/streets-v9" // eslint-disable-line
containerStyle={{
height: "100%",
width: "100%",
}}
center={{
lat: 51.5285582,
lng: -0.2416815,
}}
zoom={[12]}
>
<Marker coordinates={[-0.2416815, 51.5285582]} anchor="bottom">
<h1>marker</h1>
</Marker>
</Map>
</div>
);
});
export default IndexMap;
I think there are not enough styles for the map to set them in the right location.
I don't know what the problem was. I just moved the project to the folder with the working file. The link to the folder with the working file -https://codesandbox.io/embed/pwly8?codemirror=1

how to use react-leaflet with preact

I am trying to use react-leaflet with preact with little success. I do actually render the map but it is not rendered together, as in the tiles are scattered over the page and outside the containing element.
I have read all (some) related questions on SO that say you need to either import the css file from leaflet (import "leaflet/dist/leaflet.css") or include a css file with a class for .leaflet-container. I have tried both and it is still not working.
I followed the instruction on the react leaflet website to install the necessary dependencies.
My project uses rollup so I added the following to my rollup config:
alias({
entries: [
{ find: 'react', replacement: 'preact/compat' },
{ find: 'react-dom', replacement: 'preact/compat' },
{ find: 'react-dom/test-utils', replacement: 'preact/test-utils' },
{ find: 'react/jsx-runtime', replacement: 'preact/jsx-runtime' }
]
}),
And my component looks like this:
import { h } from 'preact';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import { Icon } from "leaflet";
import "../../styles/leaflet.css";
const Map = (): JSX.Element => {
type LatLngTuple = [number, number];
const position: LatLngTuple = [51.505, -0.09];
return (
<MapContainer center={position} zoom={13} scrollWheelZoom={false} >
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
);
};
export default Map;

Problem whit sending my local position Leaflet

When I'm initializing the const it's like that
-lat and leg are available only inside the function and when I'm send them to the map (I'm usinge Leaflet) it doesn't show them and it gave me that error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
import {Component,useState,useEffect,useRef, ReactNode, CSSProperties, Link} from 'react';
import { MapContainer, TileLayer, Marker, Popup,useLeaflet,leafletElement} from 'react-leaflet'
function HomePage(){
const [lat, setLat] = useState(null);
const [lng, setLng] = useState(null);
const [status, setStatus] = useState(null);
if (!navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
} else {
setStatus('Locating...');
navigator.geolocation.getCurrentPosition((position) => {
setStatus(null);
setLat(position.coords.latitude);
setLng(position.coords.longitude);
console.log(lat,lng)
}, () => {
setStatus('Unable to retrieve your location');
});
}
const position = [lat,lng]
return(
<MapContainer center={position} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={position}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
)};
export default HomePage
Your component is re-rendering constantly. I'm not sure but I think below should work. You have to use useEffect hook and add a check on navigator at the start of component if(!navigator.geolocation) { return null; }

Performance issues with 1k+ markers with popups in React Leaflet

I have a React application with the React Leaflet library and I'm displaying a marker for each building in the map, in a small town. I have about 5k markers in total and a filter to display only the markers I want.
However, I noticed that I was having a huge performance hit with the code below. I've looked at some alternatives such as PixiOverlay and marker clustering, but the former is quite complicated to migrate the current code base to and the latter doesn't solve my problems at all.
My current code:
import React, {
useRef, useEffect, useContext, useState,
} from 'react';
import ReactDOMServer from 'react-dom/server';
import {
Marker, useLeaflet, Popup, Tooltip, CircleMarker, Circle,
} from 'react-leaflet';
import L from 'leaflet';
import styled from 'styled-components';
interface IProps {
coords: [number, number]
description: string,
name: string
}
let timeoutPopupRef: any = null;
let timeoutPopupRefClose: any = null;
const DynamicMarker: React.FC<IProps> = ({ coords, description, name }) => {
const markerRef = useRef<any>(null);
const popupRef = useRef<Popup>(null);
const tooltipRef = useRef<Tooltip>(null);
const leaflet = useLeaflet();
const divIcon: L.DivIcon = L.divIcon({
iconSize: [25, 25],
className: 'marker-white',
});
const onComponentMount = () => {
if (!leaflet.map) return;
if (!markerRef.current) return;
const mapZoom: number = leaflet.map.getZoom();
if (popupRef.current) {
if (mapZoom <= 17) {
markerRef.current.leafletElement.unbindPopup();
} else if (mapZoom > 17) {
markerRef.current.leafletElement.bindPopup(popupRef.current!.leafletElement);
}
}
if (tooltipRef.current) {
if (mapZoom <= 15) {
markerRef.current.leafletElement.unbindTooltip();
} else if (mapZoom > 15) {
markerRef.current.leafletElement.bindTooltip(tooltipRef.current!.leafletElement);
}
}
leaflet.map!.on('zoomend', onMapZoomEnd);
};
useEffect(onComponentMount, []);
const onMapZoomEnd = () => {
if (!markerRef.current) return;
if (!popupRef.current) return;
if (!leaflet.map) return;
const zoom = leaflet.map.getZoom();
if (zoom < 17) {
if (!markerRef.current!.leafletElement.isPopupOpen()) {
markerRef.current!.leafletElement.unbindPopup();
}
} else if (zoom >= 17) {
markerRef.current!.leafletElement.bindPopup(popupRef.current.leafletElement);
}
};
const handlePopupVisible = (value: boolean) => {
if (!markerRef.current) return;
if (timeoutPopupRefClose) clearTimeout(timeoutPopupRefClose);
if (value) {
if (!markerRef.current!.leafletElement.isPopupOpen()) {
timeoutPopupRef = setTimeout(() => {
markerRef.current!.leafletElement.openPopup();
}, 400);
}
} else {
if (timeoutPopupRef) {
clearTimeout(timeoutPopupRef);
}
if (markerRef.current!.leafletElement.isPopupOpen()) {
timeoutPopupRefClose = setTimeout(() => {
markerRef.current!.leafletElement.closePopup();
}, 100);
}
}
};
const onComponentDismount = () => {
leaflet.map!.off('zoomend', onMapZoomEnd);
if (!markerRef.current) return;
markerRef.current.leafletElement.remove();
};
useEffect(() => onComponentDismount, []);
return (
<Marker
icon={divIcon}
position={coords}
onmouseover={() => handlePopupVisible(true)}
onmouseout={() => handlePopupVisible(false)}
ref={markerRef}
>
<Popup className="custom-popup-content" ref={popupRef} closeButton={false}>
<div
onMouseEnter={() => handlePopupVisible(true)}
onMouseLeave={() => handlePopupVisible(false)}
>
<img
className="popup-img"
alt='image'
src='https://cdn.discordapp.com/attachments/578931223775281162/644181902215086094/default_geocode-1x.png'
/>
<div className="popup-content">
<span className="popup-content-title">{name}</span>
{description && <span className="popup-content-subtitle">{description}</span>}
</div>
</div>
</Popup>
</Marker>
);
};
export default DynamicMarker;
The code above unbinds popups from markers if the map zoom is below a threshold, and binds them when the zoom is above the threshold. I also implemented event handlers to onMouseOver and onMouseOut events on the marker component to open my popup when the user hovers the marker icon and it will only close the popup if the cursor isn't hovering over the popup or the marker icon.
When I zoom in or out with about 2k markers being displayed, the map freezes for about 5-10 seconds and updates all of the components inside the Map component exported by react-leaflet.
After testing with marker clustering via react-leaflet-markercluster, I noticed that the performance issues were still present. I tried commenting out the Popup component passed as a children to the marker component and the lag issues I had were gone.
With that in mind, I realized that my bottleneck was actually rendering 2k popups in the DOM even though they were invisible. So, after some trial and error, I came across a solution: states.
I added a boolean state called shouldDrawPopup, with a default value of false and only changed its value inside the handlePopupVisible function. The value of this boolean state will change only if:
Map zoom is above a threshold; and
Popup is not open
And then I changed the render function of my component to include a popup only if the shouldDrawPopup state is true:
return (
{shouldDrawPopup && (
<Marker
icon={divIcon}
position={coords}
onmouseover={() => handlePopupVisible(true)}
onmouseout={() => handlePopupVisible(false)}
ref={markerRef}
>
<Popup className="custom-popup-content" ref={popupRef} closeButton={false}>
<div
onMouseEnter={() => handlePopupVisible(true)}
onMouseLeave={() => handlePopupVisible(false)}
>
<img
className="popup-img"
alt='image'
src='https://cdn.discordapp.com/attachments/578931223775281162/644181902215086094/default_geocode-1x.png'
/>
<div className="popup-content">
<span className="popup-content-title">{name}</span>
{description && <span className="popup-content-subtitle">{description}</span>}
</div>
</div>
</Popup>
</Marker>
)}
);
If anyone has other solutions or any feedback to this problem, feel free to share!

I was trying out Uber's deck.gl by adding the component to my react app. But nothing appears. Any help would be appreciated

I was trying out Uber's deck.gl by adding the component to my react app. But nothing appears.
I believe it could be related to mapbox. It appeared once but that was it.
I set the width, height, etc. But nothing works.
This is basic example in their site.
Deck Gl with React
Here is my code. deckgl.component.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import { StaticMap } from 'react-map-gl';
import DeckGL, { LineLayer, ScatterplotLayer } from 'deck.gl';
const MAPBOX_ACCESS_TOKEN = '<MAPBOX_TOKEN>';
// Viewport settings
const INITIAL_VIEW_STATE = {
latitude: 37.785164,
longitude: -122.41669,
zoom: 16,
bearing: -20,
pitch: 60
};
class DeckGlComponent extends Component {
render() {
return (
<DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} width="100%" height="100%">
<StaticMap mapboxApiAccessToken={MAPBOX_ACCESS_TOKEN} />
<LineLayer
data={[{ sourcePosition: [-122.41669, 37.7883], targetPosition: [-122.41669, 37.781] }]}
getStrokeWidth={5}
/>
<ScatterplotLayer
data={[{ position: [-122.41669, 37.79] }]}
radiusScale={100}
getFillColor={[0, 0, 255]}
/>
</DeckGL>
);
}
}
export default DeckGlComponent;
and index.js
import React from 'react';
import { render } from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import DeckGlComponent from './deckgl.component';
render(
<DeckGlComponent />,
document.getElementById('root')
);
serviceWorker.unregister();
It's absolutely basic. But nothing turns up. I created a new mapbox token just to be sure and still nothing.
According to your description (since there's not too much information), and mapbox token is active as you said, I suspect if you create a HTML file contains root element, like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
#root {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>
This file is required when you calling these codes:
render(
<DeckGlComponent />,
document.getElementById('root')
);
You can put your code on codepen or some online editors, so that we can help you more specifically.
Besides, I recommend you read codes in this folder https://github.com/uber/deck.gl/tree/master/examples/get-started rather than the codes in documents. Sometimes, codes in documents is for explaining concepts, and not ready for running.