Documentation

8 Configuration instructions

This section contains information on how to configure different functionalities of Oskari.

8.1.1 Server configuration

Default configuration

The preconfigured Tomcat uses these defaults. These can be changed by modifying {tomcat.base}/lib/oskari-ext.properties.

Redis:

  • running on localhost
  • default port (6379)

Database (Postgres with PostGIS extension):

  • db URL: localhost in default port (5432)
  • db name: oskaridb
  • db user: oskari/oskari

Oskari (provided in download example):

Custom configurations

1) Removing the unnecessary parts

The download example uses prebuilt frontend application in the sample-application folder and prebuilt server application as oskari-map.war. These have repositories that you can use as templates to create your own copies of them:

For modifying what the application initializes for the database content and changing functionalities that you want to include on your application, you will need to create your own modifications and these templates provides an easy way of getting started. One of the easiest way of modifying your application is adding/removing code to/from the application repository, include more dependencies or drop ones that you don't need (like statistical-data related ones if you don't want to use that Oskari functionality in your application).

Similarly, you will need to add/remove references to the functionalities on the frontend application. This is usually done on the sample-application/applications/geoportal/main.js file by importing new functionalities or removing unused ones. The frontend application desides what code is included for the frontend. However, it is the database that actually tracks what functionalities are shown on the user interface. Functionalities are packaged as "bundles" in Oskari terms. To use a bundle, the frontend must include it for the frontend build AND the server/database must reference the bundle so it is started by the frontend. You can have bundles that are started based on user roles so not all bundles that are included by the frontend will be shown for all users (for example admin functionalities).

The different kinds of views that the user can see are saved as "appsetup" on the database. An appsetup has some metadata like which JSP-file is used for the appsetup, which frontend application (geoportal/embedded/custom one) is shown and it can have a UI theme to select colors etc for that specific views. A usual setup is having the default appsetup that is shown for all users like a geoportal. Users can use the publisher functionality in Oskari to create new appsetups as they publish maps or even just saving a customized geoportal appsetup.

Appsetups have a list of "bundles" that are linked to an appsetup. This list determines which functionalities are used by that appsetup. Related database tables are:

  • oskari_appsetup
  • oskari_appsetup_bundles

2) Editing article content

  • User guide: edit the file in {tomcat.base}/lib/articlesByTag/userguide.html
  • Publisher terms of use: edit the file in {tomcat.base}/lib/articlesByTag/termsofuse__mappublication__en.html

3) Changing the default port**

Change {tomcat.base}/conf/server.xml where ever 8080 is referenced in here

<Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8080"
            maxParameterCount="1000"
            relaxedQueryChars="|"
            scheme="https" secure="true"
            parseBodyMethods="POST,PUT" 
            />

4) Proxy settings

If you need a proxy to access internet you can pass the configuration in the application server command line start commaned with:

-Dhttp.proxyHost=
-Dhttp.proxyPort=
-Dhttp.nonProxyHosts=
-Dhttps.proxyHost=
-Dhttps.proxyPort=
-Dhttps.nonProxyHosts=

In the Oskari download the variables.sh holds placeholder for configuring proxy settings

5) Database url/name/user/pass are changed {tomcat.base}/lib/oskari-ext.properties needs to be updated

db.url=jdbc:postgresql://[host]:[port]/[dbname]
db.username=[user]
db.password=[passwd]

5) Using external Redis {tomcat.base}/lib/oskari-ext.properties needs to be updated

redis.hostname=localhost
redis.port=6379
redis.pool.size=10

8.1.2 Configuring a reverse proxy (Optional)

This guide gives an example for configuring a reverse proxy in front of an Oskari-based application. You can use software such as nginx or Apache httpd or similar for this and use the chosen server to serve the static frontend application files as well. The server can function as a load balancer or just as a dummy proxy that decides to respond with static files for the frontend application or proxy the request to the underlying application server.

Using a reverse proxy is not required for development, but is recommended for production use

C4Deployment

Person(user, "User", "")

Boundary(service, "Oskari-based service", "") {
    Boundary(frontend, "Web server", "nginx/httpd") {
        Component(sample-app, "sample-application", "Javascript")
    }
    Boundary(server, "Servlet container", "tomcat/jetty") {
        Component(sample-server_1, "sample-server-extension", "Java")
    }
    Boundary(server2, "Servlet container", "tomcat/jetty") {
        Component(sample-server_2, "sample-server-extension", "Java")
    }
    Boundary(db, "Databases", "") {
        ContainerDb(postgres, "PostgreSQL", "PostGIS/SQL", "Application config and user data")
        ContainerDb(redis, "Redis", "", "Caching/Cluster messaging")
    }
}


