georaster-layer-for-leaflet icon indicating copy to clipboard operation
georaster-layer-for-leaflet copied to clipboard

Display TIFF Files in React Application as LayerControl.BaseLayer TileLayer

Open LukeHayesss opened this issue 3 years ago • 11 comments

Hi, using React-Leaflet I'm able to display PNG layers within LayerControl.Baselayer TileLayer, and can display the various layers as additional layers on top of the initial baselayer. Utilizing a basic index.html file, I can build a simple Leaflet application that can parse TIFF files, and add them as layers, but within React-Leaflet, I'm struggling to get the layers to display. We've used Georaster Layer For Leaflet parse the data and have everything show up in the console correctly (can access everything when debugging as well), I'm just unsure how to proceed with what to do from here as far as adding these TIFF files to our base map (I have extensively searched online for guidance and cannot find much, unfortunately). Any help would be appreciated!

LukeHayesss avatar Nov 22 '22 20:11 LukeHayesss

Were you able to solve the issue?

floss-order avatar Dec 08 '22 10:12 floss-order

I was yeah! Took some time but it's working now :)

LukeHayesss avatar Dec 09 '22 05:12 LukeHayesss

I was yeah! Took some time but it's working now :)

can you explain how did you manage to do it? I have layercontrol, baselayer and tilelayer with multiple maps. Georaster layer shows only when I have one map only.

floss-order avatar Dec 09 '22 12:12 floss-order

I have each Tiff layer as a separate component, where I parse the data. Then I import it into my main Map component, as a LayerControl.Overlay. Within the Overlay, I import each Tiff file via the local url, which I import at the top of my component; this let's the users choose between multiple Tiff overlays (let me know if you need a more detailed explanation, happy to share my code haha)

LukeHayesss avatar Dec 12 '22 16:12 LukeHayesss

I have each Tiff layer as a separate component, where I parse the data. Then I import it into my main Map component, as a LayerControl.Overlay. Within the Overlay, I import each Tiff file via the local url, which I import at the top of my component; this let's the users choose between multiple Tiff overlays (let me know if you need a more detailed explanation, happy to share my code haha)

In my project I have only one layer and I change the data. Thanks a lot. I would also like to take a look at your code.

floss-order avatar Dec 13 '22 09:12 floss-order

for sure! i have the tiff components structured like this -

`import { useEffect, useRef } from "react"; import proj4 from "proj4"; import { useLeafletContext } from "@react-leaflet/core"; import { useMap } from "react-leaflet"; import parseGeoraster from "georaster"; import GeoRasterLayer from "georaster-layer-for-leaflet"; import chroma from "chroma-js";

window.proj4 = proj4;

const TminLayer = ({ url }) => { const geoTiffLayerRef = useRef(); const context = useLeafletContext(); const map = useMap();

useEffect(() => {
const container = context.layerContainer || context.map;
fetch(url, {
mode: "no-cors"
})
.then((response) => response.arrayBuffer())
.then((arrayBuffer) => {
    parseGeoraster(arrayBuffer).then((georaster) => {
        const min = georaster.mins[0];
        const range = georaster.ranges[0];
        const scale = chroma.scale('Spectral').domain([1, 0]);
        const options = {
        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,
            opacity: 0.7
        }
        options.georaster = georaster;
        geoTiffLayerRef.current = new GeoRasterLayer(options);
        container.addLayer(geoTiffLayerRef.current);
        })
    })
    return () => {
    };
}, [context, url, map]);

return null;

};

export default TminLayer; `

Then in my main Map component, I import those Tiff components, and import the Tiff data, then combine into Overlays as below - <LayersControl.Overlay name="T-Min"> <TminLayer url={tmin}/> </LayersControl.Overlay>

This was the only solution I could get working, there must be others out there but it's definitely a start...let me know?

LukeHayesss avatar Dec 19 '22 16:12 LukeHayesss

Hello @LukeHayesss, I tried to reproduce your approach and got the following error: error cc @aghand0ur

Louma20 avatar Jun 05 '23 11:06 Louma20

@LukeHayesss, thank you so, so much for posting this. As a relative newcomer to maps, this information seemed impossible to find. React-Leaflet is the cleanest tool I've found, but it's still a lot to learn.

I made a few small changes that have worked better for me - your mileage may vary.

import { useEffect, useRef } from 'react'
import { useLeafletContext } from '@react-leaflet/core'
import { useMap } from 'react-leaflet'
import parseGeoraster from 'georaster'
import GeoRasterLayer from 'georaster-layer-for-leaflet'
import chroma from 'chroma-js'

export function TifLayer(props: { url: string }) {
    const { url } = props
    const geoTiffLayerRef = useRef()
    const context = useLeafletContext()
    const map = useMap()

    useEffect(() => {
        const container = context.layerContainer || context.map
        fetch(url, {
            mode: 'no-cors',
        })
            .then((response) => response.arrayBuffer())
            .then((arrayBuffer) => {
                parseGeoraster(arrayBuffer).then((georaster) => {
                    const min = georaster.mins[0]
                    const range = georaster.ranges[0]
                    const scale = chroma.scale('Spectral').domain([1, 0])
                    const options = {
                        pixelValuesToColorFn: function (pixelValues: number[]) {
                            const pixelValue = pixelValues[0]
                            if (pixelValue === 0) return null
                            const scaledPixelValue = (pixelValue - min) / range
                            const color = scale(scaledPixelValue).hex()
                            return color
                        },
                        resolution: 256,
                        opacity: 0.7,
                        georaster: undefined,
                    }
                    options.georaster = georaster
                    if (geoTiffLayerRef.current) return // Prevent duplicate loading of layers
                    geoTiffLayerRef.current = new GeoRasterLayer(options)
                    container.addLayer(geoTiffLayerRef.current)
                })
            })
        return () => {
            if (geoTiffLayerRef.current && map) {
                map.removeLayer(geoTiffLayerRef.current)
            }
        } // Cleanup function on map unload
    }, [url]) // I had changes to the context/map that were causing extra renders I didn't need

    return null
}
...

const tifs = [ {url: '/src/geoTif/rainfall.tif', name: 'Rainfall'}, {url: '/src/geoTif/pop.tif', name: 'Population (2023)'} ]
...

 {tifs.map((tif) => {
          return (
              <LayersControl.Overlay
                  name={tif.name}
                  key={tif.name}
              >
                  <TifLayer url={tif.url} />
              </LayersControl.Overlay>
          )
      })}

NovamationCEO avatar May 13 '24 17:05 NovamationCEO