Skip to content

Nunaliit Documentation for Atlas Builders

Amos Hayes edited this page Dec 2, 2024 · 125 revisions

Table of Contents

  1. Introduction
  2. Nunaliit Overview
  3. Obtaining and Installing Nunaliit
  4. Using Nunaliit
  5. Running the Atlas as a Daemon
  6. Atlas Directory Structure
  7. Nunaliit Commands
  8. Nunaliit Configuration
  9. Upload Process
  10. On-Disk Document Format
  11. Upgrading Nunaliit
  12. Atlas Content
  13. Module Models
  14. Module Widgets
  15. Module Canvas
  16. Module Utilities
  17. Instances in Module Definitions
  18. Atlas Customization
  19. Handling of Dates
  20. Multi-Language Support
  21. Submission Database
  22. Atlas Tools
  23. Importing Data
  24. Exporting Data
  25. inReach Device Support

This documentation applies to the latest available version of Nunaliit (2.1). For notes on changes from previous releases and required upgrade procedures, visit the Changes and Upgrades page.

The Nunaliit Atlas Framework aims to make it easy to tell stories and highlight relationships between many different forms of information from a variety of sources, using maps as a central way to connect and interact with the data.

The Nunaliit Atlas Framework was born out of a multi-disciplinary research project led by Dr. Fraser Taylor, a Distinguished Research Professor in the department of Geography and Environmental Studies and director of the Geomatics and Cartographic Research Centre at Carleton University in Ottawa, Canada.

Please consider including a credit and link to Nunaliit if you use it so that we can grow our community and support. Assets have been developed to help.

The Nunaliit home page is http://nunaliit.org.

The documentation you are reading lives at https://github.com/GCRC/nunaliit/wiki/

If you are interested in working on the development of Nunaliit itself, see the Nunaliit Documentation for Developers

Nunaliit bugs, questions, and feature requests can be submitted in our issue tracker.

Please consult the Installation page for a fresh installation. If you are upgrading from a previous release, it is important that you consult the Changes and Upgrades page to see what, if any, special steps must be taken. Then refer to the Nunaliit Commands - Upgrade section of this document for documentation on Nunaliit's built-in upgrade command.

Nunaliit comes with a command-line tool ("nunaliit") that allows a user to create, maintain and publish an on-line atlas. It manages each atlas in its own directory, keeping files relating to an atlas in a confined area of disk, facilitating back-ups and other life cycle tasks.

Nunaliit packages Jetty within its distribution, permitting the publication of the atlas without requiring any other downloads or set-up. The command-line tool can publish the atlas directly.

The remaining of this section assumes that Nunaliit is installed and that the PATH variable has been updated to include the "bin" directory found within the nunaliit folder.

In an atlas creation, a new directory is created already populated with a number of starting files. The command is:

nunaliit create

The user must answer a number of questions relating to the atlas:

