LeafletJS 地图

Leaflet 是领先的开源 JavaScript 库,用于创建移动友好的交互式地图。它只有大约 42 KB 的 JS 代码,包含了大多数开发者需要的全部地图功能。

Leaflet 在设计时充分考虑了简洁性、性能和易用性。它在所有主要桌面和移动平台上都能高效运行,可以通过大量插件进行扩展,拥有美观、易用且文档完善的 API,以及简单易读的源代码,让人乐于为其贡献代码。 LeafletJS 地图网站

使用

您可以使用以下 Qwik 启动脚本轻松添加 LeafletJS 地图

npm run qwik add leaflet-map

之前的命令会更新您的应用程序,添加必要的依赖项。

它还会将新文件添加到您的项目文件夹中

  • src/helpers/boundary-box.tsx: 检查地图区域边界函数。
  • src/models/location.ts: 用于定义位置信息元素的模型,在 props 中使用。
  • src/models/map.ts: 用于定义地图信息的模型,在 props 中使用。
  • src/components/leaflet-map/index.tsx: Leaflet 地图简单地图功能组件。
  • src/routes/basic-map/index.tsx: 使用演示数据使用 Leaflet 地图组件的示例

示例

主组件配置地图,包括初始位置和要加载的标记组。此设置允许您创建动态且交互式的地图,可以轻松地配置和扩展,使用不同的位置和标记。

这是一个示例

import {
  component$,
  noSerialize,
  useSignal,
  useStyles$,
  useVisibleTask$,
  type Signal,
} from '@builder.io/qwik';
import * as L from 'leaflet';
import leafletStyles from 'leaflet/dist/leaflet.css?inline';
 
// Sample data json and geojson
 
export const fvg: any = {
  type: 'FeatureCollection',
  name: 'FVG_line_0_001',
  crs: { type: 'name', properties: { name: 'urn:ogc:def:crs:OGC:1.3:CRS84' } },
  features: [
    {
      type: 'Feature',
      properties: { ID_OGG: '08020060000', NAME: 'GEOJSON NAME' },
      geometry: {
        type: 'MultiLineString',
        coordinates: [
          [
            [12.4188, 46.3528],
            [12.4178, 46.3547],
            [12.4284, 46.3517],
            [12.4425, 46.3599],
            [12.4488, 46.3605],
            [12.4554, 46.3652],
            [12.4552, 46.3672],
            [12.4513, 46.3706],
          ],
        ],
      },
    },
  ],
};
 
const markers: Record<string, MarkersProps[]> = {
  FDA: [
    {
      name: "Terzo d'Aquileia",
      label: 'TRZ',
      lat: '45.770946',
      lon: '13.31338',
    },
    {
      name: 'Musi',
      label: 'MUS',
      lat: '46.312663',
      lon: '13.274682',
    },
  ],
  FVG: [
    {
      name: 'Borgo Grotta Gigante',
      label: 'BGG',
      lat: '45.709385',
      lon: '13.764681',
    },
    {
      name: 'Muggia',
      label: 'MGG',
      lat: '45.610495',
      lon: '13.752682',
    },
  ],
};
 
export default component$(() => {
  useStyles$(
    leafletStyles +
      `
    .marker-label {
      color: red;
      font-weight: 700;
    }
  `
  );
 
  const groupSig = useSignal('FDA');
  const currentLocation = useSignal<LocationsProps>({
    name: 'Udine',
    point: [46.06600881056668, 13.237724558490601],
    zoom: 10,
    marker: true,
  });
 
  return (
    <>
      Change markers:{'  '}
      <select name="group" class="leaflet-ctrl" bind:value={groupSig}>
        <option value="FDA">FDA</option>
        <option value="FVG">FVG</option>
      </select>
      <LeafletMap
        location={currentLocation}
        markers={markers[groupSig.value]}
        group={groupSig}
      ></LeafletMap>
    </>
  );
});
 
// The properties (props) used in the `LeafletMap` component and other related components are defined as follows:
 
export interface MapProps {
  location: Signal<LocationsProps>;
  markers?: MarkersProps[];
  group?: Signal<string>;
}
 
