Leaflet-geosearch Implementation by JSON

80 views Asked by At

I encountered a problem that I can’t connect my JSON list in the search field. I can’t find a solution on how to change the provider(const provider = new OpenStreetMapProvider()) correctly so that it searches through JSON.

import React, { useEffect } from 'react';

import { MapContainer, TileLayer, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-geosearch/dist/geosearch.css';
import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch';
import customMarkerIcon from 'assets/img/maps/marker-icon.png';
import markerData3 from 'assets/data/json/airports.json';


const customIcon = L.icon({
  iconUrl: customMarkerIcon,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
});



const Search = () => {
  const map = useMap();

  useEffect(() => {
    const locations = markerData3.map(location => ({
      ...location,
      lat: parseFloat(location.coordinates.split(',')[1].trim()),
      lng: parseFloat(location.coordinates.split(',')[0].trim()),
    }));

    const provider = new OpenStreetMapProvider();
    const searchControl = new GeoSearchControl({
      provider: provider,
      style: 'bar',
      showMarker: true,
      showPopup: false,
      marker: {
        icon: new L.Icon.Default(),
        draggable: false,
      },
      autoClose: true, // Automatically close the search results container on selection
      searchLabel: 'Enter location...', // Change the placeholder text
      retainZoomLevel: false, // Maintain the zoom level after searching
      animateZoom: true, // Animate the zoom to the selected location
    }).addTo(map);

    // Prepare the data for the search
    provider.search({
      query: '', // Empty query to load all locations
      latlng: locations.map(location => [location.lat, location.lng]),
    });

    return () => {
      map.removeControl(searchControl);
    };
  }, [map]);

  return null;
};

const MapWithMarkers = ({ onMarkerClick }) => {
  const map = useMap();
  const markerClusterGroupRef = L.markerClusterGroup();

  useEffect(() => {
    // Clear the existing markers from the cluster group
    markerClusterGroupRef.clearLayers();

    // Add new markers to the cluster group
    markerData3.forEach(marker => {
      const [lng, lat] = marker.coordinates.split(',').map(coord => parseFloat(coord.trim()));
      const leafletMarker = L.marker([lat, lng], { icon: customIcon });

      // Attach a click event listener to the marker
      leafletMarker.on('click', () => {
        onMarkerClick(`${marker.iso_country} - ${marker.name} - ${marker.ident}`);
      });

      const popupContent = `<strong>${marker.ident}</strong><br>${marker.name}`;
      leafletMarker.bindPopup(popupContent);
      markerClusterGroupRef.addLayer(leafletMarker);
    });
    

    // Add the updated cluster group to the map
    map.addLayer(markerClusterGroupRef);

    // Clean up when the component unmounts
    return () => {
      map.removeLayer(markerClusterGroupRef);
    };
  }, [map, onMarkerClick]);

  return null;
};


const AirportsMapNewAircraftSection = ({ mapType, onMarkerClick }) => {

  // Callback function to handle marker clicks
  const handleMarkerClick = (basePoint) => {
    onMarkerClick(basePoint)
  };

  

  return (
    <div className="AirportsMap" style={{ width: '100%'}}>
      <MapContainer center={[56.9235, 23.9710]} zoom={4} maxZoom={18} style={{ height: '500px', width: '100%', borderRadius: "0px 0px 10px 10px"}}>
        <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        <MapWithMarkers onMarkerClick={handleMarkerClick}/>
        <Search />
      </MapContainer>
    </div>
  );
};


export default AirportsMapNewAircraftSection;
Example from JSON :

{"continent": "NA", "coordinates": "-74.93360137939453, 40.07080078125", "elevation_ft": "11", "gps_code": "00A", "iata_code": null, "ident": "00A", "iso_country": "US", "iso_region": "US-PA", "local_code": "00A", "municipality": "Bensalem", "name": "Total Rf Heliport", "type": "heliport"}

Package.json version of plugins

    "react-leaflet": "^4.2.1",
    "react-leaflet-cluster": "^2.1.0",
    "react-leaflet-markercluster": "^3.0.0-rc1",
    "react-leaflet-search": "^2.0.1",
    "leaflet": "^1.9.4",
    "leaflet-easybutton": "^2.4.0",
    "leaflet-geosearch": "^3.11.0",
    "leaflet-search": "^4.0.0",
    "leaflet-search-control": "^2.1.2",
    "leaflet.markercluster": "^1.5.3",
1

There are 1 answers

0
Destroyer369 On

Finally! Still working without input lag and without errors.

Created CustomJSONProvider

const CustomJSONProvider = {
  search: async ({ query }) => {
    if (!query) {
      return [];
    }

    const searchResults = markerData3.filter(location =>
      location.ident.toLowerCase().includes(query.toLowerCase())
    );

    return searchResults.map(location => ({
      y: parseFloat(location.coordinates.split(',')[1].trim()),
      x: parseFloat(location.coordinates.split(',')[0].trim()),
      label: location.ident,
    }));
  },
};

Full code

import React, { useEffect } from 'react';

import { MapContainer, TileLayer, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-geosearch/dist/geosearch.css';
import { GeoSearchControl } from 'leaflet-geosearch';
import customMarkerIcon from 'assets/img/maps/marker-icon.png';
import markerData3 from 'assets/data/json/airports.json';
import 'leaflet/dist/leaflet.css';


const customIcon = L.icon({
  iconUrl: customMarkerIcon,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
});



const CustomJSONProvider = {
  search: async ({ query }) => {
    if (!query) {
      return [];
    }

    const searchResults = markerData3.filter(location =>
      location.ident.toLowerCase().includes(query.toLowerCase())
    );

    return searchResults.map(location => ({
      y: parseFloat(location.coordinates.split(',')[1].trim()),
      x: parseFloat(location.coordinates.split(',')[0].trim()),
      label: location.ident,
    }));
  },
};

const Search = () => {
  const map = useMap();

  useEffect(() => {
    const searchControl = new GeoSearchControl({
      provider: CustomJSONProvider,
      style: 'bar',
      showMarker: true,
      showPopup: false,
      marker: {
        icon: new L.Icon.Default(),
        draggable: false,
      },
      autoClose: true,
      searchLabel: 'Enter location...',
      retainZoomLevel: false,
      animateZoom: true,
    }).addTo(map);

    return () => {
      map.removeControl(searchControl);
    };
  }, [map]);

  return null;
};


const MapWithMarkers = ({ onMarkerClick }) => {
  const map = useMap();
  const markerClusterGroupRef = L.markerClusterGroup();

  useEffect(() => {
    // Clear the existing markers from the cluster group
    markerClusterGroupRef.clearLayers();

    // Add new markers to the cluster group
    markerData3.forEach(marker => {
      const [lng, lat] = marker.coordinates.split(',').map(coord => parseFloat(coord.trim()));
      const leafletMarker = L.marker([lat, lng], { icon: customIcon });

      // Attach a click event listener to the marker
      leafletMarker.on('click', () => {
        onMarkerClick(`${marker.iso_country} - ${marker.name} - ${marker.ident}`);
      });

      const popupContent = `<strong>${marker.ident}</strong><br>${marker.name}`;
      leafletMarker.bindPopup(popupContent);
      markerClusterGroupRef.addLayer(leafletMarker);
    });

    // Add the updated cluster group to the map
    map.addLayer(markerClusterGroupRef);

    // Clean up when the component unmounts
    return () => {
      map.removeLayer(markerClusterGroupRef);
    };
  }, [map, onMarkerClick]);

  return null;
};


const AirportsMapNewAircraftSection = ({ mapType, onMarkerClick }) => {

  // Callback function to handle marker clicks
  const handleMarkerClick = (basePoint) => {
    onMarkerClick(basePoint)
  };

  return (
    <div className="AirportsMap" style={{ width: '100%'}}>
      <MapContainer center={[56.9235, 23.9710]} zoom={4} maxZoom={18} style={{ height: '500px', width: '100%', borderRadius: "0px 0px 10px 10px"}}>
        <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
        <MapWithMarkers onMarkerClick={handleMarkerClick}/>
        <Search />
      </MapContainer>
    </div>
  );
};


export default AirportsMapNewAircraftSection;

The url that helped me

https://smeijer.github.io/leaflet-geosearch/providers/custom-providers