tuhbooh commited on
Commit
1dfa066
·
verified ·
1 Parent(s): 2dab26e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +173 -115
index.html CHANGED
@@ -3,157 +3,215 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>P2P Locator - Minh Đức Edition</title>
 
 
7
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  <style>
9
- body, html { margin: 0; padding: 0; height: 100%; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
10
- #map { height: 100vh; width: 100%; }
11
 
12
- /* Hiệu ứng nhấp nháy cho dấu chấm */
13
- .pulse {
14
- width: 15px; height: 15px; background: #ff4444;
15
- border-radius: 50%; box-shadow: 0 0 0 rgba(255, 68, 68, 0.4);
16
- animation: pulse 2s infinite;
17
- }
18
- @keyframes pulse {
19
- 0% { box-shadow: 0 0 0 0 rgba(255, 68, 68, 0.7); }
20
- 70% { box-shadow: 0 0 0 15px rgba(255, 68, 68, 0); }
21
- 100% { box-shadow: 0 0 0 0 rgba(255, 68, 68, 0); }
22
- }
23
-
24
- /* Danh sách người dùng */
25
- #user-list {
26
- position: absolute; top: 70px; right: 10px; z-index: 1000;
27
- background: white; padding: 10px; border-radius: 8px;
28
- max-height: 200px; overflow-y: auto; box-shadow: 0 2px 10px rgba(0,0,0,0.2);
29
- width: 180px;
30
  }
