WME RPP

Shows House Numbers on RPPs.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         WME RPP
// @namespace    https://greasyfork.org/users/velezss
// @version      1.0
// @description  Shows House Numbers on RPPs.
// @author       velezss
// @match        https://beta.waze.com/*editor*
// @match        https://www.waze.com/*editor*
// @exclude      https://www.waze.com/user/editor*
// @require      https://greasyfork.org/scripts/459348-wme-sdk/code/WME%20SDK.js
// @grant        none
// ==/UserScript==

/* global W, OpenLayers, getWmeSdk */

(function () {
  "use strict";

  const SCRIPT_ID = "wme-rpp-hn-sdk-en";
  const SCRIPT_NAME = "RPP Numbers";

  // --- SDK Initialization Helper ---
  function onceSdkReady(cb) {
    const tick = () => {
      if (window.getWmeSdk) {
          if (window.SDK_INITIALIZED) {
              window.SDK_INITIALIZED.then(cb).catch(console.error);
          } else {
              cb();
          }
      } else {
          setTimeout(tick, 250);
      }
    };
    tick();
  }

  // --- Main Logic ---
  onceSdkReady(async () => {
    const sdk = getWmeSdk({ scriptId: SCRIPT_ID, scriptName: SCRIPT_NAME });
    
    // 1. Register Tab
    const { tabLabel, tabPane } = await sdk.Sidebar.registerScriptTab();
    tabLabel.textContent = "RPP #"; 
    tabLabel.title = "RPP House Numbers";

    // 2. Create UI (English)
    const style = document.createElement("style");
    style.textContent = `
      #wrpp-root { padding: 12px; font-family: "Boing", "Open Sans", sans-serif; }
      #wrpp-root .row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
      #wrpp-root .status { font-size: 12px; color: #666; background: #f0f0f0; padding: 8px; border-radius: 6px; margin-top: 10px; }
      #wrpp-root .switch { position: relative; display: inline-block; width: 40px; height: 20px; }
      #wrpp-root .switch input { opacity: 0; width: 0; height: 0; }
      #wrpp-root .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 20px; }
      #wrpp-root .slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%; }
      input:checked + .slider { background-color: #2196F3; }
      input:checked + .slider:before { transform: translateX(20px); }
    `;
    tabPane.appendChild(style);

    const root = document.createElement("div");
    root.id = "wrpp-root";
    
    // UI Template: Checkbox is explicitly 'checked' to force ON state at startup
    root.innerHTML = `
      <div class="row">
        <span style="font-weight:bold; color:#333;">Show Numbers</span>
        <label class="switch">
          <input type="checkbox" id="wrpp-toggle" checked>
          <span class="slider"></span>
        </label>
      </div>
      <div id="wrpp-status" class="status">Initializing...</div>
      <div style="font-size:10px; color:#999; margin-top:15px; text-align:center;">Gemini SDK Edition</div>
    `;
    tabPane.appendChild(root);

    // 3. Map Layer Logic
    let labelLayer;

    function setupLayer() {
        try {
            const oldLayers = W.map.getLayersByName("RPP_HouseNumbers_SDK");
            oldLayers.forEach(l => W.map.removeLayer(l));
        } catch(e) {}

        labelLayer = new OpenLayers.Layer.Vector("RPP_HouseNumbers_SDK", {
            displayInLayerSwitcher: false,
            styleMap: new OpenLayers.StyleMap({
                "default": new OpenLayers.Style({
                    label: "${label}",
                    fontColor: "#ffffff",
                    fontWeight: "bold",
                    fontSize: "12px",
                    fontFamily: "Arial, sans-serif",
                    labelOutlineColor: "#000000",
                    labelOutlineWidth: 3,
                    labelYOffset: -20
                })
            })
        });
        W.map.addLayer(labelLayer);
    }

    function updateRPPs() {
        const toggle = document.getElementById('wrpp-toggle');
        const statusBox = document.getElementById('wrpp-status');
        
        // Safety check
        if (!toggle) return;

        // If user manually turns it off during session
        if (!toggle.checked) {
            if (labelLayer) labelLayer.removeAllFeatures();
            if (statusBox) statusBox.textContent = "Visuals disabled.";
            return;
        }

        if (!labelLayer) setupLayer();
        labelLayer.removeAllFeatures();

        // Object Fetching Strategy
        let objects = [];
        if (W.model.venues.getObjectArray) {
             objects = W.model.venues.getObjectArray();
        } else {
            for (let id in W.model.venues.objects) {
                objects.push(W.model.venues.objects[id]);
            }
        }

        const rpps = objects.filter(p => p.isResidential && p.isResidential());
        const features = [];
        let countConNumero = 0;

        rpps.forEach((rpp) => {
            let hn = rpp.attributes.houseNumber;
            // Handle empty or null numbers
            if (!hn || hn.trim() === "") {
                hn = "?";
            } else {
                countConNumero++;
            }

            const point = rpp.geometry.getCentroid();
            const feature = new OpenLayers.Feature.Vector(point, {
                label: hn.toString()
            });
            features.push(feature);
        });

        labelLayer.addFeatures(features);
        
        if (statusBox) {
            statusBox.innerHTML = `
                <b>${rpps.length}</b> visible RPPs.<br>
                <b>${countConNumero}</b> have numbers.
            `;
        }
    }

    // 4. Event Listeners
    const toggleBtn = document.getElementById('wrpp-toggle');
    toggleBtn.addEventListener('change', updateRPPs);

    // Initial Setup
    setupLayer();
    
    // Waze Events
    W.map.events.register("moveend", null, updateRPPs);
    W.map.events.register("zoomend", null, updateRPPs);
    W.model.events.register("mergeend", null, updateRPPs);

    // Trigger first run
    setTimeout(updateRPPs, 1000);
  });
})();