-
Map chart
+
Map chart
+
+
+
+
+
diff --git a/cubesviewer/views/cube/chart/chart.js b/cubesviewer/views/cube/chart/chart.js
index 5196626..4555ddc 100644
--- a/cubesviewer/views/cube/chart/chart.js
+++ b/cubesviewer/views/cube/chart/chart.js
@@ -115,8 +115,14 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartController"
$(rows).each(function(idx, e) {
var jointkey = [];
+ var jointlabel = [];
for (var i = 0; i < view.params.drilldown.length; i++) jointkey.push(e["key" + i]);
- e["key"] = jointkey.join(" / ");
+ for (var i = 0; i < view.params.drilldown.length; i++) jointlabel.push(e["label" + i]);
+ e["key"] = jointkey.join("_");
+ e["label"] = jointlabel.join(" / ");
+
+ // FIXME: Using label as key for charts, but we should properly use keys/labels for charts and column definitions in general
+ e["key"] = e["label"];
});
}
diff --git a/cubesviewer/views/cube/chart/map/chart-map-layers.js b/cubesviewer/views/cube/chart/map/chart-map-layers.js
new file mode 100644
index 0000000..5dfc549
--- /dev/null
+++ b/cubesviewer/views/cube/chart/map/chart-map-layers.js
@@ -0,0 +1,189 @@
+/*
+ * CubesViewer
+ * Copyright (c) 2012-2016 Jose Juan Montes, see AUTHORS for more details
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+"use strict";
+
+
+var defineMapControllerLayerMethods = function($scope) {
+
+ /**
+ * Creates a Tile XYZ layer.
+ * @param mapLayer
+ * @returns The created layer.
+ */
+ $scope.createLayerXYZ = function (mapLayer) {
+
+ var sourceParams = {};
+ angular.extend(sourceParams, mapLayer.params);
+ if (mapLayer.attribution) sourceParams['attributions'] = [ new ol.Attribution({ 'html': "" + mapLayer.attribution }) ];
+
+ var layer = new ol.layer.Tile({
+ source: new ol.source.XYZ(sourceParams),
+ opacity: mapLayer.opacity ? mapLayer.opacity : 1.0,
+ visible: false
+ });
+
+ return layer;
+ };
+
+ /**
+ * Creates a WMTS layer. This provides a set of default parameters.
+ * @returns The created layer.
+ */
+ $scope.createLayerWMTS = function (mapLayer) {
+
+ // Generate resolutions and matrixIds arrays for this WMTS (having seen capabilities)
+ // Note: Grid may be built also by: ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet(matrixSet, opt_extent)
+ var projection = ol.proj.get('EPSG:3857');
+ var projectionExtent = projection.getExtent();
+ var size = ol.extent.getWidth(projectionExtent) / 256;
+ var resolutions = new Array(20);
+ var matrixIds = new Array(20);
+ for (var z = 0; z < 20; ++z) {
+ resolutions[z] = size / Math.pow(2, z);
+ matrixIds[z] = z;
+ }
+
+ var sourceParamsBase = {
+ //url: 'http://www.ign.es/wmts/pnoa-ma?service=WMTS',
+ //layer: 'OI.OrthoimageCoverage',
+ matrixSet: 'GoogleMapsCompatible', // 'EPSG:3857',
+ format: 'image/png',
+ projection: projection,
+ tileGrid: new ol.tilegrid.WMTS({
+ origin: ol.extent.getTopLeft(projectionExtent),
+ resolutions: resolutions,
+ matrixIds: matrixIds
+ }),
+ style: 'default'
+ };
+
+ var sourceParams = {};
+ angular.extend(sourceParams, sourceParamsBase);
+ angular.extend(sourceParams, mapLayer.params);
+ if (mapLayer.attribution) sourceParams['attributions'] = [ new ol.Attribution({ 'html': "" + mapLayer.attribution }) ];
+
+ var layer = new ol.layer.Tile({
+ source: new ol.source.WMTS(sourceParams),
+ opacity: mapLayer.opacity ? mapLayer.opacity : 1.0,
+ visible: false
+ //extent: projectionExtent,
+ })
+
+ return layer;
+ };
+
+ /**
+ * Creates a KML layer.
+ * @returns The created layer.
+ */
+ $scope.createLayerKML = function (mapLayer) {
+
+ var sourceParams = {};
+ angular.extend(sourceParams, { format: new ol.format.KML() });
+ angular.extend(sourceParams, mapLayer.params);
+ if (mapLayer.attribution) sourceParams['attributions'] = [ new ol.Attribution({ 'html': "" + mapLayer.attribution }) ];
+
+ var layer = new ol.layer.Vector({
+ source: new ol.source.Vector(sourceParams)
+ });
+
+ return layer;
+ };
+
+ /**
+ * Creates a GeoJSON layer.
+ * @returns The created layer.
+ */
+ $scope.createLayerGeoJSON = function (mapLayer) {
+
+ var sourceParams = {};
+ angular.extend(sourceParams, { format: new ol.format.GeoJSON() });
+ angular.extend(sourceParams, mapLayer.params);
+
+ var cityNamesStyle = function(feature, resolution) {
+ var fontSize = 1.5 - 0.5 * feature.get("scalerank") / 10;
+ return [ new ol.style.Style({
+ text: new ol.style.Text({
+ text: resolution < 3600 || (resolution < 14400 && feature.get("scalerank") < 4) ? feature.get("name") : "",
+ font: 'bold ' + (fontSize * 10) + 'px Calibri,sans-serif',
+ fill: new ol.style.Fill({color: "#ffffff"}),
+ stroke: new ol.style.Stroke({color: "#000000", width: 3}),
+ })
+ }) ]
+ };
+
+ // Populated places GeoJSON
+ var layer = new ol.layer.Vector({
+ title: mapLayer.label,
+ source: new ol.source.Vector(sourceParams),
+ visible: false,
+
+ // TODO: Layer styles shall be optional and chosen from several possibilities (if applied)
+ style: cityNamesStyle
+ });
+
+ return layer;
+ };
+
+
+ /**
+ * Creates various types of map layers based on settings.
+ */
+ $scope.createLayers = function(mapLayers) {
+
+ var layers = {};
+ layers['_order'] = [];
+ angular.forEach(mapLayers, function(mapLayer) {
+
+ var layer = null;
+
+ if (mapLayer.params.url && (mapLayer.params.url.search('{}') >= 0)) {
+ mapLayer.params.url = mapLayer.params.url.replace('{}', $location.host());
+ }
+
+ if (mapLayer.source == 'wmts') {
+ layer = $scope.createLayerWMTS(mapLayer);
+ } else if (mapLayer.source == 'xyz') {
+ layer = $scope.createLayerXYZ(mapLayer);
+ } else if (mapLayer.source == 'geojson') {
+ layer = $scope.createLayerGeoJSON(mapLayer);
+ } else if (mapLayer.source == 'kml') {
+ layer = $scope.createLayerKML(mapLayer);
+ } else {
+ console.error('Wrong map settings. Could not create map layer of source type: ' + mapLayer.source);
+ return;
+ }
+
+ layers[mapLayer.name] = layer;
+ layers['_order'].push(layer);
+
+
+ });
+ };
+
+};
+
+
+
+
diff --git a/cubesviewer/views/cube/chart/map/chart-map.js b/cubesviewer/views/cube/chart/map/chart-map.js
index d42c45e..0d0e7c1 100644
--- a/cubesviewer/views/cube/chart/map/chart-map.js
+++ b/cubesviewer/views/cube/chart/map/chart-map.js
@@ -35,6 +35,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
$scope.map = null;
+
$scope.initialize = function() {
// Add chart view parameters to view definition
};
@@ -57,11 +58,31 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
var container = $($element).find(".cv-map-container").get(0);
$(container).empty();
- var xAxisLabel = ( (view.params.xaxis != null) ? view.cube.dimensionParts(view.params.xaxis).label : "None")
+ var xAxisLabel = ( (view.params.xaxis != null) ? view.cube.dimensionParts(view.params.xaxis).label : "None");
var projection = ol.proj.get('EPSG:3857');
- var raster = new ol.layer.Tile({
+ // Select column
+ var geoLevels = $.grep(view.params.drilldown, function(e) {
+ var dimParts = view.cube.dimensionParts(e);
+ return dimParts.level.isGeoLevel();
+ });
+
+ // Get geo info from model
+ $scope.geoLevel = null;
+ if (geoLevels.length > 0) {
+ $scope.geoLevel = geoLevels[0];
+ console.debug($scope.geoLevel);
+ } else {
+ return;
+ }
+
+ // Create layers
+ var mapLayers = $scope.geoLevel.info['cv-geo-map-layers'];
+ $scope.mapLayers = $scope.createLayers(mapLayers);
+
+ /*
+ var raster = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'http://tile.openstreetmap.org/{z}/{x}/{y}.png'
})
@@ -76,9 +97,10 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
}),
projection: projection
});
+ */
$scope.map = new ol.Map({
- layers: [raster, vector], // raster
+ layers: $scope.mapLayers['_order'],
target: container,
view: new ol.View({
center: [876970.8463461736, 5859807.853963373],
@@ -109,7 +131,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
font: '10px Verdana',
text: text, // getText(feature),
fill: new ol.style.Fill({color: 'black'}),
- stroke: new ol.style.Stroke({color: 'white', width: 0.0})
+ stroke: new ol.style.Stroke({color: 'white', width: 2.0})
});
};
@@ -139,7 +161,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
feature.setStyle(
new ol.style.Style({
fill: new ol.style.Fill({color: color, opacity: 0.7}), // colorArr[colorindex]
- stroke: new ol.style.Stroke({color: "#ffffff", width: 2,opacity: 0.7} ),
+ stroke: new ol.style.Stroke({color: color, width: 0.0, opacity: 0.7} ), // "#ffffff"
text: createTextStyle(feature, label + "\n" + valueFormatted)
})
);
@@ -195,6 +217,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeChartMapControll
};
+ defineMapControllerLayerMethods($scope);
$scope.initialize();
}]);
diff --git a/cubesviewer/views/cube/cube.js b/cubesviewer/views/cube/cube.js
index efbfdc3..cb5f367 100644
--- a/cubesviewer/views/cube/cube.js
+++ b/cubesviewer/views/cube/cube.js
@@ -111,7 +111,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$
};
$scope.setViewMode = function(mode) {
- console.debug("Remove setViewMode call on the controller?")
+ // TODO: Remove setViewMode call on the controller
$scope.view.setViewMode(mode);
};
diff --git a/cubesviewer/views/cube/facts/facts.js b/cubesviewer/views/cube/facts/facts.js
index 5a3ab82..89ece8d 100644
--- a/cubesviewer/views/cube/facts/facts.js
+++ b/cubesviewer/views/cube/facts/facts.js
@@ -107,7 +107,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
});
view.grid.columnDefs.push({
- name: "id",
+ name: "Id",
field: "id",
index: "id",
enableHiding: false,
@@ -271,7 +271,9 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
// Set key
row["id"] = counter++;
+
if ("id" in e) row["id"] = e["id"];
+ if ("__fact_key__" in e) row["id"] = e["__fact_key__"];
row["key"] = row["id"];
row["_cell"] = e;
diff --git a/cubesviewer/views/cube/series/series.js b/cubesviewer/views/cube/series/series.js
index c8b7ee3..5e81162 100644
--- a/cubesviewer/views/cube/series/series.js
+++ b/cubesviewer/views/cube/series/series.js
@@ -179,6 +179,7 @@ cubesviewer._seriesAddRows = function($scope, data) {
// Copy drilldown as we'll modify it
var drilldown = view.params.drilldown.slice(0);
+ var ag = $.grep(view.cube.aggregates, function(ag) { return ag.ref == view.params.yaxis; })[0];
// Include X Axis if necessary
if (view.params.xaxis != null) {
@@ -189,8 +190,8 @@ cubesviewer._seriesAddRows = function($scope, data) {
var addedCols = [];
$(data.cells).each(function (idx, e) {
- var row = [];
var key = [];
+ var label = [];
// For the drilldown level, if present
for (var i = 0; i < drilldown.length; i++) {
@@ -200,22 +201,24 @@ cubesviewer._seriesAddRows = function($scope, data) {
var infos = parts.hierarchy.readCell(e, parts.level);
// Values and Labels
- var drilldownLevelValues = [];
+ var drilldownLevelKeys = [];
var drilldownLevelLabels = [];
$(infos).each(function(idx, info) {
- drilldownLevelValues.push(info.key);
+ drilldownLevelKeys.push(info.key);
drilldownLevelLabels.push(info.label);
});
- key.push (drilldownLevelLabels.join(" / "));
+ label.push(drilldownLevelLabels.join(" / "));
+ key.push(drilldownLevelKeys.join("_"));
}
// Set key
- var colKey = (view.params.xaxis == null) ? view.params.yaxis : key[0];
+ var colKey = (view.params.xaxis == null) ? view.params.yaxis : key[0]; // key[0] because that's the horizontal dimension key
+ var colLabel = (view.params.xaxis == null) ? ag.label : label[0];
+ var rowKey = (view.params.xaxis == null) ? key.join (' / ') : key.slice(1).join(' / ');
var value = (e[view.params.yaxis]);
- var rowKey = (view.params.xaxis == null) ? key.join (' / ') : key.slice(1).join (' / ');
// Search or introduce
var row = $.grep(rows, function(ed) { return ed["key"] == rowKey; });
@@ -229,6 +232,7 @@ cubesviewer._seriesAddRows = function($scope, data) {
for (var i = baseidx ; i < key.length; i++) {
newrow["key" + (i - baseidx)] = key[i];
+ newrow["label" + (i - baseidx)] = label[i];
}
newrow["_cell"] = e;
@@ -240,12 +244,11 @@ cubesviewer._seriesAddRows = function($scope, data) {
if (addedCols.indexOf(colKey) < 0) {
addedCols.push(colKey);
- var ag = $.grep(view.cube.aggregates, function(ag) { return ag.ref == view.params.yaxis })[0];
var col = {
- name: colKey,
+ name: colLabel,
field: colKey,
- index : colKey,
+ index: colKey,
cellClass : "text-right",
//sorttype : "number",
cellTemplate: '
{{ col.colDef.formatter(COL_FIELD, row, col) }}
',
@@ -263,10 +266,10 @@ cubesviewer._seriesAddRows = function($scope, data) {
});
//var label = [];
- $(view.params.drilldown).each (function (idx, e) {
+ $(view.params.drilldown).each(function (idx, e) {
var col = {
name: view.cube.cvdim_dim(e).label,
- field: "key" + idx,
+ field: "label" + idx,
index : "key" + idx,
headerCellClass: "cv-grid-header-dimension",
enableHiding: false,
@@ -286,7 +289,8 @@ cubesviewer._seriesAddRows = function($scope, data) {
});
if (view.params.drilldown.length == 0 && rows.length > 0) {
- rows[0]["key0"] = view.cube.aggregateFromName(view.params.yaxis).label;
+ rows[0]["key0"] = view.cube.aggregateFromName(view.params.yaxis).name;
+ rows[0]["label0"] = view.cube.aggregateFromName(view.params.yaxis).label;
var col = {
name: "Measure",
diff --git a/doc/guide/cubesviewer-maps.md b/doc/guide/cubesviewer-maps.md
index 9313bf8..3d2fec1 100644
--- a/doc/guide/cubesviewer-maps.md
+++ b/doc/guide/cubesviewer-maps.md
@@ -4,20 +4,22 @@ CubesViewer Map Charts
Features
--------
-- Geographic data on dimension attributes and fact details
-- Features: points, lines, polygons
-- Sources: latitude/longitude (x/y), WKG, GeoJSON, reference to Layer/Feature
-- Support for multiple layers (basemap)
+- Support geographic data in dimension attributes and fact details
+- Geographic feature types: points, lines, polygons
+- Sources: latitude/longitude (x/y), WKT, GeoJSON, reference to layer feature
+- Multiple layers
- Layer types: GeoJson, WMTS
+- Selectable CRS
+
- Configurable view:
- Automatic bounding box, fixed bounding box, free view
- Configurable min/max zoom level
- Representation:
-` - cloropleth: applies to all possible, info on hover
- - points: info on hover
- - circles (measure in radius), info on hover
- - legends
+` - Cloropleth: applies to all possible, info on hover
+ - Points: info on hover
+ - Circles (measure in radius), info on hover
+ - Legends
Model configuration
-------------------
@@ -25,23 +27,43 @@ Model configuration
```
"dimensions": [
{
- "name": "date_created",
- "label": "Date Created",
- "role": "time",
- "info": {
- "cv-datefilter-hierarchy": "weekly"
- },
+ "label": "Country",
"levels": [
- {
- "name":"year",
- "label":"Year",
- "role": "year"
- },
- {
- "name":"quarter",
- "label":"Quarter",
- "role": "quarter"
- },
+ {
+ "attributes": [
+ "country_code",
+ "country_label",
+ "country_iso3",
+ ],
+ "key": "country_code",
+ "label": "Country",
+ "label_attribute": "geo_label",
+ "info": {
+ "cv-geo-source": "ref",
+ "cv-geo-ref-layer": "countries",
+ "cv-geo-ref-attribute": "country_iso3",
+ "cv-geo-map-layers": [ {
+ "name": "osm-standard",
+ "type": "xyz",
+ "attribution": "© Dataset owner",
+ "params": {
+ "url": "http://tile.openstreetmap.org/{z}/{x}/{y}.png"
+ }
+ }, {
+ "name": "countries",
+ "type": "geojson",
+ "attribution": "© Dataset owner",
+ "params": {
+ "url": "maps/ne_110m_admin_0_countries.geo.json"
+ }
+ } ]
+ },
+ "name": "country",
+ "role": "geo"
+ }
+ ],
+ "name": "country"
+ },
```