blob: 63fa4cef2140ac445bcf48fc2c20372821f4f15b [file] [log] [blame]
Simon Huntf8173382015-01-13 14:12:09 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 ONOS GUI -- SVG -- GeoData Service
Simon Huntf8173382015-01-13 14:12:09 -080019 */
20
21/*
Simon Hunta7b6a6b2015-01-13 19:53:09 -080022 The GeoData Service facilitates the fetching and caching of TopoJSON data
23 from the server, as well as providing a way of creating a path generator
24 for that data, to be used to render the map in an SVG layer.
Simon Huntf8173382015-01-13 14:12:09 -080025
Simon Hunta7b6a6b2015-01-13 19:53:09 -080026 A TopoData object can be fetched by ID. IDs that start with an asterisk
Simon Huntf8173382015-01-13 14:12:09 -080027 identify maps bundled with the GUI. IDs that do not start with an
Simon Hunta7b6a6b2015-01-13 19:53:09 -080028 asterisk are assumed to be URLs to externally provided data.
Simon Huntf8173382015-01-13 14:12:09 -080029
Simon Hunta7b6a6b2015-01-13 19:53:09 -080030 var topodata = GeoDataService.fetchTopoData('*continental-us');
Simon Huntf8173382015-01-13 14:12:09 -080031
Simon Hunta7b6a6b2015-01-13 19:53:09 -080032 The path generator can then be created for that data-set:
33
34 var gen = GeoDataService.createPathGenerator(topodata, opts);
35
36 opts is an optional argument that allows the override of default settings:
37 {
38 objectTag: 'states',
39 projection: d3.geo.mercator(),
40 logicalSize: 1000,
41 mapFillScale: .95
42 };
43
44 The returned object (gen) comprises transformed data (TopoJSON -> GeoJSON),
45 the D3 path generator function, and the settings used ...
46
47 {
48 geodata: { ... },
49 pathgen: function (...) { ... },
50 settings: { ... }
51 }
Simon Huntf8173382015-01-13 14:12:09 -080052 */
53
54(function () {
55 'use strict';
56
57 // injected references
58 var $log, $http, fs;
59
60 // internal state
61 var cache = d3.map(),
62 bundledUrlPrefix = '../data/map/';
63
64 function getUrl(id) {
65 if (id[0] === '*') {
66 return bundledUrlPrefix + id.slice(1) + '.json';
67 }
68 return id + '.json';
69 }
70
71 angular.module('onosSvg')
72 .factory('GeoDataService', ['$log', '$http', 'FnService',
73 function (_$log_, _$http_, _fs_) {
74 $log = _$log_;
75 $http = _$http_;
76 fs = _fs_;
77
78 // start afresh...
79 function clearCache() {
80 cache = d3.map();
81 }
82
83 // returns a promise decorated with:
84 // .meta -- id, url, and whether the data was cached
Simon Hunta7b6a6b2015-01-13 19:53:09 -080085 // .topodata -- TopoJSON data (on response from server)
Simon Huntf8173382015-01-13 14:12:09 -080086
Simon Hunta7b6a6b2015-01-13 19:53:09 -080087 function fetchTopoData(id) {
Simon Huntf8173382015-01-13 14:12:09 -080088 if (!fs.isS(id)) {
89 return null;
90 }
91 var url = getUrl(id),
92 promise = cache.get(id);
93
94 if (!promise) {
95 // need to fetch the data, build the object,
96 // cache it, and return it.
97 promise = $http.get(url);
98
99 promise.meta = {
100 id: id,
101 url: url,
102 wasCached: false
103 };
104
105 promise.then(function (response) {
106 // success
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800107 promise.topodata = response.data;
Simon Huntf8173382015-01-13 14:12:09 -0800108 }, function (response) {
109 // error
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800110 $log.warn('Failed to retrieve map TopoJSON data: ' + url,
Simon Huntf8173382015-01-13 14:12:09 -0800111 response.status, response.data);
112 });
113
114 cache.set(id, promise);
115
116 } else {
117 promise.meta.wasCached = true;
118 }
119
120 return promise;
121 }
122
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800123 var defaultGenSettings = {
124 objectTag: 'states',
125 projection: d3.geo.mercator(),
126 logicalSize: 1000,
127 mapFillScale: .95
128 };
129
130 // converts given TopoJSON-format data into corresponding GeoJSON
131 // data, and creates a path generator for that data.
132 function createPathGenerator(topoData, opts) {
Simon Hunt404f6b22015-01-21 14:00:56 -0800133 var settings = angular.extend({}, defaultGenSettings, opts),
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800134 topoObject = topoData.objects[settings.objectTag],
135 geoData = topojson.feature(topoData, topoObject),
136 proj = settings.projection,
137 dim = settings.logicalSize,
138 mfs = settings.mapFillScale,
139 path = d3.geo.path().projection(proj);
140
141 // adjust projection scale and translation to fill the view
142 // with the map
Simon Huntf8173382015-01-13 14:12:09 -0800143
144 // start with unit scale, no translation..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800145 proj.scale(1).translate([0, 0]);
Simon Huntf8173382015-01-13 14:12:09 -0800146
147 // figure out dimensions of map data..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800148 var b = path.bounds(geoData),
Simon Huntf8173382015-01-13 14:12:09 -0800149 x1 = b[0][0],
150 y1 = b[0][1],
151 x2 = b[1][0],
152 y2 = b[1][1],
153 dx = x2 - x1,
154 dy = y2 - y1,
155 x = (x1 + x2) / 2,
156 y = (y1 + y2) / 2;
157
158 // size map to 95% of minimum dimension to fill space..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800159 var s = mfs / Math.min(dx / dim, dy / dim),
160 t = [dim / 2 - s * x, dim / 2 - s * y];
Simon Huntf8173382015-01-13 14:12:09 -0800161
162 // set new scale, translation on the projection..
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800163 proj.scale(s).translate(t);
Simon Huntf8173382015-01-13 14:12:09 -0800164
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800165 // return the results
166 return {
167 geodata: geoData,
168 pathgen: path,
169 settings: settings
170 };
171 }
Simon Huntf8173382015-01-13 14:12:09 -0800172
173 return {
174 clearCache: clearCache,
Simon Hunta7b6a6b2015-01-13 19:53:09 -0800175 fetchTopoData: fetchTopoData,
176 createPathGenerator: createPathGenerator
Simon Huntf8173382015-01-13 14:12:09 -0800177 };
178 }]);
179}());