【问题标题】:GoJS Leaflet layer zoomingGoJS Leaflet 图层缩放
【发布时间】:2016-02-29 10:17:50
【问题描述】:

我正在尝试创建一个自定义 Leaflet 层,该层应启用GoJS 库的使用。我已经解决了大部分主要问题,例如:

  • 根据图层转换重新定位图表
  • 在图表视口之外绘制元素
  • 在拖动地图时重新定位图表节点

但是我遇到了缩放时调整节点大小的问题。我正在计算 scaleFactor 并更改节点的 location。该方法到目前为止有效,但就地图缩小到水平0 并且用户放大到该位置计算不正确。 y轴的位置完全错误。我还设置了一个fiddle,这样你就可以轻松地使用源代码。

(function () {
    if (typeof(L) !== 'undefined' && typeof(go) !== 'undefined') {
        L.GoJsLayer = L.Class.extend({
            includes: [L.Mixin.Events],

            options: {
                "animationManager.isEnabled": false,
                allowZoom: false,
                allowHorizontalScroll: false,
                hasHorizontalScrollbar: false,
                allowVerticalScroll: false,
                hasVerticalScrollbar: false,
                padding: 0
            },

            initialize: function (options) {
                L.setOptions(this, options);
            },

            onAdd: function (map) {
                this._map = map;

                if (!this.diagram) {
                    this._initDiagram();
                }

                this._map
                    .on('viewreset', this._reset, this)
                    .on('moveend', this._updateViewport, this);
            },

            onRemove: function (map) {
                this._map
                    .getPanes()
                    .overlayPane
                    .removeChild(this._el);

                this._map
                    .off('moveend', this._updateViewport, this);
            },

            addTo: function (map) {
                map.addLayer(this);

                return this;
            },

            _initDiagram: function () {
                this._initElement();
                this._viewport = this._map.getBounds();

                this.diagram = new go.Diagram(
                    this._el.getAttribute('id')
                );
                this._setFixedBounds();
                this.diagram.setProperties(this.options);
                
                this._setCanvas();
            },

            _initElement: function () {
                var size = this._map.getSize();

                this._el = L
                    .DomUtil
                    .create('div', 'leaflet-layer');
                this._el.setAttribute(
                    'id',
                    'leaflet-gojs-diagram-' + L.Util.stamp(this)
                );
                this._el
                    .setAttribute('style', this._getElementStyle());
                
                L.DomUtil.addClass(this._el, 'leaflet-zoom-hide');

                this._map
                    .getPanes()
                    .overlayPane
                    .appendChild(this._el);
            },

            _getElementStyle: function (options) {
                var size = this._map.getSize(),
                    paneTranslation,
                    vpOffset,
                    translation;

                if (this._canvas) {
                    // This is a dirty solution due to the pressure of time.
                    // This needs to be refractored!
                    paneTranslation = L.DomUtil
                        .getStyle(this._map.getPanes()
                            .mapPane, 'transform')
                        .match(/\-?\d+px/g)
                        .map(function (value) {
                            return parseInt(value);
                        });

                    vpOffset = L.point(paneTranslation[0], paneTranslation[1]);

                    translation = L
                        .DomUtil
                        .getTranslateString(vpOffset.multiplyBy(-1));

                    return ''
                        .concat('width: ' + size.x + 'px;')
                        .concat('height: ' + size.y + 'px;')
                        .concat('transform: ' + translation);
                } else {
                    translation = L.DomUtil.getTranslateString(L.point(0, 0));

                    return ''
                        .concat('width: ' + size.x + 'px;')
                        .concat('height: ' + size.y + 'px;')
                        .concat('transform: ' + translation);
                }
            },

            _setFixedBounds: function () {
                var width = parseInt(L.DomUtil.getStyle(this._el, 'width')),
                    height = parseInt(L.DomUtil.getStyle(this._el, 'height'));

                this.diagram.setProperties({
                    fixedBounds: new go.Rect(0, 0, width, height)
                });
            },

            _setCanvas: function () {
                var canvasElements = this._el.getElementsByTagName('canvas');

                if (canvasElements.length) {
                    this._canvas = canvasElements.item(0);
                    return true;
                }

                return false;
            },

            _reset: function () {
            	this._resizeNodes();
            },

            _resizeNodes: function () {
            	var scale = this._map.options.crs.scale,
                	currentScale = scale(this._map.getZoom()),
                    previousScale = scale(this._calcPreviousScale()),
                    scaleFactor = currentScale / previousScale;

				this.diagram.startTransaction('reposition');
                this.diagram.nodes.each(this._resizeNode.bind(this, scaleFactor));
                this.diagram.commitTransaction('reposition');	
            },
            
             _calcPreviousScale: function () {
                var vp = this._viewport,
                    vpNw = vp.getNorthWest(),
                    vpSw = vp.getSouthWest(),
                    mb = this._map.getBounds(),
                    mbNw = mb.getNorthWest(),
                    mbSw = mb.getSouthWest(),
                    currentScale = this._map.getZoom(),
                    previousScale;

                if (mbNw.distanceTo(mbSw) > vpNw.distanceTo(vpSw)) {
                    previousScale = currentScale + 1;
                } else {
                    previousScale = currentScale - 1;
                }

                return previousScale;
            },
            
            _resizeNode: function (scaleFactor, node) {
                node.location = new go.Point(
                    node.location.x * scaleFactor, 
                    node.location.y * scaleFactor
                );
            },
            
            _updateViewport: function (options) {
                this._el.setAttribute('style', this._getElementStyle(options));
                this._setFixedBounds();

                this._repositionNodes();
                this._viewport = this._map.getBounds();
            },
            
            _repositionNodes: function () {
                this.diagram.startTransaction('reposition');
                this.diagram.nodes.each(this._repositionNode.bind(this));
                this.diagram.commitTransaction('reposition');
            },

            _repositionNode: function (node) {
                var vp = this._viewport,
                    vpNw = vp.getNorthWest(),
                    vpOffset = this._map.latLngToContainerPoint(vpNw),
                    vpOffsetInverse = vpOffset.multiplyBy(-1),
                    newX = node.location.x - vpOffsetInverse.x,
                    newY = node.location.y - vpOffsetInverse.y;

                node.location = new go.Point(newX, newY);
            }
        });

        L.goJsLayer = function (options) {
            return new L.GoJsLayer(options);
        };
    }
}());