31
- .user-item {
32
- padding: 5px; cursor: pointer; border-bottom: 1px solid #eee; font-size: 13px;
33
- }
34
- .user-item:hover { background: #f0f0f0; }
35
-
36
- .map-title {
37
- position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
38
- z-index: 1000; background: #333; color: white; padding: 8px 20px;
39
- border-radius: 30px; font-size: 14px;
40
  }
 
41
  </style>
42
  </head>
43
  <body>
44
 
45
- <div class="map-title">📍 Hệ th���ng định vị P2P - Minh Đức</div>
46
- <div id="user-list"><b>Đang online:</b><div id="list-content"></div></div>
47
- <div id="map"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
50
- <script src="https://unpkg.com/peerjs@1.5.2/dist/peerjs.min.js"></script>
51
 
52
  <script>
53
- const ROOM_PREFIX = "m-duc-group-";
54
- const MAX_PEERS = 10;
55
- const remoteMarkers = {};
56
- let myMarker;
 
 
 
 
 
 
 
 
57
 
58
- // 1. Khởi tạo Map
59
- const map = L.map('map').setView([10.7626, 106.6602], 15);
60
- L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
 
 
 
 
 
 
 
61
 
62
- // Icon chấm tròn nhấp nháy
63
- const pulseIcon = L.divIcon({
64
- className: 'pulse-container',
65
- html: '<div class="pulse"></div>',
66
- iconSize: [20, 20]
67
  });
68
 
69
- // 2. PeerJS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  const myNum = Math.floor(Math.random() * MAX_PEERS);
71
- const myId = ROOM_PREFIX + myNum;
72
- const peer = new Peer(myId);
73
 
74
  peer.on('open', (id) => {
75
- for (let i = 0; i < MAX_PEERS; i++) {
76
- if (i !== myNum) connectToPeer(ROOM_PREFIX + i);
77
- }
 
 
 
 
 
 
78
  });
79
 
80
- peer.on('connection', (conn) => { handleData(conn); });
81
-
82
- function connectToPeer(targetId) {
83
- const conn = peer.connect(targetId);
84
- handleData(conn);
85
- }
86
 
87
- function handleData(conn) {
88
- conn.on('data', (data) => {
89
- updateRemoteUser(conn.peer, data);
90
- });
91
- // Khi bạn bè offline
92
  conn.on('close', () => {
93
- if(remoteMarkers[conn.peer]) {
94
- map.removeLayer(remoteMarkers[conn.peer].marker);
95
  delete remoteMarkers[conn.peer];
96
- renderUserList();
97
  }
98
  });
99
  }
100
 
101
- // 3. Cập nhật vị trí bạn bè
102
- async function updateRemoteUser(peerId, data) {
103
- if (remoteMarkers[peerId]) {
104
- remoteMarkers[peerId].marker.setLatLng([data.lat, data.lng]);
105
  } else {
106
- const marker = L.marker([data.lat, data.lng], {icon: pulseIcon}).addTo(map);
107
- marker.bindPopup(`<b>${peerId}</b><br>Đang lấy địa chỉ...`);
108
-
109
- remoteMarkers[peerId] = { marker, lat: data.lat, lng: data.lng };
110
- renderUserList();
111
-
112
- // Lấy địa chỉ (Reverse Geocoding)
113
- fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${data.lat}&lon=${data.lng}`)
114
- .then(res => res.json())
115
- .then(d => marker.getPopup().setContent(`<b>${peerId}</b><br>${d.display_name}`));
116
  }
117
  }
118
 
119
- // 4. Cập nhật vị trí chính mình (Chức năng cũ)
120
- function updateMyLocation() {
121
- navigator.geolocation.watchPosition((pos) => {
122
- const { latitude: lat, longitude: lng } = pos.coords;
123
- const myCoords = { lat, lng };
124
-
125
- if (!myMarker) {
126
- myMarker = L.marker([lat, lng]).addTo(map).bindPopup("Bạn đang ở đây").openPopup();
127
- map.setView([lat, lng]); // Tự động zoom vào mình lúc đầu
128
- } else {
129
- myMarker.setLatLng([lat, lng]);
130
- }
131
-
132
- // Gửi cho bạn bè
133
- Object.values(peer.connections).forEach(conns => {
134
- conns.forEach(c => { if (c.open) c.send(myCoords); });
135
- });
136
- }, null, { enableHighAccuracy: true });
137
- }
138
-
139
- // 5. Danh sách người dùng để Click-to-View
140
- function renderUserList() {
141
- const container = document.getElementById('list-content');
142
- container.innerHTML = "";
143
  Object.keys(remoteMarkers).forEach(id => {
144
  const div = document.createElement('div');
145
- div.className = "user-item";
146
- div.innerText = "👤 " + id;
147
- div.onclick = () => {
148
- const m = remoteMarkers[id];
149
- map.flyTo([m.marker.getLatLng().lat, m.marker.getLatLng().lng], 17);
150
- m.marker.openPopup();
151
- };
152
- container.appendChild(div);
153
  });
154
  }
155
 
156
- updateMyLocation();
 
 
 
 
 
157
  </script>
158
  </body>
159
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Minh Đức P2P - GIS Pro Edition</title>
7
+
8
+ <!-- Leaflet Core -->
9
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
10
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
11
+
12
+ <!-- PeerJS -->
13
+ <script src="https://unpkg.com/peerjs@1.5.2/dist/peerjs.min.js"></script>
14
+
15
+ <!-- 1. Locate -->
16
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.css" />
17
+ <script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.js"></script>
18
+
19
+ <!-- 2. Fullscreen -->
20
+ <script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/Leaflet.fullscreen.min.js'></script>
21
+ <link href='https://api.mapbox.com/mapbox.js/plugins/leaflet-fullscreen/v1.0.1/leaflet.fullscreen.css' rel='stylesheet' />
22
+
23
+ <!-- 3. Geocoder -->
24
+ <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
25
+ <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
26
+
27
+ <!-- 4. Measure -->
28
+ <link rel="stylesheet" href="https://unpkg.com/leaflet-measure/dist/leaflet-measure.css" />
29
+ <script src="https://unpkg.com/leaflet-measure/dist/leaflet-measure.js"></script>
30
+
31
+ <!-- 5. MiniMap -->
32
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.min.css" />
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.6.1/Control.MiniMap.min.js"></script>
34
+
35
+ <!-- 6. Sidebar-v2 -->
36
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-sidebar-v2@3.2.3/css/leaflet-sidebar.min.css" />
37
+ <script src="https://cdn.jsdelivr.net/npm/leaflet-sidebar-v2@3.2.3/js/leaflet-sidebar.min.js"></script>
38
+
39
+ <!-- 7. Leaflet.Draw -->
40
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>
41
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
42
+
43
+ <!-- 8. MousePosition -->
44
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-mouse-position@1.2.0/src/L.Control.MousePosition.min.css" />
45
+ <script src="https://cdn.jsdelivr.net/npm/leaflet-mouse-position@1.2.0/src/L.Control.MousePosition.min.js"></script>
46
+
47
+ <!-- 9. EasyButton -->
48
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.css">
49
+ <script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.js"></script>
50
+
51
  <style>
52
+ body, html { margin: 0; padding: 0; height: 100%; font-family: 'Segoe UI', sans-serif; }
53
+ #map { height: 100vh; width: 100%; background: #f0f0f0; }
54
 
55
+ /* Marker radar mượt */
56
+ .p2p-radar {
57
+ background: #2ecc71;
58
+ border: 2px solid #ffffff;
59
+ border-radius: 50%;
60
+ box-shadow: 0 0 15px rgba(46, 204, 113, 0.6);
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
+ .p2p-radar::before {
63
+ content: ''; display: block; width: 300%; height: 300%; margin-left: -100%; margin-top: -100%;
64
+ border-radius: 50%; background-color: rgba(46, 204, 113, 0.4);
65
+ animation: radar-pulse 2s infinite ease-out;
 
 
 
 
 
66
  }
67
+ @keyframes radar-pulse { 0% { transform: scale(0.1); opacity: 1; } 100% { transform: scale(1); opacity: 0; } }
68
  </style>
69
  </head>
70
  <body>
71
 
72
+ <div id="sidebar" class="leaflet-sidebar collapsed">
73
+ <div class="leaflet-sidebar-tabs">
74
+ <ul role="tablist">
75
+ <li><a href="#home" role="tab">🏠</a></li>
76
+ <li><a href="#p2p" role="tab">🛰️</a></li>
77
+ <li><a href="#tools" role="tab">🛠️</a></li>
78
+ </ul>
79
+ </div>
80
+ <div class="leaflet-sidebar-content">
81
+ <div class="leaflet-sidebar-pane" id="home">
82
+ <h1 class="leaflet-sidebar-header">Minh Đức GIS System <span class="leaflet-sidebar-close">❌</span></h1>
83
+ <p style="padding: 20px 0;"><b>Trạng thái:</b> <span style="color: green;">Online</span></p>
84
+ <p>ID Peer của bạn: <br><strong id="peer-id-display">...</strong></p>
85
+ </div>
86
+ <div class="leaflet-sidebar-pane" id="p2p">
87
+ <h1 class="leaflet-sidebar-header">Kết nối P2P <span class="leaflet-sidebar-close">❌</span></h1>
88
+ <div id="user-list-ui" style="padding: 10px 0;"></div>
89
+ </div>
90
+ <div class="leaflet-sidebar-pane" id="tools">
91
+ <h1 class="leaflet-sidebar-header">Công cụ chuyên sâu <span class="leaflet-sidebar-close">❌</span></h1>
92
+ <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>
93
+ </div>
94
+ </div>
95
+ </div>
96
 
97
+ <div id="map"></div>
 
98
 
99
  <script>
100
+ // --- KHỞI TẠO BẢN ĐỒ (STANDARD SETTINGS) ---
101
+ // Reverted to default settings to ensure stable loading
102
+ const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'OSM' });
103
+ const satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { attribution: 'Esri' });
104
+ const terrain = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { attribution: 'Topo' });
105
+
106
+ const map = L.map('map', {
107
+ center: [10.7626, 106.6602],
108
+ zoom: 13,
109
+ layers: [osm],
110
+ fullscreenControl: true
111
+ });
112
 
113
+ // --- CÁC CÔNG CỤ ĐI KÈM ---
114
+ const sidebar = L.control.sidebar({ container: 'sidebar' }).addTo(map);
115
+
116
+ const drawnItems = new L.FeatureGroup();
117
+ map.addLayer(drawnItems);
118
+ const drawControl = new L.Control.Draw({
119
+ edit: { featureGroup: drawnItems },
120
+ draw: { polygon: true, polyline: true, rectangle: true, circle: true, marker: true }
121
+ });
122
+ map.addControl(drawControl);
123
 
124
+ map.on(L.Draw.Event.CREATED, (e) => {
125
+ drawnItems.addLayer(e.layer);
 
 
 
126
  });
127
 
128
+ L.control.mousePosition({ position: 'bottomleft', separator: ' | ', lngFirst: true }).addTo(map);
129
+
130
+ const measureControl = new L.Control.Measure({
131
+ position: 'topright',
132
+ primaryLengthUnit: 'meters',
133
+ localization: 'vi'
134
+ }).addTo(map);
135
+
136
+ const miniLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
137
+ new L.Control.MiniMap(miniLayer, { toggleDisplay: true, position: 'bottomright' }).addTo(map);
138
+
139
+ L.control.locate({ flyTo: true, keepCurrentZoomLevel: true }).addTo(map).start();
140
+
141
+ L.Control.geocoder({ defaultMarkGeocode: true }).addTo(map);
142
+
143
+ L.control.layers({ "Đường phố": osm, "Vệ tinh": satellite, "Địa hình": terrain }, { "Vùng vẽ": drawnItems }, { collapsed: false }).addTo(map);
144
+
145
+ L.easyButton('<span>🏠</span>', function(btn, map){
146
+ map.setView([10.7626, 106.6602], 13);
147
+ }, 'Về trung tâm').addTo(map);
148
+
149
+ L.control.scale({ metric: true, imperial: false }).addTo(map);
150
+
151
+ // --- LOGIC KẾT NỐI P2P ---
152
+ const GROUP_ID = "M_DUC_PRO_GIS_";
153
+ const MAX_PEERS = 15;
154
  const myNum = Math.floor(Math.random() * MAX_PEERS);
155
+ const peer = new Peer(GROUP_ID + myNum);
156
+ const remoteMarkers = {};
157
 
158
  peer.on('open', (id) => {
159
+ document.getElementById('peer-id-display').innerText = id;
160
+ setInterval(() => {
161
+ for (let i = 0; i < MAX_PEERS; i++) {
162
+ if (i !== myNum) {
163
+ const targetId = GROUP_ID + i;
164
+ if (!peer.connections[targetId]) setupConn(peer.connect(targetId));
165
+ }
166
+ }
167
+ }, 5000);
168
  });
169
 
170
+ peer.on('connection', setupConn);
 
 
 
 
 
171
 
172
+ function setupConn(conn) {
173
+ conn.on('data', (data) => { if (data.lat) updateRemote(conn.peer, data); });
 
 
 
174
  conn.on('close', () => {
175
+ if (remoteMarkers[conn.peer]) {
176
+ map.removeLayer(remoteMarkers[conn.peer]);
177
  delete remoteMarkers[conn.peer];
178
+ updateListUI();
179
  }
180
  });
181
  }
182
 
183
+ function updateRemote(id, coords) {
184
+ if (remoteMarkers[id]) {
185
+ remoteMarkers[id].setLatLng([coords.lat, coords.lng]);
 
186
  } else {
187
+ const icon = L.divIcon({ className: 'p2p-radar', iconSize: [12, 12] });
188
+ remoteMarkers[id] = L.marker([coords.lat, coords.lng], { icon: icon })
189
+ .addTo(map)
190
+ .bindPopup(`👤 <b>${id}</b>`);
191
+ updateListUI();
 
 
 
 
 
192
  }
193
  }
194
 
195
+ function updateListUI() {
196
+ const ui = document.getElementById('user-list-ui');
197
+ ui.innerHTML = "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  Object.keys(remoteMarkers).forEach(id => {
199
  const div = document.createElement('div');
200
+ div.style.padding = "10px";
201
+ div.style.borderBottom = "1px solid #ddd";
202
+ div.style.cursor = "pointer";
203
+ div.innerHTML = `📡 <b>${id}</b>`;
204
+ div.onclick = () => { map.flyTo(remoteMarkers[id].getLatLng(), 17); sidebar.close(); };
205
+ ui.appendChild(div);
 
 
206
  });
207
  }
208
 
209
+ map.on('locationfound', (e) => {
210
+ const myData = { lat: e.latlng.lat, lng: e.latlng.lng };
211
+ Object.keys(peer.connections).forEach(targetId => {
212
+ peer.connections[targetId].forEach(c => { if (c.open) c.send(myData); });
213
+ });
214
+ });
215
  </script>
216
  </body>
217
+ </html>