Code Sample: JavaScript

This JavaScript code powers the front end of my scenic drives application. It is built on a foundation of CesiumJS, uses Ajax to load data from the database, and contains a complex query mechanism for querying routes.

1/**

2 * Closes the menu when a user clicks on the "x" in the corner of the filter menu

3 *

4 * @returns {Void}

5 */

6function closeFilterForm() {

7    $('#form_filter').fadeOut();

8}

9

10/**

11 * Returns Query String from URL

12 *

13 * @returns {String} The query string, in the format ?var1=x&var2=y

14 */

15function getQueryString() {

16    const DEFAULT_QUERY_STRING = '?country=us&state=az';

17    

18    var queryString = window.location.search;

19    if (queryString === '') {

20        queryString = DEFAULT_QUERY_STRING;

21    }

22    

23    return queryString;

24}

25

26/**

27 * Displays the disclaimer box

28 *

29 * @returns {Void}

30 */

31function displayDisclaimer() {

32    alert("\*** DISCLAIMER *** \n\nSome of these scenic drives venture into extremely remote and/or dangerous areas. Please head all warnings and advisories prior to travelling to these areas.\n\nWe are not responsible for anything that happens to you on these drives, and cannot guarantee that the information on this website is 100% accurate.");

33}

34

35/**

36 * Parses the query string in the current URL

37 *

38 * @returns {Object} The query string elements in key, value pairs

39 */

40function parseQueryString() {

41    var queryString = getQueryString();

42    queryString = queryString.replace('?', '');

43    var qsArray = queryString.split('&');

44    var qsDict = {};

45    

46    for (var i = 0; i < qsArray.length; i++) {

47        var pair = qsArray[i].split('=');

48        var key = pair[0];

49        var val = pair[1];

50        qsDict[key] = val;

51    }

52    

53    return qsDict;

54}

55

56/**

57 * Opens the "Filter Drives" menu when the button is clicked

58 *

59 * @returns {Void}

60 */

61function showFilterForm() {

62    $('#form_filter').fadeIn();

63    if (document.getElementById('countrySelect').value !== '') {

64        loadState();

65        loadDrives();

66    }

67}

68

69/**

70 * Submits the filter form

71 *

72 */

73function submitFilterForm() {

74    var submittedQueryString = '';

75    var country = document.getElementById('countrySelect').value;

76    var state = document.getElementById('stateSelect').value;

77    var drive = document.getElementById('driveSelect').value;

78    if (country === '' && state === '' && drive === '') {

79        closeFilterForm();

80        return false;

81    }

82    

83    if (drive !== '') {

84        submittedQueryString = 'id=' + drive;

85    } else {

86        if (country !== '') {

87            submittedQueryString += 'country=' + country;

88        }

89        if (state !== '') {

90            submittedQueryString += '&state=' + state;

91        }

92    }

93    

94    window.location = 'http://' + window.location.host + window.location.pathname + '?' + submittedQueryString;

95    return false;

96}

97

98/**

99 * Flies the map to a rectange (region, country, or state).

100 * The coordinates for each rectangle are stored in the

101 * database. Coordinates are passed in a comma-separated

102 * string with no spaces, in the form 'north,south,east,west'.

103 *

104 * @param {String} coordinates  Comma-separated string in the form

105 * 'north,south,east,west', with coordinates in degrees of latitude

106 * and longitude.

107 * @returns {Void}

108 */

109function flyToRectangle(coordinates) {

110    coordinates = coordinates.trim();

111    var coords = coordinates.split(',');

112    var north = parseInt(coords[0]);

113    var south = parseInt(coords[1]);

114    var east = parseInt(coords[2]);

115    var west = parseInt(coords[3]);

116    var rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);

117    

118    viewer.camera.flyTo({

119        destination: rectangle

120    });

121}

122

123/**

124 * Retrieves the boundary coordinates of a region or country/state and zooms to

125 * that rectangle. It should be fed either a country/state pair or a region ID.

126 *

127 * @param {String} country  The 2-letter country code

128 * @param {String} state    The 2-letter state abbreviation

129 * @param {Int} region      The region code

130 * @returns {Void}

131 */

132function zoomToArea(country=null, state=null, region=null) {

133    var url;

134    if (region === null) {

135        if (state === null || state === '') {

136            url = 'python/get-boundaries.py?country=' + country;

137        } else {

138            url = 'python/get-boundaries.py?country=' + country + '&state=' + state;

139        }

140    } else {

141        url = 'python/get-boundaries.py?region=' + region;

142    }

143    

144    $.ajax({

145        url: url,

146        success: function(result) {

147            flyToRectangle(result);

148        }

149    });

150}

151

152/**

153 * Loads data into the viewer based on what's provided in the JSON

154 *

155 * @param {String} url  The URL the JSON is located at

156 * @returns {Void}

157 */

158function loadDataFromJSON(url) {

159    queryString = '?' + url.split('?')[1];

160    var counter = 0;

161    var json = $.getJSON(url, function(result) {

162        counter = Object.keys(result).length;

163        $.each(result, function(id, obj) {

164            var kmlFile = documentRoot + 'php/' + id + '.kml' + queryString;

165            var datasource = new Cesium.KmlDataSource();

166            

167            if (counter == 1) {     // Fly to drive

168                viewer.dataSources.add(datasource.load(kmlFile)).then(function(data) {

169                    var drive = data.entities.values[0];

170                    viewer.flyTo(drive);

171                });

172            } else {    // Fly to region, country, or state

173                viewer.dataSources.add(datasource.load(kmlFile));

174            }

175        });

176    });

177}

178

