Problem whit sending my local position Leaflet - 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; }

Related

Displaying TIFF data on React-Leaflet Map

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;

Accessing Parameters in SolidJS Router

I am trying to rewrite a project I made in React in Solid. I am trying to use the Solid Router as the documentation advises.
Here are my components so far.
index.js
import { render } from 'solid-js/web'
import { Router } from '#solidjs/router'
import './index.css'
import App from './App'
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById('root')
)
App.jsx
import Header from './components/Header'
import styles from './App.module.css'
import Navbar from './components/Navbar'
import Topics from './components/Topics'
function App() {
return (
<div className={styles.container}>
<Header />
<Navbar />
<Routes>
<Route path="/" element={<Articles />} />
<Route path="/:topic" component={<Topics />} />
</Routes>
</div>
)
}
export default App
Navbar.jsx
import { NavLink } from '#solidjs/router'
import { getTopics } from '../utils/api'
const Navbar = () => {
const [topics, setTopics] = createSignal([])
onMount(() => {
getTopics().then(({ topics }) => {
setTopics(topics)
})
})
return (
<nav>
<ul>
<For each={topics()}>
{topic => (
<li>
<NavLink href={`/${topic.slug}`}>{topic.slug}</NavLink>
</li>
)}
</For>
</ul>
</nav>
)
}
export default Navbar
The problem I think seems to be in the component below
Topics.jsx
import { useParams } from '#solidjs/router'
import { createSignal, For, onMount, createResource } from 'solid-js'
import { getTopicArticles } from '../utils/api'
const Topics = () => {
const { topic } = useParams()
console.log(topic)
return (
<div>
<h1>{topic}</h1>
</div>
)
}
export default Topics
The params seem to be undefined no matter what. I understand that Solid router is not exactly the same as React-Router but for this simple example I can't see where I am going wrong.
The desired outcome is to be able to click on the NavLink in the Navbar.jsx component and that routes to the desired path, for example http://localhost:3000/cooking and render the topic I need, but the params are always undefined.
This is the result of the api call, api/articles?topic=undefined
The desired result is to attach the param at the end of the api with useParams, just like in my React version
Edit: below is the Topics.jsx component updated to a working version, not sure if it is the best way.
import { useParams } from '#solidjs/router'
import { getTopicArticles } from '../utils/api'
import Article from './Article'
const Topics = () => {
const params = useParams()
const [articles, setArticles] = createSignal([])
const [loading, setLoading] = createSignal(true)
createEffect(() => {
setLoading(true)
getTopicArticles(params.topic).then(({ articles }) => {
setArticles(articles)
setLoading(false)
})
})
return (
<>
{loading() && <div>Loading...</div>}
<h2>{params.topic}</h2>
<For each={articles()}>{article => <Article article={article} />}</For>
</>
)
}
export default Topics
Could be related to the object returned from useParams is being reactive. console.log returns an empty object but destructing outputs the values as expected. That is because of the proxy and totally normal.
Retrieves a reactive, store-like object containing the current route path parameters as defined in the Route.
https://github.com/solidjs/solid-router#useparams
Also regular query parameters like ?id=1&name=John does not work with useParams, for those use useSearchParams.
import { render } from "solid-js/web";
import {
Router,
useParams,
useSearchParams,
Route,
Routes,
Link
} from "#solidjs/router";
const Home = () => {
const [params, setParams] = useSearchParams();
console.log({ ...params });
return <div>Home</div>;
};
const Blog = () => {
const params = useParams();
console.log({ ...params });
return <div>Blog {JSON.stringify(params)}</div>;
};
const App = () => {
return (
<Router>
<ul>
<li>
<Link href="/?id=1&name=john">Home</Link>
</li>
<li>
<Link href="/blog/js/1">Blog</Link>
</li>
</ul>
<Routes>
<Route path="/" component={Home} />
<Route path="/blog/:category/:id" element={Blog} />
</Routes>
</Router>
);
};
render(App, document.getElementById("app")!);
Check https://codesandbox.io/s/solid-router-demo-forked-71ef9x?file=/index.tsx for live demo.
Also, we pass component name to the component prop like so:
<Route path="/" component={Home} />

React-leaflet trying to add EditControl on my code, but it doesnt work

