diff --git a/example/custom-source.js b/example/custom-source.js new file mode 100644 index 0000000..e056a1f --- /dev/null +++ b/example/custom-source.js @@ -0,0 +1,109 @@ +import mapboxgl from 'mapbox-gl' +import MapboxAccessibility from '../' + +mapboxgl.accessToken = 'pk.eyJ1IjoidHJpc3RlbiIsImEiOiJjajZoOXU4Z2kwNnppMnlxd2d6bTFvZ2xtIn0.PP7AoUakMfeqdXFHdsY0oA'; + +const div = document.body.appendChild(document.createElement('div')); +div.style.position = 'absolute'; +div.style.top = 0; +div.style.right = 0; +div.style.left = 0; +div.style.bottom = 0; + +const map = new mapboxgl.Map({ + container: div, + style: 'mapbox://styles/mapbox/streets-v9', + center: [-93.87, 41.65], + zoom: 3 +}); + +map.on('load', () => { + map.addSource('data', { + 'type': 'geojson', + 'data': { + 'type': 'FeatureCollection', + 'features': [{ + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [-77.03238901390978, 38.913188059745586] + }, + 'properties': { + 'title': 'Mapbox DC', + 'icon': 'monument' + } + }, { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [-122.414, 37.776] + }, + 'properties': { + 'title': 'Mapbox SF', + 'icon': 'harbor' + } + }, { + 'type': 'Feature', + 'id': 0, + 'properties': { + 'title': 'Maine', + }, + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[[-67.13734351262877, 45.137451890638886], + [-66.96466, 44.8097], + [-68.03252, 44.3252], + [-69.06, 43.98], + [-70.11617, 43.68405], + [-70.64573401557249, 43.090083319667144], + [-70.75102474636725, 43.08003225358635], + [-70.79761105007827, 43.21973948828747], + [-70.98176001655037, 43.36789581966826], + [-70.94416541205806, 43.46633942318431], + [-71.08482, 45.3052400000002], + [-70.6600225491012, 45.46022288673396], + [-70.30495378282376, 45.914794623389355], + [-70.00014034695016, 46.69317088478567], + [-69.23708614772835, 47.44777598732787], + [-68.90478084987546, 47.184794623394396], + [-68.23430497910454, 47.35462921812177], + [-67.79035274928509, 47.066248887716995], + [-67.79141211614706, 45.702585354182816], + [-67.13734351262877, 45.137451890638886]]] + } + }] + } + }); + map.addLayer({ + 'id': 'points', + 'type': 'symbol', + 'source': 'data', + 'layout': { + 'icon-image': '{icon}-15', + 'text-field': '{title}', + 'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'], + 'text-offset': [0, 0.6], + 'text-anchor': 'top' + }, + 'filter': ['==', '$type', 'Point'] + }); + map.addLayer({ + 'id': 'polygons', + 'type': 'fill', + 'source': 'data', + 'layout': {}, + 'paint': { + 'fill-color': '#088', + 'fill-opacity': 0.8 + }, + 'filter': ['==', '$type', 'Polygon'] + }); + + map.addControl(new MapboxAccessibility({ + accessibleLabelProperty: 'title', + layers: [ + 'points', + 'polygons' + ] + })); +}); diff --git a/index.js b/index.js index a59d822..efa22b8 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict'; import xtend from 'xtend'; +import bbox from '@turf/bbox'; export default class MapboxAccessibility { constructor(options) { @@ -34,8 +35,7 @@ export default class MapboxAccessibility { queryFeatures = () => { this.features = this.map.queryRenderedFeatures({ layers: this.options.layers }); this.features.map((feature) => { - const { width, height } = this.options; - const position = this.map.project(feature.geometry.coordinates); + let { width, height } = this.options; const label = feature.properties[this.options.accessibleLabelProperty]; feature.marker = document.createElement('button'); @@ -43,6 +43,23 @@ export default class MapboxAccessibility { feature.marker.setAttribute('title', label); feature.marker.setAttribute('tabindex', 0); feature.marker.style.display = 'block'; + + let position; + if (feature.geometry.type === 'Point') { + position = this.map.project(feature.geometry.coordinates); + } else { + const featureBbox = bbox(feature); + const bl = this.map.project([featureBbox[0], featureBbox[1]]); + const tr = this.map.project([featureBbox[2], featureBbox[3]]); + + width = Math.abs(tr.x - bl.x); + height = Math.abs(tr.y - bl.y); + + position = { + x: ((tr.x + bl.x) / 2), + y: ((tr.y + bl.y) / 2), + }; + } feature.marker.style.width = `${width}px`; feature.marker.style.height = `${height}px`; feature.marker.style.transform = `translate(-50%, -50%) translate(${position.x}px, ${position.y}px)`; diff --git a/package-lock.json b/package-lock.json index c6bc254..324d891 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,28 @@ "integrity": "sha1-wd5CkwgUJNo6wwwjr6hQrxAZu1Q=", "dev": true }, + "@turf/bbox": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.0.1.tgz", + "integrity": "sha512-EGgaRLettBG25Iyx7VyUINsPpVj1x3nFQFiGS3ER8KCI1MximzNLsam3eXRabqQDjyAKyAE1bJ4EZEpGvspQxw==", + "requires": { + "@turf/helpers": "6.1.3", + "@turf/meta": "6.0.2" + } + }, + "@turf/helpers": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.3.tgz", + "integrity": "sha512-nBbSk/hi7WC+5F/CKbfkB42ckIm7jms79DutLCD9Q5AYVITNxitP5uPrgE+Sf4VPFTBaEe0e8yzFCRzH9+SAzw==" + }, + "@turf/meta": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz", + "integrity": "sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA==", + "requires": { + "@turf/helpers": "6.1.3" + } + }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", diff --git a/package.json b/package.json index bfb0b58..d7bbacd 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "mapbox-gl": "^0.44.2" }, "dependencies": { + "@turf/bbox": "^6.0.1", "xtend": "^4.0.1" } }