--------------------------------------------------------------------------------
/assets/css/_site.scss:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 62px;
3 | overflow: hidden;
4 |
5 | color: white;
6 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#991d40+0,d1455a+55 */
7 | background-color: $primary-color;
8 | }
9 |
10 | select {
11 | color: black;
12 | }
13 |
14 | .row {
15 | top: 0;
16 |
17 | margin: 0;
18 | }
19 |
20 | #main-bg {
21 | padding-top: 0;
22 | margin: 10px;
23 | }
24 | #nga-logo {
25 | width: 500px;
26 | height: 500px;
27 | margin: 100px auto;
28 | }
29 | /* Specify styling for tooltip contents */
30 | .tooltip.customClass .tooltip-inner {
31 | width: auto;
32 |
33 | background-color: #999;
34 | box-shadow: 0 6px 12px rgba(0,0,0,.175);
35 | }
36 |
37 | #sub-menu-elements {
38 | position: absolute;
39 | right: 10px;
40 |
41 | width: 200px;
42 | }
43 |
--------------------------------------------------------------------------------
/assets/css/_threeDimMediaClusterer.scss:
--------------------------------------------------------------------------------
1 | #three-dim-media-clusterer #container {
2 | margin: 0;
3 | }
4 | .three-dim-media-clusterer-stats {
5 | position: relative;
6 |
7 | width: 100%;
8 | height: auto;
9 | padding: 7px;
10 | margin-bottom: 10px;
11 |
12 | font-size: 14px;
13 | font-style: $primary-font;
14 |
15 | border-radius: 5px;
16 | background-color: $secondary-color;
17 | }
18 |
19 | #three-dim-media-clusterer-nav {
20 | width: 200px;
21 | height: auto;
22 | padding: 7px;
23 |
24 | font-size: 14px;
25 | font-style: $primary-font;
26 |
27 | border-radius: 5px;
28 | background-color: $secondary-color;
29 | }
30 | .three-dim-media-clusterer-stats-select {
31 | padding: 1px 10px 1px 10px;
32 |
33 | font-weight: bold;
34 |
35 | cursor: pointer;
36 |
37 | border-radius: 3px;
38 | background-color: #433d4d;
39 | }
40 |
--------------------------------------------------------------------------------
/app/shared/selectableMediaDisplay/selectableMediaDisplayDirective.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | 'use strict';
4 |
5 | angular
6 | .module('socialMediaExplorerApp')
7 | .directive("selectableMediaDisplay", [selectableMediaDisplayDirective]);
8 |
9 | function selectableMediaDisplayDirective() {
10 |
11 | var directive = {
12 | restrict: "E",
13 | scope: {
14 | media: "=media"
15 | },
16 | templateUrl: 'views/selectableMediaDisplayView.html',
17 | controller: controller,
18 | controllerAs: 'vm',
19 | bindToController: true,
20 | link: link
21 | };
22 |
23 | return directive;
24 |
25 | function controller($scope) {
26 | var vm = this;
27 |
28 | vm.mediaClick = function(index) {
29 | vm.media.selected = index;
30 | }
31 | };
32 |
33 | function link($scope, elem, attr) {}
34 | }
35 | })();
--------------------------------------------------------------------------------
/app/components/threeDimMediaClusterer/threeDimMediaClustererInnerView.html:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
--------------------------------------------------------------------------------
/assets/css/_navigation.scss:
--------------------------------------------------------------------------------
1 | nav {
2 | margin: 10px;
3 |
4 | border: 0;
5 | }
6 | .navbar-fixed-top {
7 | top: 0;
8 | }
9 | .navbar-inverse {
10 | background-color: $secondary-color;
11 | }
12 | .navbar-inverse .navbar-nav > .active > a,
13 | .navbar-inverse .navbar-nav > .active > a:hover,
14 | .navbar-inverse .navbar-nav > .active > a:focus {
15 | color: #fff;
16 | background-color: #1d99af;
17 | }
18 | /* entire container, keeps perspective */
19 | .navbar-inverse .navbar-brand {
20 | font-family: "Roboto Condensed", sans-serif;
21 | font-size: 24px;
22 | font-weight: bold;
23 |
24 | color: $tertiary-color;
25 | }
26 | .navbar-inverse .navbar-nav > li > a {
27 | font-family: $primary-font, sans-serif;
28 | font-size: 16px;
29 | font-weight: lighter;
30 |
31 | color: $tertiary-color;
32 | }
33 | .side-nav {
34 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#5e9291+75,4c6f75+100 */
35 | background: #f1e061; /* Old browsers */
36 | }
37 |
--------------------------------------------------------------------------------
/assets/css/_selectedMediaDisplay.scss:
--------------------------------------------------------------------------------
1 | #selected-media {
2 | position: absolute;
3 | bottom: 0;
4 | left: 10px;
5 |
6 | max-width: 90%;
7 | overflow-x: auto;
8 | overflow-y: hidden;
9 |
10 | white-space: nowrap;
11 |
12 | background: none;
13 | }
14 |
15 | .selected-media {
16 | bottom: 0;
17 |
18 | width: 85px;
19 | height: 85px;
20 | padding: 0;
21 | margin: 0;
22 |
23 | cursor: pointer;
24 | transition: all .2s;
25 | vertical-align: bottom;
26 |
27 | border-width: 3px 2px 3px 1px;
28 | border-style: solid;
29 | border-color: #000;
30 | border-radius: 5px;
31 | }
32 |
33 | .selected-media:hover {
34 | width: 190px;
35 | height: 190px;
36 |
37 | border-color: #635d6d;
38 | border-radius: 2px;
39 | }
40 |
41 | #no-selected-media {
42 | font-family: "Roboto Condensed";
43 |
44 | background: none;
45 | }
46 |
47 | .selected-media-container {
48 | bottom: 0;
49 | left: 0;
50 |
51 | width: auto;
52 | min-height: 20px;
53 | padding: 0;
54 |
55 | color: white;
56 | }
57 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "social-media-explorer",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "^1.3.0",
6 | "angular-animate": "^1.3.0",
7 | "angular-cookies": "^1.3.0",
8 | "angular-resource": "^1.3.0",
9 | "angular-route": "^1.3.0",
10 | "angular-sanitize": "^1.3.0",
11 | "angular-touch": "^1.3.0",
12 | "angular-leaflet-directive": "~0.9.3",
13 | "three.js": "threejs#*",
14 | "angularjs-dropdown-multiselect": "~1.5.2",
15 | "angular-ui-router": "~0.2.15",
16 | "angular-bootstrap": "~0.14.3",
17 | "THREEOrbitControls": "~71.1.0",
18 | "bootstrap": "~3.3.4",
19 | "bootstrap-css": "~3.3.4",
20 | "components-font-awesome": "~4.4.0",
21 | "angular-underscore": "~0.5.0"
22 | },
23 | "devDependencies": {
24 | "angular-mocks": "^1.3.0",
25 | "angular-bootstrap": "~0.14.3",
26 | "bootstrap": "~3.3.4"
27 | },
28 | "appPath": "app",
29 | "moduleName": "socialMediaExplorerApp",
30 | "resolutions": {
31 | "angular": "1.4.7"
32 | }
33 | }
--------------------------------------------------------------------------------
/assets/css/_timeSlider.scss:
--------------------------------------------------------------------------------
1 | .startTime,
2 | .curTime {
3 | font: 15px $primary-font, sans-serif;
4 | }
5 |
6 | .curTime {
7 | padding: 6px;
8 |
9 | background-color: #1d99af;
10 | }
11 |
12 | .endTime {
13 | float: right;
14 |
15 | font: 15px $primary-font, sans-serif;
16 | }
17 |
18 | .startTime,
19 | .endTime {
20 | padding: 4px;
21 |
22 | background-color: $secondary-color;
23 | }
24 | input[type=range] {
25 | margin: 20px 0 20px 0;
26 |
27 | -webkit-appearance: none;
28 | }
29 |
30 | input[type=range]::-webkit-slider-runnable-track {
31 | width: 300px;
32 | height: 5px;
33 |
34 | border: none;
35 | border-radius: 3px;
36 | background: #ddd;
37 | }
38 |
39 | input[type=range]::-webkit-slider-thumb {
40 | width: 20px;
41 | height: 20px;
42 | margin-top: -4px;
43 |
44 | border: none;
45 | border-radius: 50%;
46 | background: $tertiary-color;
47 |
48 | -webkit-appearance: none;
49 | }
50 |
51 | input[type=range]:focus {
52 | outline: none;
53 | }
54 |
55 | input[type=range]:focus::-webkit-slider-runnable-track {
56 | background: #ccc;
57 | }
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "socialmediaexplorer",
3 | "version": "0.0.0",
4 | "private": true,
5 | "dependencies": {},
6 | "repository": {},
7 | "devDependencies": {
8 | "grunt": "^0.4.5",
9 | "grunt-autoprefixer": "^2.0.0",
10 | "grunt-concurrent": "^1.0.0",
11 | "grunt-contrib-clean": "^0.6.0",
12 | "grunt-contrib-compass": "^1.0.0",
13 | "grunt-contrib-concat": "^0.5.0",
14 | "grunt-contrib-connect": "^0.9.0",
15 | "grunt-contrib-copy": "^0.7.0",
16 | "grunt-contrib-cssmin": "^0.12.0",
17 | "grunt-contrib-htmlmin": "^0.4.0",
18 | "grunt-contrib-imagemin": "^0.9.2",
19 | "grunt-contrib-jshint": "^0.11.0",
20 | "grunt-contrib-uglify": "^0.7.0",
21 | "grunt-contrib-watch": "^0.6.1",
22 | "grunt-file-blocks": "^0.4.0",
23 | "grunt-filerev": "^2.1.2",
24 | "grunt-google-cdn": "^0.4.3",
25 | "grunt-include-source": "^0.7.0",
26 | "grunt-newer": "^1.1.0",
27 | "grunt-ng-annotate": "^0.9.2",
28 | "grunt-svgmin": "^2.0.0",
29 | "grunt-usemin": "^3.0.0",
30 | "grunt-wiredep": "^2.0.0",
31 | "jshint-stylish": "^1.0.0",
32 | "load-grunt-tasks": "^3.1.0",
33 | "time-grunt": "^1.0.0"
34 | },
35 | "engines": {
36 | "node": ">=0.10.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/components/shiptracker/shiptrackerView.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/shared/navigation/navigationView.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
23 |
--------------------------------------------------------------------------------
/assets/js/Detector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | * @author mr.doob / http://mrdoob.com/
4 | */
5 |
6 | var Detector = {
7 |
8 | canvas: !! window.CanvasRenderingContext2D,
9 | webgl: ( function () {
10 |
11 | try {
12 |
13 | var canvas = document.createElement( 'canvas' ); return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
14 |
15 | } catch ( e ) {
16 |
17 | return false;
18 |
19 | }
20 |
21 | } )(),
22 | workers: !! window.Worker,
23 | fileapi: window.File && window.FileReader && window.FileList && window.Blob,
24 |
25 | getWebGLErrorMessage: function () {
26 |
27 | var element = document.createElement( 'div' );
28 | element.id = 'webgl-error-message';
29 | element.style.fontFamily = 'monospace';
30 | element.style.fontSize = '13px';
31 | element.style.fontWeight = 'normal';
32 | element.style.textAlign = 'center';
33 | element.style.background = '#fff';
34 | element.style.color = '#000';
35 | element.style.padding = '1.5em';
36 | element.style.width = '400px';
37 | element.style.margin = '5em auto 0';
38 |
39 | if ( ! this.webgl ) {
40 |
41 | element.innerHTML = window.WebGLRenderingContext ? [
42 | 'Your graphics card does not seem to support
WebGL .
',
43 | 'Find out how to get it
here .'
44 | ].join( '\n' ) : [
45 | 'Your browser does not seem to support
WebGL .
',
46 | 'Find out how to get it
here .'
47 | ].join( '\n' );
48 |
49 | }
50 |
51 | return element;
52 |
53 | },
54 |
55 | addGetWebGLMessage: function ( parameters ) {
56 |
57 | var parent, id, element;
58 |
59 | parameters = parameters || {};
60 |
61 | parent = parameters.parent !== undefined ? parameters.parent : document.body;
62 | id = parameters.id !== undefined ? parameters.id : 'oldie';
63 |
64 | element = Detector.getWebGLErrorMessage();
65 | element.id = id;
66 |
67 | parent.appendChild( element );
68 |
69 | }
70 |
71 | };
72 |
73 | // browserify support
74 | if ( typeof module === 'object' ) {
75 |
76 | module.exports = Detector;
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/shared/factories/mediaFactory.js:
--------------------------------------------------------------------------------
1 | /*
2 | mediaFactory loads data about the media including three dimensional representation
3 | and provides an object to access the data. Upcoming versions should include
4 | informatino such as latitude, longitude, media details, etc.
5 | */
6 |
7 | (function() {
8 |
9 | 'use strict';
10 |
11 | angular
12 | .module('socialMediaExplorerApp')
13 | .factory('mediaFactory', ['$http', 'APP_CONFIG', mediaFactory]);
14 |
15 | function mediaFactory($http, APP_CONFIG) {
16 | function media(mediaData) {
17 | if (mediaData) {
18 | this.setData(mediaData);
19 | }
20 | // Some other initializations related to book
21 | };
22 | media.prototype = {
23 | setData: function(mediaData) {
24 | this.mediaFilenameIdx = 0;
25 | this.xCoordinateIdx = 1;
26 | this.yCoordinateIdx = 2;
27 | this.zCoordinateIdx = 3;
28 | this.splitChar = ",";
29 | this.pathToMedia = APP_CONFIG.mediaThumbnailUrl;
30 | this.data = mediaData.split("\n");
31 | },
32 | getRowAsArray: function(idx) {
33 | return this.data[idx].split(this.splitChar);
34 | },
35 | getMediaUrl: function(idx) {
36 | return this.pathToMedia + this.getRowAsArray(idx)[this.mediaFilenameIdx];
37 | },
38 | //the x, y, z coordinates correspond to represenation of the media
39 | //in 3d space in relation to the similarity with the other media
40 | getXCoordinate: function(idx) {
41 | return this.getRowAsArray(idx)[this.xCoordinateIdx];
42 | },
43 | getYCoordinate: function(idx) {
44 | return this.getRowAsArray(idx)[this.yCoordinateIdx];
45 | },
46 | getZCoordinate: function(idx) {
47 | return this.getRowAsArray(idx)[this.zCoordinateIdx];
48 | },
49 | getCount: function() {
50 | return this.data.length;
51 | }
52 | };
53 | return {
54 | getMedia: function() {
55 |
56 | return $http({
57 | url: APP_CONFIG.baseDataUrl + APP_CONFIG.mediaFactoryFilename,
58 | method: "GET",
59 | cache: true
60 | })
61 | .then(function(response) {
62 | return new media(response.data);
63 | });
64 | }
65 | };
66 | }
67 | })();
--------------------------------------------------------------------------------
/app/shared/factories/labeledMediaFactory.js:
--------------------------------------------------------------------------------
1 | /*
2 | labeledMediaFactory loads data from the labeled media files
3 | and provides an object to access the data.
4 | */
5 |
6 | (function() {
7 |
8 | 'use strict';
9 |
10 | angular
11 | .module('socialMediaExplorerApp')
12 | .factory('labeledMediaFactory', ['$http', 'APP_CONFIG', labeledMediaFactory]);
13 |
14 | function labeledMediaFactory($http, APP_CONFIG) {
15 | function labeledMedia(labeledMediaData, path) {
16 | if (labeledMediaData) {
17 | this.setData(labeledMediaData);
18 | this.path = path;
19 | }
20 | }
21 | labeledMedia.prototype = {
22 | setData: function(labeledMediaData) {
23 | this.data = labeledMediaData.split("\n");
24 | this.mediaFilenameIdx = 0;
25 | this.latitudeIdx = 1;
26 | this.longitudeIdx = 2;
27 | this.messageIdx = 3;
28 | this.pathToMedia = APP_CONFIG.baseImageUrl;
29 | },
30 | getRowAsArray: function(idx) {
31 | return this.data[idx].trim().match(/(".*?"|[^",\s]+)(?=\s*,|\s*$)/g);
32 | },
33 | getId: function(idx) {
34 | return this.getRowAsArray(idx)[this.mediaFilenameIdx].toString();
35 | },
36 | getMediaUrl: function(idx) {
37 | return this.pathToMedia + this.path + "/" + this.getRowAsArray(idx)[this.mediaFilenameIdx] + ".jpg";
38 | },
39 | getLatitude: function(idx) {
40 | return parseFloat(this.getRowAsArray(idx)[this.latitudeIdx]);
41 | },
42 | getLongitude: function(idx) {
43 | return parseFloat(this.getRowAsArray(idx)[this.longitudeIdx]);
44 | },
45 | getMessage: function(idx) {
46 | return this.getRowAsArray(idx)[this.messageIdx];
47 | },
48 | getCount: function() {
49 | return this.data.length;
50 | }
51 | }
52 | return {
53 | //getLabeledMedia takes a path and returns
54 | //a labeled media object from the data file
55 | //stored at that path
56 | getLabeledMedia: function(path) {
57 | return (function(path) {
58 | var p = path;
59 | return $http({
60 | url: APP_CONFIG.baseImageUrl + path + "/" + APP_CONFIG.labeledMediaFactoryFilename,
61 | method: "GET",
62 | cache: true
63 | }).then(function(response) {
64 | return new labeledMedia(response.data, p);
65 | });
66 | })(path);
67 | }
68 | }
69 | }
70 | })();
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
NGA Pathfinder Social Media Explorer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |