I haven’t done any fun projects for almost 2 years (WorldWind .net and WiiMote) at home. There are several TODOs on my list ranging from building a FITR based multitouch surface to writing an iPhone game to fixing bugs in WW.NET and revamping how icons work there.
Instead I’ve been doing a lot of coding with WorldWind Java (WWJ) at work and coming home a bit late in the evenings. It’s nice coding again for work and I find I need to do that every couple years and take a break from technical management. The side effect is I don’t have to code on the side to keep my hand in.
But Java API for KML came out recently and while it isn’t necessary for our work project I would like to play with it a bit and see if it can replace some of our custom KML code. I’ve also had a long running itch to scratch in terms of getting the Worldwide SAM Site Overview data into WorldWind.
So a 24 hour project to do that. That’s a little TOO simple so here are the overall requirements:
- Import the SSO kmz file into java using JAK
- Display the SSO data on WWJ
- Automatically generate range circles based on the SAM type
- Export the range circle and extra data back into KML
Extra credit:
- Import CIA World Factbook data and display that as well
- Use raycasting to limit the range cicles based on terrain
- Provide ranges limits based on target altitude as a function of terrain and curvature of the earth
I started this weekend (12 hours worth) and things went quickly until they bogged down from gold plating:
- I got JAK to import the SSO data (easy).
- I got WWJ to show an icon (not the one that is used in the SSO kml but a generic one)
- I parsed the site name to get the SAM type (S300-P, Patriot, Hawk, etc)
That took maybe 4 hours. Then I went into la-la land. I started looking at HSQL and H2 for an embedded DB. I started wandering down the H2 spatial path. I stated defining fairly complex data tables that this program would never need. Then I ran out of weekend without being in a demoable state.
The project, alas, will have to go through my work to release but I can talk about JAK here. Other than an a lack of documentation (common for open source) it was easy to figure out. But since I couldn’t find an unmarshall example anywhere on the net here’s a simple example based on the SSO kml (I extracted it from the KMZ file first):
package edu.jhuapl.ssov.core;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import de.micromata.opengis.kml.v_2_2_0.Document;
import de.micromata.opengis.kml.v_2_2_0.Feature;
import de.micromata.opengis.kml.v_2_2_0.Folder;
import de.micromata.opengis.kml.v_2_2_0.Kml;
import de.micromata.opengis.kml.v_2_2_0.Placemark;
public class SsoParser {
HashMap<String, List > samTable = new HashMap<String, List >();
ArrayList samType = new ArrayList();
public void parseKML(String filename)
{
Kml kml = Kml.unmarshal(new File(filename));
initSamType();
if (kml != null)
{
Feature feature = kml.getFeature();
processFeature(null, feature);
}
System.out.println(samTable.size() + " SAM types.");
List unknown = samTable.get("UNKNOWN");
if (unknown != null)
{
System.out.println(unknown.size() + " unknown installations.");
for (Placemark placemark : unknown)
{
System.out.println("UNKNOWN: " + placemark.getName());
}
}
List genericSAM = samTable.get("SAM");
if (genericSAM != null)
{
System.out.println(genericSAM.size() + " generic SAM installations.");
for (Placemark placemark : genericSAM)
{
System.out.println("SAM: " + placemark.getName());
}
}
List genericEW= samTable.get("EW");
if (genericSAM != null)
{
System.out.println(genericEW.size() + " generic EW installations.");
for (Placemark placemark : genericEW)
{
System.out.println("EW: " + placemark.getName());
}
}
}
public void processFeature(Feature parentFeature, Feature feature)
{
if (feature instanceof Document)
{
processDocument(parentFeature, (Document) feature);
}
else if (feature instanceof Folder)
{
processFolder(parentFeature, (Folder) feature);
}
else if (feature instanceof Placemark)
{
processPlacemark(parentFeature, (Placemark) feature);
}
else
{
System.out.println("Feature " + feature.getName() + " : " + feature);
}
}
public void processDocument(Feature parentFeature, Document doc)
{
List features = doc.getFeature();
// System.out.println("Document " + doc.getName());
for (Feature docFeature : features)
{
processFeature(doc, docFeature);
}
}
public void processFolder(Feature parentFeature, Folder folder)
{
List features = folder.getFeature();
// System.out.println("Folder " + folder.getName());
for (Feature folderFeature : features)
{
processFeature(folder, folderFeature);
}
}
public void processPlacemark(Feature parentFeature, Placemark placemark)
{
String samType = processPlacemarkName (placemark.getName());
List placemarks = samTable.get(samType);
if (placemarks == null)
{
// System.out.println("SAM Type " + samType +" found: " + placemark.getName());
placemarks = new ArrayList();
samTable.put(samType, placemarks);
}
placemarks.add(placemark);
}
public String processPlacemarkName(String name)
{
boolean emptyFlag = false;
for (String samName : samType)
{
emptyFlag = false;
if (name.toLowerCase().contains("Empty"))
{
emptyFlag = true;
}
if (name.toLowerCase().contains(samName.toLowerCase()))
{
if (emptyFlag)
return "Empty " + samName;
else
return samName;
}
}
return "UNKNOWN";
}
private void initSamType()
{
samType.add("SA-11");
samType.add("SA-15");
samType.add("SA-20");
samType.add("SA-1");
samType.add("SA-2");
samType.add("SA-3");
samType.add("SA-4");
samType.add("SA-5");
samType.add("SA-6");
samType.add("SA-8");
samType.add("Buk-M1-2");
samType.add("HAWK");
samType.add("FM-90");
samType.add("KS-1A");
samType.add("HQ-2");
samType.add("HQ-6");
samType.add("HQ-9");
samType.add("S-300PS/PMU");
samType.add("S-300PMU-1");
samType.add("S-300PMU");
samType.add("S-300PT");
samType.add("S-300P");
samType.add("S-300V");
samType.add("Chu-SAM");
samType.add("NIKE HERCULES");
samType.add("NIKE-HERCULES");
samType.add("PATRIOT");
samType.add("Arrow II");
samType.add("SL-AMRAAM");
samType.add("Rapier");
samType.add("Crotale");
samType.add("Skyguard");
samType.add("Shahine");
samType.add("TACSAM");
samType.add("Bloodhound");
samType.add("NASAMS");
samType.add("36D6");
samType.add("64N6");
samType.add("96L6");
samType.add("9S18M1");
samType.add("TK-II");
samType.add("HT-233");
samType.add("5V11 Dal");
samType.add("OTH-R");
samType.add("OTH-T");
samType.add("OTH");
samType.add("LPAR");
samType.add("7010 ABM RADAR");
samType.add("AN/FPS-108 COBRA DANE");
samType.add("AN/TPS-71 Receiver");
samType.add("AN/TPS-71 Transmitter");
samType.add("SAM Garrison");
samType.add("SAM Training Facility");
samType.add("SAM Training Range");
samType.add("SAM Test Range");
samType.add("SAM");
samType.add("BMEW");
samType.add("ABM");
samType.add("RADAR");
samType.add("EW");
samType.add("TEST");
}
}
Yes, my java conventions are kinda funny. I’m mostly a C/C++/C# developer.
Also, some things are stubbed in there (like SAM types) that will come from the embedded database (Yes, this is where I went off track).
So I went from 4 hours to 12 hours very quickly. On the other hand I have some hibernate hbm files and a semi-working database (HSQL because I have code samples for that and I only have 12 hours left).