Я пытаюсь проработать учебник здесь *. Но я обнаружил нечто неожиданное с точки зрения компонента моей карты.
Справочная информация о приложении / установке:
Я установил GeoExt3, клонировав этот репозиторий:
https://github.com/geoext/geoext3
Приложение началось как универсальное приложение:
https://github.com/geoext/geoext3/blob/master/universal-app.md
mapComponent моего приложения расширяет GeoExt.component.Map, который расширяет Ext.Component. getWidth () - это метод Ext.Component. Интересно, может ли моя проблема быть связана с этим наследством. Следующие два API-интерфейса меня смущали на секунду, но затем я понял, что первый - это GeoExt без компонентов ext, а второй - с компонентами ext.
https://geoext.github.io/geoext3/master/docs/#!/api/GeoExt.component.Map
http://geoext.github.io/geoext3/master/docs-w-ext/#!/api/GeoExt.component.Map
Код приложения здесь .
Мои слои, карты и компонент карты создаются (успешно) следующим образом:
var t_coupureaerien_source = new ol.source.VectorTile({
format: new ol.format.MVT(),
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' + 'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
})
var t_coupureaerien_style = new ol.style.Style({
fill: new ol.style.Fill({color: 'rgba(255, 255, 255, 0.6)'}),
stroke: new ol.style.Stroke({color: '#319FD3', width: 1})
});
var t_coupureaerien_layer = new ol.layer.VectorTile({
title: 'Coupure Aerien HTA',
style: t_coupureaerien_style,
source: t_coupureaerien_source,
legendUrl: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' + 'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf',
name: 'Appareil de coupure aerien'
});
var osm_source = new ol.source.OSM({url: "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"});
var osm_basemap = new ol.layer.Tile({
title: 'OSM basemap',
source: osm_source,
//legendUrl: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
name: 'OSM basemap'})
var extentLayer = new ol.layer.Vector({
source: new ol.source.Vector()
});
var view = new ol.View({
center: ol.proj.fromLonLat([-149.3, -17.7]),
zoom: 7
});
var my_map = new ol.Map({
controls: ol.control.defaults().extend([new ol.control.ScaleLine]),
target: 'map',
view: view,
layers: [
new ol.layer.Group({title: 'Fond de plans', layers: [osm_basemap], name: 'Fond de plans'}),
new ol.layer.Group({title: 'Réseau Secosud', layers: [coupureaerien_layer], name: 'Réseau Secosud'}),
extentLayer
],
//overlays: [overlay],
});
var mapComponent = Ext.define('SIG.view.main.Map', {
// extend: "Ext.panel.Panel",
extend: 'GeoExt.component.Map',
xtype: 'mappanel',
//region: 'center',
requires: [
'SIG.view.main.MapController', 'SIG.view.main.MapModel'
],
controller: 'main-map',
viewModel: {
type: 'main-map'
},
// html: "Hello, World!!"
map: my_map
});
console.log(mapComponent);
console.log(mapComponent.getWidth());
Но getWidth () выдает эту ошибку, несмотря на то, что это метод компонента карты :
Uncaught TypeError: mapComponent.getWidth is not a function
Я напечатал объект mapComponent на консоли, и суперкласс показывает следующие методы. Похоже, что getWidth там нет. Это часть проблемы? (Извините за форматирование JSON ... я не мог заставить toSource Firefox работать). Сначала думают, что это может быть, но затем мой объект расширяет GeoExt.map.Component, который, в свою очередь, расширяет Ext.Component, так что это может быть не печать всей цепочки (?).
superclass: {…}
"$className": "GeoExt.component.Map"
"$inheritableStatics": Object { _checked: true, check: true, normalizeSymbol: true, … }
"$noClearOnDestroy": Object { events: true, hasListeners: true, managedListeners: true, … }
HasListeners: function HasListeners()
addLayer: function addLayer()
alias: Array [ "widget.gx_map", "widget.gx_component_map" ]
applyPointerRest: function applyPointerRest()
applyPointerRestInterval: function applyPointerRestInterval()
applyState: function applyState()
bindOverOutListeners: function bindOverOutListeners()
bindStateOlEvents: function bindStateOlEvents()
bufferedPointerMove: function emptyFn()
childEls: Object { frameTable: {}, frameTL: {}, frameTC: {}, … }
config: Object { pointerRest: false, pointerRestInterval: 1000, pointerRestPixelTolerance: 3, … }
constructor: function constructor()
defaultConfig: Object { pointerRest: false, pointerRestInterval: 1000, pointerRestPixelTolerance: 3, … }
defaultListenerScope: false
getCenter: function getCenter()
getExtent: function getExtent()
getLayers: function getLayers()
getMap: function makeGetter()
getPointerRest: function makeGetter()
getPointerRestInterval: function makeGetter()
getPointerRestPixelTolerance: function makeGetter()
getState: function getState()
getStore: function getStore()
getView: function getView()
isMouseOverMapEl: null
lastPointerPixel: null
layerStore: null
map: null
mapRendered: false
mixins: Object { isDefinedSymbol: {…} }
onMouseOut: function onMouseOut()
onMouseOver: function onMouseOver()
onResize: function onResize()
pointerRestPixelTolerance: 3
registerPointerRestEvents: function registerPointerRestEvents()
removeLayer: function removeLayer()
requires: Array [ Layers()
, Version()
]
self: function Map()
setCenter: function setCenter()
setExtent: function setExtent()
setMap: function setter()
setPointerRest: function setter()
setPointerRestInterval: function setter()
setPointerRestPixelTolerance: function setter()
setView: function setView()
stateEvents: Array [ "aftermapmove" ]
superclass: Object { self: Component()
, superclass: {…}, defaultConfig: {…}, … }
symbols: Array(15) [ "ol.layer.Base", "ol.Map", "ol.Map#addLayer", … ]
unbindOverOutListeners: function unbindOverOutListeners()
unbufferedPointerMove: function unbufferedPointerMove()
unregisterPointerRestEvents: function unregisterPointerRestEvents()
xtype: "gx_map"
xtypes: Array [ "gx_map", "gx_component_map" ]
xtypesChain: Array(4) [ "component", "box", "gx_map", … ]
xtypesMap: Object { component: true, box: true, gx_map: true, … }
<prototype>: Object { self: Component()
, superclass: {…}, defaultConfig: {…}, … }
Это вызывается модулем MapFish, поэтому использование следующего кода вызывает ту же ошибку:
Ext.require([
'GeoExt.component.Map',
'SIG.view.main.Map']);
/**
* Once the store is loaded, we can create the button with the
* following assumptions:
*
* * The button will print the first layout
* * The attributes used are the first of the above layout
* * We'll request the first dpi value of the suggested ones
* @param {GeoExt.data.MapfishPrintProvider} provider The print
* provider.
*/
var onPrintProviderReady = function(provider) {
// this is the assumption: take the first layout and render an
// appropriate extent on the map
var capabilities = provider.capabilityRec;
var layout = capabilities.layouts().getAt(0);
var attr = layout.attributes().getAt(0);
var clientInfo = attr.get('clientInfo');
var render = GeoExt.data.MapfishPrintProvider.renderPrintExtent;
console.log(mapComponent);
render(mapComponent, extentLayer, clientInfo);
mapComponent.getView().on('propertychange', function() {
extentLayer.getSource().clear();
render(mapComponent, extentLayer, clientInfo);
});
description.add({
xtype: 'button',
text: 'Print',
handler: function() {
var spec = {
layout: layout.get('name'),
attributes: {}
};
var firstFeature = extentLayer.getSource().getFeatures()[0];
var bbox = firstFeature.getGeometry().getExtent();
var util = GeoExt.data.MapfishPrintProvider;
var mapView = mapComponent.getView();
var serializedLayers = util.getSerializedLayers(
mapComponent,
function(layer) {
// do not print the extent layer
var isExtentLayer = (extentLayer === layer);
return !isExtentLayer;
}
);
serializedLayers = unHttpsLayers(serializedLayers);
serializedLayers.reverse();
spec.attributes[attr.get('name')] = {
bbox: bbox,
dpi: clientInfo.dpiSuggestions[0],
layers: serializedLayers,
projection: mapView.getProjection().getCode(),
rotation: mapView.getRotation()
};
Ext.create('Ext.form.Panel', {
standardSubmit: true,
url: 'https://apps.terrestris.de/print-servlet-3.1.2/' +
'print/geoext/buildreport.pdf',
method: 'POST',
items: [
{
xtype: 'textfield',
name: 'spec',
value: Ext.encode(spec)
}
]
}).submit();
}
});
};
Ext.create('GeoExt.data.MapfishPrintProvider', {
url: 'https://apps.terrestris.de/print-servlet-3.1.2/' +
'print/geoext/capabilities.json',
listeners: {
ready: onPrintProviderReady
}
});
Для тех, кто не знаком с MapFishPrintProvider. Проблема в renderPrintExtent:
/* 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/>.
*/
/**
* Provides an interface to a Mapfish or GeoServer print module.
*
* @class GeoExt.data.MapfishPrintProvider
*/
Ext.define('GeoExt.data.MapfishPrintProvider', {
extend: 'Ext.Base',
mixins: [
'Ext.mixin.Observable',
'GeoExt.mixin.SymbolCheck'
],
requires: [
'GeoExt.data.model.print.Capability',
'Ext.data.JsonStore'
],
// <debug>
symbols: [
'ol.Collection',
'ol.geom.Polygon.fromExtent',
'ol.Feature',
'ol.layer.Layer#getSource',
'ol.layer.Group',
'ol.source.Vector.prototype.addFeature',
'ol.View#calculateExtent'
],
// </debug>
/**
* @event ready
* Fires after the PrintCapability store is loaded.
*
* @param {GeoExt.data.MapfishPrintProvider} provider The
* GeoExt.data.MapfishPrintProvider itself
*/
config: {
capabilities: null,
url: ''
},
inheritableStatics: {
/**
* An array of objects specifying a serializer and a connected
* OpenLayers class. This should not be manipulated by hand, but rather
* with the method #registerSerializer.
*
* @private
*/
_serializers: [],
/**
* Registers the passed serializer class as an appropriate serializer
* for the passed OpenLayers source class.
*
* @param {ol.source.Source} olSourceCls The OpenLayers source class
* that the passed serializer can serialize.
* @param {GeoExt.data.serializer.Base} serializerCls The serializer
* that can serialize the passed source.
*/
registerSerializer: function(olSourceCls, serializerCls) {
var staticMe = GeoExt.data.MapfishPrintProvider;
staticMe._serializers.push({
olSourceCls: olSourceCls,
serializerCls: serializerCls
});
},
/**
* Unregisters the passed serializer class from the array of available
* serializers. This may be useful if you want to register a new
* serializer that is different from a serializer that we provide.
*
* @param {GeoExt.data.serializer.Base} serializerCls The serializer
* that can serialize the passed source.
* @return {Boolean} Whether we could unregister the serializer.
*/
unregisterSerializer: function(serializerCls) {
var available = GeoExt.data.MapfishPrintProvider._serializers;
var index;
Ext.each(available, function(candidate, idx) {
if (candidate.serializerCls === serializerCls) {
index = idx;
return false; // break early
}
});
if (Ext.isDefined(index)) {
Ext.Array.removeAt(available, index);
return true;
}
return false;
},
/**
* Returns a GeoExt.data.serializer.Base capable of serializing the
* passed source instance or undefined, if no such serializer was
* previously registered.
*
* @param {ol.source.Source} source The source instance to find a
* serializer for.
* @return {GeoExt.data.serializer.Base} A serializer for the passed
* source or `undefined`.
*/
findSerializerBySource: function(source) {
var available = GeoExt.data.MapfishPrintProvider._serializers;
var serializer;
Ext.each(available, function(candidate) {
if (source instanceof candidate.olSourceCls) {
serializer = candidate.serializerCls;
return false; // break early
}
});
if (!serializer) {
Ext.log.warn('Couldn\'t find a suitable serializer for source.'
+ ' Did you require() an appropriate serializer class?');
}
return serializer;
},
/**
* Will return an array of ol-layers by the given collection. Layers
* contained in `ol.layer.Group`s get extracted and groups get removed
* from returning array
*
* @param {GeoExt.data.store.Layers|ol.Collection|ol.layer.Base[]} coll
* The 'collection' of layers to get as array. If passed as
* ol.Collection, all items must be `ol.layer.Base`.
* @return {Array} The flat layers array.
*/
getLayerArray: function(coll) {
var me = this;
var inputLayers = [];
var outputLayers = [];
if (coll instanceof GeoExt.data.store.Layers) {
coll.each(function(layerRec) {
var layer = layerRec.getOlLayer();
inputLayers.push(layer);
});
} else if (coll instanceof ol.Collection) {
inputLayers = Ext.clone(coll.getArray());
} else {
inputLayers = Ext.clone(coll);
}
inputLayers.forEach(function(layer) {
if (layer instanceof ol.layer.Group) {
Ext.each(me.getLayerArray(layer.getLayers()),
function(subLayer) {
outputLayers.push(subLayer);
});
} else {
outputLayers.push(layer);
}
});
return outputLayers;
},
/**
* Will return an array of serialized layers for mapfish print servlet
* v3.0.
*
* @param {GeoExt.component.Map} mapComponent The GeoExt map component
* to get the the layers from.
* @param {Function} [filterFn] A function to filter the layers to be
* serialized.
* @param {ol.layer.Base} filterFn.item The layer to check for
* inclusion.
* @param {Number} filterFn.index The index of the layer in the
* flattened list.
* @param {Array} filterFn.array The complete flattened array of layers.
* @param {Boolean} filterFn.return Return a truthy value to keep the
* layer and serialize it.
* @param {Object} [filterScope] The scope in which the filtering
* function will be executed.
* @return {Array<Object>} An array of serialized layers.
* @static
*/
getSerializedLayers: function(mapComponent, filterFn, filterScope) {
var layers = mapComponent.getLayers();
var viewRes = mapComponent.getView().getResolution();
var serializedLayers = [];
var inputLayers = this.getLayerArray(layers);
if (Ext.isDefined(filterFn)) {
inputLayers = Ext.Array.filter(
inputLayers, filterFn, filterScope
);
}
Ext.each(inputLayers, function(layer) {
var source = layer.getSource();
var serialized = {};
var serializer = this.findSerializerBySource(source);
if (serializer) {
serialized = serializer.serialize(layer, source, viewRes);
serializedLayers.push(serialized);
}
}, this);
return serializedLayers;
},
/**
* Renders the extent of the printout. Will ensure that the extent is
* always visible and that the ratio matches the ratio that clientInfo
* contains.
*
* @param {GeoExt.component.Map} mapComponent The map component to
* render the print extent to.
* @param {ol.layer.Vector} extentLayer The vector layer to render the
* print extent to.
* @param {Object} clientInfo Information about the desired print
* dimensions.
* @param {Number} clientInfo.width The target width.
* @param {Number} clientInfo.height The target height.
* @return {ol.Feature} The feature representing the print extent.
*/
renderPrintExtent: function(mapComponent, extentLayer, clientInfo) {
var mapComponentWidth = mapComponent.getWidth();
var mapComponentHeight = mapComponent.getHeight();
var currentMapRatio = mapComponentWidth / mapComponentHeight;
var scaleFactor = 0.6;
var desiredPrintRatio = clientInfo.width / clientInfo.height;
var targetWidth;
var targetHeight;
var geomExtent;
var feat;
if (desiredPrintRatio >= currentMapRatio) {
targetWidth = mapComponentWidth * scaleFactor;
targetHeight = targetWidth / desiredPrintRatio;
} else {
targetHeight = mapComponentHeight * scaleFactor;
targetWidth = targetHeight * desiredPrintRatio;
}
geomExtent = mapComponent.getView().calculateExtent([
targetWidth,
targetHeight
]);
feat = new ol.Feature(ol.geom.Polygon.fromExtent(geomExtent));
extentLayer.getSource().addFeature(feat);
return feat;
}
},
/**
* The capabiltyRec is an instance of 'GeoExt.data.model.print.Capability'
* and contans the PrintCapabilities of the Printprovider.
*
* @property
* @readonly
*/
capabilityRec: null,
constructor: function(cfg) {
this.mixins.observable.constructor.call(this, cfg);
if (!cfg.capabilities && !cfg.url) {
Ext.Error.raise('Print capabilities or Url required');
}
this.initConfig(cfg);
this.fillCapabilityRec();
},
/**
* Creates the store from object or url.
*
* @private
*/
fillCapabilityRec: function() {
// enhance checks
var store;
var capabilities = this.getCapabilities();
var url = this.getUrl();
var fillRecordAndFireEvent = function() {
this.capabilityRec = store.getAt(0);
if (!this.capabilityRec) {
this.fireEvent('error', this);
} else {
this.fireEvent('ready', this);
}
};
if (capabilities) { // if capability object is passed
store = Ext.create('Ext.data.JsonStore', {
model: 'GeoExt.data.model.print.Capability',
listeners: {
datachanged: fillRecordAndFireEvent,
scope: this
}
});
store.loadRawData(capabilities);
} else if (url) { // if servlet url is passed
store = Ext.create('Ext.data.Store', {
autoLoad: true,
model: 'GeoExt.data.model.print.Capability',
proxy: {
type: 'jsonp',
url: url,
callbackKey: 'jsonp'
},
listeners: {
load: fillRecordAndFireEvent,
scope: this
}
});
}
}
});
Мой файл Main.js:
Ext.define('SIG.view.main.Main', {
extend: 'Ext.container.Viewport',
requires: [
'Ext.plugin.Viewport', 'Ext.window.MessageBox', 'SIG.view.main.MainController', 'SIG.view.main.MainModel', 'SIG.view.main.List', 'SIG.view.main.Map', 'SIG.view.main.Print'
],
controller: 'main',
viewModel: 'main',
layout: 'border',
items: [
mapComponent,
layerTreePanel
]
})
* Примечание. Это приложение не работает для меня, кстати, с ошибкой: «Ошибка при обработке запроса:
java.net.MalformedURLException: неизвестный протокол: данные ", но я все равно решил попытаться проработать его.
EDIT
Я обновил код для создания MapFishPrintProvider с использованием прослушивателя на компоненте рендеринга карты, как показано в ответе ниже. К сожалению, это не решило мою проблему, и сообщение об ошибке остается прежним.
var mapComponent = Ext.define('SIG.view.main.Map', {
// extend: "Ext.panel.Panel",
extend: 'GeoExt.component.Map',
xtype: 'mappanel',
//region: 'center',
requires: [
'SIG.view.main.MapController', 'SIG.view.main.MapModel'
],
controller: 'main-map',
viewModel: {
type: 'main-map'
},
// html: "Hello, World!!"
map: my_map,
// Register the on render here
listeners: {
render: function(){
console.log("Map rendered"); //this prints
Ext.create('GeoExt.data.MapfishPrintProvider', {
url: 'https://apps.terrestris.de/print-servlet-3.1.2/' +
'print/geoext/capabilities.json',
listeners: {
ready: onPrintProviderReady
}
});
}
}
});