Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
LukeHayesss opened this issue Nov 22, 2022 · 8 comments

Comments

@LukeHayesss
Copy link

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
Copy link

Were you able to solve the issue?

@LukeHayesss
Copy link
Author

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

@floss-order
Copy link

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
Copy link
Author

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
Copy link

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
Copy link
Author

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?

@Louma20
Copy link

Louma20 commented Jun 5, 2023

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

@NovamationCEO
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants