如何使用 Mapbox 将可随时更改的图像显示为地图?
How to display an image that can be changed at any time as a map using Mapbox?
在我的应用程序中,用户必须能够向地图添加平面图。用户使用简单的表单上传 PNG 图片,然后这张图片必须显示为地图背景。所以我们这里有:
- 用户可以随时更改的PNG图像。我从服务器收到这张图片的 URL。
- 图像的尺寸(宽度和高度)。
Mapbox 有 sources
和 layers
,我需要利用它们将此图像添加为地图背景,而实际世界地图根本不能显示。
我见过很多这样的例子(这个用的是mapbox-gl-js
):
...
"sources": {
"overlay": {
"type": "image",
"url": "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif",
"coordinates": [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
}
},
...
还有这个(这个使用 deck.gl
层):
import DeckGL from '@deck.gl/react';
import {BitmapLayer} from '@deck.gl/layers';
const App = ({data, viewport}) => {
const layer = new BitmapLayer({
id: 'bitmap-layer',
bounds: [-122.5190, 37.7045, -122.355, 37.829],
image: 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/website/sf-districts.png'
});
return (<DeckGL {...viewport} layers={[layer]} />);
}
但它们始终具有图像的预定义 坐标。因为用户可以随时更新我的图像,所以我需要以某种方式 计算这些坐标,同时考虑图像的纵横比。我数学不太好,你能帮帮我吗? deck.gl
能够指定层的坐标系甚至 4x4 投影矩阵,但我不太明白如何在我的案例中使用它。
好的,我解决了这个问题。解决方案的关键是停止尝试让计划填满整个地图,而是调整图像大小使其非常小,并将其放置在地图上的 [0, 0]
坐标处。这样我们就可以假设这里的世界是平的,根本不用担心它的曲率。
所以当地图加载时,我正在加载图像以获取其尺寸:
this.map.current.on('load', () => {
const img = new Image()
const self = this
img.addEventListener('load', function () {
// ...
})
img.src = planUrl
})
完成后,我将在图像的 load
处理程序中调整图像大小并为其创建 LngLatBounds
。我只是在这里简单地除以宽度和高度以获得图像的 lng
和 lat
— 它们在 lng
和 lat
上都小于 1,所以我不'认为地球的曲率会是这个层面的问题:
img.addEventListener('load', function () {
const maxWidth = 1
const maxHeight = 0.5
const [width, height] = resizeImage(
this.naturalWidth,
this.naturalHeight,
maxWidth,
maxHeight
)
const sw = [-width / 2, -height / 2]
const ne = [width / 2, height / 2]
const bounds = new LngLatBounds(sw, ne)
// ...
})
然后我在地图上添加一个带有平面图的源和一个显示平面图的图层:
self.map.current.addSource('plan', {
type: 'image',
url: planUrl,
coordinates: [
bounds.getNorthWest().toArray(),
bounds.getNorthEast().toArray(),
bounds.getSouthEast().toArray(),
bounds.getSouthWest().toArray()
]
})
self.map.current.addLayer({
id: 'image',
source: 'plan',
type: 'raster'
})
然后我将地图边界设置为图像边界大小的 2 倍,这样平面图周围就会有很好的填充。
const mapBounds = new LngLatBounds(sw, ne)
mapBounds.extend(new LngLatBounds([-width, -height], [width, height]))
self.map.current.setMaxBounds(mapBounds)
可能有比这个更好的解决方案,但看起来它对我来说效果很好。希望它能帮助其他人。
在我的应用程序中,用户必须能够向地图添加平面图。用户使用简单的表单上传 PNG 图片,然后这张图片必须显示为地图背景。所以我们这里有:
- 用户可以随时更改的PNG图像。我从服务器收到这张图片的 URL。
- 图像的尺寸(宽度和高度)。
Mapbox 有 sources
和 layers
,我需要利用它们将此图像添加为地图背景,而实际世界地图根本不能显示。
我见过很多这样的例子(这个用的是mapbox-gl-js
):
...
"sources": {
"overlay": {
"type": "image",
"url": "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif",
"coordinates": [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
}
},
...
还有这个(这个使用 deck.gl
层):
import DeckGL from '@deck.gl/react';
import {BitmapLayer} from '@deck.gl/layers';
const App = ({data, viewport}) => {
const layer = new BitmapLayer({
id: 'bitmap-layer',
bounds: [-122.5190, 37.7045, -122.355, 37.829],
image: 'https://raw.githubusercontent.com/uber-common/deck.gl-data/master/website/sf-districts.png'
});
return (<DeckGL {...viewport} layers={[layer]} />);
}
但它们始终具有图像的预定义 坐标。因为用户可以随时更新我的图像,所以我需要以某种方式 计算这些坐标,同时考虑图像的纵横比。我数学不太好,你能帮帮我吗? deck.gl
能够指定层的坐标系甚至 4x4 投影矩阵,但我不太明白如何在我的案例中使用它。
好的,我解决了这个问题。解决方案的关键是停止尝试让计划填满整个地图,而是调整图像大小使其非常小,并将其放置在地图上的 [0, 0]
坐标处。这样我们就可以假设这里的世界是平的,根本不用担心它的曲率。
所以当地图加载时,我正在加载图像以获取其尺寸:
this.map.current.on('load', () => {
const img = new Image()
const self = this
img.addEventListener('load', function () {
// ...
})
img.src = planUrl
})
完成后,我将在图像的 load
处理程序中调整图像大小并为其创建 LngLatBounds
。我只是在这里简单地除以宽度和高度以获得图像的 lng
和 lat
— 它们在 lng
和 lat
上都小于 1,所以我不'认为地球的曲率会是这个层面的问题:
img.addEventListener('load', function () {
const maxWidth = 1
const maxHeight = 0.5
const [width, height] = resizeImage(
this.naturalWidth,
this.naturalHeight,
maxWidth,
maxHeight
)
const sw = [-width / 2, -height / 2]
const ne = [width / 2, height / 2]
const bounds = new LngLatBounds(sw, ne)
// ...
})
然后我在地图上添加一个带有平面图的源和一个显示平面图的图层:
self.map.current.addSource('plan', {
type: 'image',
url: planUrl,
coordinates: [
bounds.getNorthWest().toArray(),
bounds.getNorthEast().toArray(),
bounds.getSouthEast().toArray(),
bounds.getSouthWest().toArray()
]
})
self.map.current.addLayer({
id: 'image',
source: 'plan',
type: 'raster'
})
然后我将地图边界设置为图像边界大小的 2 倍,这样平面图周围就会有很好的填充。
const mapBounds = new LngLatBounds(sw, ne)
mapBounds.extend(new LngLatBounds([-width, -height], [width, height]))
self.map.current.setMaxBounds(mapBounds)
可能有比这个更好的解决方案,但看起来它对我来说效果很好。希望它能帮助其他人。