Rel(user, sample-app, "", "")
Rel(sample-app, sample-server_1, "", "")
Rel(sample-app, sample-server_2, "", "")
Rel(sample-server_1, postgres, "", "")
Rel(sample-server_2, postgres, "", "")
Rel(sample-server_1, redis, "", "")
Rel(sample-server_2, redis, "", "")

8.1.3 Configurations for production use

Gzip

The frontend files and JSON responses from the server can sometimes be quite large and you should turn on gzip to reduce the size for network traffic.

nginx

In /etc/nginx/nginx.conf turn on gzip support by adding:

gzip  on;

httpd

For httpd you can add these lines to your virtualhost configuration file:

# Enable gzip for these types
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/plain

8.1.4 Example configurations

You can find some example configurations in https://github.com/oskariorg/sample-configs/tree/master/nginx. The nginx version at the time of writing the configs was 1.8.1 which was used to test these configurations.

Most configurations for nginx can be done in /etc/nginx/conf.d/default.conf

Apache httpd has a nice page for setting up reverse proxy: https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html

Assumptions

Below are some of the assumptions for the nginx example configurations.

Oskari frontend code.

Oskari frontend code should be made available in the server directory /opt/public/oskari. This can be changed by modifying these lines:

    root /opt/public/;

    # Oskari frontend files
    location ^~ /Oskari/ {
        rewrite ^/Oskari/(.*)$ $1 break;
        try_files /oskari/$1 oskari/$1/ =404;
    }
Oskari-server

Oskari server should be running on localhost in port 8080. This can be changed by modifying these lines:

upstream oskariserver {
    server localhost:8080;
}
Protecting cookies

Before Oskari 3.0 the Java libraries didn't support the SameSite-flag for cookies but Set-Cookie headers from the application server can be manipulated by both nginx and httpd. We recommend using secure, HttpOnly and SameSite=Lax flags for cookies.

This snippet modifies the cookie the application server gives and adds the flags whenever the application server wants to add a cookie. SameSite-flag means that browsers don't send for example the session cookie when requests originate from a different domain.

    # Oskari-server application server path
    location / {
        ...

        # set all cookies to secure, httponly and samesite by modifying "path"
        proxy_cookie_path / "/; secure; HttpOnly; SameSite=lax";
    }

HTTPS-configuration

The following enables HTTPS on the server. Add the certificates on:

  • /etc/nginx/ssl/public.crt for public key
  • /etc/nginx/ssl/private.rsa for private key

or change the configuration accordingly.

    # ssl config - optional, but recommended for offering https-urls
    listen       443 ssl;
    ssl_certificate /etc/nginx/ssl/public.crt;
    ssl_certificate_key /etc/nginx/ssl/private.rsa;

    # ssl security settings - optional, but recommended
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
    server_tokens off;
    # /ssl config

Check the chosen server software instructions for securing web servers/listing of current recommended protocols, ciphers etc. All the usual best practices work and hardening flags works with Oskari as any other web application. The only thing that makes things a bit different is if you allow users to published embedded maps from your application, these are embedded using iframe. Usual hardening tips might advice for disabling support for the service to be shown in an iframe, but embedded maps need this to be useful so you shouldn't disable embedding.

8.1.5 Server clustering (Optional)

Oskari server can be used in a clustered environment setup. A server cluster can be used to increase availability, reliability and scalability and help deal with software crashes/hardware failures etc in the system.

A simple clustered environment can be set up by having 1-n application servers (Tomcat/Jetty etc) running Oskari-server based webapp and having a load balancer like nginx or Apache httpd as a an entrypoint that forwards (and even load-balances) requests to the application servers. The requests can be forwarded in a round-robin fashion. "Sticky sessions" are not required but could benefit debugging etc (see Identified challenges).

Currently this means:

  • User sessions can be saved/tracked in Redis (shared between nodes)
  • Caches communicate removals/flushes between cluster nodes
  • Programmatically setting log level is communicated between cluster nodes

