blob: 1150c39a6ade3cbaf2fc444cc13042d54deb80bc [file] [log] [blame]
Sean Condon1ae15802019-03-02 09:07:18 +00001/*
2 * Copyright 2019-present Open Networking Foundation
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
18import {LogService} from '../log.service';
19
20const LONGITUDE_EXTENT = 180;
21const LATITUDE_EXTENT = 75;
22const GRID_EXTENT_X = 2000;
23const GRID_EXTENT_Y = 1000;
24const GRID_DIAGONAL = 2236; // 2236 is the length of the diagonal of the 2000x1000 box
25const GRID_CENTRE_X = 500;
26const GRID_CENTRE_Y = 500;
27
28
29/**
30 * A model of the map bounds bottom left to top right in lat and long
31 */
32export interface MapBounds {
33 lngMin: number;
34 latMin: number;
35 lngMax: number;
36 latMax: number;
37}
38
39/**
40 * model of the topo2CurrentRegion Loc part of the MetaUi below
41 */
42export interface LocMeta {
43 lng: number;
44 lat: number;
45}
46
47/**
48 * model of the topo2CurrentRegion MetaUi from Device below
49 */
50export interface MetaUi {
51 equivLoc: LocMeta;
52 x: number;
53 y: number;
54}
55
56/**
57 * Model of the Zoom preferences
58 */
59export interface TopoZoomPrefs {
60 tx: number;
61 ty: number;
62 sc: number;
63}
64
65/**
66 * Utility class with static functions for scaling maps
67 *
68 * This is left as a class, so that the functions are loaded only as needed
69 */
70export class ZoomUtils {
71 static convertGeoToCanvas(location: LocMeta ): MetaUi {
72 const calcX = (LONGITUDE_EXTENT + location.lng) / (LONGITUDE_EXTENT * 2) * GRID_EXTENT_X - GRID_CENTRE_X;
73 const calcY = (LATITUDE_EXTENT - location.lat) / (LATITUDE_EXTENT * 2) * GRID_EXTENT_Y;
74 return <MetaUi>{
75 x: calcX,
76 y: calcY,
77 equivLoc: {
78 lat: location.lat,
79 lng: location.lng
80 }
81 };
82 }
83
84 static convertXYtoGeo(x: number, y: number): MetaUi {
85 const calcLong: number = (x + GRID_CENTRE_X) * 2 * LONGITUDE_EXTENT / GRID_EXTENT_X - LONGITUDE_EXTENT;
86 const calcLat: number = -(y * 2 * LATITUDE_EXTENT / GRID_EXTENT_Y - LATITUDE_EXTENT);
87 return <MetaUi>{
88 x: x,
89 y: y,
90 equivLoc: <LocMeta>{
91 lat: (calcLat === -0) ? 0 : calcLat,
92 lng: calcLong
93 }
94 };
95 }
96
97 /**
98 * This converts the bounds of a map loaded from a TopoGson file that has been
99 * converted in to a GEOJson format by d3
100 *
101 * The bounds are in latitude and longitude from bottom left (min) to top right (max)
102 *
103 * First they are converted in to SVG viewbox coordinates 0,0 top left 1000x1000
104 *
105 * The the zoom level is calculated by scaling to the grid diagonal
106 *
107 * Finally the translation is calculated by applying the zoom first, and then
108 * translating on the zoomed coordinate system
109 * @param mapBounds - the bounding box of the chosen map in lat and long
110 * @param log The LogService
111 */
Sean Condonff85fbe2019-03-16 14:28:46 +0000112 static convertBoundsToZoomLevel(mapBounds: MapBounds, log?: LogService): TopoZoomPrefs {
Sean Condon1ae15802019-03-02 09:07:18 +0000113
114 const min: MetaUi = this.convertGeoToCanvas(<LocMeta>{
115 lng: mapBounds.lngMin,
116 lat: mapBounds.latMin
117 });
118
119 const max: MetaUi = this.convertGeoToCanvas(<LocMeta>{
120 lng: mapBounds.lngMax,
121 lat: mapBounds.latMax
122 });
123
124 const diagonal = Math.sqrt(Math.pow(max.x - min.x, 2) + Math.pow(max.y - min.y, 2));
125 const centreX = (max.x - min.x) / 2 + min.x;
126 const centreY = (max.y - min.y) / 2 + min.y;
127 // Zoom works from the top left of the 1000x1000 viewbox
128 // The scale is applied first and then the translate is on the scaled coordinates
129 const zoomscale = 0.5 * GRID_DIAGONAL / ((diagonal < 100) ? 100 : diagonal); // Don't divide by zero
130 const zoomx = -centreX * zoomscale + GRID_CENTRE_X;
131 const zoomy = -centreY * zoomscale + GRID_CENTRE_Y;
132
133 // log.debug('MapBounds', mapBounds, 'XYMin', min, 'XYMax', max, 'Diag', diagonal,
134 // 'Centre', centreX, centreY, 'translate', zoomx, zoomy, 'Scale', zoomscale);
135
136 return <TopoZoomPrefs>{tx: zoomx, ty: zoomy, sc: zoomscale};
137 }
Sean Condonff85fbe2019-03-16 14:28:46 +0000138
139 /**
140 * Calculate Zoom settings to fit the 1000x1000 grid in to the available window height
141 * less the banner height
142 *
143 * Scaling always happens from the top left 0,0
144 * If the height is greater than the width then no scaling is required - grid will
145 * need to fill the SVG canvas
146 * @param bannerHeight - the top band of the screen for the mast
147 * @param innerWidth - the actual width of the screen
148 * @param innerHeight - the actual height of the screen
149 * @return Zoom settings - scale and translate
150 */
151 static zoomToWindowSize(bannerHeight: number, innerWidth: number, innerHeight: number): TopoZoomPrefs {
152 const newHeight = innerHeight - bannerHeight;
153 if (newHeight > innerWidth) {
154 return <TopoZoomPrefs>{
155 sc: 1.0,
156 tx: 0,
157 ty: 0
158 };
159 } else {
160 const scale = newHeight / innerWidth;
161 return <TopoZoomPrefs>{
162 sc: scale,
163 tx: (500 / scale - 500) * scale,
164 ty: 0
165 };
166 }
167 }
Sean Condon1ae15802019-03-02 09:07:18 +0000168}