diff --git a/ng-workspace/projects/OpenWaterFoundation/common/CHANGELOG.md b/ng-workspace/projects/OpenWaterFoundation/common/CHANGELOG.md index b402aa9..59db0e5 100644 --- a/ng-workspace/projects/OpenWaterFoundation/common/CHANGELOG.md +++ b/ng-workspace/projects/OpenWaterFoundation/common/CHANGELOG.md @@ -21,6 +21,13 @@ in its table's cells. The fixed component's were: * `DialogDataTableComponent` * `DialogDataTableLightComponent` * `DialogTSTableComponent` +* Fixed a bug in the Dashboard Component that would briefly show the 404 page before +the dashboard was displayed. + +### Refactoring ### + +* Moved all +* Removed a half dozen unused class variables in the Map Component. ### Features / Enhancements ### @@ -29,7 +36,7 @@ to the console. Depending on debugLevel, different amounts of messages are print The way it is used now will probably be changed/updated in the future. * Added the first iteration of the Story component. This is essentially a story map that can be added as either a main or sub menu to the InfoMapper, and utilizes -the Dashboard component and its widgets for data layout. +the Dashboard component and its widgets for data layout and visualization. # 4.0.2 (2022-08-05) # diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-background-group/legend-background-group.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-background-group/legend-background-group.component.ts index 283088c..a623de6 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-background-group/legend-background-group.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-background-group/legend-background-group.component.ts @@ -15,10 +15,10 @@ export class LegendBackgroundGroupComponent implements AfterViewInit { /** The background geoLayerViewGroup passed as input from the Map Component when * this component is created. */ - @Input() geoLayerViewGroup: any; + @Input('geoLayerViewGroup') geoLayerViewGroup: any; /** EventEmitter that alerts the Map component (parent) that an update has happened, * and sends the selected background's geoLayerView name property. */ - @Output() callSelectBackgroundLayer = new EventEmitter(); + @Output('callSelectBackgroundLayer') callSelectBackgroundLayer = new EventEmitter(); /** diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-layer-group/legend-layer-group.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-layer-group/legend-layer-group.component.ts index 1c6ab22..2e5b9e8 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-layer-group/legend-layer-group.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/legend/legend-layer-group/legend-layer-group.component.ts @@ -45,15 +45,15 @@ export class LegendLayerGroupComponent implements AfterViewInit { /** An object with each geoLayerId as the key, and all features of a geoLayerView, * usually a FeatureCollection, as the value. */ - @Input() allFeatures: any; + @Input('allFeatures') allFeatures: any; /** EventEmitter that alerts the Map component (parent) that an update has happened, * and sends the selected background's geoLayerView name property. */ // TODO: Not being used, can be used for another emitter to the Map Component. - @Output() callSelectBackgroundLayer = new EventEmitter(); + @Output('callSelectBackgroundLayer') callSelectBackgroundLayer = new EventEmitter(); /** A categorized configuration object with the geoLayerId as key and a list of * name followed by color for each feature in the Leaflet layer to be shown in * the sidebar. */ - @Input() categorizedLayerColors: any; + @Input('categorizedLayerColors') categorizedLayerColors: any; /** * */ @@ -64,24 +64,24 @@ export class LegendLayerGroupComponent implements AfterViewInit { destroyed = new Subject(); /** An object containing any event actions with their id as the key and the action * object itself as the value. */ - @Input() eventActions: any; + @Input('eventActions') eventActions: any; /** The geoLayerViewGroup passed as input from the Map Component when * this component is created. */ - @Input() geoLayerViewGroup: any; + @Input('geoLayerViewGroup') geoLayerViewGroup: any; /** An object of Style-like objects containing: * key : geoLayerId * value: object with style properties * For displaying a graduated symbol in the Leaflet legend. */ - @Input() graduatedLayerColors: any; + @Input('graduatedLayerColors') graduatedLayerColors: any; /** Boolean test variable for use with Angular Material slide toggle. */ isChecked = true; /** Represents the Date string since the last time a layer was updated. */ - @Input() lastRefresh: any; + @Input('lastRefresh') lastRefresh: any; /** Object containing a layer geoLayerId as the ID, and an object of properties * set by a user-defined classification file. */ - @Input() layerClassificationInfo: any; + @Input('layerClassificationInfo') layerClassificationInfo: any; /** Reference to the Map Component Leaflet map object. */ - @Input() mainMap: any; + @Input('mainMap') mainMap: any; @Input('mapConfig') mapConfig: IM.GeoMapProject; /** The instance of the MapLayerManager, a helper class that manages MapLayerItem diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map-manager.ts b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map-manager.ts index 9d1a765..cf9a2df 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map-manager.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map-manager.ts @@ -9,10 +9,6 @@ /** Object to hold each Leaflet map reference as the value, with the map * configuration's geoMapId property as the key. */ maps: {} = {}; - /** - * - */ - uniqueId = 0; /** @@ -33,25 +29,36 @@ /** * Adds a Leaflet map reference to the @var maps object with the unique mapID as the key. * @param mapID A string representing the geoMapId from the map configuration file. - * @param mapRef The reference to the Map Component's @var mainMap Leaflet map. + * @param map The reference to the Map Component's @var mainMap Leaflet map. */ - addMap(mapID: string, mapRef: any): void { - this.maps[mapID] = mapRef; + addMap(mapID: string, map: any): void { + this.maps[mapID] = map; } /** - * + * @returns A boolean on whether this map has already been created. + * @param geoMapId The map's geoMapId property from it's configuration file. */ - createUniqueId(): string { - return 'mapID' + (this.uniqueId += 1); + doesMapExist(geoMapId: string): boolean { + return geoMapId in this.maps; } /** - * @returns A boolean on whether this map has already been created. - * @param geoMapId The map's geoMapId property from it's configuration file. + * + * @param mapID */ - mapAlreadyCreated(geoMapId: string): boolean { - return geoMapId in this.maps; + getMap(mapID: string): any { + + if (this.maps[mapID]) { + return this.maps[mapID]; + } else { + for (const key in this.maps) { + if (key.includes(mapID)) { + return this.maps[key]; + } + } + } + return null; } /** diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.css b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.css index 183b49b..66e0737 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.css +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.css @@ -129,6 +129,40 @@ line-break points, such as dashes '-'. */ padding: 5px 10px !important; } +.scroll-toggle { + height: 30px; + width: 30px; + background-color: rgba(255, 255, 255, 0.9); + border-radius: 5px; + border: rgba(0,0,0,0.3) solid 2px; + + display: flex; + justify-content: center; + align-items: center; +} + +/* */ +.scroll-toggle .scroll-toggle-tooltip { + visibility: hidden; + width: 250px; + background-color: rgba(0,0,0,0.7); + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 0; + + /* Position the tooltip. */ + position: absolute; + z-index: 1; + top: -15px; + right: 150%; +} + +/* */ +.scroll-toggle:hover .scroll-toggle-tooltip { + visibility: visible; +} + /* Div to be displayed if the correct app config object ID is given in the URL. */ .show-map-container { height: 100%; diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.ts index 733b326..5794412 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/map/map.component.ts @@ -1,9 +1,11 @@ import { AfterViewInit, Component, + Inject, Input, OnDestroy, ViewContainerRef, ViewEncapsulation } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; @@ -67,16 +69,10 @@ export class MapComponent implements AfterViewInit, OnDestroy { @Input('app-config') appConfigStandalonePath: any; /** Application version. */ appVersion: string; - /** Array of background map groups from the map config file. Used for displaying - * background maps in the sidebar panel. */ - backgroundMapGroups = []; - /** Accesses container ref in order to add and remove background layer components - * dynamically. */ - backgroundViewContainerRef: ViewContainerRef; /** Boolean showing if the path given to some file is incorrect. */ badPath = false; /** Object that holds the base maps that populates the leaflet sidebar. */ - baseMaps: {} = {}; + mapBackgroundLayers = {}; /** A categorized configuration object with the geoLayerId as key and a list of * name followed by color for each feature in the Leaflet layer to be shown in * the sidebar. */ @@ -100,11 +96,16 @@ export class MapComponent implements AfterViewInit, OnDestroy { /** Subject that is completed when this component is destroyed. The breakpoint * observer will stop listening to screen size at that time. */ destroyed = new Subject(); - /** The number of seconds since the last layer refresh. */ - elapsedSeconds = 0; + /** Set to true if this map is currently being shown in a story, and new actions + * will need to be performed. */ + @Input('story') displayedInStory = false; /** An object containing any event actions with their id as the key and the action * object itself as the value. */ - eventActions: {} = {}; + eventActions = {}; + /** All used icons in the MapComponent. */ + faCaretLeft = faCaretLeft; + faInfoCircle = faInfoCircle; + faLayerGroup = faLayerGroup; /** For the Leaflet map's config file subscription object so it can be closed on * this component's destruction. */ private forkJoinSub = null; @@ -113,25 +114,13 @@ export class MapComponent implements AfterViewInit, OnDestroy { * value: object with style properties * For displaying a graduated symbol in the Leaflet legend. */ graduatedLayerColors = {}; - /** Global value to access container ref in order to add and remove sidebar info - * components dynamically. */ - infoViewContainerRef: ViewContainerRef; /** Represents the Date string since the last time a layer was updated. */ lastRefresh = {}; /** Object containing a layer geoLayerId as the ID, and an object of properties * set by a user-defined classification file. */ layerClassificationInfo = {}; - /** Class variable to access container ref in order to add and remove map layer - * component dynamically. */ - layerViewContainerRef: ViewContainerRef; /** Unique string for this Map component's Leaflet div Id attribute. */ leafletMapContainerId: string; - /** Object that contains each geoLayerViewGroupId as the key, and a boolean describing - * whether the group's legend expansion panel is open or closed. */ - backgroundLegendExpansion = {}; - /** Global value to access container ref in order to add and remove symbol descriptions - * components dynamically. */ - legendSymbolsViewContainerRef: ViewContainerRef; /** The reference for the Leaflet map. */ mainMap: any; /** The map configuration object read in as this component's map configuration file. */ @@ -141,10 +130,6 @@ export class MapComponent implements AfterViewInit, OnDestroy { @Input('map-config') mapConfigStandalonePath: string; /** The map configuration subscription, unsubscribed to on component destruction. */ private mapConfigSub = null; - /** Determines whether the map config file path was correct, found, and read in. - * If true, the map will be displayed. If false, the 404 div will let the user - * know there was an issue with the URL/path to the */ - mapFilePresent: boolean; /** A variable to keep track of whether or not the leaflet map has already been * initialized. This is useful for resetting the page and clearing the map using * map.remove() which can only be called on a previously initialized map. */ @@ -169,18 +154,12 @@ export class MapComponent implements AfterViewInit, OnDestroy { * waste time/resources initializing sidebar twice, but rather edit the information * in the already initialized sidebar. */ sidebarInitialized: boolean = false; - /** Boolean of whether or not refresh is displayed. */ - showRefresh: boolean = true; /** The windowManager instance; To create, maintain, and remove multiple open dialogs. */ windowManager: WindowManager = WindowManager.getInstance(); /** * */ validMapID: boolean; - /** All used icons in the MapComponent. */ - faCaretLeft = faCaretLeft; - faInfoCircle = faInfoCircle; - faLayerGroup = faLayerGroup; /** @@ -197,7 +176,7 @@ export class MapComponent implements AfterViewInit, OnDestroy { */ constructor(private actRoute: ActivatedRoute, private breakpointObserver: BreakpointObserver, public commonService: OwfCommonService, public dialog: MatDialog, private router: Router, - private logger: CommonLoggerService) { + private logger: CommonLoggerService, @Inject(DOCUMENT) private document: Document) { if (window['Cypress']) window['MapComponent'] = this; @@ -255,10 +234,35 @@ export class MapComponent implements AfterViewInit, OnDestroy { } /** - * Get the unique number for this Leaflet map from the Map Manager. + * Creates all Leaflet Controls on the map and ensures that they're drawn in the + * correct order. */ - get newLeafletMapContainerID(): string { - return this.mapManager.createUniqueId(); + private addAllMapControls(): void { + + // Add background layers to the map in the topright. + L.control.layers(this.mapBackgroundLayers).addTo(this.mainMap); + // Add home & zoom in/zoom out to the map in the topright. + this.addZoomHomeControl(); + // Conditionally add a mouse toggle control if the map is shown in a story. + if (this.displayedInStory) { + this.addMouseScrollToggleControl(); + } + + // Create the zoom level control. + var mapZoom = this.createZoomLevelControl(); + // Create the lat and long of the mouse position. + var mousePosition = this.createMousePositionControl(); + // Bottom Left corner control that shows the scale in km and miles of the map. + var mapScale = L.control.scale({ position: 'bottomleft', imperial: true }); + + // Add each control in the desired order. From top to bottom on the map (they + // are stacked on top of each other): + // Scale + // Map zoom level + // Mouse Position / Coordinates + this.mainMap.addControl(mousePosition); + this.mainMap.addControl(mapZoom); + this.mainMap.addControl(mapScale); } /** @@ -271,29 +275,69 @@ export class MapComponent implements AfterViewInit, OnDestroy { } /** - * Dynamically add the layer information to the sidebar coming in from the map - * configuration file. - * @param configFile - */ - private addLayerToSidebar(configFile: any) { - // Reset the sidebar components so elements are added on top of each other. - this.resetSidebarComponents(); - - // Creates new layerToggle component in sideBar for each layer specified in - // the config file, sets data based on map service. - // var geoLayers = configFile.geoMaps[0].geoLayers; - let mapGroups: any[] = []; - let viewGroups: any = configFile.geoMaps[0].geoLayerViewGroups; - - viewGroups.forEach((group: any) => { - if (group.properties.isBackground === undefined || - group.properties.isBackground === "false") { - mapGroups.push(group); + * Creates the div that displays the Map title and layer feature information. + */ + private addMapTitle(): void { + + var _this = this; + + // Create the control on the Leaflet map + var mapTitle = L.control({ position: 'topleft' }); + // Add the title to the map in a div whose class name is 'info' + mapTitle.onAdd = function () { + this._div = L.DomUtil.create('div', 'upper-left-map-info'); + this._div.id = _this.geoMapId + '-title-card'; + this.update(); + return this._div; + }; + // When the title-card is created, have it say this + mapTitle.update = function () { + this._div.innerHTML = ('

