# Source: src/js/models/maps/Map.js

    'use strict';

    define(
      [
        'jquery',
        'underscore',
        'backbone',
        'collections/maps/Features',
        'models/maps/Feature',
        'collections/maps/MapAssets',
      ],
      function (
        $,
        _,
        Backbone,
        Features,
        Feature,
        MapAssets,
      ) {
        /**
         * @class MapModel
         * @classdesc The Map Model contains all of the settings and options for a required to
         * render a map view.
         * @classcategory Models/Maps
         * @name MapModel
         * @since 2.18.0
         * @extends Backbone.Model
         */
        var MapModel = Backbone.Model.extend(
          /** @lends MapModel.prototype */ {

            /**
             * Configuration options for a {@link MapModel} that control the appearance of the
             * map, the data/imagery displayed, and which UI components are rendered. A
             * MapConfig object can be used when initializing a Map model, e.g. `new
             * Map(myMapConfig)`
             * @namespace {Object} MapConfig
             * @property {MapConfig#CameraPosition} [homePosition] - A set of coordinates that
             * give the (3D) starting point of the Viewer. This position is also where the
             * "home" button in the Cesium widget will navigate to when clicked.
             * @property {MapConfig#MapAssetConfig[]} [layers] - A collection of imagery,
             * tiles, vector data, etc. to display on the map. Layers wil be displayed in the
             * order they appear. The array of the layer MapAssetConfigs are passed to a
             * {@link MapAssets} collection.
             * @property {MapConfig#MapAssetConfig[]} [terrains] - Configuration for one or more digital
             * elevation models (DEM) for the surface of the earth. Note: Though multiple
             * terrains are supported, currently only the first terrain is used in the
             * CesiumWidgetView and there is not yet a UI for switching terrains in the map.
             * The array of the terrain MapAssetConfigs are passed to a {@link MapAssets}
             * collection.
             * @property {Boolean} [showToolbar=true] - Whether or not to show the side bar
             * with layer list, etc. If true, the {@link MapView} will render a
             * {@link ToolbarView}.
             * @property {Boolean} [toolbarOpen=false] - Whether or not the toolbar
             * is open when the map is initialized. Set to false by default, so
             * that the toolbar is hidden by default.
             * @property {Boolean} [showScaleBar=true] - Whether or not to show a scale bar.
             * If true, the {@link MapView} will render a {@link ScaleBarView}.
             * @property {Boolean} [showFeatureInfo=true] - Whether or not to allow users to
             * click on map features to show more information about them.  If true, the
             * {@link MapView} will render a {@link FeatureInfoView} and will initialize
             * "picking" in the {@link CesiumWidgetView}.
             *
             * @example
             * {
             *   "homePosition": {
             *     "latitude": 74.23,
             *     "longitude": -105.7
             *   },
             *   "layers": [
             *     {
             *       "label": "My 3D Tile layer",
             *       "type": "Cesium3DTileset",
             *       "description": "This is an example 3D tileset. This description will be visible in the LayerDetailsView. It will be the default color, since to colorPalette is specified.",
             *       "cesiumOptions": {
             *         "ionAssetId": "555"
             *       },
             *     }
             *   ],
             *   "terrains": [
             *     {
             *       "label": "Arctic DEM",
             *       "type": "CesiumTerrainProvider",
             *       "cesiumOptions": {
             *         "ionAssetId": "3956",
             *         "requestVertexNormals": true
             *       }
             *     }
             *   ],
             *   "showToolbar": true,
             *   "showScaleBar": false,
             *   "showFeatureInfo": false
             * }
            */

            /**
             * Coordinates that describe a camera position for Cesium. Requires at least a
             * longitude and latitude.
             * @typedef {Object} MapConfig#CameraPosition
             * @property {number} longitude - Longitude of the central home point
             * @property {number} latitude - Latitude of the central home point
             * @property {number} [height] - Height above sea level (meters)
             * @property {number} [heading] -  The rotation about the negative z axis
             * (degrees)
             * @property {number} [pitch] - The rotation about the negative y axis (degrees)
             * @property {number} [roll] - The rotation about the positive x axis (degrees)
             *
             * @example
             * {
             *  longitude: -119.8489,
             *  latitude: 34.4140
             * }
             *
             * @example
             * {
             *  longitude: -65,
             *  latitude: 56,
             *  height: 10000000,
             *  heading: 1,
             *  pitch: -90,
             *  roll: 0
             * }
            */

            /**
             * Overrides the default Backbone.Model.defaults() function to specify default
             * attributes for the Map
             * @name MapModel#defaults
             * @type {Object}
             * @property {MapConfig#CameraPosition} [homePosition={longitude: -65, latitude: 56, height: 10000000, heading: 1, pitch: -90, roll: 0}]
             * A set of coordinates that give the
             * (3D) starting point of the Viewer. This position is also where the "home"
             * button in the Cesium viewer will navigate to when clicked.
             * @property {MapAssets} [terrains = new MapAssets()] - The terrain options to
             * show in the map.
             * @property {MapAssets} [layers = new MapAssets()] - The imagery and vector data
             * to render in the map.
             * @property {Features} [selectedFeatures = new Features()] - Particular features
             * from one or more layers that are highlighted/selected on the map. The
             * 'selectedFeatures' attribute is updated by the map widget (cesium) with a
             * Feature model when a user selects a geographical feature on the map (e.g. by
             * clicking)
             * @property {Boolean} [showToolbar=true] - Whether or not to show the side bar
             * with layer list, etc. True by default.
             * @property {Boolean} [toolbarOpen=false] - Whether or not the toolbar
             * is open when the map is initialized. Set to false by default, so
             * that the toolbar is hidden by default.
             * @property {Boolean} [showScaleBar=true] - Whether or not to show a scale bar.
             * @property {Boolean} [showFeatureInfo=true] - Whether or not to allow users to
             * click on map features to show more information about them.
             * @property {Object} [currentPosition={ longitude: null, latitude: null, height: null}]
             * An object updated by the map widget to show the longitude, latitude, and
             * height (elevation) at the position of the mouse on the map. Note: The
             * CesiumWidgetView does not yet update the height property.
             * @property {Object} [currentScale={ meters: null, pixels: null }] An object
             * updated by the map widget that gives two equivalent measurements based on the
             * map's current position and zoom level: The number of pixels on the screen that
             * equal the number of meters on the map/globe.
             * @property {Object} [currentViewExtent={ north: null, east: null, south: null, west: null }]
             * An object updated by the map widget that gives the extent of the current
             * visible area as a bounding box in longitude/latitude coordinates.
            */
            defaults: function () {
              return {
                homePosition: {
                  longitude: -65,
                  latitude: 56,
                  height: 10000000,
                  heading: 1,
                  pitch: -90,
                  roll: 0
                },
                layers: new MapAssets([{
                    type: 'NaturalEarthII',
                    label: 'Base layer'
                  }]),
                terrains: new MapAssets(),
                selectedFeatures: new Features(),
                showToolbar: true,
                toolbarOpen: false,
                showScaleBar: true,
                showFeatureInfo: true,
                currentPosition: {
                  longitude: null,
                  latitude: null,
                  height: null
                },
                currentScale: {
                  meters: null,
                  pixels: null
                },
                currentViewExtent: {
                  north: null,
                  east: null,
                  south: null,
                  west: null
                }
              };
            },

            /**
             * Run when a new Map is created.
             * @param {MapConfig} config - An object specifying configuration options for the
             * map. If any config option is not specified, the default will be used instead
             * (see {@link MapModel#defaults}).
             */
            initialize: function (config) {
              try {
                if (config) {

                  if (config.layers && config.layers.length && Array.isArray(config.layers)) {
                    this.set('layers', new MapAssets(config.layers))
                    this.get('layers').setMapModel(this)
                  }

                  if (config.terrains && config.terrains.length && Array.isArray(config.terrains)) {
                    this.set('terrains', new MapAssets(config.terrains))
                  }

                }
              }
              catch (error) {
                console.log(
                  'There was an error initializing a Map model' +
                  '. Error details: ' + error
                );
              }
            },

            /**
             * Set or unset the selected Features on the map model. A selected feature is a
             * polygon, line, point, or other element of vector data that is in focus on the
             * map (e.g. because a user clicked it to show more details.)
             * @param {(Feature|Object[])} features - An array of Feature models or objects with
             * attributes to set on new Feature models. If no features argument is passed to
             * this function, then any currently selected feature will be removed (as long as
             * replace is set to true)
             * @param {Boolean} [replace=true] - If true, any currently selected features will
             * be replaced with the newly selected features. If false, then the newly selected
             * features will be added to any that are currently selected.
             */
            selectFeatures(features, replace = true) {
              try {

                const model = this;
                const defaults = new Feature().defaults()

                if (!model.get('selectedFeatures')) {
                  model.set('selectedFeatures', new Features())
                }

                // If no feature is passed to this function (and replace is true), then empty
                // the Features collection
                if (!features || !Array.isArray(features)) {
                  features = []
                }

                // If feature is a Feature model, get the attributes to update the model.
                features.forEach(function (feature, i) {
                  if (feature instanceof Feature) {
                    feature = feature.attributes
                  }
                  features[i] = _.extend(_.clone(defaults), feature)
                })

                // Update the Feature model with the new selected feature information.
                const options = {
                  remove: replace
                }
                model.get('selectedFeatures').set(features, options)

              }
              catch (error) {
                console.log(
                  'Failed to select a Feature in a Map model' +
                  '. Error details: ' + error
                );
              }
            },

          });

        return MapModel;
      });