179/**

180 * Loads the Scenic Drives into a Dropdown Menu

181 *

182 * @returns {Void}

183 */

184function loadDrives() {

185    var country = document.getElementById('countrySelect').value;

186    var state = document.getElementById('stateSelect').value;

187    if (state == 'States/Provinces Not Available') {

188        state = '';

189    }

190    

191    $.ajax({

192        url: 'python/drive_dropdown_menu.py?country=' + country + '&state=' + state,

193        success: function(result) {

194            result = $.trim(result);

195            if (result != 'Drives Not Available') {

196                $('#driveSelect').html(result);

197                $('#driveSelect').removeAttr('disabled');

198            } else {

199                $('#driveSelect').html('<option value="">No Scenic Drives Found in Database</option>');

200                $('#driveSelect').attr('disabled', true);

201            }

202        }

203    });

204}

205

206/**

207 * Loads the form that filters the data in the map

208 *

209 * @returns {Void}

210 */

211function loadFilterForm() {

212    $.ajax({

213        url: 'python/country_dropdown_menu.py' + window.location.search,

214        success: function(result) {

215            $('#form_filter').html(result);

216        }

217    });

218}

219

220/**

221 * Loads data for a given region from the "Select a Region" dropdown menu

222 *

223 * @returns {Void}

224 */

225function loadRegionDataFromForm() {

226    // change to loadRegionData()

227    var id = document.getElementById('regionSelect').value;

228    var documentRoot = 'http://' + window.location.host + window.location.pathname;

229    var queryString = '?region=' + id;

230    var url = documentRoot + 'python/json-from-mysql.py' + queryString;

231    var index = id - 1;

232    if (regionIsLoaded[index] === false) {

233        loadDataFromJSON(url);

234        regionIsLoaded[index] = true;

235    }

236    zoomToArea(null, null, parseInt(id));

237}

238

239/**

240 * Loads Region data into the dropdown menu

241 *

242 * @returns {Void}

243 */

244function loadRegionSelectForm(queryString) {

245    $.ajax({

246        url: 'python/region-form.py' + queryString,

247        success: function(result) {

248            $('#toolbar').html(result);

249        }

250    });

251}

252

253/**

254 * Loads the states into a dropdown menu

255 *

256 * @returns {Void}

257 */

258function loadState() {

259    var country = document.getElementById('countrySelect').value;

260    

261    $.ajax({

262        url: 'python/state_dropdown_menu.py?country=' + country,

263        success: function(result) {

264            result = $.trim(result);

265            if (result != 'States Not Available') {

266                $('#stateSelect').html(result);

267                $('#stateSelect').removeAttr('disabled');

268            } else {

269                $('#stateSelect').html('<option value="">States/Provinces Not Available</option>');

270                $('#stateSelect').attr('disabled', true);

271            }

272            loadDrives();

273        }

274    });

275}

276

277var regionIsLoaded = [

278    false,  // US Northeast/Mid-Atlantic

279    false,  // US Southeast

280    false,  // US Great Lakes

281    false,  // US Great Plains

282    false,  // US Northwest

283    false,  // US Southwest

284    false,  // Alaska and Hawaii

285    false,  // Canada

286    false,  // Mexico/Central America/Caribbean

287    false,  // South America

288    false,  // Europe

289    false,  // Arabia/Northern Africa

290    false,  // Central/Southern Africa

291    false,  // Central Asia

292    false,  // Far East

293    false,  // South Pacific/Oceania

294];

295

296// For North America. Leave as is and do a fly to for regions that are not North America.

297var west = -140.0;

298var south = 10.0;

299var east = -50.0;

300var north = 65.0;

301

302var rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);

303Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;

304Cesium.Camera.DEFAULT_VIEW_RECTANGLE = rectangle;

305Cesium.BingMapsApi.defaultKey = 'this will make the message go away';

306

307var imageryProviders = Cesium.createDefaultImageryProviderViewModels();

308var selectedImageryProviderIndex = 7; //4;

309

310var viewer = new Cesium.Viewer('cesiumContainer', {

311    animation: false,

312    timeline: false,

313    geocoder: false,

314    homeButton: false,

315    sceneMode: Cesium.SceneMode.SCENE2D,

316    mapProjection: new Cesium.WebMercatorProjection(),

317    imageryProviderViewModels: imageryProviders,

318    selectedImageryProviderViewModel: imageryProviders[selectedImageryProviderIndex]

319});

320

321var documentRoot = 'http://' + window.location.host + window.location.pathname;

322var queryString = getQueryString();

323var url = documentRoot + 'python/json-from-mysql.py' + queryString;

324var queryStringToPassToPython = queryString;

325loadDataFromJSON(url);

326

327$(document).ready(function() {

328    if (window.location.search === '') {

329        displayDisclaimer();

330    }

331    

332    var queryStringObj = parseQueryString();

333    

334    if ('region' in queryStringObj && Object.keys(queryStringObj).length == 1) {    // region only

335        region = queryStringObj.region;

336        regionIsLoaded[region - 1] = true;

337        zoomToArea(null, null, region);

338    } else if ('country' in queryStringObj) {   // country/state

339        country = queryStringObj.country;

340        if ('state' in queryStringObj) {

341            state = queryStringObj.state;

342        } else {

343            state = '';

344        }

345        zoomToArea(country, state, null);

346    }

347    

348    // Load Region Select and Filter Forms

349    loadRegionSelectForm(queryStringToPassToPython);

350    loadFilterForm();

351    

352    $('body').on('click', '#close_modal', closeFilterForm);

353    $(document).on('submit', '#filterForm', submitFilterForm);

354});