blob: 1150c39a6ade3cbaf2fc444cc13042d54deb80bc [file] [log] [blame]
/*
* Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LogService} from '../log.service';
const LONGITUDE_EXTENT = 180;
const LATITUDE_EXTENT = 75;
const GRID_EXTENT_X = 2000;
const GRID_EXTENT_Y = 1000;
const GRID_DIAGONAL = 2236; // 2236 is the length of the diagonal of the 2000x1000 box
const GRID_CENTRE_X = 500;
const GRID_CENTRE_Y = 500;
/**
* A model of the map bounds bottom left to top right in lat and long
*/
export interface MapBounds {
lngMin: number;
latMin: number;
lngMax: number;
latMax: number;
}
/**
* model of the topo2CurrentRegion Loc part of the MetaUi below
*/
export interface LocMeta {
lng: number;
lat: number;
}
/**
* model of the topo2CurrentRegion MetaUi from Device below
*/
export interface MetaUi {
equivLoc: LocMeta;
x: number;
y: number;
}
/**
* Model of the Zoom preferences
*/
export interface TopoZoomPrefs {
tx: number;
ty: number;
sc: number;
}
/**
* Utility class with static functions for scaling maps
*
* This is left as a class, so that the functions are loaded only as needed
*/
export class ZoomUtils {
static convertGeoToCanvas(location: LocMeta ): MetaUi {
const calcX = (LONGITUDE_EXTENT + location.lng) / (LONGITUDE_EXTENT * 2) * GRID_EXTENT_X - GRID_CENTRE_X;
const calcY = (LATITUDE_EXTENT - location.lat) / (LATITUDE_EXTENT * 2) * GRID_EXTENT_Y;
return <MetaUi>{
x: calcX,
y: calcY,
equivLoc: {
lat: location.lat,
lng: location.lng
}
};
}
static convertXYtoGeo(x: number, y: number): MetaUi {
const calcLong: number = (x + GRID_CENTRE_X) * 2 * LONGITUDE_EXTENT / GRID_EXTENT_X - LONGITUDE_EXTENT;
const calcLat: number = -(y * 2 * LATITUDE_EXTENT / GRID_EXTENT_Y - LATITUDE_EXTENT);
return <MetaUi>{
x: x,
y: y,
equivLoc: <LocMeta>{
lat: (calcLat === -0) ? 0 : calcLat,
lng: calcLong
}
};
}
/**
* This converts the bounds of a map loaded from a TopoGson file that has been
* converted in to a GEOJson format by d3
*
* The bounds are in latitude and longitude from bottom left (min) to top right (max)
*
* First they are converted in to SVG viewbox coordinates 0,0 top left 1000x1000
*
* The the zoom level is calculated by scaling to the grid diagonal
*
* Finally the translation is calculated by applying the zoom first, and then
* translating on the zoomed coordinate system
* @param mapBounds - the bounding box of the chosen map in lat and long
* @param log The LogService
*/
static convertBoundsToZoomLevel(mapBounds: MapBounds, log?: LogService): TopoZoomPrefs {
const min: MetaUi = this.convertGeoToCanvas(<LocMeta>{
lng: mapBounds.lngMin,
lat: mapBounds.latMin
});
const max: MetaUi = this.convertGeoToCanvas(<LocMeta>{
lng: mapBounds.lngMax,
lat: mapBounds.latMax
});
const diagonal = Math.sqrt(Math.pow(max.x - min.x, 2) + Math.pow(max.y - min.y, 2));
const centreX = (max.x - min.x) / 2 + min.x;
const centreY = (max.y - min.y) / 2 + min.y;
// Zoom works from the top left of the 1000x1000 viewbox
// The scale is applied first and then the translate is on the scaled coordinates
const zoomscale = 0.5 * GRID_DIAGONAL / ((diagonal < 100) ? 100 : diagonal); // Don't divide by zero
const zoomx = -centreX * zoomscale + GRID_CENTRE_X;
const zoomy = -centreY * zoomscale + GRID_CENTRE_Y;
// log.debug('MapBounds', mapBounds, 'XYMin', min, 'XYMax', max, 'Diag', diagonal,
// 'Centre', centreX, centreY, 'translate', zoomx, zoomy, 'Scale', zoomscale);
return <TopoZoomPrefs>{tx: zoomx, ty: zoomy, sc: zoomscale};
}
/**
* Calculate Zoom settings to fit the 1000x1000 grid in to the available window height
* less the banner height
*
* Scaling always happens from the top left 0,0
* If the height is greater than the width then no scaling is required - grid will
* need to fill the SVG canvas
* @param bannerHeight - the top band of the screen for the mast
* @param innerWidth - the actual width of the screen
* @param innerHeight - the actual height of the screen
* @return Zoom settings - scale and translate
*/
static zoomToWindowSize(bannerHeight: number, innerWidth: number, innerHeight: number): TopoZoomPrefs {
const newHeight = innerHeight - bannerHeight;
if (newHeight > innerWidth) {
return <TopoZoomPrefs>{
sc: 1.0,
tx: 0,
ty: 0
};
} else {
const scale = newHeight / innerWidth;
return <TopoZoomPrefs>{
sc: scale,
tx: (500 / scale - 500) * scale,
ty: 0
};
}
}
}