WME FC Layer

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

当前为 2019-02-13 提交的版本,查看 最新版本

  1. /* global W */
  2. /* global Promise */
  3. /* global OL */
  4. /* global I18n */
  5. /* global unsafeWindow */
  6. /* global GM_info */
  7. /* global WazeWrap */
  8.  
  9. // // ==UserScript==
  10. // @name WME FC Layer
  11. // @namespace https://greasyfork.org/users/45389
  12. // @version 2019.02.11.001
  13. // @description Adds a Functional Class layer for states that publish ArcGIS FC data.
  14. // @author MapOMatic
  15. // @include /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
  16. // @license GNU GPLv3
  17. // @contributionURL https://github.com/WazeDev/Thank-The-Authors
  18. // @require https://greasyfork.org/scripts/39002-bluebird/code/Bluebird.js?version=255146
  19. // @require https://greasyfork.org/scripts/24851-wazewrap/code/WazeWrap.js
  20. // @grant GM_xmlhttpRequest
  21. // @connect md.gov
  22. // @connect in.gov
  23. // @connect arcgis.com
  24. // @connect ncdot.gov
  25. // @connect state.mi.us
  26. // @connect dc.gov
  27. // @connect la.gov
  28. // @connect nd.gov
  29. // @connect pa.gov
  30. // @connect oh.us
  31. // @connect iowadot.gov
  32. // @connect ksdot.org
  33. // @connect ky.gov
  34. // @connect shelbycountytn.gov
  35. // @connect illinois.gov
  36. // @connect ny.gov
  37. // @connect utah.gov
  38. // @connect idaho.gov
  39. // @connect wv.gov
  40. // @connect ga.gov
  41. // @connect uga.edu
  42. // @connect nevadadot.com
  43. // @connect sd.gov
  44. // @connect mt.gov
  45. // @connect arkansas.gov
  46. // @connect azdot.gov
  47. // @connect coloradodot.info
  48. // @connect unh.edu
  49. // @connect vermont.gov
  50. // @connect ma.us
  51. // @connect maine.gov
  52. // ==/UserScript==
  53.  
  54. (function () {
  55. 'use strict';
  56.  
  57. var _settingsStoreName = 'wme_fc_layer';
  58. var _alertUpdate = false;
  59. var _debugLevel = 0;
  60. var _scriptVersion = GM_info.script.version;
  61. var _scriptVersionChanges = [
  62. GM_info.script.name,
  63. 'v' + _scriptVersion,
  64. '',
  65. 'What\'s New',
  66. '------------------------------',
  67. '' // Add important stuff here when _alertUpdate = true.
  68. ].join('\n');
  69. var _mapLayer = null;
  70. var _isAM = false;
  71. var _uid;
  72. var _settings = {};
  73. var _r;
  74. var _mapLayerZIndex = 334;
  75. var _betaIDs = [103400892];
  76. var _statesHash = {
  77. 'Alabama': 'AL', 'Alaska': 'AK', 'American Samoa': 'AS', 'Arizona': 'AZ', 'Arkansas': 'AR', 'California': 'CA', 'Colorado': 'CO', 'Connecticut': 'CT', 'Delaware': 'DE', 'District of Columbia': 'DC',
  78. 'Federated States Of Micronesia': 'FM', 'Florida': 'FL', 'Georgia': 'GA', 'Guam': 'GU', 'Hawaii': 'HI', 'Idaho': 'ID', 'Illinois': 'IL', 'Indiana': 'IN', 'Iowa': 'IA', 'Kansas': 'KS',
  79. 'Kentucky': 'KY', 'Louisiana': 'LA', 'Maine': 'ME', 'Marshall Islands': 'MH', 'Maryland': 'MD', 'Massachusetts': 'MA', 'Michigan': 'MI', 'Minnesota': 'MN', 'Mississippi': 'MS', 'Missouri': 'MO',
  80. 'Montana': 'MT', 'Nebraska': 'NE', 'Nevada': 'NV', 'New Hampshire': 'NH', 'New Jersey': 'NJ', 'New Mexico': 'NM', 'New York': 'NY', 'North Carolina': 'NC', 'North Dakota': 'ND',
  81. 'Northern Mariana Islands': 'MP', 'Ohio': 'OH', 'Oklahoma': 'OK', 'Oregon': 'OR', 'Palau': 'PW', 'Pennsylvania': 'PA', 'Puerto Rico': 'PR', 'Rhode Island': 'RI', 'South Carolina': 'SC',
  82. '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'
  83. };
  84.  
  85. function reverseStatesHash(stateAbbr) {
  86. for (var stateName in _statesHash) {
  87. if (_statesHash[stateName] === stateAbbr) return stateName;
  88. }
  89. }
  90. var _stateSettings = {
  91. global: {
  92. 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.
  93. getFeatureRoadType: function (feature, layer) {
  94. var fc = feature.attributes[layer.fcPropName];
  95. return this.getRoadTypeFromFC(fc, layer);
  96. },
  97. getRoadTypeFromFC: function (fc, layer) {
  98. for (var roadType in layer.roadTypeMap) {
  99. if (layer.roadTypeMap[roadType].indexOf(fc) !== -1) {
  100. return roadType;
  101. }
  102. }
  103. return null;
  104. },
  105. 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); } },
  106. getMapLayer: function (stateAbbr, layerID) {
  107. var returnValue;
  108. _stateSettings[stateAbbr].fcMapLayers.forEach(function (layer) {
  109. if (layer.layerID === layerID) {
  110. returnValue = layer;
  111. }
  112. });
  113. return returnValue;
  114. }
  115. },
  116. AZ: {
  117. baseUrl: 'https://gis.azdot.gov/gis/rest/services/AGOL/FunClass_NHS/MapServer/',
  118. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  119. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  120. fcMapLayers: [
  121. {
  122. layerID: 8, fcPropName: 'FunctionalClass', idPropName: 'OBJECTID',
  123. outFields: ['OBJECTID', 'FunctionalClass', 'RouteId'],
  124. roadTypeMap: { Fw: [1, 11], Ew: [2, 3, 12], MH: [4, 14], mH: [6, 16], PS: [7, 17, 8, 18], St: [] }, maxRecordCount: 1000, supportsPagination: false
  125. }
  126. ],
  127. getWhereClause: function (context) {
  128. return context.layer.fcPropName + '<>9 OR ' + context.layer.fcPropName + '<>19';
  129. },
  130. getFeatureRoadType: function (feature, layer) {
  131. var roadID = feature.attributes.RouteId.trim().replace(/ +/g, ' ');
  132. var roadNum = parseInt(roadID.substring(2,5));
  133. var fc = parseInt(feature.attributes[layer.fcPropName]);
  134. if (fc === 2) { fc = 4; }
  135. fc = fc % 10;
  136. var azIH = [8, 10, 11, 17, 19, 40];
  137. var isUS = RegExp(/^U\D\d{3}\b/).test(roadID);
  138. var isState = RegExp(/^S\D\d{3}\b/).test(roadID);
  139. var isBiz = RegExp(/^SB\d{3}\b/).test(roadID);
  140. if (fc > 4 && isState && azIH.includes(roadNum) && isBiz) {
  141. fc = 4;
  142. } else if (fc > 4 && isUS) {
  143. fc = isBiz ? 6 : 4;
  144. } else if (fc > 6 && isState) {
  145. fc = isBiz ? 7 : 6;
  146. }
  147. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  148. }
  149. },
  150. AR: {
  151. baseUrl: 'https://gis.arkansas.gov/arcgis/rest/services/FEATURESERVICES/Transportation/FeatureServer/',
  152. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  153. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  154. fcMapLayers: [
  155. {
  156. layerID: 8, fcPropName: 'FunctionalClass', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FunctionalClass','AH_Route','AH_Section'],
  157. roadTypeMap: { Fw: [1,2], Ew: [], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  158. }
  159. ],
  160. getWhereClause: function (context) {
  161. return null;
  162. },
  163. getFeatureRoadType: function (feature, layer) {
  164. var attr = feature.attributes;
  165. var fc = parseInt(attr[layer.fcPropName]);
  166. var roadID = parseInt(attr.AH_Route);
  167. var usHwys = [49,59,61,62,63,64,65,67,70,71,79,82,165,167,270,271,278,371,412,425];
  168. var isUS = usHwys.includes(roadID);
  169. var isState = roadID < 613;
  170. var isBiz = attr.AH_Section[attr.AH_Section.length - 1] === 'B';
  171. if (fc > 3 && isUS) {
  172. fc = isBiz ? 4 : 3;
  173. } else if (fc > 4 && isState) {
  174. fc = isBiz ? 5 : 4;
  175. }
  176. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  177. }
  178. },
  179. CO: {
  180. baseUrl: 'http://dtdapps.coloradodot.info/arcgis/rest/services/MapView/BaseLayers_MapView_ext/MapServer/',
  181. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  182. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  183. fcMapLayers: [
  184. { layerID: 7, fcPropName: 'FUNCCLASS', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCCLASS','ROUTE','REFPT'],
  185. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false },
  186. { layerID: 17, fcPropName: 'FUNCCLASSID', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCCLASSID','ROUTE','FIPSCOUNTY'],
  187. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false },
  188. { layerID: 18, fcPropName: 'FUNCCLASSID', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCCLASSID','ROUTE'],
  189. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false }
  190. ],
  191. isPermitted: function () { return _r >= 4; },
  192. getWhereClause: function (context) {
  193. if (context.mapContext.zoom < 4) {
  194. return context.layer.fcPropName + "<>'7'";
  195. } else {
  196. return null;
  197. }
  198. },
  199. getFeatureRoadType: function (feature, layer) {
  200. var attr = feature.attributes;
  201. var fc = parseInt(attr[layer.fcPropName]);
  202. var route = attr.ROUTE.replace(/ +/g, ' ');
  203. if (layer.layerID === 7) {
  204. var rtnum = parseInt(route.slice(0,3));
  205. var refpt = attr.REFPT;
  206. var hwys = [6, 24, 25, 34, 36, 40, 50, 70, 84, 85, 87, 138, 160, 285, 287, 350, 385, 400, 491, 550];
  207. var IH = [25,70];
  208. // Exceptions first, then normal classification
  209. var doNothing = ['024D', '040G'];
  210. var notNothing = ['070K', '070L', '070O', '070Q', '070R'];
  211. var doMin = ['024E', '050D', '070O', '085F', '160D'];
  212. if (doNothing.includes(route) || (rtnum === 70 && route !== '070K' && !notNothing.includes(route))) { }
  213. else if (doMin.includes(route) ||
  214. (rtnum === 40 && refpt > 320 && refpt < 385) ||
  215. (rtnum === 36 && refpt > 79 && refpt < 100.99) ||
  216. (route === '034D' && refpt > 11)) {
  217. fc = 4;
  218. } else if (hwys.includes(rtnum)) {
  219. fc = Math.min(fc, 3);
  220. }
  221. else {
  222. fc = Math.min(fc, 4);
  223. }
  224. }
  225. else {
  226. // All exceptions
  227. var fips = parseInt(attr.FIPSCOUNTY);
  228. if ((fips === 19 && route === 'COLORADO BD') ||
  229. (fips === 37 && (route === 'GRAND AV' || route === 'S H6'))) { fc = 3; }
  230. else if (fips === 67 && route === 'BAYFIELDPAY') { fc = 4; }
  231. }
  232. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  233. }
  234. },
  235. DC: {
  236. baseUrl: 'https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Transportation_WebMercator/MapServer/',
  237. supportsPagination: false,
  238. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  239. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  240. fetchAllFC: false,
  241. fcMapLayers: [
  242. {
  243. layerID: 48, fcPropName: 'FUNCTIONALCLASS', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCTIONALCLASS'], maxRecordCount: 1000, supportsPagination: false,
  244. roadTypeMap: { Fw: ['Interstate'], Ew: ['Other Freeway and Expressway'], MH: ['Principal Arterial'], mH: ['Minor Arterial'], PS: ['Collector'] }
  245. }
  246. ],
  247. getFeatureRoadType: function (feature, layer) {
  248. if (layer.getFeatureRoadType) {
  249. return layer.getFeatureRoadType(feature);
  250. } else {
  251. return _stateSettings.global.getFeatureRoadType(feature, layer);
  252. }
  253. },
  254. getWhereClause: function (context) {
  255. return null;
  256. }
  257. },
  258. FL: {
  259. baseUrl: 'https://services1.arcgis.com/O1JpcwDW8sjYuddV/ArcGIS/rest/services/Functional_Classification_TDA/FeatureServer/',
  260. supportsPagination: false,
  261. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  262. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  263. fetchAllFC: false,
  264. fcMapLayers: [
  265. {
  266. layerID: 0, fcPropName: 'FUNCLASS', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCLASS'], maxRecordCount: 1000, supportsPagination: false,
  267. roadTypeMap: { Fw: ['01', '11'], Ew: ['02', '12'], MH: ['04', '14'], mH: ['06', '16'], PS: ['07', '08', '17', '18'] }
  268. }
  269. ],
  270. getFeatureRoadType: function (feature, layer) {
  271. if (layer.getFeatureRoadType) {
  272. return layer.getFeatureRoadType(feature);
  273. } else {
  274. return _stateSettings.global.getFeatureRoadType(feature, layer);
  275. }
  276. },
  277. getWhereClause: function (context) {
  278. return null;
  279. }
  280. },
  281. GA: {
  282. baseUrl: 'https://maps.itos.uga.edu/arcgis/rest/services/GDOT/GDOT_FunctionalClass/mapserver/',
  283. supportsPagination: true,
  284. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  285. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  286. fetchAllFC: false,
  287. fcMapLayers: [
  288. { 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] } },
  289. { 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] } },
  290. { 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] } },
  291. { 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] } },
  292. { 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] } },
  293. { 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] } },
  294. { 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] } }
  295. ],
  296. getFeatureRoadType: function (feature, layer) {
  297. if (layer.getFeatureRoadType) {
  298. return layer.getFeatureRoadType(feature);
  299. } else {
  300. var attr = feature.attributes;
  301. var fc = attr.FUNCTIONAL_CLASS;
  302. if (attr.SYSTEM_CODE === '1' && fc > 4) {
  303. return _stateSettings.global.getRoadTypeFromFC(4, layer);
  304. } else {
  305. return _stateSettings.global.getFeatureRoadType(feature, layer);
  306. }
  307. }
  308. },
  309. getWhereClause: function (context) {
  310. return null;
  311. }
  312. },
  313. ID: {
  314. baseUrl: 'https://gis.itd.idaho.gov/arcgisprod/rest/services/IPLAN/Functional_Classification/MapServer/',
  315. supportsPagination: false,
  316. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  317. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  318. fetchAllFC: true,
  319. fcMapLayers: [
  320. { 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] } },
  321. { 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] } },
  322. { 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] } },
  323. { 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] } },
  324. { 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] } },
  325. { 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] } }
  326. ],
  327. getFeatureRoadType: function (feature, layer) {
  328. if (layer.getFeatureRoadType) {
  329. return layer.getFeatureRoadType(feature);
  330. } else {
  331. return _stateSettings.global.getFeatureRoadType(feature, layer);
  332. }
  333. },
  334. getWhereClause: function (context) {
  335. return null;
  336. }
  337. },
  338. IL: {
  339. baseUrl: 'http://ags10s1.dot.illinois.gov/ArcGIS/rest/services/IRoads/IRoads_53/MapServer/',
  340. supportsPagination: false,
  341. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee', CH: '#ff5e0e' },
  342. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  343. fcMapLayers: [
  344. {
  345. layerID: 3, idPropName: 'OBJECTID', fcPropName: 'FC', outFields: ['FC', 'MRK_RT_TYP', 'CH', 'OBJECTID'],
  346. roadTypeMap: { Fw: ['1'], Ew: ['2'], MH: ['3'], mH: ['4'], PS: ['5', '6'], St: ['7'] }, maxRecordCount: 1000, supportsPagination: false
  347. }
  348. ],
  349. isPermitted: function () { return _r >= 3; },
  350. getWhereClause: function (context) {
  351. return context.mapContext.zoom < 4 ? "FC<>7" : null;
  352. },
  353. getFeatureRoadType: function (feature, layer) {
  354. var attr = feature.attributes;
  355. var fc = attr.FC;
  356. var type = attr.MRK_RT_TYP;
  357. if (fc > 3 && type === 'U') { // US Route
  358. fc = 3;
  359. } else if (fc > 4 && type === 'S') { // State Route
  360. fc = 4;
  361. } else if (fc > 6 && attr.CH !== '0000') { // County Route
  362. fc = 6;
  363. }
  364. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  365. }
  366. },
  367. IN: {
  368. baseUrl: 'https://gis.in.gov/arcgis/rest/services/DOT/INDOT_LTAP/FeatureServer/',
  369. supportsPagination: false,
  370. overrideUrl: '1Sbwc7e6BfHpZWSTfU3_1otXGSxHrdDYcbn7fOf1VjpA',
  371. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  372. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []], hideRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  373. fcMapLayers: [
  374. {
  375. layerID: 10, idPropName: 'OBJECTID', fcPropName: 'FUNCTIONAL_CLASS', outFields: ['FUNCTIONAL_CLASS', 'OBJECTID', 'TO_DATE'],
  376. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 100000, supportsPagination: false
  377. }
  378. ],
  379. isPermitted: function () { return true; },
  380. getWhereClauses: function (context) {
  381. },
  382. getWhereClause: function (context) {
  383. var whereParts = [];
  384. if (context.mapContext.zoom < 4) {
  385. whereParts.push(context.layer.fcPropName + '<>7');
  386. }
  387. whereParts.push('TO_DATE IS NULL');
  388. return whereParts.join(' AND ');
  389. },
  390. getFeatureRoadType: function (feature, layer) {
  391. if (layer.getFeatureRoadType) {
  392. return layer.getFeatureRoadType(feature);
  393. } else {
  394. return _stateSettings.global.getFeatureRoadType(feature, layer);
  395. }
  396. }
  397. },
  398. IA: {
  399. baseUrl: 'https://gis.iowadot.gov/public/rest/services/RAMS/Road_Network/MapServer/',
  400. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee', PSGr: '#cc6533', StGr: '#e99cb6' },
  401. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  402. fcMapLayers: [
  403. {
  404. layerID: 0, fcPropName: 'FED_FUNCTIONAL_CLASS', idPropName: 'OBJECTID',
  405. outFields: ['OBJECTID', 'FED_FUNCTIONAL_CLASS', 'STATE_ROUTE_NAME_1', 'ACCESS_CONTROL', 'SURFACE_TYPE'],
  406. roadTypeMap: { Fw: [1], MH: [2, 3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  407. }
  408. ],
  409. getWhereClause: function (context) {
  410. var theWhereClause = "FACILITY_TYPE<>'7'"; // Removed proposed roads
  411. if (context.mapContext.zoom < 4) {
  412. theWhereClause += " AND " + context.layer.fcPropName + "<>'7'";
  413. }
  414. return theWhereClause;
  415. },
  416. getFeatureRoadType: function (feature, layer) {
  417. var attr = feature.attributes;
  418. var fc = parseInt(attr[layer.fcPropName]);
  419. var isFw = attr.ACCESS_CONTROL === 1;
  420. var isUS = RegExp('^STATE OF IOWA, US').test(attr.STATE_ROUTE_NAME_1);
  421. var isState = RegExp('^STATE OF IOWA, IA').test(attr.STATE_ROUTE_NAME_1);
  422. if (isFw) {
  423. fc = 1;
  424. } else if (fc > 3 && isUS) {
  425. fc = 3;
  426. } else if (fc > 4 && isState) {
  427. fc = 4;
  428. }
  429. if (fc > 4 && attr.SURFACE_TYPE === 20) {
  430. return fc < 7 ? 'PSGr' : 'StGr';
  431. } else {
  432. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  433. }
  434. }
  435. },
  436. KS: {
  437. baseUrl: 'http://wfs.ksdot.org/arcgis_web_adaptor/rest/services/Transportation/',
  438. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee'},
  439. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  440. fcMapLayers: [
  441. {
  442. layerID: 0, layerPath: 'Non_State_System/MapServer/', idPropName: 'ID2', fcPropName: 'FUNCLASS', outFields: ['FUNCLASS', 'ID2', 'ROUTE_ID'],
  443. roadTypeMap: { Fw: [1], MH: [2, 3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  444. },
  445. {
  446. layerID: 1, layerPath: 'National_Highway_System/MapServer/', idPropName: 'OBJECTID', fcPropName: 'FUN_CLASS_CD', outFields: ['FUN_CLASS_CD', 'OBJECTID', 'PREFIX', 'ACCESS_CONTROL'],
  447. roadTypeMap: { Fw: [1], MH: [2, 3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  448. },
  449. {
  450. layerID: 0, layerPath: 'State_System/MapServer/', idPropName: 'OBJECTID', fcPropName: 'FUN_CLASS_CD', outFields: ['FUN_CLASS_CD', 'OBJECTID', 'PREFIX', 'ACCESS_CONTROL'],
  451. roadTypeMap: { Fw: [1], MH: [2, 3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  452. }
  453. ],
  454. getWhereClause: function (context) {
  455. if (context.mapContext.zoom < 4) {
  456. return context.layer.fcPropName + "<>'7'";
  457. } else {
  458. return null;
  459. }
  460. },
  461. getFeatureRoadType: function (feature, layer) {
  462. var attr = feature.attributes;
  463. var fc = parseInt(attr[layer.fcPropName]);
  464. var roadPrefix = attr.PREFIX;
  465. var isFw = parseInt(attr.ACCESS_CONTROL) === 1;
  466. var isUS = roadPrefix === 'U';
  467. var isState = roadPrefix === 'K';
  468. if (isFw) {
  469. fc = 1;
  470. } else if (fc > 3 && isUS) {
  471. fc = 3;
  472. } else if (fc > 4 && isState) {
  473. fc = 4;
  474. }
  475. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  476. },
  477. },
  478. KY: {
  479. baseUrl: 'https://maps.kytc.ky.gov/arcgis/rest/services/BaseMap/System/MapServer/',
  480. supportsPagination: false,
  481. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  482. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  483. fcMapLayers: [
  484. {
  485. layerID: 0, idPropName: 'OBJECTID', fcPropName: 'FC', outFields: ['FC', 'OBJECTID', 'RT_PREFIX', 'RT_SUFFIX'],
  486. roadTypeMap: { Fw: ['1'], Ew: ['2'], MH: ['3'], mH: ['4'], PS: ['5', '6'], St: ['7'] }, maxRecordCount: 1000, supportsPagination: false
  487. }
  488. ],
  489. isPermitted: function () { return true; },
  490. getWhereClause: function (context) {
  491. if (context.mapContext.zoom < 4) {
  492. return context.layer.fcPropName + "<>'7'";
  493. } else {
  494. return null;
  495. }
  496. },
  497. getFeatureRoadType: function (feature, layer) {
  498. if (feature.attributes.RT_PREFIX === 'US') {
  499. var suffix = feature.attributes.RT_SUFFIX;
  500. var type = 'MH';
  501. if (suffix && suffix.indexOf('X') > -1) type = 'mH';
  502. return type;
  503. } else {
  504. return _stateSettings.global.getFeatureRoadType(feature, layer);
  505. }
  506. }
  507. },
  508. LA: {
  509. baseUrl: 'https://giswebnew.dotd.la.gov/arcgis/rest/services/Transportation/LA_RoadwayFunctionalClassification/FeatureServer/',
  510. supportsPagination: false,
  511. defaultColors: { Fw: '#4094ff', Ew: '#ffbf40', MH: '#fb674d', mH: '#6abe40', PS1: '#bf40ec', PS2: '#ffff40', St: '#a2a2a2' },
  512. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  513. fcMapLayers: [
  514. { 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 },
  515. { 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 },
  516. { 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 },
  517. { 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 },
  518. { 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 },
  519. { 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 },
  520. { 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 }
  521. ],
  522. getWhereClause: function (context) {
  523. if (context.mapContext.zoom < 4) {
  524. return context.layer.fcPropName + "<>'7'"; // OR State_Route LIKE 'US%' OR State_Route LIKE 'LA%'";
  525. } else {
  526. return null;
  527. }
  528. },
  529. getFeatureRoadType: function (feature, layer) {
  530. var fc = feature.attributes[layer.fcPropName];
  531. if (fc === '2a' || fc === '2b') { fc = 2; }
  532. fc = parseInt(fc);
  533. // var stateRoute = feature.attributes.State_Route;
  534. // var isBusiness = /BUS$/.test(stateRoute);
  535. // if (fc > 3 && /^US\s/.test(stateRoute) && !isBusiness) {
  536. // fc = 3;
  537. // } else if (fc > 4 && /^LA\s/.test(stateRoute) && !isBusiness) {
  538. // fc = 4;
  539. // }
  540. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  541. }
  542. },
  543. ME: {
  544. baseUrl: 'https://arcgisserver.maine.gov/arcgis/rest/services/mdot/MaineDOT_Dynamic/MapServer/',
  545. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  546. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  547. fcMapLayers: [
  548. {
  549. layerID: 811, fcPropName: 'fedfunccls', idPropName: 'objectid', outFields: ['objectid', 'fedfunccls', 'prirtename'],
  550. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  551. }
  552. ],
  553. getWhereClause: function (context) {
  554. if (context.mapContext.zoom < 4) {
  555. return context.layer.fcPropName + "<>'Local'";
  556. } else {
  557. return null;
  558. }
  559. },
  560. getFeatureRoadType: function (feature, layer) {
  561. var attr = feature.attributes;
  562. var fc = attr[layer.fcPropName];
  563. switch (fc) {
  564. case 'Princ art interstate': fc = 1; break;
  565. case 'Princ art other f&e': fc = 2; break;
  566. case 'Other princ arterial': fc = 3; break;
  567. case 'Minor arterial': fc = 4; break;
  568. case 'Major/urb collector':
  569. case 'Minor collector': fc = 5; break;
  570. default: fc = 7;
  571. }
  572. var route = attr.prirtename;
  573. var isUS = RegExp(/^US \d/).test(route);
  574. var isState = RegExp(/^ST RTE \d/).test(route);
  575. var isBiz = (isUS && RegExp(/(1B|1BS)$/).test(route)) || (isState && RegExp(/(15B|24B|25B|137B)$/).test(route));
  576. if (fc > 3 && isUS) {
  577. fc = isBiz ? 4 : 3;
  578. } else if (fc > 4 && isState) {
  579. fc = isBiz ? 5 : 4;
  580. }
  581. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  582. }
  583. },
  584. MD: {
  585. baseUrl: 'https://geodata.md.gov/imap/rest/services/Transportation/MD_HighwayPerformanceMonitoringSystem/MapServer/',
  586. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#ffff00', St: '#eeeeee' },
  587. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  588. fcMapLayers: [
  589. { layerID: 2, fcPropName: 'FUNCTIONAL_CLASS', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCTIONAL_CLASS', 'ID_PREFIX', 'MP_SUFFIX'], roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false }
  590. ],
  591. getWhereClause: function (context) {
  592. if (context.mapContext.zoom < 4) {
  593. return "(FUNCTIONAL_CLASS < 7 OR ID_PREFIX IN('MD'))";
  594. } else {
  595. return null;
  596. }
  597. },
  598. getFeatureRoadType: function (feature, layer) {
  599. var attr = feature.attributes;
  600. var fc = parseInt(attr.FUNCTIONAL_CLASS);
  601. var isState = attr.ID_PREFIX === 'MD';
  602. var isUS = attr.ID_PREFIX === 'US';
  603. var isBusiness = attr.MP_SUFFIX === 'BU';
  604. if (fc > 3 && isUS) {
  605. fc = isBusiness ? 4 : 3;
  606. } else if (fc > 4 && isState) {
  607. fc = isBusiness ? 5 : 4;
  608. }
  609. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  610. }
  611. },
  612. MA: {
  613. baseUrl: 'https://gis.massdot.state.ma.us/arcgis/rest/services/Roads/RoadInventory/MapServer/',
  614. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  615. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  616. fcMapLayers: [
  617. {
  618. layerID: 0, fcPropName: 'F_F_Class', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'F_F_Class', 'Route_ID'],
  619. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  620. }
  621. ],
  622. getWhereClause: function (context) {
  623. if (context.mapContext.zoom < 4) {
  624. return context.layer.fcPropName + "<>'7'";
  625. } else {
  626. return null;
  627. }
  628. },
  629. getFeatureRoadType: function (feature, layer) {
  630. var attr = feature.attributes;
  631. var fc = parseInt(attr[layer.fcPropName]);
  632. var route = attr.Route_ID;
  633. var isUS = /^US\d/.test(route);
  634. var isState = /^SR\d/.test(route);
  635. if (fc > 3 && isUS) {
  636. fc = 3;
  637. } else if (fc > 4 && isState) {
  638. fc = 4;
  639. }
  640. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  641. }
  642. },
  643. MI: {
  644. baseUrl: 'https://gisp.mcgi.state.mi.us/arcgis/rest/services/MDOT/NFC/MapServer/',
  645. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  646. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  647. fcMapLayers: [
  648. { 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 }
  649. ],
  650. isPermitted: function () { return true; },
  651. getWhereClause: function (context) {
  652. if (context.mapContext.zoom < 4) {
  653. return context.layer.fcPropName + '<>7';
  654. } else {
  655. return null;
  656. }
  657. },
  658. getFeatureRoadType: function (feature, layer) {
  659. if (layer.getFeatureRoadType) {
  660. return layer.getFeatureRoadType(feature);
  661. } else {
  662. return _stateSettings.global.getFeatureRoadType(feature, layer);
  663. }
  664. }
  665. },
  666. MT: {
  667. baseUrl: 'https://app.mdt.mt.gov/arcgis/rest/services/Standard/ROUTES/MapServer/',
  668. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  669. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  670. fcMapLayers: [
  671. { layerID: 0, fcPropName: 'FC' , idPropName: 'OBJECTID', outFields:['OBJECTID', 'FC', 'SIGN_ROUTE', 'ROUTE_NAME'], roadTypeMap:{Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5,6], St: [7]}, maxRecordCount:1000, supportsPagination:false },
  672. { layerID: 1, fcPropName: 'FC' , idPropName: 'OBJECTID', outFields:['OBJECTID', 'FC', 'SIGN_ROUTE', 'ROUTE_NAME'], roadTypeMap:{Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5,6], St: [7]}, maxRecordCount:1000, supportsPagination:false }
  673. ],
  674. isPermitted: function () { return _r >= 3; },
  675. getWhereClause: function (context) {
  676. if (context.mapContext.zoom < 4) {
  677. return context.layer.fcPropName + "<>'LOCAL'";
  678. } else {
  679. return null;
  680. }
  681. },
  682. getFeatureRoadType: function (feature, layer) {
  683. var fc = feature.attributes.FC;
  684. switch (fc) {
  685. case 'PRINCIPAL ARTERIAL - INTERSTATE': fc = 1; break;
  686. case 'PRINCIPAL ARTERIAL - NON-INTERSTATE': fc = 3; break;
  687. case 'MINOR ARTERIAL': fc = 4; break;
  688. case 'MAJOR COLLECTOR':
  689. case 'MINOR COLLECTOR': fc = 5; break;
  690. default: fc = 7;
  691. }
  692. var roadID = feature.attributes.SIGN_ROUTE;
  693. if (!roadID) { roadID = feature.attributes.ROUTE_NAME; }
  694. var isUS = RegExp(/^US \d+/).test(roadID);
  695. var isState = RegExp(/^MONTANA \d+|ROUTE \d+|S \d{3}\b/).test(roadID);
  696. if (fc > 3 && isUS) { fc = 3; }
  697. else if (fc > 4 && isState) { fc = 4; }
  698. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  699. }
  700. },
  701. NH: {
  702. baseUrl: 'https://nhgeodata.unh.edu/nhgeodata/rest/services/GRANITView/GV_BaseLayers/MapServer/',
  703. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  704. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  705. fcMapLayers: [
  706. {
  707. layerID: 18, fcPropName: 'FUNCT_SYSTEM', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCT_SYSTEM', 'STREET_ALIASES', 'TIER'],
  708. roadTypeMap: { Fw: [1], Ew: [2], MH: [2,3], mH: [4], PS: [5, 6], St: [7,0] }, maxRecordCount: 1000, supportsPagination: false
  709. }
  710. ],
  711. isPermitted: function () { return _r >= 3; },
  712. getWhereClause: function (context) {
  713. if (context.mapContext.zoom < 4) {
  714. return context.layer.fcPropName + "<>7 AND " + context.layer.fcPropName + "<>0";
  715. } else {
  716. return null;
  717. }
  718. },
  719. getFeatureRoadType: function (feature, layer) {
  720. var fc = parseInt(feature.attributes[layer.fcPropName]);
  721. if (!(fc > 0)) { fc = 7; }
  722. var route = feature.attributes.STREET_ALIASES;
  723. var isUS = RegExp(/US /).test(route);
  724. var isState = RegExp(/NH /).test(route);
  725. if (fc === 2) { feature.attributes.TIER === 1 ? fc = 1 : fc = 3; }
  726. else if (fc > 3 && isUS) { RegExp(/US 3B/).test(route) ? fc = 4 : fc = 3; }
  727. else if (fc > 4 && isState) { fc = 4; }
  728. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  729. }
  730. },
  731. NM: {
  732. baseUrl: 'https://services.arcgis.com/hOpd7wfnKm16p9D9/ArcGIS/rest/services/NMDOT_Functional_Class/FeatureServer/',
  733. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  734. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  735. fcMapLayers: [
  736. {
  737. layerID: 0, fcPropName: 'Func_Class', idPropName: 'OBJECTID_1', maxRecordCount: 1000, supportsPagination: false,
  738. outFields: ['OBJECTID_1', 'Func_Class', 'D_RT_ROUTE'], roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }
  739. }
  740. ],
  741. getWhereClause: function (context) {
  742. return null;
  743. },
  744. getFeatureRoadType: function (feature, layer) {
  745. var fc = parseInt(feature.attributes[layer.fcPropName]);
  746. var roadType = feature.attributes.D_RT_ROUTE.split('-',1).shift();
  747. var isBiz = roadType === 'BL'; // Interstate Business Loop
  748. var isUS = roadType === 'US';
  749. var isState = roadType === 'NM';
  750. if (roadType === 'IX') { fc = 0; }
  751. else if (fc > 3 && (isBiz || isUS)) { fc = 3; }
  752. else if (fc > 4 && isState) { fc = 4; }
  753. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  754. }
  755. },
  756. NY: {//https://gis3.dot.ny.gov/arcgis/rest/services/Basemap/MapServer/21
  757. baseUrl: 'https://gis3.dot.ny.gov/arcgis/rest/services/',
  758. defaultColors: { Fw: '#ff00c5', Ew: '#5f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  759. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  760. fcMapLayers: [
  761. {
  762. 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] },
  763. maxRecordCount: 1000, supportsPagination: false
  764. },
  765. { layerID: 'Basemap/MapServer/21', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'SHIELD'], maxRecordCount: 1000, supportsPagination: false }
  766. ],
  767. getWhereClause: function (context) {
  768. if (context.layer.layerID === 'Basemap/MapServer/21') {
  769. return ("SHIELD IN ('C','CT')");
  770. } else {
  771. return null;
  772. }
  773. },
  774. getFeatureRoadType: function (feature, layer) {
  775. var roadType;
  776. if (layer.layerID === 'Basemap/MapServer/21') {
  777. roadType = 'PS';
  778. } else {
  779. roadType = _stateSettings.global.getFeatureRoadType(feature, layer);
  780. var routeNo = feature.attributes.ROUTE_NO;
  781. if (/^NY.*/.test(routeNo)) {
  782. if (roadType === 'PS') roadType = 'mH';
  783. } else if (/^US.*/.test(routeNo)) {
  784. if (roadType === 'PS' || roadType === 'mH') roadType = 'MH';
  785. }
  786. }
  787. return roadType;
  788. }
  789. },
  790. NC: {
  791. baseUrl: 'https://gis11.services.ncdot.gov/arcgis/rest/services/NCDOT_FunctionalClass/MapServer/',
  792. defaultColors: { Fw: '#ff00c5', Rmp: '#999999', Ew: '#5f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  793. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  794. fcMapLayers: [
  795. { 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 }
  796. //{ 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 },
  797. //{ 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 },
  798. //{ 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 },
  799. //{ 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 },
  800. //{ 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 }
  801. ],
  802. isPermitted: function () { return _r > 1; },
  803. getWhereClause: function (context) {
  804. if (context.mapContext.zoom < 4) {
  805. var clause = '(' + context.layer.fcPropName + " < 7 OR RTE_1_CLSS_CD IN ('I','FED','NC','RMP','US'))";
  806. return clause;
  807. } else {
  808. return null;
  809. }
  810. },
  811. getFeatureRoadType: function (feature, layer) {
  812. var fc = feature.attributes[layer.fcPropName];
  813. var roadType;
  814. switch (this.getHwySys(feature)) {
  815. case 'interstate':
  816. roadType = 'Fw';
  817. break;
  818. case 'us':
  819. roadType = fc <= 2 ? 'Ew' : 'MH';
  820. break;
  821. case 'state':
  822. roadType = fc === 2 ? 'Ew' : (fc === 3 ? 'MH' : 'mH');
  823. break;
  824. case 'ramp':
  825. roadType = 'Rmp';
  826. break;
  827. default:
  828. roadType = fc === 2 ? 'Ew' : (fc === 3 ? 'MH' : (fc === 4 ? 'mH' : (fc <= 6 ? 'PS' : 'St')));
  829. }
  830. return roadType;
  831. },
  832. getHwySys: function (feature) {
  833. var hwySys;
  834. switch (feature.attributes.RTE_1_CLSS_CD) {
  835. case 'I':
  836. hwySys = 'interstate';
  837. break;
  838. case 'FED':
  839. case 'US':
  840. hwySys = 'us';
  841. break;
  842. case 'NC':
  843. hwySys = 'state';
  844. break;
  845. case 'RMP':
  846. hwySys = 'ramp';
  847. break;
  848. default:
  849. hwySys = 'local';
  850. }
  851. return hwySys;
  852. }
  853. },
  854. ND: {
  855. baseUrl: 'https://gis.dot.nd.gov/arcgis/rest/services/external/transinfo/MapServer/',
  856. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  857. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  858. fcMapLayers: [
  859. {
  860. 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'] },
  861. maxRecordCount: 1000, supportsPagination: false
  862. },
  863. {
  864. 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'] },
  865. maxRecordCount: 1000, supportsPagination: false
  866. },
  867. {
  868. layerID: 12, fcPropName: 'FUNCTION_CLASS', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCTION_CLASS'], roadTypeMap: { PS: ['Major Collector', 'Collector'] },
  869. maxRecordCount: 1000, supportsPagination: false
  870. },
  871. {
  872. 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] },
  873. maxRecordCount: 1000, supportsPagination: false
  874. }
  875. ],
  876. getWhereClause: function (context) {
  877. if (context.mapContext.zoom < 4) {
  878. if (context.layer.layerID !== 16) return context.layer.fcPropName + "<>'Local'";
  879. } else {
  880. return null;
  881. }
  882. },
  883. getFeatureRoadType: function (feature, layer) {
  884. return _stateSettings.global.getFeatureRoadType(feature, layer);
  885. }
  886. },
  887. OH: {
  888. baseUrl: 'https://gis.dot.state.oh.us/arcgis/rest/services/TIMS/Roadway_Information/MapServer/',
  889. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  890. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  891.  
  892. fcMapLayers: [
  893. {
  894. layerID: 8, fcPropName: 'FUNCTION_CLASS', idPropName: 'ObjectID', outFields: ['FUNCTION_CLASS', 'ROUTE_TYPE', 'ROUTE_NBR', 'ObjectID'],
  895. maxRecordCount: 1000, supportsPagination: false, roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }
  896. }
  897. ],
  898. isPermitted: function () { return true; },
  899. getWhereClause: function (context) {
  900. if (context.mapContext.zoom < 4) {
  901. var clause = '(' + context.layer.fcPropName + " < 7 OR ROUTE_TYPE IN ('CR','SR','US'))";
  902. return clause;
  903. } else {
  904. return null;
  905. }
  906. },
  907. getFeatureRoadType: function (feature, layer) {
  908. var fc = feature.attributes[layer.fcPropName];
  909. var prefix = feature.attributes.ROUTE_TYPE;
  910. var isUS = prefix === 'US';
  911. var isState = prefix === 'SR';
  912. var isCounty = prefix === 'CR';
  913. if (isUS && fc > 3) { fc = 3; }
  914. if (isState && fc > 4) { fc = 4; }
  915. if (isCounty && fc > 6) { fc = 6; }
  916. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  917. }
  918. },
  919. OK: {
  920. baseUrl: 'https://services6.arcgis.com/RBtoEUQ2lmN0K3GY/arcgis/rest/services/Roadways/FeatureServer/',
  921. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  922. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  923. fcMapLayers: [
  924. {
  925. layerID: 0, fcPropName: 'NFC', idPropName: 'OBJECTID', outFields: ['F_PRIMARY_', 'NFC', 'OBJECTID', 'ROUTE_CLAS', 'ACCESS_CON'],
  926. maxRecordCount: 1000, supportsPagination: false, roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }
  927. }
  928. ],
  929. getWhereClause: function (context) {
  930. if (context.mapContext.zoom < 4) {
  931. var clause = '(' + context.layer.fcPropName + " < 7 OR ROUTE_CLAS IN ('U','S','I'))";
  932. return clause;
  933. } else {
  934. return null;
  935. }
  936. },
  937. getFeatureRoadType: function (feature, layer) {
  938. var fc = feature.attributes[layer.fcPropName];
  939. var route = (feature.attributes.F_PRIMARY_ || '').trim();
  940. var isBusinessOrSpur = /BUS$|SPR$/i.test(route);
  941. var prefix = isBusinessOrSpur ? route.substring(0, 1) : feature.attributes.ROUTE_CLAS;
  942. var isFw = parseInt(feature.attributes.ACCESS_CON) === 1;
  943. var isInterstate = prefix === 'I';
  944. var isUS = prefix === 'U';
  945. var isState = prefix === 'S';
  946. if (isFw) { fc = 1; }
  947. else if (fc > 3 && ((isUS && !isBusinessOrSpur) || (isInterstate && isBusinessOrSpur))) { fc = 3; }
  948. else if (fc > 4 && ((isUS && isBusinessOrSpur) || (isState && !isBusinessOrSpur))) { fc = 4; }
  949. else if (fc > 5 && isState && isBusinessOrSpur) { fc = 5; }
  950. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  951. }
  952. },
  953. PA: {
  954. baseUrl: 'https://www.pdarcgissvr.pa.gov/penndotgis/rest/services/PennShare/PennShare/MapServer/',
  955. supportsPagination: false,
  956. defaultColors: { Fw: '#00ffff', Ew: '#732500', MH: '#ff0000', mH: '#00ff00', PS: '#b724ff', PS2: '#f3f300', St: '#ff9700' },
  957. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  958. isPermitted: function () { return _r >= 3; },
  959. fcMapLayers: [
  960. {
  961. layerID: 3, features: new Map(), fcPropName: 'FUNC_CLS', idPropName: 'MSLINK', outFields: ['MSLINK', 'FUNC_CLS'],
  962. maxRecordCount: 1000, supportsPagination: false, roadTypeMap: { Fw: ['01', '11'], Ew: ['12'], MH: ['02', '14'], mH: ['06', '16'], PS: ['07', '08', '17'], St: ['09', '19'] }
  963. }
  964. ],
  965. getWhereClause: function (context) {
  966. return null;
  967. },
  968. getFeatureRoadType: function (feature, layer) {
  969. if (layer.getFeatureRoadType) {
  970. return layer.getFeatureRoadType(feature);
  971. } else {
  972. var fc = feature.attributes[layer.fcPropName];
  973. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  974. }
  975. }
  976. },
  977. RI: {
  978. baseUrl: 'https://services2.arcgis.com/S8zZg9pg23JUEexQ/arcgis/rest/services/RIDOT_Roads_2016/FeatureServer/',
  979. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  980. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [[], [], [], [], [], [], [], [], [], [], []] },
  981. fcMapLayers: [
  982. {
  983. layerID: 0, fcPropName: 'F_SYSTEM', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'F_SYSTEM','ROADTYPE','RTNO'],
  984. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7,0] }, maxRecordCount: 1000, supportsPagination: false
  985. }
  986. ],
  987. isPermitted: function () { return _r >= 3; },
  988. getWhereClause: function (context) {
  989. if (context.mapContext.zoom < 4) {
  990. return context.layer.fcPropName + "<>'7 OR ";
  991. } else {
  992. return null;
  993. }
  994. },
  995. getFeatureRoadType: function (feature, layer) {
  996. var fc = parseInt(feature.attributes[layer.fcPropName]);
  997. var type = feature.attributes.ROADTYPE;
  998. var rtnum = feature.attributes.RTNO;
  999. if (fc === 2 && ['10','24','37','78','99','138','403'].includes(rtnum)) { fc = 1; } //isFw
  1000. else if ((fc > 3 && type === 'US') || rtnum === '1') { fc = 3; } //isUS
  1001. else if (fc > 4 && rtnum.trim() !== '') { fc = 4; } //isState
  1002. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1003. }
  1004. },
  1005. SC: {
  1006. baseUrl: 'https://services1.arcgis.com/VaY7cY9pvUYUP1Lf/arcgis/rest/services/Functional_Class/FeatureServer/',
  1007. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1008. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  1009. fcMapLayers: [
  1010. {
  1011. layerID: 0, fcPropName: 'FC_GIS' , idPropName: 'FID', outFields:['FID', 'FC_GIS', 'ROUTE_LRS'],
  1012. maxRecordCount:1000, supportsPagination:false, roadTypeMap:{Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5,6], St: [7]}
  1013. }
  1014. ],
  1015. isPermitted: function () { return _r >= 3; },
  1016. getWhereClause: function (context) {
  1017. return null;
  1018. },
  1019. getFeatureRoadType: function (feature, layer) {
  1020. var roadID = feature.attributes.ROUTE_LRS;
  1021. var roadType = parseInt(roadID.slice(3,4));
  1022. var isFw = roadType === 1;
  1023. var isUS = roadType === 2;
  1024. var isState = roadType === 4;
  1025. var isBus = parseInt(roadID.slice(-2,-1)) === 7;
  1026. var fc = 7;
  1027. switch(feature.attributes[layer.fcPropName]) {
  1028. case 'INT': fc = 1; break;
  1029. case 'EXP': fc = 2; break;
  1030. case 'PRA': fc = 3; break;
  1031. case 'MIA': fc = 4; break;
  1032. case 'MAC':
  1033. case 'MIC': fc = 5; break;
  1034. }
  1035. if (fc > 1 && isFw) {
  1036. fc = 1;
  1037. } else if (fc > 3 && isUS) {
  1038. fc = (isBus ? 4 : 3);
  1039. } else if (fc > 4 && isState) {
  1040. fc = (isBus ? 5 : 4);
  1041. }
  1042. if (layer.getFeatureRoadType) {
  1043. return layer.getFeatureRoadType(feature);
  1044. } else {
  1045. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1046. }
  1047. }
  1048. },
  1049. SD: {
  1050. baseUrl: 'https://arcgis.sd.gov/arcgis/rest/services/DOT/LocalRoads/MapServer/',
  1051. defaultColors: { Fw: '#ff00c5', Ew: '#149ece', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee', PSGr: '#cc6533', StGr: '#e99cb6' },
  1052. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  1053. fcMapLayers: [{
  1054. layerID: 1, fcPropName: 'FUNC_CLASS', idPropName: 'OBJECTID', maxRecordCount: 1000, supportsPagination: false,
  1055. outFields: ['OBJECTID', 'FUNC_CLASS', 'SURFACE_TYPE', 'ROADNAME'],
  1056. roadTypeMap: { Fw: [1, 11], Ew: [2, 12], MH: [4, 14], mH: [6, 16], PS: [7, 8, 17], St: [9, 19] }
  1057. }
  1058. ],
  1059. getWhereClause: function (context) {
  1060. if (context.mapContext.zoom < 4) {
  1061. return context.layer.fcPropName + "<>19 AND " + context.layer.fcPropName + "<>9";
  1062. } else {
  1063. return null;
  1064. }
  1065. },
  1066. getFeatureRoadType: function (feature, layer) {
  1067. var attr = feature.attributes;
  1068. var fc = parseInt(attr[layer.fcPropName]) % 10;
  1069. var isFw = attr.ACCESS_CONTROL === 1;
  1070. var isUS = RegExp('^US HWY ', 'i').test(attr.ROADNAME);
  1071. var isState = RegExp('^SD HWY ', 'i').test(attr.ROADNAME);
  1072. var isBus = RegExp('^(US|SD) HWY .* (E|W)?(B|L)$', 'i').test(attr.ROADNAME);
  1073. var isPaved = parseInt(attr.SURFACE_TYPE) > 5;
  1074. if (isFw) {
  1075. fc = 1;
  1076. } else if (fc > 4 && isUS) {
  1077. fc = (isBus ? 6 : 4);
  1078. } else if (fc > 6 && isState) {
  1079. fc = (isBus ? 7 : 6);
  1080. }
  1081. if (fc > 6 && !isPaved) {
  1082. return fc < 9 ? 'PSGr' : 'StGr';
  1083. } else {
  1084. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1085. }
  1086. }
  1087. },
  1088. TN: {
  1089. // NOTE: DUE TO ERRORS FROM THE SHELBY COUNTY SERVER, FC IS NOT WORKING PROPERLY HERE YET (9/23/2016)
  1090. baseUrl: 'https://testuasiportal.shelbycountytn.gov/arcgis/rest/services/MPO/Webmap_2015_04_20_TMPO/MapServer/',
  1091.  
  1092. // TODO: UPDATE COLORS TO MATCH ORIGINAL TN FC MAP COLORS.
  1093. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', PS2: '#cfae0e', St: '#eeeeee' },
  1094. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  1095. fcMapLayers: [
  1096. { 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] } }
  1097. ],
  1098. getWhereClause: function (context) {
  1099. if (context.mapContext.zoom < 4) {
  1100. return context.layer.fcPropName + ' NOT IN (9,19)';
  1101. } else {
  1102. return null;
  1103. }
  1104. },
  1105. getFeatureRoadType: function (feature, layer) {
  1106. if (layer.getFeatureRoadType) {
  1107. return layer.getFeatureRoadType(feature);
  1108. } else {
  1109. var fc = feature.attributes[layer.fcPropName];
  1110. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1111. }
  1112. }
  1113. },
  1114. TX: {
  1115. baseUrl: 'https://services.arcgis.com/KTcxiTD9dsQw4r7Z/ArcGIS/rest/services/TxDOT_Functional_Classification/FeatureServer/',
  1116. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1117. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  1118. fcMapLayers: [
  1119. { layerID: 0, fcPropName: 'F_SYSTEM', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'F_SYSTEM', 'RTE_PRFX'], maxRecordCount: 1000, supportsPagination: false, roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] } }
  1120. ],
  1121. getWhereClause: function (context) {
  1122. var where = " F_SYSTEM IS NOT NULL AND RTE_PRFX IS NOT NULL";
  1123. if (context.mapContext.zoom < 4) {
  1124. where += ' AND ' + context.layer.fcPropName + " <> 7";
  1125. }
  1126. return where;
  1127. },
  1128. getFeatureRoadType: function (feature, layer) {
  1129. // On-System:
  1130. // IH=Interstate BF=Business FM
  1131. // US=US Highway FM=Farm to Mkt
  1132. // UA=US Alt. RM=Ranch to Mkt
  1133. // UP=US Spur RR=Ranch Road
  1134. // SH=State Highway PR=Park Road
  1135. // SA=State Alt. RE=Rec Road
  1136. // SL=State Loop RP=Rec Rd Spur
  1137. // SS=State Spur FS=FM Spur
  1138. // BI=Business IH RS=RM Spur
  1139. // BU=Business US RU=RR Spur
  1140. // BS=Business State PA=Principal Arterial
  1141. // Off-System:
  1142. // TL=Off-System Tollroad CR=County Road
  1143. // FC=Func. Classified St. LS=Local Street
  1144. if (layer.getFeatureRoadType) {
  1145. return layer.getFeatureRoadType(feature);
  1146. } else {
  1147. var fc = feature.attributes[layer.fcPropName];
  1148. var type = feature.attributes.RTE_PRFX.substring(0, 2).toUpperCase();
  1149. if (type === 'IH' && fc > 1) {
  1150. fc = 1;
  1151. } else if ((type === 'US' || type === 'BI' || type === 'UA') && fc > 3) {
  1152. fc = 3;
  1153. } else if ((type === 'UP' || type === 'BU' || type === 'SH' || type === 'SA') && fc > 4) {
  1154. fc = 4;
  1155. } else if ((type === 'SL' || type === 'SS' || type === 'BS') && fc > 6) {
  1156. fc = 6;
  1157. }
  1158. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1159. }
  1160. },
  1161. isPermitted: function () { return _r >= 1; }
  1162. },
  1163. UT: {
  1164. baseUrl: 'https://maps.udot.utah.gov/arcgis/rest/services/Functional_Class/MapServer/',
  1165. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1166. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  1167. fcMapLayers: [
  1168. {
  1169. 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] },
  1170. maxRecordCount: 1000, supportsPagination: false
  1171. }
  1172. ],
  1173. getWhereClause: function (context) {
  1174. var clause = context.layer.fcPropName + '<=7';
  1175. if (context.mapContext.zoom < 4) {
  1176. clause += ' OR ' + context.layer.fcPropName + '<7';
  1177. }
  1178. return clause;
  1179. },
  1180. getFeatureRoadType: function (feature, layer) {
  1181. var routeId = feature.attributes.ROUTE_ID;
  1182. var fc = feature.attributes.FC_CODE;
  1183. if ([6, 40, 50, 89, 91, 163, 189, 191, 491].indexOf(routeId) > -1 && fc > 3) {
  1184. // US highway
  1185. fc = 3;
  1186. } else if (routeId <= 491 && fc > 4) {
  1187. // State highway
  1188. fc = 4;
  1189. }
  1190. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1191. }
  1192. },
  1193. VT: {
  1194. baseUrl: 'https://maps.vtrans.vermont.gov/arcgis/rest/services/Master/General/FeatureServer/',
  1195. defaultColors: { Fw: '#ff00c5', Ew: '#4f33df', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1196. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1] },
  1197. fcMapLayers: [
  1198. {
  1199. layerID: 39, fcPropName: 'FUNCL', idPropName: 'OBJECTID', outFields: ['OBJECTID', 'FUNCL', 'HWYSIGN'],
  1200. roadTypeMap: { Fw: [1], Ew: [2], MH: [3], mH: [4], PS: [5, 6], St: [7] }, maxRecordCount: 1000, supportsPagination: false
  1201. }
  1202. ],
  1203. getWhereClause: function (context) {
  1204. if (context.mapContext.zoom < 4) {
  1205. return context.layer.fcPropName + "<>7 AND " + context.layer.fcPropName + "<>0";
  1206. } else {
  1207. return null;
  1208. }
  1209. },
  1210. getFeatureRoadType: function (feature, layer) {
  1211. var roadID = feature.attributes.HWYSIGN;
  1212. var fc = feature.attributes[layer.fcPropName];
  1213. if (!(fc > 0)) { fc = 7; }
  1214. var isUS = RegExp(/^U/).test(roadID);
  1215. var isState = RegExp(/^V/).test(roadID);
  1216. var isUSBiz = RegExp(/^B/).test(roadID);
  1217. if (fc > 3 && isUS) {
  1218. fc = 3;
  1219. } else if (fc > 4 && (isUSBiz || isState)) {
  1220. fc = 4;
  1221. }
  1222. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1223. }
  1224. },
  1225. VA: {
  1226. baseUrl: 'https://services.arcgis.com/p5v98VHDX9Atv3l7/arcgis/rest/services/FC_2014_FHWA_Submittal1/FeatureServer/',
  1227. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1228. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  1229. fcMapLayers: [
  1230. { 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] } },
  1231. { 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] } },
  1232. { 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] } }
  1233. ],
  1234. 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],
  1235. getWhereClause: function (context) {
  1236. if (context.mapContext.zoom < 4) {
  1237. return context.layer.fcPropName + '<>7';
  1238. } else {
  1239. //NOTE: As of 9/14/2016 there does not appear to be any US/SR/VA labeled routes with FC = 7.
  1240. return null;
  1241. }
  1242. },
  1243. getFeatureRoadType: function (feature, layer) {
  1244. if (layer.getFeatureRoadType) {
  1245. return layer.getFeatureRoadType(feature);
  1246. } else {
  1247. var fc = parseInt(feature.attributes[layer.fcPropName]);
  1248. var rtName = feature.attributes.RTE_NM;
  1249. var match = /^R-VA\s*(US|VA|SR)(\d{5})..(BUS)?/.exec(rtName);
  1250. var isBusiness = (match && (match !== null) && (match[3] === 'BUS'));
  1251. var isState = (match && (match !== null) && (match[1] === 'VA' || match[1] === 'SR'));
  1252. var rtNum = parseInt((layer.layerID === 1) ? feature.attributes.ROUTE_NO : (match ? match[2] : 99999));
  1253. var rtPrefix = match && match[1];
  1254. if (fc > 3 && rtPrefix === 'US') {
  1255. fc = isBusiness ? 4 : 3;
  1256. } else if (isState && fc > 4 && this.srExceptions.indexOf(rtNum) === -1 && rtNum < 600) {
  1257. fc = isBusiness ? 5 : 4;
  1258. }
  1259. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1260. }
  1261. }
  1262. },
  1263. WV: {
  1264. baseUrl: 'https://gis.transportation.wv.gov/arcgis/rest/services/Roads_And_Highways/Publication_LRS/MapServer/',
  1265. defaultColors: { Fw: '#ff00c5', Ew: '#ff00c5', MH: '#149ece', mH: '#4ce600', PS: '#cfae0e', St: '#eeeeee' },
  1266. zoomSettings: { maxOffset: [30, 15, 8, 4, 2, 1, 1, 1, 1, 1], excludeRoadTypes: [['St'], ['St'], ['St'], ['St'], [], [], [], [], [], [], []] },
  1267. fcMapLayers: [
  1268. { 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] } }
  1269. ],
  1270. getWhereClause: function (context) {
  1271. if (context.mapContext.zoom < 4) {
  1272. return context.layer.fcPropName + ' NOT IN(9,19)';
  1273. } else {
  1274. return null;
  1275. }
  1276. },
  1277. getFeatureRoadType: function (feature, layer) {
  1278. if (layer.getFeatureRoadType) {
  1279. return layer.getFeatureRoadType(feature);
  1280. } else {
  1281. var fcCode = feature.attributes[layer.fcPropName];
  1282. var fc = fcCode;
  1283. if (fcCode === 11) fc = 1;
  1284. else if (fcCode === 4 || fcCode === 12) fc = 2;
  1285. else if (fcCode === 2 || fcCode === 14) fc = 3;
  1286. else if (fcCode === 6 || fcCode === 16) fc = 4;
  1287. else if (fcCode === 7 || fcCode === 17 || fcCode === 8 || fcCode === 18) fc = 5;
  1288. else fc = 7;
  1289. var id = feature.attributes.ROUTE_ID;
  1290. var prefix = id.substr(2, 1);
  1291. var isInterstate = false;
  1292. var isUS = false;
  1293. var isState = false;
  1294. switch (prefix) {
  1295. case '1':
  1296. isInterstate = true;
  1297. break;
  1298. case '2':
  1299. isUS = true;
  1300. break;
  1301. case '3':
  1302. isState = true;
  1303. break;
  1304. }
  1305. if (fc > 1 && isInterstate)
  1306. fc = 1;
  1307. else if (fc > 3 && isUS)
  1308. fc = 3;
  1309. else if (fc > 4 && isState)
  1310. fc = 4;
  1311. return _stateSettings.global.getRoadTypeFromFC(fc, layer);
  1312. }
  1313. }
  1314. }
  1315. };
  1316.  
  1317. function log(message, level) {
  1318. if (message && (!level || (level <= _debugLevel))) {
  1319. console.log('FC Layer: ', message);
  1320. }
  1321. }
  1322.  
  1323. function dynamicSort(property) {
  1324. var sortOrder = 1;
  1325. if (property[0] === "-") {
  1326. sortOrder = -1;
  1327. property = property.substr(1);
  1328. }
  1329. return function (a, b) {
  1330. var props = property.split('.');
  1331. props.forEach(function (prop) {
  1332. a = a[prop];
  1333. b = b[prop];
  1334. });
  1335. var result = (a < b) ? -1 : (a > b) ? 1 : 0;
  1336. return result * sortOrder;
  1337. };
  1338. }
  1339.  
  1340. function dynamicSortMultiple() {
  1341. /*
  1342. * save the arguments object as it will be overwritten
  1343. * note that arguments object is an array-like object
  1344. * consisting of the names of the properties to sort by
  1345. */
  1346. var props = arguments;
  1347. if (arguments[0] && Array.isArray(arguments[0])) {
  1348. props = arguments[0];
  1349. }
  1350. return function (obj1, obj2) {
  1351. var i = 0, result = 0, numberOfProperties = props.length;
  1352. /* try getting a different result from 0 (equal)
  1353. * as long as we have extra properties to compare
  1354. */
  1355. while (result === 0 && i < numberOfProperties) {
  1356. result = dynamicSort(props[i])(obj1, obj2);
  1357. i++;
  1358. }
  1359. return result;
  1360. };
  1361. }
  1362.  
  1363. function generateUUID() {
  1364. var d = new Date().getTime();
  1365. var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
  1366. var r = (d + Math.random() * 16) % 16 | 0;
  1367. d = Math.floor(d / 16);
  1368. return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  1369. });
  1370. return uuid;
  1371. }
  1372.  
  1373. function loadSettingsFromStorage() {
  1374. var loadedSettings = $.parseJSON(localStorage.getItem(_settingsStoreName));
  1375. var defaultSettings = {
  1376. lastVersion: null,
  1377. layerVisible: true,
  1378. activeStateAbbr: 'ALL',
  1379. hideStreet: false
  1380. };
  1381. _settings = loadedSettings ? loadedSettings : defaultSettings;
  1382. for (var prop in defaultSettings) {
  1383. if (!_settings.hasOwnProperty(prop)) {
  1384. _settings[prop] = defaultSettings[prop];
  1385. }
  1386. }
  1387. }
  1388.  
  1389. function saveSettingsToStorage() {
  1390. if (localStorage) {
  1391. _settings.lastVersion = _scriptVersion;
  1392. _settings.layerVisible = _mapLayer.visibility;
  1393. localStorage.setItem(_settingsStoreName, JSON.stringify(_settings));
  1394. log('Settings saved', 1);
  1395. }
  1396. }
  1397.  
  1398. function getLineWidth() {
  1399. return 12 * Math.pow(1.15, (W.map.getZoom() - 1));
  1400. }
  1401.  
  1402. function sortArray(array) {
  1403. array.sort(function (a, b) { if (a < b) return -1; if (a > b) return 1; else return 0; });
  1404. }
  1405.  
  1406. function getVisibleStateAbbrs() {
  1407. var visibleStates = [];
  1408. W.model.states.additionalInfo.forEach(function (state) {
  1409. var stateAbbr = _statesHash[state.name];
  1410. var activeStateAbbr = _settings.activeStateAbbr;
  1411. if (_stateSettings[stateAbbr] && _stateSettings.global.isPermitted(stateAbbr) && (!activeStateAbbr || activeStateAbbr === 'ALL' || activeStateAbbr === stateAbbr)) {
  1412. visibleStates.push(stateAbbr);
  1413. }
  1414. });
  1415. return visibleStates;
  1416. }
  1417.  
  1418. function getAsync(url, context) {
  1419. return new Promise(function (resolve, reject) {
  1420. GM_xmlhttpRequest({
  1421. context: context, method: "GET", url: url,
  1422. onload: function (res) {
  1423. if (res.status.toString() === '200') {
  1424. resolve({ responseText: res.responseText, context: context });
  1425. } else {
  1426. reject({ responseText: res.responseText, context: context });
  1427. }
  1428. },
  1429. onerror: function () {
  1430. reject(Error("Network Error"));
  1431. }
  1432. });
  1433. });
  1434. }
  1435. function wait(ms) {
  1436. var start = new Date().getTime();
  1437. var end = start;
  1438. while (end < start + ms) {
  1439. end = new Date().getTime();
  1440. }
  1441. }
  1442. function getUrl(context, queryType, queryParams) {
  1443. var extent = context.mapContext.extent,
  1444. zoom = context.mapContext.zoom,
  1445. layer = context.layer,
  1446. state = context.state;
  1447.  
  1448. var whereParts = [];
  1449. var geometry = { xmin: extent.left, ymin: extent.bottom, xmax: extent.right, ymax: extent.top, spatialReference: { wkid: 102100, latestWkid: 3857 } };
  1450. var geometryStr = JSON.stringify(geometry);
  1451. var stateWhereClause = state.getWhereClause(context);
  1452. var layerPath = layer.layerPath || '';
  1453. var url = state.baseUrl + layerPath + layer.layerID + '/query?geometry=' + encodeURIComponent(geometryStr);
  1454.  
  1455. if (queryType === 'countOnly') {
  1456. url += '&returnCountOnly=true';
  1457. } else if (queryType === 'idsOnly') {
  1458. url += '&returnIdsOnly=true';
  1459. } else if (queryType === 'paged') {
  1460. // TODO
  1461. } else {
  1462. url += '&returnGeometry=true&maxAllowableOffset=' + state.zoomSettings.maxOffset[zoom];
  1463. url += '&outFields=' + encodeURIComponent(layer.outFields.join(','));
  1464. if (queryType === 'idRange') {
  1465. var idPropName = context.layer.idPropName;
  1466. whereParts.push('(' + queryParams.idFieldName + '>=' + queryParams.range[0] + ' AND ' + queryParams.idFieldName + '<=' + queryParams.range[1] + ')');
  1467. }
  1468. }
  1469. if (stateWhereClause) whereParts.push(stateWhereClause);
  1470. if (whereParts.length > 0) url += '&where=' + encodeURIComponent(whereParts.join(' AND '));
  1471. url += '&spatialRel=esriSpatialRelIntersects&geometryType=esriGeometryEnvelope&inSR=102100&outSR=3857&f=json';
  1472. //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.
  1473. return url;
  1474. }
  1475.  
  1476. function convertFcToRoadTypeVectors(feature, state, stateAbbr, layer, zoom) {
  1477. var roadType = state.getFeatureRoadType(feature, layer);
  1478. log(feature, 3);
  1479. var zIndex = _stateSettings.global.roadTypes.indexOf(roadType) * 100;
  1480. var vectors = [];
  1481. var lineFeatures = [];
  1482. var attr = {
  1483. //fcFeatureUniqueId: stateAbbr + '-' + layer.layerID + '-' + feature.attributes[layer.idPropName],
  1484. //fcFeatureId: feature.attributes[layer.idPropName],
  1485. state: stateAbbr,
  1486. layerID: layer.layerID,
  1487. roadType: roadType,
  1488. dotAttributes: $.extend({}, feature.attributes),
  1489. color: state.defaultColors[roadType],
  1490. strokeWidth: getLineWidth,
  1491. zIndex: zIndex
  1492. };
  1493.  
  1494. feature.geometry.paths.forEach(function (path) {
  1495. var pointList = [];
  1496. var newPoint = null;
  1497. var lastPoint = null;
  1498. path.forEach(function (point) {
  1499. pointList.push(new OL.Geometry.Point(point[0], point[1]));
  1500. });
  1501. var vectorFeature = new OL.Feature.Vector(new OL.Geometry.LineString(pointList), attr);
  1502. vectors.push(vectorFeature);
  1503. });
  1504.  
  1505. return vectors;
  1506. }
  1507.  
  1508. function fetchLayerFC(context) {
  1509. var url = getUrl(context, 'idsOnly');
  1510. log(url, 2);
  1511. if (!context.parentContext.cancel) {
  1512. return getAsync(url, context).bind(context).then(function (res) {
  1513. var ids = $.parseJSON(res.responseText);
  1514. if (!ids.objectIds) ids.objectIds = [];
  1515. sortArray(ids.objectIds);
  1516. log(ids, 2);
  1517. return ids;
  1518. }).then(function (res) {
  1519. var context = this;
  1520. var idRanges = [];
  1521. if (res.objectIds) {
  1522. var len = res.objectIds ? res.objectIds.length : 0;
  1523. var currentIndex = 0;
  1524. var offset = Math.min(this.layer.maxRecordCount, 1000);
  1525. while (currentIndex < len) {
  1526. var nextIndex = currentIndex + offset;
  1527. if (nextIndex >= len) nextIndex = len - 1;
  1528. idRanges.push({ range: [res.objectIds[currentIndex], res.objectIds[nextIndex]], idFieldName: res.objectIdFieldName });
  1529. currentIndex = nextIndex + 1;
  1530. }
  1531. log(context.layer.layerID, 2);
  1532. log(idRanges, 2);
  1533. }
  1534. return idRanges;
  1535. }).map(function (idRange) {
  1536. var context = this;
  1537. if (!context.parentContext.cancel) {
  1538. var url = getUrl(this, 'idRange', idRange);
  1539. log(url, 2);
  1540. return getAsync(url, context).then(function (res) {
  1541. var context = res.context;
  1542. if (!context.parentContext.cancel) {
  1543. var features = $.parseJSON(res.responseText).features;
  1544. // if (context.parentContext.callCount === 0 ) {
  1545. // _mapLayer.removeAllFeatures();
  1546. // }
  1547. context.parentContext.callCount++;
  1548. log('Feature Count=' + (features ? features.length : 0), 2);
  1549. features = features ? features : [];
  1550. var vectors = [];
  1551. features.forEach(function (feature) {
  1552. if (!res.context.parentContext.cancel) {
  1553. var vector = convertFcToRoadTypeVectors(feature, context.state, context.stateAbbr, context.layer, context.mapContext.zoom);
  1554. //var fcFeatureUniqueId = vector[0].attributes.fcFeatureUniqueId;
  1555. //context.parentContext.addedFcFeatureUniqueIds.push(fcFeatureUniqueId);
  1556. if (/*!context.parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId] &&*/ !(vector[0].attributes.roadType === 'St' && _settings.hideStreet)) {
  1557. vectors.push(vector);
  1558. }
  1559. }
  1560. });
  1561. return vectors;
  1562. }
  1563. });
  1564. } else {
  1565. log('Async call cancelled', 1);
  1566. }
  1567. });
  1568. }
  1569. }
  1570.  
  1571. function fetchStateFC(context) {
  1572. var state = _stateSettings[context.stateAbbr];
  1573. var contexts = [];
  1574. state.fcMapLayers.forEach(function (layer) {
  1575. contexts.push({ parentContext: context.parentContext, layer: layer, state: state, stateAbbr: context.stateAbbr, mapContext: context.mapContext });
  1576. });
  1577. return Promise.map(contexts, function (context) {
  1578. return fetchLayerFC(context);
  1579. });
  1580. }
  1581.  
  1582. var _lastPromise = null;
  1583. var _lastContext = null;
  1584. var _fcCallCount = 0;
  1585. function fetchAllFC() {
  1586. if (!_mapLayer.visibility) return;
  1587.  
  1588. if (_lastPromise) { _lastPromise.cancel(); }
  1589. $('#fc-loading-indicator').text('Loading FC...');
  1590.  
  1591. var mapContext = { zoom: W.map.getZoom(), extent: W.map.getExtent() };
  1592. var contexts = [];
  1593. var parentContext = { callCount: 0,/*existingFcFeatureUniqueIds:{}, addedFcFeatureUniqueIds:[],*/ startTime: Date.now() };
  1594. // _mapLayer.features.forEach(function(vectorFeature) {
  1595. // var fcFeatureUniqueId = vectorFeature.attributes.fcFeatureUniqueId;
  1596. // var existingFcFeatureUniqueIdArray = parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId];
  1597. // if (!existingFcFeatureUniqueIdArray) {
  1598. // existingFcFeatureUniqueIdArray = [];
  1599. // parentContext.existingFcFeatureUniqueIds[fcFeatureUniqueId] = existingFcFeatureUniqueIdArray;
  1600. // }
  1601. // existingFcFeatureUniqueIdArray.push(vectorFeature);
  1602. // });
  1603. if (_lastContext) _lastContext.cancel = true;
  1604. _lastContext = parentContext;
  1605. getVisibleStateAbbrs().forEach(function (stateAbbr) {
  1606. contexts.push({ parentContext: parentContext, stateAbbr: stateAbbr, mapContext: mapContext });
  1607. });
  1608. var map = Promise.map(contexts, function (context) {
  1609. return fetchStateFC(context);
  1610. }).bind(parentContext).then(function (statesVectorArrays) {
  1611. if (!this.cancel) {
  1612. _mapLayer.removeAllFeatures();
  1613. statesVectorArrays.forEach(function (vectorsArray) {
  1614. vectorsArray.forEach(function (vectors) {
  1615. vectors.forEach(function (vector) {
  1616. vector.forEach(function (vectorFeature) {
  1617. _mapLayer.addFeatures(vectorFeature);
  1618. });
  1619. });
  1620. });
  1621. });
  1622. //buildTable();
  1623. // for(var fcFeatureUniqueId in this.existingFcFeatureUniqueIds) {
  1624. // if(this.addedFcFeatureUniqueIds.indexOf(fcFeatureUniqueId) === -1) {
  1625. // if (!this.cancel) _mapLayer.removeFeatures(this.existingFcFeatureUniqueIds[fcFeatureUniqueId]);
  1626. // }
  1627. // }
  1628. log('TOTAL RETRIEVAL TIME = ' + (Date.now() - parentContext.startTime), 1);
  1629. log(statesVectorArrays, 1);
  1630. }
  1631. return statesVectorArrays;
  1632. }).catch(function (e) {
  1633. $('#fc-loading-indicator').text('FC Error! (check console for details)');
  1634. log(e, 0);
  1635. }).finally(function () {
  1636. _fcCallCount -= 1;
  1637. if (_fcCallCount === 0) {
  1638. $('#fc-loading-indicator').text('');
  1639. }
  1640. });
  1641.  
  1642. _fcCallCount += 1;
  1643. _lastPromise = map;
  1644. }
  1645.  
  1646. function onLayerCheckboxChanged(checked) {
  1647. _mapLayer.setVisibility(checked);
  1648. }
  1649.  
  1650. function onLayerVisibilityChanged(evt) {
  1651. _settings.layerVisible = _mapLayer.visibility;
  1652. saveSettingsToStorage();
  1653. if (_mapLayer.visibility) {
  1654. fetchAllFC();
  1655. }
  1656. }
  1657.  
  1658. function onModeChanged(model, modeId, context) {
  1659. if (!modeId || modeId === 1) {
  1660. initUserPanel();
  1661. }
  1662. }
  1663.  
  1664. function showScriptInfoAlert() {
  1665. /* Check version and alert on update */
  1666. if (_alertUpdate && _scriptVersion !== _settings.lastVersion) {
  1667. alert(_scriptVersionChanges);
  1668. }
  1669. }
  1670.  
  1671. function initLayer() {
  1672. var _drawingContext = {
  1673. getZIndex: function (feature) {
  1674. return feature.attributes.zIndex;
  1675. },
  1676. getStrokeWidth: function () { return getLineWidth(); }
  1677. };
  1678. var defaultStyle = new OL.Style({
  1679. strokeColor: '${color}', //'#00aaff',
  1680. strokeDashstyle: "solid",
  1681. strokeOpacity: 1.0,
  1682. strokeWidth: '${strokeWidth}',
  1683. graphicZIndex: '${zIndex}'
  1684. });
  1685.  
  1686. var selectStyle = new OL.Style({
  1687. //strokeOpacity: 1.0,
  1688. strokeColor: '#000000'
  1689. });
  1690.  
  1691. _mapLayer = new OL.Layer.Vector("FC Layer", {
  1692. uniqueName: "__FCLayer",
  1693. displayInLayerSwitcher: false,
  1694. rendererOptions: { zIndexing: true },
  1695. styleMap: new OL.StyleMap({
  1696. 'default': defaultStyle,
  1697. 'select': selectStyle
  1698. })
  1699. });
  1700.  
  1701. _mapLayer.setOpacity(0.5);
  1702.  
  1703. I18n.translations[I18n.locale].layers.name.__FCLayer = "FC Layer";
  1704.  
  1705. _mapLayer.displayInLayerSwitcher = true;
  1706. _mapLayer.events.register('visibilitychanged', null, onLayerVisibilityChanged);
  1707. _mapLayer.setVisibility(_settings.layerVisible);
  1708.  
  1709. W.map.addLayer(_mapLayer);
  1710. _mapLayer.setZIndex(_mapLayerZIndex);
  1711. WazeWrap.Interface.AddLayerCheckbox('Display', 'FC Layer', _settings.layerVisible, onLayerCheckboxChanged);
  1712. // Hack to fix layer zIndex. Some other code is changing it sometimes but I have not been able to figure out why.
  1713. // 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. (?)
  1714.  
  1715. var checkLayerZIndex = function () {
  1716. if (_mapLayer.getZIndex() != _mapLayerZIndex) {
  1717. log("ADJUSTED FC LAYER Z-INDEX " + _mapLayerZIndex + ', ' + _mapLayer.getZIndex(), 1);
  1718. _mapLayer.setZIndex(_mapLayerZIndex);
  1719. }
  1720. };
  1721.  
  1722. setInterval(function () { checkLayerZIndex(); }, 200);
  1723.  
  1724. W.map.events.register("moveend", W.map, function (e) {
  1725. fetchAllFC();
  1726. return true;
  1727. }, true);
  1728. }
  1729.  
  1730. function initUserPanel() {
  1731. var $tab = $('<li>').append($('<a>', { 'data-toggle': 'tab', href: '#sidepanel-fc-layer' }).text('FC'));
  1732. var $panel = $('<div>', { class: 'tab-pane', id: 'sidepanel-fc-layer' });
  1733. var $stateSelect = $('<select>', { id: 'fcl-state-select', class: 'form-control disabled', style: 'disabled' }).append($('<option>', { value: 'ALL' }).text('All'));
  1734. // $stateSelect.change(function(evt) {
  1735. // _settings.activeStateAbbr = evt.target.value;
  1736. // saveSettingsToStorage();
  1737. // _mapLayer.removeAllFeatures();
  1738. // fetchAllFC();
  1739. // });
  1740. for (var stateAbbr in _stateSettings) {
  1741. if (stateAbbr !== 'global') {
  1742. $stateSelect.append($('<option>', { value: stateAbbr }).text(reverseStatesHash(stateAbbr)));
  1743. }
  1744. }
  1745.  
  1746. var $hideStreet = $('<div>', { id: 'fcl-hide-street-container', class: 'controls-container' })
  1747. .append($('<input>', { type: 'checkbox', name: 'fcl-hide-street', id: 'fcl-hide-street' }).prop('checked', _settings.hideStreet).click(function () {
  1748. _settings.hideStreet = $(this).is(':checked');
  1749. saveSettingsToStorage();
  1750. _mapLayer.removeAllFeatures();
  1751. fetchAllFC();
  1752. }))
  1753. .append($('<label>', { for: 'fcl-hide-street' }).text('Hide local street highlights'));
  1754.  
  1755. $stateSelect.val(_settings.activeStateAbbr ? _settings.activeStateAbbr : 'ALL');
  1756.  
  1757. $panel.append(
  1758. $('<div>', { class: 'form-group' }).append(
  1759. $('<label>', { class: 'control-label' }).text('Select a state')
  1760. ).append(
  1761. $('<div>', { class: 'controls', id: 'fcl-state-select-container' }).append(
  1762. $('<div>').append($stateSelect)
  1763. )
  1764. ),
  1765. $hideStreet,
  1766. $('<div>', { id: 'fcl-table-container' })
  1767. );
  1768.  
  1769. $panel.append(
  1770. $('<div>', { style: 'margin-top:10px;font-size:10px;color:#999999;' })
  1771. .append($('<div>').text('version ' + _scriptVersion))
  1772. .append(
  1773. $('<div>').append(
  1774. $('<a>', { href: '#' /*, target:'__blank'*/ }).text('Discussion Forum (currently n/a)')
  1775. )
  1776. )
  1777. );
  1778.  
  1779. $('#user-tabs > .nav-tabs').append($tab);
  1780. $('#user-info > .flex-parent > .tab-content').append($panel);
  1781. $('#fcl-state-select').change(function () {
  1782. _settings.activeStateAbbr = this.value;
  1783. saveSettingsToStorage();
  1784. fetchAllFC();
  1785. });
  1786. }
  1787.  
  1788. function addLoadingIndicator() {
  1789. $('.loading-indicator').after($('<div class="loading-indicator" style="margin-right:10px" id="fc-loading-indicator">'));
  1790. }
  1791.  
  1792. function initGui() {
  1793. addLoadingIndicator();
  1794. initLayer();
  1795. initUserPanel();
  1796. showScriptInfoAlert();
  1797. }
  1798.  
  1799. function processText(text) {
  1800. return new Promise(function (resolve, reject) {
  1801. var newText = text.replace(/(e)/, 'E');
  1802. resolve(newText);
  1803. });
  1804. }
  1805.  
  1806. function init() {
  1807. if (_debugLevel > 0 && Promise.config) {
  1808. Promise.config({
  1809. warnings: true,
  1810. longStackTraces: true,
  1811. cancellation: true,
  1812. monitoring: false
  1813. });
  1814. } else {
  1815. Promise.config({
  1816. warnings: false,
  1817. longStackTraces: false,
  1818. cancellation: true,
  1819. monitoring: false
  1820. });
  1821. }
  1822.  
  1823. var u = W.loginManager.user;
  1824. _uid = u.id;
  1825. _r = u.rank;
  1826. _isAM = u.isAreaManager;
  1827. loadSettingsFromStorage();
  1828. String.prototype.replaceAll = function (search, replacement) {
  1829. var target = this;
  1830. return target.replace(new RegExp(search, 'g'), replacement);
  1831. };
  1832. initGui();
  1833. W.app.modeController.model.bind('change:mode', onModeChanged);
  1834. W.prefs.on("change:isImperial", function () { initUserPanel(); loadSettingsFromStorage(); });
  1835. fetchAllFC();
  1836. log('Initialized.', 0);
  1837. }
  1838.  
  1839. function bootstrap() {
  1840. if (W && W.loginManager &&
  1841. W.loginManager.events &&
  1842. W.loginManager.events.register &&
  1843. W.model && W.model.states && W.model.states.additionalInfo &&
  1844. W.map && W.loginManager.user &&
  1845. WazeWrap.Ready) {
  1846. log('Initializing...', 0);
  1847.  
  1848. init();
  1849. } else {
  1850. log('Bootstrap failed. Trying again...', 0);
  1851. unsafeWindow.setTimeout(function () {
  1852. bootstrap();
  1853. }, 1000);
  1854. }
  1855. }
  1856.  
  1857. log('Bootstrap...', 0);
  1858. bootstrap();
  1859. })();