Generate KML Overlays for Google maps using SharpKml on .Net core WebAPI
Greetings Everyone!!
Today we are going to discuss on how we could apply an overlay on Google maps javascript generated at server side. For that we will be using markup language named KML. We will be discussing about when and why we use KML overlays, how to create a KML file on .Net core and consume it by Google maps. Are you interested? Then stay tuned…
What is KML(Keyhole markup language)?
KML is a file format used to display geographic data in an Earth browser such as Google Earth. KML uses a tag-based structure with nested elements and attributes and is based on the XML standard. KML can be used to generate an overlay for Google maps.
Why use KML overlays?
If you have past experience using Google maps javascript API, you may know that we can..
1. Add push pins to a map.
2. Draw a paths using coodinates.
3. Draw shapes on the map and do many more.
All these functionalities can be achieved using the google maps javascript API and they have to be implemented on the client app. Have you ever wondered whether we could achieve the same at the server side. Well, that’s where KML comes into play. Below are few important points we should look at.
Using KML we can…
Add multiple features as one overlay.
Modify icons, paths and colors dynamically.
Add clickable info window popups to overlays.
Apply multiple overlays at the same time.
Use less Javascript code. Let server side maintain the business.
Render faster compared to drawing markers at client side with minimal memory footprint
Take a snapshot of the content displayed on the map and export to a PDF or as an image.
Zip the kml markup and resource files to KMZ file.
DO NOT USE KML overlays when..
You have to manipulate markers individually.You have only few elements to display.
You do not want to take snapshots of the map.
You have to subscribe and listen to event ‘
mouseover'
when hovering over markers or other elementsYou are not willing to expose your kml file using publicly acessible url due to security concerns. You can use a token in the query string, but that involves some additional work.
Let’s get started!...
Use following command on the package manager console on the Visual Studio to install SharpKml library
NuGet\Install-Package SharpKml.Core -Version 5.2.0
Add following imports to your class
using SharpKml.Dom;
using SharpKml.Base;
using SharpKml.Engine;
Let’s try something simple to be shown on the map. Below code will add a pushpin to your map layer
public async Task<byte[]> GetKmlFile(string uniqueID, string token)
{
var document = new Document();
// create marker styles
var style = new Style();
style.Id = "icon";
style.Icon = new IconStyle();
style.Icon.Icon = new IconStyle.IconLink(new Uri("https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png"));
document.AddStyle(style);
var kml = new Kml();
// specify coodinates, name of the place and apply the icon styling
var placemark = new Placemark();
placemark.Name = "San Diego";
placemark.StyleUrl = new Uri("#icon", UriKind.Relative);
placemark.Geometry = new SharpKml.Dom.Point
{
Coordinate = new Vector(32.715736,-117.161087)
};
// add place to a document
document.AddFeature(placemark);
// finally add document to kml
kml.Feature = document;
// create xml based kml file
KmlFile kmlFile = KmlFile.Create(kml, false);
// save file to a memory stream
using MemoryStream memStream = new();
kmlFile.Save(memStream);
// returns the result as an array of bytes
var data = memStream.ToArray();
return data;
}
Following method creates the xml based kml file in text format.
KmlFile kmlFile = KmlFile.Create(kml, false);
Generated KML content in XML format looks as following.
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Style id="randomColorIcon">
<IconStyle>
<color>ff00ff00</color>
<colorMode>random</colorMode>
<scale>1.1000000000000001</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pal3/icon21.png</href>
</Icon>
</IconStyle>
</Style>
<Placemark>
<name>San Diego</name>
<styleUrl>#randomColorIcon</styleUrl>
<Point>
<coordinates>-117.424453,34.141170000000002</coordinates>
</Point>
</Placemark>
</Document>
</kml>
Finally you should publish your KML file as a publicly exposed resource URL and tell google maps to refer it. Google servers will first download the KML file from the publicly exposed URL, process it and signal google maps javascript client to download processed map tiles in a well optimized manner instead of loading everything at once.
Expose your kml file as a static file using a controller.
[Route("api/[controller]")]
[ApiController]
public class KmlController : ControllerBase
{
private readonly IKmlService kmlService;
public KmlController(IKmlService kmlService)
{
this.kmlService = kmlService;
}
[HttpGet("get/{uniqueId}")]
[AllowAnonymous]
//depending on requirements you can pass parameters on the query string
public async Task<IActionResult> GetKmlFile(string uniqueId)
{
var result = await kmlService.GetKmlFile();
// generate a physical file using the array of bytes
return File(result, "application/vnd.google-earth.kml+xml");
}
}
Make sure content type is set as “application/vnd.google-earth.kml+xml”
Let’s consume it on the client side using Google maps javascript API
Note that src variable specifies endpoint url to the .Net core WebAPI url so that Google Maps API can tell Google servers to download the file.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>KML Click Capture Sample</title>
<style>
html, body {
height: 370px;
padding: 0;
margin: 0;
}
#map {
height: 360px;
width: 300px;
overflow: hidden;
float: left;
border: thin solid #333;
}
#capture {
height: 360px;
width: 480px;
overflow: hidden;
float: left;
background-color: #ECECFB;
border: thin solid #333;
border-left: none;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="capture"></div>
<script>
var map;
var src = 'https://mysite.com/api/kml/get/1124';
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: new google.maps.LatLng(32.715736,-117.161087),
zoom: 2,
mapTypeId: 'terrain'
});
var kmlLayer = new google.maps.KmlLayer(src, {
suppressInfoWindows: true,
preserveViewport: false,
map: map
});
}
</script>
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
</body>
</html>
Now you should be able to see your push pin on the map.
If you want to learn more on SharpKml and do more amazing stuff please go check out the following link.
https://github.com/samcragg/sharpkml.
You may find a plenty of examples in the Examples folder.
Hope you have learnt something on mapping.
If you have any questions, concerns or feedback feel free to comment.
Cheers!!!!