Widget:Custom map
<script type="text/javascript">
let map;
var drawnItems;
var drawControl;
var editMode = false;
window.savePopupContent = savePopupContent;
// 表示する画像 var imageBase = {
url: , width: , // 画像のサイズ height:
};
// 地図初期化 var imageBounds = L.latLngBounds(
[0, 0], [imageBase.height, imageBase.width],
);
map = L.map('map', {
crs: L.CRS.Simple, maxBounds: imageBounds.pad(0.5), minZoom: , // minZoomを0に設定 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 } }, 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 =
'
'<textarea id="popup-title" cols="30" rows="1" placeholder="タイトル"></textarea>' + '<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>' +'
';
layer.bindPopup(popupContent); } else { layer.bindPopup('No description'); } createEditablePopup(layer); drawnItems.addLayer(layer); if (layer instanceof L.Marker) { layer.openPopup(); }
});
//////////////////権限を確認 function checkUserGroup(group, callback) {
$.ajax({ url: mw.util.wikiScript('api'), 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: , 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: , 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: , 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: , 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: , 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: , 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 =
'
'<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>' +'
';
popup.setContent(editableContent); } else { // 編集モードでない場合は表示用のコンテンツを設定 if (title === && content === ) { layer.unbindPopup(); // タイトルと内容が空の場合はポップアップを表示しない } else { var displayContent = '' + title + '
' + content; var renderedContent = renderMediaWikiContent(displayContent); popup.setContent(renderedContent); } } });
// 既にポップアップが設定されている場合に備え、再設定 if (!(title === && content === )) { var displayContent = '' + title + '
' + content; var renderedContent = renderMediaWikiContent(displayContent); layer.bindPopup(renderedContent); }
}
// MediaWikiコンテンツのレンダリング
function renderMediaWikiContent(content) {
var renderedContent = ; $.ajax({ url: mw.util.wikiScript('api'), data: { action: 'parse', text: content, format: 'json' }, async: false, success: function(data) { renderedContent = data.parse.text['*']; }, error: function() { console.error('Failed to render MediaWiki content'); } }); return renderedContent;
}
// ポップアップの内容を保存
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; }); if (layer) { var savedContent = '' + title + '
' + text; layer.setPopupContent(savedContent); layer.options.popupData = {title: title, content: text, iconType: iconType}; if (layer instanceof L.Marker && iconType) { var icon = iconDefinitions[iconType] || iconDefinitions.icon1; layer.setIcon(icon); } layer.closePopup(); layer.openPopup(); } else { console.error('Layer not found'); }
}
// 編集イベントのリスナーも追加
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) {
var layers = event.layers; layers.eachLayer(function (layer) { drawnItems.removeLayer(layer); });
});
// 編集ボタン 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('sysop', function(isInGroup) { if (isInGroup) { editMode = true; map.addControl(drawControl); btn.state('disable-edit'); updateAllPopups(); saveButton.addTo(map); // saveButtonを表示する } else { alert('あなたには編集権限がありません。\n攻略に参加することでマップが編集できるようになります。'); } }); } }, { 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 + "
" + layer.options.popupData.content); } } });
}
// 保存ボタン 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);
saveToWikiPage(geoJSONData); saveButton.remove(); // saveButtonを非表示にする } else { alert('Error: Invalid GeoJSON data'); } } else { alert('Please enable edit mode before saving.'); }
}, '変更を保存');
function loadPinsFromWikiPage() {
var host = window.location.origin; var geojsonPage = ""; var url = host + "/zelda_eow/index.php/" + geojsonPage;
$.ajax({ type: "GET", url: url, dataType: "html", success: function(htmlContent) { try {
// console.log("HTML Content: ", htmlContent); // 取得したHTMLをコンソールに出力
var parser = new DOMParser(); var doc = parser.parseFromString(htmlContent, "text/html"); var scriptTag = doc.querySelector('#mw-content-text script'); if (!scriptTag) { throw new Error("Script tag not found"); } var scriptContent = scriptTag.textContent.trim(); var jsonDataMatch = scriptContent.match(/var GeoJson =(\{[\s\S]*\});/); if (!jsonDataMatch || jsonDataMatch.length < 2) { throw new Error("GeoJSON data not found in script tag"); } var geoJSONData = jsonDataMatch[1]; if (!geoJSONData) { throw new Error("GeoJSON data is empty"); } var parsedData = JSON.parse(geoJSONData); processGeoJSONData(parsedData); } catch (error) { console.error("Error processing data:", error);
// console.log("Raw HTML content:", htmlContent); // console.log("Extracted script content:", scriptContent);
if (typeof geoJSONData !== 'undefined') {
// console.log("Extracted GeoJSON data:", geoJSONData);
} } }, error: function(xhr, status, error) {
// console.error("Error loading data:", error); // console.log("XHR status:", status); // console.log("XHR response:", xhr.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 = '' + feature.properties.title + '
' + 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 convertToGeoJSON(drawnItems) {
var geoJSON = { "type": "FeatureCollection", "features": [] };
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 { // その他のタイプのレイヤーは無視する 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 ; case 'icon2': return ; case 'icon3': return ; case 'icon4': return ; case 'icon5': return ; case 'icon6': return ; default: return ; }
}
// フィルタボタンの定義
// Leaflet.Control.TagFilterButton の設定 L.control.tagFilterButton({
data: [ , , , , , ], icon: '<img src="">', filterOnEveryClick: true
}).addTo(map);
function saveToWikiPage(geoJSONData) {
var formattedData = geoJSONData; $.ajax({ type: "POST", url: 'http://ec2-13-114-153-166.ap-northeast-1.compute.amazonaws.com/api.php', data: { action: 'edit', title: , text: formattedData, token: mw.user.tokens.get('csrfToken'), format: 'json' }, success: function(response) {
// console.log("Save response:", response);
if (response.edit && response.edit.result === 'Success') { alert('データは正常に保存されました'); editMode = false; drawControl.remove(); editButton.state('enable-edit'); saveButton.remove(); // saveButtonを非表示にする setTimeout(function() { loadPinsFromWikiPage(); }, 1000); } else { alert('Error saving data: ' + JSON.stringify(response)); } }, error: function(xhr, status, error) { console.error("Save error:", error);
// console.log("XHR status:", status); // console.log("XHR response:", xhr.responseText);
alert('Error saving data: ' + error); } });
} 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() {
setTimeout(function() { map.invalidateSize(); loadPinsFromWikiPage(); }, 500); // 少し長めの遅延を設定
}); </script>
</includeonly>