geojson
Version:
Turn your geo data into GeoJSON
471 lines (390 loc) • 17.6 kB
JavaScript
if (typeof window === 'undefined') {
var expect = require('expect.js');
var GeoJSON = require('../geojson');
var util = require('util');
}
describe('GeoJSON', function() {
describe('#defaults', function(){
it('exists as a public object of GeoJSON', function(){
expect(typeof GeoJSON.defaults).to.eql('object');
});
it('is initially empty', function() {
var count = 0;
for(var key in GeoJSON.defaults) {
if(GeoJSON.defaults.hasOwnProperty(key)) {
count++;
}
}
expect(count).to.be(1);
});
});
describe('#parse', function(){
var data;
before(function() {
// Sample Data
data = [
{ name: 'Location A', category: 'Store', lat: 39.984, lng: -75.343, street: 'Market' },
{ name: 'Location B', category: 'House', lat: 39.284, lng: -75.833, street: 'Broad' },
{ name: 'Location C', category: 'Office', lat: 39.123, lng: -74.534, street: 'South' }
];
});
it('returns output with the same number of features as the input', function(){
var output = GeoJSON.parse(data, {Point: ['lat', 'lng']});
expect(output.features.length).to.be(3);
});
it('doesn\'t include geometry fields in feature properties', function(){
var output = GeoJSON.parse(data, {Point: ['lat', 'lng']});
output.features.forEach(function(feature){
expect(feature.properties.lat).to.not.be.ok();
expect(feature.properties.lng).to.not.be.ok();
expect(feature.geometry.coordinates[0]).to.be.ok();
expect(feature.geometry.coordinates[1]).to.be.ok();
});
});
it('includes all properties besides geometry attributes when include or exclude isn\'t set', function() {
var output = GeoJSON.parse(data, {Point: ['lat', 'lng']});
output.features.forEach(function(feature){
expect(feature.properties.name).to.be.ok();
expect(feature.properties.category).to.be.ok();
expect(feature.properties.street).to.be.ok();
});
});
it('only includes attributes that are listed in the include parameter', function(){
var output = GeoJSON.parse(data, {Point: ['lat', 'lng'], include: ['name']});
output.features.forEach(function(feature){
expect(feature.properties.name).to.be.ok();
expect(feature.properties.category).to.not.be.ok();
expect(feature.properties.street).to.not.be.ok();
});
});
it('does not include attributes listed in the exclude parameter', function(){
var output = GeoJSON.parse(data, {Point: ['lat', 'lng'], exclude: ['name']});
output.features.forEach(function(feature){
expect(feature.properties.name).to.not.be.ok();
expect(feature.properties.category).to.be.ok();
expect(feature.properties.street).to.be.ok();
});
});
it('handles Point geom with x,y stored in one or two attributes', function(){
var twoAttrs = [{ name: 'test location', y: -74, x: 39.0, foo: 'bar' }];
var geoTwoAttrs = GeoJSON.parse(twoAttrs, {Point: ['x', 'y']});
expect(geoTwoAttrs.features[0].geometry.coordinates[0]).to.be(-74);
expect(geoTwoAttrs.features[0].geometry.coordinates[1]).to.be(39.0);
var oneAttr = [{ name: 'test location', coords: [-74, 39], foo: 'bar'}];
var geoOneAttr = GeoJSON.parse(oneAttr, {Point: 'coords'});
expect(geoOneAttr.features[0].geometry.coordinates[0]).to.be(-74);
expect(geoOneAttr.features[0].geometry.coordinates[1]).to.be(39.0);
});
it('parses object to single feature', function() {
var output = GeoJSON.parse(data[0], {Point: ['lat', 'lng']});
expect(output.type).to.be('Feature');
expect(output.geometry.type).to.be('Point');
expect(output.geometry.coordinates[1]).to.be(39.984);
expect(output.geometry.coordinates[0]).to.be(-75.343);
expect(output.properties.name).to.be('Location A');
expect(output.properties.category).to.be('Store');
expect(output.properties.street).to.be('Market');
});
it('parses data with different geometry types', function(){
// Based off example spec at http://geojson.org/geojson-spec.html
var data2 = [
{
x: 0.5,
y: 102.0,
prop0: 'value0'
},
{
line: [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]],
prop0: 'value0',
prop1: 0.0
},
{
polygon: [
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ]
],
prop0: 'value0',
prop1: {"this": "that"}
},
{
multipoint: [
[100.0, 0.0], [101.0, 1.0]
],
prop0: 'value0'
},
{
multipolygon: [
[[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
[[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
],
prop1: {'this': 'that'}
},
{
multilinestring: [
[ [100.0, 0.0], [101.0, 1.0] ],
[ [102.0, 2.0], [103.0, 3.0] ]
],
prop0: 'value1'
}
];
var output = GeoJSON.parse(data2, {
'Point': ['x', 'y'],
'LineString': 'line',
'Polygon': 'polygon',
'MultiPoint': 'multipoint',
'MultiPolygon': 'multipolygon',
'MultiLineString': 'multilinestring'
});
expect(output.features.length).to.be(6);
output.features.forEach(function(feature){
if(feature.geometry.type === 'Point') {
expect(feature.geometry.coordinates[1]).to.be(0.5);
expect(feature.geometry.coordinates[0]).to.be(102);
expect(feature.properties.prop0).to.be("value0");
} else if (feature.geometry.type === 'LineString') {
expect(feature.geometry.coordinates.length).to.be(4);
expect(feature.geometry.coordinates[0][1]).to.be(0);
expect(feature.geometry.coordinates[0][0]).to.be(102);
expect(feature.geometry.coordinates[3][1]).to.be(1);
expect(feature.geometry.coordinates[3][0]).to.be(105);
expect(feature.properties.prop0).to.be("value0");
expect(feature.properties.prop1).to.be(0);
} else if (feature.geometry.type === 'Polygon') {
expect(feature.geometry.coordinates[0].length).to.be(5);
expect(feature.geometry.coordinates[0][0][1]).to.be(0);
expect(feature.geometry.coordinates[0][0][0]).to.be(100);
expect(feature.geometry.coordinates[0][4][1]).to.be(0);
expect(feature.geometry.coordinates[0][4][0]).to.be(100);
expect(feature.properties.prop0).to.be("value0");
expect(feature.properties.prop1['this']).to.be('that');
}
});
});
it('uses the default settings when they have been specified', function(){
GeoJSON.defaults = {
Point: ['lat', 'lng'],
include: ['name'],
crs: { 'type': 'name', 'properties': { 'name': 'urn:ogc:def:crs:OGC:1.3:CRS84' }}
};
var output = GeoJSON.parse(data, {});
expect(output.crs.properties.name).to.be('urn:ogc:def:crs:OGC:1.3:CRS84');
output.features.forEach(function(feature){
expect(feature.properties.name).to.be.ok();
expect(feature.properties.lat).to.not.be.ok();
expect(feature.properties.lng).to.not.be.ok();
expect(feature.geometry.coordinates[0]).to.be.ok();
expect(feature.geometry.coordinates[1]).to.be.ok();
});
it('only applies default settings that haven\'t been set in params', function(){
var output = GeoJSON.parse(data, {include: ['category', 'street']});
expect(output.crs.properties.name).to.be('urn:ogc:def:crs:EPSG::4326');
output.features.forEach(function(feature){
expect(feature.properties.name).to.not.be.ok();
expect(feature.properties.category).to.be.ok();
expect(feature.properties.street).to.be.ok();
});
});
it('keeps the default settings until they have been explicity reset', function(){
var output = GeoJSON.parse(data, {});
expect(output.crs.properties.name).to.be('urn:ogc:def:crs:EPSG::4326');
output.features.forEach(function(feature){
expect(feature.properties.name).to.be.ok();
expect(feature.properties.lat).to.not.be.ok();
expect(feature.properties.lng).to.not.be.ok();
expect(feature.geometry.coordinates[0]).to.be.ok();
expect(feature.geometry.coordinates[1]).to.be.ok();
});
});
GeoJSON.defaults = {};
});
it("adds 'bbox' and/or 'crs' to the output if either is specified in the parameters", function(){
var output = GeoJSON.parse(data, {
Point: ['lat', 'lng'],
bbox: [-75, 39, -76, 40],
crs: { 'type': 'name', 'properties': { 'name': 'urn:ogc:def:crs:EPSG::4326' }}});
expect(output.crs.properties.name).to.be('urn:ogc:def:crs:EPSG::4326');
expect(output.bbox[0]).to.be(-75);
expect(output.bbox[1]).to.be(39);
expect(output.bbox[2]).to.be(-76);
expect(output.bbox[3]).to.be(40);
});
it("adds extra attributes if extra param is set", function() {
var output = GeoJSON.parse(data, {Point: ['lat', 'lng'], extra: { 'foo':'bar', 'bar':'foo'}});
output.features.forEach(function(feature){
expect(feature.properties.foo).to.be('bar');
expect(feature.properties.bar).to.be('foo');
});
var output2 = GeoJSON.parse(data, {
Point: ['lat', 'lng'],
extra: {
style: {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
}
}
});
output2.features.forEach(function(feature) {
expect(feature.properties.style.color).to.be('#ff7800');
expect(feature.properties.style.weight).to.be(5);
expect(feature.properties.style.opacity).to.be(0.65);
});
});
it("adds a properties key at the top level if the extraGlobal parameter is set", function() {
var output = GeoJSON.parse(data, {
Point: ['lat', 'lng'],
extra: { 'foo':'bar', 'bar':'foo'},
extraGlobal: { 'name': 'A bunch of points', 'source': 'Government website'}
});
expect(output.properties).to.be.ok();
expect(output.properties.name).to.be('A bunch of points');
expect(output.properties.source).to.be('Government website');
});
it("returns valid GeoJSON output when input length is 0", function(done){
GeoJSON.parse([], {Point: ['lat', 'lng']}, function(geojson){
expect(geojson.type).to.be('FeatureCollection');
expect(geojson.features).to.be.an('array');
expect(geojson.features.length).to.be(0);
done();
});
});
it("returns valid GeoJSON output for 0,0", function(done){
GeoJSON.parse([{ lat: 0, lng: 0 }], {Point: ['lat', 'lng']}, function(geojson){
expect(geojson.type).to.be('FeatureCollection');
expect(geojson.features).to.be.an('array');
expect(geojson.features.length).to.be(1);
expect(geojson.features[0].geometry.coordinates[0]).to.equal(0);
expect(geojson.features[0].geometry.coordinates[1]).to.equal(0);
done();
});
});
it("throws an error if no geometry attributes have been specified", function() {
expect(function(){ GeoJSON.parse(data); }).to.throwException(/No geometry attributes specified/);
});
it("calls the calback function if one is provided", function(done){
GeoJSON.parse(data, {Point: ['lat', 'lng']}, function(geojson){
expect(geojson.features.length).to.be(3);
geojson.features.forEach(function(feature){
expect(feature.properties.lat).to.not.be.ok();
expect(feature.properties.lng).to.not.be.ok();
expect(feature.geometry.coordinates[0]).to.be.ok();
expect(feature.geometry.coordinates[1]).to.be.ok();
});
done();
});
});
it("returns the GeoJSON output if the callback parameter is not a function", function(){
var output = GeoJSON.parse(data, {Point: ['lat', 'lng']}, 'foo');
output.features.forEach(function(feature){
expect(feature.properties.lat).to.not.be.ok();
expect(feature.properties.lng).to.not.be.ok();
expect(feature.geometry.coordinates[0]).to.be.ok();
expect(feature.geometry.coordinates[1]).to.be.ok();
});
});
it("checks for invalid CRS's", function() {
var options = {
Point: ['lat', 'lng'],
crs: { 'type': 'foo' }
};
expect(function(){ GeoJSON.parse(data, options); }).to.throwException('Invald CRS. Type attribute must be "name" or "link"');
options = {
Point: ['lat', 'lng'],
crs: { 'type': 'name', 'properties': 'foo' }
};
expect(function(){ GeoJSON.parse(data, options); }).to.throwException('Invalid CRS. Properties must contain "name" key');
options = {
Point: ['lat', 'lng'],
crs: { 'type': 'name', 'properties': { 'name': 'foo' }}
};
expect(function(){ GeoJSON.parse(data, options); }).to.not.throwException();
options = {
Point: ['lat', 'lng'],
crs: { 'type': 'link', 'properties': { 'name': 'foo' }}
};
expect(function(){ GeoJSON.parse(data, options); }).to.throwException('Invalid CRS. Properties must contain "href" and "type" key');
options = {
Point: ['lat', 'lng'],
crs: { 'type': 'link', 'properties': { 'type': 'foo' }}
};
expect(function(){ GeoJSON.parse(data, options); }).to.throwException('Invalid CRS. Properties must contain "href" and "type" key');
options = {
Point: ['lat', 'lng'],
crs: { 'type': 'link', 'properties': { 'type': 'foo', 'href': 'bar' }}
};
expect(function(){ GeoJSON.parse(data, options); }).to.not.throwException();
});
it("accepts already formatted GeoJSON", function() {
var data = [{name: 'Location A', geo: {"type": "Point", "coordinates": [125.6, 10.1]}}];
var output = GeoJSON.parse(data, {GeoJSON: 'geo'});
expect(output.type).to.be('FeatureCollection');
expect(output.features).to.be.an('array');
expect(output.features.length).to.be(1);
expect(output.features[0].geometry.coordinates[0]).to.equal(125.6);
expect(output.features[0].geometry.coordinates[1]).to.equal(10.1);
expect(output.features[0].geometry.type).to.equal('Point');
expect(output.features[0].properties.name).to.equal('Location A');
});
it("converts string coordinates into numbers", function() {
var data = [{ lat: '39.343', lng: '-74.454'}];
var output = GeoJSON.parse(data, {Point: ['lat', 'lng']});
output.features.forEach(function(feature) {
expect(feature.geometry.coordinates[0]).to.be.a('number');
expect(feature.geometry.coordinates[1]).to.be.a('number');
});
});
it("throws via doThrows on InvalidGeometryError", function() {
var data = [{ lat: '39.343', lng: '-74.454'}];
expect(function(){
GeoJSON.parse(data, {doThrows: {invalidGeometry: true}, Point: 'lat'});
}).to.throwException(GeoJSON.InvalidGeometryError);
});
it("nested polygon", function() {
var data = {
northeast: { lat: 29.8399961, lng: -82.38140709999999 },
southwest: { lat: 29.7183041, lng: -82.555449 }
};
var output = GeoJSON.parse(data, {
doThrows: {invalidGeometry: true},
Polygon: {
northeast: ['lat', 'lng'],
southwest: ['lat', 'lng']
}
});
// console.log(util.inspect(output.geometry,4));
expect(output.geometry.type).to.be.equal('Polygon');
expect(output.geometry.coordinates.length).to.be.equal(2);
output.geometry.coordinates.forEach(function(coords){
expect(coords.length).to.be.equal(2);
});
expect(output.geometry.type).to.be.equal('Polygon');
expect(output.geometry.coordinates[0]).to.be.eql([-82.38140709999999, 29.8399961]);
expect(output.geometry.coordinates[1]).to.be.eql([-82.555449, 29.7183041]);
});
it("nested polygon, nested", function() {
var data = {
northeast: { crap:{lat: 29.8399961, lng: -82.38140709999999} },
southwest: { crap1:{lat: 29.7183041}, crap2: {lng: -82.555449} }
};
var output = GeoJSON.parse(data, {
doThrows: {invalidGeometry: true},
Polygon: {
northeast: ['crap.lat', 'crap.lng'],
southwest: ['crap1.lat', 'crap2.lng']
},
isPostgres: true,
crs: { 'type': 'name', 'properties': { 'name': 'urn:ogc:def:crs:OGC:1.3:CRS84' }}
});
// console.log(util.inspect(output.geometry,4));
expect(output.geometry.crs).to.be.ok();
expect(output.geometry.type).to.be.equal('Polygon');
expect(output.geometry.coordinates.length).to.be.equal(2);
output.geometry.coordinates.forEach(function(coords){
expect(coords.length).to.be.equal(2);
});
expect(output.geometry.type).to.be.equal('Polygon');
expect(output.geometry.coordinates[0]).to.be.eql([-82.38140709999999, 29.8399961]);
expect(output.geometry.coordinates[1]).to.be.eql([-82.555449, 29.7183041]);
});
});
});