Skip to content

Commit

Permalink
Merge pull request #570 from OpenGeoscience/faster-transforms
Browse files Browse the repository at this point in the history
Speed up transforms by caching the transform class instances.
  • Loading branch information
manthey committed May 9, 2016
2 parents 445558b + 1a60f79 commit 6dcb286
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 120 deletions.
154 changes: 70 additions & 84 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,18 @@ var map = function (arg) {
* [0, width] and [0, height] instead. */
var mcx = ((m_maxBounds.left || 0) + (m_maxBounds.right || 0)) / 2,
mcy = ((m_maxBounds.bottom || 0) + (m_maxBounds.top || 0)) / 2;
m_maxBounds.left = transform.transformCoordinates(m_ingcs, m_gcs, [{
m_maxBounds.left = transform.transformCoordinates(m_ingcs, m_gcs, {
x: m_maxBounds.left !== undefined ? m_maxBounds.left : -180, y: mcy
}])[0].x;
m_maxBounds.right = transform.transformCoordinates(m_ingcs, m_gcs, [{
}).x;
m_maxBounds.right = transform.transformCoordinates(m_ingcs, m_gcs, {
x: m_maxBounds.right !== undefined ? m_maxBounds.right : 180, y: mcy
}])[0].x;
}).x;
m_maxBounds.top = (m_maxBounds.top !== undefined ?
transform.transformCoordinates(m_ingcs, m_gcs, [{
x: mcx, y: m_maxBounds.top}])[0].y : m_maxBounds.right);
transform.transformCoordinates(m_ingcs, m_gcs, {
x: mcx, y: m_maxBounds.top}).y : m_maxBounds.right);
m_maxBounds.bottom = (m_maxBounds.bottom !== undefined ?
transform.transformCoordinates(m_ingcs, m_gcs, [{
x: mcx, y: m_maxBounds.bottom}])[0].y : m_maxBounds.left);
transform.transformCoordinates(m_ingcs, m_gcs, {
x: mcx, y: m_maxBounds.bottom}).y : m_maxBounds.left);
m_unitsPerPixel = (arg.unitsPerPixel || (
m_maxBounds.right - m_maxBounds.left) / 256);

