Custom popups in Geoserver
When we upload any layer to Geoserver, we can check the data on click by going in to Layer preview and display layer as Openlayers. Once we click on feature, Geoserver returns information table as following.
This is fine for testing purpose, but what about using this in production ready app? There are bunch of things that you may think when you look at this
Till now, to solve all of these problems I was using following hack.
Instead of loading this HTML response in my app, I was requesting data in GeoJSON and then I was styling it in my HTML.
But that means if I have to use same layer in some another app, I will have to style the popup again. Frustrating right??
GetFeatureInfo Templates
Geoserver allows us to overwrite default Geoserver feature info template. These templates are written in Freemaker which is a template engine for JAVA. WAIT WAIT!! Before you close the blog to watch another cat video, I want to tell you that I also don't know/like JAVA. But freemaker is easy to learn ( at least to solve our purpose of creating custom template in Geoserver)
By Default, Geoserver uses 3 templates for HTML output.
Freemaker files has extension of .ftl , these files needs to be places in the Data Directory of Geoserver.
If you need to change behaviour of all layers in the Geoserver, you can simply create templates folder in data directory and put .ftl files in it.
Remember, Names of the files should be exactly same
Creating Custom Templates
Header template
For this exercise we'll take default layer in Geoserver. If your Geoserver is running, hit following URL
https://localhost:8080/geoserver/topp/wms?service=WMS&version=1.1.0&request=GetMap&layers=topp%3Astates&bbox=-124.73142200000001%2C24.955967%2C-66.969849%2C49.371735&width=768&height=330&srs=EPSG%3A4326&styles=&format=application/openlayers
Once opened, turn on Inspect Element and hover on the result table generated by clicking on any of the feature. There you will see that , HTML is generating a <table> tag with class name as featureInfo .Inside the table tag we have various tags such as <caption> <th> <tr> <td> <tbody> .In this exercise, we'll focus on keeping this tags same but only updating class information of it.
Now head over to <GeoServer_data_dir>/templates and create new file header.ftl. For this blog, we'll try to create a table which looks like this
To do this, we can use the featureInfo class and sibling CSS Selector . This file is exactly like HTML file but it ends at open body tag.
<html>
<head>
<title>Modified Geoserver template</title>
</head>
<style type="text/css">
.featureInfo{
background-color: beige;
}
.featureInfo th {
background-color: black;
color: chartreuse;
}
.featureInfo td {
color: slateblue;
}
</style>
<body>
Note : headers.ftl file will always end with open body tag. no need to close body tag and html tag
Once you add the code and restart the Geoserver, head back to layer preview using same link
Footer template
To create custom footer, create new file in <GeoServer_data_dir>/templates and name if footer.ftl. Format of this file will be as follows
<#--
Add custom code here
-->
</body>
</html>
For exercise, we'll simply write our name in footer.
<h2> GeoServer maintained by - Krishna Lodha </h2>
</body>
</html>
Again restart Geoserver and hit the link
领英推荐
Content template
Content will be little bit complicated compared to other two, as this will require us to write conditions, parameters, etc.
For this exercise, we'll try to do following things
This will help you understand the logic we should use while creating such file.
You can read more about available data models here
Ultimately the content.ftl file will look like this
<table class="featureInfo" >
<tr>
<#list type.attributes as attribute>
<#if !attribute.isGeometry>
<th >${attribute.name}</th>
</#if>
</#list>
<th >Population Density</th>
</tr>
<#list features as feature>
<#assign area = 0>
<#assign pop = 0>
<tr>
<#list feature.attributes as attribute>
<#if !attribute.isGeometry>
<td>${attribute.value}</td>
</#if>
<#if attribute.name == 'LAND_KM'>
<#assign area = attribute.value?number>
</#if>
<#if attribute.name == 'PERSONS'>
<#assign pop = attribute.value?number>
</#if>
</#list>
<td>${(pop/area)} / KM<sup>2</sup></td>
</tr>
</#list>
</table>
<br/>
And we'll have something like this
Similar to this we can add conditions for showing images.
We can also use external libraries such as bootstrap in the templates.
header.ftl
<html>
<head>
<title>GeoServer GetFeatureInfo output</title>
</head>
<link rel="stylesheet" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style type="text/css">
.featureInfo{
background-color: beige;
}
.featureInfo th {
background-color: black;
color: chartreuse;
}
.featureInfo td {
color: slateblue;
}
</style>
<body>
footer.ftl
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
content.ftl
<table class="table table-bordered" >
<thead class="thead-dark">
<tr>
<#list type.attributes as attribute>
<#if !attribute.isGeometry>
<th >${attribute.name}</th>
</#if>
</#list>
<th >Population Density</th>
</tr>
</thead>
<tbody>
<#list features as feature>
<#assign area = 0>
<#assign pop = 0>
<tr>
<#list feature.attributes as attribute>
<#if !attribute.isGeometry>
<td>${attribute.value}</td>
</#if>
<#if attribute.name == 'LAND_KM'>
<#assign area = attribute.value?number>
</#if>
<#if attribute.name == 'PERSONS'>
<#assign pop = attribute.value?number>
</#if>
</#list>
<td>${(pop/area)} / KM<sup>2</sup></td>
</tr>
</#list>
</tbody>
</table>
<br/>
It will create table with bootstrap css
The Problem
Even though this template is working fine with this layer, if you try to open another layer and check the response, Geoserver will through error.
It's because columns LAND_KM , PERSONS does not exist. To solve this issue we'll have to understand logic of adding template files in directory. The hierarchy in which Geoserver looks for template file is as follows
So if we decide to move files we created in GEOSERVER_DATA_DIR/workspaces/topp/states_shapefile/states/
folder and restart the Geoserver, we'll see that the style is only now visible in states and not in any other layer
Similar to HTML, We can also create custom template for GeoJSON. Please write in comments if you want blog for GeoJSON.
Realtor Associate @ Next Trend Realty LLC | HAR REALTOR, IRS Tax Preparer
2 年Love this.