/* Copyright (c) 2017 Jean-Marc VIGLINO, released under the CeCILL-B license (French BSD license) (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt). */ import ol_style_Style from 'ol/style/Style.js' import ol_style_Stroke from 'ol/style/Stroke.js' import ol_style_Fill from 'ol/style/Fill.js' import ol_style_Text from 'ol/style/Text.js' import {boundingExtent as ol_extent_boundingExtent} from 'ol/extent.js' import ol_control_CanvasBase from './CanvasBase.js' /** * Draw a grid reference on the map and add an index. * * @constructor * @extends {ol_control_CanvasBase} * @fires select * @param {Object=} Control options. * @param {ol_style_Style} options.style Style to use for drawing the grid (stroke and text), default black. * @param {number} options.maxResolution max resolution to display the graticule * @param {ol.extent} options.extent extent of the grid, required * @param {ol.size} options.size number of lines and cols, required * @param {number} options.margin margin to display text (in px), default 0px * @param {ol.source.Vector} options.source source to use for the index, default none (use setIndex to reset the index) * @param {string | function} options.property a property to display in the index or a function that takes a feature and return the name to display in the index, default 'name'. * @param {function|undefined} options.sortFeatures sort function to sort 2 features in the index, default sort on property option * @param {function|undefined} options.indexTitle a function that takes a feature and return the title to display in the index, default the first letter of property option * @param {string} options.filterLabel label to display in the search bar, default 'filter' */ var ol_control_GridReference = class olcontrolGridReference extends ol_control_CanvasBase { constructor(options) { options = options || {} // Initialize parent var elt = document.createElement("div") elt.className = (!options.target ? "ol-control " : "") + "ol-gridreference ol-unselectable " + (options.className || "") options.style = options.style || new ol_style_Style({ stroke: new ol_style_Stroke({ color: "#000", width: 1 }), text: new ol_style_Text({ font: "bold 14px Arial", stroke: new ol_style_Stroke({ color: "#fff", width: 2 }), fill: new ol_style_Fill({ color: "#000" }), }) }) super({ element: elt, target: options.target, style: options.style }) if (typeof (options.property) == 'function'){ this.getFeatureName = options.property } if (typeof (options.sortFeatures) == 'function') { this.sortFeatures = options.sortFeatures } if (typeof (options.indexTitle) == 'function') { this.indexTitle = options.indexTitle } // Set index using the source this.source_ = options.source if (options.source) { this.setIndex(options.source.getFeatures(), options) // reload on ready options.source.once('change', function () { if (options.source.getState() === 'ready') { this.setIndex(options.source.getFeatures(), options) } }.bind(this)) } // Options this.set('maxResolution', options.maxResolution || Infinity) this.set('extent', options.extent) this.set('size', options.size) this.set('margin', options.margin || 0) this.set('property', options.property || 'name') this.set('filterLabel', options.filterLabel || 'filter') } /** * Set the map instance the control is associated with. * @param {ol_Map} map The map instance. */ setMap(map) { super.setMap(map) this.setIndex(this.source_.getFeatures()) } /** Returns the text to be displayed in the index * @param {ol.Feature} f the feature * @return {string} the text to be displayed in the index * @api */ getFeatureName(f) { return f.get(this.get('property') || 'name') } /** Sort function * @param {ol.Feature} a first feature * @param {ol.Feature} b second feature * @return {Number} 0 if a==b, -1 if ab * @api */ sortFeatures(a, b) { return (this.getFeatureName(a) == this.getFeatureName(b)) ? 0 : (this.getFeatureName(a) < this.getFeatureName(b)) ? -1 : 1 } /** Get the feature title * @param {ol.Feature} f * @return the first letter of the eature name (getFeatureName) * @api */ indexTitle(f) { return this.getFeatureName(f).charAt(0) } /** Display features in the index * @param { Array | ol.Collection } features */ setIndex(features) { if (!this.getMap()) return var self = this if (features.getArray) features = features.getArray() features.sort(function (a, b) { return self.sortFeatures(a, b) }) this.element.innerHTML = "" var elt = this.element var search = document.createElement("input") search.setAttribute('type', 'search') search.setAttribute('placeholder', this.get('filterLabel') || 'filter') var searchKeyupFunction = function () { var v = this.value.replace(/^\*/, '') // console.log(v) var r = new RegExp(v, 'i') var list = ul.querySelectorAll('li') Array.prototype.forEach.call(list, function (li) { if (li.classList.contains('ol-title')) { li.style.display = '' } else { if (r.test(li.querySelector('.ol-name').textContent)) li.style.display = '' else li.style.display = 'none' } }) Array.prototype.forEach.call(ul.querySelectorAll("li.ol-title"), function (li) { var nextVisible var start = false for (var i = 0; i < list.length; i++) { if (start) { if (list[i].classList.contains('ol-title')) break if (!list[i].style.display) { nextVisible = list[i] break } } if (list[i] === li) start = true } if (nextVisible) li.style.display = '' else li.style.display = 'none' }) } search.addEventListener('search', searchKeyupFunction) search.addEventListener('keyup', searchKeyupFunction) elt.appendChild(search) var ul = document.createElement("ul") elt.appendChild(ul) var r, title features.forEach(function (feat) { r = this.getReference(feat.getGeometry().getFirstCoordinate()) if (r) { var name = this.getFeatureName(feat) var c = this.indexTitle(feat) if (c != title) { var li_title = document.createElement("li") li_title.classList.add('ol-title') li_title.textContent = c ul.appendChild(li_title) } title = c var li_ref_name = document.createElement("li") var span_name = document.createElement("span") span_name.classList.add("ol-name") span_name.textContent = name li_ref_name.appendChild(span_name) var span_ref = document.createElement("span") span_ref.classList.add("ol-ref") span_ref.textContent = r li_ref_name.appendChild(span_ref) var feature = feat li_ref_name.addEventListener("click", function () { this.dispatchEvent({ type: "select", feature: feature }) }.bind(this)) ul.appendChild(li_ref_name) } }.bind(this)) } /** Get reference for a coord * @param {ol.coordinate} coords * @return {string} the reference */ getReference(coords) { if (!this.getMap()) return var extent = this.get('extent') var size = this.get('size') var dx = Math.floor((coords[0] - extent[0]) / (extent[2] - extent[0]) * size[0]) if (dx < 0 || dx >= size[0]) return "" var dy = Math.floor((extent[3] - coords[1]) / (extent[3] - extent[1]) * size[1]) if (dy < 0 || dy >= size[1]) return "" return this.getHIndex(dx) + this.getVIndex(dy) } /** Get vertical index (0,1,2,3...) * @param {number} index * @returns {string} * @api */ getVIndex(index) { return index } /** Get horizontal index (A,B,C...) * @param {number} index * @returns {string} * @api */ getHIndex(index) { return String.fromCharCode(65 + index) } /** Draw the grid * @param {ol.event} e postcompose event * @private */ _draw(e) { if (this.get('maxResolution') < e.frameState.viewState.resolution) return var ctx = this.getContext(e) var canvas = ctx.canvas var ratio = e.frameState.pixelRatio var w = canvas.width / ratio var h = canvas.height / ratio var extent = this.get('extent') var size = this.get('size') var map = this.getMap() var ex = ol_extent_boundingExtent([map.getPixelFromCoordinate([extent[0], extent[1]]), map.getPixelFromCoordinate([extent[2], extent[3]])]) var p0 = [ex[0], ex[1]] var p1 = [ex[2], ex[3]] var dx = (p1[0] - p0[0]) / size[0] var dy = (p1[1] - p0[1]) / size[1] ctx.save() var margin = this.get('margin') ctx.scale(ratio, ratio) ctx.strokeStyle = this.getStroke().getColor() ctx.lineWidth = this.getStroke().getWidth() // Draw grid ctx.beginPath() var i for (i = 0; i <= size[0]; i++) { ctx.moveTo(p0[0] + i * dx, p0[1]) ctx.lineTo(p0[0] + i * dx, p1[1]) } for (i = 0; i <= size[1]; i++) { ctx.moveTo(p0[0], p0[1] + i * dy) ctx.lineTo(p1[0], p0[1] + i * dy) } ctx.stroke() // Draw text ctx.font = this.getTextFont() ctx.fillStyle = this.getTextFill().getColor() ctx.strokeStyle = this.getTextStroke().getColor() var lw = ctx.lineWidth = this.getTextStroke().getWidth() var spacing = margin + lw ctx.textAlign = 'center' var letter, x, y for (i = 0; i < size[0]; i++) { letter = this.getHIndex(i) x = p0[0] + i * dx + dx / 2 y = p0[1] - spacing if (y < 0) { y = spacing ctx.textBaseline = 'hanging' } else ctx.textBaseline = 'alphabetic' ctx.strokeText(letter, x, y) ctx.fillText(letter, x, y) y = p1[1] + spacing if (y > h) { y = h - spacing ctx.textBaseline = 'alphabetic' } else ctx.textBaseline = 'hanging' ctx.strokeText(letter, x, y) ctx.fillText(letter, x, y) } ctx.textBaseline = 'middle' for (i = 0; i < size[1]; i++) { letter = this.getVIndex(i) y = p0[1] + i * dy + dy / 2 ctx.textAlign = 'right' x = p0[0] - spacing if (x < 0) { x = spacing ctx.textAlign = 'left' } else ctx.textAlign = 'right' ctx.strokeText(letter, x, y) ctx.fillText(letter, x, y) x = p1[0] + spacing if (x > w) { x = w - spacing ctx.textAlign = 'right' } else ctx.textAlign = 'left' ctx.strokeText(letter, x, y) ctx.fillText(letter, x, y) } ctx.restore() } } export default ol_control_GridReference