var $ = go.GraphObject.make,
  	nodeTemplate, 
    linkTemplate, 
    model, 
    canvasLayer, 
    map;
    
// the node template describes how each Node should be constructed
nodeTemplate = $(go.Node, 'Auto',  
	$(go.Shape, 'Rectangle',  
    	{
        	fill: '#FFF',
            width: 10,
            height: 10
        }
    ),
    new go.Binding('location', 'loc', go.Point.parse)
);

// the linkTemplates describes how each link should be constructed
linkTemplate = $(go.Link, $(go.Shape));

// the Model holds only the essential information describing the diagram
model = new go.GraphLinksModel(
    [ 
    	{ key: 1, loc: '320 100' },
      	{ key: 2, loc: '320 300' }
    ],
    [ 
    	{ from: 1, to: 2 }
    ]
);

// Caution: The model property has to be set after the template properties
canvasLayer = L.goJsLayer({
	nodeTemplate: nodeTemplate,
    linkTemplate: linkTemplate,
    model: model
});

map = L.map('map', {
	zoom: 	4,
    center:	[51.505, -0.09],
    layers: 	[
    	L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {noWrap: true}),
        canvasLayer
    ],
    //dragging: false
});
html, body, .map {
    padding: 0px;
    margin: 0px;
    height: 100%;
}

div canvas {
    outline: none;
}
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
<script type="text/javascript" src="http://gojs.net/latest/release/go-debug.js"></script>
<script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>

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

