GeoTIFF / georaster-layer-for-leaflet

Display GeoTIFFs and soon other types of raster on your Leaflet Map
https://geotiff.github.io/georaster-layer-for-leaflet-example/
Apache License 2.0
303 stars 58 forks source link

Display TIFF Files in React Application as LayerControl.BaseLayer TileLayer #113

Open LukeHayesss opened 2 years ago

LukeHayesss commented 2 years ago

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!

floss-order commented 1 year ago

Were you able to solve the issue?

LukeHayesss commented 1 year ago

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

floss-order commented 1 year ago

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.

LukeHayesss commented 1 year ago

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)

floss-order commented 1 year ago

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.

LukeHayesss commented 1 year ago

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 - `

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

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

NovamationCEO commented 6 months ago

@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>
          )
      })}