geostyler-logo

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:

Final application

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


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:

  1. The UI - a set of UI components to provide user interaction
  2. The Style Parsers - translation entities that translate between different styling formats
  3. 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

Additionally, we have to install a few dependencies, which can be installed in the same manner:

npm i ol@6 antd@3

Through this, we install OpenLayers in version 6 and antd in version 3. 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
npm i geostyler-openlayers-parser
npm i geostyler-wfs-parser

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);
    });

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);
    });

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/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/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);
      });
  }, [sld]);

  useEffect(() => {
    if (!style) {
      return;
    }

    sldParser.writeStyle(style)
      .then((sldStyle) => {
        setSld(sldStyle);
      });
  }, [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:

Read and written SLD

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);
    });

olParser.writeStyle(geostylerStyle)
    .then((olStyle) => {
        // Run your actions with the written style here, e.g.
        console.log(JSON.stringify(olStyle));
    });

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);
      });
  }, [olStyle]);

  useEffect(() => {
    if (!style) {
      return;
    }

    olParser.writeStyle(style)
      .then((newOlStyle) => {
        setOlStyle(newOlStyle);
      });
  }, [style]);

  return (
    <div>
      <p>
        {JSON.stringify(originalOlStyle)}
      </p>
      <p>
        {JSON.stringify(style)}
      </p>
      <p>
        {JSON.stringify(olStyle)}
      </p>
    </div>
  );
}

export default App;

Read and written OpenLayers style

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);
    })
    .then((sld) => {
        // Run your actions with the converted style here
        console.log(sld);
    });

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);
        return sldParser.writeStyle(gsStyle);
      })
      .then((sldStyle) => {
        setSld(sldStyle);
      });
  }, []);

  return (
    <div>
      <p>
        {JSON.stringify(originalOlStyle)}
      </p>
      <p>
        {JSON.stringify(style)}
      </p>
      <p>
        {sld}
      </p>
    </div>
  );
}

export default App;

To SLD converted OpenLayers style

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.

Style Component

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:

Compact Layout

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:

PreviewMap Component

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);
      });
  }, []);

  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);
      });
  }, []);

  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.

The OpenLayers style will be displayed directly in the GeoStyler UI.


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.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.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);
      });

    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.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);
      });

    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):

Attributive Styling. We already created a classification here.


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