您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Carrega a camada JSON/GeoJSON do IBGE diretamente no WME.
当前为
// ==UserScript== // @name WME JSON IBGE Loader // @description Carrega a camada JSON/GeoJSON do IBGE diretamente no WME. // @namespace [email protected] // @version 1.2.0 // @author T0NINI // @match https://www.waze.com/*/editor* // @match https://www.waze.com/editor* // @match https://beta.waze.com/* // @exclude https://www.waze.com/*user/*editor/* // @icon https://www.google.com/s2/favicons?sz=64&domain=waze.com // @grant none // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js // ==/UserScript== (function() { 'use strict'; const SCRIPT_VERSION = "1.2.0"; let lineLayer = null; let clickControl = null; let selectedFeature = null; let isInitialized = false; function bootstrap() { if (typeof W === 'undefined' || typeof W.map === 'undefined' || typeof OpenLayers === 'undefined' || !WazeWrap.Ready) { setTimeout(bootstrap, 500); return; } initializeScript(); } function initializeScript() { if (isInitialized) return; isInitialized = true; console.log(`JSON IBGE Loader: Script inicializado com sucesso! (v${SCRIPT_VERSION})`); createUI(); createPopup(); initializeHotkeyControls(); } function createUI() { const tabTitle = 'JSON IBGE Loader'; const navTabs = document.querySelector('#user-info .nav-tabs'); const tabContent = document.querySelector('#user-info .tab-content'); if (!navTabs || !tabContent || document.getElementById('sidepanel-jsonloader')) return; const newTab = document.createElement('li'); newTab.innerHTML = `<a href="#sidepanel-jsonloader" data-toggle="tab">${tabTitle}</a>`; navTabs.appendChild(newTab); const newTabPanel = document.createElement('div'); newTabPanel.id = 'sidepanel-jsonloader'; newTabPanel.className = 'tab-pane'; newTabPanel.innerHTML = ` <style> #sidepanel-jsonloader .side-panel-section { padding: 15px 10px; } #sidepanel-jsonloader .side-panel-section + .side-panel-section { border-top: 1px solid #ddd; } #sidepanel-jsonloader h4 { font-size: 28px; font-weight: 500; margin-bottom: 3px; } #sidepanel-jsonloader h5 { margin-top: 0; margin-bottom: 15px; font-size: 13px; font-weight: 500; text-transform: uppercase; color: #555; } #sidepanel-jsonloader .w-100 { width: 100%; } #json-hotkey-input { cursor: pointer; } #sidepanel-jsonloader a:not(.btn) { text-decoration: none; } #sidepanel-jsonloader a:not(.btn):hover { text-decoration: underline; } #sidepanel-jsonloader .btn.btn-danger { border-radius: 50px; font-weight: 400; font-size: 12px; } /* Mantemos o flexbox para garantir o texto sempre centralizado */ #sidepanel-jsonloader .btn.btn-primary { display: flex; align-items: center; justify-content: center; background-color: #008CFF; border: none; border-radius: 50px; font-weight: 400; font-size: 12px; padding-top: 8px; padding-bottom: 8px; margin-bottom: 12px; transition: background-color 0.2s ease; text-decoration: none; } #sidepanel-jsonloader .btn.btn-primary:hover { background-color: #0073cc; text-decoration: none; } #sidepanel-jsonloader .btn.btn-primary:active { background-color: #005a9e; } </style> <div class="side-panel-section"><h4>JSON IBGE Loader</h4><p>Versão: <b>${SCRIPT_VERSION}</b></p></div> <div class="side-panel-section"> <h5>Carregar Arquivo GeoJSON</h5> <input type="file" id="json-file-input" accept=".json,.geojson" style="display:none;"/> <label for="json-file-input" class="btn btn-primary w-100" style="margin: 0; text-align: center; cursor: pointer;">Escolher Arquivo</label> <p style="text-align: center; margin-top: 8px; font-style: italic; color: #888;"> <span id="json-file-name">Nenhum arquivo selecionado</span> </p> <p id="json-status" style="text-align: center; font-weight: bold;"></p> <button id="json-clear-button" class="btn btn-danger w-100" disabled style="margin-top: 8px;">Limpar Camada</button> </div> <div class="side-panel-section"><h5>Ocultar Camada de Linhas (Atalho)</h5><input type="text" id="json-hotkey-input" class="form-control" placeholder="Definir novo atalho" readonly><p style="font-size: 11px; color: #888; margin-top: 10px;">Pressione a tecla de atalho para mostrar/ocultar a camada. O padrão é <b>TAB</b>. Clique na caixa acima para definir um novo atalho.</p></div> <div class="side-panel-section"><h5>Obter Dados</h5><a href="https://cidades.ibge.gov.br/" target="_blank" rel="noopener noreferrer" class="btn btn-primary w-100">Consultar Código do Município</a><a href="https://www.ibge.gov.br/geociencias/downloads-geociencias.html?caminho=recortes_para_fins_estatisticos/malha_de_setores_censitarios/censo_2022/base_de_faces_de_logradouros_versao_2022_censo_demografico/json" target="_blank" rel="noopener noreferrer" class="btn btn-primary w-100">Download JSON por UF</a></div>`; tabContent.appendChild(newTabPanel); document.getElementById('json-file-input').addEventListener('change', handleFileSelect); document.getElementById('json-clear-button').addEventListener('click', clearLayer); } function toggleLayerVisibility() { if (lineLayer) { lineLayer.setVisibility(!lineLayer.getVisibility()); } } function initializeHotkeyControls() { const hotkeyInput = document.getElementById('json-hotkey-input'); if(!hotkeyInput) return; const storageKey = 'wme_json_loader_hotkey'; const defaultHotkey = 'TAB'; hotkeyInput.value = localStorage.getItem(storageKey) || defaultHotkey; hotkeyInput.addEventListener('keydown', (event) => { event.preventDefault(); let hotkeyString = [ event.ctrlKey && 'Ctrl', event.altKey && 'Alt', event.shiftKey && 'Shift', !['Control', 'Alt', 'Shift'].includes(event.key) && event.key.toUpperCase() ].filter(Boolean).join(' + '); hotkeyInput.value = hotkeyString; localStorage.setItem(storageKey, hotkeyString); }); document.addEventListener('keydown', (event) => { const activeEl = document.activeElement; if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA')) return; const targetHotkey = localStorage.getItem(storageKey) || defaultHotkey; const currentPress = [ event.ctrlKey && 'Ctrl', event.altKey && 'Alt', event.shiftKey && 'Shift', !['Control', 'Alt', 'Shift'].includes(event.key) && event.key.toUpperCase() ].filter(Boolean).join(' + '); if (currentPress === targetHotkey) { event.preventDefault(); toggleLayerVisibility(); } }); } function createPopup() { if (document.getElementById('wme-json-popup')) return; const styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = ` #wme-json-popup { position: absolute; display: none; background: white; color: black; padding: 8px 12px; border-radius: 6px; border: 1px solid #ccc; z-index: 2001; font-family: sans-serif; font-size: 13px; font-weight: bold; pointer-events: none; transform: translate(-50%, -125%); white-space: nowrap; box-shadow: 0 3px 8px rgba(0,0,0,0.2); } #wme-json-popup::after, #wme-json-popup::before { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border-style: solid; border-color: transparent; border-top-color: #ccc; } #wme-json-popup::after { border-width: 8px; margin-top: -1px; border-top-color: white; } #wme-json-popup::before { border-width: 9px; }`; document.head.appendChild(styleSheet); const popup = document.createElement('div'); popup.id = 'wme-json-popup'; document.getElementById('WazeMap').appendChild(popup); } function onFeatureSelect(feature, lonlat) { const popup = document.getElementById('wme-json-popup'); const attr = feature.attributes; const nomeCompleto = [attr.NM_TIP_LOG, attr.NM_TIT_LOG, attr.NM_LOG].filter(Boolean).join(' ') || "Nome não disponível"; popup.innerText = nomeCompleto; const pixel = W.map.getPixelFromLonLat(lonlat); popup.style.left = `${pixel.x}px`; popup.style.top = `${pixel.y}px`; popup.style.display = 'block'; } function onFeatureUnselect() { const popup = document.getElementById('wme-json-popup'); if (popup) popup.style.display = 'none'; if (selectedFeature && lineLayer) { lineLayer.drawFeature(selectedFeature, 'default'); } selectedFeature = null; } function initializeCustomClickHandler() { const OL = OpenLayers; const CustomClick = OL.Class(OL.Control, { defaultHandlerOptions: { 'single': true, 'double': false, 'pixelTolerance': 2, 'stopSingle': false, 'stopDouble': false }, initialize: function(options) { this.handlerOptions = OL.Util.extend({}, this.defaultHandlerOptions); OL.Control.prototype.initialize.apply(this, arguments); this.handler = new OL.Handler.Click(this, { 'click': this.trigger }, this.handlerOptions); }, trigger: function(e) { if (!lineLayer) return; const lonlat = W.map.getLonLatFromPixel(e.xy); const clickPoint = new OL.Geometry.Point(lonlat.lon, lonlat.lat).transform(new OL.Projection("EPSG:4326"), W.map.getProjectionObject()); let closestFeature = null, minDistance = Infinity; for (const feature of lineLayer.features) { const distance = feature.geometry.distanceTo(clickPoint, {details: false}); if (distance < minDistance) { minDistance = distance; closestFeature = feature; } } if (closestFeature && minDistance < 5) { if (selectedFeature !== closestFeature) { onFeatureUnselect(); selectedFeature = closestFeature; lineLayer.drawFeature(closestFeature, 'select'); onFeatureSelect(closestFeature, lonlat); } } } }); clickControl = new CustomClick(); W.map.addControl(clickControl); clickControl.activate(); W.map.events.register('movestart', null, onFeatureUnselect); } function handleFileSelect(event) { const file = event.target.files[0]; if (!file) { clearLayer(); return; } document.getElementById('json-file-name').textContent = file.name; const statusEl = document.getElementById('json-status'); const reader = new FileReader(); reader.onload = function(e) { statusEl.textContent = 'Analisando arquivo...'; try { const geojson = JSON.parse(e.target.result); statusEl.textContent = `Arquivo válido. Desenhando...`; setTimeout(() => drawGeoJSON(geojson), 50); } catch (error) { alert('Erro: O arquivo não parece ser um JSON válido.'); console.error("Erro de Análise JSON:", error); clearLayer(); } }; reader.readAsText(file); } function drawGeoJSON(geojson) { clearMapData(); const OL = OpenLayers; const sourceProjection = new OL.Projection("EPSG:4326"); const mapProjection = W.map.getProjectionObject(); const lineStyleMap = new OL.StyleMap({ 'default': new OL.Style({ strokeColor: '#0078FF', strokeWidth: 2, strokeOpacity: 0.8 }), 'select': new OL.Style({ strokeColor: '#00FF00', strokeWidth: 4, strokeOpacity: 1.0 }) }); lineLayer = new OL.Layer.Vector("JSON Linhas", { styleMap: lineStyleMap }); let nomeCidade = null; const allFeatures = []; for (const feature of geojson.features) { if (feature.geometry && feature.geometry.type === 'LineString' && feature.geometry.coordinates.length > 1) { if (!nomeCidade && feature.properties && feature.properties.NM_MUN) { nomeCidade = feature.properties.NM_MUN; } const points = feature.geometry.coordinates.map(coord => new OL.Geometry.Point(coord[0], coord[1])); const line = new OL.Geometry.LineString(points); const vectorFeature = new OL.Feature.Vector(line, feature.properties); allFeatures.push(vectorFeature); } } allFeatures.forEach(f => f.geometry.transform(sourceProjection, mapProjection)); lineLayer.addFeatures(allFeatures); W.map.addLayer(lineLayer); initializeCustomClickHandler(); const statusMessage = nomeCidade ? `Camada para ${nomeCidade} carregada com ${allFeatures.length} linhas.` : `Camada desenhada com ${allFeatures.length} linhas.`; document.getElementById('json-status').textContent = statusMessage; document.getElementById('json-clear-button').disabled = false; } function clearMapData() { onFeatureUnselect(); if (clickControl) { W.map.events.unregister('movestart', null, onFeatureUnselect); clickControl.deactivate(); W.map.removeControl(clickControl); clickControl = null; } if (lineLayer) { W.map.removeLayer(lineLayer); lineLayer.destroy(); lineLayer = null; } selectedFeature = null; } function clearLayer() { clearMapData(); const status = document.getElementById('json-status'); if (status) status.textContent = ''; const fileInput = document.getElementById('json-file-input'); if (fileInput) fileInput.value = ''; const fileNameEl = document.getElementById('json-file-name'); if (fileNameEl) fileNameEl.textContent = 'Nenhum arquivo selecionado'; const clearButton = document.getElementById('json-clear-button'); if(clearButton) clearButton.disabled = true; } bootstrap(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址