Note! A "cluster" with just one server instance still means persistent sessions (sessions don't "die" with server restart etc) so it's worth considering.

Enabling cluster handling

Clustered environment handling is enabled with the oskari.profiles key in oskari-ext.properties:

# Comma-separated list of profiles to activate.
oskari.profiles=redis-session

Add redis-session to the profiles list

Code examples for adding more cluster aware functionality

Check status

Check if we have cluster handling enabled:

boolean isClustered = org.oskari.cluster.ClusterManager.isClustered();
Messaging

Calling ClusterManager.getClientFor([functionality id]) returns a ClusterClient instance that is created on first call and shared between returning calls. This keeps the number of Redis-connections manageable. The ClusterClient can be used to send messages and listen to messages between cluster nodes (application server instances). The ClusterManager handles that messages from an instance is not received by listeners of that same instance. ClusterClient handles message scoping so messages from one functionality are not received by other functionalities. For example "cache" functionality doesn't receive messages from "logger" functionality making it easier to handle messages based on the functionality.

For example cache is used by Oskari cache classes as functionality id.

Messaging also has a channel concept that can be used to further filter messages for specific listeners. Channel could be considered an event name for usual event based code. In the cache functionality channel is used to identify the cache instance (maplayer, dataprovider etc).

Sending messages
String functionalityId = "cache";
String channel = "maplayer";
String msg = "REM:1";
ClusterManager
    .getClientFor(functionalityId)
    .sendMessage(channel, msg);

This would send a message REM:1 to cache functionality with channel maplayer. This would signal other cluster nodes that cache with name maplayer should be notified with the message REM:1. The functionality that is doing messaging is responsible for implementing the message parsing/protocol ie. what kind of message signifies what in the functionality.

Listening to messages

Listen to messages from other cluster nodes based on functionality id and more specific message type channel. The code below can be used to listen to messages from other cluster nodes that have been sent with the code snippet above.

String functionalityId = "cache";
String channel = "maplayer";
ClusterManager
    .getClientFor(functionalityId)
    .addListener(channel, (msg) -> handleClusterMsg(msg));

The handleClusterMsg() will receive the REM:1 message from the above example and in the case of caches will remove/flush the value with key 1 (maplayer id used as cache key) from the cache so an updated version is loaded from the database when it's next requested.. Note that channel is used to identify the cache so only the maplayer cache would remove the item with key 1, not for example a cache for dataproviders.

Identified challenges

Server clusters usually present some challenges for servers environments as well. Here's something we've identified:

Logging

Logging in clustered environment means that a single page view by a single user can generate log messages on different servers (different log files). This requires an instance specific solution like:

  • "sticky session" where the load balancer in front of Oskari-server forwards requests from single user to the same server instance.
  • a centralized logging system where the log from each node is gathered on a central system.

The centralized logging would also benefit from a "request id" that could be used to group log messages/user without identifying the user details. This hasn't been implemented as it is not trivial and would require passing some identifier to different parts of the system that don't require it now.

The logo of the service is by default displayed on the:

  • LogoPlugin (lower left corner on the map by default)
  • On printouts when the print functionality is enabled
  • Browser tab/favicon

You can customize these on the server by adding binary images on the server classpath (or package them inside the war-file) and referencing them on the oskari-ext.properties configuration file:

# These are the built-in logos for Oskari
# You can override them by changing the path to point to a png on the server classpath (for example resources under jetty)
logo.path.print=/org/oskari/util/logo_print.png
logo.path=/org/oskari/util/logo.png

# Or if they are under jetty/resources/images
#  logo.path.print=/images/logo_print.png
#  logo.path=/images/logo.png

Optionally you can also overwrite the image from LogoPlugin with CSS (for example on the overwritten.css file when having a my_logo.png on the same folder) with:

.logoplugin .logo {
    content: url('my_logo.png');
}

If the logo is not square you might need this selector for set the size of the container for it, but otherwise the server configuration is the preferred method.

8.2 Custom favicon

Easiest way to override the browser tab icon (favicon.ico) is to replace the file under webapp-map/src/main/resources/favicon.ico on your server repository. You can configure another location with oskari-ext.properties, but the file name needs to match favicon.ico regardless:

# under {tomcat.base}/lib (when working folder is {tomcat.base} folder)
#   favicon.path=file:resources/img/favicon.ico
# with absolute path on the server
#   favicon.path=file:/opt/public/img/favicon.ico
# reference file inside oskari-map.war
#   favicon.path=classpath:images/favicon.ico

8.3 Customizing index map icon

The icon is displayed on published maps on the button that toggles the index map.

You can overwrite the images for dark and light themes with CSS (for example on the overwritten.css file when indexmap_icon_dark.png and indexmap_icon_light.png are on the same folder) with:

div.mapplugin.indexmap .indexmapToggle[class$=-dark] div.icon {
    background-image: url('indexmap_icon_light.png');
}
div.mapplugin.indexmap .indexmapToggle[class$=-light] div.icon {
    background-image: url('indexmap_icon_dark.png');
}

If the image's sides are not even or it is too large, you might also need the following style:

background-size: contain;

8.4 How to modify app setups

Oskari applications are generated by selecting a combination of functionalities (bundles). These combinations are called application setups (appsetup). You can think of them as a shopping list or feature toggles for items you want your application to have. As Oskari was born from a geoportal platform and is mostly used as a solution for web-mapping software the one thing most applications have on their shopping list is the map-functionality (mapfull bundle). Other features that you can include in your application include features like search, layer listing, thematic maps functionalities and of course you can implement and include application specific features for your application.

8.4.1 Frontend

When building a customized application the main.js on the customized app determines what functionalities can be used with that app (what code is packaged to be used in the app). When starting the app the frontend usually makes a GetAppSetup XHR-request to the server asking what functionalities will be used on that app and for that user. The response is then constructed by the server based on what the uuid parameter was in the request (this matches the uuid column on oskari_appsetup database table) and who the user is or what roles the user has (guest, user, admin etc). The UUID parameter has a default and is optional but it is used to construct a listing of bundles that are linked to that application on the database table oskari_appsetup_bundles.

After the frontend receives the response for GetAppSetup it will try to start the bundles referenced in the response with the configuration and initial state received as part of the response. If the server response references a bundle that is NOT packed in the applications main.js the developer console will show a warning about this. Also worth noting is that everything referenced in main.js might not be used by the application. In this case it's best to use the lazy-loader option when referencing bundles in main.js so bundles that are not used with all users don't need to be loaded by users that don't see them (like the admin bundles that are only used by users with the admin role).

8.4.2 Database

On the database these application setups are saved on the oskari_appsetup table. The setups can be named and they have a type to separate between the default geoportal application, an embedded map application (users can create these with the publisher functionality bundle), applications that the users have saved as their own personalized starting "views" etc. The setups reference the frontend application code with the application column and the HTML-template (JSP-page) with the page column.

The selection of functionalities for applications are stored on the database table oskari_appsetup_bundles. A row on the table is a bundle in the application with possible configuration and state information. For example most applications have the map as functionality. You can see that the mapfull bundle is repeated on the table multiple times with the appsetup_id column pointing to the application where the map is used, state has information like what are the coordinates and zoom level the map is initially set and what map layers are on the map when the application is shown. The config column holds configuration like what feature toggles are activated for the user on that bundle. For the mapfull bundle the config are toggles from supporting wms-layers (usually always enabled) to supporting adding markers on the map and enabling adding vector features to the map programmatically. The difference between state and config is that state usually changes during runtime on the client (user moves the map and coordinates are changed) and config doesn't (the user either has the ability use a feature or not). Both config and state options are described for bundles on the API documentation. The seqno column is used to set the order in which the functionalities are started. Map is usually near the start of the sequence with a low value in seqno. The bundle_id column references the oskari_bundle database table where name is the "bundle id" that is recognized by the frontend.

8.4.3 Role based functionalities

The user roles can be used to inject more bundles for the frontend to start on top of the ones that match the application based on the database. This configuration is done in the oskari-ext.properties file on the server. It is usually used to inject administrative functionalities (bundles) for users with the admin role, but it can be used for other role based injection as well.

# bundles that are added on runtime to view if user has one of configured role
actionhandler.GetAppSetup.dynamic.bundles = admin-layereditor, admin-users

# Linking dynamic bundles based on user roles
# Properties are named 'actionhandler.GetAppSetup.dynamic.[BUNDLE ID].roles'
# with value as comma-separated list of role names that should be served the bundle
actionhandler.GetAppSetup.dynamic.bundle.admin-layereditor.roles = Admin
actionhandler.GetAppSetup.dynamic.bundle.admin-users.roles = Admin

8.4.4 Oskari instance initial applications

The initial applications can be described as JSON-files on the server code and inserted to the database with Flyway-migrations. You can see these on the sample-server-extension that you can use as a template to build your own Oskari instance.

More information about Flyway and migrations:

The application specific migrations can of course be done however you want but this is a built-in option to use for migrating the database and can be used to modify the content on the database like adding or removing bundles from oskari_appsetup_bundles. Notice that these migrations are only run once so when it has been run, modifying the code doesn't get the migration run again. You will need to delete rows on the oskari_status_[your apps module] to the migrations to run again on a given database.

8.4.5 Modifying applications after the initial database population

Here's an example Flyway migration that you can add on your server-extension code.

Note that the order in which migrations are run is determined by the version number on the filename (the below would be 1.0.0). When you want to add a new migration after one has been run, you need to bump the version to a higher number like 1.0.1, 1.1.0, 2.0.0. So take a note a consider a versioning strategy for your Oskari instance. For best practices: the version on the frontend codes package.json, the version on the server Maven modules pom.xml and the version on the Flyway migration should be tied to each other some way.

Open the folder sample-server-extension/app-resources/src/main/java/flyway/app. Inside, you will find various files that can be used as templates for your flyway migration.

The code in each file begins with package flyway.app;

The app on our sample-server-extension can and should be changed to something that describes your app (custom_flyway_module). Note that you can also have multiple Flyway-modules on your app. The modules that are used when the server runs are set on oskari-ext.properties in oskari-server as db.additional.modules.

The migration name is after two underscores after the version. This can be anything you like on your application but it's easier to maintain if naming makes sense with the migration that is being run.

For removing bundles after they have been added, you can see the other methods available on the AppSetupHelper class. You also don't need to use these helpers and can modify the tables with for example SQL. These are just common operations so we have helpers for them to make it as easy as it can be.

8.5 How to configure 3D map view

Oskari supports 3D map view since version 1.54 as an optional 3D-capable mapmodule has been added to Oskari. 3D mapmodule is based on Ol-Cesium library, which is meant for integrating OpenLayers and Cesium. Since Oskari 2D uses OpenLayers for map rendering and many map tools are built using OpenLayers most of the tools work out-of-the-box in 3D with Ol-Cesium.

Oskari supports 3D Tiles for 3D data rendering. 3D Tiles is an OGC Community standard and is designed for streaming and rendering massive 3D geospatial content.

Example 3D application can be seen on demo.oskari.org.

8.5.1 3D map view setup frontend

Oskari Sample application has example 3D application (geoportal-3d) and publishing template for embedded 3D maps (embedded-3d).

The changes from a usual 2D geoportal are listed below:

Add Ol-Cesium packages to main.js

Mandatory requirements is to add Ol-Cesium based mapmodule and layerplugin for 3D Tiles.

// Remove the default mapmodule import if you have one ('oskari-loader!oskari-frontend/packages/mapping/ol/mapmodule/bundle.js')

import 'oskari-loader!oskari-frontend/packages/mapping/olcs/mapmodule/bundle.js';
import 'oskari-loader!oskari-frontend/packages/mapping/olcs/map3dtiles/bundle.js';

Oskari frontend contains additional bundles for controlling camera and displayed time.

import 'oskari-loader!oskari-frontend/packages/mapping/time-control-3d/bundle.js';
import 'oskari-loader!oskari-frontend/packages/mapping/camera-controls-3d/bundle.js';

Cesium adjusts the sun's position and visualizes the shadows according to the time set for the map. You can also adjust displayed time with SetTimeRequest.

Using 2D and 3D maps side by side

You can have different appsetups for 2D and 3D maps like in https://demo.oskari.org/. Oskari frontend contains bundle for changing appsetups which adds 2D or 3D button to toolbar for switchint between the appsetups.

import 'oskari-loader!oskari-frontend/packages/mapping/dimension-change/bundle.js';

Note! You need to import 3D tiles layerplugin also with 2D geoportal application to support layer listing.

8.5.2 D map view setup backend

Oskari Sample server-extension has example Flyway module example3d to set backend ready for 3D. Oskari Jetty bundle's sample configuration doesn't have 3D enabled by default. You should add example3d to db.additional.modules.

Example view configuration geoportal-3d.json.

Add 3D tiles plugin to mapfull bundle's plugins array

{ "id": "Oskari.mapframework.mapmodule.Tiles3DLayerPlugin" }

Setting camera and time state

Optional camera settings and displayed time can be given in mapfull's bundle state.

Configuring the terrain

Optional terrain configuration can be given in mapfull's bundle configuration under mapOptions.

"mapOptions": {
    "terrain": {
        "providerUrl": "{{url}}",
        "ionAssetId": "{{asset-id}}",
        "ionAccessToken": "{{your-ion-access-token}}"
    }
}

terrain and all it's children are optional. 

  • Using providerUrl one can set the TMS service end point. Supported terrain formats are heightmap-1.0 and quantized-mesh-1.0. 
  • When ionAssetId is provided, the terrain is streamed from Cesium ion service which also requires ionAccessToken
  • When ionAccessToken is provided without ionAssetId Cesium World Terrain is used for providing detailed terrain all over the globe.

Configuration can be set also in oskari-ext.properties which overrides mapfull's terrain configuration:

oskari.map.terrain.url=
oskari.map.terrain.token=
oskari.map.terrain.asset=

Configuring dimension change

Dimension change bundle loads page with different appsetup. Changing between 2D and 3D appsetups bundle should have wanted view's uuid in the configuration. {"uuid": uuidToLoad}

Enable 3D map publishing

Embedded 3D maps needs own template which have to be set to 3D view. The uuid of the appsetup for 3D-publish template needs to be configured to the 3D geoportal appsetup metadata.

8.5.3 Adding your own 3D data

3D tilesets can be added to the map using admin tools and choosing 3D Tiles as the layer type. Example application http://demo.oskari.org has 3D model and buildings with textures layers from City of Helsinki.

8.5.4 Configuring 3D-tiles map layers

The 3D-tiles maplayers are saved in the database with all other datasets/services used for map layers in Oskari. The default fill color for 3D objects like buildings is a brownish tint/color in Oskari. This might make a textured surface look "dirty" by default but you can configure this. Also a tileset including a mesh-type scanned 3D environment has pretty low resolution with default settings in Cesium JS. Both of these can be tuned with database configuration in oskari_maplayer table by configuring the options column:

UPDATE oskari_maplayer
SET options='{
    "maximumScreenSpaceError": 0,
    "styles": {
        "buildings": {
            "fill": {
                "color":"#FFFFFF"
            }
        }
    }
}'
 where id = [layer id for 3d tileset to configure];

