基于V6.7稳定拖动与独立搜索;增加餐厅/酒店/推荐活动/博物馆/公共交通/药店/ATM快捷搜索按钮;Google卫星WGS84 + 道路GCJ02自动纠偏
이 스크립트는 직접 설치하는 용도가 아닙니다. 다른 스크립트에서 메타 지시문 // @require https://update.greasyfork.org/scripts/593597/1916994/Google%E4%B8%AD%E5%9B%BD%E5%8D%AB%E6%98%9F%E9%81%93%E8%B7%AF%E7%BA%A0%E5%81%8F%20V69.js을(를) 사용하여 포함하는 라이브러리입니다.
// ==UserScript== // @name Google中国卫星道路纠偏 V6.9 // @namespace avhzy-google-map-fix // @version 6.9.0 // @description 基于V6.8稳定拖动/快捷按钮;修复地名搜索定位,使用Google Maps标准搜索入口并等待真实解析坐标后再同步纠偏地图 // @match https://www.google.com/maps/* // @match https://maps.google.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.js // @resource LEAFLET_CSS https://cdn.jsdelivr.net/npm/[email protected]/dist/leaflet.css // @grant GM_getResourceText // @grant GM_addStyle // @run-at document-end // ==/UserScript== (function () { 'use strict'; const PI = Math.PI; const A = 6378245.0; const EE = 0.00669342162296594323; const MAP_Z = 100000; const UI_Z = 2147483000; const STORAGE_KEY = 'avhzy-v69-last-view'; const PENDING_SEARCH_KEY = 'avhzy-v69-pending-search'; let autoFollowGoogle = true; let roadsVisible = true; let roadOpacity = 1.0; let isDraggingMap = false; let isZoomingMap = false; let suppressGoogleSyncUntil = 0; let lastGoogleURL = location.href; let lastGoogleLat = null; let lastGoogleLng = null; let zoomAnimationFrame = 0; let pendingZoomEvent = null; GM_addStyle( GM_getResourceText('LEAFLET_CSS') ); GM_addStyle(` #avhzyChinaMapV69 { position: fixed !important; inset: 0 !important; z-index: ${MAP_Z} !important; background: #000; overflow: hidden; pointer-events: auto !important; } #avhzyChinaMapV69 .leaflet-top.leaflet-left { top: 76px; left: 10px; } #avhzyV69SearchBar { position: fixed; left: 18px; top: 14px; z-index: ${UI_Z}; width: 398px; max-width: calc(100vw - 36px); height: 48px; display: flex; align-items: center; gap: 4px; padding: 0 7px 0 17px; border-radius: 24px; background: rgba(255,255,255,.98); box-shadow: 0 2px 8px rgba(0,0,0,.28); box-sizing: border-box; font: 14px Arial, sans-serif; } #avhzyV69SearchInput { flex: 1; min-width: 0; height: 40px; border: 0; outline: 0; background: transparent; color: #202124; font-size: 15px; padding: 0 3px; } #avhzyV69SearchInput::placeholder { color: #70757a; } #avhzyV69SearchBar button { flex: 0 0 auto; width: 38px; height: 38px; border: 0; border-radius: 50%; background: transparent; cursor: pointer; font-size: 18px; line-height: 38px; padding: 0; color: #3c4043; } #avhzyV69SearchBar button:hover { background: #f1f3f4; } #avhzyV69QuickBar { position: fixed; left: 432px; right: 18px; top: 14px; z-index: ${UI_Z}; height: 48px; display: flex; align-items: center; gap: 7px; overflow-x: auto; overflow-y: hidden; white-space: nowrap; scrollbar-width: none; pointer-events: auto; padding: 0 2px; box-sizing: border-box; } #avhzyV69QuickBar::-webkit-scrollbar { display: none; } #avhzyV69QuickBar button { flex: 0 0 auto; height: 38px; padding: 0 14px; border: 0; border-radius: 19px; background: rgba(255,255,255,.98); box-shadow: 0 1px 4px rgba(0,0,0,.24); color: #202124; cursor: pointer; font: 600 14px Arial, "Microsoft YaHei", sans-serif; display: inline-flex; align-items: center; gap: 7px; } #avhzyV69QuickBar button:hover { background: #f1f3f4; } .avhzy-v69-chip-icon { font-size: 16px; line-height: 1; } #avhzyV69SearchState { position: fixed; left: 29px; top: 68px; z-index: ${UI_Z}; display: none; max-width: 520px; padding: 7px 11px; border-radius: 6px; background: rgba(32,33,36,.9); color: #fff; font: 12px Arial, sans-serif; pointer-events: none; } #avhzyV69Panel { position: fixed; right: 14px; top: 86px; z-index: ${UI_Z}; width: 198px; padding: 10px 12px; border-radius: 9px; background: rgba(255,255,255,.96); box-shadow: 0 2px 12px rgba(0,0,0,.28); font: 12px Arial, sans-serif; color: #222; } #avhzyV69Panel button { width: 100%; min-height: 31px; margin-top: 7px; cursor: pointer; border: 1px solid #ccc; border-radius: 4px; background: #fff; } #avhzyV69Panel input[type="range"] { width: 100%; } #avhzyV69Reopen { position: fixed; right: 15px; bottom: 100px; z-index: ${UI_Z}; padding: 10px 14px; cursor: pointer; border-radius: 7px; border: 1px solid #bbb; background: white; box-shadow: 0 2px 10px rgba(0,0,0,.25); } @media (max-width: 1050px) { #avhzyV69SearchBar { width: 360px; } #avhzyV69QuickBar { left: 390px; } } @media (max-width: 760px) { #avhzyV69SearchBar { left: 10px; right: 10px; width: auto; max-width: none; } #avhzyV69QuickBar { left: 10px; right: 10px; top: 68px; } #avhzyChinaMapV69 .leaflet-top.leaflet-left { top: 126px; } #avhzyV69Panel { top: 128px; right: 8px; width: 178px; } #avhzyV69SearchState { top: 118px; } } `); /************************************************************ * GCJ-02 ************************************************************/ function outOfChina(lng, lat) { return ( lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271 ); } function transformLat(x, y) { let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt( Math.abs(x) ); ret += ( 20.0 * Math.sin( 6.0 * x * PI ) + 20.0 * Math.sin( 2.0 * x * PI ) ) * 2.0 / 3.0; ret += ( 20.0 * Math.sin( y * PI ) + 40.0 * Math.sin( y / 3.0 * PI ) ) * 2.0 / 3.0; ret += ( 160.0 * Math.sin( y / 12.0 * PI ) + 320.0 * Math.sin( y * PI / 30.0 ) ) * 2.0 / 3.0; return ret; } function transformLng(x, y) { let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt( Math.abs(x) ); ret += ( 20.0 * Math.sin( 6.0 * x * PI ) + 20.0 * Math.sin( 2.0 * x * PI ) ) * 2.0 / 3.0; ret += ( 20.0 * Math.sin( x * PI ) + 40.0 * Math.sin( x / 3.0 * PI ) ) * 2.0 / 3.0; ret += ( 150.0 * Math.sin( x / 12.0 * PI ) + 300.0 * Math.sin( x / 30.0 * PI ) ) * 2.0 / 3.0; return ret; } function wgsToGcj(lng, lat) { if ( outOfChina( lng, lat ) ) { return { lng, lat }; } let dLat = transformLat( lng - 105.0, lat - 35.0 ); let dLng = transformLng( lng - 105.0, lat - 35.0 ); const radLat = lat / 180.0 * PI; let magic = Math.sin( radLat ); magic = 1 - EE * magic * magic; const sqrtMagic = Math.sqrt( magic ); dLat = ( dLat * 180.0 ) / ( ( A * ( 1 - EE ) ) / ( magic * sqrtMagic ) * PI ); dLng = ( dLng * 180.0 ) / ( A / sqrtMagic * Math.cos( radLat ) * PI ); return { lng: lng + dLng, lat: lat + dLat }; } /************************************************************ * Google Maps URL ************************************************************/ function estimateZoomFromMeters( meters ) { if ( !Number.isFinite( meters ) || meters <= 0 ) { return 15; } return Math.max( 3, Math.min( 21, Math.round( 20 - Math.log2( Math.max( meters, 1 ) / 100 ) ) ) ); } function getGooglePosition() { const url = decodeURIComponent( location.href ); /* * @28.01,120.78,17z */ let match = url.match( /@(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(\d+(?:\.\d+)?)z/ ); if ( match ) { return { lat: Number( match[1] ), lng: Number( match[2] ), zoom: Number( match[3] ) }; } /* * @28.01,120.78,8029m */ match = url.match( /@(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(\d+(?:\.\d+)?)m/ ); if ( match ) { return { lat: Number( match[1] ), lng: Number( match[2] ), zoom: estimateZoomFromMeters( Number( match[3] ) ) }; } /* * 地点详情 */ match = url.match( /!3d(-?\d+(?:\.\d+)?).*?!4d(-?\d+(?:\.\d+)?)/ ); if ( match ) { return { lat: Number( match[1] ), lng: Number( match[2] ), zoom: 17 }; } return null; } function getQueryFromURL() { try { const url = new URL( location.href ); const query = url .searchParams .get( 'query' ); if ( query ) { return query; } const match = decodeURIComponent( url.pathname ) .match( /\/maps\/(?:search|place)\/([^/]+)/i ); if ( match && match[1] ) { return match[1] .replace( /\+/g, ' ' ); } } catch (_) { } return ''; } /************************************************************ * V6.9 搜索定位核心 ************************************************************/ function buildStandardSearchURL( query ) { return ( 'https://www.google.com/maps/search/?api=1&query=' + encodeURIComponent( String( query ).trim() ) ); } function getPendingSearch() { try { const raw = sessionStorage .getItem( PENDING_SEARCH_KEY ); if ( !raw ) { return null; } const value = JSON.parse( raw ); if ( !value || typeof value.query !== 'string' ) { return null; } return value; } catch (_) { return null; } } function setPendingSearch( query, center ) { const value = { query: String( query ).trim(), startLat: center.lat, startLng: center.lng, startedAt: Date.now() }; try { sessionStorage .setItem( PENDING_SEARCH_KEY, JSON.stringify( value ) ); } catch (_) { } return value; } function clearPendingSearch() { try { sessionStorage .removeItem( PENDING_SEARCH_KEY ); } catch (_) { } } function shouldAcceptSearchPosition( position, pending, href, now ) { if ( !position ) { return false; } if ( !pending ) { return true; } /* * Google已经进入明确的place页面。 */ if ( /\/maps\/place\//i .test( href ) || /!3d-?\d/ .test( href ) ) { return true; } /* * 新坐标和搜索前旧中心不同。 */ const moved = Math.abs( position.lat - pending.startLat ) > 0.000001 || Math.abs( position.lng - pending.startLng ) > 0.000001; if ( moved ) { return true; } /* * 防止极少数情况下无限等待。 */ return ( now - pending.startedAt > 12000 ); } /************************************************************ * 保存视图 ************************************************************/ function loadStoredView() { try { const raw = sessionStorage .getItem( STORAGE_KEY ); if ( !raw ) { return null; } const value = JSON.parse( raw ); if ( ![ value.lat, value.lng, value.zoom ] .every( Number.isFinite ) ) { return null; } return value; } catch (_) { return null; } } function saveStoredView( center, zoom ) { try { sessionStorage .setItem( STORAGE_KEY, JSON.stringify( { lat: center.lat, lng: center.lng, zoom: zoom } ) ); } catch (_) { } } /************************************************************ * Leaflet ************************************************************/ const mapDiv = document.createElement( 'div' ); mapDiv.id = 'avhzyChinaMapV69'; document.body.appendChild( mapDiv ); const stored = loadStoredView(); const initial = getGooglePosition() || stored || { lat: 27.9943, lng: 120.6994, zoom: 15 }; const map = L.map( mapDiv, { zoomControl: true, attributionControl: false, zoomAnimation: true, fadeAnimation: false, markerZoomAnimation: true, zoomSnap: 1, zoomDelta: 1, wheelDebounceTime: 18, wheelPxPerZoomLevel: 60, doubleClickZoom: true, dragging: true, scrollWheelZoom: true, inertia: true, inertiaDeceleration: 2800, inertiaMaxSpeed: 1800, easeLinearity: 0.22 } ); map.setView( [ initial.lat, initial.lng ], Math.max( 3, Math.min( 21, Math.round( initial.zoom ) ) ), { animate: false } ); /************************************************************ * Google Satellite ************************************************************/ const satellite = L.tileLayer( 'https://mt{s}.google.com/vt?' + 'lyrs=s' + '&hl=zh-CN' + '&x={x}' + '&y={y}' + '&z={z}', { subdomains: [ '0', '1', '2', '3' ], minZoom: 3, maxZoom: 21, maxNativeZoom: 21, tileSize: 256, keepBuffer: 5, updateWhenZooming: true, updateWhenIdle: true } ); satellite.addTo( map ); /************************************************************ * Google Roads ************************************************************/ map.createPane( 'avhzyRoadPane' ); const roadPane = map.getPane( 'avhzyRoadPane' ); roadPane.style.zIndex = '450'; roadPane.style.pointerEvents = 'none'; roadPane.style.willChange = 'transform, translate'; const roads = L.tileLayer( 'https://mt{s}.google.com/vt?' + 'lyrs=h' + '&hl=zh-CN' + '&gl=CN' + '&x={x}' + '&y={y}' + '&z={z}', { subdomains: [ '0', '1', '2', '3' ], pane: 'avhzyRoadPane', minZoom: 3, maxZoom: 21, maxNativeZoom: 21, tileSize: 256, opacity: roadOpacity, keepBuffer: 5, updateWhenZooming: true, updateWhenIdle: true } ); roads.addTo( map ); /************************************************************ * 道路纠偏 ************************************************************/ function calculateRoadOffset( center, zoom ) { if ( outOfChina( center.lng, center.lat ) ) { return { x: 0, y: 0 }; } const gcj = wgsToGcj( center.lng, center.lat ); const wgsPixel = map.project( center, zoom ); const gcjPixel = map.project( L.latLng( gcj.lat, gcj.lng ), zoom ); return { x: wgsPixel.x - gcjPixel.x, y: wgsPixel.y - gcjPixel.y }; } function renderStatus( offset ) { const element = document .getElementById( 'avhzyV69Offset' ); if ( !element ) { return; } const center = map.getCenter(); element.innerHTML = '偏移:' + offset.x.toFixed(1) + ' / ' + offset.y.toFixed(1) + ' px' + '<br>' + center.lat.toFixed(6) + ', ' + center.lng.toFixed(6) + '<br>Zoom ' + map .getZoom() .toFixed(0); } function applyRoadOffset( center, zoom, updateText = true ) { if ( !roadsVisible ) { return; } const targetCenter = center || map.getCenter(); const targetZoom = zoom === undefined ? map.getZoom() : zoom; const offset = calculateRoadOffset( targetCenter, targetZoom ); roadPane.style.translate = offset.x + 'px ' + offset.y + 'px'; if ( updateText ) { renderStatus( offset ); } } function scheduleZoomOffset( event ) { pendingZoomEvent = event; if ( zoomAnimationFrame ) { return; } zoomAnimationFrame = requestAnimationFrame( function () { zoomAnimationFrame = 0; if ( !pendingZoomEvent ) { return; } applyRoadOffset( pendingZoomEvent.center, pendingZoomEvent.zoom, false ); pendingZoomEvent = null; } ); } /************************************************************ * 拖动 / Zoom ************************************************************/ map.on( 'dragstart', function () { isDraggingMap = true; } ); map.on( 'dragend', function () { isDraggingMap = false; suppressGoogleSyncUntil = performance.now() + 900; applyRoadOffset(); saveStoredView( map.getCenter(), map.getZoom() ); } ); map.on( 'zoomstart', function () { isZoomingMap = true; } ); map.on( 'zoomanim', scheduleZoomOffset ); map.on( 'zoomend', function () { isZoomingMap = false; suppressGoogleSyncUntil = performance.now() + 700; applyRoadOffset(); saveStoredView( map.getCenter(), map.getZoom() ); } ); /************************************************************ * 搜索框 ************************************************************/ const searchBar = document.createElement( 'form' ); searchBar.id = 'avhzyV69SearchBar'; searchBar.innerHTML = ` <input id="avhzyV69SearchInput" type="text" autocomplete="off" spellcheck="false" placeholder="搜索 Google 地图" aria-label="搜索 Google 地图" > <button id="avhzyV69Clear" type="button" title="清空" > × </button> <button id="avhzyV69SearchButton" type="submit" title="搜索" > 🔍 </button> `; document.body.appendChild( searchBar ); const searchInput = document.getElementById( 'avhzyV69SearchInput' ); const existingQuery = getQueryFromURL(); const existingPending = getPendingSearch(); if ( existingPending && existingPending.query ) { searchInput.value = existingPending.query; } else if ( existingQuery ) { searchInput.value = existingQuery; } const searchState = document.createElement( 'div' ); searchState.id = 'avhzyV69SearchState'; document.body.appendChild( searchState ); function showSearchState( text, timeout = 0 ) { searchState.textContent = text; searchState.style.display = 'block'; if ( timeout > 0 ) { setTimeout( function () { if ( searchState.textContent === text ) { searchState.style.display = 'none'; } }, timeout ); } } if ( existingPending ) { showSearchState( '正在等待 Google 定位:' + existingPending.query ); } function parseCoordinates( text ) { const match = String( text ) .trim() .match( /^\s*(-?\d{1,2}(?:\.\d+)?)\s*[,, ]\s*(-?\d{1,3}(?:\.\d+)?)\s*$/ ); if ( !match ) { return null; } const lat = Number( match[1] ); const lng = Number( match[2] ); if ( !Number.isFinite( lat ) || !Number.isFinite( lng ) || lat < -90 || lat > 90 || lng < -180 || lng > 180 ) { return null; } return { lat, lng }; } function performTextSearch( query ) { const cleanQuery = String( query || '' ) .trim(); if ( !cleanQuery ) { searchInput.focus(); return; } /* * 经纬度直接定位。 */ const coords = parseCoordinates( cleanQuery ); if ( coords ) { clearPendingSearch(); map.setView( [ coords.lat, coords.lng ], Math.max( 16, map.getZoom() ), { animate: false } ); applyRoadOffset(); saveStoredView( map.getCenter(), map.getZoom() ); showSearchState( '已跳转到输入坐标', 1600 ); return; } /* * 普通地名: * * 保存搜索前中心。 */ const center = map.getCenter(); saveStoredView( center, map.getZoom() ); /* * 写入pending状态。 * * 页面重新加载后仍可知道: * 正在等待Google解析新地点。 */ setPendingSearch( cleanQuery, center ); showSearchState( 'Google 正在定位:' + cleanQuery ); /* * V6.9最关键变化: * * 不再使用: * * /maps/search/地名/@旧坐标... * * 只用标准Google Maps搜索入口。 */ location.href = buildStandardSearchURL( cleanQuery ); } searchBar.addEventListener( 'submit', function ( event ) { event.preventDefault(); performTextSearch( searchInput.value ); } ); document .getElementById( 'avhzyV69Clear' ) .addEventListener( 'click', function () { searchInput.value = ''; searchInput.focus(); } ); /************************************************************ * 快捷按钮 ************************************************************/ const quickBar = document.createElement( 'div' ); quickBar.id = 'avhzyV69QuickBar'; const quickItems = [ { label: '餐厅', query: '餐厅', icon: '🍴' }, { label: '酒店', query: '酒店', icon: '🛏' }, { label: '推荐活动', query: '旅游景点', icon: '📷' }, { label: '博物馆', query: '博物馆', icon: '🏛' }, { label: '公共交通', query: '公共交通', icon: '🚌' }, { label: '药店', query: '药店', icon: '⚕' }, { label: '自动取款机', query: 'ATM', icon: '🏧' } ]; quickItems.forEach( function ( item ) { const button = document.createElement( 'button' ); button.type = 'button'; button.innerHTML = '<span class="avhzy-v69-chip-icon">' + item.icon + '</span>' + '<span>' + item.label + '</span>'; button.title = '搜索' + item.label; button.addEventListener( 'click', function () { searchInput.value = item.label; performTextSearch( item.query ); } ); quickBar.appendChild( button ); } ); document.body.appendChild( quickBar ); /************************************************************ * 控制面板 ************************************************************/ const panel = document.createElement( 'div' ); panel.id = 'avhzyV69Panel'; panel.innerHTML = ` <div style=" font-weight:bold; font-size:14px; margin-bottom:5px; " > 🇨🇳 Google纠偏 V6.9 </div> <div style=" color:#188038; margin-bottom:6px; " > ● 地名搜索定位修复 </div> <div id="avhzyV69Offset" style=" color:#666; font-size:11px; line-height:16px; margin-bottom:7px; " > 正在计算... </div> <div> 道路透明度 </div> <input id="avhzyV69Opacity" type="range" min="0" max="100" value="100" > <button id="avhzyV69Road" > 隐藏道路 </button> <button id="avhzyV69Sync" > 📍 同步 Google 当前地点 </button> <button id="avhzyV69Follow" > 自动跟随搜索:开启 </button> <button id="avhzyV69Native" > 返回原版 Google Maps </button> `; document.body.appendChild( panel ); document .getElementById( 'avhzyV69Opacity' ) .addEventListener( 'input', function () { roadOpacity = Number( this.value ) / 100; roads.setOpacity( roadOpacity ); } ); document .getElementById( 'avhzyV69Road' ) .addEventListener( 'click', function () { roadsVisible = !roadsVisible; if ( roadsVisible ) { if ( !map.hasLayer( roads ) ) { roads.addTo( map ); } this.textContent = '隐藏道路'; requestAnimationFrame( function () { applyRoadOffset(); } ); } else { if ( map.hasLayer( roads ) ) { map.removeLayer( roads ); } this.textContent = '显示道路'; } } ); /************************************************************ * Google搜索同步 ************************************************************/ function syncFromGoogle( force = false ) { if ( !autoFollowGoogle && !force ) { return; } /* * 用户正在手动拖动/缩放时, * Google不能把地图拉回去。 */ if ( !force && ( isDraggingMap || isZoomingMap || performance.now() < suppressGoogleSyncUntil ) ) { return; } const position = getGooglePosition(); if ( !position ) { return; } const pending = getPendingSearch(); /* * 有地名搜索正在进行时, * 如果Google还没有真正解析出新地点, * 就继续等。 */ if ( !force && pending && !shouldAcceptSearchPosition( position, pending, location.href, Date.now() ) ) { showSearchState( 'Google 正在解析:' + pending.query ); return; } if ( !force && lastGoogleLat !== null && Math.abs( position.lat - lastGoogleLat ) < 0.0000001 && Math.abs( position.lng - lastGoogleLng ) < 0.0000001 && !pending ) { return; } lastGoogleLat = position.lat; lastGoogleLng = position.lng; const zoom = Math.max( 3, Math.min( 21, Number.isFinite( position.zoom ) ? Math.round( position.zoom ) : map.getZoom() ) ); map.setView( [ position.lat, position.lng ], zoom, { animate: false } ); applyRoadOffset(); saveStoredView( map.getCenter(), map.getZoom() ); /* * pending搜索成功完成。 */ if ( pending ) { searchInput.value = pending.query; clearPendingSearch(); showSearchState( '已定位:' + pending.query, 1800 ); } else { const query = getQueryFromURL(); if ( query && document.activeElement !== searchInput ) { searchInput.value = query; } searchState.style.display = 'none'; } } function monitorGoogleURL() { const current = location.href; if ( current === lastGoogleURL ) { return; } lastGoogleURL = current; /* * Google解析地点时URL可能多次变化。 */ [ 180, 600, 1200, 2500 ] .forEach( function ( delay ) { setTimeout( function () { syncFromGoogle( false ); }, delay ); } ); } /* * 关键: * * 即使Google内部状态变化但URL暂时没变, * pending搜索期间仍持续尝试读取新坐标。 */ function pollPendingSearch() { if ( getPendingSearch() ) { syncFromGoogle( false ); } } document .getElementById( 'avhzyV69Sync' ) .addEventListener( 'click', function () { clearPendingSearch(); lastGoogleLat = null; lastGoogleLng = null; suppressGoogleSyncUntil = 0; syncFromGoogle( true ); } ); const followButton = document.getElementById( 'avhzyV69Follow' ); followButton.addEventListener( 'click', function () { autoFollowGoogle = !autoFollowGoogle; followButton.textContent = '自动跟随搜索:' + ( autoFollowGoogle ? '开启' : '关闭' ); if ( autoFollowGoogle ) { lastGoogleLat = null; lastGoogleLng = null; suppressGoogleSyncUntil = 0; syncFromGoogle( false ); } } ); /************************************************************ * 返回原Google ************************************************************/ const reopen = document.createElement( 'button' ); reopen.id = 'avhzyV69Reopen'; reopen.textContent = '🇨🇳 打开纠偏地图'; reopen.style.display = 'none'; document.body.appendChild( reopen ); document .getElementById( 'avhzyV69Native' ) .addEventListener( 'click', function () { mapDiv.style.display = 'none'; panel.style.display = 'none'; searchBar.style.display = 'none'; quickBar.style.display = 'none'; searchState.style.display = 'none'; reopen.style.display = 'block'; } ); reopen.addEventListener( 'click', function () { mapDiv.style.display = 'block'; panel.style.display = 'block'; searchBar.style.display = 'flex'; quickBar.style.display = 'flex'; reopen.style.display = 'none'; setTimeout( function () { map.invalidateSize( true ); lastGoogleLat = null; lastGoogleLng = null; syncFromGoogle( false ); applyRoadOffset(); }, 80 ); } ); /************************************************************ * Resize ************************************************************/ window.addEventListener( 'resize', function () { setTimeout( function () { map.invalidateSize( false ); applyRoadOffset(); }, 80 ); } ); /************************************************************ * 监控 ************************************************************/ setInterval( monitorGoogleURL, 700 ); setInterval( pollPendingSearch, 900 ); /************************************************************ * 初始化 ************************************************************/ setTimeout( function () { map.invalidateSize( true ); lastGoogleLat = null; lastGoogleLng = null; /* * 如果这是刚搜索后重新加载的页面, * 不允许强行同步旧坐标。 */ if ( getPendingSearch() ) { syncFromGoogle( false ); } else { syncFromGoogle( true ); } applyRoadOffset(); }, 700 ); })();