A CesiumJS Starter Kit with GeoServer
If you like CesiumJS and want to get started quickly using it with something like GeoServer, then this write-up is for you. This blog has a corresponding GitHub repo you can pull down via git clone
and use to get going quickly with CesiumJS to have a map that looks like the below picture in a matter of minutes. You could use this starter kit without GeoServer but you would need to edit the JS to not allow calling GeoServer. The rest will work for you.
If you have not read my other post on how to setup a local GeoServer and load maps, you may want to do that first. It is about 15 minutes to read that other post because it involves so many steps. (Note to self: put that setup with maps into a Dockerfile or Buildah script…) This post builds on the previous blog information to show adding local map layers incrementally with event triggers as an interactive map! And it has a menu on top that can show and hide as well as a way to add shapes to the map canvas and track them in the menu. The best way to learn all this is to clone the repo, pull down the information, update the pointers to GeoServer and use the Developer Tools in Chrome, Firefox, or your favorite browser to see the cesium-scripts.js file in action. That information is outlined below to get you started.
To get used to CesiumJS and get a base of knowledge to use this article, I recommend you go to their great Getting Started tutorial. It is worth a few minutes to get you going. If you are familiar with CesiumJS then keep on reading.
Introduction on the Code and the GitHub Repo
I did this with the latest CesiumJS as of June 2019. And I did not want to add jQuery and all that heavy stuff in here I was not going to use. So I did plain JavaScript functions. I could have put these into a class structure to group them better for sure. But for now they are all in one main cesium-scripts.js file for this. In my day-job I made so many functions on top of CesiumJS in addition to these that I ended putting things into classes for maintainability and structure. I was getting tired of scrolling and I knew I needed to fix it now for less headaches later. That could be one way to update this if you wish to use it. Feel free!
The setup in the GitHub repo is yours to use, fork, clone, download, etc. whether for your job or to just learn. I did not do enough “try / catch” blocks and I definitely did not do enough input validation on the forms. So please make this secure if you plan to use it anywhere for real. I may go back and update this GitHub repo to do just that as I don’t trust people will do that 100% themselves. That is one big caveat on using this code: add your own input validation and security to it to make it as bulletproof as possible!
Setup GeoServer with CORS
First things first: GeoServer has to be able to respond from a client request on another website/port for this code to work. I will assume you have a local GeoServer running with some maps to serve up. If not skip to the next section. I use http://localhost:9000/ for my local CesiumJS HTML file and http://localhost:8080/geoserver/ for my GeoServer. Calling the built in Bing Maps or other providers of CesiumJS is fine. But to call a local GeoServer as we will talk about below is basically “8080” calling to “9000” which means a different “domain”. So it requires Cross Origin Resource Sharing (CORS) to be enabled in GeoServer. This article here for the GeoServer docs (at the bottom) has a section titled “Enable CORS”. Follow the steps in there. Basically in the geoserver main directory, there is a filewebapps/geoserver/WEB-INF/web.xml
that has 2 areas where you can uncomment configurations that say “uncomment to enable CORS”. Uncomment those 2 sections (there are 2 specific sections to check), save the web.xml file, and then restart GeoServer to allow this to work.
Git Clone the Repo
You can run git clone
against https://github.com/Cingulara/cesium-starterkit.git to pull down the files we are talking about here. Feel free to fork that, clone it, make changes, suggest fixes, etc. This is a starter kit. STARTER kit. It is not an end-all-be-all library to do anything GIS related. Treat it as such.
To get started you can just run a web server locally with the index.html in the root of the server and browse to it with your favorite modern web browser. I did this with the python -m SimpleHTTPServer 9000
command on my Mac in the root of the GitHub repo directory I have locally. so mine is http://localhost:9000/ for my CesiumJS frontend. Do whatever you like to do in that regard. If you are a developer you probably have a favorite way to do this, even if it is with a Go program that has a file listener to return HTML, JS and CSS. Remember, whether you have GeoServer running locally you can run this code in a local web browser and use it against the main CesiumJS library. Just do not select the Map Layer options for roads or hybrid (or comment out that HTML and JS). We will go through a couple examples of the code below to get you started. The rest you should try on your own.
Explaining the GitHub Repo
The GitHub repo has a few things to point out. In the root is the index.html file that contains the main setup for CesiumJS as well as links to the JSColor plugin for choosing drawing object colors. The CSS has a single index.css for styles on the menu parts, the style of the main container for the map and modal window CSS pieces. The lib folder only has the JSColor plugin file to keep local. And the js folder has the extra JavaScript file cesium-scripts.js to enable the map, add event listeners to buttons and the map canvas, as well as calls to the CesiumJS API to do things on the map.
This file cesium-scripts.js is the best file to put breakpoints on within the developer tools in a modern web browser and see the interactions, the calls, the variables, and the inner workings of the CesiumJS API with the web map servers IMO. You are free to edit whatever you want in your local clone or fork. I personally think the best way to use this is for you to fork this repo, do your work locally, add to it on your own fork (branching if you deem it necessary), and see what you can do and extend for your own education. That is the best way to learn. This blog and repo are meant to jump start that process.
Update the URL for your Local GeoServer
Ok, so now that you have something working you can go to the area that has the `Cesium.WebMapServiceImageryProvider` area to point to your local GeoServer setup. In the included JS file there are two areas to consider: one for the worldMap and one for the detailedMaps variables. In those sections around line 44 in the cesium-scripts.js are some pieces to look at: the url, the parameters, and the layers to use. The URL is the root URL for the web mapping server (i.e. http://localhost:8080/geoserver/wms) and the Layers are listed in the Layers area of GeoServer. The image below outlines the layer name for the roads we will show later. The layer ‘florida:NE1_50M_SR_W’ is the world map layer and is called out in the var worldMap=… code. I only did one layer at a time, but you can actually add more layers in the “layers :” area if you wish. If you looked at the previous blog post on GeoServer you can start to frame in your mind how these parameters are formed into the proper URL for accessing the GeoServer web mapping server.
If you have other layers to try put them in here, kill your web cache, reload and see what you get! The menu we have in index.html around line 32 has a changeLayers()
function that passes a parameter. These two things work together, the code area for the map layers and the HTML menu. This JS function turns on and off the layers with a show: true or show: false update on the CesiumJS object around line 391 in the cesium-scripts.js file. This is not the only way to do it, it is the way I did it to get used to it. You can make this contain whatever layers you want. You could even add layers programmatically, save the Id, and show/hide based on a search of the viewer.imageryLayers if you wanted. And dynamically update the menu and radio buttons to toggle them on and off. Again this is a STARTER kit. I am working to show you how it works. In the current menu setup, you click the radio button and based on that, it calls the CesiumJS API and shows/hides layers on the map canvas. Go try it! The picture below is for the Roads to show up on top of the default Bing maps in the Tampa, Florida area from the “Roads” radio button in the menu.
Explaining the Menu
I keep talking about the menu but I have not gone over it much. The menu in the picture above shown scrolls down when you click on the “Cesium Options” floating text. It is minimized when you load the page by default. Click on it to expand and then click on it again to hide the map, a.k.a. toggle it. I have the menu setup with sections as well so you can group things and show/hide them also. You can click on a section title in teal green to show/hide that section and group it accordingly. I am not going to explain all the CSS and JS to do it. You can check the code, the CSS, the HTML and breakpoint it. It is not so complex that it takes a long time to figure out. I believe in the K.I.S.S. method. And the menu is initially setup in the main function in the cesium-scripts.js file around line 140. Again feel free to update this for yourself. This is just to get you “started” with some ideas. Hence the name…
One cool thing in the menu we will go over below is that as you click the buttons in the “Add Shapes” area to add shapes, a few things happen in the menu. The buttons have events waiting for them, and once you draw on the map the “Shapes” area fills in what what you drew! It has a scrollable div to list them as you add more and more shapes, and the count of them shows in the main title of that menu section. It is easier to see when you try it. Or you can just read below and we explain it. That model can be expanded to other sections on the menu. For my day-job work, I have done just that with points of interest, waypoints, and other things grouped like this with a total count. You may have other ideas to use this as well once you get “started”. See what I did there?? :)
Drawing on the Map
Drawing shapes on the map has a few moving parts. I am going to explain them in steps so you see how it is built and working. The end result is pretty cool IMO (since I am writing this!) and I love the JSColor plugin. Love it. Soooo easy to use. The 50,000 ft view on this functionality is you click a button to launch a drawing options window. Then you set options of the Name, Description and Color. And then you click “Draw” to put it on the map. Depending on what you are drawing, you click a few times on the map and then the shape appears.
As an example, in the code around line 456 of the cesium-scripts.js file there is a listener for clicking the “Draw Circle” button on the menu. When you click it, it does some housekeeping (turn off keyboard commands, kill all earlier listeners, show the location on the map) and then opens the options modal. When you pick your options and click the Draw button, you then have the mouse over the map showing your location. Click (when I say “click” I mean left-click) the mouse at the center of your circle. Then move the mouse to the side/top/bottom and see it draw a line. This will roughly be the radius of your circle, the “width”. Click the mouse a 2nd time and the 2 sets of latitude/longitude are used to calculate the circle. When all pieces are known the “add entity” CesiumJS API is called for the ellipse with a width and height the same (a circle would have the same) and the circle is drawn on the map. There are many moving parts here you can study in the JS by breakpointing on the lines around 475 and stepping through them. I can do a 25-page write-up or you could see for yourself. I vote option 2. Teach a person to fish in essence!
For now as far as drawing options on the map there is a Circle and a Polygon. Feel free to add more. The polygon works in a similar way however there is a trick to it. Click the polygon menu button, choose your options, then click on the map mark the edge points of your polygon. You will in essence “draw lines” between the points and fill in the center. For the final point of your polygon you double-click the left mouse button and your polygon is drawn and filled in. And all the points are removed from the map (line 532). That is a way to use a few of the CesiumJS API calls as well as JavaScript to make this map interactive. If you notice the handler of this, I had to put the double-click listener first in the order of event listeners. If not, then a single-click is registered first and the double-click never fires. That took a couple minutes to figure out.
When you add a shape the actual shape name and latitude and longitude (which are tracked constantly in this setup as currentLatitude and currentLongitude) show up in the Menu under “Shapes” and the count is updated. You will see buttons under the shape title to “go to” or fly to that coordinate pair. You also can delete the shape (with a confirm option) as well as Edit the shape. The Edit only lets you change color, name, and description. You could extend that to allow moving its location but I will leave that up to you to implement!
Taking a SnapShot
I wanted to have a way to save the map to a PNG file. I figured you could do it, but I had to search for this for a bit. And I saw this code in a few forums in various ways. I did it the way the “btnSnapshotElement.addEventListener” is coded around line 410 in the cesium-scripts.js file. You set a resolution (See the CesiumJS documentation on this), a timeout on when to take the snapshot in milliseconds, and then setup the screenshot. Then you take it. The “download” in this code will actually download a PNG file to your browser and show up as a file like below. I do this when I draw on the map (my day-job lets me put tracks, trackpoints, shapes, text, points of interest, etc.) and this allows a download for a presentation, email, report, etc. It is a very useful function IMO. So I included it in this.
Ways to Extend this Starter Kit for Your Purposes
This is just a small piece of what you can build with the CesiumJS API and your own JavaScript or JS libraries included. I know there are ways to extend this as I have done that very thing in my day-job. Some of the things I added were listeners to draw a line based on a menu button click, drawing a box by choosing 2 corners, as well as adding text with a font face and font size for the map by adding additional fields to the drawing options modal. For example, to draw a line you could do the following:
- click a button on the menu to say ‘listen out for a click to start a line’
- click to put a point and then draw a line to wherever the mouse pointer is currently
- have a listener for the mouse movement to extend the line as you move the mouse
- have an event listener to listen for the second click
- using those two points draw the line
There are a few ways to do things just like this to draw a box (you click the upper left corner and lower right corner and mix the lat/lon to pass to the function to draw a polygon), draw an ellipse (click on the center, then click above and click to the side to find the height and width of the ellipse, or add text. All of these can be used with mouse click event listeners in a similar fashion to what is in this code repo for interacting with your map and making your GIS application come alive.
You could even use the “draw a line” with the calculateDistance
function in the included JavaScript file (around line 700) to draw a line, find the distance, and either keep the line there OR delete the line (delete the “entity” by the id of the line). There are so many ways to mix and match the APIs within CesiumJS to do a boatload of things. You just have to explore, try, and fail forward to get there. Again this writeup and kit are to get you comfortable with the workflow, the API, and the documentation to get rolling quickly.
The CesiumJS API (documentation) is AWESOME!
The CesiumJS API documentation is pretty awesome as far as explaining the pieces you can use to call APIs. It is not particularly good IMO at scenarios you may want to do with it. However the CesiumJS Sandbox, Demos and other GIS forums along the Internet help fill in the gaps. I hope this has helped you get comfortable playing with CesiumJS and GeoServer so you can start to build your GIS project, prototype, or side project even quicker with more rapid success.
Enjoy!