Meta Description" name="description" />

Share this result

Previews are deleted daily. Get a permanent share link sent to your inbox:
Script
<!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <title>GIS Mobile Pro - MVT Masterpiece</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet" /> <script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script> <script src="https://unpkg.com/pmtiles@2.11.0/dist/index.js"></script> <script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script> <script src="https://unpkg.com/shpjs@latest/dist/shp.min.js"></script> <style> :root { --primary: #1e88e5; --dark: #111; --panel-bg: rgba(26, 26, 26, 0.98); } html,body,#map{height:100%;margin:0;font-family: 'Segoe UI', sans-serif; background:var(--dark); overflow:hidden;} /* PUSAT PETA (CROSSHAIR) */ .map-reticle { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1000; pointer-events: none; color: white; font-size: 24px; text-shadow: 0 0 4px #000; opacity: 0.8; } /* MENU TOGGLE */ .menu-toggle { position:absolute; top:15px; left:15px; z-index:1200; background:var(--primary); color:white; padding:12px 20px; border-radius:10px; font-weight:bold; cursor:pointer; box-shadow:0 4px 15px rgba(0,0,0,0.5); } /* PANEL UTAMA */ .panel { position:absolute; top:0; left:-330px; width:310px; height:100%; background:var(--panel-bg); color:white; padding:80px 15px 20px; transition:0.3s ease; z-index:1150; overflow-y:auto; border-right: 1px solid rgba(255,255,255,0.1); } .panel.active { left:0; } .tool-section { margin-bottom: 20px; border-bottom: 1px solid #333; padding-bottom: 15px; } .tool-label { font-size: 10px; text-transform: uppercase; color: #777; letter-spacing: 1px; margin-bottom: 10px; display: block; } /* TOMBOL TOOL */ .btn-tool { width: 100%; padding: 12px; margin-bottom: 6px; border: none; border-radius: 8px; background: rgba(255,255,255,0.05); color: #eee; text-align: left; cursor: pointer; font-size: 13px; display: flex; align-items: center; gap: 10px; transition: 0.2s; } .btn-tool:hover { background: rgba(255,255,255,0.1); } .btn-tool:active { background: var(--primary); transform: scale(0.98); } /* UI KONTROL PLOT & UKUR */ #drawControls { position: absolute; bottom: 30px; left: 50%; transform: translateX(-50%); display: none; background: white; padding: 10px; border-radius: 50px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); z-index: 1300; } .btn-draw { padding: 12px 22px; border-radius: 30px; border: none; font-weight: bold; cursor: pointer; margin: 0 5px; } .btn-plot { background: var(--primary); color: white; } .btn-done { background: #2e7d32; color: white; } /* INFO UKURAN */ #measureInfo { position: absolute; top: 15px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.8); color: #00e676; padding: 10px 20px; border-radius: 8px; font-family: monospace; z-index: 1100; display: none; border: 1px solid #00e676; text-align: center; } </style> </head> <body> <div class="map-reticle">+</div> <div id="measureInfo">Unit: 0.00</div> <div id="map"></div> <div class="menu-toggle" id="menuBtn">☰ DASHBOARD</div> <div class="panel" id="sidePanel"> <div class="tool-section"> <span class="tool-label">Navigation</span> <button class="btn-tool" id="btnGPS">πŸ“ Posisi Saya</button> </div> <div class="tool-section"> <span class="tool-label">Survey Tools</span> <button class="btn-tool" id="btnStartPlot">πŸ“ Plotting Titik</button> <button class="btn-tool" id="btnStartPoly">β¬’ Buat Polygon</button> <button class="btn-tool" id="btnStartTrack">🧭 Start Tracking</button> </div> <div class="tool-section"> <span class="tool-label">Measurement</span> <button class="btn-tool" id="btnMeasureLine">πŸ“ Ukur Garis</button> <button class="btn-tool" id="btnMeasureArea">πŸ“ Ukur Luas Polygon</button> </div> <div class="tool-section"> <span class="tool-label">Advanced & Data</span> <button class="btn-tool" id="btnImportSHP">πŸ“¦ Import SHP (ZIP)</button> <button class="btn-tool" id="btnExportKML">⬇ Export Plotting KML</button> <button class="btn-tool" id="btnAttrTable" style="color: #ffca28;">πŸ“Š Tabel Atribut</button> </div> <div class="tool-section"> <span class="tool-label">Gallery Plotting</span> <div id="galleryContainer" style="font-size: 12px; color: #aaa;">Belum ada data...</div> </div> </div> <div id="drawControls"> <button class="btn-draw btn-plot" id="btnConfirmAction">βž• PLOT</button> <button class="btn-draw btn-done" id="btnFinishAction">βœ… SELESAI</button> <button class="btn-draw" id="btnCancelAction" style="background:#f5f5f5">βœ–</button> </div> <script> /* --- 1. INISIALISASI ENGINE PETA --- */ const map = new maplibregl.Map({ container: 'map', style: { 'version': 8, 'sources': { 'satellite': { 'type': 'raster', 'tiles': ['https://mt1.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}'], 'tileSize': 256 } }, 'layers': [{'id': 'satellite', 'type': 'raster', 'source': 'satellite'}] }, center: [118, -2.5], zoom: 5 }); /* --- 2. STATE & DATA MANAGEMENT --- */ let activeMode = null; let currentCoords = []; let plotGallery = []; let trackWatchId = null; // Struktur GeoJSON Awal let surveyGeoJSON = { 'type': 'FeatureCollection', 'features': [] }; /* --- 3. SETUP LAYER DATA (LOAD) --- */ map.on('load', () => { map.addSource('survey-data', { 'type': 'geojson', 'data': surveyGeoJSON }); // Layer Poligon (Fill) map.addLayer({ 'id': 'survey-fill', 'type': 'fill', 'source': 'survey-data', 'filter': ['==', '$type', 'Polygon'], 'paint': { 'fill-color': '#1e88e5', 'fill-opacity': 0.4 } }); // Layer Garis (Outline/Tracking) map.addLayer({ 'id': 'survey-line', 'type': 'line', 'source': 'survey-data', 'filter': ['any', ['==', '$type', 'LineString'], ['==', '$type', 'Polygon']], 'paint': { 'line-color': '#ffffff', 'line-width': 2.5 } }); // Layer Titik (Plotting) map.addLayer({ 'id': 'survey-point', 'type': 'circle', 'source': 'survey-data', 'filter': ['==', '$type', 'Point'], 'paint': { 'circle-radius': 7, 'circle-color': '#ff5252', 'circle-stroke-width': 2, 'circle-stroke-color': '#fff' } }); }); /* --- 4. UI INTERACTION HANDLER --- */ const panel = document.getElementById('sidePanel'); const menuBtn = document.getElementById('menuBtn'); menuBtn.addEventListener('click', () => panel.classList.toggle('active')); function closePanel() { panel.classList.remove('active'); } // Mencegah interaksi peta saat menyentuh UI const preventMapDrag = (id) => { const el = document.getElementById(id); if(el) { ['mousedown', 'touchstart', 'dblclick'].forEach(ev => { el.addEventListener(ev, e => e.stopPropagation()); }); } }; ['sidePanel', 'drawControls'].forEach(preventMapDrag); /* --- 5. FUNGSI NAVIGATION (GPS) --- */ document.getElementById('btnGPS').addEventListener('click', () => { if (!navigator.geolocation) return alert("GPS Tidak Tersedia"); navigator.geolocation.getCurrentPosition(pos => { const { longitude, latitude } = pos.coords; map.flyTo({ center: [longitude, latitude], zoom: 18 }); new maplibregl.Marker({ color: "#2196f3" }) .setLngLat([longitude, latitude]) .addTo(map); }, (err) => alert("Gagal mendapatkan lokasi: " + err.message), { enableHighAccuracy: true }); closePanel(); }); /* --- 6. CORE SURVEY LOGIC --- */ const startSurvey = (mode) => { activeMode = mode; currentCoords = []; document.getElementById('drawControls').style.display = 'flex'; document.getElementById('measureInfo').style.display = (mode.includes('measure')) ? 'block' : 'none'; closePanel(); }; document.getElementById('btnStartPlot').addEventListener('click', () => startSurvey('plotting')); document.getElementById('btnStartPoly').addEventListener('click', () => startSurvey('polygon')); document.getElementById('btnMeasureLine').addEventListener('click', () => startSurvey('measure_line')); document.getElementById('btnMeasureArea').addEventListener('click', () => startSurvey('measure_area')); // Tombol Konfirmasi PLOT (+) di tengah layar document.getElementById('btnConfirmAction').addEventListener('click', () => { const center = map.getCenter(); const coord = [center.lng, center.lat]; if (activeMode === 'plotting') { const name = prompt("Nama Titik:", "Titik " + (plotGallery.length + 1)); if (name) saveFeature(turf.point(coord, { name, id: Date.now() })); exitMode(); } else { currentCoords.push(coord); updatePreview(); } }); </script> /* --- 7. LOGIKA PREVIEW & PENGUKURAN --- */ function updatePreview() { if (currentCoords.length < 2) return; let preview; if (activeMode === 'polygon' || activeMode === 'measure_area') { if (currentCoords.length > 2) { preview = turf.polygon([[...currentCoords, currentCoords[0]]]); if (activeMode === 'measure_area') updateMeasureLabel(turf.area(preview), 'area'); } } else { preview = turf.lineString(currentCoords); if (activeMode.includes('measure')) updateMeasureLabel(turf.length(preview, {units: 'kilometers'}), 'length'); } // Tampilkan preview di peta secara real-time if (preview) { const source = map.getSource('survey-data'); const tempFeatures = [...surveyGeoJSON.features, preview]; source.setData({ 'type': 'FeatureCollection', 'features': tempFeatures }); } } /* --- 8. TRACKING GPS --- */ document.getElementById('btnStartTrack').addEventListener('click', function() { if (activeMode === 'tracking') { stopTracking(); this.innerText = "🧭 Start Tracking"; this.style.background = ""; } else { activeMode = 'tracking'; currentCoords = []; this.innerText = "πŸ›‘ Stop Tracking"; this.style.background = "#d32f2f"; trackWatchId = navigator.geolocation.watchPosition(pos => { const coord = [pos.coords.longitude, pos.coords.latitude]; currentCoords.push(coord); map.setCenter(coord); updatePreview(); }, null, { enableHighAccuracy: true }); } closePanel(); }); function stopTracking() { navigator.geolocation.clearWatch(trackWatchId); if (currentCoords.length > 1) { saveFeature(turf.lineString(currentCoords, { name: "Track " + new Date().toLocaleTimeString() })); } exitMode(); } /* --- 9. PENGELOLAAN DATA (SAVE & GALLERY) --- */ function saveFeature(feature) { surveyGeoJSON.features.push(feature); map.getSource('survey-data').setData(surveyGeoJSON); plotGallery.push(feature); updateGallery(); } function updateGallery() { const container = document.getElementById('galleryContainer'); container.innerHTML = plotGallery.map(f => ` <div class="btn-tool" style="font-size:11px"> πŸ“¦ ${f.properties.name || 'Unnamed'} </div> `).join(''); } function exitMode() { activeMode = null; currentCoords = []; document.getElementById('drawControls').style.display = 'none'; document.getElementById('measureInfo').style.display = 'none'; map.getSource('survey-data').setData(surveyGeoJSON); // Reset view ke data tersimpan } document.getElementById('btnFinishAction').addEventListener('click', () => { if (activeMode === 'polygon' && currentCoords.length > 2) { const name = prompt("Nama Polygon:", "Area " + plotGallery.length); saveFeature(turf.polygon([[...currentCoords, currentCoords[0]]], { name })); } exitMode(); }); document.getElementById('btnCancelAction').addEventListener('click', exitMode); function updateMeasureLabel(val, type) { const el = document.getElementById('measureInfo'); if (type === 'length') el.innerText = `Jarak: ${val.toFixed(3)} km | ${(val*1000).toFixed(0)} m`; else el.innerText = `Luas: ${(val/10000).toFixed(2)} Ha | ${(val/1000000).toFixed(3)} kmΒ²`; } /* --- 10. ADVANCED SYMBOLOGY & UI COMPONENTS --- */ const symStyle = ` <style> #symPanel { position:fixed; top:0; right:-320px; width:300px; height:100%; background:#1a1a1a; color:white; z-index:1500; transition:0.3s; padding:20px; border-left:1px solid #333; overflow-y:auto; } #symPanel.active { right:0; } .attr-wrap { position:fixed; bottom:-50%; left:0; width:100%; height:50%; background:#1a1a1a; z-index:1400; transition:0.3s; display:flex; flex-direction:column; border-top: 2px solid var(--primary); } .attr-wrap.active { bottom:0; } .attr-handle { height:30px; background:#333; display:flex; justify-content:center; align-items:center; cursor:grab; } .table-scroll { overflow:auto; flex:1; } table { width:100%; border-collapse:collapse; font-size:12px; } th { background:#222; position:sticky; top:0; padding:10px; border:1px solid #444; } td { padding:8px; border:1px solid #444; text-align:center; } </style>`; document.head.insertAdjacentHTML('beforeend', symStyle); const symHTML = ` <div id="symPanel"> <span class="tool-label">Simbologi & Labeling</span> <div style="margin-bottom:15px;"> <label><input type="checkbox" id="checkHollow"> Hollow (Tanpa Isian)</label> </div> <label>Warna Isian:</label> <input type="color" id="colorFill" value="#1e88e5" style="width:100%; margin-bottom:10px;"> <label>Warna Garis:</label> <input type="color" id="colorLine" value="#ffffff" style="width:100%; margin-bottom:10px;"> <label>Ketebalan Garis:</label> <input type="range" id="widthLine" min="1" max="10" value="2" style="width:100%; margin-bottom:15px;"> <label>Pilih Field Label:</label> <select id="labelFieldSelect" style="width:100%; padding:8px; background:#333; color:white; border:none; margin-bottom:15px;"><option value="">Tanpa Label</option></select> <button class="btn-tool" style="background:var(--primary)" id="applySymBtn">TERAPKAN GAYA</button> <button class="btn-tool" style="background:#444; margin-top:10px;" onclick="document.getElementById('symPanel').classList.remove('active')">TUTUP</button> </div> <div id="attrWrap" class="attr-wrap"> <div class="attr-handle" id="attrHandle">━ TABEL ATRIBUT ━</div> <div class="table-scroll" id="attrTableBody"></div> </div>`; document.body.insertAdjacentHTML('beforeend', symHTML); /* --- 11. IMPORT SHP & TABEL ATRIBUT --- */ document.getElementById('btnImportSHP').addEventListener('click', () => { const input = document.createElement('input'); input.type = 'file'; input.accept = '.zip'; input.onchange = async (e) => { const file = e.target.files[0]; if (!file) return; try { const buffer = await file.arrayBuffer(); const geojson = await shp(buffer); const features = Array.isArray(geojson) ? geojson[0].features : geojson.features; surveyGeoJSON.features.push(...features); map.getSource('survey-data').setData(surveyGeoJSON); const fields = Object.keys(features[0].properties); document.getElementById('labelFieldSelect').innerHTML = '<option value="">Tanpa Label</option>' + fields.map(f => `<option value="${f}">${f}</option>`).join(''); const bbox = turf.bbox({type: 'FeatureCollection', features: features}); map.fitBounds([[bbox[0], bbox[1]], [bbox[2], bbox[3]]], {padding: 50}); buildAttributeTable(features); alert("SHP Berhasil Dimuat!"); } catch (err) { alert("Error: Pastikan ZIP berisi file .shp, .dbf, dll."); } }; input.click(); closePanel(); }); function buildAttributeTable(features) { const container = document.getElementById('attrTableBody'); if (!features || features.length === 0) return; let html = `<table><thead><tr>`; const keys = Object.keys(features[0].properties); keys.forEach(k => html += `<th>${k}</th>`); html += `</tr></thead><tbody>`; features.forEach(f => { html += `<tr>`; keys.forEach(k => html += `<td contenteditable="true">${f.properties[k] || ''}</td>`); html += `</tr>`; }); html += `</tbody></table>`; container.innerHTML = html; } document.getElementById('btnAttrTable').addEventListener('click', () => { document.getElementById('attrWrap').classList.toggle('active'); closePanel(); }); /* --- 12. APPLY SYMBOLOGY & EXPORT KML --- */ document.getElementById('applySymBtn').addEventListener('click', () => { const hollow = document.getElementById('checkHollow').checked; const fill = document.getElementById('colorFill').value; const line = document.getElementById('colorLine').value; const width = parseFloat(document.getElementById('widthLine').value); const labelField = document.getElementById('labelFieldSelect').value; map.setPaintProperty('survey-fill', 'fill-color', fill); map.setPaintProperty('survey-fill', 'fill-opacity', hollow ? 0 : 0.4); map.setPaintProperty('survey-line', 'line-color', line); map.setPaintProperty('survey-line', 'line-width', width); if (labelField) { if (!map.getLayer('survey-labels')) { map.addLayer({ 'id': 'survey-labels', 'type': 'symbol', 'source': 'survey-data', 'layout': { 'text-field': ['get', labelField], 'text-size': 12, 'text-variable-anchor': ['top'], 'text-radial-offset': 0.5 }, 'paint': { 'text-color': '#fff', 'text-halo-color': '#000', 'text-halo-width': 1 } }); } else { map.setLayoutProperty('survey-labels', 'text-field', ['get', labelField]); } } }); document.getElementById('btnExportKML').addEventListener('click', () => { if (surveyGeoJSON.features.length === 0) return alert("Belum ada data!"); let kml = `<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><Document>`; surveyGeoJSON.features.forEach(f => { kml += `<Placemark><name>${f.properties.name || 'Feature'}</name>`; if (f.geometry.type === 'Point') { kml += `<Point><coordinates>${f.geometry.coordinates.join(',')}</coordinates></Point>`; } else if (f.geometry.type === 'Polygon') { const coords = f.geometry.coordinates[0].map(c => c.join(',')).join(' '); kml += `<Polygon><outerBoundaryIs><LinearRing><coordinates>${coords}</coordinates></LinearRing></outerBoundaryIs></Polygon>`; } else if (f.geometry.type === 'LineString') { const coords = f.geometry.coordinates.map(c => c.join(',')).join(' '); kml += `<LineString><coordinates>${coords}</coordinates></LineString>`; } kml += `</Placemark>`; }); kml += `</Document></kml>`; const blob = new Blob([kml], {type: "application/vnd.google-earth.kml+xml"}); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = "Survey_Export.kml"; a.click(); }); </script> </body> </html>
Landing Page
This ad does not have a landing page available
Network Timeline
Performance Summary

49

Requests

4

Domains

906KB

Transfer Size

2042KB

Content Size

5,301.0ms

Dom Content Loaded

3,492.0ms

First Paint

5,393.0ms

Load Time
Domain Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...
Timings (ms)
Loading...
Total Time
Loading...
Content Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...