Source: formats/wkt/Wkt.js

/*
 * Copyright 2015-2017 WorldWind Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define([
    '../../error/ArgumentError',
    '../../util/Logger',
    './WktTokens'
], function (ArgumentError,
             Logger,
             WktTokens) {
    /**
     * Wkt is capable of parsing the text representation of the WKT objects. The explanation of what all is
     * supported is to be found in the README.MD in this directory.<br/>
     * <br/>
     * The simplest possible usage is:<br/>
     * var layer = new WorldWind.RenderableLayer();<br/>
     * var parser = new Wkt('POINT (19 23)');<br/>
     * parser.load(null, null, layer);<br/>
     * wwd.addLayer(layer);<br/>
     * This example adds the WKT into the map<br/>
     *
     * The more complex usage allows you to do whatever you want with the parsed objects. For example you can make
     * sure that only points are displayed<br/>
     * var layer = new WorldWind.RenderableLayer();<br/>
     * var parser = new Wkt('POINT (19 23)');<br/>
     * parser.load(function(wkt, objects){<br/>
     *   var shapes = []; <br/>
     *   objects.forEach(function(object){<br/>
     *     if(object.type == WorldWind.WktType.SupportedGeometries.POINT) { <br/>
     *       shapes.push.apply(shapes, object.shapes());<br/>
     *     } <br/>
     *   }); <br/>
     *   <br/>
     *   if(wkt.layer) {<br/>
     *     wkt.layer.addRenderables(shapes);<br/>
     *   }<br/>
     * }, null, layer);<br/>
     * wwd.addLayer(layer);<br/>
     *
     * The most complex usage is when you want to supply different configuration for object before it is added to the layer.<br/>
     * var layer = new WorldWind.RenderableLayer();<br/>
     * var parser = new Wkt('POINT (19 23)');<br/>
     * parser.load(null, function(shape) {<br/>
     *   if(shape.type == WktType.SupportedGeometries.POINT) {<br/>
     *     var shapeAttributes = new ShapeAttributes(null);<br/>
     *     shapeAttributes.fontColor = Color.RED;<br/>
     *     return {<br/>
     *         attributes: shapeAttributes<br/>
     *     };<br/>
     *   }<br/>
     * }, layer);<br/>
     * wwd.addLayer(layer);<br/>
     *
     * @param textRepresentation {String} Text representation of WKT objects.
     * @constructor
     * @alias Wkt
     */
    var Wkt = function (textRepresentation) {
        this.textRepresentation = textRepresentation;

        this._parserCompletionCallback = this.defaultParserCompletionCallback;
        this._shapeConfigurationCallback = this.defaultShapeConfigurationCallback;

        this._layer = null;
    };

    Object.defineProperties(Wkt.prototype, {
        /**
         * The completion callback specified to [load]{@link Wkt#load}. This function is called when
         * wkt parsing is done but before creating shapes for the wkt. It's single argument is
         * the WKT string.
         * @memberof Wkt.prototype
         * @type {Function}
         * @default [defaultParserCompletionCallback]{@link Wkt#defaultParserCompletionCallback}
         * @readonly
         */
        parserCompletionCallback: {
            get: function () {
                return this._parserCompletionCallback;
            }
        },

        /**
         * The attribute callback specified to [load]{@link Wkt#load}.
         * See that method's description for details.
         * @memberof Wkt.prototype
         * @type {Function}
         * @default [defaultShapeConfigurationCallback]{@link Wkt#defaultShapeConfigurationCallback}
         * @readonly
         */
        shapeConfigurationCallback: {
            get: function () {
                return this._shapeConfigurationCallback;
            }
        },

        /**
         * The layer containing the shapes representing the records in this wkt, as specified to this
         * wkt's constructor.
         * @memberof Wkt.prototype
         * @type {RenderableLayer}
         * @readonly
         */
        layer: {
            get: function() {
                return this._layer;
            }
        }
    });

    /**
     * It parses the received string and create the Objects, which then can be rendered.
     * @param parserCompletionCallback {Function} An optional function called when the WKT loading is
     *   complete and all the shapes have been added to the layer.
     * @param shapeConfigurationCallback {Function} This function  is called whenever new shape is created. It provides
     *   the current shape as the first argument. In this way it is possible to modify the shape even provide another one.
     *   If any shape is returned it is used in place of the previous one. This function should be synchronous and if
     *   you want to provide custom shape, it has to be synchronous.
     * @param layer {RenderableLayer} Layer to use for adding all the parsed shapes. It is optional. It is possible to
     *   use this class as only a parser by providing custom parser completion callback.
     */
    Wkt.prototype.load = function (parserCompletionCallback, shapeConfigurationCallback, layer) {
        if(layer) {
            this._layer = layer;
        }

        if (parserCompletionCallback) {
            this._parserCompletionCallback = parserCompletionCallback;
        }

        if (shapeConfigurationCallback) {
            this._shapeConfigurationCallback = shapeConfigurationCallback;
        }

        this.parserCompletionCallback(
            this,
            new WktTokens(this.textRepresentation).objects()
        );
    };

    /**
     * It is the default implementation of the shape configuration callback. It is called for every generated shape.
     * @param shape {Renderable} It is a renderable for which you can provide custom attributes.
     * @returns {Object} This object can contain attributes to be used for the shape, highlight attributes to be used
     *   for the shape, pickDelegate to be used and userProperties. All these properties are applied to the shape.
     */
    Wkt.prototype.defaultShapeConfigurationCallback = function(shape) {
        // The default implementation doesn't change the defaults for the Shapes.
    };

    /**
     * It is the default implementation of the parser completion callback. It is called with all parsed objects and
     * the default one then calls shape configuration callback for each of them and then add the shapes into
     * the provided layer if such is provided.
     * @param objects {WktObject[]} Array of the Renderables to be displayed. This is the last time to modify them.
     * @param wkt {Wkt} Object representing the Wkt. It is used to retrieve layer, shape configuration callback.
     */
    Wkt.prototype.defaultParserCompletionCallback = function(wkt, objects) {
        // The default implementation doesn't change the defaults for the Shapes.
        var shapeConfigurationCallback = wkt.shapeConfigurationCallback;
        var shapes = [];
        objects.forEach(function(object){
            object.shapes().forEach(function(shape){
                var configuration = shapeConfigurationCallback(object);
                if(configuration && configuration.attributes) {
                    shape.attributes = configuration.attributes;
                }
                if(configuration && configuration.highlightAttributes) {
                    shape.highlightAttributes = configuration.highlightAttributes;
                }
                if(configuration && configuration.pickDelegate) {
                    shape.pickDelegate = configuration.pickDelegate;
                }
                if(configuration && configuration.userProperties) {
                    shape.userProperties = configuration.userProperties;
                }
                shapes.push(shape);
            });
        });

        if(wkt.layer) {
            wkt.layer.addRenderables(shapes);
        }
    };

    return Wkt;
});