diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0ee9e808..07938308c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Added an idle property to objects (#894) - Better handling and changing of camera clipbounds (#899) +- File readers (the geojsonReader) now returns a Promise. The layer will report that it is not idle until this promise is finalized (#905) ### Bug Fixes - Fixed an issue with overlapping, cropped tiles on old browsers (#901) @@ -12,6 +13,7 @@ ### Changes - Changed build process: optional dependencies are now included in the bundle by default (#890) - Transpile with Babel to support old browsers and new language features (#900) +- The geojsonReader has been renamed from `jsonReader` to `geojsonReader`. The old name still works as an alias (#905) ## Version 0.17.0 diff --git a/docs/users.rst b/docs/users.rst index b2ca12242b..83ac1f18cf 100644 --- a/docs/users.rst +++ b/docs/users.rst @@ -181,7 +181,7 @@ documentation for each of the classes. `geo.fileReader `_ This is an abstract class defining the interface for file readers. Currently, the only implemented reader is - `geo.jsonReader `_, + `geo.geojsonReader `_, which is an extendable geojson reader. Coordinate systems diff --git a/examples/geoJSON/main.css b/examples/geoJSON/main.css index 43824ba8a4..6bc3b6098c 100644 --- a/examples/geoJSON/main.css +++ b/examples/geoJSON/main.css @@ -3,7 +3,7 @@ left: 10px; top: 80px; width: calc(50% - 10px); - height: calc(70% - 100px) !important; + height: calc(87% - 80px) !important; z-index: 50; border-radius: 5px; border: 1px solid grey; diff --git a/examples/geoJSON/main.js b/examples/geoJSON/main.js index 43cac8ccfe..af355571e0 100644 --- a/examples/geoJSON/main.js +++ b/examples/geoJSON/main.js @@ -1,7 +1,11 @@ +/* globals utils */ + // Run after the DOM loads $(function () { 'use strict'; + var query = utils.getQuery(); + // Create a map object var map = geo.map({ node: '#map', @@ -27,11 +31,15 @@ $(function () { // Create a layer to put the features in. We could need point, line, and // polygon features, so ask for a layer that supports all of them. - var layer = map.createLayer('feature', {features: ['point', 'line', 'polygon']}); + // Optionally handle a query parameter to try out specific renderers. + var layer = map.createLayer('feature', { + renderer: query.renderer ? (query.renderer === 'html' ? null : query.renderer) : undefined, + features: query.renderer ? undefined : ['point', 'line', 'polygon'] + }); map.draw(); // Initialize the json reader. - var reader = geo.createFileReader('jsonReader', {'layer': layer}); + var reader = geo.createFileReader('geojsonReader', {'layer': layer}); // At this point we could just attach the reader to the map like // this: diff --git a/package.json b/package.json index 335373d1a8..c59aaebc85 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "babel-core": "^6.26.0", "babel-loader": "^7.1.5", "babel-plugin-istanbul": "^4.1.6", + "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", "body-parser": "^1.15.0", diff --git a/src/annotationLayer.js b/src/annotationLayer.js index 3d795f132a..11fb841cc2 100644 --- a/src/annotationLayer.js +++ b/src/annotationLayer.js @@ -12,8 +12,9 @@ var textFeature = require('./textFeature'); * @typedef {object} geo.annotationLayer.labelRecord * @property {string} text The text of the label * @property {geo.geoPosition} position The position of the label in map gcs - * coordinates. - * @property {object} [style] A {@link geo.textFeature} style object. + * coordinates. + * @property {geo.textFeature.styleSpec} [style] A {@link geo.textFeature} + * style object. */ /** @@ -601,7 +602,7 @@ var annotationLayer = function (args) { */ this.geojson = function (geojson, clear, gcs, includeCrs) { if (geojson !== undefined) { - var reader = registry.createFileReader('jsonReader', {layer: m_this}); + var reader = registry.createFileReader('geojsonReader', {layer: m_this}); if (!reader.canRead(geojson)) { return; } diff --git a/src/fileReader.js b/src/fileReader.js index 03dcc4055e..d9cb140df4 100644 --- a/src/fileReader.js +++ b/src/fileReader.js @@ -3,11 +3,19 @@ var featureLayer = require('./featureLayer'); var object = require('./object'); /** - * Create a new instance of class fileReader + * Object specification for a fileReader. + * + * @typedef {object} geo.fileReader.spec + * @property {geo.featureLayer} layer The target feature layer. + */ + +/** + * Create a new instance of class fileReader. * * @class * @alias geo.fileReader * @extends geo.object + * @param {geo.fileReader.spec} arg * @returns {geo.fileReader} */ var fileReader = function (arg) { @@ -29,7 +37,9 @@ var fileReader = function (arg) { var m_layer = arg.layer; /** - * Get the feature layer attached to the reader + * Get the feature layer attached to the reader. + * + * @returns {geo.featureLayer} The layer associated with the reader. */ this.layer = function () { return m_layer; @@ -37,23 +47,55 @@ var fileReader = function (arg) { /** * Tells the caller if it can handle the given file by returning a boolean. + * + * @param {File|Blob|string|object} file This is either a `File` object, a + * `Blob` object, a string representation of a file, or an object + * representing data from a file. + * @returns {boolean} `true` if this reader can read a file. */ - this.canRead = function () { + this.canRead = function (file) { return false; }; /** - * Reads the file object and calls the done function when finished. As an - * argument to done, provides a boolean that reports if the read was a - * success. Possibly, it can call done with an object containing details + * Reads the file and optionally calls a function when finished. The `done` + * function is called with a value that is truthy if the read was a success. + * Depending on the specific reader, this value may be an object with details * of the read operation. + * + * @param {File|Blob|string|object} file This is either a `File` object, a + * `Blob` object, a string representation of a file, or an object + * representing data from a file. + * @param {function} [done] An optional callback function when the read is + * complete. This is called with `false` on error or the object that was + * read and parsed by the reader. + * @param {function} [progress] A function which is passed `ProgressEvent` + * information from a `FileReader`. This includes `loaded` and `total` + * each with a number of bytes. + * @returns {Promise} A `Promise` that resolves with object parsed by the + * reader or is rejected if the reader fails. */ - this.read = function (file, done) { - done(false); + this.read = function (file, done, progress) { + var promise = new Promise(function (resolve, reject) { + if (done) { + done(false); + } + throw new Error('The default file reader always fails'); + }); + this.addPromise(promise); + return promise; }; /** - * Return a FileReader with handlers attached. + * Return a `FileReader` with handlers attached. + * + * @param {function} done A callback that receives either the string read + * from the file or a `DOMException` with an error. + * @param {function} [progress] A function which is passed `ProgressEvent` + * information from a `FileReader`. This includes `loaded` and `total` + * each with a number of bytes. + * @returns {FileReader} The `FileReader` with done and progress handles + * attached to it. */ function newFileReader(done, progress) { var reader = new FileReader(); @@ -61,33 +103,27 @@ var fileReader = function (arg) { reader.onprogress = progress; } reader.onloadend = function () { - if (!reader.result) { - done(reader.error); - } - done(reader.result); + done(reader.error || reader.result); }; return reader; } /** - * Private method for reading a file object as a string. Calls done with - * the string content when finished or an error object if unsuccessful. - * Optionally, the caller can provide a progress method that is called - * after reading each slice. + * Read a file object as a string. Calls `done` with the string content + * when finished or an error object if unsuccessful. + * + * @param {File|Blob} file A `File` or `Blob` object to read. + * @param {function} done A callback that receives either the string read + * from the file or a `DOMException` with an error. + * @param {function} [progress] A function which is passed `ProgressEvent` + * information from a `FileReader`. This includes `loaded` and `total` + * each with a number of bytes. */ this._getString = function (file, done, progress) { var reader = newFileReader(done, progress); reader.readAsText(file); }; - /** - * Like _getString, but returns an ArrayBuffer object. - */ - this._getArrayBuffer = function (file, done, progress) { - var reader = newFileReader(done, progress); - reader.readAsText(file); - }; - return this; }; diff --git a/src/geojsonReader.js b/src/geojsonReader.js new file mode 100644 index 0000000000..b731765945 --- /dev/null +++ b/src/geojsonReader.js @@ -0,0 +1,423 @@ +var inherit = require('./inherit'); +var registerFileReader = require('./registry').registerFileReader; +var fileReader = require('./fileReader'); + +/** + * Object specification for a geojsonReader. + * + * @typedef {geo.fileReader.spec} geo.geojsonReader.spec + * @extends geo.fileReader.spec + * @property {geo.pointFeature.styleSpec} [pointStyle] Default style for + * points. + * @property {geo.pointFeature.styleSpec} [lineStyle] Default style for lines. + * @property {geo.pointFeature.styleSpec} [polygonStyle] Default style for + * polygons. + */ + +/** + * Create a new instance of class geo.geojsonReader. + * + * @class + * @alias geo.geojsonReader + * @extends geo.fileReader + * @param {geo.fileReader.spec} arg + * @returns {geo.geojsonReader} + */ +var geojsonReader = function (arg) { + 'use strict'; + if (!(this instanceof geojsonReader)) { + return new geojsonReader(arg); + } + + var $ = require('jquery'); + var convertColor = require('./util').convertColor; + + var m_this = this, + m_options = { + ...arg, + pointStyle: { + fill: true, + fillColor: '#ff7800', + fillOpacity: 0.8, + stroke: true, + strokeColor: '#000', + strokeWidth: 1, + strokeOpacity: 1, + radius: 8, + ...arg.pointStyle + }, + lineStyle: { + strokeColor: '#ff7800', + strokeWidth: 4, + strokeOpacity: 0.5, + strokeOffset: 0, + lineCap: 'butt', + lineJoin: 'miter', + closed: false, + ...arg.lineStyle + }, + polygonStyle: { + fill: true, + fillColor: '#b0de5c', + fillOpacity: 0.8, + stroke: true, + strokeColor: '#999999', + strokeWidth: 2, + strokeOpacity: 1, + ...arg.polygonStyle + } + }; + + fileReader.call(this, m_options); + + /** + * Tells the caller if it can handle the given file by returning a boolean. + * + * @param {File|Blob|string|object} file This is either a `File` object, a + * `Blob` object, a string representation of a file, or an object + * representing data from a file. + * @returns {boolean} `true` if this reader can read a file. + */ + this.canRead = function (file) { + if (file instanceof File || file instanceof Blob) { + return !!(file.type === 'application/json' || (file.name && file.name.match(/\.json$/))); + } else if (typeof file === 'string') { + try { + JSON.parse(file); + } catch (e) { + return false; + } + return true; + } + try { + if (Array.isArray(m_this._featureArray(file))) { + return true; + } + } catch (e) {} + return false; + }; + + /** + * Read or parse a file or object, then call a done function. + * + * @param {File|Blob|string|object} file This is either a `File` object, a + * `Blob` object, a string representation of a file, or an object + * representing data from a file. + * @param {function} done A callback function when the read is complete. + * This is called with `false` on error or the object that was read but + * not yet parsed. + * @param {function} [progress] A function which is passed `ProgressEvent` + * information from a `FileReader`. This includes `loaded` and `total` + * each with a number of bytes. + */ + this._readObject = function (file, done, progress) { + var object; + function onDone(fileString) { + // if fileString is not a JSON string, expect it to be a URL. + try { + object = JSON.parse(fileString); + done(object); + } catch (err) { + if (!object) { + $.ajax({ + type: 'GET', + url: fileString, + dataType: 'text' + }).done(function (data) { + try { + object = JSON.parse(data); + done(object); + } catch (err) { + if (!object) { + done(false); + } + } + }).fail(function () { + done(false); + }); + } + } + } + + if (file instanceof File || file instanceof Blob) { + m_this._getString(file, onDone, progress); + } else if (typeof file === 'string') { + onDone(file); + } else { + done(file); + } + }; + + /** + * Return an array of normalized geojson features. This turns bare + * geometries into features and multi-geometry features into single geometry + * features. + * + * Returns an array of Point, LineString, or Polygon features. + * @param {geojson.object} spec A parsed geojson object. + * @returns {geojson.FeatureObject[]} An array of feature objects, none of + * which include multi-geometries, and none have empty geometries. + */ + this._featureArray = function (spec) { + var features, normalized = []; + switch (spec.type) { + case 'FeatureCollection': + features = spec.features; + break; + + case 'Feature': + features = [spec]; + break; + + case 'GeometryCollection': + features = spec.geometries.map(function (g) { + return { + type: 'Feature', + geometry: g, + properties: {} + }; + }); + break; + + case 'Point': + case 'LineString': + case 'Polygon': + case 'MultiPoint': + case 'MultiLineString': + case 'MultiPolygon': + features = [{ + type: 'Feature', + geometry: spec, + properties: {} + }]; + break; + + default: + throw new Error('Invalid json type'); + } + + // flatten multi features + features.forEach(function (feature) { + Array.prototype.push.apply(normalized, m_this._feature(feature)); + }); + + // remove features with empty geometries + normalized = normalized.filter(function (feature) { + return feature.geometry && + feature.geometry.coordinates && + feature.geometry.coordinates.length; + }); + return normalized; + }; + + /** + * Normalize a feature object turning multi geometry features into an array + * of features, and single geometry features into an array containing one + * feature. + * + * @param {geojson.object} spec A parsed geojson object. + * @returns {geojson.FeatureObject[]} An array of feature objects, none of + * which include multi-geometries. + */ + this._feature = function (spec) { + if (spec.type !== 'Feature') { + throw new Error('Invalid feature object'); + } + switch (spec.geometry.type) { + case 'Point': + case 'LineString': + case 'Polygon': + return [spec]; + + case 'MultiPoint': + case 'MultiLineString': + case 'MultiPolygon': + return spec.geometry.coordinates.map(function (c) { + return { + type: 'Feature', + geometry: { + type: spec.geometry.type.replace('Multi', ''), + coordinates: c + }, + properties: spec.properties + }; + }); + + default: + throw new Error('Invalid geometry type'); + } + }; + + /** + * Convert from a geojson position array into a geojs position object. + * + * @param {number[]} p A coordinate in the form of an array with two or three + * components. + * @returns {geo.geoPosition} + */ + this._position = function (p) { + return { + x: p[0], + y: p[1], + z: p[2] || 0 + }; + }; + + /** + * Defines a style accessor the returns the given value of the property + * object or a default value. + * + * @param {string} prop The property name. + * @param {object} _default The default value. + * @returns {function} A style function for the property. + */ + this._style = function (prop, _default) { + var isColor = prop.toLowerCase().match(/color$/); + if (isColor) { + _default = convertColor(_default); + } + return function (v, j, d, i) { + var p; + if (d !== undefined && d.properties) { + p = d.properties; + } else { + p = v.properties; + } + if (p !== undefined && p.hasOwnProperty(prop)) { + return isColor ? convertColor(p[prop]) : p[prop]; + } + return _default; + }; + }; + + /** + * Reads the file and optionally calls a function when finished. The `done` + * function is called with a list of {@link geo.feature} on success or + * `false` on failure. + * + * @param {File|Blob|string|object} file This is either a `File` object, a + * `Blob` object, a string representation of a file, or an object + * representing data from a file. + * @param {function} [done] An optional callback function when the read is + * complete. This is called with `false` on error or the object that was + * read and parsed by the reader. + * @param {function} [progress] A function which is passed `ProgressEvent` + * information from a `FileReader`. This includes `loaded` and `total` + * each with a number of bytes. + * @returns {Promise} A `Promise` that resolves with a list of + * {@link geo.feature} or is rejected if the reader fails. + */ + this.read = function (file, done, progress) { + var promise = new Promise(function (resolve, reject) { + /** + * Given a parsed GeoJSON object, convert it into features on the + * reader's layer. + * + * @param {geojson.object|false} object Either a parse GeoJSON object or + * `false` for an error. + */ + function _done(object) { + if (object === false) { + if (done) { + done(object); + } + reject(new Error('Failed to parse GeoJSON')); + return; + } + let features, allFeatures = [], points, lines, polygons, feature; + + try { + features = m_this._featureArray(object); + } catch (err) { + reject(err); + return; + } + + // process points + points = features.filter(f => f.geometry.type === 'Point'); + if (points.length) { + feature = m_this.layer().createFeature('point'); + if (feature) { + feature + .data(points) + .position(d => m_this._position(d.geometry.coordinates)) + // create an object with each property in m_options.pointStyle, + // mapping the values through the _style function. + .style( + [{}].concat(Object.keys(m_options.pointStyle)).reduce( + (styleObj, key) => ({ + [key]: m_this._style(key, m_options.pointStyle[key]), + ...styleObj + } + )) + ); + allFeatures.push(feature); + } + } + + // process lines + lines = features.filter(f => f.geometry.type === 'LineString'); + if (lines.length) { + feature = m_this.layer().createFeature('line'); + if (feature) { + feature + .data(lines) + .line(d => d.geometry.coordinates) + .position(m_this._position) + // create an object with each property in m_options.lineStyle, + // mapping the values through the _style function. + .style( + [{}].concat(Object.keys(m_options.lineStyle)).reduce( + (styleObj, key) => ({ + [key]: m_this._style(key, m_options.lineStyle[key]), + ...styleObj + } + )) + ); + allFeatures.push(feature); + } + } + + // process polygons + polygons = features.filter(f => f.geometry.type === 'Polygon'); + if (polygons.length) { + feature = m_this.layer().createFeature('polygon'); + if (feature) { + feature + .data(polygons) + .polygon((d, i) => ({ + outer: d.geometry.coordinates[0], + inner: d.geometry.coordinates.slice(1) + })) + .position(m_this._position) + // create an object with each property in m_options.polygonStyle, + // mapping the values through the _style function. + .style( + [{}].concat(Object.keys(m_options.polygonStyle)).reduce( + (styleObj, key) => ({ + [key]: m_this._style(key, m_options.polygonStyle[key]), + ...styleObj + } + )) + ); + allFeatures.push(feature); + } + } + if (done) { + done(allFeatures); + } + resolve(allFeatures); + } + + m_this._readObject(file, _done, progress); + }); + this.addPromise(promise); + return promise; + }; +}; + +inherit(geojsonReader, fileReader); +registerFileReader('geojsonReader', geojsonReader); +// Also register under an alternate name +registerFileReader('jsonReader', geojsonReader); +module.exports = geojsonReader; diff --git a/src/index.js b/src/index.js index a0eed596a2..601b92fd86 100644 --- a/src/index.js +++ b/src/index.js @@ -54,7 +54,7 @@ module.exports = $.extend({ heatmapFeature: require('./heatmapFeature'), imageTile: require('./imageTile'), isolineFeature: require('./isolineFeature'), - jsonReader: require('./jsonReader'), + geojsonReader: require('./geojsonReader'), layer: require('./layer'), lineFeature: require('./lineFeature'), map: require('./map'), diff --git a/src/jsonReader.js b/src/jsonReader.js deleted file mode 100644 index d53ac7bc56..0000000000 --- a/src/jsonReader.js +++ /dev/null @@ -1,308 +0,0 @@ -var inherit = require('./inherit'); -var registerFileReader = require('./registry').registerFileReader; -var fileReader = require('./fileReader'); - -/** -* Create a new instance of class jsonReader -* -* @class geo.jsonReader -* @extends geo.fileReader -* @returns {geo.jsonReader} -*/ -var jsonReader = function (arg) { - 'use strict'; - if (!(this instanceof jsonReader)) { - return new jsonReader(arg); - } - - var $ = require('jquery'); - var convertColor = require('./util').convertColor; - - var m_this = this; - - fileReader.call(this, arg); - - this.canRead = function (file) { - if (file instanceof File) { - return (file.type === 'application/json' || file.name.match(/\.json$/)); - } else if (typeof file === 'string') { - try { - JSON.parse(file); - } catch (e) { - return false; - } - return true; - } - try { - if (Array.isArray(m_this._featureArray(file))) { - return true; - } - } catch (e) {} - return false; - }; - - this._readObject = function (file, done, progress) { - var object; - function onDone(fileString) { - if (typeof fileString !== 'string') { - done(false); - } - - // We have two possibilities here: - // 1) fileString is a JSON string or is - // a URL. - try { - object = JSON.parse(fileString); - done(object); - } catch (e) { - if (!object) { - $.ajax({ - type: 'GET', - url: fileString, - dataType: 'text' - }).done(function (data) { - object = JSON.parse(data); - done(object); - }).fail(function () { - done(false); - }); - } - } - } - - if (file instanceof File) { - m_this._getString(file, onDone, progress); - } else if (typeof file === 'string') { - onDone(file); - } else { - done(file); - } - }; - - /** - * Return an array of normalized geojson features. This - * will do the following: - * - * 1. Turn bare geometries into features - * 2. Turn multi-geometry features into single geometry features - * - * Returns an array of Point, LineString, or Polygon features. - * @protected - */ - this._featureArray = function (spec) { - var features, normalized = []; - switch (spec.type) { - case 'FeatureCollection': - features = spec.features; - break; - - case 'Feature': - features = [spec]; - break; - - case 'GeometryCollection': - features = spec.geometries.map(function (g) { - return { - type: 'Feature', - geometry: g, - properties: {} - }; - }); - break; - - case 'Point': - case 'LineString': - case 'Polygon': - case 'MultiPoint': - case 'MultiLineString': - case 'MultiPolygon': - features = [{ - type: 'Feature', - geometry: spec, - properties: {} - }]; - break; - - default: - throw new Error('Invalid json type'); - } - - // flatten multi features - features.forEach(function (feature) { - Array.prototype.push.apply(normalized, m_this._feature(feature)); - }); - - // remove features with empty geometries - normalized = normalized.filter(function (feature) { - return feature.geometry && - feature.geometry.coordinates && - feature.geometry.coordinates.length; - }); - return normalized; - }; - - /** - * Normalize a feature object turning multi geometry features - * into an array of features, and single geometry features into - * an array containing one feature. - */ - this._feature = function (spec) { - if (spec.type !== 'Feature') { - throw new Error('Invalid feature object'); - } - switch (spec.geometry.type) { - case 'Point': - case 'LineString': - case 'Polygon': - return [spec]; - - case 'MultiPoint': - case 'MultiLineString': - case 'MultiPolygon': - return spec.geometry.coordinates.map(function (c) { - return { - type: 'Feature', - geometry: { - type: spec.geometry.type.replace('Multi', ''), - coordinates: c - }, - properties: spec.properties - }; - }); - - default: - throw new Error('Invalid geometry type'); - } - }; - - /** - * Convert from a geojson position array into a geojs position object. - */ - this._position = function (p) { - return { - x: p[0], - y: p[1], - z: p[2] || 0 - }; - }; - - /** - * Defines a style accessor the returns the given - * value of the property object, or a default value. - * - * @protected - * @param {string} prop The property name - * @param {object} default The default value - * @param {object} [spec] The argument containing the main property object - * @param {function} [convert] An optional conversion function - */ - this._style = function (prop, _default, spec, convert) { - convert = convert || function (d) { return d; }; - _default = convert(_default); - return function (d, i, e, j) { - var p; - if (spec && j !== undefined && spec[j] !== undefined) { - p = spec[j].properties; - } else { - p = d.properties; - } - if (p !== undefined && p.hasOwnProperty(prop)) { - return convert(p[prop]); - } - return _default; - }; - }; - - this.read = function (file, done, progress) { - - function _done(object) { - var features, allFeatures = [], points, lines, polygons, feature; - - features = m_this._featureArray(object); - - // process points - points = features.filter(function (f) { return f.geometry.type === 'Point'; }); - if (points.length) { - feature = m_this.layer().createFeature('point'); - if (feature) { - feature - .data(points) - .position(function (d) { - return m_this._position(d.geometry.coordinates); - }) - .style({ - fill: m_this._style('fill', true), - fillColor: m_this._style('fillColor', '#ff7800', null, convertColor), - fillOpacity: m_this._style('fillOpacity', 0.8), - stroke: m_this._style('stroke', true), - strokeColor: m_this._style('strokeColor', '#000000', null, convertColor), - strokeWidth: m_this._style('strokeWidth', 1), - strokeOpacity: m_this._style('strokeOpacity', 1), - radius: m_this._style('radius', 8) - }); - allFeatures.push(feature); - } - } - - // process lines - lines = features.filter(function (f) { return f.geometry.type === 'LineString'; }); - if (lines.length) { - feature = m_this.layer().createFeature('line'); - if (feature) { - feature - .data(lines) - .line(function (d) { - return d.geometry.coordinates; - }) - .position(m_this._position) - .style({ - strokeColor: m_this._style('strokeColor', '#ff7800', lines, convertColor), - strokeWidth: m_this._style('strokeWidth', 4, lines), - strokeOpacity: m_this._style('strokeOpacity', 0.5, lines), - strokeOffset: m_this._style('strokeOffset', 0, lines), - lineCap: m_this._style('lineCap', 'butt', lines), - lineJoin: m_this._style('lineCap', 'miter', lines), - closed: m_this._style('closed', false, lines) - }); - allFeatures.push(feature); - } - } - - // process polygons - polygons = features.filter(function (f) { return f.geometry.type === 'Polygon'; }); - if (polygons.length) { - feature = m_this.layer().createFeature('polygon'); - if (feature) { - feature - .data(polygons) - .polygon(function (d, i) { - return { - outer: d.geometry.coordinates[0], - inner: d.geometry.coordinates.slice(1) - }; - }) - .position(m_this._position) - .style({ - fill: m_this._style('fill', true), - fillColor: m_this._style('fillColor', '#b0de5c', polygons, convertColor), - fillOpacity: m_this._style('fillOpacity', 0.8, polygons), - stroke: m_this._style('stroke', true), - strokeColor: m_this._style('strokeColor', '#999999', polygons, convertColor), - strokeWidth: m_this._style('strokeWidth', 2, polygons), - strokeOpacity: m_this._style('strokeOpacity', 1, polygons) - }); - allFeatures.push(feature); - } - } - if (done) { - done(allFeatures); - } - } - - m_this._readObject(file, _done, progress); - }; -}; - -inherit(jsonReader, fileReader); -registerFileReader('jsonReader', jsonReader); -module.exports = jsonReader; diff --git a/tests/cases/fileReader.js b/tests/cases/fileReader.js new file mode 100644 index 0000000000..c5df192ca0 --- /dev/null +++ b/tests/cases/fileReader.js @@ -0,0 +1,73 @@ +/* globals Blob */ + +var geo = require('../test-utils').geo; +var createMap = require('../test-utils').createMap; + +describe('geo.fileReader', function () { + 'use strict'; + + describe('create', function () { + it('create function', function () { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.fileReader({'layer': layer}); + expect(reader instanceof geo.fileReader).toBe(true); + expect(function () { + geo.fileReader(); + }).toThrow(new Error('fileReader must be given a feature layer')); + }); + }); + + describe('Check class accessors', function () { + it('layer', function () { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.fileReader({'layer': layer}); + expect(reader.layer()).toBe(layer); + }); + }); + + describe('Public utility methods', function () { + it('canRead', function () { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.fileReader({'layer': layer}); + // the default canRead implementation returns false + expect(reader.canRead('')).toBe(false); + }); + it('read', function (done) { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.fileReader({'layer': layer}); + expect(reader.read('', function (result) { + expect(result).toBe(false); + done(); + }) instanceof Promise); + }); + }); + + describe('Private utility methods', function () { + it('_getString', function (done) { + var map, layer, reader, file, progress; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.fileReader({'layer': layer}); + // The PhantomJS browser does't support `new File`, so use `new Blob` + file = new Blob(['This is ', 'a test'], {type: 'text/plain'}); + file.lastModifiedDate = new Date(); + file.name = 'test.txt'; + reader._getString(file, function (result) { + expect(progress.loaded).toBe(14); + expect(progress.total).toBe(14); + expect(result).toBe('This is a test'); + done(); + }, function (evt) { + progress = evt; + }); + }); + }); +}); diff --git a/tests/cases/geojsonReader.js b/tests/cases/geojsonReader.js index 008f8f75b6..ec68b39ea2 100644 --- a/tests/cases/geojsonReader.js +++ b/tests/cases/geojsonReader.js @@ -1,365 +1,457 @@ -describe('geojsonReader', function () { - var geo = require('../test-utils').geo; - var createMap = require('../test-utils').createMap; +/* globals Blob */ - describe('geojsonReader', function () { - 'use strict'; +var geo = require('../test-utils').geo; +var createMap = require('../test-utils').createMap; - var obj, map, layer; +describe('geo.geojsonReader', function () { + var obj = { + features: [{ + geometry: { + coordinates: [102.0, 0.5], + type: 'Point' + }, + properties: { + // these are deliberately properties that are not used + color: [1, 0, 0], + size: [10] + }, + type: 'Feature' + }, { + geometry: { + coordinates: [ + [102.0, 0.0, 0], + [103.0, 1.0, 1], + [104.0, 0.0, 2], + [105.0, 1.0, 3] + ], + type: 'LineString' + }, + properties: { + color: [0, 1, 0], + width: [3] + }, + type: 'Feature' + }, { + geometry: { + coordinates: [ + [10.0, 0.5], + [10.0, -0.5] + ], + type: 'MultiPoint' + }, + properties: { + fillColor: '#0000ff', + radius: 7 + }, + type: 'Feature' + }], + type: 'FeatureCollection' + }; - describe('Feature normalization', function () { - var reader; + describe('create', () => { + it('create function', () => { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.geojsonReader({'layer': layer}); + expect(reader instanceof geo.geojsonReader).toBe(true); + }); + it('create by name', () => { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.createFileReader('geojsonReader', {'layer': layer}); + expect(reader instanceof geo.geojsonReader).toBe(true); + // create by old name + reader = geo.createFileReader('jsonReader', {'layer': layer}); + expect(reader instanceof geo.geojsonReader).toBe(true); + }); - beforeEach(function () { - map = createMap({center: [0, 0], zoom: 3}); + describe('with styles', () => { + it('default', done => { + var map, layer, reader; + map = createMap(); layer = map.createLayer('feature', {renderer: 'd3'}); - sinon.stub(layer, 'createFeature'); - reader = geo.createFileReader('jsonReader', {'layer': layer}); + reader = geo.geojsonReader({'layer': layer}); + reader.read(obj).then(result => { + expect(layer.features()[0].style.get('fillColor')(layer.features()[0].data()[0], 0)).toEqual({r: 1, g: 0x78 / 0xff, b: 0}); + done(); + }); }); - afterEach(function () { - layer.createFeature.restore(); + it('specified', done => { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature', {renderer: 'd3'}); + reader = geo.geojsonReader({'layer': layer, pointStyle: {fillColor: 'lightblue'}}); + reader.read(obj).then(result => { + expect(layer.features()[0].style.get('fillColor')(layer.features()[0].data()[0], 0)).toEqual({r: 0xad / 0xff, g: 0xd8 / 0xff, b: 0xe6 / 0xff}); + done(); + }); }); + it('from data', done => { + var map, layer, reader; + map = createMap(); + layer = map.createLayer('feature', {renderer: 'd3'}); + reader = geo.geojsonReader({'layer': layer, pointStyle: {fillColor: 'lightblue'}}); + obj.features[0].properties.fillColor = 'yellow'; + reader.read(obj).then(result => { + expect(layer.features()[0].style.get('fillColor')(layer.features()[0].data()[0], 0)).toEqual({r: 1, g: 1, b: 0}); + done(); + }); + }); + }); + }); - describe('bare geometry', function () { - it('Point', function () { - expect(reader._featureArray({ - type: 'Point', - coordinates: [1, 2] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [1, 2] - } - }]); + describe('Public utility methods', () => { + it('canRead', () => { + var map, layer, reader, file; + map = createMap(); + layer = map.createLayer('feature'); + reader = geo.geojsonReader({'layer': layer}); + expect(reader.canRead('')).toBe(false); + expect(reader.canRead(['not a geojson object'])).toBe(false); + expect(reader.canRead(obj)).toBe(true); + // The PhantomJS browser don't support `new File`, so use `new Blob` + file = new Blob([JSON.stringify(obj)], {type: 'text/plain'}); + file.lastModifiedDate = new Date(); + file.name = 'test.txt'; + expect(reader.canRead(file)).toBe(false); + file.name = 'test.json'; + expect(reader.canRead(file)).toBe(true); + }); + // tests all of _readObject as part of the read test + describe('read', () => { + var map, layer, reader; + it('bad object', done => { + map = createMap(); + layer = map.createLayer('feature', {renderer: 'd3'}); + reader = geo.geojsonReader({'layer': layer}); + + reader.read(['not geojson object']).catch(err => { + expect(!!err.message.match(/Invalid json type/)).toBe(true); + done(); + reader.read(['not geojson object'], result => { + expect(result).toBe(false); + done(); + }); }); - it('LineString', function () { - expect(reader._featureArray({ - type: 'LineString', - coordinates: [[1, 2], [3, 4]] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'LineString', - coordinates: [[1, 2], [3, 4]] - } - }]); + }); + it('non geojson', done => { + reader.read('not geojson').catch(err => { + expect(!!err.message.match(/Failed to parse/)).toBe(true); + reader.read('not geojson', result => { + expect(result).toBe(false); + done(); + }); }); - it('Polygon', function () { - expect(reader._featureArray({ - type: 'Polygon', - coordinates: [[[1, 2], [3, 4], [5, 6]]] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [[[1, 2], [3, 4], [5, 6]]] - } - }]); + }); + it('object', done => { + reader.read(obj).then(result => { + expect(result.length).toBe(2); + done(); }); - it('MultiPoint', function () { - expect(reader._featureArray({ - type: 'MultiPoint', - coordinates: [[1, 2], [3, 4]] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [1, 2] - } - }, { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [3, 4] - } - }]); + }); + it('blob', done => { + var file = new Blob([JSON.stringify(obj)], {type: 'text/plain'}); + file.lastModifiedDate = new Date(); + file.name = 'test.json'; + reader.read(file).then(result => { + expect(result.length).toBe(2); + done(); }); - it('MultiLineString', function () { - expect(reader._featureArray({ - type: 'MultiLineString', - coordinates: [[[1, 2], [3, 4]]] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'LineString', - coordinates: [[1, 2], [3, 4]] - } - }]); + }); + it('url', done => { + reader.read('/testdata/sample.json').then(result => { + expect(result.length).toBe(2); + done(); }); - it('MultiPolygon', function () { - expect(reader._featureArray({ - type: 'MultiPolygon', - coordinates: [[[[1, 2], [3, 4], [5, 6]]]] - })).toEqual([{ - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [[[1, 2], [3, 4], [5, 6]]] - } - }]); + }); + it('url with non-json data', done => { + reader.read('/testdata/white.jpg').catch(err => { + expect(!!err.message.match(/Failed to parse/)).toBe(true); + done(); }); }); + }); + }); - it('GeometryCollection', function () { - expect(reader._featureArray({ - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [0, 0] - }, { - type: 'MultiPoint', - coordinates: [[0, 1], [2, 3]] - } - ] - })).toEqual([ - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [0, 0] - } - }, { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [0, 1] - } - }, { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [2, 3] - } - } - ]); - }); + describe('Feature normalization', function () { + var map, layer, reader; - it('Feature', function () { + beforeEach(function () { + map = createMap({center: [0, 0], zoom: 3}); + layer = map.createLayer('feature'); + sinon.stub(layer, 'createFeature'); + reader = geo.createFileReader('geojsonReader', {'layer': layer}); + }); + afterEach(function () { + layer.createFeature.restore(); + }); + + describe('bare geometry', function () { + it('Point', function () { expect(reader._featureArray({ + type: 'Point', + coordinates: [1, 2] + })).toEqual([{ type: 'Feature', + properties: {}, geometry: { type: 'Point', coordinates: [1, 2] - }, - properties: {a: 1} + } + }]); + }); + it('LineString', function () { + expect(reader._featureArray({ + type: 'LineString', + coordinates: [[1, 2], [3, 4]] })).toEqual([{ type: 'Feature', - properties: {a: 1}, + properties: {}, geometry: { - type: 'Point', - coordinates: [1, 2] + type: 'LineString', + coordinates: [[1, 2], [3, 4]] } }]); }); - - it('FeatureCollection', function () { + it('Polygon', function () { expect(reader._featureArray({ - type: 'FeatureCollection', - features: [{ - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [1, 2] - }, - properties: {a: 1} - }, { - type: 'Feature', - geometry: { - type: 'MultiPoint', - coordinates: [[0, 0], [1, 1]] - }, - properties: {b: 2} - }] + type: 'Polygon', + coordinates: [[[1, 2], [3, 4], [5, 6]]] + })).toEqual([{ + type: 'Feature', + properties: {}, + geometry: { + type: 'Polygon', + coordinates: [[[1, 2], [3, 4], [5, 6]]] + } + }]); + }); + it('MultiPoint', function () { + expect(reader._featureArray({ + type: 'MultiPoint', + coordinates: [[1, 2], [3, 4]] })).toEqual([{ type: 'Feature', - properties: {a: 1}, + properties: {}, geometry: { type: 'Point', coordinates: [1, 2] } }, { type: 'Feature', - properties: {b: 2}, + properties: {}, + geometry: { + type: 'Point', + coordinates: [3, 4] + } + }]); + }); + it('MultiLineString', function () { + expect(reader._featureArray({ + type: 'MultiLineString', + coordinates: [[[1, 2], [3, 4]]] + })).toEqual([{ + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [[1, 2], [3, 4]] + } + }]); + }); + it('MultiPolygon', function () { + expect(reader._featureArray({ + type: 'MultiPolygon', + coordinates: [[[[1, 2], [3, 4], [5, 6]]]] + })).toEqual([{ + type: 'Feature', + properties: {}, + geometry: { + type: 'Polygon', + coordinates: [[[1, 2], [3, 4], [5, 6]]] + } + }]); + }); + }); + + it('GeometryCollection', function () { + expect(reader._featureArray({ + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [0, 0] + }, { + type: 'MultiPoint', + coordinates: [[0, 1], [2, 3]] + } + ] + })).toEqual([ + { + type: 'Feature', + properties: {}, geometry: { type: 'Point', coordinates: [0, 0] } }, { type: 'Feature', - properties: {b: 2}, + properties: {}, geometry: { type: 'Point', - coordinates: [1, 1] + coordinates: [0, 1] } - }]); + }, { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [2, 3] + } + } + ]); + }); - }); + it('Feature', function () { + expect(reader._featureArray({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [1, 2] + }, + properties: {a: 1} + })).toEqual([{ + type: 'Feature', + properties: {a: 1}, + geometry: { + type: 'Point', + coordinates: [1, 2] + } + }]); + }); - it('empty geometry', function () { - expect(reader._featureArray({ - type: 'FeatureCollection', - features: [{ - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [] - } - }, { + it('FeatureCollection', function () { + expect(reader._featureArray({ + type: 'FeatureCollection', + features: [{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [1, 2] + }, + properties: {a: 1} + }, { + type: 'Feature', + geometry: { + type: 'MultiPoint', + coordinates: [[0, 0], [1, 1]] + }, + properties: {b: 2} + }] + })).toEqual([{ + type: 'Feature', + properties: {a: 1}, + geometry: { + type: 'Point', + coordinates: [1, 2] + } + }, { + type: 'Feature', + properties: {b: 2}, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }, { + type: 'Feature', + properties: {b: 2}, + geometry: { + type: 'Point', + coordinates: [1, 1] + } + }]); + + }); + + it('empty geometry', function () { + expect(reader._featureArray({ + type: 'FeatureCollection', + features: [{ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [] + } + }, { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [] + } + }, { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [] + } + }] + })).toEqual([]); + }); + + describe('Errors', function () { + it('Invalid geometry', function () { + expect(function () { + reader._feature({ type: 'Feature', + properties: {}, geometry: { - type: 'LineString', - coordinates: [] + type: 'pt', + coordinates: [0, 0] } - }, { - type: 'Feature', + }); + }).toThrow(); + }); + + it('Invalid feature', function () { + expect(function () { + reader._feature({ + properties: {}, geometry: { - type: 'Polygon', - coordinates: [] + type: 'Point', + coordinates: [0, 0] } - }] - })).toEqual([]); + }); + }).toThrow(); }); - - describe('Errors', function () { - it('Invalid geometry', function () { - expect(function () { - reader._feature({ - type: 'Feature', - properties: {}, - geometry: { - type: 'pt', - coordinates: [0, 0] - } - }); - }).toThrow(); - }); - - it('Invalid feature', function () { - expect(function () { - reader._feature({ - properties: {}, - geometry: { - type: 'Point', - coordinates: [0, 0] - } - }); - }).toThrow(); - }); - it('Invalid JSON', function () { - expect(function () { - reader._featureArray({ - features: [] - }); - }).toThrow(); - }); + it('Invalid JSON', function () { + expect(function () { + reader._featureArray({ + features: [] + }); + }).toThrow(); }); }); + }); - it('Setup map', function () { - map = createMap({center: [0, 0], zoom: 3}); - layer = map.createLayer('feature', {renderer: 'd3'}); - - obj = { - 'features': [ - { - 'geometry': { - 'coordinates': [ - 102.0, - 0.5 - ], - 'type': 'Point' - }, - 'properties': { - 'color': [1, 0, 0], - 'size': [10] - }, - 'type': 'Feature' - }, - { - 'geometry': { - 'coordinates': [ - [ - 102.0, - 0.0, - 0 - ], - [ - 103.0, - 1.0, - 1 - ], - [ - 104.0, - 0.0, - 2 - ], - [ - 105.0, - 1.0, - 3 - ] - ], - 'type': 'LineString' - }, - 'properties': { - 'color': [0, 1, 0], - 'width': [3] - }, - 'type': 'Feature' - }, - { - 'geometry': { - 'coordinates': [ - [ - 10.0, - 0.5 - ], - [ - 10.0, - -0.5 - ] - ], - 'type': 'MultiPoint' - }, - 'properties': { - 'fillColor': '#0000ff', - 'radius': 7 - }, - 'type': 'Feature' - } - ], - 'type': 'FeatureCollection' - }; - }); - it('read from object', function (done) { - var reader = geo.createFileReader('jsonReader', {'layer': layer}), - data, i; + it('read from object', function (done) { + var map = createMap({center: [0, 0], zoom: 3}); + var layer = map.createLayer('feature', {renderer: 'd3'}); + var reader = geo.createFileReader('geojsonReader', {'layer': layer}), + data, i; - expect(reader.canRead(obj)).toBe(true); - reader.read(obj, function (features) { - expect(features.length).toEqual(2); + expect(reader.canRead(obj)).toBe(true); + reader.read(obj, function (features) { + expect(features.length).toEqual(2); - // Validate that we are getting the correct Z values - data = features[1].data()[0]; - for (i = 0; i < data.length; i += 1) { - expect(data[i].z()).toEqual(i); - } + // Validate that we are getting the correct Z values + data = features[1].data()[0]; + for (i = 0; i < data.length; i += 1) { + expect(data[i].z()).toEqual(i); + } - done(); - }); + done(); }); - }); }); diff --git a/tests/cases/map.js b/tests/cases/map.js index 9a0bdf8b79..4ccab84ae3 100644 --- a/tests/cases/map.js +++ b/tests/cases/map.js @@ -384,13 +384,13 @@ describe('geo.core.map', function () { var m = createMap(); expect(m.fileReader()).toBe(null); var layerCount = m.layers().length; - expect(m.fileReader('jsonReader')).toBe(m); + expect(m.fileReader('geojsonReader')).toBe(m); expect(m.fileReader()).not.toBe(null); expect(m.layers().length).toBe(layerCount + 1); expect(m.layers()[m.layers().length - 1].renderer().api()).not.toBe('d3'); - expect(m.fileReader('jsonReader', {renderer: 'd3'})).toBe(m); + expect(m.fileReader('geojsonReader', {renderer: 'd3'})).toBe(m); expect(m.layers()[m.layers().length - 1].renderer().api()).toBe('d3'); - var r = geo.createFileReader('jsonReader', {layer: m.layers()[m.layers().length - 1]}); + var r = geo.createFileReader('geojsonReader', {layer: m.layers()[m.layers().length - 1]}); expect(m.fileReader(r)).toBe(m); expect(m.fileReader()).toBe(r); }); @@ -974,7 +974,7 @@ describe('geo.core.map', function () { evt.originalEvent.dataTransfer = {}; $(m.node()).trigger(evt); expect(evt.originalEvent.dataTransfer.dropEffect).not.toBe('copy'); - m.fileReader('jsonReader'); + m.fileReader('geojsonReader'); evt = $.Event('dragover'); evt.originalEvent = new window.Event('dragover'); evt.originalEvent.dataTransfer = {}; @@ -983,7 +983,7 @@ describe('geo.core.map', function () { }); it('drop', function () { var m = createMap(); - m.fileReader('jsonReader', {renderer: 'd3'}); + m.fileReader('geojsonReader', {renderer: 'd3'}); var evt = $.Event('drop'); evt.originalEvent = new window.Event('drop'); evt.originalEvent.dataTransfer = {files: [{ diff --git a/tests/gl-cases/d3GeoJson.js b/tests/gl-cases/d3GeoJson.js index 882d01e98d..a8914abb7c 100644 --- a/tests/gl-cases/d3GeoJson.js +++ b/tests/gl-cases/d3GeoJson.js @@ -73,7 +73,7 @@ describe('d3GeoJSON', function () { var mapOptions = {center: {x: -105.0, y: 40.0}, zoom: 3.5}; myMap = common.createOsmMap(mapOptions, {}, true); var layer = myMap.createLayer('feature', {'renderer': 'd3'}); - var reader = geo.createFileReader('jsonReader', {'layer': layer}); + var reader = geo.createFileReader('geojsonReader', {'layer': layer}); reader.read(obj, function () { myMap.draw(); imageTest.imageTest('d3GeoJson', null, 0.0015, done, myMap.onIdle, 0, 2); diff --git a/tests/gl-cases/glPolygons.js b/tests/gl-cases/glPolygons.js index a62e8868ec..8b5e6bee7b 100644 --- a/tests/gl-cases/glPolygons.js +++ b/tests/gl-cases/glPolygons.js @@ -2442,7 +2442,7 @@ describe('glPolygons', function () { var layer = myMap.createLayer('feature'); - geo.createFileReader('jsonReader', { + geo.createFileReader('geojsonReader', { layer: layer }).read(JSON.stringify(data), function () { myMap.draw(); diff --git a/tutorials/choropleth/index.pug b/tutorials/choropleth/index.pug index fd29656648..a3052b2fa6 100644 --- a/tutorials/choropleth/index.pug +++ b/tutorials/choropleth/index.pug @@ -4,7 +4,7 @@ block mainTutorial :markdown-it # Tutorial - Choropleth Map First, let's create our map, add a base map, and create a feature layer. Also, to keep it simple, we import our data inline. - + +codeblock('javascript', 1, undefined, true). var map = geo.map({ node: '#map', @@ -21,7 +21,7 @@ block mainTutorial We can use GeoJSON reader to create the polygon feature. +codeblock('javascript', 2, 1, true). - var reader = geo.createFileReader('jsonReader', { 'layer': layer }); + var reader = geo.createFileReader('geojsonReader', { 'layer': layer }); var polygon = null; reader.read(states, function (features) { polygon = features[0]; diff --git a/tutorials/simple_point/index.pug b/tutorials/simple_point/index.pug index 6240ba8632..b9676fd23e 100644 --- a/tutorials/simple_point/index.pug +++ b/tutorials/simple_point/index.pug @@ -24,9 +24,9 @@ block mainTutorial :markdown-it We create a layer first. - + +codeblock('javascript', 2). - var layer = map.createLayer('feature'); + var layer = map.createLayer('feature'); :markdown-it @@ -49,7 +49,7 @@ block mainTutorial +codeblock('javascript', 10, 2, false, 'Step 3-B'). var geojsonCities = {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-74.0059413,40.7127837]},"properties":{"name":"New York","population":"8405837"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-118.2436849,34.0522342]},"properties":{"name":"Los Angeles","population":"3884307"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-87.6297982,41.8781136]},"properties":{"name":"Chicago","population":"2718782"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-95.3698028,29.7604267]},"properties":{"name":"Houston","population":"2195914"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-75.1652215,39.9525839]},"properties":{"name":"Philadelphia","population":"1553165"}},{"type":"Feature","geometry":{"type":"Point","coordinates":[-112.0740373,33.4483771]},"properties":{"name":"Phoenix","population":"1513367"}}]}; - var reader = geo.createFileReader('jsonReader', { 'layer': layer }); + var reader = geo.createFileReader('geojsonReader', { 'layer': layer }); reader.read(geojsonCities, function (features) { features[0].draw(); // Or we can draw the whole map diff --git a/webpack.base.config.js b/webpack.base.config.js index 094059bb46..d87d983820 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -83,6 +83,7 @@ module.exports = { use: [{ loader: 'babel-loader', options: { + plugins: [require('babel-plugin-transform-object-rest-spread')], presets: [[ 'env', { targets: { @@ -105,6 +106,7 @@ module.exports = { use: [{ loader: 'babel-loader', options: { + plugins: [require('babel-plugin-transform-object-rest-spread')], presets: [[ 'env', { targets: {