UNPKG

@geoext/geoext

Version:

GeoExt is Open Source and enables building desktop-like GIS applications through the web. It is a JavaScript framework that combines the GIS functionality of OpenLayers with the user interface savvy of the ExtJS library provided by Sencha.

464 lines (446 loc) 12 kB
/* Copyright (c) 2015-2017 The Open Source Geospatial Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * A component that renders a `ol.style.Style` with an optional `ol.Feature`. * * @example preview * var poly = Ext.create('GeoExt.component.FeatureRenderer', { * symbolizers: new ol.style.Style({ * fill: new ol.style.Fill({color: 'red'}) * }) * }); * var line = Ext.create('GeoExt.component.FeatureRenderer', { * symbolizers: new ol.style.Style({ * stroke: new ol.style.Stroke({color: 'orange', width: 3}), * }), * symbolType: 'Line' * }); * var point = Ext.create('GeoExt.component.FeatureRenderer', { * symbolizers: new ol.style.Style({ * image: new ol.style.Circle({ * radius: 7, * fill: new ol.style.Fill({color: 'gray'}), * stroke: new ol.style.Stroke({color: 'black', width: 3}), * }) * }), * symbolType: 'Point' * }); * var star = Ext.create('GeoExt.component.FeatureRenderer', { * symbolizers: new ol.style.Style({ * image: new ol.style.RegularShape({ * fill: new ol.style.Fill({color: 'blue'}), * stroke: new ol.style.Stroke({color: 'green', width: 3}), * points: 7, * radius: 15, * radius2: 7, * angle: 0 * }) * }), * minWidth: 40, * minHeight: 40, * symbolType: 'Point' * }); * Ext.create('Ext.panel.Panel', { * title: 'Rendering of ol.Features in a panel', * items: [poly, line, point, star], * border: false, * renderTo: Ext.getBody() * }); * * @class GeoExt.component.FeatureRenderer */ Ext.define('GeoExt.component.FeatureRenderer', { extend: 'Ext.Component', alias: 'widget.gx_renderer', requires: ['GeoExt.util.Version'], mixins: ['GeoExt.mixin.SymbolCheck'], // <debug> symbols: [ 'ol.extent.getCenter', 'ol.extent.getWidth', 'ol.extent.getHeight', 'ol.Feature', 'ol.Feature#getGeometry', 'ol.Feature#setStyle', 'ol.geom.Geometry#getExtent', 'ol.geom.Point', 'ol.geom.LineString', 'ol.geom.Polygon', 'ol.layer.Vector', 'ol.layer.Vector#getSource', 'ol.Map#getSize', 'ol.Map#getView', 'ol.Map#setView', 'ol.Map#updateSize', 'ol.proj.Projection', 'ol.source.Vector', 'ol.source.Vector#addFeature', 'ol.View', 'ol.View#fit', ], // </debug> /** * Fires when the feature is clicked on. * * @event click * @param {GeoExt.component.FeatureRenderer} renderer The feature renderer. */ config: { /** * Optional class to set on the feature renderer div. * * @cfg {string} */ imgCls: '', /** * The minimum width. * * @cfg {number} */ minWidth: 20, /** * The minimum height. * * @cfg {number} */ minHeight: 20, /** * The resolution for the renderer. * * @cfg {number} */ resolution: 1, /** * Optional vector to be drawn. * * @cfg {ol.Feature} */ feature: undefined, /** * Feature to use for point swatches. Optional. * * @cfg {ol.Feature} */ pointFeature: undefined, /** * Feature to use for line swatches. Optional. * * @cfg {ol.Feature} */ lineFeature: undefined, /** * Feature to use for polygon swatches. Optional. * * @cfg {ol.Feature} */ polygonFeature: undefined, /** * Feature to use for text label swatches. Optional. * * @cfg {ol.Feature} */ textFeature: undefined, /** * An `ol.style.Style` instance or an array of `ol.style.Style` * instances for rendering a feature. If no symbolizers are * provided, the default style from OpenLayers will be used. * * @cfg {Array<ol.style.Style> | ol.style.Style} */ symbolizers: undefined, /** * One of `"Point"`, `"Line"`, `"Polygon"` or `"Text"`. Only relevant * if `feature` is not provided. * * @cfg {string} */ symbolType: 'Polygon', }, inheritableStatics: { /** * Determines the style for the given feature record. * * @param {GeoExt.data.model.Feature} record A feature record to get the * styler for. * @return {Array<ol.style.Style> | ol.style.Style} The style(s) applied to the * given feature record. */ determineStyle: function (record) { const feature = record.getFeature(); return ( feature.getStyle() || feature.getStyleFunction() || (record.store ? record.store.layer.getStyle() : null) ); }, }, /** * Initialize the GeoExt.component.FeatureRenderer. */ initComponent: function () { const me = this; const id = me.getId(); me.autoEl = { id: id, tag: 'div', class: this.getImgCls(), }; if (!me.getLineFeature()) { me.setLineFeature( new ol.Feature({ geometry: new ol.geom.LineString([ [-8, -3], [-3, 3], [3, -3], [8, 3], ]), }), ); } if (!me.getPointFeature()) { me.setPointFeature( new ol.Feature({ geometry: new ol.geom.Point([0, 0]), }), ); } if (!me.getPolygonFeature()) { me.setPolygonFeature( new ol.Feature({ geometry: new ol.geom.Polygon([ [ [-8, -4], [-6, -6], [6, -6], [8, -4], [8, 4], [6, 6], [-6, 6], [-8, 4], ], ]), }), ); } if (!me.getTextFeature()) { me.setTextFeature( new ol.Feature({ geometry: new ol.geom.Point([0, 0]), }), ); } me.map = new ol.Map({ controls: [], interactions: [], layers: [ new ol.layer.Vector({ source: new ol.source.Vector(), }), ], }); const feature = me.getFeature(); if (!feature) { me.setFeature(me['get' + me.getSymbolType() + 'Feature']()); } else { me.applyFeature(feature); } me.callParent(); }, /** * Draw the feature when we are rendered. * * @private */ onRender: function () { this.callParent(arguments); this.drawFeature(); }, /** * After rendering we setup our own custom events using #initCustomEvents. * * @private */ afterRender: function () { this.callParent(arguments); this.initCustomEvents(); }, /** * (Re-)Initializes our custom event listeners, mainly #onClick. * * @private */ initCustomEvents: function () { const me = this; me.clearCustomEvents(); me.el.on('click', me.onClick, me); }, /** * Unbinds previously bound listeners on #el. * * @private */ clearCustomEvents: function () { const el = this.el; if (el && el.clearListeners) { el.clearListeners(); } }, /** * Bound to the click event on the #el, this fires the click event. * * @private */ onClick: function () { this.fireEvent('click', this); }, /** * Private method called during the destroy sequence. * * @private */ beforeDestroy: function () { const me = this; me.clearCustomEvents(); if (me.map) { me.map.setTarget(null); } }, /** * When resizing has happened, we might need to re-set the renderer's * dimensions via #setRendererDimensions. * * @private */ onResize: function () { this.setRendererDimensions(); this.callParent(arguments); }, /** * Draw the feature in the map. * * @private */ drawFeature: function () { const me = this; me.map.setTarget(me.el.id); // TODO why not me.el? me.setRendererDimensions(); }, /** * Set the dimension of our renderer, i.e. map and view. * * @private */ setRendererDimensions: function () { const me = this; const gb = me.feature.getGeometry().getExtent(); const gw = ol.extent.getWidth(gb); const gh = ol.extent.getHeight(gb); /* * Determine resolution based on the following rules: * 1) always use value specified in config * 2) if not specified, use max res based on width or height of element * 3) if no width or height, assume a resolution of 1 */ let resolution = me.initialConfig.resolution; if (!resolution) { resolution = Math.max(gw / me.width || 0, gh / me.height || 0) || 1; } me.map.setView( new ol.View({ minResolution: resolution, maxResolution: resolution, projection: new ol.proj.Projection({ code: '', units: 'pixels', }), }), ); // determine height and width of element const width = Math.max(me.width || me.getMinWidth(), gw / resolution); const height = Math.max(me.height || me.getMinHeight(), gh / resolution); // determine bounds of renderer const center = ol.extent.getCenter(gb); const bhalfw = (width * resolution) / 2; const bhalfh = (height * resolution) / 2; const bounds = [ center[0] - bhalfw, center[1] - bhalfh, center[0] + bhalfw, center[1] + bhalfh, ]; me.el.setSize(Math.round(width), Math.round(height)); me.map.updateSize(); // Check for backwards compatibility if (GeoExt.util.Version.isOl3()) { me.map.getView().fit(bounds, me.map.getSize()); } else { me.map.getView().fit(bounds); } }, /** * We're setting the symbolizers on the feature. * * @param {Array<ol.style.Style> | ol.style.Style} symbolizers The style (or * array of styles) that have been set. * @return {Array<ol.style.Style> | ol.style.Style} The style (or * array of styles) that have been set. * @private */ applySymbolizers: function (symbolizers) { const feature = this.getFeature(); if (feature && symbolizers) { feature.setStyle(symbolizers); } return symbolizers; }, /** * We're setting the feature and add it to the source. * * @param {ol.Feature} feature The feature that has been set. * @return {ol.Feature} feature The feature that has been set. * @private */ applyFeature: function (feature) { const symbolizers = this.getSymbolizers(); if (feature && symbolizers) { feature.setStyle(symbolizers); } if (this.map) { const source = this.map.getLayers().item(0).getSource(); source.clear(); source.addFeature(feature); } return feature; }, /** * Update the `feature` or `symbolizers` and redraw the feature. * * Valid options: * * @param {Object} options Object with properties to be updated. * @param {ol.Feature} options.feature The new or updated feature. * @param {Array<ol.style.Style> | ol.style.Style} options.symbolizers The * symbolizers. */ update: function (options) { if (options.feature) { this.setFeature(options.feature); } if (options.symbolizers) { this.setSymbolizers(options.symbolizers); } }, });