Skip to content

Commit

Permalink
Added geo components and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
robhawkes committed Feb 13, 2016
1 parent d745b5a commit 54e0156
Show file tree
Hide file tree
Showing 28 changed files with 1,816 additions and 17 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -1,3 +1,5 @@
tmp/

# Created by https://www.gitignore.io/api/node,osx,windows # Created by https://www.gitignore.io/api/node,osx,windows


### Node ### ### Node ###
Expand Down
6 changes: 3 additions & 3 deletions dist/vizicities.js
Expand Up @@ -64,9 +64,9 @@ return /******/ (function(modules) { // webpackBootstrap


var _World2 = _interopRequireDefault(_World); var _World2 = _interopRequireDefault(_World);


var _controlsControls = __webpack_require__(8); var _controlsIndex = __webpack_require__(8);


var _controlsControls2 = _interopRequireDefault(_controlsControls); var _controlsIndex2 = _interopRequireDefault(_controlsIndex);


var _layerEnvironmentEnvironmentLayer = __webpack_require__(11); var _layerEnvironmentEnvironmentLayer = __webpack_require__(11);


Expand All @@ -77,7 +77,7 @@ return /******/ (function(modules) { // webpackBootstrap


// Public API // Public API
World: _World2['default'], World: _World2['default'],
Controls: _controlsControls2['default'], Controls: _controlsIndex2['default'],
EnvironmentLayer: _layerEnvironmentEnvironmentLayer2['default'] EnvironmentLayer: _layerEnvironmentEnvironmentLayer2['default']
}; };


Expand Down
2 changes: 1 addition & 1 deletion dist/vizicities.min.js.map

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions gulpfile.babel.js
Expand Up @@ -156,6 +156,10 @@ function testBrowser() {
output: { output: {
filename: '__spec-build.js' filename: '__spec-build.js'
}, },
externals: {
// Proxy the global THREE variable to require('three')
'three': 'THREE'
},
module: { module: {
loaders: [ loaders: [
// This is what allows us to author in future JavaScript // This is what allows us to author in future JavaScript
Expand Down
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -80,6 +80,8 @@
}, },
"dependencies": { "dependencies": {
"eventemitter3": "^1.1.1", "eventemitter3": "^1.1.1",
"lodash.assign": "^4.0.2",
"proj4": "^2.3.12",
"three-orbit-controls": "github:robhawkes/three-orbit-controls" "three-orbit-controls": "github:robhawkes/three-orbit-controls"
} }
} }
File renamed without changes.
32 changes: 32 additions & 0 deletions src/geo/crs/CRS.EPSG3395.js
@@ -0,0 +1,32 @@
/*
* CRS.EPSG3395 (WGS 84 / World Mercator) CRS implementation.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG3395.js
*/

import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import Mercator from '../projection/Projection.Mercator';
import Transformation from '../../util/Transformation';

var _EPSG3395 = {
code: 'EPSG:3395',
projection: Mercator,

// Work out how to de-dupe this (scoping issue)
transformScale: 1 / (Math.PI * Mercator.R),

// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
transformation: (function() {
// TODO: Cannot use this.transformScale due to scope
var scale = 1 / (Math.PI * Mercator.R);

return new Transformation(scale, 0, -scale, 0);
}())
};

const EPSG3395 = extend({}, Earth, _EPSG3395);

export default EPSG3395;
38 changes: 38 additions & 0 deletions src/geo/crs/CRS.EPSG3857.js
@@ -0,0 +1,38 @@
/*
* CRS.EPSG3857 (WGS 84 / Pseudo-Mercator) CRS implementation.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG3857.js
*/

import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import SphericalMercator from '../projection/Projection.SphericalMercator';
import Transformation from '../../util/Transformation';

var _EPSG3857 = {
code: 'EPSG:3857',
projection: SphericalMercator,

// Work out how to de-dupe this (scoping issue)
transformScale: 1 / (Math.PI * SphericalMercator.R),

// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
transformation: (function() {
// TODO: Cannot use this.transformScale due to scope
var scale = 1 / (Math.PI * SphericalMercator.R);

return new Transformation(scale, 0, -scale, 0);
}())
};

const EPSG3857 = extend({}, Earth, _EPSG3857);

const EPSG900913 = extend({}, EPSG3857, {
code: 'EPSG:900913'
});

export {EPSG900913};

export default EPSG3857;
29 changes: 29 additions & 0 deletions src/geo/crs/CRS.EPSG4326.js
@@ -0,0 +1,29 @@
/*
* CRS.EPSG4326 is a CRS popular among advanced GIS specialists.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.EPSG4326.js
*/

import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import LatLon from '../projection/Projection.LatLon';
import Transformation from '../../util/Transformation';

var _EPSG4326 = {
code: 'EPSG:4326',
projection: LatLon,

// Work out how to de-dupe this (scoping issue)
transformScale: 1 / 180,

// Scale and transformation inputs changed to account for central origin in
// WebGL, instead of top-left origin used in Leaflet
//
// TODO: Cannot use this.transformScale due to scope
transformation: new Transformation(1 / 180, 0, -1 / 180, 0)
};

const EPSG4326 = extend({}, Earth, _EPSG4326);

export default EPSG4326;
121 changes: 121 additions & 0 deletions src/geo/crs/CRS.Earth.js
@@ -0,0 +1,121 @@
/*
* CRS.Earth is the base class for all CRS representing Earth.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.Earth.js
*/

import extend from 'lodash.assign';
import CRS from './CRS';

const Earth = {
wrapLon: [-180, 180],

R: 6378137,

// Distance between two geographical points using spherical law of cosines
// approximation or Haversine
//
// See: http://www.movable-type.co.uk/scripts/latlong.html
distance: function(latlon1, latlon2, accurate) {
var rad = Math.PI / 180;

var lat1;
var lat2;

var a;

if (!accurate) {
lat1 = latlon1[0] * rad;
lat2 = latlon2[0] * rad;

a = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos((latlon2[1] - latlon1[1]) * rad);

return this.R * Math.acos(Math.min(a, 1));
} else {
lat1 = latlon1[0] * rad;
lat2 = latlon2[0] * rad;

var lon1 = latlon1[1] * rad;
var lon2 = latlon2[1] * rad;

var deltaLat = lat2 - lat1;
var deltaLon = lon2 - lon1;

var halfDeltaLat = deltaLat / 2;
var halfDeltaLon = deltaLon / 2;

a = Math.sin(halfDeltaLat) * Math.sin(halfDeltaLat) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(halfDeltaLon) * Math.sin(halfDeltaLon);

var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

return this.R * c;
}
},

// Scale factor for converting between real metres and projected metres
//
// projectedMetres = realMetres * pointScale
// realMetres = projectedMetres / pointScale
//
// Defaults to a scale factor of 1 if no calculation method exists
//
// Probably need to run this through the CRS transformation or similar so the
// resulting scale is relative to the dimensions of the world space
// Eg. 1 metre in projected space is likly scaled up or down to some other
// number
pointScale: function(latlon, accurate) {
return (this.projection.pointScale) ? this.projection.pointScale(latlon, accurate) : [1, 1];
},

// Convert real metres to projected units
//
// Latitude scale is chosen because it fluctuates more than longitude
metresToProjected: function(metres, pointScale) {
return metres * pointScale[1];
},

// Convert projected units to real metres
//
// Latitude scale is chosen because it fluctuates more than longitude
projectedToMetres: function(projectedUnits, pointScale) {
return projectedUnits / pointScale[1];
},

// Convert real metres to a value in world (WebGL) units
metresToWorld: function(metres, pointScale, zoom) {
// Transform metres to projected metres using the latitude point scale
//
// Latitude scale is chosen because it fluctuates more than longitude
var projectedMetres = this.metresToProjected(metres, pointScale);

var scale = this.scale(zoom);

// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}

// Scale projected metres
var scaledMetres = (scale * (this.transformScale * projectedMetres)) / pointScale[1];

return scaledMetres;
},

// Convert world (WebGL) units to a value in real metres
worldToMetres: function(worldUnits, pointScale, zoom) {
var scale = this.scale(zoom);

// Half scale if using zoom as WebGL origin is in the centre, not top left
if (zoom) {
scale /= 2;
}

var projectedUnits = ((worldUnits / scale) / this.transformScale) * pointScale[1];
var realMetres = this.projectedToMetres(projectedUnits, pointScale);

return realMetres;
}
};

export default extend({}, CRS, Earth);
50 changes: 50 additions & 0 deletions src/geo/crs/CRS.Proj4.js
@@ -0,0 +1,50 @@
/*
* CRS.Proj4 for any Proj4-supported CRS.
*/

import extend from 'lodash.assign';
import Earth from './CRS.Earth';
import Proj4Projection from '../projection/Projection.Proj4';
import Transformation from '../../util/Transformation';

var _Proj4 = function(code, def, bounds) {
var projection = Proj4Projection(def, bounds);

// Transformation calcuations
var diffX = projection.bounds[1][0] - projection.bounds[0][0];
var diffY = projection.bounds[1][1] - projection.bounds[0][1];

var halfX = diffX / 2;
var halfY = diffY / 2;

// This is the raw scale factor
var scaleX = 1 / halfX;
var scaleY = 1 / halfY;

// Find the minimum scale factor
//
// The minimum scale factor comes from the largest side and is the one
// you want to use for both axis so they stay relative in dimension
var scale = Math.min(scaleX, scaleY);

// Find amount to offset each axis by to make the central point lie on
// the [0,0] origin
var offsetX = scale * (projection.bounds[0][0] + halfX);
var offsetY = scale * (projection.bounds[0][1] + halfY);

return {
code: code,
projection: projection,

transformScale: scale,

// Map the input to a [-1,1] range with [0,0] in the centre
transformation: new Transformation(scale, -offsetX, -scale, offsetY)
};
};

const Proj4 = function(code, def, bounds) {
return extend({}, Earth, _Proj4(code, def, bounds));
};

export default Proj4;
47 changes: 47 additions & 0 deletions src/geo/crs/CRS.Simple.js
@@ -0,0 +1,47 @@
/*
* A simple CRS that can be used for flat non-Earth maps like panoramas or game
* maps.
*
* Based on:
* https://github.com/Leaflet/Leaflet/blob/master/src/geo/crs/CRS.Simple.js
*/

import extend from 'lodash.assign';
import CRS from './CRS';
import LatLon from '../projection/Projection.LatLon';
import Transformation from '../../util/Transformation';

var _Simple = {
projection: LatLon,

// Straight 1:1 mapping (-1, -1 would be top-left)
transformation: new Transformation(1, 0, 1, 0),

scale: function(zoom) {
// If zoom is provided then return scale based on map tile zoom
if (zoom) {
return Math.pow(2, zoom);
// Else, make no change to scale – may need to increase this or make it a
// user-definable variable
} else {
return 1;
}
},

zoom: function(scale) {
return Math.log(scale) / Math.LN2;
},

distance: function(latlon1, latlon2) {
var dx = latlon2[1] - latlon1[1];
var dy = latlon2[0] - latlon1[0];

return Math.sqrt(dx * dx + dy * dy);
},

infinite: true
};

const Simple = extend({}, CRS, _Simple);

export default Simple;

0 comments on commit 54e0156

Please sign in to comment.