i want to draw shapes on my map using react-leaflet, but im having issues with editControl, when i use editControl on my code i get the following errors:
Here is the code im using for this page ( still under development) `
console.log(teslaData);
const navigate = useNavigate();
const [age, setAge] = React.useState('');
const [ordem, setOrdem] = React.useState('');
const position = [51.505, -0.09]
const ZOOM_LEVEL = 9;
const mapRef = useRef();
var coordenadas = [
[28.35390453844, -81.56443119049],
[28.35390453844, -81.55619144439],
[28.35983376526, -81.55619144439],
[28.35983376526, -81.56443119049],
[28.35390453844, -81.56443119049],
];
const myIcon = L.icon({
iconUrl: require('../../images/download__1_-removebg-preview.png'),
iconSize: [64, 64],
iconAnchor: [32, 64]
});
const _onDeleted = (e) => {
console.log(e);
}
const _onCreate = (e) => {
console.log(e);
}
const _onEdited = (e) => {
console.log(e);
}
return (
<>
<BarraLateral >
<div className='adjContainerMap'>
<MapContainer center={[51.505, -0.09]} zoom={2} scrollWheelZoom={true}>
<FeatureGroup>
<EditControl
position="topright"
/>
</FeatureGroup>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Polygon color="blue" positions={coordenadas} />
{teslaData.map(tesla => (
<Marker
key={tesla.id}
position={[tesla.gps.latitude, tesla.gps.longitude]}
icon={myIcon}
>
</Marker>
))}
</MapContainer>
</div>
</BarraLateral>
</>
);
}
`
i already tried to downgrade leaflet version but it still doesnt work, i installed #react-leaflet/core as it recommends in another stackoverflow question ( still doesnt work) and if i remove editControl the code works fine, is there an alternative to it? does leaflet stop to support editControl? thanks in advance!

How can I disable or change the href on a React-Leaflet v4 Popup close button?

In react-leaflet v4, the Popup component has a default href associated with the close button that directs to #close. Is there a way in React to modify this href or disable the href redirection? It's breaking my react-dom HashRouter.
Of note, Popup.js in Leaflet 1.8 has the following code:
var closeButton = this._closeButton = DomUtil.create('a', prefix + '-close-button', container);
closeButton.setAttribute('role', 'button'); // overrides the implicit role=link of <a> elements #7399
closeButton.setAttribute('aria-label', 'Close popup');
closeButton.href = '#close';
closeButton.innerHTML = '<span aria-hidden="true">×</span>';
The same issue is also in angular - means it seems to be the leaflet Lib:
// leaflet.js
close: function () {
if (this._map) {
this._map.removeLayer(this);
}
return this;
},
The close function has not even the $event as an argument and the "default" isn't prevented. This leaves us only dirty hacks:
Get the close button after the marker was displayed
Add a click handler more
Add a prefentDefault
yourMethodOpensTheToolTip(marker: Marker) {
if (marker && marker.openPopup) {
marker.openPopup();
// 1. get the close buttons, after the opened the popup
const closeButtons = document.getElementsByClassName('leaflet-popup-close-button');
// 2. add the event handler - if you have more than one, loop here
if (closeButtons && closeButtons.length > 0) {
L.DomEvent.on(closeButtons[0] as HTMLElement, 'click', function(ev){
ev.preventDefault(); // 3. stop it here
});
}
Just for reference the #close button as HTML:
Try something like this. It will probably disable any other hrefs that you may have in the popup though.
document.querySelector('.leaflet-pane.leaflet-popup-pane')!.addEventListener('click', event => {
event.preventDefault();
});
You can utilize useRef hooks and create a click event in the marker
const mapRef = useRef(null);
// event listener to handle marker click
const handleClick = () => {
mapRef.current._popup._closeButton.addEventListener('click', (event) => {
event.preventDefault();
})
};
const map = (<MapContainer center={position} zoom={13} scrollWheelZoom={false} style={{ height: '350px', width: '100%' }} ref={mapRef}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker
position={position}
eventHandlers={{
click: (e) => handleClick(),
}}
>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>)
if you are using GeoJSON you can use onEachFeature props
const onEachCountry = (country, layer) => {
const countryName = country.properties.ADMIN;
layer.on('click', function (e) {
layer
.bindPopup(countryName)
.openPopup()
._popup._closeButton.addEventListener('click', (event) => {
event.preventDefault();
});
});
};
const map = (<MapContainer style={{ height: '300px' }} zoom={1} center={[20, 100]}>
<GeoJSON style={countryStyle} data={mapData.features} onEachFeature={onEachCountry} />
</MapContainer>)
In my React project with react-leaflet v4, I had the same issue and I solved it with the "popupopen" event :
https://leafletjs.com/reference.html#marker-popupopen
<Marker
position={position}
eventHandlers={{
popupopen: (e) => {
e.popup._closeButton.removeAttribute("href");
e.popup._closeButton.style.cursor = "pointer";
}
}}
>
<Popup>
<p>Lorem ipsum dolor sit amet</p>
</Popup>
</Marker>
I hope it will help.
Building on Paul's answer. Here is the solution if you have multiple popups. This will handle the close button click event on all the popups that are open on the leaflet map.
// This is a stopgap till Leaflet fixes its issue with close buttons in popups in Leaflet maps
let popupCloseButtonsHTMLCollection = document.getElementsByClassName('leaflet-popup-close-button');
if(popupCloseButtonsHTMLCollection && popupCloseButtonsHTMLCollection.length > 0){
//convert the popupCloseButtonsHTMLCollection to array
var popupArray = [].slice.call(popupCloseButtonsHTMLCollection);
popupArray.forEach(button =>{
L.DomEvent.on(button as HTMLElement, 'click', function(ev){
ev.preventDefault();
});
});
}

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;