Skip to content

Commit

Permalink
feat: bing Maps layer
Browse files Browse the repository at this point in the history
  • Loading branch information
turban committed Jan 15, 2020
1 parent 20c7d23 commit e86d51a
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 109 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@turf/area": "^6.0.1",
"@turf/bbox": "^6.0.1",
"@turf/circle": "^6.0.1",
"fetch-jsonp": "^1.1.3",
"lodash.throttle": "^4.1.1",
"mapbox-gl": "^1.6.1",
"mapboxgl-spiderifier": "^1.0.9",
Expand Down
7 changes: 7 additions & 0 deletions src/Map.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}

#dhis2-map-container .bing-maps-logo {
position: absolute;
bottom: 0;
left: 0;
}

#dhis2-map-container .mapboxgl-popup em {
font-style: normal;
font-weight: bold;
Expand All @@ -25,3 +31,4 @@
font-weight: bold;
padding-right: 5px;
}

21 changes: 16 additions & 5 deletions src/Map.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mapboxgl from 'mapbox-gl'
import { Map, AttributionControl, Popup } from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css'
import { Evented } from 'mapbox-gl'
import getControl from './controls'
Expand All @@ -8,7 +8,7 @@ import { getBoundsFromLayers } from './utils/geometry'
import syncMaps from './utils/sync'
import './Map.css'

