Leaflet标记在生产环境中未找到

33

我在使用leaflet时遇到了问题。

在开发中一切都正常,但是在生产环境中,我的应用程序无法定位marker-icon.pngmarker-shadow.png图像。

它正在寻找路径assets/station/images/marker-icon.png

在我的html.erb文件中,Leaflet js是这样包含的

<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.js"></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.css" />

如果有人可以帮忙!


2
Leaflet的哪个版本?如何部署Leaflet JS和CSS文件?这些文件来自哪里(框架、手动下载等)? - IvanSanchez
我已经编辑了我的帖子! - maluss
请查看下面的答案,如果有帮助,请点赞或标记为已接受。 - Alex G Rice
12个回答

58

Leaflet存在一个已知的bug,根本问题是在打包过程中错误地引用了Leaflet图标的位置。

您可以通过验证此参数(在运行时)来验证这是否是您的问题:L.Icon.Default.prototype._getIconUrl()
正确的值应该是<some_directory>/leaflet/dist/images/
但是,如果您遇到此bug,则其值为:data:image/png;base64,iVBO....K5CYII=")undefined

根据您使用的捆绑加载器(Vanila WebPack、Angular-Cli-WebPack的超集等),有不同的解决方案(解决方法)。

您可以在此处查看原始问题(以及根据您的捆绑加载器提供的不同解决方案):
https://github.com/Leaflet/Leaflet/issues/4968

如果您正在使用Angular-Cli,则此解决方案适用于您。 在设置Maker之前添加此代码:

import { icon, Marker } from 'leaflet';
const iconRetinaUrl = 'assets/marker-icon-2x.png';
const iconUrl = 'assets/marker-icon.png';
const shadowUrl = 'assets/marker-shadow.png';
const iconDefault = icon({
  iconRetinaUrl,
  iconUrl,
  shadowUrl,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  tooltipAnchor: [16, -28],
  shadowSize: [41, 41]
});
Marker.prototype.options.icon = iconDefault;

(此代码将更改损坏的标记URL,以从您的资产文件夹中获取有效的图像。)

并在您的 angular.json 中添加此代码(适用于 Angular 版本>=6.x),或在您的 angular-cli.json 中添加此代码(适用于 Angular 版本<=5.x):

"assets":
[
   "src/favicon.ico",
   "src/assets",
   {
      "glob": "**/*",
      "input": "node_modules/leaflet/dist/images/", // you may need to change this path, according to your files structure
      "output": "./assets/"
   }
] ...

(此代码将原始标记图像复制到/assets文件夹中,以便angular-cli可以加载它们)


虽然这可能是一个不错的Angular解释,但请注意OP从未提到使用JS捆绑器,他们不正确的路径不是数据URI,并且包括来自CDN的旧(0.x)Leaflet资产。 - ghybs
1
此修复适用于 vite (rollup) 和 vue3 构建。进入 node_modules/leaflet/dist/images 目录并获取这三个标记 png 文件。将它们放在 public/assets 中并进行构建。 - evendiagram
1
应该是被接受的答案。这个方法非常有效。谢谢! - Dinesh Shekhawat

24
import "leaflet/dist/images/marker-shadow.png";

如果使用webpack,只需导入图像


1
你确定这个回答提供了其他答案没有提供的新东西吗? - user3956566
3
这是一个好的一行代码修复吗? - user43284
1
这个解决方案很简单,适用于所有版本的Angular,非常棒。 - Juanma Font
太棒了。这解决了我的问题。我正在使用ngx-leaflet。谢谢! - Kelvin Cayman
我同意用户43284的观点 - 更少、更简单的代码在我看来更好、更干净。我可以确认这适用于Angular10。 - Philipp Doerner

23

以下代码对我有用:

delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
  iconUrl: require("leaflet/dist/images/marker-icon.png"),
  shadowUrl: require("leaflet/dist/images/marker-shadow.png")
});

1
我不清楚,你在哪里建议添加这段代码? - edjm
1
@NashGC,你能详细说明一下你是如何在Nuxt中实现的吗?我将leaflet本地导入到一个单独的组件中,而不是使用插件,你把这个代码块放在哪里? - Thorvald
1
@NashGC 这对我来说确实有效.. 但是我的标记在Safari浏览器中消失了。有什么办法可以解决吗?我使用的是leaflet 1.7.1,vue2-leaflet 2.7.0。 - Thorvald
1
使用 TypeScript 和 create-react-app 时出现错误 Property '_getIconUrl' does not exist on type 'Default' - Denis Molodtsov
1
谢谢,这对我也有用...但没有使用require函数。我只是添加了iconUrl: "/webroot/assets/js/leaflet/images/marker-icon.png"等等...当然要适应我的基本url。 - dlg_
显示剩余2条评论

