您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a layer to display US (federal, state, and/or local) boundaries.
当前为
/* eslint-disable no-template-curly-in-string */ // ==UserScript== // @name WME US Government Boundaries // @namespace https://gf.qytechs.cn/users/45389 // @version 2018.12.30.001 // @description Adds a layer to display US (federal, state, and/or local) boundaries. // @author MapOMatic // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/ // @require https://cdnjs.cloudflare.com/ajax/libs/Turf.js/4.7.3/turf.min.js // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js // @grant GM_xmlhttpRequest // @license GNU GPLv3 // @contributionURL https://github.com/WazeDev/Thank-The-Authors // @connect census.gov // @connect wazex.us // @connect usps.com // ==/UserScript== /* global $ */ /* global OL */ /* global GM_info */ /* global W */ /* global GM_xmlhttpRequest */ /* global turf */ /* global WazeWrap */ /* global localStorage */ /* global alert */ const SETTINGS_STORE_NAME = 'wme_us_government_boundaries'; const ALERT_UPDATE = false; const ZIPS_LAYER_URL = 'https://tigerweb.geo.census.gov/arcgis/rest/services/TIGERweb/PUMA_TAD_TAZ_UGA_ZCTA/MapServer/4/'; const COUNTIES_LAYER_URL = 'https://tigerweb.geo.census.gov/arcgis/rest/services/Census2010/State_County/MapServer/1/'; const USPS_ROUTE_COLORS = ['#f00', '#0a0', '#00f', '#a0a', '#6c82cb', '#0aa']; const USPS_ROUTES_URL_TEMPLATE = 'https://gis.usps.com/arcgis/rest/services/EDDM/selectNear/GPServer/routes/execute?f=json&env%3AoutSR=102100&' + 'Selecting_Features=%7B%22geometryType%22%3A%22esriGeometryPoint%22%2C%22features%22%3A%5B%7B%22' + 'geometry%22%3A%7B%22x%22%3A{lon}%2C%22y%22%3A{lat}%2C%22spatialReference%22%3A%7B%22wkid%22%3A' + '102100%2C%22latestWkid%22%3A3857%7D%7D%7D%5D%2C%22sr%22%3A%7B%22wkid%22%3A102100%2C%22latestWkid' + '%22%3A3857%7D%7D&Distance={radius}&Rte_Box=R&userName=EDDM'; const USPS_ROUTES_RADIUS = 0.5; // miles const PROCESS_CONTEXTS = []; const SCRIPT_VERSION = GM_info.script.version; const SCRIPT_VERSION_CHANGES = [ GM_info.script.name, `v${SCRIPT_VERSION}`, '', 'What\'s New', '------------------------------' // add new stuff here ].join('\n'); let _zipsLayer; let _countiesLayer; let _uspsRoutesMapLayer = null; let _circleFeature; let _$resultsDiv; let _$getRoutesButton; let _settings = {}; function log(message) { console.log('USGB:', message); } // Recursively checks the settings object and fills in missing properties from the // default settings object. function checkSettings(obj, defaultObj) { Object.keys(defaultObj).forEach(key => { if (!obj.hasOwnProperty(key)) { obj[key] = defaultObj[key]; } else if (defaultObj[key] && (defaultObj[key].constructor === {}.constructor)) { checkSettings(obj[key], defaultObj[key]); } }); } function loadSettings() { const loadedSettings = $.parseJSON(localStorage.getItem(SETTINGS_STORE_NAME)); const defaultSettings = { lastVersion: null, layers: { zips: { visible: true, dynamicLabels: false, junk: 123.42 }, counties: { visible: true, dynamicLabels: true } } }; if (loadedSettings) { _settings = loadedSettings; checkSettings(_settings, defaultSettings); } else { _settings = defaultSettings; } } function saveSettings() { if (localStorage) { _settings.lastVersion = SCRIPT_VERSION; _settings.layers.zips.visible = _zipsLayer.visibility; _settings.layers.counties.visible = _countiesLayer.visibility; localStorage.setItem(SETTINGS_STORE_NAME, JSON.stringify(_settings)); log('Settings saved'); } } function getUrl(baseUrl, extent, zoom, outFields) { const geometry = { xmin: extent.left, ymin: extent.bottom, xmax: extent.right, ymax: extent.top, spatialReference: { wkid: 102100, latestWkid: 3857 } }; const geometryStr = JSON.stringify(geometry); let url = `${baseUrl}query?geometry=${encodeURIComponent(geometryStr)}`; url += '&returnGeometry=true'; url += `&outFields=${encodeURIComponent(outFields.join(','))}`; url += '&quantizationParameters={tolerance:100}'; url += '&spatialRel=esriSpatialRelIntersects&geometryType=esriGeometryEnvelope&inSR=102100&outSR=3857&f=json'; return url; } const _zipCities = {}; function appendCityToZip(zip, cityState, context) { if (!context.cancel) { if (!cityState.error) { _zipCities[zip] = cityState; $('#zip-text').append(` (${cityState.city}, ${cityState.state})`); } } } function updateNameDisplay(context) { const center = W.map.getCenter(); const mapCenter = new OL.Geometry.Point(center.lon, center.lat); const baseUrl = 'https://tools.usps.com/go/ZipLookupResultsAction!input.action?resultMode=2&companyName=&address1=&address2=&city=&state=Select&urbanCode=&postalCode='; let feature; let text = ''; let label; let url; if (context.cancel) return; if (_zipsLayer && _zipsLayer.visibility) { const onload = res => appendCityToZip(text, $.parseJSON(res.responseText), res.context); for (let i = 0; i < _zipsLayer.features.length; i++) { feature = _zipsLayer.features[i]; if (feature.geometry.containsPoint && feature.geometry.containsPoint(mapCenter)) { text = feature.attributes.name; url = `${baseUrl + text}&zip=`; $('<span>', { id: 'zip-text' }).empty().css({ display: 'inline-block' }).append( $('<a>', { href: url, target: '__blank', title: 'Look up USPS zip code' }) .text(text) .css({ color: 'white', display: 'inline-block' }), ).appendTo($('#zip-boundary')); if (!context.cancel) { if (_zipCities[text]) { appendCityToZip(text, _zipCities[text], context); } else { GM_xmlhttpRequest({ url: `https://wazex.us/zips/ziptocity2.php?zip=${text}`, context, method: 'GET', onload }); } } } } } if (_countiesLayer && _countiesLayer.visibility) { for (let i = 0; i < _countiesLayer.features.length; i++) { feature = _countiesLayer.features[i]; if (feature.attributes.type !== 'label' && feature.geometry.containsPoint(mapCenter)) { label = feature.attributes.name; $('<span>', { id: 'county-text' }).css({ display: 'inline-block' }) .text(label) .appendTo($('#county-boundary')); } } } } function arcgisFeatureToOLFeature(feature, attributes) { const rings = []; feature.geometry.rings.forEach(ringIn => { const pnts = []; for (let i = 0; i < ringIn.length; i++) { pnts.push(new OL.Geometry.Point(ringIn[i][0], ringIn[i][1])); } rings.push(new OL.Geometry.LinearRing(pnts)); }); return new OL.Feature.Vector(new OL.Geometry.Polygon(rings), attributes); } function getRingArrayFromFeature(feature) { return feature.geometry.components.map( featureRing => featureRing.components.map(pt => [pt.x, pt.y]) ); } function getLabelPoints(feature) { const e = W.map.getExtent(); const screenPoly = turf.polygon([[ [e.left, e.top], [e.right, e.top], [e.right, e.bottom], [e.left, e.bottom], [e.left, e.top] ]]); // The intersect function doesn't seem to like holes in polygons, so assume the // first ring is the outer boundary and ignore any holes. const featurePoly = turf.polygon([getRingArrayFromFeature(feature)[0]]); const intersection = turf.intersect(screenPoly, featurePoly); let pts; if (intersection && intersection.geometry && intersection.geometry.coordinates) { let turfPt = turf.centerOfMass(intersection); if (!turf.inside(turfPt, intersection)) { turfPt = turf.pointOnSurface(intersection); } const turfCoords = turfPt.geometry.coordinates; const pt = new OL.Geometry.Point(turfCoords[0], turfCoords[1]); const { attributes } = feature; attributes.label = feature.attributes.name; pts = [new OL.Feature.Vector(pt, attributes)]; } else { pts = null; } return pts; } function processBoundaries(boundaries, context, type, nameField) { let layer; let layerSettings; switch (type) { case 'zip': layerSettings = _settings.layers.zips; layer = _zipsLayer; break; case 'county': layerSettings = _settings.layers.counties; layer = _countiesLayer; break; default: throw new Error('USBG: Unexpected type argument in processBoundaries'); } if (context.cancel || !layerSettings.visible) { // do nothing } else { layer.removeAllFeatures(); if (!context.cancel) { boundaries.forEach(boundary => { const attributes = { name: boundary.attributes[nameField], label: layerSettings.dynamicLabels ? '' : boundary.attributes[nameField], type }; if (!context.cancel) { const feature = arcgisFeatureToOLFeature(boundary, attributes); layer.addFeatures([feature]); if (layerSettings.dynamicLabels) { const labels = getLabelPoints(feature); if (labels) { labels.forEach(labelFeature => { labelFeature.attributes.type = 'label'; }); layer.addFeatures(labels); } } } }); } } context.callCount--; if (context.callCount === 0) { updateNameDisplay(context); const idx = PROCESS_CONTEXTS.indexOf(context); if (idx > -1) { PROCESS_CONTEXTS.splice(idx, 1); } } } function getUspsRoutesUrl(lon, lat, radius) { return USPS_ROUTES_URL_TEMPLATE.replace('{lon}', lon).replace('{lat}', lat).replace('{radius}', radius); } function getCircleLinearRing() { const center = W.map.getCenter(); const radius = USPS_ROUTES_RADIUS * 1609.344; // miles to meters const points = []; for (let degree = 0; degree < 360; degree += 5) { const radians = degree * Math.PI / 180; const lon = center.lon + radius * Math.cos(radians); const lat = center.lat + radius * Math.sin(radians); points.push(new OL.Geometry.Point(lon, lat)); } return new OL.Geometry.LinearRing(points); } function processUspsRoutesResponse(res) { const data = $.parseJSON(res.responseText); const routes = data.results[0].value.features; const zipRoutes = {}; routes.forEach(route => { const id = `${route.attributes.CITY_STATE} ${route.attributes.ZIP_CODE}`; let zipRoute = zipRoutes[id]; if (!zipRoute) { zipRoute = { paths: [] }; zipRoutes[id] = zipRoute; } zipRoute.paths = zipRoute.paths.concat(route.geometry.paths); }); const features = []; let routeIdx = 0; _$resultsDiv.empty(); Object.keys(zipRoutes).forEach(zipName => { const route = zipRoutes[zipName]; const paths = route.paths.map(path => { const pointList = path.map(point => new OL.Geometry.Point(point[0], point[1])); return new OL.Geometry.LineString(pointList); }); const color = USPS_ROUTE_COLORS[routeIdx]; const style = { strokeColor: color, strokeDashstyle: 'solid', strokeWidth: 18 }; features.push(new OL.Feature.Vector( new OL.Geometry.MultiLineString(paths), null, style )); _$resultsDiv.append($('<div>').text(zipName).css({ color, fontWeight: 'bold' })); routeIdx++; }); _$getRoutesButton.removeAttr('disabled').css({ color: '#000' }); _uspsRoutesMapLayer.addFeatures(features); } function fetchUspsRoutesFeatures() { const center = W.map.getCenter(); const url = getUspsRoutesUrl(center.lon, center.lat, USPS_ROUTES_RADIUS); _$getRoutesButton.attr('disabled', 'true').css({ color: '#888' }); _$resultsDiv.empty().append('<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>'); _uspsRoutesMapLayer.removeAllFeatures(); GM_xmlhttpRequest({ url, onload: processUspsRoutesResponse }); } function fetchBoundaries() { if (PROCESS_CONTEXTS.length > 0) { PROCESS_CONTEXTS.forEach(context => { context.cancel = true; }); } const extent = W.map.getExtent(); const zoom = W.map.getZoom(); let url; const context = { callCount: 0, cancel: false }; PROCESS_CONTEXTS.push(context); $('.us-boundary-region').remove(); $('.loading-indicator-region').before( $('<div>', { id: 'county-boundary', class: 'us-boundary-region' }) .css({ color: 'white', float: 'left', marginLeft: '10px' }), $('<div>', { id: 'zip-boundary', class: 'us-boundary-region' }) .css({ color: 'white', float: 'left', marginLeft: '10px' }) ); if (_settings.layers.zips.visible) { url = getUrl(ZIPS_LAYER_URL, extent, zoom, ['ZCTA5']); context.callCount++; $.ajax({ url, context, method: 'GET', datatype: 'json', success(data) { processBoundaries($.parseJSON(data).features, this, 'zip', 'ZCTA5', 'ZCTA5'); } }); } if (_settings.layers.counties.visible) { url = getUrl(COUNTIES_LAYER_URL, extent, zoom, ['NAME']); context.callCount++; $.ajax({ url, context, method: 'GET', datatype: 'json', success(data) { processBoundaries($.parseJSON(data).features, this, 'county', 'NAME', 'NAME'); } }); } } // function fetchTimeZone() { // let center = W.map.getCenter(); // center.transform(W.map.projection, W.map.displayProjection); // let dt = new Date(); // $.ajax({ // url: 'https://maps.googleapis.com/maps/api/timezone/json?location=' + center.lat + ',' // + center.lon + '×tamp=' + (dt.getTime() / 1000), // method: 'GET', // success: function(data) { // console.log(data); // } // }); // } function onZipsLayerVisibilityChanged() { _settings.layers.zips.visible = _zipsLayer.visibility; saveSettings(); fetchBoundaries(); } function onCountiesLayerVisibilityChanged() { _settings.layers.counties.visible = _countiesLayer.visibility; saveSettings(); fetchBoundaries(); } function onZipsLayerToggleChanged(checked) { _zipsLayer.setVisibility(checked); } function onCountiesLayerToggleChanged(checked) { _countiesLayer.setVisibility(checked); } function onGetRoutesButtonClick() { fetchUspsRoutesFeatures(); } function onGetRoutesButtonMouseEnter() { _$getRoutesButton.css({ color: '#00a' }); const style = { strokeColor: '#ff0', strokeDashstyle: 'solid', strokeWidth: 6, fillColor: '#ff0', fillOpacity: 0.2 }; _circleFeature = new OL.Feature.Vector(getCircleLinearRing(), null, style); _uspsRoutesMapLayer.addFeatures([_circleFeature]); } function onGetRoutesButtonMouseLeave() { _$getRoutesButton.css({ color: '#000' }); _uspsRoutesMapLayer.removeFeatures([_circleFeature]); } function onClearRoutesButtonClick() { _uspsRoutesMapLayer.removeAllFeatures(); _$resultsDiv.empty(); } function showScriptInfoAlert() { /* Check version and alert on update */ if (ALERT_UPDATE && SCRIPT_VERSION !== _settings.lastVersion) { alert(SCRIPT_VERSION_CHANGES); } } let _zipsStyle; let _countiesStyle; function initLayer() { _zipsStyle = { strokeColor: '#FF0000', strokeOpacity: 1, strokeWidth: 3, strokeDashstyle: 'solid', fillOpacity: 0, fontSize: '16px', fontFamily: 'Arial', fontWeight: 'bold', fontColor: 'red', label: '${label}', labelYOffset: '-20', labelOutlineColor: 'white', labelOutlineWidth: 2 }; _countiesStyle = { strokeColor: 'pink', strokeOpacity: 1, strokeWidth: 6, strokeDashstyle: 'solid', fillOpacity: 0, fontSize: '18px', fontFamily: 'Arial', fontWeight: 'bold', fontColor: 'pink', label: '${label}', labelOutlineColor: 'black', labelOutlineWidth: 2 }; _zipsLayer = new OL.Layer.Vector('US Gov\'t Boundaries - Zip Codes', { uniqueName: '__WMEUSBoundaries_Zips', styleMap: new OL.StyleMap({ default: _zipsStyle }) }); _countiesLayer = new OL.Layer.Vector('US Gov\'t Boundaries - Counties', { uniqueName: '__WMEUSBoundaries_Counties', styleMap: new OL.StyleMap({ default: _countiesStyle }) }); _zipsLayer.setOpacity(0.6); _countiesLayer.setOpacity(0.6); _zipsLayer.setVisibility(_settings.layers.zips.visible); _countiesLayer.setVisibility(_settings.layers.counties.visible); W.map.addLayers([_countiesLayer, _zipsLayer]); _zipsLayer.events.register('visibilitychanged', null, onZipsLayerVisibilityChanged); _countiesLayer.events.register('visibilitychanged', null, onCountiesLayerVisibilityChanged); W.map.events.register('moveend', W.map, () => { fetchBoundaries(); // fetchTimeZone(); return true; }, true); // Add the layer checkbox to the Layers menu. WazeWrap.Interface.AddLayerCheckbox('display', 'Zip Codes', _settings.layers.zips.visible, onZipsLayerToggleChanged); WazeWrap.Interface.AddLayerCheckbox('display', 'Counties', _settings.layers.counties.visible, onCountiesLayerToggleChanged); } function initTab() { const $content = $('<div>').append( $('<fieldset>', { style: 'border:1px solid silver;padding:8px;border-radius:4px;' }).append( $('<legend>', { style: 'margin-bottom:0px;borer-bottom-style:none;width:auto;' }).append( $('<h4>').text('ZIP Codes') ), $('<div>', { class: 'controls-container', style: 'padding-top:0px' }).append( $('<input>', { type: 'checkbox', id: 'usgb-zips-dynamicLabels' }), $('<label>', { for: 'usgb-zips-dynamicLabels' }).text('Dynamic label positions') ) ), $('<fieldset>', { style: 'border:1px solid silver;padding:8px;border-radius:4px;' }).append( $('<legend>', { style: 'margin-bottom:0px;borer-bottom-style:none;width:auto;' }).append( $('<h4>').text('Counties') ), $('<div>', { class: 'controls-container', style: 'padding-top:0px' }).append( $('<input>', { type: 'checkbox', id: 'usgb-counties-dynamicLabels' }), $('<label>', { for: 'usgb-counties-dynamicLabels' }).text('Dynamic label positions') ) ) ); new WazeWrap.Interface.Tab('USGB', $content.html(), () => { $('#usgb-zips-dynamicLabels').prop('checked', _settings.layers.zips.dynamicLabels).change(() => { _settings.layers.zips.dynamicLabels = $('#usgb-zips-dynamicLabels').is(':checked'); saveSettings(); fetchBoundaries(); }); $('#usgb-counties-dynamicLabels').prop('checked', _settings.layers.counties.dynamicLabels).change(() => { _settings.layers.counties.dynamicLabels = $('#usgb-counties-dynamicLabels').is(':checked'); saveSettings(); fetchBoundaries(); }); }); } function initUspsRoutesLayer() { _uspsRoutesMapLayer = new OL.Layer.Vector('USPS Routes', { uniqueName: '__wmeUSPSroutes' }); W.map.addLayer(_uspsRoutesMapLayer); // W.map.setLayerIndex(_uspsRoutesMapLayer, W.map.getLayerIndex(W.map.roadLayers[0])-1); // HACK to get around conflict with URO+. If URO+ is fixed, this can be replaced with the setLayerIndex line above. _uspsRoutesMapLayer.setZIndex(334); const checkLayerZIndex = () => { if (_uspsRoutesMapLayer.getZIndex() !== 334) _uspsRoutesMapLayer.setZIndex(334); }; setInterval(checkLayerZIndex, 100); // END HACK _uspsRoutesMapLayer.setOpacity(0.8); } function init() { loadSettings(); initLayer(); initTab(); showScriptInfoAlert(); fetchBoundaries(); initUspsRoutesLayer(); _$resultsDiv = $('<div>', { id: 'usps-route-results', style: 'margin-top:3px;' }); _$getRoutesButton = $('<button>', { id: 'get-usps-routes', style: 'height:23px;' }).text('Get USPS routes'); $('#sidebar').prepend( $('<div>', { style: 'margin-left:10px;' }).append( _$getRoutesButton .click(onGetRoutesButtonClick) .mouseenter(onGetRoutesButtonMouseEnter) .mouseout(onGetRoutesButtonMouseLeave), $('<button>', { id: 'clear-usps-routes', style: 'height:23px; margin-left:4px;' }) .text('Clear') .click(onClearRoutesButtonClick), _$resultsDiv ) ); log('Initialized.'); } function bootstrap(tries = 1) { if (W && W.loginManager && W.loginManager.events && W.loginManager.events.register && W.model && W.model.states && W.model.states.additionalInfo && W.map && W.loginManager.user && WazeWrap.Ready) { log('Initializing...'); init(); } else { if (tries % 20 === 0) log('Bootstrap failed. Trying again...'); setTimeout(() => bootstrap(++tries), 250); } } log('Bootstrap...'); bootstrap();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址