The maximumScreenSpaceError defaults at value 16 in Cesium and affects the mesh type tileset resolution heavily. The styles value is similar to oskari style JSON but the styling isn't fully supported yet for 3D-tiles. The buildings part of the snippet above is the style name and you can choose it yourself. To make textured objects look better you might want to set the fill color to white.

8.5.5 Oskari 3D current state

The released version of 3D map is the initial version which can be developed further based on requirements that will emerge from use cases. The following features have been developed to support the use of 3D map:

  • adding 3D Tiles datasets
  • 3D map publishing
  • support for WFS map layers and feature data in 3D map view
  • camera flights with RPC API
  • measure tools, coordinate tool, markers and own places extended to work also in 3D map view
  • support for adding tilesets from Cesium ion platform with admin tool

8.5.6 Creating your own 3D datasets using Cesium Ion

Cesium ion is a platform for 3D geospatial data tiling, hosting and streaming. It converts different types of data (e.g. point clouds, 3D buildings, photogrammetry) to 3D Tiles format. A free Community account can be used for non-commercial personal projects, exploratory development, or unfunded educational activities within the defined usage limits. See the pricing page for more information.

Run in console

var mapmodule = Oskari.getSandbox().getStatefulComponents().mapfull.mapmodule;
mapmodule.getCesiumScene().primitives.add(
    new Cesium.Cesium3DTileset({
        url: Cesium.IonResource.fromAssetId(yourAssetId, {
            accessToken: 'your-access-token-here'
        })
    })
);

