【问题标题】:Openlayers: Using for loop for click events with multiple maps/views on single pageOpenlayers:在单个页面上使用 for 循环单击具有多个地图/视图的事件
【发布时间】:2020-06-06 04:03:17
【问题描述】:

我正在尝试整理一个页面,该页面包含多个地图(一个包含所有一组路线,每条路线各有一个),每个地图位于不同的(引导)选项卡中。通过重复并独立地为每个地图分配我想要的 click 和 pointermove 事件,我让它与最重复的代码一起工作,但理想情况下,我会在某种循环中分配这些事件。

我的尝试是我在下面粘贴的内容,它有效,但仅适用于最终地图 - 如果我更改地图的顺序,它们中的任何一个都可以工作,但前提是它是最后一张。我无法找到我正在寻找的方向,this other question being the closest,它提到了仅在最后一张地图上工作的相同问题,尽管我无法充分利用所提供的答案。 有没有办法循环遍历这些事件并让它们在多个视图/地图上工作?

非常感谢任何方向!

Javascript:

//----------/MAP 0/----------//

var myView0 = new ol.View({
    center: ol.proj.fromLonLat([-87.068669, 8.470072]),
    zoom: 3,
});

var map0 = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        vectorLayerJNY10,
        vectorLayerJNY11,
        vectorLayerJNY12,
        hoverLayers['route'],
    ],
    target: 'map0',
    view: myView0,
});

//----------/MAP 1/----------//

var myView1 = new ol.View({
    center: ol.proj.fromLonLat([-94.803044, 36.719405]),
    zoom: 3.5,
});

var map1 = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        vectorLayerJNY10,
        hoverLayers['route'],
    ],
    target: 'map1',
    view: myView1,
});

//----------/MAP 2/----------//

var myView2 = new ol.View({
    center: ol.proj.fromLonLat([-114.778097, 32.698996]),
    zoom: 4,
});

var map2 = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        vectorLayerJNY11,
        hoverLayers['route'],
    ],
    target: 'map2',
    view: myView2
});

//----------/MAP 3/----------//

var myView3 = new ol.View({
    center: ol.proj.fromLonLat([-93.988196, 19.253431]),
    zoom: 4 
});

var map3 = new ol.Map({
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        }),
        vectorLayerJNY12,
        hoverLayers['route'],
    ],
    target: 'map3',
    view: myView3
});

var maps = [map0, map1, map2, map3];
var views = [myView0, myView1, myView2, myView3]

var mapOnClick = function (event) {
    if (event.map.hasFeatureAtPixel(event.pixel) === true) {
        var name = event.map.forEachFeatureAtPixel(event.pixel, function(feature) {
            return feature.get('name');
        })
        var coordinate = event.coordinate;
        content.innerHTML = name;
        olay.setPosition(coordinate);
    } else {
        olay.setPosition(undefined);
        closer.blur();
    }
};

var elmHover = function(evt) {
    if (evt.dragging) {
        return;
    }
    var pixel = evt.map.getEventPixel(evt.originalEvent);
    var map = elem;
    hover(pixel, map);
};

var cursor = function(evt) {
    elem.getTargetElement().style.cursor = elem.hasFeatureAtPixel(evt.pixel) ? 'pointer' : '';
};

var mapResize = function(event){
    setTimeout(function() {
        elem.updateSize();
    }, 200);
};

for(var i = 0; i < maps.length; i++){
    var elem = maps[i];

    var container = document.getElementById('popup' + i);
    var content = document.getElementById('popup-content' + i);
    var closer = document.getElementById('popup-closer' + i);

    var overlays = [];
    overlays[i] = new ol.Overlay({
        element: container,
        autoPan: true,
        autoPanAnimation: {
            duration: 250
        }
    });
    elem.addOverlay(overlays[i]);

    olay = overlays[i];
    view = views[i];

    elem.on('pointermove', elmHover);

    elem.on('singleclick', mapOnClick);

    elem.on('pointermove', cursor);

    closer.onclick = function() {
        layover.setPosition(undefined);
        closer.blur();
        return false;
    };

    $('.nav-tabs').on('shown.bs.tab', mapResize)

    $('.nav-tabs').on('hide.bs.tab', function(event){
        elem.setView(view);
        olay.setPosition(undefined);
    });
}

HTML(以防万一:3):

<ul class="nav nav-tabs nav-justified" role="tablist">
    <li class="nav-item" >
        <a class="nav-link active" data-toggle="tab" href="#About">About</a>
    </li>
    <li class="nav-item" >
        <a class="nav-link" data-toggle="tab" href="#Journey-I">Journeys I</a>
    </li>
    <li class="nav-item" >
        <a class="nav-link" data-toggle="tab" href="#Journey-II">Journey II</a>
    </li>
    <li class="nav-item" >
        <a class="nav-link" data-toggle="tab" href="#Journey-III">Journey III</a>
    </li>
</ul>

<div class="tab-content">
    <div id="About" class="tab-pane fade show active" role="tabpanel">
        <h3>About</h3>

        <div id="map0" class="map"></div>

        <div id="popup0" class="ol-popup">
            <a href="#" id="popup-closer0" class="ol-popup-closer"></a>
            <div id="popup-content0"></div>
        </div>

    </div>

    <div id="Journey-I" class="tab-pane fade" role="tabpanel">
        <h3>Journey I</h3>

        <div id="map1" class="map"></div>

        <div id="popup1" class="ol-popup">
            <a href="#" id="popup-closer1" class="ol-popup-closer"></a>
            <div id="popup-content1"></div>
        </div>

    </div>

    <div id="Journey-II" class="tab-pane fade" role="tabpanel">
        <h3>Journey II</h3>

        <div id="map2" class="map"></div>

        <div id="popup2" class="ol-popup">
            <a href="#" id="popup-closer2" class="ol-popup-closer"></a>
            <div id="popup-content2"></div>
        </div>

    </div>

    <div id="Journey-III" class="tab-pane fade" role="tabpanel">
        <h3>Journey III</h3>

        <div id="map3" class="map"></div>

        <div id="popup3" class="ol-popup">
            <a href="#" id="popup-closer3" class="ol-popup-closer"></a>
            <div id="popup-content3"></div>
        </div>

    </div>

    <script src="{{ url_for('static', filename='JS/OLmap_ajall.js') }}" type="text/javascript"></script>