Expand Down Expand Up @@ -410,19 +410,20 @@ var map = function (arg) {
*/
////////////////////////////////////////////////////////////////////////////
this.pan = function (delta, ignoreDiscreteZoom) {
var evt, unit;
evt = {
var evt = {
geo: {},
screenDelta: delta
};

unit = m_this.unitsPerPixel(m_zoom);
if (delta.x || delta.y) {
var unit = m_this.unitsPerPixel(m_zoom);

var sinr = Math.sin(m_rotation), cosr = Math.cos(m_rotation);
m_camera.pan({
x: (delta.x * cosr - (-delta.y) * sinr) * unit,
y: (delta.x * sinr + (-delta.y) * cosr) * unit
});
var sinr = Math.sin(m_rotation), cosr = Math.cos(m_rotation);
m_camera.pan({
x: (delta.x * cosr - (-delta.y) * sinr) * unit,
y: (delta.x * sinr + (-delta.y) * cosr) * unit
});
}
/* If m_clampBounds* is true, clamp the pan */
var bounds = fix_bounds(m_camera.bounds, m_rotation);
if (bounds !== m_camera.bounds) {
Expand Down Expand Up @@ -526,13 +527,10 @@ var map = function (arg) {
m_zoom, m_center, m_rotation, null, ignoreDiscreteZoom), m_rotation);
m_this.modified();
// trigger a pan event
m_this.geoTrigger(
geo_event.pan,
{
geo: coordinates,
screenDelta: null
}
);
m_this.geoTrigger(geo_event.pan, {
geo: coordinates,
screenDelta: null
});
return m_this;
};

Expand Down Expand Up @@ -696,12 +694,17 @@ var map = function (arg) {
this.gcsToWorld = function (c, gcs) {
gcs = (gcs === null ? m_gcs : (gcs === undefined ? m_ingcs : gcs));
if (gcs !== m_gcs) {
c = transform.transformCoordinates(gcs, m_gcs, [c])[0];
c = transform.transformCoordinates(gcs, m_gcs, c);
}
return transform.affineForward(
{origin: m_origin},
[c]
)[0];
if (m_origin.x || m_origin.y || m_origin.z) {
c = transform.affineForward(
{origin: m_origin},
[c]
)[0];
} else if (!('z' in c)) {
c = {x: c.x, y: c.y, z: 0};
}
return c;
};

////////////////////////////////////////////////////////////////////////////
Expand All @@ -717,13 +720,17 @@ var map = function (arg) {
*/
////////////////////////////////////////////////////////////////////////////
this.worldToGcs = function (c, gcs) {
c = transform.affineInverse(
{origin: m_origin},
[c]
)[0];
if (m_origin.x || m_origin.y || m_origin.z) {
c = transform.affineInverse(
{origin: m_origin},
[c]
)[0];
} else if (!('z' in c)) {
c = {x: c.x, y: c.y, z: 0};
}
gcs = (gcs === null ? m_gcs : (gcs === undefined ? m_ingcs : gcs));
if (gcs !== m_gcs) {
c = transform.transformCoordinates(m_gcs, gcs, [c])[0];
c = transform.transformCoordinates(m_gcs, gcs, c);
}
return c;
};
Expand Down Expand Up @@ -1038,7 +1045,7 @@ var map = function (arg) {
var transitionEnd = $.extend(true, {}, m_transition.end);
if (transitionEnd.center && m_gcs !== m_ingcs) {
transitionEnd.center = transform.transformCoordinates(
m_gcs, m_ingcs, [transitionEnd.center])[0];
m_gcs, m_ingcs, transitionEnd.center);
}
m_queuedTransition = $.extend(
{}, transitionEnd || {}, m_queuedTransition || {}, opts);
Expand Down Expand Up @@ -1086,8 +1093,7 @@ var map = function (arg) {
opts = $.extend(true, {}, opts);
opts.center = util.normalizeCoordinates(opts.center);
if (gcs !== m_gcs) {
opts.center = transform.transformCoordinates(gcs, m_gcs, [
opts.center])[0];
opts.center = transform.transformCoordinates(gcs, m_gcs, opts.center);
}
}
opts = $.extend(true, {}, defaultOpts, opts);
Expand All @@ -1110,37 +1116,17 @@ var map = function (arg) {
zoomOrigin: opts.zoomOrigin
};

if (opts.zCoord) {
m_transition.interp = opts.interp(
[
m_transition.start.center.x,
m_transition.start.center.y,
zoom2z(m_transition.start.zoom),
m_transition.start.rotation
],
[
m_transition.end.center.x,
m_transition.end.center.y,
zoom2z(m_transition.end.zoom),
m_transition.end.rotation
]
);
} else {
m_transition.interp = opts.interp(
[
m_transition.start.center.x,
m_transition.start.center.y,
m_transition.start.zoom,
m_transition.start.rotation
],
[
m_transition.end.center.x,
m_transition.end.center.y,
m_transition.end.zoom,
m_transition.end.rotation
]
);
}
m_transition.interp = opts.interp([
m_transition.start.center.x,
m_transition.start.center.y,
opts.zCoord ? zoom2z(m_transition.start.zoom) : m_transition.start.zoom,
m_transition.start.rotation
], [
m_transition.end.center.x,
m_transition.end.center.y,
opts.zCoord ? zoom2z(m_transition.end.zoom) : m_transition.end.zoom,
m_transition.end.rotation
]);

function anim(time) {
var done = m_transition.done,
Expand Down Expand Up @@ -1303,33 +1289,33 @@ var map = function (arg) {
gcs = (gcs === null ? m_gcs : (gcs === undefined ? m_ingcs : gcs));
if (bounds === undefined) {
return {
left: transform.transformCoordinates(m_gcs, gcs, [{
x: m_maxBounds.left, y: 0}])[0].x,
right: transform.transformCoordinates(m_gcs, gcs, [{
x: m_maxBounds.right, y: 0}])[0].x,
bottom: transform.transformCoordinates(m_gcs, gcs, [{
x: 0, y: m_maxBounds.bottom}])[0].y,
top: transform.transformCoordinates(m_gcs, gcs, [{
x: 0, y: m_maxBounds.top}])[0].y
left: transform.transformCoordinates(m_gcs, gcs, {
x: m_maxBounds.left, y: 0}).x,
right: transform.transformCoordinates(m_gcs, gcs, {
x: m_maxBounds.right, y: 0}).x,
bottom: transform.transformCoordinates(m_gcs, gcs, {
x: 0, y: m_maxBounds.bottom}).y,
top: transform.transformCoordinates(m_gcs, gcs, {
x: 0, y: m_maxBounds.top}).y
};
}
var cx = ((bounds.left || 0) + (bounds.right || 0)) / 2,
cy = ((bounds.bottom || 0) + (bounds.top || 0)) / 2;
if (bounds.left !== undefined) {
m_maxBounds.left = transform.transformCoordinates(gcs, m_gcs, [{
x: bounds.left, y: cy}])[0].x;
m_maxBounds.left = transform.transformCoordinates(gcs, m_gcs, {
x: bounds.left, y: cy}).x;
}
if (bounds.right !== undefined) {
m_maxBounds.right = transform.transformCoordinates(gcs, m_gcs, [{
x: bounds.right, y: cy}])[0].x;
m_maxBounds.right = transform.transformCoordinates(gcs, m_gcs, {
x: bounds.right, y: cy}).x;
}
if (bounds.bottom !== undefined) {
m_maxBounds.bottom = transform.transformCoordinates(gcs, m_gcs, [{
x: cx, y: bounds.bottom}])[0].y;
m_maxBounds.bottom = transform.transformCoordinates(gcs, m_gcs, {
x: cx, y: bounds.bottom}).y;
}
if (bounds.top !== undefined) {
m_maxBounds.top = transform.transformCoordinates(gcs, m_gcs, [{
x: cx, y: bounds.top}])[0].y;
m_maxBounds.top = transform.transformCoordinates(gcs, m_gcs, {
x: cx, y: bounds.top}).y;
}
reset_minimum_zoom();
m_this.zoom(m_zoom);
Expand Down Expand Up @@ -1380,7 +1366,7 @@ var map = function (arg) {
y: (bounds.top + bounds.bottom) / 2 - m_origin.y
};
if (gcs !== m_gcs) {
center = transform.transformCoordinates(m_gcs, gcs, [center])[0];
center = transform.transformCoordinates(m_gcs, gcs, center);
}
return {
zoom: zoom,
Expand Down
2 changes: 1 addition & 1 deletion src/quadFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ var quadFeature = function (arg) {
map = m_this.layer().map(),
order1 = [0, 1, 2, 0], order2 = [1, 2, 3, 1];
coordinate = transform.transformCoordinates(
map.ingcs(), map.gcs(), [coordinate])[0];
map.ingcs(), map.gcs(), coordinate);
if (!m_quads) {
this._generateQuads();
}
Expand Down
95 changes: 60 additions & 35 deletions src/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,30 @@ var proj4 = require('proj4');
*/
//////////////////////////////////////////////////////////////////////////////

var transformCache = {};
/* Up to maxTransformCacheSize squared might be cached. When the maximum cache
* size is reached, the cache is completely emptied. Since we probably won't
* be rapidly switching between a large number of transforms, this is adequate
* simple behavior. */
var maxTransformCacheSize = 10;

var transform = function (options) {
'use strict';
if (!(this instanceof transform)) {
return new transform(options);
options = options || {};
if (!(options.source in transformCache)) {
if (Object.size(transformCache) >= maxTransformCacheSize) {
transformCache = {};
}
transformCache[options.source] = {};
}
if (!(options.target in transformCache[options.source])) {
if (Object.size(transformCache[options.source]) >= maxTransformCacheSize) {
transformCache[options.source] = {};
}
transformCache[options.source][options.target] = new transform(options);
}
return transformCache[options.source][options.target];
}

var m_this = this,
Expand Down Expand Up @@ -227,8 +247,39 @@ transform.transformCoordinates = function (
return coordinates;
}

var i, count, offset, xAcc, yAcc, zAcc, writer, output, projPoint,
trans = transform({source: srcPrj, target: tgtPrj});
var trans = transform({source: srcPrj, target: tgtPrj}), output;
if (coordinates instanceof Object && 'x' in coordinates && 'y' in coordinates) {
output = trans.forward({x: coordinates.x, y: coordinates.y, z: coordinates.z || 0});
if ('z' in coordinates) {
return output;
}
return {x: output.x, y: output.y};
}
if (coordinates instanceof Array && coordinates.length === 1 && coordinates[0] instanceof Object && 'x' in coordinates[0] && 'y' in coordinates[0]) {
output = trans.forward({x: coordinates[0].x, y: coordinates[0].y, z: coordinates[0].z || 0});
if ('z' in coordinates[0]) {
return [output];
}
return [{x: output.x, y: output.y}];
}
return transform.transformCoordinatesArray(trans, coordinates, numberOfComponents);
};

/**
* Transform an array of coordinates from one projection into another. The
* transformation may occur in place (modifying the input coordinate array),
* depending on the input format. The coordinates can be an array of 2 or 3
* values, or an array of either of those, or a single flat array with 2 or 3
* components per coordinate. The array is modified in place.
*
* @param {object} trans The transformation object.
* @param {geoPosition[]} coordinates An array of coordinate objects
* @param {number} numberOfComponents for flat arrays, either 2 or 3.
*
* @returns {geoPosition[]} The transformed coordinates
*/
transform.transformCoordinatesArray = function (trans, coordinates, numberOfComponents) {
var i, count, offset, xAcc, yAcc, zAcc, writer, output, projPoint;

/// Default Z accessor
zAcc = function () {
Expand Down Expand Up @@ -262,7 +313,7 @@ transform.transformCoordinates = function (
output[index] = [x, y, z];
};
} else {
throw 'Invalid coordinates. Requires two or three components per array';
throw new Error('Invalid coordinates. Requires two or three components per array');
}
} else {
if (coordinates.length === 2) {
Expand Down Expand Up @@ -321,10 +372,10 @@ transform.transformCoordinates = function (
};
}
} else {
throw 'Number of components should be two or three';
throw new Error('Number of components should be two or three');
}
} else {
throw 'Invalid coordinates';
throw new Error('Invalid coordinates');
}
}
}
Expand Down Expand Up @@ -353,28 +404,8 @@ transform.transformCoordinates = function (
output[index] = {x: x, y: y};
};
}
} else if (coordinates && 'x' in coordinates && 'y' in coordinates) {
xAcc = function () {
return coordinates.x;
};
yAcc = function () {
return coordinates.y;
};

if ('z' in coordinates) {
zAcc = function () {
return coordinates.z;
};
writer = function (index, x, y, z) {
output = {x: x, y: y, z: z};
};
} else {
writer = function (index, x, y) {
output = {x: x, y: y};
};
}
} else {
throw 'Invalid coordinates';
throw new Error('Invalid coordinates');
}
}

Expand All @@ -395,14 +426,8 @@ transform.transformCoordinates = function (
} else {
handleArrayCoordinates();
}
} else if (coordinates && coordinates instanceof Object) {
count = 1;
offset = 1;
if (coordinates && 'x' in coordinates && 'y' in coordinates) {
handleObjectCoordinates();
} else {
throw 'Coordinates are not valid';
}
} else {
throw new Error('Coordinates are not valid');
}

for (i = 0; i < count; i += offset) {
Expand Down
Loading

0 comments on commit 6dcb286

Please sign in to comment.