WME Split POI

Split POI with a new seg

  1. /* eslint-disable max-len */
  2. /* eslint-disable prefer-destructuring */
  3. /* eslint-disable camelcase */
  4. // ==UserScript==
  5. // @name WME Split POI
  6. // @namespace https://gf.qytechs.cn/fr/scripts/13008-wme-split-poi
  7. // @description Split POI with a new seg
  8. // @description:fr Découpage d'un POI en deux en utisant un nouveau segment
  9. // @include https://www.waze.com/editor*
  10. // @include https://www.waze.com/*/editor*
  11. // @include https://beta.waze.com/editor*
  12. // @include https://beta.waze.com/*/editor*
  13. // @exclude https://www.waze.com/user*
  14. // @exclude https://www.waze.com/*/user*
  15. // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js
  16. // eslint-disable-next-line max-len
  17. // @icon data:image/png;base64,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
  18. // @author seb-d59, WazeDev (2023-?)
  19. // @version 2025-02-19-000
  20. // @license GPLv3
  21. // @grant GM_xmlhttpRequest
  22. // @connect gf.qytechs.cn
  23. // ==/UserScript==
  24.  
  25. /* global W */
  26. /* global OpenLayers */
  27. /* global WazeWrap */
  28.  
  29. (function main() {
  30. 'use strict';
  31.  
  32. const DEBUG = false;
  33. const SCRIPT_VERSION = GM_info.script.version;
  34. const SCRIPT_NAME = GM_info.script.name;
  35. const DOWNLOAD_URL = 'https://gf.qytechs.cn/scripts/13008-wme-split-poi/code/WME%20Split%20POI.user.js';
  36. const MINIMUM_AREA = 100.0;
  37.  
  38. let LandmarkVectorFeature;
  39. let DeleteObjectAction;
  40. let DeleteSegmentAction;
  41. let UpdateFeatureAddressAction;
  42. let MultiAction;
  43.  
  44. function bootstrap() {
  45. if (WazeWrap.Ready) {
  46. initialize();
  47. } else {
  48. setTimeout(bootstrap, 100);
  49. }
  50. }
  51.  
  52. function getId(node) {
  53. return document.getElementById(node);
  54. }
  55.  
  56. function log(msg, obj) {
  57. if (obj == null) {
  58. console.log(`WME Split POI v${SCRIPT_VERSION} - ${msg}`);
  59. } else if (DEBUG) {
  60. console.debug(`WME Split POI v${SCRIPT_VERSION} - ${msg} `, obj);
  61. }
  62. }
  63.  
  64. function initialize() {
  65. log('init');
  66. startScriptUpdateMonitor();
  67. initializeWazeObjects();
  68. }
  69.  
  70. function startScriptUpdateMonitor() {
  71. let updateMonitor;
  72. try {
  73. updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
  74. updateMonitor.start();
  75. } catch (ex) {
  76. // Report, but don't stop if ScriptUpdateMonitor fails.
  77. console.error(`${SCRIPT_NAME}:`, ex);
  78. }
  79. }
  80.  
  81. function initializeWazeObjects() {
  82. DeleteObjectAction = require('Waze/Action/DeleteObject');
  83. DeleteSegmentAction = require('Waze/Action/DeleteSegment');
  84. LandmarkVectorFeature = require('Waze/Feature/Vector/Landmark');
  85. UpdateFeatureAddressAction = require('Waze/Action/UpdateFeatureAddress');
  86. MultiAction = require('Waze/Action/MultiAction');
  87. W.selectionManager.events.register('selectionchanged', null, onSelectionChanged);
  88. }
  89.  
  90. function onSelectionChanged() {
  91. try {
  92. if (W.selectionManager.getSelectedDataModelObjects().length !== 1) return;
  93.  
  94. const selectedObject = W.selectionManager.getSelectedDataModelObjects()[0];
  95. if (selectedObject.type !== 'venue' || selectedObject.isPoint()) return;
  96.  
  97. // const landmarkPoi = '(NATURAL_FEATURES|ISLAND|SEA_LAKE_POOL|RIVER_STREAM|FOREST_GROVE|FARM|CANAL|SWAMP_MARSH|DAM|PARK)';
  98. // if (new RegExp(landmarkPoi).test(attributes.categories) === false) return;
  99.  
  100. log('selectionManager', W.selectionManager);
  101.  
  102. const editPanel = getId('edit-panel');
  103. if (editPanel.firstElementChild.style.display === 'none') {
  104. window.setTimeout(onSelectionChanged, 100);
  105. }
  106.  
  107. // ok: 1 selected item and pannel is shown
  108.  
  109. // On verifie que le segment est éditable
  110. if (!objIsEditable(selectedObject)) return;
  111.  
  112. // Exclude gas station and EVCS categories (don't ever want to delete those by splitting):
  113. if (selectedObject.attributes.categories.some(cat => ['GAS_STATION', 'CHARGING_STATION'].includes(cat))) return;
  114.  
  115. if (selectedObject.type === 'venue' && !$('#split-poi-button').length) {
  116. let $btnHandle = $('.geometry-type-control-area')[0];
  117. if (!$btnHandle) {
  118. $btnHandle = $('.external-providers-control')[0];
  119. }
  120. const WMESP_Controle = document.createElement('wz-button');
  121. WMESP_Controle.color = 'secondary';
  122. WMESP_Controle.size = 'sm';
  123. WMESP_Controle.id = 'split-poi-button';
  124. WMESP_Controle.className = 'geometry-type-control-button geometry-type-control-point';
  125. WMESP_Controle.innerHTML = '<i class="fa fa-cut" style="font-size:24px;" title="Split POI"></i>';
  126.  
  127. $btnHandle.after(WMESP_Controle);
  128.  
  129. WMESP_Controle.onclick = onSplitPoiButtonClick;
  130. }
  131. } catch (ex) {
  132. console.error('Split POI:', ex);
  133. }
  134. }
  135.  
  136. function onScreen(obj) {
  137. if (obj.geometry) {
  138. return (W.map.getExtent().intersectsBounds(obj.geometry.getBounds()));
  139. }
  140. return false;
  141. }
  142.  
  143. function objIsEditable(obj) {
  144. if (obj == null) return false;
  145. if (W.loginManager.user.isCountryManager()) return true;
  146. if (obj.attributes.permissions === 0) return false;
  147.  
  148. return true;
  149. }
  150.  
  151. // This will return null if more than one object is selected
  152. function getSelectedAreaPlace() {
  153. const selectedObjects = W.selectionManager.getSelectedDataModelObjects();
  154. if (selectedObjects.length > 1) return null;
  155. const object = selectedObjects[0];
  156. if (object.type !== 'venue' || object.isPoint()) return null;
  157. return object;
  158. }
  159.  
  160. function getNewestUnconnectedOnScreenSegment() {
  161. const newSegs = W.model.segments.getObjectArray(seg => seg.isNew());
  162. let newestSeg;
  163. let newestId = 0;
  164. newSegs.forEach(seg => {
  165. const hasConnections = seg.getToNode().getSegmentIds().length > 1 || seg.getFromNode().getSegmentIds().length > 1;
  166. if (seg.getID() < newestId && onScreen(seg) && !hasConnections) {
  167. newestSeg = seg;
  168. newestId = seg.getID();
  169. }
  170. });
  171. return newestSeg;
  172. }
  173.  
  174. function getPoiAndSegIntersectionPoints(poi, seg) {
  175. function clearComponent(geometry) {
  176. geometry.removeComponent(0);
  177. geometry.removeComponent(1);
  178. }
  179.  
  180. function copyComponent(sourceGeometry, sourceIndex, targetGeometry) {
  181. targetGeometry.components[0] = sourceGeometry.components[sourceIndex].clone();
  182. targetGeometry.components[1] = sourceGeometry.components[sourceIndex + 1].clone();
  183. }
  184.  
  185. const poiAttr = poi.attributes;
  186. const poiGeo = poiAttr.geometry.clone();
  187. const poiLineString = poiGeo.components[0].clone();
  188. const segLineString = seg.attributes.geometry.clone();
  189.  
  190. const intersectPoint = [];
  191. const poiLine = new OpenLayers.Geometry.LinearRing();
  192. const segLine = new OpenLayers.Geometry.LinearRing();
  193.  
  194. // Calcul des point d'intersection seg // poi
  195. for (let n = 0; n < poiLineString.components.length - 1; n++) {
  196. copyComponent(poiLineString, n, poiLine);
  197. for (let m = 0; m < segLineString.components.length - 1; m++) {
  198. copyComponent(segLineString, m, segLine);
  199. if (poiLine.intersects(segLine)) {
  200. intersectPoint.push({ index: n, intersect: intersection(poiLine, segLine) });
  201. }
  202. clearComponent(segLine);
  203. }
  204. clearComponent(poiLine);
  205. }
  206.  
  207. return intersectPoint;
  208. }
  209.  
  210. function createTwoPolygonsFromIntersectPoints(poi, intersectPoints) {
  211. const poiLineString = poi.attributes.geometry.components[0].clone();
  212. // intégration des points au contour du POI avec memo du nouvel index
  213. let i = 1;
  214. for (let n = 0; n < intersectPoints.length; n++) {
  215. const point = intersectPoints[n].intersect;
  216. const index = intersectPoints[n].index + i;
  217. poiLineString.addComponent(point, index);
  218. intersectPoints[n].newIndex = index;
  219. i++;
  220. }
  221.  
  222. // création des deux nouvelles géométries
  223. const lineString1 = [];
  224. const lineString2 = [];
  225.  
  226. const index1 = intersectPoints[0].newIndex;
  227. const index2 = intersectPoints[1].newIndex;
  228.  
  229. for (let n = 0; n < poiLineString.components.length; n++) {
  230. const x = poiLineString.components[n].x;
  231. const y = poiLineString.components[n].y;
  232. const point = new OpenLayers.Geometry.Point(x, y);
  233.  
  234. if (n < index1) {
  235. lineString1.push(point);
  236. } else if (n === index1) {
  237. lineString1.push(point);
  238. lineString2.push(point.clone());
  239. } else if ((index1 < n) && (n < index2)) {
  240. lineString2.push(point);
  241. } else if (n === index2) {
  242. lineString1.push(point);
  243. lineString2.push(point.clone());
  244. } else if (index2 < n) {
  245. lineString1.push(point);
  246. }
  247. }
  248.  
  249. return [
  250. new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(lineString1)),
  251. new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(lineString2))
  252. ];
  253. }
  254.  
  255. function cloneAttribute(poi, attrName, newAttributesObject) {
  256. if (poi.attributes.hasOwnProperty(attrName)) {
  257. let value = poi.attributes[attrName];
  258.  
  259. if (Array.isArray(value)) {
  260. value = value.slice(0); // copy array
  261. }
  262. newAttributesObject[attrName] = poi.attributes[attrName];
  263. }
  264. }
  265.  
  266. function addClonePoiAction(poi, newGeometry, nameSuffixIndex, actions) {
  267. const clonePoi = new LandmarkVectorFeature({ geoJSONGeometry: W.userscripts.toGeoJSONGeometry(newGeometry) });
  268. [
  269. 'aliases',
  270. 'categories',
  271. 'description',
  272. 'entryExitPoints',
  273. 'externalProviderIDs',
  274. 'houseNumber',
  275. 'lockRank',
  276. 'name',
  277. 'openingHours',
  278. 'phone',
  279. 'services',
  280. 'streetID',
  281. 'url'
  282. ].forEach(attrName => cloneAttribute(poi, attrName, clonePoi.attributes));
  283. if (clonePoi.attributes.name) clonePoi.attributes.name += ` (copy ${nameSuffixIndex})`; // IMPORTANT! Won't save for some reason without changing the names (at least for PLAs).
  284. if (poi.attributes.categoryAttributes.PARKING_LOT) {
  285. clonePoi.attributes.categoryAttributes.PARKING_LOT = JSON.parse(JSON.stringify(poi.attributes.categoryAttributes.PARKING_LOT));
  286. }
  287.  
  288. const WazeActionAddLandmark = require('Waze/Action/AddLandmark');
  289. actions.push(new WazeActionAddLandmark(clonePoi));
  290.  
  291. const street = W.model.streets.getObjectById(poi.attributes.streetID);
  292. const streetName = street.attributes.name;
  293. const cityID = street.attributes.cityID;
  294. const city = W.model.cities.getObjectById(cityID);
  295. const stateID = city.attributes.stateID;
  296. const countryID = city.attributes.countryID;
  297. const houseNumber = poi.attributes.houseNumber;
  298. if (!street.attributes.isEmpty || !city.attributes.isEmpty) { // nok
  299. const newAtts = {
  300. emptyStreet: street.attributes.isEmpty, // TODO: fix this
  301. stateID,
  302. countryID,
  303. cityName: city.attributes.name,
  304. houseNumber,
  305. streetName,
  306. emptyCity: city.attributes.isEmpty // TODO: fix this
  307. };
  308. const updateAddressAction = new UpdateFeatureAddressAction(clonePoi, newAtts);
  309. updateAddressAction.options.updateHouseNumber = true;
  310. actions.push(updateAddressAction);
  311. }
  312. }
  313.  
  314. function confirmBeforeSplitting(poi) {
  315. const entryExitPointsLen = poi.attributes.entryExitPoints?.length;
  316. const imagesLen = poi.attributes.images?.length;
  317. const extProvidersLen = poi.attributes.externalProviderIDs?.length;
  318. let warningText = 'WARNING: The original place will be deleted!';
  319.  
  320. if (imagesLen) {
  321. warningText += '\n\nThe following property(s) will be lost:';
  322. if (imagesLen) warningText += `\n ${imagesLen} photo${imagesLen === 1 ? '' : 's'} (permanently deleted after saving)`;
  323. }
  324. warningText += '\n\nThe following properties likely need to be changed after splitting:';
  325. warningText += '\n • name ("copy #" will be appended)';
  326. if (entryExitPointsLen) warningText += `\n ${entryExitPointsLen} entry/exit point${entryExitPointsLen === 1 ? '' : 's'}`;
  327. if (extProvidersLen) warningText += `\n ${extProvidersLen} linked Google place${extProvidersLen === 1 ? '' : 's'}`;
  328. warningText += '\n\nReview <i>all</i> properties of both new places before saving.';
  329. warningText += '\n';
  330. return new Promise(resolve => {
  331. WazeWrap.Alerts.confirm(
  332. SCRIPT_NAME,
  333. warningText,
  334. () => resolve(true),
  335. () => resolve(false)
  336. );
  337. });
  338. }
  339.  
  340. async function onSplitPoiButtonClick() {
  341. const poi = getSelectedAreaPlace();
  342. if (!poi) return;
  343.  
  344. // This is needed in case the category is changed to GS or EVCS and the split button is still there.
  345. if (poi.attributes.categories.some(cat => ['GAS_STATION', 'CHARGING_STATION'].includes(cat))) {
  346. WazeWrap.Alerts.error(SCRIPT_NAME, 'Cannot split gas stations or EV charging stations');
  347. return;
  348. }
  349.  
  350. const seg = getNewestUnconnectedOnScreenSegment();
  351. if (!seg) {
  352. WazeWrap.Alerts.error(SCRIPT_NAME, 'Create a temporary unconnected road segment through the area place first.');
  353. return;
  354. }
  355.  
  356. if (seg.geometry.components.some(pt => poi.geometry.containsPoint(pt))) {
  357. WazeWrap.Alerts.error(SCRIPT_NAME, 'The splitting road segment must be straight (no geometry handles within the POI).');
  358. return;
  359. }
  360.  
  361. const intersectPoints = getPoiAndSegIntersectionPoints(poi, seg);
  362. if (intersectPoints.length !== 2) {
  363. WazeWrap.Alerts.error(SCRIPT_NAME, 'The temporary road segment must intersect the area place boundary at two points.');
  364. return;
  365. }
  366.  
  367. const newPolygons = createTwoPolygonsFromIntersectPoints(poi, intersectPoints);
  368. if (newPolygons[0].getArea() < MINIMUM_AREA || newPolygons[1].getArea() < MINIMUM_AREA) {
  369. WazeWrap.Alerts.error(SCRIPT_NAME, 'New area place would be too small. Move the temporary road segment.');
  370. return;
  371. }
  372.  
  373. const confirm = await confirmBeforeSplitting(poi);
  374. if (confirm) {
  375. const actions = [];
  376. addClonePoiAction(poi, newPolygons[0], 1, actions);
  377. addClonePoiAction(poi, newPolygons[1], 2, actions);
  378. actions.push(new DeleteObjectAction(poi, null));
  379. actions.push(new DeleteSegmentAction(seg));
  380. const multiaction = new MultiAction(actions, { description: 'Split POI' });
  381. W.model.actionManager.add(multiaction);
  382. }
  383. }
  384.  
  385. function intersection(D1, D2) {
  386. // let a, b, c, d, x, y;
  387. // const seg = {}; // {x1, y1, x2, y2};
  388. const seg1 = {}; // {x1, y1, x2, y2};
  389. const seg2 = {}; // {x1, y1, x2, y2};
  390. const options = {};
  391. options.point = true;
  392.  
  393. if (D1.components[0].x <= D1.components[1].x) {
  394. seg1.x1 = D1.components[0].x;
  395. seg1.y1 = D1.components[0].y;
  396. seg1.x2 = D1.components[1].x;
  397. seg1.y2 = D1.components[1].y;
  398. } else if (D1.components[0].x > D1.components[1].x) {
  399. seg1.x1 = D1.components[1].x;
  400. seg1.y1 = D1.components[1].y;
  401. seg1.x2 = D1.components[0].x;
  402. seg1.y2 = D1.components[0].y;
  403. }
  404.  
  405. if (D2.components[0].x <= D2.components[1].x) {
  406. seg2.x1 = D2.components[0].x;
  407. seg2.y1 = D2.components[0].y;
  408. seg2.x2 = D2.components[1].x;
  409. seg2.y2 = D2.components[1].y;
  410. } else if (D2.components[0].x > D2.components[1].x) {
  411. seg2.x1 = D2.components[1].x;
  412. seg2.y1 = D2.components[1].y;
  413. seg2.x2 = D2.components[0].x;
  414. seg2.y2 = D2.components[0].y;
  415. }
  416. return OpenLayers.Geometry.segmentsIntersect(seg1, seg2, options);
  417. }
  418.  
  419. bootstrap();
  420. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址