</div>

【问题讨论】:

    标签: javascript loops openlayers


    【解决方案1】:

    就像你检查的问题中所说的那样,这是一个上下文问题。

    在每个事件的处理程序中,您正在使用在循环中取值的变量elem、olay、view。问题是当事件发生时,这些变量的值会引用它们各自数组的最后一个地图、叠加层和视图。

    有几种方法可以解决这个问题。我建议你使用bind函数方法(JavaScript - function bind)。

    var mapOnClick = function (event) {
        if (this.map.hasFeatureAtPixel(event.pixel) === true) {
            var name = this.map.forEachFeatureAtPixel(event.pixel, function(feature) {
                return feature.get('name');
            })
            var coordinate = event.coordinate;
            this.content.innerHTML = name;
            this.overlay.setPosition(coordinate);
        } else {
            this.overlay.setPosition(undefined);
            this.closer.blur();
        }
    };
    
    var elmHover = function(evt) {
        if (evt.dragging) {
            return;
        }
        var pixel = this.map.getEventPixel(evt.originalEvent);
        // var map = elem; <- what is this for?
        hover(pixel, this.map); // <- this function is not defined, look out
    };
    
    var cursor = function(evt) {
        this.map.getTargetElement().style.cursor = 
            this.map.hasFeatureAtPixel(evt.pixel) ? 'pointer' : '';
    };
    
    var mapResize = function(event){
        var self = this;
        setTimeout(function() {
            self.map.updateSize();
        }, 200);
    };
    
    var closerOnClick = function() {
        this.overlay.setPosition(undefined); // I am guessing is overlay
        this.closer.blur();
        return false;
    }
    
    var onHide = function(event){
        this.map.setView(this.view); // <- don't understand this
        this.overlay.setPosition(undefined);
    }
    
    var contexts = [
        { map: map0, view: myView0 },
        { map: map1, view: myView1 },
        { map: map2, view: myView2 },
        { map: map3, view: myView3 }
    ];
    
    for(var i = 0; i < contexts.length; i++){
        contexts[i].container = document.getElementById('popup' + i);
        contexts[i].content = document.getElementById('popup-content' + i);
        contexts[i].closer = document.getElementById('popup-closer' + i);
        contexts[i].overlay = new ol.Overlay({
            element: contexts[i].container,
            autoPan: true,
            autoPanAnimation: {
                duration: 250
            }
        });
        contexts[i].map.addOverlay(contexts[i].overlay);
        contexts[i].mapOnClick = mapOnClick.bind(contexts[i]);
        contexts[i].elmHover = elmHover.bind(contexts[i]);
        contexts[i].cursor = cursor.bind(contexts[i]);
        contexts[i].mapResize = mapResize.bind(contexts[i]);
    
        contexts[i].map.on('singleclick', contexts[i].mapOnClick);
        contexts[i].map.on('pointermove', contexts[i].cursor);
        contexts[i].closer.onclick = contexts[i].closerOnClick;
    
        // don't understand why these are in the loop
        $('.nav-tabs').on('shown.bs.tab', contexts[i].mapResize);
        $('.nav-tabs').on('hide.bs.tab', contexts[i].onHide);
    }
    

    注意: 有几件事我不理解您的代码,而且您在每次迭代中都初始化了覆盖数组,并且您的变量名称错误。这是很长的代码,没有检查它,所以请注意可能存在一些问题。只是为了你的想法。

    我注意到您正在使用 jQuery,您也可以使用 proxy 来保持上下文。虽然我注意到它已被弃用。

    【讨论】:

    • 非常感谢您非常有帮助的回答,这在很大程度上解决了问题。我很抱歉,我只包含了我认为是我的代码的必要部分,因此例如这里只调用了“悬停”功能,但我觉得与包含自身无关(并且工作正常,一旦它被包含在哪里你把它漏掉了)。我知道您没有也不能期望您彻底审查代码,但是当您提到“错误的变量名称”时,您确实注意到了一个?
    • 此外,.nav-tabs 功能也包括在内,因为地图必须在第一次调整大小后出现在激活的选项卡上(或者我的经验 coincided with these),另一个是因为我想要每次激活选项卡时都会重新设置地图并删除弹出窗口,因此这些功能需要选项卡的地图。这实际上是一个不起作用的部分,告诉我它“无法读取未定义的属性'updateSize'”。 This.map 可以在其他任何地方使用,你能看出它不存在的原因吗?
    • 你是对的,它不起作用的原因是因为在setTimeout的处理函数的上下文中map不是this的属性。您可以像其他情况一样,定义一个函数并将其绑定到正确的上下文,或者在这种情况下,您可以做一些小技巧,例如将this(或您想要的引用)分配给局部变量。检查更新的代码。
    • 再次感谢您提供的非常有帮助的答案!但是,现在,setView 不起作用,奇怪的是 - 用于同一 onHide 函数中的覆盖的 setPosition 确实有效,如果我为其分配一个视图,它确实有效。这会不会像setTimeout,view 不是它的属性?我尝试将其分离为自己的功能,以及您的 this 分配技巧,但无法实现。
    猜你喜欢
    • 2015-08-02
    • 1970-01-01
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    相关资源
    最近更新 更多