「Widget:Custom map」の版間の差分
提供:ロマサガ2リメイク 攻略Wiki(ロマンシング サガ2 リベンジオブザセブン)
編集の要約なし |
編集の要約なし |
||
1行目: | 1行目: | ||
<includeonly> | <includeonly> | ||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | ||
<script src="https://dq.h1g.jp/leaflet/leaflet.js" ></script> | |||
<script src="https://dq.h1g.jp/img/dq10_offline/map/js/leaflet-easy-button.js"></script> | |||
<script src="https://dq.h1g.jp/img/dq10_offline/map/js/leaflet-tag-filter-button.js"></script> | |||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script> | |||
<script src="https://dq.h1g.jp/leaflet/custom-leaflet-draw-locale.js" ></script> | |||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/2.7.2/lightgallery.min.js"></script> | |||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/> | |||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> | |||
<link rel="stylesheet" href="https://dq.h1g.jp/img/dq10_offline/map/css/leaflet-easy-button.css" /> | |||
<link rel="stylesheet" href="https://dq.h1g.jp/img/dq10_offline/map/css/leaflet-tag-filter-button.css" /> | |||
<link rel="stylesheet" href="https://dq.h1g.jp/img/marker-icon-img/editable-popup.css" /> | |||
<link rel="stylesheet" href="https://dq.h1g.jp/img/lightgallery.min.css"/> | |||
<script type="text/javascript"> | |||
let map; | |||
var drawnItems; | |||
var drawControl; | |||
var editMode = false; | |||
// APIベースURLを定数として定義 | |||
const API_BASE_URL = Object.freeze('https://prd-h1g-elb-2067013247.ap-northeast-1.elb.amazonaws.com/rs2r'); | |||
// 定数の保護 | |||
Object.defineProperty(window, 'API_BASE_URL', { | |||
value: API_BASE_URL, | |||
writable: false, | |||
configurable: false | |||
}); | |||
window.savePopupContent = savePopupContent; | |||
< | // 表示する画像 | ||
var imageBase = { | |||
url: '<!--{$img}-->', | |||
width: <!--{$imgwidth}-->, // 画像のサイズ | |||
height: <!--{$imgheight}--> | |||
}; | |||
// 地図初期化 | |||
var imageBounds = L.latLngBounds( | |||
[0, 0], | |||
[imageBase.height, imageBase.width], | |||
); | |||
// マップ初期化前のクリーンアップ | |||
cleanupMap(); | |||
cleanupLocalStorage(); | |||
cleanupSessionCookies(); | |||
map = L.map('map', { | |||
crs: L.CRS.Simple, | |||
maxBounds: imageBounds.pad(0.5), | |||
minZoom: <!--{$minzoom}-->, // minZoomを0に設定 | |||
maxZoom: <!--{$maxzoom}-->, | |||
}); | |||
map.fitBounds(imageBounds); | |||
L.imageOverlay(imageBase.url, imageBounds,{ | |||
attribution: '<a href="https://h1g.jp/" target="_blank">【ヘイグ】</a>' | |||
}).addTo(map); | |||
var drawnItems = new L.FeatureGroup(); | |||
map.addLayer(drawnItems); | |||
// Leaflet.Draw | |||
var drawnItems = new L.FeatureGroup(); | |||
map.addLayer(drawnItems); | |||
drawControl = new L.Control.Draw({ | |||
edit: { | |||
featureGroup: drawnItems, | |||
poly: { | |||
allowIntersection: false | |||
} | |||
map = L.map('map', { | |||
}); | |||
map.fitBounds(imageBounds); | |||
L.imageOverlay(imageBase.url, imageBounds,{ | |||
}).addTo(map); | |||
var drawnItems = new L.FeatureGroup(); | |||
map.addLayer(drawnItems); | |||
// Leaflet.Draw | |||
var drawnItems = new L.FeatureGroup(); | |||
map.addLayer(drawnItems); | |||
drawControl = new L.Control.Draw({ | |||
}, | }, | ||
draw: { | |||
polygon: false, | |||
polyline: false, | |||
rectangle: false, | |||
circle: true, | |||
marker: true, | |||
circlemarker: false | |||
} | } | ||
}); | }); | ||
map.addControl(drawControl); | |||
// 初期状態では編集モードを無効にする | |||
drawControl.remove(); | |||
map.on(L.Draw.Event.CREATED, function (event) { | |||
var layer = event.layer; | |||
if (layer instanceof L.Marker) { | |||
layer.setIcon(iconDefinitions.icon1); | |||
layer.options.popupData = {title: '', content: '', iconType: 'icon1'}; | |||
var popupContent = | |||
function | |||
if ( | |||
layer. | |||
layer. | |||
var | |||
'<div>' + | '<div>' + | ||
'<textarea id="popup-title" cols=" | '<textarea id="popup-title" cols="30" rows="1" placeholder="タイトル"></textarea>' + | ||
'<textarea id="popup-content" cols=" | '<textarea id="popup-content" cols="30" rows="2" placeholder="内容"></textarea>' + | ||
'<select id="icon-type">' + | |||
'<option value="icon1" selected>アイコン1</option>' + | |||
'<option value="icon2">アイコン2</option>' + | |||
'<option value="icon3">アイコン3</option>' + | |||
'<option value="icon4">アイコン4</option>' + | |||
'<option value="icon5">アイコン5</option>' + | |||
'<option value="icon6">アイコン6</option>' + | |||
'</select>' + | |||
'<button onclick="savePopupContent(this)">保存</button>' + | '<button onclick="savePopupContent(this)">保存</button>' + | ||
'</div>'; | '</div>'; | ||
layer.bindPopup(popupContent); | |||
} else { | } else { | ||
layer.bindPopup('No description'); | |||
} | |||
createEditablePopup(layer); | |||
drawnItems.addLayer(layer); | |||
if (layer instanceof L.Marker) { | |||
layer.openPopup(); | |||
} | } | ||
}); | }); | ||
// | // クリーンアップ関数群を追加 | ||
if ( | function cleanupMap() { | ||
if (map) { | |||
map.eachLayer((layer) => { | |||
map.removeLayer(layer); | |||
}); | |||
for (let i in map._controlCorners) { | |||
map._controlCorners[i].innerHTML = ''; | |||
} | |||
if (drawnItems) { | |||
drawnItems.clearLayers(); | |||
} | |||
map.off(); | |||
$('.leaflet-popup-content').each(function() { | |||
if ($(this).data('lightGallery')) { | |||
$(this).data('lightGallery').destroy(true); | |||
} | |||
}); | |||
$('.lg-container').remove(); | |||
} | } | ||
} | } | ||
function cleanupLocalStorage() { | |||
Object.keys(localStorage).forEach(key => { | |||
if (key.includes('MediaWikiModuleStore')) { | |||
localStorage.removeItem(key); | |||
} | |||
}); | |||
} | |||
function cleanupSessionCookies() { | |||
document.cookie.split(';').forEach(cookie => { | |||
const name = cookie.split('=')[0].trim(); | |||
function | if (name.includes('mwuser-sessionId')) { | ||
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; | |||
} | |||
} | } | ||
}); | }); | ||
} | } | ||
// | //////////////////権限を確認 | ||
function checkUserGroup(group, callback) { | |||
$.ajax({ | |||
url: API_BASE_URL + '/api.php', | |||
data: { | |||
action: 'query', | |||
meta: 'userinfo', | |||
uiprop: 'groups', | |||
format: 'json' | |||
}, | |||
type: 'GET', | |||
success: function(data) { | |||
var userGroups = data.query.userinfo.groups; | |||
var isInGroup = userGroups.indexOf(group) !== -1; | |||
callback(isInGroup); | |||
}, | |||
error: function() { | |||
console.error('Failed to check user group'); | |||
callback(false); | |||
} | |||
}); | |||
} | } | ||
////////////////////////////////////////////////////////////// | |||
// | // アイコンの定義 | ||
var iconDefinitions = { | |||
icon1: L.icon({ | |||
iconUrl: '<!--{$icon1|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-blue.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}), | |||
icon2: L.icon({ | |||
iconUrl: '<!--{$icon2|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-red.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}), | |||
icon3: L.icon({ | |||
iconUrl: '<!--{$icon3|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-violet.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}), | |||
icon4: L.icon({ | |||
iconUrl: '<!--{$icon4|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-green.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}), | |||
icon5: L.icon({ | |||
iconUrl: '<!--{$icon5|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-gold.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}), | |||
icon6: L.icon({ | |||
iconUrl: '<!--{$icon6|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-black.png'}-->', | |||
shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', | |||
iconSize: [25, 41], | |||
iconAnchor: [12, 41], | |||
popupAnchor: [1, -41], | |||
shadowSize: [41, 41] | |||
}) | |||
}; | |||
// ポップアップの作成 | |||
function createEditablePopup(layer) { | |||
var popupData = layer.options.popupData || {}; | |||
var title = popupData.title || ''; | |||
var content = popupData.content || ''; | |||
var iconType = popupData.iconType || 'icon1'; | |||
layer.on('popupopen', function() { | |||
var popup = this.getPopup(); | |||
var popupData = this.options.popupData || {}; | |||
var title = popupData.title || ''; | |||
var content = popupData.content || ''; | |||
var iconType = popupData.iconType || 'icon1'; | |||
// タイトルと内容が両方空の場合、ポップアップを表示しない | |||
if (!editMode && title === '' && content === '') { | |||
layer.closePopup(); | |||
// カーソルをデフォルトに設定 | |||
layer._icon.style.cursor = 'default'; | |||
return; | |||
} | |||
if (editMode) { // 編集モードの場合のみtextareaを表示 | |||
var iconSelector = ''; | |||
if (layer instanceof L.Marker) { | |||
iconSelector = '<select id="icon-type">' + | |||
'<option value="icon1"' + (iconType === 'icon1' ? ' selected' : '') + '>アイコン1</option>' + | |||
'<option value="icon2"' + (iconType === 'icon2' ? ' selected' : '') + '>アイコン2</option>' + | |||
'<option value="icon3"' + (iconType === 'icon3' ? ' selected' : '') + '>アイコン3</option>' + | |||
'<option value="icon4"' + (iconType === 'icon4' ? ' selected' : '') + '>アイコン4</option>' + | |||
'<option value="icon5"' + (iconType === 'icon5' ? ' selected' : '') + '>アイコン5</option>' + | |||
'<option value="icon6"' + (iconType === 'icon6' ? ' selected' : '') + '>アイコン6</option>' + | |||
'</select>'; | |||
} | |||
var editableContent = | |||
'<div>' + | |||
'<textarea id="popup-title" cols="60" rows="3" placeholder="タイトル">' + title + '</textarea>' + | |||
'<textarea id="popup-content" cols="60" rows="10" placeholder="内容">' + content + '</textarea>' + | |||
iconSelector + | |||
'<button onclick="savePopupContent(this)">保存</button>' + | |||
'</div>'; | |||
popup.setContent(editableContent); | |||
} else { | |||
// 編集モードでない場合は表示用のコンテンツを設定 | |||
if (title === '' && content === '') { | |||
layer.unbindPopup(); // タイトルと内容が空の場合はポップアップを表示しない | |||
} else { | |||
var displayContent = '<strong>' + title + '</strong><br>' + content; | |||
var renderedContent = renderMediaWikiContent(displayContent); | |||
popup.setContent(renderedContent); | |||
} | |||
} | |||
}); | |||
// 既にポップアップが設定されている場合に備え、再設定 | |||
if (!(title === '' && content === '')) { | |||
// | var displayContent = '<strong>' + title + '</strong><br>' + content; | ||
if (! | var renderedContent = renderMediaWikiContent(displayContent); | ||
layer.bindPopup(renderedContent); | |||
} | } | ||
} | } | ||
// | |||
// MediaWikiコンテンツのレンダリング | |||
function renderMediaWikiContent(content) { | |||
var renderedContent = ''; | |||
$.ajax({ | |||
url: API_BASE_URL + '/api.php', | |||
data: { | |||
action: 'parse', | |||
text: content, | |||
format: 'json' | |||
}, | |||
async: false, | |||
success: function(data) { | |||
renderedContent = data.parse.text['*']; | |||
// レンダリング後の画像処理 | |||
setTimeout(function() { | |||
initLightGallery(renderedContent); | |||
}, 100); | |||
}, | |||
error: function() { | |||
console.error('Failed to render MediaWiki content'); | |||
} | |||
}); | }); | ||
return renderedContent; | |||
} | } | ||
// LightGalleryの初期化を修正 | |||
// | function initLightGallery(content) { | ||
const popupContent = $('.leaflet-popup-content'); | |||
if ( | // 既存のLightGalleryインスタンスを破棄 | ||
popupContent.data('lightGallery'). | if (popupContent.data('lightGallery')) { | ||
popupContent.data('lightGallery').destroy(true); | |||
} | |||
// 既存のlg-containerを削除 | |||
$('.lg-container').remove(); | |||
popupContent.find('img').each(function() { | |||
const img = $(this); | |||
// gallery-itemクラスを持つdivで既に囲まれていない場合のみ処理 | |||
if (!img.parent().hasClass('gallery-item')) { | |||
let fullSizeUrl = convertToFullSize(img.attr('src')); | |||
img.wrap('<div class="gallery-item" data-src="' + fullSizeUrl + '"></div>'); | |||
} | |||
}); | |||
// LightGalleryを初期化(ナビゲーション矢印を非表示に) | |||
if (!popupContent.data('lightGallery')) { | |||
lightGallery(popupContent[0], { | |||
selector: '.gallery-item', | |||
plugins: [], | |||
speed: 500, | |||
download: false, | |||
counter: false, | |||
enableDrag: false, | |||
enableTouch: false, | |||
hideControlOnEnd: true, | |||
controls: false, | |||
prevHtml: '', | |||
nextHtml: '', | |||
backdropDuration: 300, // 背景のフェード時間 | |||
}); | |||
} | |||
} | } | ||
}); | |||
// MediaWikiの画像URLを最大サイズに変換する関数 | // ポップアップが閉じられたときの処理を追加 | ||
function convertToFullSize(url) { | map.on('popupclose', function(e) { | ||
const popupContent = $(e.popup.getContent()); | |||
if (popupContent.data('lightGallery')) { | |||
popupContent.data('lightGallery').destroy(true); | |||
} | |||
}); | |||
// MediaWikiの画像URLを最大サイズに変換する関数 | |||
function convertToFullSize(url) { | |||
// MediaWikiのサムネイルURLをパースして最大サイズのURLに変換 | |||
if (url.includes('/thumb/')) { | |||
// /thumb/を除去し、最後の/以降を削除して元のファイル名に戻す | |||
return url.replace('/thumb/', '/') | |||
.replace(/\/\d+px-[^/]+$/, ''); | |||
} | |||
return url; | |||
} | } | ||
// ポップアップが開かれたときのイベントを追加 | |||
map.on('popupopen', function(e) { | |||
// ポップアップが開かれたときのイベントを追加 | setTimeout(function() { | ||
map.on('popupopen', function(e) { | initLightGallery(e.popup.getContent()); | ||
}, 100); | |||
}); | }); | ||
// マップのクリーンアップ処理も追加 | |||
map.on('unload', function() { | |||
$('.lg-container').remove(); | |||
$('.leaflet-popup-content').each(function() { | |||
if ($(this).data('lightGallery')) { | |||
$(this).data('lightGallery').destroy(true); | |||
} | |||
}); | |||
}); | }); | ||
/// スタイルを更新 | |||
var | const styles = ` | ||
<style> | |||
.gallery-item { | |||
cursor: pointer; | |||
display: inline-block; | |||
} | |||
.gallery-item img { | |||
max-width: 200px; | |||
height: auto; | |||
transition: transform 0.3s ease; | |||
} | |||
.gallery-item:hover img { | |||
transform: scale(1.05); | |||
} | |||
.lg-img-wrap { | |||
text-align: center; | |||
} | |||
.lg-img-wrap img { | |||
max-height: 90vh !important; | |||
max-width: 90vw !important; | |||
object-fit: contain; | |||
} | |||
/* ナビゲーション矢印を非表示 */ | |||
.lg-next, .lg-prev { | |||
display: none !important; | |||
} | |||
</style> | |||
`; | |||
// ポップアップの内容を保存 | |||
function savePopupContent(button) { | |||
var popup = button.closest('.leaflet-popup'); | |||
var content = popup.querySelector('.leaflet-popup-content'); | |||
var title = content.querySelector('#popup-title').value; | |||
var text = content.querySelector('#popup-content').value; | |||
var iconType = content.querySelector('#icon-type') ? content.querySelector('#icon-type').value : null; | |||
var layer = drawnItems.getLayers().find(function(layer) { | |||
return layer.getPopup() && layer.getPopup().getElement() === popup; | |||
}); | |||
} | |||
layer.closePopup(); | if (layer) { | ||
var savedContent = '<strong>' + title + '</strong><br>' + text; | |||
layer.setPopupContent(savedContent); | |||
layer.options.popupData = {title: title, content: text, iconType: iconType}; | |||
// ポップアップを更新した後にLightGalleryを再初期化 | |||
setTimeout(function() { | |||
initLightGallery(savedContent); | |||
}, 100); | |||
layer.closePopup(); | |||
layer.openPopup(); | |||
} | |||
} | } | ||
// 編集イベントのリスナーも追加 | |||
// 編集イベントのリスナーも追加 | map.on(L.Draw.Event.EDITED, function (event) { | ||
map.on(L.Draw.Event.EDITED, function (event) { | var layers = event.layers; | ||
layers.eachLayer(function (layer) { | |||
drawnItems.addLayer(layer); | |||
}); | |||
}); | }); | ||
// 削除イベントのリスナーも追加 | |||
// 削除イベントのリスナーも追加 | map.on(L.Draw.Event.DELETED, function (event) { | ||
map.on(L.Draw.Event.DELETED, function (event) { | var layers = event.layers; | ||
layers.eachLayer(function (layer) { | |||
drawnItems.removeLayer(layer); | |||
}); | |||
}); | }); | ||
// 編集ボタン | |||
// 編集ボタン | var editButton = L.easyButton({ | ||
var editButton = L.easyButton({ | states: [{ | ||
stateName: 'enable-edit', | |||
icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/edit-solid.svg">', | |||
title: 'マップを編集する', | |||
onClick: function(btn, map) { | |||
// checkUserGroup('map-edit-member', function(isInGroup) { | |||
// checkUserGroup('map-edit-member', function(isInGroup) { | checkUserGroup('sysop', function(isInGroup) { | ||
// checkUserGroup('internal-staff', function(isInGroup) { | |||
// checkUserGroup('internal-staff', function(isInGroup) { | if (isInGroup) { | ||
editMode = true; | |||
map.addControl(drawControl); | |||
btn.state('disable-edit'); | |||
updateAllPopups(); | |||
saveButton.addTo(map); // saveButtonを表示する | |||
} else { | |||
// alert('あなたには編集権限がありません。\n攻略に参加することでマップが編集できるようになります。'); | |||
// alert('あなたには編集権限がありません。\n攻略に参加することでマップが編集できるようになります。'); | alert('あなたには編集権限がありません。'); | ||
} | |||
}); | |||
} | |||
}, { | |||
stateName: 'disable-edit', | |||
icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/edit-solid.svg">', | |||
title: '編集の終了', | |||
onClick: function(btn, map) { | |||
editMode = false; | |||
drawControl.remove(); | |||
btn.state('enable-edit'); | |||
updateAllPopups(); | |||
saveButton.remove(); // saveButtonを非表示にする | |||
} | |||
}] | |||
}).addTo(map); | |||
function updateAllPopups() { | |||
drawnItems.eachLayer(function(layer) { | |||
if (layer.getPopup()) { | |||
layer.closePopup(); | |||
if (editMode) { | |||
createEditablePopup(layer); | |||
layer.openPopup(); | |||
} else if (!(layer.options.popupData && layer.options.popupData.title === '' && layer.options.popupData.content === '')) { | |||
layer.bindPopup(layer.options.popupData.title + "<br>" + layer.options.popupData.content); | |||
} | } | ||
} | } | ||
}); | |||
} | |||
} | |||
// 保存ボタン | |||
// 保存ボタン | var saveButton = L.easyButton('<img src="https://dq.h1g.jp/img/marker-icon-img/save-solid.svg">', function() { | ||
var saveButton = L.easyButton('<img src="https://dq.h1g.jp/img/marker-icon-img/save-solid.svg">', function() { | if (editMode) { | ||
var geoJSONData = convertToGeoJSON(drawnItems); | |||
if (geoJSONData) { | |||
// console.log(geoJSONData); | |||
// console.log(geoJSONData); | saveToWikiPage(geoJSONData); | ||
saveButton.remove(); // saveButtonを非表示にする | |||
} else { | |||
alert('Error: Invalid GeoJSON data'); | |||
} | |||
} else { | } else { | ||
alert(' | alert('Please enable edit mode before saving.'); | ||
} | } | ||
}, '変更を保存'); | |||
}, '変更を保存'); | |||
function loadPinsFromWikiPage() { | |||
getMwToken() | |||
.then(function(token) { | |||
function loadPinsFromWikiPage() { | var geojsonPage = "<!--{$geojson}-->"; | ||
var url = API_BASE_URL + '/api.php'; | |||
console.log('Request URL:', url); | |||
return $.ajax({ | |||
type: "GET", | |||
url: url, | |||
data: { | |||
action: 'query', | |||
titles: geojsonPage, | |||
prop: 'revisions', | |||
rvprop: 'content', | |||
format: 'json', | |||
// 明示的にwikiを指定 | |||
redirects: 0 // リダイレクトを防ぐ | |||
}, | |||
headers: { | |||
'Authorization': 'Bearer ' + token | |||
} | |||
}); | |||
}) | |||
.then(function(response) { | |||
try { | |||
// レスポンスからページコンテンツを取得 | |||
const pages = response.query.pages; | |||
const pageId = Object.keys(pages)[0]; | |||
const content = pages[pageId].revisions[0]['*']; | |||
// GeoJSONデータを抽出して処理 | |||
const geoJSONData = JSON.parse(content); | |||
processGeoJSONData(geoJSONData); | |||
} catch (error) { | |||
console.error("Error processing data:", error); | |||
throw error; | |||
} | } | ||
}) | |||
.catch(function(error) { | |||
console.error("Error loading data:", error); | |||
// より詳細なエラー情報を表示 | |||
console.error('Detailed error:', { | |||
message: error.message, | |||
status: error.status, | |||
responseText: error.responseText | |||
}); | |||
}); | }); | ||
} | |||
. | |||
function processGeoJSONData(geoJSON) { | |||
drawnItems.clearLayers(); | |||
L.geoJSON(geoJSON, { | |||
pointToLayer: function (feature, latlng) { | |||
var iconType = feature.properties.iconType || 'icon1'; | |||
var icon = iconDefinitions[iconType] || iconDefinitions.icon1; | |||
if (feature.properties.radius) { | |||
return L.circle(latlng, { | |||
radius: feature.properties.radius, | |||
} | tags: feature.properties.tags | ||
}); | |||
} else { | |||
return L.marker(latlng, { | |||
icon: icon, | |||
tags: feature.properties.tags | |||
}); | |||
} | |||
}, | |||
onEachFeature: function (feature, layer) { | |||
var popupContent = '<strong>' + feature.properties.title + '</strong><br>' + feature.properties.content; | |||
layer.bindPopup(popupContent); | |||
layer.options.popupData = { | |||
title: feature.properties.title, | |||
content: feature.properties.content, | |||
iconType: feature.properties.iconType, | |||
tags: feature.properties.tags | |||
}; | |||
createEditablePopup(layer); | |||
drawnItems.addLayer(layer); | |||
} | } | ||
}); | }); | ||
} | } | ||
function | |||
drawnItems. | |||
////////////////////////////////////////////////////////////////////////////// | |||
function convertToGeoJSON(drawnItems) { | |||
var | var geoJSON = { | ||
"type": "FeatureCollection", | |||
"features": [] | |||
if (feature.properties.radius) { | }; | ||
drawnItems.eachLayer(function(layer) { | |||
var feature = layer.toGeoJSON(); | |||
} | var popupData = layer.options.popupData || {}; | ||
feature.properties = { | |||
title: popupData.title || '', | |||
content: popupData.content || '', | |||
iconType: popupData.iconType || 'icon1', | |||
tags: popupData.tags || [getTagFromIconType(popupData.iconType || 'icon1')] | |||
}; | |||
if (layer instanceof L.Circle) { | |||
feature.properties.radius = layer.getRadius(); | |||
feature.geometry = { | |||
type: "Point", | |||
coordinates: [layer.getLatLng().lng, layer.getLatLng().lat] | |||
}; | |||
} else if (layer instanceof L.Marker) { | |||
feature.geometry = { | |||
type: "Point", | |||
coordinates: [layer.getLatLng().lng, layer.getLatLng().lat] | |||
}; | |||
} else { | } else { | ||
// その他のタイプのレイヤーは無視する | |||
return; | |||
} | } | ||
geoJSON.features.push(feature); | |||
}); | |||
// GeoJSONデータの有効性をチェック | |||
try { | |||
JSON.parse(JSON.stringify(geoJSON)); | |||
} catch (error) { | |||
console.error("Invalid GeoJSON data:", error); | |||
return null; | |||
} | } | ||
return JSON.stringify(geoJSON, null, 2); // 整形されたJSONを返す | |||
} | |||
function getTagFromIconType(iconType) { | |||
switch (iconType) { | |||
case 'icon1': | |||
return '<!--{$filter1|default:"分類1"}-->'; | |||
case 'icon2': | |||
return '<!--{$filter2|default:"分類2"}-->'; | |||
case 'icon3': | |||
return '<!--{$filter3|default:"分類3"}-->'; | |||
case 'icon4': | |||
return '<!--{$filter4|default:"分類4"}-->'; | |||
case 'icon5': | |||
return '<!--{$filter5|default:"分類5"}-->'; | |||
case 'icon6': | |||
return '<!--{$filter6|default:"分類6"}-->'; | |||
default: | |||
return ''; | |||
} | } | ||
} | } | ||
// | // フィルタボタンの定義 | ||
getMwToken() | |||
// Leaflet.Control.TagFilterButton の設定 | |||
L.control.tagFilterButton({ | |||
data: [ | |||
'<!--{$filter1|default:"分類1"}-->', | |||
'<!--{$filter2|default:"分類2"}-->', | |||
'<!--{$filter3|default:"分類3"}-->', | |||
'<!--{$filter4|default:"分類4"}-->', | |||
'<!--{$filter5|default:"分類5"}-->', | |||
'<!--{$filter6|default:"分類6"}-->' | |||
], | |||
icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/filter.png">', | |||
filterOnEveryClick: true | |||
}).addTo(map); | |||
// トークンを取得する関数 | |||
function getMwToken() { | |||
return new Promise((resolve, reject) => { | |||
$.ajax({ | |||
url: API_BASE_URL + '/api.php', | url: API_BASE_URL + '/api.php', | ||
data: { | data: { | ||
action: ' | action: 'query', | ||
meta: 'tokens', | |||
type: 'csrf', | |||
format: 'json' | format: 'json' | ||
}, | |||
type: 'GET', | |||
success: function(response) { | |||
if (response.query && response.query.tokens && response.query.tokens.csrftoken) { | |||
resolve(response.query.tokens.csrftoken); | |||
} else { | |||
reject(new Error('Failed to get token')); | |||
} | |||
}, | |||
error: function(xhr, status, error) { | |||
reject(error); | |||
} | } | ||
}); | }); | ||
}); | }); | ||
} | } | ||
createEditablePopup(layer); | |||
function saveToWikiPage(geoJSONData) { | |||
}); | var formattedData = geoJSONData; | ||
// 保存前に新しいトークンを取得 | |||
getMwToken() | |||
.then(function(token) { | |||
return $.ajax({ | |||
type: "POST", | |||
url: API_BASE_URL + '/api.php', | |||
data: { | |||
action: 'edit', | |||
title: '<!--{$geojson}-->', | |||
text: formattedData, | |||
token: token, | |||
format: 'json' | |||
} | |||
}); | |||
}) | |||
.then(function(response) { | |||
if (response.edit && response.edit.result === 'Success') { | |||
alert('データは正常に保存されました'); | |||
editMode = false; | |||
drawControl.remove(); | |||
editButton.state('enable-edit'); | |||
saveButton.remove(); | |||
// 保存成功後、新しいトークンで再読み込み | |||
getMwToken() | |||
.then(function(newToken) { | |||
loadPinsFromWikiPage(newToken); | |||
}) | |||
.catch(function(error) { | |||
console.error("Failed to get token for reload:", error); | |||
}); | |||
} else { | |||
throw new Error('保存に失敗しました'); | |||
} | |||
}) | |||
.catch(function(error) { | |||
console.error("Save error:", error); | |||
alert('データの保存に失敗しました: ' + error.message); | |||
}); | |||
} | |||
map.on(L.Draw.Event.CREATED, function (event) { | |||
var layer = event.layer; | |||
if (layer instanceof L.Marker) { | |||
layer.setIcon(iconDefinitions.icon1); | |||
layer.options.popupData = {title: '', content: '', iconType: 'icon1'}; | |||
} | |||
createEditablePopup(layer); | |||
drawnItems.addLayer(layer); | |||
}); | |||
//////////////////////////////////////////////////// | |||
$(document).ready(function() { | |||
// ページ離脱時のクリーンアップ処理を追加 | |||
window.addEventListener('beforeunload', () => { | |||
cleanupMap(); | |||
cleanupLocalStorage(); | |||
cleanupSessionCookies(); | |||
}); | |||
setTimeout(function() { | setTimeout(function() { | ||
map.invalidateSize(); | map.invalidateSize(); | ||
getMwToken() | getMwToken() | ||
.then(function(token) { | .then(function(token) { | ||
784行目: | 846行目: | ||
}, 500); | }, 500); | ||
}); | }); | ||
</script> | </script> | ||
</includeonly> | </includeonly> |