export interface LocationsProps {
  name: string;
  point: [number, number];
  zoom: number;
  marker: boolean;
}
 
export interface MarkersProps {
  name: string;
  label: string;
  lat: string;
  lon: string;
}
 
/*
The `LeafletMap` component leverages the Leaflet library to render an interactive map. 
This component can be configured with various properties (props) to set the central location, add markers, and draw boundaries.
In the `LeafletMap` component, both the location and the group signal are tracked.
This ensures that when the signal changes, the server function is called, and the map is updated with the new data.
*/
 
export const LeafletMap = component$<MapProps>(
  ({ location, markers, group }) => {
    const mapContainerSig = useSignal<L.Map>();
 
    useVisibleTask$(async ({ track }) => {
      track(location);
      group && track(group);
 
      if (mapContainerSig.value) {
        mapContainerSig.value.remove();
      }
 
      // center location
      const { value: locationData } = location;
      const centerPosition = locationData.point;
 
      // layers
      const markersLayer = new L.LayerGroup();
      const bordersLayer = new L.LayerGroup();
 
      // map
      const map = L.map('map', {
        layers: [markersLayer, bordersLayer],
      }).setView(centerPosition, locationData.zoom || 14);
      L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution:
          '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
      }).addTo(map);
 
      // center position marker
 
      const qwikMarker = L.divIcon({
        html: ` 
          <svg xmlns="http://www.w3.org/2000/svg" width="30.12" height="32" viewBox="0 0 256 272">
            <path fill="#18B6F6"
              d="m224.803 271.548l-48.76-48.483l-.744.107v-.532L71.606 120.252l25.55-24.667l-15.01-86.12l-71.222 88.247c-12.136 12.226-14.372 32.109-5.642 46.781l44.5 73.788c6.813 11.376 19.163 18.18 32.47 18.074l22.038-.213z" />
            <path fill="#AC7EF4"
              d="m251.414 96.01l-9.795-18.075l-5.11-9.25l-2.023-3.615l-.212.213l-26.829-46.463C200.738 7.125 188.176-.105 174.55 0l-23.527.639l-70.158.213c-13.307.106-25.444 7.123-32.151 18.5l-42.69 84.632L82.353 9.25l100.073 109.937l-17.779 17.968l10.646 86.015l.107-.213v.213h-.213l.213.212l8.304 8.081l40.348 39.445c1.704 1.595 4.472-.318 3.3-2.339l-24.911-49.014l43.436-80.273l1.383-1.595c.533-.638 1.065-1.276 1.491-1.914c8.517-11.589 9.688-27.112 2.662-39.764" />
            <path fill="#FFF" d="M182.746 118.763L82.353 9.358l14.266 85.695l-25.55 24.773L175.08 223.065l-9.368-85.696z" />
          </svg>
        `,
        className: '',
        iconSize: [24, 40],
      });
 
      locationData.marker &&
        L.marker(centerPosition, { icon: qwikMarker })
          .bindPopup(`Udine`)
          .addTo(map);
 
      // add boundaries to map
      L.geoJSON(fvg, { style: { color: '#005DA4' } }).addTo(bordersLayer);
 
      // add markers to map
      const markersList = await markers;
      markersList &&
        markersList.map((m) => {
          const myIcon = L.divIcon({
            className: 'marker-point',
            html: `<div class="marker-label" title="${m.name}" >${m.label}</div>`,
          });
          L.marker([+m.lat, +m.lon], { icon: myIcon }).addTo(markersLayer);
        });
 
      mapContainerSig.value = noSerialize(map);
    });
 
    return <div id="map" style={{ height: '25rem' }}></div>;
  }
);

关于 LeafletJS 地图的有趣信息

官方

  • 教程: 使用文档创建新功能的逐步示例。 参考.
  • 文档: 使用 LeafletJS 所需的所有信息。 参考

贡献者

感谢所有帮助改进本文档的贡献者!

  • mugan86
  • igorbabko
  • anartzdev
  • gimonaa
  • gioboa