src/sources/SourceServerVector.js
import $ from 'jquery'
import GeoJSON from 'ol/format/GeoJSON'
import KML from 'ol/format/KML'
import { transformExtent } from 'ol/proj'
import VectorSource from 'ol/source/Vector'
import { copy, take } from '../utilitiesObject'
import { Debug } from '../Debug'
/**
* @typedef {module:ol/source/Vector~Options} SourceServerVectorOptions
* @property {string} type the format to use
* @property {URLLike} url
* @property {module:ol/proj~ProjectionLike} [urlProjection] coordinates will be inserted into the url in this format.
* defaults to the sourceProjection
* @property {StyleLike} [defaultStyle] a default style to fallback to
* @property {boolean} [extractStyles=true] if styles should get extracted from the KML
* @property {boolean} [cache=] true, false for dataType 'script' and 'jsonp'
* @property {number} [refresh] if set the layer will refresh itself in the specified time (in ms)
* @property {L10N} localiser
* @property {boolean} localised
* @property {Styling} styling
*/
/**
* A custom Source class handling Vector Sources.
*
* Let you set the loading loadingStrategy, if a proxy is used and you should specify in which format it comes in.
*
* This class defines a custom loader function which makes it possible to use different loading strategies.
*/
export class SourceServerVector extends VectorSource {
/**
* @param {SourceServerVectorOptions} [options={}]
*/
constructor (options = {}) {
const parentOptions = copy(options)
const urlTemplate = take(options, 'url')
const type = take(options, 'type') || ''
parentOptions.loader = (...args) => this.loader(...args)
super(parentOptions)
/**
* @type {string}
* @private
*/
this.strategyType_ = options.loadingStrategyType
/**
* @type {L10N}
* @private
*/
this.localiser_ = options.localiser
this.localised_ = options.localised || false
/**
* @type {URL}
* @protected
*/
this.urlTemplate = urlTemplate
/**
* @type {string}
* @private
*/
this.type_ = type
const formatOptions = {}
if (options.hasOwnProperty('defaultStyle')) {
formatOptions.defaultStyle = options.defaultStyle
}
if (options.hasOwnProperty('extractStyles')) {
formatOptions.extractStyles = options.extractStyles
}
if (options.hasOwnProperty('showPointNames')) {
formatOptions.showPointNames = options.showPointNames
} else {
formatOptions.showPointNames = false
}
let formatProjection
switch (this.type_) {
case 'KML':
this.format_ = new KML(formatOptions)
this.dataType_ = 'text xml' // for $.ajax (GET-request)
formatProjection = 'EPSG:4326'
break
case 'GeoJSON':
this.format_ = new GeoJSON(formatOptions)
this.dataType_ = 'text json' // for $.ajax (GET-request)
formatProjection = 'EPSG:4326'
break
default:
throw new Error(`${this.type_} is not supported by SourceServerVector!`)
}
/**
* @type {number}
* @private
*/
this.refresh_ = 0
if (options.hasOwnProperty('refresh')) {
this.setRefresh(options.refresh)
}
this.refreshTimeoutId_ = null
/**
* indicates if the source needs to be emptied
* @type {boolean}
* @private
*/
this.doClear_ = false
/**
* @type {module:ol/proj~ProjectionLike}
* @private
*/
this.urlProjection_ = options.urlProjection || formatProjection
}
/**
* @param {module:ol/extent~Extent} extent
* @param {number} resolution
* @param {module:ol/proj~Projection} projection
*/
loader (extent, resolution, projection) {
const url = this.urlTemplate.clone()
if (this.strategyType_ === 'BBOX' || this.strategyType_ === 'TILE') {
const transformedExtent = transformExtent(extent, projection, this.urlProjection_)
if (url.url.includes('{bbox')) {
Debug.warn('The {bbox...} url parameters are deprecated, please use {minx}, {miny}, {maxx}, {maxy} instead.')
url.url.replace(/{bboxleft}/g, '{minx}')
url.url.replace(/{bboxbottom}/g, '{miny}')
url.url.replace(/{bboxright}/g, '{maxx}')
url.url.replace(/{bboxtop}/g, '{maxy}')
}
url.expandTemplate('minx', transformedExtent[0].toString())
.expandTemplate('miny', transformedExtent[1].toString())
.expandTemplate('maxx', transformedExtent[2].toString())
.expandTemplate('maxy', transformedExtent[3].toString())
.expandTemplate('resolution', resolution.toString(), false)
}
if (this.refresh_) {
if (this.refreshTimeoutId_) {
clearTimeout(this.refreshTimeoutId_)
}
this.refreshTimeoutId_ = setTimeout(() => {
this.doClear_ = true // clears the source
this.loader(extent, resolution, projection) // calls the loader recursively
}, this.refresh_)
}
if (this.localised_) {
url.cache = false
}
const finalUrl = url.finalize()
$.ajax({
url: finalUrl,
dataType: this.dataType_,
beforeSend: () => this.dispatchEvent('vectorloadstart'),
success: (response) => {
// processing urls in the xml-Data (e.g. for images)
if (url.useProxy && /xml$/.test(this.dataType_)) {
response = this.addProxyToHrefTags(response)
}
if (this.doClear_) {
this.clear(true)
this.doClear_ = false
}
const features = this.format_.readFeatures(response, { featureProjection: projection })
this.addFeatures(features)
this.dispatchEvent('vectorloadend')
},
error: () => {
Debug.error(`Getting Feature resource failed with url ${finalUrl}`)
this.dispatchEvent('vectorloaderror')
},
headers: this.localiser_ ? {
'Accept-Language': this.localiser_.getCurrentLang()
} : {}
})
}
/**
* This sets the refresh rate. A value of 0 or smaller turns refresh off.
* @param {number} refresh
*/
setRefresh (refresh) {
this.refresh_ = (refresh > 0) ? refresh : 0
}
/**
* makes all urls in href-tags inside of a xmlDocument use the proxy address.
* this function needs to extended with all Tags which could contain urls
* @param {HTMLElement} text an xml-Document
* @returns {HTMLElement} the xmlDocument
*/
addProxyToHrefTags (text) {
const hrefTags = text.getElementsByTagName('href') // not working in IE11
let i, ii
for (i = 0, ii = hrefTags.length; i < ii; i++) {
if (hrefTags[i].textContent) {
hrefTags[i].textContent = this.urlTemplate.useProxyFor(hrefTags[i].textContent.trim()).finalize()
} else if (hrefTags[i].innerHTML) {
hrefTags[i].innerHTML = this.urlTemplate.useProxyFor(hrefTags[i].innerHTML.trim()).finalize()
} else {
throw new Error("Can't prepend proxy inside KML (textContent and innerHTML missing)")
}
}
return text
}
/**
* @returns {URL}
*/
getUrlTemplate () {
return this.urlTemplate
}
/**
* @param {URL} urlTemplate
*/
setUrlTemplate (urlTemplate) {
this.urlTemplate = urlTemplate
}
}