Henrique
Henrique

Reputation: 31

React-leaflet + leaflet.elevation usage

I'm posting to see if anyone can give me some light on this 'problem/issue'. I'm using react-leaflet and also leaflet-elevation, a 'plug-in' that creates an elevation graph and also adds some extra things

As you can see from the image below, the L.control.elevation is being interpreted (I've imported it like this: import "leaflet.elevation/dist/Leaflet.Elevation-0.0.2.min.js";

Console.log of the function.

but compiling I get this:

Error message from the console.

Thanks.

EDITING: Adding some more info

So in terms of code:

Map Component (updated)

import React from "react";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import L from "leaflet";
import 'leaflet.elevation/dist/Leaflet.Elevation-0.0.2.min.js'
import "./Map.css";

const Map = () => {

  function InitProfile() {
    var elevation_options = {
      // Default chart colors: theme lime-theme, magenta-theme, ...
      theme: "lightblue-theme",
      // Chart container outside/inside map container
      detached: true,
      // if (detached), the elevation chart container
      elevationDiv: "#elevation-div",
      // if (!detached) autohide chart profile on chart mouseleave
      autohide: false,
      // if (!detached) initial state of chart profile control
      collapsed: false,
      // if (!detached) control position on one of map corners
      position: "topright",
      // Toggle close icon visibility
      closeBtn: true,
      // Autoupdate map center on chart mouseover.
      followMarker: true,
      // Autoupdate map bounds on chart update.
      autofitBounds: true,
      // Chart distance/elevation units.
      imperial: false,
      // [Lat, Long] vs [Long, Lat] points. (leaflet default: [Lat, Long])
      reverseCoords: false,
      // Acceleration chart profile: true || "summary" || "disabled" || false
      acceleration: false,
      // Slope chart profile: true || "summary" || "disabled" || false
      slope: false,
      // Speed chart profile: true || "summary" || "disabled" || false
      speed: false,
      // Altitude chart profile: true || "summary" || "disabled" || false
      altitude: true,
      // Display time info: true || "summary" || false
      time: true,
      // Display distance info: true || "summary" || false
      distance: true,
      // Summary track info style: "inline" || "multiline" || false
      summary: "multiline",
      // Download link: "link" || false || "modal"
      downloadLink: "link",
      // Toggle chart ruler filter
      ruler: true,
      // Toggle chart legend filter
      legend: true,
      // Toggle "leaflet-almostover" integration
      almostOver: true,
      // Toggle "leaflet-distance-markers" integration
      distanceMarkers: false,
      // Toggle "leaflet-hotline" integration
      hotline: true,
      // Display track datetimes: true || false
      timestamps: false,
      // Display track waypoints: true || "markers" || "dots" || false
      waypoints: true,
      // Toggle custom waypoint icons: true || { associative array of <sym> tags } || false
      wptIcons: {
        "": L.divIcon({
          className: "elevation-waypoint-marker",
          html: '<i class="elevation-waypoint-icon"></i>',
          iconSize: [30, 30],
          iconAnchor: [8, 30],
        }),
      },
      // Toggle waypoint labels: true || "markers" || "dots" || false
      wptLabels: true,
      // Render chart profiles as Canvas or SVG Paths
      preferCanvas: true,
    };

    // Instantiate map (leaflet-ui).
    var map = useMap();

    // Instantiate elevation control.
    var controlElevation = L.control.elevation(elevation_options).addTo(map);

    // Load track from url (allowed data types: "*.geojson", "*.gpx", "*.tcx")
    // controlElevation.load(
    //   "https://raruto.github.io/leaflet-elevation/examples/via-emilia.gpx"
    // );
  }

  return (
    <MapContainer
      id="map"
      center={[51.505, -0.09]}
      zoom={13}
      scrollWheelZoom={false}
    >
      <InitProfile />
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={[51.505, -0.09]}>
        <Popup>
          A pretty CSS3 popup. <br /> Easily customizable.
        </Popup>
      </Marker>
    </MapContainer>
  );
};

export default Map;

Map.css

#map {
  height: 75vh;
}

App.js

import './App.css';
import Map from "./components/Map"

function App() {
  return (
    <>
      <Map />
    </>
  );
}

export default App;

And the error: (NOW irrelevant, was missing the import for leaflet.elevation)