【问题讨论】:

    标签: javascript leaflet gojs


    【解决方案1】:
    // This is a dirty solution due to the pressure of time.
    // This needs to be refractored!
    paneTranslation = L.DomUtil
        .getStyle(this._map.getPanes()
            .mapPane, 'transform')
        .match(/\-?\d+px/g)
    

    哇哦。确实很脏:-O

    我自己开发了一些 Leaflet 插件,我的建议是更多地依赖map.latLngToPoint()(用于点)和map.getPixelOrigin()(用于视口)之类的方法 - 使用绝对坐标会有所帮助。现在你的所有定位都基于map.latLngToContainerPoint(),它隐式使用getPixelOrigin(),每次用户平移和缩放时都会改变,这会让你失去控制。

    如果您坚持使用像素坐标来初始化图形,请在初始化图形时将它们投影到 LatLngs 和 map.project()。这样,它们在移动和缩放时会得到正确的重新投影。

    如果您希望 Leaflet 具有平移和缩放功能而不是制图功能,请考虑使用 CRS.Simple 来使用简单的非地理笛卡尔坐标系。

    【讨论】:

    【解决方案2】:

    非常感谢 IvanSanchez 的贡献。由于GoJS-Forum 的一些帮助,我已经解决了我的问题。我已经使用GoJS data binding 优化了我的代码。除此之外,我还更新了使用私有 _getMapPanePos 方法确定翻译的丑陋方法。

    小提琴:http://jsfiddle.net/5x0vtk81/

    (function () {
        if (typeof(L) !== 'undefined' && typeof(go) !== 'undefined') {
            L.GoJsLayer = L.Class.extend({
                includes: [L.Mixin.Events],
    
                options: {
                    "animationManager.isEnabled": false,
                    allowZoom: false,
                    allowHorizontalScroll: false,
                    hasHorizontalScrollbar: false,
                    allowVerticalScroll: false,
                    hasVerticalScrollbar: false,
                    padding: 0
                },
    
                initialize: function (options) {
                    L.setOptions(this, options);
                },
    
                onAdd: function (map) {
                    this._map = map;
    
                    if (!this.diagram) {
                        this._initDiagram();
                    }
    
                    this._map
                        .on('viewreset', this._reset, this)
                        .on('moveend', this._updateViewport, this);
                },
    
                onRemove: function (map) {
                    this._map
                        .getPanes()
                        .overlayPane
                        .removeChild(this._el);
    
                    this._map
                        .off('moveend', this._updateViewport, this);
                },
    
                addTo: function (map) {
                    map.addLayer(this);
    
                    return this;
                },
    
                _initDiagram: function () {
                    this._initElement();
    
                    this.diagram = new go.Diagram(
                        this._el.getAttribute('id')
                    );
    
                    this._setFixedBounds();
                    this.diagram.setProperties(this.options);
    
                    this._setCanvas();
                },
    
                _initElement: function () {
                    var size = this._map.getSize();
    
                    this._el = L
                        .DomUtil
                        .create('div', 'leaflet-layer');
                    this._el.setAttribute(
                        'id',
                        'leaflet-gojs-diagram-' + L.Util.stamp(this)
                    );
                    this._el
                        .setAttribute('style', this._getElementStyle());
    
                    L.DomUtil.addClass(this._el, 'leaflet-zoom-hide');
    
                    this._map
                        .getPanes()
                        .overlayPane
                        .appendChild(this._el);
                },
    
                _getElementStyle: function (options) {
                	var size = this._map.getSize(),
                    	panePosition,
                        transform;
    
                    if (this._canvas) {
                    	panePosition = this._map._getMapPanePos();
                        
                        transform = L
                            .DomUtil
                            .getTranslateString(panePosition.multiplyBy(-1));
                    } else {
                        transform = L
                        	.DomUtil
                            .getTranslateString(L.point(0, 0));
                    }
                    
                    return L.Util.template(
                        'width: {width}px; ' +
                        'height: {height}px; ' +
                        'transform: {transform}', 
                        {
                            width: size.x, 
                            height: size.y, 
                            transform: transform
                        }
                    );
                },
    
                _setFixedBounds: function () {
                    var width = parseInt(L.DomUtil.getStyle(this._el, 'width')),
                        height = parseInt(L.DomUtil.getStyle(this._el, 'height'));
    
                    this.diagram.setProperties({
                        fixedBounds: new go.Rect(0, 0, width, height)
                    });
                },
    
                _setCanvas: function () {
                    var canvasElements = this._el.getElementsByTagName('canvas');
    
                    if (canvasElements.length) {
                        this._canvas = canvasElements.item(0);
                        return true;
                    }
    
                    return false;
                },
    
                _reset: function () {
                	this.diagram.updateAllTargetBindings('latlong')
                },
    
                _updateViewport: function (options) {
                    this._el.setAttribute('style', this._getElementStyle(options));
                    this._setFixedBounds();
                    
                    this.diagram.updateAllTargetBindings('latlong');
                }
            });
    
            L.goJsLayer = function (options) {
                return new L.GoJsLayer(options);
            };
        }
    }());
    
    var $ = go.GraphObject.make,
    	map,
        calcDiagramLocation,
        nodeTemplate,
        linkTemplate,
        model,
        canvasLayer;
    
    map = L.map('map', {
    	zoom: 	4,
        center:	[51.505, -0.09],
        layers: 	[
        	L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {noWrap: true})
        ],
        dragging: true
    });
    
    calcDiagramLocation = function(map, data) {
        var point = map.latLngToContainerPoint(data);
        return new go.Point(point.x, point.y);
    };
    
    // the node template describes how each Node should be constructed
    nodeTemplate = $(go.Node, 'Auto',  
    	$(go.Shape, 'Rectangle',  
        	{
            	fill: '#FFF',
                width: 10,
                height: 10
            }
        ),
        new go.Binding('location', 'latlong', calcDiagramLocation.bind(this, map))
    );
    
    // the linkTemplates describes how each link should be constructed
    linkTemplate = $(go.Link, $(go.Shape));
    
    // the Model holds only the essential information describing the diagram
    model = new go.GraphLinksModel(
        [
        	{ key: 1, latlong: [51.507884, -0.087765] }, // london bridge
          	{ key: 2, latlong: [48.853039, 2.349952] }, // Notre-Dame cathedral
        ],
        [ 
        	{ from: 1, to: 2 }
        ]
    );
    
    // Caution: The model property has to be set after the template properties
    canvasLayer = L.goJsLayer({
    	nodeTemplate: nodeTemplate,
        linkTemplate: linkTemplate,
        model: model
    }).addTo(map);
    html, body, .map {
        padding: 0px;
        margin: 0px;
        height: 100%;
    }
    
    div canvas {
        outline: none;
    }
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css" />
    <script type="text/javascript" src="http://gojs.net/latest/release/go-debug.js"></script>
    <script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
    
    <div id="map" class="map"></div>

    【讨论】:

    猜你喜欢
    • 2016-02-27
    • 2016-12-04
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多