Компонент карты .getWidth () не является функцией (GeoExt3, MapFish, OpenLayers 4) - PullRequest
0 голосов
/ 15 июня 2019

Я пытаюсь проработать учебник здесь *. Но я обнаружил нечто неожиданное с точки зрения компонента моей карты.

Справочная информация о приложении / установке:

Я установил 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
         }
      });
    }
  }
});

1 Ответ

0 голосов
/ 04 июля 2019

Гарантируете ли вы, что ваша карта создается и обрабатывается при создании MapfishPrintProvider?Потому что, как только провайдер получает возможности, он запускает событие ready, независимо от того, создана карта или отрисована или нет.

Зарегистрируйте событие on render на своей карте и создайте там своего провайдера:

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(){
      Ext.create('GeoExt.data.MapfishPrintProvider', {
         url: 'https://apps.terrestris.de/print-servlet-3.1.2/' +
               'print/geoext/capabilities.json',
         listeners: {
           ready: onPrintProviderReady
         }
      });
    }
  }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...