Skip to content

Commit 77a6e62

Browse files
committed
Working Java model using Apache Jena
1 parent 8d315c1 commit 77a6e62

File tree

10 files changed

+326
-16
lines changed

10 files changed

+326
-16
lines changed

java/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# CASE Conversion Examples (Java)
22

3-
This is an example script that uses the CASE graph to generate a GeoJSON output of locations. This highlights the value
4-
of utilizing the CASE Ontology to create a graph that can be used to query and generate data in a variety of formats,
5-
regardless of the source application's data model. This script is not production ready and does not properly handle
6-
errors. It is intended to be used as a starting point for a more robust solution.
3+
This is an example script that uses the CASE graph to generate a GeoJSON output of locations. This highlights the value of utilizing the CASE Ontology to create a graph that can be used to query and generate data in a variety of formats, regardless of the source application's data model. This script is not production ready and does not properly handle errors. It is intended to be used as a starting point for a more robust solution.
74

85
## Usage
96

@@ -22,3 +19,4 @@ java -jar ./target/case2geo-0.1.0.jar ../../data/locations.json ../../output/jav
2219
```
2320

2421
## Dependencies
22+
This repository depends on several Java libraries, primarily Apache Jena. These dependencies are managed via Maven and can be viewed in the [`pom.xml`](./case2geo/pom.xml).

java/case2geo/pom.xml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<properties>
1717
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18-
<maven.compiler.source>1.8</maven.compiler.source>
19-
<maven.compiler.target>1.8</maven.compiler.target>
18+
<maven.compiler.source>1.18</maven.compiler.source>
19+
<maven.compiler.target>1.18</maven.compiler.target>
2020
<exec.mainClass>org.caseontology.examples.CASE2Geo</exec.mainClass>
2121
</properties>
2222

@@ -27,6 +27,33 @@
2727
<version>4.11</version>
2828
<scope>test</scope>
2929
</dependency>
30+
<dependency>
31+
<groupId>org.apache.jena</groupId>
32+
<artifactId>apache-jena-libs</artifactId>
33+
<version>3.9.0</version>
34+
<exclusions>
35+
<exclusion>
36+
<groupId>org.apache.jena</groupId>
37+
<artifactId>jena-tdb</artifactId>
38+
</exclusion>
39+
</exclusions>
40+
<type>pom</type>
41+
</dependency>
42+
<dependency>
43+
<groupId>ch.qos.logback</groupId>
44+
<artifactId>logback-classic</artifactId>
45+
<version>1.2.3</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>com.github.jsonld-java</groupId>
49+
<artifactId>jsonld-java</artifactId>
50+
<version>0.13.4</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>com.google.code.gson</groupId>
54+
<artifactId>gson</artifactId>
55+
<version>2.10.1</version>
56+
</dependency>
3057
</dependencies>
3158

3259
<build>

java/case2geo/src/main/java/org/caseontology/examples/CASE2Geo.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package org.caseontology.examples;
22

33
import java.io.File;
4+
import java.io.FileWriter;
5+
import java.io.IOException;
46

57
/**
6-
* Hello world!
7-
*
8+
* The primary entrypoint for the CASE2GeoJSON application. This class is a simple command line application that
9+
* accepts two positional arguments: the path to an input file and the path to an output file. The input file is
10+
* expected to be an RDF graph containing location information. The output file will be a GeoJSON representation of
11+
* the input file.
812
*/
9-
public class CASE2Geo
10-
{
11-
public static void main( String[] args )
12-
{
13-
// Ensure two arguments were provided and save them as the input and output paths
13+
public class CASE2Geo {
14+
public static void main(String[] args) {
15+
// Ensure two arguments were provided and save them as the input and output
16+
// paths
1417
if (args.length != 2) {
1518
System.out.println("Usage: java -jar CASE2GeoJSON.jar <inputPath> <outputPath>");
1619
System.exit(1);
@@ -35,10 +38,18 @@ public static void main( String[] args )
3538
System.exit(1);
3639
}
3740

38-
// Build an RDF graph from the input file
41+
// Build an RDF graph from the input file and convert it to GeoJSON
3942
GeoReader reader = new GeoReader(inputPath);
43+
String geoJSON = reader.run();
4044

41-
// Convert the RDF graph to GeoJSON
42-
// TODO
45+
// Write the GeoJSON to the output file
46+
try {
47+
FileWriter fileWriter = new FileWriter(outputPath);
48+
fileWriter.write(geoJSON);
49+
fileWriter.close();
50+
} catch (IOException e) {
51+
System.out.println("Error writing output: " + e.getMessage());
52+
System.exit(1);
53+
}
4354
}
4455
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,207 @@
11
package org.caseontology.examples;
22

3+
import org.apache.jena.query.QueryExecution;
4+
import org.apache.jena.query.QueryExecutionFactory;
5+
import org.apache.jena.query.QueryFactory;
6+
import org.apache.jena.query.QuerySolution;
7+
import org.apache.jena.rdf.model.Model;
8+
import java.io.FileInputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.lang.reflect.Field;
12+
import java.util.ArrayList;
13+
import java.util.Arrays;
14+
import java.util.Iterator;
15+
import java.util.List;
16+
17+
import org.apache.jena.graph.Node;
18+
import org.apache.jena.query.Query;
19+
import org.apache.jena.query.ResultSet;
20+
import org.apache.jena.rdf.model.ModelFactory;
21+
import org.apache.jena.riot.Lang;
22+
import org.apache.jena.riot.RDFDataMgr;
23+
import org.caseontology.examples.geojson.Feature;
24+
import org.caseontology.examples.geojson.GeoRecord;
25+
import org.caseontology.examples.geojson.Geometry;
26+
import org.caseontology.examples.geojson.Properties;
27+
import org.caseontology.examples.geojson.Root;
28+
29+
import com.google.gson.Gson;
30+
import com.google.gson.GsonBuilder;
31+
332
public class GeoReader {
433

534
private String InputPath;
635

736
public GeoReader(String inputPath) {
837
this.InputPath = inputPath;
938
}
39+
40+
/**
41+
* The primary entrypoint for the GeoReader class. This method will read the RDF graph from the input
42+
* file and convert it to GeoJSON string.
43+
*
44+
* @return the GeoJSON string generated from the input file
45+
*/
46+
public String run() {
47+
48+
// Define the SPARQL query to execute to retrieve the location information from
49+
// the input file
50+
String queryString = """
51+
PREFIX uco-core: <https://ontology.unifiedcyberontology.org/uco/core/>
52+
PREFIX uco-location: <https://ontology.unifiedcyberontology.org/uco/location/>
53+
54+
SELECT ?lLatitude ?lLongitude ?lAddressType ?lCountry ?lLocality ?lPostalCode ?lRegion ?lStreet
55+
WHERE
56+
{
57+
?nLocation a uco-location:Location .
58+
OPTIONAL
59+
{
60+
?nLocation uco-core:hasFacet ?nLatLongFacet .
61+
?nLatLongFacet a uco-location:LatLongCoordinatesFacet .
62+
OPTIONAL { ?nLatLongFacet uco-location:latitude ?lLatitude . }
63+
OPTIONAL { ?nLatLongFacet uco-location:longitude ?lLongitude . }
64+
}
65+
66+
OPTIONAL {
67+
?nLocation uco-core:hasFacet ?nSimpleAddressFacet .
68+
?nSimpleAddressFacet a uco-location:SimpleAddressFacet .
69+
OPTIONAL { ?nSimpleAddressFacet uco-location:addressType ?lAddressType . }
70+
OPTIONAL { ?nSimpleAddressFacet uco-location:country ?lCountry . }
71+
OPTIONAL { ?nSimpleAddressFacet uco-location:locality ?lLocality . }
72+
OPTIONAL { ?nSimpleAddressFacet uco-location:postalCode ?lPostalCode . }
73+
OPTIONAL { ?nSimpleAddressFacet uco-location:region ?lRegion . }
74+
OPTIONAL { ?nSimpleAddressFacet uco-location:street ?lStreet . }
75+
}
76+
}
77+
""";
78+
79+
try {
80+
// Load the input file into an RDF model
81+
Model model = getModel(this.InputPath);
82+
83+
// Execute the SPARQL query on the RDF model
84+
ResultSet result = executeSparqlQuery(model, queryString);
85+
86+
// Convert the SPARQL query result to a list of GeoRecord objects
87+
List<GeoRecord> records = resultToObjects(result);
88+
89+
// Convert the list of GeoRecord objects to a GeoJSON string and return the string
90+
Gson gson = new GsonBuilder().setPrettyPrinting().create();
91+
Root geoJSON = recordsToGeoJSON(records);
92+
return gson.toJson(geoJSON);
93+
} catch (IOException e) {
94+
System.out.println("Error processing input: " + e.getMessage());
95+
e.printStackTrace();
96+
System.exit(1);
97+
}
98+
99+
// This line will never be reached but the compiler complains if it is not explicitly returning a String value
100+
return "";
101+
}
102+
103+
/**
104+
* Creates an RDF model from the provided input file.
105+
*
106+
* @param inputPath the path to the input file
107+
* @return Model the RDF graph built from the input file
108+
* @throws IOException if the input file does not exist or cannot be read
109+
*/
110+
private static Model getModel(String inputPath) throws IOException {
111+
Model model = ModelFactory.createDefaultModel();
112+
113+
InputStream inputStream = new FileInputStream(inputPath);
114+
RDFDataMgr.read(model, inputStream, Lang.JSONLD);
115+
116+
return model;
117+
}
118+
119+
/**
120+
* Executes a SPARQL query on the provided RDF model.
121+
*
122+
* @param model the RDF model to query
123+
* @param sparqlQuery the SPARQL query to execute
124+
*/
125+
private static ResultSet executeSparqlQuery(Model model, String sparqlQuery) {
126+
Query query = QueryFactory.create(sparqlQuery);
127+
QueryExecution queryExecution = QueryExecutionFactory.create(query, model);
128+
return queryExecution.execSelect();
129+
}
130+
131+
/**
132+
* Converts the result of a SPARQL query to a list of GeoRecord objects.
133+
*
134+
* @param resultSet the result of a SPARQL query to be converted
135+
* @return List<GeoRecord> a list of GeoRecord objects
136+
*/
137+
private static List<GeoRecord> resultToObjects(ResultSet resultSet) {
138+
// Initialize the list of geo Record objects
139+
List<GeoRecord> records = new ArrayList<GeoRecord>();
140+
141+
while (resultSet.hasNext()) {
142+
QuerySolution soln = resultSet.nextSolution();
143+
144+
// Initialize a new Record object
145+
GeoRecord record = new GeoRecord();
146+
// Get an iterator over the property names in the result set
147+
Iterator<String> names = soln.varNames();
148+
// For each property in the result set, strip the "l" prefix from the property
149+
// name and set the value of the corresponding property on the Record object
150+
while (names.hasNext()) {
151+
String property = names.next();
152+
String propertyName = property.substring(1);
153+
Node node = soln.get(property).asNode();
154+
if (node != null) {
155+
String value = node.getLiteralLexicalForm();
156+
try {
157+
Field field = record.getClass().getDeclaredField(propertyName);
158+
field.set(record, value);
159+
} catch (NoSuchFieldException | IllegalAccessException e) {
160+
System.out.println(
161+
"Error setting property " + propertyName + " on Record object: " + e.getMessage());
162+
}
163+
}
164+
}
165+
166+
// Add the record to the list of records to be returned
167+
records.add(record);
168+
}
169+
170+
return records;
171+
}
172+
173+
/**
174+
* Converts a list of GeoRecord objects to a GeoJSON object.
175+
*
176+
* @param records the list of GeoRecord objects to be converted
177+
* @return Root the GeoJSON object
178+
*/
179+
private static Root recordsToGeoJSON(List<GeoRecord> records) {
180+
Root root = new Root();
181+
root.type = "FeatureCollection";
182+
183+
for (GeoRecord record : records) {
184+
Feature feature = new Feature();
185+
feature.type = "Feature";
186+
feature.properties = new Properties();
187+
feature.properties.addressType = record.AddressType;
188+
feature.properties.country = record.Country;
189+
feature.properties.locality = record.Locality;
190+
feature.properties.postalCode = record.PostalCode;
191+
feature.properties.region = record.Region;
192+
feature.properties.street = record.Street;
193+
// The GeoJSON format expects coordinates in the format [longitude, latitude]
194+
// Only add the coordinates if the latitude and longitude are not null
195+
if (record.Latitude != null && record.Longitude != null) {
196+
feature.geometry = new Geometry();
197+
feature.geometry.coordinates = Arrays.asList(Double.parseDouble(record.Longitude),
198+
Double.parseDouble(record.Latitude));
199+
}
200+
201+
// Add the feature to the list of features in the GeoJSON object
202+
root.features.add(feature);
203+
}
204+
205+
return root;
206+
}
10207
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.caseontology.examples.geojson;
2+
3+
public class Feature {
4+
public String type;
5+
public Geometry geometry;
6+
public Properties properties;
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.caseontology.examples.geojson;
2+
3+
public class GeoRecord {
4+
5+
public String Latitude;
6+
public String Longitude;
7+
public String AddressType;
8+
public String Country;
9+
public String Locality;
10+
public String PostalCode;
11+
public String Region;
12+
public String Street;
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.caseontology.examples.geojson;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class Geometry {
7+
public String type;
8+
public List<Double> coordinates = new ArrayList<Double>();
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.caseontology.examples.geojson;
2+
3+
public class Properties {
4+
public String addressType;
5+
public String street;
6+
public String locality;
7+
public String region;
8+
public String postalCode;
9+
public String country;
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.caseontology.examples.geojson;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class Root {
7+
public String type;
8+
public List<Feature> features = new ArrayList<Feature>();
9+
}

0 commit comments

Comments
 (0)