8

在Angular 10中工作

对我来说,将PNG文件复制粘贴到assets文件夹并执行一个命令即可。

ngOnInit() { L.Icon.Default.ImagePath = "assets/leaflet/" }

对于Leaflet 1.7和Angular 13

ngOnInit() { L.Icon.Default.imagePath = "assets/leaflet/" }


3
你可以更改leaflet css中图标的当前路径。因此,请勿使用url leaflet.css,下载文件并进行更改。
.leaflet-default-icon-path {
    background-image: url(https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png);
}

谢谢!这正是我所需要的,而且是一个非常干净的解决方案。 - ItaiRoded

3

对于使用Vue3、TypeScript和vue-cli + Webpack 5的项目,我采用了一种解决方法,即在created()钩子中显式定义所需的图标。

    created() {
        L.Marker.prototype.options.icon = L.icon({
            iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
            iconUrl: require("leaflet/dist/images/marker-icon.png"),
            shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
            iconSize: [25, 41],
            iconAnchor: [12, 41],
            popupAnchor: [1, -34],
            tooltipAnchor: [16, -28],
            shadowSize: [41, 41],
        });
    },

然后我在mounted()钩子中创建我的地图:

    mounted() {
        const map:L.Map = L.map('map').setView([51.505, -0.09], 13);
        L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
            maxZoom: 18,
            id: 'mapbox/streets-v11',
            tileSize: 512,
            zoomOffset: -1,
            accessToken: process.env.VUE_APP_MAPBOX_TOKEN
        }).addTo(map);
    }

注意:我将我的Mapbox存储在以VUE_APP_为前缀的环境变量中。


1

最便携的解决方案(适用于Rails 7)是:

在您的Javascript控制器中,在通常的Leaflet地图配置代码之前放置以下代码:

import { Controller } from "@hotwired/stimulus"

import L from "leaflet";    
delete L.Icon.Default.prototype._getIconUrl;

