/**
* @module nyc/ol/MapLocator
*/
import OlFormatGeoJson from 'ol/format/GeoJSON'
import OlStyleStyle from 'ol/style/Style'
import OlStyleIcon from 'ol/style/Icon'
import OlSourceVector from 'ol/source/Vector'
import OlLayerVector from 'ol/layer/Vector'
import OlFeature from 'ol/Feature'
import OlGeomPoint from 'ol/geom/Point'
import NycMapLocator from 'nyc/MapLocator'
import FeatureTip from 'nyc/ol/FeatureTip'
/**
* @desc A class for managing map location
* @public
* @class
* @extends module:nyc/MapLocator~MapLocator
*/
class MapLocator extends NycMapLocator {
/**
* @desc Create an instance of MapLocator
* @public
* @constructor
* @param {module:nyc/ol/MapLocator~MapLocator.Options} options Constructor options
*/
constructor(options) {
super()
/**
* @desc The layer on which to render locations
* @public
* @member {ol.layer.Vector}
*/
this.layer = null
/**
* @private
* @member {ol.source.Vector}
*/
this.source = null
/**
* @private
* @member {ol.Map}
*/
this.map = options.map
/**
* @private
* @member {ol.View}
*/
this.view = this.map.getView()
/**
* @private
* @member {number}
*/
this.zoom = options.zoom !== undefined ? options.zoom : NycMapLocator.ZOOM_LEVEL
/**
* @private
* @member {ol.format.GeoJSON}
*/
this.format = new OlFormatGeoJson()
/**
* @private
* @member {module:nyc/ol/FeatureTip~FeatureTip}
*/
this.tip = null
this.createLayer(options.style)
}
/**
* @desc Zoom to the provided location then optionally invoke a callback function
* @public
* @override
* @method
* @param {module:nyc/Locator~Locator.Result} data The location to which the map will be oriented
* @param {module:nyc/MapLocator~MapLocator#zoomLocationCallback=} callback The function to call after the locator has zoomed to the location
*/
zoomLocation(data, callback) {
const map = this.map
const view = this.view
const source = this.source
const feature = this.feature(data)
if (feature) {
const geom = feature.getGeometry()
source.clear()
source.addFeature(feature)
if (callback) {
map.once('moveend', callback)
}
if (geom.getType() === 'Point') {
view.animate({center: data.coordinate, zoom: this.zoom})
} else {
view.fit(geom.getExtent(), {size: map.getSize(), duration: 500})
}
}
}
/**
* @desc Set the location to the provided location without moving the map
* @public
* @override
* @method
* @param {module:nyc/Locator~Locator.Result} data The location to which the map will be oriented
*/
setLocation(data) {
this.source.clear()
this.source.addFeature(this.feature(data))
}
/**
* @desc Get the projection of the map
* @public
* @override
* @method
* @returns {string} The map projection
*/
getProjection() {
return this.view.getProjection()
}
/**
* @private
* @method
* @param {module:nyc/Locator~Locator.Result} location Location
* @return {ol.Feature} The OpenLayers feature
*/
feature(location) {
const geoJson = location.geometry
const coord = location.coordinate
if (!geoJson && !coord) {
console.error('Unable to map invalid geometry:', location)
} else {
const feature = new OlFeature({name: location.name, isFeature: location.isFeature})
if (geoJson) {
feature.setGeometry(this.format.readGeometry(geoJson))
} else {
feature.setGeometry(new OlGeomPoint(coord))
}
return feature
}
}
/**
* @private
* @method
* @param {ol.style.Style|Array<ol.style.Style>|ol.StyleFunction=} style Style
*/
createLayer(style) {
this.source = new OlSourceVector()
this.layer = new OlLayerVector({
source: this.source,
style: style || MapLocator.LOCATION_STYLE,
zIndex: 10000
})
this.map.addLayer(this.layer)
this.tip = new FeatureTip({
map: this.map,
tips: [{
layer: this.layer,
label: feature => {
return {css: 'nyc-user-location', html: feature.get('name')}
}
}]
})
}
}
/**
* @desc Object type to hold constructor options for {@link module:nyc/ol/MapLocator~MapLocator}
* @public
* @typedef {Object}
* @property {ol.Map} map The map on which location will be managed
* @property {ol.style.Style=} style The style for the layer on which user-specified locations will be displayed
* @property {number} [zoom={@link module:nyc/MapLocator~MapLocator.ZOOM_LEVEL}] The zoom level used when locating coordinate
*/
MapLocator.Options
/**
* @desc The marker icon style for user location
* @public
* @const {ol.style.Style}
*/
MapLocator.LOCATION_STYLE = new OlStyleStyle({
image: new OlStyleIcon({
scale: 48 / 512,
imgSize: [1024, 1024],
src: 'data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%221024%22%20height%3D%221024%22%3E%3Cfilter%20id%3D%22filter%22%20style%3D%22color-interpolation-filters%3AsRGB%22%20x%3D%22-0.035750399%22%20width%3D%221.0715008%22%20y%3D%22-0.03625311%22%20%20height%3D%221.0725062%22%3E%3CfeGaussianBlur%20stdDeviation%3D%226.0015266%22%2F%3E%3C%2Ffilter%3E%3Cpath%20style%3D%22opacity%3A0.1%3Bfill%3A%23000000%3Bfill-opacity%3A1%3Bstroke%3A%23000000%3Bstroke-width%3A10%3Bstroke-opacity%3A1%3Bfilter%3Aurl(%23filter)%22%20d%3D%22M%20868.91078%2C141.10492%20C%20806.53392%2C79.855774%20695.84806%2C89.539532%20621.70905%2C162.73227%20547.57005%2C235.92501%20492.08486%2C476.05139%20510.9101%2C494.53629%20529.73535%2C513.02119%20773.473%2C457.74384%20847.612%2C384.5511%20921.751%2C311.35836%20931.28764%2C202.35407%20868.91078%2C141.10492%20Z%20M%20645.16036%2C361.99953%20c%20-41.52967%2C-40.77885%20-35.13918%2C-113.58881%2014.19918%2C-162.29746%2049.33837%2C-48.70864%20123.27149%2C-55.19708%20164.80115%2C-14.41823%2041.52967%2C40.77885%2035.13918%2C113.58881%20-14.19918%2C162.29745%20-49.33837%2C48.70864%20-123.27149%2C55.19709%20-164.80115%2C14.41824%20z%22%2F%3E%3Cpath%20style%3D%22fill%3A%23000%3Bstroke%3A%23fff%3Bstroke-width%3A20%22%20d%3D%22m%20512%2C25.6%20c%20-95.4279%2C0%20-172.8%2C77.3721%20-172.8%2C172.8%200%2C95.4279%20144%2C288%20172.8%2C288%2028.8%2C0%20172.8%2C-192.5721%20172.8%2C-288%20C%20684.8%2C102.9721%20607.4279%2C25.6%20512%2C25.6%20Z%20m%200%2C288%20c%20-63.5346%2C0%20-115.2%2C-51.6942%20-115.2%2C-115.2%200%2C-63.5058%2051.6654%2C-115.2%20115.2%2C-115.2%2063.5346%2C0%20115.2%2C51.6942%20115.2%2C115.2%200%2C63.5058%20-51.6654%2C115.2%20-115.2%2C115.2%20z%22%2F%3E%3C%2Fsvg%3E'
})
})
export default MapLocator