WME GIS Layers

Adds GIS layers in WME

目前为 2024-11-05 提交的版本。查看 最新版本

  1. /* eslint-disable camelcase */
  2. /* eslint-disable brace-style, curly, nonblock-statement-body-position, no-template-curly-in-string, func-names */
  3. // ==UserScript==
  4. // @name WME GIS Layers
  5. // @namespace https://gf.qytechs.cn/users/45389
  6. // @version 2024.11.05.000
  7. // @description Adds GIS layers in WME
  8. // @author MapOMatic
  9. // @match *://*.waze.com/*editor*
  10. // @exclude *://*.waze.com/user/editor*
  11. // @require https://gf.qytechs.cn/scripts/24851-wazewrap/code/WazeWrap.js
  12. // @require https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js
  13. // @require https://update.gf.qytechs.cn/scripts/506614/1441195/ESTreeProcessor.js
  14. // @connect gf.qytechs.cn
  15. // @grant GM_xmlhttpRequest
  16. // @grant GM_info
  17. // @license GNU GPLv3
  18. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  19. // @connect *
  20. // @connect tigerweb.geo.census.gov
  21. // @connect 136.234.13.165
  22. // @connect 216.167.160.20
  23. // @connect 35.172.145.31
  24. // @connect 52.37.30.30
  25. // @connect 54.213.14.253
  26. // @connect 65.183.210.212
  27. // @connect 72.10.206.73
  28. // @connect a2maps.a2gov.org
  29. // @connect adairgis.integritygis.com
  30. // @connect agis.charlottecountyfl.gov
  31. // @connect ago.clarkcountyohio.gov
  32. // @connect agomaps.larimer.org
  33. // @connect ags.agdmaps.com
  34. // @connect ags.bhamaps.com
  35. // @connect ags.kitsapgov.com
  36. // @connect ags.myokaloosa.com
  37. // @connect ags.roseville.ca.us
  38. // @connect ags1.wgxtreme.com
  39. // @connect ags10s1.dot.illinois.gov
  40. // @connect ags2maps.srcity.org
  41. // @connect ags3.scgov.net
  42. // @connect aldotgis.dot.state.al.us
  43. // @connect alleganygis.allconet.org
  44. // @connect alphagis.alpharetta.ga.us
  45. // @connect andrewgis.integritygis.com
  46. // @connect anrmaps.vermont.gov
  47. // @connect ansoncountygis.com
  48. // @connect apnsgis1.apsu.edu
  49. // @connect apnsgis4.apsu.edu
  50. // @connect app.mdt.mt.gov
  51. // @connect apps.alamance-nc.com
  52. // @connect apps.douglas.co.us
  53. // @connect apps.fs.usda.gov
  54. // @connect apps.lickingcounty.gov
  55. // @connect apps.vernoncounty.org
  56. // @connect apps.wyoroad.info
  57. // @connect arcgis-morrowarcgis-1015369042.us-east-1.elb.amazonaws.com
  58. // @connect arcgis-web.chinohills.org
  59. // @connect arcgis.atlantaregional.com
  60. // @connect arcgis.c3gov.com
  61. // @connect arcgis.cityofcapegirardeau.org
  62. // @connect arcgis.cityofwatsonville.org
  63. // @connect arcgis.co.beltrami.mn.us
  64. // @connect arcgis.co.henry.ga.us
  65. // @connect arcgis.co.lancaster.pa.us
  66. // @connect arcgis.forneytx.gov
  67. // @connect arcgis.gis.lacounty.gov
  68. // @connect arcgis.kingsporttn.gov
  69. // @connect arcgis.leaguecitytx.gov
  70. // @connect arcgis.lewiscountywa.gov
  71. // @connect arcgis.mobile311.com
  72. // @connect arcgis.racinecounty.com
  73. // @connect arcgis.tampagov.net
  74. // @connect arcgis.tuscco.com
  75. // @connect arcgis.vgsi.com
  76. // @connect arcgis.water.nv.gov
  77. // @connect arcgis.waxahachie.com
  78. // @connect arcgis.yumacountyaz.gov
  79. // @connect arcgis4.roktech.net
  80. // @connect arcgis5.roktech.net
  81. // @connect arcgisce.co.valencia.nm.us
  82. // @connect arcgisserver.digital.mass.gov
  83. // @connect arcgisserver.lincolncounty.org
  84. // @connect arcgisserver.maine.gov
  85. // @connect arcgisserver2.morpc.org
  86. // @connect arcgissrv.cityofbartlesville.org
  87. // @connect arcgiswap01.ci.temple.tx.us
  88. // @connect arcgisweb.carteretcountync.gov
  89. // @connect arcgisweb.countyofnewaygo.com
  90. // @connect arcmobile.co.albany.wy.us
  91. // @connect arcportal.florenceco.org
  92. // @connect arcserv.co.washington.ar.us
  93. // @connect arcserver.madisoncountyky.us
  94. // @connect arcserver2.oconeesc.com
  95. // @connect arcweb.hcad.org
  96. // @connect ardmoregis.ardmorecity.org
  97. // @connect arlgis.arlingtonva.us
  98. // @connect atchisongis.integritygis.com
  99. // @connect atlas.co.chelan.wa.us
  100. // @connect atlas.geoportalmaps.com
  101. // @connect audraingis.integritygis.com
  102. // @connect batesgis.integritygis.com
  103. // @connect bcgis.baltimorecountymd.gov
  104. // @connect bcgis.brunswickcountync.gov
  105. // @connect bcgishub.broward.org
  106. // @connect bcmaps.bradfordco.org
  107. // @connect bentongis.integritygis.com
  108. // @connect biamaps.geoplatform.gov
  109. // @connect bocagis.ci.boca-raton.fl.us
  110. // @connect bonneville.esriemcs.com
  111. // @connect bpagis.bossierparish.org
  112. // @connect bryangis.bryan-county.org
  113. // @connect buchanangis.integritygis.com
  114. // @connect butlergis.integritygis.com
  115. // @connect c39gisserver.co.richland.nd.us
  116. // @connect ca.dep.state.fl.us
  117. // @connect cagisonline.hamilton-co.org
  118. // @connect calmaps.co.calumet.wi.us
  119. // @connect caltrans-gis.dot.ca.gov
  120. // @connect cama.shelbycountyauditors.com
  121. // @connect camdengis.integritygis.com
  122. // @connect cassweb3.co.cass.mn.us
  123. // @connect cceo.co.comal.tx.us
  124. // @connect ccmap.cccounty.us
  125. // @connect cecilmaps.org
  126. // @connect charitongis.integritygis.com
  127. // @connect christiangis.integritygis.com
  128. // @connect clearfieldco.org
  129. // @connect cloud.longviewtexas.gov
  130. // @connect cloudgis.bonnercountyid.gov
  131. // @connect co.knox.il.us
  132. // @connect coagisweb.cabq.gov
  133. // @connect com.blountgis.org
  134. // @connect conservationgis.alabama.gov
  135. // @connect coopergis.integritygis.com
  136. // @connect covgis.cityofvacaville.com
  137. // @connect coweta-gis-web.coweta.ga.us
  138. // @connect cowlitzgis.net
  139. // @connect crgis.cedar-rapids.org
  140. // @connect cteco.uconn.edu
  141. // @connect currituckncgov.com
  142. // @connect cw.townofclaytonnc.org
  143. // @connect dadegis.integritygis.com
  144. // @connect dallasgis.integritygis.com
  145. // @connect data.wsdot.wa.gov
  146. // @connect data1.digitaldataservices.com
  147. // @connect dc-web-2.co.douglas.mn.us
  148. // @connect dcgis.dekalbcountyga.gov
  149. // @connect dcimapapps.countyofdane.com
  150. // @connect dekalbgis.integritygis.com
  151. // @connect delta.co.clatsop.or.us
  152. // @connect doniphangis.integritygis.com
  153. // @connect dotapp9.dot.state.mn.us
  154. // @connect douglasgis.integritygis.com
  155. // @connect dtdapps.coloradodot.info
  156. // @connect dungis.dunwoodyga.gov
  157. // @connect dunklingis.integritygis.com
  158. // @connect ecgis.co.ellis.tx.us
  159. // @connect egis.baltimorecity.gov
  160. // @connect egis.pinellas.gov
  161. // @connect elb.elevatemaps.io
  162. // @connect emapsplus.com
  163. // @connect enigma.accgov.com
  164. // @connect enterprise.firstmap.delaware.gov
  165. // @connect eoc.franklin-gov.com
  166. // @connect epv.ci.juneau.ak.us
  167. // @connect eservices.co.crook.or.us
  168. // @connect essex-gis.co.essex.ny.us
  169. // @connect feature.geographic.texas.gov
  170. // @connect feature.tnris.org
  171. // @connect fieldstone.orangecountync.gov
  172. // @connect firstmap.gis.delaware.gov
  173. // @connect fremontgis.com
  174. // @connect gasconadegis.integritygis.com
  175. // @connect gcgis.guilfordcountync.gov
  176. // @connect geaugarealink.co.geauga.oh.us
  177. // @connect geo.co.harrison.ms.us
  178. // @connect geo.dentoncad.com
  179. // @connect geo.forsythco.com
  180. // @connect geo.friscotexas.gov
  181. // @connect geo.oit.ohio.gov
  182. // @connect geo.sandag.org
  183. // @connect geo.sanjoseca.gov
  184. // @connect geo.tompkins-co.org
  185. // @connect geo1.oit.ohio.gov
  186. // @connect geo2.co.dodge.wi.us
  187. // @connect geodata.hawaii.gov
  188. // @connect geodata.md.gov
  189. // @connect geodata.sarpy.com
  190. // @connect geodataportal.net
  191. // @connect geoint2.odessa-tx.gov
  192. // @connect geopower.jws.com
  193. // @connect geoweb.martin.fl.us
  194. // @connect geoweb02.ci.richmond.ca.us
  195. // @connect gis-2.warrencountyny.gov
  196. // @connect gis-server.co.becker.mn.us
  197. // @connect gis-server.co.montezuma.co.us
  198. // @connect gis.aacounty.org
  199. // @connect gis.abilenetx.com
  200. // @connect gis.adamscounty.org
  201. // @connect gis.addisontx.gov
  202. // @connect gis.aecomonline.net
  203. // @connect gis.allegancounty.org
  204. // @connect gis.allencountyohio.com
  205. // @connect gis.apachejunctionaz.gov
  206. // @connect gis.arapahoegov.com
  207. // @connect gis.arkansas.gov
  208. // @connect gis.arlingtonva.us
  209. // @connect gis.ashecountygov.com
  210. // @connect gis.ashevillenc.gov
  211. // @connect gis.atlantaga.gov
  212. // @connect gis.auburnalabama.org
  213. // @connect gis.auglaizecounty.org
  214. // @connect gis.azdot.gov
  215. // @connect gis.bakersfieldcity.us
  216. // @connect gis.baycountyfl.gov
  217. // @connect gis.beaufortcountysc.gov
  218. // @connect gis.beaumonttexas.gov
  219. // @connect gis.belmont.gov
  220. // @connect gis.bentoncountyar.gov
  221. // @connect gis.berkeleycountysc.gov
  222. // @connect gis.bgky.org
  223. // @connect gis.bigstonecounty.gov
  224. // @connect gis.bladenco.org
  225. // @connect gis.blairco.org
  226. // @connect gis.blm.gov
  227. // @connect gis.blueearthcountymn.gov
  228. // @connect gis.bransonmo.gov
  229. // @connect gis.brevardfl.gov
  230. // @connect gis.browncountywi.gov
  231. // @connect gis.buncombecounty.org
  232. // @connect gis.burkenc.org
  233. // @connect gis.burleighco.com
  234. // @connect gis.buttecounty.net
  235. // @connect gis.caldwellcountync.org
  236. // @connect gis.calhouncounty.org
  237. // @connect gis.carboncounty.com
  238. // @connect gis.cayugacounty.us
  239. // @connect gis.cccounty.us
  240. // @connect gis.ccgisonline.com
  241. // @connect gis.ccgov.net
  242. // @connect gis.ccpa.net
  243. // @connect gis.cedarhilltx.com
  244. // @connect gis.cherokeega.com
  245. // @connect gis.chippewa.mn
  246. // @connect gis.chisagocountymn.gov
  247. // @connect gis.ci.janesville.wi.us
  248. // @connect gis.ci.mcminnville.or.us
  249. // @connect gis.ci.waco.tx.us
  250. // @connect gis.citruspa.org
  251. // @connect gis.cityofaikensc.gov
  252. // @connect gis.cityofberkeley.info
  253. // @connect gis.cityofboston.gov
  254. // @connect gis.cityofdenton.com
  255. // @connect gis.cityofirvine.org
  256. // @connect gis.cityofmiddletown.com
  257. // @connect gis.cityofmoore.com
  258. // @connect gis.cityofsanmateo.org
  259. // @connect gis.clark.wa.gov
  260. // @connect gis.clevelandtn.gov
  261. // @connect gis.cmpdd.org
  262. // @connect gis.co.benton.or.us
  263. // @connect gis.co.berks.pa.us
  264. // @connect gis.co.butler.pa.us
  265. // @connect gis.co.carlton.mn.us
  266. // @connect gis.co.carver.mn.us
  267. // @connect gis.co.clarion.pa.us
  268. // @connect gis.co.cumberland.nc.us
  269. // @connect gis.co.douglas.or.us
  270. // @connect gis.co.eau-claire.wi.us
  271. // @connect gis.co.fillmore.mn.us
  272. // @connect gis.co.grand.co.us
  273. // @connect gis.co.grant.mn.us
  274. // @connect gis.co.grant.wi.gov
  275. // @connect gis.co.green-lake.wi.us
  276. // @connect gis.co.holmes.oh.us
  277. // @connect gis.co.hubbard.mn.us
  278. // @connect gis.co.isanti.mn.us
  279. // @connect gis.co.josephine.or.us
  280. // @connect gis.co.kittitas.wa.us
  281. // @connect gis.co.linn.or.us
  282. // @connect gis.co.mille-lacs.mn.us
  283. // @connect gis.co.nezperce.id.us
  284. // @connect gis.co.oneida.wi.us
  285. // @connect gis.co.pepin.wi.us
  286. // @connect gis.co.pierce.wi.us
  287. // @connect gis.co.polk.mn.us
  288. // @connect gis.co.richland.wi.us
  289. // @connect gis.co.roseau.mn.us
  290. // @connect gis.co.routt.co.us
  291. // @connect gis.co.sangamon.il.us
  292. // @connect gis.co.sauk.wi.us
  293. // @connect gis.co.sherburne.mn.us
  294. // @connect gis.co.stearns.mn.us
  295. // @connect gis.co.stevens.mn.us
  296. // @connect gis.co.tuscarawas.oh.us
  297. // @connect gis.co.wadena.mn.us
  298. // @connect gis.co.waseca.mn.us
  299. // @connect gis.co.waushara.wi.us
  300. // @connect gis.co.wood.wi.us
  301. // @connect gis.co.ym.mn.gov
  302. // @connect gis.cobbcounty.org
  303. // @connect gis.colorado.gov
  304. // @connect gis.coloradosprings.gov
  305. // @connect gis.columbiacountyga.gov
  306. // @connect gis.columbiacountymaps.com
  307. // @connect gis.columbiasc.gov
  308. // @connect gis.columbusga.org
  309. // @connect gis.concordnh.gov
  310. // @connect gis.cookeville-tn.org
  311. // @connect gis.corvallisoregon.gov
  312. // @connect gis.cosb.us
  313. // @connect gis.countyofriverside.us
  314. // @connect gis.cowleycounty.org
  315. // @connect gis.cranstonri.org
  316. // @connect gis.cravencountync.gov
  317. // @connect gis.crcog.org
  318. // @connect gis.crookcounty.wy.gov
  319. // @connect gis.crowwing.us
  320. // @connect gis.cstx.gov
  321. // @connect gis.danville-va.gov
  322. // @connect gis.dauphincounty.org
  323. // @connect gis.dbqco.org
  324. // @connect gis.deerparktx.gov
  325. // @connect gis.dekalbcountyga.gov
  326. // @connect gis.delcopa.gov
  327. // @connect gis.dentoncounty.gov
  328. // @connect gis.districtiii.org
  329. // @connect gis.dogis.org
  330. // @connect gis.donaanacounty.org
  331. // @connect gis.dot.nh.gov
  332. // @connect gis.dot.nv.gov
  333. // @connect gis.dot.state.oh.us
  334. // @connect gis.douglascountyks.org
  335. // @connect gis.dupageco.org
  336. // @connect gis.duplincountync.com
  337. // @connect gis.dutchessny.gov
  338. // @connect gis.eastgreenwichri.com
  339. // @connect gis.ebparks.org
  340. // @connect gis.edgecombecountync.gov
  341. // @connect gis.edmondok.gov
  342. // @connect gis.elkocountynv.net
  343. // @connect gis.elpasotexas.gov
  344. // @connect gis.emmetcounty.org
  345. // @connect gis.eriecountypa.gov
  346. // @connect gis.fortlauderdale.gov
  347. // @connect gis.franklincountyohio.gov
  348. // @connect gis.franklincountypa.gov
  349. // @connect gis.fultoncountyoh.com
  350. // @connect gis.fwb.org
  351. // @connect gis.fwp.mt.gov
  352. // @connect gis.gallatin.mt.gov
  353. // @connect gis.gallupnm.us
  354. // @connect gis.garrettcounty.org
  355. // @connect gis.gastongov.com
  356. // @connect gis.gcrc.org
  357. // @connect gis.gilacountyaz.gov
  358. // @connect gis.gocolumbiamo.com
  359. // @connect gis.goshencounty.org
  360. // @connect gis.gptx.org
  361. // @connect gis.grandcountyutah.net
  362. // @connect gis.greenecountyohio.gov
  363. // @connect gis.greenegovernment.com
  364. // @connect gis.greensboro-nc.gov
  365. // @connect gis.gscplanning.com
  366. // @connect gis.hardeecounty.net
  367. // @connect gis.harnett.org
  368. // @connect gis.hartford.gov
  369. // @connect gis.hawaiicounty.gov
  370. // @connect gis.hcpafl.org
  371. // @connect gis.hennepin.us
  372. // @connect gis.huntingtonbeachca.gov
  373. // @connect gis.iberiagov.net
  374. // @connect gis.interdev.com
  375. // @connect gis.inyoco.com
  376. // @connect gis.iowadot.gov
  377. // @connect gis.itd.idaho.gov
  378. // @connect gis.jacksonnc.org
  379. // @connect gis.jccal.org
  380. // @connect gis.johnson-county.com
  381. // @connect gis.johnsoncitytn.org
  382. // @connect gis.kalamazoocity.org
  383. // @connect gis.kanawhacountyassessor.com
  384. // @connect gis.kaufmancounty.net
  385. // @connect gis.kcgov.us
  386. // @connect gis.kcmn.us
  387. // @connect gis.kentcountyde.gov
  388. // @connect gis.kentcountymi.gov
  389. // @connect gis.kleinfelder.com
  390. // @connect gis.lacrossecounty.org
  391. // @connect gis.lafayettecountywi.org
  392. // @connect gis.lakecountyfl.gov
  393. // @connect gis.lakecountyohio.gov
  394. // @connect gis.lapazcountyaz.org
  395. // @connect gis.laplata.co.us
  396. // @connect gis.lasallecounty.org
  397. // @connect gis.latah.id.us
  398. // @connect gis.leecountyil.com
  399. // @connect gis.lehighcounty.org
  400. // @connect gis.leoc.net
  401. // @connect gis.linncounty.org
  402. // @connect gis.littleelm.org
  403. // @connect gis.livingstoncounty.us
  404. // @connect gis.lja.com
  405. // @connect gis.lojic.org
  406. // @connect gis.losalamosnm.us
  407. // @connect gis.luzernecounty.org
  408. // @connect gis.lyco.org
  409. // @connect gis.lyon-county.org
  410. // @connect gis.macombgov.org
  411. // @connect gis.maconnc.org
  412. // @connect gis.maderacounty.com
  413. // @connect gis.marinpublic.com
  414. // @connect gis.marionfl.org
  415. // @connect gis.masoncountywa.gov
  416. // @connect gis.massdot.state.ma.us
  417. // @connect gis.mbakerintl.com
  418. // @connect gis.mckeancountypa.gov
  419. // @connect gis.mcohio.org
  420. // @connect gis.mendocinocounty.org
  421. // @connect gis.mercercountyohio.org
  422. // @connect gis.mercercountypa.gov
  423. // @connect gis.mesaaz.gov
  424. // @connect gis.mifflincountypa.gov
  425. // @connect gis.minnehahacounty.org
  426. // @connect gis.miottawa.org
  427. // @connect gis.missoulacounty.us
  428. // @connect gis.modestogov.com
  429. // @connect gis.mono.ca.gov
  430. // @connect gis.montgomeryal.gov
  431. // @connect gis.moorecountync.gov
  432. // @connect gis.mytoddcounty.com
  433. // @connect gis.napa.ca.gov
  434. // @connect gis.nashcountync.gov
  435. // @connect gis.nassaucountyny.gov
  436. // @connect gis.nccde.org
  437. // @connect gis.ne.gov
  438. // @connect gis.neccog.org
  439. // @connect gis.newedgeservices.com
  440. // @connect gis.newhavenct.gov
  441. // @connect gis.nhcgov.com
  442. // @connect gis.niagaracounty.com
  443. // @connect gis.nola.gov
  444. // @connect gis.norrycopa.net
  445. // @connect gis.northamptoncounty.org
  446. // @connect gis.odot.state.or.us
  447. // @connect gis.ohiodnr.gov
  448. // @connect gis.okc.gov
  449. // @connect gis.orangecountygov.com
  450. // @connect gis.orangecountyva.gov
  451. // @connect gis.osceola.org
  452. // @connect gis.outagamie.org
  453. // @connect gis.owensboro.org
  454. // @connect gis.pandai.com
  455. // @connect gis.pendercountync.gov
  456. // @connect gis.pendoreilleco.org
  457. // @connect gis.penndot.gov
  458. // @connect gis.penndot.pa.gov
  459. // @connect gis.peoriacounty.gov
  460. // @connect gis.personcountync.gov
  461. // @connect gis.pgatlas.com
  462. // @connect gis.pikepa.org
  463. // @connect gis.pinal.gov
  464. // @connect gis.pittcountync.gov
  465. // @connect gis.pittsburgca.gov
  466. // @connect gis.popecountymn.gov
  467. // @connect gis.port-orange.org
  468. // @connect gis.pottcounty-ia.gov
  469. // @connect gis.putnam-fl.com
  470. // @connect gis.qac.org
  471. // @connect gis.randolphcountync.gov
  472. // @connect gis.rapides911.org
  473. // @connect gis.rcgov.org
  474. // @connect gis.renvillecountymn.com
  475. // @connect gis.rileycountyks.gov
  476. // @connect gis.rocklin.ca.us
  477. // @connect gis.rowancountync.gov
  478. // @connect gis.rrnm.gov
  479. // @connect gis.rtcsnv.com
  480. // @connect gis.rutherfordcountync.gov
  481. // @connect gis.salkeiz.k12.or.us
  482. // @connect gis.sanjuanco.com
  483. // @connect gis.santa-clarita.com
  484. // @connect gis.santacruzcounty.us
  485. // @connect gis.sawyerwi.org
  486. // @connect gis.sccwi.gov
  487. // @connect gis.shastacounty.gov
  488. // @connect gis.sheboygancounty.com
  489. // @connect gis.shelbycountytn.gov
  490. // @connect gis.showmeboone.com
  491. // @connect gis.siouxfalls.gov
  492. // @connect gis.slocounty.ca.gov
  493. // @connect gis.sncoapps.us
  494. // @connect gis.southkingstownri.com
  495. // @connect gis.steele.mn
  496. // @connect gis.stlouiscountymn.gov
  497. // @connect gis.stlucieco.gov
  498. // @connect gis.sullivanny.us
  499. // @connect gis.sumtercountyfl.gov
  500. // @connect gis.surryinfo.net
  501. // @connect gis.talbotdes.org
  502. // @connect gis.tazewell.com
  503. // @connect gis.texoma.cog.tx.us
  504. // @connect gis.thecolonytx.gov
  505. // @connect gis.thomsonreuters.com
  506. // @connect gis.transportation.wv.gov
  507. // @connect gis.transylvaniacounty.org
  508. // @connect gis.traviscountytx.gov
  509. // @connect gis.tularecounty.ca.gov
  510. // @connect gis.ucdavis.edu
  511. // @connect gis.ulstercountyny.gov
  512. // @connect gis.unioncountync.gov
  513. // @connect gis.vernon-ct.gov
  514. // @connect gis.victorvilleca.gov
  515. // @connect gis.warrensburg-mo.com
  516. // @connect gis.washingtoncountyny.gov
  517. // @connect gis.watertownwi.gov
  518. // @connect gis.waukesha-wi.gov
  519. // @connect gis.weatherfordtx.gov
  520. // @connect gis.westplains.net
  521. // @connect gis.whitfieldcountyga.com
  522. // @connect gis.wilco.org
  523. // @connect gis.wilkescounty.net
  524. // @connect gis.willcountyillinois.com
  525. // @connect gis.wilson-co.com
  526. // @connect gis.wilsonnc.org
  527. // @connect gis.wiu.edu
  528. // @connect gis.worldviewsolutions.com
  529. // @connect gis.wyo.gov
  530. // @connect gis.yadkincountync.gov
  531. // @connect gis.yanceycountync.org
  532. // @connect gis.yavapaiaz.gov
  533. // @connect gis.yolocounty.gov
  534. // @connect gis.yolocounty.org
  535. // @connect gis.yuba.org
  536. // @connect gis1.acimap.us
  537. // @connect gis1.georgetowncountysc.org
  538. // @connect gis1.hamiltoncounty.in.gov
  539. // @connect gis11.services.ncdot.gov
  540. // @connect gis12.cookcountyil.gov
  541. // @connect gis2.arlingtontx.gov
  542. // @connect gis2.arlingtonva.us
  543. // @connect gis2.co.dakota.mn.us
  544. // @connect gis2.co.marathon.wi.us
  545. // @connect gis2.co.ozaukee.wi.us
  546. // @connect gis2.erie.gov
  547. // @connect gis2.gworks.com
  548. // @connect gis2.idaho.gov
  549. // @connect gis2.lawrenceks.org
  550. // @connect gis2.orangeburgcounty.org
  551. // @connect gis2.sandyspringsga.gov
  552. // @connect gis2.totaland.com
  553. // @connect gis2.waukcogeo.com
  554. // @connect gis21svweb.lincolnparish.org
  555. // @connect gis3.cdmsmithgis.com
  556. // @connect gis3.cmpdd.org
  557. // @connect gis3.gwinnettcounty.com
  558. // @connect gis3.gworks.com
  559. // @connect gis3.montgomerycountymd.gov
  560. // @connect gis3.richmondnc.com
  561. // @connect gis4.montgomerycountymd.gov
  562. // @connect gisago-qa.mcgi.state.mi.us
  563. // @connect gisago.mcgi.state.mi.us
  564. // @connect gisapp.adcogov.org
  565. // @connect gisapp.mahoningcountyoh.gov
  566. // @connect gisapps.cityofchicago.org
  567. // @connect gisapps.glendaleca.gov
  568. // @connect gisapps.rileycountyks.gov
  569. // @connect gisapps.wicomicocounty.org
  570. // @connect gisapps1.mapoakland.com
  571. // @connect gisarcweb.jeffersoncountywv.org
  572. // @connect gisccapps.charlestoncounty.org
  573. // @connect gisdata.alleghenycounty.us
  574. // @connect gisdata.dot.ca.gov
  575. // @connect gisdata.farrwestengineering.com
  576. // @connect gisdata.in.gov
  577. // @connect gisdata.jeffersoncountyoh.com
  578. // @connect gisdata.kingcounty.gov
  579. // @connect gisdata.pandai.com
  580. // @connect gisdata.pima.gov
  581. // @connect gisdata.seattle.gov
  582. // @connect gisdemo1.cdmsmith.com
  583. // @connect gisdemo2.cdmsmith.com
  584. // @connect gisentapp01.highpointnc.gov
  585. // @connect gisext.lincoln.ne.gov
  586. // @connect gishost.cdmsmithgis.com
  587. // @connect gisinfo.co.portage.wi.gov
  588. // @connect gisinfo.co.walworth.wi.us
  589. // @connect gisinfo.lawrencevillega.org
  590. // @connect gismap.augustaga.gov
  591. // @connect gismap.cityofboise.org
  592. // @connect gismap.co.juneau.wi.us
  593. // @connect gismap.co.marshall.mn.us
  594. // @connect gismap.co.norman.mn.us
  595. // @connect gismap.co.red-lake.mn.us
  596. // @connect gismapping.stafford.va.us
  597. // @connect gismaps.cityofboise.org
  598. // @connect gismaps.cityofgreer.org
  599. // @connect gismaps.co.cerro-gordo.ia.us
  600. // @connect gismaps.coconino.az.gov
  601. // @connect gismaps.columbiapa.org
  602. // @connect gismaps.flower-mound.com
  603. // @connect gismaps.fultoncountyga.gov
  604. // @connect gismaps.hctra.org
  605. // @connect gismaps.kingcounty.gov
  606. // @connect gismaps.redwoodcity.org
  607. // @connect gismaps.sedgwickcounty.org
  608. // @connect gismaps.wichita.gov
  609. // @connect gismapserver.leegov.com
  610. // @connect gismo.spokanecounty.org
  611. // @connect gisonline.greenvillenc.gov
  612. // @connect gisp.co.genesee.ny.us
  613. // @connect gisp.mcgi.state.mi.us
  614. // @connect gisp1.polkcountyiowa.gov
  615. // @connect gisportal.champaignil.gov
  616. // @connect gisportal.co.calaveras.ca.us
  617. // @connect gisportal.co.madison.il.us
  618. // @connect gisportal.co.warren.oh.us
  619. // @connect gisportal.dorchestercounty.net
  620. // @connect gisportal.dot.ct.gov
  621. // @connect gisportal.fnsb.gov
  622. // @connect gisportal.ircgov.com
  623. // @connect gisportal.ontarioca.gov
  624. // @connect gisportal.stocktonca.gov
  625. // @connect gisportal.stpgov.org
  626. // @connect gispro.porterco.org
  627. // @connect gisprod10.co.fresno.ca.us
  628. // @connect gisprodops.chesco.org
  629. // @connect gispub.cityofaspen.com
  630. // @connect gispub.co.washington.or.us
  631. // @connect gispublic.co.lake.ca.us
  632. // @connect gispw.coloradosprings.gov
  633. // @connect gisrevprxy.seattle.gov
  634. // @connect giss3.cmpdd.org
  635. // @connect gisserver.christiancountymo.gov
  636. // @connect gisservice.cityofmesquite.com
  637. // @connect gisservicemt.gov
  638. // @connect gisservices.chathamcountync.gov
  639. // @connect gisservices.chathamnc.org
  640. // @connect gisservices.co.anoka.mn.us
  641. // @connect gisservices.douglasnv.us
  642. // @connect gisservices.its.ny.gov
  643. // @connect gisservices.oakgov.com
  644. // @connect gisservices2.suffolkcountyny.gov
  645. // @connect gissites4.centrecountypa.gov
  646. // @connect gissvr.watgov.org
  647. // @connect gisweb-18.ci.killeen.tx.us
  648. // @connect gisweb-adapters.bcpa.net
  649. // @connect gisweb.albemarle.org
  650. // @connect gisweb.birminghamal.gov
  651. // @connect gisweb.casscountynd.gov
  652. // @connect gisweb.champaignil.gov
  653. // @connect gisweb.ci.manteca.ca.us
  654. // @connect gisweb.co.aitkin.mn.us
  655. // @connect gisweb.co.mower.mn.us
  656. // @connect gisweb.co.wilkin.mn.us
  657. // @connect gisweb.fdlco.wi.gov
  658. // @connect gisweb.fortbendcountytx.gov
  659. // @connect gisweb.jeffcowa.us
  660. // @connect gisweb.miamidade.gov
  661. // @connect gisweb.pwcva.gov
  662. // @connect gisweb.wycokck.org
  663. // @connect gisweb2014.gordoncounty.org
  664. // @connect giswww.westchestergov.com
  665. // @connect git.co.tioga.ny.us
  666. // @connect gmdnags.colliercountyfl.gov
  667. // @connect grant.co.jefferson.id.us
  668. // @connect gweb01.co.olmsted.mn.us
  669. // @connect harpergis.integritygis.com
  670. // @connect harrisonms.geopowered.com
  671. // @connect haslet.halff.com
  672. // @connect hazards.fema.gov
  673. // @connect hdgis.ingham.org
  674. // @connect heartlandmpo.com
  675. // @connect helenamontanamaps.org
  676. // @connect henrygis.integritygis.com
  677. // @connect hgis.hialeahfl.gov
  678. // @connect holtgis.integritygis.com
  679. // @connect host.cdmsmithgis.com
  680. // @connect hostingdata2.tighebond.com
  681. // @connect hostingdata3.tighebond.com
  682. // @connect huntsvillegis.com
  683. // @connect ifgis.idahofallsidaho.gov
  684. // @connect ihost.tularecounty.ca.gov
  685. // @connect imap.klickitatcounty.org
  686. // @connect ims.districtiii.org
  687. // @connect intervector.leoncountyfl.gov
  688. // @connect iowagis.integritygis.com
  689. // @connect ira.property-appraiser.org
  690. // @connect jeffarcgis.jeffersoncountywi.gov
  691. // @connect joplingis.org
  692. // @connect k3gis.com
  693. // @connect kanplan.ksdot.gov
  694. // @connect kenhagis.kenha.co.ke
  695. // @connect kygisserver.ky.gov
  696. // @connect lacledegis.integritygis.com
  697. // @connect lafayettegis.integritygis.com
  698. // @connect landrecords.greencountywi.org
  699. // @connect lawrencegis.integritygis.com
  700. // @connect lcapps.co.lucas.oh.us
  701. // @connect lcmaps.lanecounty.org
  702. // @connect lee-arcgis.leecountync.gov
  703. // @connect lincolngis.integritygis.com
  704. // @connect linngis.integritygis.com
  705. // @connect lio.milwaukeecountywi.gov
  706. // @connect livingstongis.integritygis.com
  707. // @connect location.cabarruscounty.us
  708. // @connect logis.loudoun.gov
  709. // @connect loraincountyauditor.com
  710. // @connect lrs.co.columbia.wi.us
  711. // @connect lucity.sbpg.net
  712. // @connect macongis.integritygis.com
  713. // @connect madison.rexburg.org
  714. // @connect madisongis.cityofalbany.net
  715. // @connect manitowocmaps.info
  716. // @connect map.claycountymn.gov
  717. // @connect map.co.clear-creek.co.us
  718. // @connect map.co.clearwater.mn.us
  719. // @connect map.co.merced.ca.us
  720. // @connect map.co.thurston.wa.us
  721. // @connect map.co.trempealeau.wi.us
  722. // @connect map.coppelltx.gov
  723. // @connect map.eaglecounty.us
  724. // @connect map.newberrycounty.net
  725. // @connect map.opkansas.org
  726. // @connect map.stclairco.com
  727. // @connect map.sussexcountyde.gov
  728. // @connect map.wyoroad.info
  729. // @connect map9.incog.org
  730. // @connect mapd.kcmo.org
  731. // @connect mapdata.baytown.org
  732. // @connect mapdata.lasvegasnevada.gov
  733. // @connect mapit.fortworthtexas.gov
  734. // @connect mapitwest.fortworthtexas.gov
  735. // @connect mapnew.polkpa.org
  736. // @connect mapping.adamscountypa.gov
  737. // @connect mapping.kenoshacountywi.gov
  738. // @connect mapping.mitchellcounty.org
  739. // @connect mapping.modot.org
  740. // @connect maps.adaok.com
  741. // @connect maps.alexandercountync.gov
  742. // @connect maps.alexandriava.gov
  743. // @connect maps.austintexas.gov
  744. // @connect maps.bannockcounty.us
  745. // @connect maps.bayfieldcounty.wi.gov
  746. // @connect maps.bcad.org
  747. // @connect maps.berkeleywv.org
  748. // @connect maps.boonecountyil.org
  749. // @connect maps.bossierparishgis.org
  750. // @connect maps.bouldercounty.org
  751. // @connect maps.brazoriacountytx.gov
  752. // @connect maps.brla.gov
  753. // @connect maps.brookhavenga.gov
  754. // @connect maps.bryantx.gov
  755. // @connect maps.burlesontx.com
  756. // @connect maps.butlercountyauditor.org
  757. // @connect maps.canyonco.org
  758. // @connect maps.capturecama.com
  759. // @connect maps.casperwy.gov
  760. // @connect maps.chautauquacounty.com
  761. // @connect maps.cherokeecounty-nc.gov
  762. // @connect maps.ci.longmont.co.us
  763. // @connect maps.ci.nacogdoches.tx.us
  764. // @connect maps.cityhs.net
  765. // @connect maps.cityofconroe.org
  766. // @connect maps.cityofhenderson.com
  767. // @connect maps.cityofls.net
  768. // @connect maps.cityofmadison.com
  769. // @connect maps.cityofmobile.org
  770. // @connect maps.cityofsherman.com
  771. // @connect maps.cityoftulsa.org
  772. // @connect maps.clarkcountynv.gov
  773. // @connect maps.claycountygov.com
  774. // @connect maps.clermontauditor.org
  775. // @connect maps.clintoncountypa.com
  776. // @connect maps.co.blaine.id.us
  777. // @connect maps.co.forsyth.nc.us
  778. // @connect maps.co.goodhue.mn.us
  779. // @connect maps.co.gov
  780. // @connect maps.co.grayson.tx.us
  781. // @connect maps.co.itasca.mn.us
  782. // @connect maps.co.kendall.il.us
  783. // @connect maps.co.kern.ca.us
  784. // @connect maps.co.lincoln.wi.us
  785. // @connect maps.co.palm-beach.fl.us
  786. // @connect maps.co.polk.or.us
  787. // @connect maps.co.pueblo.co.us
  788. // @connect maps.co.ramsey.mn.us
  789. // @connect maps.co.shawano.wi.us
  790. // @connect maps.co.warren.oh.us
  791. // @connect maps.co.washington.mn.us
  792. // @connect maps.co.yellowstone.mt.gov
  793. // @connect maps.coj.net
  794. // @connect maps.collincountytx.gov
  795. // @connect maps.countyofmerced.com
  796. // @connect maps.crc.ga.gov
  797. // @connect maps.ctmetro.org
  798. // @connect maps.dancgis.org
  799. // @connect maps.dcad.org
  800. // @connect maps.delco-gis.org
  801. // @connect maps.deltacountyco.gov
  802. // @connect maps.deschutes.org
  803. // @connect maps.desotocountyms.gov
  804. // @connect maps.dmgov.org
  805. // @connect maps.dotd.la.gov
  806. // @connect maps.douglascountyga.gov
  807. // @connect maps.douglascountywa.net
  808. // @connect maps.elbertcounty-co.gov
  809. // @connect maps.escpa.org
  810. // @connect maps.etcog.org
  811. // @connect maps.evansvillegis.com
  812. // @connect maps.fayetteville-ar.gov
  813. // @connect maps.fishers.in.us
  814. // @connect maps.flathead.mt.gov
  815. // @connect maps.floridadisaster.org
  816. // @connect maps.frederickcountymd.gov
  817. // @connect maps.fredericksburgva.gov
  818. // @connect maps.garfield-county.com
  819. // @connect maps.garlandtx.gov
  820. // @connect maps.grcity.us
  821. // @connect maps.groton-ct.gov
  822. // @connect maps.grundyco.org
  823. // @connect maps.hayward-ca.gov
  824. // @connect maps.haywoodnc.net
  825. // @connect maps.highlandvillage.org
  826. // @connect maps.hokecounty.org
  827. // @connect maps.huerfano.us
  828. // @connect maps.huntsvilleal.gov
  829. // @connect maps.indiana.edu
  830. // @connect maps.iredellcountync.gov
  831. // @connect maps.itos.uga.edu
  832. // @connect maps.jocogov.org
  833. // @connect maps.kpb.us
  834. // @connect maps.kytc.ky.gov
  835. // @connect maps.lacity.org
  836. // @connect maps.lagrange-ga.org
  837. // @connect maps.lakecountyil.gov
  838. // @connect maps.laramiecounty.com
  839. // @connect maps.lcwy.org
  840. // @connect maps.lex-co.com
  841. // @connect maps.lexingtonky.gov
  842. // @connect maps.libertymo.gov
  843. // @connect maps.lincolncountysd.org
  844. // @connect maps.linkgis.org
  845. // @connect maps.matsugov.us
  846. // @connect maps.mckinneytexas.org
  847. // @connect maps.meshekgis.com
  848. // @connect maps.miamigov.com
  849. // @connect maps.midlandtexas.gov
  850. // @connect maps.muskegoncountygis.com
  851. // @connect maps.nashville.gov
  852. // @connect maps.nassauflpa.com
  853. // @connect maps.nevadacountyca.gov
  854. // @connect maps.nj.gov
  855. // @connect maps.normanok.gov
  856. // @connect maps.northaugustasc.gov
  857. // @connect maps.ocgov.net
  858. // @connect maps.opkansas.org
  859. // @connect maps.orcity.org
  860. // @connect maps.palmcoastgov.com
  861. // @connect maps.parkco.us
  862. // @connect maps.phoenix.gov
  863. // @connect maps.pitkincounty.com
  864. // @connect maps.planogis.org
  865. // @connect maps.pottercountypa.net
  866. // @connect maps.raleighnc.gov
  867. // @connect maps.richlandcountyoh.us
  868. // @connect maps.rutherfordcountytn.gov
  869. // @connect maps.sanmiguelcountyco.gov
  870. // @connect maps.santa-clarita.com
  871. // @connect maps.santabarbaraca.gov
  872. // @connect maps.sccmo.org
  873. // @connect maps.semogis.com
  874. // @connect maps.sfdpw.org
  875. // @connect maps.sgcity.org
  876. // @connect maps.shelbyal.com
  877. // @connect maps.slocity.org
  878. // @connect maps.spartanburgcounty.org
  879. // @connect maps.springfieldmo.gov
  880. // @connect maps.stlouisco.com
  881. // @connect maps.swaincountync.gov
  882. // @connect maps.tippecanoe.in.gov
  883. // @connect maps.townofcary.org
  884. // @connect maps.udot.utah.gov
  885. // @connect maps.vcgi.vermont.gov
  886. // @connect maps.ventura.org
  887. // @connect maps.victoriatx.org
  888. // @connect maps.vilascountywi.gov
  889. // @connect maps.vtrans.vermont.gov
  890. // @connect maps.wake.gov
  891. // @connect maps.washco-md.net
  892. // @connect maps.washcowisco.gov
  893. // @connect maps1.eriecounty.oh.gov
  894. // @connect maps1.larimer.org
  895. // @connect maps11.eriecounty.oh.gov
  896. // @connect maps2.bgadd.org
  897. // @connect maps2.cattco.org
  898. // @connect maps2.ci.euless.tx.us
  899. // @connect maps2.columbus.gov
  900. // @connect maps2.dcgis.dc.gov
  901. // @connect maps2.san-marcos.net
  902. // @connect maps2.timmons.com
  903. // @connect maps2.tucsonaz.gov
  904. // @connect maps2.vcgov.org
  905. // @connect maps6.stlouis-mo.gov
  906. // @connect maps7.eriecounty.oh.gov
  907. // @connect maps8.eriecounty.oh.gov
  908. // @connect mapsdev.hamiltontn.gov
  909. // @connect mapserv.cityofloveland.org
  910. // @connect mapserv.mesquitenv.gov
  911. // @connect mapservice.nmstatelands.org
  912. // @connect mapservices.gis.saccounty.net
  913. // @connect mapservices.pasda.psu.edu
  914. // @connect mapservices.santacruzcountyaz.gov
  915. // @connect mapservices.sccgov.org
  916. // @connect mapservices.weather.noaa.gov
  917. // @connect mapservices1.jeffco.us
  918. // @connect mapservices2.jeffco.us
  919. // @connect mariesgis.integritygis.com
  920. // @connect mariongis.integritygis.com
  921. // @connect mcdonaldgis.integritygis.com
  922. // @connect mcggis.mcgtn.org
  923. // @connect mcgis.mesacounty.us
  924. // @connect mcgis.mohave.gov
  925. // @connect mcgis4.monroecounty-fl.gov
  926. // @connect mcmap2.montrosecounty.net
  927. // @connect mcogis.co.marion.oh.us
  928. // @connect millergis.integritygis.com
  929. // @connect mms.hursttx.gov
  930. // @connect mndotgis.dot.state.mn.us
  931. // @connect moberlygis.integritygis.com
  932. // @connect mobile.alamedaca.gov
  933. // @connect moniteaugis.integritygis.com
  934. // @connect morgangis.integritygis.com
  935. // @connect msdisweb.missouri.edu
  936. // @connect mycity2.houstontx.gov
  937. // @connect navigator.state.or.us
  938. // @connect ndgishub.nd.gov
  939. // @connect newtongis.integritygis.com
  940. // @connect nhgeodata.unh.edu
  941. // @connect northlake.halff.com
  942. // @connect nspdcwebsrv.csuchico.edu
  943. // @connect oak.co.lake-of-the-woods.mn.us
  944. // @connect oc17maps.co.oconto.wi.us
  945. // @connect ocgis4.ocfl.net
  946. // @connect oncorng.co.ontario.ny.us
  947. // @connect operationserver.ci.henderson.nc.us
  948. // @connect orfmaps.norfolk.gov
  949. // @connect osagegis.integritygis.com
  950. // @connect pagis.org
  951. // @connect pamap.putnam-fl.gov
  952. // @connect parcelmap.ashtabulacounty.us
  953. // @connect parcels.rsdigital.com
  954. // @connect parcelviewer.geodecisions.com
  955. // @connect pascogis.pascocountyfl.net
  956. // @connect pgis.plantation.org
  957. // @connect phelpsgis.integritygis.com
  958. // @connect polaris2.mecklenburgcountync.gov
  959. // @connect polkgis.integritygis.com
  960. // @connect portal.carolinabeach.org
  961. // @connect portal.carson.org
  962. // @connect programs.iowadnr.gov
  963. // @connect propaccess.wadtx.com
  964. // @connect propertyviewer.andersoncountysc.org
  965. // @connect proxy2.roktech.net
  966. // @connect psportal.harrisoncountywv.com
  967. // @connect pubgis.ci.lubbock.tx.us
  968. // @connect public.co.wasco.or.us
  969. // @connect public1.co.waupaca.wi.us
  970. // @connect publicmap01.co.st-clair.il.us
  971. // @connect publicmaps.txkusa.org
  972. // @connect pulaskigis.integritygis.com
  973. // @connect putnamcountygis.com
  974. // @connect pwmaps.cityofloveland.org
  975. // @connect pwmaps.reno.gov
  976. // @connect rallsgis.integritygis.com
  977. // @connect raygis.integritygis.com
  978. // @connect rc-arcgis01.co.rice.mn.us
  979. // @connect rdsgis.nctgis.nct911.org
  980. // @connect renogis3.renogov.org
  981. // @connect roads.udot.utah.gov
  982. // @connect rockgis.co.rock.wi.us
  983. // @connect rockgis.rockfordil.gov
  984. // @connect romefloyd.agdmaps.com
  985. // @connect rptsgisweb.oswegocounty.com
  986. // @connect salinegis.integritygis.com
  987. // @connect saludacountysc.net
  988. // @connect scgisa.starkcountyohio.gov
  989. // @connect sdgis.sd.gov
  990. // @connect secure.boonecountygis.com
  991. // @connect sedaliagis.integritygis.com
  992. // @connect see-eldorado.edcgov.us
  993. // @connect sei.cloudsmartgis.com
  994. // @connect seminolearcgis.seminolecountyfl.gov
  995. // @connect server.boundarycountyid.org
  996. // @connect server1.mapxpress.net
  997. // @connect server2.mapxpress.net
  998. // @connect services.arcgis.com
  999. // @connect services.gis.ca.gov
  1000. // @connect services.gisqatar.org.qa
  1001. // @connect services.mh-gis.com
  1002. // @connect services.nconemap.gov
  1003. // @connect services.putnamco.org
  1004. // @connect services.sagis.org
  1005. // @connect services.wvgis.wvu.edu
  1006. // @connect services1.arcgis.com
  1007. // @connect services2.arcgis.com
  1008. // @connect services2.integritygis.com
  1009. // @connect services3.arcgis.com
  1010. // @connect services5.arcgis.com
  1011. // @connect services6.arcgis.com
  1012. // @connect services7.arcgis.com
  1013. // @connect services8.arcgis.com
  1014. // @connect services9.arcgis.com
  1015. // @connect showlowmaps.com
  1016. // @connect skyview.hornershifrin.com
  1017. // @connect slco.org
  1018. // @connect smgis.sanmarcostx.gov
  1019. // @connect smithvillegis.integritygis.com
  1020. // @connect smpesri.scdot.org
  1021. // @connect socogis.sonomacounty.ca.gov
  1022. // @connect spatial.gishost.com
  1023. // @connect spatial.jacksoncountyor.gov
  1024. // @connect spatialags.vhb.com
  1025. // @connect stclairgis.integritygis.com
  1026. // @connect stmgis.stmarysmd.com
  1027. // @connect stokescountygis.com
  1028. // @connect stonegis.integritygis.com
  1029. // @connect summitmaps.summitoh.net
  1030. // @connect svr4.sumtercountysc.org
  1031. // @connect tcgisws.tooeleco.gov
  1032. // @connect tcweb.co.teller.co.us
  1033. // @connect tfportal.tfid.org
  1034. // @connect tharcgis2.thewoodlands-tx.gov
  1035. // @connect tigerweb.geo.census.gov
  1036. // @connect tiogagis.tiogacountypa.us
  1037. // @connect tnmap.tn.gov
  1038. // @connect tpwd.texas.gov
  1039. // @connect tsc-gis-ags101a.schneidercorp.com
  1040. // @connect twu.newedgeservices.com
  1041. // @connect utility.arcgis.com
  1042. // @connect vernongis.integritygis.com
  1043. // @connect vginmaps.vdem.virginia.gov
  1044. // @connect vtransmap01.aot.state.vt.us
  1045. // @connect wallawallagis.com
  1046. // @connect warrengis.integritygis.com
  1047. // @connect warrensburggis.integritygis.com
  1048. // @connect wcg-gisweb.co.worcester.md.us
  1049. // @connect wcgis3.co.winnebago.wi.us
  1050. // @connect wcgisweb.washoecounty.us
  1051. // @connect wcoh.geopowered.com
  1052. // @connect web.binghamid.gov
  1053. // @connect web2.co.ottertail.mn.us
  1054. // @connect web2.kcsgis.com
  1055. // @connect web3.kcsgis.com
  1056. // @connect web4.kcsgis.com
  1057. // @connect web5.kcsgis.com
  1058. // @connect webadaptor.glynncounty-ga.gov
  1059. // @connect webgis.bedfordcountyva.gov
  1060. // @connect webgis.co.davidson.nc.us
  1061. // @connect webgis.co.humboldt.ca.us
  1062. // @connect webgis.durhamnc.gov
  1063. // @connect webgis.lafayetteassessor.com
  1064. // @connect webgis.providenceri.gov
  1065. // @connect webgis.waterburyct.org
  1066. // @connect webgis.yorbalindaca.gov
  1067. // @connect webmap.co.jackson.ms.us
  1068. // @connect webmap.jeffparish.net
  1069. // @connect webmap.trueautomation.com
  1070. // @connect webmaps.elkgrovecity.org
  1071. // @connect webmaps.sjcounty.net
  1072. // @connect webportal.co.marquette.wi.us
  1073. // @connect websrv31.clallamcountywa.gov
  1074. // @connect webstergis.integritygis.com
  1075. // @connect wfs.ksdot.org
  1076. // @connect wfs.schneidercorp.com
  1077. // @connect wvsams.mapwv.org
  1078. // @connect ww1.bucoks.com
  1079. // @connect www.1stdistrict.org
  1080. // @connect www.adacountyassessor.org
  1081. // @connect www.adamscountyarcserver.com
  1082. // @connect www.ancgis.com
  1083. // @connect www.bartowgis.org
  1084. // @connect www.bcgis.com
  1085. // @connect www.bcpao.us
  1086. // @connect www.centralilmaps.com
  1087. // @connect www.cmbgis.com
  1088. // @connect www.colesco.illinois.gov
  1089. // @connect www.ctgismaps2.ct.gov
  1090. // @connect www.denvergov.org
  1091. // @connect www.dmcwebgis.com
  1092. // @connect www.efsedge.com
  1093. // @connect www.finneycountygis.com
  1094. // @connect www.franklinmo.net
  1095. // @connect www.gcgis.org
  1096. // @connect www.gfgis.com
  1097. // @connect www.gis.hctx.net
  1098. // @connect www.gis.sjcfl.us
  1099. // @connect www.gismidwest.com
  1100. // @connect www.gisonline.ms.gov
  1101. // @connect www.greenwoodsc.gov
  1102. // @connect www.hernandocountygis-florida.us
  1103. // @connect www.hogarcmaps.org
  1104. // @connect www.horrycountysc.gov
  1105. // @connect www.iqmap.org
  1106. // @connect www.landmarkgeospatial.com
  1107. // @connect www.laurenscountygis.org
  1108. // @connect www.mcgisweb.org
  1109. // @connect www.mchenrycountygis.org
  1110. // @connect www.midmogis.org
  1111. // @connect www.monroegis.org
  1112. // @connect www.mymanatee.org
  1113. // @connect www.ocgis.com
  1114. // @connect www.sciotocountyengineer.org
  1115. // @connect www.semogis.com
  1116. // @connect www.sgrcmaps.com
  1117. // @connect www.sjmap.org
  1118. // @connect www.skagitcounty.net
  1119. // @connect www.smithcountymapsite.org
  1120. // @connect www.tgisites.com
  1121. // @connect www.unionco.org
  1122. // @connect www.valorgis.com
  1123. // @connect www.waynecounty.com
  1124. // @connect www.wcgis.us
  1125. // @connect www.webgis.net
  1126. // @connect www1.cityofwebster.com
  1127. // @connect www2.ci.lancaster.oh.us
  1128. // @connect www2.pottcounty.org
  1129. // @connect www3.multco.us
  1130. // @connect www7.co.union.oh.us
  1131. // @connect xara1-4.cityofpetaluma.net
  1132. // @connect xmaps.indy.gov
  1133. // ==/UserScript==
  1134.  
  1135. /* global OpenLayers */
  1136. /* global W */
  1137. /* global WazeWrap */
  1138. /* global _ */
  1139. /* global turf */
  1140. /* global ESTreeProcessor */
  1141.  
  1142. (function main() {
  1143. 'use strict';
  1144.  
  1145. // **************************************************************************************************************
  1146. // IMPORTANT: Update this when releasing a new version of script that includes changes to the spreadsheet format
  1147. // that may cause old code to break. This # should match the version listed in the spreadsheet
  1148. // i.e. update them at the same time.
  1149.  
  1150. // const LAYER_DEF_VERSION = '2018.04.27.001'; // NOT ACTUALLY USED YET
  1151.  
  1152. // **************************************************************************************************************
  1153. // const UPDATE_MESSAGE = 'Bug fix due to WME update';
  1154. // const UPDATE_MESSAGE = `<ul>${[
  1155. // 'Added ability to shift layers. Right click a layer in the list to bring up the layer settings window.'
  1156. // ].map(item => `<li>${item}</li>`).join('')}</ul><br>`;
  1157. const GF_URL = 'https://gf.qytechs.cn/scripts/369632-wme-gis-layers';
  1158. // Used in tooltips to tell people who to report issues to. Update if a new author takes ownership of this script.
  1159. const SCRIPT_AUTHOR = 'MapOMatic';
  1160. // const LAYER_INFO_URL = 'https://spreadsheets.google.com/feeds/list/1cEG3CvXSCI4TOZyMQTI50SQGbVhJ48Xip-jjWg4blWw/o7gusx3/public/values?alt=json';
  1161. const LAYER_DEF_SPREADSHEET_URL = 'https://sheets.googleapis.com/v4/spreadsheets/1cEG3CvXSCI4TOZyMQTI50SQGbVhJ48Xip-jjWg4blWw/values/layerDefs';
  1162. const API_KEY = 'YTJWNVBVRkplbUZUZVVGTlNXOWlVR1pWVjIxcE9VdHJNbVY0TTFoeWNrSlpXbFZuVmtWelRrMVVWUT09';
  1163. const REQUEST_FORM_URL = 'https://docs.google.com/forms/d/e/1FAIpQLSevPQLz2ohu_LTge9gJ9Nv6PURmCmaSSjq0ayOJpGdRr2xI0g/viewform?usp=pp_url&entry.2116052852={username}';
  1164. const DEC = s => atob(atob(s));
  1165. const PRIVATE_LAYERS = { 'nc-henderson-sl-signs': ['the_cre8r', 'mapomatic'] }; // case sensitive -- use all lower case
  1166. // const COUNTRIES = {
  1167. // 'United States': {
  1168. // sheetId: '1cEG3CvXSCI4TOZyMQTI50SQGbVhJ48Xip-jjWg4blWw',
  1169. // sheetLayerRange: 'layerDefs'
  1170. // }
  1171. // };
  1172. const DEFAULT_STYLE = {
  1173. fillColor: '#000',
  1174. pointRadius: 4,
  1175. label: '${label}',
  1176. strokeColor: '#ffa500',
  1177. strokeOpacity: '0.95',
  1178. strokeWidth: 1.5,
  1179. fontColor: '#ffc520',
  1180. fontSize: '13',
  1181. labelOutlineColor: 'black',
  1182. labelOutlineWidth: 3
  1183. };
  1184. const LAYER_STYLES = {
  1185. cities: {
  1186. fillOpacity: 0.3,
  1187. fillColor: '#f65',
  1188. strokeColor: '#f65',
  1189. fontColor: '#f62'
  1190. },
  1191. forests_parks: {
  1192. fillOpacity: 0.4,
  1193. fillColor: '#585',
  1194. strokeColor: '#484',
  1195. fontColor: '#8b8'
  1196. },
  1197. milemarkers: {
  1198. strokeColor: '#fff',
  1199. fontColor: '#fff',
  1200. fontWeight: 'bold',
  1201. fillOpacity: 0,
  1202. labelYOffset: 10,
  1203. pointRadius: 2,
  1204. fontSize: 12
  1205. },
  1206. parcels: {
  1207. fillOpacity: 0,
  1208. fillColor: '#ffa500'
  1209. },
  1210. points: {
  1211. strokeColor: '#000',
  1212. fontColor: '#0ff',
  1213. fillColor: '#0ff',
  1214. labelYOffset: -10,
  1215. labelAlign: 'ct'
  1216. },
  1217. post_offices: {
  1218. strokeColor: '#000',
  1219. fontColor: '#f84',
  1220. fillColor: '#f84',
  1221. fontWeight: 'bold',
  1222. labelYOffset: -10,
  1223. labelAlign: 'ct'
  1224. },
  1225. state_parcels: {
  1226. fillOpacity: 0,
  1227. strokeColor: '#e62',
  1228. fillColor: '#e62',
  1229. fontColor: '#e73'
  1230. },
  1231. state_points: {
  1232. strokeColor: '#000',
  1233. fontColor: '#3cf',
  1234. fillColor: '#3cf',
  1235. labelYOffset: -10,
  1236. labelAlign: 'ct'
  1237. },
  1238. road_labels: {
  1239. strokeOpacity: 0,
  1240. fillOpacity: 0,
  1241. fontColor: '#faf'
  1242. },
  1243. structures: {
  1244. fillOpacity: 0,
  1245. strokeColor: '#f7f',
  1246. fontColor: '#f7f'
  1247. }
  1248. };
  1249. let ROAD_STYLE;
  1250. function initRoadStyle() {
  1251. ROAD_STYLE = new OpenLayers.Style({
  1252. pointRadius: 12,
  1253. fillColor: '#369',
  1254. pathLabel: '${label}',
  1255. label: '',
  1256. fontColor: '#faf',
  1257. labelSelect: true,
  1258. pathLabelYOffset: '${getOffset}',
  1259. pathLabelCurve: '${getSmooth}',
  1260. pathLabelReadable: '${getReadable}',
  1261. labelAlign: '${getAlign}',
  1262. labelOutlineWidth: 3,
  1263. labelOutlineColor: '#000',
  1264. strokeWidth: 3,
  1265. stroke: true,
  1266. strokeColor: '#f0f',
  1267. strokeOpacity: 0.4,
  1268. fontWeight: 'bold',
  1269. fontSize: 11
  1270. }, {
  1271. context: {
  1272. getOffset() { return -(W.map.getZoom() + 5); },
  1273. getSmooth() { return ''; },
  1274. getReadable() { return '1'; },
  1275. getAlign() { return 'cb'; }
  1276. }
  1277. });
  1278. }
  1279.  
  1280. // eslint-disable-next-line no-unused-vars
  1281. const _regexReplace = {
  1282. // Strip leading zeros or blank full label for any label starting with a non-digit or
  1283. // is a Zero Address, use with '' as replace.
  1284. r0: /^(0+(\s.*)?|\D.*)/,
  1285. // Strip Everything After Street Type to end of the string by use $1 and $2 capture
  1286. // groups, use with replace '$1$2'
  1287. // eslint-disable-next-line max-len
  1288. r1: /^(.* )(Ave(nue)?|Dr(ive)?|St(reet)?|C(our)?t|Cir(cle)?|Blvd|Boulevard|Pl(ace)?|Ln|Lane|Fwy|Freeway|R(oa)?d|Ter(r|race)?|Tr(ai)?l|Way|Rte \d+|Route \d+)\b.*/gi,
  1289. // Strip SPACE 5 Digits from end of string, use with replace ''
  1290. r2: /\s\d{5}$/,
  1291. // Strip Everything after a "~", ",", ";" to the end of the string, use with replace ''
  1292. r3: /(~|,|;|\s?\r\n).*$/,
  1293. // Move the digits after the last space to before the rest of the string using, use with
  1294. // replace '$2 $1'
  1295. r4: /^(.*)\s(\d+).*/,
  1296. // Insert newline between digits (including "-") and everything after the digits,
  1297. // except(and before) a ",", use with replace '$1\n$2'
  1298. r5: /^([-\d]+)\s+([^,]+).*/,
  1299. // Insert newline between digits and everything after the digits, use with
  1300. // replace '$1\n$2'
  1301. r6: /^(\d+)\s+(.*)/
  1302. };
  1303.  
  1304. let _gisLayers = [];
  1305.  
  1306. const _layerRefinements = [
  1307. {
  1308. id: 'us-post-offices',
  1309. labelHeaderFields: ['LOCALE_NAME']
  1310. }
  1311. ];
  1312.  
  1313. const STATES = {
  1314. _states: [
  1315. ['US (Country)', 'US', -1], ['Alabama', 'AL', 1], ['Alaska', 'AK', 2],
  1316. ['American Samoa', 'AS', 60], ['Arizona', 'AZ', 4], ['Arkansas', 'AR', 5],
  1317. ['California', 'CA', 6], ['Colorado', 'CO', 8], ['Connecticut', 'CT', 9],
  1318. ['Delaware', 'DE', 10], ['District of Columbia', 'DC', 11], ['Florida', 'FL', 12],
  1319. ['Georgia', 'GA', 13], ['Guam', 'GU', 66], ['Hawaii', 'HI', 15], ['Idaho', 'ID', 16],
  1320. ['Illinois', 'IL', 17], ['Indiana', 'IN', 18], ['Iowa', 'IA', 19],
  1321. ['Kansas', 'KS', 20], ['Kentucky', 'KY', 21], ['Louisiana', 'LA', 22],
  1322. ['Maine', 'ME', 23], ['Maryland', 'MD', 24], ['Massachusetts', 'MA', 25],
  1323. ['Michigan', 'MI', 26], ['Minnesota', 'MN', 27], ['Mississippi', 'MS', 28],
  1324. ['Missouri', 'MO', 29], ['Montana', 'MT', 30], ['Nebraska', 'NE', 31],
  1325. ['Nevada', 'NV', 32], ['New Hampshire', 'NH', 33], ['New Jersey', 'NJ', 34],
  1326. ['New Mexico', 'NM', 35], ['New York', 'NY', 36], ['North Carolina', 'NC', 37],
  1327. ['North Dakota', 'ND', 38], ['Northern Mariana Islands', 'MP', 69], ['Ohio', 'OH', 39],
  1328. ['Oklahoma', 'OK', 40], ['Oregon', 'OR', 41], ['Pennsylvania', 'PA', 42],
  1329. ['Puerto Rico', 'PR', 72], ['Rhode Island', 'RI', 44], ['South Carolina', 'SC', 45],
  1330. ['South Dakota', 'SD', 46], ['Tennessee', 'TN', 47], ['Texas', 'TX', 48],
  1331. ['Utah', 'UT', 49], ['Vermont', 'VT', 50], ['Virgin Islands', 'VI', 78],
  1332. ['Virginia', 'VA', 51], ['Washington', 'WA', 53], ['West Virginia', 'WV', 54],
  1333. ['Wisconsin', 'WI', 55], ['Wyoming', 'WY', 56]
  1334. ],
  1335. toAbbr(fullName) { return this._states.find(a => a[0] === fullName)[1]; },
  1336. toFullName(abbr) { return this._states.find(a => a[1] === abbr)[0]; },
  1337. toFullNameArray() { return this._states.map(a => a[0]); },
  1338. toAbbrArray() { return this._states.map(a => a[1]); },
  1339. fromId(id) { return this._states.find(a => a[2] === id); }
  1340. };
  1341. const DEFAULT_VISIBLE_AT_ZOOM = 18;
  1342. const SETTINGS_STORE_NAME = 'wme_gis_layers_fl';
  1343. const COUNTIES_URL = 'https://tigerweb.geo.census.gov/arcgis/rest/services/Census2020/State_County/MapServer/1/';
  1344. const ALERT_UPDATE = false;
  1345. const SCRIPT_NAME = GM_info.script.name;
  1346. const SCRIPT_VERSION = GM_info.script.version;
  1347. const DOWNLOAD_URL = 'https://gf.qytechs.cn/scripts/369632-wme-gis-layers/code/WME%20GIS%20Layers.user.js';
  1348. const SCRIPT_VERSION_CHANGES = [];
  1349. let _mapLayer = null;
  1350. let _roadLayer = null;
  1351. let _settings = {};
  1352. let _ignoreFetch = false;
  1353. let _lastToken = {};
  1354.  
  1355. const DEBUG = true;
  1356. // function log(message) { console.log('GIS Layers:', message); }
  1357. function logError(message) { console.error(`${SCRIPT_NAME}:`, message); }
  1358. function logDebug(message) { if (DEBUG) console.debug(`${SCRIPT_NAME}:`, message); }
  1359. // function logWarning(message) { console.warn('GIS Layers:', message); }
  1360.  
  1361. let _layerSettingsDialog;
  1362.  
  1363. class LayerSettingsDialog {
  1364. constructor() {
  1365. this._$titleText = $('<span>');
  1366. this._$closeButton = $('<span>', {
  1367. style: 'cursor:pointer;padding-left:4px;font-size:17px;color:#d6e6f3;float:right;',
  1368. class: 'fa fa-window-close'
  1369. }).click(() => this._onCloseButtonClick());
  1370. this._$shiftUpButton = LayerSettingsDialog._createShiftButton('fa-angle-up').click(() => this._onShiftButtonClick(0, 1));
  1371. this._$shiftLeftButton = LayerSettingsDialog._createShiftButton('fa-angle-left').click(() => this._onShiftButtonClick(-1, 0));
  1372. this._$shiftRightButton = LayerSettingsDialog._createShiftButton('fa-angle-right').click(() => this._onShiftButtonClick(1, 0));
  1373. this._$shiftDownButton = LayerSettingsDialog._createShiftButton('fa-angle-down').click(() => this._onShiftButtonClick(0, -1));
  1374. this._$resetButton = $('<button>', {
  1375. class: 'form-control',
  1376. style: 'height: 24px; width: auto; padding: 2px 6px 0px 6px; display: inline-block; float: right;'
  1377. }).text('Reset').click(() => this._onResetButtonClick());
  1378.  
  1379. this._dialogDiv = $('<div>', {
  1380. style: 'position: fixed; top: 15%; left: 400px; width: 200px; z-index: 100; background-color: #73a9bd; border-width: 1px; border-style: solid;'
  1381. + 'border-radius: 10px; box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.7); border-color: #50667b; padding: 4px;'
  1382. }).append($('<div>').append( // The extra div is needed here. When the header text wraps, the main dialog div won't expand properly without it.
  1383. // HEADER
  1384. $('<div>', { style: 'border-radius:5px 5px 0px 0px; padding: 4px; color: #fff; font-weight: bold; text-align:left; cursor: default;' }).append(
  1385. this._$closeButton,
  1386. this._$titleText
  1387. ),
  1388. // BODY
  1389. $('<div>', { style: 'border-radius: 5px; width: 100%; padding: 4px; background-color:#d6e6f3; display:inline-block; margin-right:5px;' }).append(
  1390. this._$resetButton,
  1391. $('<input>', {
  1392. type: 'radio', id: 'gisLayerShiftAmt1', name: 'gisLayerShiftAmt', value: '1', checked: 'checked'
  1393. }),
  1394. $('<label>', { for: 'gisLayerShiftAmt1' }).text('1m'),
  1395. $('<input>', {
  1396. type: 'radio', id: 'gisLayerShiftAmt10', name: 'gisLayerShiftAmt', value: '10', style: 'margin-left: 6px'
  1397. }),
  1398. $('<label>', { for: 'gisLayerShiftAmt10' }).text('10m'),
  1399. $('<div>', { style: 'padding: 4px' }).append(
  1400. $('<table>', { style: 'table-layout:fixed; width:60px; height:84px; margin-left:auto;margin-right:auto;' }).append(
  1401. $('<tr>', { style: 'width: 20px; height: 28px;' }).append(
  1402. $('<td>', { align: 'center' }),
  1403. $('<td>', { align: 'center' }).append(this._$shiftUpButton),
  1404. $('<td>', { align: 'center' })
  1405. ),
  1406. $('<tr>', { style: 'width: 20px; height: 28px;' }).append(
  1407. $('<td>', { align: 'center' }).append(this._$shiftLeftButton),
  1408. $('<td>', { align: 'center' }),
  1409. $('<td>', { align: 'center' }).append(this._$shiftRightButton)
  1410. ),
  1411. $('<tr>', { style: 'width: 20px; height: 28px;' }).append(
  1412. $('<td>', { align: 'center' }),
  1413. $('<td>', { align: 'center' }).append(this._$shiftDownButton),
  1414. $('<td>', { align: 'center' })
  1415. )
  1416. )
  1417. )
  1418. )
  1419. ));
  1420.  
  1421. this.hide();
  1422. this._dialogDiv.appendTo('body');
  1423.  
  1424. if (typeof jQuery.ui !== 'undefined') {
  1425. const that = this;
  1426. this._dialogDiv.draggable({
  1427. // Gotta nuke the height setting the dragging inserts otherwise the panel cannot dynamically resize
  1428. stop() { that._dialogDiv.css('height', ''); }
  1429. });
  1430. }
  1431. }
  1432.  
  1433. get gisLayer() {
  1434. return this._gisLayer;
  1435. }
  1436.  
  1437. set gisLayer(value) {
  1438. if (value !== this._gisLayer) {
  1439. this._gisLayer = value;
  1440. this.title = value.name;
  1441. }
  1442. }
  1443.  
  1444. get title() {
  1445. return this._$titleText.text();
  1446. }
  1447.  
  1448. set title(value) {
  1449. this._$titleText.text(value);
  1450. }
  1451.  
  1452. // eslint-disable-next-line class-methods-use-this
  1453. getShiftAmount() {
  1454. return $('input[name=gisLayerShiftAmt]:checked').val();
  1455. }
  1456.  
  1457. show() {
  1458. this._dialogDiv.show();
  1459. }
  1460.  
  1461. hide() {
  1462. this._dialogDiv.hide();
  1463. }
  1464.  
  1465. _onCloseButtonClick() {
  1466. this.hide();
  1467. }
  1468.  
  1469. _onShiftButtonClick(x, y) {
  1470. const shiftAmount = this.getShiftAmount();
  1471. x *= shiftAmount;
  1472. y *= shiftAmount;
  1473. this._shiftLayerFeatures(x, y);
  1474. const { id } = this._gisLayer;
  1475. let offset = _settings.getLayerSetting(id, 'offset');
  1476. if (!offset) {
  1477. offset = { x: 0, y: 0 };
  1478. _settings.setLayerSetting(id, 'offset', offset);
  1479. }
  1480. offset.x += x;
  1481. offset.y += y;
  1482. saveSettingsToStorage();
  1483. }
  1484.  
  1485. _onResetButtonClick() {
  1486. const offset = _settings.getLayerSetting(this._gisLayer.id, 'offset');
  1487. if (offset) {
  1488. this._shiftLayerFeatures(offset.x * -1, offset.y * -1);
  1489. delete _settings.layers[this._gisLayer.id].offset;
  1490. saveSettingsToStorage();
  1491. }
  1492. }
  1493.  
  1494. _shiftLayerFeatures(x, y) {
  1495. const layer = this.gisLayer.isRoadLayer ? _roadLayer : _mapLayer;
  1496. layer.getFeaturesByAttribute('layerID', this.gisLayer.id).forEach(f => f.geometry.move(x, y));
  1497. layer.redraw();
  1498. }
  1499.  
  1500. static _createShiftButton(fontAwesomeClass) {
  1501. return $('<button>', {
  1502. class: 'form-control',
  1503. style: 'cursor:pointer;font-size:14px;padding: 3px;border-radius: 5px;width: 21px;height: 21px;'
  1504. }).append(
  1505. $('<i>', { class: 'fa', style: 'vertical-align: super' }).addClass(fontAwesomeClass)
  1506. );
  1507. }
  1508. }
  1509.  
  1510. function loadSettingsFromStorage() {
  1511. const loadedSettings = $.parseJSON(localStorage.getItem(SETTINGS_STORE_NAME));
  1512. const defaultSettings = {
  1513. lastVersion: null,
  1514. visibleLayers: [],
  1515. onlyShowApplicableLayers: false,
  1516. selectedStates: [],
  1517. enabled: true,
  1518. fillParcels: false,
  1519. toggleHnsOnlyShortcut: '',
  1520. toggleEnabledShortcut: '',
  1521. oneTimeAlerts: {},
  1522. layers: {}
  1523. };
  1524. _settings = loadedSettings || defaultSettings;
  1525. Object.keys(defaultSettings).forEach(prop => {
  1526. if (!_settings.hasOwnProperty(prop)) {
  1527. _settings[prop] = defaultSettings[prop];
  1528. }
  1529. });
  1530.  
  1531. _settings.getLayerSetting = function getLayerSetting(layerID, settingName) {
  1532. const layerSettings = this.layers[layerID];
  1533. if (!layerSettings) {
  1534. return undefined;
  1535. }
  1536. return layerSettings[settingName];
  1537. };
  1538. _settings.setLayerSetting = function setLayerSetting(layerID, settingName, value) {
  1539. let layerSettings = this.layers[layerID];
  1540. if (!layerSettings) {
  1541. layerSettings = {};
  1542. this.layers[layerID] = layerSettings;
  1543. }
  1544. layerSettings[settingName] = value;
  1545. };
  1546. }
  1547.  
  1548. function saveSettingsToStorage() {
  1549. // Check for existance of action first, due to WME beta issue.
  1550. if (W.accelerators.Actions.GisLayersAddrDisplay) {
  1551. let keys = '';
  1552. const { shortcut } = W.accelerators.Actions.GisLayersAddrDisplay;
  1553. if (shortcut) {
  1554. if (shortcut.altKey) keys += 'A';
  1555. if (shortcut.shiftKey) keys += 'S';
  1556. if (shortcut.ctrlKey) keys += 'C';
  1557. if (keys.length) keys += '+';
  1558. if (shortcut.keyCode) keys += shortcut.keyCode;
  1559. }
  1560. _settings.toggleHnsOnlyShortcut = keys;
  1561. }
  1562. if (W.accelerators.Actions.GisLayersToggleEnabled) {
  1563. let keys = '';
  1564. const { shortcut } = W.accelerators.Actions.GisLayersToggleEnabled;
  1565. if (shortcut) {
  1566. if (shortcut.altKey) keys += 'A';
  1567. if (shortcut.shiftKey) keys += 'S';
  1568. if (shortcut.ctrlKey) keys += 'C';
  1569. if (keys.length) keys += '+';
  1570. if (shortcut.keyCode) keys += shortcut.keyCode;
  1571. }
  1572. _settings.toggleEnabledShortcut = keys;
  1573. }
  1574. _settings.lastVersion = SCRIPT_VERSION;
  1575. localStorage.setItem(SETTINGS_STORE_NAME, JSON.stringify(_settings));
  1576. logDebug('Settings saved');
  1577. }
  1578.  
  1579. function getOLMapExtent() {
  1580. let extent = W.map.getExtent();
  1581. if (Array.isArray(extent)) {
  1582. extent = new OpenLayers.Bounds(extent);
  1583. extent.transform('EPSG:4326', 'EPSG:3857');
  1584. }
  1585. return extent;
  1586. }
  1587.  
  1588. function getUrl(extent, gisLayer) {
  1589. if (gisLayer.spatialReference) {
  1590. const proj = new OpenLayers.Projection(`EPSG:${gisLayer.spatialReference}`);
  1591. extent.transform(W.map.getProjectionObject(), proj);
  1592. }
  1593. let layerOffset = _settings.getLayerSetting(gisLayer.id, 'offset');
  1594. if (!layerOffset) {
  1595. layerOffset = { x: 0, y: 0 };
  1596. }
  1597. const geometry = {
  1598. xmin: extent.left - layerOffset.x,
  1599. ymin: extent.bottom - layerOffset.y,
  1600. xmax: extent.right - layerOffset.x,
  1601. ymax: extent.top - layerOffset.y,
  1602. spatialReference: {
  1603. wkid: gisLayer.spatialReference ? gisLayer.spatialReference : 102100,
  1604. latestWkid: gisLayer.spatialReference ? gisLayer.spatialReference : 3857
  1605. }
  1606. };
  1607. const geometryStr = JSON.stringify(geometry);
  1608. let fields = gisLayer.labelFields;
  1609. if (gisLayer.labelHeaderFields) {
  1610. fields = fields.concat(gisLayer.labelHeaderFields);
  1611. }
  1612. if (gisLayer.distinctFields) {
  1613. fields = fields.concat(gisLayer.distinctFields);
  1614. }
  1615. let url = `${gisLayer.url}/query?geometry=${encodeURIComponent(geometryStr)}`;
  1616. url += gisLayer.token ? `&token=${gisLayer.token}` : '';
  1617. url += `&outFields=${encodeURIComponent(fields.join(','))}`;
  1618. url += '&returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometryType=esriGeometryEnvelope';
  1619. url += `&inSR=${gisLayer.spatialReference ? gisLayer.spatialReference : '102100'}`;
  1620. url += '&outSR=3857&f=json';
  1621. url += gisLayer.where ? `&where=${encodeURIComponent(gisLayer.where)}` : '';
  1622.  
  1623. logDebug(`Request URL: ${url}`);
  1624. return url;
  1625. }
  1626.  
  1627. function hashString(value) {
  1628. let hash = 0;
  1629. if (value.length === 0) return hash;
  1630. for (let i = 0; i < value.length; i++) {
  1631. const chr = value.charCodeAt(i);
  1632. // eslint-disable-next-line no-bitwise
  1633. hash = ((hash << 5) - hash) + chr;
  1634. // eslint-disable-next-line no-bitwise
  1635. hash |= 0; // Convert to 32bit integer
  1636. }
  1637. return hash;
  1638. }
  1639.  
  1640. function getCountiesUrl(extent) {
  1641. const geometry = {
  1642. xmin: extent.left,
  1643. ymin: extent.bottom,
  1644. xmax: extent.right,
  1645. ymax: extent.top,
  1646. spatialReference: { wkid: 102100, latestWkid: 3857 }
  1647. };
  1648. const url = `${COUNTIES_URL}/query?geometry=${encodeURIComponent(JSON.stringify(geometry))}`;
  1649. return `${url}&outFields=BASENAME%2CSTATE&returnGeometry=false&spatialRel=esriSpatialRelIntersects`
  1650. + '&geometryType=esriGeometryEnvelope&inSR=102100&outSR=3857&f=json';
  1651. }
  1652.  
  1653. let _countiesInExtent = [];
  1654.  
  1655. function getFetchableLayers(getInvisible) {
  1656. if (W.map.getZoom() < 12) return [];
  1657. return _gisLayers.filter(gisLayer => {
  1658. const isValidUrl = gisLayer.url && gisLayer.url.trim().length > 0;
  1659. const isVisible = (getInvisible || _settings.visibleLayers.includes(gisLayer.id))
  1660. && _settings.selectedStates.includes(gisLayer.state);
  1661. const isInState = gisLayer.state === 'US' || _countiesInExtent.some(county => county.stateInfo[1] === gisLayer.state);
  1662. // Be sure to use hasOwnProperty when checking this, since 0 is a valid value.
  1663. const isValidZoom = getInvisible || W.map.getZoom() >= (gisLayer.hasOwnProperty('visibleAtZoom')
  1664. ? gisLayer.visibleAtZoom : DEFAULT_VISIBLE_AT_ZOOM);
  1665. return isValidUrl && isInState && isVisible && isValidZoom;
  1666. });
  1667. }
  1668.  
  1669. function filterLayerCheckboxes() {
  1670. const applicableLayers = getFetchableLayers(true).filter(layer => {
  1671. const hasCounties = layer.hasOwnProperty('counties');
  1672. return (hasCounties && layer.counties.some(countyName => _countiesInExtent.some(county => county.name === countyName.toLowerCase()
  1673. && layer.state === county.stateInfo[1]))) || !hasCounties;
  1674. });
  1675. const statesToHide = STATES.toAbbrArray();
  1676.  
  1677. _gisLayers.forEach(gisLayer => {
  1678. const id = `#gis-layer-${gisLayer.id}-container`;
  1679. if (!_settings.onlyShowApplicableLayers || applicableLayers.includes(gisLayer)) {
  1680. $(id).show();
  1681. $(`#gis-layers-for-${gisLayer.state}`).show();
  1682. const idx = statesToHide.indexOf(gisLayer.state);
  1683. if (idx > -1) statesToHide.splice(idx, 1);
  1684. } else {
  1685. $(id).hide();
  1686. }
  1687. });
  1688. if (_settings.onlyShowApplicableLayers) {
  1689. statesToHide.forEach(st => $(`#gis-layers-for-${st}`).hide());
  1690. }
  1691. }
  1692.  
  1693. const ROAD_ABBR = [
  1694. [/\bAVENUE$/, 'AVE'], [/\bCIRCLE$/, 'CIR'], [/\bCOURT$/, 'CT'], [/\bDRIVE$/, 'DR'],
  1695. [/\bLANE$/, 'LN'], [/\bPARK$/, 'PK'], [/\bPLACE$/, 'PL'], [/\bROAD$/, 'RD'], [/\bSTREET$/, 'ST'],
  1696. [/\bTERRACE$/, 'TER']
  1697. ];
  1698.  
  1699. const labelProcessingGlobalVariables = {
  1700. Number,
  1701. Math,
  1702. Boolean,
  1703. parseInt,
  1704. Date,
  1705. _regexReplace: {
  1706. // Strip leading zeros or blank full label for any label starting with a non-digit or
  1707. // is a Zero Address, use with '' as replace.
  1708. r0: /^(0+(\s.*)?|\D.*)/,
  1709. // Strip Everything After Street Type to end of the string by use $1 and $2 capture
  1710. // groups, use with replace '$1$2'
  1711. // eslint-disable-next-line max-len
  1712. r1: /^(.* )(Ave(nue)?|Dr(ive)?|St(reet)?|C(our)?t|Cir(cle)?|Blvd|Boulevard|Pl(ace)?|Ln|Lane|Fwy|Freeway|R(oa)?d|Ter(r|race)?|Tr(ai)?l|Way|Rte \d+|Route \d+)\b.*/gi,
  1713. // Strip SPACE 5 Digits from end of string, use with replace ''
  1714. r2: /\s\d{5}$/,
  1715. // Strip Everything after a "~", ",", ";" to the end of the string, use with replace ''
  1716. r3: /(~|,|;|\s?\r\n).*$/,
  1717. // Move the digits after the last space to before the rest of the string using, use with
  1718. // replace '$2 $1'
  1719. r4: /^(.*)\s(\d+).*/,
  1720. // Insert newline between digits (including "-") and everything after the digits,
  1721. // except(and before) a ",", use with replace '$1\n$2'
  1722. r5: /^([-\d]+)\s+([^,]+).*/,
  1723. // Insert newline between digits and everything after the digits, use with
  1724. // replace '$1\n$2'
  1725. r6: /^(\d+)\s+(.*)/
  1726. }
  1727. };
  1728.  
  1729. function processLabel(gisLayer, item, displayLabelsAtZoom, area, isPolyLine = false) {
  1730. let label = '';
  1731. if (gisLayer.labelHeaderFields) {
  1732. label = `${gisLayer.labelHeaderFields.map(
  1733. fieldName => item.attributes[fieldName]
  1734. ).join(' ').trim()}\n`;
  1735. }
  1736. if (W.map.getZoom() >= displayLabelsAtZoom || area >= 5000) {
  1737. label += gisLayer.labelFields.map(
  1738. fieldName => item.attributes[fieldName]
  1739. ).join(' ').trim();
  1740. if (gisLayer.processLabel) {
  1741. if (gisLayer.labelProcessingError) {
  1742. label = 'ERROR';
  1743. } else {
  1744. labelProcessingGlobalVariables.label = label;
  1745. labelProcessingGlobalVariables.fieldValues = item.attributes;
  1746. const result = ESTreeProcessor.execute(gisLayer.processLabel, labelProcessingGlobalVariables);
  1747. label = result.output?.trim() ?? '';
  1748. }
  1749. }
  1750. }
  1751.  
  1752. if (!isPolyLine) {
  1753. if (label && [
  1754. LAYER_STYLES.points, LAYER_STYLES.parcels, LAYER_STYLES.state_points,
  1755. LAYER_STYLES.state_parcels
  1756. ].includes(gisLayer.style)) {
  1757. if (_settings.addrLabelDisplay === 'hn') {
  1758. const m = label.match(/^\d+/);
  1759. label = m ? m[0] : '';
  1760. } else if (_settings.addrLabelDisplay === 'street') {
  1761. const m = label.match(/^(?:\d+\s)?(.*)/);
  1762. label = m ? m[1].trim() : '';
  1763. }
  1764. else if (_settings.addrLabelDisplay === 'none') {
  1765. label = '';
  1766. }
  1767. }
  1768. }
  1769. return label;
  1770. }
  1771. function processFeatures(data, token, gisLayer) {
  1772. const features = [];
  1773. if (data.skipIt) {
  1774. // do nothing
  1775. } else if (data.error) {
  1776. logError(`Error in layer "${gisLayer.name}": ${data.error.message}`);
  1777. } else {
  1778. const items = data.features || [];
  1779. if (!token.cancel) {
  1780. let error = false;
  1781. const distinctValues = [];
  1782. items.forEach(item => {
  1783. let skipIt = false;
  1784. if (!token.cancel && !error) {
  1785. let feature;
  1786. let featureGeometry;
  1787. let area;
  1788. if (gisLayer.distinctFields) {
  1789. if (distinctValues.some(v => gisLayer.distinctFields.every(
  1790. fld => v[fld] === item.attributes[fld]
  1791. ))) {
  1792. skipIt = true;
  1793. } else {
  1794. const dist = {};
  1795. gisLayer.distinctFields.forEach(fld => (dist[fld] = item.attributes[fld]));
  1796. distinctValues.push(dist);
  1797. }
  1798. }
  1799. if (!skipIt) {
  1800. let isPolyLine = false;
  1801. let layerOffset = _settings.getLayerSetting(gisLayer.id, 'offset');
  1802. if (!layerOffset) {
  1803. layerOffset = { x: 0, y: 0 };
  1804. }
  1805. // Special handling for this layer, because it doesn't have a geometry property.
  1806. // Coordinates are stored in the attributes.
  1807. if (gisLayer.id === 'nc-richmond-co-pts') {
  1808. const pt = new OpenLayers.Geometry.Point(item.attributes.XCOOR, item.attributes.YCOOR);
  1809. pt.transform(W.map.getOLMap().displayProjection, W.map.getProjectionObject());
  1810. item.geometry = pt;
  1811. }
  1812. if (item.geometry) {
  1813. if (item.geometry.x) {
  1814. featureGeometry = new OpenLayers.Geometry.Point(
  1815. item.geometry.x + layerOffset.x,
  1816. item.geometry.y + layerOffset.y
  1817. );
  1818. } else if (item.geometry.points) {
  1819. // @TODO Fix for multiple points instead of just grabbing first.
  1820. featureGeometry = new OpenLayers.Geometry.Point(
  1821. item.geometry.points[0][0] + layerOffset.x,
  1822. item.geometry.points[0][1] + layerOffset.y
  1823. );
  1824. } else if (item.geometry.rings) {
  1825. const rings = [];
  1826. item.geometry.rings.forEach(ringIn => {
  1827. const pnts = [];
  1828. for (let i = 0; i < ringIn.length; i++) {
  1829. pnts.push(new OpenLayers.Geometry.Point(
  1830. ringIn[i][0] + layerOffset.x,
  1831. ringIn[i][1] + layerOffset.y
  1832. ));
  1833. }
  1834. rings.push(new OpenLayers.Geometry.LinearRing(pnts));
  1835. });
  1836. featureGeometry = new OpenLayers.Geometry.Polygon(rings);
  1837. if (gisLayer.areaToPoint) {
  1838. featureGeometry = featureGeometry.getCentroid();
  1839. } else {
  1840. area = featureGeometry.getArea();
  1841. }
  1842. } else if (data.geometryType === 'esriGeometryPolyline') {
  1843. // We have to handle polylines differently since each item can have multiple features.
  1844. // In terms of ArcGIS, each feature's geometry can have multiple paths. For instance
  1845. // a single road can be broken into parts that are physically not connected to each other.
  1846. const hasVisibleAtZoom = gisLayer.hasOwnProperty('visibleAtZoom');
  1847. const hasLabelsVisibleAtZoom = gisLayer.hasOwnProperty('labelsVisibleAtZoom');
  1848. const displayLabelsAtZoom = hasLabelsVisibleAtZoom ? gisLayer.labelsVisibleAtZoom
  1849. : (hasVisibleAtZoom ? gisLayer.visibleAtZoom : DEFAULT_VISIBLE_AT_ZOOM) + 1;
  1850. const label = processLabel(gisLayer, item, displayLabelsAtZoom, area, true);
  1851.  
  1852. // Use Turf library to clip the geometry to the screen bounds.
  1853. // This allows labels to stay in view on very long roads.
  1854. const mls = turf.multiLineString(item.geometry.paths);
  1855. const e = getOLMapExtent();
  1856. const bbox = [e.left, e.bottom, e.right, e.top];
  1857. const clipped = turf.bboxClip(mls, bbox);
  1858. if (clipped.geometry.type === 'LineString') {
  1859. item.geometry.paths = [clipped.geometry.coordinates];
  1860. } else if (clipped.geometry.type === 'MultiLineString') {
  1861. item.geometry.paths = clipped.geometry.coordinates;
  1862. }
  1863.  
  1864. item.geometry.paths.forEach(path => {
  1865. const pointList = [];
  1866. path.forEach(point => pointList.push(new OpenLayers.Geometry.Point(
  1867. point[0] + layerOffset.x,
  1868. point[1] + layerOffset.y
  1869. )));
  1870. featureGeometry = new OpenLayers.Geometry.LineString(pointList);
  1871. featureGeometry.skipDupeCheck = true;
  1872.  
  1873. const attributes = {
  1874. layerID: gisLayer.id,
  1875. label
  1876. };
  1877.  
  1878. const lineFeature = new OpenLayers.Feature.Vector(featureGeometry, attributes);
  1879. features.push(lineFeature);
  1880. });
  1881. isPolyLine = true;
  1882. } else {
  1883. logDebug(`Unexpected feature type in layer: ${JSON.stringify(item)}`);
  1884. logError(`Error: Unexpected feature type in layer "${gisLayer.name}"`);
  1885. error = true;
  1886. }
  1887. if (!error && !isPolyLine) {
  1888. const hasVisibleAtZoom = gisLayer.hasOwnProperty('visibleAtZoom');
  1889. const hasLabelsVisibleAtZoom = gisLayer.hasOwnProperty('labelsVisibleAtZoom');
  1890. const displayLabelsAtZoom = hasLabelsVisibleAtZoom ? gisLayer.labelsVisibleAtZoom
  1891. : (hasVisibleAtZoom ? gisLayer.visibleAtZoom : DEFAULT_VISIBLE_AT_ZOOM) + 1;
  1892. const label = processLabel(gisLayer, item, displayLabelsAtZoom, area);
  1893. const attributes = {
  1894. layerID: gisLayer.id,
  1895. label
  1896. };
  1897. feature = new OpenLayers.Feature.Vector(featureGeometry, attributes);
  1898. features.push(feature);
  1899. }
  1900. }
  1901. }
  1902. }
  1903. });
  1904. }
  1905. }
  1906. if (!token.cancel) {
  1907. // Check for duplicate geometries.
  1908. for (let i = 0; i < features.length; i++) {
  1909. const f1 = features[i];
  1910. if (!f1.geometry.skipDupeCheck) {
  1911. const c1 = f1.geometry.getCentroid();
  1912. let labels = [f1.attributes.label];
  1913. for (let j = i + 1; j < features.length; j++) {
  1914. const f2 = features[j];
  1915. if (!f2.geometry.skipDupeCheck && f2.geometry.getCentroid().distanceTo(c1) < 1) {
  1916. features.splice(j, 1);
  1917. labels.push(f2.attributes.label);
  1918. j--;
  1919. }
  1920. }
  1921. labels = _.uniq(labels);
  1922. if (labels.length > 1) {
  1923. labels.forEach((label, idx) => {
  1924. label = label.replace(/\n/g, ' ').replace(/\s{2,}/, ' ').replace(/\bUNIT\s.{1,5}$/i, '').trim();
  1925. ROAD_ABBR.forEach(abbr => (label = label.replace(abbr[0], abbr[1])));
  1926. labels[idx] = label;
  1927. });
  1928. labels = _.uniq(labels);
  1929. labels.sort();
  1930. if (labels.length > 12) {
  1931. const len = labels.length;
  1932. labels = labels.slice(0, 10);
  1933. labels.push(`(${len - 10} more...)`);
  1934. }
  1935. f1.attributes.label = _.uniq(labels).join('\n');
  1936. } else {
  1937. let { label } = f1.attributes;
  1938. ROAD_ABBR.forEach(abbr => (label = label.replace(abbr[0], abbr[1])));
  1939. f1.attributes.label = label;
  1940. }
  1941. }
  1942. }
  1943.  
  1944. const layer = gisLayer.isRoadLayer ? _roadLayer : _mapLayer;
  1945. layer.removeFeatures(layer.getFeaturesByAttribute('layerID', gisLayer.id));
  1946. layer.addFeatures(features);
  1947.  
  1948. if (features.length) {
  1949. $(`label[for="gis-layer-${gisLayer.id}"]`).css({ color: '#00a009' });
  1950. }
  1951. }
  1952. } // END processFeatures()
  1953.  
  1954. function fetchFeatures() {
  1955. if (_ignoreFetch) return;
  1956. if (W.map.getZoom() < 12) {
  1957. filterLayerCheckboxes();
  1958. return;
  1959. }
  1960. _lastToken.cancel = true;
  1961. _lastToken = { cancel: false, features: [], layersProcessed: 0 };
  1962. $('.gis-state-layer-label').css({ color: '#777' });
  1963.  
  1964. let _layersCleared = false;
  1965.  
  1966. // if (layersToFetch.length) {
  1967. const extent = getOLMapExtent();
  1968. GM_xmlhttpRequest({
  1969. url: getCountiesUrl(extent),
  1970. method: 'GET',
  1971. onload(res) {
  1972. if (res.status < 400) {
  1973. const data = $.parseJSON(res.responseText);
  1974. if (data.error) {
  1975. logError(`Error in US Census counties data: ${data.error.message}`);
  1976. } else {
  1977. _countiesInExtent = data.features.map(feature => {
  1978. const name = feature.attributes.BASENAME.toLowerCase();
  1979. const stateInfo = STATES.fromId(parseInt(feature.attributes.STATE, 10));
  1980. return { name, stateInfo };
  1981. });
  1982. logDebug(`US Census counties: ${_countiesInExtent.map(c => `${c.name} ${c.stateInfo[1]}`).join(', ')}`);
  1983.  
  1984. let layersToFetch;
  1985. if (!_layersCleared) {
  1986. _layersCleared = true;
  1987. layersToFetch = getFetchableLayers();
  1988.  
  1989. // Remove features of any layers that won't be mapped.
  1990. _gisLayers.forEach(gisLayer => {
  1991. if (!layersToFetch.includes(gisLayer)) {
  1992. _mapLayer.removeFeatures(_mapLayer.getFeaturesByAttribute('layerID', gisLayer.id));
  1993. _roadLayer.removeFeatures(_roadLayer.getFeaturesByAttribute('layerID', gisLayer.id));
  1994. }
  1995. });
  1996. }
  1997.  
  1998. layersToFetch = layersToFetch.filter(layer => !layer.hasOwnProperty('counties')
  1999. || layer.counties.some(countyName => _countiesInExtent.some(county => county.name === countyName.toLowerCase()
  2000. && layer.state === county.stateInfo[1])));
  2001. filterLayerCheckboxes();
  2002. logDebug(`Fetching ${layersToFetch.length} layers...`);
  2003. logDebug(layersToFetch);
  2004. layersToFetch.forEach(gisLayer => {
  2005. const url = getUrl(extent, gisLayer);
  2006. GM_xmlhttpRequest({
  2007. url,
  2008. context: _lastToken,
  2009. method: 'GET',
  2010. onload(res2) {
  2011. if (res2.status < 400) { // Handle stupid issue where http 4## is considered success
  2012. processFeatures($.parseJSON(res2.responseText), res2.context, gisLayer);
  2013. } else {
  2014. logDebug(`HTTP request error: ${JSON.stringify(res2)}`);
  2015. logError(`Could not fetch layer "${gisLayer.id}". Request returned ${res2.status}`);
  2016. }
  2017. },
  2018. onerror(res3) {
  2019. logDebug(`xmlhttpRequest error:${JSON.stringify(res3)}`);
  2020. logError(`Could not fetch layer "${gisLayer.id}". An error was thrown.`);
  2021. }
  2022. });
  2023. });
  2024. }
  2025. } else {
  2026. logDebug(`HTTP request error: ${JSON.stringify(res)}`);
  2027. logError(`Could not fetch counties from US Census site. Request returned ${res.status}`);
  2028. }
  2029. },
  2030. onerror(res) {
  2031. logDebug(`xmlhttpRequest error:${JSON.stringify(res)}`);
  2032. logError('Could not fetch counties from US Census site. An error was thrown.');
  2033. }
  2034. });
  2035. }
  2036.  
  2037. function showScriptInfoAlert() {
  2038. /* Check version and alert on update */
  2039. if (ALERT_UPDATE && SCRIPT_VERSION !== _settings.lastVersion) {
  2040. // alert(SCRIPT_VERSION_CHANGES);
  2041. let releaseNotes = '';
  2042. releaseNotes += '<p>What\'s New:</p>';
  2043. if (SCRIPT_VERSION_CHANGES.length > 0) {
  2044. releaseNotes += '<ul>';
  2045. for (let idx = 0; idx < SCRIPT_VERSION_CHANGES.length; idx++)
  2046. releaseNotes += `<li>${SCRIPT_VERSION_CHANGES[idx]}`;
  2047. releaseNotes += '</ul>';
  2048. }
  2049. else {
  2050. releaseNotes += '<ul><li>Nothing major.</ul>';
  2051. }
  2052. WazeWrap.Interface.ShowScriptUpdate(GM_info.script.name, SCRIPT_VERSION, releaseNotes, GF_URL);
  2053. }
  2054. }
  2055.  
  2056. function setEnabled(value) {
  2057. _settings.enabled = value;
  2058. saveSettingsToStorage();
  2059. _mapLayer.setVisibility(value);
  2060. _roadLayer.setVisibility(value);
  2061. const color = value ? '#00bd00' : '#ccc';
  2062. $('span#gis-layers-power-btn').css({ color });
  2063. if (value) fetchFeatures();
  2064. $('#layer-switcher-item_gis_layers').prop('checked', value);
  2065. }
  2066.  
  2067. function onGisLayerToggleChanged() {
  2068. const checked = $(this).is(':checked');
  2069. const layerId = $(this).data('layer-id');
  2070. const idx = _settings.visibleLayers.indexOf(layerId);
  2071. if (checked) {
  2072. const gisLayer = _gisLayers.find(l => l.id === layerId);
  2073. if (gisLayer.oneTimeAlert) {
  2074. const lastAlertHash = _settings.oneTimeAlerts[layerId];
  2075. const newAlertHash = hashString(gisLayer.oneTimeAlert);
  2076. if (lastAlertHash !== newAlertHash) {
  2077. // alert(`Layer: ${gisLayer.name}\n\nMessage:\n${gisLayer.oneTimeAlert}`);
  2078. WazeWrap.Alerts.info(GM_info.script.name, `Layer: ${gisLayer.name}<br><br>Message:<br>${gisLayer.oneTimeAlert}`);
  2079. _settings.oneTimeAlerts[layerId] = newAlertHash;
  2080. saveSettingsToStorage();
  2081. }
  2082. }
  2083. if (idx === -1) _settings.visibleLayers.push(layerId);
  2084. } else if (idx > -1) _settings.visibleLayers.splice(idx, 1);
  2085. if (!_ignoreFetch) {
  2086. saveSettingsToStorage();
  2087. fetchFeatures();
  2088. }
  2089. }
  2090.  
  2091. function onOnlyShowApplicableLayersChanged() {
  2092. _settings.onlyShowApplicableLayers = $(this).is(':checked');
  2093. saveSettingsToStorage();
  2094. fetchFeatures();
  2095. }
  2096.  
  2097. function onStateCheckChanged(evt) {
  2098. const state = evt.data;
  2099. const idx = _settings.selectedStates.indexOf(state);
  2100. if (evt.target.checked) {
  2101. if (idx === -1) _settings.selectedStates.push(state);
  2102. } else if (idx > -1) _settings.selectedStates.splice(idx, 1);
  2103. if (!_ignoreFetch) {
  2104. saveSettingsToStorage();
  2105. initLayersTab();
  2106. fetchFeatures();
  2107. }
  2108. }
  2109.  
  2110. function onLayerCheckboxChanged(checked) {
  2111. setEnabled(checked);
  2112. }
  2113.  
  2114. function setFillParcels(doFill) {
  2115. [LAYER_STYLES.parcels, LAYER_STYLES.state_parcels].forEach(style => {
  2116. style.fillOpacity = doFill ? 0.2 : 0;
  2117. });
  2118. }
  2119.  
  2120. function onFillParcelsCheckedChanged(evt) {
  2121. const { checked } = evt.target;
  2122. setFillParcels(checked);
  2123. _settings.fillParcels = checked;
  2124. saveSettingsToStorage();
  2125. fetchFeatures();
  2126. }
  2127.  
  2128. function onMapMove() {
  2129. if (_settings.enabled) fetchFeatures();
  2130. }
  2131.  
  2132. function onRefreshLayersClick() {
  2133. const $btn = $('#gis-layers-refresh');
  2134. if (!$btn.hasClass('fa-spin')) {
  2135. $btn.css({ cursor: 'auto' });
  2136. $btn.addClass('fa-spin');
  2137. init(false);
  2138. }
  2139. }
  2140.  
  2141. function onChevronClick(evt) {
  2142. const $target = $(evt.currentTarget);
  2143. $($target.children()[0])
  2144. .toggleClass('fa fa-fw fa-chevron-down')
  2145. .toggleClass('fa fa-fw fa-chevron-right');
  2146. const $div = $($target.siblings()[0]);
  2147. if ($div.css('display') === 'none') {
  2148. $div.css('display', 'block');
  2149. } else {
  2150. $div.css('display', 'none');
  2151. }
  2152. }
  2153.  
  2154. function doToggleABunch(evt, checkState) {
  2155. _ignoreFetch = true;
  2156. $(evt.target).closest('fieldset').find('input').prop('checked', !checkState).trigger('click');
  2157. _ignoreFetch = false;
  2158. saveSettingsToStorage();
  2159. if (evt.data) initLayersTab();
  2160. fetchFeatures();
  2161. }
  2162.  
  2163. function onSelectAllClick(evt) {
  2164. doToggleABunch(evt, true);
  2165. }
  2166.  
  2167. function onSelectNoneClick(evt) {
  2168. doToggleABunch(evt, false);
  2169. }
  2170.  
  2171. function onGisAddrDisplayChange(evt) {
  2172. _settings.addrLabelDisplay = evt.target.value;
  2173. saveSettingsToStorage();
  2174. fetchFeatures();
  2175. }
  2176.  
  2177. function onAddressDisplayShortcutKey() {
  2178. if (!$('#gisAddrDisplay-hn').is(':checked')) {
  2179. $('#gisAddrDisplay-hn').click();
  2180. } else {
  2181. $('#gisAddrDisplay-all').click();
  2182. }
  2183. }
  2184.  
  2185. function onToggleGisLayersShortcutKey() {
  2186. setEnabled(!_settings.enabled);
  2187. }
  2188.  
  2189. function initLayer() {
  2190. const rules = _gisLayers.map(gisLayer => new OpenLayers.Rule({
  2191. filter: new OpenLayers.Filter.Comparison({
  2192. type: OpenLayers.Filter.Comparison.EQUAL_TO,
  2193. property: 'layerID',
  2194. value: gisLayer.id
  2195. }),
  2196. symbolizer: gisLayer.style
  2197. }));
  2198.  
  2199. setFillParcels(_settings.fillParcels);
  2200.  
  2201. const style = new OpenLayers.Style(DEFAULT_STYLE, { rules });
  2202. let existingLayer;
  2203. let uniqueName;
  2204.  
  2205. uniqueName = 'wmeGISLayersDefault';
  2206. existingLayer = W.map.layers.find(l => l.uniqueName === uniqueName); // Note: W.map.getLayerByUniqueName(...) isn't working.
  2207. if (existingLayer) W.map.removeLayer(existingLayer);
  2208. _mapLayer = new OpenLayers.Layer.Vector('GIS Layers - Default', {
  2209. uniqueName,
  2210. styleMap: new OpenLayers.StyleMap(style)
  2211. });
  2212.  
  2213. uniqueName = 'wmeGISLayersRoads';
  2214. existingLayer = W.map.layers.find(l => l.uniqueName === uniqueName); // Note: W.map.getLayerByUniqueName(...) isn't wworking.
  2215. if (existingLayer) W.map.removeLayer(existingLayer);
  2216. _roadLayer = new OpenLayers.Layer.Vector('GIS Layers - Roads', {
  2217. uniqueName,
  2218. styleMap: new OpenLayers.StyleMap(ROAD_STYLE)
  2219. });
  2220.  
  2221. _mapLayer.setVisibility(_settings.enabled);
  2222. _roadLayer.setVisibility(_settings.enabled);
  2223.  
  2224. W.map.addLayers([_roadLayer, _mapLayer]);
  2225. } // END InitLayer
  2226.  
  2227. function initLayersTab() {
  2228. const user = W.loginManager.user.attributes.userName.toLowerCase();
  2229. const states = _.uniq(_gisLayers.map(l => l.state)).filter(st => _settings.selectedStates.includes(st));
  2230.  
  2231. $('#panel-gis-state-layers').empty().append(
  2232. $('<div>', { class: 'controls-container' }).css({ 'padding-top': '0px' }).append(
  2233. $('<input>', { type: 'checkbox', id: 'only-show-applicable-gis-layers' }).change(
  2234. onOnlyShowApplicableLayersChanged
  2235. ).prop('checked', _settings.onlyShowApplicableLayers),
  2236. $('<label>', { for: 'only-show-applicable-gis-layers' })
  2237. .css({ 'white-space': 'pre-line' }).text('Only show applicable layers')
  2238. ),
  2239. $('.gis-layers-state-checkbox:checked').length === 0
  2240. ? $('<div>').text('Turn on layer categories in the Settings tab.')
  2241. : states.map(st => $('<fieldset>', {
  2242. id: `gis-layers-for-${st}`,
  2243. style: 'border:1px solid silver;padding:4px;border-radius:4px;-webkit-padding-before: 0;'
  2244. }).append(
  2245. $('<legend>', { style: 'margin-bottom:0px;border-bottom-style:none;width:auto;' })
  2246. .click(onChevronClick).append(
  2247. $('<i>', {
  2248. class: 'fa fa-fw fa-chevron-down',
  2249. style: 'cursor: pointer;font-size: 12px;margin-right: 4px'
  2250. }),
  2251. $('<span>', {
  2252. style: 'font-size:14px;font-weight:600;text-transform: uppercase; cursor: pointer'
  2253. }).text(STATES.toFullName(st))
  2254. ),
  2255. $('<div>', { id: `${st}_body` }).append(
  2256. $('<div>').css({ 'font-size': '11px' }).append(
  2257. $('<span>').append(
  2258. 'Select ',
  2259. $('<a>', { href: '#' })
  2260. .text('All')
  2261. .click(onSelectAllClick),
  2262. ' / ',
  2263. $('<a>', { href: '#' })
  2264. .text('None')
  2265. .click(onSelectNoneClick)
  2266. )
  2267. ),
  2268. $('<div>', { class: 'controls-container', style: 'padding-top:0px;' }).append(
  2269. _gisLayers.filter(l => (l.state === st && (!PRIVATE_LAYERS.hasOwnProperty(l.id)
  2270. || PRIVATE_LAYERS[l.id].includes(user))))
  2271. .map(gisLayer => {
  2272. const id = `gis-layer-${gisLayer.id}`;
  2273. return $('<div>', { class: 'controls-container', id: `${id}-container` })
  2274. .css({ 'padding-top': '0px', display: 'block' })
  2275. .append(
  2276. $('<input>', { type: 'checkbox', id })
  2277. .data('layer-id', gisLayer.id)
  2278. .change(onGisLayerToggleChanged)
  2279. .prop('checked', _settings.visibleLayers.includes(gisLayer.id)),
  2280. $('<label>', { for: id, class: 'gis-state-layer-label' })
  2281. .css({ 'white-space': 'pre-line' })
  2282. .text(`${gisLayer.name}${gisLayer.restrictTo ? ' *' : ''}`)
  2283. .attr('title', gisLayer.restrictTo ? `Restricted to: ${gisLayer.restrictTo}` : '')
  2284. .contextmenu(evt => {
  2285. evt.preventDefault();
  2286. // TODO - enable the layer if it isn't already.
  2287. // Tried using click function on the evt target, but that didn't work.
  2288. _layerSettingsDialog.gisLayer = gisLayer;
  2289. _layerSettingsDialog.show();
  2290. })
  2291. );
  2292. })
  2293. )
  2294. )
  2295. ))
  2296. );
  2297. }
  2298.  
  2299. function initSettingsTab() {
  2300. const states = _.uniq(_gisLayers.map(l => l.state));
  2301. const createRadioBtn = (name, value, text, checked) => {
  2302. const id = `${name}-${value}`;
  2303. return [$('<input>', {
  2304. type: 'radio', id, name, value
  2305. }).prop('checked', checked), $('<label>', { for: id }).text(text).css({
  2306. paddingLeft: '15px', marginRight: '4px'
  2307. })];
  2308. };
  2309. $('#panel-gis-layers-settings').empty().append(
  2310. $('<fieldset>', {
  2311. style: 'border:1px solid silver;padding:8px;border-radius:4px;-webkit-padding-before: 0;margin-top:-8px;'
  2312. }).append(
  2313. $('<legend>', {
  2314. style: 'margin-bottom:0px;border-bottom-style:none;width:auto;'
  2315. }).append($('<span>', {
  2316. style: 'font-size:14px;font-weight:600;text-transform: uppercase;'
  2317. }).text('Labels')),
  2318. $('<div>', { id: 'labelSettings' }).append(
  2319. $('<div>', { class: 'controls-container' }).css({ 'padding-top': '2px' }).append(
  2320. $('<label>', { style: 'font-weight:normal;' }).text('Addresses:'),
  2321. createRadioBtn('gisAddrDisplay', 'hn', 'HN', _settings.addrLabelDisplay === 'hn'),
  2322. createRadioBtn('gisAddrDisplay', 'street', 'Street', _settings.addrLabelDisplay === 'street'),
  2323. createRadioBtn('gisAddrDisplay', 'all', 'Both', _settings.addrLabelDisplay === 'all'),
  2324. createRadioBtn('gisAddrDisplay', 'none', 'None', _settings.addrLabelDisplay === 'none'),
  2325. $('<i>', {
  2326. class: 'waze-tooltip',
  2327. id: 'gisAddrDisplayInfo',
  2328. 'data-toggle': 'tooltip',
  2329. style: 'margin-left:8px; font-size:12px',
  2330. 'data-placement': 'bottom',
  2331. title: `This may not work properly for all layers. Please report issues to ${SCRIPT_AUTHOR}.`
  2332. }).tooltip()
  2333. )
  2334. )
  2335. ),
  2336. $('<fieldset>', {
  2337. style: 'border:1px solid silver;padding:8px;border-radius:4px;-webkit-padding-before: 0;'
  2338. }).append(
  2339. $('<legend>', {
  2340. style: 'margin-bottom:0px;border-bottom-style:none;width:auto;'
  2341. }).append($('<span>', {
  2342. style: 'font-size:14px;font-weight:600;text-transform: uppercase;'
  2343. }).text('Layer Categories')),
  2344. $('<div>', { id: 'states_body' }).append(
  2345. $('<div>').css({ 'font-size': '11px' }).append(
  2346. $('<span>').append(
  2347. 'Select ',
  2348. $('<a>', { href: '#' }).text('All').click(true, onSelectAllClick),
  2349. ' / ',
  2350. $('<a>', { href: '#' }).text('None').click(true, onSelectNoneClick)
  2351. )
  2352. ),
  2353. $('<div>', { class: 'controls-container', style: 'padding-top:0px;' }).append(
  2354. states.map(st => {
  2355. const fullName = STATES.toFullName(st);
  2356. const id = `gis-layer-enable-state-${st}`;
  2357. return $('<div>', { class: 'controls-container' })
  2358. .css({ 'padding-top': '0px', display: 'block' })
  2359. .append(
  2360. $('<input>', { type: 'checkbox', id, class: 'gis-layers-state-checkbox' })
  2361. .change(st, onStateCheckChanged)
  2362. .prop('checked', _settings.selectedStates.includes(st)),
  2363. $('<label>', { for: id }).css({ 'white-space': 'pre-line', color: '#777' }).text(fullName)
  2364. );
  2365. })
  2366. )
  2367. )
  2368. )
  2369. );
  2370. $('#panel-gis-layers-settings').append(
  2371. $('<fieldset>', { style: 'border:1px solid silver;padding:8px;border-radius:4px;-webkit-padding-before: 0;' })
  2372. .append(
  2373. $('<legend>', { style: 'margin-bottom:0px;border-bottom-style:none;width:auto;' })
  2374. .append(
  2375. $('<span>', { style: 'font-size:14px;font-weight:600;text-transform: uppercase;' })
  2376. .text('Appearance')
  2377. ),
  2378. $('<div>', { class: 'controls-container' }).css({ 'padding-top': '2px' }).append(
  2379. $('<input>', { type: 'checkbox', id: 'fill-parcels' })
  2380. .change(onFillParcelsCheckedChanged)
  2381. .prop('checked', _settings.fillParcels),
  2382. $('<label>', { for: 'fill-parcels' }).css({ 'white-space': 'pre-line', color: '#777' }).text('Fill parcels')
  2383. )
  2384. )
  2385. );
  2386. $('input[name=gisAddrDisplay]').change(onGisAddrDisplayChange);
  2387. }
  2388.  
  2389. async function initTab(firstCall = true) {
  2390. if (firstCall) {
  2391. const { user } = W.loginManager;
  2392. const content = $('<div>').append(
  2393. $('<span>', { style: 'font-size:14px;font-weight:600' }).text('GIS Layers'),
  2394. $('<span>', { style: 'font-size:11px;margin-left:10px;color:#aaa;' }).text(GM_info.script.version),
  2395. // <a href="https://docs.google.com/forms/d/e/1FAIpQLSevPQLz2ohu_LTge9gJ9Nv6PURmCmaSSjq0ayOJpGdRr2xI0g/viewform?usp=pp_url&entry.2116052852=test" target="_blank" style="color: #6290b7;font-size: 12px;margin-left: 8px;" title="Report broken layers, bugs, request new layers, script features">Report an issue</a>
  2396. $('<a>', {
  2397. href: REQUEST_FORM_URL.replace('{username}', user.attributes.userName),
  2398. target: '_blank',
  2399. style: 'color: #6290b7;font-size: 12px;margin-left: 8px;',
  2400. title: 'Report broken layers, bugs, request new layers, script features'
  2401. }).text('Submit a request'),
  2402. $('<span>', {
  2403. id: 'gis-layers-refresh',
  2404. class: 'fa fa-refresh',
  2405. style: 'float: right;',
  2406. 'data-toggle': 'tooltip',
  2407. title: 'Pull new layer info from master sheet and refresh all layers.'
  2408. }),
  2409. '<ul class="nav nav-tabs">'
  2410. + '<li class="active"><a data-toggle="tab" href="#panel-gis-state-layers" aria-expanded="true">'
  2411. + 'Layers'
  2412. + '</a></li>'
  2413. + '<li><a data-toggle="tab" href="#panel-gis-layers-settings" aria-expanded="true">'
  2414. + 'Settings'
  2415. + '</a></li> '
  2416. + '</ul>',
  2417. $('<div>', { class: 'tab-content', style: 'padding:8px;padding-top:2px' }).append(
  2418. $('<div>', { class: 'tab-pane active', id: 'panel-gis-state-layers', style: 'padding: 4px 0px 0px 0px; width: auto' }),
  2419. $('<div>', { class: 'tab-pane', id: 'panel-gis-layers-settings', style: 'padding: 4px 0px 0px 0px; width: auto' })
  2420. )
  2421. ).html();
  2422.  
  2423. const powerButtonColor = _settings.enabled ? '#00bd00' : '#ccc';
  2424. const labelText = $('<div>').append(
  2425. $('<span>', {
  2426. class: 'fa fa-power-off',
  2427. id: 'gis-layers-power-btn',
  2428. style: `margin-right: 5px;cursor: pointer;color: ${powerButtonColor};font-size: 13px;`,
  2429. title: 'Toggle GIS Layers'
  2430. }),
  2431. $('<span>', { title: 'GIS Layers' }).text('GIS-L')
  2432. ).html();
  2433.  
  2434. const { tabLabel, tabPane } = W.userscripts.registerSidebarTab('GIS-L');
  2435. tabLabel.innerHTML = labelText;
  2436. tabPane.innerHTML = content;
  2437. // Fix tab content div spacing.
  2438. $(tabPane).parent().css({ width: 'auto', padding: '6px' });
  2439.  
  2440. await W.userscripts.waitForElementConnected(tabPane);
  2441. $('#gis-layers-power-btn').click(() => {
  2442. setEnabled(!_settings.enabled);
  2443. });
  2444. $('#gis-layers-refresh').click(onRefreshLayersClick);
  2445. }
  2446.  
  2447. initSettingsTab();
  2448. initLayersTab();
  2449. }
  2450.  
  2451. function initGui(firstCall = true) {
  2452. initLayer();
  2453.  
  2454. if (firstCall) {
  2455. initTab(true);
  2456.  
  2457. WazeWrap.Interface.AddLayerCheckbox('Display', 'GIS Layers', _settings.enabled, onLayerCheckboxChanged);
  2458. // W.map.events.register('moveend', null, onMapMove);
  2459. WazeWrap.Events.register('moveend', null, onMapMove);
  2460. showScriptInfoAlert();
  2461. } else {
  2462. initTab(firstCall);
  2463. }
  2464. }
  2465.  
  2466. async function loadSpreadsheetAsync() {
  2467. let data;
  2468. try {
  2469. data = await $.getJSON(`${LAYER_DEF_SPREADSHEET_URL}?${DEC(API_KEY)}`);
  2470. } catch (err) {
  2471. throw new Error(`Spreadsheet call failed. (${err.status}: ${err.statusText})`);
  2472. }
  2473. const [[minVersion], fieldNames, ...layerDefRows] = data.values;
  2474. const REQUIRED_FIELD_NAMES = [
  2475. 'state', 'name', 'id', 'counties', 'url', 'where', 'labelFields',
  2476. 'processLabel', 'style', 'visibleAtZoom', 'labelsVisibleAtZoom', 'enabled',
  2477. 'restrictTo', 'oneTimeAlert'
  2478. ];
  2479. const result = { error: null };
  2480. const checkFieldNames = fldName => fieldNames.includes(fldName);
  2481.  
  2482. if (SCRIPT_VERSION < minVersion) {
  2483. result.error = `Script must be updated to at least version ${
  2484. minVersion} before layer definitions can be loaded.`;
  2485. } else if (fieldNames.length < REQUIRED_FIELD_NAMES.length) {
  2486. result.error = `Expected ${
  2487. REQUIRED_FIELD_NAMES.length} columns in layer definition data. Spreadsheet returned ${
  2488. fieldNames.length}.`;
  2489. } else if (!REQUIRED_FIELD_NAMES.every(fldName => checkFieldNames(fldName))) {
  2490. result.error = 'Script expected to see the following column names in the layer '
  2491. + `definition spreadsheet:\n${REQUIRED_FIELD_NAMES.join(', ')}\n`
  2492. + `But the spreadsheet returned these:\n${fieldNames.join(', ')}`;
  2493. }
  2494. if (!result.error) {
  2495. layerDefRows.filter(row => row.length).forEach(layerDefRow => {
  2496. const layerDef = { enabled: '0' };
  2497. fieldNames.forEach((fldName, fldIdx) => {
  2498. let value = layerDefRow[fldIdx];
  2499. if (value !== undefined && value.trim().length > 0) {
  2500. value = value.trim();
  2501. if (fldName === 'counties' || fldName === 'labelFields') {
  2502. value = value.split(',').map(item => item.trim());
  2503. } else if (fldName === 'processLabel') {
  2504. try {
  2505. value = ESTreeProcessor.compile(`function __$proc(){${value}} __$proc();`);
  2506. } catch (ex) {
  2507. layerDef.labelProcessingError = true;
  2508. logError(`Error loading label processing function for layer "${layerDef.id}".`);
  2509. logDebug(ex);
  2510. }
  2511. } else if (fldName === 'style') {
  2512. layerDef.isRoadLayer = value === 'roads';
  2513. if (LAYER_STYLES.hasOwnProperty(value)) {
  2514. value = LAYER_STYLES[value];
  2515. } else if (!layerDef.isRoadLayer) {
  2516. // If style is not defined, try to read in as JSON (custom style)
  2517. try {
  2518. value = JSON.parse(value);
  2519. } catch (ex) {
  2520. // ignore error
  2521. }
  2522. }
  2523. } else if (fldName === 'state') {
  2524. value = value ? value.toUpperCase() : value;
  2525. } else if (fldName === 'restrictTo') {
  2526. try {
  2527. const { user } = W.loginManager;
  2528. const values = value.split(',').map(v => v.trim().toLowerCase());
  2529. layerDef.notAllowed = !values.some(entry => {
  2530. const rankMatch = entry.match(/^r(\d)(\+am)?$/);
  2531. if (rankMatch) {
  2532. if (rankMatch[1] <= (user.attributes.rank + 1) && (!rankMatch[2] || user.attributes.isAreaManager)) {
  2533. return true;
  2534. }
  2535. } else if (entry === 'am' && user.attributes.isAreaManager) {
  2536. return true;
  2537. } else if (entry === user.attributes.userName.toLowerCase()) {
  2538. return true;
  2539. }
  2540. return false;
  2541. });
  2542. } catch (ex) {
  2543. logError(ex);
  2544. }
  2545. }
  2546. layerDef[fldName] = value;
  2547. } else if (fldName === 'labelFields') {
  2548. layerDef[fldName] = [''];
  2549. }
  2550. });
  2551. const enabled = layerDef.enabled && !['0', 'false', 'no', 'n'].includes(layerDef.enabled.toString().trim().toLowerCase());
  2552. if (!layerDef.notAllowed && (enabled || layerDef.restrictTo)) {
  2553. _gisLayers.push(layerDef);
  2554. }
  2555. });
  2556. }
  2557.  
  2558. return result;
  2559. }
  2560.  
  2561. function loadScriptUpdateMonitor() {
  2562. try {
  2563. const updateMonitor = new WazeWrap.Alerts.ScriptUpdateMonitor(SCRIPT_NAME, SCRIPT_VERSION, DOWNLOAD_URL, GM_xmlhttpRequest);
  2564. updateMonitor.start();
  2565. } catch (ex) {
  2566. // Report, but don't stop if ScriptUpdateMonitor fails.
  2567. logError(ex);
  2568. }
  2569. }
  2570.  
  2571. async function init(firstCall = true) {
  2572. _gisLayers = [];
  2573. if (firstCall) {
  2574. labelProcessingGlobalVariables.W = W;
  2575. loadScriptUpdateMonitor();
  2576. initRoadStyle();
  2577. loadSettingsFromStorage();
  2578. installPathFollowingLabels();
  2579. new WazeWrap.Interface.Shortcut(
  2580. 'GisLayersAddrDisplay',
  2581. 'Toggle HN-only address labels (GIS Layers)',
  2582. 'layers',
  2583. 'layersToggleGisAddressLabelDisplay',
  2584. _settings.toggleHnsOnlyShortcut,
  2585. onAddressDisplayShortcutKey,
  2586. null
  2587. ).add();
  2588. new WazeWrap.Interface.Shortcut(
  2589. 'GisLayersToggleEnabled',
  2590. 'Toggle display of GIS Layers',
  2591. 'layers',
  2592. 'layersToggleGisLayersEnabled',
  2593. _settings.toggleEnabledShortcut,
  2594. onToggleGisLayersShortcutKey,
  2595. null
  2596. ).add();
  2597. window.addEventListener('beforeunload', saveSettingsToStorage, false);
  2598. _layerSettingsDialog = new LayerSettingsDialog();
  2599. }
  2600. const t0 = performance.now();
  2601. try {
  2602. const result = await loadSpreadsheetAsync();
  2603. if (result.error) {
  2604. logError(result.error);
  2605. return;
  2606. }
  2607. _layerRefinements.forEach(layerRefinement => {
  2608. const layerDef = _gisLayers.find(layerDef2 => layerDef2.id === layerRefinement.id);
  2609. if (layerDef) {
  2610. Object.keys(layerRefinement).forEach(fldName => {
  2611. const value = layerRefinement[fldName];
  2612. if (fldName !== 'id' && layerDef.hasOwnProperty(fldName)) {
  2613. logDebug(`The "${fldName}" property of layer "${
  2614. layerDef.id}" has a value hardcoded in the script, and also defined in the spreadsheet.`
  2615. + ' The spreadsheet value takes precedence.');
  2616. } else if (value) layerDef[fldName] = value;
  2617. });
  2618. } else {
  2619. logDebug(`Refined layer "${layerRefinement.id}" does not have a corresponding layer defined`
  2620. + ' in the spreadsheet. It can probably be removed from the script.');
  2621. }
  2622. });
  2623. logDebug(`Loaded ${_gisLayers.length} layer definitions in ${Math.round(performance.now() - t0)} ms.`);
  2624. initGui(firstCall);
  2625. fetchFeatures();
  2626. $('#gis-layers-refresh').removeClass('fa-spin').css({ cursor: 'pointer' });
  2627. logDebug('Initialized.');
  2628. } catch (err) {
  2629. logError(err);
  2630. }
  2631. }
  2632.  
  2633. function onWmeReady() {
  2634. if (WazeWrap && WazeWrap.Ready) {
  2635. logDebug('Initializing...');
  2636. init();
  2637. } else {
  2638. setTimeout(onWmeReady, 100);
  2639. }
  2640. }
  2641.  
  2642. function bootstrap() {
  2643. if (typeof W === 'object' && W.userscripts?.state.isReady) {
  2644. onWmeReady();
  2645. } else {
  2646. document.addEventListener('wme-ready', onWmeReady, { once: true });
  2647. }
  2648. }
  2649.  
  2650. bootstrap();
  2651.  
  2652. /*eslint-disable*/
  2653. function installPathFollowingLabels() {
  2654. // Copyright (c) 2015 by Jean-Marc.Viglino [at]ign.fr
  2655. // Dual-licensed under the CeCILL-B Licence (http://www.cecill.info/)
  2656. // and the Beerware license (http://en.wikipedia.org/wiki/Beerware),
  2657. // feel free to use and abuse it in your projects (the code, not the beer ;-).
  2658. //
  2659. //* Overwrite the SVG function to allow text along a path
  2660. //* setStyle function
  2661. //*
  2662. //* Add new options to the Openlayers.Style
  2663.  
  2664. // pathLabel: {String} Label to draw on the path
  2665. // pathLabelXOffset: {String} Offset along the line to start drawing text in pixel or %, default: "50%"
  2666. // pathLabelYOffset: {Number} Distance of the line to draw the text
  2667. // pathLabelCurve: {String} Smooth the line the label is drawn on (empty string for no)
  2668. // pathLabelReadable: {String} Make the label readable (empty string for no)
  2669.  
  2670. // * Extra standard values : all label and text values
  2671.  
  2672.  
  2673. // *
  2674. // * Method: removeChildById
  2675. // * Remove child in a node.
  2676. // *
  2677.  
  2678. function removeChildById(node, id) {
  2679. if (node.querySelector) {
  2680. var c = node.querySelector('#' + id);
  2681. if (c) node.removeChild(c);
  2682. return;
  2683. }
  2684. // For old browsers
  2685. var c = node.childNodes;
  2686. if (c) for (var i = 0; i < c.length; i++) {
  2687. if (c[i].id === id) {
  2688. node.removeChild(c[i]);
  2689. return;
  2690. }
  2691. }
  2692. }
  2693.  
  2694.  
  2695. // *
  2696. // * Method: setStyle
  2697. // * Use to set all the style attributes to a SVG node.
  2698. // *
  2699. // * Takes care to adjust stroke width and point radius to be
  2700. // * resolution-relative
  2701. // *
  2702. // * Parameters:
  2703. // * node - {SVGDomElement} An SVG element to decorate
  2704. // * style - {Object}
  2705. // * options - {Object} Currently supported options include
  2706. // * 'isFilled' {Boolean} and
  2707. // * 'isStroked' {Boolean}
  2708.  
  2709. var setStyle = OpenLayers.Renderer.SVG.prototype.setStyle;
  2710. OpenLayers.Renderer.SVG.LABEL_STARTOFFSET = { 'l': '0%', 'r': '100%', 'm': '50%' };
  2711.  
  2712. OpenLayers.Renderer.SVG.prototype.pathText = function (node, style, suffix) {
  2713. var label = this.nodeFactory(null, 'text');
  2714. label.setAttribute('id', node._featureId + '_' + suffix);
  2715. if (style.fontColor) label.setAttributeNS(null, 'fill', style.fontColor);
  2716. if (style.fontStrokeColor) label.setAttributeNS(null, 'stroke', style.fontStrokeColor);
  2717. if (style.fontStrokeWidth) label.setAttributeNS(null, 'stroke-width', style.fontStrokeWidth);
  2718. if (style.fontOpacity) label.setAttributeNS(null, 'opacity', style.fontOpacity);
  2719. if (style.fontFamily) label.setAttributeNS(null, 'font-family', style.fontFamily);
  2720. if (style.fontSize) label.setAttributeNS(null, 'font-size', style.fontSize);
  2721. if (style.fontWeight) label.setAttributeNS(null, 'font-weight', style.fontWeight);
  2722. if (style.fontStyle) label.setAttributeNS(null, 'font-style', style.fontStyle);
  2723. if (style.labelSelect === true) {
  2724. label.setAttributeNS(null, 'pointer-events', 'visible');
  2725. label._featureId = node._featureId;
  2726. } else {
  2727. label.setAttributeNS(null, 'pointer-events', 'none');
  2728. }
  2729.  
  2730. function getpath(pathStr, readeable) {
  2731. var npath = pathStr.split(',');
  2732. var pts = [];
  2733. if (!readeable || Number(npath[0]) - Number(npath[npath.length - 2]) < 0) {
  2734. while (npath.length) pts.push({ x: Number(npath.shift()), y: Number(npath.shift()) });
  2735. } else {
  2736. while (npath.length) pts.unshift({ x: Number(npath.shift()), y: Number(npath.shift()) });
  2737. }
  2738. return pts;
  2739. }
  2740.  
  2741. var path = this.nodeFactory(null, 'path');
  2742. var tpid = node._featureId + '_t' + suffix;
  2743. var tpath = node.getAttribute('points');
  2744. if (style.pathLabelCurve) {
  2745. var pts = getpath(tpath, style.pathLabelReadable);
  2746. var p = pts[0].x + ' ' + pts[0].y;
  2747. var dx, dy, s1, s2;
  2748. dx = (pts[0].x - pts[1].x) / 4;
  2749. dy = (pts[0].y - pts[1].y) / 4;
  2750. for (var i = 1; i < pts.length - 1; i++) {
  2751. p += ' C ' + (pts[i - 1].x - dx) + ' ' + (pts[i - 1].y - dy);
  2752. dx = (pts[i - 1].x - pts[i + 1].x) / 4;
  2753. dy = (pts[i - 1].y - pts[i + 1].y) / 4;
  2754. s1 = Math.sqrt(Math.pow(pts[i - 1].x - pts[i].x, 2) + Math.pow(pts[i - 1].y - pts[i].y, 2));
  2755. s2 = Math.sqrt(Math.pow(pts[i + 1].x - pts[i].x, 2) + Math.pow(pts[i + 1].y - pts[i].y, 2));
  2756. p += ' ' + (pts[i].x + s1 * dx / s2) + ' ' + (pts[i].y + s1 * dy / s2);
  2757. dx *= s2 / s1;
  2758. dy *= s2 / s1;
  2759. p += ' ' + pts[i].x + ' ' + pts[i].y;
  2760. }
  2761. p += ' C ' + (pts[i - 1].x - dx) + ' ' + (pts[i - 1].y - dy);
  2762. dx = (pts[i - 1].x - pts[i].x) / 4;
  2763. dy = (pts[i - 1].y - pts[i].y) / 4;
  2764. p += ' ' + (pts[i].x + dx) + ' ' + (pts[i].y + dy);
  2765. p += ' ' + pts[i].x + ' ' + pts[i].y;
  2766.  
  2767. path.setAttribute('d', 'M ' + p);
  2768. } else {
  2769. if (style.pathLabelReadable) {
  2770. var pts = getpath(tpath, style.pathLabelReadable);
  2771. var p = '';
  2772. for (var i = 0; i < pts.length; i++) p += ' ' + pts[i].x + ' ' + pts[i].y;
  2773. path.setAttribute('d', 'M ' + p);
  2774. } else path.setAttribute('d', 'M ' + tpath);
  2775. }
  2776. path.setAttribute('id', tpid);
  2777.  
  2778. var defs = this.createDefs();
  2779. removeChildById(defs, tpid);
  2780. defs.appendChild(path);
  2781.  
  2782. var textPath = this.nodeFactory(null, 'textPath');
  2783. textPath.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + tpid);
  2784. var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
  2785. label.setAttributeNS(null, 'text-anchor', OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || 'middle');
  2786. textPath.setAttribute('startOffset', style.pathLabelXOffset || OpenLayers.Renderer.SVG.LABEL_STARTOFFSET[align[0]] || '50%');
  2787. label.setAttributeNS(null, 'dominant-baseline', OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || 'central');
  2788. if (style.pathLabelYOffset) label.setAttribute('dy', style.pathLabelYOffset);
  2789. //textPath.setAttribute('method','stretch');
  2790. //textPath.setAttribute('spacing','auto');
  2791.  
  2792. textPath.textContent = style.pathLabel;
  2793. label.appendChild(textPath);
  2794.  
  2795. removeChildById(this.textRoot, node._featureId + '_' + suffix);
  2796. this.textRoot.appendChild(label);
  2797. };
  2798.  
  2799. OpenLayers.Renderer.SVG.prototype.setStyle = function (node, style, options) {
  2800. if (node._geometryClass === 'OpenLayers.Geometry.LineString' && style.pathLabel) {
  2801. if (node._geometryClass === 'OpenLayers.Geometry.LineString' && style.pathLabel) {
  2802. var drawOutline = (!!style.labelOutlineWidth);
  2803. // First draw text in halo color and size and overlay the
  2804. // normal text afterwards
  2805. if (drawOutline) {
  2806. var outlineStyle = OpenLayers.Util.extend({}, style);
  2807. outlineStyle.fontColor = outlineStyle.labelOutlineColor;
  2808. outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
  2809. outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
  2810. if (style.labelOutlineOpacity) outlineStyle.fontOpacity = style.labelOutlineOpacity;
  2811. delete outlineStyle.labelOutlineWidth;
  2812. this.pathText(node, outlineStyle, 'txtpath0');
  2813. }
  2814. this.pathText(node, style, 'txtpath');
  2815. setStyle.apply(this, arguments);
  2816. }
  2817. } else setStyle.apply(this, arguments);
  2818. return node;
  2819. };
  2820.  
  2821. // *
  2822. // * Method: drawGeometry
  2823. // * Remove the textpath if no geometry is drawn.
  2824. // *
  2825. // * Parameters:
  2826. // * geometry - {<OpenLayers.Geometry>}
  2827. // * style - {Object}
  2828. // * featureId - {String}
  2829. // *
  2830. // * Returns:
  2831. // * {Boolean} true if the geometry has been drawn completely; null if
  2832. // * incomplete; false otherwise
  2833.  
  2834. var drawGeometry = OpenLayers.Renderer.SVG.prototype.drawGeometry;
  2835. OpenLayers.Renderer.SVG.prototype.drawGeometry = function (geometry, style, id) {
  2836. var rendered = drawGeometry.apply(this, arguments);
  2837. if (rendered === false) {
  2838. removeChildById(this.textRoot, id + '_txtpath');
  2839. removeChildById(this.textRoot, id + '_txtpath0');
  2840. }
  2841. return rendered;
  2842. };
  2843.  
  2844. // *
  2845. // * Method: eraseGeometry
  2846. // * Erase a geometry from the renderer. In the case of a multi-geometry,
  2847. // * we cycle through and recurse on ourselves. Otherwise, we look for a
  2848. // * node with the geometry.id, destroy its geometry, and remove it from
  2849. // * the DOM.
  2850. // *
  2851. // * Parameters:
  2852. // * geometry - {<OpenLayers.Geometry>}
  2853. // * featureId - {String}
  2854.  
  2855. var eraseGeometry = OpenLayers.Renderer.SVG.prototype.eraseGeometry;
  2856. OpenLayers.Renderer.SVG.prototype.eraseGeometry = function (geometry, featureId) {
  2857. eraseGeometry.apply(this, arguments);
  2858. removeChildById(this.textRoot, featureId + '_txtpath');
  2859. removeChildById(this.textRoot, featureId + '_txtpath0');
  2860. };
  2861.  
  2862. }
  2863. })();

QingJ © 2025

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