2012-03-02 27 views
5

Tôi đang sử dụng v7 Bản đồ Bing Javascript "kiểm soát" (Tôi không biết tại sao nó được gọi là "kiểm soát" ...). Tôi gọi số Microsoft.Maps.Map.setView({bounds: bounds}) và nó không hoạt động như tôi mong đợi hoặc mong muốn.Cách lấy hộp giới hạn bằng cách sử dụng LocationRect.fromLocations() khi các vị trí trải dài 180 kinh tuyến?

Tôi có một tập hợp các Đa giác có các điểm trải dài trên kinh tuyến thứ 180. Một ví dụ là ranh giới của các đảo New Zealand - một số trong số đó nằm ở phía tây kinh tuyến thứ 180, một số phần (Chatham ISlands) nằm ở phía đông.

Khi tôi tạo đa giác với các giới hạn đó và gọi setView(), bản đồ thu phóng waaaaaay.

enter image description here

Tại sao? và làm thế nào để tránh nó?


This page trình bày vấn đề.

Đây là mã.

var map, MM = Microsoft.Maps; 

function showMap(m) { 
    var options = { 
    mapTypeId: MM.MapTypeId.road // aerial, 
    // center will be recalculated 
    // zoom will be recalculated 
    }, 
    map1 = new MM.Map(m, options); 
    return map1; 
} 

function doubleclickCallback(e) { 
    e.handled = true; 
    var bounds = map.getBounds(); 
    map.setView({ bounds: bounds }); 
} 

function init() { 
    var mapDiv = document.getElementById("map1"); 
    map = showMap(mapDiv); 

    MM.Events.addHandler(map, "dblclick", doubleclickCallback); 
} 

Nếu bạn nhấp đúp vào bản đồ không có đường kinh tuyến thứ 180, sẽ không có gì xảy ra. Nếu bạn nhấp đúp khi bản đồ hiển thị kinh tuyến thứ 180, bản đồ sẽ đặt lại thành mức thu phóng 1.

Trả lời

11

Tôi đã xem xét điều này.

Đặc biệt tôi nhìn vào veapicore.js, phiên bản 7.0.2012.91, có sẵn tại

http://ecn.dev.virtualearth.net/mapcontrol/v7.0/js/bin/7.0.2012.91/en-us/veapicore.js

Module này sẽ được tải về khi bạn bao gồm sự kiểm soát bản đồ bing, như thế này:

<script charset="UTF-8" type="text/javascript" 
     src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"> 
</script> 

Tôi tìm thấy những gì tôi nghĩ là hai vấn đề khác biệt.

• Chức năng MapMath.locationRectToMercatorZoom (chức năng nội bộ, không nhằm mục đích sử dụng trực tiếp bởi ứng dụng) luôn trả về mức thu phóng 1 khi hộp giới hạn của LocationRect mở rộng kinh tuyến thứ 180. Điều này là không chính xác. Chức năng này được sử dụng bởi chức năng Map.setView() để tự động thiết lập zoom, dẫn đến hiện tượng zoom waaay out.

• LocationRect.fromLocations() sử dụng cách tiếp cận ngây thơ để xác định hộp giới hạn cho một tập hợp các vị trí. Trong thực tế, nó không được đảm bảo là "hộp giới hạn tối thiểu" hoặc "hình chữ nhật giới hạn tối thiểu". Hộp trở về từ phương pháp đó không bao giờ kéo dài kinh tuyến thứ 180, theo như tôi có thể nói. Ví dụ, LocationRect được trả lại cho một tập hợp các vị trí đại diện cho các biên giới của các đảo New Zealand, sẽ bắt đầu tại vĩ độ -176 và kéo dài Đông, tất cả các cách để kinh tuyến + 165. Điều này là sai.

Tôi đã khắc phục các sự cố này bằng cách vá lỗi mã trong veapicore.js.