8.6 How to configure a map projection

In Oskari application it is possible to configure different map projection for each view/appsetup. The map projection is configured for the mapfull bundle. Bundle configurations are in JSON-format that can be found in the database table portti_view_bundle_seq in column config where bundle_id matches the bundles id in portti_bundle table. If there are many views in the database, there are also many mapfull configurations.

To get all the configurations for the "mapfull" bundle from database run this script:

SELECT view_id, config FROM portti_view_bundle_seq WHERE bundle_id IN (SELECT id FROM portti_bundle WHERE name='mapfull')

Select the view_id that matches the view you want to configure (default view/publish template probably) and get the 'config' for that row. The default views are configured in oskari-ext.properties or if there is no configuration, the publish template is a view with type 'PUBLISH' and the default view with type 'DEFAULT'. Notice that if you have an old database with personalized default views you will also need to update all views. For this we recommend doing a Flyway-script.

oskari-ext.properties under {jetty.home}/resources:

# this is the publish template id
view.template.publish=3

# this is the generic default view id
view.default=4

# These are role specific default views
view.default.Admin=4
view.default.Guest=1
view.default.User=3

# This is the order in which user roles are matched to default views.
view.default.roles=Admin, User, Guest

SQL to get default view and publish template if not configured in oskari-ext.properties:

SELECT * from portti_view where type IN ('DEFAULT', 'PUBLISH');

The config includes functional configuration for the bundle which also includes for example which features to activate (plugins). The interesting part for projection configuration is the mapOptions and projectionDefs JSON-segments. These might not be present in the config in which case Oskari uses these defaults:

{
"mapOptions": {
   "maxExtent": {
      "left": 0,
      "bottom": 0,
      "right": 10000000,
      "top": 10000000
   },
   "srsName": "EPSG:3067",
   "resolutions":[2000, 1000, 500, 200, 100, 50, 20, 10, 4, 2, 1, 0.5, 0.25],
   "units": "m"
},
"projectionDefs": {
   "EPSG:3067": "+proj=utm +zone=35 +ellps=GRS80 +units=m +no_defs",
   "EPSG:4326": "+title=WGS 84 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
}
... there's propably more than these in the config, but the above are projection related ...
}

To configure the projection you must add or edit these JSON-segments. An example fragment for EPSG:3057 based map:

    {
        plugins : [ ....],
        ...,
        "mapOptions": {
           "maxExtent": {
              "left": -39925.6142,
              "bottom": -38980.4932,
              "right": 1001273.8754,
              "top": 986514.2978
           },
           "srsName": "EPSG:3057",
           "resolutions":[2048,1024,512,256,128,64,32,16,8,4,2,1,0.5,0.25]
        },
        "projectionDefs": {
           "EPSG:4326": "+title=WGS 84 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
           "EPSG:3057": "+proj=lcc +lat_1=64.25 +lat_2=65.75 +lat_0=65 +lon_0=-19 +x_0=500000 +y_0=500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
        }
    }