export default class extends Controller {
  connect() {

    L.Icon.Default.mergeOptions({
      iconRetinaUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAABSCAMAAAAhFXfZAAAC91BMVEVMaXEzeak2f7I4g7g3g7cua5gzeKg8hJo3grY4g7c3grU0gLI2frE0daAubJc2gbQwd6QzeKk2gLMtd5sxdKIua5g1frA2f7IydaM0e6w2fq41fK01eqo3grgubJgta5cxdKI1f7AydaQydaMxc6EubJgvbJkwcZ4ubZkwcJwubZgubJcydqUydKIxapgubJctbJcubZcubJcvbJYubJcvbZkubJctbJctbZcubJg2f7AubJcrbZcubJcubJcua5g3grY0fq8ubJcubJdEkdEwhsw6i88vhswuhcsuhMtBjMgthMsrg8srgss6is8qgcs8i9A9iMYtg8spgcoogMo7hcMngMonf8olfso4gr8kfck5iM8jfMk4iM8he8k1fro7itAgesk2hs8eecgzfLcofssdeMg0hc4cd8g2hcsxeLQbdsgZdcgxeLImfcszhM0vda4xgckzhM4xg84wf8Yxgs4udKsvfcQucqhUndROmdM1fK0wcZ8vb5w0eqpQm9MzeKhXoNVcpdYydKNWn9VZotVKltJFjsIwcJ1Rms9OlslLmtH///8+kc9epdYzd6dbo9VHkMM2f7FHmNBClM8ydqVcpNY9hro3gLM9hLczealQmcw3fa46f7A8gLMxc6I3eagyc6FIldJMl9JSnNRSntNNl9JPnNJFi75UnM9ZodVKksg8kM45jc09e6ZHltFBk883gbRBh7pDk9EwcaBzn784g7dKkcY2i81Om9M7j85Llc81is09g7Q4grY/j9A0eqxKmdFFltBEjcXf6fFImdBCiLxJl9FGlNFBi78yiMxVndEvbpo6js74+vx+psPP3+o/ks5HkcpGmNCjwdZCkNDM3ehYoNJEls+lxNkxh8xHks0+jdC1zd5Lg6r+/v/H2ufz9/o3jM3t8/edvdM/k89Th61OiLBSjbZklbaTt9BfptdjmL1AicBHj8hGk9FAgK1dkLNTjLRekrdClc/k7fM0icy0y9tgp9c4jc2NtM9Dlc8zicxeXZn3AAAAQ3RSTlMAHDdTb4yPA+LtnEQmC4L2EmHqB7XA0d0sr478x4/Yd5i1zOfyPkf1sLVq4Nh3FvjxopQ2/STNuFzUwFIwxKaejILpIBEV9wAABhVJREFUeF6s1NdyFEcY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',
      iconUrl:       'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=',
      shadowUrl:     'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAQAAAACach9AAACMUlEQVR4Ae3ShY7jQBAE0Aoz/f9/HTMzhg1zrdKUrJbdx+Kd2nD8VNudfsL/Th///dyQN2TH6f3y/BGpC379rV+S+qqetBOxImNQXL8JCAr2V4iMQXHGNJxeCfZXhSRBcQMfvkOWUdtfzlLgAENmZDcmo2TVmt8OSM2eXxBp3DjHSMFutqS7SbmemzBiR+xpKCNUIRkdkkYxhAkyGoBvyQFEJEefwSmmvBfJuJ6aKqKWnAkvGZOaZXTUgFqYULWNSHUckZuR1HIIimUExutRxwzOLROIG4vKmCKQt364mIlhSyzAf1m9lHZHJZrlAOMMztRRiKimp/rpdJDc9Awry5xTZCte7FHtuS8wJgeYGrex28xNTd086Dik7vUMscQOa8y4DoGtCCSkAKlNwpgNtphjrC6MIHUkR6YWxxs6Sc5xqn222mmCRFzIt8lEdKx+ikCtg91qS2WpwVfBelJCiQJwvzixfI9cxZQWgiSJelKnwBElKYtDOb2MFbhmUigbReQBV0Cg4+qMXSxXSyGUn4UbF8l+7qdSGnTC0XLCmahIgUHLhLOhpVCtw4CzYXvLQWQbJNmxoCsOKAxSgBJno75avolkRw8iIAFcsdc02e9iyCd8tHwmeSSoKTowIgvscSGZUOA7PuCN5b2BX9mQM7S0wYhMNU74zgsPBj3HU7wguAfnxxjFQGBE6pwN+GjME9zHY7zGp8wVxMShYX9NXvEWD3HbwJf4giO4CFIQxXScH1/TM+04kkBiAAAAAElFTkSuQmCC'
    });


...
... // [Put the rest of the usual Leaflet map config code here]
...

1
我在使用Parcel作为打包工具与TypeScriptLeaflet v1.4(通过npm安装,以及其类型定义)结合时遇到了类似的问题,并通过Gil的答案以稍微不同的方式解决了它。
import 'leaflet/dist/leaflet.css';
import 'leaflet-draw/dist/leaflet.draw.css';
import L, {
  LatLngExpression,
  FeatureGroup,
  TileLayerOptions,
  LayerEvent,
  LeafletMouseEvent,
  Marker,
  Layer,
  icon,
  LayerGroup,
  GeoJSON
} from 'leaflet';
import 'leaflet-draw';
import iconRetinaUrl from './assets/marker-icon-2x.png';
import iconUrl from './assets/marker-icon.png';
import shadowUrl from './assets/marker-shadow.png';

// Assign the imported image assets before you do anything with Leaflet.
Marker.prototype.options.icon = icon({
  iconRetinaUrl,
  iconUrl,
  shadowUrl,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  tooltipAnchor: [16, -28],
  shadowSize: [41, 41],
});

在另一个文件中,我添加了必要的声明,以便TypeScript允许我导入png图片,例如:

declare module "*.png" {
  const content: string;
  export default content;
}

此外需要注意的是,如果您使用了需要访问这些图片的Leaflet插件,您可能需要显式地分配它,例如Leaflet绘图插件也需要它。示例:
        map.addLayer(drawLayer);
        const drawControl = new L.Control.Draw({
          draw: {
            circle: false,
            circlemarker: false,
            marker: {
              icon: Marker.prototype.options.icon, // Assign icon explicitly
            },
          },
          edit: {
            featureGroup: drawLayer,
          },
        });
        map.addControl(drawControl);
        map.on(L.Draw.Event.CREATED, event => {
          const layer = (event as LayerEvent).layer;
          drawLayer.addLayer(layer);
        });

1
答案是使用leaflet css更新您的index.html。
 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
   integrity="sha512-xodZBdNfgdfTC5n17Xddt2atTPudf34rdf45erfggrrddfdffdfEdf1HxjVMSvLVW9odfdfcdfdf3rfddfdferdfdfqdfgdfUKLsCC5gdfdfdCXdbqCmblFFAshOdfdfMAS6/kddderfdfeqq/sMZMZ19scRerdfggdfddffd4PsZChyertfgRRRSR7A=="
   crossorigin=""/>

将CSS放在头部。

0

@GilEpshtain的答案对我在Angular 9.1.9上起了作用。

额外说明-为了调试目的,您应该能够确认由@GilEpshtain指定的angular.json#assets更改已生效,方法是将其中一个图标URL直接放入浏览器中。例如,http://localhost:4200/assets/marker-icon.png应该会显示标记图标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接