function monkeyPatchMapMath() { 
    Microsoft.Maps.InternalNamespaceForDelay.MapMath. 
    locationRectToMercatorZoom = function (windowDimensions, bounds) { 
     var ins = Microsoft.Maps.InternalNamespaceForDelay, 
     d = windowDimensions, 
     g = Microsoft.Maps.Globals, 
     n = bounds.getNorth(), 
     s = bounds.getSouth(), 
     e = bounds.getEast(), 
     w = bounds.getWest(), 
     f = ((e+360 - w) % 360)/360, 
     //f = Math.abs(w - e)/360, 
     u = Math.abs(ins.MercatorCube.latitudeToY(n) - 
        ins.MercatorCube.latitudeToY(s)), 
     r = Math.min(d.width/(g.zoomOriginWidth * f), 
        d.height/(g.zoomOriginWidth * u)); 
     return ins.VectorMath.log2(r); 
    }; 
} 



function monkeyPatchFromLocations() { 
    Microsoft.Maps.LocationRect.fromLocations = function() { 
    var com = Microsoft.Maps.InternalNamespaceForDelay.Common, 
     o = com.isArray(arguments[0]) ? arguments[0] : arguments, 
     latMax, latMin, lngMin1, lngMin2, lngMax1, lngMax2, c, 
     lngMin, lngMax, LL, dx1, dx2, 
     pt = Microsoft.Maps.AltitudeReference, 
     s, e, n, f = o.length; 

    while (f--) 
     n = o[f], 
    isFinite(n.latitude) && isFinite(n.longitude) && 
     (latMax = latMax === c ? n.latitude : Math.max(latMax, n.latitude), 
     latMin = latMin === c ? n.latitude : Math.min(latMin, n.latitude), 
     lngMax1 = lngMax1 === c ? n.longitude : Math.max(lngMax1, n.longitude), 
     lngMin1 = lngMin1 === c ? n.longitude : Math.min(lngMin1, n.longitude), 
     LL = n.longitude, 
     (LL < 0) && (LL += 360), 
     lngMax2 = lngMax2 === c ? LL : Math.max(lngMax2, LL), 
     lngMin2 = lngMin2 === c ? LL : Math.min(lngMin2, LL), 
     isFinite(n.altitude) && pt.isValid(n.altitudeReference) && 
     (e = n.altitude, s = n.altitudeReference)); 

    dx1 = lngMax1 - lngMin1, 
    dx2 = lngMax2 - lngMin2, 
    lngMax = (dx1 > dx2) ? lngMax2 : lngMax1, 
    lngMin = (dx1 > dx2) ? lngMin2 : lngMin1; 

    return Microsoft.Maps.LocationRect.fromEdges(latMax, lngMin, latMin, lngMax, e, s); 
    }; 
} 

Những chức năng cần phải được gọi là lần trước khi sử dụng, nhưng sau khi nạp. Việc đầu tiên được tải chậm trễ tôi nghĩ, vì vậy bạn không thể làm các bản vá khỉ trên tài liệu đã sẵn sàng; bạn cần đợi cho đến khi bạn đã tạo một Microsoft.Maps.Map.

Điều đầu tiên chỉ thực hiện điều đúng cho một LocationRect. Phương pháp ban đầu lật các cạnh phía đông và phía tây trong trường hợp hình chữ nhật kéo dài kinh tuyến thứ 180.

Hàm thứ hai sửa phương thức fromLocations. Việc triển khai ban đầu lặp qua tất cả các vị trí và lấy kinh độ tối thiểu là "trái" và kinh độ tối đa là "đúng". Điều này không thành công khi kinh độ tối thiểu chỉ ở phía đông của kinh tuyến thứ 180 (giả sử, -178), và giá trị kinh độ tối đa chỉ là ở phía tây của cùng một dòng (nói, +165). Hộp giới hạn kết quả nên trải dài trên kinh tuyến thứ 180 nhưng trên thực tế giá trị được tính toán bằng cách sử dụng cách tiếp cận ngây thơ này đi theo con đường dài.

Việc thực hiện đã sửa sẽ tính toán hộp đó và cũng tính toán hộp giới hạn thứ hai. Đối với cái thứ hai, thay vì sử dụng giá trị kinh độ, nó sử dụng giá trị kinh độ hoặc kinh độ + 360, khi kinh độ là âm. Biến đổi kết quả thay đổi kinh độ từ một giá trị nằm trong khoảng từ -180 đến 180, thành một giá trị nằm trong khoảng từ 0 đến 360. Và sau đó hàm tính toán giá trị lớn nhất và tối thiểu của tập hợp giá trị mới đó.

