{"id":680,"date":"2026-02-23T04:25:42","date_gmt":"2026-02-23T04:25:42","guid":{"rendered":"https:\/\/marineconservation.id\/?page_id=680"},"modified":"2026-03-11T21:18:23","modified_gmt":"2026-03-11T21:18:23","slug":"analisis-photo-berskala","status":"publish","type":"page","link":"https:\/\/marineconservation.id\/id\/analisis-photo-berskala\/","title":{"rendered":"Analisis Photo Berskala"},"content":{"rendered":"<style>.elementor-680 .elementor-element.elementor-element-50487e8{--display:flex;--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}.elementor-680 .elementor-element.elementor-element-199ad97{--display:flex;--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}.elementor-680 .elementor-element.elementor-element-61ce592{--display:flex;--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}.elementor-widget-heading .elementor-heading-title{font-family:var( --e-global-typography-primary-font-family ), Sans-serif;font-weight:var( --e-global-typography-primary-font-weight );color:var( --e-global-color-primary );}.elementor-680 .elementor-element.elementor-element-eb38ee2 .elementor-heading-title{color:#00399F;}.elementor-680 .elementor-element.elementor-element-1fbc709{--display:flex;}.elementor-680 .elementor-element.elementor-element-4dbe900 .elementor-wrapper{--video-aspect-ratio:1.77777;}.elementor-680 .elementor-element.elementor-element-7a5b5a2{--display:flex;--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}.elementor-widget-text-editor{font-family:var( --e-global-typography-text-font-family ), Sans-serif;font-weight:var( --e-global-typography-text-font-weight );color:var( --e-global-color-text );}.elementor-widget-text-editor.elementor-drop-cap-view-stacked .elementor-drop-cap{background-color:var( --e-global-color-primary );}.elementor-widget-text-editor.elementor-drop-cap-view-framed .elementor-drop-cap, .elementor-widget-text-editor.elementor-drop-cap-view-default .elementor-drop-cap{color:var( --e-global-color-primary );border-color:var( --e-global-color-primary );}@media(max-width:1024px){.elementor-680 .elementor-element.elementor-element-50487e8{--flex-direction:column;--container-widget-width:100%;--container-widget-height:initial;--container-widget-flex-grow:0;--container-widget-align-self:initial;--flex-wrap-mobile:wrap;}}<\/style>\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"680\" class=\"elementor elementor-680\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-50487e8 e-con-full e-flex e-con e-parent\" data-id=\"50487e8\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d7d9d7d elementor-widget elementor-widget-html\" data-id=\"d7d9d7d\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>Ukur Foto \u00b7 Skala, Panjang, Luas \u00b7 WordPress<\/title>\r\n    <!-- Font Awesome 6 -->\r\n    <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.0.0-beta3\/css\/all.min.css\">\r\n    <!-- SheetJS (XLSX) untuk ekspor Excel -->\r\n    <script src=\"https:\/\/cdn.sheetjs.com\/xlsx-0.20.2\/package\/dist\/xlsx.full.min.js\"><\/script>\r\n    <!-- Leaflet CSS & JS (open-source map) -->\r\n    <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\" \/>\r\n    <script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\r\n    <style>\r\n        * {\r\n            box-sizing: border-box;\r\n            margin: 0;\r\n            padding: 0;\r\n        }\r\n\r\n        body {\r\n            background: #eef2f6;\r\n            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;\r\n            display: flex;\r\n            justify-content: center;\r\n            align-items: center;\r\n            min-height: 100vh;\r\n            margin: 20px;\r\n        }\r\n\r\n        .wp-panel {\r\n            max-width: 1600px;\r\n            width: 100%;\r\n            background: white;\r\n            border-radius: 32px;\r\n            box-shadow: 0 25px 50px -12px rgba(0,0,0,0.25);\r\n            padding: 30px;\r\n        }\r\n\r\n        h1 {\r\n            font-size: 2.2rem;\r\n            font-weight: 600;\r\n            color: #0b1e33;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: space-between;\r\n            gap: 15px;\r\n            border-bottom: 2px solid #dde3ed;\r\n            padding-bottom: 20px;\r\n            margin-bottom: 25px;\r\n            flex-wrap: wrap;\r\n        }\r\n        h1 i { color: #1d4ed8; }\r\n        #languageSelect {\r\n            padding: 8px 16px;\r\n            border-radius: 40px;\r\n            border: 1px solid #1d4ed8;\r\n            background: white;\r\n            font-weight: 500;\r\n            cursor: pointer;\r\n        }\r\n        .project-buttons {\r\n            display: flex;\r\n            gap: 8px;\r\n            margin-left: auto;\r\n        }\r\n        .btn-outline {\r\n            background: white;\r\n            border: 1.5px solid #1d4ed8;\r\n            color: #1d4ed8;\r\n            padding: 8px 20px;\r\n            border-radius: 40px;\r\n            font-weight: 600;\r\n            cursor: pointer;\r\n            transition: 0.15s;\r\n            font-size: 0.9rem;\r\n            display: inline-flex;\r\n            align-items: center;\r\n            gap: 6px;\r\n        }\r\n        .btn-outline:hover {\r\n            background: #1d4ed8;\r\n            color: white;\r\n        }\r\n\r\n        \/* Layout atas: informasi project dan peta dalam grid 2 kolom *\/\r\n        .info-map-grid {\r\n            display: grid;\r\n            grid-template-columns: 1fr 1fr;\r\n            gap: 25px;\r\n            margin-bottom: 30px;\r\n        }\r\n\r\n        \/* Kolom informasi project *\/\r\n        .project-info {\r\n            background: white;\r\n            border-radius: 28px;\r\n            padding: 20px;\r\n            border: 1px solid #b9d3f0;\r\n            display: grid;\r\n            grid-template-columns: repeat(2, 1fr);\r\n            gap: 15px;\r\n        }\r\n        .info-field {\r\n            display: flex;\r\n            flex-direction: column;\r\n            gap: 5px;\r\n        }\r\n        .info-field label {\r\n            font-weight: 600;\r\n            color: #1e3a8a;\r\n            font-size: 0.9rem;\r\n        }\r\n        .info-field input, .info-field select {\r\n            padding: 8px 12px;\r\n            border: 1px solid #8ba9db;\r\n            border-radius: 30px;\r\n            background: #f8fcff;\r\n            font-weight: 500;\r\n        }\r\n\r\n        \/* Peta Lokasi *\/\r\n        .location-card {\r\n            background: white;\r\n            border-radius: 28px;\r\n            padding: 18px;\r\n            border: 1px solid #b9d3f0;\r\n            box-shadow: 0 6px 14px #dde3ed;\r\n        }\r\n        .location-header {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n            font-weight: 700;\r\n            color: #1e3a8a;\r\n            margin-bottom: 15px;\r\n        }\r\n        .map-container {\r\n            height: 200px;\r\n            width: 100%;\r\n            border-radius: 20px;\r\n            overflow: hidden;\r\n            border: 1px solid #abc2df;\r\n            margin-bottom: 15px;\r\n            z-index: 5;\r\n        }\r\n        #map {\r\n            height: 100%;\r\n            width: 100%;\r\n        }\r\n        .coord-input {\r\n            display: flex;\r\n            gap: 15px;\r\n            align-items: center;\r\n            flex-wrap: wrap;\r\n        }\r\n        .coord-field {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 6px;\r\n            background: #f1f7ff;\r\n            padding: 6px 15px;\r\n            border-radius: 30px;\r\n        }\r\n        .coord-field input {\r\n            width: 100px;\r\n            padding: 6px 10px;\r\n            border: 1px solid #8ba9db;\r\n            border-radius: 30px;\r\n            text-align: center;\r\n        }\r\n\r\n        \/* Toolbar *\/\r\n        .toolbar {\r\n            display: flex;\r\n            flex-wrap: wrap;\r\n            align-items: center;\r\n            gap: 18px 25px;\r\n            background: #f9fcff;\r\n            padding: 15px 25px;\r\n            border-radius: 60px;\r\n            border: 1px solid #b9d3f0;\r\n            margin-bottom: 30px;\r\n        }\r\n\r\n        .upload-group {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 8px;\r\n            flex-wrap: wrap;\r\n        }\r\n        .btn-primary {\r\n            background: #1d4ed8;\r\n            color: white;\r\n            border: none;\r\n            padding: 12px 28px;\r\n            border-radius: 40px;\r\n            font-weight: 600;\r\n            font-size: 1rem;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n            cursor: pointer;\r\n            transition: 0.15s;\r\n            box-shadow: 0 8px 14px -6px #1e3a8a70;\r\n        }\r\n        .btn-primary:hover { background: #1e3a8a; }\r\n        #file-info {\r\n            background: white;\r\n            border-radius: 40px;\r\n            padding: 8px 20px;\r\n            border: 1px solid #aac3e0;\r\n            font-size: 0.9rem;\r\n            max-width: 280px;\r\n            white-space: nowrap;\r\n            overflow: hidden;\r\n            text-overflow: ellipsis;\r\n        }\r\n\r\n        \/* Multiple photo selector *\/\r\n        .photo-selector {\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n            background: white;\r\n            padding: 5px 15px;\r\n            border-radius: 40px;\r\n            border: 1px solid #8ba9db;\r\n            margin-left: 10px;\r\n        }\r\n        .photo-nav-btn {\r\n            background: none;\r\n            border: none;\r\n            font-size: 1.2rem;\r\n            cursor: pointer;\r\n            color: #1d4ed8;\r\n            width: 32px;\r\n            height: 32px;\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .photo-nav-btn:hover:not(:disabled) {\r\n            background: #dbeafe;\r\n        }\r\n        .photo-nav-btn:disabled {\r\n            opacity: 0.3;\r\n            cursor: not-allowed;\r\n        }\r\n        #photoCounter {\r\n            font-weight: 600;\r\n            color: #1e3a8a;\r\n            min-width: 60px;\r\n            text-align: center;\r\n        }\r\n\r\n        .calibrate-card {\r\n            background: #e6f0ff;\r\n            border-radius: 50px;\r\n            padding: 8px 20px 8px 25px;\r\n            display: flex;\r\n            flex-wrap: wrap;\r\n            align-items: center;\r\n            gap: 15px;\r\n            border: 1px solid #5184db;\r\n        }\r\n        .calibrate-card .label {\r\n            font-weight: 600;\r\n            color: #0b2c5f;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 6px;\r\n        }\r\n        .known-length-input {\r\n            width: 80px;\r\n            padding: 8px;\r\n            border: 1px solid #5184db;\r\n            border-radius: 30px;\r\n            text-align: center;\r\n            font-weight: 600;\r\n        }\r\n        .unit-select {\r\n            padding: 7px 12px;\r\n            border-radius: 30px;\r\n            border: 1px solid #5184db;\r\n            background: white;\r\n            font-weight: 500;\r\n        }\r\n        .badge-tip {\r\n            background: #ffedc0;\r\n            color: #92400e;\r\n            padding: 5px 15px;\r\n            border-radius: 30px;\r\n            font-size: 0.85rem;\r\n        }\r\n\r\n        .mode-group {\r\n            display: flex;\r\n            gap: 8px;\r\n            background: white;\r\n            padding: 4px;\r\n            border-radius: 40px;\r\n            border: 1px solid #abc2df;\r\n            margin-left: auto;\r\n        }\r\n        .mode-btn {\r\n            padding: 9px 22px;\r\n            border-radius: 40px;\r\n            border: none;\r\n            background: transparent;\r\n            font-weight: 600;\r\n            cursor: pointer;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 8px;\r\n            transition: 0.1s;\r\n        }\r\n        .mode-btn.active {\r\n            background: #1d4ed8;\r\n            color: white;\r\n        }\r\n        .mode-btn:not(.active):hover {\r\n            background: #dee9fc;\r\n        }\r\n\r\n        \/* Layout bawah: canvas dan panel analisis *\/\r\n        .analysis-grid {\r\n            display: grid;\r\n            grid-template-columns: 1.2fr 0.8fr;\r\n            gap: 25px;\r\n            margin-top: 10px;\r\n        }\r\n\r\n        \/* Canvas area dengan kontrol zoom *\/\r\n        .canvas-area {\r\n            background: #f2f6fd;\r\n            border-radius: 32px;\r\n            padding: 20px;\r\n            border: 2px dashed #8ba9db;\r\n        }\r\n        .canvas-header {\r\n            display: flex;\r\n            justify-content: space-between;\r\n            align-items: center;\r\n            margin-bottom: 15px;\r\n        }\r\n        .zoom-controls {\r\n            display: flex;\r\n            gap: 8px;\r\n            align-items: center;\r\n            background: white;\r\n            padding: 5px 15px;\r\n            border-radius: 40px;\r\n            border: 1px solid #8ba9db;\r\n        }\r\n        .zoom-btn {\r\n            background: none;\r\n            border: none;\r\n            font-size: 1.3rem;\r\n            cursor: pointer;\r\n            color: #1d4ed8;\r\n            width: 32px;\r\n            height: 32px;\r\n            border-radius: 50%;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n        }\r\n        .zoom-btn:hover {\r\n            background: #dbeafe;\r\n        }\r\n        #zoomSlider {\r\n            width: 120px;\r\n        }\r\n        .image-container {\r\n            position: relative;\r\n            display: inline-block;\r\n            width: 100%;\r\n            text-align: center;\r\n            overflow: auto;\r\n            max-height: 550px;\r\n        }\r\n        #imageCanvas {\r\n            display: block;\r\n            max-width: none;\r\n            width: auto;\r\n            height: auto;\r\n            border-radius: 20px;\r\n            box-shadow: 0 20px 28px -12px #1e293b80;\r\n            background: white;\r\n            cursor: crosshair;\r\n            margin: 0 auto;\r\n            transition: transform 0.1s ease;\r\n            transform-origin: top left;\r\n        }\r\n        .hint {\r\n            margin-top: 15px;\r\n            color: #1e3a5f;\r\n            background: #d9e9ff;\r\n            padding: 8px 20px;\r\n            border-radius: 40px;\r\n            font-size: 0.95rem;\r\n            display: inline-block;\r\n        }\r\n\r\n        \/* Side panel *\/\r\n        .side-panel {\r\n            background: #fafdff;\r\n            border-radius: 32px;\r\n            padding: 25px;\r\n            border: 1px solid #bfd6f5;\r\n            align-self: start;\r\n        }\r\n        .section-title {\r\n            font-weight: 700;\r\n            font-size: 1.4rem;\r\n            color: #0a1e3c;\r\n            margin-bottom: 20px;\r\n            display: flex;\r\n            gap: 8px;\r\n        }\r\n        .list-container {\r\n            background: white;\r\n            border-radius: 24px;\r\n            padding: 8px;\r\n            max-height: 240px;\r\n            overflow-y: auto;\r\n            border: 1px solid #d4e0f0;\r\n            margin-bottom: 20px;\r\n        }\r\n        .measure-item {\r\n            display: flex;\r\n            justify-content: space-between;\r\n            align-items: center;\r\n            padding: 10px 12px;\r\n            border-bottom: 1px solid #eef3fc;\r\n        }\r\n        .measure-item:last-child { border: none; }\r\n        .badge {\r\n            background: #dbeafe;\r\n            color: #1d4ed8;\r\n            border-radius: 30px;\r\n            padding: 4px 12px;\r\n            font-weight: 500;\r\n            font-size: 0.9rem;\r\n        }\r\n        .value {\r\n            font-weight: 700;\r\n            color: #0f2b4b;\r\n        }\r\n        .delete-btn {\r\n            color: #94a3b8;\r\n            background: none;\r\n            border: none;\r\n            font-size: 1.1rem;\r\n            cursor: pointer;\r\n            padding: 0 5px;\r\n        }\r\n        .delete-btn:hover { color: #dc2626; }\r\n\r\n        .summary {\r\n            background: #e3f2fd;\r\n            border-radius: 24px;\r\n            padding: 20px;\r\n            margin-bottom: 20px;\r\n            border-left: 6px solid #1d4ed8;\r\n        }\r\n        .stat-row {\r\n            display: flex;\r\n            justify-content: space-between;\r\n            font-weight: 600;\r\n            font-size: 1.1rem;\r\n            margin: 8px 0;\r\n        }\r\n        .stat-label { color: #1e3a8a; }\r\n        .stat-number { color: #0c4a6e; }\r\n\r\n        .export-group {\r\n            display: flex;\r\n            flex-direction: column;\r\n            gap: 12px;\r\n            margin: 20px 0;\r\n        }\r\n        .btn-export {\r\n            background: white;\r\n            border: 2px solid #1d4ed8;\r\n            color: #1d4ed8;\r\n            padding: 12px;\r\n            border-radius: 40px;\r\n            font-weight: 700;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 12px;\r\n            transition: 0.15s;\r\n            cursor: pointer;\r\n            width: 100%;\r\n        }\r\n        .btn-export:hover {\r\n            background: #1d4ed8;\r\n            color: white;\r\n        }\r\n        .btn-export:hover i { color: white; }\r\n        .btn-export i { color: #1d4ed8; font-size: 1.2rem; }\r\n\r\n        .footer {\r\n            margin-top: 30px;\r\n            text-align: center;\r\n            color: #556b86;\r\n            border-top: 1px solid #cbd9ee;\r\n            padding-top: 20px;\r\n            clear: both;\r\n        }\r\n        .reset-small {\r\n            background: none;\r\n            border: 1px solid #b1c9e8;\r\n            border-radius: 30px;\r\n            padding: 8px 18px;\r\n            font-weight: 500;\r\n            cursor: pointer;\r\n            margin-right: 8px;\r\n            margin-bottom: 5px;\r\n        }\r\n\r\n        .area-tools {\r\n            display: flex;\r\n            gap: 8px;\r\n            margin: 10px 0;\r\n            flex-wrap: wrap;\r\n        }\r\n\r\n        \/* Modal dialog *\/\r\n        .modal {\r\n            display: none;\r\n            position: fixed;\r\n            top: 0; left: 0; width: 100%; height: 100%;\r\n            background: rgba(0,0,0,0.5);\r\n            justify-content: center;\r\n            align-items: center;\r\n            z-index: 1000;\r\n        }\r\n        .modal-content {\r\n            background: white;\r\n            padding: 30px;\r\n            border-radius: 40px;\r\n            max-width: 500px;\r\n            width: 90%;\r\n        }\r\n        .modal-content h3 { margin-bottom: 15px; color: #1d4ed8; }\r\n        .modal-content input, .modal-content select {\r\n            width: 100%;\r\n            padding: 12px;\r\n            margin: 8px 0;\r\n            border-radius: 30px;\r\n            border: 1px solid #8ba9db;\r\n        }\r\n        .modal-btns {\r\n            display: flex;\r\n            gap: 10px;\r\n            margin-top: 20px;\r\n            flex-wrap: wrap;\r\n        }\r\n\r\n        \/* Responsive *\/\r\n        @media (max-width: 1200px) {\r\n            .analysis-grid {\r\n                grid-template-columns: 1fr;\r\n            }\r\n            .toolbar {\r\n                flex-direction: column;\r\n                align-items: stretch;\r\n            }\r\n            .mode-group {\r\n                margin-left: 0;\r\n                justify-content: center;\r\n            }\r\n        }\r\n\r\n        @media (max-width: 768px) {\r\n            .info-map-grid {\r\n                grid-template-columns: 1fr;\r\n            }\r\n            .project-info {\r\n                grid-template-columns: 1fr;\r\n            }\r\n            .coord-input {\r\n                flex-direction: column;\r\n            }\r\n            .coord-field {\r\n                width: 100%;\r\n            }\r\n            .btn-outline {\r\n                width: 100%;\r\n            }\r\n            .upload-group {\r\n                flex-direction: column;\r\n                align-items: stretch;\r\n            }\r\n            #file-info {\r\n                max-width: 100%;\r\n            }\r\n            .photo-selector {\r\n                margin-left: 0;\r\n                justify-content: center;\r\n            }\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n<div class=\"wp-panel\">\r\n    <h1>\r\n        <span>\r\n            <i class=\"fas fa-draw-polygon\"><\/i>\r\n            <span data-i18n=\"appTitle\">Skala & Luas Foto \u00b7 Marine Conservation Indonesia<\/span>\r\n        <\/span>\r\n        <div style=\"display: flex; gap: 8px; align-items: center;\">\r\n            <select id=\"languageSelect\">\r\n                <option value=\"id\" selected>\ud83c\uddee\ud83c\udde9 Indonesia<\/option>\r\n                <option value=\"en\">\ud83c\uddec\ud83c\udde7 English<\/option>\r\n            <\/select>\r\n            <!-- TOMBOL SIMPAN & MUAT PROYEK -->\r\n            <button class=\"btn-outline\" id=\"saveProjectBtn\"><i class=\"fas fa-save\"><\/i> <span data-i18n=\"saveProject\">Simpan Proyek<\/span><\/button>\r\n            <button class=\"btn-outline\" id=\"loadProjectBtn\"><i class=\"fas fa-folder-open\"><\/i> <span data-i18n=\"loadProject\">Muat Proyek<\/span><\/button>\r\n            <input type=\"file\" id=\"loadProjectFile\" accept=\".json\" style=\"display: none;\">\r\n        <\/div>\r\n    <\/h1>\r\n\r\n    <!-- Bagian PALING ATAS: Informasi Project dan Peta -->\r\n    <div class=\"info-map-grid\">\r\n        <!-- Kolom Kiri: 5 Field Informasi (termasuk waktu) -->\r\n        <div class=\"project-info\">\r\n            <div class=\"info-field\">\r\n                <label data-i18n=\"projectNameLabel\"><i class=\"fas fa-folder\"><\/i> Nama Project<\/label>\r\n                <input type=\"text\" id=\"projectName\" data-i18n-placeholder=\"projectNamePlaceholder\" placeholder=\"Nama Project\" value=\"Marine Conservation Indonesia\">\r\n            <\/div>\r\n            <div class=\"info-field\">\r\n                <label data-i18n=\"stationLabel\"><i class=\"fas fa-map-pin\"><\/i> Stasiun<\/label>\r\n                <input type=\"text\" id=\"stationName\" data-i18n-placeholder=\"stationPlaceholder\" placeholder=\"Stasiun\" value=\"Stasiun Pusat\">\r\n            <\/div>\r\n            <div class=\"info-field\">\r\n                <label data-i18n=\"objectNameLabel\"><i class=\"fas fa-tag\"><\/i> Nama Objek<\/label>\r\n                <input type=\"text\" id=\"objectName\" data-i18n-placeholder=\"objectNamePlaceholder\" placeholder=\"Nama Objek\" value=\"Terumbu Karang\">\r\n            <\/div>\r\n            <div class=\"info-field\">\r\n                <label data-i18n=\"depthLabel\"><i class=\"fas fa-water\"><\/i> Kedalaman (m)<\/label>\r\n                <input type=\"number\" id=\"depth\" value=\"8\" step=\"0.1\" min=\"0\">\r\n            <\/div>\r\n            <div class=\"info-field\">\r\n                <label data-i18n=\"timeLabel\"><i class=\"fas fa-clock\"><\/i> Waktu Pengukuran<\/label>\r\n                <input type=\"datetime-local\" id=\"measurementTime\" value=\"2026-02-25T10:00\">\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Kolom Kanan: PETA LOKASI -->\r\n        <div class=\"location-card\">\r\n            <div class=\"location-header\">\r\n                <i class=\"fas fa-map-marker-alt\" style=\"color: #e11d48;\"><\/i>\r\n                <span data-i18n=\"locationHeader\">Lokasi Pengukuran (klik peta untuk isi koordinat)<\/span>\r\n            <\/div>\r\n            <div class=\"map-container\" id=\"map\"><\/div>\r\n            <div class=\"coord-input\">\r\n                <div class=\"coord-field\">\r\n                    <i class=\"fas fa-latitude\"><\/i>\r\n                    <input type=\"number\" id=\"latInput\" step=\"any\" placeholder=\"Latitude\" value=\"-6.2088\">\r\n                <\/div>\r\n                <div class=\"coord-field\">\r\n                    <i class=\"fas fa-longitude\"><\/i>\r\n                    <input type=\"number\" id=\"lngInput\" step=\"any\" placeholder=\"Longitude\" value=\"106.8456\">\r\n                <\/div>\r\n                <button class=\"btn-outline\" id=\"applyLocationBtn\"><i class=\"fas fa-check\"><\/i> <span data-i18n=\"applyBtn\">Terapkan<\/span><\/button>\r\n            <\/div>\r\n            <div style=\"margin-top: 12px; font-size: 0.9rem; color: #2c3e66;\">\r\n                <i class=\"far fa-save\"><\/i> <span data-i18n=\"locationDisplayLabel\">Lokasi:<\/span> <span id=\"locationDisplay\">Jakarta (default)<\/span>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Toolbar utama -->\r\n    <div class=\"toolbar\">\r\n        <div class=\"upload-group\">\r\n            <button class=\"btn-primary\" id=\"uploadBtn\"><i class=\"fas fa-cloud-upload-alt\"><\/i> <span data-i18n=\"uploadBtn\">Upload Foto<\/span><\/button>\r\n            <input type=\"file\" id=\"fileInput\" accept=\"image\/*\" multiple style=\"display: none;\">\r\n            <span id=\"file-info\" data-i18n=\"noPhoto\">Belum ada foto<\/span>\r\n            \r\n            <!-- Multiple photo selector -->\r\n            <div class=\"photo-selector\" id=\"photoSelector\" style=\"display: none;\">\r\n                <button class=\"photo-nav-btn\" id=\"prevPhotoBtn\" disabled><i class=\"fas fa-chevron-left\"><\/i><\/button>\r\n                <span id=\"photoCounter\">0\/0<\/span>\r\n                <button class=\"photo-nav-btn\" id=\"nextPhotoBtn\" disabled><i class=\"fas fa-chevron-right\"><\/i><\/button>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Kalibrasi skala -->\r\n        <div class=\"calibrate-card\">\r\n            <span class=\"label\"><i class=\"fas fa-ruler\"><\/i> <span data-i18n=\"calibrateLabel\">Set skala via 2 titik diketahui<\/span><\/span>\r\n            <input type=\"number\" id=\"knownLength\" class=\"known-length-input\" value=\"10\" step=\"0.1\" min=\"0.1\">\r\n            <select id=\"unitSelect\" class=\"unit-select\">\r\n                <option value=\"cm\" selected data-i18n=\"cm\">cm<\/option>\r\n                <option value=\"m\" data-i18n=\"m\">meter<\/option>\r\n                <option value=\"inchi\" data-i18n=\"inchi\">inchi<\/option>\r\n            <\/select>\r\n            <button class=\"btn-primary\" id=\"applyScaleBtn\" style=\"padding:8px 22px; box-shadow: none;\" disabled><i class=\"fas fa-check\"><\/i> <span data-i18n=\"applyScaleBtn\">Terapkan skala<\/span><\/button>\r\n            <span class=\"badge-tip\"><i class=\"fas fa-lightbulb\"><\/i> <span data-i18n=\"calibrateTip\">klik 2 titik objek yg diketahui<\/span><\/span>\r\n        <\/div>\r\n\r\n        <!-- Mode ukur -->\r\n        <div class=\"mode-group\">\r\n            <button id=\"modeLength\" class=\"mode-btn active\"><i class=\"fas fa-ruler\"><\/i> <span data-i18n=\"modeLength\">Panjang<\/span><\/button>\r\n            <button id=\"modeArea\" class=\"mode-btn\"><i class=\"fas fa-draw-polygon\"><\/i> <span data-i18n=\"modeArea\">Luas<\/span><\/button>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Bagian BAWAH: Analisis Foto -->\r\n    <div class=\"analysis-grid\">\r\n        <!-- Kolom KIRI: Canvas foto -->\r\n        <div class=\"canvas-area\">\r\n            <div class=\"canvas-header\">\r\n                <span><i class=\"fas fa-image\"><\/i> <span data-i18n=\"imageArea\">Area Gambar<\/span><\/span>\r\n                <div class=\"zoom-controls\">\r\n                    <button class=\"zoom-btn\" id=\"zoomOut\"><i class=\"fas fa-search-minus\"><\/i><\/button>\r\n                    <input type=\"range\" id=\"zoomSlider\" min=\"0.5\" max=\"3\" step=\"0.1\" value=\"1\">\r\n                    <button class=\"zoom-btn\" id=\"zoomIn\"><i class=\"fas fa-search-plus\"><\/i><\/button>\r\n                    <span id=\"zoomPercent\">100%<\/span>\r\n                <\/div>\r\n            <\/div>\r\n            <div class=\"image-container\" id=\"imageContainer\">\r\n                <canvas id=\"imageCanvas\" width=\"900\" height=\"550\"><\/canvas>\r\n            <\/div>\r\n            <div class=\"hint\" id=\"contextHint\">\r\n                <i class=\"fas fa-hand-pointer\"><\/i> <span id=\"hintText\" data-i18n=\"hintDefault\">Klik dua titik untuk mengukur PANJANG (skala aktif)<\/span>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Kolom KANAN: Side Panel -->\r\n        <div class=\"side-panel\">\r\n            <div class=\"section-title\"><i class=\"fas fa-clipboard-list\"><\/i> <span data-i18n=\"dataTitle\">Data Ukur<\/span><\/div>\r\n            \r\n            <!-- Daftar pengukuran -->\r\n            <div id=\"measureList\" class=\"list-container\">\r\n                <div style=\"text-align:center; padding:20px; color:#8a9dc2;\" data-i18n=\"noMeasurement\">Belum ada pengukuran<\/div>\r\n            <\/div>\r\n\r\n            <!-- Ringkasan Panjang & Luas -->\r\n            <div class=\"summary\" id=\"summaryPanel\" style=\"display: none;\">\r\n                <div class=\"stat-row\"><span class=\"stat-label\"><i class=\"fas fa-arrows-left-right\"><\/i> <span data-i18n=\"totalLength\">Total panjang<\/span><\/span> <span class=\"stat-number\" id=\"totalLengthVal\">0.00<\/span><\/div>\r\n                <div class=\"stat-row\"><span class=\"stat-label\"><i class=\"fas fa-chart-area\"><\/i> <span data-i18n=\"totalArea\">Total luas<\/span><\/span> <span class=\"stat-number\" id=\"totalAreaVal\">0.00<\/span><\/div>\r\n                <div style=\"font-size:0.9rem; margin-top:8px;\" id=\"unitLabel\" data-i18n=\"unitLabel\">satuan: cm<\/div>\r\n            <\/div>\r\n\r\n            <!-- Tools -->\r\n            <div class=\"area-tools\">\r\n                <button class=\"reset-small\" id=\"undoBtn\"><i class=\"fas fa-undo\"><\/i> <span data-i18n=\"undoBtn\">Undo<\/span><\/button>\r\n                <button class=\"reset-small\" id=\"clearAllBtn\"><i class=\"fas fa-eraser\"><\/i> <span data-i18n=\"clearAllBtn\">Hapus Semua<\/span><\/button>\r\n                <button class=\"reset-small\" id=\"resetCanvasBtn\"><i class=\"fas fa-undo-alt\"><\/i> <span data-i18n=\"resetBtn\">Reset Foto<\/span><\/button>\r\n            <\/div>\r\n\r\n            <!-- Tombol Ekspor -->\r\n            <div class=\"export-group\">\r\n                <button class=\"btn-export\" id=\"exportExcelBtn\" disabled><i class=\"fas fa-file-excel\"><\/i> <span data-i18n=\"exportExcel\">Ekspor ke Excel (XLSX)<\/span><\/button>\r\n                <button class=\"btn-export\" id=\"exportTxt\" disabled><i class=\"fas fa-file-lines\"><\/i> <span data-i18n=\"exportTxt\">Ekspor Laporan TXT<\/span><\/button>\r\n            <\/div>\r\n            <p style=\"font-size:0.8rem; color:#4b6589;\"><i class=\"fas fa-info-circle\"><\/i> <span data-i18n=\"exportNote\">Semua data foto dalam 1 file Excel<\/span><\/p>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Modal Dialog untuk Simpan Panjang (dengan input nama) -->\r\n    <div id=\"saveLengthModal\" class=\"modal\">\r\n        <div class=\"modal-content\">\r\n            <h3><i class=\"fas fa-save\"><\/i> <span data-i18n=\"saveLengthTitle\">Simpan Pengukuran Panjang<\/span><\/h3>\r\n            <p><span data-i18n=\"lengthValueLabel\">Panjang:<\/span> <span id=\"modalLengthValue\"><\/span><\/p>\r\n            <input type=\"text\" id=\"lengthNameInput\" data-i18n-placeholder=\"lengthNamePlaceholder\" placeholder=\"Nama pengukuran (contoh: Garis Pantai)\" value=\"Garis \">\r\n            <div class=\"modal-btns\">\r\n                <button class=\"btn-primary\" id=\"confirmSaveLength\"><span data-i18n=\"saveBtn\">Simpan<\/span><\/button>\r\n                <button class=\"btn-outline\" id=\"cancelSaveLength\"><span data-i18n=\"cancelBtn\">Batal<\/span><\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <!-- Modal Dialog untuk Simpan Area -->\r\n    <div id=\"saveAreaModal\" class=\"modal\">\r\n        <div class=\"modal-content\">\r\n            <h3><i class=\"fas fa-save\"><\/i> <span data-i18n=\"saveAreaTitle\">Simpan Area<\/span><\/h3>\r\n            <p><span data-i18n=\"areaValueLabel\">Luas area:<\/span> <span id=\"modalAreaValue\"><\/span><\/p>\r\n            <input type=\"text\" id=\"areaNameInput\" data-i18n-placeholder=\"areaNamePlaceholder\" placeholder=\"Nama area (contoh: Area Utara)\" value=\"Area \">\r\n            <select id=\"areaGroupSelect\">\r\n                <option value=\"Karang\" data-i18n=\"groupCoral\">Karang<\/option>\r\n                <option value=\"Pasir\" data-i18n=\"groupSand\">Pasir<\/option>\r\n                <option value=\"Lamun\" data-i18n=\"groupSeagrass\">Lamun<\/option>\r\n                <option value=\"Lainnya\" data-i18n=\"groupOther\">Lainnya<\/option>\r\n            <\/select>\r\n            <div class=\"modal-btns\">\r\n                <button class=\"btn-primary\" id=\"confirmSaveArea\"><span data-i18n=\"saveBtn\">Simpan<\/span><\/button>\r\n                <button class=\"btn-outline\" id=\"cancelSaveArea\"><span data-i18n=\"cancelBtn\">Batal<\/span><\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"footer\" data-i18n=\"footer\">\r\n        \u26a1 Klik 2 titik untuk panjang \u00b7 Klik minimal 3 titik (kembali ke awal) untuk luas \u00b7 Skala diatur dari objek referensi.\r\n    <\/div>\r\n<\/div>\r\n\r\n<script>\r\n(function() {\r\n    \/\/ ----- ELEMEN DOM -----\r\n    const canvas = document.getElementById('imageCanvas');\r\n    const ctx = canvas.getContext('2d');\r\n    const imageContainer = document.getElementById('imageContainer');\r\n    const fileInput = document.getElementById('fileInput');\r\n    const uploadBtn = document.getElementById('uploadBtn');\r\n    const fileInfo = document.getElementById('file-info');\r\n    const knownLengthInput = document.getElementById('knownLength');\r\n    const unitSelect = document.getElementById('unitSelect');\r\n    const applyScaleBtn = document.getElementById('applyScaleBtn');\r\n    const modeLengthBtn = document.getElementById('modeLength');\r\n    const modeAreaBtn = document.getElementById('modeArea');\r\n    const hintText = document.getElementById('hintText');\r\n    const measureListDiv = document.getElementById('measureList');\r\n    const summaryPanel = document.getElementById('summaryPanel');\r\n    const totalLengthSpan = document.getElementById('totalLengthVal');\r\n    const totalAreaSpan = document.getElementById('totalAreaVal');\r\n    const unitLabel = document.getElementById('unitLabel');\r\n    const clearAllBtn = document.getElementById('clearAllBtn');\r\n    const resetCanvasBtn = document.getElementById('resetCanvasBtn');\r\n    const undoBtn = document.getElementById('undoBtn');\r\n    const exportExcelBtn = document.getElementById('exportExcelBtn');\r\n    const exportTxt = document.getElementById('exportTxt');\r\n\r\n    \/\/ Zoom elements\r\n    const zoomSlider = document.getElementById('zoomSlider');\r\n    const zoomIn = document.getElementById('zoomIn');\r\n    const zoomOut = document.getElementById('zoomOut');\r\n    const zoomPercent = document.getElementById('zoomPercent');\r\n\r\n    \/\/ Multiple photo elements\r\n    const photoSelector = document.getElementById('photoSelector');\r\n    const prevPhotoBtn = document.getElementById('prevPhotoBtn');\r\n    const nextPhotoBtn = document.getElementById('nextPhotoBtn');\r\n    const photoCounter = document.getElementById('photoCounter');\r\n\r\n    \/\/ Elemen informasi project\r\n    const projectNameInput = document.getElementById('projectName');\r\n    const stationNameInput = document.getElementById('stationName');\r\n    const objectNameInput = document.getElementById('objectName');\r\n    const depthInput = document.getElementById('depth');\r\n    const measurementTimeInput = document.getElementById('measurementTime');\r\n\r\n    \/\/ Elemen peta\r\n    const latInput = document.getElementById('latInput');\r\n    const lngInput = document.getElementById('lngInput');\r\n    const applyLocationBtn = document.getElementById('applyLocationBtn');\r\n    const locationDisplay = document.getElementById('locationDisplay');\r\n    \r\n    \/\/ Modal Panjang (dengan input nama)\r\n    const lengthModal = document.getElementById('saveLengthModal');\r\n    const modalLengthValue = document.getElementById('modalLengthValue');\r\n    const lengthNameInput = document.getElementById('lengthNameInput');\r\n    const confirmLengthBtn = document.getElementById('confirmSaveLength');\r\n    const cancelLengthBtn = document.getElementById('cancelSaveLength');\r\n\r\n    \/\/ Modal Area\r\n    const areaModal = document.getElementById('saveAreaModal');\r\n    const modalAreaValue = document.getElementById('modalAreaValue');\r\n    const areaNameInput = document.getElementById('areaNameInput');\r\n    const areaGroupSelect = document.getElementById('areaGroupSelect');\r\n    const confirmAreaBtn = document.getElementById('confirmSaveArea');\r\n    const cancelAreaBtn = document.getElementById('cancelSaveArea');\r\n\r\n    \/\/ Tombol simpan\/muat proyek\r\n    const saveProjectBtn = document.getElementById('saveProjectBtn');\r\n    const loadProjectBtn = document.getElementById('loadProjectBtn');\r\n    const loadProjectFile = document.getElementById('loadProjectFile');\r\n\r\n    \/\/ ----- STATE VARIABLES (sama persis seperti asli) -----\r\n    let photos = [];\r\n    let currentPhotoIndex = -1;\r\n    let img = new Image();\r\n    let imgLoaded = false;\r\n    let imgNaturalW = 0, imgNaturalH = 0;\r\n    let displayW = 0, displayH = 0;\r\n    let zoomLevel = 1.0;\r\n    let scaleFactor = 1.0;\r\n    let currentUnit = 'cm';\r\n    let lengthMeasures = [];\r\n    let areaPolygons = [];\r\n    let historyStack = [];\r\n    let currentMode = 'length';\r\n    let tempPoints = [];\r\n    let tempLengthStart = null;\r\n    let mousePos = { x: 0, y: 0 };\r\n    let calibrationDone = false;\r\n    let pendingCalibrationPoints = [];\r\n    let currentLocation = {\r\n        lat: -6.2088,\r\n        lng: 106.8456,\r\n        name: 'Jakarta (default)'\r\n    };\r\n    let pendingLengthData = null;\r\n    let pendingAreaPoints = null;\r\n    let pendingAreaPx = 0;\r\n\r\n    class PhotoData {\r\n        constructor(file, imageElement, width, height) {\r\n            this.file = file; \/\/ bisa berupa object {name: ...}\r\n            this.image = imageElement;\r\n            this.imgNaturalW = width;\r\n            this.imgNaturalH = height;\r\n            this.displayW = 0;\r\n            this.displayH = 0;\r\n            this.scaleFactor = 1.0;\r\n            this.currentUnit = 'cm';\r\n            this.lengthMeasures = [];\r\n            this.areaPolygons = [];\r\n            this.calibrationDone = false;\r\n            this.pendingCalibrationPoints = [];\r\n            this.tempPoints = [];\r\n            this.tempLengthStart = null;\r\n            this.historyStack = [];\r\n        }\r\n    }\r\n\r\n    \/\/ Inisialisasi Peta Leaflet dengan peta satelit (Esri World Imagery)\r\n    let map = L.map('map').setView([currentLocation.lat, currentLocation.lng], 13);\r\n    L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/World_Imagery\/MapServer\/tile\/{z}\/{y}\/{x}', {\r\n        attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'\r\n    }).addTo(map);\r\n    let marker = L.marker([currentLocation.lat, currentLocation.lng]).addTo(map);\r\n    \r\n    map.on('click', function(e) {\r\n        let { lat, lng } = e.latlng;\r\n        latInput.value = lat.toFixed(6);\r\n        lngInput.value = lng.toFixed(6);\r\n        marker.setLatLng([lat, lng]);\r\n        currentLocation = { lat, lng, name: `(${lat.toFixed(4)}, ${lng.toFixed(4)})` };\r\n        locationDisplay.innerText = currentLocation.name;\r\n    });\r\n\r\n    applyLocationBtn.addEventListener('click', function() {\r\n        let lat = parseFloat(latInput.value);\r\n        let lng = parseFloat(lngInput.value);\r\n        if (isNaN(lat) || isNaN(lng)) return;\r\n        map.setView([lat, lng], 15);\r\n        marker.setLatLng([lat, lng]);\r\n        currentLocation = { lat, lng, name: `(${lat.toFixed(4)}, ${lng.toFixed(4)})` };\r\n        locationDisplay.innerText = currentLocation.name;\r\n    });\r\n\r\n    \/\/ ----- ZOOM FUNCTIONS -----\r\n    function applyZoom() {\r\n        canvas.style.transform = `scale(${zoomLevel})`;\r\n        imageContainer.style.height = (displayH * zoomLevel) + 'px';\r\n    }\r\n\r\n    zoomSlider.addEventListener('input', function() {\r\n        zoomLevel = parseFloat(this.value);\r\n        zoomPercent.innerText = Math.round(zoomLevel * 100) + '%';\r\n        applyZoom();\r\n    });\r\n\r\n    zoomIn.addEventListener('click', function() {\r\n        zoomLevel = Math.min(3, zoomLevel + 0.1);\r\n        zoomSlider.value = zoomLevel;\r\n        zoomPercent.innerText = Math.round(zoomLevel * 100) + '%';\r\n        applyZoom();\r\n    });\r\n\r\n    zoomOut.addEventListener('click', function() {\r\n        zoomLevel = Math.max(0.5, zoomLevel - 0.1);\r\n        zoomSlider.value = zoomLevel;\r\n        zoomPercent.innerText = Math.round(zoomLevel * 100) + '%';\r\n        applyZoom();\r\n    });\r\n\r\n    \/\/ ----- FUNGSI UNTUK BERGANTI FOTO -----\r\n    function saveCurrentPhotoState() {\r\n        if (currentPhotoIndex < 0 || currentPhotoIndex >= photos.length) return;\r\n        let photo = photos[currentPhotoIndex];\r\n        photo.displayW = displayW;\r\n        photo.displayH = displayH;\r\n        photo.scaleFactor = scaleFactor;\r\n        photo.currentUnit = currentUnit;\r\n        photo.lengthMeasures = JSON.parse(JSON.stringify(lengthMeasures));\r\n        photo.areaPolygons = JSON.parse(JSON.stringify(areaPolygons));\r\n        photo.calibrationDone = calibrationDone;\r\n        photo.pendingCalibrationPoints = [...pendingCalibrationPoints];\r\n        photo.tempPoints = [...tempPoints];\r\n        photo.tempLengthStart = tempLengthStart ? {...tempLengthStart} : null;\r\n        photo.historyStack = historyStack.map(item => JSON.parse(JSON.stringify(item)));\r\n    }\r\n\r\n    function loadPhotoState(photo) {\r\n        img = photo.image;\r\n        displayW = photo.displayW;\r\n        displayH = photo.displayH;\r\n        canvas.width = displayW;\r\n        canvas.height = displayH;\r\n        scaleFactor = photo.scaleFactor;\r\n        currentUnit = photo.currentUnit;\r\n        lengthMeasures = JSON.parse(JSON.stringify(photo.lengthMeasures));\r\n        areaPolygons = JSON.parse(JSON.stringify(photo.areaPolygons));\r\n        calibrationDone = photo.calibrationDone;\r\n        pendingCalibrationPoints = [...photo.pendingCalibrationPoints];\r\n        tempPoints = [...photo.tempPoints];\r\n        tempLengthStart = photo.tempLengthStart ? {...photo.tempLengthStart} : null;\r\n        historyStack = photo.historyStack.map(item => JSON.parse(JSON.stringify(item)));\r\n        imgLoaded = true;\r\n        unitSelect.value = currentUnit;\r\n        updateListAndSummary();\r\n        redrawCanvas();\r\n        if (calibrationDone) {\r\n            hintText.innerText = `Skala aktif: 1 px = ${scaleFactor.toFixed(4)} ${currentUnit}. Klik untuk mengukur.`;\r\n            applyScaleBtn.disabled = false;\r\n        } else {\r\n            hintText.innerText = 'Klik 2 titik pada objek yang diketahui ukurannya untuk kalibrasi skala.';\r\n            applyScaleBtn.disabled = false;\r\n        }\r\n        fileInfo.innerText = `\ud83d\udcf7 ${photo.file.name} (${photo.imgNaturalW}x${photo.imgNaturalH})`;\r\n        disableInteractions(false);\r\n    }\r\n\r\n    function switchToPhoto(index) {\r\n        if (index < 0 || index >= photos.length) return;\r\n        saveCurrentPhotoState();\r\n        currentPhotoIndex = index;\r\n        loadPhotoState(photos[currentPhotoIndex]);\r\n        updatePhotoCounter();\r\n        updateNavButtons();\r\n    }\r\n\r\n    function updatePhotoCounter() {\r\n        if (photos.length > 0) {\r\n            photoCounter.innerText = `${currentPhotoIndex + 1}\/${photos.length}`;\r\n        } else {\r\n            photoCounter.innerText = '0\/0';\r\n        }\r\n    }\r\n\r\n    function updateNavButtons() {\r\n        prevPhotoBtn.disabled = currentPhotoIndex <= 0;\r\n        nextPhotoBtn.disabled = currentPhotoIndex >= photos.length - 1;\r\n    }\r\n\r\n    \/\/ ----- FUNGSI LOAD MULTIPLE FOTO -----\r\n    function loadImages(fileList) {\r\n        if (!fileList || fileList.length === 0) return;\r\n        let filesToProcess = Array.from(fileList);\r\n        let loadedCount = 0;\r\n        filesToProcess.forEach((file) => {\r\n            if (!file.type.startsWith('image\/')) return;\r\n            const reader = new FileReader();\r\n            reader.onload = (ev) => {\r\n                const imgElement = new Image();\r\n                imgElement.onload = () => {\r\n                    let photo = new PhotoData(file, imgElement, imgElement.width, imgElement.height);\r\n                    let newW = imgElement.width, newH = imgElement.height;\r\n                    const maxW = 900, maxH = 550;\r\n                    if (newW > maxW) { newH = (maxW \/ newW) * newH; newW = maxW; }\r\n                    if (newH > maxH) { newW = (maxH \/ newH) * newW; newH = maxH; }\r\n                    photo.displayW = newW;\r\n                    photo.displayH = newH;\r\n                    photos.push(photo);\r\n                    loadedCount++;\r\n                    if (photos.length === 1) {\r\n                        currentPhotoIndex = 0;\r\n                        loadPhotoState(photos[0]);\r\n                        photoSelector.style.display = 'flex';\r\n                    }\r\n                    updatePhotoCounter();\r\n                    updateNavButtons();\r\n                };\r\n                imgElement.src = ev.target.result;\r\n            };\r\n            reader.readAsDataURL(file);\r\n        });\r\n    }\r\n\r\n    uploadBtn.addEventListener('click', () => fileInput.click());\r\n    \r\n    fileInput.addEventListener('change', (e) => {\r\n        if (e.target.files.length > 0) {\r\n            loadImages(e.target.files);\r\n        }\r\n        fileInput.value = '';\r\n    });\r\n\r\n    prevPhotoBtn.addEventListener('click', () => {\r\n        if (currentPhotoIndex > 0) {\r\n            switchToPhoto(currentPhotoIndex - 1);\r\n        }\r\n    });\r\n\r\n    nextPhotoBtn.addEventListener('click', () => {\r\n        if (currentPhotoIndex < photos.length - 1) {\r\n            switchToPhoto(currentPhotoIndex + 1);\r\n        }\r\n    });\r\n\r\n    \/\/ ----- INISIALISASI CANVAS -----\r\n    function drawPlaceholder() {\r\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n        ctx.fillStyle = '#f0f5fe';\r\n        ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n        ctx.font = '500 18px system-ui';\r\n        ctx.fillStyle = '#000000';\r\n        ctx.textAlign = 'center';\r\n        ctx.fillText('\ud83d\udcc1 Unggah foto untuk memulai', canvas.width\/2, canvas.height\/2 - 20);\r\n        ctx.font = '14px sans-serif';\r\n        ctx.fillText('Gambar JPG, PNG, WEBP', canvas.width\/2, canvas.height\/2 + 20);\r\n        imgLoaded = false;\r\n        disableInteractions(true);\r\n    }\r\n    drawPlaceholder();\r\n\r\n    function disableInteractions(disabled) {\r\n        applyScaleBtn.disabled = disabled;\r\n        exportExcelBtn.disabled = disabled;\r\n        exportTxt.disabled = disabled;\r\n        if (disabled) summaryPanel.style.display = 'none';\r\n    }\r\n\r\n    function saveState() {\r\n        historyStack.push({\r\n            lengthMeasures: JSON.parse(JSON.stringify(lengthMeasures)),\r\n            areaPolygons: JSON.parse(JSON.stringify(areaPolygons))\r\n        });\r\n        if (historyStack.length > 20) historyStack.shift();\r\n    }\r\n\r\n    function undo() {\r\n        if (historyStack.length > 0) {\r\n            let prev = historyStack.pop();\r\n            lengthMeasures = prev.lengthMeasures;\r\n            areaPolygons = prev.areaPolygons;\r\n            updateListAndSummary();\r\n            redrawCanvas();\r\n        } else {\r\n            alert('Tidak ada yang bisa di-undo');\r\n        }\r\n    }\r\n\r\n    \/\/ ----- FUNGSI GEOMETRI -----\r\n    function calculateArea(points) {\r\n        if (points.length < 3) return 0;\r\n        let sum = 0;\r\n        for (let i = 0; i < points.length; i++) {\r\n            let p1 = points[i];\r\n            let p2 = points[(i + 1) % points.length];\r\n            sum += (p1.x * p2.y - p2.x * p1.y);\r\n        }\r\n        return Math.abs(sum) \/ 2;\r\n    }\r\n\r\n    \/\/ ----- FUNGSI KONVERSI KOORDINAT DENGAN ZOOM -----\r\n    function getCanvasCoordinates(e) {\r\n        const rect = canvas.getBoundingClientRect();\r\n        const scaleX = canvas.width \/ rect.width;\r\n        const scaleY = canvas.height \/ rect.height;\r\n        let mouseX = (e.clientX - rect.left);\r\n        let mouseY = (e.clientY - rect.top);\r\n        let canvasX = mouseX * scaleX;\r\n        let canvasY = mouseY * scaleY;\r\n        canvasX = Math.min(Math.max(canvasX, 0), displayW);\r\n        canvasY = Math.min(Math.max(canvasY, 0), displayH);\r\n        return { x: canvasX, y: canvasY };\r\n    }\r\n\r\n    \/\/ ----- REDRAW CANVAS -----\r\n    function redrawCanvas() {\r\n        if (!imgLoaded || !displayW) return;\r\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n        ctx.drawImage(img, 0, 0, displayW, displayH);\r\n        ctx.lineWidth = 3;\r\n        ctx.strokeStyle = '#2563eb';\r\n        ctx.fillStyle = '#1e40af';\r\n        lengthMeasures.forEach(m => {\r\n            ctx.beginPath();\r\n            ctx.moveTo(m.x1, m.y1);\r\n            ctx.lineTo(m.x2, m.y2);\r\n            ctx.strokeStyle = '#2563eb';\r\n            ctx.stroke();\r\n            ctx.beginPath(); ctx.arc(m.x1, m.y1, 6, 0, 2*Math.PI); ctx.fill();\r\n            ctx.beginPath(); ctx.arc(m.x2, m.y2, 6, 0, 2*Math.PI); ctx.fill();\r\n            ctx.font = 'bold 13px monospace';\r\n            ctx.fillStyle = '#000000';\r\n            ctx.shadowColor = '#fff'; ctx.shadowBlur = 4;\r\n            ctx.fillText(`${m.name}: ${m.panjangUnit.toFixed(2)} ${currentUnit}`, (m.x1+m.x2)\/2 + 5, (m.y1+m.y2)\/2 - 8);\r\n            ctx.shadowBlur = 0;\r\n        });\r\n        areaPolygons.forEach(poly => {\r\n            if (poly.points.length < 2) return;\r\n            ctx.fillStyle = 'rgba(34, 197, 94, 0.2)';\r\n            ctx.strokeStyle = '#16a34a';\r\n            ctx.lineWidth = 2.5;\r\n            ctx.beginPath();\r\n            ctx.moveTo(poly.points[0].x, poly.points[0].y);\r\n            for (let i=1; i<poly.points.length; i++) ctx.lineTo(poly.points[i].x, poly.points[i].y);\r\n            if (poly.closed) {\r\n                ctx.closePath();\r\n                ctx.fill();\r\n            }\r\n            ctx.stroke();\r\n            ctx.fillStyle = '#14532d';\r\n            poly.points.forEach(p => { \r\n                ctx.beginPath(); \r\n                ctx.arc(p.x, p.y, 5, 0, 2*Math.PI); \r\n                ctx.fill(); \r\n            });\r\n            if (poly.closed) {\r\n                ctx.fillStyle = '#000000';\r\n                ctx.font = 'bold 13px monospace';\r\n                ctx.fillText(`${poly.name}: ${poly.areaUnit.toFixed(2)} ${currentUnit}\u00b2`, poly.points[0].x+10, poly.points[0].y-10);\r\n            }\r\n        });\r\n        if (currentMode === 'length' && tempLengthStart) {\r\n            ctx.beginPath();\r\n            ctx.moveTo(tempLengthStart.x, tempLengthStart.y);\r\n            ctx.lineTo(mousePos.x, mousePos.y);\r\n            ctx.strokeStyle = '#f97316';\r\n            ctx.lineWidth = 2.5;\r\n            ctx.setLineDash([7, 5]);\r\n            ctx.stroke();\r\n            ctx.setLineDash([]);\r\n            ctx.fillStyle = '#f97316';\r\n            ctx.beginPath(); ctx.arc(tempLengthStart.x, tempLengthStart.y, 7, 0, 2*Math.PI); ctx.fill();\r\n        }\r\n        if (currentMode === 'area' && tempPoints.length > 0) {\r\n            ctx.setLineDash([6, 4]);\r\n            ctx.strokeStyle = '#e07c2c';\r\n            ctx.lineWidth = 2.5;\r\n            ctx.beginPath();\r\n            ctx.moveTo(tempPoints[0].x, tempPoints[0].y);\r\n            for (let i=1; i<tempPoints.length; i++) ctx.lineTo(tempPoints[i].x, tempPoints[i].y);\r\n            ctx.lineTo(mousePos.x, mousePos.y);\r\n            ctx.stroke();\r\n            ctx.setLineDash([]);\r\n            ctx.fillStyle = '#b45309';\r\n            tempPoints.forEach(p => { ctx.beginPath(); ctx.arc(p.x, p.y, 6, 0, 2*Math.PI); ctx.fill(); });\r\n        }\r\n        if (pendingCalibrationPoints.length === 1) {\r\n            ctx.fillStyle = '#a855f7';\r\n            ctx.beginPath(); ctx.arc(pendingCalibrationPoints[0].x, pendingCalibrationPoints[0].y, 8, 0, 2*Math.PI); ctx.fill();\r\n        }\r\n    }\r\n\r\n    \/\/ ----- UPDATE LIST & SUMMARY -----\r\n    function updateListAndSummary() {\r\n        if (!imgLoaded) return;\r\n        let totalLength = lengthMeasures.reduce((acc, m) => acc + m.panjangUnit, 0);\r\n        let totalArea = areaPolygons.reduce((acc, p) => acc + p.areaUnit, 0);\r\n        let html = '';\r\n        if (lengthMeasures.length === 0 && areaPolygons.length === 0) {\r\n            html = '<div style=\"text-align:center; padding:20px; color:#8a9dc2;\">Belum ada pengukuran<\/div>';\r\n        } else {\r\n            lengthMeasures.forEach((m, idx) => {\r\n                html += `<div class=\"measure-item\"><span class=\"badge\">${m.name || 'L'+(idx+1)}<\/span> <span class=\"value\">${m.panjangUnit.toFixed(2)} ${currentUnit}<\/span> <button class=\"delete-btn\" data-type=\"length\" data-index=\"${idx}\"><i class=\"fas fa-trash\"><\/i><\/button><\/div>`;\r\n            });\r\n            areaPolygons.forEach((p, idx) => {\r\n                html += `<div class=\"measure-item\"><span class=\"badge\" style=\"background:#c7e6c7;\">${p.name || 'A'+(idx+1)}<\/span> <span class=\"value\">${p.areaUnit.toFixed(2)} ${currentUnit}\u00b2<\/span> <button class=\"delete-btn\" data-type=\"area\" data-index=\"${idx}\"><i class=\"fas fa-trash\"><\/i><\/button><\/div>`;\r\n            });\r\n        }\r\n        measureListDiv.innerHTML = html;\r\n        document.querySelectorAll('.delete-btn').forEach(btn => {\r\n            btn.addEventListener('click', (e) => {\r\n                e.stopPropagation();\r\n                saveState();\r\n                const type = btn.dataset.type;\r\n                const idx = btn.dataset.index;\r\n                if (type === 'length') lengthMeasures.splice(idx, 1);\r\n                else if (type === 'area') areaPolygons.splice(idx, 1);\r\n                updateListAndSummary();\r\n                redrawCanvas();\r\n            });\r\n        });\r\n        if (lengthMeasures.length || areaPolygons.length) {\r\n            summaryPanel.style.display = 'block';\r\n            totalLengthSpan.innerText = totalLength.toFixed(2) + ' ' + currentUnit;\r\n            totalAreaSpan.innerText = totalArea.toFixed(2) + ' ' + currentUnit + '\u00b2';\r\n            unitLabel.innerText = `satuan: ${currentUnit} (1 px = ${scaleFactor.toFixed(4)} ${currentUnit})`;\r\n        } else {\r\n            summaryPanel.style.display = 'none';\r\n        }\r\n    }\r\n\r\n    \/\/ ----- KALIBRASI SKALA -----\r\n    function tryApplyScale() {\r\n        if (pendingCalibrationPoints.length !== 2) {\r\n            alert('Pilih dua titik pada objek yang diketahui ukurannya');\r\n            return;\r\n        }\r\n        const p1 = pendingCalibrationPoints[0];\r\n        const p2 = pendingCalibrationPoints[1];\r\n        const pixelDist = Math.hypot(p2.x - p1.x, p2.y - p1.y);\r\n        if (pixelDist < 2) {\r\n            alert('Jarak terlalu pendek');\r\n            return;\r\n        }\r\n        const known = parseFloat(knownLengthInput.value);\r\n        if (isNaN(known) || known <= 0) return;\r\n        scaleFactor = known \/ pixelDist;\r\n        currentUnit = unitSelect.value;\r\n        lengthMeasures.forEach(m => { m.panjangUnit = m.panjangPixel * scaleFactor; });\r\n        areaPolygons.forEach(p => { p.areaUnit = calculateArea(p.points) * scaleFactor * scaleFactor; });\r\n        pendingCalibrationPoints = [];\r\n        calibrationDone = true;\r\n        applyScaleBtn.disabled = false;\r\n        updateListAndSummary();\r\n        redrawCanvas();\r\n        hintText.innerText = `Skala aktif: 1 px = ${scaleFactor.toFixed(4)} ${currentUnit}. Klik untuk mengukur.`;\r\n    }\r\n\r\n    \/\/ ----- EVENT CANVAS -----\r\n    function onCanvasClick(e) {\r\n        if (!imgLoaded) return;\r\n        const point = getCanvasCoordinates(e);\r\n        if (!calibrationDone && pendingCalibrationPoints.length < 2) {\r\n            pendingCalibrationPoints.push(point);\r\n            if (pendingCalibrationPoints.length === 2) {\r\n                hintText.innerText = 'Dua titik dipilih. Masukkan ukuran sebenarnya & klik \"Terapkan skala\".';\r\n            } else {\r\n                hintText.innerText = 'Titik pertama kalibrasi disimpan. Klik titik kedua objek.';\r\n            }\r\n            redrawCanvas();\r\n            return;\r\n        }\r\n        if (currentMode === 'length') {\r\n            if (!tempLengthStart) {\r\n                tempLengthStart = point;\r\n                hintText.innerText = 'Klik titik kedua untuk panjang';\r\n            } else {\r\n                const dx = point.x - tempLengthStart.x;\r\n                const dy = point.y - tempLengthStart.y;\r\n                const pixelDist = Math.hypot(dx, dy);\r\n                const unitDist = pixelDist * scaleFactor;\r\n                pendingLengthData = {\r\n                    x1: tempLengthStart.x, y1: tempLengthStart.y,\r\n                    x2: point.x, y2: point.y,\r\n                    panjangPixel: pixelDist,\r\n                    panjangUnit: unitDist\r\n                };\r\n                modalLengthValue.innerText = unitDist.toFixed(2) + ' ' + currentUnit;\r\n                lengthNameInput.value = `Garis ${lengthMeasures.length + 1}`;\r\n                lengthModal.style.display = 'flex';\r\n            }\r\n        } \r\n        else if (currentMode === 'area') {\r\n            if (tempPoints.length === 0) {\r\n                tempPoints.push(point);\r\n                hintText.innerText = 'Titik 1. Klik titik berikutnya. Klik titik pertama lagi untuk tutup polygon.';\r\n            } else {\r\n                const first = tempPoints[0];\r\n                const distToFirst = Math.hypot(point.x - first.x, point.y - first.y);\r\n                if (distToFirst < 12 && tempPoints.length >= 2) {\r\n                    const areaPx = calculateArea(tempPoints);\r\n                    const areaUnit = areaPx * scaleFactor * scaleFactor;\r\n                    pendingAreaPoints = [...tempPoints];\r\n                    pendingAreaPx = areaPx;\r\n                    modalAreaValue.innerText = areaUnit.toFixed(2) + ' ' + currentUnit + '\u00b2';\r\n                    areaNameInput.value = `Area ${areaPolygons.length + 1}`;\r\n                    areaModal.style.display = 'flex';\r\n                } else {\r\n                    tempPoints.push(point);\r\n                    hintText.innerText = `Titik ${tempPoints.length} tambah. Klik titik awal untuk tutup.`;\r\n                }\r\n            }\r\n            redrawCanvas();\r\n        }\r\n    }\r\n\r\n    function onMouseMove(e) {\r\n        if (!imgLoaded) return;\r\n        const point = getCanvasCoordinates(e);\r\n        mousePos.x = point.x;\r\n        mousePos.y = point.y;\r\n        redrawCanvas();\r\n    }\r\n\r\n    confirmLengthBtn.addEventListener('click', () => {\r\n        if (pendingLengthData) {\r\n            saveState();\r\n            lengthMeasures.push({\r\n                ...pendingLengthData,\r\n                name: lengthNameInput.value || `Garis ${lengthMeasures.length + 1}`,\r\n                createdAt: Date.now()\r\n            });\r\n            tempLengthStart = null;\r\n            pendingLengthData = null;\r\n            lengthModal.style.display = 'none';\r\n            updateListAndSummary();\r\n            redrawCanvas();\r\n            hintText.innerText = 'Panjang tersimpan. Klik dua titik lagi.';\r\n        }\r\n    });\r\n\r\n    cancelLengthBtn.addEventListener('click', () => {\r\n        tempLengthStart = null;\r\n        pendingLengthData = null;\r\n        lengthModal.style.display = 'none';\r\n        redrawCanvas();\r\n        hintText.innerText = 'Pengukuran dibatalkan.';\r\n    });\r\n\r\n    confirmAreaBtn.addEventListener('click', () => {\r\n        if (pendingAreaPoints) {\r\n            saveState();\r\n            const areaPx = calculateArea(pendingAreaPoints);\r\n            const areaUnit = areaPx * scaleFactor * scaleFactor;\r\n            areaPolygons.push({\r\n                points: pendingAreaPoints,\r\n                areaUnit: areaUnit,\r\n                closed: true,\r\n                name: areaNameInput.value || 'Area',\r\n                group: areaGroupSelect.value,\r\n                id: Date.now() + Math.random(),\r\n                createdAt: Date.now()\r\n            });\r\n            tempPoints = [];\r\n            pendingAreaPoints = null;\r\n            areaModal.style.display = 'none';\r\n            updateListAndSummary();\r\n            redrawCanvas();\r\n            hintText.innerText = 'Luas area tersimpan.';\r\n        }\r\n    });\r\n\r\n    cancelAreaBtn.addEventListener('click', () => {\r\n        tempPoints = [];\r\n        pendingAreaPoints = null;\r\n        areaModal.style.display = 'none';\r\n        redrawCanvas();\r\n        hintText.innerText = 'Penyimpanan dibatalkan.';\r\n    });\r\n\r\n    window.addEventListener('click', (e) => {\r\n        if (e.target === lengthModal) {\r\n            tempLengthStart = null;\r\n            pendingLengthData = null;\r\n            lengthModal.style.display = 'none';\r\n            redrawCanvas();\r\n        }\r\n        if (e.target === areaModal) {\r\n            tempPoints = [];\r\n            pendingAreaPoints = null;\r\n            areaModal.style.display = 'none';\r\n            redrawCanvas();\r\n        }\r\n    });\r\n\r\n    modeLengthBtn.addEventListener('click', () => {\r\n        currentMode = 'length';\r\n        modeLengthBtn.classList.add('active');\r\n        modeAreaBtn.classList.remove('active');\r\n        tempPoints = []; tempLengthStart = null;\r\n        hintText.innerText = calibrationDone ? 'Mode panjang: klik dua titik.' : 'Kalibrasi dulu dengan 2 titik + \"Terapkan skala\"';\r\n        redrawCanvas();\r\n    });\r\n    \r\n    modeAreaBtn.addEventListener('click', () => {\r\n        currentMode = 'area';\r\n        modeAreaBtn.classList.add('active');\r\n        modeLengthBtn.classList.remove('active');\r\n        tempPoints = []; tempLengthStart = null;\r\n        hintText.innerText = calibrationDone ? 'Mode luas: klik titik-titik polygon, tutup di titik awal.' : 'Kalibrasi dulu.';\r\n        redrawCanvas();\r\n    });\r\n\r\n    applyScaleBtn.addEventListener('click', tryApplyScale);\r\n    undoBtn.addEventListener('click', undo);\r\n\r\n    clearAllBtn.addEventListener('click', () => {\r\n        saveState();\r\n        lengthMeasures = [];\r\n        areaPolygons = [];\r\n        tempPoints = []; tempLengthStart = null;\r\n        updateListAndSummary();\r\n        redrawCanvas();\r\n    });\r\n    \r\n    resetCanvasBtn.addEventListener('click', () => {\r\n        if (imgLoaded) {\r\n            lengthMeasures = []; \r\n            areaPolygons = []; \r\n            tempPoints = []; \r\n            tempLengthStart = null; \r\n            pendingCalibrationPoints = [];\r\n            calibrationDone = false; \r\n            scaleFactor = 1.0;\r\n            historyStack = [];\r\n            updateListAndSummary();\r\n            redrawCanvas();\r\n            hintText.innerText = 'Kalibrasi: klik dua titik pada objek diketahui.';\r\n        }\r\n    });\r\n\r\n    \/\/ ----- EXPORT EXCEL (SEMUA DATA DALAM 1 SHEET BERURUTAN) -----\r\n    function exportToExcel() {\r\n        if (photos.length === 0) {\r\n            alert('Tidak ada foto untuk diekspor');\r\n            return;\r\n        }\r\n        saveCurrentPhotoState();\r\n        const wb = XLSX.utils.book_new();\r\n        const infoData = [\r\n            ['Parameter', 'Nilai'],\r\n            ['Nama Project', projectNameInput.value],\r\n            ['Stasiun', stationNameInput.value],\r\n            ['Nama Objek', objectNameInput.value],\r\n            ['Kedalaman (m)', depthInput.value],\r\n            ['Waktu Pengukuran', measurementTimeInput.value],\r\n            ['Latitude', currentLocation.lat],\r\n            ['Longitude', currentLocation.lng],\r\n            ['Lokasi', currentLocation.name],\r\n            ['Tanggal Ekspor', new Date().toLocaleString()],\r\n            ['Total Foto', photos.length]\r\n        ];\r\n        const wsInfo = XLSX.utils.aoa_to_sheet(infoData);\r\n        XLSX.utils.book_append_sheet(wb, wsInfo, 'Info Project');\r\n        let satuanPanjang = '';\r\n        let satuanLuas = '';\r\n        if (photos.length > 0 && photos[0].currentUnit) {\r\n            satuanPanjang = photos[0].currentUnit;\r\n            satuanLuas = photos[0].currentUnit + '\u00b2';\r\n        }\r\n        const mainSheetData = [\r\n            ['No', 'Nama File Foto', 'Nama', satuanPanjang, satuanLuas]\r\n        ];\r\n        let rowCounter = 1;\r\n        photos.forEach(photo => {\r\n            const fileName = photo.file.name;\r\n            photo.lengthMeasures.forEach(m => {\r\n                mainSheetData.push([\r\n                    rowCounter++,\r\n                    fileName,\r\n                    m.name || '-',\r\n                    m.panjangUnit.toFixed(4),\r\n                    ''\r\n                ]);\r\n            });\r\n            photo.areaPolygons.forEach(p => {\r\n                mainSheetData.push([\r\n                    rowCounter++,\r\n                    fileName,\r\n                    p.name || '-',\r\n                    '',\r\n                    p.areaUnit.toFixed(4)\r\n                ]);\r\n            });\r\n        });\r\n        if (mainSheetData.length === 1) {\r\n            mainSheetData.push(['-', '-', 'Belum ada pengukuran', '-', '-']);\r\n        }\r\n        const wsMain = XLSX.utils.aoa_to_sheet(mainSheetData);\r\n        XLSX.utils.book_append_sheet(wb, wsMain, 'Data Pengukuran');\r\n        const fileName = `pengukuran_${projectNameInput.value.replace(\/\\s\/g,'_')}_${new Date().toISOString().slice(0,10)}.xlsx`;\r\n        XLSX.writeFile(wb, fileName);\r\n    }\r\n    exportExcelBtn.addEventListener('click', exportToExcel);\r\n\r\n    \/\/ Ekspor TXT (tetap untuk foto aktif saja)\r\n    function exportTxtFile() {\r\n        if (!imgLoaded) return;\r\n        let content = '';\r\n        const totalLength = lengthMeasures.reduce((a,m)=>a+m.panjangUnit,0);\r\n        const totalArea = areaPolygons.reduce((a,p)=>a+p.areaUnit,0);\r\n        content = `LAPORAN PENGUKURAN FOTO\\n`;\r\n        content += `================================\\n`;\r\n        content += `Project: ${projectNameInput.value}\\n`;\r\n        content += `Stasiun: ${stationNameInput.value}\\n`;\r\n        content += `Nama Objek: ${objectNameInput.value}\\n`;\r\n        content += `Kedalaman: ${depthInput.value} m\\n`;\r\n        content += `Waktu Pengukuran: ${measurementTimeInput.value}\\n`;\r\n        content += `Lokasi: ${currentLocation.name} (${currentLocation.lat}, ${currentLocation.lng})\\n`;\r\n        content += `Nama File: ${photos[currentPhotoIndex]?.file.name || 'Unknown'}\\n`;\r\n        content += `Skala: 1 px = ${scaleFactor.toFixed(6)} ${currentUnit}\\n`;\r\n        content += `Jumlah ukuran panjang: ${lengthMeasures.length}\\n`;\r\n        content += `Luas polygon: ${areaPolygons.length}\\n\\n`;\r\n        content += `--- DAFTAR PANJANG ---\\n`;\r\n        lengthMeasures.forEach((m,i)=> content += `${m.name || 'P'+(i+1)}: ${m.panjangUnit.toFixed(4)} ${currentUnit}\\n`);\r\n        content += `\\n--- DAFTAR LUAS ---\\n`;\r\n        areaPolygons.forEach((p,i)=> {\r\n            content += `${p.name || 'A'+(i+1)} (${p.group || '-'}): ${p.areaUnit.toFixed(4)} ${currentUnit}\u00b2\\n`;\r\n        });\r\n        content += `\\n--------------------------------\\n`;\r\n        content += `TOTAL PANJANG = ${totalLength.toFixed(4)} ${currentUnit}\\n`;\r\n        content += `TOTAL LUAS = ${totalArea.toFixed(4)} ${currentUnit}\u00b2`;\r\n        const blob = new Blob([content], {type: 'text\/plain'});\r\n        const link = document.createElement('a');\r\n        link.href = URL.createObjectURL(blob);\r\n        link.download = `ukur_${projectNameInput.value.replace(\/\\s\/g,'_')}_${new Date().toISOString().slice(0,10)}.txt`;\r\n        link.click();\r\n    }\r\n    exportTxt.addEventListener('click', exportTxtFile);\r\n\r\n    canvas.addEventListener('click', onCanvasClick);\r\n    canvas.addEventListener('mousemove', onMouseMove);\r\n    canvas.addEventListener('mouseleave', ()=> { if (imgLoaded) redrawCanvas(); });\r\n\r\n    \/\/ ========== FUNGSI SIMPAN & MUAT PROYEK ==========\r\n    function saveProject() {\r\n        \/\/ Simpan state foto saat ini\r\n        saveCurrentPhotoState();\r\n        \r\n        const projectInfo = {\r\n            projectName: projectNameInput.value,\r\n            stationName: stationNameInput.value,\r\n            objectName: objectNameInput.value,\r\n            depth: depthInput.value,\r\n            measurementTime: measurementTimeInput.value,\r\n            location: currentLocation\r\n        };\r\n        \r\n        const photosData = photos.map(photo => {\r\n            \/\/ Simpan dataURL gambar dan properti lainnya\r\n            return {\r\n                file: { name: photo.file.name },\r\n                dataURL: photo.image.src, \/\/ base64\r\n                imgNaturalW: photo.imgNaturalW,\r\n                imgNaturalH: photo.imgNaturalH,\r\n                displayW: photo.displayW,\r\n                displayH: photo.displayH,\r\n                scaleFactor: photo.scaleFactor,\r\n                currentUnit: photo.currentUnit,\r\n                lengthMeasures: photo.lengthMeasures,\r\n                areaPolygons: photo.areaPolygons,\r\n                calibrationDone: photo.calibrationDone,\r\n                pendingCalibrationPoints: photo.pendingCalibrationPoints,\r\n                tempPoints: photo.tempPoints,\r\n                tempLengthStart: photo.tempLengthStart,\r\n                historyStack: photo.historyStack\r\n            };\r\n        });\r\n        \r\n        const state = {\r\n            version: 1,\r\n            projectInfo,\r\n            photosData,\r\n            currentPhotoIndex,\r\n            currentMode,\r\n            zoomLevel: zoomLevel\r\n        };\r\n        \r\n        const json = JSON.stringify(state, null, 2);\r\n        const blob = new Blob([json], { type: 'application\/json' });\r\n        const url = URL.createObjectURL(blob);\r\n        const a = document.createElement('a');\r\n        a.href = url;\r\n        a.download = `proyek_${projectNameInput.value.replace(\/\\s\/g,'_')}_${new Date().toISOString().slice(0,10)}.json`;\r\n        a.click();\r\n        URL.revokeObjectURL(url);\r\n    }\r\n\r\n    async function loadProject(file) {\r\n        const reader = new FileReader();\r\n        reader.onload = async (e) => {\r\n            try {\r\n                const state = JSON.parse(e.target.result);\r\n                \r\n                \/\/ Restore project info\r\n                projectNameInput.value = state.projectInfo.projectName || '';\r\n                stationNameInput.value = state.projectInfo.stationName || '';\r\n                objectNameInput.value = state.projectInfo.objectName || '';\r\n                depthInput.value = state.projectInfo.depth || '';\r\n                measurementTimeInput.value = state.projectInfo.measurementTime || '';\r\n                currentLocation = state.projectInfo.location || { lat: -6.2088, lng: 106.8456, name: 'Jakarta' };\r\n                latInput.value = currentLocation.lat;\r\n                lngInput.value = currentLocation.lng;\r\n                locationDisplay.innerText = currentLocation.name;\r\n                marker.setLatLng([currentLocation.lat, currentLocation.lng]);\r\n                map.setView([currentLocation.lat, currentLocation.lng], 13);\r\n                \r\n                \/\/ Restore photos\r\n                photos = [];\r\n                for (let photoData of state.photosData) {\r\n                    const img = new Image();\r\n                    await new Promise((resolve, reject) => {\r\n                        img.onload = resolve;\r\n                        img.onerror = reject;\r\n                        img.src = photoData.dataURL;\r\n                    });\r\n                    \/\/ Buat objek file tiruan dengan nama\r\n                    const fakeFile = { name: photoData.file.name };\r\n                    const photo = new PhotoData(fakeFile, img, photoData.imgNaturalW, photoData.imgNaturalH);\r\n                    photo.displayW = photoData.displayW;\r\n                    photo.displayH = photoData.displayH;\r\n                    photo.scaleFactor = photoData.scaleFactor;\r\n                    photo.currentUnit = photoData.currentUnit;\r\n                    photo.lengthMeasures = photoData.lengthMeasures || [];\r\n                    photo.areaPolygons = photoData.areaPolygons || [];\r\n                    photo.calibrationDone = photoData.calibrationDone || false;\r\n                    photo.pendingCalibrationPoints = photoData.pendingCalibrationPoints || [];\r\n                    photo.tempPoints = photoData.tempPoints || [];\r\n                    photo.tempLengthStart = photoData.tempLengthStart || null;\r\n                    photo.historyStack = photoData.historyStack || [];\r\n                    photos.push(photo);\r\n                }\r\n                \r\n                \/\/ Restore index dan mode\r\n                currentPhotoIndex = state.currentPhotoIndex || 0;\r\n                currentMode = state.currentMode || 'length';\r\n                zoomLevel = state.zoomLevel || 1;\r\n                \r\n                \/\/ Update UI mode buttons\r\n                if (currentMode === 'length') {\r\n                    modeLengthBtn.classList.add('active');\r\n                    modeAreaBtn.classList.remove('active');\r\n                } else {\r\n                    modeAreaBtn.classList.add('active');\r\n                    modeLengthBtn.classList.remove('active');\r\n                }\r\n                \r\n                \/\/ Load foto aktif\r\n                if (photos.length > 0) {\r\n                    if (currentPhotoIndex >= photos.length) currentPhotoIndex = 0;\r\n                    loadPhotoState(photos[currentPhotoIndex]);\r\n                    photoSelector.style.display = 'flex';\r\n                    updatePhotoCounter();\r\n                    updateNavButtons();\r\n                } else {\r\n                    drawPlaceholder();\r\n                    photoSelector.style.display = 'none';\r\n                }\r\n                \r\n                \/\/ Terapkan zoom\r\n                zoomSlider.value = zoomLevel;\r\n                zoomPercent.innerText = Math.round(zoomLevel * 100) + '%';\r\n                applyZoom();\r\n                \r\n            } catch (err) {\r\n                alert('Gagal memuat proyek: ' + err);\r\n            }\r\n        };\r\n        reader.readAsText(file);\r\n    }\r\n\r\n    saveProjectBtn.addEventListener('click', saveProject);\r\n    loadProjectBtn.addEventListener('click', () => loadProjectFile.click());\r\n    loadProjectFile.addEventListener('change', (e) => {\r\n        if (e.target.files.length > 0) {\r\n            loadProject(e.target.files[0]);\r\n        }\r\n        loadProjectFile.value = ''; \/\/ reset\r\n    });\r\n\r\n    \/\/ ------------------- FITUR BAHASA BARU (tanpa mengubah fungsi asli) -------------------\r\n    const translations = {\r\n        id: {\r\n            appTitle: \"Skala & Luas Foto \u00b7 Marine Conservation Indonesia\",\r\n            projectNameLabel: \"Nama Project\",\r\n            projectNamePlaceholder: \"Nama Project\",\r\n            stationLabel: \"Stasiun\",\r\n            stationPlaceholder: \"Stasiun\",\r\n            objectNameLabel: \"Nama Objek\",\r\n            objectNamePlaceholder: \"Nama Objek\",\r\n            depthLabel: \"Kedalaman (m)\",\r\n            timeLabel: \"Waktu Pengukuran\",\r\n            locationHeader: \"Lokasi Pengukuran (klik peta untuk isi koordinat)\",\r\n            applyBtn: \"Terapkan\",\r\n            locationDisplayLabel: \"Lokasi:\",\r\n            uploadBtn: \"Upload Foto\",\r\n            noPhoto: \"Belum ada foto\",\r\n            calibrateLabel: \"Set skala via 2 titik diketahui\",\r\n            cm: \"cm\",\r\n            m: \"meter\",\r\n            inchi: \"inchi\",\r\n            applyScaleBtn: \"Terapkan skala\",\r\n            calibrateTip: \"klik 2 titik objek yg diketahui\",\r\n            modeLength: \"Panjang\",\r\n            modeArea: \"Luas\",\r\n            imageArea: \"Area Gambar\",\r\n            hintDefault: \"Klik dua titik untuk mengukur PANJANG (skala aktif)\",\r\n            dataTitle: \"Data Ukur\",\r\n            noMeasurement: \"Belum ada pengukuran\",\r\n            totalLength: \"Total panjang\",\r\n            totalArea: \"Total luas\",\r\n            unitLabel: \"satuan: cm\",\r\n            undoBtn: \"Undo\",\r\n            clearAllBtn: \"Hapus Semua\",\r\n            resetBtn: \"Reset Foto\",\r\n            exportExcel: \"Ekspor ke Excel (XLSX)\",\r\n            exportTxt: \"Ekspor Laporan TXT\",\r\n            exportNote: \"Semua data foto dalam 1 file Excel\",\r\n            saveLengthTitle: \"Simpan Pengukuran Panjang\",\r\n            lengthValueLabel: \"Panjang:\",\r\n            lengthNamePlaceholder: \"Nama pengukuran (contoh: Garis Pantai)\",\r\n            saveBtn: \"Simpan\",\r\n            cancelBtn: \"Batal\",\r\n            saveAreaTitle: \"Simpan Area\",\r\n            areaValueLabel: \"Luas area:\",\r\n            areaNamePlaceholder: \"Nama area (contoh: Area Utara)\",\r\n            groupCoral: \"Karang\",\r\n            groupSand: \"Pasir\",\r\n            groupSeagrass: \"Lamun\",\r\n            groupOther: \"Lainnya\",\r\n            footer: \"\u26a1 Klik 2 titik untuk panjang \u00b7 Klik minimal 3 titik (kembali ke awal) untuk luas \u00b7 Skala diatur dari objek referensi.\",\r\n            saveProject: \"Simpan Proyek\",\r\n            loadProject: \"Muat Proyek\"\r\n        },\r\n        en: {\r\n            appTitle: \"Scale & Area Measurement \u00b7 Marine Conservation Indonesia\",\r\n            projectNameLabel: \"Project Name\",\r\n            projectNamePlaceholder: \"Project Name\",\r\n            stationLabel: \"Station\",\r\n            stationPlaceholder: \"Station\",\r\n            objectNameLabel: \"Object Name\",\r\n            objectNamePlaceholder: \"Object Name\",\r\n            depthLabel: \"Depth (m)\",\r\n            timeLabel: \"Measurement Time\",\r\n            locationHeader: \"Measurement Location (click map to set coordinates)\",\r\n            applyBtn: \"Apply\",\r\n            locationDisplayLabel: \"Location:\",\r\n            uploadBtn: \"Upload Photo\",\r\n            noPhoto: \"No photo\",\r\n            calibrateLabel: \"Set scale via 2 known points\",\r\n            cm: \"cm\",\r\n            m: \"m\",\r\n            inchi: \"inch\",\r\n            applyScaleBtn: \"Apply scale\",\r\n            calibrateTip: \"click 2 points on known object\",\r\n            modeLength: \"Length\",\r\n            modeArea: \"Area\",\r\n            imageArea: \"Image Area\",\r\n            hintDefault: \"Click two points to measure LENGTH (scale active)\",\r\n            dataTitle: \"Measurement Data\",\r\n            noMeasurement: \"No measurements yet\",\r\n            totalLength: \"Total length\",\r\n            totalArea: \"Total area\",\r\n            unitLabel: \"unit: cm\",\r\n            undoBtn: \"Undo\",\r\n            clearAllBtn: \"Clear All\",\r\n            resetBtn: \"Reset Image\",\r\n            exportExcel: \"Export to Excel (XLSX)\",\r\n            exportTxt: \"Export TXT Report\",\r\n            exportNote: \"All photo data in one Excel file\",\r\n            saveLengthTitle: \"Save Length Measurement\",\r\n            lengthValueLabel: \"Length:\",\r\n            lengthNamePlaceholder: \"Measurement name (e.g., Coastline)\",\r\n            saveBtn: \"Save\",\r\n            cancelBtn: \"Cancel\",\r\n            saveAreaTitle: \"Save Area\",\r\n            areaValueLabel: \"Area:\",\r\n            areaNamePlaceholder: \"Area name (e.g., North Area)\",\r\n            groupCoral: \"Coral\",\r\n            groupSand: \"Sand\",\r\n            groupSeagrass: \"Seagrass\",\r\n            groupOther: \"Other\",\r\n            footer: \"\u26a1 Click 2 points for length \u00b7 Click at least 3 points (back to start) for area \u00b7 Scale set from reference object.\",\r\n            saveProject: \"Save Project\",\r\n            loadProject: \"Load Project\"\r\n        }\r\n    };\r\n\r\n    const languageSelect = document.getElementById('languageSelect');\r\n\r\n    function applyLanguage(lang) {\r\n        const trans = translations[lang];\r\n        document.querySelectorAll('[data-i18n]').forEach(el => {\r\n            const key = el.getAttribute('data-i18n');\r\n            if (trans[key]) {\r\n                if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {\r\n                    \/\/ placeholder diatur terpisah\r\n                } else {\r\n                    el.innerText = trans[key];\r\n                }\r\n            }\r\n        });\r\n        document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {\r\n            const key = el.getAttribute('data-i18n-placeholder');\r\n            if (trans[key]) {\r\n                el.placeholder = trans[key];\r\n            }\r\n        });\r\n        document.querySelectorAll('option[data-i18n]').forEach(el => {\r\n            const key = el.getAttribute('data-i18n');\r\n            if (trans[key]) {\r\n                el.innerText = trans[key];\r\n            }\r\n        });\r\n        localStorage.setItem('preferredLanguage', lang);\r\n    }\r\n\r\n    languageSelect.addEventListener('change', (e) => {\r\n        applyLanguage(e.target.value);\r\n    });\r\n\r\n    const savedLang = localStorage.getItem('preferredLanguage') || 'id';\r\n    languageSelect.value = savedLang;\r\n    applyLanguage(savedLang);\r\n})();\r\n<\/script>\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-199ad97 e-flex e-con-boxed e-con e-parent\" data-id=\"199ad97\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-61ce592 e-con-full e-flex e-con e-child\" data-id=\"61ce592\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-eb38ee2 elementor-widget elementor-widget-heading\" data-id=\"eb38ee2\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Tutorial<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-1fbc709 e-con-full e-flex e-con e-child\" data-id=\"1fbc709\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4dbe900 elementor-widget elementor-widget-video\" data-id=\"4dbe900\" data-element_type=\"widget\" data-settings=\"{&quot;youtube_url&quot;:&quot;https:\\\/\\\/youtu.be\\\/8Dchlrb5Bpo&quot;,&quot;cc_load_policy&quot;:&quot;yes&quot;,&quot;video_type&quot;:&quot;youtube&quot;,&quot;controls&quot;:&quot;yes&quot;}\" data-widget_type=\"video.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-wrapper elementor-open-inline\">\n\t\t\t<div class=\"elementor-video\"><\/div>\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-7a5b5a2 e-flex e-con-boxed e-con e-parent\" data-id=\"7a5b5a2\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d117da7 elementor-widget elementor-widget-text-editor\" data-id=\"d117da7\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<h1><strong>Penggunaan Aplikasi\u00a0<\/strong><\/h1><p>\ud83d\udcf1 <strong>Tampilan<\/strong><br \/>&#8211; **Desktop**: Grid 2 kolom (info + peta di atas, canvas + data di bawah)<br \/>&#8211; **Mobile**: Semua konten ditumpuk vertikal, tombol lebih besar<\/p><p>&#8212;<\/p><h4>\ud83d\ude80 7 Langkah Mudah<\/h4><p><strong>1. Isi Informasi Project<\/strong><br \/>Nama Project | Stasiun | Nama Objek | Kedalaman (m)<\/p><p><strong>2.<\/strong> <strong>Tandai Lokasi (Peta)<\/strong><br \/>&#8211; Klik peta atau geser marker atau isi manual Lat\/Lon \u2192 klik Terapkan<\/p><p><strong>3. Upload Foto<\/strong><br \/>&#8211; Klik Upload Foto \u2192 Pilih satu atau beberapa foto<br \/>&#8211; Jika &gt;1 foto: gunakan \u25c0 \u25b6 untuk navigasi antar foto<\/p><p><strong>4. Kalibrasi Skala (WAJIB!)<\/strong><br \/>&#8211; Masukkan ukuran sebenarnya (contoh: 10)<br \/>&#8211; Pilih satuan (cm\/m\/inchi)<br \/>&#8211; Klik 2 titik pada objek referensi di foto<br \/>&#8211; Klik Terapkan skala<\/p><p><strong>5. Ukur Objek<\/strong><\/p><p>| Mode | Cara | Hasil |<br \/>|&#8212;&#8212;|&#8212;&#8212;|&#8212;&#8212;-|<br \/>| Panjang \ud83d\udccf | Klik 2 titik \u2192 isi nama \u2192 Simpan | Garis biru + label |<br \/>| Luas \ud83d\udd37 | Klik titik-titik polygon \u2192 klik titik awal \u2192 isi nama &amp; grup \u2192 Simpan | Area hijau + label |<\/p><p><strong>6. Kelola Data (Panel Kanan)<\/strong><br \/>&#8211; Lihat daftar semua ukuran<br \/>&#8211; Hapus satu ukuran (\ud83d\uddd1\ufe0f)<br \/>&#8211; Undo (\u21a9) \/ Hapus Semua \/ Reset Foto<\/p><p><strong>7. Ekspor Data<\/strong><\/p><p>| Tombol | Hasil |<br \/>|&#8212;&#8212;&#8211;|&#8212;&#8212;-|<br \/>| Ekspor Excel | 1 file dengan sheet per foto + ringkasan |<br \/>| Ekspor TXT | Laporan cepat untuk foto aktif |<\/p><p>\ud83c\udfaf<strong> Tips Penting<\/strong><br \/>&#8211; \u2705 Kalibrasi<strong> wajib<\/strong> sebelum mengukur<br \/>&#8211; \u2705 Setiap foto punya <strong>data sendiri<\/strong><br \/>&#8211; \u2705 Ekspor Excel menggabungkan <strong>semua foto<\/strong><br \/>&#8211; \u2705 Gunakan <strong>Undo<\/strong> jika salah klik<br \/>&#8211; \u2705 Beri <strong>nama deskriptif<\/strong> untuk memudahkan identifikasi<\/p><p>\u00a0<\/p><p>\u2753 <strong>Error<\/strong><br \/>| Masalah | Solusi |<br \/>| Kalibrasi gagal | Titik terlalu dekat |<br \/>| Area tidak bisa ditutup | Klik lebih dekat ke titik awal |<br \/>| Tombol export tidak aktif | Upload foto &amp; ukur dulu |<\/p><p>\u2705 <strong>Selesai!<\/strong> \ud83c\udf89<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Ukur Foto \u00b7 Skala, Panjang, Luas \u00b7 WordPress Skala &#038; Luas Foto \u00b7 Marine Conservation Indonesia \ud83c\uddee\ud83c\udde9 Indonesia\ud83c\uddec\ud83c\udde7 English Simpan [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-680","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v22.3 (Yoast SEO v24.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Analisis Photo Berskala - marineconservation.id<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/marineconservation.id\/id\/analisis-photo-berskala\/\" \/>\n<meta property=\"og:locale\" content=\"id_ID\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Analisis Photo Berskala\" \/>\n<meta property=\"og:description\" content=\"Ukur Foto \u00b7 Skala, Panjang, Luas \u00b7 WordPress Skala &#038; Luas Foto \u00b7 Marine Conservation Indonesia \ud83c\uddee\ud83c\udde9 Indonesia\ud83c\uddec\ud83c\udde7 English Simpan [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/marineconservation.id\/id\/analisis-photo-berskala\/\" \/>\n<meta property=\"og:site_name\" content=\"marineconservation.id\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61572311944591\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-11T21:18:23+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Estimasi waktu membaca\" \/>\n\t<meta name=\"twitter:data1\" content=\"4 menit\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/marineconservation.id\/analisis-photo-berskala\/\",\"url\":\"https:\/\/marineconservation.id\/analisis-photo-berskala\/\",\"name\":\"Analisis Photo Berskala - marineconservation.id\",\"isPartOf\":{\"@id\":\"https:\/\/marineconservation.id\/#website\"},\"datePublished\":\"2026-02-23T04:25:42+00:00\",\"dateModified\":\"2026-03-11T21:18:23+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/marineconservation.id\/analisis-photo-berskala\/#breadcrumb\"},\"inLanguage\":\"id\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/marineconservation.id\/analisis-photo-berskala\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/marineconservation.id\/analisis-photo-berskala\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/marineconservation.id\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Analisis Photo Berskala\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/marineconservation.id\/#website\",\"url\":\"https:\/\/marineconservation.id\/\",\"name\":\"marineconservation.id\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/marineconservation.id\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/marineconservation.id\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"id\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/marineconservation.id\/#organization\",\"name\":\"marineconservation.id\",\"url\":\"https:\/\/marineconservation.id\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"id\",\"@id\":\"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png\",\"contentUrl\":\"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png\",\"width\":500,\"height\":500,\"caption\":\"marineconservation.id\"},\"image\":{\"@id\":\"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/profile.php?id=61572311944591\",\"https:\/\/www.instagram.com\/marineconservation.id\/\",\"https:\/\/www.youtube.com\/watch?v=nOmjtk-dTg8&list=LL\"]}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Analisis Photo Berskala - marineconservation.id","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/marineconservation.id\/id\/analisis-photo-berskala\/","og_locale":"id_ID","og_type":"article","og_title":"Analisis Photo Berskala","og_description":"Ukur Foto \u00b7 Skala, Panjang, Luas \u00b7 WordPress Skala &#038; Luas Foto \u00b7 Marine Conservation Indonesia \ud83c\uddee\ud83c\udde9 Indonesia\ud83c\uddec\ud83c\udde7 English Simpan [&hellip;]","og_url":"https:\/\/marineconservation.id\/id\/analisis-photo-berskala\/","og_site_name":"marineconservation.id","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=61572311944591","article_modified_time":"2026-03-11T21:18:23+00:00","twitter_card":"summary_large_image","twitter_misc":{"Estimasi waktu membaca":"4 menit"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/marineconservation.id\/analisis-photo-berskala\/","url":"https:\/\/marineconservation.id\/analisis-photo-berskala\/","name":"Analisis Photo Berskala - marineconservation.id","isPartOf":{"@id":"https:\/\/marineconservation.id\/#website"},"datePublished":"2026-02-23T04:25:42+00:00","dateModified":"2026-03-11T21:18:23+00:00","breadcrumb":{"@id":"https:\/\/marineconservation.id\/analisis-photo-berskala\/#breadcrumb"},"inLanguage":"id","potentialAction":[{"@type":"ReadAction","target":["https:\/\/marineconservation.id\/analisis-photo-berskala\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/marineconservation.id\/analisis-photo-berskala\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/marineconservation.id\/"},{"@type":"ListItem","position":2,"name":"Analisis Photo Berskala"}]},{"@type":"WebSite","@id":"https:\/\/marineconservation.id\/#website","url":"https:\/\/marineconservation.id\/","name":"marineconservation.id","description":"","publisher":{"@id":"https:\/\/marineconservation.id\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/marineconservation.id\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"id"},{"@type":"Organization","@id":"https:\/\/marineconservation.id\/#organization","name":"marineconservation.id","url":"https:\/\/marineconservation.id\/","logo":{"@type":"ImageObject","inLanguage":"id","@id":"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/","url":"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png","contentUrl":"https:\/\/marineconservation.id\/wp-content\/uploads\/2025\/02\/Marine-Conservation-Indonesia.png","width":500,"height":500,"caption":"marineconservation.id"},"image":{"@id":"https:\/\/marineconservation.id\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61572311944591","https:\/\/www.instagram.com\/marineconservation.id\/","https:\/\/www.youtube.com\/watch?v=nOmjtk-dTg8&list=LL"]}]}},"_links":{"self":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/680","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/comments?post=680"}],"version-history":[{"count":94,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/680\/revisions"}],"predecessor-version":[{"id":1846,"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/pages\/680\/revisions\/1846"}],"wp:attachment":[{"href":"https:\/\/marineconservation.id\/id\/wp-json\/wp\/v2\/media?parent=680"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}