/**
* Đưa khung bản đồ OpenStreetMap tiếng Việt vào các bài địa danh có thẻ
* {{coord|display=title}}.
*
* Xem [[Trợ giúp:OpenStreetMap]] và <http://wiki.osm.org/wiki/Vi:Main_Page>.
*/
mw.loader.using("mediawiki.util", function () {
var wpMapDefaults = {
//* {string} Phiên bản Leaflet
leafletVersion: "0.4.4",
//* {number} Chiều cao bản đồ (điểm ảnh)
height: 350,
//* {string} Địa chỉ hình tượng ([[Tập tin:WMA button2b.png]])
iconUrl: "//upload.wikimedia.org/wikipedia/commons/thumb/5/55/WMA_button2b.png/17px-WMA_button2b.png",
/**
* {string} Định dạng của các địa chỉ hình ảnh bản đồ. Ngoài các placeholder
* chuẩn của Leaflet, kịch bản này hỗ trợ các placeholder sau:
*
* <dl>
* <dt>{layer}</dt><dd>tên của lớp bản đồ</dd>
* </dl>
*
* @see http://leaflet.cloudmade.com/reference.html#url-template
*/
layerUrlFormat: location.protocol + "//{s}.www.toolserver.org/tiles/{layer}/{z}/{x}/{y}.png",
// Địa phương hóa
//* {string} Mã ngôn ngữ của các địa danh trên bản đồ
language: mw.config.get("wgContentLanguage"),
//* {string} Tooltip của hình tượng hiện/ẩn bản đổ
iconTooltip: "Xem vị trí này trên bản đồ tương tác",
//* {string} Phím tắt (không bao gồm Ctrl v.v.) hiện/ẩn bản đồ
iconAccessKey: undefined, // thí dụ "y"
zoomIn: "Phóng to",
zoomOut: "Thu nhỏ",
layerBlank: "Bản đồ",
layerLabels: "Địa danh",
layerArticles: "Bài viết",
/**
* {string} Định dạng của lời ghi công những người đóng góp vào
* OpenStreetMap. Chuỗi có thể có các placeholder sau:
*
* <dl>
* <dt>{osm}</dt>
* <dd>liên kết đến #creditOsmUrl có văn bản #creditOsm</dd>
* <dt>{osmLicense}</dt>
* <dd>liên kết đến #creditOsmUrlLicenseUrl có văn bản #creditOsmLicense</dd>
* <dt>{help}</dt>
* <dd>liên kết đến #helpArticle có văn bản #help</dd>
* </dl>
*/
credit: "© những nguời đóng góp vào {osm} ({osmLicense}, {help})",
creditOsm: "OpenStreetMap",
creditOsmUrl: "//www.openstreetmap.org/",
creditOsmLicense: "ODbL + CC BY-SA",
creditOsmLicenseUrl: "//www.openstreetmap.org/copyright",
help: "Trợ giúp",
helpArticle: "Trợ giúp:OpenStreetMap",
// WIWOSM
layerShapes: "Hình dạng",
creditShapes: "WIWOSM",
creditShapesUrl: "//wiki.openstreetmap.org/wiki/WIWOSM?uselang=vi",
// Bản đồ biển xa lộ Mỹ
layerShields: "Biển xa lộ",
shieldsUrlFormat: "http://elrond.aperiodic.net/mtiles/cutouts/{z}/{x}/{y}.png",
creditShields: "Shields",
creditShieldsUrl: "http://elrond.aperiodic.net/shields/",
tunnelUrl: location.protocol + "//toolserver.org/~mxn/poitunnel.html",
};
window.wpMapConfig = $.extend({}, window.wpMapConfig, wpMapDefaults);
/**
* Tải các kịch bản và bảng kiểu của thư viện bản đồ Leaflet của CloudMade. Hàm
* này chạy một cách bất đồng bộ.
*
* @param done {function} Hàm sẽ được gọi sau khi kịch bản của thư viện được
* tải xong.
*/
function loadLeaflet(done) {
// var root = "//toolserver.org/~osm/libs/leaflet/" + wpMapConfig.leafletVersion + "/dist/";
var root = "//cdn.leafletjs.com/leaflet-" + wpMapConfig.leafletVersion + "/";
mw.loader.load(root + "leaflet.css", "text/css");
if ($.browser.msie && parseInt($.browser.version, 10) < 9) {
mw.loader.load(root + "leaflet.ie.css", "text/css");
}
$.getScript(root + "leaflet.js").done(done);
}
/**
* Cho ra định dạng của các địa chỉ hình bản đồ trong lớp được cho vào.
*
* @param name {string} Tên của lớp bản đồ.
* @returns {string} Định dạng của địa chỉ hình bản đồ.
*/
function layerUrl(name) {
return wpMapConfig.layerUrlFormat.replace("{layer}", name);
}
/**
* Phân tích mảng chứa các chuỗi tham số thành một đối tượng tham số.
*
* @param components {array} Các thành phần của tọa độ.
* @param width {number} Chiều rộng hiện tại của bản đồ.
* @returns {object} Từ điển có tọa độ và các tham số.
*/
function parsedParameters(components, width) {
//* Phân tích một thành phần của tọa độ.
var parseCoord = function (pos, neg) {
var i = 0, coord = 0;
for (; i < components.length; i++) {
var component = components[i].toUpperCase();
if (!component || component == pos || component == neg) {
if (component == neg) coord *= -1;
break;
}
coord += parseFloat(component, 10) / Math.pow(60, i);
}
components.splice(0, i + 1);
return coord;
};
// Phân tích vĩ độ và kinh độ.
var lat = parseCoord("N", "S");
var lng = parseCoord("E", "W");
// Đưa các tham số khác vào từ điển.
var params = {
center: new L.LatLng(lat, lng),
};
for (var i = 0; i < components.length; i++) {
var param = components[i].split(":");
params[param[0]] = param[1];
}
// Phân tích các tham số tỷ lệ theo [[:en:Template:Coord#type:T]] và
// [[OpenStreetMap:Zoom levels]].
var zoomsByType = {
country: 6, satellite: 6,
adm1st: 9,
adm2nd: 11,
adm3rd: 12, mountain: 12, isle: 12, waterbody: 12, river: 12,
city: 12, // city(dân số) là 11–14, tùy dân số
forest: 13, glacier: 13, event: 13,
airport: 14,
edu: 16, pass: 16, railwaystation: 16, landmark: 16,
};
var type = params.type ? (params.type.match(/(\w+)(?:\(([\d,]+)\))?/) || []) : [];
params.zoom = zoomsByType[type[1]] || 11;
if (type[1] == "city" && type[2]) {
var pop = params.pop = parseInt(type[2].replace(",", ""), 10);
if (pop >= 1e7) params.zoom = 11;
else if (pop >= 1e5) params.zoom = 12;
else if (pop >= 1e3) params.zoom = 13;
else params.zoom = 14;
}
// 1:...
var scalesByZoom = [5e8, 2.5e8, 1.5e8, 7e7, 3.5e7, 1.5e7, 1e7, 4e6, 2e6, 1e6,
1e5, 2.5e5, 1.5e5, 7e4, 3.5e4, 1.5e4, 8e3, 4e3, 2e3];
if (params.scale) {
params.scale = parseFloat(params.scale);
for (var i = scalesByZoom.length - 1; i >= 0; i--) {
if (params.scale <= scalesByZoom[i]) {
params.zoom = i;
break;
}
}
}
// Mét / điểm ảnh
var mppByZoom = [156412, 78206, 39103, 19551, 9776, 4888, 2444, 1222,
610.984, 305.492, 152.746, 76.373, 38.187, 19.093, 9.547,
4.773, 2.387, 1.193, 0.596];
if (params.dim) {
var dim = parseFloat(params.dim, 10);
var unit = params.dim.indexOf("km") < 0 ? 1 : 1e3;
dim *= unit;
for (var i = mppByZoom.length - 1; i >= 0; i--) {
if (dim <= mppByZoom[i] * wpMapConfig.height) {
params.zoom = i;
break;
}
}
if (width) {
for (var i = mppByZoom.length - 1; i >= 0; i--) {
if (dim <= mppByZoom[i] * width) {
params.zoom = Math.round((params.zoom + i) / 2.);
break;
}
}
}
}
// [[en:ISO 3166-1 alpha-2]]
if (params.region) params.country = params.region.match(/^\w\w(?=-)/);
return params;
}
//window.parsedParameters = parsedParameters; // debug
var earthRadius = 6378137 /* m */;
/**
* Chuyển đổi các điểm EPSG:3857 (Mercator hình cầu) thành tọa độ.
*/
function unprojectLayerPoints(points) {
if (typeof(points[0]) !== "number") {
for (var i = 0; i < points.length; i++) {
unprojectLayerPoints(points[i]);
}
return;
}
var pt = new L.Point(points[0], points[1]).divideBy(earthRadius);
var coord = L.Projection.SphericalMercator.unproject(pt);
points[0] = coord.lng;
points[1] = coord.lat;
}
/**
* Tạo ra đối tượng bản đồ và thiết lập nội dung.
*
* @param id {string} ID của phần tử sẽ trở thành bản đồ.
* @param params {object} Từ điển các tham số của bản đồ.
* @returns {object} Bản đồ Leaflet.
*/
function createMap(id, params) {
// Tải các lớp OpenStreetMap.
var osmAttrib = wpMapConfig.credit.replace("{osm}", "<a id='openstreetmap-credit' href='" +
wpMapConfig.creditOsmUrl + "'>" + wpMapConfig.creditOsm + "</a>")
.replace("{osmLicense}", "<a href='" + wpMapConfig.creditOsmLicenseUrl +
"'>" + wpMapConfig.creditOsmLicense + "</a>")
.replace("{help}", "<a href='" + mw.util.getUrl(wpMapConfig.helpArticle) +
"'>" + wpMapConfig.help + "</a>");
var blank = new L.TileLayer(layerUrl("osm-no-labels"), {
attribution: osmAttrib,
});
var shapes = new L.GeoJSON(undefined, {
style: function (feature) {
return {
opacity: 0.5,
fillColor: "white",
clickable: false,
};
},
});
var labels = new L.TileLayer(layerUrl("osm-labels-" + wpMapConfig.language));
labels.on("tileerror", function (evt) {
// Nếu lớp chưa có vị trí nào đó, thay thế bằng hình bản đồ đa ngôn ngữ.
var layerName = "osm-labels-" + wpMapConfig.language;
if (evt.url.indexOf(layerName) < 0) return;
evt.tile.src = evt.url.replace(layerName, "osm");
});
var articles = new L.LayerGroup();
// <a href='/wiki/Wikipedia:Quyền_tác_giả'>Wikipedia</a>
// Tạo bản đồ.
var map = new L.Map(id, {
center: params.center,
zoom: params.zoom,
layers: [blank, shapes, labels, articles],
});
// Việt hóa các điều khiển.
map.attributionControl.setPrefix("<a href='http://leaflet.cloudmade.com'>Leaflet</a>");
$(".leaflet-control-zoom-in").attr("title", wpMapConfig.zoomIn);
$(".leaflet-control-zoom-out").attr("title", wpMapConfig.zoomOut);
// Tạo điều khiển để bật/tắt các lớp.
var bases = {};
bases[wpMapConfig.layerBlank] = blank;
var overlays = {};
overlays[wpMapConfig.layerLabels] = labels;
// Thiết lập lớp biển xa lộ Mỹ.
if (["US", "CA", "MX"].indexOf(params.country) >= 0) {
var shieldsAttrib = ("<a id='shields-credit' href='" +
wpMapConfig.creditShieldsUrl + "'>" +
wpMapConfig.creditShields + "</a>");
var shields = new L.TileLayer(wpMapConfig.shieldsUrlFormat, {
attribution: shieldsAttrib,
});
overlays[wpMapConfig.layerShields] = shields;
}
overlays[wpMapConfig.layerShapes] = shapes;
overlays[wpMapConfig.layerArticles] = articles;
map.addControl(new L.Control.Layers(bases, overlays));
// Liên kết đến vị trí hiện tại trên trang chủ OpenStreetMap.
var updateOsmLink = function (link) {
if (!link.length) return;
var center = map.getCenter();
var base = link.attr("href").match(/^[^?]+/);
link.attr("href", base + "?lat=" + center.lat + "&lon=" + center.lng +
"&zoom=" + map.getZoom());
};
var updateOsmLinks = function () {
updateOsmLink($("#openstreetmap-credit"));
updateOsmLink($("#shields-credit"));
};
updateOsmLinks();
map.on("moveend", updateOsmLinks);
// Thiết lập lớp ghim và lớp hình dạng.
var tunnel = $("#poi-tunnel");
if (!tunnel.length) {
tunnel = $("<iframe id='poi-tunnel' src='" + wpMapConfig.tunnelUrl + "'></iframe>");
$(document.body).after(tunnel);
tunnel.hide();
}
var receiveArticles = function (pois) {
if (pois.length === undefined) return;
articles.clearLayers();
for (var i = 0; i < pois.length; i++) {
if (!pois[i].visible) continue;
var marker = new L.CircleMarker(new L.LatLng(pois[i].lat, pois[i].lon), {
color: "red",
opacity: 0.4,
fillOpacity: 0.1,
radius: 5,
});
articles.addLayer(marker);
var content = mw.html.element("a", {
href: mw.util.getUrl(pois[i].name),
title: pois[i].name,
}, pois[i].name);
marker.bindPopup(content);
}
};
var articleTitle = mw.config.get("wgTitle");
var receiveShapes = function (json) {
shapes.clearLayers();
if (json.coordinates) unprojectLayerPoints(json.coordinates);
else if (json.geometries) {
for (var i = 0; i < json.geometries.length; i++) {
unprojectLayerPoints(json.geometries[i].coordinates);
}
}
if (shapes.addData(json) && json.type !== "Point") {
var zoom = map.getBoundsZoom(shapes.getBounds());
if (zoom > 1) map.fitBounds(shapes.getBounds());
//else {
// var centerHemisphere = params.center.lng >= 0 ? 1 : -1;
// shapes.eachLayer(function (layer) {
// // Nếu hình đa giác cùng bán cầu với điểm trung tâm, thì
// // không cần di chuyển hình đa giác.
// var layerPt = (layer instanceof L.Marker ? layer.getLatLng() :
// layer.getBounds().getCenter());
// var hemisphere = layerPt.lng >= 0 ? 1 : -1;
// if (centerHemisphere < 0 && hemisphere < 0) return;
// if (centerHemisphere > 0 && hemisphere > 0) return;
//
// if (layer instanceof L.Marker) {
// layer.setLatLng(layerPt.lat,
// layerPt.lng + 360 * centerHemisphere);
// return;
// }
//
// var coords = layer.getLatLngs();
// for (var i = 0; i < coords.length; i++) {
// coords[i].lng += 360 * centerHemisphere;
// }
// layer._latlngs = coords; // tránh _convertLatLngs()
// layer.redraw();
// });
// zoom = map.getBoundsZoom(shapes.getBounds());
// if (zoom > 1) map.fitBounds(shapes.getBounds());
//}
}
};
addEventListener("message", function (evt) {
if (evt.origin !== location.protocol + "//toolserver.org") return;
var data = evt.data;
if (data.length !== undefined) receiveArticles(data); // poitunnel cũ
else if (data.subject === "pois") receiveArticles(data.data);
else if (data.subject === "shape") receiveShapes(data.data);
}, false);
var requestShapes = function () {
tunnel[0].contentWindow.postMessage({
subject: "shape",
lang: wpMapConfig.language,
article: articleTitle,
}, wpMapConfig.tunnelUrl);
};
tunnel.load(requestShapes);
var requestArticles = function () {
tunnel[0].contentWindow.postMessage({
subject: "pois",
lang: wpMapConfig.language,
bbox: map.getBounds().toBBoxString(),
}, wpMapConfig.tunnelUrl);
};
tunnel.load(requestArticles);
map.on("moveend", requestArticles);
map.on("layeradd", function (evt) {
if (evt.layer !== articles) return;
requestArticles();
map.on("moveend", requestArticles);
});
map.on("layerremove", function (evt) {
if (evt.layer === articles) map.off("moveend", requestArticles);
});
// Đặt ghim vào vị trí của chủ đề bài.
var marker = new L.Marker(params.center);
map.addLayer(marker);
var content = mw.html.element("strong", {}, articleTitle);
marker.bindPopup(content);
return map;
}
/**
* Cài đặt khung bản đồ và hình tượng hiện/ẩn nó và gỡ bỏ WikiMiniAtlas.
*/
function installMap() {
// Tìm tọa độ trong liên kết của GeoHack.
var link = $("#coordinates a[href*='geohack']:not([href*='_globe:'])");
var params = link && link.attr("href");
params = params && params.match(/[?&]params=(.+?)(?:&|$)/);
params = params && params[1].split("_");
if (!params) return;
// Tạo khung đựng bản đồ.
$("#contentSub").append("<div id='openstreetmap-container' style='clear: both; display: none;'><div id='openstreetmap' style='height: " +
wpMapConfig.height + "px; width: 100%;'></div></div>");
// Vô hiệu WikiMiniAtlas và xóa hình tượng của nó nếu bản đồ đã tải.
$("#coordinates img").unbind("click").remove();
wma_settings = {enabled: false};
// Tạo hình tượng địa cầu.
var icon = $("<a id='openstreetmap-icon' href='#'><img src='" +
wpMapConfig.iconUrl + "' /></a>");
// Đặt tooltip và phím tắt nếu có.
var tooltip = wpMapConfig.iconTooltip;
if (wpMapConfig.iconAccessKey) {
tooltip += " [" + wpMapConfig.iconAccessKey + "]";
icon.attr("accesskey", wpMapConfig.iconAccessKey);
}
icon.attr("title", tooltip);
if (wpMapConfig.iconAccessKey) mw.util.updateTooltipAccessKeys(icon);
// Khi nhấn chuột vào hình tượng, mở/đóng bản đồ.
icon.click(function (evt) {
// Nếu bản đồ đã được thiết lập, chỉ việc hiện/ẩn.
var container = $("#openstreetmap-container");
if (!container || container.hasClass("openstreetmap-loaded")) {
container.slideToggle("slow", function () {
this.map.setView(this.mapParams.center, this.mapParams.zoom);
});
return;
}
container.addClass("openstreetmap-loaded");
loadLeaflet(function () {
// Thiết lập và hiển thị bản đồ.
container.slideDown("slow", function () {
this.mapParams = parsedParameters(params, $(this).width());
// console.log(this.mapParams); // debug
this.map = createMap("openstreetmap", this.mapParams);
});
});
});
// Chèn hình tượng đằng trước liên kết tọa độ.
link.before(icon);
link.before(" ");
};
$(installMap);
});