diff --git a/static/cartoview_time_data_viewer/css/map.css b/static/cartoview_time_data_viewer/css/map.css new file mode 100644 index 0000000..17c533c --- /dev/null +++ b/static/cartoview_time_data_viewer/css/map.css @@ -0,0 +1,99 @@ +html, body, .full-height{ + height: 100%; + padding: 0; + overflow: hidden; +} +.ol3-map{ + position: relative; + height: 100%; + width: 100%; + +} + +.map-widget .popup-popover .navbar-text { + margin-right: 0; + margin-left: 0; +} +.map-widget .popup-popover .table-responsive{ + max-height: 150px; +} + +.map-widget .popup-popover { + min-height: 300px; + max-height: 300px; + min-width: 400px; +} +.layers-switcher button{ + padding-top: 2px; +} +.layers-switcher{ + top: 70px; + right: .5em; +} + +.ol-control button:hover, .ol-control button:focus { + background-color: #f4f4f4; +} +.ol-control button{ + width: 26px; + height: 26px; + line-height: 26px; + display: block; + color: black; + background-color: #fff; + border-width: 0; +} + +.ol-control{ + padding:0; + box-shadow: rgba(0, 0, 0, 0.65098) 0px 1px 5px 0px; + border-radius: 4px; +} +.ol-scale-line.ol-unselectable{ + background-color: rgba(255, 255, 255, 0.8); +} + + +.menu-toggle-btn{ + position: absolute; + top: 8px; + left: 8px; + z-index: 9999; +} + +/* ZOOMBAR*/ +.zoombar{ + position: absolute; + top: 10px; + right: 15px; + z-index: 1; + width: 30px; + padding: 0; +} +.zoombar .md-button{ + min-width: 30px; + margin: 0; +} + +.zoombar .zoom-in-btn{ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.zoombar .zoom-out-btn{ + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.rotation{ + position: absolute; + top: 90px; + right: 15px; + z-index: 1; + width: 30px; + padding: 0; +} +.rotation .md-button{ + min-width: 30px; + margin: 0; +} \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/css/style.css b/static/cartoview_time_data_viewer/css/style.css new file mode 100644 index 0000000..96c7db7 --- /dev/null +++ b/static/cartoview_time_data_viewer/css/style.css @@ -0,0 +1,82 @@ + + +.pointer { + cursor: pointer; +} +.floating-menu .md-button .floating-menu-item-icon{ + font-size: 20px; +} +.floating-menu .md-button{ + border-radius: 0; + cursor: pointer; + display: block; + align-items: inherit; + line-height: 40px; + margin: 0; + max-height: 40px; + overflow: hidden; + padding: 0px 16px; + text-align: left; + text-decoration: none; + white-space: normal; + width: 100%; +} +.md-button-toggle .md-toggle-icon { + display: block; + margin-left: auto; + speak: none; + vertical-align: middle; + transform: rotate(180deg); + -webkit-transform: rotate(180deg); + transition: transform 0.3s ease-in-out; + -webkit-transition: -webkit-transform 0.3s ease-in-out; +} + +.md-button-toggle .md-toggle-icon.toggled { + transform: rotate(0deg); + -webkit-transform: rotate(0deg); +} + +.content-panel { + position: absolute; + top: 0; + left: 0; + z-index: 2; + +} +.content-panel .header{ + background-color: transparent; +} +.content-panel.has-content .header .md-button{ + min-width: 56px; +} +.content-panel.has-content { + bottom: 0; + background-color: #FFFFFF; + width: 360px; + min-width: 30%; + max-width: 100%; +} +.content-panel .content{ + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); +} +.map-results .map-results-header{ + min-height: auto; +} +.map-results .map-results-header h4{ + margin: 0; +} + +.map-results table{ + border-spacing: 0; +} +.map-results td, +.map-results th{ + padding: 8px; + text-align: left; + vertical-align: top; +} +.map-results th{ + font-weight: bold; +} \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/edit.js b/static/cartoview_time_data_viewer/map_viewer/js/edit.js new file mode 100644 index 0000000..5085c4a --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/edit.js @@ -0,0 +1,25 @@ +/** + * Created by kamal on 6/28/16. + */ +'use strict'; +(function () { + window.angularAppDependencies = window.angularAppDependencies || []; + console.debug(window.angularAppDependencies); + var module = angular.module('cartoview.viewer.editor', [ + 'ngMaterial', + 'cartoview.base', + 'ngResourceTastypie', + 'cartoview.viewer.widgetsInfo', + 'cartoview.viewer.urlsHelper', + 'dcbImgFallback' + ].concat(window.angularAppDependencies)); + module.config(function($tastypieProvider, $httpProvider, urlsHelper){ + $tastypieProvider.setResourceUrl(urlsHelper.rest); + + $tastypieProvider.add('geonode', {url: urlsHelper.geonodeRest}); + //$tastypieProvider.setDefault('cartoview'); + + $httpProvider.defaults.xsrfCookieName = 'csrftoken'; + $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; + }); +})(); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/map.js b/static/cartoview_time_data_viewer/map_viewer/js/map.js new file mode 100644 index 0000000..95207a3 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/map.js @@ -0,0 +1,450 @@ +'use strict'; +angular.module('cartoview.map', [ ]); +// ol.Map.prototype.updateSize = function() { +// var targetElement = this.getTargetElement(); +// +// if (!targetElement) { +// this.setSize(undefined); +// } else { +// +// this.setSize([targetElement.offsetWidth,targetElement.offsetHeight]); +// } +// }; + +angular.module('cartoview.map').service("mapService", function($http, $q) { + var map = { + content: { + results: [] + }, + loading: 0 + }; + Object.defineProperty(map, 'hasContent', { + get: function() { + var hasContent = false; + map.content.results.forEach(function (result) { + if(result.features && result.features.length > 0){ + hasContent = true; + return false; + } + }); + return hasContent; + } + }); + map.addContent = function (content) { + map.content.results.push(content); + }; + map.clearContent = function () { + map.content.results = []; + map.resultsLayer.get('source').clear(); + delete map.selected; + }; + var initMap = function() { + var config = map.config; + var overlays = [], + baseLayers = []; + function getTileLayer(layerConfig, layerSource) { + return new ol.layer.Tile({ + source: layerSource, + visible: layerConfig.visibility, + title: layerConfig.title, + type: layerConfig.fixed ? 'base' : '' + }); + } + var order = 1; + config.map.layers.forEach(function (layerConfig) { + //var layerConfig = config.map.layers[i]; + var layerSourceConfig = config.sources[layerConfig.source]; + var layer = null; + if (layerSourceConfig.ptype == 'gxp_olsource') { + layer = getTileLayer(layerConfig); + } + else if (layerSourceConfig.ptype == 'gxp_osmsource') { + layer = getTileLayer(layerConfig, new ol.source.OSM()); + } else if (layerSourceConfig.ptype === 'gxp_mapquestsource') { + layer = getTileLayer(layerConfig, new ol.source.MapQuest({ + layer: layerConfig.name == 'naip' ? 'sat' : 'osm' + })); + } else if (layerSourceConfig.ptype === 'gxp_wmscsource') { + var layerSource = new ol.source.TileWMS({ + url: layerSourceConfig.url, + params: { + LAYERS: layerConfig.name, + //VERSION: '1.1.1', + TILED: true, + STYLES: layerConfig.styles, + FORMAT: layerConfig.format, + TRANSPARENT: layerConfig.transparent + } + //, + // tileLoadFunction:function(imageTile, src) { + // + // var extent = map.olMap.getView().calculateExtent(map.olMap.getSize()); + // //extent = ol.proj.transformExtent(extent, 'EPSG:3857', 'EPSG:4326'); + // var coordinates = ol.extent.getBottomLeft(extent); + // imageTile.getImage().src = src + "&TILESORIGIN=" + coordinates[0] + "," + coordinates[1]; + // console.debug(imageTile.getImage()); + // console.debug(imageTile.getImage().src); + // } + }); + layer = getTileLayer(layerConfig, layerSource); + } + + if (layer != null) { + layer.set('title', layerConfig.title); + if (layerConfig.fixed) { + baseLayers.push(layer); + if(layerConfig.visibility){ + baseLayers.active = layer; + } + + } else { + overlays.push(layer); + + } + Object.defineProperty(layer, 'visible', { + configurable: true, + get: function() { + return layer.getVisible(); + }, + set: function(val) { + layer.setVisible(val); + } + }); + } + }); + map.overlays = overlays; + map.backgrounds = baseLayers; + map.olMap = new ol.Map({ + controls:[], + view: new ol.View({ + center: config.map.center, + zoom: config.map.zoom + }), + //renderer: 'canvas', + layers:[new ol.layer.Group({ + layers:baseLayers + }), + new ol.layer.Group({ + layers:overlays + })] + }); + map.resultsLayer = new ol.layer.Vector({ + source: new ol.source.Vector(), + visible: true, + style: styleResults + }); + map.olMap.addLayer(map.resultsLayer); + initIdentify(); + //initSearch(); + }; + + var deferObj = $q.defer(); + var notRequested = true; + var get = function() { + if(notRequested){ + notRequested = false; + $http.get(MAP_CONFIG_URL).then(function (response) { + map.config = response.data; + initMap(); + deferObj.resolve(map); + }); + } + return deferObj.promise; + }; + // zoom functions + map.zoomHome = function () { + var view = map.olMap.getView(); + if (!view) { + return; + } + var bounce = ol.animation.bounce({ + resolution: view.getResolution() * 2 + }); + var pan = ol.animation.pan({ + source: view.getCenter() + }); + map.olMap.beforeRender(bounce); + map.olMap.beforeRender(pan); + view.setCenter(map.config.map.center); + view.setZoom(map.config.map.zoom); + }; + + var zoom = function(delta){ + var view = map.olMap.getView(); + if (!view) { + return; + } + map.olMap.beforeRender(ol.animation.zoom({ + 'resolution': view.getResolution(), + 'duration': 500 + })); + view.setZoom(view.getZoom() + delta); + }; + map.zoomIn = function () { + zoom(1); + }; + map.zoomOut = function () { + zoom(-1) + }; + + //identify + var initIdentify = function () { + if(!map.config.identify){ // set default identify config + var config = {}; + map.overlays.forEach(function (layer) { + var layerName = layer.get("source").getParams().LAYERS; + config[layerName] = {} + }); + map.config.identify = config; + } + map.olMap.on('singleclick', function(evt) { + map.clearContent(); + var view = map.olMap.getView(); + var viewResolution = view.getResolution(); + var resultsVectorSource = map.resultsLayer.get('source'); + resultsVectorSource.clear(); + map.overlays.forEach(function (layer) { + var source = layer.get('source'); + var layerName = source.getParams().LAYERS; + + if(!layer.visible || !map.config.identify[layerName] ) return; + var url = source.getGetFeatureInfoUrl(evt.coordinate, viewResolution, + view.getProjection(), + {'INFO_FORMAT': 'application/json', 'FEATURE_COUNT': 10}); + if(window.PROXY_URL) + url = PROXY_URL + encodeURIComponent(url); + map.loading++; + var result = { + layer: layer, + features: [], + title: layer.get('title'), + listItemTpl: map.config.identify[layerName].listItemTpl || "default-list-item-tpl.html" + }; + map.content.results.push(result); + var addFeatures = function (features, crs) { + features.forEach(function(f) { + f.getGeometry().transform('EPSG:' + crs, 'EPSG:3857'); + f.properties = f.getProperties(); + delete f.properties[f.getGeometryName()]; + }); + resultsVectorSource.addFeatures(result.features); + }; + $http.get(url).then(function(response) { + map.loading--; + result.features = new ol.format.GeoJSON().readFeatures(response.data); + var crs = response.data.crs.properties.name.split(":").pop(); + if(proj4.defs('EPSG:' + crs)){ + addFeatures(result.features, crs ); + } + else{ + //load the proj def first + $http.get("http://epsg.io/?format=json&q=" + crs).then(function (res) { + proj4.defs('EPSG:' + crs, res.data.results[0].proj4); + addFeatures(result.features, crs); + }); + + } + + });//end $http.then + });//end forEach overlay + });// end on singleClick + + };//end initIdentify + //search + var initSearch = function () { + if(!map.config.search){ + var searchConfig = []; + map.config.search = searchConfig; + var urls = {}; + map.overlays.forEach(function (layer) { + var source = layer.get('source'); + var name = source.getParams().LAYERS; + var url = source.getUrls()[0]; + if(!urls[url]) + urls[url] = {}; + urls[url][name] = layer; + }); + angular.forEach(urls, function(layer, url){ + url += "?service=WFS&request=DescribeFeatureType&outputFormat=application/json&version=2.0.0"; + if(window.PROXY_URL){ + url = window.PROXY_URL + encodeURIComponent(url); + } + $http.get(url).then(function (res) { + var workspace = res.data.targetPrefix; + res.data.featureTypes.forEach(function (featureType) { + var name = workspace + ":" + featureType.name; + if(url[name]){ + var fields = []; + featureType.properties.forEach(function (property) { + if(property.localType == 'string'){ + fields.push(property.name); + } + }); + searchConfig.push({ + layer: layer, + fields: fields + }) + } + + }); + }) + }); + + } + };//end initSearch + + //results management + map.selectFeature = function (feature) { + if(map.selected){ + map.selected.set('isSelected', false); + } + map.selected = feature; + if(feature){ + feature.set('isSelected', true); + } + }; + + var defaultPointStyle = new ol.style.Style({ + image: new ol.style.Circle({ + // fill: new ol.style.Fill({ + // color: '#ff0000' + // }), + stroke: new ol.style.Stroke({ + color: '#000088', + width: 2 + }), + radius: 6 + }) + }); + var defaultPolygonStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#000088', + width: 2 + }) + }); + var selectedPointStyle = new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ + color: '#ffccff' + }), + stroke: new ol.style.Stroke({ + color: '#ffffff' + }), + radius: 6 + }) + }); + var selectedPolygonStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#880000', + width: 3 + }) + }); + + var styleResults = function (feature) { + var polygonStyle = defaultPolygonStyle, pointStyle = defaultPointStyle; + if(feature.get('isSelected')){ + polygonStyle = selectedPolygonStyle; + pointStyle = selectedPointStyle; + } + return [polygonStyle, pointStyle]; + }; + + + return { + get: get, + map: map + } +}); + + +angular.module('cartoview.map').directive('cartoviewMap', function(mapService) { + return { + restrict: 'A', + link:function(scope, element){ + mapService.get().then(function () { + mapService.map.olMap.setTarget(element[0]); + mapService.map.olMap.updateSize(); + }) + } + } +}); + +angular.module('cartoview.map').directive('layersSwitcher', function() { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: "layers-switcher.html", + controller: function ($scope, mapService){ + mapService.get().then(function(){ + $scope.overlays = mapService.map.overlays; + }); + + } + } +}); +angular.module('cartoview.map').directive('layersLegend', function() { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: "layers-legend.html", + controller: function ($scope, mapService){ + mapService.get().then(function(){ + $scope.overlays = mapService.map.overlays; + $scope.overlays.forEach(function (layer) { + console.debug(layer); + }) + }); + + } + } +}); +angular.module('cartoview.map').directive('basemapsSwitcher', function() { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: "basemaps-switcher.html", + controller: function ($scope, $element, $compile, mapService) { + mapService.get().then(function(){ + var map = mapService.map.olMap; + $scope.backgrounds = mapService.map.backgrounds; + var activeBg; + $scope.setBackground = function () { + var layer = $scope.backgrounds.active; + if (activeBg==layer) return; + layer.visible = true; + activeBg.visible = false; + activeBg = layer; + }; + activeBg = $scope.backgrounds.active; + }); + + } + } +}); + +angular.module('cartoview.map').directive('zoomBar', function() { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: "zoombar.html", + controller: function ($scope, mapService) { + $scope.map = mapService.map; + } + } +}); + +angular.module('cartoview.map').directive('mapResults', function() { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: "map-results.html", + controller: function ($scope, mapService) { + $scope.map = mapService.map; + } + } +}); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view-app.js b/static/cartoview_time_data_viewer/map_viewer/js/view-app.js new file mode 100644 index 0000000..4048ca5 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view-app.js @@ -0,0 +1,78 @@ +(function () { + window.angularAppDependencies = window.angularAppDependencies || []; + var dependencies = ['cartoview.map', 'ngMaterial'].concat(window.angularAppDependencies) + var app = angular.module('cartoview.mapViewerApp', dependencies); + app.config(function ($httpProvider) { + $httpProvider.useApplyAsync(true); + }); + app.controller('cartoview.mapViewerController', function ($scope, $mdSidenav, mapService, $mdMedia, $mdDialog) { + $scope.toggleSidenav = function () { + return $mdSidenav('left').toggle(); + }; + $scope.map = mapService.map; + mapService.get().then(function () { + $scope.title = mapService.map.config.about.title; + $scope.abstract = mapService.map.config.about.abstract; + }); + $scope.showAboutDialog = function (ev) { + var useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && $scope.customFullscreen; + $mdDialog.show({ + controller: DialogController, + templateUrl: 'about-dialog.html', + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose: true, + fullscreen: useFullScreen + }); + + $scope.$watch(function () { + return $mdMedia('xs') || $mdMedia('sm'); + }, function (wantsFullScreen) { + $scope.customFullscreen = (wantsFullScreen === true); + }); + }; + }); + + function DialogController($scope, $mdDialog, mapService) { + mapService.get().then(function () { + $scope.title = mapService.map.config.about.title; + $scope.abstract = mapService.map.config.about.abstract; + }); + $scope.hide = function () { + $mdDialog.hide(); + }; + $scope.cancel = function () { + $mdDialog.cancel(); + }; + $scope.answer = function (answer) { + $mdDialog.hide(answer); + }; + } + + app.directive('searchBox', function () { + return { + restrict: 'E', + templateUrl: 'search-box.html', + replace: true, + controller: function ($scope, $element, $compile, mapService) { + + } + }; + }); + app.directive('toggleButton', function () { + return { + restrict: 'E', + scope: { + toggle: '=', + title: '@', + icon: '@' + }, + templateUrl: 'toggle-button.html', + replace: true, + link: function ($scope, elem, attr, ctrl) { + // console.debug($scope); + } + }; + }); + +})(); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/app.js b/static/cartoview_time_data_viewer/map_viewer/js/view/app.js new file mode 100644 index 0000000..cfb4d7d --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/app.js @@ -0,0 +1,9 @@ + +var app = angular.module('cartoview.mapViewerApp', [ + 'ngMaterial', + 'cartoview.base', + 'cartoview.viewer.urlsHelper', + 'cartoview.viewer.mapConfig', + 'cartoview.viewer.appConfig', + 'cartoview.viewer.widgetsInfo' +]); diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/basic-controls-directives.js b/static/cartoview_time_data_viewer/map_viewer/js/view/basic-controls-directives.js new file mode 100644 index 0000000..88dec95 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/basic-controls-directives.js @@ -0,0 +1,164 @@ +/** + * Created by kamal on 7/2/16. + */ + +angular.module('cartoview.mapViewerApp').directive('toggleButton', function(urlsHelper) { + return { + restrict: 'E', + scope: { + toggle: '=', + title: '@', + icon: '@' + }, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/toggle-button.html", + replace: true, + link: function($scope, elem, attr, ctrl) { + // console.debug($scope); + } + }; +}); + +function DialogController($scope, $mdDialog, appConfig) { + + $scope.title = appConfig.title; + $scope.abstract = appConfig.abstract; + + $scope.hide = function() { + $mdDialog.hide(); + }; + $scope.cancel = function() { + $mdDialog.cancel(); + }; + $scope.answer = function(answer) { + $mdDialog.hide(answer); + }; +} + +angular.module('cartoview.mapViewerApp').directive('aboutButton', function(mapService,urlsHelper, $mdDialog, $mdMedia) { + return { + restrict: 'A', + link:function(scope, element){ + + var showAboutDialog = function(ev) { + var useFullScreen = ($mdMedia('sm') || $mdMedia('xs')) && scope.customFullscreen; + $mdDialog.show({ + controller: DialogController, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/about-dialog.html", + parent: angular.element(document.body), + targetEvent: ev, + clickOutsideToClose:true, + fullscreen: useFullScreen + }); + + scope.$watch(function() { + return $mdMedia('xs') || $mdMedia('sm'); + }, function(wantsFullScreen) { + scope.customFullscreen = (wantsFullScreen === true); + }); + }; + element.on('click',showAboutDialog); + } + }; +}); + +angular.module('cartoview.mapViewerApp').directive('layersSwitcher', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/layers-switcher.html", + controller: function ($scope, mapService){ + mapService.get().then(function(){ + $scope.overlays = mapService.map.overlays; + }); + + } + } +}); + +angular.module('cartoview.mapViewerApp').directive('layersLegend', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/layers-legend.html", + controller: function ($scope, mapService){ + mapService.get().then(function(){ + $scope.overlays = mapService.map.overlays; + // $scope.overlays.forEach(function (layer) { + // console.debug(layer); + // }) + }); + + } + } +}); +angular.module('cartoview.mapViewerApp').directive('basemapsSwitcher', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/basemaps-switcher.html", + controller: function ($scope, $element, $compile, mapService) { + mapService.get().then(function(){ + var map = mapService.map.olMap; + $scope.backgrounds = mapService.map.backgrounds; + var activeBg; + $scope.setBackground = function () { + var layer = $scope.backgrounds.active; + if (activeBg==layer) return; + layer.visible = true; + activeBg.visible = false; + activeBg = layer; + }; + activeBg = $scope.backgrounds.active; + }); + + } + } +}); + +angular.module('cartoview.mapViewerApp').directive('zoomBar', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/zoom-bar.html", + controller: function ($scope, mapService) { + $scope.map = mapService.map; + } + } +}); + + + +angular.module('cartoview.mapViewerApp').directive('rotationBar', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/rotation.html", + controller: function ($scope, mapService) { + $scope.rotation = 0; + mapService.get().then(function () { + mapService.map.olMap.getView().on('change:rotation', function () { + $scope.rotation = mapService.map.olMap.getView().getRotation(); + $scope.$apply() + }) + }); + $scope.arrowStyle = function () { + { + transform: 'rotate(' + $scope.rotation + 'deg)' + } + } + $scope.resetRotation = function () { + mapService.map.olMap.getView().setRotation(0); + } + $scope.setRotation = function () { + var r = mapService.map.olMap.getView().getRotation() + mapService.map.olMap.getView().setRotation(r + 10); + } + + } + } +}); diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/identify-results-directive.js b/static/cartoview_time_data_viewer/map_viewer/js/view/identify-results-directive.js new file mode 100644 index 0000000..d217a2a --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/identify-results-directive.js @@ -0,0 +1,14 @@ +/** + * Created by kamal on 7/2/16. + */ + +angular.module('cartoview.mapViewerApp').directive('identifyResults', function(urlsHelper) { + return { + restrict: 'E', + replace: true, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/identify-results.html", + controller: function ($scope, identifyService) { + $scope.identify = identifyService; + } + } +}); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/identify-service.js b/static/cartoview_time_data_viewer/map_viewer/js/view/identify-service.js new file mode 100644 index 0000000..be3f646 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/identify-service.js @@ -0,0 +1,168 @@ +/** + * Created by kamal on 7/2/16. + */ +angular.module('cartoview.mapViewerApp').service('identifyService', function(mapService, urlsHelper, $http) { + var DEFAULT_ITEM_TPL = urlsHelper.static + "cartoview_map_viewer/angular-templates/view/default-list-item-tpl.html"; + var service = this; + service.content = { + results: [] + }; + var map = mapService.map; + service.loading = 0; + Object.defineProperty(this, 'hasContent', { + get: function() { + var hasContent = false; + service.content.results.forEach(function (result) { + if(result.features && result.features.length > 0){ + hasContent = true; + return false; + } + }); + return hasContent; + } + }); + service.clearContent = function () { + service.content.results = []; + service.resultsLayer.get('source').clear(); + delete service.selected; + }; + //results management + service.selectFeature = function (feature) { + if(service.selected){ + service.selected.set('isSelected', false); + } + service.selected = feature; + if(feature){ + feature.set('isSelected', true); + } + }; + + var defaultPointStyle = new ol.style.Style({ + image: new ol.style.Circle({ + // fill: new ol.style.Fill({ + // color: '#ff0000' + // }), + stroke: new ol.style.Stroke({ + color: '#000088', + width: 2 + }), + radius: 6 + }) + }); + var defaultPolygonStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#000088', + width: 2 + }) + }); + var selectedPointStyle = new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ + color: '#ffccff' + }), + stroke: new ol.style.Stroke({ + color: '#ffffff' + }), + radius: 6 + }) + }); + var selectedPolygonStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#880000', + width: 3 + }) + }); + + var styleResults = function (feature) { + var polygonStyle = defaultPolygonStyle, pointStyle = defaultPointStyle; + if(feature.get('isSelected')){ + polygonStyle = selectedPolygonStyle; + pointStyle = selectedPointStyle; + } + return [polygonStyle, pointStyle]; + }; + mapService.get().then(function () { + if(!map.config.identify) { // set default identify config + var config = {}; + map.overlays.forEach(function (layer) { + var layerName = layer.get("source").getParams().LAYERS; + config[layerName] = {} + }); + map.config.identify = config; + } + service.resultsLayer = new ol.layer.Vector({ + source: new ol.source.Vector(), + visible: true, + style: styleResults + }); + map.olMap.addLayer(service.resultsLayer); + + map.olMap.on('singleclick', function(evt) { + service.clearContent(); + var view = map.olMap.getView(); + var viewResolution = view.getResolution(); + var resultsVectorSource = service.resultsLayer.get('source'); + resultsVectorSource.clear(); + map.overlays.forEach(function (layer) { + var source = layer.get('source'); + var layerName = source.getParams().LAYERS; + + if(!layer.visible || !map.config.identify[layerName] ) return; + var url = source.getGetFeatureInfoUrl(evt.coordinate, viewResolution, + view.getProjection(), + {'INFO_FORMAT': 'application/json', 'FEATURE_COUNT': 10}); + //uncomment to get wfs working + // if(urlsHelper.proxy) + // url = urlsHelper.proxy + encodeURIComponent(url); + service.loading++; + var result = { + layer: layer, + features: [], + title: layer.get('title'), + listItemTpl: map.config.identify[layerName].listItemTpl || DEFAULT_ITEM_TPL + }; + service.content.results.push(result); + var addFeatures = function (features, crs) { + features.forEach(function(f) { + f.getGeometry().transform('EPSG:' + crs, 'EPSG:3857'); + f.properties = f.getProperties(); + delete f.properties[f.getGeometryName()]; + }); + resultsVectorSource.addFeatures(result.features); + service.featuresCount = resultsVectorSource.getFeatures().length; + + if(service.featuresCount == 1){ + + service.selectFeature(resultsVectorSource.getFeatures()[0]) + } + else{ + service.selectFeature() + } + }; + $http.get(url).then(function(response) { + console.error(url); + service.loading--; + result.features = new ol.format.GeoJSON().readFeatures(response.data); + if(result.features.length == 0){ + return; + } + var crs = response.data.crs.properties.name.split(":").pop(); + if(proj4.defs('EPSG:' + crs)){ + console.error(result.features); + addFeatures(result.features, crs ); + } + else{ + //load the proj def first + $http.get("http://epsg.io/?format=json&q=" + crs).then(function (res) { + console.error(res.data.results); + proj4.defs('EPSG:' + crs, res.data.results[0].proj4); + addFeatures(result.features, crs); + }); + + } + + });//end $http.then + });//end forEach overlay + });// end on singleClick + }); +}); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/main-controller.js b/static/cartoview_time_data_viewer/map_viewer/js/view/main-controller.js new file mode 100644 index 0000000..0775d5c --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/main-controller.js @@ -0,0 +1,21 @@ +/** + * Created by kamal on 7/2/16. + */ + +angular.module('cartoview.mapViewerApp').controller('cartoview.MainController', + function($scope, mapService, identifyService, $mdSidenav, $mdMedia, $mdDialog, appConfig){ + $scope.config = appConfig; + console.debug(appConfig) + console.debug("==============================") + $scope.toggleSidenav = function() { + return $mdSidenav('left').toggle(); + }; + $scope.map = mapService.map; + $scope.identify = identifyService; + + // mapService.get().then(function () { + // $scope.title = mapService.map.config.about.title; + // $scope.abstract = mapService.map.config.about.abstract; + // }); + // + }); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/map-directive.js b/static/cartoview_time_data_viewer/map_viewer/js/view/map-directive.js new file mode 100644 index 0000000..d0bc7b7 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/map-directive.js @@ -0,0 +1,21 @@ +/** + * Created by kamal on 7/2/16. + */ + +angular.module('cartoview.mapViewerApp').directive('cartoviewMap', function(mapService, $timeout) { + return { + restrict: 'A', + link: function (scope, element) { + mapService.get().then(function () { + mapService.map.olMap.setTarget(element[0]); + mapService.map.olMap.updateSize(); + scope.$watch(function () { + return [element[0].offsetWidth, element[0].offsetHeight].join('x'); + }, + function (value) { + mapService.map.olMap.updateSize(); + }); + }) + } + } +}); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/map-service.js b/static/cartoview_time_data_viewer/map_viewer/js/view/map-service.js new file mode 100644 index 0000000..fd22569 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/map-service.js @@ -0,0 +1,219 @@ +'use strict'; +angular.module('cartoview.mapViewerApp').service("mapService", function ($http, $q, urlsHelper, mapConfig) { + var map = { + loading: 0 + }; + + var initMap = function () { + var config = map.config; + var overlays = [], + baseLayers = []; + + function getTileLayer(layerConfig, layerSource) { + return new ol.layer.Tile({ + source: layerSource, + visible: layerConfig.visibility, + title: layerConfig.title, + type: layerConfig.fixed ? 'base' : '' + }); + } + + var order = 1; + config.map.layers.forEach(function (layerConfig) { + //var layerConfig = config.map.layers[i]; + var layerSourceConfig = config.sources[layerConfig.source]; + var layer = null; + if (layerSourceConfig.ptype == 'gxp_olsource') { + layer = getTileLayer(layerConfig); + } + else if (layerSourceConfig.ptype == 'gxp_osmsource') { + layer = getTileLayer(layerConfig, new ol.source.OSM()); + } else if (layerSourceConfig.ptype === 'gxp_mapquestsource') { + layer = getTileLayer(layerConfig, new ol.source.MapQuest({ + layer: layerConfig.name == 'naip' ? 'sat' : 'osm' + })); + } else if (layerSourceConfig.ptype === 'gxp_wmscsource') { + var layerSource = new ol.source.TileWMS({ + url: layerSourceConfig.url, + params: { + LAYERS: layerConfig.name, + //VERSION: '1.1.1', + TILED: true, + STYLES: layerConfig.styles, + FORMAT: layerConfig.format ? layerConfig.format : 'image/png', + TRANSPARENT: layerConfig.transparent ? layerConfig.transparent : true + } + //, + // tileLoadFunction:function(imageTile, src) { + // + // var extent = map.olMap.getView().calculateExtent(map.olMap.getSize()); + // //extent = ol.proj.transformExtent(extent, 'EPSG:3857', 'EPSG:4326'); + // var coordinates = ol.extent.getBottomLeft(extent); + // imageTile.getImage().src = src + "&TILESORIGIN=" + coordinates[0] + "," + coordinates[1]; + // console.debug(imageTile.getImage()); + // console.debug(imageTile.getImage().src); + // } + }); + layer = getTileLayer(layerConfig, layerSource); + console.log(layerSource.getParams()) + } + + if (layer != null) { + layer.set('title', layerConfig.title); + if (layerConfig.fixed) { + baseLayers.push(layer); + if (layerConfig.visibility) { + baseLayers.active = layer; + } + + } else { + overlays.push(layer); + + } + Object.defineProperty(layer, 'visible', { + configurable: true, + get: function () { + return layer.getVisible(); + }, + set: function (val) { + layer.setVisible(val); + } + }); + } + }); + map.overlays = overlays; + map.backgrounds = baseLayers; + map.olMap = new ol.Map({ + controls: [], + view: new ol.View({ + center: config.map.center, + zoom: config.map.zoom, + rotation: 0 + }), + //renderer: 'canvas', + layers: [new ol.layer.Group({ + layers: baseLayers + }), + new ol.layer.Group({ + layers: overlays + })] + }); + // initIdentify(); + //initSearch(); + }; + + var deferObj = $q.defer(); + var notRequested = true; + var get = function () { + if (notRequested) { + notRequested = false; + // $http.get(MAP_CONFIG_URL).then(function (response) { + // map.config = response.data; + // initMap(); + // deferObj.resolve(map); + // }); + map.config = mapConfig; + initMap(); + deferObj.resolve(map); + } + return deferObj.promise; + }; + // zoom functions + map.zoomHome = function () { + var view = map.olMap.getView(); + if (!view) { + return; + } + var bounce = ol.animation.bounce({ + resolution: view.getResolution() * 2 + }); + var pan = ol.animation.pan({ + source: view.getCenter() + }); + map.olMap.beforeRender(bounce); + map.olMap.beforeRender(pan); + view.setCenter(map.config.map.center); + view.setZoom(map.config.map.zoom); + }; + map.fit = function (geom) { + var view = map.olMap.getView(); + if (!view) { + return; + } + var bounce = ol.animation.bounce({ + resolution: view.getResolution() * 2 + }); + var pan = ol.animation.pan({ + source: view.getCenter() + }); + map.olMap.beforeRender(bounce); + map.olMap.beforeRender(pan); + view.fit(geom, map.olMap.getSize(), {maxZoom: 16}); + }; + var zoom = function (delta) { + var view = map.olMap.getView(); + if (!view) { + return; + } + map.olMap.beforeRender(ol.animation.zoom({ + 'resolution': view.getResolution(), + 'duration': 500 + })); + view.setZoom(view.getZoom() + delta); + }; + map.zoomIn = function () { + zoom(1); + }; + map.zoomOut = function () { + zoom(-1) + }; + + //search + var initSearch = function () { + if (!map.config.search) { + var searchConfig = []; + map.config.search = searchConfig; + var urls = {}; + map.overlays.forEach(function (layer) { + var source = layer.get('source'); + var name = source.getParams().LAYERS; + var url = source.getUrls()[0]; + if (!urls[url]) + urls[url] = {}; + urls[url][name] = layer; + }); + angular.forEach(urls, function (layer, url) { + url += "?service=WFS&request=DescribeFeatureType&outputFormat=application/json&version=2.0.0"; + if (window.PROXY_URL) { + url = window.PROXY_URL + encodeURIComponent(url); + } + $http.get(url).then(function (res) { + var workspace = res.data.targetPrefix; + res.data.featureTypes.forEach(function (featureType) { + var name = workspace + ":" + featureType.name; + if (url[name]) { + var fields = []; + featureType.properties.forEach(function (property) { + if (property.localType == 'string') { + fields.push(property.name); + } + }); + searchConfig.push({ + layer: layer, + fields: fields + }) + } + + }); + }) + }); + + } + };//end initSearch + + + return { + get: get, + map: map + } +}); \ No newline at end of file diff --git a/static/cartoview_time_data_viewer/map_viewer/js/view/widget-view-directive.js b/static/cartoview_time_data_viewer/map_viewer/js/view/widget-view-directive.js new file mode 100644 index 0000000..c0fcf57 --- /dev/null +++ b/static/cartoview_time_data_viewer/map_viewer/js/view/widget-view-directive.js @@ -0,0 +1,18 @@ +/** + * Created by kamal on 6/28/16. + */ +angular.module('cartoview.mapViewerApp').directive('widgetView', function(urlsHelper) { + return { + restrict: 'E', + transclude: true, + replace: true, + scope:{ + widgetName: "@" + }, + templateUrl: urlsHelper.static + "cartoview_map_viewer/angular-templates/view/widget-view.html", + controller: function($scope, appConfig, widgetsInfo) { + $scope.config = appConfig; + $scope.widget = widgetsInfo.get($scope.widgetName); + } + } +}); diff --git a/templates/cartoview_time_data_viewer/dynamic_view_js.html b/templates/cartoview_time_data_viewer/dynamic_view_js.html new file mode 100644 index 0000000..2b38b78 --- /dev/null +++ b/templates/cartoview_time_data_viewer/dynamic_view_js.html @@ -0,0 +1,59 @@ +{% load viewer_helper_tags %} + diff --git a/templates/cartoview_time_data_viewer/edit__.html b/templates/cartoview_time_data_viewer/edit__.html deleted file mode 100644 index 0cbf97d..0000000 --- a/templates/cartoview_time_data_viewer/edit__.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends "cartoview/base.html" %} -{% block title %} - {{ block.super }} - {% if instance %}{{ instance.title }}{% else %}New Map Viewer{% endif %} -{% endblock %} -{% block styles %} - - {% for w in widgets %} - {% for css_url in w.config.css %}{% endfor %} - {% endfor %} -{% endblock %} -{% block scripts %} - - - {% include "cartoview_map_viewer/dynamic_edit_js.html" %} - - - - - - - - {% for w in widgets %} - {% for js_url in w.config.js %}{% endfor %} - {% endfor %} -{% endblock %} -{% block body_directives %}ng-app="cartoview.viewer.editor"{% endblock %} -{% block body %} -
- - {% load static %} -
-
- - - -
-
-

Cartoview Time and Date viewer

-
- -
-
- -
- -

{% if instance %}Edit: {{ instance.title }}{% else %}New Map Viewer{% endif %}

- -
- - -
- {% for w in widgets %} - - {% if w.config.directive %}<{{ w.config.directive }}>{% endif %} - - {% endfor %} -
- -
-
-
- -{% endblock %} diff --git a/templates/cartoview_time_data_viewer/view.html b/templates/cartoview_time_data_viewer/view.html index dd4c4c3..ddfd99f 100644 --- a/templates/cartoview_time_data_viewer/view.html +++ b/templates/cartoview_time_data_viewer/view.html @@ -4,8 +4,8 @@ {% endblock %} {% block styles %} - - + + {% for w in widgets %} {% for css_url in w.view.css %}{% endfor %} {% endfor %} @@ -15,14 +15,14 @@ - {% include "cartoview_map_viewer/dynamic_view_js.html" %} - - - - - - - + {% include "cartoview_time_data_viewer/dynamic_view_js.html" %} + + + + + + + {% for w in widgets %} {% for js_url in w.view.js %}{% endfor %}