Oracle JET is a toolkit for the creation of rich web applications. Many applications will have location-related aspects. Such applications can benefit from advanced map capabilities – for presenting data in maps, allowing users to interact with maps in order to formulate queries, navigate to relevant details or manipulate data. OpenLayers is one of the most prominent open source JavaScript libraries for working with maps in web applications. It provides an API for building rich web-based geographic applications similar to Google Maps and Bing Maps. One of the geographic data providers that OpenLayers works well with is Open Street Map (OSM) – also fully open source.
In this article, I will report on my first steps with OpenLayers and OSM integrated in Oracle JET. In a few simple steps, I will create the JET application illustrated below –a mix of a JET Checkbox Set where countries can be selected and an OpenLayers map that is manipulated from JavaScript to show (and hide) markers for the countries that are selected (and deselected).
This article should provide you with a starting point for working with OpenLayers in JET yourself. Source code for this article can be downloaded from GitHub: https://github.com/lucasjellema/jet-and-openlayers .
Steps:
- create new JET application (for example with JET CLI)
- download OpenLayers distribution and add to JET application’s folders
- configure the JET application for the OpenLayers library
- add a DIV as map container to the HTML file
- add the JavaScript code to initialize and manipulate the map to the ViewModel JS file
In more detail:
1. Create a new Oracle JET application
Follow for example the steps described on the Oracle JET Web Pages: http://www.oracle.com/webfolder/technetwork/jet/globalGetStarted.html
Use
ojet create projectname
to create the new JET application
2. Download OpenLayers Distribution and Add to the JET Application
Download the OpenLayers distribution – a zip-file with the combined JavaScript and CSS files for OpenLayers (4.x) from https://github.com/openlayers/openlayers/releases/
In the JET application’s js/libs directory, create a new directory openlayers and add the new library and any accompanying files to it.
3. Configure the JET application for the OpenLayers library
In the js directory update the js/main-release-paths.json file to include the new library.
"ol": "libs/openlayers/ol-debug", "olcss": "libs/openlayers/ol.css" }
In your RequireJS bootstrap file, typically main.js, add a link to the new file in the path mapping section and include the new library in the require() definition.
paths: //injector:mainReleasePaths { ... 'ol': 'libs/openlayers/ol-debug' } //endinjector
In the same file add a Shim Configuration for OpenLayers
// Shim configurations for modules that do not expose AMD shim: { 'jquery': { exports: ['jQuery', '$'] } ,'ol': { exports: ['ol'] } } }
Finally, add module ‘ol’ to the call to require ad as parameter in the callback function (if you want to perform special initialization on this module):
require(['ojs/ojcore', 'knockout', 'appController','ol', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojtoolbar', 'ojs/ojmenu','ojs/ojmodule'], function (oj, ko, app, ol) { // this callback gets executed when all required modules are loaded ...
Now to actually include map in a View in the JET application:
4. Add a DIV as map container to the HTML file
The View contains a DIV that will act as the container for the map. It also contains a Checkbox Set with checkboxes for five different countries. The checkbox set is data bound to the ViewModel; any change in selection status will trigger an event listener. Additionally, the currentCountries variable in the ViewModel is updated with any change by the user.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.6.4/ol-debug.css" /> <h2>Workarea with Map - Select Countries</h2> <div id="div1"> <oj-checkboxset id="countriesCheckboxSetId" labelled-by="mainlabelid" class="oj-choice-direction-row" value="{{currentCountries}}" on-value-changed="[[selectionListener]]"> <oj-option id="uruopt" value="uy">Uruguay</oj-option> <oj-option id="romopt" value="ro">Romania</oj-option> <oj-option id="moropt" value="ma">Morocco</oj-option> <oj-option id="spaopt" value="es">Spain</oj-option> <oj-option id="indopt" value="in">India</oj-option> </oj-checkboxset> <br/> </div> <div id="map2" class="map"></div>
5. Add JavaScript code to initialize and manipulate the map to the ViewModel JS file
Add OpenLayers dependency in workArea.js:
define( ['ojs/ojcore', 'knockout', 'jquery', 'ol', 'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojbutton', 'ojs/ojlabel', 'ojs/ojcheckboxset'], function (oj, ko, $, ol) { 'use strict'; function WorkAreaViewModel() { var self = this;
The following code defines a countryMap – a collection of five elements (one for each of five countries) that hold longitude and lattitude for each country, as well as a display name and country code (also the key in the map). Subsequenty, an OpenLayers feature is created for each country, and referenced from the countryMap element for later use.
self.currentCountries = ko.observableArray([]); self.countryMap = {}; self.countryMap['in'] = { "place_id": "177729185", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "304716", "boundingbox": ["6.5546079", "35.6745457", "68.1113787", "97.395561"], "lat": "22.3511148", "lon": "78.6677428", "display_name": "India", "class": "boundary", "type": "administrative", "importance": 0.3133568788165, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "India", "country_code": "in" } }; self.countryMap['es'] = { "place_id": "179962651", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "1311341", "boundingbox": ["27.4335426", "43.9933088", "-18.3936845", "4.5918885"], "lat": "40.0028028", "lon": "-4.003104", "display_name": "Spain", "class": "boundary", "type": "administrative", "importance": 0.22447060272487, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Spain", "country_code": "es" } }; self.countryMap['ma'] = { "place_id": "217466685", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "3630439", "boundingbox": ["21.3365321", "36.0505269", "-17.2551456", "-0.998429"], "lat": "31.1728192", "lon": "-7.3366043", "display_name": "Morocco", "class": "boundary", "type": "administrative", "importance": 0.19300832455819, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Morocco", "country_code": "ma" } } self.countryMap['ro'] = { "place_id": "177563889", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "90689", "boundingbox": ["43.618682", "48.2653964", "20.2619773", "30.0454257"], "lat": "45.9852129", "lon": "24.6859225", "display_name": "Romania", "class": "boundary", "type": "administrative", "importance": 0.30982735099944, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Romania", "country_code": "ro" } }; self.countryMap['uy'] = { "place_id": "179428864", "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", "osm_type": "relation", "osm_id": "287072", "boundingbox": ["-35.7824481", "-30.0853962", "-58.4948438", "-53.0755833"], "lat": "-32.8755548", "lon": "-56.0201525", "display_name": "Uruguay", "class": "boundary", "type": "administrative", "importance": 0.18848351906936, "icon": "http://nominatim.openstreetmap.org/images/mapicons/poi_boundary_administrative.p.20.png", "address": { "country": "Uruguay", "country_code": "uy" } }; for (const c in self.countryMap) { // create a feature for each country in the map var coordinates = ol.proj.transform([1 * self.countryMap[c].lon, 1 * self.countryMap[c].lat], 'EPSG:4326', 'EPSG:3857'); var featurething = new ol.Feature({ name: self.countryMap[c].display_name, geometry: new ol.geom.Point(coordinates) }); self.countryMap[c].feature = featurething; }
Then add the code to do the initialization of the Map itself – to be performed when the DOM is ready
$(document).ready ( // when the document is fully loaded and the DOM has been initialized // then instantiate the map function () { initMap(); }) function initMap() { self.elem = document.getElementById("text-input"); self.map = new ol.Map({ target: 'map2', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ], view: new ol.View({ center: ol.proj.fromLonLat([-2, -5]), zoom: 3 }) }); }
and the DIV target container is available:
Also add the code for the selectionListener to be executed whenever countries are selected or deselected.
This code adds OpenLayers features for each of the currently selected countries. Next, construct a layer which contains these features and has a specific style (red circle with big X) associated with it. Finally, add this layer to the map – to have the features displayed in the web page.
// triggered whenever a checkbox is selected or deselected self.selectionListener = function (event) { console.log("Country Selection Changed"); var vectorSource = new ol.source.Vector({}); // to hold features for currently selected countries for (var i = 0; i < self.currentCountries().length; i++) { // add the feature to the map for each currently selected country vectorSource.addFeature(self.countryMap[self.currentCountries()[i]].feature); }//for var layers = self.map.getLayers(); // remove the feature layer from the map if it already was added if (layers.getLength() > 1) { self.map.removeLayer(layers.item(1)); } //Create and add the vector layer with features to the map // define the style to apply to these features: bright red, circle with radius 10 and a X as (text) content var vector_layer = new ol.layer.Vector({ source: vectorSource ,style: function(feature) { var style = new ol.style.Style({ image: new ol.style.Circle({ radius: 10, stroke: new ol.style.Stroke({ color: '#fff' }), fill: new ol.style.Fill({ //color: '#3399CC' // light blue color: 'red' // light blue }) }), text: new ol.style.Text({ text: "X", fill: new ol.style.Fill({ color: '#fff' }) }) }); return style; } } ) self.map.addLayer(vector_layer); }//selectionListener }
References
Source code in GitHub Repo: https://github.com/lucasjellema/jet-and-openlayers
Blog article by Enno Schulte (Virtual7) on adding Socket.io as third part library to a JET 3.x application: http://www.virtual7.de/blog/2017/07/oracle-jet-3-add-third-party-libraries-example-socket-io/
Documentation on adding 3rd party libraries to JET 4.0: https://docs.oracle.com/middleware/jet410/jet/developer/GUID-EC40DF3C-57FB-4919-A066-73E573D66B67.htm#JETDG-GUID-EC40DF3C-57FB-4919-A066-73E573D66B67
OJET Docs Checkbox Set – http://www.oracle.com/webfolder/technetwork/jet/jsdocs/oj.ojCheckboxset.html