import olVectorSource from 'ol/source/Vector'
import olStyle from 'ol/style/Style'
import olCircleStyle from 'ol/style/Circle'
import olFill from 'ol/style/Fill'
import olStroke from 'ol/style/Stroke'
import Map from 'ol/Map'
import GeoJSON from 'ol/format/GeoJSON'
import KML from 'ol/format/KML'
import { VectorLayer } from 'classes'
import ugh from 'ugh'
const getFeaturesFromDataSet = (map, dataSet) => {
const mapProjection = map.getView().getProjection().getCode()
const opts = { featureProjection: mapProjection }
try {
const geoJson = new GeoJSON(opts)
const features = geoJson.readFeatures(dataSet)
return features
} catch (e) { /* must not be JSON */ }
try {
const kml = new KML({ extractStyles: false })
const features = kml.readFeatures(dataSet, opts)
return features
} catch (e) { /* must not be KML */ }
// not a supported data format, return empty features array
return []
}
const urlValidator = string => {
try {
new URL(string) // eslint-disable-line no-new
} catch (_) {
return false
}
return true
}
/**
* Async fetch for data layers - supports geojson, kml
* @function
* @category DataLayers
* @since 0.8.0
* @param {ol.Map} map - reference to the openlayer map object
* @param {String} query - url string pointing to geojson/kml file or the geojson/kml file itself
* @param {Object} [opts] - Object of optional params
* @param {Boolean} [opts.addToMap] - opt out of adding the layer to the map (default true)
* @param {String} [opts.style] - style object used to apply styles to the layer
* @param {String} [opts.title] - set custom title on layer (default: "Data Layer")
* @returns {ol.Layer} Promise that resolves with the newly created data layer
*/
export const loadDataLayer = async (map, query, optsArg = {}) => {
if (!(map instanceof Map)) return ugh.throw('\'loadDataLayer\' requires a valid openlayers map as the first argument')
const style = { fill: { color: '#fefefe91' }, stroke: { color: '#3399cd', width: 2 }, ...optsArg.style }
const opts = { addToMap: true, title: 'Data Layer', ...optsArg }
// getFeaturesFromDataSet returns empty array if query arg is not a supported data type (ex. url)
let features = getFeaturesFromDataSet(map, query)
const isValidUrl = urlValidator(query)
if (!features.length && isValidUrl) {
// query is an endpoint to fetch valid data set
let response
try {
const request = await fetch(query)
response = await request.text() // either xml or json
} catch (e) {
return ugh.throw(`'loadDataLayer' error when making network request:`, e)
}
let dataSet
try {
// geojson
dataSet = JSON.parse(response)
} catch (e) {
// kml
const parser = new DOMParser()
dataSet = parser.parseFromString(response, 'text/xml')
}
features = getFeaturesFromDataSet(map, dataSet)
} else if (!features.length) {
// catch malformed queries here
return ugh.throw(`'loadDataLayer' recieved invalid query: '${query}' as second argument`)
}
// create the layer and add features
const source = new olVectorSource()
const layer = new VectorLayer({ source })
// set attribute for LayerPanel title
layer.set('title', opts.title)
if (isValidUrl) layer.set('_ol_kit_data_source', query)
features.forEach(feature => feature.set('_ol_kit_parent', layer))
source.addFeatures(features)
// style based on opts
layer.setStyle(
new olStyle({
fill: new olFill(style.fill),
stroke: new olStroke(style.stroke),
image: new olCircleStyle({
radius: 4,
fill: new olFill(style.fill),
stroke: new olStroke(style.stroke)
})
})
)
// conditionally add this new layer to the map
if (opts.addToMap) map.addLayer(layer)
return layer
}