地趣续
坐在陪审团召集室里翻阅自己以前写的博客,看到十七年半以前这篇《地趣》,决定搞个大新闻,把当时的自己批判一番了:当时怎么那么狂妄地出题目考读者,好像自己会做似的,真是有趣。
欧盟最西的边界线,在什么地方?
如今我查维基百科的相关条目,所谓的欧盟“最西”需要各种限定条件的:如果在地理意义的“欧洲”范围内找,“最西”点在亚速尔群岛的某个小岛,属于葡萄牙;如果在欧洲大陆找,那么是葡萄牙的罗卡角。但是,当时的我说到“边界线”,那么想必我指的是陆路边界了,那么真相只有一个:当时的我心里想的是法属圭亚那和苏里南的边界。但是如今的我反倒疑惑了,就去查,查下来的结果是:法属圭亚那确实法理上是属于欧盟的,而阿鲁巴、法属波利尼西亚这样的属于“海外领地”而不属于欧盟。所以我当时的答案应当是合理的。不过如今又有一个特例需要检查了:北极有个汉斯岛,加拿大和丹麦争执了很多年,最终在2022年决定将这个岛一分为二,所以这个岛上也有一条陆地边界线了。这个岛的经度还确实比法属圭亚那更靠西,是不是时过境迁答案也要更新了呢?我仔细检查了一下,还问了GenAI,结论是不算:汉斯岛一半属于加拿大,另一半属于丹麦治下的格陵兰,而格陵兰早就退出欧盟了。
(function() {
var mapElementId = 'post-map-eu';
var mapElement = document.getElementById(mapElementId);
var maxZoom = 13;
// Location IDs needed for this map (generated at build time)
var requestedLocationIds = [
'cayenne',
'hans_island',
'monchique_islet'
];
// Custom popups and titles (generated at build time)
var customPopups = null;
var locationTitles = {
'cabinda': "卡宾达,属于安哥拉,以前是葡属刚果。",
'cayenne': "卡宴,法属圭亚那首府",
'hans_island': "汉斯岛,加拿大的第二个陆上邻国在这里",
'monchique_islet': "Monchique Islet,属于葡萄牙亚速尔群岛,一般认为的欧盟最西端。",
'neum': "尼姆,波黑出海口",
'dubrovnik': "杜布罗夫尼克,克罗地亚飞地",
};
// Mapbox GL version and CDN sources
var mapboxVersion = '2.14.1';
var mapboxScriptSources = [
'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.js',
'https://cdn.jsdelivr.net/npm/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js',
'https://unpkg.com/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js'
];
// Load Mapbox GL CSS
function loadMapboxCSS() {
if (document.querySelector('link[href*="mapbox-gl.css"]')) return;
var css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.css';
document.head.appendChild(css);
}
// Load Mapbox GL JS with fallbacks
function loadMapbox(callback, index) {
index = index || 0;
if (window.mapboxgl) {
callback();
return;
}
if (index >= mapboxScriptSources.length) {
callback(new Error('All Mapbox sources failed'));
return;
}
var src = mapboxScriptSources[index];
var script = document.createElement('script');
script.src = src;
script.onload = function() { callback(); };
script.onerror = function() {
console.warn('Mapbox GL JS load failed from:', src);
loadMapbox(callback, index + 1);
};
document.head.appendChild(script);
}
// Get popup content for a location
function getPopupContent(loc) {
if (customPopups && customPopups[loc.id]) {
return customPopups[loc.id];
} else if (locationTitles[loc.id]) {
return '' + locationTitles[loc.id] + '';
} else {
return '' + loc.name + '';
}
}
// Initialize Mapbox map
function initMapbox(locations) {
var MAPBOX_TOKEN = 'pk.eyJ1IjoibGVvbnNvbiIsImEiOiJjbWpraWtqOTUyYnZnM2dvd2thajNtMGZsIn0.08xHonUXcanx3ELmKUv0dg';
if (!MAPBOX_TOKEN) {
throw new Error('Missing mapbox_token');
}
if (typeof mapboxgl === 'undefined') {
throw new Error('mapboxgl not defined');
}
if (!mapboxgl.supported()) {
throw new Error('Mapbox GL not supported');
}
mapboxgl.accessToken = MAPBOX_TOKEN;
// Calculate bounds
var bounds = new mapboxgl.LngLatBounds();
locations.forEach(function(loc) {
bounds.extend([loc.lng, loc.lat]);
});
var map = new mapboxgl.Map({
container: mapElementId,
style: 'mapbox://styles/mapbox/streets-v12',
bounds: bounds,
fitBoundsOptions: { padding: 50, maxZoom: maxZoom }
});
map.addControl(new mapboxgl.NavigationControl({ showCompass: false, showZoom: true }), 'top-right');
map.on('load', function() {
locations.forEach(function(loc) {
new mapboxgl.Marker()
.setLngLat([loc.lng, loc.lat])
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(getPopupContent(loc)))
.addTo(map);
});
setTimeout(function() { map.resize(); }, 100);
});
}
// Initialize Leaflet map (fallback)
function initLeaflet(locations) {
if (typeof L === 'undefined') {
mapElement.textContent = 'Map failed to load.';
console.error('Leaflet not available for fallback.');
return;
}
var bounds = L.latLngBounds(locations.map(function(loc) { return [loc.lat, loc.lng]; }));
var map = L.map(mapElementId);
map.fitBounds(bounds, { padding: [50, 50], maxZoom: maxZoom });
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
locations.forEach(function(loc) {
L.marker([loc.lat, loc.lng])
.addTo(map)
.bindPopup(getPopupContent(loc));
});
setTimeout(function() { map.invalidateSize(); }, 100);
console.warn('Using Leaflet fallback for inline map.');
}
// Main initialization
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/locations.json')
.then(function(response) { return response.json(); })
.then(function(locationData) {
var locations = [];
requestedLocationIds.forEach(function(locId) {
if (locationData[locId]) {
var locData = locationData[locId];
locations.push({
lat: locData.lat,
lng: locData.lng,
name: locData.name,
id: locId
});
}
});
if (locations.length === 0) {
console.warn('No valid locations found for inline map.');
return;
}
// Try Mapbox first, fall back to Leaflet
loadMapboxCSS();
loadMapbox(function(err) {
if (err) {
console.warn('Mapbox failed to load, using Leaflet:', err);
initLeaflet(locations);
return;
}
try {
initMapbox(locations);
} catch (e) {
console.warn('Mapbox init failed, using Leaflet:', e);
initLeaflet(locations);
}
});
})
.catch(function(error) {
console.error('Failed to load location data:', error);
mapElement.textContent = 'Failed to load map data.';
});
});
})();
欧盟最西端在哪里
为什么安哥拉本土和卡宾达之间被两个刚果包围,却不属于任何一个刚果?
这句话有语病,如果我不是我自己,那我完全不知道我在说什么。现在我可以帮我自己解释一下了,我的意思是,为什么“卡宾达”这个地方被刚果共和国和刚果民主共和国包围,并且和安哥拉并不接壤,却不属于那两个刚果之一,而是属于安哥拉。
我很确定我当时的理解是一知半解的,或者说是皮毛的。这许多年过去,我越来越不敢说我答的上来这个问题了。不过我可以分享我所知道的:在当时我提到的那本我小时候的地图册里,卡宾达被标注成一个独立的国家,不过后来在我任何一本接触到的世界地图里,卡宾达都只是安哥拉的一个省。原因可能是1975年卡宾达短暂独立过,但是几个月之后就被安哥拉出兵吞并了。而安哥拉出兵也有依据:欧洲殖民时期,那一带有法属刚果(后来成为刚果共和国)、比属刚果(后来成为刚果民主共和国)和葡属刚果(就是卡宾达),安哥拉则是葡萄牙殖民地。原本葡萄牙殖民者将葡属刚果与安哥拉区分对待,但后来又合并起来对待了,所以独立后的安哥拉对卡宾达主张了主权并且成功,当然卡宾达当地总有不同意见,并且也有人使用武力表达不同意见,安哥拉也使用武力对应不同意见就是了。
(function() {
var mapElementId = 'post-map-cabinda';
var mapElement = document.getElementById(mapElementId);
var maxZoom = 13;
// Location IDs needed for this map (generated at build time)
var requestedLocationIds = [
'cabinda',
'point_noire',
'mbanza_congo',
'matadi'
];
// Custom popups and titles (generated at build time)
var customPopups = null;
var locationTitles = {
'cabinda': "卡宾达,属于安哥拉,以前是葡属刚果。",
'cayenne': "卡宴,法属圭亚那首府",
'hans_island': "汉斯岛,加拿大的第二个陆上邻国在这里",
'monchique_islet': "Monchique Islet,属于葡萄牙亚速尔群岛,一般认为的欧盟最西端。",
'neum': "尼姆,波黑出海口",
'dubrovnik': "杜布罗夫尼克,克罗地亚飞地",
};
// Mapbox GL version and CDN sources
var mapboxVersion = '2.14.1';
var mapboxScriptSources = [
'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.js',
'https://cdn.jsdelivr.net/npm/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js',
'https://unpkg.com/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js'
];
// Load Mapbox GL CSS
function loadMapboxCSS() {
if (document.querySelector('link[href*="mapbox-gl.css"]')) return;
var css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.css';
document.head.appendChild(css);
}
// Load Mapbox GL JS with fallbacks
function loadMapbox(callback, index) {
index = index || 0;
if (window.mapboxgl) {
callback();
return;
}
if (index >= mapboxScriptSources.length) {
callback(new Error('All Mapbox sources failed'));
return;
}
var src = mapboxScriptSources[index];
var script = document.createElement('script');
script.src = src;
script.onload = function() { callback(); };
script.onerror = function() {
console.warn('Mapbox GL JS load failed from:', src);
loadMapbox(callback, index + 1);
};
document.head.appendChild(script);
}
// Get popup content for a location
function getPopupContent(loc) {
if (customPopups && customPopups[loc.id]) {
return customPopups[loc.id];
} else if (locationTitles[loc.id]) {
return '' + locationTitles[loc.id] + '';
} else {
return '' + loc.name + '';
}
}
// Initialize Mapbox map
function initMapbox(locations) {
var MAPBOX_TOKEN = 'pk.eyJ1IjoibGVvbnNvbiIsImEiOiJjbWpraWtqOTUyYnZnM2dvd2thajNtMGZsIn0.08xHonUXcanx3ELmKUv0dg';
if (!MAPBOX_TOKEN) {
throw new Error('Missing mapbox_token');
}
if (typeof mapboxgl === 'undefined') {
throw new Error('mapboxgl not defined');
}
if (!mapboxgl.supported()) {
throw new Error('Mapbox GL not supported');
}
mapboxgl.accessToken = MAPBOX_TOKEN;
// Calculate bounds
var bounds = new mapboxgl.LngLatBounds();
locations.forEach(function(loc) {
bounds.extend([loc.lng, loc.lat]);
});
var map = new mapboxgl.Map({
container: mapElementId,
style: 'mapbox://styles/mapbox/streets-v12',
bounds: bounds,
fitBoundsOptions: { padding: 50, maxZoom: maxZoom }
});
map.addControl(new mapboxgl.NavigationControl({ showCompass: false, showZoom: true }), 'top-right');
map.on('load', function() {
locations.forEach(function(loc) {
new mapboxgl.Marker()
.setLngLat([loc.lng, loc.lat])
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(getPopupContent(loc)))
.addTo(map);
});
setTimeout(function() { map.resize(); }, 100);
});
}
// Initialize Leaflet map (fallback)
function initLeaflet(locations) {
if (typeof L === 'undefined') {
mapElement.textContent = 'Map failed to load.';
console.error('Leaflet not available for fallback.');
return;
}
var bounds = L.latLngBounds(locations.map(function(loc) { return [loc.lat, loc.lng]; }));
var map = L.map(mapElementId);
map.fitBounds(bounds, { padding: [50, 50], maxZoom: maxZoom });
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
locations.forEach(function(loc) {
L.marker([loc.lat, loc.lng])
.addTo(map)
.bindPopup(getPopupContent(loc));
});
setTimeout(function() { map.invalidateSize(); }, 100);
console.warn('Using Leaflet fallback for inline map.');
}
// Main initialization
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/locations.json')
.then(function(response) { return response.json(); })
.then(function(locationData) {
var locations = [];
requestedLocationIds.forEach(function(locId) {
if (locationData[locId]) {
var locData = locationData[locId];
locations.push({
lat: locData.lat,
lng: locData.lng,
name: locData.name,
id: locId
});
}
});
if (locations.length === 0) {
console.warn('No valid locations found for inline map.');
return;
}
// Try Mapbox first, fall back to Leaflet
loadMapboxCSS();
loadMapbox(function(err) {
if (err) {
console.warn('Mapbox failed to load, using Leaflet:', err);
initLeaflet(locations);
return;
}
try {
initMapbox(locations);
} catch (e) {
console.warn('Mapbox init failed, using Leaflet:', e);
initLeaflet(locations);
}
});
})
.catch(function(error) {
console.error('Failed to load location data:', error);
mapElement.textContent = 'Failed to load map data.';
});
});
})();
卡宾达位置
为什么克罗地亚的领土贪婪地向东南延伸,一条狭长的海岸线冷酷地堵住波黑,使其没有一个出海口?
当时的我对细节的注意并不够,其实波黑是有出海口的,叫做尼姆。波斯尼亚和黑塞哥维那在这里有二十公里的海岸线,这二十公里海岸线两边的延长线都属于克罗地亚。著名的杜布罗夫尼克1位于尼姆的东边,是克罗地亚的飞地:所以波黑出海不方便(港口条件不太好),克罗地亚杜布罗夫尼克的陆路交通也不方便,两国只好协商,克罗地亚提供更好的港口,波黑保证陆路畅通。另外,虽然我是我自己,但是我完全不知道我当时的答案是什么,而我现在查到波黑和克罗地亚如此这般的边界划分,在1699年就协定了:当时拉古萨共和国(首都在杜布罗夫尼克)把尼姆割让给了奥斯曼土耳其。
(function() {
var mapElementId = 'post-map-bc';
var mapElement = document.getElementById(mapElementId);
var maxZoom = 13;
// Location IDs needed for this map (generated at build time)
var requestedLocationIds = [
'dubrovnik',
'neum'
];
// Custom popups and titles (generated at build time)
var customPopups = null;
var locationTitles = {
'cabinda': "卡宾达,属于安哥拉,以前是葡属刚果。",
'cayenne': "卡宴,法属圭亚那首府",
'hans_island': "汉斯岛,加拿大的第二个陆上邻国在这里",
'monchique_islet': "Monchique Islet,属于葡萄牙亚速尔群岛,一般认为的欧盟最西端。",
'neum': "尼姆,波黑出海口",
'dubrovnik': "杜布罗夫尼克,克罗地亚飞地",
};
// Mapbox GL version and CDN sources
var mapboxVersion = '2.14.1';
var mapboxScriptSources = [
'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.js',
'https://cdn.jsdelivr.net/npm/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js',
'https://unpkg.com/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js'
];
// Load Mapbox GL CSS
function loadMapboxCSS() {
if (document.querySelector('link[href*="mapbox-gl.css"]')) return;
var css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.css';
document.head.appendChild(css);
}
// Load Mapbox GL JS with fallbacks
function loadMapbox(callback, index) {
index = index || 0;
if (window.mapboxgl) {
callback();
return;
}
if (index >= mapboxScriptSources.length) {
callback(new Error('All Mapbox sources failed'));
return;
}
var src = mapboxScriptSources[index];
var script = document.createElement('script');
script.src = src;
script.onload = function() { callback(); };
script.onerror = function() {
console.warn('Mapbox GL JS load failed from:', src);
loadMapbox(callback, index + 1);
};
document.head.appendChild(script);
}
// Get popup content for a location
function getPopupContent(loc) {
if (customPopups && customPopups[loc.id]) {
return customPopups[loc.id];
} else if (locationTitles[loc.id]) {
return '' + locationTitles[loc.id] + '';
} else {
return '' + loc.name + '';
}
}
// Initialize Mapbox map
function initMapbox(locations) {
var MAPBOX_TOKEN = 'pk.eyJ1IjoibGVvbnNvbiIsImEiOiJjbWpraWtqOTUyYnZnM2dvd2thajNtMGZsIn0.08xHonUXcanx3ELmKUv0dg';
if (!MAPBOX_TOKEN) {
throw new Error('Missing mapbox_token');
}
if (typeof mapboxgl === 'undefined') {
throw new Error('mapboxgl not defined');
}
if (!mapboxgl.supported()) {
throw new Error('Mapbox GL not supported');
}
mapboxgl.accessToken = MAPBOX_TOKEN;
// Calculate bounds
var bounds = new mapboxgl.LngLatBounds();
locations.forEach(function(loc) {
bounds.extend([loc.lng, loc.lat]);
});
var map = new mapboxgl.Map({
container: mapElementId,
style: 'mapbox://styles/mapbox/streets-v12',
bounds: bounds,
fitBoundsOptions: { padding: 50, maxZoom: maxZoom }
});
map.addControl(new mapboxgl.NavigationControl({ showCompass: false, showZoom: true }), 'top-right');
map.on('load', function() {
locations.forEach(function(loc) {
new mapboxgl.Marker()
.setLngLat([loc.lng, loc.lat])
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(getPopupContent(loc)))
.addTo(map);
});
setTimeout(function() { map.resize(); }, 100);
});
}
// Initialize Leaflet map (fallback)
function initLeaflet(locations) {
if (typeof L === 'undefined') {
mapElement.textContent = 'Map failed to load.';
console.error('Leaflet not available for fallback.');
return;
}
var bounds = L.latLngBounds(locations.map(function(loc) { return [loc.lat, loc.lng]; }));
var map = L.map(mapElementId);
map.fitBounds(bounds, { padding: [50, 50], maxZoom: maxZoom });
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
locations.forEach(function(loc) {
L.marker([loc.lat, loc.lng])
.addTo(map)
.bindPopup(getPopupContent(loc));
});
setTimeout(function() { map.invalidateSize(); }, 100);
console.warn('Using Leaflet fallback for inline map.');
}
// Main initialization
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/locations.json')
.then(function(response) { return response.json(); })
.then(function(locationData) {
var locations = [];
requestedLocationIds.forEach(function(locId) {
if (locationData[locId]) {
var locData = locationData[locId];
locations.push({
lat: locData.lat,
lng: locData.lng,
name: locData.name,
id: locId
});
}
});
if (locations.length === 0) {
console.warn('No valid locations found for inline map.');
return;
}
// Try Mapbox first, fall back to Leaflet
loadMapboxCSS();
loadMapbox(function(err) {
if (err) {
console.warn('Mapbox failed to load, using Leaflet:', err);
initLeaflet(locations);
return;
}
try {
initMapbox(locations);
} catch (e) {
console.warn('Mapbox init failed, using Leaflet:', e);
initLeaflet(locations);
}
});
})
.catch(function(error) {
console.error('Failed to load location data:', error);
mapElement.textContent = 'Failed to load map data.';
});
});
})();
尼姆和杜布罗夫尼克
1945年的波兰边界和1939年的相比,相对于发生了整体的平移,波兰的领土在战前战后有何得失,领土面积是增多了,还是减少了?
这道题出的还行,答案是减少了。我之所以知道,是在曾经满大街报刊亭的时代,从一本买来的《世界军事》杂志上读到一篇《欧洲的重组》了解到的:1939年德国和苏联瓜分了波兰,苏联占据了波兰东部的大片领土。后来苏德又打起来,德国战败。战争结束后波兰复国,苏联没打算把他们1939年占领的波兰领土还回去,而是将德国东部的大片领土交给波兰作为补偿。所以,1939年的波兰城市利沃夫1945年成了苏联乌克兰加盟共和国城市(是的,就是俄乌战争期间大体安全,偶尔被轰炸的利沃夫),1939年的但泽自由市成了1945年的波兰格但斯克,1939年的东普鲁士哥尼斯堡变成了1945年的苏联俄罗斯加盟共和国(现俄罗斯联邦)城市加里宁格勒。
(function() {
var mapElementId = 'post-map-poland';
var mapElement = document.getElementById(mapElementId);
var maxZoom = 13;
// Location IDs needed for this map (generated at build time)
var requestedLocationIds = [
'lviv',
'kaliningrad',
'gdansk'
];
// Custom popups and titles (generated at build time)
var customPopups = null;
var locationTitles = {
'cabinda': "卡宾达,属于安哥拉,以前是葡属刚果。",
'cayenne': "卡宴,法属圭亚那首府",
'hans_island': "汉斯岛,加拿大的第二个陆上邻国在这里",
'monchique_islet': "Monchique Islet,属于葡萄牙亚速尔群岛,一般认为的欧盟最西端。",
'neum': "尼姆,波黑出海口",
'dubrovnik': "杜布罗夫尼克,克罗地亚飞地",
};
// Mapbox GL version and CDN sources
var mapboxVersion = '2.14.1';
var mapboxScriptSources = [
'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.js',
'https://cdn.jsdelivr.net/npm/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js',
'https://unpkg.com/mapbox-gl@' + mapboxVersion + '/dist/mapbox-gl.js'
];
// Load Mapbox GL CSS
function loadMapboxCSS() {
if (document.querySelector('link[href*="mapbox-gl.css"]')) return;
var css = document.createElement('link');
css.rel = 'stylesheet';
css.href = 'https://api.mapbox.com/mapbox-gl-js/v' + mapboxVersion + '/mapbox-gl.css';
document.head.appendChild(css);
}
// Load Mapbox GL JS with fallbacks
function loadMapbox(callback, index) {
index = index || 0;
if (window.mapboxgl) {
callback();
return;
}
if (index >= mapboxScriptSources.length) {
callback(new Error('All Mapbox sources failed'));
return;
}
var src = mapboxScriptSources[index];
var script = document.createElement('script');
script.src = src;
script.onload = function() { callback(); };
script.onerror = function() {
console.warn('Mapbox GL JS load failed from:', src);
loadMapbox(callback, index + 1);
};
document.head.appendChild(script);
}
// Get popup content for a location
function getPopupContent(loc) {
if (customPopups && customPopups[loc.id]) {
return customPopups[loc.id];
} else if (locationTitles[loc.id]) {
return '' + locationTitles[loc.id] + '';
} else {
return '' + loc.name + '';
}
}
// Initialize Mapbox map
function initMapbox(locations) {
var MAPBOX_TOKEN = 'pk.eyJ1IjoibGVvbnNvbiIsImEiOiJjbWpraWtqOTUyYnZnM2dvd2thajNtMGZsIn0.08xHonUXcanx3ELmKUv0dg';
if (!MAPBOX_TOKEN) {
throw new Error('Missing mapbox_token');
}
if (typeof mapboxgl === 'undefined') {
throw new Error('mapboxgl not defined');
}
if (!mapboxgl.supported()) {
throw new Error('Mapbox GL not supported');
}
mapboxgl.accessToken = MAPBOX_TOKEN;
// Calculate bounds
var bounds = new mapboxgl.LngLatBounds();
locations.forEach(function(loc) {
bounds.extend([loc.lng, loc.lat]);
});
var map = new mapboxgl.Map({
container: mapElementId,
style: 'mapbox://styles/mapbox/streets-v12',
bounds: bounds,
fitBoundsOptions: { padding: 50, maxZoom: maxZoom }
});
map.addControl(new mapboxgl.NavigationControl({ showCompass: false, showZoom: true }), 'top-right');
map.on('load', function() {
locations.forEach(function(loc) {
new mapboxgl.Marker()
.setLngLat([loc.lng, loc.lat])
.setPopup(new mapboxgl.Popup({ offset: 12 }).setHTML(getPopupContent(loc)))
.addTo(map);
});
setTimeout(function() { map.resize(); }, 100);
});
}
// Initialize Leaflet map (fallback)
function initLeaflet(locations) {
if (typeof L === 'undefined') {
mapElement.textContent = 'Map failed to load.';
console.error('Leaflet not available for fallback.');
return;
}
var bounds = L.latLngBounds(locations.map(function(loc) { return [loc.lat, loc.lng]; }));
var map = L.map(mapElementId);
map.fitBounds(bounds, { padding: [50, 50], maxZoom: maxZoom });
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
locations.forEach(function(loc) {
L.marker([loc.lat, loc.lng])
.addTo(map)
.bindPopup(getPopupContent(loc));
});
setTimeout(function() { map.invalidateSize(); }, 100);
console.warn('Using Leaflet fallback for inline map.');
}
// Main initialization
document.addEventListener('DOMContentLoaded', function() {
fetch('/api/locations.json')
.then(function(response) { return response.json(); })
.then(function(locationData) {
var locations = [];
requestedLocationIds.forEach(function(locId) {
if (locationData[locId]) {
var locData = locationData[locId];
locations.push({
lat: locData.lat,
lng: locData.lng,
name: locData.name,
id: locId
});
}
});
if (locations.length === 0) {
console.warn('No valid locations found for inline map.');
return;
}
// Try Mapbox first, fall back to Leaflet
loadMapboxCSS();
loadMapbox(function(err) {
if (err) {
console.warn('Mapbox failed to load, using Leaflet:', err);
initLeaflet(locations);
return;
}
try {
initMapbox(locations);
} catch (e) {
console.warn('Mapbox init failed, using Leaflet:', e);
initLeaflet(locations);
}
});
})
.catch(function(error) {
console.error('Failed to load location data:', error);
mapElement.textContent = 'Failed to load map data.';
});
});
})();
波兰的平移
感谢读者耐心地读了这么久,是时候送上个文末彩蛋了:如果读者以后遇到任何加拿大地理方面的问题,那么答案必然是下面三者之一:(A)因为离美国近。(B)因为冷。(C)因为加拿大地盾。如果不能决定,那么答案一定是加拿大地盾(安大略为什么这么平?加拿大地盾。魁北克为什么这么多湖?加拿大地盾。为什么加拿大中间没怎么住人?加拿大地盾)。这是我不时去看Reddit的geography subreddit学到的。不用谢。
写《地趣》之前一周我也写了一下杜布罗夫尼克,所以当时提这个出海口的问题也算是recency bias了。 ↩