mapOptions

  • srsName is the name of the projection that should be used
  • maxExtent means projected bounds of the chosen projection. These are the bounds that the map engine will allow the user to pan the map. The bounds can be found f. e. in spatial reference web site.
  • resolutions are the zoom levels

projectionDefs should include proj4js configurations for all the projections that are going to be used in the application. The oskari-server will try to detect the projections that are used and inject the projectionDefs automatically. The projectionDefs are stored in this file: https://github.com/oskariorg/oskari-server/blob/master/control-base/src/main/resources/fi/nls/oskari/control/view/modifier/bundle/epsg_proj4_formats.json. If the projection you want to use isn't available in the file you can insert the definition manually to the database config or make a pull request/issue to oskari-server to add the missing projection to the epsg_proj4_formats.json file.

The easiest way to change only the projection is to query the existing config from the database, modify the above projection specific configs and update the modified version back to the database. The projection can be configured to the database with the following kind of SQL. Change the configuration and the view_id to match your application!

    UPDATE portti_view_bundle_seq
    set config = '{ ... modified config ...}'
    WHERE
    bundle_id = (SELECT id FROM portti_bundle WHERE name = 'mapfull')
    AND view_id = [the view id you want to update]

8.7 How to configure properties

Most settings can and should be overridden to match your environment by including an oskari-ext.properties in the server classpath for example in Jetty you can add the custom properties as {JETTY_HOME}/resources/oskari-ext.properties.

8.7.1 Properties

The default values for properties used in the map application is found in oskari-server/servlet-map/src/main/resources/oskari.properties. To override all or any of these you can add an oskari-ext.properties file in your classpath.

Any existing property will be overridden and any new ones will be available through PropertyUtil methods in the application (for example PropertyUtil.get("propertyName")).

Examples:

# set to true to get database populated with initial demo content
oskari.init.db=false

# true all ssl certs/hosts for debugging! configure certs on the server for production
oskari.trustAllCerts=true
oskari.trustAllHosts=true

# url path to call for ajax requests/action routes
oskari.ajax.url.prefix=/action?

# Supported locales, comma separated and default first
oskari.locales=en_US,fi_FI,sv_SE

