blob: 4db612849dbeb5257cc0817e4a203f2b025f9c75 [file] [log] [blame]
Ian Maxon305858c2015-10-01 19:00:14 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Zachary Heilbrond98423c2014-03-06 17:37:28 -080019$(function() {
20
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070021 // Initialize connection to AsterixDB. Just one connection is needed and contains
22 // logic for connecting to each API endpoint. This object A is reused throughout the
Zachary Heilbrond98423c2014-03-06 17:37:28 -080023 // code but does not store information about any individual API call.
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070024 A = new AsterixDBConnection({
Zachary Heilbrond98423c2014-03-06 17:37:28 -080025
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070026 // We will be using the twitter dataverse, which we can configure either like this
27 // or by following our AsterixDBConnection with a dataverse call, like so:
28 // A = new AsterixDBConnection().dataverse("twitter");
29 "dataverse" : "twitter",
Zachary Heilbrond98423c2014-03-06 17:37:28 -080030
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070031 // Due to the setup of this demo using the Bottle server, it is necessary to change the
32 // default endpoint of API calls. The proxy server handles the call to http://localhost:19002
33 // for us, and we reconfigure this connection to connect to the proxy server.
34 "endpoint_root" : "/",
Zachary Heilbrond98423c2014-03-06 17:37:28 -080035
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070036 // Finally, we want to make our error function nicer so that we show errors with a call to the
37 // reportUserMessage function. Update the "error" property to do that.
38 "error" : function(data) {
39 // For an error, data will look like this:
40 // {
41 // "error-code" : [error-number, error-text]
42 // "stacktrace" : ...stack trace...
43 // "summary" : ...summary of error...
44 // }
45 // We will report this as an Asterix REST API Error, an error code, and a reason message.
46 // Note the method signature: reportUserMessage(message, isPositiveMessage, target). We will provide
Zachary Heilbrond98423c2014-03-06 17:37:28 -080047 // an error message to display, a positivity value (false in this case, errors are bad), and a
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070048 // target html element in which to report the message.
49 var showErrorMessage = "Asterix Error #" + data["error-code"][0] + ": " + data["error-code"][1];
50 var isPositive = false;
51 var showReportAt = "report-message";
Zachary Heilbrond98423c2014-03-06 17:37:28 -080052
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -070053 reportUserMessage(showErrorMessage, isPositive, showReportAt);
54 }
55 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -080056
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -070057 // This little bit of code manages period checks of the asynchronous query manager,
58 // which holds onto handles asynchornously received. We can set the handle update
59 // frequency using seconds, and it will let us know when it is ready.
Zachary Heilbrond98423c2014-03-06 17:37:28 -080060 var intervalID = setInterval(
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -070061 function() {
62 asynchronousQueryIntervalUpdate();
Zachary Heilbrond98423c2014-03-06 17:37:28 -080063 },
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -070064 asynchronousQueryGetInterval()
65 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -080066
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -070067 // Legend Container
Zachary Heilbrond98423c2014-03-06 17:37:28 -080068 // Create a rainbow from a pretty color scheme.
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -070069 // http://www.colourlovers.com/palette/292482/Terra
70 rainbow = new Rainbow();
71 rainbow.setSpectrum("#E8DDCB", "#CDB380", "#036564", "#033649", "#031634");
72 buildLegend();
Zachary Heilbrond98423c2014-03-06 17:37:28 -080073
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -070074 // Initialization of UI Tabas
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -070075 initDemoPrepareTabs();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070076
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -070077 // UI Elements - Creates Map, Location Auto-Complete, Selection Rectangle
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070078 var mapOptions = {
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -070079 center: new google.maps.LatLng(38.89, -77.03),
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070080 zoom: 4,
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -070081 mapTypeId: google.maps.MapTypeId.ROADMAP,
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070082 streetViewControl: false,
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -070083 draggable : false,
84 mapTypeControl: false
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070085 };
86 map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
87
88 var input = document.getElementById('location-text-box');
89 var autocomplete = new google.maps.places.Autocomplete(input);
90 autocomplete.bindTo('bounds', map);
Zachary Heilbrond98423c2014-03-06 17:37:28 -080091
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -070092 google.maps.event.addListener(autocomplete, 'place_changed', function() {
93 var place = autocomplete.getPlace();
94 if (place.geometry.viewport) {
95 map.fitBounds(place.geometry.viewport);
96 } else {
97 map.setCenter(place.geometry.location);
98 map.setZoom(17); // Why 17? Because it looks good.
99 }
100 var address = '';
101 if (place.address_components) {
102 address = [(place.address_components[0] && place.address_components[0].short_name || ''),
103 (place.address_components[1] && place.address_components[1].short_name || ''),
104 (place.address_components[2] && place.address_components[2].short_name || '') ].join(' ');
105 }
106 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800107
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700108 // Drawing Manager for selecting map regions. See documentation here:
109 // https://developers.google.com/maps/documentation/javascript/reference#DrawingManager
110 rectangleManager = new google.maps.drawing.DrawingManager({
111 drawingMode : google.maps.drawing.OverlayType.RECTANGLE,
112 drawingControl : false,
113 rectangleOptions : {
114 strokeWeight: 1,
115 clickable: false,
116 editable: true,
Zachary Heilbron1eab8c92014-03-10 13:57:00 -0700117 strokeColor: "#2b3f8c",
118 fillColor: "#2b3f8c",
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700119 zIndex: 1
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700120 }
121 });
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700122 rectangleManager.setMap(map);
123 selectionRectangle = null;
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800124
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700125 // Drawing Manager: Just one editable rectangle!
126 google.maps.event.addListener(rectangleManager, 'rectanglecomplete', function(rectangle) {
127 selectionRectangle = rectangle;
128 rectangleManager.setDrawingMode(null);
129 });
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700130
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700131 // Initialize data structures
132 APIqueryTracker = {};
133 asyncQueryManager = {};
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700134 map_cells = [];
135 map_tweet_markers = [];
136 map_info_windows = {};
137 review_mode_tweetbooks = [];
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700138
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700139 getAllDataverseTweetbooks();
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700140 initDemoUIButtonControls();
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800141
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700142 google.maps.event.addListenerOnce(map, 'idle', function(){
143 // Show tutorial tab only the first time the map is loaded
144 $('#mode-tabs a:first').tab('show');
145 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800146
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700147});
148
149function initDemoUIButtonControls() {
150
151 // Explore Mode - Update Sliders
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700152 var updateSliderDisplay = function(event, ui) {
153 if (event.target.id == "grid-lat-slider") {
154 $("#gridlat").text(""+ui.value);
155 } else {
156 $("#gridlng").text(""+ui.value);
157 }
158 };
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700159 sliderOptions = {
160 max: 10,
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700161 min: 2.0,
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700162 step: .1,
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700163 value: 3.0,
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700164 slidechange: updateSliderDisplay,
165 slide: updateSliderDisplay,
166 start: updateSliderDisplay,
167 stop: updateSliderDisplay
168 };
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700169 $("#gridlat").text(""+sliderOptions.value);
170 $("#gridlng").text(""+sliderOptions.value);
171 $(".grid-slider").slider(sliderOptions);
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700172
173 // Explore Mode - Query Builder Date Pickers
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700174 var dateOptions = {
175 dateFormat: "yy-mm-dd",
176 defaultDate: "2012-01-02",
177 navigationAsDateFormat: true,
178 constrainInput: true
179 };
180 var start_dp = $("#start-date").datepicker(dateOptions);
181 start_dp.val(dateOptions.defaultDate);
182 dateOptions['defaultDate'] = "2012-12-31";
183 var end_dp= $("#end-date").datepicker(dateOptions);
184 end_dp.val(dateOptions.defaultDate);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800185
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700186 // Explore Mode: Toggle Selection/Location Search
Eugenia Gabrielova3d7bfe52013-10-28 02:36:44 -0700187 $('#selection-button').on('change', function (e) {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700188 $("#location-text-box").attr("disabled", "disabled");
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700189 rectangleManager.setMap(map);
190 if (selectionRectangle) {
191 selectionRectangle.setMap(null);
192 selectionRectangle = null;
193 } else {
194 rectangleManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700195 }
196 });
Eugenia Gabrielova3d7bfe52013-10-28 02:36:44 -0700197 $('#location-button').on('change', function (e) {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700198 $("#location-text-box").removeAttr("disabled");
Zachary Heilbronf3e45752014-03-10 14:47:55 -0700199 selectionRectangle.setMap(null);
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700200 rectangleManager.setMap(null);
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700201 rectangleManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700202 });
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700203 $("#selection-button").trigger("click");
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700204
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700205 // Review Mode: New Tweetbook
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700206 $('#new-tweetbook-button').on('click', function (e) {
207 onCreateNewTweetBook($('#new-tweetbook-entry').val());
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800208
genia.likes.science@gmail.com2de4c182013-10-04 11:39:11 -0700209 $('#new-tweetbook-entry').val("");
210 $('#new-tweetbook-entry').attr("placeholder", "Name a new tweetbook");
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700211 });
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700212
213 // Explore Mode - Clear Button
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800214 $("#clear-button").click(mapWidgetResetMap);
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700215
216 // Explore Mode: Query Submission
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700217 $("#submit-button").on("click", function () {
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700218
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700219 $("#report-message").html('');
220 $("#submit-button").attr("disabled", true);
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700221 rectangleManager.setDrawingMode(null);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800222
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700223 var kwterm = $("#keyword-textbox").val();
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700224 var startdp = $("#start-date").datepicker("getDate");
225 var enddp = $("#end-date").datepicker("getDate");
226 var startdt = $.datepicker.formatDate("yy-mm-dd", startdp)+"T00:00:00Z";
227 var enddt = $.datepicker.formatDate("yy-mm-dd", enddp)+"T23:59:59Z";
228
229 var formData = {
230 "keyword": kwterm,
231 "startdt": startdt,
232 "enddt": enddt,
233 "gridlat": $("#grid-lat-slider").slider("value"),
234 "gridlng": $("#grid-lng-slider").slider("value")
235 };
236
237 // Get Map Bounds
238 var bounds;
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700239 if ($('#selection-label').hasClass("active") && selectionRectangle) {
240 bounds = selectionRectangle.getBounds();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700241 } else {
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700242 bounds = map.getBounds();
243 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800244
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700245 var swLat = Math.abs(bounds.getSouthWest().lat());
246 var swLng = Math.abs(bounds.getSouthWest().lng());
247 var neLat = Math.abs(bounds.getNorthEast().lat());
248 var neLng = Math.abs(bounds.getNorthEast().lng());
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800249
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700250 formData["swLat"] = Math.min(swLat, neLat);
251 formData["swLng"] = Math.max(swLng, neLng);
252 formData["neLat"] = Math.max(swLat, neLat);
253 formData["neLng"] = Math.min(swLng, neLng);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700254
zheilbronbebb2202014-03-28 14:36:52 -0700255 var build_tweetbook_mode = "synchronous";
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700256 if ($('#asbox').is(":checked")) {
zheilbronbebb2202014-03-28 14:36:52 -0700257 build_tweetbook_mode = "asynchronous";
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700258 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800259
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700260 var f = buildAQLQueryFromForm(formData);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800261
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700262 APIqueryTracker = {
263 "query" : "use dataverse twitter;\n" + f.val(),
264 "data" : formData
265 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800266
zheilbronbebb2202014-03-28 14:36:52 -0700267 if (build_tweetbook_mode == "synchronous") {
268 A.query(f.val(), tweetbookQuerySyncCallback, build_tweetbook_mode);
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700269 } else {
zheilbronbebb2202014-03-28 14:36:52 -0700270 A.query(f.val(), tweetbookQueryAsyncCallback, build_tweetbook_mode);
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700271 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800272
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700273 // Clears selection rectangle on query execution, rather than waiting for another clear call.
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -0700274 if (selectionRectangle) {
275 selectionRectangle.setMap(null);
276 selectionRectangle = null;
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700277 }
278 });
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700279}
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700280
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -0700281/**
282* Builds AsterixDB REST Query from explore mode form.
283*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700284function buildAQLQueryFromForm(parameters) {
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -0700285
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700286 var bounds = {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800287 "ne" : { "lat" : parameters["neLat"], "lng" : -1*parameters["neLng"]},
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -0700288 "sw" : { "lat" : parameters["swLat"], "lng" : -1*parameters["swLng"]}
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700289 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800290
291 var rectangle =
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700292 new FunctionExpression("create-rectangle",
293 new FunctionExpression("create-point", bounds["sw"]["lat"], bounds["sw"]["lng"]),
294 new FunctionExpression("create-point", bounds["ne"]["lat"], bounds["ne"]["lng"]));
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800295
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700296 // You can chain these all together, but let's take them one at a time.
297 // Let's start with a ForClause. Here we go through each tweet $t in the
298 // dataset TweetMessageShifted.
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700299 var aql = new FLWOGRExpression()
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700300 .ForClause("$t", new AExpression("dataset TweetMessagesShifted"));
301
302 // We know we have bounds for our region, so we can add that LetClause next.
303 aql = aql.LetClause("$region", rectangle);
304
305 // Now, let's change it up. The keyword term doesn't always show up, so it might be blank.
306 // We'll attach a new let clause for it, and then a WhereClause.
307 if (parameters["keyword"].length > 0) {
308 aql = aql
309 .LetClause("$keyword", new AExpression('"' + parameters["keyword"] + '"'))
310 .WhereClause().and(
311 new FunctionExpression("spatial-intersect", "$t.sender-location", "$region"),
312 new AExpression('$t.send-time > datetime("' + parameters["startdt"] + '")'),
313 new AExpression('$t.send-time < datetime("' + parameters["enddt"] + '")'),
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800314 new FunctionExpression("contains", "$t.message-text", "$keyword")
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700315 );
316 } else {
317 aql = aql
318 .WhereClause().and(
319 new FunctionExpression("spatial-intersect", "$t.sender-location", "$region"),
320 new AExpression('$t.send-time > datetime("' + parameters["startdt"] + '")'),
321 new AExpression('$t.send-time < datetime("' + parameters["enddt"] + '")')
322 );
323 }
324
325 // Finally, we'll group our results into spatial cells.
326 aql = aql.GroupClause(
327 "$c",
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800328 new FunctionExpression("spatial-cell", "$t.sender-location",
329 new FunctionExpression("create-point", "24.5", "-125.5"),
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700330 parameters["gridlat"].toFixed(1), parameters["gridlng"].toFixed(1)),
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800331 "with",
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700332 "$t"
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700333 );
334
335 // ...and return a resulting cell and a count of results in that cell.
336 aql = aql.ReturnClause({ "cell" : "$c", "count" : "count($t)" });
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700337
338 return aql;
339}
340
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700341/**
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700342* getAllDataverseTweetbooks
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800343*
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700344* Returns all datasets of type TweetbookEntry, populates review_mode_tweetbooks
345*/
346function getAllDataverseTweetbooks(fn_tweetbooks) {
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700347
348 // This creates a query to the Metadata for datasets of type
349 // TweetBookEntry. Note that if we throw in a WhereClause (commented out below)
350 // there is an odd error. This is being fixed and will be removed from this demo.
351 var getTweetbooksQuery = new FLWOGRExpression()
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700352 .ForClause("$ds", new AExpression("dataset Metadata.Dataset"))
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700353 //.WhereClause(new AExpression('$ds.DataTypeName = "TweetbookEntry"'))
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700354 .ReturnClause({
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700355 "DataTypeName" : "$ds.DataTypeName",
356 "DatasetName" : "$ds.DatasetName"
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700357 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800358
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700359 // Now create a function that will be called when tweetbooks succeed.
360 // In this case, we want to parse out the results object from the Asterix
361 // REST API response.
362 var tweetbooksSuccess = function(r) {
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700363 // Parse tweetbook metadata results
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700364 $.each(r.results, function(i, data) {
365 if ($.parseJSON(data)["DataTypeName"] == "TweetbookEntry") {
366 review_mode_tweetbooks.push($.parseJSON(data)["DatasetName"]);
367 }
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700368 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800369
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700370 // Now, if any tweetbooks already exist, opulate review screen.
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700371 $('#review-tweetbook-titles').html('');
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700372 $.each(review_mode_tweetbooks, function(i, tweetbook) {
373 addTweetBookDropdownItem(tweetbook);
374 });
375 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800376
377 // Now, we are ready to run a query.
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700378 A.meta(getTweetbooksQuery.val(), tweetbooksSuccess);
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700379}
380
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700381/**
382* Checks through each asynchronous query to see if they are ready yet
383*/
384function asynchronousQueryIntervalUpdate() {
385 for (var handle_key in asyncQueryManager) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800386 if (!asyncQueryManager[handle_key].hasOwnProperty("ready")) {
387 asynchronousQueryGetAPIQueryStatus( asyncQueryManager[handle_key]["handle"], handle_key );
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700388 }
389 }
390}
391
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700392/**
393* Returns current time interval to check for asynchronous query readiness
394* @returns {number} milliseconds between asychronous query checks
395*/
396function asynchronousQueryGetInterval() {
397 var seconds = 10;
398 return seconds * 1000;
399}
400
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700401/**
402* Retrieves status of an asynchronous query, using an opaque result handle from API
403* @param {Object} handle, an object previously returned from an async call
404* @param {number} handle_id, the integer ID parsed from the handle object
405*/
406function asynchronousQueryGetAPIQueryStatus (handle, handle_id) {
407
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800408 A.query_status(
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700409 {
410 "handle" : JSON.stringify(handle)
411 },
412 function (res) {
413 if (res["status"] == "SUCCESS") {
414 // We don't need to check if this one is ready again, it's not going anywhere...
415 // Unless the life cycle of handles has changed drastically
416 asyncQueryManager[handle_id]["ready"] = true;
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800417
418 // Indicate success.
genia.likes.science@gmail.com4259a542013-08-18 20:06:58 -0700419 $('#handle_' + handle_id).removeClass("btn-disabled").prop('disabled', false).addClass("btn-success");
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700420 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800421 }
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700422 );
423}
424
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700425/**
426* On-success callback after async API query
427* @param {object} res, a result object containing an opaque result handle to Asterix
428*/
zheilbronbebb2202014-03-28 14:36:52 -0700429function tweetbookQueryAsyncCallback(res) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800430
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700431 // Parse handle, handle id and query from async call result
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700432 var handle_query = APIqueryTracker["query"];
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700433 var handle = res;
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800434 var handle_id = res["handle"].toString().split(',')[0];
435
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700436 // Add to stored map of existing handles
437 asyncQueryManager[handle_id] = {
438 "handle" : handle,
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700439 "query" : handle_query, // This will show up when query control button is clicked.
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700440 "data" : APIqueryTracker["data"]
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700441 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800442
443 // Create a container for this async query handle
genia.likes.science@gmail.com4259a542013-08-18 20:06:58 -0700444 $('<div/>')
445 .css("margin-left", "1em")
446 .css("margin-bottom", "1em")
447 .css("display", "block")
448 .attr({
449 "class" : "btn-group",
450 "id" : "async_container_" + handle_id
451 })
452 .appendTo("#async-handle-controls");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800453
genia.likes.science@gmail.com4259a542013-08-18 20:06:58 -0700454 // Adds the main button for this async handle
455 var handle_action_button = '<button class="btn btn-disabled" id="handle_' + handle_id + '">Handle ' + handle_id + '</button>';
456 $('#async_container_' + handle_id).append(handle_action_button);
457 $('#handle_' + handle_id).prop('disabled', true);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700458 $('#handle_' + handle_id).on('click', function (e) {
genia.likes.science@gmail.com4259a542013-08-18 20:06:58 -0700459
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700460 // make sure query is ready to be run
461 if (asyncQueryManager[handle_id]["ready"]) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800462
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700463 APIqueryTracker = {
464 "query" : asyncQueryManager[handle_id]["query"],
465 "data" : asyncQueryManager[handle_id]["data"]
466 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800467
genia.likes.science@gmail.com84711b82013-08-22 03:47:41 -0700468 if (!asyncQueryManager[handle_id].hasOwnProperty("result")) {
469 // Generate new Asterix Core API Query
470 A.query_result(
471 { "handle" : JSON.stringify(asyncQueryManager[handle_id]["handle"]) },
472 function(res) {
473 asyncQueryManager[handle_id]["result"] = res;
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800474
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700475 var resultTransform = {
Zachary Heilbron87251352014-03-10 14:41:17 -0700476 "results" : res.results
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700477 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800478
zheilbronbebb2202014-03-28 14:36:52 -0700479 tweetbookQuerySyncCallback(resultTransform);
genia.likes.science@gmail.com84711b82013-08-22 03:47:41 -0700480 }
481 );
482 } else {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800483
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700484 var resultTransform = {
Zachary Heilbron87251352014-03-10 14:41:17 -0700485 "results" : asyncQueryManager[handle_id]["result"].results
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700486 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800487
zheilbronbebb2202014-03-28 14:36:52 -0700488 tweetbookQuerySyncCallback(resultTransform);
genia.likes.science@gmail.com84711b82013-08-22 03:47:41 -0700489 }
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700490 }
491 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800492
genia.likes.science@gmail.com4259a542013-08-18 20:06:58 -0700493 // Adds a removal button for this async handle
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700494 var asyncDeleteButton = addDeleteButton(
495 "trashhandle_" + handle_id,
496 "async_container_" + handle_id,
497 function (e) {
498 $('#async_container_' + handle_id).remove();
499 delete asyncQueryManager[handle_id];
500 }
501 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800502
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700503 $('#async_container_' + handle_id).append('<br/>');
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800504
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700505 $("#submit-button").attr("disabled", false);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700506}
507
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700508/**
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700509* A spatial data cleaning and mapping call
zheilbronbebb2202014-03-28 14:36:52 -0700510* @param {Object} res, a result object from a tweetbook geospatial query
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700511*/
zheilbronbebb2202014-03-28 14:36:52 -0700512function tweetbookQuerySyncCallback(res) {
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700513 // First, we check if any results came back in.
514 // If they didn't, return.
515 if (!res.hasOwnProperty("results")) {
516 reportUserMessage("Oops, no results found for those parameters.", false, "report-message");
517 return;
518 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800519
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700520 // Initialize coordinates and weights, to store
521 // coordinates of map cells and their weights
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700522 var coordinates = [];
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700523 var maxWeight = 0;
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700524 var minWeight = Number.MAX_VALUE;
Eugenia Gabrielovaf9fcd712013-10-20 02:37:35 -0700525
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700526 // Parse resulting JSON objects. Here is an example record:
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700527 // { "cell": rectangle("21.5,-98.5 24.5,-95.5"), "count": 78i64 }
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700528 $.each(res.results, function(i, data) {
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700529
530 // We need to clean the JSON a bit to parse it properly in javascript
531 var cleanRecord = $.parseJSON(data
532 .replace('rectangle(', '')
533 .replace(')', '')
534 .replace('i64', ''));
535
536 var recordCount = cleanRecord["count"];
537 var rectangle = cleanRecord["cell"]
538 .replace(' ', ',')
539 .split(',')
540 .map( parseFloat );
541
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800542 // Now, using the record count and coordinates, we can create a
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700543 // coordinate system for this spatial cell.
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700544 var coordinate = {
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700545 "latSW" : rectangle[0],
546 "lngSW" : rectangle[1],
547 "latNE" : rectangle[2],
548 "lngNE" : rectangle[3],
549 "weight" : recordCount
550 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800551
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700552 // We track the minimum and maximum weight to support our legend.
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700553 maxWeight = Math.max(coordinate["weight"], maxWeight);
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700554 minWeight = Math.min(coordinate["weight"], minWeight);
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700555
556 // Save completed coordinate and move to next one.
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700557 coordinates.push(coordinate);
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700558 });
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700559
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700560 triggerUIUpdate(coordinates, maxWeight, minWeight);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700561}
562
563/**
564* Triggers a map update based on a set of spatial query result cells
565* @param [Array] mapPlotData, an array of coordinate and weight objects
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700566* @param [Array] plotWeights, a list of weights of the spatial cells - e.g., number of tweets
567*/
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700568function triggerUIUpdate(mapPlotData, maxWeight, minWeight) {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700569 /** Clear anything currently on the map **/
570 mapWidgetClearMap();
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800571
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700572 // Initialize info windows.
genia.likes.science@gmail.com65e04182013-10-04 04:31:41 -0700573 map_info_windows = {};
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800574
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700575 $.each(mapPlotData, function (m) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800576
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700577 var point_center = new google.maps.LatLng(
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800578 (mapPlotData[m].latSW + mapPlotData[m].latNE)/2.0,
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700579 (mapPlotData[m].lngSW + mapPlotData[m].lngNE)/2.0);
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -0700580
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700581 var map_circle_options = {
582 center: point_center,
583 anchorPoint: point_center,
584 radius: mapWidgetComputeCircleRadius(mapPlotData[m], maxWeight),
585 map: map,
586 fillOpacity: 0.85,
Zachary Heilbron046724c2014-03-10 13:42:23 -0700587 fillColor: "#" + rainbow.colourAt(Math.ceil(100 * (mapPlotData[m].weight / maxWeight))),
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700588 clickable: true
589 };
590 var map_circle = new google.maps.Circle(map_circle_options);
591 map_circle.val = mapPlotData[m];
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800592
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700593 map_info_windows[m] = new google.maps.InfoWindow({
594 content: mapPlotData[m].weight + " tweets",
595 position: point_center
596 });
597
598 // Clicking on a circle drills down map to that value, hovering over it displays a count
599 // of tweets at that location.
600 google.maps.event.addListener(map_circle, 'click', function (event) {
601 $.each(map_info_windows, function(i) {
602 map_info_windows[i].close();
genia.likes.science@gmail.comec46c772013-09-07 18:13:00 -0700603 });
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700604 onMapPointDrillDown(map_circle.val);
605 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800606
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700607 google.maps.event.addListener(map_circle, 'mouseover', function(event) {
608 if (!map_info_windows[m].getMap()) {
609 map_info_windows[m].setPosition(map_circle.center);
610 map_info_windows[m].open(map);
611 }
612 });
Zachary Heilbron566ba062014-03-11 13:22:54 -0700613 google.maps.event.addListener(map, 'mousemove', function(event) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800614 map_info_windows[m].close();
Zachary Heilbron046724c2014-03-10 13:42:23 -0700615
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800616 });
617
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -0700618 // Add this marker to global marker cells
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700619 map_cells.push(map_circle);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800620
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -0700621 // Show legend
622 $("#legend-min").html(minWeight);
623 $("#legend-max").html(maxWeight);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800624 $("#rainbow-legend-container").show();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700625 });
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700626}
627
628/**
629* prepares an Asterix API query to drill down in a rectangular spatial zone
630*
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700631* @params {object} marker_borders a set of bounds for a region from a previous api result
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700632*/
633function onMapPointDrillDown(marker_borders) {
Eugenia Gabrielovaf29a55f2013-10-28 03:18:18 -0700634
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700635 var zoneData = APIqueryTracker["data"];
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800636
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700637 var zswBounds = new google.maps.LatLng(marker_borders.latSW, marker_borders.lngSW);
638 var zneBounds = new google.maps.LatLng(marker_borders.latNE, marker_borders.lngNE);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800639
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700640 var zoneBounds = new google.maps.LatLngBounds(zswBounds, zneBounds);
641 zoneData["swLat"] = zoneBounds.getSouthWest().lat();
642 zoneData["swLng"] = zoneBounds.getSouthWest().lng();
643 zoneData["neLat"] = zoneBounds.getNorthEast().lat();
644 zoneData["neLng"] = zoneBounds.getNorthEast().lng();
645 var zB = {
646 "sw" : {
647 "lat" : zoneBounds.getSouthWest().lat(),
648 "lng" : zoneBounds.getSouthWest().lng()
649 },
650 "ne" : {
651 "lat" : zoneBounds.getNorthEast().lat(),
652 "lng" : zoneBounds.getNorthEast().lng()
653 }
654 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800655
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700656 mapWidgetClearMap();
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800657
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700658 var customBounds = new google.maps.LatLngBounds();
659 var zoomSWBounds = new google.maps.LatLng(zoneData["swLat"], zoneData["swLng"]);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800660 var zoomNEBounds = new google.maps.LatLng(zoneData["neLat"], zoneData["neLng"]);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700661 customBounds.extend(zoomSWBounds);
662 customBounds.extend(zoomNEBounds);
663 map.fitBounds(customBounds);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800664
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700665 var df = getDrillDownQuery(zoneData, zB);
666
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700667 APIqueryTracker = {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700668 "query_string" : "use dataverse twitter;\n" + df.val(),
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700669 "marker_path" : "static/img/mobile2.png"
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700670 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800671
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700672 A.query(df.val(), onTweetbookQuerySuccessPlot);
673}
674
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700675/**
676* Generates an aql query for zooming on a spatial cell and obtaining tweets contained therein.
677* @param parameters, the original query parameters
678* @param bounds, the bounds of the zone to zoom in on.
679*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700680function getDrillDownQuery(parameters, bounds) {
681
682 var zoomRectangle = new FunctionExpression("create-rectangle",
683 new FunctionExpression("create-point", bounds["sw"]["lat"], bounds["sw"]["lng"]),
684 new FunctionExpression("create-point", bounds["ne"]["lat"], bounds["ne"]["lng"]));
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800685
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700686 var drillDown = new FLWOGRExpression()
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -0700687 .ForClause("$t", new AExpression("dataset TweetMessagesShifted"))
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700688 .LetClause("$region", zoomRectangle);
689
690 if (parameters["keyword"].length == 0) {
691 drillDown = drillDown
692 .WhereClause().and(
693 new FunctionExpression('spatial-intersect', '$t.sender-location', '$region'),
694 new AExpression().set('$t.send-time > datetime("' + parameters["startdt"] + '")'),
695 new AExpression().set('$t.send-time < datetime("' + parameters["enddt"] + '")')
696 );
697 } else {
698 drillDown = drillDown
699 .LetClause("$keyword", new AExpression('"' + parameters["keyword"] + '"'))
700 .WhereClause().and(
701 new FunctionExpression('spatial-intersect', '$t.sender-location', '$region'),
702 new AExpression().set('$t.send-time > datetime("' + parameters["startdt"] + '")'),
703 new AExpression().set('$t.send-time < datetime("' + parameters["enddt"] + '")'),
704 new FunctionExpression('contains', '$t.message-text', '$keyword')
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800705 );
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700706 }
707
708 drillDown = drillDown
709 .ReturnClause({
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800710 "tweetId" : "$t.tweetid",
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700711 "tweetText" : "$t.message-text",
712 "tweetLoc" : "$t.sender-location"
713 });
714
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700715 return drillDown;
716}
717
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700718/**
719* Given a location where a tweet exists, opens a modal to examine or update a tweet's content.
720* @param t0, a tweetobject that has a location, text, id, and optionally a comment.
721*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700722function onDrillDownAtLocation(tO) {
723
724 var tweetId = tO["tweetEntryId"];
725 var tweetText = tO["tweetText"];
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800726
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700727 // First, set tweet in drilldown modal to be this tweet's text
728 $('#modal-body-tweet').html('Tweet #' + tweetId + ": " + tweetText);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800729
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700730 // Next, empty any leftover tweetbook comments or error/success messages
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700731 $("#modal-body-add-to").val('');
732 $("#modal-body-add-note").val('');
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700733 $("#modal-body-message-holder").html("");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800734
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700735 // Next, if there is an existing tweetcomment reported, show it.
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700736 if (tO.hasOwnProperty("tweetComment")) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800737
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700738 // Show correct panel
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700739 $("#modal-existing-note").show();
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700740 $("#modal-save-tweet-panel").hide();
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800741
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700742 // Fill in existing tweet comment
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700743 $("#modal-body-tweet-note").val(tO["tweetComment"]);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800744
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700745 // Change Tweetbook Badge
746 $("#modal-current-tweetbook").val(APIqueryTracker["active_tweetbook"]);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800747
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700748 // Add deletion functionality
749 $("#modal-body-trash-icon").on('click', function () {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800750 // Send comment deletion to asterix
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700751 var deleteTweetCommentOnId = '"' + tweetId + '"';
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700752 var toDelete = new DeleteStatement(
753 "$mt",
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700754 APIqueryTracker["active_tweetbook"],
755 new AExpression("$mt.tweetid = " + deleteTweetCommentOnId.toString())
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700756 );
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700757 A.update(
758 toDelete.val()
759 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800760
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700761 // Hide comment from map
762 $('#drilldown_modal').modal('hide');
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800763
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700764 // Replot tweetbook
765 onPlotTweetbook(APIqueryTracker["active_tweetbook"]);
766 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800767
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700768 } else {
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700769 // Show correct panel
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700770 $("#modal-existing-note").hide();
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700771 $("#modal-save-tweet-panel").show();
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800772
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700773 // Now, when adding a comment on an available tweet to a tweetbook
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700774 $('#save-comment-tweetbook-modal').unbind('click');
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700775 $("#save-comment-tweetbook-modal").on('click', function(e) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800776
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700777 // Stuff to save about new comment
778 var save_metacomment_target_tweetbook = $("#modal-body-add-to").val();
779 var save_metacomment_target_comment = '"' + $("#modal-body-add-note").val() + '"';
780 var save_metacomment_target_tweet = '"' + tweetId + '"';
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800781
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700782 // Make sure content is entered, and then save this comment.
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700783 if ($("#modal-body-add-note").val() == "") {
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700784
785 reportUserMessage("Please enter a comment about the tweet", false, "report-message");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800786
Eugenia Gabrielova12de0302013-10-18 02:25:39 -0700787 } else if ($("#modal-body-add-to").val() == "") {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800788
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700789 reportUserMessage("Please enter a tweetbook.", false, "report-message");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800790
genia.likes.science@gmail.com0f67d9e2013-10-04 17:08:50 -0700791 } else {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800792
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700793 // Check if tweetbook exists. If not, create it.
794 if (!(existsTweetbook(save_metacomment_target_tweetbook))) {
795 onCreateNewTweetBook(save_metacomment_target_tweetbook);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700796 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800797
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700798 var toInsert = new InsertStatement(
799 save_metacomment_target_tweetbook,
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800800 {
801 "tweetid" : save_metacomment_target_tweet.toString(),
802 "comment-text" : save_metacomment_target_comment
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700803 }
804 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800805
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700806 A.update(toInsert.val(), function () {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800807 var successMessage = "Saved comment on <b>Tweet #" + tweetId +
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700808 "</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
809 reportUserMessage(successMessage, true, "report-message");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800810
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700811 $("#modal-body-add-to").val('');
812 $("#modal-body-add-note").val('');
813 $('#save-comment-tweetbook-modal').unbind('click');
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800814
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -0700815 // Close modal
816 $('#drilldown_modal').modal('hide');
817 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800818 }
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -0700819 });
820 }
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700821}
822
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700823/**
824* Adds a new tweetbook entry to the menu and creates a dataset of type TweetbookEntry.
825*/
826function onCreateNewTweetBook(tweetbook_title) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800827
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700828 var tweetbook_title = tweetbook_title.split(' ').join('_');
829
830 A.ddl(
831 "create dataset " + tweetbook_title + "(TweetbookEntry) primary key tweetid;",
832 function () {}
833 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800834
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700835 if (!(existsTweetbook(tweetbook_title))) {
836 review_mode_tweetbooks.push(tweetbook_title);
837 addTweetBookDropdownItem(tweetbook_title);
838 }
839}
840
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700841/**
842* Removes a tweetbook from both demo and from
843* dataverse metadata.
844*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700845function onDropTweetBook(tweetbook_title) {
846
847 // AQL Call
848 A.ddl(
849 "drop dataset " + tweetbook_title + " if exists;",
850 function () {}
851 );
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800852
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700853 // Removes tweetbook from review_mode_tweetbooks
854 var remove_position = $.inArray(tweetbook_title, review_mode_tweetbooks);
855 if (remove_position >= 0) review_mode_tweetbooks.splice(remove_position, 1);
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800856
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700857 // Clear UI with review tweetbook titles
858 $('#review-tweetbook-titles').html('');
859 for (r in review_mode_tweetbooks) {
860 addTweetBookDropdownItem(review_mode_tweetbooks[r]);
861 }
862}
863
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700864/**
865* Adds a tweetbook action button to the dropdown box in review mode.
866* @param tweetbook, a string representing a tweetbook
867*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700868function addTweetBookDropdownItem(tweetbook) {
genia.likes.science@gmail.com2de4c182013-10-04 11:39:11 -0700869 // Add placeholder for this tweetbook
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700870 $('<div/>')
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700871 .attr({
872 "class" : "btn-group",
873 "id" : "rm_holder_" + tweetbook
874 }).appendTo("#review-tweetbook-titles");
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800875
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700876 // Add plotting button for this tweetbook
genia.likes.science@gmail.com2de4c182013-10-04 11:39:11 -0700877 var plot_button = '<button class="btn btn-default" id="rm_plotbook_' + tweetbook + '">' + tweetbook + '</button>';
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700878 $("#rm_holder_" + tweetbook).append(plot_button);
genia.likes.science@gmail.com2de4c182013-10-04 11:39:11 -0700879 $("#rm_plotbook_" + tweetbook).width("200px");
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700880 $("#rm_plotbook_" + tweetbook).on('click', function(e) {
881 onPlotTweetbook(tweetbook);
882 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800883
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700884 // Add trash button for this tweetbook
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700885 var onTrashTweetbookButton = addDeleteButton(
886 "rm_trashbook_" + tweetbook,
887 "rm_holder_" + tweetbook,
888 function(e) {
889 onDropTweetBook(tweetbook);
890 }
891 );
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700892}
893
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700894/**
895* Generates AsterixDB query to plot existing tweetbook commnets
896* @param tweetbook, a string representing a tweetbook
897*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700898function onPlotTweetbook(tweetbook) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800899
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700900 // Clear map for this one
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700901 mapWidgetClearMap();
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700902
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700903 var plotTweetQuery = new FLWOGRExpression()
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -0700904 .ForClause("$t", new AExpression("dataset TweetMessagesShifted"))
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700905 .ForClause("$m", new AExpression("dataset " + tweetbook))
906 .WhereClause(new AExpression("$m.tweetid = $t.tweetid"))
907 .ReturnClause({
908 "tweetId" : "$m.tweetid",
909 "tweetText" : "$t.message-text",
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700910 "tweetCom" : "$m.comment-text",
911 "tweetLoc" : "$t.sender-location"
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700912 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800913
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700914 APIqueryTracker = {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700915 "query_string" : "use dataverse twitter;\n" + plotTweetQuery.val(),
genia.likes.science@gmail.com4833b412013-08-09 06:20:58 -0700916 "marker_path" : "static/img/mobile_green2.png",
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -0700917 "active_tweetbook" : tweetbook
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700918 };
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800919
920 A.query(plotTweetQuery.val(), onTweetbookQuerySuccessPlot);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700921}
922
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700923/**
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800924* Given an output response set of tweet data,
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700925* prepares markers on map to represent individual tweets.
926* @param res, a JSON Object
927*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700928function onTweetbookQuerySuccessPlot (res) {
929
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -0700930 // First, we check if any results came back in.
931 // If they didn't, return.
932 if (!res.hasOwnProperty("results")) {
933 reportUserMessage("Oops, no data matches this query.", false, "report-message");
934 return;
935 }
936
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700937 // Parse out tweet Ids, texts, and locations
938 var tweets = [];
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700939 var al = 1;
940
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700941 $.each(res.results, function(i, data) {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700942
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700943 // First, clean up the data
944 //{ "tweetId": "100293", "tweetText": " like at&t the touch-screen is amazing", "tweetLoc": point("31.59,-84.23") }
945 // We need to turn the point object at the end into a string
946 var json = $.parseJSON(data
947 .replace(': point(',': ')
948 .replace(') }', ' }'));
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700949
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700950 // Now, we construct a tweet object
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700951 var tweetData = {
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700952 "tweetEntryId" : parseInt(json.tweetId),
953 "tweetText" : json.tweetText,
954 "tweetLat" : json.tweetLoc.split(",")[0],
955 "tweetLng" : json.tweetLoc.split(",")[1]
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700956 };
957
958 // If we are parsing out tweetbook data with comments, we need to check
959 // for those here as well.
960 if (json.hasOwnProperty("tweetCom")) {
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700961 tweetData["tweetComment"] = json.tweetCom;
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700962 }
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700963
964 tweets.push(tweetData)
965 });
966
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700967 // Create a marker for each tweet
968 $.each(tweets, function(i, t) {
969 // Create a phone marker at tweet's position
970 var map_tweet_m = new google.maps.Marker({
971 position: new google.maps.LatLng(tweets[i]["tweetLat"], tweets[i]["tweetLng"]),
972 map: map,
Eugenia Gabrielovacacc9f82013-10-21 14:10:21 -0700973 icon: APIqueryTracker["marker_path"],
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700974 clickable: true,
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700975 });
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700976 map_tweet_m["test"] = t;
977
978 // Open Tweet exploration window on click
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -0700979 google.maps.event.addListener(map_tweet_m, 'click', function (event) {
980 onClickTweetbookMapMarker(map_tweet_markers[i]["test"]);
981 });
982
983 // Add marker to index of tweets
Zachary Heilbrond98423c2014-03-06 17:37:28 -0800984 map_tweet_markers.push(map_tweet_m);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700985 });
986}
987
Eugenia Gabrielovab2457982013-10-21 14:36:09 -0700988/**
989* Checks if a tweetbook exists
990* @param tweetbook, a String
991*/
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -0700992function existsTweetbook(tweetbook) {
993 if (parseInt($.inArray(tweetbook, review_mode_tweetbooks)) == -1) {
994 return false;
995 } else {
996 return true;
997 }
998}
999
Eugenia Gabrielovafcd28682013-10-20 05:36:36 -07001000/**
1001* When a marker is clicked on in the tweetbook, it will launch a modal
1002* view to examine or edit the appropriate tweet
1003*/
1004function onClickTweetbookMapMarker(t) {
1005 onDrillDownAtLocation(t)
genia.likes.science@gmail.com1400a752013-10-04 04:59:12 -07001006 $('#drilldown_modal').modal();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001007}
1008
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001009/**
1010* Explore mode: Initial map creation and screen alignment
1011*/
1012function onOpenExploreMap () {
genia.likes.science@gmail.com724476d2013-10-04 03:31:16 -07001013 var explore_column_height = $('#explore-well').height();
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001014 var right_column_width = $('#right-col').width();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001015 $('#map_canvas').height(explore_column_height + "px");
genia.likes.science@gmail.com724476d2013-10-04 03:31:16 -07001016 $('#map_canvas').width(right_column_width + "px");
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001017
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001018 $('#review-well').height(explore_column_height + "px");
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001019 $('#review-well').css('max-height', explore_column_height + "px");
genia.likes.science@gmail.com724476d2013-10-04 03:31:16 -07001020 $('#right-col').height(explore_column_height + "px");
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001021}
1022
1023/**
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001024* initializes demo - adds some extra events when review/explore
1025* mode are clicked, initializes tabs, aligns map box, moves
1026* active tab to about tab
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001027*/
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001028function initDemoPrepareTabs() {
genia.likes.science@gmail.com2de4c182013-10-04 11:39:11 -07001029
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001030 // Tab behavior for About, Explore, and Demo
1031 $('#mode-tabs a').click(function (e) {
1032 e.preventDefault()
1033 $(this).tab('show')
1034 })
1035
1036 // Explore mode should show explore-mode query-builder UI
1037 $('#explore-mode').click(function(e) {
1038 $('#review-well').hide();
1039 $('#explore-well').show();
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001040 rectangleManager.setMap(map);
1041 rectangleManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001042 mapWidgetResetMap();
1043 });
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001044
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001045 // Review mode should show review well and hide explore well
1046 $('#review-mode').click(function(e) {
1047 $('#explore-well').hide();
1048 $('#review-well').show();
1049 mapWidgetResetMap();
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001050 rectangleManager.setMap(null);
Eugenia Gabrielovae11965b2013-10-29 19:07:42 -07001051 rectangleManager.setDrawingMode(null);
Eugenia Gabrielovaab1ae552013-10-20 01:12:19 -07001052 });
1053
1054 // Does some alignment necessary for the map canvas
1055 onOpenExploreMap();
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001056}
1057
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001058/**
genia.likes.science@gmail.comdd669072013-08-21 22:53:34 -07001059* Creates a delete icon button using default trash icon
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -07001060* @param {String} id, id for this element
1061* @param {String} attachTo, id string of an element to which I can attach this button.
1062* @param {Function} onClick, a function to fire when this icon is clicked
1063*/
1064function addDeleteButton(iconId, attachTo, onClick) {
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001065
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001066 var trashIcon = '<button class="btn btn-default" id="' + iconId + '"><span class="glyphicon glyphicon-trash"></span></button>';
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -07001067 $('#' + attachTo).append(trashIcon);
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001068
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001069 // When this trash button is clicked, the function is called.
genia.likes.science@gmail.com8125bd92013-08-21 18:04:27 -07001070 $('#' + iconId).on('click', onClick);
1071}
1072
genia.likes.science@gmail.comdd669072013-08-21 22:53:34 -07001073/**
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001074* Creates a message and attaches it to data management area.
genia.likes.science@gmail.comdd669072013-08-21 22:53:34 -07001075* @param {String} message, a message to post
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001076* @param {Boolean} isPositiveMessage, whether or not this is a positive message.
1077* @param {String} target, the target div to attach this message.
genia.likes.science@gmail.comdd669072013-08-21 22:53:34 -07001078*/
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001079function reportUserMessage(message, isPositiveMessage, target) {
1080 // Clear out any existing messages
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001081 $('#' + target).html('');
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001082
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001083 // Select appropriate alert-type
1084 var alertType = "alert-success";
1085 if (!isPositiveMessage) {
1086 alertType = "alert-danger";
1087 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001088
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001089 // Append the appropriate message
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001090 $('<div/>')
Eugenia Gabrielovadbd50a42013-10-19 00:30:27 -07001091 .attr("class", "alert " + alertType)
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001092 .html('<button type="button" class="close" data-dismiss="alert">&times;</button>' + message)
1093 .appendTo('#' + target);
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001094}
1095
1096/**
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001097* mapWidgetResetMap
1098*
1099* [No Parameters]
1100*
1101* Clears ALL map elements - plotted items, overlays, then resets position
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001102*/
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -07001103function mapWidgetResetMap() {
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001104
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -07001105 mapWidgetClearMap();
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001106
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -07001107 // Reset map center and zoom
1108 map.setCenter(new google.maps.LatLng(38.89, -77.03));
1109 map.setZoom(4);
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001110
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001111 // Selection button
1112 $("#selection-button").trigger("click");
1113 rectangleManager.setMap(map);
1114 rectangleManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -07001115}
1116
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001117/**
1118* mapWidgetClearMap
Eugenia Gabrielovad07556a2013-10-11 03:45:06 -07001119* Removes data/markers
1120*/
genia.likes.science@gmail.com1b30f3d2013-08-17 23:53:37 -07001121function mapWidgetClearMap() {
1122
1123 // Remove previously plotted data/markers
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001124 for (c in map_cells) {
1125 map_cells[c].setMap(null);
1126 }
1127 map_cells = [];
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001128
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001129 $.each(map_info_windows, function(i) {
1130 map_info_windows[i].close();
1131 });
1132 map_info_windows = {};
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001133
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001134 for (m in map_tweet_markers) {
1135 map_tweet_markers[m].setMap(null);
1136 }
1137 map_tweet_markers = [];
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001138
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001139 // Hide legend
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -07001140 $("#rainbow-legend-container").hide();
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001141
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001142 // Reenable submit button
Eugenia Gabrielova12de0302013-10-18 02:25:39 -07001143 $("#submit-button").attr("disabled", false);
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001144
Eugenia Gabrielovaea8f9482013-10-28 04:49:18 -07001145 // Hide selection rectangle
1146 if (selectionRectangle) {
1147 selectionRectangle.setMap(null);
1148 selectionRectangle = null;
1149 }
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001150}
1151
1152/**
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001153* buildLegend
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001154*
1155* Generates gradient, button action for legend bar
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001156*/
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001157function buildLegend() {
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001158
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001159 // Fill in legend area with colors
1160 var gradientColor;
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001161
Eugenia Gabrielovad6e88e02013-10-19 08:26:54 -07001162 for (i = 0; i<100; i++) {
1163 //$("#rainbow-legend-container").append("" + rainbow.colourAt(i));
1164 $("#legend-gradient").append('<div style="display:inline-block; max-width:2px; background-color:#' + rainbow.colourAt(i) +';">&nbsp;</div>');
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001165 }
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001166}
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001167
1168/**
1169* Computes radius for a given data point from a spatial cell
1170* @param {Object} keys => ["latSW" "lngSW" "latNE" "lngNE" "weight"]
1171* @returns {number} radius between 2 points in metres
1172*/
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001173function mapWidgetComputeCircleRadius(spatialCell, wLimit) {
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001174
1175 // Define Boundary Points
1176 var point_center = new google.maps.LatLng((spatialCell.latSW + spatialCell.latNE)/2.0, (spatialCell.lngSW + spatialCell.lngNE)/2.0);
1177 var point_left = new google.maps.LatLng((spatialCell.latSW + spatialCell.latNE)/2.0, spatialCell.lngSW);
1178 var point_top = new google.maps.LatLng(spatialCell.latNE, (spatialCell.lngSW + spatialCell.lngNE)/2.0);
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001179
1180 // Circle scale modifier =
Zachary Heilbron566ba062014-03-11 13:22:54 -07001181 var scale = 425 + 425*(spatialCell.weight / wLimit);
Zachary Heilbrond98423c2014-03-06 17:37:28 -08001182
Eugenia Gabrielova8b34c652013-10-19 06:53:27 -07001183 // Return proportionate value so that circles mostly line up.
Eugenia Gabrielovaf29a55f2013-10-28 03:18:18 -07001184 return scale * Math.min(distanceBetweenPoints(point_center, point_left), distanceBetweenPoints(point_center, point_top));
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001185}
1186
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001187/**
Eugenia Gabrielovaf29a55f2013-10-28 03:18:18 -07001188* Calculates the distance between two latlng locations in km, using Google Geometry API.
1189* @param p1, a LatLng
1190* @param p2, a LatLng
genia.likes.science@gmail.com6d6aa8e2013-07-23 01:23:21 -07001191*/
Eugenia Gabrielovaf29a55f2013-10-28 03:18:18 -07001192function distanceBetweenPoints (p1, p2) {
1193 return 0.001 * google.maps.geometry.spherical.computeDistanceBetween(p1, p2);
Eugenia Gabrielova3d7bfe52013-10-28 02:36:44 -07001194};