您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Miscelannia
当前为
// ==UserScript== // @name LV WME Helper // @namespace https://dev.laacz.lv/ // @description Miscelannia // @include https://www.waze.com/*/editor* // @include https://www.waze.com/editor* // @include https://beta.waze.com/* // @exclude https://www.waze.com/*user/*editor/* // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js // @require https://cdnjs.cloudflare.com/ajax/libs/jsts/2.0.6/jsts.min.js // @license CC-BY-4.0; https://creativecommons.org/licenses/by/4.0/ // @version 2.07 // @connect waze.dev.laacz.lv // @grant GM.xmlHttpRequest // ==/UserScript== // noinspection DuplicatedCode /* global W */ /* global WazeWrap */ /* global axios */ (function () { /** * @typedef Address * @property code {number} * @property name {string} * @property waze_house_number {string} * @property waze_name {string} * @property iela_name {string} * @property ciems_name {string} * @property pilseta_name {string} * @property pagasts_name {string} * @property novads_name {string} * @property full_name {string} * @property parent_code {number} * @property geom {object} * @property lat {float} * @property lng {float} * @property Point {Point} */ /** * Fetched and mutated address list. * @type {Address[]} */ let addresses = []; // Reassigned by requure() let UpdateObject = null; let Landmark = null; let AddLandmark = null; let DeleteSegment = null; // Minimum venue area, which is drawn on the map, in m² const MIN_VISIBLE_AREA = 514; // Regluar expression for validating street name const REGEXP_STREET_NAMES = /(^.+(aleja|apvedceļš|bulvāris|ceļš|dambis|gatve|iela|krastmala|laukums|līnija|prospekts|šķērslīnija|šoseja) (\d+.+$))/; // Regular expression for validating house number const REGEXP_HOUSE_NUMBERS = /^(\d+[a-zA-Z]*)( k-\d+)?$/; // Regular expression for validating waze house number const REGEXP_WAZE_HOUSE_NUMBER = /^\d+[A-Z]?(-\d+)?$/; // Game loop's interval let updateTimeout = undefined; // Indicates that REST request is in progress let wmelvLoadingIndicator = true; function wmelvLoading(value) { if (value !== undefined) { wmelvLoadingIndicator = value; qs('#wmelv-loading-indicator').style.visibility = value ? 'visible' : 'hidden'; } return wmelvLoadingIndicator; } /** * https://gf.qytechs.cn/en/scripts/38421-wme-utils-navigationpoint */ class NavigationPoint { constructor(point) { this._point = point.clone(); this._entry = true; this._exit = true; this._isPrimary = true; this._name = ""; } with() { var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; if (e.point == null) e.point = this.toJSON().point; return new this.constructor((this.toJSON().point, e.point)); } getPoint() { return this._point.clone(); } getEntry() { return this._entry; } getExit() { return this._exit; } getName() { return this._name; } isPrimary() { return this._isPrimary; } toJSON() { return { point: this._point, entry: this._entry, exit: this._exit, primary: this._isPrimary, name: this._name }; } clone() { return this.with(); } } /** * Settings object with getters, setters, and default values. */ const settings = { configuration: { minHNZoomLevel: 16, hlIncorrectAddress: true, hlSmallArea: true, hlLowerCaseHN: true, hlNoHN: true, hlNameHNMismatch: true, addressFixer: false, hlDupes: true, hlIela: true, autoHN: false, // autoHNSegmentSelection: true, }, get: function (key, def) { return typeof this.configuration[key] !== 'undefined' ? this.configuration[key] : def; }, set: function (key, value) { this.configuration[key] = value; this.save(); }, save() { if (localStorage) { localStorage.setItem("_wmelv3_settings", JSON.stringify(this.configuration)); } }, load() { let loadedSettings = JSON.parse(localStorage.getItem("_wmelv3_settings")); this.config = Object.assign(this.configuration, loadedSettings ? loadedSettings : {}); } }; /** * querySelectorAll shorthand * @param selector * @returns {*[]} */ function qsa(selector) { return Array.from(document.querySelectorAll(selector), e => e); } /** * querySelector shorthand. * @param selector * @returns {*} */ function qs(selector) { return document.querySelector(selector); } /** * Shamelessly taken from WME PlaceNames Russian: * https://gf.qytechs.cn/en/scripts/15310-wme-placenames-russian/code */ function fixPlaceArea(place) { let requiredArea = 516, oldGeometry = place.geometry.clone(), newGeometry = place.geometry.clone(), centerPT = newGeometry.getCentroid(), oldArea = oldGeometry.getGeodesicArea(W.map.getProjectionObject()), scale = Math.sqrt(requiredArea / oldArea); newGeometry.resize(scale, centerPT); let wazeActionUpdateFeatureGeometry = require("Waze/Action/UpdateFeatureGeometry"), action = new wazeActionUpdateFeatureGeometry(place, W.model.venues, oldGeometry, newGeometry); place.attributes.fixArea = false; W.model.actionManager.add(action); } function fixCurrentlySelectedArea(e) { if (e) e.preventDefault(); if (!W.selectionManager.hasSelectedFeatures() || W.selectionManager.getSelectedFeatures()[0].model.type !== "venue" || !W.selectionManager.getSelectedFeatures()[0].model.isGeometryEditable()) { return; } fixPlaceArea(W.selectionManager.getSelectedFeatures()[0].model); wmelvUpdate(); } /** * Colorful logger ;) * @type {{warn(...[*]): void, debug(...[*]): void, crit(...[*]): void, log(*, ...[*]): void, info(...[*]): void}} */ const Logger = { log(style, ...args) { console.log("%c●%c", style, 'color: #000; background-color: #fff;', ...args) }, debug(...args) { Logger.log('background-color: gray; color: #fff; font-weight: bold; padding: .2em .5em;', ...args) }, info(...args) { Logger.log('background-color: navy; color: #fff; font-weight: bold; padding: .2em .5em;', ...args) }, crit(...args) { Logger.log('background-color: maroon; color: #fff; font-weight: bold; padding: .2em .5em;', ...args) }, warn(...args) { Logger.log('background-color: orange; color: #fff; font-weight: bold; padding: .2em .5em;', ...args) }, } /** * Returns closest address to a given point on the map. * @param point {Point} * @return Address|null */ function getClosestAddress(point) { // Logger.info(point, addresses[0].Point); point.transform('EPSG:900913', "EPSG:4326"); return addresses.length ? addresses.reduce((prev, curr) => { if (!prev) return curr; return prev.Point.distanceTo(point) < curr.Point.distanceTo(point) ? prev : curr; }) : null; } /** * Helper to build a bbox string from the map's extent. * @returns {string} */ function wmelvBBOX() { const bounds = W.map.getExtent(); bounds.transform("EPSG:900913", "EPSG:4326"); return bounds.toBBOX(); } /** * Adds stuff to addresses which cannot be added in the backend. * @param addresses * @returns {*} */ function wmelvMutateAddresses(addresses) { if (addresses && addresses.length) { addresses = addresses.map((ads) => { ads.Point = new OpenLayers.Geometry.Point(ads.lng, ads.lat); return ads; }) } return addresses; } /** * Creates a new place, given a geometry and attributes, selects it and registers in the actionmanager's history. * @param geometry * @param params * @returns {*} */ function wmelvCreateNewPlace(geometry, params) { let place = new Landmark(); if (!Object.values(params).length) params = {}; if (!params.categories || !params.categories.length) params.categories = ["OTHER"]; place.geometry = geometry; place.attributes.name = params.name ? params.name : ''; place.attributes.lockRank = params.lockRank ? params.lockRank : 2; params.categories.forEach(cat => { place.attributes.categories.push(cat); }); place.attributes.entryExitPoints.push(new NavigationPoint(place.geometry.getCentroid())); W.model.actionManager.add(new AddLandmark(place)); W.model.actionManager.add(new UpdateObject(place, params)); // console.log(params); W.selectionManager.setSelectedModels([place]); return place; } /** * Game loop as they say. */ function wmelvUpdate() { if (updateTimeout) { clearTimeout(updateTimeout); } if (!wmelvLoading()) { Object.keys(W.model.venues.objects).forEach((k) => { // Object.values(W.model.venues.objects).forEach((v) => { const venue = W.model.venues.objects[k]; const el = qs('#' + venue.geometry.id); if (!el) { return } if ('attributes' in venue) { let v = venue.attributes; // Logger.info(v.attributes.name, 'closest address is', closest.full_name) let hn = v.houseNumber; let street = {name: ''}; let city = {name: ''}; if (v.streetID) { street = W.model.streets.objects[v.streetID]; } let full_address = ''; if (street) { city = W.model.cities.objects[street.cityID].attributes } if (street.name.length) { full_address = `${hn}, ${street.name}, ${city.name}`; } else { full_address = `${v.name}, ${city.name}` } full_address = full_address.replace(/[, ]+$/g, ''); const area = W.model.venues.objects[k].geometry.toString().indexOf('POLYGON') === 0 ? W.model.venues.objects[k].geometry.getGeodesicArea(W.map.getProjectionObject()) : false; W.model.venues.objects[k].attributes.fixLowerCase = (v.houseNumber && v.houseNumber.toUpperCase() !== v.houseNumber) || (v.houseNumber && v.name && v.name.toUpperCase() === v.houseNumber.toUpperCase() && v.name !== v.houseNumber.toUpperCase()) || (v.name && v.name.replace(/ k-\d+$/, '').match(/^\d+[a-z]+/) && v.name.replace(/ k-\d+$/, '').toUpperCase() !== v.name.replace(/ k-\d+$/, '')); W.model.venues.objects[k].attributes.fixNameHNMismatch = !v.residential && ( (v.houseNumber && !v.name) || ( v.houseNumber && v.name && v.name.match(REGEXP_HOUSE_NUMBERS) && v.houseNumber.toLowerCase() !== v.name.toLowerCase().replace(' k-', '-')) ); W.model.venues.objects[k].attributes.fixNoHN = !v.houseNumber && v.streetID && W.model.streets.objects[v.streetID] && W.model.streets.objects[v.streetID].name && W.model.streets.objects[v.streetID].cityID && W.model.cities.objects[W.model.streets.objects[v.streetID].cityID] && W.model.cities.objects[W.model.streets.objects[v.streetID].cityID].attributes.name && v.categories.indexOf('PARKING_LOT') === -1 && v.categories.indexOf('TAXI_STATION') === -1 && v.categories.indexOf('PARK') === -1; W.model.venues.objects[k].attributes.fixIela = v.name.toLowerCase().match(REGEXP_STREET_NAMES); W.model.venues.objects[k].attributes.fixArea = !v.residential && area !== false && area < MIN_VISIBLE_AREA ? area : false; const closest = getClosestAddress(venue.geometry.getCentroid()); if (closest) { W.model.venues.objects[k].attributes.fixAddress = closest.waze_name !== full_address && !W.model.venues.objects[k].attributes.categories.some((c) => ['BEACH', 'BRIDGE', 'CAMPING_TRAILER_PARK', 'CANAL', 'CEMETERY', 'DAM', 'DESSERT', 'FARM', 'FERRY_PIER', 'FOREST_GROVE', 'GOLF_COURSE', 'ISLAND', 'JUNCTION_INTERCHANGE', 'NATURAL_FEATURES', 'OUTDOORS', 'PARK', 'PARKING_LOT', 'PLAYGROUND', 'POOL', 'PROMENADE', 'RACING_TRACK', 'REST_AREAS', 'RIVER_STREAM', 'SCENIC_LOOKOUT_VIEWPOINT', 'SEA_LAKE_POOL', 'SEAPORT_MARINA_HARBOR', 'SKI_AREA', 'SWAMP_MARSH', 'TAXI_STATION', 'THEME_PARK', 'TOURIST_ATTRACTION_HISTORIC_SITE', 'TUNNEL', 'ZOO_AQUARIUM'].indexOf(c) > -1) ; W.model.venues.objects[k].attributes.ads = closest; } } }); wmelvDraw(); } updateTimeout = setTimeout(wmelvUpdate, 345); } /** * Sets venue's address, changing only parts, which are ough to be changed. * @param venue * @param address */ function wmelvSetVenueAddress(venue, address) { let attrs = venue.model.attributes; const venue_id = attrs.id; const changes = {}; const street_name = address.iela_name ? address.iela_name : ''; const city_name = address.pilseta_name ? address.pilseta_name : (address.ciems_name ? address.ciems_name : ''); let house_name = address.name.replace(' k-', '-'); if (!city_name) { house_name += ', ' + (address.pagasts_name ? address.pagasts_name : address.novads_name); } // Find the street from waze model (there is always a street, even when there is none) const street = Object.values(W.model.streets.objects).find((street) => { if (street.name === street_name && W.model.cities.objects[street.cityID].attributes.name === city_name) { return true; } }); if (!street) { Logger.warn('Could not find a street "' + street_name + '" in ' + city_name); return; } if (attrs.streetID !== street.id) { changes.streetID = street.id; } if (address.name.match(REGEXP_HOUSE_NUMBERS)) { // It's a house number if (attrs.houseNumber !== house_name) { changes.houseNumber = house_name; } if (!attrs.name || attrs.name.match(REGEXP_HOUSE_NUMBERS) && attrs.name !== house_name) { // If name is a house number, and it is different, replace changes.name = address.name; } else { // If name is in an alias, and it differs, fix that let modified = false; for (const alias_idx in attrs.aliases) { if (attrs.aliases[alias_idx].match(REGEXP_HOUSE_NUMBERS)) { if (house_name === attrs.aliases[alias_idx]) { modified = true; break; } changes.aliases = attrs.aliases; changes.aliases[alias_idx] = address.name; modified = true; break; } } if (!modified) { changes.aliases = attrs.aliases; changes.aliases.push(address.name); } } } else if (attrs.name !== house_name) { // It's a house name changes.name = house_name; } if (Object.values(changes).length > 0) { Logger.debug('Updating venue', venue_id, 'with', changes); W.model.actionManager.add(new UpdateObject(venue.model, changes)); W.model.venues.objects[venue_id].attributes.fixAddress = false; W.selectionManager.unselectAll(); W.selectionManager.setSelectedModels([W.model.venues.objects[venue_id]]); wmelvUpdate(); } } /** * Updates visualizations. */ function wmelvDraw() { Object.values(W.model.venues.objects).forEach((v) => { let el = qs('#' + v.geometry.id), hled = false; if (el) { if (!el.getAttribute('ogAttributes')) { el.setAttribute('ogAttributes', { 'stroke': el.getAttribute('stroke'), "stroke-width": el.getAttribute("stroke-width"), "stroke-dash-array": el.getAttribute("stroke-dash-array"), }); } if (!hled && settings.get('hlIncorrectAddress') && v.attributes.fixAddress) { el.setAttribute('stroke', '#ff00ff'); el.setAttribute("stroke-width", "4"); el.setAttribute("stroke-dash-array", "none"); hled = true; } if (!hled && ((settings.get('hlLowerCaseHN') && v.attributes.fixLowerCase) || (settings.get('hlNoHN') && v.attributes.fixNoHN) || (settings.get('hlIela') && v.attributes.fixIela))) { el.setAttribute('stroke', '#ff0000'); el.setAttribute("stroke-width", "4"); el.setAttribute("stroke-dash-array", "none"); hled = true; } if (!hled && settings.get('hlSmallArea') && el && v.attributes.fixArea) { el.setAttribute('stroke', '#0000ff'); el.setAttribute("stroke-width", "4"); el.setAttribute("stroke-dash-array", "none"); hled = true; } if (!hled && settings.get('hlNameHNMismatch') && el && v.attributes.fixNameHNMismatch) { el.setAttribute('stroke', '#ffff00'); el.setAttribute("stroke-width", "4"); el.setAttribute("stroke-dash-array", "none"); hled = true; } if (!hled && settings.get('hlDupes') && el && v.attributes.fixDupes) { el.setAttribute('stroke', '#00ffff'); el.setAttribute("stroke-width", "4"); el.setAttribute("stroke-dash-array", "none"); hled = true; } // const shouldRevert = !hled && el.getAttribute('ogAttributes').all((k, v) => el.getAttribute(k) === v); // if (!hled) { // Logger.info('Reverting HL'); // const attrs = el.getAttribute('ogAttributes'); // el.setAttribute('stroke', attrs['stroke']); // el.setAttribute("stroke-width", attrs["stroke-width"]); // el.setAttribute("stroke-dash-array", attrs["stroke-dash-array"]); // } } }); } /** * Fetches VZD addresses from an API. Restricts to viewport plus a tiny buffer. */ function wmelvLoadAddresses() { if (!settings.get('autoHN') || W.map.getZoom() < 16) { if (W.map.getLayersByName("pointLayer").length) { W.map.removeLayer(W.map.getLayersByName("pointLayer")[0]); } return; } const request = { bounds: wmelvBBOX(), } wmelvLoading(true); GM.xmlHttpRequest({ method: 'POST', url: '//waze.dev.laacz.lv/api/2/addresses', data: JSON.stringify(request), headers: { 'Content-Type': 'application/json' }, onload: function (response) { addresses = JSON.parse(response.responseText); addresses = wmelvMutateAddresses(addresses) if (W.map.getLayersByName("pointLayer").length) { W.map.removeLayer(W.map.getLayersByName("pointLayer")[0]); } const pointLayer = new OpenLayers.Layer.Vector("pointLayer"); const proj = new OpenLayers.Projection("EPSG:4326"); const features = []; for (const addr of addresses) { const point = new OpenLayers.Geometry.Point(addr.lng, addr.lat).transform(proj, W.map.getProjectionObject()); const ft = new OpenLayers.Feature.Vector(point, null, null); ft.style = { label: addr.name, pointRadius: 15, fillColor: addr.color, fillOpacity: 0.8, strokeColor: "#cc6633", strokeWidth: 2, strokeOpacity: 0.8, fontColor: "black", labelOutlineColor: "white", labelOutlineWidth: 3, }; features.push(ft); } pointLayer.addFeatures(features); W.map.addLayer(pointLayer); wmelvLoading(false); wmelvUpdate(); }, onerror: (e) => { Logger.crit("Failed to load addresses", e); wmelvLoading(false); }, onabort: () => { Logger.crit("Aborted address loading"); wmelvLoading(false); }, ontimeout: () => { Logger.crit("Loading timed out"); wmelvLoading(false); }, }); } /** * Triggered when selection changes (user selects or deselects a WME feature) * @param e {Event} */ function wmelvDrawVenueDetails(e) { if (W.selectionManager.hasSelectedFeatures() && W.selectionManager.getSelectedFeatures().length === 1 && W.selectionManager.getSelectedFeatures()[0].model.type === "venue") { const venue = W.selectionManager.getSelectedFeatures()[0].model.attributes; let warnings = [] let html = `<div class="action-buttons"><div class="alert addresses"></div>`; if (venue.fixAddress) warnings.push('Ēkas adrese, iespējams, nav korekta (tuvākā ir ' + venue.ads.waze_name + '; <a href="#" id="fixCurrentlySelectedVenueAddress">salabot</a>)'); if (venue.fixLowerCase) warnings.push('Ēkas numurs satur mazo burtu (<a href="" id="fixThisLowerCase">salabot</a>)'); if (venue.fixNameHNMismatch) warnings.push('Ēkas numurs nesakrīt ar numuru nosaukumā<br/>' + '(<a href="#" id="fixNameHNMismatchToAddress">pareiza adrese</a>, <a href="" id="fixNameHNMismatchToName">pareizs nosaukums</a>)'); if (venue.fixIela) warnings.push('Vietas nosaukums satur "' + venue.name.match(REGEXP_STREET_NAMES)[0] + '" (<a href="#" id="fixIelaInName">salabot</a>)'); if (venue.fixNoHN) warnings.push('Adrese ir, bet ēkas numura nav'); if (venue.fixArea) warnings.push('Laukuma platība ' + Math.floor(venue.fixArea) + 'm² ir mazāka par ' + MIN_VISIBLE_AREA + 'm² (<a href="#" id="fixThisArea">salabot</a>)'); html += warnings.map(warning => `<div class="alert alert-danger">${warning}</div>`).join(''); html += '</div>'; /** * Update landmark edit tab. */ if (!qs('#wmelv-landmark-edit')) { let div = document.createElement('div'); div.id = 'wmelv-landmark-edit'; qs('#venue-edit-general').insertBefore(div, qs('#venue-edit-general').firstChild); } qs('#wmelv-landmark-edit').innerHTML = html; if (qs('#fixCurrentlySelectedVenueAddress')) { qs('#fixCurrentlySelectedVenueAddress').addEventListener('click', (e) => { e.preventDefault(); let venue = W.selectionManager.getSelectedFeatures()[0]; const closest = getClosestAddress(venue.geometry.getCentroid()); wmelvSetVenueAddress(venue, closest); }) } if (qs('#fixNameHNMismatchToAddress')) { qs('#fixNameHNMismatchToAddress').addEventListener('click', (e) => { e.preventDefault(); let venue = W.selectionManager.getSelectedFeatures()[0], changes = { name: venue.model.attributes.houseNumber.replace('-', ' k-'), }; if (changes) W.model.actionManager.add(new UpdateObject(venue.model, changes)); wmelvUpdate(); }) } if (qs('#fixNameHNMismatchToName')) { qs('#fixNameHNMismatchToName').addEventListener('click', (e) => { e.preventDefault(); let venue = W.selectionManager.getSelectedFeatures()[0], changes = { houseNumber: venue.model.attributes.name.replace(' k-', '-'), }; if (changes) W.model.actionManager.add(new UpdateObject(venue.model, changes)); wmelvUpdate(); }) } if (qs('#fixThisArea')) { qs('#fixThisArea').addEventListener('click', fixCurrentlySelectedArea); } if (qs('#fixIelaInName')) { qs('#fixIelaInName').addEventListener('click', (e) => { e.preventDefault(); let venue = W.selectionManager.getSelectedFeatures()[0], name = venue.model.attributes.name, changes = { name: name.replace(REGEXP_STREET_NAMES, '$3'), }; if (changes) W.model.actionManager.add(new UpdateObject(venue.model, changes)); wmelvUpdate(); }); } if (qs('#fixThisLowerCase')) { qs('#fixThisLowerCase').addEventListener('click', (e) => { e.preventDefault(); let changes = {}; if (venue.attributes.houseNumber && venue.attributes.houseNumber.toUpperCase() !== venue.attributes.houseNumber) { changes.houseNumber = venue.attributes.houseNumber.toUpperCase(); } if (venue.attributes.name && venue.attributes.name.match(/^\d+[a-z][^a-z]*/)) { changes.name = venue.attributes.name.toUpperCase().replace(' K-', ' k-'); if (venue.attributes.houseNumber) changes.houseNumber = venue.attributes.houseNumber.toUpperCase(); } if (changes) W.model.actionManager.add(new UpdateObject(venue, changes)); wmelvUpdate(); }); } } } /** * Creates WME tab and registers all corresponding event listeners on inputs. */ function wmelvRegisterTab() { let style = document.getElementById('wmelv-style'); if (!style) { style = document.createElement('style'); style.id = 'wmelv-style'; document.head.appendChild(style); } style.innerText = ` #wmelv-loading-indicator { text-align: center; display: inline-block; } #wmelv-loading-indicator path, #wmelv-loading-indicator rect { fill: #FF6700; } #sidepanel-wmelv hr { height: 1px; width: 100%; border-top: 1px solid #ccc; } #sidepanel-wmelv label { white-space: normal; } .waze-btn:disabled { cursor: no-drop; } #applyAllAddresses { text-align: center; } .count { background-color: #009900; color: #fff; font-size: 90%; border-radius: 25%; padding-left: .5rem; margin-left: 1rem; padding-right: .5rem; } #wmelv-landmark-edit .alert { margin-bottom: .25rem; text-transform: none; font-size: 12px; } #wmelv-landmark-edit .alert-danger { border: 1px solid #ed503b; } .alert .waze-btn.waze-btn-blue { box-shadow: none; } #zoom-level-warning { color: red; font-weight: bold; } .wme-lv-details { font-weight: bold; color: green; } #wmelv-convert-to-area { margin-top: .25rem; } #wmelv-venues-list ul { margin: 1rem; padding: 0; } #wmelv-venues-list ul li { list-style-type: none; margin: 0; padding: 0; } #wmelv-venues-list [data-type]:before { content: ""; display: inline-block; position: relative; top: 1px; background-image: url(//editor-assets.waze.com/production/img/buttons756c103910d73f4d45328e08f6016871.png); width: 11px; height: 11px; margin-right: 5px; } #wmelv-venues-list [data-type="point"]:before { background-position: -49px -58px; } #wmelv-venues-list [data-type="area"]:before { background-position: -25px -58px; } #wmelv-venues-list li a { text-decoration: none; } .wmelv-lock-rank { box-shadow: 0 4px 4px 0 #def7ff; color: #fff; background: #32a852; height: 23px; line-height: 23px; margin-right: 1px; text-align: center; font-weight: normal; font-size: 13px; padding-left: 8px; padding-right: 8px; border-radius: 13px; } [data-lock-rank="0"] .wmelv-lock-rank, [data-lock-rank="1"] .wmelv-lock-rank { background: #ff7f00; } `; settings.load(); const html = ` <div class="form-group"> <span id="zoom-level-warning" style="display: none;">Zoom par mazu. Funkcionalitāte atslēgta.</span> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlSmallArea" id="hlSmallArea" ${settings.get('hlSmallArea') ? 'checked' : ''} ><label for="hlSmallArea"> Vietas, kuras mazākas par ${MIN_VISIBLE_AREA}m² (zils)</label> </div> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlIncorrectAddress" id="hlIncorrectAddress" ${settings.get('hlIncorrectAddress') ? 'checked' : ''} ><label for="hlIncorrectAddress"> Vietas, kurām adrešu reģistra adrese atšķiras no Waze (lillā)</label> </div> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlNoHN" id="hlNoHN" ${settings.get('hlNoHN') ? 'checked' : ''} ><label for="hlNoHN"> Adresē ir iela, pilsēta, bet nav ēkas numura (sarkans)</label> </div> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlIela" id="hlIela" ${settings.get('hlIela') ? 'checked' : ''} ><label for="hlIela"> Vietas nosaukumā ir vārds "iela" (sarkans)</label> </div> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlLowerCaseHN" id="hlLowerCaseHN" ${settings.get('hlLowerCaseHN') ? 'checked' : ''} ><label for="hlLowerCaseHN"> Ēkas numurā ir mazie burti (sarkans)</label> </div> </div> <div class="form-group"> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlNameHNMismatch" id="hlNameHNMismatch" ${settings.get('hlNameHNMismatch') ? 'checked' : ''} ><label for="hlNameHNMismatch"> Ēkas numurs adresē un numurs nosaukumā nesakrīt (dzeltens)</label> </div> </div> <div class="form-group" data-hide> <div class="controls-container"> <input type="checkbox" class="settings-input" name="hlDupes" id="hlDupes" ${settings.get('hlDupes') ? 'checked' : ''} ><label for="hlDupes"> Identiski objekti, viens uz otra (gaiši zils)</label> </div> </div> <div class="form-group" data-api-required> <div class="controls-container"> <input type="checkbox" class="settings-input" name="autoHN" id="autoHN" ${settings.get('autoHN') ? 'checked' : ''} ><label for="autoHN"> Ielādēt adreses automātiski</label> </div> </div> <div id="wmelv-place-info" data-api-required> <hr> <p id="wmlelv-place-info">Izveidot paralelograma formas vietu kartes centrā:</p> <div class="action-buttons"> <button class="waze-btn waze-btn-smaller" type="submit" id="wmelvCreatePlace" data-action="create-place" data-ratio="1:1">Izveidot 1:1</button> <button class="waze-btn waze-btn-smaller" type="submit" id="wmelvCreatePlace2" data-action="create-place" data-ratio="2:1">Izveidot 2:1</button> </div> </div> <hr> <p> Tu esi ${W.loginManager.user.userName} (level ${W.loginManager.user.rank + 1}), es esmu skripts. </p> <hr> <div class="form-group" data-api-required> <label class="control-label">Funkcionalitāte sāk darboties, ja zoom ir vismaz:</label> <div class="controls"> <div class="form-control waze-radio-container" style="display: block;"> <input type="radio" name="minHNZoomLevel" value="3" id="minHNZoomLevel-3" ${parseInt(settings.get('minHNZoomLevel')) === 3 ? 'checked' : ''}> <label for="minHNZoomLevel-3">3 (drosmīgajiem)</label> <input type="radio" name="minHNZoomLevel" value="4" id="minHNZoomLevel-4" ${parseInt(settings.get('minHNZoomLevel')) === 4 ? 'checked' : ''}> <label for="minHNZoomLevel-4">4</label> <input type="radio" name="minHNZoomLevel" value="5" id="minHNZoomLevel-5" ${parseInt(settings.get('minHNZoomLevel')) === 5 ? 'checked' : ''}> <label for="minHNZoomLevel-5">5</label> <input type="radio" name="minHNZoomLevel" value="6" id="minHNZoomLevel-6" ${parseInt(settings.get('minHNZoomLevel')) === 6 ? 'checked' : ''}> <label for="minHNZoomLevel-6">6</label> </div> </div> </div> <hr> <dl> <dt>2.07</dt> <dd>Veidojot vietas ārpus apdzīvotām vietām, adrese tagad tiek norādīta pareizi</dd> <dd>Adrešu validācija notiek tikai tām vietu kategorijām, kurām to ir jēga darīt</dd> <dt>2.06</dt> <dd>Minimālais laukuma izmērs samazināts uz uz 514m²</dd> <dd>Iespēja izveidot poligonu vietā, kur ieklikšķināts ar peli</dd> <dd>Adrešu labotājs apdzīvotās vietās darbojas.</dd> <dd>Ārpus apdzīvotajām vietām adreses tagad arī tiek ieteiktas korekti.</dd> <dt>2.05</dt> <dd>Minimālais <em>area</em> izmērs palielināts uz 550m²</dd> <dt>2.04</dt> <dd>Adreses maiņas rezultātā tā nomainās arī saskarnē.</dd> <dt>2.03</dt> <dd>Iespēja koriģēt kļūda (laukumu, adreses, utt) ar vienu pogas klikšķi.</dd> <dd>Paplašinājums vairs nesalauž Google StreetView.</dd> <dd>Vēl kārtīgi jātestē, jo daudz kas nestrādā.</dd> <dd>Ielādes indikācija blakus cilnes nosaukumam.</dd> <dt>2.02</dt> <dd>Salasāmāki māju numuri un nosaukumi.</dd> <dt>2.01</dt> <dd>Labota kļūda ar ķekškastīti pie VZD adrešu neatbilstību krāsošanas</dd> <dt>2.00</dt> <dd>Pirmā publiskā versija</dd> </dl> <hr> `; new WazeWrap.Interface.Tab('WME LV', html, () => { Array.from(document.querySelectorAll("#user-tabs > ul > li")).map((el) => { const a = el.querySelector('a') // Change tab title to title plus loading indicator if (a && a.innerText === 'WME LV') { a.innerHTML = ` WME LV <svg id="wmelv-loading-indicator" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="10px" height="10px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve"><path fill="#000" d="M43.935,25.145c0-10.318-8.364-18.683-18.683-18.683c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615c8.072,0,14.615,6.543,14.615,14.615H43.935z"><animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.6s" repeatCount="indefinite"/></path></svg> ` } }) // Attach settings change handler to corresponding inputs qsa('.settings-input').forEach(element => { element.addEventListener('change', event => { settings.set(event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value); wmelvUpdate(); wmelvLoadAddresses(); }) }); qsa('[name="minHNZoomLevel"]').forEach(element => { element.addEventListener('change', event => { settings.set('minHNZoomLevel', parseInt(event.target.value)); }); }); qsa('[data-action="create-place"]').forEach(el => { el.addEventListener('click', (e) => { const vertex = 22.57; const point = W.map.getCenter(); let place = new Landmark() let poly = new OpenLayers.Geometry.LinearRing([ new OpenLayers.Geometry.Point(point.lon - vertex, point.lat - (e.target.dataset.ratio === '2:1' ? vertex / 2 : vertex)), new OpenLayers.Geometry.Point(point.lon - vertex, point.lat + (e.target.dataset.ratio === '2:1' ? vertex / 2 : vertex)), new OpenLayers.Geometry.Point(point.lon + vertex, point.lat + (e.target.dataset.ratio === '2:1' ? vertex / 2 : vertex)), new OpenLayers.Geometry.Point(point.lon + vertex, point.lat - (e.target.dataset.ratio === '2:1' ? vertex / 2 : vertex)), new OpenLayers.Geometry.Point(point.lon - vertex, point.lat - (e.target.dataset.ratio === '2:1' ? vertex / 2 : vertex)), ]); poly.rotate(10, poly.getCentroid()); const closest = getClosestAddress(poly.getCentroid()); if (closest) { const house_name = closest.name.replace(' k-', '-'); const street_name = closest.iela_name ? closest.iela_name : ''; const city_name = closest.pilseta_name ? closest.pilseta_name : (closest.ciems_name ? closest.ciems_name : ''); const street = Object.values(W.model.streets.objects).find((street) => { if (street.name === street_name && W.model.cities.objects[street.cityID].attributes.name === city_name) { return true; } }); const params = {} params.streetID = street.id; if (closest.name.match(REGEXP_HOUSE_NUMBERS)) { params.houseNumber = house_name; params.name = closest.name; } else { params.name = closest.waze_name; } const place = wmelvCreateNewPlace(new OpenLayers.Geometry.Polygon([poly]), params); fixPlaceArea(place); } }); }); }); // // update(); } /** * Registers global handlers which apply to W and W.map. */ function wmelvRegisterGlobalHandlers() { W.map.events.register('zoomend', this, () => { // updateVenuesFilterList(); // updateAddressSuggestions(); }); W.map.events.register('moveend', this, () => { wmelvLoadAddresses(); }); W.selectionManager.events.register('selectionchanged', this, wmelvDrawVenueDetails); /** * Registers click handler to the main map. That's me - being a smartass. */ // W.map.events.register('mousedown', (e) => { // if (e && e.xy) { // lastClick = W.map.getLonLatFromViewPortPx(e.xy).clone(); // const point = (new OpenLayers.Geometry.Point(lastClick.lon, lastClick.lat)).transform('EPSG:900913', 'EPSG:4326'); // const closest = getClosestAddress(point); // console.log(closest); // qs('#wmlelv-place-info').innerHTML = closest // ? `Tuvākā vieta: <strong>${closest.waze_name}<strong>` // : 'Noklikšķini adreses tuvumā kartē un tad uz kādas no šīm pogām.'; // } // }, true) } /** * Initializes application. */ function init() { Logger.info('WMELV initializing...'); UpdateObject = require("Waze/Action/UpdateObject"); Landmark = require("Waze/Feature/Vector/Landmark"); AddLandmark = require("Waze/Action/AddLandmark"); DeleteSegment = require("Waze/Action/DeleteSegment"); wmelvRegisterGlobalHandlers(); wmelvRegisterTab(); wmelvLoadAddresses(); wmelvUpdate(); Logger.info('WMELV initialized.'); } /** * Starts waiting for W to load. Max 1000 times (100 seconds) * @param tries {number} */ function bootstrap(tries = 1) { if (W && W.map && W.model && W.loginManager.user && typeof W.selectionManager !== 'undefined' && typeof WazeWrap !== "undefined" && WazeWrap.Ready ) { init(); } else if (tries < 1000) { setTimeout(() => { bootstrap(tries + 1); }, 200); } } Logger.info('WMELV waiting...'); bootstrap(); }()); /* end ======================================================================= */
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址