HuggingMaps / index.html
tuhbooh's picture
Update index.html
1dfa066 verified
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minh Đức P2P - GIS Pro Edition</title>
<!-- Leaflet Core -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- PeerJS -->
<script src="https://unpkg.com/peerjs@1.5.2/dist/peerjs.min.js"></script>
<!-- 1. Locate -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.js"></script>
<!-- 2. Fullscreen -->
<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/Leaflet.fullscreen.min.js'></script>
<link href='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/leaflet.fullscreen.css' rel='stylesheet' />
<!-- 3. Geocoder -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<!-- 4. Measure -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-measure/dist/leaflet-measure.css" />
<script src="https://unpkg.com/leaflet-measure/dist/leaflet-measure.js"></script>
<!-- 5. MiniMap -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.min.js"></script>
<!-- 6. Sidebar-v2 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-sidebar-v2@3.2.3/css/leaflet-sidebar.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet-sidebar-v2@3.2.3/js/leaflet-sidebar.min.js"></script>
<!-- 7. Leaflet.Draw -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<!-- 8. MousePosition -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-mouse-position@1.2.0/src/L.Control.MousePosition.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet-mouse-position@1.2.0/src/L.Control.MousePosition.min.js"></script>
<!-- 9. EasyButton -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.js"></script>
<style>
body, html { margin: 0; padding: 0; height: 100%; font-family: 'Segoe UI', sans-serif; }
#map { height: 100vh; width: 100%; background: #f0f0f0; }
/* Marker radar mượt mà */
.p2p-radar {
background: #2ecc71;
border: 2px solid #ffffff;
border-radius: 50%;
box-shadow: 0 0 15px rgba(46, 204, 113, 0.6);
}
.p2p-radar::before {
content: ''; display: block; width: 300%; height: 300%; margin-left: -100%; margin-top: -100%;
border-radius: 50%; background-color: rgba(46, 204, 113, 0.4);
animation: radar-pulse 2s infinite ease-out;
}
@keyframes radar-pulse { 0% { transform: scale(0.1); opacity: 1; } 100% { transform: scale(1); opacity: 0; } }
</style>
</head>
<body>
<div id="sidebar" class="leaflet-sidebar collapsed">
<div class="leaflet-sidebar-tabs">
<ul role="tablist">
<li><a href="#home" role="tab">🏠</a></li>
<li><a href="#p2p" role="tab">🛰️</a></li>
<li><a href="#tools" role="tab">🛠️</a></li>
</ul>
</div>
<div class="leaflet-sidebar-content">
<div class="leaflet-sidebar-pane" id="home">
<h1 class="leaflet-sidebar-header">Minh Đức GIS System <span class="leaflet-sidebar-close"></span></h1>
<p style="padding: 20px 0;"><b>Trạng thái:</b> <span style="color: green;">Online</span></p>
<p>ID Peer của bạn: <br><strong id="peer-id-display">...</strong></p>
</div>
<div class="leaflet-sidebar-pane" id="p2p">
<h1 class="leaflet-sidebar-header">Kết nối P2P <span class="leaflet-sidebar-close"></span></h1>
<div id="user-list-ui" style="padding: 10px 0;"></div>
</div>
<div class="leaflet-sidebar-pane" id="tools">
<h1 class="leaflet-sidebar-header">Công cụ chuyên sâu <span class="leaflet-sidebar-close"></span></h1>
<p>Sử dụng thanh công cụ bên trái để vẽ vùng/đường đi và thanh bên phải để đo đạc.</p>
</div>
</div>
</div>
<div id="map"></div>
<script>
// --- KHỞI TẠO BẢN ĐỒ (STANDARD SETTINGS) ---
// Reverted to default settings to ensure stable loading
const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'OSM' });
const satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { attribution: 'Esri' });
const terrain = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { attribution: 'Topo' });
const map = L.map('map', {
center: [10.7626, 106.6602],
zoom: 13,
layers: [osm],
fullscreenControl: true
});
// --- CÁC CÔNG CỤ ĐI KÈM ---
const sidebar = L.control.sidebar({ container: 'sidebar' }).addTo(map);
const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawnItems },
draw: { polygon: true, polyline: true, rectangle: true, circle: true, marker: true }
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, (e) => {
drawnItems.addLayer(e.layer);
});
L.control.mousePosition({ position: 'bottomleft', separator: ' | ', lngFirst: true }).addTo(map);
const measureControl = new L.Control.Measure({
position: 'topright',
primaryLengthUnit: 'meters',
localization: 'vi'
}).addTo(map);
const miniLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
new L.Control.MiniMap(miniLayer, { toggleDisplay: true, position: 'bottomright' }).addTo(map);
L.control.locate({ flyTo: true, keepCurrentZoomLevel: true }).addTo(map).start();
L.Control.geocoder({ defaultMarkGeocode: true }).addTo(map);
L.control.layers({ "Đường phố": osm, "Vệ tinh": satellite, "Địa hình": terrain }, { "Vùng vẽ": drawnItems }, { collapsed: false }).addTo(map);
L.easyButton('<span>🏠</span>', function(btn, map){
map.setView([10.7626, 106.6602], 13);
}, 'Về trung tâm').addTo(map);
L.control.scale({ metric: true, imperial: false }).addTo(map);
// --- LOGIC KẾT NỐI P2P ---
const GROUP_ID = "M_DUC_PRO_GIS_";
const MAX_PEERS = 15;
const myNum = Math.floor(Math.random() * MAX_PEERS);
const peer = new Peer(GROUP_ID + myNum);
const remoteMarkers = {};
peer.on('open', (id) => {
document.getElementById('peer-id-display').innerText = id;
setInterval(() => {
for (let i = 0; i < MAX_PEERS; i++) {
if (i !== myNum) {
const targetId = GROUP_ID + i;
if (!peer.connections[targetId]) setupConn(peer.connect(targetId));
}
}
}, 5000);
});
peer.on('connection', setupConn);
function setupConn(conn) {
conn.on('data', (data) => { if (data.lat) updateRemote(conn.peer, data); });
conn.on('close', () => {
if (remoteMarkers[conn.peer]) {
map.removeLayer(remoteMarkers[conn.peer]);
delete remoteMarkers[conn.peer];
updateListUI();
}
});
}
function updateRemote(id, coords) {
if (remoteMarkers[id]) {
remoteMarkers[id].setLatLng([coords.lat, coords.lng]);
} else {
const icon = L.divIcon({ className: 'p2p-radar', iconSize: [12, 12] });
remoteMarkers[id] = L.marker([coords.lat, coords.lng], { icon: icon })
.addTo(map)
.bindPopup(`👤 <b>${id}</b>`);
updateListUI();
}
}
function updateListUI() {
const ui = document.getElementById('user-list-ui');
ui.innerHTML = "";
Object.keys(remoteMarkers).forEach(id => {
const div = document.createElement('div');
div.style.padding = "10px";
div.style.borderBottom = "1px solid #ddd";
div.style.cursor = "pointer";
div.innerHTML = `📡 <b>${id}</b>`;
div.onclick = () => { map.flyTo(remoteMarkers[id].getLatLng(), 17); sidebar.close(); };
ui.appendChild(div);
});
}
map.on('locationfound', (e) => {
const myData = { lat: e.latlng.lat, lng: e.latlng.lng };
Object.keys(peer.connections).forEach(targetId => {
peer.connections[targetId].forEach(c => { if (c.open) c.send(myData); });
});
});
</script>
</body>
</html>