Enter location where atlas should be created: atlas1
Created atlas directory at: /.../atlas1
Copying templates from: /.../templates
Enter the name of the atlas [atlas1]: 
Enter the URL to CouchDB [http://127.0.0.1:5984/]: 
Enter the name of the database where atlas resides [atlas1]: 
Do you wish to manually verify each document submission?(Y/N) [N]:
Enter the name of the admin user for CouchDB [admin]: 
Enter the password for the admin user: password
Enter the port where the atlas is served [8080]:
Enter a Google Map API key (empty if not using):

If the initial configuration must be changed, it can be accomplished by running the configuration command:

cd atlas1
nunaliit config

In general, all commands other than the creation command should be run from the top directory of an atlas as such:

cd <atlas-dir>
nunaliit <command>

However, if the command is not run from the top directory of an atlas, the atlas can be specified using the "--atlas-dir" option, as shown here:

nunaliit --atlas-dir <atlas-dir> <command>

Before running an atlas, the files found in the atlas directory must be propagated to their appropriate place. This process is known as "updating" the atlas. This is accomplished using the "update" command.

nunaliit update

Once an atlas is up to date, it can be published using the "run" command.

nunaliit run

When the atlas is running, it can be viewed using a web browser. The address for the atlas is the local host, localhost or 127.0.0.1. However, the port on which the atlas is published is configured during the "config" command.

During configuration, the user is prompted for a "Google Map API Key". If the key is not provided, then map backgrounds based on the Google Map services will not be available.

A key can be obtained directly from Google from this page.

This section explains how to run an instance of Nunaliit atlas as a service. In this section, when a file name is in the form "nunaliit-XXX", it should be assumed that the XXX is the name of the atlas.

Ubuntu, as of version 16.04, is using systemd for managing services. Therefore, for platforms running Ubuntu 16.04 or newer, the following procedures should be used to install a Nunaliit instance as a service.

In the "extra" directory, a file named "nunaliit-XXX.service" is created when the commands "nunaliit create" or "nunaliit config" are run. First, this file should be edited and the line where the user is set should be changed to the name of the user under which the service should run (User=).

Then, the following commands should be issued:

cd .../extra
sudo cp nunaliit-XXX.service /etc/systemd/system/.
sudo systemctl enable nunaliit-XXX
sudo systemctl start nunaliit-XXX

Platforms running Ubuntu with a version older than 16.04 should use the following procedures to install a Nunaliit instance as a service.

In the "extra" directory, a file named "nunaliit-XXX.service" is created when the commands "nunaliit create" or "nunaliit config" are run. First, this file should be edited and the line where the user is set should be changed to the name of the user under which the service should run (NUNALIIT_USER).

Then, the following commands should be issued:

cd .../extra
sudo cp nunaliit-XXX.sh /etc/init.d/nunaliit-XXX
cd /etc/init.d
sudo update-rc.d nunaliit-XXX defaults 95
sudo /etc/init.d/nunaliit-XXX start

An atlas directory is created using the "create" command. At first, an atlas is populated with a number of default files. Most of these files can be modified to change the look and behaviour of the atlas.

This section discusses the directory structure of the atlas and the types of files found in it.

The top directory contain a number of sub-directories:

  • htdocs : This directory contain files that are served to the clients visiting the atlas. This is similar to any web site, where HTML and Javascript files are served along with other required resources.
  • docs : This directory contain documents that are loaded to the database. Database documents are accessed by the web application during the viewing of an atlas.
  • media : This directory is used by the atlas while running and contains files uploaded by users of the atlas. When files stored in this directory are approved, they are copied in the database along the document they relate to.
  • config : This directory contains a number of configuration files that influences the functioning of the atlas.
  • logs : This directory contains log files that are generated while the atlas is running.
  • extra : This directory contains a script, nunaliit.sh, that can be used to run the atlas in a "daemon" mode.
  • dump : This directory is used to save snapshots of the database using the "dump" command.
  • site : This directory contains files to make up a CouchDb Design Document specific to the published atlas. The content of this directory is mixed with the files found in "htdocs" to create the "site design document" (_design/site) where all atlas-specific resources are stored. The "site" directory is useful to add CouchDB views (database index), list functions, show functions and a validate_doc_update function, that are not already part of Nunaliit.
nunaliit help [<command>]

This command provides general help about syntax and purpose, if a command is not specified. If a nunaliit command is specified as argument, help is reported about the syntax and purpose of the requested command.

nunaliit version

This command provides the version of the command line tool.

nunaliit [<global-options>] create [<create-options>]

This command creates a new directory structure to hold the content of an atlas managed by Nunaliit. Then, the configuration command is run to set-up the appropriate configuration properties.

The options that can be specified for the create command (<create-options>) are defined as follows:

  • --no-config : If this option is specified, the directory structure is created, without running the implicit "config" command.
nunaliit [<global-options>] config

This command configures the atlas by asking the user a number of questions and setting the atlas configuration properties accordingly.

Details about the Google Map API Key can be found in this section.

nunaliit [<global-options>] update

This command updates the CouchDb database used to support the atlas with the files found in the atlas directory. This command should be run every time changes are performed on the files found within the atlas directory.

If the CouchDb database associated with the atlas is not yet created, the update command creates it.

The update process is complex and encompasses many activities:

  • _design/site : The document _design/site is created by merging together a template from the framework, files found in the "site" sub-directory and the files found in "htdocs". This design document is then uploaded to the CouchDb database associated with the atlas.
  • _design/atlas : The document _design/atlas is special design document that contains views, validation functions and common javascript libraries used by the atlas. This design document is generated by using a basic template from the framework and adjusting settings for the given atlas. It is uploaded to the CouchDb database associated with the atlas.
  • _design/mobile : The document _design/mobile is special design document that is used to access the atlas using mobile devices. This design document is obtained from the framework and uploaded to the CouchDb database associated with the atlas.
  • docs : All documents found in the "docs" directory are uploaded to the CouchDb database associated with the atlas. The documents found in the "docs" directory are documents that are under the control of the atlas developper.
  • framework documents : A number of documents are required by the atlas and are specified by the framework. These documents are uploaded to the CouchDb database associated with the atlas.
nunaliit [<global-options>] run

This command updates starts an HTTP service which serves the content of the atlas. The address used by the service is the localhost. The port used by the service is specified during configuration.

During the run command, the console output is used to report log messages. The service is ended by using CTRL-C

Log messages are also sent to a file located in the "logs" directory

nunaliit [<global-options>] add-schema [<add-schema-options>]

This creates a new schema for the atlas. More specifically, it creates a new directory under the "docs" directory. The content of this new directory is a set of files that represent a schema document. The command add-schema performs operations on disk alone and does not affect the database in any way. To push the created schema to the database, one must first execute the command "update".

The options that can be specified for the add-schema command (<add-schema-options>) are defined as follows:

  • --id <name> : Either this option or the --def must be specified. If this option is provided, it contributes to the name of the schema. It is good practice to have schema names be specific to an atlas, to avoid name collision. Therefore, the name of a schema is <group>_<id>. The --id option provides the tail of the name.
  • --group <name> : This option is considered only if the --id option is specified. The group option contributes to the name. However, if this option is not specified, it defaults to the name of the atlas.
  • --def <file> : Either this option or the --id must be specified. If this option is specified, it locates a file that contains a schema definition. This file is then used to gather all information needed to create a new schema.

The add-schema command is useful to create a skeleton schema. After a schema is create this way, a user should edit and augment the file "definition.json", found in the new directory. Then, the command "update-schema" should be used to modified the schema, based on the definition.

See Schema Definition for more information on schema defitions.

nunaliit [<global-options>] update-schema [<update-schema-options>]

This refreshes the content of a schema based on its definition. More specifically, it reads a schema definition file (definition.json) from the atlas directory and updates the associated files accordingly. This does not modify the content of the database in any way.

The options that can be specified for the update-schema command (<update-schema-options>) are defined as follows:

  • --name <name> : This is the name of the schema that should be refreshed. When spcified, the atlas directory is searched for a schema that matches the given name. If found, the definition file associated with the schema is loaded and all files derived from the definition are recreated.
  • --all : When this option is specified, all schemas defined on disk that have a schema definition file are refreshed. One must use the command "update" to push the changes that have been accomplished using the update-schema command.

When a schema is refreshed, the definition file is loaded (definition.json) and all derived files are re-created from the informaition provided in the definition.

See Schema Definition for more information on schema defitions.

nunaliit [<global-options>] dump [<dump-options>]

This command takes a snapshot of some or all the documents found in the database associated with the atlas and stores them to disk. This snapshot can be later restored using the "restore" command.

Specific documents can be selected for the dump using the options --doc-id or --skeleton. If no specific document is selected, then the dump process is performed over all documents found in the database.

The options that can be specified for the dump command (<dump-options>) are defined as follows:

  • --dump-dir <dir> : If this option is specified, it indicates the directory where the snapshot is to be stored. If this option is not specified, the dump command creates a dated sub-directory under the "dump" directory.
  • --doc-id <doc-id> : If this option is specified, it indicates a document that is to be dumped. This option can be specified multiple times.
  • --schema <schema id> : Specifies which document(s) should be dumped by selecting a schema type.
  • --layer <layer id> : Specifies which document(s) should be dumped by selecting a layer identifier.
  • --skeleton : If this option is specified, it indicates that the documents that form the atlas itself, not the documents that represent the data, are desired in the dump. This usually include the schema documents, the module documents, the navigation documents and documents specially marked as skeleton documents.
  • --overwrite-docs : If this option is specified, it implies the --skeleton option. Furthermore, it implies that the --dump-dir option is set to the "docs" directory of the atlas. This should be used to update the atlas implementation with the documents found in the database. One should make a copy of the atlas directory before using this option.
nunaliit [<global-options>] restore --dump-dir <dir> [<restore-options>]

This command restores documents previously saved using the "dump" command. It can restore a complete snapshot, when the "--doc-id" option is not used. It can also restore a subset of the documents by using the "doc-id" option once or multiple times. The option "--dump-dir" must be specified and refers to a directory created by previously running the "dump" command.

The options that can be specified for the restore command (<restore-options>) are defined as follows:

  • --dump-dir <dir> : This option must be specified. If the specified directory is a fully qualified path, then this directory is used to find the document to be restored. If the specified directory is not fully qualified, then it is used as a relative path from the "dump" directory.
  • --doc-id <doc-id> : If this option is specified, it indicates a document that is to be restored. This option can be specified multiple times. If this option is not specified, then all documents are restored.
nunaliit [<global-options>] upgrade [<upgrade-options>]

This command upgrades an atlas from one version of Nunaliit to another. During the upgrade, the files that would be used to create a new atlas under the upgraded version are copied in the atlas directory to overwrite the files that were initially installed at creation. Since the files found in the atlas directory can be modified by a user, only files that were unchanged are replaced. Files that were modified or deleted are considered as "collisions" and are not replaced. Instead, collisions are reported during the upgrade process, and the files that would have been copied are stored in a directory under the "upgrade" sub-directory of the atlas.

Once the upgrade process is completed, a "config" command is automatically initiated unless the "--test" or "--no-config" options are used.

After the "upgrade" command is completed, a user should run the following commands:

  1. config : A config command is required to update the references stored in the atlas directory to the new Nunaliit tool. This is usually not necessary since a "config" command is naturally initiated at the end of the upgrade process.
  2. update : An update command is required to push to the atlas the new versions of the files found in the atlas directory.

The options that can be specified for the upgrade command (<upgrade-options>) are defined as follows:

  • --test : When this option is specified, the upgrade process does not modify the atlas directory. Instead, it computes and reports the changes that would happen if the upgrade process was allowed to take place. Also, the automatic "config" command is skipped.
  • --no-config : When this option is specified, the upgrade process does not start a "config" command automatically when completed.
nunaliit [<global-options>] schemas-for-inreach [<command-options>]

This is a helper command to help generate schemas based on inReach form definitions. inReach form definitions are saved in a file named .../config/inreach_forms.xml. These forms are used to load inReach device to submit information back to the database. The Nunaliit robot uses these form to interpret and augment the documents submitted that way. However, schemas are needed for displaying the information correctly.

The command "schemas-for-inreach" reads the form definitions and prints schema definitions derived from the forms. It is up to the atlas builder to integrate these schemas to the atlas.

Global options are accepted by a number of commands and are defined as follows:

  • --atlas-dir <dir> : If specified, indicates the location of the atlas directory. If this option is omitted, then the current directory is assumed to be the atlas directory. The atlas directory contains all the atlas files.
  • --debug : When specified, sets the logging level of the current logger to DEBUG. The current logger is controlled by --set-logger. If --set-logger has not yet been specified, then the root logger is affected.
  • --trace : When specified, sets the logging level of the current logger to TRACE. The current logger is controlled by --set-logger. If --set-logger has not yet been specified, then the root logger is affected.
  • --info : When specified, sets the logging level of the current logger to INFO. The current logger is controlled by --set-logger. If --set-logger has not yet been specified, then the root logger is affected.
  • --error : When specified, sets the logging level of the current logger to ERROR. The current logger is controlled by --set-logger. If --set-logger has not yet been specified, then the root logger is affected.
  • --set-logger <logger-name> : This option can be specified multiple times. It sets the current logger to the named one. Subsequent --debug, --trace, --info and --error options set the level of the current logger.

It is possible to change the behaviour of the logging system used in Nunaliit by adding options to the command line. The options used are '--set-logger', '--error', '--info', '--debug' and '--trace'. These options can be specified multiple times. The '--set-logger' option is used to name a specific logger and make it current. All other logging options affect this current logger.

For example, to have all processes at ERROR level, all GCRC code at INFO level and the utility classes reporting at DEBUG, the following command can be used:

  > nunaliit run --error --set-logger ca.carleton.gcrc --info --set-logger ca.carleton.gcrc.utils --debug

If this level is insufficient, it is possible to configure the level that is reported by adding a file named "log4j.properties" to the "config" directory of an atlas. This file follows the syntax imposed by log4j. As an example, the following content would prompt Nunaliit to log all DEBUG messages:

log4j.debug=TRUE

log4j.rootLogger=ERROR, GCRC_CONSOLE

log4j.logger.ca.carleton.gcrc=DEBUG, GCRC_CONSOLE

log4j.appender.GCRC_CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.GCRC_CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.GCRC_CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %t %c - %m%n

Nunaliit uses external tools to handle multimedia files submitted to an atlas. For processing images, the tool ImageMagick is used. For audio and video files, ffmpeg is invoked.

From platform to platform, these tools can be slightly different. For example, not all CODECs are available on all platforms. Therefore, Nunaliit offers a means to change the commands that are used to invoke external tools for processing multimedia uploads. A file named ".../config/multimedia.properties" contains a number of commented out entries which relate to the way Nunaliit calls external tools. This file can be modified to change the behaviour of those calls.

For example, some platforms used "avconv" in place of "ffmpeg". Since Nunaliit uses "ffmpeg" by default, failures can be encountered when uploading a video. To fix this situation, avconv equivalents can be substituted in the "multimedia.properties" file. This might look similar to:

ffmpegVersionCommand=avconv -version
ffmpegInfoCommand=avprobe %1$s
ffmpegConvertVideoCommand=avconv -i %1$s -y -acodec aac -ab 48000 -ac 2 -vcodec libx264 -b:v 128000 -r 24 -vf scale=320:-2 -threads 0 -f mp4 %2$s
ffmpegConvertAudioCommand=avconv -i %1$s -y -acodec libmp3lame -ab 48000 -ac 2 -threads 0 -f mp3 %2$s
ffmpegCreateThumbnailCommand=avconv -y -ss %5$s -i %1$s -s %3$dx%4$d -r 1 -vframes 1 -f image2 %2$s

Configuring Audio and Video Upload Thresholds

The "multimedia.properties" file has settings that allows an atlas builder to specifiy one or more sets of valid image, audio, and video threshholds to be evaluated when an upload occurs. If at least one of these threshold sets is met, then no conversion is performed. If none of the thresholds are met, the media file will be converted using Nunaliit's default settings or the overriding settings in multimedia.properties.

It is generally best to use the preconfigured defaults for multimedia file conversion. These settings offered here can be used to support specific atlas limitations (e.g., bandwidth, file sizes, etc.).

Media type-specific thresholds are explained below. All media type threshold settings offer the ability to specify more than one threshold. Only one must be met in order to pass and not undergo conversion. The '|' is an "OR" conditional. However, all tests in a single threshold must be met for the requirement to pass. For example, an image threshold of "jpeg,500,*" requires that image file be of JPEG type **and** 500px maximum dimension **and** any file size.

Image Thresholds

The default image thresholds require that the image file is in JPEG, GIF, or PNG format and has a maximum width or height of 500px to avoid conversion. These settings can be changed with the following configuration item.

#
# multimedia.conversion.image.threshold
#
# Values, separated by commas, used as threshold to initiate conversion of external image
# files. Multiple thresholds can be defined and separated by an OR operator ('|'). For example:
#    jpeg,500,*|png,500,*|gif,500,*
#
# Each threshold is defined with these fields:
# 0 : Acceptable image format (* for any)
# 1 : Largest acceptable dimension, in pixels (* for any)
# 2 : Maximum file size in megabytes (MB), whole numbers only (* for any)
#
#multimedia.conversion.image.threshold=JPEG,500,*
Audio Thresholds

The default audio thresholds require that the audio file is in AAC format and has a bitrate of 250,0000 or less to avoid conversion. These settings can be changed with the following configuration item.

#
# multimedia.conversion.audio.threshold
#
# Values, separated by commas, used as threshold to initiate conversion of external audio
# files. Multiple thresholds can be defined and separated by an OR operator ('|'). For example:
#    mpeg,250000,10|mov,128000,10
#
# Each threshold is defined with these fields:
# 0 : Acceptable codec (* for any)
# 1 : Maximum audio bitrate (* for any)
# 2 : Maximum file size in megabytes (MB), whole numbers only (* for any)
#
#multimedia.conversion.audio.threshold=mpeg,250000,10
Video Thresholds

The default video thresholds require that the video file is in H264 format, the audio encoding is AAC, both tracks have a maximum average bitrate of 250,0000, and has a maximum file size of 1GB to avoid conversion. These settings can be changed with the following configuration item.

#
# multimedia.conversion.video.threshold
#
# Values, separated by commas, used as threshold to initiate conversion of external video
# files.  Multiple thresholds can be defined and separated by an OR operator ('|'). For example:
#    h264,250000,aac,250000,*,1000|mp4,250000,aac,250000,*,1000
#
# Each threshold is defined with these fields:
# 0 : Acceptable video codec (* for any)
# 1 : Maximum video bitrate (* for any)
# 2 : Acceptable audio codec (* for any)
# 3 : Maximum audio bitrate (* for any)
# 4 : Largest acceptable dimension, in pixels (* for any)
# 5 : Maximum file size in megabytes (MB), whole numbers only (* for any)
#
#multimedia.conversion.video.threshold=h264,250000,aac,250000,*,1000

By default, an atlas allows any user to create new documents. This behaviour is intended to foster contributions from the community. However, there are instances when a published atlas should restrict the creation of new documents to a restricted number of users. This latter approach is a "Locked-Down Atlas" and only users that have an atlas-specific role can create or modify documents. The most common role for a locked-down atlas is the "Atlas User" role. See section on User Roles for more information.

A locked-down atlas requires special configuration. The first step is to set the "atlas.restricted" configuration option. This option is found in the "install.properties" file under the atlas sub-directory named "config". To set this option, replace the appropriate line with:

atlas.restricted=true

The final step is to update the atlas. See the update command.

The Nunaliit framework sends mail notifications to support user registration and upload notifications. For these services to work, the mail configuration parameters must be set correctly.

The mail configuration file is named "mail.properties" and is located in the "config" sub-directory of the atlas. Here is an excerpt from an example mail configuration file:

mail.transport.protocol=smtp
mail.smtp.host=
mail.smtp.port=25

The example above works correctly for an SMTP server accepting un-authenticated requests on the standard port.

Since Nunaliit uses JavaMail for mail delivery, it is possible to set any parameters valid for JavaMail 1.4 in the mail configuration file. Documentation about JavaMail can be found here.

If Nunaliit is installed on a platform where a SMTP server accepts any requests from local processes, it is possible to comment out all mail properties. By default, JavaMail connects to port 25 on "localhost"

To specify a user name and password for authentication, the following properties must be added to the configuration file:

mail.smtp.auth=true
mail.smtp.user=
mail.smtp.password=

If configuring the mail service to use a SMTP server that supports STARTTLS, then the mail properties should be adjusted according to the following example:

mail.transport.protocol=smtp
mail.smtp.host=
mail.smtp.port=587
mail.smtp.starttls.enable=true

If configuring the mail service to use SMTP over SSL, then the mail properties should be adjusted according to the following example:

mail.transport.protocol=smtp
mail.smtp.host=
mail.smtp.port=465
mail.smtp.socketFactory.port=465
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory

Finally, to help debugging problems occurring with the mail service, the following property can be added so that JavaMail outputs explanations in the logs:

mail.debug=true

When the mail properties are changed, the server must be restarted for the changes to take effect.

The following is an example provided by a user of Nunaliit where they used their Gmail account (which uses SSL) to send emails on behalf of the atlas. They note that you may receive an email on your Gmail account blocking Nunaliit from using it to send emails, and that you must override this setting in Gmail to continue. The Nunaliit developers recommend caution with this approach since it involves storing your Google password in plain text in the config file. Setting up your own SMTP relay or using one provided by your ISP would be better options in most cases.

mail.transport.protocol=smtp
mail.smtp.host=smtp.gmail.com
mail.smtp.port=465
mail.smtp.auth=true
mail.smtp.socketFactory.port=465
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.smtp.user=[email protected]
mail.smtp.password=mygmailpassword
mail.debug=true

user.sendNotification=true user.sender=[email protected] user.url.creation=http://atlasURL/tools/user_creation.html user.url.passwordRecovery=http://atlasURL/tools/user_password_recovery.html

The Nunaliit framework sends e-mail messages during user auto-registration and password recovery. Before the user registration service can send e-mail notifications, the mail configuration file must be set correctly. See the mail configuration section for more details.

The configuration parameters relating to the user registration service are stored in the same file as the mail parameters. The configuration file is named "mail.properties" and is located in the "config" sub-directory of the atlas.

Here is an example for the user registration properties:

user.sendNotification=true
[email protected]|Atlas Server
user.url.creation=http://atlas.yourcompany.com/tools/user_creation.html
user.url.passwordRecovery=http://atlas.yourcompany.com/tools/user_password_recovery.html
user.url.management=http://atlas.yourcompany.com/tools/users.html
admin.notify.userCreation=true

The property user.sendNotification acts as a global switch to enable/disable e-mail notifications altogether, without disrupting any other properties.

The property user.sender is a string that controls the "From" field in the notification e-mails. This string should contain the e-mail address of the server. If a display name is provided, it should follow the e-mail address, separated by a vertical bar ('|') character.

The property user.url.creation is a string that should contain the URL for the user creation page. This URL is sent in the user registration notifications to help users reach back to the server.

The property user.url.passwordRecovery is a string that should contain the URL for the user password recovery page. This URL is sent in the user recovery notifications to help users reach back to the server.

The property user.url.management is a string that should contain the URL that links to the user management page. This URL is included in an email (if provided) for ease of access.

The property admin.notify.userCreation is a switch/boolean value that should either be true or false. When set to true, all administrators will be notified by email whenever a user has registered an account with the atlas if auto-registration is active. When false, administrators are not emailed when a user registers an account.

The Nunaliit framework can send e-mail notifications when uploaded files are waiting for approval. Before the framework can send upload notification e-mails, the mail configuration file must be set correctly. See the mail configuration section for more details.

The configuration parameters relating to the upload notifications are stored in the same file as the mail parameters. The configuration file is named "mail.properties" and is located in the "config" sub-directory of the atlas.

Here is an example for the upload notification properties:

upload.sendNotification=true
[email protected]|Atlas Server
upload.approval.url=http://atlas.yourcompany.com/upload_approval.html

The property upload.sendNotification acts as a global switch to enable/disable e-mail notifications altogether, without disrupting any other properties.

The property upload.sender is a string that controls the "From" field in the notification e-mails. This string should contain the e-mail address of the server. If a display name is provided, it should follow the e-mail address, separated by a vertical bar ('|') character.

The property upload.approval.url is a string that should contain the URL of the file upload approval page. This URL is sent in the upload notification e-mails to help users to reach back to the server.

Nunaliit uses an upload process during atlas updating, upgrading and restoring. This process modifies documents in the database with the content of documents found on disk.

When the upload process installs a document in the database, it creates a fingerprint of the document that it stores along with the document. When this document is modified, the fingerprint computed by the upload process no longer matches the content of the document. By verifying the fingerprint stored with a document, the upload process can determine if the document was modified since it was last installed in the database.

The upload process uses the following approach for each document to be uploaded:

  1. The document to be uploaded is read in memory, a fingerprint is computed against it and the fingerprint is added to the document.
  2. The database is queried for a document that has the same identifier.
  3. If a document with the same identifier is not found in the database, the document in memory is uploaded and the process ends.
  4. If the document found in the database is equivalent to the one in memory, the process ends.
  5. If the document found in the database does not contain a fingerprint, it is deemed modified. In this case, the upload process is aborted and an error is reported.
  6. If the document found in the database contains a fingerprint and that the fingerprint does not match its content, the document is deemed modified. In this case, the upload process is aborted and an error is reported.
  7. The document in memory is uploaded to the database.

Documents in CouchDb are represented as JSON objects. Therefore, all document transfers between CouchDb and applications are performed using JSON objects. In the Nunaliit framework, there are a number of instances where documents are stored on disk within the atlas directory. This section describes the format used by Nunaliit to load documents from disk.

A document stored on disk is represented by a directory. The name of the directory is not relevant to the loading process and should be selected to help user locate documents. This directory is known as the "document directory".

In the document directory, each entry (file or sub-directory) represents a key of the top-most object that constitutes the document. Each file contains the value associated with the key. Each sub-directory contains the object or array value associated with the key.

For each entry (file and sub-directory) in the document directory, a key is derived from the name. The extension for the entry name is used to determine the format of the entry while the remainder of the name is used as the key.

For a file entry with an extension of "txt", the content of the file is used as a string value. For a file entry with an extension of "json", the content of the file is interpreted as JSON and used as the value.

For a directory entry without an extension, the content of the directory is used to create an object that is used as the value. The process to load this object is identical to the one used by the document loader, and thus is recursive.

For a directory entry with an extension of "array", the content of the directory represents an array. The names of the entries in directory are sorted and values are loaded in this order.

Here is an example illustrates this process. In this example, the directory structure is:

/
|- _id.txt : 123456
|- a.txt   : Just a test
|- b.json  : {"array":[1,2,3]}
|- inner
|  |- c.txt  : Another string value
|  |- d.json : {}
|- e.array
   |- 0.txt : one
   |- 1.json : 2  

From the above directory structure, the loading process would create a JSON object equivalent to:

{
	"_id":"123456"
	,"a":"Just a test"
	,"b": {
		"array":[1,2,3]
	}
	,"inner":{
		"c":"Another string value"
		,"d": {}
	}
	,"e":["one",2]
}

CouchDb associates files to documents as attachments. Attachments are saved in a document under a special key: "_attachments"". On disk, the attachments are saved in a directory called "_attachments" in the top document directory.

Each file found in the "_attachments" directory structure is uploaded as an attachment to the document, with a name that is the relative path from the "_attachments" directory. Sub-drectories to the "_attachments" directory have no meaning other than to contribute to the naming of the files deemed as attachments.

Other information is associated with attachments, such as the content-type. This information is stored in a file adjacent to the attachment itself. This information file has the same name as the attachment, however it is appended with "._nunaliit". The content of the information file is plain text that contains a JSON object that is examplified as:

{
	"name": <attachment-name>
	,"content_type": <content-type>
}

The <attachment-name> is a string specifying the name of the attachment and is useful in case the name associated with an attachment can not be converted into a directory structure. The field <content-type> is a string depicting the content type of the attachment, such as "text/plain", "text/html" or "video/mp4".

VTT attachments

If you want subtitles associated with a video file being attached then upload .vtt files with the file `name pattern _.vtt` to the same document as the video file. The language code will be run through the localization to find a label. For instance if you have the video file `mytest.mp4` and want to add vtt files for English and French you would upload files `mytest_en.vtt` and `mytest_fr.vtt`.

Over the life of an atlas, new releases of Nunaliit might become available. This section discusses the procedure to migrate from one version of Nunaliit to another. In this process, new and updated libraries will be installed as well as new and updated documents that are specified by the framework.

At a high level, the upgrade process follows these steps:

  1. Install new version of Nunaliit
  2. Stop atlas
  3. Re-configure atlas
  4. Update atlas
  5. Restart atlas

When a new version of Nunaliit is installed on a platform, it should be hosted in a directory different than the previous version. The old version of Nunaliit should be kept until the new atlas is performing properly. This enables a user of the Nunaliit framework to revert to an earlier version.

Installing Nunaliit is as simple as uncompressing the distributed file in its own directory. To make this new version active, modify the PATH variable used by the operating system to detect executable files. The PATH variable should include the "bin" directory of the new installation, not the old one.

Stopping the atlas is dependent on the way the atlas was started. If the atlas was started using the "run" command, then pressing CTRL-C in the console window where the atlas is run should be sufficient. If the atlas was started as a daemon, the following command should be used:

.../nunaliit.sh stop

Some of the files in the atlas directory reference the location of the Nunaliit framework. These files must be updated to reference the new framework location. This is accomplished by running the configuration command. In the case of re-configuring, all default values can be accepted during the "config" command.

cd <atlas-dir>
nunaliit config

Updating the atlas is required to load new and modified documents and libraries. If an updated document was changed manually in the database, then the document is skipped during the upload process. This means that a user upgrading Nunaliit should remain vigilent of the errors reported during the update process.

After the re-configuration, the script used to start Nunaliit as a daemon is modified. One should review this file to ensure that all properties are in order. Then:

<atlas-dir>/nunaliit.sh start

Reverting to an older version of Nunaliit is similar. Instead of installing a new version of Nunaliit, change the PATH variable back to the "bin" directory of the desired version. Then, follow the remaining steps of this section.

There are two main types of documents that are served from a running instance of the atlas framework: Web Resources and Database Documents.

Web resources are files that generally build a web site and include such things as HTML files, CSS files, Javascript libraries, images, etc. A number of web resources are provided by the atlas framework, itself. Others are specified by the atlas designer by including them in the "htdocs" directory of the atlas.

On the other hand, database documents are JSON objects that are stored in CouchDb. These documents can be accessed directly by providing the document identifier to the database, or via views that are installed by the atlas framework. As with web resources, a certain number of database documents are provided by the framework. Atlas designers can specify documents to be loaded to the database by including them in the "docs" directory of the atlas.

Although database documents can be specified and uploaded by the framework, the majority of documents found in the database are created and updated by using the atlas itself. Geometries, comments, and most of the interactions that users have with the atlas ends up recorded as database documents.

The main distinction between web resources and database documents is that web resources are static, while database documents can be modified at run-time. Updating an atlas provides an opportunity to the atlas designer to change the web resources. However, between updates, web resources remain unchanged.

All documents, including those provided by the framework, can be modified by users as long as the modifications respect the authorization rules. Some of the documents found in the database, such as schemas, affect the run-time behaviour of the atlas. Since some of these documents are provided by the framework, that these documents are subject to modifications and that the update process does not overwrite documents that were modified while stored in the database, great care must be taken when modifying framework provided documents. This is because an upgrade/update process would not revert these modifications.

In CouchDb, there is a special class of documents called "Design Documents". These documents have identifiers that begins with the prefix "_design/" and are used to provide index functions (views), validation functions (authorization), list functions and show functions. These documents generally contains web resources for on-line applications served out of the database.

In Nunaliit, a number of design documents are in use:

  • _design/atlas : This design document contains all views (index functions) and web resources necessary for Nunaliit.
  • _design/site : This design document contains all views (index functions) and web resources that are specific to the atlas. This documents is built from content found in the atlas directory. The attachments found in this design documents are loaded from the "htdocs" sub-directory in the atlas directory.
  • _design/server : This design document contains views necessary to the proper functioning of the "robot" component of the atlas. This document is maintained by the servlet that implements the "robot".
  • _design/mobile : This design document contains views and list functions needed for mobile devices that attach to the atlas. This document is provided by the framework.

Nunaliit serves the web resources and the database documents through an HTTP connection, accessible from a web browser. For simplification, all URL paths used by the atlas web application are relative to the root, where the atlas web resources are served. URL redirection is used to make CouchDb resources appear adjacent to one another.

Here are the paths served by Nunaliit:

  • / : The root directory is redirected to the attachments of the "_design/site" design document. The attachments to this document are loaded from the "htdocs" sub-directory of the atlas directory. All sub-directories from the "htdocs" sub-directory are served by following a relative path from the root, unless it is a path that is redirected elsewhere.
  • /media : This path is re-directed to the sub-directory "media" found in the atlas directory. This directory is used by the framework to store files uploaded by user, via the atlas interface.
  • /server : This path is re-directed to the root of the CouchDb server. This path is useful to access CouchDb server information, access the user database or perform replication.
  • /db : This path is re-directed to the top most directory of the CouchDb database that serves the atlas, regardless of the database name. This path is used by the framework to access all documents, views or other sevices from the database. A document is located at /db/<document-id>
  • /submitDb : This path is re-directed to the location of the submission database, if one is associated with the atlas.
  • /servlet : This path is re-directed to the various Java servlets that form the "robot" component of the framework. Among those paths are: /servlet/configuration, /servlet/upload, /servlet/progress, /servlet/user and /servlet/export.
  • /config : This path is not actually re-directed, but reserved to hold a configuration script to specify the location of all resources.
  • /nunaliit2 : This path is re-directed to the path /db/_design/atlas/nunaliit2. It contains all the Javascript libraries written specifically for the atlas framework.
  • /js-external : This path is re-directed to the path /db/_design/atlas/js-external. This path contains all the Javascript libraries obtained from projects other than Nunaliit, but required for the proper functioning of the framework.
  • /lib : This path is re-directed to /db/_design/atlas/lib and contains special scripts that are shared between the web resources and internal workings of views and lists. These scripts contain some information specific to an atlas.
  • /tools : This path is re-directed to /db/_design/atlas/tools and contains web pages and web tools used to managed an atlas. These tools are common to all instances of atlas and are provided by the framework.

HTML documents and other web resources provided by an atlas designer should be located in the "htdocs" sub-directory of the atlas directory. In particular, HTML documents that are expected to perform interactions with the framework should:

  • include all CSS required by the framework
  • include all Javascript librairies required by the framework
  • include a Javascript section to initiate the configuration

The CSS resources required by Nunaliit should be defined in the header of a HTML file as follows:

<link rel="stylesheet" href="js-external/css/jquery-ui/smoothness/jquery-ui.custom.css" type="text/css" />
<link rel="stylesheet" href="js-external/css/jquery.lightbox-0.5.css" type="text/css" />
<link rel="stylesheet" href="nunaliit2/css/basic/nunaliit2.css" type="text/css" />
<link rel="stylesheet" href="_list/css_merge/css" type="text/css" />

The Javascript libraries required by Nunaliit should be defined in the header or the body of a HTML file as follows:

<script type="text/javascript" src="js-external/js/jquery.min.js"></script>
<script type="text/javascript" src="js-external/js/jquery-ui.min.js"></script>
<script type="text/javascript" src="js-external/OpenLayers/OpenLayers.js"></script>
<script type="text/javascript" src="js-external/js/jquery.lightbox-0.5.js"></script>
<script type="text/javascript" src="js-external/js/jquery.cycle.pack.js"></script>
<script type="text/javascript" src="js-external/js/jquery.form.js"></script>
<script type="text/javascript" src="js-external/js/jquery.cookie-1.0.js"></script>
<script type="text/javascript" src="js-external/js/sha1.js"></script>
<script type="text/javascript" src="lib/atlas.js"></script>
<script type="text/javascript" src="nunaliit2/nunaliit2.js"></script>
<script type="text/javascript" src="lib/utils.js"></script>
<script type="text/javascript" src="config/configuration.js"></script>
<script type="text/javascript" src="nunaliit_custom.js"></script>

The initialization of Nunaliit is performed in multiple steps:

  1. The initialization should be started by the application once the document is loaded
  2. The atlas framework creates components that are configured in a default manner
  3. Custom configuration code is called
  4. Control is returned to the application via a callback

Initialization is started by the application by calling a function called "nunaliitConfigure". This should be done after the document completed to load. Since jQuery is available (jQuery is required by Nunaliit), a simple way to perform this is to use the jQuery's "ready" function, as demontrated in this example:

jQuery().ready(function() {
	nunaliitConfigure({
		configuredFunction: main_init
		,rootPath: './'
	});
});

The nunaliitConfigure() takes an object as argument to pass options to the configuration process. There are two options that can be specified:

  • configuredFunction : This is a function (callback) that is executed when the configuration process has completed. This function has one argument, which is an object that contains all configuration information.
  • rootPath : This is a string that specifies the relative path to the root of the application. If not specified, the rootPath is assumed to be './'. This relative path should always end with a slash character. For all HTML documents in the root directory, the default value is correct. For all HTML documents in direct sub-directories, this value should be '../'.

During the configuration process, custom configuration code is called. This is covered in its own section, later.

At the end of the framework configuration, a callback is executed. This callback can be thought of as the application's main entry point or main function. As described earlier, this callback is specified in the "nunaliitConfigure()" function. The main function receives an object, which contains all configuration information. The structure of this object is:

  • atlasDb : Javascript object configured to access the CouchDb database associated with the atlas.
  • atlasDesign : Javascript object configured to access the design document "_design/atlas" in the CouchDb database.
  • siteDesign : Javascript object configured to access the design document "_design/site" in the CouchDb database.
  • mediaRelativePath : String that depicts the relative path, from the current application, to the directory where media files are uploaded.
  • documentSource : Database abstraction for accessing document.
  • directory.authService : Javascript object that provides authentication services.
  • directory.customService : Javascript object that provides custom services. It accepts and distributes named options.
  • directory.dispatchService : Javascript object that communicates events throughout the Nunaliit framework.
  • directory.dispatchSupport : Javascript object used to listen to some events and take actions on them.
  • directory.editService : Javascript object that provides the functions to edit a database document based on schemas.
  • directory.eventService : Javascript object used to derive some events given original ones. Among others, it translates events such as 'userSelect' to 'selected'.
  • directory.exportService : Javascript object used to communicate with the export servlet and provides services to export data from the atlas.
  • directory.geoNamesService : Javascript object used to perform gazeteer services.
  • directory.historyMonitor : Javascript object used to monitor the changes in browser history.
  • directory.historyTracker : Javascript object used to replay events relating to changes in the browser's URL.
  • directory.hoverSoundService : Javascript object which plays ambient sound that is generally associated with hovering over a feature.
  • directory.languageService : Javascript object which keeps track of currently selected language and provides the translated strings to the rest of the Nunaliit components.
  • directory.notifierService : Javascript object that monitors changes in the document database. This object will be deprecated in future.
  • directory.progressService : Javascript object that communicates with the progress servlet.
  • directory.requestService : Javascript object that asynchronously retrieves documents from the document database. The request service uses caches to minimize the communication to the database.
  • directory.schemaEditorService : Javascript object that provides a document editor with the schema perspective.
  • directory.schemaRepository : Javascript object that provides access to all schemas defined in the atlas.
  • directory.searchService : Javascript object that provides search services for the atlas.
  • directory.serverSideNotifier : Javascript object that provides functions needed access notifications sent from the server.
  • directory.showService : Javascript object that provides the functions needed to display the content of a database document based on schemas.
  • directory.uploadService : Javascript object that provides services to upload files to the atlas.
  • directory.userService : Javascript object that provides communications to the users servlet and information from the users database.

From the main application function, an application can create a map, a side text panel, and many more widgets, based on the configuration. This is covered later. The next script fragment shows the "nunaliitConfigure()" and application main function together:

function main(config){
	// Here starts the application
};

jQuery().ready(function() { nunaliitConfigure({ configuredFunction: main ,rootPath: './' }); });

Information in Nunaliit is saved in documents that are represented as JSON objects. In order for Nunaliit to not collide with other applications that may be using the same database, the keys used within the Nunaliit all start with a prefix "nunaliit_".

This section describes the keys reserved by the Nunaliit framework. The following document is not valid but is shown as a quick reference for the keys reserved by Nunaliit:

{
	"nunaliit_attachments": {
		"nunaliit_type": "attachments"
		...
	},
	"nunaliit_created": {
		"nunaliit_type": "actionstamp",
		"time": ,
		"name": ,
		"action": "created"
	},
	"nunaliit_date_clusters": {
		...
	},
	"nunaliit_email_template": {
		...
	},
	"nunaliit_geom": {
		"nunaliit_type": "geometry"
		...
	},
	"nunaliit_help": {
		...
	},
	"nunaliit_last_updated": {
		"nunaliit_type": "actionstamp",
		"time": ,
		"name": ,
		"action": "updated"
	},
	"nunaliit_layer_definition": {
		"id": 
		,"name": 
	},
	"nunaliit_layers": [ , ... ],
	"nunaliit_manifest": {
		...
	},
	"nunaliit_module": {
		...
	},
	"nunaliit_navigation": {
		...
	},
	"nunaliit_origin": ,
	"nunaliit_relations": [ , ... ],
	"nunaliit_schema": ,
	"nunaliit_script": {
		...
	},
	"nunaliit_source": ,
	"nunaliit_type": ,
	"nunaliit_user_agreement": {
		...
	}
}

The keys reserved by the Nunaliit framework are explained in this list:

  • nunaliit_attachments : Reserved for internal use. This section is used to keep information about each attachment.
  • nunaliit_created : Time stamp that reports the when the document was first created. In this structure, the name attribute is the identifier for the user that created the document. The attribute time represents when the document was created.
  • nunaliit_date_clusters : Reserved for internal use
  • nunaliit_email_template : Reserved for internal use
  • nunaliit_geom : Structure used to save information about a feature geometry.
  • nunaliit_help : Reserved for internal use
  • nunaliit_hoverSound : Reference to a document that contains an audio file that should be played as "hover sound".
  • nunaliit_key_media_ref : Reference to a document that contains media to use when displaying this document (tile display, etc)
  • nunaliit_last_updated : Time stamp that reports the when the document was last updated. In this structure, the name attribute is the identifier for the user that updated the document. The attribute time represents when the document was updated.
  • nunaliit_layer_definition : This structure is used to report a layer. This associates a layer identifier with a localized name.
  • nunaliit_layers : Array of layer identifiers. If a document is associated with one or multiple layers, then this array list the identifiers associated with those layers.
  • nunaliit_manifest : Reserved for internal use
  • nunaliit_module : This structure is used to define a module. This is better explained in its own section
  • nunaliit_navigation : Reserved for internal use
  • nunaliit_origin : This reference is used in comments. In a comment stream, the attribute "nunaliit_origin" is a reference to the document associated with the subject of the stream. The attribute "nunaliit_source" represents the document that this comment was in reply to. Top level comments have "nunaliit_origin" and "nunaliit_source" set to the subject document.
  • nunaliit_relations : Generic array of references.
  • nunaliit_schema : String identifier of the schema associated with this document. The associated schema defines how a document is displayed and how it should be presented in an edit session.
  • nunaliit_script : Reserved for internal use
  • nunaliit_source : Reference to a document that is related to this one. When a user selects to "add a related item", then the new document contains "nunaliit_source" pointing to the document that was selected when the relation was added.
  • nunaliit_type : Reserved for internal use
  • nunaliit_user_agreement : Reserved for internal use

One of the way to build an atlas page is to use a module document. A module document specifies the characteristics of a module, such as which map layers are used, what the introductory text is, which schemas are used, etc. A module document can be stored in the database or specified in-line by Javascript code. With a module document, the ModuleDisplay class can be used to display the module to a web page, by specifying where the elements should be displayed.

Module Document

The example of a module document is shown here to help with explanation:

{
	"nunaliit_module": {
		"title":"Generic Atlas"
		,"introduction": {
			"type": "html"
			,"content": "<div class=\"intro\"><b>Welcome to a Nunaliit atlas served from CouchDB</b><br/><a href=\"./tools/index.html\">Nunaliit Tools</a></div>"
		}
		,"map":{
			"coordinates": {
				"initialBounds": [-75.72,45.41,-75.67,45.43]
				,"srsName": "EPSG:4326"
			}
			,"backgrounds": [
				{ 
					"name": "Google Satellite"
					,"type": "Google Maps"
					,"options": {
						"type": "SATELLITE"
						, "numZoomLevels": 19 
					}
				}
				,{ 
					"name": "Google Hybrid"
					,"type": "Google Maps"
					,"options": {
						"type": "HYBRID"
						,"numZoomLevels": 19
					}
				}
				,{ 
					"name": "Google Physical"
					,"type": "Google Maps"
					,"options": {
						"type": "TERRAIN"
						,"numZoomLevels": 19 
					}
				}
			]
			,"overlays": [
				{
					"name": "Approved Layer"
					,"type": "couchdb"
					,"options": {
						"layerName": "approved"
					}
					,"featurePopupDelayMs": 0
					,"useHoverSound": true
				}
				,{
					"name": "Public"
					,"type": "couchdb"
					,"options": {
						"layerName": "public"
					}
					,"featurePopupDelayMs": 0
					,"useHoverSound": true
					,"minimumLinePixelSize": 20
					,"minimumPolygonPixelSize": 20
					,"clustering": {
						"distance": 20
						,"threshold": 1
						,"disableDynamicClustering": false
						,"clusterPointsOnly": false
					}
				}
				,{
					"name": "Geometries"
					,"type": "model"
					,"options": {
						"sourceModelId": "geometries"
					}
					,"featurePopupDelayMs": 0
					,"useHoverSound": true
				}
			]
			,"styles":{
				"base":{
					"normal":{
						"fillColor": "#ffffff"
					}
				}
			}
			,"addPointsOnly": false
			,"showSRSAttribution": true
			,"enableKeyboardControls": true
			,"toggleClick": true
			,"scaleLine":{
				"visible": true
			}
			,"enableWheelZoom": true
			,"layerSelector":{
				"suppress": false
				,"initiallyOpened": false
			}
		}
		,"canvas": null
		,"display":{
			"defaultSchemaName":"genericDoc" 
		}
		,"edit":{
			"defaultSchemaName": "genericDoc"
			,"newDocumentSchemaNames": "ALL_SCHEMAS" 
		}
		,"search":{
			"disabled":false
			,"constraint": {
                            "type": "layer",
                            "layerNames": ["layer1", "layer2"]
                        }
		}
		,"widgets":[
			{
				"widgetType": "createDocument"
				,"showAsLink": true
				,"containerId": "module_title_bar"
			}
		]
		,"utilities":[
			{
				"utilityType": "assignLayerOnDocumentCreation"
				,"layerId": "public"
			}
		]
		,"css": ".allo { border: #000 1px solid; }"
	}
}

At the top level of a module document, a key "nunaliit_module" must be present. This key is associated with an object (module object) that contains all the pertinent information relating to a module. The keys that can be specified in a module object are:

  • title : String used to populate the title elements of the displayed module.
  • introduction : Object that specifies the introductory text displayed when a module is first loaded. This object must contain a "type" key which can be: "html", "text" or "attachment". If the specified type is "html" or "text", then another key named "content" must be specified. In the case of "html", the "content" key must be associated with a String that contains the HTML markup to be displayed. In the case of "text", the "content" key must be associated with a String that contains plain text to be displayed. If the "type" specified is "attachment", then a key named "attachmentName" must be specified. The "attachmentName" refers to the attachment, associated with the document, that contains the text to be displayed. The type "attachment" is meaningful only when a module document is stored in the database. The values associated with "content", "text" or "attachmentName" can be localized string structures.
  • display : If specified, this object is used to provide configuration information to the display process. The possible options are:
    • type : specifies the type of display process. There are two possible values: classic and tiled.
    • defaultSchemaName : specifies the name of the schema used if the diplsay process attempts to show a document that is not associated with any schema.
  • edit : If specified, this object is used to provide configuration information to the document editor. The key "defaultSchemaName" can be defined, which specifies the name of the schema used if the editor attempts to show a document that is not associated with any schema. The key "newDocumentSchemaNames", if provided, controls the set of schemas available to create new documents. If the string "ALL_SCHEMAS" is used, then all schemas will be offered when a new document is created. Otherwise, the key "newDocumentSchemaNames" should be associated with an array of strings, each string specifying the name of a schema to be included at the creation process.
  • search : If specified, this object is used to provide configuration information to the search service. The attribute "disabled" (boolean) can be defined, which specifies if the search bar will be included in the display of the module. The attribute "constraint", if specified, can be a layer string (i.e. a single layer constraint), an array of layer constraints, or an object with a type of either "layer" or "model" with an array of strings which identify the constraint imposed on the search results (either "layerNames" or "sourceModelIds"). E.g. If layer identifiers are specified, then only documents posted on those layers are reported in search results.
  • widgets : If specified, this array is used to provide definitinos for additional widgets displayed by the module. A widget is a graphical representation which may include controls. The array contains object, each of which must have the compulsory attribute "widgetType". The widget type defines what widget is added to the display of the module. The order of the array defines the order in which the widgets are created. All other attributes in the widget definition object are dependent on the type of the requested widget.
  • canvas : If specified, this object defines the main canvas (map, graph) of the module. This option is mutually exclusive with the "map" attribute. Therefore, one must specify "map" or "canvas", but not both.
  • utilities : If specified, this array is used to provide definitions for utilities to be operating while the module is displayed. A utility is a logical process that modifies the stock Nunaliit operations. The array contains object, each of which must have the compulsory attribute "utilityType". The type defines what utility is added to the module. The order of the array defines the order in which the utilities are created. All other attributes in the utility definition object are dependent on the type of the requested utility.
  • css : If specified, this string is loaded as CSS style when the module is loaded. This allows a module creator to override CSS rules for a specific module.

The module object can also define a key named "map", which is associated with an object that describes a map that can be displayed. The map description object is defined with the following keys:

  • coordinates : Object that describes the coordinate system used by the map. A key named "initialBounds" must be specified, which is an array that describes the bounding box where the map should be initially opened. If the key "autoInitialBounds" contains true, then the map attempts to open over a region that encompasses all geometries. Optionally, a map projection can be specified with the key "srsName" and the mouse position projection can be set with the key "mousePositionSrsName".
  • addPointsOnly : If this key is specified and that this is associated with the Boolean true, then while editing, the map accepts only new geometries that are points. Otherwise, the map offers point, lines and polygons for newly created geometries.
  • showSRSAttribution : If this property is specified and is set to true, then the map shows attribution for the spatial reference system (SRS) in use.
  • enableKeyboardControls : If this property is specified and is set to true, then the map will be keyboard navigable when it is focused. While the map is focused, a plus icon should be visible. Arrow keys pan the map, +/- zoom in and out, Page Up/Page Down/Home/End scroll the map by a larger step (than panning), and the Enter will select nearby features (hit detection varies by map zoom level).
  • toggleClick : If this key is specified and that this is associated with the Boolean true, then the map unselects a feature when an already selected feature is clicked again. If false, clicking on an already selected feature selects it again.
  • scaleLine: An object that specifies options related to the scale line. This functionality is only available from Nunaliit 2.2.9 onwards. A scale line can have the following options:
    • visible: Required Boolean. Determines if the scale line is initially visible (true), or not (false). Default is false.
    • bottomOutUnits: Optional String. Define the type of units used for the bottom label of the scale line when zoomed out. Valid unit types include: “m”, “km”, “inches”, “ft”, and “mi”. Default is "mi".
    • bottomInUnits: Optional String. Define the type of units used for the bottom label of the scale line when zoomed in. Default is "ft".
    • topOutUnits: Optional String. Define the type of units used for the top label of the scale line when zoomed out. Default is "km".
    • topInUnits: Optional String. Define the type of units used for the top label of the scale line when zoomed in. Default is "m".
    • maxWidth: Optional Number. Defines in pixels the max width of the scale line. Default is 100.
    • geodesic: Optional Boolean. Use a geodesic measurement. Default is false.
  • enableWheelZoom : If this key is specified and that this is associated with the Boolean true, then the map can be zoomed in and out using the mouse wheel. This option is off, by default. This functionality is only available from Nunaliit 2.2.9 onwards.
  • gazetteerFeatureFilter : If specified, sets the feature classes to be used when using the Gazetteer process to create features. Defaults to querying for "Places" if not specified, or if the provided array of strings do not correspond to any accepted values. Must be an array of any of the following strings:
    • ADMIN
    • HYDRO
    • LANDMARKS
    • PLACES
    • ROADS
    • SPOT
    • MOUNTAINS
    • UNDERSEA
    • FOREST
  • backgrounds : This key must be associated with an array of objects, each object describing a background layer for the map. A background layer provides imaging on which the map displays features. Multiple background layers can be specified, but only one is shown at an given time.
  • overlays : This key must be associated with an array of objects, each object describing a foreround layer for the map. Overlays generally draw features on a map. Multiple overlays can be specified and shown simultaneously.
  • styles : This key must be associated with an object that describes the styling of the geometries on the map. This object is complex in itself and is covered in its own section.
  • layerSelector : Object that specifies options relating to the layer selector. If the option "suppress" is set, then the layer selector will not be drawn on the map. If the option "initiallyOpened" is set, then the layer selector will initially be maximized when the map is first drawn.

When defining a map background, the object must contain a key named "name" for specifying the name of the background, a key named "type" for specifying the type of the background and a key named "options", which is an object that contains information about the background depending on the type of background. The possible background types are: "Bing", "Google Maps", "wms", "wmts", "osm", "stamen", "stadia", and "image".

A "Bing" background layer must specify the following options:

A "Google Maps" background layer must specify the following options:

  • type : This is a required string which specifies the Google map -layer identifier. Possible values are (but not resricted to): "SATELLITE", "HYBRID" and "TERRAIN".
  • numZoomLevels : Specifies how many zoom levels are available to the imagery.

A "wms" background layer refers to a layer that is served from a WMS server. The object associated with a WMS layer can specify the following options:

  • url : Required. The URL of the WMS server
  • layers : Required. A string containing a list of comma-separated layer names, to be assembled by the WMS server.
  • styles : Required. A string containing a list of comma-separated style names, to apply style to each named layer.
  • srsName : Optional. Sets the projection associated with the layer.

A "wmts" background layer refers to a layer that is served from a WMTS server. The object associated with a WMTS layer can specify the following options:

  • url : Required. The URL of the WMTS server
  • layer : Required. A string containing the layer name, to be assembled by the WMTS server.
  • style : Required. A string containing the style name, to apply style to each named layer.
  • requestEncoding : Required. Sets the encoding (REST|KVP) of the tile requests.
  • numZoomLevels : Required. Sets the number of zoom levels associated with the service.
  • matrixSet : Required. Sets the matrixSet/projection name associated with the layer.
  • srsName : Optional. Sets the projection associated with the layer.
  • layername : Optional. Sets the layername associated with the layer.
  • fullTileExtent : Optional. An object configuration specifying the bounds of the extent and the source and destination projections.
  • opacity : Optional. Sets the opacity associated with the layer.
  • isBaseLayer : Required if Openlayers 2 is used, optional if Openlayers 5 is on-call.
Hint: Similar to wms service, the information and options list above, can be obtained from GetCapabilities request. An exmaple wmts background definition would be:
{
	"name" : "GCRC"
	,"type" : "wmts"
	,"options" : {
		"name": "GCRC"
		,"layer": "s2cloudless"
		,"requestEncoding": "REST"
		,"url": "http://localhost:8080/geowebcache/service/wmts/rest/s2cloudless-2018_3857/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}?format=image/jpeg"
		,"style": "raster"
		,"matrixSet": "EPSG:900913"
		,"numZoomLevels" : 31
		,"srsName" :"EPSG:900913"
		,"opacity": 1.0
		,"layername": "s2cloudless"
		,"isBaseLayer": true
		,"tileFullExtent": {
			"bounds": [-95.1530662,42.0583162,-74.3477424,51.0745523000001],
			"sourceProjection": "EPSG:4326",
			"destinationProjection": "EPSG:900913"
		}
	}
}

An "osm" background layer refers to a layer that is served by OpenStreetMap. The object associated with a OSM layer does not require any other option. However, the following options can be specified:

  • url : An array of URL strings, denoting the URL scheme to access the OpenStreetMap layer.
An example osm background definition would be:
{
  "name": "Open Street Map",
  "type": "osm",
  "options": {
    "url": [
      "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/${z}/${x}/${y}.png",
      "https://cartodb-basemaps-b.global.ssl.fastly.net/light_all/${z}/${x}/${y}.png",
      "https://cartodb-basemaps-c.global.ssl.fastly.net/light_all/${z}/${x}/${y}.png"
    ]
  }
}

A "stamen" background layer refered to a layer that was served from Stamen Design. Note that the Stamen services referenced using this type were taken down by Stamen Designs on October 31st, 2023. The reference information on this type is here to assist with migration. Use another background type instead. Stadia Maps (documented below) is the successor service where Stamen Designs layers may still be available.

For Stamen background layers to be served, the page that hosts the map must include the Javascript library explicitly. If the library is omitted, then the background layer definition is skipped. The object associated with a Stamen layer must specify the following options:

  • layerName : A string specifying which map design is to be used.
The Javascript library to support Stamen background layers can be added by inserting the following line in the host page (index.html). Ensure that the following Javascript inclusion statement appears after the inclusion of OpenLayers:
<script type="text/javascript" src="http://maps.stamen.com/js/tile.stamen.js?v1.2.1"></script>

A "stadia" background layer refers to a layer that is served by StadiaMaps, the successor to the Stamen background type. The only configuration option to provide is the layer name. Stadia backgrounds require the use of an account in order to set up domain authentication or an API key. It is recommended that domain-based authentication is used.

  • layerName : A string specifying which map design is to be used.

Currently, all free map styles are configurable. All out-of-the-box map styles from Stadia can be found here. A valid list of layer names currently accepted are:

  • alidade_smooth
  • alidade_smooth_dark
  • outdoors
  • stamen_toner
  • stamen_terrain
  • stamen_watercolor
  • osm_bright
{
	"name": "OSM Bright",
	"type": "stadia",
	"options": {
		"layerName": "osm_bright"
	}
}

An "image" background layer refers to a layer that serves an image file. The object associated with an image layer requires a url for the image file, image dimensions (width and height), and spatial extent to be specified:

  • url: A string denoting the URL to access the image file.
  • width: The width of the image file in pixels
  • height: The height of the image file in pixels
  • extent: An array representing the spatial extent the image will cover. e.g. [lower-left-x, lower-left-y, upper-right-x, upper-right-y]
  • numZoomLevels: The number of zoom levels provided for the image (optional)
An example image background definition would be:
{
                        "name": "My Image",
                        "type": "image"
                        ,"options": {
                            "url": "images/my_image.gif",
                            "width": 2000,
                            "height": 1200,
                            "extent": [-180, -88, 180, 88],
                            "numZoomLevels": 5
                        }
                }

When defining map overlays, any layer definition acceptable for background can be included as an overlay. When defining an overlay, the overlay object is specified as follows:

  • name : String that specifies the name of the layer
  • type : String that specifies the type of overlay. Possible values are: "couchdb", "model" and "wfs".
  • options : Object defining parameters. Based on the type of overlay.
  • featurePopupDelayMs : A number that specifies the delay, in milliseconds, between the time a mouse hovers over a feature and that the pop-up window is displayed.
  • clustering : An object. If specified, a clustering algorithm is used to group geometries that are close on the map. This object contains attributes to configure the clustering algorithm: "distance", an integer representing the maximum distance, in pixels, for two geometries to be considered as a cluster; "threshold", an integer that represents the minimum number of geometries in a cluster; "disableDynamicClustering", a boolean that controls how small lines and polygons are clustered; "clusterPointsOnly", a boolean that, if set, disable the clustering algorithm for lines and polygons. If "clusterPointsOnly" is set to true, "disableDynamicClustering" also needs to be set to true.
  • visibility : Optional Boolean. Determines if the layer is initially visible (true), or not (false).
  • minimumLinePixelSize : Optional Number. Determines the smallest size for a line. Lines smaller than this specified size are converted to a point.
  • minimumPolygonPixelSize : Optional Number. Determines the smallest size for a polygon. Polygons smaller than this specified size are converted to a point.

A "couchdb" overlay is associated with an options object defined as follows:

  • layerName : String that specifies the layer identifier.

A "model" overlay is associated with an options object defined as follows:

  • sourceModelId : String that specifies the identifier of the document model used to retrieve geometries for map.

A "wfs" overlay is associated with an options object defined as follows:

  • url : Required. String that contains the URL to the WFS server
  • featureType : String that specifies the feature type requested from the WFS server.
  • featureNS : String that specifies the full name space associated with the feature type.
  • featurePrefix : String that specifies the prefix relating to the name space.
  • version : Optional. Version of WFS protocol used to access WFS server.
  • geometryName : Optional String. Attribute name of the geometry used to display feature. If not specified, defaults to 'the_geom'.
  • sourceSrsName : Optional String. Specifies the projection name of the geometries served by the WFS server. Defaults to 'EPSG:4326'
  • filter : Optional Object. Specifies a filter to select the features returned by the WFS server.

Module Display

A document module can be rendered by a web application by using the module display function. There are two methods: using an in-line module document, or using a module document saved in the database.

The following script shows how to call the display function:

function main_init(config) {
// Get module name from URL parameters
var moduleName = 'generic.module:default';

var moduleDisplay = new $n2.couchModule.ModuleDisplay({
	moduleName: moduleName
	,config: config
	,titleName: 'title'
	,mapName: 'map'
	,sidePanelName: 'side'
	,searchPanelName: 'searchInput'
	,navigationName: 'navigation'
	,navigationDoc: 'navigation.demo'
	,languageSwitcherName: 'language_switcher'
	,helpButtonName: 'help_button'
	,onSuccess: function(){}
	,onError: function(err){ alert('Unable to display module('+moduleName+'): '+err); }
});

};

The instance of ModuleDisplay is initialized using an object that provided all necessary parameters. This initialization object is defined as follows:

  • moduleName : This is the identifier for the document, saved in the database, that contains the module object to be rendered. This option is mutually exclusive with "moduleDoc".
  • moduleDoc : This is the module document to be rendered. This option is mutually exclusive with "moduleName".
  • config : Configuration object generated by the configuration process.
  • titleName : HTML identifier of the element where the title of the module is rendered.
  • mapName : HTML identifier of the element where the map of the module is rendered.
  • sidePanelName : HTML identifier of the element where the textual information of the module is rendered.
  • searchPanelName : HTML identifier of the element where the database search input is rendered.
  • navigationName : (Optional) HTML identifier of the element where the Table of Content (TOC) is rendered. The TOC is derived from the information found in the navigation document.
  • navigationDoc : (Optional) Identifier of the document that contains the Table of Content for the atlas.
  • languageSwitcherName : (Optional) HTML identifier of the element where the button to change language is installed. This option makes sense for instances of Nunaliit atlas where multiple languages are employed.
  • helpButtonName : (Optional) HTML identifier of the element where the help button is installed.
  • onSuccess : Function called when the rendering initialization is completed.
  • onError : Function called when an error is encountered in the rendering initialization.

The styling of geometries is controlled by an object found within the map object contained in a module document. There are many styles that can be defined for a map, and two map styling approaches exist; a conditional map styling and a legacy map styling.

Both approaches include a value to the "styles" attribute of a map object. In the case of the conditional styling, an array is installed as the value of the "styles" attribute; in the legacy case, it is an object.

In legacy map styling, a number of styles are computed. Each style is composed of four basic rendering styles that corresponds to the four states:

  • normal: This is the default state of a geometry.
  • hovered: This is the state of a geometry when the user's mouse is on top of (hovering over) the geometry.
  • clicked: This is the state of a geometry when it has previously been selected by a user. This generally happens when a user clicks on the geometry.
  • hoveredClicked: This is the state of a geometry when a geometry is both selected and hovered over.

The styles computed in this approach are:

  • base : The base style is the default style of a map. While drawing a map, if a geometry is not matched by any particular style, then the map base style is applied. Since all other styles found in a map are derived from the base style, it is important that it is fully defined.
  • geometry type styles : It is possible to apply a style different than the base style given the type of a geometry: "point", "line" or "polygon". Geometry type styles can be defined at the same level as the base style, or within layer, schema or intent styles. A geometry type style does not need to be fully defined, since the definition of a geometry type style simply indicates the changes from the map base style.
  • layer styles : It is possible to apply a style different than the base style given the layer that a geometry belongs to. A layer style does not need to be fully defined, since the definition of a layer style simply indicates the changes from the map base style.
  • schema styles : It is possible to apply a style different than the base style given the schema of the document that contains a geometry. A schema style does not need to be fully defined, since the definition of a schema style simply indicates the changes from the map base style.
  • intent styles : In some circumstances, it is possible to apply an "intent" to a feature. When this happens, if a style is associated with the intent, then it is used to define the appearance of the feature. An intent style does not need to be fully defined, since the definition of the intent style simply indicates the changes from the map base style.

For a geometry, all styles are considered and those that apply are merged in order of precedence:

  1. intent style
  2. schema style
  3. layer style
  4. geometry type style
  5. base style

The way that two styles are merged is performed by merging the matching basic rendering styles (normal, hovered, clicked, hoveredClicked) of each merged style. The resulting basic rendering style is a union of all attributes found in the two merged ones. In the case of a collision, the basic rendering style with precedence wins.

The final rendering style for a geometry is based on the state of the geometry. The four basic rendering styles are merged to represent the state of the geometry. In this process, the "normal" component is taken as the base and all other states are simply denoting the differences from the "normal" styling.

An example of a map style object is as follow:

...
"styles":{
	"base":{
		"normal":{
			"fillColor": "#ffffff"
			,"strokeColor": "#ee9999"
			,"strokeWidth": 2
			,"fillOpacity": 0.4
			,"strokeOpacity": 1
			,"strokeLinecap": "round"
			,"strokeDashstyle": "solid"
			,"pointRadius": 6
			,"pointerEvents": "visiblePainted"
		}
		,"clicked":{
			"strokeColor": "#ff2200"
			,"strokeWidth": 3
		}
		,"hovered":{
			"fillColor": "#0000ff"
			,"cursor": "pointer"
		}
		,"hoveredClicked":{
			"fillColor": "#0000ff"
			,"strokeColor": "#ff2200"
			,"strokeWidth": 3
			,"cursor": "pointer"
		}
	}
	,"line":{
		"normal":{
			"strokeWidth": 4
		}
		,"hovered":{
			"strokeColor": "#0000ff"
		}
	}
	,"layers":{
		"approved":{
			"base":{
				"normal":{
					"fillColor": "#ee9900"
				}
			}
		}
	}
	,"schemas":{
		"research_document":{
			"base":{
				"normal":{
					"fillColor": "#bbbb00"
					,"strokeColor": "#eeee00"
					,"graphicName": "square"
				}
			}
		}
	}
	,"intents":{
		"red":{
			"base":{
				"normal":{
					"fillColor": "#ff0000"
				}
			}
		}
	}
}
...

In the previous example, a base style is defined where hovering is denoted with a change in fill colour and selection is denoted with a change in stroke colour. Since line geometries do not have a fill colour, a geometry type style is defined for lines overriding the hover behaviour so that it is shown using the stroke colour. The example also demonstrates how the geometries on the layer identified by "approved" are styled differently from the map base style. Geometries from documents known as "research_document" (schema name) are also styled differently, regardless of which layer they appear on. Finally, if a geometry is associated with an intent named "red", then its fill colour is red (#ff0000).

Currently, there are two intents defined by the Nunaliit framework: 'cluster' and 'find'. The use for those intent styles are as follows:

  • cluster: This is the style used when a number of geometries are combined as one on the map. This usually happens given the proximity of the geometries.
  • find: This is the style used when a geometry is currently highlighted as a result of the 'Find on Map' button.

The following example of a cluster intent style defines a square marker labelled with the number of features contained in the cluster:

...
,"intents":{
			"cluster":{
				"base":{
					"normal":{
						"fillColor": "#ee9900",
						"fontColor": "#ffffff",
						"pointRadius": 14,
						"fontFamily": "Courier New, monospace",
						"fontSize": "14px",
						"fontWeight": "bold",
						"graphicName": "square",
						"label": "${count}",
						"labelAlign": "cm",
						"labelOutlineColor": "#000000",
						"labelOutlineWidth": 3,
						"labelXOffset": "0",
						"labelYOffset": "0",
						"strokeColor": "#ffffff",
						"strokeWidth":2 
					}
				}
			}
		}
...

In conditional map styling, a number of rules are defined in an array. The order of the rules in the array is important as it defines the order of precedence, with a later rule taking precedence over all previous ones.

Each rule is made of a condition a set of named basic rendering styles. The condition of the rule, is used to determine if a rule applies to a geometry, or not. The basic rendering styles are used to holds styling information to be applied to the geometries.

The named basic rendering styles are:

  • normal: This is the default style for a geometry.
  • hovered: This is the style applied when the user's mouse is on top of (hovering over) the geometry.
  • selected: This is the style applied when the geometry is selected. This generally happens when a user clicks on the geometry.
  • found: This is the style applied when a geometry is found using the "Find on Map" button.

For a given geometry, all rules are considered. The rules where the condition is met (condition returns true), are retained and merged in order of precedence. Two rules are merged by combining the basic rendering styles with the same name. The merging is the union of the attributes found in each basic rendering style. In case of collision, the style with precedence wins.

Once all the rules have been merged, a final rendering style is derived from the basic rendering styles. This merging is performed as described above, with the following precedence:

  • hovered
  • found
  • selected
  • normal

The rendering style computed from this last merge is used to affect the rendering of geometry.

Rule Conditions:

A rule condition is written similar to a condition in a Javascript program. In the context of a condition, not all facilities available in standard Javascript are provided. However, a number of objects and functions are defined to ease the writing of those conditions:

  • doc : The document associated with this geometry
  • n2_selected : A boolean representing if this geometry is selected
  • n2_hovered : A boolean representing if this geometry is in focus
  • n2_found : A boolean representing if this geometry is in "found" state
  • n2_elem : The actual DOM element being rendered
  • isSelected() : A function that returns true if the geometry is selected
  • isHovered() : A function that returns true if the geometry is in focus
  • isFound() : A function that returns true if the geometry is in focus
  • isPoint() : A function that returns true if the geometry is a point
  • isLine() : A function that returns true if the geometry is a line
  • isPolygon() : A function that returns true if the geometry is a polygon
  • isSchema(schemaName) : A function that returns true if the document associated with this geometry is of the type "schemaName".
  • onLayer(layerId) : A function that returns true if the document associated with this geometry is on the layer "layerId".
  • hasClass(className) : A function that returns true if the DOM element associated with this geometry contains the class "className).
  • Math : Javascript object that provides all mathematical functions.

In addition to all the attributes and function defined above, all variables provided in the context by an element generator are also available in the context of a rule condition.

Finally, other functions can be added to the list via the global context. The global context can be obtained via the function nunaliit.styleRuleParser.getGlobalContext().

Basic Rendering Style:

Each rule has a number of basic rendering styles. Each of those styles is made of a set of attributes (key-value pairs). These attributes are applied similar to many modern styling systems (e.g. css). The following style attributes are available:

  • fill: The fill rule defines the colour used for the fill of a geometry. e.g. "fill":"#ff0000"
  • fill-opacity: The fill-opacity rule defines the opacity of the fill in a geometry, ranging from 0 (transparent) to 1 (opaque). e.g. "fill-opacity":0.5
  • stroke: The stroke rule defines the colour used for the stroke (i.e. the outline) of a geometry. e.g. "stroke":"#00ff00"
  • stroke-width: The stroke-width rule defines the width of the stroke. e.g. "stroke-width":4
  • stroke-opacity: The stroke-opacity rule defines the opacity of the stroke in a geometry, ranging from 0 (completely see though) to 1 (completely opaque). e.g. "stroke-opacity":0.8
  • stroke-linecap: The stroke-linecap rule defines the style of the start and end points of the stroke line, and can be set to round, butt, or square. e.g. "stroke-linecap":"round"
  • stroke-dasharray: The stroke-dasharray rule defines the stroke style pattern, and can be set to; dot, dash, dashdot, longdash, longdashdot, or solid (default). e.g. "stroke-dasharray":"dash"
  • r: The r rule defines the radius of a point geometry. e.g. "r": 8
  • graphicName: The name of a graphic which defines a shape used by a point geometry. e.g. "graphicName": "square"
  • label: The label rule defines the map label placed over the feature. e.g. "label":"Nunaliit" or "label":"=doc.demo_doc.title"
  • color: The color rule defines the font colour of text. e.g. "color":"#ffffff"
  • font-family: The font-family rule defines the font family used by text. e.g. "font-family": "Courier New, monospace"
  • font-size: The font-size rule defines the size of the font used by text. e.g. "font-size": “12px”
  • font-weight: The font-weight rule defines the font weight used by text. e.g. "font-weight": "bold"

In addition to the style attributes mentioned above, any style attribute understood by the underlying rendering engine can be used.

The following is an example of map conditional styling:

 
...
,"styles":[
	{ 
		"condition": "true"
		,"normal": {
			"stroke-width": 2
			,"stroke": "#559955"
			,"fill": "#99FF99"
			,"fill-opacity": 0.4
			,"r": 8
		}
		,"selected": {
			"stroke-width": 4
			,"stroke": "#FF9999"
			,"fill": "#FF9999"
			,"r": 10
		}
		,"hovered": {
			"stroke-width": 3
			,"stroke": "#9999FF"
			,"fill": "#9999FF"
			,"r": 12
		}
		,"found": {
			"stroke-width": 4
			,"stroke": "#FF9999"
			,"fill": "#9999FF"
		}
	}
	,{
		"condition": "isPoint()"
		,"normal": {
			"r": 4
		}
	}
	,{
		"condition": "isLine()"
		,"normal": {
			"stroke-width": 8
			,"stroke-linecap": "round"
			,"stroke-dasharray": "dash"
			,"stroke": "#555555"
		}
	}
	,{
		"condition": "isPolygon()"
		,"normal": {
			"stroke-width": 1
			,"stroke": "#333333"
			,"fill": "#bbbbbb"
			,"fill-opacity": 0.8
		}
	}
	,{
		"condition": "onLayer('approved')"
		,"normal": {
			"stroke": "#009955"
			,"fill": "#00FF99"
			,"fill-opacity": 0.4
		}
	}
	,{
		"condition": "isSchema('demo_media')"
		,"normal": {
			"stroke": "#AAAAAA"
			,"fill": "#AA8888"
			,"fill-opacity": 0.6
		}
	}
	,{
		"condition": "doc.nunaliit_layers.length > 2"
		,"normal": {
			"fill": "rgb(223,223,12)"
			,"r": 15
			,"label": "=doc.demo_doc.title"
			,"color": "#000000"
			,"font-family": "Courier New, monospace"
			,"font-size": "12px"
			,"font-weight": "bold"
			,"graphicName": "square"
		}
	}
]
...

Schemas are used to define how a database document is displayed to the user within the context of an atlas. There are multiple instances when a user can be presented the content of a database document, and a schema handles all those cases. Among those cases are the "display" case, when a user is focusing on a particular database document. Another case is the "brief" view, which is displayed in a bubble when a user mouses over a map feature that relates to the database document. Yet another case is the "edit" view, displayed when a user is editting a database document.

In Nunaliit, schemas are used in conjunction with a technology called "Handlebars templates" (http://handlebarsjs.com) to render the HTML fragments necessary to display the information contained in a document. Handlebars templating, based on Mustache templates, was designed specifically for this purpose and documentation for this technology is readily available on the Internet.

A database document is associated with a schema by recording the schema name in a special key named "nunaliit_schema", at the top level of the database document. The document, offered here as an example, is associated with a schema named "placename":

{
	"_id": "just a test"
	,"place": {
		"name": "test place"
		,"description": "just a description"
	}
	,"nunaliit_schema": "placename"
}

A schema is a document that can be saved in the database. When schemas are saved in the database, it offers greater flexibility since the information recorded and displayed for each document type can be tailored at run-time to suit the atlas' needs.

Following is an example of a schema document:

{
   "_id": "myschema:placename",
   "nunaliit_type": "schema",
   "nunaliit_schema": "schema",
   "name": "placename",
   "label": "Place Name",
   "isRootSchema": true,
   "create": {
       "place": {
           "name": "",
           "description": ""
       },
       "nunaliit_schema": "placename"
   },
   "brief": "PlaceName({{#place}}{{name}}{{/place}})",
   "display": "<div>\n\t{{#place}}\n\t\t{{#name}}\n\t\t\t<div>\n\t\t\t\t<div class=\"label n2_localize\">Name</div>\n\t\t\t\t<div class=\"value\">{{name}}</div>\n\t\t\t\t<div class=\"end\"></div>\n\t\t\t</div>\n\t\t{{/name}}\n\t\t{{#description}}\n\t\t\t<div>\n\t\t\t\t<div class=\"label n2_localize\">Description</div>\n\t\t\t\t<div class=\"value n2s_preserveSpaces\">{{description}}</div>\n\t\t\t\t<div class=\"end\"></div>\n\t\t\t</div>\n\t\t{{/description}}\n\t\t{{/place}}\n\n\t</div>",
   "form": "<div>\n\t{{#place}}\n\t\t<div>\n\t\t\t<div class=\"label n2_localize\">Name</div>\n\t\t\t<div class=\"value\">{{#:field}}name{{/:field}}</div>\n\t\t\t<div class=\"end\"></div>\n\t\t</div>\n\t\t<div>\n\t\t\t<div class=\"label n2_localize\">Description</div>\n\t\t\t{{#:field}}description,textarea{{/:field}}</div>\n\t\t\t<div class=\"end\"></div>\n\t\t</div>\n\t{{/place}}\n</div>",
   "export": [
       {
           "select": "place.name",
           "label": "name"
       },
       {
           "select": "place.description",
           "label": "description"
       }
   ],
   "relatedSchemas": [
       "genericMedia"
   ]
}

An explication of each entry of the schema document is offered here:

  • nunaliit_type : For a schema document, this key must be associated with a fixed string: "schema". Otherwise, the document is not recognized as a schema.
  • nunaliit_schema : Optional. This informs the framework that the schema document should be viewed using the schema named "schema".
  • name : Required string. This string field contains the name that the schema is known as.
  • label : Optional string. This string field contains the label that should be presented to a user when a schema is to be selected.
  • isRootSchema : Required boolean. A schema that can be used to display a document should have this field set (true). Extensions, which are schemas used to display a fragment of a document, should have this field reset (false). In general, this field should be set (true).
  • create : Required object. This object field is the template for a new document when a new document based on the schema is requested.
  • brief : Required string. This string field contains a Handlebars template of a HTML fragment to be used when displaying a document associated with this schema in "brief" mode.
  • display : Required string. This string field contains a Handlebars template of a HTML fragment to be used when displaying a document associated with this schema in "display" mode.
  • form : Required string. This string field contains a Handlebars template of a HTML fragment to be used when editing a document associated with this schema.
  • export : Optional array. This array is used while exporting documents from the database. This export array specifies which fields from a document (associated with the schema) are sent during an export process. The array contains objects. Each object contains a string field called "label" that represents the name of the field being exported. Each object also contains a string field called "select", which is selector to pick the field from the object to be exported. This selector is using a dotted notation, where inner fields are accessed by naming the parent field, followed by a period and completed using the inner field's name.
  • relatedSchemas : Optional array. This array contains strings, which are names of other schemas. During the operation of the atlas, when a user is adding information relating to a document, this array is used to figure which schemas can be used to relate this new information.
  • definiton : Optional object. This structure contains the definition for the schema. A schema definition is an alternate view of the schema where all information needed to produce the other schema attributes can be found. See Schema Definition for more information.

Because the "display" field of a schema is a Javascript string containing a Handlebars template of a HTML fragment, many characters must be escaped which hinders comprehension. However, if one was to access this field using an editor that showed the content without escaping characters, this is what the "display" field from the example above would look like:

<div>
	{{#place}}
		{{#name}}
			<div>
				<div class="label n2_localize">Name</div>
				<div class="value">{{.}}</div>
				<div class="end"></div>
			</div>
		{{/name}}
		{{#description}}
			<div>
				<div class="label n2_localize">Description</div>
				<div class="value n2s_preserveSpaces">{{.}}</div>
				<div class="end"></div>
			</div>
		{{/description}}
		{{#person}}
			<div>
				<div class="label n2_localize">Person</div>
				<div class="value">{{.}}</div>
				<div class="end"></div>
			</div>
		{{/person}}
	{{/place}}
</div>

In the same manner, the "form" field from the schema example is unescaped here to help with comprehension:

<div>
	{{#place}}
		<div>
			<div class="label n2_localize">Name</div>
			<div class="value">{{#:field}}name{{/:field}}</div>
			<div class="end"></div>
		</div>
		<div>
			<div class="label n2_localize">Description</div>
			<div class="value">{{#:field}}description,textarea{{/:field}}</div>
			<div class="end"></div>
		</div>
		<div>
			<select class="{{#:input}}person{{/:input}}">
				<option value="amos">Amos</option>
				<option value="jp">JP</option>
				<option value="peter">Peter</option>
			</select>
		</div>
	{{/place}}
</div>

A Handlebars template defines how a given JSON object, a database document, is scanned to generate a HTML fragment to represent the object. All functions offered by Handlebars are available to the templates. Furthermore, in the context of the Nunaliit framework, some accessors and built-in functions are added to help with the processing of documents and generation of forms:

  • :parent : Accesses the parent object structure. Example {{#:parent}}{{name}}{{/:parent}} displays the name attribute from the parent's object structure.
  • :iterate : For an object structure, returns an array of objects. Each object contains two keys: "key" and "value". This is a way to iterate over all key-value pairs defined in an object.
  • :empty : Returns true for an object that contains no key.
  • :input : This is a function that prints all classes needed for the proper function of the editor. The classes reported by this function associate HTML input tags (<input> and <textarea>) to the proper field within an object. The syntax for a the ":input" function is: {{#:input}}field-name,field-type{{/:input}}, where field-name is the name of the field being editted; and, field-type is the type of the field. The field-type can be ommitted. Possible values for field-type are: reference, date and numeric. For a reference field, a string is presumed to be a document identifier. For a date field, a numeric value is assumed to be a date that respects the Javascript Date convention. For a numeric field, only numbers are accepted.
  • :field : This is a function that prints HTML containing editor inputs for a given document attribute. This function is preferred over :input. The syntax for the ":field" function is: {{#:field}}field-name[,field-option]...{{/:field}}, where field-name is the name of the field being edited; and, field-option is one or multiple of the possible options (textarea,checkbox,reference,date,numeric,layers,localized, placeholder). For example: {{#desc}}Description: {{#:field}}.,textarea{{/:field}}{{/desc}}
  • :localize : This is a function that prints HTML to report a localized string from a document. In Nunaliit, if a string found in a document must contain versions for multiple language, then a special structure can be used to report the various languages. The ":localize" function is used to display the most appropriate language available, and if not generate the appropriate markup to show that a fallback language was used. The syntax for the ":localize" function is: {{#:localize}}name[,option]...{{/:localize}}, where name is the name of the attribute being displayed; and, option is one or multiple of the possible options (html). For example: {{#desc}}Description: {{#:localize}}.{{/:localize}}{{/desc}}
  • :array : This is a function that prints HTML containing editor inputs for an array found in a document. The syntax for the ":array" function is: {{#:array selector}}template{{/:array}}, where selector is the name of the field which is an array; and, template is a Handlebars template used to edit each element of the array. For example: Layers: {{#:array nunaliit_layers}}<div>{{#:field}}.{{/:field}}</div>{{/:array}}

Once a HTML fragment is generated using the Handlebars templating, the Show Service is called on the result to modify the HTML tags according to a set of rules. See the section on the Show Service for more details.

Arrays are supported in the templates provided by schemas. However, there is a special case when it comes to input forms. In fact, Handlebars.js supports the displaying of information found in arrays using the stock implementation. On the other hand, a special Nunaliit function is provided to deal with the input side of array elements.

For the purpose of demonstration, the following object structure is used as document to be displayed and edited:

{
	"data":{
		"title":"a Book"
		,"authors":[
			"John Smith"
			,"Jane Doe"
		]
	}
}

To display the array of authors, a template using the Handlebars iteration primitives could be used:

	{{#data}}
		{{#authors}} <!-- this iterates over all authors -->
			<div> {{.}} </div>
		{{/authors}}
	{{/data}}

However, when it comes to editing a document containing an array, some operations are required on the array itself: adding a new entry at the end of the array, deleting an item from an array and resorting the order of the array. By using the function :array, defined by Nunaliit, the proper HTML is added to manage the array operation. Within the :array tags, a proper template must be provided to handle each element of the array. The following is an example of a template to handle the input form for authors:

	{{#data}}
		{{#:array authors}} <!-- this iterates over all authors, adding HTML to manage array -->
			<div> {{#:field}}.{{/:field}} </div> <!-- for an author name, use simple text input -->
		{{/:array}}
	{{/data}}

The creation and maintenance of schemas can be quite daunting. This is because there are multiple fields that must be kept synchronized with the attributes carried by documents of that schema. To help with the synchronization of all these fields, a tool has been developed to alleviate the work: the commands add-schema and update-schema.

These two commands, with the help of a schema definition file, can create and updates schemas based on simpler means to define what is expected to be found in documents of that schema type. A schema definition is a JSON structure that specifies the information needed to produce a schema. If a schema is derived from such a definition, then the definition can be found in the "definition" attribute. This means that when a schema is stored on disk, its definition, if present, can be found in a file named "definition.json".

The structure of a schema definition is shown in the following example:

{
	"group": "demo"
	,"id": "doc"
	,"label": "Document"
	,"emailOnCreate": false
	,"relatedSchemas": [ "demo_media" ]
	,"initialLayers": [ "public" ]
	,"attributes": [
		{...}
		,{...}
	]
}

The top attributes for the schema definition are defined as follows:

  • group : This is a string and should be the name of the atlas, or some other name to represent the group under which this schema falls under. This helps avoiding collisions in the global schema name space.
  • id : String which is the unique identifier for this schema under the given group. The resulting schema name if the concatenation of the group name and the schema identifier: <group>_<id>.
  • label : This is a string presented to users of the atlas when referring to this schema.
  • emailOnCreate : This is a boolean value indicating whether or not an email should be sent whenever a document of the particular schema type is created. It will email the user that created the document, all atlas vetters, as well as all atlas administrators. It defaults to false when not specified.
  • relatedSchemas : This is an array of strings. Each entry in this array represents the name of a schema which is related to this one.
  • initialLayers : This is an array of strings. Each entry in this array represents a layer identifier which is added to a document of this schema when first created.
  • attributes : This is an array of objects. Each entry in this array represents an attribute definition. The set of attribute definitions specifies the information carried by documents associated with this schema. This array of attribute definitions is the bulk of the schema definition.

An example of an attribute definition is given here:

{
	"id": "title"
	,"label":"Title"
	,"type": "string"
	,"includedInBrief": true
}

The top attributes for an attribute definition are defined as follows:

  • id : This is a string that represents the name of the attribute under which information is stored in documents associated with the schema. It is compulsory for all types except "title", "geometry", "hover_sound", "createdBy" and "createdTime".
  • label : This is a string presented to users of the atlas when referring to this attribute.
  • type : This is a compulsory string that represents the type of the attribute. Possible values are:
    • string
    • localized
    • date
    • reference
    • array
    • tag
    • selection
    • checkbox
    • checkbox_group
    • file
    • title
    • geometry
    • hover_sound
    • nunaliit_key_media_ref
    • triple
    • createdBy
    • createdTime
    • custom
  • textarea : This is a boolean. If set, then when this attribute is shown in an editor, a textarea is used to capture the content. When displayed, the spaces entered in the editor are preserved for printed out.
  • includedInBrief : This is a boolean. If true, this attribute is included in the "brief" portion of the schema. If includedInBrief is not specified, then it is assumed that it is set to false.
  • excludedFromDisplay : This is a boolean. If true, this attribute is excluded from the "display" portion of the schema. If excludedFromDisplay is not specified, then it is assumed that it is set to false.
  • excludedFromForm : This is a boolean. If true, this attribute is excluded from the "form" portion of the schema. If excludedFromForm is not specified, then it is assumed that it is set to false.
  • urlsToLinks : This is a boolean. This option is applicable for "string" and "localized". If true, when the field is displayed, the URLs are converted to links.
  • wikiTransform : This is a boolean. If true, this attribute is transformed using Wiki markup. If wikiTransform is not specified, then it is assumed that it is set to false.
  • subject, predicate, and object : These are nested configurations that are parsed when "type" is set to "triple". These three should all be used together. It accepts "id", "label", and "type". When "type" is specified, triple configurations can accept "string", "localized", "reference", and "selection". The respective configuration options for those types apply.
  • An example of an triple definition using various types follows:

{
	"type": "triple",
	"label": "Placename with Meaning",
	"id": "placename_with_meaning",
	"subject": {
		"label": "Placename Document",
		"type": "reference",
		"id": "placename"
	},
	"predicate": {
		"type": "selection",
		"id": "predicate",
		"options": [
			{
				"label": "has primary meaning",
				"value": "has_primary_meaning",
				"isDefault": true
			},
			{
				"label": "has secondary meaning",
				"value": "has_secondary_meaning",
				"isDefault": false
			}
		],
		"excludedFromDisplay": false,
		"excludedFromForm": false
	},
	"object": {
		"label": "Meaning",
		"type": "localized",
		"id": "meaning"
	}
}
  • elementType : This is a string. "elementType" must be specified if "type" is set to "array". "elementType" represents the type of the items in the array. It can take one of the following values:
    • string
    • localized
    • date
    • reference
    • triple
  • elementOptions : This is a nested configuration for configuring triples as elements of an array type, if "type" is "array" and "elementType" is "triple". The same configuration rules for triples applies under this option.
  • An example of an array of triples definition using various types follows:

    {
    	"type": "array",
    	"label": "Placenames with Meaning",
    	"id": "placenames_with_meaning",
    	"elementType": "triple",
    	"elementOptions": {
    		"subject": {
    			"label": "Placename Document",
    			"type": "reference",
    			"id": "placename"
    		},
    		"predicate": {
    			"type": "selection",
    			"id": "predicate",
    			"options": [
    				{
    					"label": "has primary meaning",
    					"value": "has_primary_meaning",
    					"isDefault": true
    				},
    				{
    					"label": "has secondary meaning",
    					"value": "has_secondary_meaning",
    					"isDefault": false
    				}
    			],
    			"excludedFromDisplay": false,
    			"excludedFromForm": false
    		},
    		"object": {
    			"label": "Meaning",
    			"type": "string",
    			"id": "meaning"
    		}
    	}
    }
    
  • referenceType : This is an optional string. This option is considered only if "type" is set to "reference". If specified, influences how a reference is displayed. If set to "thumbnail", then the first thumbnail found in the referenced document is shown.
  • customType : This is a string that must be provided if the type is set to "custom". This represents the name of the custom type.
  • searchFunction : This is a string. This option is considered only if "type" is set to "reference". This is the name of the function used to bring up a dialog to set a reference in the "form" version of the schema. Nunaliit provides the following functions:
    • getDocumentId: Provides the same search results as the atlas search box in module header.
    • getDocumentFromSchema('<schema-name>'): Generates a list of documents using a specified schema name (e.g. 'demo-doc').
    • getDocumentFromLayer('<layer-id>'): Generates a list of documents on a specified layer id (e.g. public).
    • getDocumentFromModel('<model-id>'): Generates a list of documents in a specified model.
    • getLayers: Displays a list of available atlas layers, similar to the layer select dialog window.
    • getRelatedMedia: Filters the search results to a list of documents which have an attachment (e.g. if you did a search for the key word 'demo', and that returned a list of 10 documents, but only 3 of which had an attached media file. Only those 3 documents would display in the list).
    • getRelatedImage: Filters the search results to a list of documents which have an attachment of the file class type 'image'.
    • getRelatedAudio: Filters the search results to a list of documents which have an attachment of the file class type 'audio'.
    • getRelatedVideo: Filters the search results to a list of documents which have an attachment of the file class type 'video'.
    • getHoverSound: Filters the search results to a list of documents which have an attachment of the file class type 'audio'.
  • maxHeight : This is an integer that represents a number of pixels. This attribute is considered only if "textarea" is set. When this option is set, the associated text area is displayed with a maximum height. This maximum height is equal to a number of pixels specified by the "maxHeight" option. If maxHeight is specified but is zero or negative, it is ignored.
  • uploadOptional : This is a boolean value that represents if a file attribute is optional when a document is created. This attribute is considered only if the type is "file". If ommitted, then a file upload is required in order to create a new document.
  • placeholder : This is a string. This attribute is considered only if the type is "string". If provided, it represents the "placeholder" attribute of the <input> or <textarea> elements used in the form to accept entry of this attribute. In other words, it the input field is blank, it offers a suggestion as to what to enter.
  • options : This is an array of objects. "options" are considered only if the "type" is set to "selection". Each entry in the array represents an option in the selection box.
  • checkboxes : This is an array of objects. "checkboxes" are considered only if the "type" is set to "checkbox_group". Each entry in the array represents a checkbox in the group.
  • Each object in the "options" array have the following structure:

    • label : This is a string. It must be specified. This is the text presented to the user for this selection option.
    • value : This is a string. It must be specified. This is the text used as value in the document when this selection option is chosen.
    • isDefault : This is a boolean. If not specified, it is assumed to be false. This attribute should be set on a single selection option. This is the selection option that is first shown to the user when no value is set.

    Each object in the "checkboxes" array have the following structure:

    • id : This is a string. It must be specified. This is the name of the attribute, in the document, where the state of the checkbox is stored.
    • label : This is a string. It must be specified. This is the text presented to the user to inform about this checkbox.

    The following is a complete example of creating a new schema using this approach:

    1. nunaliit add-schema --group demo --id place
    2. Edit the file .../docs/schema.demo_place/definition.json and modify the content:
      {
      	"group": "demo"
      	,"id": "place"
      	,"label": "Place"
      	,"relatedSchemas": [ "place_media" ]
      	,"initialLayers": [ "places" ]
      	,"attributes":[
      		{
      			"type":"title"
      			,"label":"Place"
      			,"includedInBrief":true
      		}
      		,{
      			"type":"string"
      			,"id":"name"
      			,"label":"Name"
      			,"includedInBrief":true
      		}
      		,{
      			"type":"string"
      			,"id":"description"
      			,"label":"Description"
      			,"textarea": true
      		}
      		,{
      			"type":"date"
      			,"id":"period"
      			,"label":"Period"
      		}
      		,{
      			"type":"reference"
      			,"id":"author"
      			,"label":"Author"
      		}
      		,{
      			"type":"selection"
      			,"id":"type"
      			,"label":"Type"
      			,"options":[
      				{
      					"label": "City"
      					,"value": "city"
      				}
      				,{
      					"label": "Village"
      					,"value": "village"
      				}
      			]
      		}
      		,{
      			"type":"checkbox"
      			,"id":"verified"
      			,"label":"Has been verified"
      		}
      		,{
      			"type":"checkbox_group"
      			,"id":"attributes"
      			,"label":"Attributes"
      			,"checkboxes": [
      				{
      					"id": "has_library"
      					,"label": "Has a library"
      				}
      				,{
      					"id": "has_postoffice"
      					,"label": "Has a post office"
      				}
      			]
      		}
      	]
      }
      
    3. nunaliit update-schema --name demo_place
    4. nunaliit update

    Schemas supports a variety of types: strings, localized strings, dates, numbers and references. When this set of types is insufficient for an application, it is possible to define a custom type. Custom types require a number of steps to be enabled in Nunaliit. This section covers those requirements.

    It is important to note that custom types are available for any type other than the primary Javascript types. Nunaliit does not support custom types that translate to a primary type.

    Within Nunaliit, there is a location used to store types. This location is accessible by all components using Nunaliit. Therefore, the name of a custom type must be unique. Therefore, we suggest that all names for custom types be prefixed with the name of the company or project creating the type. For example, if company ABC was creating a new type of string, the name of the custom type should probably be something like 'abcString'.

    Names of custom types should be made of alphanumeric characters, including the underscore character (_). All other special characters should be avoided.

    If a definition file is used to generate a schema where a custom type is used, then the attribute type should be set to "custom" and the field "customType" should be set to the name of the custom type. For example:

    {
    	"group": "abc"
    	,"id": "place"
    	,"label": "Place"
    	,"relatedSchemas": []
    	,"initialLayers": []
    	,"attributes":[
    		{
    			"type":"title"
    			,"label":"Place"
    			,"includedInBrief":true
    		}
    		,{
    			"type":"custom"
    			,"id":"name"
    			,"label":"Name"
    			,"includedInBrief":true
    			,"customType": "abcString"
    		}
    	]
    }
    

    The Nunaliit framework offers an abstract class to manage custom types: nunaliit2.customType.CustomType

    Creating a subclass to this abstract class is the easiest way to implement a custom type. Here is an example to describe the process:

    var AbcStringCustomType = $n2.Class('AbcStringCustomType',$n2.customType.CustomType, {
    
    initialize: function(opts_){
    	var opts = $n2.extend({
    		dispatchService: undefined
    	},opts_);
    	
    	$n2.customType.CustomType.prototype.initialize.apply(this,arguments);
    },
    
    getTypeName: function(){
    	return 'abcString';
    },
    
    show: function(opts_){
    	var opts = $n2.extend({
    		elem: undefined
    		,doc: undefined
    		,selector: undefined
    		,showService: undefined
    		,m: undefined
    	},opts_);
    
    	var $elem = opts.elem;
        var doc = opts.doc;
        var selector = opts.selector;
        var showService = opts.showService;
        
    	var value = doc;
    	if( selector ){
    		value = selector.getValue(doc);
    	};
    	
    	$elem.empty();
    	
    	if( value && value.str ){
    		$elem.text( '***' + value.str + '***' );
    	};
    },
    
    fieldHandler: function(opts_){
    	var opts = $n2.extend({
            elem: null
            ,doc: null
            ,selector: null
            ,callbackFn: null
    	},opts_);
    
    	var $elem = opts.elem;
        var doc = opts.doc;
        var selector = opts.selector;
    
    	var $input = $('&lt;input&gt;')
    		.attr('type','text')
    		.appendTo($elem)
    		.change(function(e){
    			var $input = $(this);
    			var str = $input.val();
    			var value = {'abc':true,'str':str};
    			if( '' === str ){
    				value = undefined;
    			};
    			selector.setValue(doc,value);
    			opts.callbackFn(value);
    			return true;
    		});
    	
    	var value = selector.getValue(doc);
    	if( value && value.str ){
    		$input.val(value.str);
    	};
    }
    

    });

    window.nunaliit_custom.configuration = function(config, callback){

    // Install custom type: abcString
    new AbcStringCustomType({
    	dispatchService: config.directory.dispatchService
    });
    
    callback();
    

    };

    There are three main concerns when creating a custom type. First, one must design the structure of the data type. Second, one must implement a way to display the information found in the structure to the user. This is usually a process where HTML is generated to show the state of the content. Finally, one must design a method of entering or modifying the data found in the custom structure. This is generally also a portion of HTML that actively interacts with an editor.

    In the example above, the custom data follows this structure:

    {
    	abc: true
    	,str: 
    }
    

    When subclassing nunaliit2.customType.CustomType, three methods must be provided:

    • getTypeName: This method returns the name of the custm type
    • show: This method displays the data to the user
    • fieldHandler: This method creates a portion of HTML that interacts with the editor to change the state of the data.

    The "show" method produces HTML to represent the state of the custom type. It receives one parameter which is an object containing all the attributes necessary to perform the display. Here is a list of attributes:

    • elem: jQuery element. This is where the description of the data should be inserted
    • doc: Object. This is the complete document containing the custom type to be displayed.
    • selector: Optional instance of nunaliit2.objectSelector.ObjectSelector. This is a selector representing where in the document the custom data should be retrieved. If selector is not provided, then assume that the document is the actual value.
    • showService: Instance of ShowService. This is useful for delegating display tasks to this service.
    • m: Event. Event that triggerred the custom display. Should not be used directly.

    The "fieldHandler" method produces HTML that actively interacts with the editor. This allows the entry and modification of the custom type. At first, when the method is called, a HTML skeleton must be produced to display the current state. This usually involves input fields to enable a user to change the values found in the variable of custom type. Every time these values are changed, a callback function must be called to report the change. The "fieldHandler" method receives one parameter which is an object containing all the attributes necessary to perform the handling. Here is a list of attributes:

    • elem: jQuery element. This is where the description of the data should be inserted
    • doc: Object. This is the complete document containing the custom type to be displayed.
    • selector: Optional instance of nunaliit2.objectSelector.ObjectSelector. This is a selector representing where in the document the custom data should be retrieved. If selector is not provided, then assume that the document is the actual value.
    • callbackFn: Function. This must be called everytime the value of the custom type variable is changed. This enables the editor to refresh all the perspective showing this variable.

    The "fieldHandler" method is given the responsibility to create HTML, at the given location (elem), to accept information from the user. When the user changes the input, the new information should be stored in the document (doc) at the location specified by the selector (selector). It is important to call the callback function (callbackFn) every time the value is changed to give the opportunity to the other views of the document to update themselves.

    Nunaliit provides the "Show Service" in its configuration. The Show Service is logic that tranforms HTML according to a set of rules. The rules are defined by HTML classes assigned to elements. The Show Service scans the HTML content for those special classes and performs transformations accordingly.

    The following example demonstrates how the Show Service can be used to insert a translation into a HTML fragment:

    	<span class="n2s_localize">Title</span>
    
    If the above HTML was processed by the Show Service, then the word "Title" would be replaced by an equivalent word in the currently selected language. Obviously, if the currently selected language is English, then the word would remain unchanged. However, if the currently selected language was French, then the content of the "span" element would be replaced with "Titre". Note that the translation occurs only if a translation is found in the localization dictionary.

    In general, the classes used by the Show Service have a prefix of "n2s_".

    The Show Service can be retrieved in the configuration object via the directory object.

    The Show Service is invoked after a schema is used to generate HTML and after the introduction text is displayed. Therefore, any HTML specified in a schema or in a module introduction can take advantage of the classes understood by the Show Service.

    It is possible to call the Show Service directly via the function "fixElementAndChildren". Here is an example of obtaining the Show Service in nunaliit_custom.js and asking it to transform some HTML:

    window.nunaliit_custom.configuration = function(config, callback){
    
    var showService = null;
    if( config && config.directory ){
    	showService = config.directory.showService;
    };
    
    var doc = null;	
    if( showService ){
    	showService.fixElementAndChildren($('#fixme'), {}, doc);
    };
    
    // Done with configuration
    callback();
    

    };

    Some transformations performed by the Show Service need a document in context. For example, the class "n2s_insertFirstThumbnail" requests that the first thumbnail associated with a document be displayed. In this case, no action can be taken unless a document is specified.

    When invoking the Show Service directly, by calling the function "fixElementAndChildren", the caller can specify the context document.

    When the Show Service is invoked by the schema translation process, then the document used during the schema translation is offered as the context.

    If the Show Service is not invoked directly and there is no context document (introduction text), or if the document in context is not the one desired, it is possible to use the HTML attribute "nunaliit-document" to indicate the identifier of the document that should be used in context.

    In the following example, a portion of the introduction text is made to react to user events associated with a particular document. The document in question has the identifier "12345".

    	<div>
    		<span class="n2s_userEvents" nunaliit-document="12345">This document</span>
    	</div>
    

    The following list describes the classes understood by the Show Service:

    • n2s_briefDisplay : A HTML element with this class must contain only one child, which is a text node. The content of the text node should be the identifier for a document. When the Show Service detects the class "n2s_briefDisplay", it replaces the content of the element with a brief description of the referenced document.
    • n2s_clickAddLayerFromDefinition : When the Show Service detects an element with the class "n2s_clickAddLayerFromDefinition", it verifies if the document in context is associated with a layer definition. If so, it installs a "click" event handler on the element. When the element is clicked, the map is added with a new layer based on the layer definition found in the context document. If the document is not associated with any layer definition, then the element with the class "n2s_clickAddLayerFromDefinition" is removed.
    • n2s_clickDelete : When the Show Service detects an element with the class "n2s_clickDelete", it installs a "click" event handler on the element. When the element is clicked, the process to delete the context document is initiated.
    • n2s_clickEdit : When the Show Service detects an element with the class "n2s_clickEdit", it installs a "click" event handler on the element. When the element is clicked, an edit session associated with the context document is initiated.
    • n2s_clickFindGeometryOnMap : When the Show Service detects an element with the class "n2s_clickFindGeometryOnMap", it verifies if the document in context is associated with a geometry. If so, it installs a "click" event handler on the element. When the element is clicked, the map is centred on the geometry. If the document is not associated with any geometry, then the element with the class "n2s_clickFindGeometryOnMap" is removed.
    • n2s_clickLogin : When the Show Service detects an element with the class "n2s_clickLogin", it installs a click listener on the associated element. When the element is clicked, the login form is shown.
    • n2s_clickMapEdit : When the Show Service detects an element with the class "n2s_clickMapEdit", it installs a click listener on the associated element. When the element is clicked, if a map is shown, it is changed into edit mode.
    • n2s_convertTextUrlToLink : When the Show Service detects an element with the class "n2s_convertTextUrlToLink", it searches all text nodes within this element for possible URLs. For each found URLs, it replaces the text with an anchor element (<a>) with a "href" attribute pointing to the URL.
    • n2s_custom : When the Show Service detects an element with the class "n2s_custom", it dispatches a 'showCustom' message to find a handler to populate the content of this element. When the class 'n2s_custom' is used, a number of attributes are expected on the element. The attribute "nunaliit-custom" should be set to a name representing the type of information that should be displayed. The optional attribute "nunaliit-document" can be used to specify the context document. In the case of a schema, this attribute is not necessary since the document in context is expected to be displayed. Finally, the attribute "nunaliit-selector" should be used if a portion of the document to be displayed must be specified. The syntax of the selector should follow that produced by nunaliit2.objectSelector.
    • n2s_externalMediaLink : This class is deprecated. Use n2s_insertExternalMediaLink instead.
    • n2s_fullDisplay : A HTML element with this class must contain an attribute named "nunaliit-document" which contains the identifier of a document. When the Show Service detects the class "n2s_fullDisplay", it replaces the content of the element with a full description of the referenced document.
    • n2s_handleHover : When the Show Service detects an element with the class "n2s_handleHover", it installs a "hover" event handler on the element. When the element is moused over, an event is sent to the dispatcher informing that the context document is hovered. This generally results in behaviour specific to an atlas, such as playing "hover sound" or changing the colours of associated geometries on the map.
    • n2s_insertDocumentList : When the Show Service detects an element with the class "n2s_insertDocumentList", it empties the element and replaces its content with a list of reference links according to the requested list. The list of documents displayed is based on the list type and a name. The list type is specified using the attribute "nunaliit-list-type". The list name is specified using the attribute "nunaliit-list-name". Optionally, the attribute "nunaliit-list-live" can be specified to keep the list updating with changes in state. Here are the list types offered by Nunaliit:
      • layer : When the list type is "layer", then the attribute "nunaliit-list-name" should be set to a comma separated list of layer identifiers. All documents found on those layers are displayed in the list.
      • schema : When the list type is "schema", then the attribute "nunaliit-list-name" should be set to a comma separated list of schema names. All documents associated with those schemas are displayed in the list.
      • model : When the list type is "model", then the attribute "nunaliit-list-name" should be set to the identifier of a document model. All documents reported by the identified model are displayed in the list.
    • n2s_insertExternalMediaLink : A HTML element with this class must contain an attribute named "nunaliit-attachment". The "nunaliit-attachment" attribute must contain the name of an attachment associated with the context document. If an attachment with the given name is detected, then the element is populated with a link (<a> element) to reach the media file directly. This is useful for user that intend to download the media file. If the file originally uploaded to the database is available, then the original file is offered by the link. When the link element is clicked, the user is warned that the atlas is about to be exited, before following the link. This class is generally used with <div> tags.
    • n2s_insertFirstThumbnail : When the Show Service detects the class "n2s_insertFirstThumbnail", it replaces the content of the element with an <img> tag that points to the first thumbnail found within a document. If no thumbnail is available, then the element is emptied. Optionally, the attribute "nunaliit-document" can be supplied to specify which document contains the thumbnail. If "nunaliit-document" is not specified, then the document in context is used.
    • n2s_insertHoverSoundIcon : When the Show Service detects an element with the class "n2s_insertHoverSoundIcon", it verifies if the context document is associated with a hover sound. If so, it appends the element with an icon looking like a speaker. If the speaker icon is clicked, the hover sound is played. This is useful for environments with a touch interface, where mouse hover is not generally dispatched to the atlas client.
    • n2s_insertLayerName : A HTML element with this class must contain only one child, which is a text node. The content of the text node should be the identifier for a layer. When the Show Service detects the class "n2s_insertLayerName", it replaces the content of the element with the name associated with the layer.
    • n2s_insertMailFormButton : A HTML element with this class has its content replaced with a link that opens a web mail form, when selected. The link is displayed only if the mail service is configured to send web mail. The attribute 'nunaliit-label' can be added to the element to specify the text displayed by the link.
    • n2s_insertMediaPlayer : A HTML element with this class must contain an attribute named "nunaliit-attachment". The "nunaliit-attachment" attribute must contain the name of an attachment associated with the document in context. When the Show Service detects the class "n2s_insertMediaPlayer", it replaces the content of the element with an inline media player for the attachment. If a thumbnail is available, the thumbnail will be used as the initial image in the media player. Optionally, the attribute "nunaliit-document" can be supplied to specify which document contains the attachment. If "nunaliit-document" is not specified, then the document in context is used.
    • n2s_insertMediaView : A HTML element with this class must contain an attribute named "nunaliit-attachment". The "nunaliit-attachment" attribute must contain the name of an attachment associated with the document in context. When the Show Service detects the class "n2s_insertMediaView", it replaces the content of the element with an icon that represents the attachment. If a thumbnail is available, the thumbnail is used instead of the icon. Furthermore, when the icon or thumbnail is clicked by the user, the media is displayed using a dialog. Optionally, the attribute "nunaliit-document" can be supplied to specify which document contains the attachment. If "nunaliit-document" is not specified, then the document in context is used.
    • n2s_insertModuleName : The content of this HTML element is replaced with the title of a module document. The module document can be specified by providing the document identifier using the attribute "nunaliit-document". If the attribute "nunaliit-document" is not specified, then the document in context is used.
    • n2s_insertShowSplashPageButton : A HTML element with this class has its content replaced with a link that opens the splash page, when clicked. The link is displayed only if the splash page widget is present. The attribute 'nunaliit-label' can be added to the element to specify the text displayed by the link.
    • n2s_insertTime : A HTML element with this class must contain only one child, which is a text node. The content of the text node should be a number that represents time, according to the Javascript Date convention. When the Show Service detects the class "n2s_insertTime", it replaces the content of the element with textual representation of the time based on the user's locale.
    • n2s_insertUserName : A HTML element with this class has its content modified with displaying the name of a user. The user name is initially obtained using one of two ways. One way is to insert the user name has the only text element of the element. The second way is to set the user name in the attribute named 'nunaliit-user'. The latter approach is preferred. The user name refers to the identifier for that user found in the user database. When the Show Service detects the class "n2s_insertUserName", it replaces the content of the element with a span containing the display name, if available, and a span containing the user name.
    • n2s_insertWidget : A HTML element with this class has its content replaced with a widget. The type of widget is specified by the attribute "nunaliit-widget". The configuration for the widget is specified as a JSON object within the HTML element.
    • n2s_installMaxHeight : When the Show Service detects an element with the class "n2s_installMaxHeight", it installs a widget within the element to truncate the content to a maximum size. The maximum size is specified using an attribute named _maxheight, which should be set to a number that represents the maximum height in pixels. This widget adds a link to expand and shrink the element.
    • n2s_installTiledImageClick : This tranformation is specialized for documents that contain tiled map resources as attachments. The HTML element with the class "n2s_installTiledImageClick" should also have an attribute "nunaliit-attachment", which should be set to the directory name at the root of all tiles. These tiles must all be uploaded as attachments to the context document. Finally, the context document should also have an attachment called "tilemapresource.xml', which is a file that conforms to the TMS (Tile Map Service). The show service installs a click handler on the element. When the click handler is invoked, a display is rendered to show the tiled map.
    • n2s_localize : HTML elements that have the class n2_localize have the text child element replaced by text found to translate the initial text to the language in which the page is displayed. Nunaliit offers tools to define string replacements from the native English strings, to any other language. If the atlas page is displayed in a language other than English, that the language is supported by a translation page and that the string found as child of the element is associated with a translated replacement, then that replacement is used to populate the text of the element with the "n2_localize" class.
    • n2s_preserveSpaces : HTML elements that have the class n2s_preserveSpaces are modified so that newline characters are respected. n2s_preserveSpaces is generally used in schema fields that are accepted using a <textarea> tag in the "form" of the schema. This way, newline characters inserted by users during document editing are displayed in other perspectives.
    • n2s_referenceLink : A HTML element with this class must contain only one child, which is a text node. The content of the text node should be the identifier for a database document. When the Nunaliit framework detects the class "n2s_referenceLink", it replaces the content of the element with a brief description of the referenced document. Furthermore, the content is clickable and when clicked, a selection to this document is dispatched to the framework. In general, this results in the new document becoming the focus of the atlas.
    • n2s_select : A HTML element containing this class must also have an attribute named "n2-choice". When the show service detects this situation, it finds all children of the original element with a class "n2s_choice". All the children are removed except for a child that has a matching "n2-choice" attribute. Finally, the show service removes the child associated with the class "n2s_choiceDefault", unless no matching choice was found. This is useful for creating a portion of HTML where only one of multiple possible children is desired.
    • n2s_userEvents : A HTML element containing this class is linked to the context document. From this point on, when a user mouses over the element, the "userFocusOn" event is dispatched within Nunaliit. Similarly, when a user clicks on the element, the "userSelect" event is dispatched. Finally, when the user intention changes within Nunaliit, the classes "nunaliit_hovered", "nunaliit_selected" and "nunaliit_found" are added, or removed, from the HTML element, following the user's intention. Optional attributes "nunaliit-disable-click" and "nunaliit-disable-hover" can be set to "true" to prevent the appropriate events from being dispatched.
    • n2s_wikiTransform : A HTML element containing this class is expected to contain text with Wiki markup. When the Show Service detects this element, the text is converted to HTML using the wiki markup processor. The content of the original element is replaced with the HTML markup.
    Nunaliit supports the use of various wiki markups for styling text contained in HTML elements that have the n2s_wikiTransform class. Found below are examples of how to add various markups supported by Nunaliit.

    Headings

    Wiki Markup Result
    =Heading 1=

    Heading 1

    ==Heading 2==

    Heading 2

    ===Heading 3===

    Heading 3

    ====Heading 4====

    Heading 4

    Bold and Italicizing Text

    Wiki Markup Result
    '''Bold''' text Bold text
    ''Italicizied'' text Italicizied text

    Horizontal Rule

    Wiki Markup Sample ``` ---- ```

    Results of Wiki Markup


    Sections

    Wiki Markup Sample

    {{ class="abc" | alt="alt text"
    Section content
    }}
    

    Results of Wiki Markup

    Section content

    Lists

    Wiki Markup Sample ``` *item a *item b **item b-2 ``` Results of Wiki Markup
    • item a
    • item b
      • item b-2

    Wiki Markup Sample

    #item a
    #item b
    ##item b-2
    

    Results of Wiki Markup

    1. item a
    2. item b
      1. item b-2

    Hyperlinks

    Wiki Markup Result
    [[http://nunaliit.org]] http://nunaliit.org
    [[http://nunaliit.org|Nunaliit]] Nunaliit

    Insert a Nunaliit Show Service

    Show Service Wiki Markup
    Insert a full view of a document (e.g. doc id = 0987654321) [[nunaliit:n2s_fullDisplay|nunaliit-document="0987654321"]]
    Insert an atlas media document (e.g. doc id =1234567890) [[nunaliit:n2s_insertMediaView|nunaliit-document="1234567890" nunaliit-attachment="imagename.png"]]

    A Table of Content (TOC) can be stored in a "navigation document" and be rendered as part of the atlas interface. Details on how the navigation document is selected and where it is displayed is specified with the default module and navigation documents.

    To explain the structure of the navigation document, an example is offered:

    {
    	"_id": "12345"
    	,"nunaliit_navigation": {
    		"nunaliit_type":"navigation"
    		,"title": {
    			"nunaliit_type":"localized"
    			,"en":"Our Atlas"
    			,"fr":"Notre Atlas"
    		}
    		,"items":[
    			{
    				"title": {
    					"nunaliit_type":"localized"
    					,"en":"Default Module"
    					,"fr":"Module de Base"
    				}
    				,"href": "./index.html?module=generic.module:default"
    				,"items":[
    					{
    						"title": {
    							"nunaliit_type":"localized"
    							,"en":"Default Module"
    							,"fr":"Module de Base"
    						}
    						,"href": "./index.html?module=generic.module:default"
    					}
    				]
    			}
    			,{
    				"title": {
    					"nunaliit_type":"localized"
    					,"en":"Test Module"
    					,"fr":"Module d'Essai"
    				}
    				,"module": "module.test"
    			}
    		]
    	}
    }
    

    A navigation document contains an attribute titled "nunaliit_navigation" that is associated with an object structure, the navigation structure. The navigation structure contains three attributes:

    • nunaliit_type: (Compulsory String) Must be set with a string that contains "navigation".
    • title: (Compulsory String or Localized String) The title attribute can either be a string that contains the title of the entire atlas, or a localized string structure.
    • items: (Compulsory Array) An array of objects, where each object represents a navigation item.

    Each navigation item is a JSON object that contains some of these attributes:

    • title: (Compulsory String or Localized String) The title attribute can either be a string that contains the title of the module (item), or a localized string structure.
    • href: (Optional String) Full URL associated with the item. Either "href" or "module" must be specified, but not both. When this item is selected, the full URL is used to jump to the new "page".
    • module: (Optional String) Module identifier associated with the item. Either "href" or "module" must be specified, but not both. When this item is selected, a new URL is computed and used to find the new "page". The new URL is computed by taking the current URL and replacing the "module" parameter to the value of the module identifier.
    • items: (Optional Array) An array of navigation items. These items are sub-items to the one being defined.

    The navigation document used by an atlas can be specified in two manners:

    1. By specifying the navigation document identifier in the URL by using the parameter "navigation".
    2. By specifying the navigation document identifier in the custom service using the option "defaultNavigationIdentifier".

    Skeleton documents are a class of documents, rather than a type. They are the documents needed to structure the atlas and do not contain direct data that supports the story told by an atlas.

    During the "dump" command, it is possible to select only skeleton documents using a special option.

    The Nunaliit framework recognizes module, navigation and schema documents as skeleton documents. To have other documents selected as "skeleton", one must mark them specially by setting the "nunaliit_skeleton" boolean field. The following example demonstrates a document that would be selected as a skeleton document:

    {
    	"_id": "12345"
    	,"nunaliit_skeleton": true
    	,"content": {
    		"text": "Just a test"
    	}
    }
    

    The "atlas" design document offers a view, named "skeleton-docs", to report all skeleton documents found in a database.

    If one wishes to create a new document type using a schema, where all instances of this type are considered skeleton documents, then the "nunaliit_skeleton" flag should be added to the "create" portion of the schema, as shown here:

    {
       "_id": "myschema:placename",
       "nunaliit_type": "schema",
       "nunaliit_schema": "schema",
       "name": "mySkeletonDoc",
       "label": "My Skeleton Doc",
       "isRootSchema": true,
       "create": {
           "content": {
               "text": ""
           },
           "nunaliit_schema": "mySkeletonDoc",
           "nunaliit_skeleton": true
       },
       "brief": ...,
       "display": ...,
       "form": ...
    }
    

    CouchDB uses a special database, the _users database, to store information about each user, such as user identifier, password and roles. This information is used to perform authentication and authorization. In CouchDb, authorization is usually based on roles assigned to a user.

    The content of the _users database is accessible only to administrators. However, each user can retrieve and modify the user document associated with that user. This exception allows a user to change his or her own information, such as password, display name, etc. However, the roles associated with a user can only be modified by an administrator.

    In Nunaliit, some special fields are defined for user documents in addition to the ones generally employed by CouchDb. This is information that the Nunaliit framework uses in association with users that authenticate to the platform. This section covers the additional information added to user documents. The following JSON structure is an example for a user document:

    {
       "_id": "org.couchdb.user:jsmith",
       "_rev": "1-abcdefg",
       "display": "John Smith",
       "type": "user",
       "name": "jsmith",
       "password_sha": "5ba675d7eb549a02b812e59f53a5029299307def",
       "salt": "5601de7a7d996d487bcf0fe7ae123456",
       "roles": [
           "demo_user"
       ],
       "nunaliit_emails": [
           "[email protected]"
       ],
       "nunaliit_validated_emails": [
           "[email protected]"
       ],
       "nunaliit_options": {
           "vetterNotificationDaily": false,
           "vetterNotificationInstant": true
       },
       "nunaliit_accepted_user_agreements": {
          "demo": {
             "atlas": "demo",
             "content": "This is an agreement between you, the user, and the provider...",
             "time": 1400217619737
          }
       },
       "nunaliit_answers": {
          "postscrap": {
             "favouriteColour": "red",
             "version": 1
          }
       },
       "nunaliit_atlases": {
          "demo": {
             "name": "demo",
             "auth": true
          }
       } 
    }
    

    Some of the attributes found in the user document are specific to the Nunaliit framework:

    • display : This attribute is a string that contains the name of the user. This is the string that should be displayed when a user is reported in Nunaliit.
    • nunaliit_emails and nunaliit_validated_emails : These attributes are an arrays of strings. Each string in the arrays represent an e-mail address associated with the user. These addresses can be used by the Nunaliit platform to inform the user via e-mail. The addresses stored in the array named "nunaliit_emails" are addresses provided by the user. The addresses found in the "nunaliit_validated_emails" are the ones that have been verified by the framework. This list can not be edited by the user.
    • nunaliit_options : This attribute is covered later in this section.
    • nunaliit_accepted_user_agreements : This attribute is used to keep track of which atlases a user has accepted a user agreement. It also keeps track of the content of the user agreement that the user actually agreed to.
    • nunaliit_answers : This attribute keeps trask, on a per-atlas basis, of the answers given during the user questionnaire. The user questionnaire is an optional feature where users are asked questions the first time they authenticate with the atlas.
    • nunaliit_atlases : This attribute keeps track of which atlas instance a user has authenticated with.

    The nunaliit_options attribute is a JSON object that contains various fields:

    • vetterNotificationDaily: If this option is set (true) and that the user is a vetter (see "vetter" role), then this user is sent a daily e-mail that lists the outstanding files waiting for approval.
    • vetterNotificationInstant: If a user is a vetter (see "vetter" role), then by default the user is immediately sent a e-mail each time an approval action is required in the atlas. If this option is present and set to false, the immediate emails are not generated.
    • advanced: If this option is to true, then this user will have access to two advanced UI features: The model browser at bottom right corner and the tree view button in the doc info display panel.

    Document creation and updates are controlled by a set of rules based on user roles. Each user that authenticate to the atlas is represented by a document located in a special database, the CouchDb _users database.

    User documents contain a "roles" attribute, associated with an array of strings, where each string contains a role assigned to that user. The roles used by Nunaliit to control access are:

    • _admin: This is a role defined by CouchDb. This role allows a user to create new databases and delete them. It also allows a user to make any modification to any database.
    • administrator: This role allows a user to modify the roles associated to other users. This role should be given to any user that manages a set of atlas on a server.
    • <atlas>_administrator: This role is an "atlas role" given to a user that is ultimately responsible for the atlas: the Atlas Administrator. An Atlas Administrator is allowed to create, modify and delete and documents located on the associated atlas. The Atlas Administrator is also allowed to view all user documents and, add or remove "atlas roles" to other users. An atlas role is depicted by a name that begins with the atlas name, followed by an underscore and the specific role.
    • <atlas>_vetter: This role is associated to an atlas and defines users that are "Atlas Vetter". An Atlas Vetter is allowed to approve or deny files that were uploaded to the atlas. Until a file is approved, it is not accessible for viewing by other users.
    • <atlas>_layer_<name>: This atlas role should be given to a user that manages a layer. Documents are associated to a layer via the "nunaliit_layers" attribute. A document associated with a layer can not be modified or deleted unless the user manages this layer. The only exception is the "public" layer, which does not require a manager.
    • <atlas>_user: This atlas role should be given to "Atlas Users" and is useful only for a "locked-down" atlas. A locked-down atlas restricts the creation of new documents to Atlas Users.

    Models are software entities that can be observed. Readers familiar with the Model-View-Controller concept can readily understand the value of models within Nunaliit.

    A module document can specify a number of models to be used by Nunaliit while displaying that module. Other components, such as widgets, canvas, or other models can observe models defined this way. These connections are expressed within the module definition via the use of model identifiers.

    Models defined within a module are managing a set of documents. As such, each model report changes to the set of documents to observers. Changes usually include addtions, removals and the updating of documents. Therefore, the models defined in a module could be referred to as "document models".

    Document models come in four general categories:

    • Data Sources : Data sources are models that obtain documents from external entities. For example, a couchDbDataSource fetches documents from a back end CouchDb database.
    • Filters : Filters are models that connect to other models and restrict the list of reported document based on a filter algorithm. As filters are dependent on the state of another model, their configuration usually requires a "source model", which specifies the identifier of the model to provide a state (document set).
    • Transforms : Transforms are models that connect to other models and makes modifications to the observed documents. These modifications are then visible to obervers of the tranforms.
    • Utilities : Some models do not fall in the other categories and perform specialized operations. These models can be referred to as utlity models.

    The models defined within a module document are specified in the module structure within an array called "models". Here is an excerpt of a module document specifying models:

    {
    	...
    	"nunaliit_module": {
    		...
    		"models": [
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    					,{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "approved"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Approved"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			,{
    				"modelType":"timeFilter"
    				,"modelId": "documentsFilteredByTime"
    				,"sourceModelId": "documentModel"
    				,"range": "2000/2020"
    			}
    		]
    		...
    	}
    	...
    }
    

    Within the "models" array, a number of configuration objects are inserted, one for each model defined within the module. Each object defining a model must include the following attributes:

    • modelType : Required string. Type of model being defined.
    • modelId : Required string. Identifier used to distinguish this model from all others defined in the same module.

    Besides the two compulsory attributes (modelType and modelId), a number of other attributes can or must be specified. However, these other attributes are dependent on the model type and are imposed by each model. Refer to the documentation associated with each model type.

    A CouchDB Data Source is a model that retrieves documents from the back end CouchDB database. It selects the retrieved documents based on selectors. All documents obtained from selectors are merged into one set which are then visible to any observers (other models, widgets or canvas).

    A CouchDB Data Source is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "couchDbDataSource"
    	,"modelId": "databaseDocuments"
    	,"selectors": [
    		{
    			"type": "couchDbLayer"
    			,"name": "Public"
    			,"options": {
    				"layerId": "public"
    			}
    		}
    		,{
    			"type": "couchDbSchema"
    			,"name": "Demo Documents"
    			,"options": {
    				"schemaName": "demo_doc"
    			}
    		}
    	]
    }
    

    At least one selector must be specified for CouchDB Data Source. There are currently two type of selectors: one selector based on layers and one based on schemas.

    A couchDbView document source is a model that holds a set of documents derived from a couch database view.

    A couchDbView Document Source is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "couchDbView"
    	,”modelId”: “myModelId”
    	,”view": "myViewName"
    	,”isSiteView”: true
    	,”includeValue”: true
    	,”startKey”: “2012-01-01”
    	,”endKey”: “2018-01-01”
    }
    

    Here is an explanation of the configuration attributes:

    • modelType: Required string that must be set to "couchDbView".
    • modelId: Required string that must be set to an identifier that uniquely distinguishes this model from other ones.
    • view: Required string that must be set to the CouchDb view name.
    • isSiteView: Optional boolean. If set to true, the modelType will attempt to query a view in the site design document, otherwise it will query the view in the atlas design document.
    • includeValue: Optional boolean. If set to true, the queried view document results will be include the returned docs in a ‘__view’ property. Otherwise, only the document will be returned.
    • startKey: Optional string that if set would define the start of a range of key values to include from the queried view. For example if you had a view which included key values from “1” – “10”, and you only wanted documents from “5” – “10”, you could set the startKey value to “5”.
    • endKey: Optional string that if set would define the end of a range of key values to include from a queried view.

    A static document source is a model that virtually creates the documents as specified in its `docs` array and makes them available to other components observing the model. It does not retrieve anything from the database.

    A Static Document Source is configured in a module document under the "models" attribute. Here is an example of a configuration object that creates a model holding a single document with the _id "example1" and other attributes as specified:

    {
    	"modelType": "staticDocumentSource"
    	,"modelId": "myDocuments"
    	,"docs": [
    		{
    			"_id": "example1"
    			,"title": "Example 1"
    			,"description": "Just an example"
    		}
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "staticDocumentSource"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • docs : Optional array of documents. Specifies the set of documents that should be made available. If not specified, the initial set of documents is empty.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter is based on a time interval.

    The time filter model is associated with a time interval. Each document presented by the source model is scanned for date structures. If any of the date structure found this way intersects with the time interval set for the model, then the document is retained and passed to observers. The documents that do not match the model's time interval are removed from the managed set, or in toher words, filtered out.

    A time filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "timeFilter"
    	,"modelId": "time"
    	,"sourceModelId":"databaseDocuments"
    	,"range": "2011-05-01/-"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "timeFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes
    • range : Optional string. Defines the range that the time filter operates over.

    When a time filter is first built, the allowed interval is the full range that is set. If a range is not set, then range is computed as the union of all intervals observed in documents presented from the source model.

    The interval used by a time filter can be controlled dynamically. There are multiple ways to change the interval set for a time filter is to employ a widget. See the Timeline Widget or Date Range Widget for more details.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows documents that do not contain any date structure.

    A no-time filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "noTimeFilter"
    	,"modelId": "noTime"
    	,"sourceModelId":"databaseDocuments"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "noTimeFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows documents that are associated with a specified reference and filters the other ones out.

    A reference filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "referenceFilter"
    	,"modelId": "docsWithReference"
    	,"sourceModelId":"databaseDocuments"
    	,"reference": "062e30d43cbd3d8529f199880506085a"
    	,"references": [
    		"0cab53021976eba1d41e664792062b54"
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "schemaFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes
    • reference : Optional string. This is the identifier for a document.
    • references : Optional array of strings. Each string in the array is a document identifier.

    The filter is associated with a number of document identifiers established at configuration. Each document observed from the source model is scanned from all references If any of the references matches one of the configured document identifiers, the the document is allowed through the filter. All other observed documents are filtered out.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows through only one document, the one associated with the selected document identifier (selectedDocumentId). All other documents are filtered out.

    A single document filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "singleDocumentFilter"
    	,"modelId": "selectedDocument"
    	,"sourceModelId":"databaseDocuments"
    	,"selectedDocId": "062e30d43cbd3d8529f199880506085a"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "singleDocumentFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes
    • selectedDocId : Optional string. This is the identifier of the document that is allowed through the filter.

    The filter is associated with a single document identifier, optionally established at configuration. Only the document with an identifier matching the selected one is allowed through the filter. All other observed documents are filtered out.

    This filter can be set statically by using the attribute "selectedDocId" in the configuration. Generally, the selected document identifier is set dynamically with the use of the document selector widget.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows through only a specified list of selected document identifiers (selectedDocumentIds). All other documents are filtered out.

    A single document filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "multiDocumentFilter"
    	,"modelId": "multiDocFilter"
    	,"sourceModelId":"databaseDocuments"
    	,"selectedDocIds": [
                "fa17a8bab74388d7e6a8c5fb5d010a71",
                "fa17a8bab74388d7e6a8c5fb5d00fb25"
            ]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "multiDocumentFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes
    • selectedDocIds : Optional array of strings. This is a list of document identifiers that are allowed through the filter.

    The filter is associated with a multiple document identifiers, optionally established at configuration. Only the document with an identifier matching the selected one is allowed through the filter. All other observed documents are filtered out.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows documents that are associated with a specified layer and filters the other ones out.

    A layer filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "layerFilter2"
    	,"modelId": "DocsFilteredByLayer"
    	,"sourceModelId":"databaseDocuments"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "layerFilter2"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes

    The filter needs a widget to control which layer is allowed through the filter. See Layer Selection Widget for example.

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows documents that are associated with a specified creator and filters the other ones out.

    A document filter by creator is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "documentFilterByCreator"
    	,"modelId": "DocsFilteredByCreator"
    	,"sourceModelId":"databaseDocuments"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "documentFilterByCreator"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes

    This model is a filter, meaning that it connects to another model, a source model, and filters documents from it. In this case, the filter allows documents that are associated with a specified reference and filters the other ones out.

    A reference filter is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "filter"
    	,"modelId": "conditional"
    	,"sourceModelId":"databaseDocuments"
    	,"condition": "doc.demo_doc.description"
    	,"useBuiltInFunction": "all"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "schemaFilter"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the filter observes
    • condition : Optional string. This string is parsed and a condition is generated from it.
    • useBuiltInFunction : Optional string. Name of a built-in function.

    Only one of "condition" or "useBuiltInFunction" should be specified.

    If "condition" is used, then the filter tests each observed document against this condition. If the condition return a value that is considered "true", then the document is allowed through. Otherwise, it is filtered out.

    If the filter is configured with "useBuiltInFunction", then the specified built-in function is used for testing each document. The possible built-in funcitons are:

    • all : All documents are allowed
    • none : All documents are filtered out
    • withDates : All documents that contain a date structure are allowed
    • withoutDates : All documents that contain a date structure are filtered out
    • withoutGeometry : All documents that contain a geometry structure are filtered out

    This is a transform model, meaning that it connects to another model, a source model, and tranforms documents from it. In this case, the documents are augmented with information relating to a time interval.

    The time transform model is associated with a time interval. Each document presented by the source model is scanned for date structures. The date structures are compared to the configured time interval and the document is augmented with information on how well the document matches.

    A time transform is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "timeTransform"
    	,"modelId": "time"
    	,"sourceModelId":"databaseDocuments"
    	,"range": "2011-05-01/-"
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "timeTransform"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string. Identifier of the model that the transform observes
    • range : Optional string. Defines the range that the time transform operates over.

    When a time transformed is first built, the allowed interval is the full range that is set. If a range is not set, then range is computed as the union of all intervals observed in documents presented from the source model.

    The interval used by a time filter can be controlled dynamically. There are multiple ways to change the interval set for a time filter is to employ a widget. See the Timeline Widget or Date Range Widget for more details.

    All documents observed from the source model are made available to observers of the time transform. The work performed by the time transform is stored in a special attribute on the document, named "_n2TimeTransform". The content of the object stored at _n2TimeTransform is:

    {
    	"intersects": 
    	,"intervalSize": 
    	,"intersectionSize": 
    	,"filterIntervalSize": 
    }
    

    The information found in the _n2TimeTransform object is explained here:

    • intersects : Boolean. Set to true if any of the date structures intersects with the model's time interval. Otherwise, false.
    • intervalSize : Number. The size of the interval reported by the date structures found in the document.
    • intersectionSize : Number. The size of the inersection between the time interval reported by the document and the one examined by the time transform.
    • filterIntervalSize : Number. The size of the interval examined by the transform model.

    This is a utility model that accepts documents from multiple models and merge them into one set of managed documents. Observers of this model are given the merged perspective.

    A union model is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "union"
    	,"modelId": "allDocs"
    	,"sourceModelIds": [
    		"time"
    		,"noTime"
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "union"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelIds : Required array of strings. Identifiers of the models that the union model observes

    All documents reported by the observed models are reported to the observers of the union model.

    This is a utility model that accepts documents from multiple models and merge them into one set of managed documents. The documents are added to the managed set only if they are present in all the source models (intersection). Observers of this model are given the managed perspective.

    An intersect model is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "intersect"
    	,"modelId": "intersectionDocs"
    	,"sourceModelIds": [
    		"time"
    		,"noTime"
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "intersect"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelIds : Required array of strings. Identifiers of the models that the intersect model observes

    Only the documents reported by the all observed models are reported to the observers of the intersect model.

    This is a utility model that simplifies the process of consolidating document content. The utility accepts documents from a model and copies copyFromSchema document content into a copyToSchema document, which is stored under a '_schema-name' key in the copyToSchema. This allows atlas builders to access related document content more easily.

    A schema documents join model is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "schemaDocsJoin"
    	,"modelId": "joinedDocs"
    	,"sourceModelId": "documentModel"
    	,"joins": [
    		{
    			"copyToSchema": "myatlas_account",
    			"copyToJoinKey": "myatlas_account.bank",
    			"copyFromSchema": "myatlas_bank",
    			"copyFromJoinKey": "_id",
    			"keysToCopy": [
    				"_id",
    				"myatlas_bank.name"
    			]
    		},
    		{
    			"copyToSchema": "myatlas_account",
    			"copyToJoinKey": "myatlas_account.person",
    			"copyFromSchema": "myatlas_person",
    			"copyFromJoinKey": "_id" 
    		}
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "schemaDocsJoin"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string identifying the model that the schemaDocJoin model observes
    • joins : Required array of objects which describes the joins performed with the following attributes;
      • copyToSchema : Required string that must be set to the name of the copyToSchema.
      • copyToJoinKey : Required string that must specify the key in the copyToSchema document which will be used to match with the copyFromSchema document.
      • copyFromSchema : Required string that must be set to the name of the copyFromSchema.
      • copyFromJoinKey : Required string that must specify the key in the copyFromSchema document which will be used to match with the copyToSchema document.
      • keysToCopy : Optional array of strings which specify the keys which should be copied from the copyFromSchema to the copyToSchema. If this option is not used, the entire document's content will be copied.

    There exists a set of document filters that are based on the class SelectableDocumentFilter. All these filters are used to filter out documents from a model stream based on specific conditions. What sets those filters apart from others is that they all work with the generic filter widgets such as Single Selection Filter Widget, Multiple Selection Filter Widget and Multiple Selection Filter Drop-Down Widget.

    Since all these filters are based on the same class, they inherit a number of common options. These options are explained here:

    {
    	"modelType": <dependent on filter type>
    	,"modelId": <identifier for filter>
    	,"sourceModelId": <identifier for source model>
    	,"initialSelection": [
    		"test"
    	]
    	,"saveSelection": false
    }
    
    • initialSelection : Optional array of strings. If specified, dictates what is the first selection filtered by the model. Each string represents the identifier of an acceptable choice associated with documents that are allowed through the model.
    • saveSelection : Optional boolean. If set to true, the last selection is remembered from one session to the next.

    This is a subclass of Selectable Document Filter. Therefore all options of that class apply to this filter.

    This is a document model that implements a filter. It filters documents based on the user that created a document. Here is an example of a configuration:

    {
    	"modelType": "documentFilterByCreator"
    	,"modelId": <identifier for filter>
    	,"sourceModelId": <identifier for source model>
    }
    

    This is a subclass of Selectable Document Filter. Therefore all options of that class apply to this filter.

    This is a document model that implements a filter. It filters documents based on the layers associated with a document. Here is an example of a configuration:

    {
    	"modelType": "layerFilter2"
    	,"modelId": <identifier for filter>
    	,"sourceModelId": <identifier for source model>
    }
    

    This is a subclass of Selectable Document Filter. Therefore all options of that class apply to this filter.

    This is a document model that implements a filter. It filters documents based on the schema associated with a document. Here is an example of a configuration:

    {
    	"modelType": "schemaFilter"
    	,"modelId": <identifier for filter>
    	,"sourceModelId": <identifier for source model>
    }
    

    This is a document model constructs that combines multiple filters together. It accepts multiple filters of type Selectable Document Filter and let them work in parallel.

    A simultaneous filters model is configured in a module document under the "models" attribute. Here is an example of a configuration object:

    {
    	"modelType": "simultaneousFilters"
    	,"modelId": "documents"
    	,"sourceModelId": "sourceDocuments"
    	,"filters": [
    		{
    			"modelType":"layerFilter2"
    			,"modelId": "byLayer"
    		}
    		,{
    			"modelType":"schemaFilter"
    			,"modelId": "bySchema"
    		}
    	]
    }
    

    Here is an explanation of the configuration attributes:

    • modelType : Required string that must be set to "simultaneousFilters"
    • modelId : Required string that must be set to an identifier that uniquely distinguishes this model from other ones
    • sourceModelId : Required string that must be set to the identifier of the model that is observed.
    • filters : Required array of objects. Each object in the array represent the configuration object for a filter. The filter must be an implementation of Selectable Document Filter. The configuration objects in this array should respect the requirements of each type of filter with one exception: the "sourceModelId" attribute should not be set. There should be at least two filters defined in this array.

    Document models that are located down stream of this combination should use the identifier for the "simultaneousFilters" to receive the resulting perspective. The widgets controlling the independent filters within this construct should use directly the model identifier of the component filter.

    Nunaliit provides a large selection of model filters (as listed above), however its also possible to create your own custom model filter. This can be done by either extending an existing model filter class, or creating your own from scratch.

    There’s no single way to create a custom model filter since it’s design and functionality is only limited by the imagination of the developer. So instead, this section provides the general process that is commonly followed to create and use a custom model filter.

    General Steps:

    1. Develop the custom filter class code. Often this is written in nunaliit_custom.js, but it doesn’t need to be stored in this file. Again there is no single way to create a custom filter class, but Nunaliit does provide a large selection of different filter classes in it’s codebase to draw inspiration from. I would recommend looking at the n2.modelFilter.js module for numerous filter examples, since you may find a class which can be extended.
    2. You will also need to include a function (often stored in nunaliit_custom.js), to handle model create events. This function will be used to create a new custom filter object (based on class developed in step 1) when a dispatcher message includes a specific model type.
    3. Next, inside an atlas module document, the custom filter will need to be used. Since the filters are model driven you will need to define the model(s) in your models.json file. Inside your models.json file, one of the defined models should have a model types that matches the model type being handled by the function in step 2.
    4. (Optional) Lastly if the filter includes a widget, you will need to add it to your modules widgets.json file, and make sure the model id matches the model id specified in step 3.

    Widgets are graphical representations, which may include controls, that can be added to the rendered display in Nunaliit. Widgets can be defined in a module document using the "widgets" array. Each entry in this array is a widget definition for a particular widget, which includes configuration information.

    The following example shows a portion of the module definition which includes configuration of widgets:

    	{
    		...
    		"widgets":[
    			{
    				"widgetType": "example_abc"
    				,"containerId": "elemId"
    			}
    			,{
    				"widgetType": "example_def"
    				,"containerClass": "aDisplayClass"
    			}
    		]
    		...
    	}
    

    Every widget definition includes a compulsory attribute: "widgetType". This attribute is a string which identifies the type of widget the user is interested in having displayed by Nunaliit. There are a number of pre-defined widget types. It is also possible to have custom widget types implemented by an atlas. In all cases, if a widget definition includes a widget type which is not recognized, the definition is ignored and the expected widget is not displayed.

    A widget definition can optionally include an attribute named "containerId". This attribute is a string, which should be set to an HTML element identifier (id). This indicates to Nunaliit where the widget should be displayed. If the element associated with the identifier is not found, then the widget is not displayed.

    A widget definition can optionally include an attribute named "containerClass". This attribute is a string, which should be set to a class name found on one or multiple HTML elements. When the container class attribute is specified, the entire HTML tree is searched for elements that indicate this class name. For each found element, a widget is created and appended to the element. If no element is found with the specified class name, then the widget is not displayed.

    If a widget definition does not include a "containerId" attribute nor a "containerClass" attribute, then one instance of the widget is created at the default location, which is the content pane found in the layout.

    Besides the attributes specified above, a number of other attributes can or must be specified. However, these other attributes are dependent on the widget type and are imposed by each widget. Refer to the documentation associated with each widget type.

    The remainder of this section deals with the widgets offered by Nunaliit and the requirements for the associated configuration objects.

    The "create document" widget adds a button to the display. When the button is pressed by the user, the process of creating a new document is initiated.

    The configuration object for the create document widget is examplified here:

    	{
    		"widgetType": "createDocument"
    		,"showAsLink": false
    	}
    

    The attributes of the configuration have the following meaning:

    • widgetType : This is a required attribute and must be set to the string "createDocument"
    • showAsLink : This is an optional boolean attribute. If set, the button used to create a new document is rendered using an <a> element. Otherwise, if this attribute is false or not defined, the button is rendered using a <button> element.

    If you wish to have the create document widget displayed in the module title bar, the following definition is probably preferred:

    	{
    		"widgetType": "createDocument"
    		,"containerClass": "nunaliit_module_title"
    		,"showAsLink": true
    	}
    

    The "create document from schema" widget adds a button to the display. When the button is pressed by the user, the process of creating a new document based on the configured schema is initiated.

    Here is an example for the configuration object required for this widget:

    {
    	"widgetType": "createDocumentFromSchema"
    	,"schemaName": "demo_doc"
    	,"showAsLink": false
    	,"label": "Create a Demo Doc"
    }
    

    The attributes of the configuration have the following meaning:

    • widgetType : This is a required attribute and must be set to the string "createDocumentFromSchema"
    • schemaName : This is an required string attribute. This is the name of the schema that should be used to create a new document.
    • showAsLink : This is an optional boolean attribute. If set, the button used to create a new document is rendered using an <a> element. Otherwise, if this attribute is false or not defined, the button is rendered using a <button> element.
    • label : This is an optional string attribute. If provided, the label for the link will be set to the provided label string.

    If you wish to have this widget displayed in the module title bar, then the following configuration should be used:

    {
    	"widgetType": "createDocumentFromSchema"
    	,"schemaName": "demo_doc"
    	,"containerClass": "nunaliit_module_title"
    	,"showAsLink": true
    	,"label": "Create a Demo Doc"
    }
    

    The duplicate document widget adds a button to the display. When the button is pressed by the user, a clone of the currently selected document is created and an edit session is started on the new copy.

    Here is an example for the configuration object required for this widget:

    {
    	"widgetType": "duplicateDocument"
    	,"containerClass": "nunaliit_module_title"
    	,"duplicateAllDocs": false
    	,"duplicateOnSchemas": {
    		"demo_doc": ["demo_doc","nunaliit_layers"]
    		,"demo_media": "ALL_ATTRIBUTES"
    	}
    }
    

    The attributes of the configuration have the following meaning:

    • widgetType : This is a required attribute and must be set to the string "duplicateDocument"
    • duplicateAllDocs : This is an optional attribute. If this attribute is set to anything other than "false", then it must be set to a copy descriptor. If a copy descriptor is set, then the duplicate document widget accepts to clone any selected document.
    • duplicateOnSchemas : This is an optional Object attribute. The object should be set with keys set to schema names and values set to copy descriptors. When a schema is referenced in this manner, then the associated copy descriptor is used to create a clone of the selected document.

    Copy Descriptor A copy descriptor can be defined in one of the following ways:

    • false : If set to false, then no copy is performed.
    • "ALL_ATTRIBUTES" : If a copy descriptor is the string "ALL_ATTRIBUTES", then all attributes found in a document are copied, except for attributes that should remain unique to a document (_id, _rev, _attachments, nunaliit_attachements, nunaliit_created, nunaliit_last_updated, etc.)
    • Array of Strings : If a copy descriptor an array of strings, then each string is parsed as an object selector. Each selector is used to copy a portion of the source document to the cloned one. Object selectors are parsed from a series of dotted keys. For examples, "demo_doc", "demo_doc.title", etc.

    The "document selector" widget adds a drop-down list to the display. The list contains a set of documents and the widget enables a user to select one of them.

    This widget requires two document models to operate correctly:

    1. list model : a document model used to establish the list used in the drop-down
    2. single document filter : a filter for a single document. Usually, an instance of single document filter

    Here is an example for the configuration object required for this widget:

    	{
    		"widgetType": "documentSelector"
    		,"label": "Select one..."
    		,"filterModelId": "selectedDocument"
    		,"filterParameterId": "selectedDocumentId"
    		,"listModelId": "documents"
    		,"listLabelSelectors": [
    			"demo_doc.name"
    		]
    	}
    

    The attributes of the configuration have the following meaning:

    • widgetType : This is a required attribute and must be set to the string "documentSelector"
    • label : This is an optional localized string attribute. This is the label used on the first drop-down option. This option is used when no document is selected.
    • filterModelId : This is a required string attribute. This is the identifier for a single document filter, or another model that behaves similarly.
    • filterParameterId : This is an optional string attribute. If provided, it specifies the name of the model parameter used to set the selected document identifier. If not specified, it is assumed to be 'selectedDocumentId', since this is the parameter name used by the single document filter.
    • listModelId : This is a required string attribute. This is the identifier for a document model. The drop-down list is built from the documents observed from this model. For each document reported by this model, an entry is added to the drop-down list. The list is updated with changes observed in this model.
    • listLabelSelectors : This is an optional array of strings. Each string represents an object selector. Selectors are tested in declared order against each document used to create the list. The first valid value obtained from the selectors is used as text in the list to represent the document. If selectors are ommitted, or if a valid value can not be derived in this process, then the brief is used to represent the document in the list.

    For the document selector widget to function properly, it should be configured with two related models, where the single document filter is fed by model used to generate the list. Here is an example using the excerpt of a module document:

    "nunaliit_module": {
    	"models": [
    		{
    			"_comment": "Load documents from database"
    			,"modelType":"couchDb"
    			,"modelId": "couchDb"
    			,"selectors": [
    				...
    			]
    		}
    		,{
    			"_comment": "Filter category documents"
    			,"modelType":"schemaFilter"
    			,"modelId": "categories"
    			,"sourceModelId": "couchDb"
    			,"schemaName": "demo_category"
    		}
    		,{
    			"_comment": "Select a single category"
    			,"modelType":"singleDocumentFilter"
    			,"modelId": "selectedCategory"
    			,"sourceModelId": "categories"
    		}
    	]
    	,"widgets": [
    		{
    	        "widgetType": "documentSelector"
    	        ,"label": "Categories"
    	        ,"filterModelId": "selectedCategory"
    	        ,"listModelId": "categories"
    	     }
         ]
    }
    

    This widget displays a panel where different CouchDbDataSource selectors can be enable/disable. Disabling a selector renders the documents managed by that selector invisible to the observers. In general, it hides the information from these documents from the map or canvas displayed to the user.

    The CouchDbSelector widget must be used in conjunction with a CouchDbDataSource model. Here is an example of a module document where a CouchDbDataSource model and a CouchDbSelector widget are in use:

    {
    	...
    	"nunaliit_module": {
    		"models": [
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    					,{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "approved"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Approved"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			...
    		]
    		,"widgets": [
    			{
    				"widgetType": "couchDbSelector"
    				,"sourceModelId": "documentModel"
    			}
    			...
    		]
    		...
    	}
    	...
    }
    

    The configuration object for defining a CouchDbSelector widget can contain the following attributes:

    • widgetType : Required string. Must be set to "couchDbSelector"
    • sourceModelId : Required string. Must be set to the identifier for a CouchDbDataSource model.

    Export Widget

    This widget accepts a source model and will bring up the Export By Script dialog found in the Tools section.

    {
    	"widgetType": "exportWidget",
    	"containerId": "myContainerWhereTheWidgetShouldGo",
    	"sourceModelId": "mySourceModelId",
    	"label": "Export"
    }
    
    • widgetType: A string. It must be "exportWidget" for the default behaviour.
    • containerId: A string. The "id" of an HTML element on the page that this element should render within.
    • sourceModelId: A string. The model ID of the model that contains state.
    • label: Optional, a string. The text that should be shown on the created widget element. Defaults to "Export" if not provided.

    Model Export Widget

    This widget accepts a source model and will provide the state of the model for download as JSON. This will immediately start a download when clicked.

    {
    	"widgetType": "modelExportWidget",
    	"containerId": "myContainerWhereTheWidgetShouldGo",
    	"sourceModelId": "mySourceModelId",
    	"label": "Download Model State"
    }
    
    • widgetType: A string. It must be "modelExportWidget" for the default behaviour.
    • containerId: A string. The "id" of an HTML element on the page that this element should render within.
    • sourceModelId: A string. The model ID of the model that contains state.
    • label: Optional, a string. The text that should be shown on the created widget element. Defaults to "Export Data" if not provided.

    If this widget is extended, it will also require "dispatchService" and "moduleDisplay" (from nunaliit_custom). If this widget is extended, note that there are pre and post download functions, plus a shapeState function that can be implemented.

    The legend widget is used to display a legend of the styles, each with an associated description, in use by a canvas. For a legend widget to work properly, three details must be configured appropriately:

    1. The legend widget must be defined in the module's widget definitions
    2. The associated canvas must be named
    3. The styles to be displayed in the legend must have a label assigned.

    Here is an example of a legend widget definition:

    {
    	...
    	"nunaliit_module": {
    		,"widgets": [
    			{
    				"widgetType": "legendWidget"
    				,"containerClass": "nunaliit_content"
    				,"sourceCanvasName": "The Map"
    			}
    		]
    		...
    	}
    	...
    }
    

    In the previous example, the attributes "widgetType" and "containerClass" are similar to any other widget. What is particular about this widget is the association to a specific canvas via a named specified using "sourceCanvasName". Optionally a user can also define a "labels" array of strings to include in the legend and what order they should appear in.

    To continue this example, the following excerpt could be used in the same module, where the legend widget is associated with a map canvas:

    {
    	...
    	"nunaliit_module": {
    		"map": {
    			"canvasName": "The Map"
    			,"styles":[
    				{
    					"condition": "true"
    					,"label" :"Observation"
    					,"normal": {
    		                		"strokeColor": "#FF0093",
    		                		"fillOpacity": 0.2
    					}
    				}
    				,{
    					"condition": "cluster.length > 1"
    					,"label" :"Cluster"
    					,"normal": {
    						"fillColor": "#ee9900",
    						"fontColor": "#ffffff",
    						"pointRadius": 14,
    						"fontFamily": "Courier New, monospace",
    						"fontSize": "14px",
    						"fontWeight": "bold",
    						"graphicName": "square",
    						"label": "=cluster.length",
    						"labelAlign": "cm",
    						"labelOutlineColor": "#000000",
    						"labelOutlineWidth": 3,
    						"labelXOffset": "0",
    						"labelYOffset": "0",
    						"strokeColor": "#ffffff",
    						"strokeWidth":2 
    					}
    				}
    			]
    			...
    		}
    		...
    	}
    	...
    }
    

    The second part of this example shows how the map is given a canvas name identical to the one specified in the legend widget definition. Also, the two styles in use are given labels (Observation and Cluster). Styles without a label are not shown in the legend.

    Filterable Legend Widget

    The filterable legend widget allows for model filtering control within a legend for a map canvas.

    Note that the logic of the filterable legend widget is contained mostly in the upstream filter model (sourceModelId) where it will decide what documents to allow through and what labels show up.

    A sample configuration follows:

    {
    	"widgetType": "filterableLegendWidgetWithGraphic",
    	"name": "My Filterable Legend Widget",
    	"containerId": "htmlElementId",
    	"sourceModelId": "myFilteringModel",
    	"sourceCanvasName": "My Map",
    	"allLabel": "All Selections",
    	"graphicType": "none"
    }
    
    • widgetType: A string. It must be "filterableLegendWidgetWithGraphic" for the default behaviour.
    • name: A string.
    • containerId: A string. The "id" of an HTML element on the page that this element should start rendering in reference to.
    • sourceModelId: A string. The "modelId" of the model that is providing documents to the filterable legend widget. This model should be a filter model. This will also be the source of documents for the state of the graphic to draw from, if the widget is specified to have a graphic.
    • sourceCanvasName: A string. This is the name of the corresponding canvas that this widget will be drawn on.
    • allLabel: A string. The name of the label that toggles all entries in the legend widget on or off.
    • graphicType: A string.
      • "none", for no graphic to appear beside the legend
      • "custom", where the user must override/extend the existing widget and define behaviour for drawing the graphic themselves. The function drawCustom must be implemented.

    If graphicType is not "none", then prepareGraphicData must be implemented.

    Advanced: The filterable legend widget contains a property this.preloadCallback, which is available to receive a function reference to load in data before loading in other data/graphics.

    When Nunaliit is deployed, it generally includes a navigation bar based on the navigation document defined for the atlas. However, it is possible to use the navigaiton widget to insert a navigation bar anywhere in the module.

    The configuration object for a navigation widget can contain the following attribute:

    • widgetType : Required string. Must be set to "navigation"

    For positioning this widget, use "containerClass" and "containerId".

    The wait widget displays a representation of the tasks waiting for completion. This is a way to convey to the user the reasons for delays. This is particularly useful for modules where a large amount of data needs to be loaded.

    Here is an example of the configuration object for this widget:

    	{
    		"widgetType": "wait"
    		,"showNames": true
    	}
    

    The attributes of the configuration have the following meaning:

    • widgetType : Required string. This is must be set to "wait"
    • showNames : Optional boolean. If set, the widget displays the names of the tasks waiting for completion.

    The "polar stereographic projection selector" widget adds a graphic representation of the north to enable a user to select one of the polar stereographic projections used in a module. When specifying this widget, the graphic is displayed only if a number of conditions are met:

    1. Multiple base layers must be specified in the map
    2. Each base layer must specify a different projection
    3. Each base layer must specify a polar stereographic projection

    Here is an example of the configuration object for this widget:

    	{
    		"widgetType": "polarStereographicProjectionSelector"
    		,"imageLocation": "nunaliit2/images/arctic.svg"
    		,"imageRotation": 0
    	}
    

    The attributes of the configuration have the following meaning:

    • widgetType : This is a required attribute and must be set to the string "polarStereographicProjectionSelector"
    • imageLocation : This is an optional string attribute. If set, it specifies the relative path to the image used to represent the arctic.
    • imageRotation : This is an optional numbaer attribute. If set, it specifies the angle, in degrees, to the prime meridian in the image.

    A timeline is dependent on one of two models: TimeFilter or TimeTransform. In fact, those two models analyze the incoming documents for date structures and adjust the range of time according to what is reported.

    The timeline widget has two functions. First, it reports the range currently available in the model. Second, it actuates the user intended interval. In this second function, the timeline widget changes the interval reported by the model.

    Here is an example of a timeline widget along with a TimeFilter model:

    {
    	...
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			,{
    				"modelType":"timeFilter"
    				,"modelId": "timeFilter"
    				,"sourceModelId": "documentModel"
    			}
    		]
    		,"widgets":[
    			{
    				"widgetType": "timeline"
    				,"sourceModelId": "timeFilter"
    			}
    		]
    		...
    	}
    	...
    }
    

    The technology built to display a map, mapAndControl, is much older than some of the models and widgets. Therefore, to use timelines with maps, a bridge is required.

    Mainly, once a map is defined, a model to retrieve the same documents as the map is required. Then, a timeTransform model is needed along with a bridge between the timeTransform model and the map.

    Here is an example:

    {
    	...
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			,{
    				"modelType":"timeTransform"
    				,"modelId": "timeTransform"
    				,"sourceModelId": "documentModel"
    			}
    		]
    		,"widgets":[
    			{
    				"widgetType": "timeline"
    				,"sourceModelId": "timeTransform"
    			}
    			,{
    				"widgetType": "timeTransformToMapAndControlBridge"
    				,"sourceModelId": "timeTransform"
    			}
    		]
    		...
    	}
    	...
    }
    

    The configuration object for defining a timeTransformToMapAndControlBridge widget can contain the following attributes:

    • widgetType : Required string. Must be set to "timeTransformToMapAndControlBridge"
    • sourceModelId : Required string. Must be set to the identifier of the associated time transform model.

    A date range widget can be used independently or with one of two models: TimeFilter or TimeTransform. In fact, those two models analyze the incoming documents for date structures and adjust the range of time according to what is reported.

    Here is an example of a date range widget along with a TimeFilter model:

    {
    	...
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			,{
    				"modelType":"timeFilter"
    				,"modelId": "timeFilter"
    				,"sourceModelId": "documentModel"
    			}
    		]
    		,"widgets":[
    			{
    				"widgetType": "dateRange",
    				"containerId": "module_title_bar",
    				"sourceModelId": "timefilter"
    			}
    		]
    		...
    	}
    	...
    }
    

    The technology built to display a map, mapAndControl, is much older than some of the models and widgets. Therefore, to use a date range widget with maps, a bridge is required.

    Mainly, once a map is defined, a model to retrieve the same documents as the map is required. Then, a timeTransform model is needed along with a bridge between the timeTransform model and the map.

    Here is an example:

    {
    	...
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    				]
    			}
    			,{
    				"modelType":"timeTransform"
    				,"modelId": "timeTransform"
    				,"sourceModelId": "documentModel"
    			}
    		]
    		,"widgets":[
    			{
    				"widgetType": "dateRange",
    				"containerId": "module_title_bar",
    				"sourceModelId": "timeTransform"
    			}
    			,{
    				"widgetType": "timeTransformToMapAndControlBridge"
    				,"sourceModelId": "timeTransform"
    			}
    		]
    		...
    	}
    	...
    }
    

    The configuration object for defining a timeTransformToMapAndControlBridge widget can contain the following attributes:

    • widgetType : Required string. Must be set to "timeTransformToMapAndControlBridge"
    • sourceModelId : Required string. Must be set to the identifier of the associated time transform model.

    The previous example shows how a timeline and a time tranform model can be used to control the features displayed on a legacy map (the "map" attribute of a module document).

    However, any model can be connected to a map via the modelToMapAndControlBridge widget. When this widget is used, features displayed on the map, which are not also reported by the associated model, are made invisible. The net effect is to leave on the map only features associated with documents also reported by the model.

    While the "timeTransformToMapAndControlBridge" is specialized for time transform models, the "modelToMapAndControlBridge" can be used with any model.

    Here is an excerpt of a module document using the "modelToMapAndControlBridge" to control the features displayed on a map:

    {
    	"nunaliit_module": {
    		"models": [
    			{
    				"modelType":"couchDbDataSource"
    				,"modelId": "documentModel"
    				,"selectors": [
    					{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "public"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Public"
    						}
    						,"visibility": true
    					}
    					,{
    						"type":"couchDbLayer"
    						,"options": {
    							"layerId": "approved"
    						}
    						,"name": {
    							"nunaliit_type": "localized"
    							,"en": "Approved"
    						}
    						,"visibility": true
    					}
    				]
    			}
    		]
    		,"widgets": [
    			{
    				"widgetType": "couchDbSelector"
    				,"sourceModelId": "documentModel"
    			}
    			,{
    				"widgetType": "modelToMapAndControlBridge"
    				,"sourceModelId": "documentModel"
    			}
    		]
    		,"map": {
    			"coordinates": {
    				"initialBounds": [-75.72,45.41,-75.67,45.43]
    				,"autoInitialBounds": false
    			}
    			,"backgrounds": [
    				{ 
    					"name": "Google Satellite"
    					,"type": "Google Maps"
    					,"options": {
    						"type": "SATELLITE"
    						, "numZoomLevels": 19 
    					}
    				}
    			]
    			,"overlays": [
    				{
    					"id": "approved"
    					,"name": {
    						"nunaliit_type":"localized"
    						,"en":"Approved Layer"
    						,"fr":"Couche Approuvée"
    					}
    					,"type": "couchdb"
    					,"options": {
    						"layerName": "approved"
    					}
    					,"featurePopupDelayMs": 0
    				}
    				,{
    					"id": "public"
    					,"name": {
    						"nunaliit_type":"localized"
    						,"en":"Public"
    						,"fr":"Couche Publique"
    					}
    					,"type": "couchdb"
    					,"options": {
    						"layerName": "public"
    					}
    					,"featurePopupDelayMs": 0
    				}
    			]
    			,"toggleClick": true
    			,"enableWheelZoom": true
    		}
    		...
    	}
    	...
    }
    

    The configuration object for defining a modelToMapAndControlBridge widget can contain the following attributes:

    • widgetType : Required string. Must be set to "modelToMapAndControlBridge"
    • sourceModelId : Required string. Must be set to the identifier of the source model.

    When a layer filter document model is in use (see Layer Filter), a widget is required to control the which layers are filtered. This widget is used to control the layer filter.

    The layer selection widget can be specified, like any other widgets, by adding a widget configuration object in the "widgets" array of the module document, as shown here:

    {
    	"widgetType": "layerSelectionWidget"
    	,"sourceModelId":"filterDocsByLayer"
        ,"containerClass": "nunaliit_module_title"
    }
    

    Here is an explanation of the attributes found in the layer selection configuration object:

    • widgetType : Required string that must be set to 'layerSelectionWidget'.
    • sourceModelId : Required string that must be set to model identifier for the layer filter model.
    • containerClass : Optional string. Specified the class of the element where the widget should be displayed.

    There are a number of document filters that are based on the class SelectableDocumentFilter. The single filter selection widget is a generic widget that allows a user to modify the filter, one selection at a time.

    The single filter selection widget can be specified, like any other widgets, by adding a widget configuration object in the "widgets" array of the module document. Since the document filter of type 'documentFilterByCreator' is a subclass of SelectableDocumentFilter, the following example demonstrates how the single filter selection widget can be used with one of those filters:

    {
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"documentFilterByCreator"
    				,"modelId": "filterByCreator"
    				,"sourceModelId": "couchDb"
    			}
    			...
    		]
    		,"widgets":[
    			{
    				"widgetType": "singleFilterSelectionWidget"
    				,"sourceModelId":"filterByCreator"
    			    ,"containerClass": "nunaliit_module_title"
    			}
    		]
    		...
    	}
    	...
    }
    

    Here is an explanation of the attributes found in the layer selection configuration object:

    • widgetType : Required string that must be set to 'singleFilterSelectionWidget'.
    • sourceModelId : Required string that must be set to model identifier for the controlled document filter model.
    • containerClass : Optional string. Specified the class of the element where the widget should be displayed.
    • allChoicesLabel : Optional string. Specifies the label that should be used to select all choices.
    • noChoiceLabel : Optional string. Specifies the label that should be used to deselect all choices.
    • tooltip : Optional string. Specifies the tooltip text shown when a user hovers the cursor over the widget.

    There are a number of document filters that are based on the class SelectableDocumentFilter. The multiple filter selection widget is a generic widget that allows a user to modify the filter, allowing to control multiple selections simultaneously.

    The multiple filter selection widget can be specified, like any other widgets, by adding a widget configuration object in the "widgets" array of the module document. Since the document filter of type 'documentFilterByCreator' is a subclass of SelectableDocumentFilter, the following example demonstrates how the multiple filter selection widget can be used with one of those filters:

    {
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"documentFilterByCreator"
    				,"modelId": "filterByCreator"
    				,"sourceModelId": "couchDb"
    			}
    			...
    		]
    		,"widgets":[
    			{
    				"widgetType": "multiFilterSelectionWidget"
    				,"sourceModelId":"filterByCreator"
    			    ,"containerClass": "nunaliit_content"
    			}
    		]
    		...
    	}
    	...
    }
    

    Here is an explanation of the attributes found in the layer selection configuration object:

    • widgetType : Required string that must be set to 'singleFilterSelectionWidget'.
    • sourceModelId : Required string that must be set to model identifier for the controlled document filter model.
    • containerClass : Optional string. Specified the class of the element where the widget should be displayed.
    • allChoicesLabel : Optional string. Specifies the label that should be used to select all choices.
    • tooltip : Optional string. Specifies the tooltip text shown when a user hovers the cursor over the widget.

    There are a number of document filters that are based on the class SelectableDocumentFilter. The multiple filter selection widget is a generic widget that allows a user to modify the filter, allowing to control multiple selections simultaneously using a drop-down list.

    The multiple filter selection widget can be specified, like any other widgets, by adding a widget configuration object in the "widgets" array of the module document. Since the document filter of type 'documentFilterByCreator' is a subclass of SelectableDocumentFilter, the following example demonstrates how the multiple filter selection widget can be used with one of those filters:

    {
    	"nunaliit_module":{
    		"models":[
    			{
    				"modelType":"documentFilterByCreator"
    				,"modelId": "filterByCreator"
    				,"sourceModelId": "couchDb"
    			}
    			...
    		]
    		,"widgets":[
    			{
    				"widgetType": "multiFilterSelectionDropDownWidget"
    				,"sourceModelId":"filterByCreator"
    			    ,"containerClass": "nunaliit_module_title"
    			}
    		]
    		...
    	}
    	...
    }
    

    Here is an explanation of the attributes found in the layer selection configuration object:

    • widgetType : Required string that must be set to 'multiFilterSelectionDropDownWidget'.
    • sourceModelId : Required string that must be set to model identifier for the controlled document filter model.
    • containerClass : Optional string. Specified the class of the element where the widget should be displayed.
    • label : Optional string. Specifies the label that should be used on the button that toggles the display of the drop-down list.
    • showAsLink : Optional boolean. If true, the button is displayed as a link.
    • allChoicesLabel : Optional string. Specifies the label that should be used to select all choices.
    • tooltip : Optional string. Specifies the tooltip text shown when a user hovers the cursor over the widget.

    Many sites feature a help splash page the first time a site is visited. Nunaliit offers this function via the use of a widget. If the splash page widget is configured for a module, then the first time this module is visited, the splash page is shown to the user.

    In fact, the splash page can be made of multiple screens (pages) that a user can click through before dismissing.

    The splash page widget can be specified, like any other widgets, by adding a widget configuration object in the "widgets" array of the module document, as shown here:

    {
    	"widgetType": "splashPage"
    	,"title": {
    		"nunaliit_type":"localized"
    		,"en": "Welcome"
    		,"fr": "Bienvenue"
    	}
    	,"dialogWidth": 800
    	,"version": 1
    	,"cookieName": "NunaliitWelcomeSplashDontShow"
    	,"pages": [
    		{
                    	"html": {
    				"nunaliit_type": "localized"
    				,"en": "

    Hello!

    " ,"fr": "

    Salut!

    " } } ] }

    Here is an explanation of the attributes found in the splash page configurtion object:

    • widgetType : Required string that must be set to 'splashPage'.
    • title : Optional string that specifies the title of the splash dialog. If not set, defaults to "Welcome". This can be a localized string or a regular string.
    • dialogWidth : Optional number. It specifies the width of the dialog.
    • version : Optional number. This is the version of the splash page. If the splash page is later changed, this version can be increased. Users that have not yet seen this version of the splash page will be shown the page, again, at the next visit.
    • cookieName : Optional string. This is the name of the cookie, installed in the user's browser, to keep track of whether the splash page has been seen, or not.
    • pages : Optional array of strings or array of localized strings. This array contains the content of the pages displayed to in the splash screen.

    The pages for the splash screen can be defined in the module document. However, this might not be the best approach:

    • It means that the pages must be copied in all modules
    • It means that the content of the pages is fixed and can not be derived from information found the in the database.

    Another approach is to set the pages for the splash screen in the nunaliit_custom.js file. This approach ensures that all modules using the splash screen are synchronized. Also, the pages for the splash screen can be generated from information found in the database.

    Here is an example where the splash pages are set from the nunaliit_custom.js file. This example uses the dispatcher event 'splashWidgetSetPages':

    var g_dispatchService = null;

    //++++++++++++++++++++++++++++++++++++++++++++++ function handleStart(m){ g_dispatchService.send(DH,{ type: 'splashWidgetSetPages' ,pages: [ { "nunaliit_type": "localized" ,"en": "This is the help page" ,"fr": "C'est votre page d'aide" } ] ,version: 2 ,cookieName: 'myAtlasSplash' }); };

    //++++++++++++++++++++++++++++++++++++++++++++++ window.nunaliit_custom.configuration = function(config, callback){

    // Dispatch service
    if( config.directory.dispatchService ){
    	g_dispatchService = config.directory.dispatchService;
    	
    	// Handler called when atlas starts
    	g_dispatchService.register('obi','start',handleStart);
    };
    
    callback();
    

    };

    A widget container is a widget that holds other widgets. Since a widget is a visual element, its placement can be an essential attribute that an atlas designer wishes to control.

    A widget container is itself a widget. But, it functions primarily as a holder for one or multiple other widgets. In general, a widget container will be defined with a number of widget configurations, representing each of the contained widget.

    Here is an excerpt of a module document where a widget container is in use:

    {
    	"nunaliit_module": {
    		,"widgets": [
    			{
    				"widgetType": "aWidgetContainerType"
    				,"containerClass": "nunaliit_module_title"
    				,"widgets": [
    					{
    						"widgetType": "legendWidget"
    						,"sourceCanvasName": "The Map"
    					}
    				]
    			}
    			...
    		]
    		...
    	}
    	...
    }
    

    From the above example, there are a number of points to note:

    • The container widget definition emcompasses defintion(s) for other widget (internal widgets)
    • Internal widget definitions do not specify the "containerClass" attribute, or any other placement attribute

    This is a widget container used to add a button to a canvas. When the button is opened, a sheet is shown with the content of other widgets. The internal widgets are selected from the definition.

    As an example, here is an excerpt of a module document where a collapisble widget container is used to old a legend:

    {
    	"nunaliit_module": {
    		,"widgets": [
    			{
    		        "widgetType": "collapsibleWidgetContainer"
    		        ,"containerClass": "nunaliit_content"
    		        ,"addClasses": [ "abcdef" ]
    		        ,"initiallyOpened": false
    		        ,"widgets": [
    				    {
    				        "widgetType": "legendWidget"
    				        ,"sourceCanvasName": "The Map"
    				    }
    		        ]
    			}
    			...
    		]
    		...
    	}
    	...
    }
    

    The definition of the collapsible widget container is similar to that of other widget containers. Here are the attributes that differ from others:

    • initiallyOpened : Boolean. If set, the widget will initialze opened, as opposed to closed. The default behaviour is to initialize closed.
    • addClasses : Array of strings. Class names to be added to the top element of the widget. This is useful to allow a designer to change the theme of a particular widget.

    A canvas is a graphical representation, which may include controls, that is the main visual vehicle for information. If a canvas is specified, it takes the place of the geographical map.

    A canvas can be defined in a module document using the "canvas" key. If a canvas is specified, then the map key can not be included. Both "canvas" and "map" are mutually exclusive.

    The following module document demonstrates the inclusion of a fake canvas:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		,"introduction": {
    			"type": "html"
    			,"content": "<div class=\"intro\"><b>Welcome to a Nunaliit atlas served from CouchDB</b><br/><a href=\"./tools/index.html\">Nunaliit Tools</a></div>"
    		}
    		,"canvas": {
    			"canvasType": "typeOfCanvas"
    		}
    		,"display":{
    			"defaultSchemaName":"genericDoc" 
    		}
    		,"edit":{
    			"defaultSchemaName": "genericDoc"
    			,"newDocumentSchemaNames": "ALL_SCHEMAS" 
    		}
    		,"search":{
    			"disabled":false
    			,"constraint": null
    		}
    		,"widgets":[
    			{
    				"widgetType": "createDocument"
    				,"showAsLink": true
    				,"containerId": "module_title_bar"
    			}
    		]
    	}
    }
    

    The content of the canvas structure is dependent on the type of canvas. However, one key must always be present: "canvasType". This is a string that describes the kind of canvas desired. All other attributes in the canvas structure are interpreted based on the value of "canvasType".

    The "customSvg" canvas allows a module designer to replace the map with a custom SVG image used to interact with the underlying data. When this canvas type is specified, a SVG file must be provided. When the module is displayed, the specified SVG file is loaded and presented to the user. There are means to capture user interactions with the image and affect the behaviour of the atlas accordingly. Furthermore, it is possible to change the styling of the image based on changes in the atlas.

    The customSvg canvas is designed to import SVG files created by other tools (Inkscape, OmniGraffle) and integrate the image with the behaviour of the atlas.

    A customSvg canvas is specified in a module document by using the canvas type "customSvg", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas": {
    			"canvasType": "customSvg"
    			,"svgAttachment": "image.svg"
    			,"cssAttachment": "style.css"
    			,"elemIdToDocId":{
    				"g3044":"2f6cf81060bfa0bcb5214a4e68403233"
    				,"g3037":"2f6cf81060bfa0bcb5214a4e6840a227"
    			}
    			,"unselectIds":[
    				"g3013"
    			]
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a customSvg canvas structure are defined here:

    • canvasType : This is a required string and must be set to "customSvg"
    • svgAttachment : This is a required string. It represents the attachment name for the SVG file. The SVG file must be an attachment to the module document.
    • cssAttachment : This is an optional attribute. If specified, it must be a string. It represents the attachment name for a CSS file. The content of the CSS file is loaded and included within the SVG file.
    • elemIdToDocId : This is an optional attribute. If specified, it must be an object which is a dictionary of strings. This dictionary is used to match portions of the SVG image with documents found in the atlas. The keys represent identifiers of SVG elements found in the file. The values represent identifiers for documents that are associated with the portions of the SVG image.
    • unselectIds : This is an optional attribute. If specified, it must be an array of strings. Each string in the array represents a SVG element identifier that should be associated with performing a de-selection.

    When the module is displayed, the SVG file is loaded and inserted in the canvas portion of the viewport. Then, the content of the SVG image is scanned and portions are associated with documents in the database. The associations between SVG elements and documents are bi-directional. Mouse events with SVG elements are captured and the appropriate events for the associated document are sent via the dispatcher. On the other hand, when events are received for documents that are associated with SVG elements, those SVG elements are modified with classes that represent the interactions.

    The are two methods for associating SVG elements with database documents: using in-line attributes and using a dictionary. If someone is creating a SVG file manually (without the use of a tool), the in-line attributes approach is probably the easier. In this approach, to associate a SVG element to a document, one must add the class "n2canvas_linkDocId" to the element and an attribute named "n2-doc-id" that specifies the document identifier. Here is an example of a SVG file where in-line attributes are used:

    <?xml version="1.0"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 236 120" class="n2canvas_unselect" >

    &lt;style type="text/css" &gt;
    	 &lt;![CDATA[
    		.normal {
    			fill: lime;
    			stroke: black;
    			stroke-width: 1;
    		}
    
    		.n2canvas_hovered {
    			fill: blue;
    		}
    		
    		.n2canvas_selected {
    			stroke: yellow;
    		}
    	]]&gt;
    &lt;/style&gt;
    
    &lt;rect 
    	x="14" 
    	y="10" 
    	width="100" 
    	height="100"
    	class="normal n2canvas_linkDocId"
    	n2-doc-id="0cab53021976eba1d41e66479206aa97"
    	/&gt;
    
    &lt;circle 
    	cx="150" 
    	cy="55"
    	r="50" 
    	class="normal n2canvas_linkDocId"
    	n2-doc-id="327130f3851bfc0e0c5e025c7b04639c"
    	/&gt;
    

    </svg>

    If the SVG file is produced by a tool, it might be inconvenient augmenting the resulting file with in-line attributes since these attributes might be removed by the tool if an edit of the image is required. In that case, an association dictionary is preferred. To specify an association dictionary, a structure named "elemdIdToDocId" must be added to the definition of the customSvg canvas. The keys of the dictionary represent identifiers of elements found in the SVG element. The values represent identifiers for documents found in the database. Here is an example of a SVG file:

    <?xml version="1.0"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 236 120" class="n2canvas_unselect" >

    &lt;style type="text/css" &gt;
    	 &lt;![CDATA[
    		.normal {
    			fill: lime;
    			stroke: black;
    			stroke-width: 1;
    		}
    
    		.n2canvas_hovered {
    			fill: blue;
    		}
    		
    		.n2canvas_selected {
    			stroke: yellow;
    		}
    	]]&gt;
    &lt;/style&gt;
    
    &lt;rect 
    	id="g1001"
    	x="14" 
    	y="10" 
    	width="100" 
    	height="100"
    	class="normal"
    	/&gt;
    
    &lt;circle 
    	id="g1002"
    	cx="150" 
    	cy="55"
    	r="50" 
    	class="normal"
    	/&gt;
    

    </svg>

    To continue this example, here is the canvas structure that includes an association dictionary:
    {
    	"canvasType": "customSvg"
    	,"svgAttachment": "image.svg"
    	,"elemIdToDocId":{
    		"g1001":"0cab53021976eba1d41e66479206aa97"
    		,"g1002":"327130f3851bfc0e0c5e025c7b04639c"
    	}
    }
    

    To provide visual cues to the user of the atlas, the styles of the SVG elements must be modified to represent the interactions. To achieve this, the Nunaliit framework changes the classes specified on the SVG elements to represent the state of the associated document. The following classes are added or removed:

    • n2canvas_hovered : This class is added to a SVG element when the associated document is in focus. When the document goes out of focus, this class is removed.
    • n2canvas_selected : This class is added to a SVG element when the associated document is selected. When the document becomes unselected, this class is removed.
    • n2canvas_selectedHovered : This class is added to a SVG element when the associated document is both selected and in focus. When either conditions becomes false, this class is removed. When this class is present, both classes "n2canvas_hovered" and "n2canvas_selected" are also present.

    Since the only changes made to the SVG elements as a result of user interactions are the additions and removals of classes, the visual changes must be defined using a cascading style sheet (CSS). There are two main approaches to inserting CSS in the SVG file. The first is to edit the file and insert a style block. However, this is not pratical if using an external tool to modify the file, since the CSS block might not be retained. The second approach is to store the CSS content in a file attached to the module and specify the name of the attachment in the customSvg canvas definition using the cssAttachment attribute. If a CSS file is specified, then the content is loaded and inserting in a style block within the SVG content.

    Example of a file containing CSS content is shown here:

    .n2canvas_hovered {
    	fill: blue;
    }
    

    .n2canvas_selected { stroke: yellow; }

    There are instances when clicking on a particular SVG element should un-select all documents currently selected. For example, if the background of the image is clicked, the desired behaviour might be to return to the state previous to any document selected. In the approach using in-line attributes, this effect can be accomplished by adding the class 'n2canvas_unselect' to the elements providing the de-selection. In the approach using an association dictionary, this effect is achieved by listing the SVG identifiers in the section 'unselectIds' of the canvas definition.

    The "customHtml" canvas shows all the relationships between a number of selected documents. The relations are shown in a sortable tabular format that allows a user to perform analysis. Documents are selected based on schemas.

    A custom html canvas is specified in a module document by using the canvas type "customHtml", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas": {
    			"canvasType": "customHtml"
        		        ,"htmlAttachment": "website.html"
    			,"cssAttachment": "styling.css"
    	 		,"elemIdToDocId": {
    				"g1001":"0cab53021976eba1d41e66479206aa97"
    				,"g1002":"327130f3851bfc0e0c5e025c7b04639c"
                            }
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a customHtml canvas structure are defined here:

    • canvasType : This is a required string and must be set to "customHtml"
    • htmlAttachment : This is a required string. It represents the attachment name for the HTML file. The HTML file must be an attachment to the module document.
    • cssAttachment : This is an optional attribute. If specified, it must be a string. It represents the attachment name for a CSS file. The content of the CSS file is loaded and included within the HTML file.
    • elemIdToDocId : This is an optional attribute. If specified, it must be an object which is a dictionary of strings. This dictionary is used to match portions of the HTML with documents found in the atlas. The keys represent identifiers of HTML elements found in the file. The values represent identifiers for documents that are associated with the portions of the HTML.

    The "referenceBrowser" canvas shows all the relationships between a number of selected documents. The relations are shown in a sortable tabular format that allows a user to perform analysis. Documents are selected based on schemas.

    A reference browser canvas is specified in a module document by using the canvas type "referenceBrowser", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas": {
    			"canvasType": "referenceBrowser"
    			,"schemaNames": [
    				"demo_doc"
    				,"demo_media"
    			]
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a referenceBrowser canvas structure are defined here:

    • canvasType : This is a required string and must be set to "referenceBrowser"
    • schemaNames : This is a required array of strings. It represents the schemas selected. All documents associated with these schemas are loaded in the browser.

    The table canvas displays data in an HTML table consisting of elements produced by an element generators, which can be in the format of a row or a cell.

    Row elements represent a row in a table which contain cells that represent the values under each heading. A table canvas is specified in a module document by using the canvas type "table", as demonstrated below:

    {
            "nunaliit_module": {
                    "title":"Generic Atlas"
                    ...
                    ,"canvas": {
                            "canvasType": "table"
                    	,"elementGeneratorType": "nameOfElementGenerator"
    			,"elementGeneratorOptions": {
    			}
                            ,"showRowCount": true
    			,"sourceModelId": "idOfSourceModel"
    			,"styleRules":[
    			]
                    }
                    ...
            }
    }
    

    The attributes that should be specified in a table canvas structure are defined here:

    • canvasType: This is a required string and must be set to "table"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the table cells are created from. The string should point towards a ModelID that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the table canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the element generator.
    • showRowCount: Optional property. When set to true, a row counter is displayed above the table.
    • styleRyles: Optional property containing an array of style rules relating to the table. See example below:
    [
    	{
    		"condition":"true"
    		,"selected":{
    			"font-weight":"bold"
    		}
    		,"normal":{
    			"background-color":"none" 
    			,"font-weight":"normal"
    		}
    		,"hovered":{
    			"background-color":"#ccffcc"
    		}
    	}
    ]
    

    Format of Row Elements:

    {
    	id:   (Unique identifier for this element)
    	cells: {
    		"heading1": {
    			value: "value1"
    			,sortValue: "value1"
    			,exportValue: "value1"
    			,type: "string"
    		}
    		,"heading2": {
    			value: "123456789"
    			,display: function($td, cell, element){ ... }
    			,sortValue: "ABC"
    			,exportValue: "ABC"
    			,type: "reference"
    		}
    	}
    }
    

    Format of Cell Elements:

    {
    	id:   (Unique identifier for this element)
    	,row:  (Identifier of the row)
    	,column:  (Identifier of the colmun)
    	,value: "value1"
    	,display: function($td, cell, element){ ... }
    	,sortValue: "value1"
    	,exportValue: "value1"
    	,type: "string"  ("string" or "reference")
    }
    

    The attribute for each cell is described here:

    • value: Required. This is the value of the cell. It can be a string, a number, a boolean, etc. If it is a function, it will be called with the following signature: function(cell, row). Note that depending on the type of elements provided by the element generator, it is possible that the element is the cell or the row.
    • sortValue: Optional. Value to be used when sorting the column based on this value. If not specified, the value is used.
    • exportValue: Optional. Value to be used when table is exported. If not specified, the value is used. If this is a function, the following signature is used: function(cell, row). Note that depending on the type of elements provided by the element generator, it is possible that the element is the cell or the row.
    • type: Optional. Type of the value. If not specified, it assumed to be 'string'. Supported types are 'string' and 'reference'.
    • display: Optional. Function to be used when displaying this value to the user. This replaces the default behaviour and allows the element generator to supply any display function. When used, the following signature is used: function($td, cell, row).

    Table Header Element:

    One element can be provided to specify the columns and the order in which they should be displayed. This element has the following format:

    {
    	isHeader: true,
    	columns: [
    		{
    			name: 'heading1'
    			,label: 'First'
    			,title: 'Explanation of First'
    		}
    		,{
    			name: 'heading2'
    			,label: {
    				nunaliit_type: 'localized'
    				,en: 'Second'
    			}
    			,title: {
    				nunaliit_type: 'localized'
    				,en: 'Explanation of Second'
    			}
    		}
    	]
    }
    

    The "grid" canvas displays a collections of cells representing selected documents.

    A grid canvas is specified in a module document by using the canvas type "grid", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas": {
    			"canvasType": "grid"
    			,"sourceModelId": "modelId"
    			,"elementGeneratorType": "elementGeneratorName"
    			,"elementGeneratorOptions": {
    			}
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a grid canvas structure are defined here:

    • canvasType: This is a required string and must be set to "grid"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the grid cells are created from. The string should point towards a ModelID that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the grid canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.
    • multiSelect: Optional property, false by default. If true then multiple documents can be selected by clicking on them.

    Grid cell documents are commonly generated with an ElementGenerator with elements requiring the following properties to be use in a grid canvas:

    • element.id : String. Unique identifier for this element (typically the element id is the id of an existing document)
    • element.sort : Optional String. Used to sort the cells in the grid.
    • element.fragments : Map of fragments that make this element. Gives a list of documents used to make up this element.
    • element.gridImage : An object which contains two properties (doc and attachment) to provide the required information for the insertMediaView show service inserting an image into a grid cell.
      • element.gridImage.doc : Provides the doc id of the image
      • element.gridImage.attachement : Provides the filename of the attachment

    The vertical timeline canvas presents a chronological vertical listing of selected documents.

    A vertical timeline canvas is specified in a module document by using the canvas type "vertical_timeline", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas": {
    			"canvasType": "vertical_timeline"
    			,"sourceModelId": "modelId"
    		}
    		...
    	}
    }
    

    The attributes that can be specified in a vertical timeline canvas structure are defined here:

    • canvasType: This is a required string and must be set to "vertical_timeline"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the vertical timeline cells are created from. The string should point towards a ModelID that you define in the models.json for this module.
    • ascendingSortOrder: This is an optional boolean value which can be set to either true (default) or false. If set to true, the timeline order is ascending, otherwise it's descending.
    • displayIndex: This is an optional boolean value which can be set to either true (default) or false. If set to true, the side vertical timeline index is shown, if set to false the index is excluded from the canvas.
    • labelDateFormat: This is an optional string which allows the user to define the format for the date labels in the canvas (e.g. “MMMM DD, YYYY”). Date formatting is provided using the luxon.js library, and available formatting options can be found on the luxon documentation page.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the vertical timeline canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.

    If vertical timeline items are generated with an ElementGenerator, each element should have the following properties;

    • element.id : String. Unique identifier for this element (typically the element id is the id of an existing document)
    • element.sort : String. Used to sort the items in the vertical timeline canvas.
    • element.label : String. Used to specify the label for each item in the vertical timeline canvas.
    • element.fragments : Map of fragments that make this element. Gives a list of documents used to make up this element.

    The force graph canvas displays nodes and links using a d3js (v 3.4.13) force graph layout.

    A force graph canvas is specified in a module document by using the canvas type "forceGraph", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		,"canvas":{
    			"canvasType": "forceGraph"
    			,"elementGeneratorType": "nameOfElementGenerator"
    			,"elementGeneratorOptions": {
    			}
    			,"force":{
    				"gravity": 0.5
    				,"friction": 0.6
    				,"charge": -150
    
    		}
    		,"sourceModelId": "documentModel"
    		,"background": {
    			"fill": "#333333"
    		}
    		,"popup":{
    			"delay": 2000
    		}
    		,toggleSelection: true
    		,"styleRules":[
    		]
    	}
    	...
    }
    

    }

    The attributes that can be specified in a force graph canvas structure are defined here:

    • canvasType: This is a required string and must be set to "forceGraph"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the canvas nodes and links. The string should point towards a modelId that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the force graph canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.
    • force: Optional property containing overrides of various d3js force graph parameters. Default parameter values include;
      • gravity: 0.1
      • friction: 0.9
      • theta: 0.8
      • charge: -30
      • chargeDistance: null
      • linkDistance: 30
      • linkStrength: 1
    • background: Optional style properties for the canvas background
    • popup: Optional property used to define the popup delay in milliseconds
    • toggleSelection: Optional property used to turn on/off toggleSelection. If toggleSelection is set to true, a user is able to select a node when clicking on it, and unselect it when they click on it again.
    • styleRyles: Optional property containing an array of style rules relating to the force graph. See Table Canvas documentation for example.
    • nodeLabelOffsetX: Positive or negative number indicating how much the label of the node should be offset horizontally relative to the node
    • nodeLabelOffsetY: Positive or negative number indicating how much the label of the node should be offset vertically relative to the node

    Force graph elements are commonly generated with an ElementGenerator with elements requiring the following properties to be use in a force graph canvas:

    • element.id: Required. Identifier that uniquely identifies the node or the link
    • element.isNode: Set if this element is a node
    • element.isLink: Set if this element is a link between two nodes
    • element.source: Required for links. Node that is one end of the link
    • element.target: Required for links. Node that is the other end of the link

    The following attributes are added to the elements by the force graph canvas

    • x: X position. Added only to nodes.
    • y: Y position. Added only to nodes.

    The radial canvas displays nodes and links using a d3js (v 3.4.13) radial graph layout.

    A radial canvas is specified in a module document by using the canvas type "radial", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		{
    			"canvasType": "radial"
    			,"elementGeneratorType": "nameOfElementGenerator"
    			,"elementGeneratorOptions": {
    			}
    			,"sourceModelId": "documentModel"
    			,"background": {
    				"fill": "#333333"
    			}
    			,toggleSelection: true
    			,"styleRules":[
    			]
    		}
    		...
    	}
    }
    

    The attributes that can be specified in a radial canvas structure are defined here:

    • canvasType: This is a required string and must be set to "radial"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the canvas nodes and links. The string should point towards a modelId that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the radial canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.
    • background: Optional style properties for the canvas background
    • toggleSelection: Optional property used to turn on/off toggleSelection. If toggleSelection is set to true, a user is able to select a node when clicking on it, and unselect it when they click on it again.
    • styleRyles: Optional property containing an array of style rules relating to the radial canvas. See Table Canvas documentation for example.

    The radial tree canvas displays nodes and links using a d3js (v 3.4.13) cluster layout of radial lines.

    A radial tree canvas is specified in a module document by using the canvas type "radialTree", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		{
    			"canvasType": "radialTree"
    			,"elementGeneratorType": "nameOfElementGenerator"
    			,"sourceModelId": "documentModel"
    			,"background": {
    				"color": "#333333"
    			}
    			,"toggleSelection": true
    			,"styleRules":[
    			...
    			]
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a radial canvas structure are defined here:

    • canvasType: This is a required string and must be set to "radial"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the canvas nodes and links. The string should point towards a modelId that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the radial canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.
    • background: Optional style properties for the canvas background
    • toggleSelection: Optional property used to turn on/off toggleSelection. If toggleSelection is set to true, a user is able to select a node when clicking on it, and unselect it when they click on it again.
    • styleRyles: Optional property containing an array of style rules relating to the radial canvas. See Table Canvas documentation for example.

    If radial tree items are generated with an ElementGenerator, each element should have the following properties;

    • element.id: String. Unique identifier for this element (typically the element id is the id of an existing document)
    • element.parentId: String. If this element is part of a tree, id of parent element, otherwise set to null.
    • element.isNode: Boolean. Set true if this is a node element (part of tree).
    • element.isLink: Boolean. Set true if this is a link element (lines between nodes).
    • element.source: Object. Element which is at the beginning of the line (only links).
    • element.target: Object. Element which is at the end of the line (only links).
    • element.sortValue: String. Value used to sort the elements between themselves.

    The collapsible radial tree canvas displays nodes and links using d3js (v 3.4.13).

    A collapsible radial tree canvas is specified in a module document by using the canvas type "collapsibleRadialTree", as demonstrated below:

    {
    	"nunaliit_module": {
    		"title":"Generic Atlas"
    		...
    		{
    			"canvasType": "collapsibleRadialTree"
    			,"elementGeneratorType": "nameOfElementGenerator"
    			,"sourceModelId": "documentModel"
    			,"background": {
    				"color": "#333333"
    			}
    			,"toggleSelection": true
    			,"radius": 300
    			,"originalAngle": 0
    			,"collapseBeforeExpand": true
    			,"transitionDuration": 250
    			,"styleRules":[
    			...
    			]
    		}
    		...
    	}
    }
    

    The attributes that should be specified in a radial canvas structure are defined here:

    • canvasType: This is a required string and must be set to "radial"
    • sourceModelId: This is a required string providing the id of a model. The model represents the collection of documents in which elements representing the canvas nodes and links. The string should point towards a modelId that you define in the models.json for this module.
    • elementGeneratorType: This is a required string providing the name of the ElementGenerator which prepares the model documents for the radial canvas. This is the name of the ElementGenerator, whose behaviour will be defined by the following elementGeneratorOptions
    • elementGeneratorOptions: Optional property containing possible options for the ElementGenerator.
    • background: Optional style properties for the canvas background
    • toggleSelection: Optional property used to turn on/off toggleSelection. If toggleSelection is set to true, a user is able to select a node when clicking on it, and unselect it when they click on it again.
    • radius: Optional property used to set the radius size of canvas graph. Default radius is 300 pixels in size.
    • originalAngle: Optional property used to define the starting angle for radial canvas. 0 is the default angle.
    • collapseBeforeExpand: Optional property used to turn on/off collapsing before expanding a selection. Default is true
    • transitionDuration: Optional property used to set the duration of transition. Default is 250 ms.
    • styleRyles: Optional property containing an array of style rules relating to the radial canvas. See Table Canvas documentation for example.

    If collapsible radial tree items are generated with an ElementGenerator, each element should have the following properties;

    • element.id: String. Unique identifier for this element (typically the element id is the id of an existing document)
    • element.parentId: String. If this element is part of a tree, id of parent element, otherwise set to null.
    • element.isNode: Boolean. Set true if this is a node element (part of tree).
    • element.isLink: Boolean. Set true if this is a link element (lines between nodes).
    • element.source: Object. Element which is at the beginning of the line (only links).
    • element.target: Object. Element which is at the end of the line (only links).
    • element.sortValue: String. Value used to sort the elements between themselves.
    • element.group: String. Optional. Grouping nodes.
    • element.showArc: Boolean. Optional. If set, an arc is drawn around this node.

    Description: An instance of the ElementGenerator class is responsible for taking a set of documents from a data source and producing a set of graphic elements for a canvas.

    The instance of the ElementGenerator is informed when the data set is changed and propagates those changes, in terms of elements, to the canvas. It is also responsible to keep track of user intents on the source documents, translate these intents in terms of elements and inform the canvas.

    In reverse, an instance of the ElementGenerator is informed by the canvas when the user clicks or hovers an element. It translates these intents from elements to documents and informs the remainder of the system by sending the appropriate events through the dispatcher.

    The algorithm used by instances of the ElementGenerator is to break up documents into fragments and then combine the fragments into elements.

    Elements provided to the canvas have the following attributes:

    • id: String. Unique identifier for this element fragments: Array of Objects. Used internally by ElementGenerator
    • n2_selected: Boolean. Set if element is selected
    • n2_selectedIntent: String. Selection intent, if one is specified.
    • n2_hovered: Boolean. Set if element is in focus
    • n2_hoveredIntent: String. Focus intent, if one is specified.
    • n2_found: Boolean. Set if the element is found.
    • n2_intent: String. Effective intention (selected or hovered), if one is specified.
    • n2_doc: Optional Object. Document, if only one is associated with the element.

    Conversion of Documents to Elements: doc 1---1 context 1---N fragment N---N element

    • doc: Must have the following attributes:
      • _id
    • context: Must have the following attributes:
      • n2_id : document identifier for the supporting document
      • n2_doc : supporting document
      • fragments : list of fragments generated from supporting document
    • fragment: Must have the following attributes:
      • id : unique identifier for fragment
      • n2_id : document identifier for the supporting document
      • n2_doc : supporting document
      • context : context associated with supporting document
      • elements : element map, by element id, for each element associated with the fragment
    • element: Must have the following attributes:
      • id : unique identifier for element
      • fragments : map of fragments, by fragment id, making up element

    Elements are typically used with Nunaliit canvases, however each canvas has specific element property requirements. For example the Grid Canvas requires elements to have a property called gridImage which specifies the document information for an image document. Consequently it’s common practice to extend the existing $n2.canvasElementGenerator.ElementGenerator class to create a custom ElementGenerator instance which generates elements with the required properties for the canvas type.

    Utilities are logical processes that modifies the stock operations provided by Nunaliit, that can be added to a module document using the "utilities" array. Each entry in this array is a utility definition for a particular utility, which includes configuration information.

    The following example shows a portion of the module definition which includes configuration of utilities:

    	{
    		...
    		"utilities":[
    			{
    				"utilityType": "example_abc"
    				,"abcOption1": "option"
    			}
    			,{
    				"utilityType": "example_def"
    				,"defOption1": "option"
    			}
    		]
    		...
    	}
    

    Every utility definition includes a compulsory attribute: "utilityType". This attribute is a string which identifies the type of utility the module designer is interested in having included. There are a number of pre-defined utility types. It is also possible to have custom utility types implemented by an atlas. In all cases, if a utility definition includes a utility type which is not recognized, the definition is ignored.

    Besides the attribute specified above, a number of other attributes can or must be specified. However, these other attributes are dependent on the utility type and are imposed by the design of the utility. Refer to the documentation associated with each utility type.

    The remainder of this section deals with the utilities offered by Nunaliit and the requirements for the associated configuration objects.

    The "assign layer on document creation" utility adds a process that adds a particular layer identifier to a newly created document.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "assignLayerOnDocumentCreation"
    		,"layerId": "test"
    		,"onlyWithGeometries": false
    	}
    
    	{
    		"utilityType": "assignLayerOnDocumentCreation"
    		,"layerId": ["test", "test2", "test3"]
    		,"onlyWithGeometries": false
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "assignLayerOnDocumentCreation".
    • layerId : This is a required string or array of strings attribute. It must be set to the identifier of the layer that should be added to a document upon creation.
    • onlyWithGeometries : This is an optional boolean attribute. If set, the layer identifier is set on the new document only if a geometry is associated with the document.

    The "filter monitor" utility observes the selected choices from a specified document filter. On given state, it performs a specified action.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "filterMonitor"
    		,"sourceModelId": "testModel"
    		,"onAllSelected": {
    			"actionType": "setFilterSelection"
    			,"modelId": "targetModel"
    			,"selection": ["choiceId1"]
    		}
    		,"onNotAllSelected": {
    			...
    		}
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "filterMonitor".
    • sourceModelId : This is a required string attribute. It must be set to the identifier of the filter model that is monitored.
    • onAllSelected : This is an optional object attribute. The object is an instance definition for an action. If specified, this action is executed when the monitored filter selects "all".
    • onNotAllSelected : This is an optional object attribute. The object is an instance definition for an action. If specified, this action is executed when the monitored filter performs a selection other than "all".

    This utility selects one or multiple documents instead of displaying a regular module introduction. This happens when a module is first loaded or when a user unselects the currently displayed document.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "selectDocumentOnModuleIntroduction"
    		,"docId": "1234abcd"
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "selectDocumentOnModuleIntroduction".
    • docId : This is a string attribute. It must be set to the identifier of the document that should be selected as module introduction.
    • docIds : This attribute is an array of strings. It must contain the document identifiers that should be selected as module introduction.

    This utility selects a document when an appropriate change is observed in a filter document model. In particular, the document model must be one of the types compatible with SelectableDocumentFilter (for example, one of its subclasses such as "documentFilterByCreator" or "layerFilter2").

    This utility registers with the specified filter model and observes changes in selection. When the appropriate selection is observed, it sends an event to select the associated document. The selection can be one or multiple simultaneous choices.

    For example, if a model (identified as filterDocsByLayer) was filtering documents based on layers, and that among the expected layers were "public" and "approved", the following configuration object for this utility could be used:

        {
    	    "utilityType": "selectDocumentOnFilterChange"
    	    ,"sourceModelId": "filterDocsByLayer"
    	    ,"performSelectedEvent": false
    	    ,"selectionMap": {
    	    	"123": "public"
    	    	,"456": ["public","approved"]
    	    }
    	}
    

    In this example, if the layer "public" was selected, then the document "123" would be selected. On the other hand, if both layers "public" and "approved" were selected, then document "456" would be selected.

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "selectDocumentOnFilterChange".
    • sourceModelId : This is a required string attribute. It represents the identifier for the observed model.
    • performSelectedEvent : This attribute is an optional boolean. If set, the utility uses event "selected", instead of "userSelect", as a means to select the document.
    • selectionMap : This attribute is required. It is a map where keys are identifiers of documents to be selected. The value associated with each key can be either a string or an array of strings. The value or values represent the choices to be selected in the observed model.

    This utility changes the behaviour that happens when a cluster is clicked on a map canvas. This utility zooms the map.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "mapClusterClickToZoom"
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "mapClusterClickToZoom".

    This utility changes the behaviour that happens when a cluster is clicked on a map canvas. When this utility is specified, then all documents associated with the cluster become selected.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "mapClusterClickToMultiSelect"
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "mapClusterClickToMultiSelect"

    This utility changes the behaviour that happens when a cluster is clicked on a map canvas. When this utility is specified and a map cluster is clicked, one of two possible outcomes is performed: the map might be zoomed, or all the associated document might be selected.

    The selected behaviour is dependent on the number of documents associated with the cluster and the map resoultion.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "mapClusterClickHandler"
    		,"minimumCountToZoom": 6
    		,"minimumResolutionToZoom": 5
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "mapClusterClickToMultiSelect".
    • minimumCountToZoom : This is an optional number attribute. If a cluster is associated with a number of documents equal or greater to this number, then a zoom of the map is performed.
    • minimumResolutionToZoom : This is an optional number attribute. If the resolution of the map is smaller or equal to this number, then a multi-selection is performed instead of a zoom.

    This utility automatically changes the viewport to fit all the features on a module's map (or new ol5 canvas), whenever its source model updates. This happens after all the updated features finish loading.

    An example of the configuration object for this utility is shown here:

    	{
    		"utilityType": "mapAutoZoom"
    		,"includes" : [
                          [-75.72,45.41,-75.67,45.43]
                     ]
    	}
    

    The attributes of the configuration have the following meaning:

    • utilityType : This is a required string attribute and must be set to "mapAutoZoom".
    • includes : This is an optional array attribute. Each entry in this array is a bounding box of the format [minX, minY, maxX, maxY] in decimal degrees (EPSG:4326) (similar to the autoInitialBounds map option). If this attribute is provided, the mapAutoZoom functionality will always ensure that the areas specified will remain included in the area rendered. An example use could be to anchor the map to a community so the user can retain context as features are shown nearby.

    In module definitions, a number of well defined constructs are built by providing the correct definitions. For example, constructs such as models, widgets and utilities can be added to a module in a "plug-in" fashion. These definitions specify the behaviour demonstrated to the end user of the Nunaliit application, at run-time.

    Some of these constructs need further specialization. For example, there is a module utility that monitors a model filter and performs an action on a given state (Filter Monitor). This utility is configured with an action to perform when a given state is observed.

    The Nunaliit framework provides a means to define a configuration object in JSON and create a generic instance within the application. This facility makes it possible to extend the behaviour of the main constructs.

    Unlike the other constructs, instances created this way do not have a set interface. Instead, the interface required by instances is defined by the construct that uses them.

    All instance definitions follow this format:

    	{
    		"type": "type of instance"
    		...
    	}
    

    Each instance definition includes a compulsory attribute: "type". This attribute is a string which identifies the type of instance the module designer is interested in having included.

    The "set filter selection action" instance is an action that sets the current selection on a document filter model defined in the module.

    An example of the configuration object for this instance is shown here:

    	{
    		"type": "setFilterSelection"
    		,"modelId": "test"
    		,"selection": [ "a", "b" ]
    		,"selectAll": true
    	}
    

    The attributes of the configuration have the following meaning:

    • type : This is a required string attribute and must be set to "setFilterSelection".
    • modelId : This is a required string attribute. It must be set to the identifier of a document filter model. This is the model that is modified with a selection when the action is executed.
    • selection : This is an optional array of strings. This represents the selection that is set to the associated filter.
    • selectAll : This is an optional boolean. If set, then when the action is executed, all available choices on the filter are selected.

    Customization refers to the ability of changing the behaviour or appearance of the web client application, within the Nunaliit Framework. Nunaliit applications are built by creating widgets and combining them to provide the services required. This complex task is usually performed by a simplified configuration script.

    All tools provided by the Nunaliit Framework, including the default atlas page, explicitely include a Javascript file named "nunaliit_custom.js" located in the top most directory of the atlas (root). If an atlas designer is interested in influencing the configuration script, before any tool has an opporunity to start, it can be accomplished by adding custom code to a file named "nunaliit_custom.js" in the "htdocs" sub-directory of the atlas directory. If this file exists at run-time, it is loaded and all functions exposed by this file are available to the web application.

    In general, the structure of the "nunaliit_custom.js" file is as follows:

    ;(function($n2){
    

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};

    // This is the a custom function that can be installed and give opportunity // for an atlas to configure certain components before modules are displayed // @param config Object that contains all configuration // @param callback Function to be called when custom code is complete. This // allows asynchronous calls to be performed in the custom // code. window.nunaliit_custom.configuration = function(config, callback){

    // --- This is where custom code would be inserted ---
    
    // Done with configuration, perform the offered callback
    // to complete configuration and initialize application.
    callback();
    

    };

    })(nunaliit2);

    As shown in the example above, the callback function given in argument must be called at the end of the customization. This approach is used to enable custom code to perform asynchronous calls. If the callback function is not called, then the configuration script will not complete and the application does not start appropriately.

    The configuration object provided as argument is described in a previous section and is fully populated by the time the custom configuration function is called.

    All web pages created by an atlas designer should include "nunaliit_custom.js" to provide the custom code an opportunity to run.

    A number of Javascript files are already included in the HTML files provided by Nunaliit. If other Javascript files are needed, the function nunaliit2.scripts.loadCustomScripts() can be used to load them.

    The loadCustomScripts() function is preferred to including a script element in the index.html file since it includes the Javascript file not only in the main page, but also in all page that use the Nunaliit initialization function. This includes the tool pages.

    Another advantage of the loadCustomScripts() function is that the running of the configuration function (window.nunaliit_custom.configuration()) is delayed until all custom scripts are loaded.

    Here is an example where the d3 library is loaded using the loadCustomScripts() function.

    ;(function($n2){
    "use strict";
    

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};

    ...

    window.nunaliit_custom.configuration = function(config, callback){

    ...
    
    // Done with configuration
    callback();
    

    };

    $n2.scripts.loadCustomScripts([ 'js-external/d3/d3.min.js' ]);

    })(nunaliit2);

    A certain amount of customizations in the Nunaliit framework are transacted via the custom service. The custom service can record options from the atlas creator and offer them to the various components in the Nunaliit framework to modify the default behaviour.

    Options can be specified to the custom service in during the configuration of the Nunaliit framework. The best place to set options with the custom service is in the file "nunaliit_custom.js", located in the "htdocs" directory of the atlas.

    It is possible to install a configuration function which is called after the Nunaliit services are instantiated, but before the application main line is performed. This configuration function is given a configuration object, which contains all the services. The following excerpt of Javascript should be inserted into the file named "nunaliit_custom.js". It shows how to set an option with the custom service.

    window.nunaliit_custom.configuration = function(config, callback){
    
    var customService = null;
    if( config && config.directory ){
    	customService = config.directory.customService;
    };
    
    if( customService ){
    	customService.setOption('disableCreateUserButton',true);
    };
    
    // Done with configuration
    callback();
    

    };

    The options are set on the custom service by using the customService.setOption() function. The first argument to this function is a string, which is the name of the option. The second argument could be of any type, and is dependent on the option.

    Here is a list of custom options currently supported by Nunaliit:

    • authWidgetLoginLabel
    • authWidgetLogoutLabel
    • authWidgetWelcomeLabel
    • couchDbCachingDisabled
    • couchDbCachingEnabled
    • disableCreateUserButton
    • disableDocumentHoverFunctions
    • displayBriefInRelatedInfo
    • displayDocumentInfoFunction
    • displayFilterFactory
    • displayFormat
    • displayOnlyRelatedSchemas
    • displayPostProcessFunctions
    • displayRelatedInfoFunction
    • displayRelatedInfoProcess
    • displaySortFunction
    • displaySuppressLeaveConfirmation
    • editorEnableAddFile
    • editorOnRefreshFunctions
    • editorSuppressFormView
    • editorSuppressSlideView
    • editorSuppressTreeView
    • mapClusterClickCallback
    • mapFeaturePopupCallback
    • mapFeaturePopupCallback
    • mapGetStyleFunctionForLayer
    • mapLabelCancelEdit
    • mapLabelEditFeature
    • moduleDisplayIntroFunction
    • relatedDocumentDiscoveryProcess
    • restrictAddRelatedButtonToLoggedIn
    • restrictReplyButtonToLoggedIn
    • searchButtonText
    • searchWidgetText
    • simplifiedGeometriesSizeLimit
    • simplifiedGeometriesTimeLimit
    • soundPlayMultipleOnFocus

    Some of these options are discussed, below.

    When Nunaliit first create an atlas, it offers default module and navigation documents. When the atlas is displayed, it displays the default module and navigation. To modifiy this behaviour and have the atlas display different modules and navigation, by default, options on the custom service can be used:

    1. defaultModuleIdentifier : This custom service option is a string and represents the document identifier of the default module. By default, it is 'module.demo'.
    2. defaultNavigationIdentifier : This custom service option is a string and represents the document identifier of the default navigation document. By default, it is 'navigation.demo'.

    When Nunaliit displays an atlas page and a document is selected, a display pane is used to given information about this document. By default, Nunaliit uses the "classic" display pane, where the document is shown and below it, the related documents.

    There is another format of display pane where the documents are represented by tiles and animation is used to move the tiles has selection is changed. This format is known as the "Tiled Display Format".

    There are two ways to specify that the tiled display is desired: atlas-wide or module-specific.

    The custom service is used to order Nunaliit to use the tiled display format across the entire atlas. In the nunaliit_custom.js file, add the following line:

    	customService.setOption('displayFormat','tiled');
    

    If the tiled display process is to be used with a set of modules (not atlas wide), then each module document must be modified accordingly. Here is an exaample:

    {
    	...
    	"nunaliit_module":{
    		...
    		"display":{
    			"type": "tiled"
    		}
    		...
    	}
    	...
    }
    

    The tiled display format, by default, shows the related documents in tiles surrounding the selected one. However, the tiled display format is very flexible and can be configured using other options in the custom service:

    1. relatedDocumentDiscoveryProcess : This is an object that implements the interface for requesting related documents. This controls what documents are deemed related to the selected one.
    2. displayDocumentInfoFunction : This is a function that, given a set of document identifiers, returns an information structure on each document found. This information structure gives information which is relevant to sorting and filtering.
    3. displaySortFunction : This is a function that, given an array of information structures, the array in a sorted order. This dictates the order in which the related information is presented to the user.
    4. displayFilterFactory : This is an object that implements an interface where a new filter can be created. A filter is used to minimize the number of tiles displayed and allow a user to quickly narrow the number of displayed document to the relevant ones. A filter operates on the information structures returned by the document info function.

    When Nunaliit displays an atlas page and a document is selected, a display pane is used to given information about this document. By default, Nunaliit displays selected documents and its related documents on a static panel on the right.

    Alternatively, a module can allow the side panel to function as a sliding panel, which can free up screen real estate, when a document is not selected or being edited.

    To enable the sliding side panel add the "sliding" key to the module display.json, and set it the option to true:

    {
    	...
    	"nunaliit_module":{
    		...
    		"display":{
    			"sliding": true
    		}
    		...
    	}
    	...
    }
    

    The tag assignment display is used to assign tags to multiple selected documents. It should be used with the grid display with multi select. Here is an example config:

    {
    	...
    	"nunaliit_module":{
    		...
    		"display":{
    			"type": "tagAssignment"
    		}
    		...
    	}
    	...
    }
    

    When using the "classic" display pane, it generally displays related information along with the currently selected document. "Related" information means documents that are linked by the selected document and documents that link to the selected document.

    There is a customization that allows a replacment of the related information with comment thread. In this case, instead of showing all related information, only comments about the currently selected document are show in a discussion thread. When this customization is selected, user are prompted to enter comments instead of adding related information.

    The comment customization is appropriate for those atlas that are designed to gather feedback on locations as opposed to letting the user discover relationships between entities.

    To enable the comment customization, the following script fragment should be added to the nunaliit_custom.js file, after the dispatchService has been defined:

    		config.directory.schemaRepository.getSchema({
    			name: 'demo_comment'
    			,onSuccess: function(schema){
    				if( config.directory.commentService ){
    					config.directory.commentService.setCommentSchema(schema);
    				};
    			}
    		});	
    		var commentProcess = new $n2.couchDisplay.CommentRelatedInfo({
    			dispatchService: config.directory.dispatchService
    		});
    		customService.setOption('displayRelatedInfoProcess',commentProcess);
    

    Prior to nunaliit2-2.2.9, this script fragment should be used:

    	var commentProcess = new $n2.couchDisplay.CommentRelatedInfo({
    		schema: null
    		,dispatchService: config.directory.dispatchService
    	});
    	config.directory.schemaRepository.getSchema({
    		name: 'demo_comment'
    		,onSuccess: function(schema){
    			commentProcess.commentSchema = schema;
    		}
    	});	
    	customService.setOption('displayRelatedInfoProcess',commentProcess);
    

    In the example above, the comment display process uses an atlas-specific schema for comments named 'demo_comment'. Each atlas must define its own schema to support comments.

    By default, when a user is prompted for authentication, a button offering to create a new user is made available in the login dialog. It is possible to use the custom service to remove this button.

    	customService.setOption('disableCreateUserButton',true);
    

    By default, hover functions are active in the document tile and display ribbon layouts. They can be disabled with the following custom service setting.

    	customService.setOption('disableDocumentHoverFunctions', true);
    

    By default, when a document is displayed, a button titled "Add Related Info" allows a user to add related information to the database. If a user is not logged in when the button is pressed, then the user is prompted to login.

    Having this button displayed all the time encourages new user to submit data. However, in some cases (locked-down atlas), having this button displayed all the time is confusing since most visitors to the atlas do not have the ability of posting any information. To display the "add related" button only to visitors that are logged in, the following option to the custom service can be used:

    	customService.setOption('restrictAddRelatedButtonToLoggedIn',true);
    

    When a user moves the mouse over a feature found on the map, a pop-up bubble is displayed over the map with brief information about the feature.

    It is possible to customize the information provided in the pop-up bubble by providing a special function via the custom service:

    	customService.setOption('mapFeaturePopupCallback',popUpFunction);
    

    The expected interface for the popUpFunction is:

    	function popUpFunction(opts_){
    		var opts = $n2.extend({
    			feature: null
    			,layerInfo: null
    			,onSuccess: function(html){}
    			,onError: function(err){}
    		},opts_);
    
    	...
    };
    

    When the pop-up function is called, it is provided with a map feature and a layerInfo object. The map feature contains the information displayed on the map. The layerInfo object contains information about the layer where the feature is displayed.

    The function is expected to compute a HTML fragment to be displayed in the pop-up bubble. When done computing the HTML fragment, the function providing in 'onSuccess' parameter should be called with the fragment returned as a text string. If no HTML should be display for the particular feature, 'onSuccess' should be called with a parameter of null.

    The default function for pop-up display can be found here: nunaliit2.mapAndControls.DefaultPopupHtmlFunction(). Also, a special function used to suppress the pop-up behaviour is declared at: nunaliit2.mapAndControls.SuppressPopupHtmlFunction(). Therefore, to suppress all pop-ups:

    	customService.setOption('mapFeaturePopupCallback',nunaliit2.mapAndControls.SuppressPopupHtmlFunction);
    

    Here is an example where a pop-up function shows a special text for a cluster (by default, no pop-up is generated for a cluster) and the default behaviour is called for any other feature:

    	customService.setOption('mapFeaturePopupCallback',mySpecialPopUpFn);
    
    function mySpecialPopUpFn(opts_){
    	var opts = $n2.extend({
    		feature: null
    		,layerInfo: null
    		,onSuccess: function(html){}
    		,onError: function(err){}
    	},opts_);
    	
    	var feature = opts.feature;
    	
    	// If a cluster, say so
    	if( feature && feature.cluster && feature.cluster.length > 1 ){
    		opts.onSuccess('&lt;p&gt;This is a &lt;b&gt;cluster&lt;/b&gt;!&lt;/p&gt;');
    		return;
    	};
    	
    	// If not a cluster, revert to default behaviour
    	if( nunaliit2 
    	 && nunaliit2.mapAndControls
    	 && nunaliit2.mapAndControls.DefaultPopupHtmlFunction ){
    	 	nunaliit2.mapAndControls.DefaultPopupHtmlFunction(opts_);
    	} else {
    		// Not supposed to get here
    		opts.onSuccess(null);
    	};
    };
    

    It is possible to add automatic clustering to the map via options found in the module document. When clustering is enabled for a map, the default behaviour to clicking on cluster is to have the map zoom in on the cluster. This usually results in some of the features segregating from the cluster, allowing a user to explore each one individually.

    An optional behaviour is possible where all features making the cluster are selected when the cluster is clicked. This generally results in all the features (actually the docuemnts) making the cluster be displayed in the display pane.

    To enable this optional behaviour, the custom service is used, as follows:

    	customService.setOption('mapClusterClickCallback',$n2.mapAndControls.MultiSelectClusterClickCallback);
    

    It is possible to change the behaviour when a cluster is clicked by using module utilities. See Map Cluster Click Handler for more information.

    When an atlas is first loaded, information is shown in the display pane which is usually a welcome message obtained from the module document. The same text is also shown when the last selected document is unselected.

    The message displayed in the pane is already customizable, since its content can be modified in the module document. However, if a behaviour other than displaying static text is desired, it is possible to pass a special function via the custom service:

    	customService.setOption('moduleDisplayIntroFunction',myDisplayIntroFunction);
    

    The expected interface for this special function is:

    	function myDisplayIntroFunction(opts_){
    		var opts = $n2.extend({
    			elem: null
    			,config: null
    		},opts_);
    
    	...
    };
    

    The argument named 'elem' is the DOM element (in a jQuery set) where the information should be displayed. The argument 'config' contains the object passed during configuration with the declaration of all services. This is sufficient to obtain the necessary information for displaying a welcome message.

    Here is a complete example:

    customService.setOption('moduleDisplayIntroFunction',function(opts_){
    	var opts = $n2.extend({
    		elem: null
    		,config: null
    	},opts_);
    
    $n2.log('config',opts.config);
    
    var $elem = opts.elem;
    $elem.empty().text('Hello World!');
    

    });

    The authentication widget, the portion of the UI dedicated to logging in and out of the atlas, uses default text strings to prompt the user. The custom service can be used to configure other text strings:

    1. authWidgetLoginLabel : This custom service option is a string and represents the label used on the button to allow a user to log in. By default, it is 'Login'.
    2. authWidgetLogoutLabel : This custom service option is a string and represents the label used on the button to allow a user to log out. By default, it is 'Logout'.
    3. authWidgetWelcomeLabel : This custom service option is a string and represents the text showed in the box next to the login/logout button. This is the text that replaces the user name when the atlas is in 'logged out' state. By default, it is 'Welcome'.

    When the client (browser) makes simplified geometry requests to the server, it passes limits as to the size of the response and the amount of time required to respond. These limits can be modified via the custom service.

    • simplifiedGeometriesSizeLimit : This is a number specifying the maximum number of bytes that should be returned by the simplified geometry services when querying for attachments.
    • simplifiedGeometriesTimeLimit : This is a number specifying the maximum number of milliseconds that the simplified geometry services should spend responding to the request.

    As part of custom configuration, it is possible to influence how a document appears by adding custom values to the document before it is displayed by the "ShowService". Schemas are used to render the content of a document. However, schemas contain only simple templates and do not provide a way to perform complex computations based on the content of the document.

    The "ShowService" offers the opportunity to install a function that may modify any document before it is presented to the templating process. The following script demonstrates this capability:

    window.nunaliit_custom.configuration = function(config, callback){
    
    /*
     * This is the function where documents are modified before being displayed
     * by the Show Service
     */
    config.directory.showService.options.preprocessDocument = function(doc) {
    	
    	// At this point, perform any modification required
    	doc.justATest = 'TestValue';
    
    	return doc;
    };
    
    // Done with configuration
    callback();
    

    };

    The example above shows the ability of installing a "preprocessDocument" function to the ShowService during custom configuration. Modifications made to a document during the preprocess function are available to the schema templates.

    When a document is being edited, a number of views of the document are presented to allow a user to modify the content of the document. One of those views is based on the schema associated with the document. Since a schema offers only a template, it is not possible to specify complex operations using the template alone. There is a facility, using the SchemaEditor, to modify the HTML produced by the template. To do so, one must register a "post process" function with the document editor based on schema. The following script shows how this can be accomplished:

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};
    

    // This is the a custom function that can be installed and give opportunity // for an atlas to configure certain components before modules are displayed window.nunaliit_custom.configuration = function(config, callback){

    /*
     * The following function is called after a schema editor has been
     * called to display a document. This is useful to add UI elements
     * to the edit window.
     * @param doc {Object} Document that is being edited
     * @param $div {Object} jQuery element that contains the rendered template
     *                      for editing. 
     */
    config.directory.schemaEditorService.addPostProcessFunction(function(doc, $div) {
    
    	// Find elements with class 'insertRevision' and change content with document
    	// revision
    	$div.find('.insertRevision').text(doc._rev);
    });
    
    // Done with configuration
    callback();
    

    };

    The simplistic example above shows how to enable this feature within the custom configuration function. The custom configuration function, within "nunaliit_custom.js", is the appropriate place to define this function as to make the changes available to all Nunaliit tools.

    Within the atlas framework, when a new document is created, it is generally done specifying a document schema. The schema provides a basic template for a new and empty document. If a more complex process is required when creating a new document, it is possible to install a function with the schema repository to intercept all new documents created from a schema and modify the content of the document. The following script demonstrate how a custom function can be installed:

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};
    

    // This is the a custom function that can be installed and give opportunity // for an atlas to configure certain components before modules are displayed window.nunaliit_custom.configuration = function(config, callback){

    /*
     * The following function is called when a document is created from
     * a schema.
     */
    var repositoryFunctions = config.directory.schemaRepository.getRepositoryFunctions();
    repositoryFunctions.addOnDocumentCreateFunction(function(doc, schema){
    
    	if( schema.name === 'mySchema' ){
    		doc.mySchema = ['something special'];
    	};
    
    });
    
    
    // Done with configuration
    callback();
    

    };

    The function called at creation is given both the new document and the schema used to create it. The schema can be used to discriminate which documents require modifications. The document given in argument can be modified, if it is needed.

    The example above shows how the custom creation function can be added via the use of the custom configuration function. This is the appropriate location for this definition so as to have all tools offered in the framework behave the same when a new document is created.

    Only a handful of projections are supported by the stock installation of Nunaliit, including EPSG:4326 and EPSG:900913 (needed for Google Maps). When other projections are needed by an atlas, they must be specified in the atlas page. Here are the steps required to add a new projection to Nunaliit.

    The first step is to add the projection definition to the nunaliit_custom.js file. In this example, let's say the projection to be added is EPSG:32661, then the definition would be:

    Proj4js.defs["EPSG:32661"]="+proj=stere +lat_0=90 +lat_ts=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";
    

    Following this example, the nunaliit_custom.js file would be modified as follows:

    ;(function($,$n2){
    

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};

    Proj4js.defs["EPSG:32661"]="+proj=stere +lat_0=90 +lat_ts=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 +ellps=WGS84 +datum=WGS84 +units=m +no_defs";

    // This is the a custom function that can be installed and give opportunity // for an atlas to configure certain components before modules are displayed window.nunaliit_custom.configuration = function(config, callback){

    config.directory.showService.options.preprocessDocument = function(doc) {
    	
    	return doc;
    };
    
    
    callback();
    

    };

    })(jQuery,nunaliit2);

    The definition string can be found online at http://spatialreference.org/ for most projection. Select the "Proj4js" format to obtain the correct definition string.

    The final step to using a projection is to specify the projection in the module document. The name of the projection needs to be specified in the attribute titled "srsName" under nunaliit_module/map/coordinates.

    To continue the example above, the module document would contain the following fragment:

    {
    ...
    	,"map":{
    		"coordinates": {
    			"initialBounds": [650000,-200000,1350000,220000]
    			,"autoInitialBounds": false
    			,"srsName":"EPSG:32661"
    		}
    	...
    	}
    ...
    }
    

    When the atlas page is drawn, if a language switcher is specified, then a language switcher widget is provided. This widget allows switching to any defined languages. In a bilingual atlas, a language toggling widget might be preferred. The custom configuration file is used to specify this option, as shown below:

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};
    

    // This is the a custom function that can be installed and give opportunity // for an atlas to configure certain components before modules are displayed window.nunaliit_custom.configuration = function(config, callback){

    config.directory.languageService.setUseToggleWidget(true);
    
    // Done with configuration
    callback();
    

    };

    To remove the language selection widget altogether, one can add the following CSS to css/atlas.css

    .nunaliit_header_language {
        display: none;
    }
    

    To add a supported language to the atlas, one must provide the name of the language (its label) and the language code associated with it. This is generally done in the nunaliit_custom.js file. Here is an example:

    var langDef = {
        code: 'xx',
        name: 'Language X'
    };
    

    config.directory.languageService.addLanguage(langDef);

    When an atlas is created, Nunaliit generates a document that holds a user agreement. By default, the generated user agreement is not enabled.

    To enable the user agreement, two modifications must be made to the user agreement document (identifier is org.nunaliit.user_agreement), located at <atlas-dir>/docs/org.nunaliit.user_agreement:

    • Customize the text of the user agreement (nunaliit_user_agreement/content). This is a localized string which can be offered in multiple languages.
    • Enable the user agreement by changing the "enabled" field from false to true. Within the document, this is located at nunaliit_user_agreement/enabled

    When the user agreement is in effect (enabled), a user must accept the agreement at user creation. If the user agreement is modified after a user is registered, then the user is prompted to accept a new agreement the next time he/she logs in.

    The last user agreement accepted is stored in the document associated with the user, in the user database, under the key nunaliit_accepted_user_agreements/<atlas-name>.

    It is possible to canvas users for a set of questions by installing a user questionnaire in the database. When a questionnaire is installed, users are prompted to answer questions found in the questionnaire at log in.

    The user questionnaire is an optional component which is not enabled by default when an atlas is created with Nunaliit. To enable this feature, a special document must be created. This special document is a schema with a name set to "nunaliit_user_questions". As a reminder, the name of a schema is not the document identifier.

    The schema used to hold the user questionnaire has to meet certain constraints:

    1. A version must be set (non-null) in the "create" section of the schema.
    2. The schema must not be a root schema.
    3. The schema name must be set to "nunaliit_user_questions"

    Here is an example of a schema that, if installed, queries users at log in with a questionnaire:

    {
       "_id": "schema.demo.user_questions",
       "_rev": "2-83c7efa5585a58e9a580afc46f1899c2",
       "name": "nunaliit_user_questions",
       "isRootSchema": false,
       "create": {
           "version": 1,
           "yob": null
       },
       "brief": "<span class=\"n2s_localize\">User Questions</span>",
       "display": "<div><span class=\"n2s_localize\">Year of Birth</span> : {{yob}}</div>",
       "form": "<div><span class=\"n2s_localize\">Year of Birth</span> {{#:field}}yob{{/:field}}</div>",
       "label": "User Questions",
       "extensions": [
       ],
       "nunaliit_schema": "schema",
       "nunaliit_type": "schema"
    }
    

    When a questionnaire is installed, a user is prompted the first time they log in with a question form created from the special schema. When the questions are answered, the answers are saved in the document associated with the user in the user database. The answers are atlas specific and are saved in a key unique to the atlas where the answers were provided. The exact location of the answers are nunaliit_answers/<atlas-name> Here is an example of answers saved in a user document:

    {
       "_id": "org.couchdb.user:user-123",
       "_rev": "20-bea217173d22ef488fe4b49c766dd559",
       "name": "user-123",
       "type": "user",
       "display": "Test User",
       "roles": [],
       "nunaliit_answers": {
           "demo": {
               "version": 1,
               "yob": "1900"
           }
       }
    }
    

    It is important to note that the special schema operates directly on the object where the answers are saved and it is not required to de-reference the all the keys to the answers from the user document.

    After the questionnaire is created, it is possible to modify the questions that are asked. However, users that have already answered the previous questions will not be re-canvassed unless the version number saved in the schema is changed. It is therefore a good idea to increase the version number anytime the questionnaire schema is modified.

    If the questionnaire needs to be disabled after it is created, it is possible to do so without deleting the schema. Simply set the version number to 0.

    At times the Nunaliit server must contact users via e-mail messages. The subject line and content of these e-mail messages can be customized via e-mail templates. E-mail templates are a special type of documents which specifies the contents of a subject line and message body using Handlebars templates.

    The structure of an e-mail template document is explained using an example:

    {
       "_id": "org.nunaliit.email_template.user_creation",
       "nunaliit_email_template": {
           "body": "<html>\n\t<head>\n\t\t<title>Nunaliit User Creation</title> ... </p>\n\t</body>\n</html>",
           "subject": "Nunaliit User Creation"
       },
       "nunaliit_schema": "email_template",
    }	
    

    As show in the example, an e-mail template document names "email_template" as its schema and contains a structure under the key "nunaliit_email_template". The structure contains two string attributes:

    • subject : This is a Handlebars template that should yield to a plain text string. The result of this template is used in the subject line of the e-mail message sent to the user.
    • body : This is a Handlebars template that should yield to a HTML string. The result of this template is used in the HTML body of the message sent to the user.

    As the subject line and body content are created from their templates, an object is provided to the Handlebars templates. This object conatins information appropriate to the message being sent. To continue the example provided above, the complete HTML template for body is reproduced, unescaped, here below:

    <html>
    	<head>
    		<title>Nunaliit User Creation</title>
    	</head>
    	<body>
    		<h1>Nunaliit User Creation</h1>
    		<p>Someone has requested to create a user for a Nunaliit Atlas. If it
    		was you, please follow the link below to complete the registration process.</p>
    		<p>If you did not request a user to be created, simply disregard this e-mail.</p>
    		<p>To complete the registration process, click on the link below, or paste
    		it in your favourite web browser.</p>
    		<p><a href="{{link}}">{{link}}</a></p>
    	</body>
    </html>	
    

    As shown in the body template, the text sent to the user contains a link which is inserted using the Handlebars tag "{{link}}".

    The following list describes the various e-mail templates and the object available to the Handlbars templates:

    • org.nunaliit.email_template.daily_vetter : This template is used to create a message sent to a vetter user, once a day, to remind that a number of vetting tasks are outstanding. The object used to populate the template has the following attributes:
      • count : The number of outstanding vetting tasks
      • approvalPageLink : URL to the media approval page.
    • org.nunaliit.email_template.password_recovery : This template is used to create a message sent to a user that has requested a password recovery. The object used to populate the template has the following attributes:
      • link : URL to the user recovery page, including the special token to uniquely identify the user.
    • org.nunaliit.email_template.password_reminder : This template is used to create a message sent to a user after it is first created. During the creation process, if the user requests an e-mail with a password reminder, this message is created and sent. The object used to populate the template has the following attributes:
      • password : Password, in plain text, that the user entered.
    • org.nunaliit.email_template.submission_approval : This template is used to create a message sent to a vetter when a new database change is uploaded to the submission database. The object used to populate the template has the following attributes:
      • submissionDocId : Identifier for the submission document.
      • submissionPageLink : URL to the submission tool page.
    • org.nunaliit.email_template.submission_rejection : This template is used to create a message sent to a user that submitted changes to the database that have been rejected by a vetter. The object used to populate the template has the following attributes:
      • submissionDocId : Identifier for the submission document.
      • submissionPageLink : URL to the submission tool page.
      • rejectionReason : Text entered by vetter explaining the reason that the submission was rejected.
    • org.nunaliit.email_template.schema_emailOnCreate : This template is used when a schema definition's top level attribute "emailOnCreate" is set to true and a document of that schema is created. The creator of the email, atlas vetters, and atlas administrators are sent this email. The object used to populate the template has the following attributes:
      • submissionDocId : Identifier for the document.
      • schemaName : The name of the schema.
    • org.nunaliit.email_template.upload : This template is used to create a message sent to a vetter when a new media file was uploaded to the database. The object used to populate the template has the following attributes:
      • docId : Identifier for the document where the media file was uploaded
      • attachmentName : Name of the attachment that was uploaded
      • approvalPageLink : URL to the media approval page.
    • org.nunaliit.email_template.user_creation : This template is used to create a message sent to a user to validate the user's e-mail at creation time. The object used to populate the template has the following attributes:
      • link : URL to the user creation page, including the special token to uniquely identify the user.
    • org.nunaliit.email_template.user_registration : This template is used when a user follows their initially received user creation link and completes registration of their account with an atlas. Atlas administrators are sent this email. The object used to populate the template has the following attributes:
      • link : URL to the user management page, if specified in mail properties.
      • userEmail : The email of the user that completed registration.
    • org.nunaliit.email_template.form_email : This template is used to send e-mails to recipients registered for "Form Submission E-mails". This is when a user submits an e-mail to the Nunaliit Atlas using an on-line form. The object used to populate the template has the following attributes:
      • atlasName : Name of the atlas where form was submitted
      • destination : Destination for the e-mail. This represents a group of users registered to receive those notifications.
      • subject : The subject of the message
      • body : The text of the message
      • contactInfo : This is a string representing the contact information.
      Other attributes can be available, based on the options given in the form e-mail dialog.

    The search widget displayed in the atlas populated with an initial text string to indicate to the user that the widget can be used to search the atlas. By the default, the text string is: 'search the atlas'.

    It is possible to change this string to any value via the custom service:

    	customService.setOption('searchWidgetText','Search');
    

    Nunaliit allows a user to send e-mails to administrators via an on-line web form. The form contains fields for contact information and message. An atlas can change the contact information requested by specifying custom fields.

    To specify a field, an object with proper options must be provided. Here is an example:

    {
    	"name": "contact_info"
    	,"label": "Contact Information"
    	,"placeholder": "Information to contact you"
    	,"required": true
    	,"textarea": true
    }
    
    The meaning for each attribute for a field object is as follows:
    • name : Required string. Name of the field. This is transmitted to the server.
    • label : Required string. The label associated with the field. This should be localized.
    • placeholder : Optional string. Explanation for the field. This should be localized.
    • required : Optional boolean. If set, the user must fill the field before submitting the mail form.
    • textarea : Optional boolean. If set, a textarea is used for entry of the field.

    When customizing the contact information, an array of fields must be specified using the custom option 'mailFormContactFields'. Here is an example of how this can be accomplished within the nunaliit_custom.js file:

    		customService.setOption('mailFormContactFields', [
    			{
    				"name": "last_name"
    				,"label": _loc('Last Name')
    				,"required": true
    			}
    			,{
    				"name": "first_name"
    				,"label": _loc('First Name')
    				,"required": true
    			}
    			,{
    				"name": "email"
    				,"label": _loc('E-Mail')
    				,"required": true
    			}
    		]);
    

    There are two other aspects of the web mail form that can be controlled by customization: the form title and the mail subject. Here is an example for those:

    		customService.setOption('mailFormTitle', _loc('Contact Us Form'));
    		customService.setOption('mailFormSubject', 'Contact Us Form');
    

    If you are using Google Analytics to track usage of your site, some events are defined by Nunaliit to keep track of loaded modules and selected documents.

    To integrate Google Analytics in your Nunaliit site, adapt and add the following code to the nunaliit_custom.js file:

    	// Google Analytics
    	(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    	(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    	m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    	})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    	ga('create', 'UA-XXXXXX-Y', 'mydomain.com');
    	ga('send', 'pageview');
    

    Particular attention is needed on the line with the "create" command, where the appropriate tracking ID and host domain is needed.

    CouchDb caching can greatly improve the performance of the Nunaliit, especially for users that behind networks with constrained speed. This performance improvement comes with caching the documents to the browser's database.

    Since the caching logic is still experimental, it is disabled by default. A user can opt to turn this feature on in the debug tool page.

    To invert the default, or enable the caching by default, it is possible to set a custom option that applies to all users of an atlas. To enable CouchDB caching, by default, add the following lines near the end of nunaliit_custom.js:

    // This atlas allows caching of CouchDb in IndexedDB, by default if( !window.nunaliit_custom.options ) window.nunaliit_custom.options = {}; window.nunaliit_custom.options.couchDbCachingEnabled = true;

    The resulting nunaliit_custom.js would look like this:

    ;(function($,$n2){
    

    if( typeof(window.nunaliit_custom) === 'undefined' ) window.nunaliit_custom = {};

    ...

    window.nunaliit_custom.configuration = function(config, callback){

    ...

    callback();
    

    };

    // This atlas allows caching of CouchDb in IndexedDB, by default if( !window.nunaliit_custom.options ) window.nunaliit_custom.options = {}; window.nunaliit_custom.options.couchDbCachingEnabled = true;

    })(jQuery,nunaliit2);

    If CouchDb caching is causing an issue to a user, it is possible to disable it in the debug tool page.

    The Nunaliit Atlas Framework provides the ability to record and display dates, and query documents based on dates recorded in them. The interpretation of dates within the framework attempts to mimick the way dates are conveyed by humans to facilitate the process of recording them.

    In Nunaliit, all recorded dates are interpreted as a time interval. As a result, the precision of the time interval is proportional to the precision of the given date. For example:

    • If a user was to record a date simply as a year like "2009", Nunaliit interprets this as a time interval spanning from January 1st, 2009 to December 31st, 2009.
    • If a user was to record a date using a year-month format, such as "2009-08", Nunaliit interprets this as a time interval spanning from August 1st, 2009 to August 31st, 2009.
    • If a user was to record a date using a format that specifies minutes, such as "2009-08-05 10:02", Nunaliit interprets this as a time interval spanning from 10:02:00 to 10:02:59 on August 5th, 2009.

    Within Nunaliit, in every instance where a date is accepted, a date range can be provided. A date range differs from a date in that instead of a single instance of a date, two dates are provided: one to define the start of the range and one to define the end. A date range is generally displayed as two dates separated by a slash character, such as "2009/2011". In this example, Nunaliit would interpret this instance as a time interval from January 1st, 2009 tp December 31st, 2011.

    Nunaliit also supports the concept of "on-going" date intervals. An on-going date refers to an activity which has began at a set point in the past, but continues to the present, without a set ending time. For example, if one wanted to record the fact that someone has been employed since 2008, and is still employed to this day, one could record "2008/-" in any location where a date is accepted. In this example, Nunaliit would interpret the date as a time interval that spans from January 1st, 2008 to now.

    Because of this interpretation of time, it is pointless to build forms that ask for a start and end date. Instead, one date entry is preferred where the time interval can be expressed more fully.

    When entering a date on a form, in a date field, a number of acceptable syntax is allowed to the user. Any date field accepts a time-interval. Here is the definition of a time-interval:

      time-interval := date-time '/' date-time-or-now
                     | date-time;
    

    date-time-or-now := date-time | '-';

    date-time := date [ time ]

    date := YYYY-MM-DD | YYYY-MM | YYYY;

    time := hh:mm:ss | hh:mm;

    Examples of entries that meet this syntax:

    • 2001-03-11 22:05
    • 2003-04-19 / -
    • 1990 / 1999

    Nunaliit indexes all dates found in every document stored in the database. This allows to find any document that match a queried date interval. For this process to work correctly, dates must be recorded in structure format recognized by the framework.

    A date element has the following structure:

    {
    	"nunaliit_type":"date"
    	,"date": "2002 / -"
    	,"min": 1009861200000
    	,"max": 1009861200000
    	,"ongoing": true
    	,"index": 23
    }
    
    • nunaliit_type : For a date structure, this must be set to "date".
    • date : This is the string that was originally entered by the user. This is useful in case the user wishes to edit and modify the original date.
    • min : This is a number that represents the lower boundary time of the date interval. The time is represented as the number of seconds, since January 1st, 1970.
    • max : This is a number that represents the higher boundary time of the date interval. The time is represented as the number of seconds, since January 1st, 1970. If the attribute "ongoing" is set (true), then "max" is not required.
    • ongoing : This is a boolean. If set, it means that the date is on-going. In this case, the "max" parameter is superfluous.
    • index : This is a number used by the Nunaliit framework to index the date intervals. When a date is saved to the database, this parameter must not be present. Only the Nunaliit server should be populating this attribute.

    Populating a date structure in a document is facilitated by the use of a schema form. In schema forms, a special date field is provided to manage date elements. The following document structure helps illustrate the example:

    {
    	"_id": "xxx"
    	,"_rev": "yyy"
    	,"employment":{
    		"period":{
    			"nunaliit_type":"date"
    			,"date": "2002 / -"
    			,"min": 1009861200000
    			,"max": 1009861200000
    			,"ongoing": true
    			,"index": 23
    		}
    	}
    }
    

    Based on the previous document structure, the following schema form would manage the date element:

    {{#employment}}
    	Date: {{#:field}}period,date{{/:field}}
    {{/employment}}
    

    Since a copy of the string entered by the original user is saved in the "date" attribute, displaying a date interval requires only the display of that attribute. The following schema display is based on the previous example:

    {{#employment}}
    	{{#period}}
    		Date: {{date}}
    	{{/period}}
    {{/employment}}
    

    The search box (the one used for text search) can be utilized to query for documents that match a date interval. In a search box, a date or a date range can be entered along with other search terms.

    For example, the search terms "test 1980/1990" would return all documents that contain the word "test" and had a date that intersects with the period from 1-Jan-1980 to 31-Dec-1990.

    The Nunaliit Atlas Framework provides multi-language support. Atlases published using Nunaliit can offer translations for all elements found in the User Interface (UI) as well as designated data found in the database.

    The Nunaliit UI is designed in English and can be translated by specifying translations in other languages for each encountered English string. These translations can be stored in the database or consolidated into translation files.

    Nunaliit ships with French translation files.

    The "showService" handles portions of the User Interface (UI) that needs translating by searching for elements that contains a class "n2s_localize". All encountered elements associated with this class are localized. The process of localizing these elements is to retrieve the text content from the element containing the "n2s_localize" and replacing it a localized version. If the localized version is not found, then the text is left unchanged.

    The process of modifying HTML elements this way is applied to the whole HTML document, at the start of Nunaliit, as well as all HTML fragments created by schema templating.

    For example, if the following HTML fragment is found in the main page or in a schema:

    	<span class="n2s_localize">OK</span>
    
    On a French page, the described process would replace the fragment above to:
    	<span class="n2s_localized">Accepter</span>
    

    When HTML is generated dynamically, it is possible to invoke the "showService" to detect and replace all localized elements. The "showService" can be obtained from the configuration object provided in the application initialization. Here is an example:

    function main(config){
    	config.directory.showService.fixElementAndChildren( $('.select') );
    };
    

    jQuery().ready(function() { nunaliitConfigure({ configuredFunction: main ,rootPath: './' }); });

    When text is stored in a document, it generally is stored using the Javascript primitive type: String. In Nunaliit, when storing text that should be localized to various languages when displayed, a special structure is used: the "localized string structure".

    Here is an example of a document that stores textual information without locale information:

    book = {
    	"title":"A Book"
    	,"author":"John Smith"
    }
    

    To follow the previous example, here is the equivalent document using the localized string structure to represent that the title should be translated:

    book = {
    	"title":{
    		"nunaliit_type":"localized"
    		,"en":"A Book"
    		,"fr":"Un Livre"
    	}
    	,"author":"John Smith"
    }
    

    The templating used by Nunaliit schema documents is based on Handlebars.js. Regular Javascript strings can be displayed easily with a mustache tag containing the name of the attribute. However, when a string is localizable, the "localized string structure" is used instead of the primitive type. In a Nunaliit schema, to display a string stored in localized string structure, a special function is needed: :localize

    For the following example, this document content is used:

    book = {
    	"title":{
    		"nunaliit_type":"localized"
    		,"en":"A Book"
    		,"fr":"Un Livre"
    	}
    	,"author":"John Smith"
    }
    

    Here is a possible schema to display this information:

    	<div>
    		Title: {{#:localize}}title{{/:localize}}
    	</div>
    	<div>
    		Author: {{author}}
    	</div>
    

    The schema presented above would work, even if the "title" attribute contained a primitive string. This is to allow the gradual conversion from primitive strings to localized string structures.

    In forms created using a schema, localized string structures must be handled in a special manner. As other attributes, the function used to display a form element is ":field". However, the "localized" option is needed, as well as initial language codes. The language codes are used to display the appropriate languages even if the language has not yet been filled.

    For the following example, this document content is used:

    book = {
    	"title":{
    		"nunaliit_type":"localized"
    		,"en":"A Book"
    		,"fr":"Un Livre"
    	}
    	,"author":"John Smith"
    }
    

    Here is a possible schema to provide input fields to gather this information:

    	<div>
    		Title: {{#:field}}title,localized=en+fr{{/:field}}
    	</div>
    	<div>
    		Author: {{#:field}}author{{/:field}}
    	</div>
    

    As a user navigates through the user interface (UI) provided by a Nunaliit atlas, various strings that need to be translated to the user's locale are encountered. The initial string, in English, is submitted to the localization service along with the desired language. If the service contains a suitable translation for the given English string in the desired language, then the string found in the UI is replaced with its appropriate translation. However, if the localization service does not contain a suitable translation, then the original string is left in the UI (English).

    The localization service employs two methods to find translations:

    1. It loads a number of translation files
    2. It loads translations found in the database

    When a user is logged in and a missing translation is discovered, the localization service submits a translation request to the database. A translation request contains the initial Englih string as well as the language code for the locale that is missing the translation.

    In this manner, it is possible to discover the stings that have been missed during the localization process.

    It is possible to provide translations to the localization service via the "localization" tool, a web application located along the Nunaliit pages at ./tools/translation.html

    Using this tool, an administrator can upload new translations associated with the translation requests submitted by the localization service. Once a translation has been submitted to the database, it becomes available to all the instances of the atlas.

    This is particularly useful to circumvent the generation of a new translation file and is meant to quickly correct errors found in atlas pages. However, this method is not as efficient as using a translation file. Therefore, after a number of translations have been captured using the database method, the translations should be moved to a translation file.

    The web localization tool provides a way to display the content what an equivalent translation file would have.

    It is possible to add a number of already translated strings to the Nunaliit framework by loading a resource file from the HTML file. An example of such a resource file follows:

    ;(function($n2){
    

    var lang = 'fr';

    if( !$n2.l10n ) $n2.l10n = {}; if( !$n2.l10n.strings ) $n2.l10n.strings = {}; if( !$n2.l10n.strings[lang] ) $n2.l10n.strings[lang] = {};

    function loadStrings(strings) { var dic = $n2.l10n.strings[lang]; for(var key in strings) { dic[key] = strings[key]; }; };

    loadStrings({ ,"definition":"définition" ,"author":"auteur" });

    })(nunaliit2);

    The example above shows how some English strings are translated to French.

    The Nunaliit framework automatically loads files for the currently selected language. The pattern of the file name is nunaliit_lang.<lang>.js and should be located in the directory "htdocs" of the atlas. For example, if French was the currently selected language, the file "nunaliit_lang.fr.js" would be loaded.

    When using the "localization" tool, a web application located along the Nunaliit pages at ./tools/translation.html, it is possible to obtain the content of a would be translation file based on the full-filled translation requests found in the database by using the option "Translated".

    Using a translation file, it is possible to fix an error found in an English string. The translation file "nunaliit_lang.en.js" is loaded when the current locale is English. Although initially empty, it is possible to provide some content to provide a translation from English to English. For the sake of an example, if one wishes to fix a typo from "Wrang" to "Wrong", the following content can be used:

    ;(function($n2){
    

    $n2.l10n.addLocalizedStrings("en",{ "Wrang":"Wrong" });

    })(nunaliit2);

    When a portion of Javascript creates dynamically DOM elements, the textual components of the DOM tree should be translated before they are inserted into the tree. The l10n Javascript library provides an API to request a translated string for the current locale based on an English string. The following example demonstrates this:

    	var okText = $n2.loc('OK','myPackage');
    	$('.select').text(okText);
    

    As demonstrated in the example above, a package name must be supplied when calling the localization routine. This information is recorded when a proper translation is not found in the repository and a translation request is recorded in the database. The package name helps in sorting which translated strings belong to a which resource file.

    Package names containing "nunaliit" are reserved for internal use.

    A good practice is to define a local function at the top each script that redirects "_loc" to the localization functions, including the approriate package name. This leads to more concise code and helps quickly identify the text portion that requires translation. The following example demonstrates this principle:

    ;(function($n2){
    

    // Local function var _loc = function(str,args){ return $n2.loc(str,'myPackage',args); };

    $('.select').text( _loc('OK') );

    })(nunaliit2);

    Arguments can be inserted in strings used for translation. Arguments are named and replace the portion of the string where the argument name is found, surrounded with curly braces. For example, the following script:

    	var text = $n2.loc('This round: {a}  Total: {b}','myPackage',{"a":1,"b":2});
    
    would yield, in the English locale, the following value for the variable text:
    This round: 1  Total: 2
    

    It is important to note that the arguments are passed as a dictionary of key-value pairs, transported in a Javascript object. When translating localized strings, the argument names must be left unchanged. In other words, argument names must not be translated.

    One of the features in Nunaliit is to enforce a submission database. When this feature is enabled, all changes to the database are first recorded in a separate database, where the changes are held until approved. Once approved, the changes are migrated to the main document database.

    The submission database is a way to ensure that the content published by an atlas is approved before it is viewed by visitors. Without the use of a submission database, changes submitted by users are immediately available to visitors.

    Without the use of a submission database, controlling of the content published on an atlas can be achieved by restricting the users that can authenticate to the atlas. The submisson database allows accepting changes from a larger audience with jeopardizing the published information.

    A visitor to the atlas is not able to submit changes to the database until he/she first authenticate with the atlas. Until a user is logged in, the user interface offered by Nunaliit do not present any button or widget that suggests that a user can make changes to the atlas.

    After logging in, a user specify changes to the database: new documents, modify or delete existing documents. However, when a submission database is employed, the changes are not applied to the document database. Instead, a new document, known as a submission, is created in the submission database. The submission document contains a copy of the new or modified document. However, the document which the submission is based on is left unchanged.

    As the submission document is saved in the submission database, a number of rules based on role are verified. A regular user of the atlas can add any new document. However, a regular user can only modify or delete documents that he/she created. If these rules are broken, the submissions are rejected before they are saved in the submission database.

    Submission documents that are stored in the submission database are monitored by the server application. The server application can make a number of decisions, based on the state of the submission document:

    • If a regular user submitted the changes, an e-mail is sent to all users that are able to approve the changes (users with the vetter role)
    • If the user that submitted the changes has the ability to approve changes (user with vetter role), then the submission is automatically approved.
    • If the submission is approved, the server application attempts to merge the changes to the document database. If the merge succeeds, then the changes are available in the atlas. If the merge does not succeed, an e-mail is sent to all users that are able to approve changes (users with vetter role) to notify the conflict.

    Some users are given a special role, which is the vetter role. These users are known as "vetters". Vetters are notified by e-mail when new changes are submitted to the atlas, via the submission database. Also, they are notified when merging collisions occur. Vetters can use a special tool to approve or deny changes. The tool also allows a vetter to modify the submission before it is approved.

    Once a submission is approved by a vetter, the server application move the changes to the document database. At this point, it is available in the atlas.

    To enable the submission database, a special option must be set during the configuration of the atlas. Move to the atlas directory and re-run the configuration:

    $ nunaliit config
    

    During the configuration questions, enable the submission database. By default, the name of the submission database is the name of the document database appended with "submissions".

    Enter the name of the atlas [atlas1]: 
    Enter the URL to CouchDB [http://127.0.0.1:5984/]: 
    Enter the name of the main database where atlas resides [atlas1]: 
    Do you wish to manually verify each document submission?(Y/N) [N]: Y
    Enter the name of the database where submissions will be uploaded [atlas1submissions]: 
    Enter the name of the admin user for CouchDB [admin]: 
    Enter the password for the admin user [XXXXXX]: 
    Enter the port where the atlas is served [8080]: 
    Enter a Google Map API key (empty if not using) [XXXXXXX]:
    

    A number of tools are offered by the Nunaliit Atlas framework. These tools are linked in a page located at <atlas-url>/tools/index.html.

    Whenever a file is uploaded, it first must be approved before it is displayed by the atlas. The approval page is a tool that reports which files area awaiting for approval and which files have already been denied.

    Only certain users are allowed to approve/deny file uploads. These special users are given the role of "vetter". Users that have the role of "admin" are also allowed to approve uploaded files.

    The modification tool allows a user to query for a set of documents and perform reports, modifications and export based on the list documents. A list of document can be created by various aspects of the database presented by Nunaliit. Then, a list can be refined by filtering some documents. Finally, actions can be taken such as reporting, transforming or exporting.

    The modification tool provides four panes:

    • Queries Pane : This is where a user creates lists of documents. All queries performed during the session are listed here.
    • Query View Pane : When a document list is selected in the top pane, the list of documents associated with the query is displayed in this pane. This pane can also be used to refine the selected query or transform all the documents found in the query.
    • Document View Pane : When a document is selected in the "Query View Pane", this pane displays the content of the document. From this pane, it is possible to manually edit a single document.
    • Log Pane : This pane keeps track of the operations performed by the modification tool.

    To refine a document list, select a list from the "Queries Pane" and press the button titled "Refine List" in the "Query View Pane". A dialog box appear and a list of refining algorithms appear. Select the algorithm appropriate for the filtering and press "OK".

    When creating a new query or when refining an existing query, it is possible to use a Javascript function to filter the documents to be selected. This is a very powerful approach to select document. However, one must be able to code Javascript.

    The Javascript function is called with the content of each document. The aim of the function is to return true, for document that should be selected, or false, for documents that should be rejected.

    The Javascript function is also given a configuration object to access services offered by Nunaliit and the modification tool.

    The following function selects documents that are associated with layer "a" or layer "b":

    function(doc, config){
       var layers = doc.nunaliit_layers; // nunaliit_layers is an array
       if( layers ){
          if( layers.indexOf('a') >= 0 ) return true;
          if( layers.indexOf('b') >= 0 ) return true;
       };
       return false;
    }
    
    function(doc, config){
    	function isValidOlGeom(geom){
    		if( geom.components ){
    			for(var i=0,e=geom.components.length; i<e; ++i){
    				var c = geom.components[i];
    				if( !isValidOlGeom(c) ){
    					return false;
    				};
    			};
    		} else {
    			if( geom.x !== geom.x ){
    				return false;
    			};
    			if( geom.y !== geom.y ){
    				return false;
    			};
    		};
    		
    		return true;
    	};
    
    	function countChar(str,c){
    		return str.split(c).length - 1;
    	};
    	
    	function log(str){
    		if( console && 'function' === typeof console.log ){
    			console.log(str);
    		};
    		if( config && 'function' === typeof config.log ){
    			config.log(str);
    		};
    	};
    
    	if( doc && doc.nunaliit_geom ){
    		if( 'geometry' !== doc.nunaliit_geom.nunaliit_type ){
    			log(doc._id + ' invalid nunaliit_type');
    			return true;
    		};
    		if( 'object' !== typeof doc.nunaliit_geom.bbox ){
    			log(doc._id + ' no bbox');
    			return true;
    		};
    		if( 4 !== doc.nunaliit_geom.bbox.length ){
    			log(doc._id + ' invalid length on bbox');
    			return true;
    		};
    		for(var i=0,e=doc.nunaliit_geom.bbox.length; i<e; ++i){
    			if( 'number' !== typeof doc.nunaliit_geom.bbox[i] ){
    				log(doc._id + ' bbox without a number');
    				return true;
    			};
    		};
    		if( 'string' !== typeof doc.nunaliit_geom.wkt ){
    			log(doc._id + ' no wkt');
    			return true;
    		};
    		if( doc.nunaliit_geom.wkt.length < 1 ){
    			log(doc._id + ' empty wkt');
    			return true;
    		};
    
    		// Balance the parentheses in WKT
    		var openCount = countChar(doc.nunaliit_geom.wkt,'(');
    		var closeCount = countChar(doc.nunaliit_geom.wkt,')');
    		if( openCount != closeCount ){
    			log(doc._id + ' wkt with unbalanced parentheses');
    			return true;
    		};   
    		
    		// Parse WKT if OpenLayers is available
    		if( OpenLayers 
    		 && OpenLayers.Geometry 
    		 && 'function' === typeof OpenLayers.Geometry.fromWKT ){
    			try {
    				var geom = OpenLayers.Geometry.fromWKT(doc.nunaliit_geom.wkt);
    				if( !geom ){
    					log(doc._id + ' OpenLayers return null geometry');
    					return true;
    				} else if( !isValidOlGeom(geom) ){
    					log(doc._id + ' OpenLayers geometry is not valid');
    					return true;
    				};
    			} catch(e){
    				log(doc._id + ' error during OpenLayers parsing');
    				return true;
    			};
    		};
    	};
    	
    	return false;
    }
    

    For a given list of documents, it is possible to generate a report. The report is generated by a Javascript function which is called for each document in the list, and then once more (without a document) to finalize the report.

    To create a report, select a document list in the "Queries" pane and then press the button titled "Report" in the "Queries View Pane". A Javascript function must be entered.

    A report Javascript function should be written similar to the following example:

    function(opts_){
    	var opts = nunaliit2.extend({
    		config: null
    		,doc: null
    		,logger: null
    	},opts_);
    
    if( opts.doc ){
    	// This portion is executed once, for each document
    } else {
    	// This portion is executed only once, after all documents
    	// have been processed
    	var logger = opts.logger;
    	
    	logger.log("Finished");
    };
    

    }

    The following report function is an example where a document list is analyzed for the schema each document is associated with. The report shows how many documents are associated with each schema.

    function(opts_){
    	var opts = nunaliit2.extend({
    		config: null
    		,doc: null
    		,logger: null
    	},opts_);
    	
    	var schemaMap = opts.config.schemaMap;
    	if( !schemaMap ){
    		schemaMap = {};
    		opts.config.schemaMap = schemaMap;
    	};
    
    	if( opts.doc ){
    		var doc = opts.doc;
    		if( doc.nunaliit_schema ){
    			if( schemaMap[doc.nunaliit_schema] ){
    				schemaMap[doc.nunaliit_schema] = schemaMap[doc.nunaliit_schema] + 1;
    			} else {
    				schemaMap[doc.nunaliit_schema] = 1;
    			};
    		};
    	} else {
    		opts.logger.log("Schema Names:");
    		var schemaNames = [];
    		for(var schemaName in schemaMap){
    			schemaNames.push(schemaName);
    		};
    		schemaNames.sort();
    		schemaNames.forEach(function(schemaName){
    			var count = schemaMap[schemaName];
    			opts.logger.log(''+schemaName+' ('+count+')');
    		});
    	}
    }
    

    The following report function is an example where the structure of the listed document is presented. A variable called "selector" can be adjusted so that only a portion of the document is analyzed. Each document is traversed from the point of the selector and each attribute of the document is itemized in the report, stating the type of the attribute and the number of occurrences. This is a useful report to get an idea of which attributes are in use in a document list.

    function(opts_){
    	var opts = nunaliit2.extend({
    		config: null
    		,doc: null
    		,logger: null
    	},opts_);
    	
    	var selector = 
    		$n2.objectSelector.parseSelector(''); // root
    		//$n2.objectSelector.parseSelector('demo_doc');
    		//$n2.objectSelector.parseSelector('demo_media');
    
    	var config = opts.config;
        var attributesMap = config.attributesMap;
        if( !attributesMap ){
            attributesMap = {};
            config.attributesMap = attributesMap;
        };
    	
    	if( opts.doc ){
    		var doc = opts.doc;
    
    		selector.traverse(doc,function(value, childSelector){
    	        if( typeof value === 'number' ){
    	            insert(childSelector,'number');
    	        } else if( typeof value === 'string' ){
    	            if( value !== '' ){
    	                insert(childSelector,'string');
    	            } else {
    	                insert(childSelector,'stringEmpty');
    	            };
    	        } else if( null === value ){
    	            insert(childSelector,'null');
    	        } else if( typeof value === 'object' ){
    	            insert(childSelector,'object');
    	        } else if( typeof value === 'function' ){
    	            insert(childSelector,'function');
    	        };
    		});
    
    	} else {
    		var logger = opts.logger;
    		
    		logger.log("Finished");
    		
    		// Accumulate selectors
    		var selectorStrings = [];
    		for(var selectorString in attributesMap){
    			selectorStrings.push(selectorString);
    		};
    		selectorStrings.sort();
    		
    		// Report each selector
    		selectorStrings.forEach(function(selectorString){
    			logger.log(selectorString);
    			
    			// Accumulate the types for this selector
    			var info = attributesMap[selectorString];
    			var types = [];
    			for(var type in info){
    				types.push(type);
    			};
    			types.sort();
    			
    			// Report each type
    			types.forEach(function(type){
    				var count = info[type];
    				logger.log('- '+type+': '+count);
    			});
    		});
    	};
    
    	function insert(selector, type){
            var selectorString = selector.getSelectorString();
    
            var info = attributesMap[selectorString];
            if( !info ){
                info = {};
                attributesMap[selectorString] = info;
            };
            
            if( typeof info[type] === 'number' ){
                info[type] = info[type] + 1;
            } else {
                info[type] = 1;
            };
        };
    }
    

    When transforming documents from a query, a Javascript function is used to perform the modifications. The Javascript function is given three arguments:

    1. The content of the document that is a candidate for transformation
    2. A callback function to save the document
    3. A callback function to skip this document

    Two callbacks are provided to allow a function to perform asynchronous operations. When transformations are completed on a document, the proper callback must be invoked. If during the execution of the function it is discovered that the document should remain unchanged, then the skip callback must be invoked. Only one of the two callbacks should be called.

    If no callback is invoked, then the transformation process stalls.

    Finally, the transformation function is given a configuration object which provides access to all services offered by Nunaliit and the modification tool. This is useful to query other views or documents prior to modifying the current document.

    The following example function shows how it is possible to assign a set of documents to the "genericDoc" schema:

    function(doc,onTransformedFn,onSkippedFn,config){
       doc.nunaliit_schema = "genericDoc";
       onTransformedFn();
    };
    

    There exists a tool page to configure parameters specific to a browser, known as the Debug Page. The Debug Page is located at /tools/debug.html

    The configuration parameters as discussed below.

    When this parameter is enabled, Nunaliit tries to reach resources using a different URL for each attempt. This is to circumvent caching proxy servers. This parameter should be set only if you experience strange behaviour from Nunaliit or errors relating to update conflicts.

    Since Nunaliit relies heavily on a RESTful API, it is susceptible to badly configured caching proxy server. Setting this parameter and reloading the Nunaliit page should fix issues with those servers.

    Note that one should not turn on this parameter if it is not warranted since it greatly reduce performance.

    When this parameter is enabled, Nunaliit prints to the console an entry for each dispatching event that is sent. This is useful if one is attempting to track the reason behind an unexpected behavior displayed by Nunaliit.

    This is a debugging tool and one should not turn on this parameter if it is not warranted since it greatly reduce performance.

    This tool is for uploading lots of media files in bulk. The tool starts with selecting a schema to use for the uploaded media. After selecting the schema the fields of the schema are displayed. Values put into the fields will be applied to every media file uploaded. Last select the media to upload using the file chooser 'Choose Files' and then click the upload button. The files will all have an import_profile created with the id bulk_media_upload_. As the files are uploaded a status table is displayed to show the progress.

    The natural way for Nunaliit to gain data is via the module interface where a user can add features to the map. There various other methods which includes using tools such a the "Data Browser", where any documents can be created.

    However, when a large amount of data needs to be imported, then the manual approach is not suitable. This section deals with the methods available to import data.

    The Nunaliit framework offers a tool page to import data using a profile, known as "Import Profile". Import profiles are documents that dictate how external data sets are imported to the database employed by the framework. The import tool page is located at <url>/tools/import.html

    The advantages of using import profiles are:

    • Different formats are supported: JSON, GeoJSON and CSV.
    • Data sets can be re-synchronized over time. This means that if a data set is re-imported later, the changes in the external values are taken into account and propagated to the database. Collisions are detected and presented to the user for resolution.

    During the import, the content of the input data set is broken into a list of import entries. A document is created for each import entry. On subsequent imports, each entry is matched to the associated document and the appropriate updates are performed.

    Each import entry contributes properties and, optionally, a geometry. During import, those properties are copied to the desired location in the database document.

    For each data set to be imported, an import profile must be created. Here is an example of an import profile:

    {
       "_id": "import_profile.demo_test",
       "nunaliit_import_profile": {
           "nunaliit_type": "import_profile",
           "id": "demo_test",
           "label": {
               "nunaliit_type": "localized",
               "en": "Demo Test"
           },
           "type": "geojson",
           "options": {
           },
           "operations": [
               "copyAllAndFixNames(demo_test)"
           ],
           "schemaName": "demo_test",
           "layerName": "public"
       }
    }
    

    The main section of the import profile document is the structure "nunaliit_import_profile". In fact, this structure contains all the information conveyed by the import profile. The attributes of this structure are defined here:

    • nunaliit_type : For an import profile, this must be set to the string "import_profile".
    • id : This is a compulsory string. This is the identifier for the import profile. It must be unique among all import profiles.
    • label : This is a localized string structure. It is optional. It represents the text shown to a user when selecting this import profile.
    • type : This is a compulsory string. It represents the type of data set imported using this profile. Possible values are: "json", "geojson" and "csv".
    • options : This is an optional object structure. It content is dependent on the type of the import profile.
    • operations : This is an array of operation strings. The operation strings are specifying how the data is copied from the data set into documents.
    • schemaName : This is an optional string. It represents the name of the schema assigned to documents created by the import profile.
    • layerName : This is an optional string or array of strings. It represents the identifier of the layer(s) assigned to documents created by the import profile.

    The possible operation strings are:

    • copyAll(<key>) : This operation copies all properties found in import entry to a location specified by <key>.
    • copyAllAndFixNames(<key>) : This operation copies all properties found in import entry to a location specified by <key>, similar to "copyAll". However, the attribute names are corrected to be more compatible with JSON. One such correction is to replace spaces found in the attribute name with underscores.
    • assign(<key>, '<prop-name>') : This operation copies a single property from the import entry to the target document. The name of the property to be copied is specified by '<prop-name>'. The location in the database document where the property is copied to is specified by the selector <key>.
    • reference(<key>, '<prop-name>') : This operation copies a single property from the import entry to the target document, similar to "assign". However, the resulting value in the database document is a reference to the property value.
    • importReference(<key>, '<prop-id>', '<import-id>') : This operation copies a single property from the import entry to the target document, similar to "reference". However, this operation references other documents that were previously imported. The string '<prop-id>' specifies the name of the import profile. The string '<import-id>' is the name of the property in the import entry that supplies the import identifier.

    When the type of the import profile is "json", then the "options" structure is defined with the following attributes:

    • idAttribute : This is a compulsory string, unless ignoreId is set. This is the name of the property that specifies a unique value for each import entry.
    • unrelated : This is an optional boolean. If set, the import profile is used to import unrelated data sets. If this is set, then every import is creating a new set of documents, regardless if the same data is provided.
    • ignoreId : This is an optional boolean. If set, ids are not assigned to the entries. This means that idAttribute does not need to be provided. This also sets the unrelated property.

    As a complete example for importing JSON data, here is a set of steps to test JSON import:

    1. Create an import profile document:

    {
       "_id": "import_profile.demo",
       "nunaliit_import_profile": {
           "nunaliit_type": "import_profile",
           "id": "demo",
           "label": {
               "nunaliit_type": "localized",
               "en": "Demo Doc"
           },
           "type": "json",
           "operations": [
               "copyAllAndFixNames(demo_doc)"
           ],
           "options": {
               "idAttribute": "id"
           },
           "schemaName": "demo_doc",
           "layerName": "public"
       }
    }
    

    2. Import JSON data:

    [
    {"id":"0001","title":"Test Import 0001","description":"Description 1"}
    ,
    {"id":"0002","title":"Test Import 0002","description":"Description 2"}
    ]
    

    When the type of the import profile is "geojson", then the "options" structure is defined with the following attributes:

    • idAttribute : This is an optional string. This is the name of the property that specifies a unique value for each import entry. This is needed only if the GeoJSON features do not include the "id" attribute.
    • unrelated : This is an optional boolean. If set, the import profile is used to import unrelated data sets. If this is set, then every import is creating a new set of documents, regardless if the same data is provided.

    As a complete example for importing GeoJSON data, here is a set of steps to test import:

    1. Create an import profile document:

    {
       "_id": "import_profile.demo_test",
       "nunaliit_import_profile": {
           "nunaliit_type": "import_profile",
           "id": "demo_test",
           "label": {
               "nunaliit_type": "localized",
               "en": "Demo Test"
           },
           "type": "geojson",
           "options": {
               "idAttribute": "id"
           },
           "operations": [
               "copyAllAndFixNames(demo_test)"
           ],
           "schemaName": "demo_test",
           "layerName": "public"
       }
    }
    

    2. Import GeoJSON data:

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "id": 1,
                "properties": {
                    "gid": 1,
                    "PID": "9-18-3",
                    "BCT": "BCT126",
                    "OWNER_TYPE": "A",
                    "LOCNO": 0,
                    "LOCEXT": "",
                    "STREET": "CROSBY LANE",
                    "ACQUIRED": "5/7/2010",
                    "GRANTOR": "John A. SPARGO",
                    "UPLAND": 0,
                    "WETLAND": 3.96,
                    "TOTAL": 3.96,
                    "HABITAT": "salt marsh"
                },
                "geometry": {
                    "type": "Point",
                    "coordinates": [
                        -70.03209,
                        41.78278
                    ]
                }
            }
        ]
    }
    

    When the type of the import profile is "csv", then the "options" structure is defined with the following attributes:

    • idAttribute : This is a compulsory string, unless ignoreId is set. This is the name of the column that specifies a unique value for each import entry.
    • unrelated : This is an optional boolean. If set, the import profile is used to import unrelated data sets. If this is set, then every import is creating a new set of documents, regardless if the same data is provided.
    • ignoreId : This is an optional boolean. If set, ids are not assigned to the entries. This means that idAttribute does not need to be provided. This also sets the unrelated property.

    As a complete example for importing CSV data, here is a set of steps to test CSV import:

    1. Create an import profile document:

    {
       "_id": "import_profile.demo",
       "nunaliit_import_profile": {
           "nunaliit_type": "import_profile",
           "id": "demo_csv",
           "label": {
               "nunaliit_type": "localized",
               "en": "Demo Doc CSV"
           },
           "type": "csv",
           "operations": [
               "copyAllAndFixNames(demo_doc)"
           ],
           "options": {
               "idAttribute": "id"
           },
           "schemaName": "demo_doc",
           "layerName": "public"
       }
    }
    

    2. Import CSV data:

    id,title,description,x,y,%_graph
    0,"Test Import 0001","Description 1",-75.5,45.5,5
    1,"Test Import 0002","Description 2",-75.6,45.6,10
    

    The "robot", a server component, is called whenever a new file is uploaded. The robot analyzes the file and queues the file for approval. In the case of a GeoJSON file, once it is approved, the features found in the GeoJSON file are parsed and a document for each feature is added to the database. Since features must generally be associated with a layer, a new layer definition is created and added to the document where the GeoJSON file is attached.

    For each feature, the document created looks like:

    {
       "_id": "ced904e20a5c75b00f59452a2700bb90",
       "_rev": "1-639899baf65c77077979ad0d6314ff1a",
       "nunaliit_geom": {
           "nunaliit_type": "geometry",
           "bbox": [-164.418029,60.614723,-164.418029,60.614723],
           "wkt": "POINT(-164.418029 60.614723)"
       },
       "geojson": {
           "desc": "",
           "cmt": "",
           "ogr_LabelStyle": "water"
       },
       "source": {
           "nunaliit_type": "reference",
           "doc": "ced904e20a5c75b00f59452a2700b10a"
       },
       "nunaliit_layers": ["ced904e20a5c75b00f59452a2700b10a"]
     }
    

    All attributes found for the GeoJSON feature are stored in a key called "geojson". The geometry associated with the feature is transposed to the special key named "nunaliit_geom". The key "source" contains a reference to the document where the original GeoJSON attachment is found. Finally, the special key "nunaliit_layers" is populated with the layer identifier for the new feature.

    The layer definition added to the document that contains the GeoJSON attachement is examplified here:

     "nunaliit_layer_definition": {
           "id": "ced904e20a5c75b00f59452a2700b10a",
           "nunaliit_type": "layerDefinition",
           "name": "GeoJSON - mygeojson.json",
           "bbox": [-165.416304,60.030395,-163.345956,61.247857]
       }
    

    In a layer definition, an identifier (attribute "id") is given. This identifier is used to associate a feature to a layer via the "nunaliit_layers" array. The layer definition also contains a name, which is the label used to report the layer to a user. The name can be modified to suit the atlas. Finally, a bounding box can be specified ("bbox") that represents the portion of space covered by the layer.

    The following steps explain how to exercise the import of a GeoJSON file starting from the default generic atlas that is created by Nunaliit:

    • Using the map interface (main page), add a point to the map, choosing "genericDoc" as the schema.
    • Select new point from the map and click on "Add Related" button in the display. If a schema selection is presented, select "genericMedia".
    • While adding a related generic media, choose the desired GeoJSON file for upload. Press "OK"
    • Once the file is uploaded, go to the "Approval Page" and approve the newly uploaded file.
    • Once the file is approved, return to the map page. The related media under the original point now shows a link titled "Add Layer".
    • Pressing on "Add Layer" adds the layer definition found in the document to the current map. The map moves the displayed area to show the portion covered by the layer definition.

    In general, features imported this way can not be displayed in the side pane because the document does not specify the schema to use for display. However, the content of the document can be viewed by first editing the document and then selecting the "Tree View". Using this approach, one can determine the layer identifier for the new points. Once the layer identifier is obtained, it is possible to use the Modification Tool to perform bulk changes on all the points of a layer.

    Nunaliit provides two methods to export data out of Nuanliit. On of these method is via the Modify Tool, while the other is via the "Export Tool" (look under tools page).

    Both these methods allow exporting of data into GeoJSON and CSV format. Some preparations are required to enable export of documents and depending on the intended format, these preparations differ slightly.

    When a document is exported in GeoJSON format, the schema associated with the document is examined and the property "export" is fetched. This property controls which attributes from the document are included during export. The "export" property found in the schema should be an array of objects. Each object represents an exported attribute. The following is an exmaple of a schema that specifies exported attributes.

    {
       "_id": "schema.place",
       "nunaliit_type": "schema",
       "nunaliit_schema": "schema",
       "brief": ...,
       "display": ...,
       "form": ...,
       "isRootSchema": true,
       "export": [
           {
               "select": "place.name",
               "label": "name",
               "type": "text"
           },
           {
               "select": "place.description",
               "label": "description",
               "type": "text"
           }
       ]
    }
    

    Each object in the "export" array should contain three properties:

    • select: This is a string containing a dotted notation of the document property that is exported.
    • label: This is a string which is the name of the key under which the selected value is exported.
    • type: This is a string which describes the type of the exported value. At this time, only "text" is supported.

    When exporting in GeoJSON format, a document is skipped if it does not contain a geometry.

    The process of exporting data in CSV format is very similar to the one exporting in GeoJSON format. The main difference is that the schema property used to control exported attributes is named "csvExport" instead of "export". The following is an example of a schema containing the required information to export in CSV format:

    {
       "_id": "schema.place",
       "nunaliit_type": "schema",
       "nunaliit_schema": "schema",
       "brief": ...,
       "display": ...,
       "form": ...,
       "isRootSchema": true,
       "csvExport": [
           {
               "select": "place.name",
               "label": "name",
               "type": "text"
           },
           {
               "select": "place.description",
               "label": "description",
               "type": "text"
           },
           {
               "label": "geom",
               "type": "geometry"
           }
       ]
    }
    

    In CSV format, to export the geometry associated with a document, the type "geometry" must be used in the csvExport entries. When "geometry" is specified, the "select" property must not be provided.

    Using the modification tool, a user can export data by selecting a set of document via a query. Once this is done and that the list of documents is listed, the button titled "Export Geometries" initiates the export process. During this process, a file of the appropriate format is downloaded from Nunaliit containing an entry for most documents selected. Documents that are associated with a schema that does not contain the required export information are skipped.

    Using the export tool, a user can export data by selecting a schema type or a layer. Based on the selection, the list of associated documents is retrieved and displayed (or downloaded) in the selected format by accessing the appropriate controls. During the export process, documents that are associated with a schema that does not contain the required export information are skipped.

    Export all documents and media for an atlas. See Full Atlas Export wiki page for details.

    See inReach Device Support wiki page.

    Clone this wiki locally