WME FC Layer

Adds a Functional Class layer for states that publish ArcGIS FC data.

当前为 2018-07-22 提交的版本,查看 最新版本

  1. /* global W */
  2. /* global Promise */
  3. /* global OL */
  4. /* global I18n */
  5. /* global unsafeWindow */
  6. /* global GM_info */
  7.  
  8. // // ==UserScript==
  9. // @name WME FC Layer
  10. // @namespace https://greasyfork.org/users/45389
  11. // @version 2018.07.21.001
  12. // @description Adds a Functional Class layer for states that publish ArcGIS FC data.
  13. // @author MapOMatic
  14. // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
  15. // @license GNU GPLv3
  16. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  17. // @require https://greasyfork.org/scripts/39002-bluebird/code/Bluebird.js?version=255146
  18. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  19. // @grant GM_xmlhttpRequest
  20. // @connect md.gov
  21. // @connect in.gov
  22. // @connect arcgis.com
  23. // @connect ncdot.gov
  24. // @connect state.mi.us
  25. // @connect dc.gov
  26. // @connect la.gov
  27. // @connect nd.gov
  28. // @connect pa.gov
  29. // @connect oh.us
  30. // @connect ky.gov
  31. // @connect shelbycountytn.gov
  32. // @connect illinois.gov
  33. // @connect ny.gov
  34. // @connect utah.gov
  35. // @connect idaho.gov
  36. // @connect wv.gov
  37. // @connect ga.gov
  38. // @connect uga.edu
  39. // ==/UserScript==
  40.  
  41. (function() {
  42. 'use strict';
  43.  
  44. var _settingsStoreName = 'wme_fc_layer';
  45. var _alertUpdate = false;
  46. var _debugLevel = 0;
  47. var _scriptVersion = GM_info.script.version;
  48. var _scriptVersionChanges = [
  49. GM_info.script.name,
  50. 'v' + _scriptVersion,
  51. '',
  52. 'What\'s New',
  53. '------------------------------',
  54. '' // Add important stuff here when _alertUpdate = true.
  55. ].join('\n');
  56. var _mapLayer = null;
  57. var _isAM = false;
  58. var _uid;
  59. var _settings = {};
  60. var _r;
  61. var _mapLayerZIndex = 334;
  62. var _betaIDs = [103400892];
  63. var _statesHash = {
  64. 'Alabama':'AL','Alaska':'AK','American Samoa':'AS','Arizona':'AZ','Arkansas':'AR','California':'CA','Colorado':'CO','Connecticut':'CT','Delaware':'DE','District of Columbia':'DC',
  65. 'Federated States Of Micronesia':'FM','Florida':'FL','Georgia':'GA','Guam':'GU','Hawaii':'HI','Idaho':'ID','Illinois':'IL','Indiana':'IN','Iowa':'IA','Kansas':'KS',
  66. 'Kentucky':'KY','Louisiana':'LA','Maine':'ME','Marshall Islands':'MH','Maryland':'MD','Massachusetts':'MA','Michigan':'MI','Minnesota':'MN','Mississippi':'MS','Missouri':'MO',
  67. 'Montana':'MT','Nebraska':'NE','Nevada':'NV','New Hampshire':'NH','New Jersey':'NJ','New Mexico':'NM','New York':'NY','North Carolina':'NC','North Dakota':'ND',
  68. 'Northern Mariana Islands':'MP','Ohio':'OH','Oklahoma':'OK','Oregon':'OR','Palau':'PW','Pennsylvania':'PA','Puerto Rico':'PR','Rhode Island':'RI','South Carolina':'SC',
  69. 'South Dakota':'SD','Tennessee':'TN','Texas':'TX','Utah':'UT','Vermont':'VT','Virgin Islands':'VI','Virginia':'VA','Washington':'WA','West Virginia':'WV','Wisconsin':'WI','Wyoming':'WY'
  70. };
  71.  
  72. function reverseStatesHash(stateAbbr) {
  73. for (var stateName in _statesHash) {
  74. if (_statesHash[stateName] === stateAbbr) return stateName;
  75. }
  76. }
  77. var _stateSettings = {
  78. global: {
  79. roadTypes: ['St','PS','PS2','mH','MH','Ew','Rmp','Fw'], // Ew = Expressway. For FC's that make it uncertain if they should be MH or FW.
  80. getFeatureRoadType: function(feature, layer) {
  81. var fc = feature.attributes[layer.fcPropName];
  82. return this.getRoadTypeFromFC(fc, layer);
  83. },
  84. getRoadTypeFromFC: function(fc, layer) {
  85. for (var roadType in layer.roadTypeMap) {
  86. if (layer.roadTypeMap[roadType].indexOf(fc) !== -1) {
  87. return roadType;
  88. }
  89. }
  90. return null;
  91. },
  92. isPermitted: function(stateAbbr) {if(_betaIDs.indexOf(_uid)!==-1)return true;var state=_stateSettings[stateAbbr];if(state.isPermitted){return state.isPermitted();}else{return(_r>=2&&_isAM)||(_r>=3);}},
  93. getMapLayer: function(stateAbbr, layerID) {
  94. var returnValue;
  95. _stateSettings[stateAbbr].fcMapLayers.forEach(function(layer) {
  96. if (layer.layerID === layerID) {
  97. returnValue = layer;
  98. }
  99. });
  100. return returnValue;
  101. }
  102. },
  103. DC: {
  104. baseUrl: 'http://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Transportation_WebMercator/MapServer/',
  105. supportsPagination: false,
  106. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  107. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [[],[],[],[],[],[],[],[],[],[],[]] },
  108. fetchAllFC: false,
  109. fcMapLayers: [
  110. { layerID:48, fcPropName:'FUNCTIONALCLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONALCLASS'], maxRecordCount:1000, supportsPagination:false,
  111. roadTypeMap:{Fw:['Interstate'],Ew:['Other Freeway and Expressway'],MH:['Principal Arterial'],mH:['Minor Arterial'],PS:['Collector']} }
  112. ],
  113. getFeatureRoadType: function(feature, layer) {
  114. if (layer.getFeatureRoadType) {
  115. return layer.getFeatureRoadType(feature);
  116. } else {
  117. return _stateSettings.global.getFeatureRoadType(feature, layer);
  118. }
  119. },
  120. getWhereClause: function(context) {
  121. return null;
  122. }
  123. },
  124. FL: {
  125. baseUrl: 'https://services1.arcgis.com/O1JpcwDW8sjYuddV/ArcGIS/rest/services/Functional_Classification_TDA/FeatureServer/',
  126. supportsPagination: false,
  127. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  128. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [[],[],[],[],[],[],[],[],[],[],[]] },
  129. fetchAllFC: false,
  130. fcMapLayers: [
  131. { layerID:0, fcPropName:'FUNCLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCLASS'], maxRecordCount:1000, supportsPagination:false,
  132. roadTypeMap:{Fw:['01','11'],Ew:['02','12'],MH:['04','14'],mH:['06','16'],PS:['07','08','17','18']} }
  133. ],
  134. getFeatureRoadType: function(feature, layer) {
  135. if (layer.getFeatureRoadType) {
  136. return layer.getFeatureRoadType(feature);
  137. } else {
  138. return _stateSettings.global.getFeatureRoadType(feature, layer);
  139. }
  140. },
  141. getWhereClause: function(context) {
  142. return null;
  143. }
  144. },
  145. GA: {
  146. baseUrl: 'https://maps.itos.uga.edu/arcgis/rest/services/GDOT/GDOT_FunctionalClass/mapserver/',
  147. supportsPagination: true,
  148. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  149. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [[],[],[],[],[],[],[],[],[],[],[]] },
  150. fetchAllFC: false,
  151. fcMapLayers: [
  152. { layerID:0, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  153. { layerID:1, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  154. { layerID:2, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  155. { layerID:3, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  156. { layerID:4, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  157. { layerID:5, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  158. { layerID:6, fcPropName:'FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID', 'FUNCTIONAL_CLASS', 'SYSTEM_CODE'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} }
  159. ],
  160. getFeatureRoadType: function(feature, layer) {
  161. if (layer.getFeatureRoadType) {
  162. return layer.getFeatureRoadType(feature);
  163. } else {
  164. var attr = feature.attributes;
  165. var fc = attr.FUNCTIONAL_CLASS;
  166. if (attr.SYSTEM_CODE === '1' && fc > 4) {
  167. return _stateSettings.global.getRoadTypeFromFC(4, layer);
  168. } else {
  169. return _stateSettings.global.getFeatureRoadType(feature, layer);
  170. }
  171. }
  172. },
  173. getWhereClause: function(context) {
  174. return null;
  175. }
  176. },
  177. ID: {
  178. baseUrl: 'https://gis.itd.idaho.gov/arcgisprod/rest/services/IPLAN/Functional_Classification/MapServer/',
  179. supportsPagination: false,
  180. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  181. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [[],[],[],[],[],[],[],[],[],[],[]] },
  182. fetchAllFC: true,
  183. fcMapLayers: [
  184. { layerID:0, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  185. { layerID:1, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  186. { layerID:2, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  187. { layerID:3, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  188. { layerID:4, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} },
  189. { layerID:5, fcPropName:'FCCODE', idPropName:'OBJECTID', outFields:['OBJECTID', 'FCCODE'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6]} }
  190. ],
  191. getFeatureRoadType: function(feature, layer) {
  192. if (layer.getFeatureRoadType) {
  193. return layer.getFeatureRoadType(feature);
  194. } else {
  195. return _stateSettings.global.getFeatureRoadType(feature, layer);
  196. }
  197. },
  198. getWhereClause: function(context) {
  199. return null;
  200. }
  201. },
  202. IL: {
  203. baseUrl: 'http://ags10s1.dot.illinois.gov/ArcGIS/rest/services/AdministrativeData/Roads/MapServer/',
  204. supportsPagination: false,
  205. defaultColors: {Fw:'#ff00c5',Ew:'#ff00c5',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee',CH:'#ff5e0e'},
  206. zoomSettings: { maxOffset:[30,15,8,4,2,1,1,1,1,1] },
  207. fcMapLayers: [
  208. { layerID:0, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  209. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  210. { layerID:1, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  211. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  212. { layerID:2, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  213. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  214. { layerID:3, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  215. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  216. { layerID:4, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  217. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  218. { layerID:5, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  219. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  220. { layerID:6, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  221. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  222. { layerID:7, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  223. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false },
  224. { layerID:8, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','MARKED_RT','MARKED_RT2','MARKED_RT3','MARKED_RT4','CH'],
  225. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7'],CH:['8']}, maxRecordCount:1000, supportsPagination:false }
  226. ],
  227. isPermitted: function() { return _r >= 3; },
  228. getWhereClause: function(context) {
  229. if(context.mapContext.zoom < 4) {
  230. return "FC<>'7' OR (FC='7' AND CH<>'0000')";
  231. } else {
  232. return null;
  233. }
  234. },
  235. getFeatureRoadType: function(feature, layer) {
  236. var attr = feature.attributes;
  237. var isUS = false;
  238. var isState = false;
  239. var isCounty = attr.CH !== '0000';
  240. [attr.MARKED_RT, attr.MARKED_RT2, attr.MARKED_RT3, attr.MARKED_RT4].forEach(function(rt) {
  241. if (!isUS) {
  242. isUS = /U\d+/.test(rt);
  243. if (!isUS && !isState) {
  244. isState = /S\d+/.test(rt);
  245. }
  246. }
  247. });
  248. var fc = attr.FC;
  249. fc = (fc > 3 && isUS) ? '3' : (fc > 4 && isState) ? '4' : (fc === '7' && isCounty) ? '8' : fc;
  250. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  251. }
  252. },
  253. IN: {
  254. baseUrl: 'https://gis.in.gov/arcgis/rest/services/DOT/INDOT_LTAP/FeatureServer/',
  255. supportsPagination: false,
  256. overrideUrl: '1Sbwc7e6BfHpZWSTfU3_1otXGSxHrdDYcbn7fOf1VjpA',
  257. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  258. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]], hideRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  259. fcMapLayers: [
  260. { layerID:10, idPropName:'OBJECTID', fcPropName:'FUNCTIONAL_CLASS', outFields:['FUNCTIONAL_CLASS','OBJECTID','TO_DATE'],
  261. roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, maxRecordCount:100000, supportsPagination:false }
  262. ],
  263. isPermitted: function() { return true; },
  264. getWhereClauses: function(context) {
  265. },
  266. getWhereClause: function(context) {
  267. var whereParts = [];
  268. if(context.mapContext.zoom < 4) {
  269. whereParts.push(context.layer.fcPropName + '<>7');
  270. }
  271. whereParts.push('TO_DATE IS NULL');
  272. return whereParts.join(' AND ');
  273. },
  274. getFeatureRoadType: function(feature, layer) {
  275. if (layer.getFeatureRoadType) {
  276. return layer.getFeatureRoadType(feature);
  277. } else {
  278. return _stateSettings.global.getFeatureRoadType(feature, layer);
  279. }
  280. }
  281. },
  282. KY: {
  283. baseUrl: 'https://maps.kytc.ky.gov/arcgis/rest/services/BaseMap/System/MapServer/',
  284. supportsPagination: false,
  285. defaultColors: {Fw:'#ff00c5',Ew:'#ff00c5',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  286. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1] },
  287. fcMapLayers: [
  288. { layerID:0, idPropName:'OBJECTID', fcPropName:'FC', outFields:['FC','OBJECTID','RT_PREFIX', 'RT_SUFFIX'],
  289. roadTypeMap:{Fw:['1'],Ew:['2'],MH:['3'],mH:['4'],PS:['5','6'],St:['7']}, maxRecordCount:1000, supportsPagination:false }
  290. ],
  291. isPermitted: function() { return true; },
  292. getWhereClause: function(context) {
  293. if(context.mapContext.zoom < 4) {
  294. return context.layer.fcPropName + "<>'7'";
  295. } else {
  296. return null;
  297. }
  298. },
  299. getFeatureRoadType: function(feature, layer) {
  300. if (feature.attributes.RT_PREFIX === 'US') {
  301. var suffix = feature.attributes.RT_SUFFIX;
  302. var type = 'MH';
  303. if (suffix && suffix.indexOf('X') > -1) type = 'mH';
  304. return type;
  305. } else {
  306. return _stateSettings.global.getFeatureRoadType(feature, layer);
  307. }
  308. }
  309. },
  310. LA: {
  311. baseUrl: 'https://giswebnew.dotd.la.gov/arcgis/rest/services/Transportation/LA_RoadwayFunctionalClassification/FeatureServer/',
  312. supportsPagination: false,
  313. defaultColors: {Fw:'#4094ff',Ew:'#ffbf40',MH:'#fb674d',mH:'#6abe40',PS1:'#bf40ec',PS2:'#ffff40',St:'#a2a2a2'},
  314. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  315. fcMapLayers: [
  316. { layerID:0, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  317. { layerID:1, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  318. { layerID:2, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  319. { layerID:3, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  320. { layerID:4, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  321. { layerID:5, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false },
  322. { layerID:6, fcPropName:'FunctionalSystem', idPropName:'OBJECTID', outFields:['OBJECTID','FunctionalSystem'], roadTypeMap:{Fw:[1],Ew:['2','2a','2b'],MH:[3],mH:[4],PS1:[5],PS2:[6],St:[7]}, maxRecordCount:1000, supportsPagination:false }
  323. ],
  324. getWhereClause: function(context) {
  325. if(context.mapContext.zoom < 4) {
  326. return context.layer.fcPropName + "<>'7'"; // OR State_Route LIKE 'US%' OR State_Route LIKE 'LA%'";
  327. } else {
  328. return null;
  329. }
  330. },
  331. getFeatureRoadType: function(feature, layer) {
  332. var fc = feature.attributes[layer.fcPropName];
  333. if (fc === '2a' || fc === '2b') { fc = 2; }
  334. fc = parseInt(fc);
  335. // var stateRoute = feature.attributes.State_Route;
  336. // var isBusiness = /BUS$/.test(stateRoute);
  337. // if (fc > 3 && /^US\s/.test(stateRoute) && !isBusiness) {
  338. // fc = 3;
  339. // } else if (fc > 4 && /^LA\s/.test(stateRoute) && !isBusiness) {
  340. // fc = 4;
  341. // }
  342. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  343. }
  344. },
  345. MD: {
  346. baseUrl: 'http://geodata.md.gov/imap/rest/services/Transportation/MD_HighwayPerformanceMonitoringSystem/MapServer/',
  347. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#ffff00',St:'#eeeeee'},
  348. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  349. fcMapLayers: [
  350. { layerID:3, fcPropName:'F_SYSTEM', idPropName:'OBJECTID', outFields:['OBJECTID','F_SYSTEM','ID_PREFIX','MP_SUFFIX'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, maxRecordCount:1000, supportsPagination:false }
  351. ],
  352. getWhereClause: function(context) {
  353. if(context.mapContext.zoom < 4) {
  354. return "(F_SYSTEM < 7 OR ID_PREFIX IN('MD'))";
  355. } else {
  356. return null;
  357. }
  358. },
  359. getFeatureRoadType: function(feature,layer) {
  360. var attr = feature.attributes;
  361. var fc = parseInt(attr.F_SYSTEM);
  362. var isState = attr.ID_PREFIX === 'MD';
  363. var isUS = attr.ID_PREFIX === 'US';
  364. var isBusiness = attr.MP_SUFFIX === 'BU';
  365. if (fc > 4 && isState) { fc = (isBusiness ? Math.min(fc,5) : 4); }
  366. else if (fc > 3 && isUS) { fc = (isBusiness ? Math.min(fc, 4) : 3 );}
  367. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  368. }
  369. },
  370. MI: {
  371. baseUrl: 'http://gisp.mcgi.state.mi.us/arcgis/rest/services/MDOT/NFC/MapServer/',
  372. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  373. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  374. fcMapLayers: [
  375. { layerID:2, idPropName:'OBJECTID', fcPropName:'NFC', outFields:['NFC'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, maxRecordCount:1000, supportsPagination:false }
  376. ],
  377. isPermitted: function() { return true; },
  378. getWhereClause: function(context) {
  379. if(context.mapContext.zoom < 4) {
  380. return context.layer.fcPropName + '<>7';
  381. } else {
  382. return null;
  383. }
  384. },
  385. getFeatureRoadType: function(feature, layer) {
  386. if (layer.getFeatureRoadType) {
  387. return layer.getFeatureRoadType(feature);
  388. } else {
  389. return _stateSettings.global.getFeatureRoadType(feature, layer);
  390. }
  391. }
  392. },
  393. NC: {
  394. baseUrl: 'https://gis11.services.ncdot.gov/arcgis/rest/services/NCDOT_FunctionalClass/MapServer/',
  395. defaultColors: {Fw:'#ff00c5',Rmp:'#999999',Ew:'#5f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  396. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  397. fcMapLayers: [
  398. { layerID:0, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[3,4,5,6,7,8,9,10], maxRecordCount:1000, supportsPagination:false }
  399. //{ layerID:2, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[2], maxRecordCount:1000, supportsPagination:false },
  400. //{ layerID:3, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[0,1], maxRecordCount:1000, supportsPagination:false },
  401. //{ layerID:4, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[], maxRecordCount:1000, supportsPagination:false },
  402. //{ layerID:5, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[], maxRecordCount:1000, supportsPagination:false },
  403. //{ layerID:6, fcPropName:'FC_TYP_CD', idPropName:'OBJECTID', outFields:['OBJECTID','FC_TYP_CD','RTE_1_CLSS_CD'], roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]}, zoomLevels:[], maxRecordCount:1000, supportsPagination:false }
  404. ],
  405. isPermitted: function() { return _r > 1; },
  406. getWhereClause: function(context) {
  407. if(context.mapContext.zoom < 4) {
  408. var clause = '(' + context.layer.fcPropName + " < 7 OR RTE_1_CLSS_CD IN ('I','FED','NC','RMP','US'))";
  409. return clause;
  410. } else {
  411. return null;
  412. }
  413. },
  414. getFeatureRoadType: function(feature, layer) {
  415. var fc = feature.attributes[layer.fcPropName];
  416. var roadType;
  417. switch (this.getHwySys(feature)) {
  418. case 'interstate':
  419. roadType = 'Fw';
  420. break;
  421. case 'us':
  422. roadType = fc <= 2 ? 'Ew' : 'MH';
  423. break;
  424. case 'state':
  425. roadType = fc === 2 ? 'Ew' : (fc === 3 ? 'MH' : 'mH');
  426. break;
  427. case 'ramp':
  428. roadType = 'Rmp';
  429. break;
  430. default:
  431. roadType = fc === 2 ? 'Ew' : (fc === 3 ? 'MH' : (fc === 4 ? 'mH' : (fc <= 6 ? 'PS' : 'St')));
  432. }
  433. return roadType;
  434. },
  435. getHwySys: function(feature) {
  436. var hwySys;
  437. switch (feature.attributes.RTE_1_CLSS_CD) {
  438. case 'I':
  439. hwySys = 'interstate';
  440. break;
  441. case 'FED':
  442. case 'US':
  443. hwySys = 'us';
  444. break;
  445. case 'NC':
  446. hwySys = 'state';
  447. break;
  448. case 'RMP':
  449. hwySys = 'ramp';
  450. break;
  451. default:
  452. hwySys = 'local';
  453. }
  454. return hwySys;
  455. }
  456. },
  457. ND: {
  458. baseUrl: 'https://gis.dot.nd.gov/arcgis/rest/services/external/transinfo/MapServer/',
  459. defaultColors: {Fw:'#ff00c5',Ew:'#149ece',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  460. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  461. fcMapLayers: [
  462. { layerID:10, fcPropName:'FUNCTION_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID','FUNCTION_CLASS'], roadTypeMap:{Fw:['Interstate'],MH:['Principal Arterial'],mH:['Minor Arterial'],PS:['Major Collector','Collector'],St:['Local']},
  463. maxRecordCount:1000, supportsPagination:false},
  464. { layerID:11, fcPropName:'FUNCTION_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID','FUNCTION_CLASS'], roadTypeMap:{Fw:['Interstate'],MH:['Principal Arterial'],mH:['Minor Arterial'],PS:['Major Collector','Collector'],St:['Local']},
  465. maxRecordCount:1000, supportsPagination:false},
  466. { layerID:12, fcPropName:'FUNCTION_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID','FUNCTION_CLASS'], roadTypeMap:{PS:['Major Collector','Collector']},
  467. maxRecordCount:1000, supportsPagination:false},
  468. { layerID:16, fcPropName:'SYSTEM_CD', idPropName:'OBJECTID', outFields:['OBJECTID','SYSTEM_CD','SYSTEM_DESC','HIGHWAY'], roadTypeMap:{Fw:[1,11],MH:[2,14],mH:[6,7,16,19]},
  469. maxRecordCount:1000, supportsPagination:false}
  470. ],
  471. getWhereClause: function(context) {
  472. if(context.mapContext.zoom < 4) {
  473. if (context.layer.layerID !== 16) return context.layer.fcPropName + "<>'Local'";
  474. } else {
  475. return null;
  476. }
  477. },
  478. getFeatureRoadType: function(feature, layer) {
  479. return _stateSettings.global.getFeatureRoadType(feature, layer);
  480. }
  481. },
  482. NY: {//https://gis3.dot.ny.gov/arcgis/rest/services/Basemap/MapServer/21
  483. baseUrl: 'https://gis3.dot.ny.gov/arcgis/rest/services/',
  484. defaultColors: {Fw:'#ff00c5',Ew:'#5f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  485. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1] },
  486. fcMapLayers: [
  487. { layerID:'FC/MapServer/1', fcPropName:'FUNC_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID','FUNC_CLASS','SEGMENT_NAME','ROUTE_NO'], roadTypeMap:{Fw:[1,11],Ew:[2,12],MH:[4,14],mH:[6,16],PS:[7,8,17,18]},
  488. maxRecordCount:1000, supportsPagination:false },
  489. { layerID:'Basemap/MapServer/21', idPropName:'OBJECTID', outFields:['OBJECTID','SHIELD'], maxRecordCount:1000, supportsPagination:false }
  490. ],
  491. getWhereClause: function(context) {
  492. if (context.layer.layerID === 'Basemap/MapServer/21') {
  493. return ("SHIELD IN ('C','CT')");
  494. } else {
  495. return null;
  496. }
  497. },
  498. getFeatureRoadType: function(feature, layer) {
  499. var roadType;
  500. if (layer.layerID === 'Basemap/MapServer/21') {
  501. roadType = 'PS';
  502. } else {
  503. roadType = _stateSettings.global.getFeatureRoadType(feature, layer);
  504. var routeNo = feature.attributes.ROUTE_NO;
  505. if (/^NY.*/.test(routeNo)) {
  506. if (roadType === 'PS') roadType = 'mH';
  507. } else if (/^US.*/.test(routeNo)) {
  508. if (roadType === 'PS' || roadType === 'mH') roadType = 'MH';
  509. }
  510. }
  511. return roadType;
  512. }
  513. },
  514. OH: {
  515. baseUrl: 'https://gis.dot.state.oh.us/arcgis/rest/services/TIMS/Roadway_Information/MapServer/',
  516. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  517. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  518.  
  519. fcMapLayers: [
  520. { layerID:8, fcPropName:'FUNCTION_CLASS', idPropName:'ObjectID', outFields:['FUNCTION_CLASS','ROUTE_TYPE','ROUTE_NBR','ObjectID'],
  521. maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} }
  522. ],
  523. isPermitted: function() { return true; },
  524. getWhereClause: function(context) {
  525. if(context.mapContext.zoom < 4) {
  526. var clause = '(' + context.layer.fcPropName + " < 7 OR ROUTE_TYPE IN ('CR','SR','US'))";
  527. return clause;
  528. } else {
  529. return null;
  530. }
  531. },
  532. getFeatureRoadType: function(feature, layer) {
  533. var fc = feature.attributes[layer.fcPropName];
  534. var prefix = feature.attributes.ROUTE_TYPE;
  535. var isUS = prefix === 'US';
  536. var isState = prefix === 'SR';
  537. var isCounty = prefix === 'CR';
  538. if (isUS && fc > 3) { fc = 3; }
  539. if (isState && fc > 4) { fc = 4; }
  540. if (isCounty && fc > 6) { fc = 6; }
  541. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  542. }
  543. },
  544. OK: {
  545. baseUrl: 'http://services6.arcgis.com/RBtoEUQ2lmN0K3GY/arcgis/rest/services/Roadways/FeatureServer/',
  546. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  547. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  548. fcMapLayers: [
  549. { layerID:0, fcPropName:'NFC', idPropName:'OBJECTID', outFields:['F_PRIMARY_','NFC','OBJECTID','ROUTE_CLAS'],
  550. maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} }
  551. ],
  552. getWhereClause: function(context) {
  553. if(context.mapContext.zoom < 4) {
  554. var clause = '(' + context.layer.fcPropName + " < 7 OR ROUTE_CLAS IN ('U','S','I'))";
  555. return clause;
  556. } else {
  557. return null;
  558. }
  559. },
  560. getFeatureRoadType: function(feature, layer) {
  561. var fc = feature.attributes[layer.fcPropName];
  562. var route = (feature.attributes.F_PRIMARY_ || '').trim();
  563. var isBusinessOrSpur = /BUS$|SPR$/i.test(route);
  564. var prefix = isBusinessOrSpur ? route.substring(0,1) : feature.attributes.ROUTE_CLAS;
  565. var isInterstate = prefix === 'I';
  566. var isUS = prefix === 'U';
  567. var isState = prefix === 'S';
  568. if (((isUS && !isBusinessOrSpur) || (isInterstate && isBusinessOrSpur)) && fc > 3) { fc = 3; }
  569. if (((isUS && isBusinessOrSpur) || (isState && !isBusinessOrSpur)) && fc > 4) { fc = 4; }
  570. if (isState && isBusinessOrSpur && fc > 5) { fc = 5; }
  571. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  572. }
  573. },
  574. PA: {
  575. baseUrl: 'https://www.pdarcgissvr.pa.gov/penndotgis/rest/services/PennShare/PennShare/MapServer/',
  576. supportsPagination: false,
  577. defaultColors: {Fw:'#00ffff',Ew:'#732500',MH:'#ff0000',mH:'#00ff00',PS:'#b724ff',PS2:'#f3f300',St:'#ff9700'},
  578. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  579. isPermitted: function() { return _r >= 3; },
  580. fcMapLayers: [
  581. { layerID:3, features:new Map(), fcPropName:'FUNC_CLS', idPropName:'MSLINK', outFields:['MSLINK','FUNC_CLS'],
  582. maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:['01','11'],Ew:['12'],MH:['02','14'],mH:['06','16'],PS:['07','08','17'],St:['09','19']} }
  583. ],
  584. getWhereClause: function(context) {
  585. return null;
  586. },
  587. getFeatureRoadType: function(feature, layer) {
  588. if (layer.getFeatureRoadType) {
  589. return layer.getFeatureRoadType(feature);
  590. } else {
  591. var fc = feature.attributes[layer.fcPropName];
  592. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  593. }
  594. }
  595. },
  596. TN: {
  597. // NOTE: DUE TO ERRORS FROM THE SHELBY COUNTY SERVER, FC IS NOT WORKING PROPERLY HERE YET (9/23/2016)
  598. baseUrl: 'https://testuasiportal.shelbycountytn.gov/arcgis/rest/services/MPO/Webmap_2015_04_20_TMPO/MapServer/',
  599.  
  600. // TODO: UPDATE COLORS TO MATCH ORIGINAL TN FC MAP COLORS.
  601. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',PS2:'#cfae0e',St:'#eeeeee'},
  602. zoomSettings: { maxOffset:[30,15,8,4,2,1,1,1,1,1] },
  603. fcMapLayers: [
  604. { layerID:17, fcPropName:'FuncClass', idPropName:'OBJECTID', outFields:['OBJECTID','FuncClass'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1,11],Ew:[2,12],MH:[4,14],mH:[6,16],PS:[7,17],PS2:[8,18],St:[9,19]} }
  605. ],
  606. getWhereClause: function(context) {
  607. if(context.mapContext.zoom < 4) {
  608. return context.layer.fcPropName + ' NOT IN (9,19)';
  609. } else {
  610. return null;
  611. }
  612. },
  613. getFeatureRoadType: function(feature, layer) {
  614. if (layer.getFeatureRoadType) {
  615. return layer.getFeatureRoadType(feature);
  616. } else {
  617. var fc = feature.attributes[layer.fcPropName];
  618. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  619. }
  620. }
  621. },
  622. TX: {
  623. baseUrl: 'https://services.arcgis.com/KTcxiTD9dsQw4r7Z/ArcGIS/rest/services/TxDOT_Functional_Classification/FeatureServer/',
  624. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  625. zoomSettings: { maxOffset:[30,15,8,4,2,1,1,1,1,1] },
  626. fcMapLayers: [
  627. { layerID:0, fcPropName:'F_SYSTEM', idPropName:'OBJECTID_1', outFields:['OBJECTID_1','F_SYSTEM', 'RIA_RTE_ID'], maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} }
  628. ],
  629. getWhereClause: function(context) {
  630. var where = " F_SYSTEM IS NOT NULL AND PROPOSED <> 'Y' AND RIA_RTE_ID IS NOT NULL";
  631. if(context.mapContext.zoom < 4) {
  632. where += ' AND ' + context.layer.fcPropName + " <> 7";
  633. }
  634. return where;
  635. },
  636. getFeatureRoadType: function(feature, layer) {
  637. // On-System:
  638. // IH=Interstate BF=Business FM
  639. // US=US Highway FM=Farm to Mkt
  640. // UA=US Alt. RM=Ranch to Mkt
  641. // UP=US Spur RR=Ranch Road
  642. // SH=State Highway PR=Park Road
  643. // SA=State Alt. RE=Rec Road
  644. // SL=State Loop RP=Rec Rd Spur
  645. // SS=State Spur FS=FM Spur
  646. // BI=Business IH RS=RM Spur
  647. // BU=Business US RU=RR Spur
  648. // BS=Business State PA=Principal Arterial
  649. // Off-System:
  650. // TL=Off-System Tollroad CR=County Road
  651. // FC=Func. Classified St. LS=Local Street
  652. if (layer.getFeatureRoadType) {
  653. return layer.getFeatureRoadType(feature);
  654. } else {
  655. var fc = feature.attributes[layer.fcPropName];
  656. var type = feature.attributes.RIA_RTE_ID.substring(0,2).toUpperCase();
  657. if (type === 'IH' && fc > 1) {
  658. fc = 1;
  659. } else if ((type === 'US' || type === 'BI' || type === 'UA') && fc > 3) {
  660. fc = 3;
  661. } else if ((type === 'UP' || type === 'BU' || type === 'SH' || type === 'SA') && fc > 4) {
  662. fc = 4;
  663. } else if ((type === 'SL' || type === 'SS' || type === 'BS') && fc > 6) {
  664. fc = 6;
  665. }
  666. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  667. }
  668. }
  669. },
  670. UT: {
  671. baseUrl: 'http://maps.udot.utah.gov/arcgis/rest/services/Functional_Class/MapServer/',
  672. defaultColors: {Fw:'#ff00c5',Ew:'#4f33df',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  673. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  674. fcMapLayers: [
  675. { layerID:0, fcPropName:'FC_CODE', idPropName:'OBJECTID', outFields:['*'/*'OBJECTID','FC_CODE'*/], roadTypeMap:{Fw:[1],Ew:[2,20],MH:[3,30],mH:[4,40],PS:[5,50,6,60],St:[7,77]},
  676. maxRecordCount:1000, supportsPagination:false }
  677. ],
  678. getWhereClause: function(context) {
  679. var clause = context.layer.fcPropName + '<=7';
  680. if(context.mapContext.zoom < 4) {
  681. clause += ' OR ' + context.layer.fcPropName + '<7';
  682. }
  683. return clause;
  684. },
  685. getFeatureRoadType: function(feature, layer) {
  686. var routeId = feature.attributes.ROUTE_ID;
  687. var fc = feature.attributes.FC_CODE;
  688. if ([6,40,50,89,91,163,189,191,491].indexOf(routeId) > -1 && fc > 3) {
  689. // US highway
  690. fc = 3;
  691. } else if (routeId <= 491 && fc > 4) {
  692. // State highway
  693. fc = 4;
  694. }
  695. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  696. }
  697. },
  698. VA: {
  699. baseUrl: 'http://services.arcgis.com/p5v98VHDX9Atv3l7/arcgis/rest/services/FC_2014_FHWA_Submittal1/FeatureServer/',
  700. defaultColors: {Fw:'#ff00c5',Ew:'#ff00c5',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  701. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  702. fcMapLayers: [
  703. { layerID:0, fcPropName:'FUNCTIONAL_CLASS_ID', idPropName:'OBJECTID', outFields:['OBJECTID','FUNCTIONAL_CLASS_ID','RTE_NM'], maxRecordCount:2000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} },
  704. { layerID:1, fcPropName:'STATE_FUNCT_CLASS_ID', idPropName:'OBJECTID', outFields:['OBJECTID','STATE_FUNCT_CLASS_ID','RTE_NM','ROUTE_NO'], maxRecordCount:2000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} },
  705. { layerID:3, fcPropName:'TMPD_FC', idPropName:'OBJECTID', outFields:['OBJECTID','TMPD_FC','RTE_NM'], maxRecordCount:2000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} }
  706. ],
  707. srExceptions: [217,302,303,305,308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,339,341,342,343,344,345,346,347,348,350,353,355,357,358,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,382,383,384,385,386,387,388,389,390,391,392,393,394,396,397,398,399,785,895],
  708. getWhereClause: function(context) {
  709. if(context.mapContext.zoom < 4) {
  710. return context.layer.fcPropName + '<>7';
  711. } else {
  712. //NOTE: As of 9/14/2016 there does not appear to be any US/SR/VA labeled routes with FC = 7.
  713. return null;
  714. }
  715. },
  716. getFeatureRoadType: function(feature, layer) {
  717. if (layer.getFeatureRoadType) {
  718. return layer.getFeatureRoadType(feature);
  719. } else {
  720. var fc = parseInt(feature.attributes[layer.fcPropName]);
  721. var rtName = feature.attributes.RTE_NM;
  722. var match = /^R-VA\s*(US|VA|SR)(\d{5})..(BUS)?/.exec(rtName);
  723. var isBusiness = (match && (match !== null) && (match[3] === 'BUS'));
  724. var isState = (match && (match !== null) && (match[1] === 'VA' || match[1] === 'SR'));
  725. var rtNum = parseInt((layer.layerID === 1) ? feature.attributes.ROUTE_NO : (match ? match[2] : 99999));
  726. var rtPrefix = match && match[1];
  727. if (fc > 3 && rtPrefix === 'US') {
  728. fc = isBusiness ? 4 : 3;
  729. } else if (isState && fc > 4 && this.srExceptions.indexOf(rtNum) === -1 && rtNum < 600) {
  730. fc = isBusiness ? 5 : 4;
  731. }
  732. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  733. }
  734. }
  735. },
  736. WV: {
  737. baseUrl: 'http://gis.transportation.wv.gov/arcgis/rest/services/Roads_And_Highways/Publication_LRS/MapServer/',
  738. defaultColors: {Fw:'#ff00c5',Ew:'#ff00c5',MH:'#149ece',mH:'#4ce600',PS:'#cfae0e',St:'#eeeeee'},
  739. zoomSettings: { maxOffset: [30,15,8,4,2,1,1,1,1,1], excludeRoadTypes: [['St'],['St'],['St'],['St'],[],[],[],[],[],[],[]] },
  740. fcMapLayers: [
  741. { layerID:35, fcPropName:'NAT_FUNCTIONAL_CLASS', idPropName:'OBJECTID', outFields:['OBJECTID','NAT_FUNCTIONAL_CLASS','ROUTE_ID'], maxRecordCount:1000, supportsPagination:true, roadTypeMap:{Fw:[1],Ew:[2],MH:[3],mH:[4],PS:[5,6],St:[7]} }
  742. ],
  743. getWhereClause: function(context) {
  744. if(context.mapContext.zoom < 4) {
  745. return context.layer.fcPropName + ' NOT IN(9,19)';
  746. } else {
  747. return null;
  748. }
  749. },
  750. getFeatureRoadType: function(feature, layer) {
  751. if (layer.getFeatureRoadType) {
  752. return layer.getFeatureRoadType(feature);
  753. } else {
  754. var fcCode = feature.attributes[layer.fcPropName];
  755. var fc = fcCode;
  756. if (fcCode===11) fc = 1;
  757. else if (fcCode===4 || fcCode===12) fc = 2;
  758. else if (fcCode===2 || fcCode===14) fc = 3;
  759. else if (fcCode===6 || fcCode===16) fc = 4;
  760. else if (fcCode===7 || fcCode===17 || fcCode===8 || fcCode===18) fc = 5;
  761. else fc = 7;
  762. var id = feature.attributes.ROUTE_ID;
  763. var prefix = id.substr(2,1);
  764. var isInterstate = false;
  765. var isUS = false;
  766. var isState = false;
  767. switch (prefix) {
  768. case '1':
  769. isInterstate = true;
  770. break;
  771. case '2':
  772. isUS = true;
  773. break;
  774. case '3':
  775. isState = true;
  776. break;
  777. }
  778. if (fc > 1 && isInterstate)
  779. fc = 1;
  780. else if (fc > 3 && isUS)
  781. fc = 3;
  782. else if (fc > 4 && isState)
  783. fc = 4;
  784. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  785. }
  786. }
  787. }
  788. };
  789.  
  790. function log(message, level) {
  791. if (message && (!level || (level <= _debugLevel))) {
  792. console.log('FC Layer: ', message);
  793. }
  794. }
  795.  
  796. function dynamicSort(property) {
  797. var sortOrder = 1;
  798. if(property[0] === "-") {
  799. sortOrder = -1;
  800. property = property.substr(1);
  801. }
  802. return function (a,b) {
  803. var props = property.split('.');
  804. props.forEach(function(prop) {
  805. a = a[prop];
  806. b = b[prop];
  807. });
  808. var result = (a < b) ? -1 : (a > b) ? 1 : 0;
  809. return result * sortOrder;
  810. };
  811. }
  812.  
  813. function dynamicSortMultiple() {
  814. /*
  815. * save the arguments object as it will be overwritten
  816. * note that arguments object is an array-like object
  817. * consisting of the names of the properties to sort by
  818. */
  819. var props = arguments;
  820. if (arguments[0] && Array.isArray(arguments[0])) {
  821. props = arguments[0];
  822. }
  823. return function (obj1, obj2) {
  824. var i = 0, result = 0, numberOfProperties = props.length;
  825. /* try getting a different result from 0 (equal)
  826. * as long as we have extra properties to compare
  827. */
  828. while(result === 0 && i < numberOfProperties) {
  829. result = dynamicSort(props[i])(obj1, obj2);
  830. i++;
  831. }
  832. return result;
  833. };
  834. }
  835.  
  836. function generateUUID() {
  837. var d = new Date().getTime();
  838. var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  839. var r = (d + Math.random()*16)%16 | 0;
  840. d = Math.floor(d/16);
  841. return (c==='x' ? r : (r&0x3|0x8)).toString(16);
  842. });
  843. return uuid;
  844. }
  845.  
  846. function loadSettingsFromStorage() {
  847. var loadedSettings = $.parseJSON(localStorage.getItem(_settingsStoreName));
  848. var defaultSettings = {
  849. lastVersion:null,
  850. layerVisible:true,
  851. activeStateAbbr:'ALL',
  852. hideStreet:false
  853. };
  854. _settings = loadedSettings ? loadedSettings : defaultSettings;
  855. for (var prop in defaultSettings) {
  856. if (!_settings.hasOwnProperty(prop)) {
  857. _settings[prop] = defaultSettings[prop];
  858. }
  859. }
  860. }
  861.  
  862. function saveSettingsToStorage() {
  863. if (localStorage) {
  864. _settings.lastVersion = _scriptVersion;
  865. _settings.layerVisible = _mapLayer.visibility;
  866. localStorage.setItem(_settingsStoreName, JSON.stringify(_settings));
  867. log('Settings saved', 1);
  868. }
  869. }
  870.  
  871. function getLineWidth() {
  872. return 12 * Math.pow(1.15, (W.map.getZoom()-1));
  873. }
  874.  
  875. function sortArray(array) {
  876. array.sort(function(a, b){if (a < b)return -1;if (a > b)return 1;else return 0;});
  877. }
  878.  
  879. function getVisibleStateAbbrs() {
  880. var visibleStates = [];
  881. W.model.states.additionalInfo.forEach(function(state) {
  882. var stateAbbr = _statesHash[state.name];
  883. var activeStateAbbr = _settings.activeStateAbbr;
  884. if(_stateSettings[stateAbbr] && _stateSettings.global.isPermitted(stateAbbr) && (!activeStateAbbr || activeStateAbbr === 'ALL' || activeStateAbbr === stateAbbr)) {
  885. visibleStates.push(stateAbbr);
  886. }
  887. });
  888. return visibleStates;
  889. }
  890.  
  891. function getAsync(url, context) {
  892. return new Promise(function(resolve, reject) {
  893. GM_xmlhttpRequest({
  894. context:context, method:"GET", url:url,
  895. onload:function(res) {
  896. if (res.status.toString() === '200') {
  897. resolve({responseText: res.responseText, context:context});
  898. } else {
  899. reject({responseText: res.responseText, context:context});
  900. }
  901. },
  902. onerror: function() {
  903. reject(Error("Network Error"));
  904. }
  905. });
  906. });
  907. }
  908. function wait(ms){
  909. var start = new Date().getTime();
  910. var end = start;
  911. while(end < start + ms) {
  912. end = new Date().getTime();
  913. }
  914. }
  915. function getUrl(context, queryType, queryParams) {
  916. var extent = context.mapContext.extent,
  917. zoom = context.mapContext.zoom,
  918. layer = context.layer,
  919. state = context.state;
  920.  
  921. var whereParts = [];
  922. var geometry = { xmin:extent.left, ymin:extent.bottom, xmax:extent.right, ymax:extent.top, spatialReference: {wkid: 102100, latestWkid: 3857} };
  923. var geometryStr = JSON.stringify(geometry);
  924. var stateWhereClause = state.getWhereClause(context);
  925. var url = state.baseUrl + layer.layerID + '/query?geometry=' + encodeURIComponent(geometryStr);
  926.  
  927. if (queryType === 'countOnly') {
  928. url += '&returnCountOnly=true';
  929. } else if (queryType === 'idsOnly') {
  930. url += '&returnIdsOnly=true';
  931. } else if (queryType === 'paged') {
  932. // TODO
  933. } else {
  934. url += '&returnGeometry=true&maxAllowableOffset=' + state.zoomSettings.maxOffset[zoom];
  935. url += '&outFields=' + encodeURIComponent(layer.outFields.join(','));
  936. if (queryType === 'idRange') {
  937. var idPropName = context.layer.idPropName;
  938. whereParts.push('(' + queryParams.idFieldName + '>=' + queryParams.range[0] + ' AND ' + queryParams.idFieldName + '<=' + queryParams.range[1] + ')');
  939. }
  940. }
  941. if (stateWhereClause) whereParts.push(stateWhereClause);
  942. if (whereParts.length > 0 ) url += '&where=' + encodeURIComponent(whereParts.join(' AND '));
  943. url += '&spatialRel=esriSpatialRelIntersects&geometryType=esriGeometryEnvelope&inSR=102100&outSR=3857&f=json';
  944. //wait(500); // I don't know why this was in the code. Leaving it commented here just in case it was a hack to solve some issue.
  945. return url;
  946. }
  947.  
  948. function convertFcToRoadTypeVectors(feature, state, stateAbbr, layer, zoom) {
  949. var roadType = state.getFeatureRoadType(feature, layer);
  950. log(feature,3);
  951. var zIndex = _stateSettings.global.roadTypes.indexOf(roadType) * 100;
  952. var vectors = [];
  953. var lineFeatures = [];
  954. var attr = {
  955. //fcFeatureUniqueId: stateAbbr + '-' + layer.layerID + '-' + feature.attributes[layer.idPropName],
  956. //fcFeatureId: feature.attributes[layer.idPropName],
  957. state: stateAbbr,
  958. layerID: layer.layerID,
  959. roadType: roadType,
  960. dotAttributes: $.extend({}, feature.attributes),
  961. color: state.defaultColors[roadType],
  962. strokeWidth: getLineWidth,
  963. zIndex: zIndex
  964. };
  965.  
  966. feature.geometry.paths.forEach(function(path){
  967. var pointList = [];
  968. var newPoint = null;
  969. var lastPoint = null;
  970. path.forEach(function(point){
  971. pointList.push(new OL.Geometry.Point(point[0],point[1]));
  972. });
  973. var vectorFeature = new OL.Feature.Vector(new OL.Geometry.LineString(pointList),attr);
  974. vectors.push(vectorFeature);
  975. });
  976.  
  977. return vectors;
  978. }
  979.  
  980. function fetchLayerFC(context) {
  981. var url = getUrl(context, 'idsOnly');
  982. log(url,2);
  983. if (!context.parentContext.cancel) {
  984. return getAsync(url, context).bind(context).then(function(res) {
  985. var ids = $.parseJSON(res.responseText);
  986. if(!ids.objectIds) ids.objectIds = [];
  987. sortArray(ids.objectIds);
  988. log(ids,2);
  989. return ids;
  990. }).then(function(res) {
  991. var context = this;
  992. var idRanges = [];
  993. if (res.objectIds) {
  994. var len = res.objectIds ? res.objectIds.length : 0;
  995. var currentIndex = 0;
  996. var offset = Math.min(this.layer.maxRecordCount,1000);
  997. while (currentIndex < len) {
  998. var nextIndex = currentIndex + offset;
  999. if (nextIndex >= len) nextIndex = len - 1;
  1000. idRanges.push({range:[res.objectIds[currentIndex], res.objectIds[nextIndex]], idFieldName:res.objectIdFieldName});
  1001. currentIndex = nextIndex + 1;
  1002. }
  1003. log(context.layer.layerID, 2);
  1004. log(idRanges,2);
  1005. }
  1006. return idRanges;
  1007. }).map(function(idRange) {
  1008. var context = this;
  1009. if(!context.parentContext.cancel) {
  1010. var url = getUrl(this, 'idRange', idRange);
  1011. log(url,2);
  1012. return getAsync(url, context).then(function(res) {
  1013. var context = res.context;
  1014. if(!context.parentContext.cancel) {
  1015. var features = $.parseJSON(res.responseText).features;
  1016. // if (context.parentContext.callCount === 0 ) {
  1017. // _mapLayer.removeAllFeatures();
  1018. // }
  1019. context.parentContext.callCount++;
  1020. log('Feature Count=' + (features ? features.length : 0),2);
  1021. features = features ? features : [];
  1022. var vectors = [];
  1023. features.forEach(function(feature) {
  1024. if(!res.context.parentContext.cancel) {
  1025. var vector = convertFcToRoadTypeVectors(feature, context.state, context.stateAbbr, context.layer, context.mapContext.zoom);
  1026. //var fcFeatureUniqueId = vector[0].attributes.fcFeatureUniqueId;
  1027. //context.parentContext.addedFcFeatureUniqueIds.push(fcFeatureUniqueId);
  1028. if (/*!context.parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId] &&*/ !(vector[0].attributes.roadType === 'St' && _settings.hideStreet)) {
  1029. vectors.push(vector);
  1030. }
  1031. }
  1032. });
  1033. return vectors;
  1034. }
  1035. });
  1036. } else {
  1037. log('Async call cancelled',1);
  1038. }
  1039. });
  1040. }
  1041. }
  1042.  
  1043. function fetchStateFC(context) {
  1044. var state = _stateSettings[context.stateAbbr];
  1045. var contexts = [];
  1046. state.fcMapLayers.forEach(function(layer) {
  1047. contexts.push({parentContext:context.parentContext, layer:layer, state:state, stateAbbr:context.stateAbbr, mapContext:context.mapContext});
  1048. });
  1049. return Promise.map(contexts, function(context) {
  1050. return fetchLayerFC(context);
  1051. });
  1052. }
  1053.  
  1054. var _lastPromise = null;
  1055. var _lastContext = null;
  1056. var _fcCallCount = 0;
  1057. function fetchAllFC() {
  1058. if (!_mapLayer.visibility) return;
  1059.  
  1060. if (_lastPromise) { _lastPromise.cancel(); }
  1061. $('#fc-loading-indicator').text('Loading FC...');
  1062.  
  1063. var mapContext = { zoom:W.map.getZoom(), extent:W.map.getExtent() };
  1064. var contexts = [];
  1065. var parentContext = {callCount:0,/*existingFcFeatureUniqueIds:{}, addedFcFeatureUniqueIds:[],*/ startTime:Date.now()};
  1066. // _mapLayer.features.forEach(function(vectorFeature) {
  1067. // var fcFeatureUniqueId = vectorFeature.attributes.fcFeatureUniqueId;
  1068. // var existingFcFeatureUniqueIdArray = parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId];
  1069. // if (!existingFcFeatureUniqueIdArray) {
  1070. // existingFcFeatureUniqueIdArray = [];
  1071. // parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId] = existingFcFeatureUniqueIdArray;
  1072. // }
  1073. // existingFcFeatureUniqueIdArray.push(vectorFeature);
  1074. // });
  1075. if (_lastContext) _lastContext.cancel = true;
  1076. _lastContext = parentContext;
  1077. getVisibleStateAbbrs().forEach(function(stateAbbr) {
  1078. contexts.push({ parentContext:parentContext, stateAbbr:stateAbbr, mapContext:mapContext});
  1079. });
  1080. var map = Promise.map(contexts, function(context) {
  1081. return fetchStateFC(context);
  1082. }).bind(parentContext).then(function(statesVectorArrays) {
  1083. if (!this.cancel) {
  1084. _mapLayer.removeAllFeatures();
  1085. statesVectorArrays.forEach(function(vectorsArray) {
  1086. vectorsArray.forEach(function(vectors) {
  1087. vectors.forEach(function(vector) {
  1088. vector.forEach(function(vectorFeature) {
  1089. _mapLayer.addFeatures(vectorFeature);
  1090. });
  1091. });
  1092. });
  1093. });
  1094. //buildTable();
  1095. // for(var fcFeatureUniqueId in this.existingFcFeatureUniqueIds) {
  1096. // if(this.addedFcFeatureUniqueIds.indexOf(fcFeatureUniqueId) === -1) {
  1097. // if (!this.cancel) _mapLayer.removeFeatures(this.existingFcFeatureUniqueIds[fcFeatureUniqueId]);
  1098. // }
  1099. // }
  1100. log('TOTAL RETRIEVAL TIME = ' + (Date.now() - parentContext.startTime),1);
  1101. log(statesVectorArrays,1);
  1102. }
  1103. return statesVectorArrays;
  1104. }).catch(function(e) {
  1105. $('#fc-loading-indicator').text('FC Error! (check console for details)');
  1106. log(e,0);
  1107. }).finally(function() {
  1108. _fcCallCount -= 1;
  1109. if (_fcCallCount === 0) {
  1110. $('#fc-loading-indicator').text('');
  1111. }
  1112. });
  1113.  
  1114. _fcCallCount += 1;
  1115. _lastPromise = map;
  1116. }
  1117.  
  1118. function onLayerCheckboxChanged(checked) {
  1119. _mapLayer.setVisibility(checked);
  1120. }
  1121.  
  1122. function onLayerVisibilityChanged(evt) {
  1123. _settings.layerVisible = _mapLayer.visibility;
  1124. saveSettingsToStorage();
  1125. if (_mapLayer.visibility) {
  1126. fetchAllFC();
  1127. }
  1128. }
  1129.  
  1130. function onModeChanged(model, modeId, context) {
  1131. if(!modeId || modeId === 1) {
  1132. initUserPanel();
  1133. }
  1134. }
  1135.  
  1136. function showScriptInfoAlert() {
  1137. /* Check version and alert on update */
  1138. if (_alertUpdate && _scriptVersion !== _settings.lastVersion) {
  1139. alert(_scriptVersionChanges);
  1140. }
  1141. }
  1142.  
  1143. function initLayer(){
  1144. var _drawingContext = {
  1145. getZIndex: function(feature) {
  1146. return feature.attributes.zIndex;
  1147. },
  1148. getStrokeWidth: function() { return getLineWidth(); }
  1149. };
  1150. var defaultStyle = new OL.Style({
  1151. strokeColor: '${color}', //'#00aaff',
  1152. strokeDashstyle: "solid",
  1153. strokeOpacity: 1.0,
  1154. strokeWidth: '${strokeWidth}',
  1155. graphicZIndex: '${zIndex}'
  1156. });
  1157.  
  1158. var selectStyle = new OL.Style({
  1159. //strokeOpacity: 1.0,
  1160. strokeColor: '#000000'
  1161. });
  1162.  
  1163. _mapLayer = new OL.Layer.Vector("FC Layer", {
  1164. uniqueName: "__FCLayer",
  1165. displayInLayerSwitcher: false,
  1166. rendererOptions: { zIndexing: true },
  1167. styleMap: new OL.StyleMap({
  1168. 'default': defaultStyle,
  1169. 'select': selectStyle
  1170. })
  1171. });
  1172.  
  1173. _mapLayer.setOpacity(0.5);
  1174.  
  1175. I18n.translations[I18n.locale].layers.name.__FCLayer = "FC Layer";
  1176.  
  1177. _mapLayer.displayInLayerSwitcher = true;
  1178. _mapLayer.events.register('visibilitychanged',null,onLayerVisibilityChanged);
  1179. _mapLayer.setVisibility(_settings.layerVisible);
  1180.  
  1181. W.map.addLayer(_mapLayer);
  1182. _mapLayer.setZIndex(_mapLayerZIndex);
  1183. WazeWrap.Interface.AddLayerCheckbox('Display', 'FC Layer', _settings.layerVisible, onLayerCheckboxChanged);
  1184. // Hack to fix layer zIndex. Some other code is changing it sometimes but I have not been able to figure out why.
  1185. // It may be that the FC layer is added to the map before some Waze code loads the base layers and forces other layers higher. (?)
  1186.  
  1187. var checkLayerZIndex = function() {
  1188. if (_mapLayer.getZIndex() != _mapLayerZIndex) {
  1189. log("ADJUSTED FC LAYER Z-INDEX " + _mapLayerZIndex + ', ' + _mapLayer.getZIndex(),1);
  1190. _mapLayer.setZIndex(_mapLayerZIndex);
  1191. }
  1192. };
  1193.  
  1194. setInterval(function(){checkLayerZIndex();}, 200);
  1195.  
  1196. W.map.events.register("moveend",W.map,function(e){
  1197. fetchAllFC();
  1198. return true;
  1199. },true);
  1200. }
  1201.  
  1202. function initUserPanel() {
  1203. var $tab = $('<li>').append($('<a>', {'data-toggle':'tab', href:'#sidepanel-fc-layer'}).text('FC'));
  1204. var $panel = $('<div>', {class:'tab-pane', id:'sidepanel-fc-layer'});
  1205. var $stateSelect = $('<select>', {id:'fcl-state-select',class:'form-control disabled',style:'disabled'}).append($('<option>', {value:'ALL'}).text('All'));
  1206. // $stateSelect.change(function(evt) {
  1207. // _settings.activeStateAbbr = evt.target.value;
  1208. // saveSettingsToStorage();
  1209. // _mapLayer.removeAllFeatures();
  1210. // fetchAllFC();
  1211. // });
  1212. for (var stateAbbr in _stateSettings) {
  1213. if (stateAbbr !== 'global') {
  1214. $stateSelect.append($('<option>', {value:stateAbbr}).text(reverseStatesHash(stateAbbr)));
  1215. }
  1216. }
  1217.  
  1218. var $hideStreet = $('<div>',{id: 'fcl-hide-street-container', class:'controls-container'})
  1219. .append($('<input>', {type:'checkbox',name:'fcl-hide-street',id:'fcl-hide-street'}).prop('checked', _settings.hideStreet).click(function() {
  1220. _settings.hideStreet = $(this).is(':checked');
  1221. saveSettingsToStorage();
  1222. _mapLayer.removeAllFeatures();
  1223. fetchAllFC();
  1224. }))
  1225. .append($('<label>', {for:'fcl-hide-street'}).text('Hide street highlights'));
  1226.  
  1227. $stateSelect.val(_settings.activeStateAbbr ? _settings.activeStateAbbr : 'ALL');
  1228.  
  1229. $panel.append(
  1230. $('<div>', {class:'form-group'}).append(
  1231. $('<label>', {class:'control-label'}).text('Select a state')
  1232. ).append(
  1233. $('<div>', {class:'controls', id:'fcl-state-select-container'}).append(
  1234. $('<div>').append($stateSelect)
  1235. )
  1236. ),
  1237. $hideStreet ,
  1238. $('<div>', {id:'fcl-table-container'})
  1239. );
  1240.  
  1241. $panel.append(
  1242. $('<div>',{style:'margin-top:10px;font-size:10px;color:#999999;'})
  1243. .append($('<div>').text('version ' + _scriptVersion))
  1244. .append(
  1245. $('<div>').append(
  1246. $('<a>',{href:'#' /*, target:'__blank'*/}).text('Discussion Forum (currently n/a)')
  1247. )
  1248. )
  1249. );
  1250.  
  1251. $('#user-tabs > .nav-tabs').append($tab);
  1252. $('#user-info > .flex-parent > .tab-content').append($panel);
  1253. $('#fcl-state-select').change(function () {
  1254. _settings.activeStateAbbr = this.value;
  1255. saveSettingsToStorage();
  1256. fetchAllFC();
  1257. });
  1258. }
  1259.  
  1260. function addLoadingIndicator() {
  1261. $('.loading-indicator').after($('<div class="loading-indicator" style="margin-right:10px" id="fc-loading-indicator">'));
  1262. }
  1263.  
  1264. function initGui() {
  1265. addLoadingIndicator();
  1266. initLayer();
  1267. initUserPanel();
  1268. showScriptInfoAlert();
  1269. }
  1270.  
  1271. function processText(text) {
  1272. return new Promise(function(resolve, reject) {
  1273. var newText = text.replace(/(e)/,'E');
  1274. resolve(newText);
  1275. });
  1276. }
  1277.  
  1278. function init() {
  1279. if (_debugLevel > 0 && Promise.config) {
  1280. Promise.config({
  1281. warnings: true,
  1282. longStackTraces: true,
  1283. cancellation: true,
  1284. monitoring: false
  1285. });
  1286. } else {
  1287. Promise.config({
  1288. warnings: false,
  1289. longStackTraces: false,
  1290. cancellation: true,
  1291. monitoring: false
  1292. });
  1293. }
  1294.  
  1295. var u = W.loginManager.user;
  1296. _uid = u.id;
  1297. _r = u.rank;
  1298. _isAM = u.isAreaManager;
  1299. loadSettingsFromStorage();
  1300. String.prototype.replaceAll = function(search, replacement) {
  1301. var target = this;
  1302. return target.replace(new RegExp(search, 'g'), replacement);
  1303. };
  1304. initGui();
  1305. W.app.modeController.model.bind('change:mode', onModeChanged);
  1306. W.prefs.on("change:isImperial", function() {initUserPanel();loadSettingsFromStorage();});
  1307. fetchAllFC();
  1308. log('Initialized.', 0);
  1309. }
  1310.  
  1311. function bootstrap() {
  1312. if (W && W.loginManager &&
  1313. W.loginManager.events &&
  1314. W.loginManager.events.register &&
  1315. W.model && W.model.states && W.model.states.additionalInfo &&
  1316. W.map && W.loginManager.isLoggedIn() &&
  1317. WazeWrap.Version) {
  1318. log('Initializing...', 0);
  1319.  
  1320. init();
  1321. } else {
  1322. log('Bootstrap failed. Trying again...', 0);
  1323. unsafeWindow.setTimeout(function () {
  1324. bootstrap();
  1325. }, 1000);
  1326. }
  1327. }
  1328.  
  1329. log('Bootstrap...', 0);
  1330. bootstrap();
  1331. })();