The properties also setup various url links for search service, CSW metadata, etc that needs to be modified to match the server environment.

8.7.2 Environment variables

Environment variables can also be used to append or override any property set in properties files. Any set environment variable will take precedence over a property in properties files following the two rules:

  • To avoid issues with casing and dots with environment variables all environment variables have to be uppercased and use underscores (_) instead of dots (.)
  • To avoid conflicts with environment variables meant for other processes Oskari expects all environment variables to be prefixed with OSKARI_

Examples:

# oskari-ext.properties
db.url=jdbc:postgresql://localhost:5432/oskaridb
db.username=oskari
db.password=oskari

# Using environment variables to override the configuration (bash example)
export OSKARI_DB_URL=jdbc:postgresql://localhost:2345/oskaridb
export OSKARI_DB_USERNAME=oskari2
export OSKARI_DB_PASSWORD=my-password

8.8 How to configure map layers

Note! This page is not complete but serves as a starting point for accumulating documentation for different configuration options.

Oskari servers as a registry for different kinds of spatial data infrastructure services. For example actual data for map layers are not stored in Oskari but layers are registered to Oskari so that they point to for example an existing OGC compliant data source.

Most of the registry data that is stored for such services is stored in the database table oskari_maplayer.

Most of the toggles that an admin can change in oskari_maplayer are located in the attributes column. The value of the column is a JSON-object and can have the following keys:

  • forceProxy: true | false When true even layers that don't require authentication are proxied through the server. Without this the browser gets the data directly from a WMS/WMTS or similar endpoint.

The attributes column can also have configuration specific to a layer type.

See:

8.9 Configuration options for vector feature layers

Vector features can come from WFS or OGC API Features endpoint and displaying them can be customized with a config on the database table oskari_maplayer.

For example localized names can be given for feature properties with a JSON-configuration on the attributes column.

For example consider a GeoJSON presentation of a vector feature like this:

{
    geometry: {...},
    properties: {
        NAME: 'Place name',
        LINK: 'https://my.place/241',
        IMG: 'https://my.place/241.png'
        RENT: '100€/day',
        DESCRIPTION: 'This is a nice place'
    }
}

You could configure oskari_maplayer.attributes with:

{
  "data": {
    "filter": [
      "NAME",
      "IMG",
      "DESCRIPTION",
      "RENT"
      "LINK"
    ],
    "locale": {
      "en": {
        "RENT": "Rental price",
        "LINK": "More information"
      },
      "fi": {
        "RENT": "Vuokrahinta",
        "LINK": "Lisätietoja"
      }
    },
    "format": {
      "NAME": {
        "type": "h2",
        "noLabel": true
      },
      "DESCRIPTION": {
        "type": "p",
        "noLabel": true
      },
      "IMG": {
        "type": "image",
        "noLabel": true,
        "params": {
            "link": true
        }
      },
      "LINK": {
        "type": "link"
      }
    }
  },
  "maxFeatures": 1000,
  "namespaceURL": "http://www.oskari.org"
}

Where:

  • data see below for details optional
  • maxFeatures limit for features that is requested from service (defaults to ?) optional
  • namespaceURL The feature namespace URI (needs more details where this is used) optional

8.9.1 Data

  • filter configures what properties are shown and in what order (refers to actual property names of the feature)
  • locale is an object with keys based on language codes. The language objects are used to map a user-friendly name for the actual properties name.
  • format has keys based on the property names with objects that can define type to decide how to show/format the property value. It can also include noLabel property if you don't want to show the property label to the user and only show the value of the property.

Filter

You can also configure the filter with language specific handling:

{
  "data": {
    "filter": {
        "default": [
            "name",
            "link",
            "image_url"
        ],
        "fi": [
            "name_fi",
            "link",
            "image_url"
        ],
        "en": [
            "name_en",
            "link",
            "image_url"
        ]
    },
    ...
  }
}

Format

Recognized types include:

  • link renders an a-tag using the value as href-attribute
  • image renders an img-tags using the value for src-attribute. The image can also be wrapped to a link-tag so it can be opened in another tab using the params option seen above.
  • html-tags like h1-h5, p, i, b, em

An application based on Oskari can also add handling for additional types in their code base with ValueFormatters:

import { setFormatter } from './path/to/ValueFormatters';
setFormatter('title', (value) => `<h1>${value}</h1>`);

The above would add a formatter for type title that wraps the value to an h1-tag.