Uncaught TypeError: leaflet__WEBPACK_IMPORTED_MODULE_1___default(...).control.elevation is not a function
    at InitProfile (Map.jsx:111:1)
    at renderWithHooks (react-dom.development.js:16175:1)
    at mountIndeterminateComponent (react-dom.development.js:20913:1)
    at beginWork (react-dom.development.js:22416:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
    at invokeGuardedCallback (react-dom.development.js:4274:1)
    at beginWork$1 (react-dom.development.js:27405:1)
    at performUnitOfWork (react-dom.development.js:26513:1)
    at workLoopSync (react-dom.development.js:26422:1)

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <!-- leaflet -->
    <style>
      html,
      body,
      #map,
      #elevation-div {
        height: 100%;
        width: 100%;
        padding: 0;
        margin: 0;
      }
      #map {
        height: 75%;
      }
      #elevation-div {
        height: 25%;
        font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
      }
    </style>
    <link
      rel="stylesheet"
      href="https://unpkg.com/[email protected]/dist/leaflet.css"
      integrity="sha512-hoalWLoI8r4UszCkZ5kL8vayOGVae1oxXe/2A4AO6J9+580uKHDO3JdHb7NzwwzK5xr/Fs0W40kiNHxM9vyTtQ=="
      crossorigin=""
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/@raruto/leaflet-elevation@latest/dist/leaflet-elevation.css"
    />
    <!-- Make sure you put this AFTER Leaflet's CSS -->
    <script
      src="https://unpkg.com/[email protected]/dist/leaflet.js"
      integrity="sha512-BB3hKbKWOc9Ez/TAwyWxNXeoV9c1v6FIeYiBieIWkpLjauysF18NzgR1MBNBXf8/KABdlkX68nAhlwcDFLGPCQ=="
      crossorigin=""
    ></script>
    <script src="https://unpkg.com/leaflet-ui@latest/dist/leaflet-ui.js"></script>
    <script src="https://unpkg.com/@raruto/leaflet-elevation@latest/dist/leaflet-elevation.js"></script>
    <!-- end leaflet -->
    <!-- D3js -->
    <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <!-- End D3js -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

I've also had to "npm i" d3 and leaflet.elevation.

The problem at the moment is that the ".load()" function for the L.control.elevation object is not getting recognised

Uncaught TypeError: controlElevation.load is not a function
    at InitProfile (Map.jsx:88:1)
    at renderWithHooks (react-dom.development.js:16175:1)
    at mountIndeterminateComponent (react-dom.development.js:20913:1)
    at beginWork (react-dom.development.js:22416:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
    at invokeGuardedCallback (react-dom.development.js:4274:1)
    at beginWork$1 (react-dom.development.js:27405:1)
    at performUnitOfWork (react-dom.development.js:26513:1)
    at workLoopSync (react-dom.development.js:26422:1)

and this:

App running, but with two instances of the leaflet.elevation.

Upvotes: 2

Views: 1050

Answers (2)

Markov78
Markov78

Reputation: 1

A little bit late on this topic, but this solution should work:

import { useEffect } from "react";
import { MapContainer, TileLayer, useMap } from "react-leaflet";
import L from "leaflet";
import "leaflet.elevation/dist/Leaflet.Elevation-0.0.2.min.js"; // [email protected]
import "./Map.css";

import myGeoJson from "./myGeoJson.json";

function InitProfile() {
  // Instantiate map (leaflet-ui).
  var map = useMap();
  // Instantiate elevation control.
  var elevation_options = {};

  var controlElevation = L.control.elevation(elevation_options).addTo(map);

  useEffect(() => {
    L.geoJson(myGeoJson, {
      onEachFeature: controlElevation.addData.bind(controlElevation),
    }).addTo(map);

    return () => {
      controlElevation.remove();
    };
  }, [map, controlElevation]);
}

export default function Map() {
  return (
    <MapContainer id="map" center={[-44.968683, 168.957614]} zoom={13} scrollWheelZoom={false}>
      <InitProfile />
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
    </MapContainer>
  );
}

Cleaning up the controlElevation should avoid multiple instances of the graph. Only script necessary in the index.html is the D3js, no need to include Leaflet there as it is already loaded in the component.

Upvotes: 0

Dimitris Dermanis
Dimitris Dermanis

Reputation: 43

About the second instance of the elevation chart, you should comment out the React.StrictMode tag in the index.js file, and leave only the App tag. Reproducing answer from here: Why is my React component is rendering twice?

Upvotes: 0

Related Questions