diff --git a/app/assets/scripts/actions/actions.js b/app/assets/scripts/actions/actions.js index d0ef35d2..06bf6b27 100644 --- a/app/assets/scripts/actions/actions.js +++ b/app/assets/scripts/actions/actions.js @@ -12,6 +12,7 @@ module.exports = Reflux.createActions({ 'resultsChange': {}, // Results pane related actions. + 'resultItemSelect': {}, 'resultItemView': {}, 'resultListView': {}, 'prevResult' : {}, diff --git a/app/assets/scripts/components/map.js b/app/assets/scripts/components/map.js index 36f8831a..1bfb987a 100644 --- a/app/assets/scripts/components/map.js +++ b/app/assets/scripts/components/map.js @@ -24,7 +24,9 @@ var Map = React.createClass({ Reflux.listenTo(actions.mapSquareUnselected, "onMapSquareUnselected"), Reflux.listenTo(actions.resultOver, "onResultOver"), Reflux.listenTo(actions.resultOut, "onResultOut"), + Reflux.listenTo(actions.resultItemSelect, "onResultItemSelect"), Reflux.listenTo(actions.resultItemView, "onResultItemView"), + Reflux.listenTo(actions.resultListView, "onResultListView"), Reflux.listenTo(actions.goToLatest, "onGoToLatest"), Reflux.listenTo(actions.geocoderResult, "onGeocoderResult"), @@ -38,6 +40,8 @@ var Map = React.createClass({ fauxLineGridLayer: null, // Layer to store the footprint when hovering a result. overFootprintLayer: null, + // Layer with the image of the selected result. + overImageLayer: null, // When the user clicks browse latest imagery we move the map to the correct // locations. Then, when the query is finished and the map rendered we @@ -46,19 +50,45 @@ var Map = React.createClass({ // square that contains it. Check updateGrid() selectIntersecting: null, + getInitialState: function() { + return { + loading: true + }; + }, + // Store listener. onMapData: function(data) { this.setState({ - mapData: data + mapData: data, + loading: false }); }, // Actions listener. - onResultItemView: function(feature) { + onResultItemSelect: function() { // Remove footprint highlight. this.overFootprintLayer.clearLayers(); }, + // Actions listener. + onResultItemView: function(item) { + if (this.map.hasLayer(this.overImageLayer)) { + this.map.removeLayer(this.overImageLayer); + } + + var imageBounds = [[item.bbox[1], item.bbox[0]], [item.bbox[3], item.bbox[2]]]; + this.overImageLayer = L.imageOverlay(item.properties.thumbnail, imageBounds); + + this.map.addLayer(this.overImageLayer); + }, + + // Actions listener. + onResultListView: function() { + if (this.map.hasLayer(this.overImageLayer)) { + this.map.removeLayer(this.overImageLayer); + } + }, + // Actions listener. onMapSquareSelected: function(sqrFeature) { var intersected = mapStore.getResultsIntersect(sqrFeature); @@ -73,6 +103,10 @@ var Map = React.createClass({ // Actions listener. onMapSquareUnselected: function() { + if (this.map.hasLayer(this.overImageLayer)) { + this.map.removeLayer(this.overImageLayer); + } + actions.resultsChange([]); this.updateGrid(); }, @@ -303,10 +337,10 @@ var Map = React.createClass({ var _this = this; var view = [60.177, 25.148]; - this.map = L.mapbox.map(this.getDOMNode(), 'devseed.m9i692do', { + this.map = L.mapbox.map(this.getDOMNode().querySelector('#map'), 'devseed.m9i692do', { zoomControl: false, minZoom : 4, - maxZoom : 18, + //maxZoom : 18, maxBounds: L.latLngBounds([-90, -180], [90, 180]) }).setView(view, 6); @@ -355,6 +389,7 @@ var Map = React.createClass({ // Map move listener. this.map.on('moveend', function() { + _this.setState({loading: true}); actions.mapMove(_this.map); _this.updateFauxGrid(); }); @@ -375,7 +410,10 @@ var Map = React.createClass({ render: function() { return ( -
+
+ {this.state.loading ?

Loading

: null} +
+
); }, diff --git a/app/assets/scripts/components/results_item.js b/app/assets/scripts/components/results_item.js index ae012904..3a0b24b6 100644 --- a/app/assets/scripts/components/results_item.js +++ b/app/assets/scripts/components/results_item.js @@ -38,7 +38,7 @@ var ResultsItem = React.createClass({ return (
-

{d.title}

+

{d.title.replace(/\.[a-z]+$/, '')}

{pagination.current} of {pagination.total} results

diff --git a/app/assets/scripts/components/results_list.js b/app/assets/scripts/components/results_list.js index 2e0b2537..3b89d2b9 100644 --- a/app/assets/scripts/components/results_list.js +++ b/app/assets/scripts/components/results_list.js @@ -6,7 +6,7 @@ var utils = require('../utils/utils'); var ResultsListItem = React.createClass({ onClick: function(e) { e.preventDefault(); - actions.resultItemView(this.props.data); + actions.resultItemSelect(this.props.data); }, onOver: function(e) { e.preventDefault(); diff --git a/app/assets/scripts/stores/results_store.js b/app/assets/scripts/stores/results_store.js index 181e6e25..dd22d54c 100644 --- a/app/assets/scripts/stores/results_store.js +++ b/app/assets/scripts/stores/results_store.js @@ -12,7 +12,7 @@ module.exports = Reflux.createStore({ init: function() { this.listenTo(actions.resultsChange, this.onResultsChange); - this.listenTo(actions.resultItemView, this.onResultItemView); + this.listenTo(actions.resultItemSelect, this.onResultItemSelect); this.listenTo(actions.resultListView, this.onResultListView); this.listenTo(actions.nextResult, this.onNextResult); this.listenTo(actions.prevResult, this.onPrevResult); @@ -28,8 +28,8 @@ module.exports = Reflux.createStore({ }, // Action listener. - onResultItemView: function(data) { - console.log('onImageSelect'); + onResultItemSelect: function(data) { + console.log('onResultItemSelect'); this.storage.selectedItem = data; // Find the object index. for (var i in this.storage.results) { @@ -38,6 +38,7 @@ module.exports = Reflux.createStore({ break; } } + actions.resultItemView(this.storage.selectedItem); this.trigger(this.storage); }, @@ -53,6 +54,7 @@ module.exports = Reflux.createStore({ this.storage.selectedItemIndex++; var i = this.storage.selectedItemIndex; this.storage.selectedItem = this.storage.results[i]; + actions.resultItemView(this.storage.selectedItem); this.trigger(this.storage); }, @@ -61,6 +63,7 @@ module.exports = Reflux.createStore({ this.storage.selectedItemIndex--; var i = this.storage.selectedItemIndex; this.storage.selectedItem = this.storage.results[i]; + actions.resultItemView(this.storage.selectedItem); this.trigger(this.storage); }, diff --git a/app/assets/scripts/utils/utils.js b/app/assets/scripts/utils/utils.js index 383817fd..26ce286d 100644 --- a/app/assets/scripts/utils/utils.js +++ b/app/assets/scripts/utils/utils.js @@ -38,11 +38,16 @@ module.exports.getPolygonFeature = function(coords) { /** * Coverts the given gsb to meters or centimeters - * @param float gsd + * @param float gsd in meters * @return string */ module.exports.gsdToUnit = function(gsd) { - var cm = gsd * 100 * 100; - var unit = cm >= 100 ? 'm' : 'cm'; - return Math.round(cm) + ' ' + unit; -}; \ No newline at end of file + var unit = 'm'; + // If it's less than 1m, convert to cm so it displays more nicely + if (gsd < 1) { + unit = 'cm'; + gsd *= 100; + } + + return Math.round(gsd) + ' ' + unit; +}; diff --git a/app/assets/styles/_base.scss b/app/assets/styles/_base.scss index 21136d96..0153b9e8 100644 --- a/app/assets/styles/_base.scss +++ b/app/assets/styles/_base.scss @@ -236,3 +236,46 @@ a:active{ } } + +.loading { + @extend .antialiased; + position: absolute; + z-index: 9980; + top: 50%; + left: 50%; + width: 8rem; + height: 8rem; + margin: -4rem 0 0 -4rem; + background: rgba($base-color, 0.8); + padding: 2rem 1rem; + border-radius: $global-radius; + color: #fff; + text-align: center; + + opacity: 0; + visibility: hidden; + @include transform(scale(0)); + @include transition(all 0.24s ease 0s); + + &.revealed { + opacity: 1; + visibility: visible; + @include transform(scale(1)); + } + + &:before { + display: block; + @extend .icon-spinner; + font-size: 2rem; + width: 2rem; + height: 2rem; + line-height: 1; + margin: 0 auto 0.5rem auto; + @include animation(spin-c 1s linear 0s infinite); + } +} + +@include keyframes(spin-c) { + from { @include transform(rotate(0deg)); } + to { @include transform(rotate(360deg)); } +}