Как отобразить изображение, которое можно изменить в любое время, как карту с помощью 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, но я не совсем понимаю, как я могу использовать это для своего случая.


person Randex    schedule 30.06.2019    source источник
comment
Это не то, что вы можете рассчитать. Вам нужно узнать от пользователя, где в реальном мире находится его план этажа. Или, если вам на самом деле не нужны реальные географические координаты (в этом случае я не совсем понимаю, почему вы используете картографическую библиотеку), вы можете использовать любые координаты, которые вам нравятся.   -  person Steve Bennett    schedule 01.07.2019


Ответы (1)


Хорошо, я решил проблему. Ключом к решению было перестать пытаться заполнить планом всю карту, а вместо этого изменить размер изображения, сделать его действительно маленьким и поместить его в координаты [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 изображения — они будут меньше 1 как для lng, так и для lat, поэтому я не думаю, что кривизна земли будет проблемой на этом уровне:

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)

Вероятно, есть лучшие решения для этого, чем это, но похоже, что оно отлично работает для меня. Надеюсь, это поможет кому-то еще.

person Randex    schedule 01.07.2019