export class Map extends Evented {
export class MapGL extends Evented {
// Returns true if the layer type is supported
static hasLayerSupport(type) {
return !!layerTypes[type]
Expand All @@ -17,7 +17,7 @@ export class Map extends Evented {
constructor(el) {
super()

this._mapgl = new mapboxgl.Map({
this._mapgl = new Map({
container: el,
style: {
version: 8,
Expand All @@ -26,8 +26,12 @@ export class Map extends Evented {
glyphs: 'http://fonts.openmaptiles.org/{fontstack}/{range}.pbf', // TODO: Host ourseleves
},
maxZoom: 18,
attributionControl: false,
})

this._attributionControl = new AttributionControl()
this._mapgl.addControl(this._attributionControl)

this._mapgl.on('load', evt => this.fire('ready', this))
this._mapgl.on('click', evt => this.onClick(evt))
this._mapgl.on('contextmenu', evt => this.onContextMenu(evt))
Expand All @@ -37,6 +41,8 @@ export class Map extends Evented {

this._layers = []
this._controls = {}

// console.log('AttributionControl', this._attributionControl);
}

fitBounds(bounds) {
Expand Down Expand Up @@ -261,7 +267,7 @@ export class Map extends Evented {
}

openPopup(content, lnglat, onClose, offset) {
this._popup = new mapboxgl.Popup({
this._popup = new Popup({
offset: offset,
maxWidth: 'auto',
})
Expand All @@ -281,6 +287,11 @@ export class Map extends Evented {
}
}

// Only called within the API
_updateAttributions() {
this._attributionControl._updateAttributions()
}

_createClickEvent(evt) {
const { lngLat, originalEvent } = evt
const type = 'click'
Expand All @@ -295,4 +306,4 @@ export class Map extends Evented {
}
}

export default Map
export default MapGL
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
* Wrapper around Mapbox GL JS for DHIS2 Maps
*/

import { Map } from './Map'
import Map from './Map'

export default Map
96 changes: 78 additions & 18 deletions src/layers/BingLayer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fetchJsonp from 'fetch-jsonp'
import Layer from './Layer'
import { fetchJsonp } from '../utils/jsonp'
import { bboxIntersect } from '../utils/geo'

const key = 'AotYGLQC0RDcofHC5pWLaW7k854n-6T9mTunsev9LEFwVqGaVnG8b4KERNY9PeKA' // TODO: Don't push!

Expand All @@ -8,19 +9,27 @@ const key = 'AotYGLQC0RDcofHC5pWLaW7k854n-6T9mTunsev9LEFwVqGaVnG8b4KERNY9PeKA' /
// https://github.com/mapbox/mapbox-gl-js/issues/4137
// https://github.com/mapbox/mapbox-gl-native/issues/4653
// https://github.com/digidem/leaflet-bing-layer
// TODO: Support for different locales // mkt={culture}
// https://github.com/shramov/leaflet-plugins/blob/master/layer/tile/Bing.md
class BingLayer extends Layer {
async createSource() {
const { imageUrl, imageUrlSubdomains } = await this.loadMetaData()
const tiles = imageUrlSubdomains.map(subdomain =>
imageUrl.replace('{subdomain}', subdomain)
)
const {
imageUrl,
imageUrlSubdomains,
imageryProviders,
brandLogoUri,
} = await this.loadMetaData()

this._brandLogoUri = brandLogoUri
this._imageryProviders = imageryProviders

console.log(imageUrl)

this.setSource(this.getId(), {
type: 'raster',
tiles,
tileSize: 256,
attribution: '',
tiles: imageUrlSubdomains.map(
subdomain => imageUrl.replace('{subdomain}', subdomain) // + '&dpi=d2&device=mobile' // TODO
),
tileSize: 256, // default is 512
})
}

Expand All @@ -34,17 +43,34 @@ class BingLayer extends Layer {

async addTo(map) {
await this.createSource()

this.createLayer()

super.addTo(map)

this.getMapGL().on('moveend', this.updateAttribution)
this.updateAttribution()
this.addBingMapsLogo()
}

// TODO: Called before map is added
setIndex = () => {}
onRemove() {
const mapgl = this.getMapGL()

mapgl.off('moveend', this.updateAttribution)

if (this._brandLogoImg) {
mapgl.getContainer().removeChild(this._brandLogoImg)
}
}

async loadMetaData() {
const { style = 'Road' } = this.options
const metaDataUrl = `http://dev.virtualearth.net/REST/V1/Imagery/Metadata/${style}?output=json&include=ImageryProviders&key=${key}`

// https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes
const culture = 'en-GB'

// https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata
const metaDataUrl = `http://dev.virtualearth.net/REST/V1/Imagery/Metadata/${style}?output=json&include=ImageryProviders&culture=${culture}&key=${key}`

return fetchJsonp(metaDataUrl, { jsonpCallback: 'jsonp' })
.then(response => response.json())
Expand All @@ -60,17 +86,51 @@ class BingLayer extends Layer {
)
}

const resource = metaData.resourceSets[0].resources[0]
const { imageUrl, imageryProviders, imageUrlSubdomains } = resource
const { brandLogoUri, resourceSets } = metaData

return {
imageUrl,
imageryProviders,
imageUrlSubdomains,
brandLogoUri,
...resourceSets[0].resources[0],
}
}

updateAttribution() {}
addBingMapsLogo() {
const container = this.getMap().getContainer()
const img = document.createElement('img')

img.src = this._brandLogoUri
img.className = 'bing-maps-logo'

container.appendChild(img)

this._brandLogoImg = img
}

getAttribution() {
const mapgl = this.getMapGL()
const [lngLat1, lngLat2] = mapgl.getBounds().toArray()
const mapBbox = [...lngLat1.reverse(), ...lngLat2.reverse()]
const mapZoom = mapgl.getZoom() < 1 ? 1 : mapgl.getZoom()

const providers = this._imageryProviders.filter(({ coverageAreas }) =>
coverageAreas.some(
({ bbox, zoomMin, zoomMax }) =>
bboxIntersect(bbox, mapBbox) &&
mapZoom >= zoomMin &&
mapZoom <= zoomMax
)
)

return providers.map(p => p.attribution).join(', ')
}

updateAttribution = () => {
const source = this.getMapGL().getSource(this.getId())

source.attribution = this.getAttribution()

this.getMap()._updateAttributions()
}
}

export default BingLayer
7 changes: 6 additions & 1 deletion src/layers/Layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,12 @@ class Layer extends Evented {

setIndex(index = 0) {
this.options.index = index
this.getMap().orderLayers()

const map = this.getMap()

if (map) {
map.orderLayers()
}
}

getIndex() {
Expand Down
29 changes: 19 additions & 10 deletions src/utils/geo.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
const earthRadius = 6378137

const tile2lon = (x, z) => (x / Math.pow(2, z)) * 360 - 180

const tile2lat = (y, z) => {
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z)
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
}

// Returns resolution in meters at zoom
export const getZoomResolution = zoom =>
(2 * Math.PI * earthRadius) / 256 / Math.pow(2, zoom)

// Returns lng/lat bounds for a tile
export const getTileBBox = (x, y, z) => {
var e = tile2lon(x + 1, z)
var w = tile2lon(x, z)
var s = tile2lat(y + 1, z)
var n = tile2lat(y, z)
const e = tile2lon(x + 1, z)
const w = tile2lon(x, z)
const s = tile2lat(y + 1, z)
const n = tile2lat(y, z)
return [w, s, e, n].join(',')
}

const tile2lon = (x, z) => (x / Math.pow(2, z)) * 360 - 180

const tile2lat = (y, z) => {
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z)
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
}
// Returns true if two bbox'es intersects
export const bboxIntersect = (bbox1, bbox2) =>
!(
bbox1[0] > bbox2[2] ||
bbox1[2] < bbox2[0] ||
bbox1[3] < bbox2[1] ||
bbox1[1] > bbox2[3]
)
74 changes: 0 additions & 74 deletions src/utils/jsonp.js

This file was deleted.

5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2663,6 +2663,11 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"

fetch-jsonp@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fetch-jsonp/-/fetch-jsonp-1.1.3.tgz#9eb9e585ba08aaf700563538d17bbebbcd5a3db2"
integrity sha1-nrnlhboIqvcAVjU40Xu+u81aPbI=

figures@^1.5.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
Expand Down

0 comments on commit e86d51a

Please sign in to comment.