Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Foreign Members to @turf/clone #904

Merged
merged 3 commits into from
Aug 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions packages/turf-clone/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ const fixtures = [
/**
* Benchmark Results
*
* Point: 0.149ms
* LineString: 0.036ms
* Polygon: 0.067ms
* FeatureCollection: 0.043ms
* Point x 6,972,739 ops/sec ±5.50% (86 runs sampled)
* Point -- clones entire object x 357,437 ops/sec ±0.95% (88 runs sampled)
* LineString x 1,502,039 ops/sec ±1.01% (86 runs sampled)
* LineString -- clones entire object x 291,562 ops/sec ±1.00% (88 runs sampled)
* Polygon x 723,111 ops/sec ±1.06% (87 runs sampled)
* Polygon -- clones entire object x 227,034 ops/sec ±0.62% (91 runs sampled)
* FeatureCollection x 370,012 ops/sec ±1.25% (87 runs sampled)
* FeatureCollection -- clones entire object x 91,582 ops/sec ±0.81% (86 runs sampled)
* Point: 0.380ms
* LineString: 1.302ms
* Polygon: 1.402ms
* FeatureCollection: 0.293ms
* Point x 1,889,028 ops/sec ±1.50% (90 runs sampled)
* Point -- JSON.parse + JSON.stringify x 363,861 ops/sec ±1.02% (89 runs sampled)
* LineString x 932,348 ops/sec ±1.34% (84 runs sampled)
* LineString -- JSON.parse + JSON.stringify x 296,087 ops/sec ±1.07% (92 runs sampled)
* Polygon x 577,070 ops/sec ±1.24% (86 runs sampled)
* Polygon -- JSON.parse + JSON.stringify x 228,373 ops/sec ±1.03% (88 runs sampled)
* FeatureCollection x 248,164 ops/sec ±1.50% (84 runs sampled)
* FeatureCollection -- JSON.parse + JSON.stringify x 92,873 ops/sec ±0.91% (88 runs sampled)
*/
const suite = new Benchmark.Suite('turf-clone');
for (const fixture of fixtures) {
Expand All @@ -36,7 +36,7 @@ for (const fixture of fixtures) {
clone(fixture, true);
console.timeEnd(name);
suite.add(name, () => clone(fixture));
suite.add(name + ' -- clones entire object', () => clone(fixture, true));
suite.add(name + ' -- JSON.parse + JSON.stringify', () => JSON.parse(JSON.stringify(fixture)));
}

suite
Expand Down
2 changes: 1 addition & 1 deletion packages/turf-clone/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ type Types = GeoJSON.FeatureCollection<any> | GeoJSON.Feature<any> | GeoJSON.Geo
/**
* http://turfjs.org/docs/#clone
*/
declare function clone<T extends Types>(geojson: T, cloneAll?: boolean): T;
declare function clone<T extends Types>(geojson: T): T;
declare namespace clone { }
export = clone;
134 changes: 83 additions & 51 deletions packages/turf-clone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,82 @@
*
* @name clone
* @param {GeoJSON} geojson GeoJSON Object
* @param {Boolean} [cloneAll=false] clones entire GeoJSON object, using JSON.parse(JSON.stringify(geojson))
* @returns {GeoJSON} cloned GeoJSON Object
* @example
* var line = turf.lineString([[-74, 40], [-78, 42], [-82, 35]]);
*
* var lineCloned = turf.clone(line);
*/
module.exports = function (geojson, cloneAll) {
module.exports = function (geojson) {
if (!geojson) throw new Error('geojson is required');
if (cloneAll && typeof cloneAll !== 'boolean') throw new Error('cloneAll must be a Boolean');

// Clone entire object (3-20x slower)
if (cloneAll) return JSON.parse(JSON.stringify(geojson));

// Clones only GeoJSON fields
return clone(geojson);
switch (geojson.type) {
case 'Feature':
return cloneFeature(geojson);
case 'FeatureCollection':
return cloneFeatureCollection(geojson);
case 'Point':
case 'LineString':
case 'Polygon':
case 'MultiPoint':
case 'MultiLineString':
case 'MultiPolygon':
case 'GeometryCollection':
return cloneGeometry(geojson);
default:
throw new Error('unknown GeoJSON type');
}
};

/**
* Clone
* Clone Feature
*
* @private
* @param {GeoJSON} geojson GeoJSON Feature or Geometry
* @returns {GeoJSON} cloned Feature
* @param {Feature<any>} geojson GeoJSON Feature
* @returns {Feature<any>} cloned Feature
*/
function clone(geojson) {
// Geometry Object
if (geojson.coordinates) return cloneGeometry(geojson);

// Feature
if (geojson.type === 'Feature') return cloneFeature(geojson);

// Feature Collection
if (geojson.type === 'FeatureCollection') return cloneFeatureCollection(geojson);

// Geometry Collection
if (geojson.type === 'GeometryCollection') return cloneGeometry(geojson);
function cloneFeature(geojson) {
var cloned = {type: 'Feature'};
// Preserve Foreign Members
Object.keys(geojson).forEach(function (key) {
switch (key) {
case 'type':
case 'properties':
case 'geometry':
return;
default:
cloned[key] = geojson[key];
}
});
// Add properties & geometry last
cloned.properties = cloneProperties(geojson.properties);
cloned.geometry = cloneGeometry(geojson.geometry);
return cloned;
}

/**
* Clone Feature
* Clone Properties
*
* @private
* @param {Feature<any>} feature GeoJSON Feature
* @returns {Feature<any>} cloned Feature
* @param {Object} properties GeoJSON Properties
* @returns {Object} cloned Properties
*/
function cloneFeature(feature) {
var cloned = {
type: 'Feature',
properties: feature.properties || {},
geometry: cloneGeometry(feature.geometry)
};
if (feature.id) cloned.id = feature.id;
if (feature.bbox) cloned.bbox = feature.bbox;
function cloneProperties(properties) {
var cloned = {};
if (!properties) return cloned;
Object.keys(properties).forEach(function (key) {
var value = properties[key];
switch (typeof value) {
case 'number':
case 'string':
cloned[key] = value;
break;
case 'object':
// array
if (value.length) cloned[key] = value.map(function (item) { return item; });
// object
cloned[key] = cloneProperties(value);
}
});
return cloned;
}

Expand All @@ -70,12 +92,23 @@ function cloneFeature(feature) {
* @returns {FeatureCollection<any>} cloned Feature Collection
*/
function cloneFeatureCollection(geojson) {
return {
type: 'FeatureCollection',
features: geojson.features.map(function (feature) {
return cloneFeature(feature);
})
};
var cloned = {type: 'FeatureCollection'};

// Preserve Foreign Members
Object.keys(geojson).forEach(function (key) {
switch (key) {
case 'type':
case 'features':
return;
default:
cloned[key] = geojson[key];
}
});
// Add features
cloned.features = geojson.features.map(function (feature) {
return cloneFeature(feature);
});
return cloned;
}

/**
Expand All @@ -86,18 +119,17 @@ function cloneFeatureCollection(geojson) {
* @returns {Geometry<any>} cloned Geometry
*/
function cloneGeometry(geometry) {
var geom = {type: geometry.type};
if (geometry.bbox) geom.bbox = geometry.bbox;

if (geometry.type === 'GeometryCollection') {
return {
type: 'GeometryCollection',
geometries: geometry.geometries.map(function (geom) {
return cloneGeometry(geom);
})
};
geom.geometries = geometry.geometries.map(function (geom) {
return cloneGeometry(geom);
});
return geom;
}
return {
type: geometry.type,
coordinates: deepSlice(geometry.coordinates)
};
geom.coordinates = deepSlice(geometry.coordinates);
return geom;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/turf-clone/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
"@turf/meta": "^4.6.0",
"benchmark": "2.1.4",
"tape": "4.7.0"
}
},
"dependencies": {}
}
76 changes: 73 additions & 3 deletions packages/turf-clone/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ test('turf-clone', t => {
});

test('turf-clone -- throws', t => {
const pt = point([0, 20]);

t.throws(() => clone(), /geojson is required/);
t.throws(() => clone(pt, 'foo'), /cloneAll must be a Boolean/);
t.end();
});

Expand Down Expand Up @@ -89,3 +86,76 @@ test('turf-clone -- Geometry Objects', t => {
t.deepEqual(poly.coordinates, [[[10, 40], [0, 20], [20, 0], [10, 40]]], 'geometry polygon');
t.end();
});

test('turf-clone -- Preserve Foreign Members -- Feature', t => {
const properties = {foo: 'bar'};
const bbox = [0, 20, 0, 20];
const id = 12345;
const pt = point([0, 20], properties, bbox, id);
pt.custom = 'foreign members';

const cloned = clone(pt);
t.equal(cloned.id, id);
t.equal(cloned.custom, pt.custom);
t.deepEqual(cloned.bbox, bbox);
t.deepEqual(cloned.properties, properties);
t.end();
});

test('turf-clone -- Preserve Foreign Members -- FeatureCollection', t => {
const properties = {foo: 'bar'};
const bbox = [0, 20, 0, 20];
const id = 12345;
const fc = featureCollection([point([0, 20])], bbox, id);
fc.custom = 'foreign members';
fc.properties = properties;

const cloned = clone(fc);
t.equal(cloned.id, id);
t.equal(cloned.custom, fc.custom);
t.deepEqual(cloned.bbox, bbox);
t.deepEqual(cloned.properties, properties);
t.end();
});


test('turf-clone -- Preserve all properties -- Feature', t => {
const id = 12345;
const bbox = [0, 20, 0, 20];
const properties = {foo: 'bar', object: {property: 1}, array: [0, 1, 2]};
const pt = point([0, 20], properties, bbox, id);
pt.hello = 'world'; // Foreign member

// Clone and mutate
const cloned = clone(pt);
cloned['hello'] = 'universe';
cloned.properties['foo'] = 'foo';
cloned['id'] = 54321;
cloned['bbox'] = [30, 40, 30, 40];
cloned.properties.object['property'] = 2;
cloned.properties.array[0] = 500;

t.equal(pt.hello, 'world');
t.equal(pt.properties.foo, 'bar');
t.equal(pt.id, 12345);
t.deepEqual(pt.bbox, [0, 20, 0, 20]);
t.equal(pt.properties.object.property, 1);
t.deepEqual(pt.properties.array, [0, 1, 2]);
t.end();
});

test('turf-clone -- Preserve all properties -- FeatureCollection', t => {
const fc = featureCollection([point([0, 20])], [0, 20, 0, 20], 12345);
fc.hello = 'world'; // Foreign member

// Clone and mutate
const cloned = clone(fc);
cloned['hello'] = 'universe';
cloned['id'] = 54321;
cloned['bbox'] = [30, 40, 30, 40];

t.equal(fc.hello, 'world');
t.equal(fc.id, 12345);
t.deepEqual(fc.bbox, [0, 20, 0, 20]);
t.end();
});
8 changes: 5 additions & 3 deletions packages/turf-clone/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {point} from '@turf/helpers'
import {point, lineString} from '@turf/helpers'
import * as clone from './'

const pt = point([0, 20])
const ptCloned = clone(pt)
const ptClonedAll = clone(pt, true)
const ptCloned: GeoJSON.Feature<GeoJSON.Point> = clone(pt)

const line = lineString([[0, 20], [10, 10]]).geometry
const lineCloned: GeoJSON.LineString = clone(line)
8 changes: 8 additions & 0 deletions packages/turf-clone/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
# yarn lockfile v1


"@turf/helpers@^4.6.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-4.6.0.tgz#12398733b8ae28420df6166fa44c7ee8ffd6414c"

"@turf/meta@^4.6.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-4.6.0.tgz#0d3f9a218e58d1c5e5deedf467c3321dd61203f3"

balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
Expand Down
Loading