' + _this.geoMapName + '

'); + }; + mapTitle.addTo(this.mainMap); + } + + /** + * Disables this map's mouse scroll and creates & adds the ability to toggle the + * mouse scroll in the upper right side of the map. + */ + private addMouseScrollToggleControl(): any { + + var _this = this; + + this.mainMap.scrollWheelZoom.disable(); + + let toggleScrollControl = L.control({ position: 'topright' }); + + toggleScrollControl.onAdd = function() { + this._div = L.DomUtil.create('div', 'scroll-toggle'); + + L.DomEvent.disableClickPropagation(this._div) + .disableClickPropagation(this._div); + + this._div.innerHTML = '' + + '' + + 'Click to toggle mouse scroll wheel behavior.
' + + '[ X ] Scroll story pages forward/back.
[   ] Scroll zooms map.
' + + '
'; + + return this._div; + }; + + setTimeout(function() { + var scrollToggleATagElement = this.document.querySelector('.' + + _this.mapConfig.geoMaps[0].geoMapId + '-scroll-toggle-a'); + + if (scrollToggleATagElement) { + scrollToggleATagElement.addEventListener('click', _this.scrollToggle.bind(_this)); } - if (group.properties.isBackground === "true") - this.backgroundMapGroups.push(group); }); + toggleScrollControl.addTo(this.mainMap); } /** @@ -302,7 +346,7 @@ export class MapComponent implements AfterViewInit, OnDestroy { * @param eventObject The object containing the type of event as the key (e.g. click-eCP) * and the entire event object from the popup template file. */ - private addToEventActions(eventObject: any): void { + private addToEventActions(eventObject: any): void { if (eventObject['click-eCP'] && eventObject['click-eCP'].actions) { for (let action of eventObject['click-eCP'].actions) { if (action.id) { @@ -312,6 +356,107 @@ export class MapComponent implements AfterViewInit, OnDestroy { } } + /** + * Creates the ZoomHome control by extending the Leaflet Zoom class. Displays + * a '+' for zooming in, '-' for zooming out, and a house Font Awesome icon in + * the upper right section of the map. + */ + private addZoomHomeControl(): void { + + var _this = this; + + L.Control.ZoomHome = L.Control.Zoom.extend({ + options: { + position: 'topleft', + zoomInText: '+', + zoomInTitle: 'Zoom in', + zoomOutText: '-', + zoomOutTitle: 'Zoom out', + zoomHomeIcon: '\uf008', + zoomHomeTitle: 'Home', + homeCoordinates: null, + homeZoom: null + }, + + onAdd: function (map: any) { + var controlName = 'leaflet-control-zoomhome', + container = L.DomUtil.create('div', controlName + ' leaflet-bar'), + options = this.options; + + if (options.homeCoordinates === null) { + options.homeCoordinates = map.getCenter(); + } + if (options.homeZoom === null) { + options.homeZoom = map.getZoom(); + } + + this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, + controlName + '-in', container, this._zoomIn.bind(this)); + + var zoomHomeText = ''; + this._zoomHomeButton = this._createButton(zoomHomeText, options.zoomHomeTitle, + controlName + '-home', container, this._zoomHome.bind(this)); + + this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, + controlName + '-out', container, this._zoomOut.bind(this)); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + setHomeBounds: function (bounds) { + if (bounds === undefined) { + bounds = this._map.getBounds(); + } else { + if (typeof bounds.getCenter !== 'function') { + bounds = L.latLngBounds(bounds); + } + } + this.options.homeZoom = this._map.getBoundsZoom(bounds); + this.options.homeCoordinates = bounds.getCenter(); + }, + + setHomeCoordinates: function (coordinates) { + if (coordinates === undefined) { + coordinates = this._map.getCenter(); + } + this.options.homeCoordinates = coordinates; + }, + + setHomeZoom: function (zoom) { + if (zoom === undefined) { + zoom = this._map.getZoom(); + } + this.options.homeZoom = zoom; + }, + + getHomeZoom: function () { + return this.options.homeZoom; + }, + + getHomeCoordinates: function () { + return this.options.homeCoordinates; + }, + + _zoomHome: function (e) { + this._map.closePopup(); + this._map.setView(this.options.homeCoordinates, this.options.homeZoom); + } + }); + + L.control.zoomHome = function(opt: any) { + return new L.Control.ZoomHome(opt) + } + L.control.zoomHome({ + position: 'topright', + zoomHomeTitle: 'Zoom to initial extent' + }).addTo(this.mainMap); + } + /** * A CSV classification file is given by the user, so use that to create the colorTable * to add to the categorizedLayerColors array for creating the legend colors. @@ -389,13 +534,15 @@ export class MapComponent implements AfterViewInit, OnDestroy { this.leafletMapContainerId + '"', this.debugFlag, this.debugLevelFlag); // Create a Leaflet Map and set the default layers. this.mainMap = L.map(this.leafletMapContainerId, { - layers: [this.baseMaps[this.getDefaultBackgroundLayer()]], + layers: [this.mapBackgroundLayers[this.getDefaultBackgroundLayer()]], // We're using our own zoom control for the map, so we don't need the default zoomControl: false, wheelPxPerZoomLevel: 150, zoomSnap: 0.1 }); + this.mapManager.addMap(this.mapConfig.geoMaps[0].geoMapId, this.mainMap); + // Retrieve the initial extent from the config file and set the map view let extentInitial = this.getExtentInitial(); this.mainMap.setView([extentInitial[1], extentInitial[0]], extentInitial[2]); @@ -510,7 +657,7 @@ export class MapComponent implements AfterViewInit, OnDestroy { this.allFeatures[geoLayer.geoLayerId].features.length + (this.allFeatures[geoLayer.geoLayerId].features.length === 1 ? ' feature.' : ' features.'); - this.logger.print('info', 'MapComponent.buildMap - ' + message); + this.logger.print('trace', 'MapComponent.buildMap - ' + message, this.debugFlag, this.debugLevelFlag); } var eventObject: any = {}; @@ -1589,19 +1736,19 @@ export class MapComponent implements AfterViewInit, OnDestroy { // Create background layers from the configuration file. let backgroundLayers: any[] = this.getBackgroundLayers(); // Iterate over each background layer, create them using tileLayer, and add - // them to the baseMaps class object. + // them to the mapBackgroundLayers class object. backgroundLayers.forEach((geoLayer: IM.GeoLayer) => { let leafletBackgroundLayer = L.tileLayer(geoLayer.sourcePath, { attribution: geoLayer.properties.attribution, maxZoom: geoLayer.properties.zoomLevelMax ? parseInt(geoLayer.properties.zoomLevelMax) : 18 }); - this.baseMaps[this.getBackgroundGeoLayerViewFromId(geoLayer.geoLayerId).name] = leafletBackgroundLayer; + this.mapBackgroundLayers[this.getBackgroundGeoLayerViewFromId(geoLayer.geoLayerId).name] = leafletBackgroundLayer; - var bkgdGeoLayerView = this.getBackgroundGeoLayerViewFromId(geoLayer.geoLayerId); + var backgroundGeoLayerView = this.getBackgroundGeoLayerViewFromId(geoLayer.geoLayerId); - if (bkgdGeoLayerView.properties.refreshInterval) { - var refreshInterval = this.getRefreshInterval(bkgdGeoLayerView.geoLayerId); - var refreshOffset = this.getRefreshOffset(bkgdGeoLayerView.geoLayerId, refreshInterval); + if (backgroundGeoLayerView.properties.refreshInterval) { + var refreshInterval = this.getRefreshInterval(backgroundGeoLayerView.geoLayerId); + var refreshOffset = this.getRefreshOffset(backgroundGeoLayerView.geoLayerId, refreshInterval); // Check if the parsing was successful. if (isNaN(refreshInterval)) { } else { @@ -1613,57 +1760,6 @@ export class MapComponent implements AfterViewInit, OnDestroy { }); } - /** - * Creates all Leaflet Controls on the map and ensures that they're drawn in the - * correct order. - */ - private addAllMapControls(): void { - - // Add background layers to the map in the topright. - L.control.layers(this.baseMaps).addTo(this.mainMap); - // Add home & zoom in/zoom out to the map in the topright. - this.addZoomHomeControl(); - - // Create the zoom level control. - var mapZoom = this.createZoomLevelControl(); - // Create the lat and long of the mouse position. - var mousePosition = this.createMousePositionControl(); - // Bottom Left corner control that shows the scale in km and miles of the map. - var mapScale = L.control.scale({ position: 'bottomleft', imperial: true }); - - - // Add each control in the desired order. From top to bottom on the map: - // Scale - // Map zoom level - // Mouse Position / Coordinates - this.mainMap.addControl(mousePosition); - this.mainMap.addControl(mapZoom); - this.mainMap.addControl(mapScale); - } - - /** - * Creates the div that displays the Map title and layer feature information. - */ - private addMapTitle(): void { - - var _this = this; - - // Create the control on the Leaflet map - var mapTitle = L.control({ position: 'topleft' }); - // Add the title to the map in a div whose class name is 'info' - mapTitle.onAdd = function () { - this._div = L.DomUtil.create('div', 'upper-left-map-info'); - this._div.id = _this.geoMapId + '-title-card'; - this.update(); - return this._div; - }; - // When the title-card is created, have it say this - mapTitle.update = function () { - this._div.innerHTML = ('

' + _this.geoMapName + '

'); - }; - mapTitle.addTo(this.mainMap); - } - /** * Initializes the content of the topleft title card div area. */ @@ -1683,112 +1779,11 @@ export class MapComponent implements AfterViewInit, OnDestroy { div.innerHTML = divContents; } - /** - * Creates the ZoomHome control by extending the Leaflet Zoom class. Displays - * a '+' for zooming in, '-' for zooming out, and a house Font Awesome icon in - * the upper right section of the map. - */ - private addZoomHomeControl(): void { - - var _this = this; - - L.Control.ZoomHome = L.Control.Zoom.extend({ - options: { - position: 'topleft', - zoomInText: '+', - zoomInTitle: 'Zoom in', - zoomOutText: '-', - zoomOutTitle: 'Zoom out', - zoomHomeIcon: '\uf008', - zoomHomeTitle: 'Home', - homeCoordinates: null, - homeZoom: null - }, - - onAdd: function (map: any) { - var controlName = 'leaflet-control-zoomhome', - container = L.DomUtil.create('div', controlName + ' leaflet-bar'), - options = this.options; - - if (options.homeCoordinates === null) { - options.homeCoordinates = map.getCenter(); - } - if (options.homeZoom === null) { - options.homeZoom = map.getZoom(); - } - - this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, - controlName + '-in', container, this._zoomIn.bind(this)); - - var zoomHomeText = ''; - - this._zoomHomeButton = this._createButton(zoomHomeText, options.zoomHomeTitle, - controlName + '-home', container, this._zoomHome.bind(this)); - this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, - controlName + '-out', container, this._zoomOut.bind(this)); - - this._updateDisabled(); - map.on('zoomend zoomlevelschange', this._updateDisabled, this); - - return container; - }, - - setHomeBounds: function (bounds) { - if (bounds === undefined) { - bounds = this._map.getBounds(); - } else { - if (typeof bounds.getCenter !== 'function') { - bounds = L.latLngBounds(bounds); - } - } - this.options.homeZoom = this._map.getBoundsZoom(bounds); - this.options.homeCoordinates = bounds.getCenter(); - }, - - setHomeCoordinates: function (coordinates) { - if (coordinates === undefined) { - coordinates = this._map.getCenter(); - } - this.options.homeCoordinates = coordinates; - }, - - setHomeZoom: function (zoom) { - if (zoom === undefined) { - zoom = this._map.getZoom(); - } - this.options.homeZoom = zoom; - }, - - getHomeZoom: function () { - return this.options.homeZoom; - }, - - getHomeCoordinates: function () { - return this.options.homeCoordinates; - }, - - _zoomHome: function (e) { - this._map.closePopup(); - this._map.setView(this.options.homeCoordinates, this.options.homeZoom); - } - }); - - L.control.zoomHome = function(opt: any) { - return new L.Control.ZoomHome(opt) - } - L.control.zoomHome({ - position: 'topright', - zoomHomeTitle: 'Zoom to initial extent' - }).addTo(this.mainMap); - } - /** * * @returns */ - private createMousePositionControl(): L.Control { + private createMousePositionControl(): any { var mousePosition = L.control.mousePosition({ position: 'bottomleft', @@ -1810,7 +1805,7 @@ export class MapComponent implements AfterViewInit, OnDestroy { * * @returns */ - private createZoomLevelControl(): L.Control { + private createZoomLevelControl(): any { var _this = this; let mapZoom = L.control({ position: 'bottomleft' }); @@ -2142,10 +2137,12 @@ export class MapComponent implements AfterViewInit, OnDestroy { this.mapConfigSub = this.commonService.getJSONData(fullMapConfigPath, IM.Path.fMCP, this.mapID) .subscribe((mapConfig: IM.GeoMapProject) => { + this.logger.print('info', 'MapComponent.ngAfterViewInit - Map initialization for geoMapId "' + + mapConfig.geoMaps[0].geoMapId + '".'); + // Set the configuration file class variable for the map service. // this.commonService.setMapConfig(mapConfig); this.mapConfig = mapConfig; - this.leafletMapContainerId = this.geoMapId; // Once the mapConfig object is retrieved and set, set the order in which @@ -2154,8 +2151,7 @@ export class MapComponent implements AfterViewInit, OnDestroy { // layers instead of the map service let mapLayerManager: MapLayerManager = MapLayerManager.getInstance(); mapLayerManager.setMapConfigLayerOrder(this.getMapConfigLayerOrder()); - // Add components to the sidebar. - this.addLayerToSidebar(mapConfig); + // Create the map. this.checkIfMapContainerExists(); }); @@ -2529,8 +2525,6 @@ export class MapComponent implements AfterViewInit, OnDestroy { this.mapLayerManager.getMapLayerItem(geoLayer.geoLayerId).getItemLeafletLayer().addData(geoJsonData); // Reset the layer order. this.mapLayerManager.setLayerOrder(); - - this.elapsedSeconds = 0; }); } @@ -2680,14 +2674,25 @@ export class MapComponent implements AfterViewInit, OnDestroy { } /** - * Clears the current data displayed in the sidebar. This makes sure that the sidebar - * is cleared when adding new components due to a page refresh. - */ - private resetSidebarComponents(): void { - if (this.layerViewContainerRef && this.backgroundViewContainerRef) { - if (this.layerViewContainerRef.length > 1 || this.backgroundViewContainerRef.length > 1) { - this.layerViewContainerRef.clear(); - this.backgroundViewContainerRef.clear(); + * + */ + private scrollToggle(): void { + + var scrollToggleTooltipSpanElement = this.document.querySelector('#' + + this.mapConfig.geoMaps[0].geoMapId + '-scroll-toggle-tooltip'); + + if (scrollToggleTooltipSpanElement) { + + var leafletMap = this.mapManager.getMap(this.mapConfig.geoMaps[0].geoMapId); + + if (leafletMap.scrollWheelZoom.enabled()) { + leafletMap.scrollWheelZoom.disable(); + + scrollToggleTooltipSpanElement.innerHTML = 'Click to toggle mouse scroll wheel behavior.
[ X ] Scroll story pages forward/back.
[    ] Scroll zooms map.'; + } else { + leafletMap.scrollWheelZoom.enable(); + + scrollToggleTooltipSpanElement.innerHTML = 'Click to toggle mouse scroll wheel behavior.
[    ] Scroll story pages forward/back.
[ X ] Scroll zooms map.'; } } } @@ -2697,8 +2702,8 @@ export class MapComponent implements AfterViewInit, OnDestroy { * @param name The name of the background selected to set the @var currentBackgroundLayer as. */ public selectBackgroundLayer(name: string): void { - this.mainMap.removeLayer(this.baseMaps[this.currentBackgroundLayer]); - this.mainMap.addLayer(this.baseMaps[name]); + this.mainMap.removeLayer(this.mapBackgroundLayers[this.currentBackgroundLayer]); + this.mainMap.addLayer(this.mapBackgroundLayers[name]); this.currentBackgroundLayer = name; // When a new background layer is selected, the raster layer was being covered diff --git a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/sidepanel-info/sidepanel-info.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/sidepanel-info/sidepanel-info.component.ts index b0b88e9..0d4d868 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/leaflet/sidepanel-info/sidepanel-info.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/leaflet/sidepanel-info/sidepanel-info.component.ts @@ -12,9 +12,9 @@ import * as IM from '@OpenWaterFoundation/common/services'; }) export class SidepanelInfoComponent implements OnInit { - @Input() properties: any; - @Input() appVersion: any; - @Input() projectVersion: any; + @Input('properties') properties: any; + @Input('appVersion') appVersion: any; + @Input('projectVersion') projectVersion: any; constructor(private commonService: OwfCommonService) { } diff --git a/ng-workspace/projects/OpenWaterFoundation/common/services/not-found/not-found.component.html b/ng-workspace/projects/OpenWaterFoundation/common/services/not-found/not-found.component.html index a0c9b8a..78ddd39 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/services/not-found/not-found.component.html +++ b/ng-workspace/projects/OpenWaterFoundation/common/services/not-found/not-found.component.html @@ -9,7 +9,7 @@

{{pageType}} Page does not exist

Oops! The URL is not valid for this application. Sometimes URLs change as - the application is updated. Use the the InfoMapper menu to select the page + the application is updated. Use the InfoMapper menu to select the page of interest, or go to the home page. If you think there is a problem, please report to diff --git a/ng-workspace/projects/OpenWaterFoundation/common/services/owf-common.service.ts b/ng-workspace/projects/OpenWaterFoundation/common/services/owf-common.service.ts index 752dda8..ad5b93c 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/services/owf-common.service.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/services/owf-common.service.ts @@ -57,7 +57,11 @@ export class OwfCommonService { highlighted = new BehaviorSubject(false); /** NOTE: Not currently in use. */ highlightedData = this.highlighted.asObservable(); - /** Constant for the Font Awesome house with chimney SVG path. */ + /** SVG 'd' property path for the Font Awesome computer mouse icon. */ + private readonly constComputerMouseSVGPath = 'M0 352c0 88.38 71.63 160 160 ' + + '160h64c88.38 0 160-71.63 160-160V224H0V352zM176 0H160C71.63 0 0 71.62 0 ' + + '160v32h176V0zM224 0h-16v192H384V160C384 71.62 312.4 0 224 0z' + /** SVG 'd' property path for the Font Awesome house with chimney icon. */ private readonly constHouseChimneySVGPath = 'M511.8 287.6L512.5 447.7C512.5 ' + '450.5 512.3 453.1 512 455.8V472C512 494.1 494.1 512 472 512H456C454.9 ' + '512 453.8 511.1 452.7 511.9C451.3 511.1 449.9 512 448.5 512H392C369.9 ' + @@ -122,7 +126,14 @@ export class OwfCommonService { /** - * Returns the constant SVG path string for drawing the Font Awesome 6 house with + * Returns the SVG path string for drawing the Font Awesome 6 computer mouse icon. + */ + get computerMouseSVGPath(): string { + return this.constComputerMouseSVGPath; + } + + /** + * Returns the SVG path string for drawing the Font Awesome 6 house with * a chimney icon. */ get houseChimneySVGPath(): string { diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ts/DateValueTS.ts b/ng-workspace/projects/OpenWaterFoundation/common/ts/DateValueTS.ts index bd75a38..0b855b2 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ts/DateValueTS.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ts/DateValueTS.ts @@ -714,7 +714,7 @@ All data are reset, except for the identifier, which is assumed to have been set return null; } // Only set the identifier if a new time series. - // Otherwise assume the the existing identifier is to be used (e.g., from a file name). + // Otherwise assume the existing identifier is to be used (e.g., from a file name). ts.setIdentifier(identifier); ts.getIdentifier().setInputType("DateValue"); tslist.push(ts); diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ts/DayTS.ts b/ng-workspace/projects/OpenWaterFoundation/common/ts/DayTS.ts index fe3ea0f..f332df3 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ts/DayTS.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ts/DayTS.ts @@ -381,7 +381,7 @@ export class DayTS extends TS { // // Now transfer the data. To do so, get the // // old position and then set in the new position. We are only concerned - // // with transferring the values for the the old time series that are within the new period... + // // with transferring the values for the old time series that are within the new period... // int column, row, temp_ts_am1 = temp_ts.getDate1().getAbsoluteMonth(); // boolean internDataFlagStrings = getInternDataFlagStrings(); diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.component.ts index 95bc1df..9cad90b 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.component.ts @@ -402,8 +402,7 @@ export class ChartComponent implements OnInit, OnDestroy { } // Iterate over the config array and add the necessary configuration data into - // the data object that will be added to the finalData array. The finalData array - // is what's given as the second argument to Plotly.react. + // the data object that will be added to the finalData array. for (let graphConfig of totalGraphConfig) { data = {}; @@ -445,7 +444,10 @@ export class ChartComponent implements OnInit, OnDestroy { data.y = graphConfig.isCSV ? graphConfig.datasetData : graphConfig.plotlyDatasetData; colorwayArray.push(graphConfig.datasetBackgroundColor); - finalData.push(data); + // This is done + if (finalData.length < this.graphTemplate.product.subProducts[0].data.length) { + finalData.push(data); + } } // Builds the layout object that will be provided for creating the Plotly graph. @@ -682,7 +684,7 @@ export class ChartComponent implements OnInit, OnDestroy { this.isChartError$ = this.isChartError; - switch(this.chartDisplayType) { + switch (this.chartDisplayType) { case IM.ChartDisplayType.dlg: this.setupForDialog(); break; @@ -983,7 +985,6 @@ export class ChartComponent implements OnInit, OnDestroy { this.setChartError = true; return; } - this.obtainAndCreateAllGraphs(); } }); diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.service.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.service.ts index a6bff4d..1ac7df0 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.service.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/core/chart/chart.service.ts @@ -110,7 +110,7 @@ export class ChartService { let startDate: DateTime = timeSeries.getDate1(); let endDate: DateTime = timeSeries.getDate2(); - // The DateTime iterator for the the while loop. + // The DateTime iterator for the while loop. let iter: DateTime = startDate; // The index of the x_axisLabel array to push into the chartJS_yAxisData as // the x property. diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.html b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.html index 083a939..4bf3ae4 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.html +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.html @@ -26,7 +26,7 @@

- +
diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.ts index 1105348..6b24dee 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/dashboard.component.ts @@ -22,11 +22,15 @@ export class DashboardComponent implements OnDestroy { /** The dashboard configuration object read in from the JSON file. */ dashboardConf: IM.DashboardConf; + /** + * + */ + displayedInStory = false; /** Subscription for updating the route for this component. Unsubscribed to in * ngDestroy. */ routeSub: Subscription; /** - * + * A dashboard config object passed in from a */ @Input('dashboardConfig') standaloneDashboardConf: IM.DashboardConf; /** `true` if the id in the URL matches an id from the `app-config.json` file. @@ -102,11 +106,12 @@ export class DashboardComponent implements OnDestroy { return; } - // The the dashboard component is being created and used in another component's + // The dashboard component is being created and used in another component's // template and has provided the dashboardConfig property. if (!this.standaloneDashboardConf) { this.readDashboardConfig(dashboardId); } else { + this.displayedInStory = true; this.dashboardInit(this.standaloneDashboardConf); } diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/chart/chart-widget.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/chart/chart-widget.component.ts index db0aad9..9035925 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/chart/chart-widget.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/chart/chart-widget.component.ts @@ -15,7 +15,7 @@ export class ChartWidgetComponent implements OnInit, OnDestroy { /** The attribute provided to this component when created, e.g. * */ - @Input() chartWidget: IM.ChartWidget; + @Input('chartWidget') chartWidget: IM.ChartWidget; /** diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/image/image.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/image/image.component.ts index 2b99b67..8897847 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/image/image.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/image/image.component.ts @@ -14,7 +14,7 @@ export class ImageComponent { /** The attribute provided to this component when created, e.g. * */ - @Input() imageWidget: IM.ImageWidget; + @Input('imageWidget') imageWidget: IM.ImageWidget; /** String array representing the type of error that occurred while building this * widget. Used by the error widget. */ errorTypes: string[] = []; diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/selector/selector.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/selector/selector.component.ts index 5b3850c..a19916c 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/selector/selector.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/selector/selector.component.ts @@ -43,7 +43,7 @@ export class SelectorComponent { dataLoading$ = this.dataLoading.asObservable(); /** The attribute provided to this component when created, e.g. * */ - @Input() selectorWidget: IM.SelectorWidget; + @Input('selectorWidget') selectorWidget: IM.SelectorWidget; /** The array of feature objects that have been filtered by a user search. To be * updated and reflected in the mat-select main widget dropdown. */ filteredFeatures: any[]; diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/text/text.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/text/text.component.ts index a6a0138..cc7fbc4 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/text/text.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/text/text.component.ts @@ -41,7 +41,7 @@ export class TextComponent implements OnDestroy{ textSub: Subscription; /** The attribute provided to this component when created, e.g. * */ - @Input() textWidget: IM.TextWidget; + @Input('textWidget') textWidget: IM.TextWidget; /** diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/title/title.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/title/title.component.ts index 00ae8e3..5dbea60 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/title/title.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dashboard/widget/title/title.component.ts @@ -4,7 +4,6 @@ import { Component, import { Observable } from 'rxjs'; -import { OwfCommonService } from '@OpenWaterFoundation/common/services'; import * as IM from '@OpenWaterFoundation/common/services'; import { DashboardService } from '../../dashboard.service'; @@ -24,14 +23,13 @@ export class TitleComponent implements OnDestroy{ isTitleError$: Observable; /** The widget object provided as an attribute to this component when created, e.g. * */ - @Input() titleWidget: IM.TitleWidget; + @Input('titleWidget') titleWidget: IM.TitleWidget; /** * - * @param commonService The injected Common library service. + * @param dashboardService The injected dashboard service. */ - constructor(private commonService: OwfCommonService, - private dashboardService: DashboardService) {} + constructor(private dashboardService: DashboardService) {} /** diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/dialog/dialog-heatmap/dialog-heatmap.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/dialog/dialog-heatmap/dialog-heatmap.component.ts index 5795b41..a2ffad5 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/dialog/dialog-heatmap/dialog-heatmap.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/dialog/dialog-heatmap/dialog-heatmap.component.ts @@ -74,7 +74,7 @@ export class DialogHeatmapComponent implements OnInit, OnDestroy { var monthData: any[] = []; var startDate: DateTime = resultsArray[0].getDate1(); var endDate: DateTime = resultsArray[0].getDate2(); - // The DateTime iterator for the the while loop. + // The DateTime iterator for the while loop. var currentDateIter: DateTime = startDate; if (resultsArray[0] instanceof MonthTS) { diff --git a/ng-workspace/projects/OpenWaterFoundation/common/ui/story/story.component.ts b/ng-workspace/projects/OpenWaterFoundation/common/ui/story/story.component.ts index 4f46adb..b1e19b6 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/ui/story/story.component.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/ui/story/story.component.ts @@ -118,7 +118,7 @@ export class StoryComponent implements OnInit, OnDestroy { this.debugFlag = this.actRoute.snapshot.queryParamMap.get('debug'); this.debugLevelFlag = this.actRoute.snapshot.queryParamMap.get('debugLevel'); - this.logger.print('info', 'StoryComponent.ngOnInit - Story component created.'); + this.logger.print('info', 'StoryComponent.ngOnInit - Story initialization.'); this.currentURL = this.router.url.split('#')[0].substring(1); diff --git a/ng-workspace/projects/OpenWaterFoundation/common/util/io/PropsListManager.ts b/ng-workspace/projects/OpenWaterFoundation/common/util/io/PropsListManager.ts index 9664d82..8bbc3a0 100755 --- a/ng-workspace/projects/OpenWaterFoundation/common/util/io/PropsListManager.ts +++ b/ng-workspace/projects/OpenWaterFoundation/common/util/io/PropsListManager.ts @@ -29,7 +29,7 @@ // // } // // /** -// // Add an existing PropList to the the list managed by this class. +// // Add an existing PropList to the list managed by this class. // // @param proplist The PropList to add. // // @param replace_if_match If the name of the PropList matches one that is already // // in the list, replace it (true), or add the new list additionally (false). @@ -61,7 +61,7 @@ // // } // // /** -// // Create and add a PropList to the the list managed by this class. +// // Create and add a PropList to the list managed by this class. // // @param listname The name of the list to add. // // @param listformat The format of the property list to add. // // */