Kết quả là hai hộp giới hạn: một có kinh độ từ -180 đến +180 và một có kinh độ từ 0 đến 360. Các hộp này có độ rộng khác nhau.

Việc triển khai cố định chọn hộp có chiều rộng hẹp hơn, giả sử rằng hộp nhỏ hơn là câu trả lời đúng. Điều này heuristic sẽ phá vỡ nếu bạn đang cố gắng để tính toán các bounding hộp cho một tập hợp các điểm đó là lớn hơn một nửa trái đất.

Một cách sử dụng ví dụ có thể trông như thế này:

monkeyPatchFromLocations(); 
bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints); 
monkeyPatchMapMath(); 
map1.setView({bounds:bounds}); 

Trang này chứng tỏ: http://jsbin.com/emobav/4

nhấp đúp vào bản đồ không bao giờ làm cho zoom waaay ra hiệu lực thi hành, như đã thấy trong http://jsbin.com/emobav/2

2

Có lẽ cách tiếp cận đơn giản hơn nhiều.

bounds = Microsoft.Maps.LocationRect.fromLocations(allPoints); 

LocationRect.fromLocations accepts a list of locations/array

đa giác của bạn mà bạn đã quấn quanh Úc giữ một hàm trả về một mảng các địa điểm, tên getLocations().

Có vẻ như người ta có thể gọi nó như thế này (Cú pháp kiểm tra kép);

var viewBoundaries = Microsoft.Maps.LocationRect.fromLocations(polygon.getLocations()); 

         map.setView({ bounds: viewBoundaries }); 
         map.setView({ zoom: 10 }); 

Điều này không hoạt động khi mở rộng 180? Tôi không hiểu tại sao nó không phải vì nó chỉ sử dụng các điểm từ đa giác. Nếu vậy, sau đây có thể là một giải pháp rất đơn giản cho bạn.

Bạn nói bất cứ khi nào bạn nhấp đúp vào bản đồ, nó sẽ mở bản đồ. Điều này làm cho toàn hợp lý vì bạn chỉ có thêm một event handler vào bản đồ, và thiết lập các giới hạn tới ranh giới của bản đồ trong đoạn mã sau:

function doubleclickCallback(e) { 
    e.handled = true; 
    var bounds = map.getBounds(); 
    map.setView({ bounds: bounds }); 
} 

MM.Events.addHandler(map, "dblclick", doubleclickCallback); 

tôi sẽ nghĩ rằng bạn sẽ cần phải thêm handler nhấp chuột vào đa giác để mở rộng chế độ xem thành đa giác được chỉ định.

Microsoft.Maps.Events.addHandler(polygon, 'click', doubleclickCallback); 

Sau đó, trong doubleclickCallback của bạn:

function doubleclickCallback(e) { 
    // Now we are getting the boundaries of our polygon 
    var bounds = e.target.getLocations(); 
    map.setView({ bounds: bounds }); 
    map.setView({ zoom: 9}); 
} 
+0

* Điều này không hoạt động khi bắc qua 180? Tôi không hiểu tại sao nó sẽ không ... * Chris, nó không hoạt động, bởi vì có một lỗi trong thư viện bản đồ của Microsoft. Đọc những gì tôi đã viết trong câu trả lời của tôi. Thật dễ dàng để chứng minh lỗi bằng cách chuyển bất kỳ tập hợp các điểm nào trên 180. Thuật toán mà chúng sử dụng để tính toán hộp giới hạn hoặc trung tâm là ngây thơ. Đọc câu trả lời của tôi, tôi giải thích tất cả điều này. – Cheeso

+0

@Cheeso * Hàm thứ hai sửa phương thức fromLocations. * Xin lỗi, tôi đã không hiểu rằng từLocations cũng bị lỗi khi đọc lần đầu tiên. Để xấu vì điều đó sẽ làm cho getLocations() trở thành một ứng viên tuyệt vời! Bravo khi bạn tìm và sửa Cheeso, tốt nhất! – clamchoda

1

này dường như cố định trong veapicore.js như ít nhất v7.0/7.0.20130619132259.11

Các vấn đề liên quan