GeoStyler Beginner Workshop
Welcome to the GeoStyler Beginner Workshop. This workshop will give you a first overview of the GeoStyler, a web-based tool for interactive styling of geographic data.
In this workshop, we will create a basic application that allows users to style geographic data via a graphical user interface.
The final application will look like this:
Overview
In this workshop we will get to know the three core elements of GeoStyler. No previous knowledge regarding GeoStyler is needed.
- Style Parsers - Translator entities that allow the usage of (nearly) any styling format
- UI Components - Graphical components to interactively edit styles
- Data Parsers - Translator entities that allow the usage of geodata
Every section is based on the results of the previous ones. So it makes sense to go through the workshop from start to end, if you do this workshop for the first time.
Setup
The following instructions and exercises require some already installed programs on your computer. Please make sure that the following programs are installed:
Authors
- Jan Suleiman (suleiman@terrestris.de)
Basics
Before diving into the usage of GeoStyler, we will first have a brief look at some prerequirements:
We will be using the node package manager (npm) for the installation of GeoStyler. Furthermore, we will use the JavaScript standard EcmaScript 6 as programming language when developing. We therefore suggest to do so as well, when using GeoStyler.
Since GeoStyler is a React-based component library, it is crucial to take a look at React.
GeoStyler Architecture
GeoStyler consists of three main building blocks:
- The UI - a set of UI components to provide user interaction
- The Style Parsers - translation entities that translate between different styling formats
- The Data Parsers - translation entities that translate between different data formats
UI
The GeoStyler UI allows the styling of geographic data via a graphical user interface. We provide a multitude of components, which are customizable and which can be put together to fit many project specific requirements. At the end of the day, you can decide which map elements should be styleable, and which not.
The centerpiece of the GeoStyler UI is the Style
component, at which we will take a look at a later stage of this workshop.
Style Parsers
Style Parsers allow translating between different styling formats and the internal GeoStyler styling format. They behave just like dictionaries and by that allow the GeoStyler UI being usable with many different styling formats, like SLD, OpenLayers or QML (the QGIS style), just to name a few.
Data Parsers
Data Parsers behave just like Style Parsers, with the only difference that they only translate from existing data formats, such as SHP or GeoJSON, to our internal GeoStyler data format. Data Parsers are being used to enable attributive styling.
First Steps
After talking about the basics, we will now create a React application and we will install GeoStyler
Create React Application
As mentioned before, the GeoStyler UI is a React library. So before using GeoStyler, we should create a basic React application that we then extend step by step. To do so, just navigate into any folder you like and execute the following command on the terminal:
npx create-react-app geostyler-app
In this case geostyler-app is our application name and you can give it any name you like. A folder with the same name should have been created within your current directory. With the help of the command above, a basic React application should have been created within that directory. Also, all dependencies and some neat developer tools such as hot-reloading were added as well. These tools should facilitate the development process for us.
Now, move to the project directory with following command
cd geostyler-app
and start the development server with
npm start
You should now be able to see the application on http://localhost:3000 in your browser. It should look like this:
Next, we remove the existing code, so we can start with an empty application. To do so, simply replace
the content of src/App.js
with following code:
import React from 'react';
function App() {
return (
<div></div>
);
}
export default App;
In the next step, we will show how to install GeoStyler.
Install GeoStyler
Install GeoStyler UI
Since we are using npm, the installation of GeoStyler is straightforward. Open a terminal and run the following command from the root of your application directory:
npm i geostyler@7.2.1
Additionally, we have to install a few dependencies, which can be installed in the same manner:
npm i ol@6 antd@4
Through this, we install OpenLayers in version 6 and antd in version 4. OpenLayers will be used for the displaying of (preview-) maps and antd provides the very basic ui components, such as buttons.
Install GeoStyler Parsers
The Style and Data Parsers have to be installed separately. This makes sure that you really just installed those parsers that you actually want to use. In this workshop, we will use the SLD and OpenLayers Style Parser, as well as the WFS Data Parser.
Execute following command to install the parsers:
npm i geostyler-sld-parser@3.0.1
npm i geostyler-openlayers-parser@3.0.0
npm i geostyler-wfs-parser@1.0.1
Style Parsers
In this chapter, we will explain how to use the different Style Parsers, in order to translate between different styling formats. Since all Style Parsers have a common interface, it does not matter which parser is being used. The required steps and method names are always the same.
Parsing SLD
In order to parse SLD, we need two things. The geostyler-sld-parser
and a SLD that we want to parse.
info As a reminder: A Styler Parser always translates between a styling format and the internal GeoStyler style format. In this case, we will read and write SLD.
We already installed the geostyler-sld-parser
via
npm i geostyler-sld-parser
in a previous chapter. So now, we just have to import it into our application.
To do so, add following statement to src/App.js
:
import SldParser from 'geostyler-sld-parser';
Next, the SldParser has to be instantiated. This can be done via
const sldParser = new SldParser();
In order to read a SLD, the method readStyle
of the sldParser instance will be used. This method expects
a SLD string as argument and returns a Promise with the matching GeoStyler style.
sldParser.readStyle(sld)
.then((geostylerStyle) => {
// Run your actions with the parsed style here, e.g.
console.log(geostylerStyle.output);
});
In order to write a SLD, we use the method writeStyle
of the sldParser instance. This methods expects
a GeoStyler style object as argument and returns a Promise with the matching SLD string.
sldParser.writeStyle(geostylerStyle)
.then((sld) => {
// Run your actions with the written style here, e.g.
console.log(sld.output);
});
We can manually store an example SLD in a variable or we can dynamically load a SLD available remotely via fetch()
.
E.g. a simple point style.
fetch('https://raw.githubusercontent.com/geostyler/geostyler-sld-parser/master/data/slds/1.0/point_simplepoint.sld')
.then((response) => {
return response.text();
})
.then((sld) => {
// Run your actions with the fetched style here, e.g.
console.log(sld);
});
Applying this concept to our application, the code should look like below. The variables for the written and read styles were declared as React-state-variables and we can display their contents in the application.
import React, { useState, useEffect } from 'react';
import SldParser from 'geostyler-sld-parser';
const sldParser = new SldParser();
function App() {
const [originalSld, setOriginalSld] = useState('');
const [sld, setSld] = useState('');
const [style, setStyle] = useState();
useEffect(() => {
fetch('https://raw.githubusercontent.com/geostyler/geostyler-sld-parser/master/data/slds/1.0/point_simplepoint.sld')
.then((res) => {
if (res.ok) {
return res.text();
}
})
.then((text) => {
setOriginalSld(text);
setSld(text);
});
}, []);
useEffect(() => {
if (!sld) {
return;
}
sldParser.readStyle(sld)
.then((gsStyle) => {
setStyle(gsStyle.output);
});
}, [sld]);
useEffect(() => {
if (!style) {
return;
}
sldParser.writeStyle(style)
.then((sldStyle) => {
setSld(sldStyle.output);
});
}, [style]);
return (
<div>
<p>
{originalSld}
</p>
<p>
{JSON.stringify(style)}
</p>
<p>
{sld}
</p>
</div>
);
}
export default App;
Your application should now look as follows:
The first section shows the original SLD. The second section shows the read SLD in the GeoStyler format. The third section shows the written SLD.
Parsing an OpenLayers Style
Parsing an OpenLayers style requires the same steps as parsing SLDs.
We will again use the readStyle
and writeStyle
methods of the parsers. However, this time, we will use
the geostyler-openlayers-parser
and do not use an SLD string, but rather an OpenLayers style object.
The installation of the geostyler-openlayers-parser
was already done in a previous chapter via
npm i geostyler-openlayers-parser
Next, we have to import the parser
import OlParser from 'geostyler-openlayers-parser';
and instantiate it.
const olParser = new OlParser();
Now, we just need an OpenLayers style that should be parsed
import { Stroke, Fill, Style, Circle } from 'ol/style';
const olStyle = new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
This can now be parsed via readStyle
and writeStyle
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
// Run your actions with the read style here, e.g.
console.log(geostylerStyle.output);
});
olParser.writeStyle(geostylerStyle)
.then((olStyle) => {
// Run your actions with the written style here, e.g.
console.log(JSON.stringify(olStyle.output));
});
For our application, this should work as follows:
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style, Circle } from 'ol/style';
import OlParser from 'geostyler-openlayers-parser';
const olParser = new OlParser();
function App() {
const originalOlStyle = new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [olStyle, setOlStyle] = useState(originalOlStyle);
const [style, setStyle] = useState();
useEffect(() => {
olParser.readStyle(olStyle)
.then((gsStyle) => {
setStyle(gsStyle.output);
});
}, [olStyle]);
useEffect(() => {
if (!style) {
return;
}
olParser.writeStyle(style)
.then((newOlStyle) => {
setOlStyle(newOlStyle.output);
});
}, [style]);
return (
<div>
<p>
{JSON.stringify(originalOlStyle)}
</p>
<p>
{JSON.stringify(style)}
</p>
<p>
{JSON.stringify(olStyle)}
</p>
</div>
);
}
export default App;
The first section shows the original OpenLayers style. The second section shows the parsed OpenLayers style as GeoStyler style. The third section shows the written OpenLayers style.
Converting Styles
Since every Style Parser can both read and write, we are also able to convert between different formats. This can come in very handy if you already have a bunch of styles in a certain styling format, but you now want to use another styling format.
In this section, we will convert OpenLayers styles to SLD, in order to persist them as files.
In order to convert styles, we have to first read the input style and create thereby a GeoStyler style object. This object can then be used to convert to the output style format, by using the appropriate Style Parser.
Imagine you want to translate a text from English to French but you only have a English <-> German
and a German <-> French
dictionary.
You cannot directly translate from English to French, but you can translate from English to German in a first and from German to French in
a second step. With GeoStyler we do exactly the same. At first, we translate from any input style to the internal GeoStyler format. Then,
we translate from our internal GeoStyler format to any output style. In contrast to translating texts, we do not have to care for grammar,
as can thereby be sure that the translations actually make sense.
Let’s import the required parsers
import OlParser from 'geostyler-openlayers-parser';
import SldParser from 'geostyler-sld-parser';
and instantiate them
const olParser = new OlParser();
const sldParser = new SldParser();
Next, we need an OpenLayers style that we want to convert
import { Stroke, Fill, Style, Circle } from 'ol/style';
const olStyle = new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
At first, we read the OpenLayers style with the geostyler-openlayers-parser
and use the output as argument for the
write method of the geostyler-sld-parser
.
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
return sldParser.writeStyle(geostylerStyle.output);
})
.then((sld) => {
// Run your actions with the converted style here
console.log(sld.output);
});
For our application, it will work as follows:
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style, Circle } from 'ol/style';
import SldParser from 'geostyler-sld-parser';
import OlParser from 'geostyler-openlayers-parser';
const sldParser = new SldParser();
const olParser = new OlParser();
function App() {
const originalOlStyle = new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [sld, setSld] = useState('');
const [style, setStyle] = useState();
useEffect(() => {
olParser.readStyle(originalOlStyle)
.then((gsStyle) => {
setStyle(gsStyle.output);
return sldParser.writeStyle(gsStyle.output);
})
.then((sldStyle) => {
setSld(sldStyle.output);
});
}, []);
return (
<div>
<p>
{JSON.stringify(originalOlStyle)}
</p>
<p>
{JSON.stringify(style)}
</p>
<p>
{sld}
</p>
</div>
);
}
export default App;
The first section shows the original OpenLayers style. The second section the parsed OpenLayers style as GeoStyler style. The third section shows the written SLD style.
Using UI Components
In the previous section, we showed how to use Style Parsers. In this chapter, we will take a look at how to create and customize different UI components, as well as how to connect these with the Style Parsers.
Import GeoStyler Component
In order to use GeoStyler components, we have to import these via the import
statement.
Afterwards, we are able to use the respective component within our application.
import { Component } from 'geostyler';
...
return (
<div>
<Component property1=... />
</div>
)
It is very important to take a look at the documentation of the used component and to set the properties accordingly.
We will use the Style
component in this chapter. The documentation of the component can be
found here.
The Style
component does not have any required properties, so we can directly use it in our application.
import React from 'react';
import { Style } from 'geostyler';
import 'antd/dist/antd.css';
function App() {
return (
<div>
<Style />
</div>
);
}
export default App;
Afterwards you should be able to see the Style
component and you should be able to edit styles.
Through the compact
property, we are able to use the tabular layout of the Style
component. To do so, we just have to
set the property compact
to true
.
<Style
compact={true}
/>
Your application should now look as follows:
PreviewMap Component
The PreviewMap
component displays a created style in the map.
In order to do so, the component expects a style
property that contains the GeoStyler style to display.
To get the style edited in the Style
component, we use the onStyleChange
method and store the style
in a state-variable.
import React, { useState } from 'react';
import { Style } from 'geostyler';
import 'antd/dist/antd.css';
function App() {
const [style, setStyle] = useState();
return (
<div>
<Style
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
</div>
);
}
export default App;
Afterwards, we can import the PreviewMap
component
import { PreviewMap } from 'geostyler';
and add it to the application
import React, { useState } from 'react';
import { Style, PreviewMap } from 'geostyler';
import 'antd/dist/antd.css';
function App() {
const [style, setStyle] = useState();
return (
<div>
<Style
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
{
style && (
<PreviewMap
style={style}
/>
)
}
</div>
);
}
export default App;
The application should now look like this:
Connect Style Parsers with UI
We can connect Style Parsers with the UI in order to display and edit existing styles in the UI.
In this section, we will connect an OpenLayers style with the UI. We will make use of the
geostyler-openlayers-parser
in order to read the OpenLayers style. The general approach is the same for all parsers.
At first, we have to import the geostyler-openlayers-parser
import OlParser from 'geostyler-openlayers-parser';
and then instantiate it
const olParser = new OlParser();
Then, we need an OpenLayers style object
import { Stroke, Fill, Style, Circle } from 'ol/style';
const olStyle = new Style({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
which we then can parse into the GeoStyler style format.
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
// Run your actions with the parsed style here
});
In our application we make use of React’s state-variables. Please notice the renaming of the OpenLayers class
Style
to OlStyle
to avoid naming conflicts with the GeoStyler Style
component.
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style as OlStyle, Circle } from 'ol/style';
import { Style, PreviewMap } from 'geostyler';
import OlParser from 'geostyler-openlayers-parser';
import 'antd/dist/antd.css';
const olParser = new OlParser();
function App() {
const olStyle = new OlStyle({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [style, setStyle] = useState();
useEffect(() => {
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
setStyle(geostylerStyle.output);
});
}, []);
return (
<div>
<Style
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
{
style && (
<PreviewMap
style={style}
/>
)
}
</div>
);
}
export default App;
Afterwards we set the style
property of the Style
component to the parsed style.
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style as OlStyle, Circle } from 'ol/style';
import { Style, PreviewMap } from 'geostyler';
import OlParser from 'geostyler-openlayers-parser';
import 'antd/dist/antd.css';
const olParser = new OlParser();
function App() {
const olStyle = new OlStyle({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [style, setStyle] = useState();
useEffect(() => {
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
setStyle(geostylerStyle.output);
});
}, []);
return (
<div>
<Style
style={style}
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
{
style && (
<PreviewMap
style={style}
/>
)
}
</div>
);
}
export default App;
The style defined in the variable olStyle
will now be displayed directly in the Style
component of our application, on startup.
Data Parsers
In this chapter, we will show how to use Data Parsers for attributive styling.
Parsing WFS
In contrast to Style Parsers, Data Parsers can only read data formats, not write them. This means, we can only convert existing data formats into the GeoStyler data format, not the other way around.
Therefore, Data Parses only have one single method - readData
. This method is the same for all Data Parsers and
always returns a GeoStyler data object.
In this chapter, we will show how to parse WFS. The parsed WFS will then be used in the next chapter to enable attributive styling in the UI.
Since we already installed the geostyler-wfs-parser
via
npm i geostyler-wfs-parser
in a previous chapter, we just have to import it via
import WfsParser from 'geostyler-wfs-parser';
and instantiate it.
const wfsParser = new WfsParser();
Afterwards, a WFS can be read via
wfsParser.readData(wfsParams)
.then((geostylerData) => {
// Run your actions with the read WFS here, e.g.
console.log(JSON.stringify(geostylerData));
});
The variable wfsParams
expects at least the properties url
, version
, typeName
and srs
of a WFS.
const wfsParams = {
url: 'https://ows-demo.terrestris.de/geoserver/terrestris/ows',
version: '1.1.0',
typeName: 'terrestris:bundeslaender',
srsName: 'EPSG:4326'
};
In our application, we can use the WFS Data Parser as follows:
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style as OlStyle, Circle } from 'ol/style';
import { Style, PreviewMap } from 'geostyler';
import OlParser from 'geostyler-openlayers-parser';
import WfsParser from 'geostyler-wfs-parser';
import 'antd/dist/antd.css';
const olParser = new OlParser();
const wfsParser = new WfsParser();
function App() {
const wfsParams = {
url: 'https://ows-demo.terrestris.de/geoserver/terrestris/ows',
version: '1.1.0',
typeName: 'terrestris:bundeslaender',
srsName: 'EPSG:4326'
};
const olStyle = new OlStyle({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [style, setStyle] = useState();
const [data, setData] = useState();
useEffect(() => {
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
setStyle(geostylerStyle.output);
});
wfsParser.readData(wfsParams)
.then((gsData) => {
setData(gsData);
});
}, []);
return (
<div>
<Style
style={style}
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
{
style && (
<PreviewMap
style={style}
/>
)
}
</div>
);
}
export default App;
Connect Data Parsers With The UI
To enable attributive styling, we have to add the data format we already read in to the Style
component. This can be done
via the data
property.
//...
<Style
data={data}
/>
//...
We can do the same for the PreviewMap
component, so that the preview also displays the data.
//...
<PreviewMap
style={style}
data={data}
/>
//...
In our application this looks as follows:
import React, { useState, useEffect } from 'react';
import { Stroke, Fill, Style as OlStyle, Circle } from 'ol/style';
import { Style, PreviewMap } from 'geostyler';
import OlParser from 'geostyler-openlayers-parser';
import WfsParser from 'geostyler-wfs-parser';
import 'antd/dist/antd.css';
const olParser = new OlParser();
const wfsParser = new WfsParser();
function App() {
const wfsParams = {
url: 'https://ows-demo.terrestris.de/geoserver/terrestris/ows',
version: '1.1.0',
typeName: 'terrestris:bundeslaender',
srsName: 'EPSG:4326'
};
const olStyle = new OlStyle({
stroke: new Stroke({
color: 'rgba(255, 255, 255, 1.0)',
width: 1
}),
fill: new Fill({
color: 'rgba(0, 0, 0, 1)'
}),
image: new Circle({
fill: new Fill({
color: 'rgba(255, 0, 0, 1.0)'
}),
radius: 5
})
});
const [style, setStyle] = useState();
const [data, setData] = useState();
useEffect(() => {
olParser.readStyle(olStyle)
.then((geostylerStyle) => {
setStyle(geostylerStyle.output);
});
wfsParser.readData(wfsParams)
.then((gsData) => {
setData(gsData);
});
}, []);
return (
<div>
<Style
style={style}
data={data}
compact={true}
onStyleChange={(newStyle) => {setStyle(newStyle)}}
/>
{
style && (
<PreviewMap
style={style}
data={data}
/>
)
}
</div>
);
}
export default App;
By that, we activated data dependent features, such as classifications. The application should now look as follows
(notice the enabled Classification
button):
Summary
In this workshop you learned how GeoStyler and its core components work.
This included the usage of Style Parsers, in order to convert between different styling formats, such as SLD or OpenLayers styles. But also connecting already existing styles to the GeoStyler UI.
Additionally, you learned how to use and customize UI components.
Finally, you got a brief overview of using Data Parsers and how they enable attributive styling with the GeoStyler UI.
We hope you enjoyed the workshop and you could get a first impression working with GeoStyler. We also hope that this workshop motivates you to use GeoStyler in your own application.
If you have questions, feedback, feature requests or noticed a few bugs, just create an Issue in the GeoStyler Repository.
Imprint
This workshop is licensed under the CC BY-SA license. If you have questions, you can contact us on GitHub @terrestris, via E-Mail info@terrestris.de, reports@geostyler.org or via telephone 0228 – 962 899 51.
Authors
- Jan Suleiman (suleiman@terrestris.de)