diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
index 8325b47918241003466ef1f0d12ef8da02ee81b6..ff351afcb4ad7f3a2c86760e719c20a69d73aeb3 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java
@@ -21,7 +21,7 @@ import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
 
 public class App {
 
-	private GeoLocationsPersistence geoLocationsPersistence = new GeoLocationsJsonPersistence();
+	private GeoLocationsStreamPersistence geoLocationsPersistence = new GeoLocationsJsonPersistence();
 	
 	private Collection<GeoLocations> geoLocations = null;
 
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
index 6fb4f23d85e0e08852f2032b5a1c491970b3c3f2..5c6d552702fccff1ef5acf04e9f15a00e1224cd3 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java
@@ -35,26 +35,4 @@ public class GeoLocation extends TimedTaggedImpl implements GeoLocated, Timed, T
 	public void setDescription(String description) {
 		this.description = description;
 	}
-	
-	//
-	
-	private Tags tags = null;
-
-	@Override
-	public boolean hasTags(String... tags) {
-		return this.tags != null && this.tags.hasTags(tags);
-	}
-	
-	public void addTags(String... tags) {
-		if (this.tags == null) {
-			this.tags = new Tags();
-		}
-		this.tags.addTags(tags);
-	}
-	
-	public void removeTags(String... tags) {
-		if (this.tags != null) {
-			this.tags.removeTags(tags);
-		}
-	}
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java
index 8eeaf5c13593ce488998caa0ce4e0aa2625ccf14..2d373b27d012f032accf12373956b974244d3313 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocations.java
@@ -9,6 +9,16 @@ import java.util.stream.Collectors;
 
 public class GeoLocations extends TimedTaggedImpl implements Iterable<GeoLocated>, Tagged, Timed {
 
+	private final GeoLocationsOwner owner;
+	
+	public GeoLocations(GeoLocationsOwner owner) {
+		this.owner = owner;
+	}
+
+	public GeoLocationsOwner getOwner() {
+		return owner;
+	}
+	
 	private String name;
 	
 	public String getName() {
@@ -19,12 +29,23 @@ public class GeoLocations extends TimedTaggedImpl implements Iterable<GeoLocated
 		this.name = name;
 	}
 
+	private String description;
+
+	public String getDescription() {
+		return description;
+	}
+	
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
 	private Collection<GeoLocated> locations = new ArrayList<GeoLocated>();
 	private boolean path = false;
 	
-	public GeoLocations(LatLong...latLongs) {
-		for (int i = 0; i < latLongs.length; i++) {
-			addLocation(latLongs[i]);
+	public GeoLocations(GeoLocated...geoLocs) {
+		this((GeoLocationsOwner) null);
+		for (int i = 0; i < geoLocs.length; i++) {
+			addLocation(geoLocs[i]);
 		}
 	}
 
@@ -60,7 +81,7 @@ public class GeoLocations extends TimedTaggedImpl implements Iterable<GeoLocated
 
 	//
 	
-	public void addLocation(LatLong geoLoc) {
+	public void addLocation(GeoLocated geoLoc) {
 		locations.add(geoLoc);
 	}
 
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsOwner.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsOwner.java
new file mode 100644
index 0000000000000000000000000000000000000000..55c54ef126840d0aaff7eaa3da763f7291a5bc15
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsOwner.java
@@ -0,0 +1,100 @@
+package tdt4140.gr1800.app.core;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class GeoLocationsOwner {
+
+	private Collection<GeoLocations> geoLocations = null;
+
+	public Iterable<String> getGeoLocationsNames() {
+		Collection<String> names = new ArrayList<String>(geoLocations != null ? geoLocations.size() : 0);
+		if (geoLocations != null) {
+			for (GeoLocations geoLocations : geoLocations) {
+				names.add(geoLocations.getName());
+			}
+		}
+		return names;
+	}
+
+	public boolean hasGeoLocations(String... names) {
+		if (geoLocations != null) {
+			outer: for (String name : names) {
+				for (GeoLocations geoLocations : geoLocations) {
+					if (name.equals(geoLocations.getName())) {
+						continue outer;
+					}
+				}
+				return false;
+			}
+			return true;
+		}
+		return names.length == 0;
+	}
+
+	public GeoLocations getGeoLocations(String name) {
+		if (geoLocations != null) {
+			for (GeoLocations geoLocations : geoLocations) {
+				if (name.equals(geoLocations.getName())) {
+					return geoLocations;
+				}
+			}
+		}
+		return null;
+	}
+
+	public Collection<GeoLocations> getGeoLocations(String... names) {
+		Collection<GeoLocations> result = new ArrayList<GeoLocations>();
+		if (geoLocations != null) {
+			if (names != null) {
+				for (GeoLocations geoLocations : geoLocations) {
+					for (String name : names) {
+						if (name.equals(geoLocations.getName())) {
+							result.add(geoLocations);
+						}
+					}
+				}
+			} else {
+				result.addAll(geoLocations);
+			}
+		}
+		return result;
+	}
+
+	//
+
+	public void removeGeolocations(String... names) {
+		if (geoLocations != null) {
+			Iterator<GeoLocations> it = geoLocations.iterator();
+			while (it.hasNext()) {
+				GeoLocations next = it.next();
+				if (names != null) {
+					for (String name : names) {
+						if (name.equals(next.getName())) {
+							it.remove();
+							break;
+						}
+					}
+				} else {
+					it.remove();				
+				}
+			}
+		}
+	}
+
+	public void addGeolocations(GeoLocations geoLocations) {
+		if (this.geoLocations == null) {
+			this.geoLocations = new ArrayList<>();
+		}
+		if (! this.geoLocations.contains(geoLocations)) {
+			this.geoLocations.add(geoLocations);
+		}
+	}
+
+	public void removeGeolocations(GeoLocations geoLocations) {
+		if (this.geoLocations != null) {
+			this.geoLocations.remove(geoLocations);
+		}
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistence.java
similarity index 86%
rename from tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsPersistence.java
rename to tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistence.java
index 27798efbb53f54058b6172a293220c72bcf092d1..98a709927c20e45c6782a1b4c5d5fd14d06fa3df 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsPersistence.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistence.java
@@ -4,7 +4,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Collection;
 
-public interface GeoLocationsPersistence {
+public interface GeoLocationsStreamPersistence {
 	public Collection<GeoLocations> loadLocations(InputStream inputStream) throws Exception;
 	public void saveLocations(Collection<GeoLocations> geoLocations, OutputStream outputStream) throws Exception;
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tagged.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tagged.java
index f6bacd1841a99a9ef7d742f6aa1551bc1825ad37..ddd1ae71fddcde5b0fc54afc4a7610304f903527 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tagged.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tagged.java
@@ -2,6 +2,9 @@ package tdt4140.gr1800.app.core;
 
 public interface Tagged {
 	public boolean hasTags(String... tags);
+	public String[] getTags();
+	public String getTags(String prefix, String separator, String suffix);
+	public void setTags(String... tags);
 	public void addTags(String... tags);	
 	public void removeTags(String... tags);
 }
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tags.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tags.java
index 8fdc743865e5c5585c770b3c5d92a9156b4a0e2f..58c01a3271f0a8b92cb619ee5b726f10cfe196e4 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tags.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/Tags.java
@@ -9,18 +9,66 @@ public class Tags implements Tagged {
 	private Collection<String> tags = null;
 
 	public Tags(String... tags) {
-		addTags(tags);
+		setTags(tags);
 	}
-	
+
+	public Tags(Tagged tags) {
+		setTags(tags.getTags());
+	}
+
+	public static Tags valueOf(String tags) {
+		return valueOf(tags, ",");
+	}
+
+	public static Tags valueOf(String tags, String separator) {
+		return new Tags(tags.split(separator));
+	}
+
 	public int getTagCount() {
 		return (tags == null ? 0 : tags.size());
 	}
 	
 	@Override
 	public boolean hasTags(String... tags) {
-		return this.tags != null && this.tags.containsAll(Arrays.asList(tags));
+		return (tags.length == 0 || (this.tags != null && this.tags.containsAll(Arrays.asList(tags))));
+	}
+
+	final static String[] EMPTY_STRINGS = {};
+
+	@Override
+	public String[] getTags() {
+		return (tags != null ? tags.toArray(new String[tags.size()]) : EMPTY_STRINGS);
 	}
 	
+	@Override
+	public String getTags(String prefix, String separator, String suffix) {
+		StringBuilder buffer = new StringBuilder();
+		append(buffer, prefix);
+		int tagNum = 0;
+		for (String tag : tags) {
+			if (tagNum > 0 && separator != null) {
+				buffer.append(separator);				
+			}
+			buffer.append(tag);
+			tagNum++;
+		}
+		append(buffer, suffix);
+		return buffer.toString();
+	}
+
+	static StringBuilder append(StringBuilder buffer, String s) {
+		if (s != null) {
+			buffer.append(s);
+		}
+		return buffer;
+	}
+	
+	@Override
+	public void setTags(String... tags) {
+		this.tags = new ArrayList<>();
+		addTags(tags);
+	}
+
 	public void addTags(String... tags) {
 		if (this.tags == null && tags != null && tags.length > 0) {
 			this.tags = new ArrayList<>();
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/TimedTaggedImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/TimedTaggedImpl.java
index 09a651108a7db812c0edc763de25a55d4c5088a3..c8ccbdf4228e7d668c13c5d84de1920dd29244c4 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/TimedTaggedImpl.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/TimedTaggedImpl.java
@@ -4,11 +4,47 @@ public class TimedTaggedImpl extends TimedImpl implements Tagged {
 	
 	private Tags tags = null;
 
+	public TimedTaggedImpl() {
+	}
+
+	public TimedTaggedImpl(Tagged tags) {
+		setTags(tags.getTags());
+	}
+
+	private static TimedTaggedImpl valueOf(Tags tags) {
+		TimedTaggedImpl timedTags = new TimedTaggedImpl();
+		timedTags.tags = tags;
+		return timedTags;
+	}
+
+	public static TimedTaggedImpl valueOf(String tags) {
+		return valueOf(Tags.valueOf(tags));
+	}
+	
+	public static TimedTaggedImpl valueOf(String tags, String separator) {
+		return valueOf(Tags.valueOf(tags, separator));
+	}
+
 	@Override
 	public boolean hasTags(String... tags) {
 		return this.tags != null && this.tags.hasTags(tags);
 	}
+
+	@Override
+	public String[] getTags() {
+		return (tags != null ? tags.getTags() : Tags.EMPTY_STRINGS);
+	}
 	
+	@Override
+	public String getTags(String prefix, String separator, String suffix) {
+		return (tags != null ? tags.getTags(prefix, separator, suffix) : Tags.append(Tags.append(new StringBuilder(), prefix), suffix).toString());
+	}
+
+	@Override
+	public void setTags(String... tags) {
+		this.tags = (tags != null && tags.length > 0 ? new Tags(tags) : null);
+	}
+
 	public void addTags(String... tags) {
 		if (this.tags == null) {
 			this.tags = new Tags();
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentConverter.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..8794aabc76f54f9b665f1055e477aa9aa0f2cf3e
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentConverter.java
@@ -0,0 +1,67 @@
+package tdt4140.gr1800.app.geojson;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import org.geojson.GeoJsonObject;
+import org.geojson.LngLatAlt;
+
+import tdt4140.gr1800.app.core.GeoLocations;
+import tdt4140.gr1800.app.core.LatLong;
+import tdt4140.gr1800.app.doc.IDocumentLoader;
+
+public class GeoJsonDocumentConverter implements IDocumentLoader<Collection<GeoLocations>> {
+
+	private GeoJsonDocumentLoader geoJsonLoader = new GeoJsonDocumentLoader();
+	
+	@Override
+	public Collection<GeoLocations> loadDocument(InputStream inputStream) throws Exception {
+		GeoJsonObject geoJson = geoJsonLoader.loadDocument(inputStream);
+		return convert(geoJson);
+	}
+
+	private String trackNameFormat = "%s";
+	private String trackCountFormat = "Track %s";
+	private String trackSegmentFormat = "%s.%s";
+
+	public void setTrackNameFormat(String trackNameFormat) {
+		this.trackNameFormat = trackNameFormat;
+	}
+	
+	public void setTrackCountFormat(String trackCountFormat) {
+		this.trackCountFormat = trackCountFormat;
+	}
+	
+	public void setTrackSegmentFormat(String trackSegmentFormat) {
+		this.trackSegmentFormat = trackSegmentFormat;
+	}
+
+	private String routeNameFormat = "%s";
+	private String routeCountFormat = "Route %s";
+	
+	public void setRouteNameFormat(String routeNameFormat) {
+		this.routeNameFormat = routeNameFormat;
+	}
+	
+	public void setRouteCountFormat(String routeCountFormat) {
+		this.routeCountFormat = routeCountFormat;
+	}
+	
+	public Collection<GeoLocations> convert(GeoJsonObject geoJson) throws Exception {
+		Collection<GeoLocations> geoLocations = new ArrayList<GeoLocations>();
+		return geoLocations;
+	}
+
+	private LatLong[] convert(Collection<LngLatAlt> points) {
+		Collection<LatLong> latLongs = points.stream()
+				.map(point -> convert(point))
+				.collect(Collectors.toList());
+		return latLongs.toArray(new LatLong[latLongs.size()]);
+	}
+
+	private LatLong convert(LngLatAlt point) {
+		return new LatLong(point.getLatitude(), point.getLongitude());
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentLoader.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..612f4b4e12b9633664b3bbdad5a97687961ed398
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/geojson/GeoJsonDocumentLoader.java
@@ -0,0 +1,17 @@
+package tdt4140.gr1800.app.geojson;
+
+import java.io.InputStream;
+
+import org.geojson.GeoJsonObject;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import tdt4140.gr1800.app.doc.IDocumentLoader;
+
+public class GeoJsonDocumentLoader implements IDocumentLoader<GeoJsonObject> {
+
+	@Override
+	public GeoJsonObject loadDocument(InputStream inputStream) throws Exception {
+		return new ObjectMapper().readValue(inputStream, GeoJsonObject.class);
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java
index 48804fb300bf362c0bfeffd57e24da07f328697a..66a078eca89a05489d52a219554a910463781b8f 100644
--- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java
+++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java
@@ -9,9 +9,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsPersistence;
+import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
 
-public class GeoLocationsJsonPersistence implements GeoLocationsPersistence {
+public class GeoLocationsJsonPersistence implements GeoLocationsStreamPersistence {
 
 	private final ObjectMapper objectMapper;
 
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
index 60baab304af770ab0be1412a37b19e277ae35556..fc5e13c0da1df6808f2a142d304e164161f9242b 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java
@@ -35,7 +35,7 @@ public class AppTest {
 			Assert.fail("Couldn't open " + file);
 		}
 		Assert.assertEquals(file, documentStorage.getDocumentLocation());
-		GeoLocationsPersistenceTest.testGeoLocationsDotJson(app.getGeoLocations((String[]) null));
+		GeoLocationsStreamPersistenceTest.testGeoLocationsDotJson(app.getGeoLocations((String[]) null));
 	}
 	
 	@Test
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsOwnerTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsOwnerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..15bc437da10d77845b9218c895f5241311dc95e8
--- /dev/null
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsOwnerTest.java
@@ -0,0 +1,141 @@
+package tdt4140.gr1800.app.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GeoLocationsOwnerTest {
+
+	private GeoLocationsOwner owner;
+	
+	@Before
+	public void setUp() {
+		this.owner = new GeoLocationsOwner();
+	}
+	
+	private static <T> void check(Iterable<T> iterable, T... ts) {
+		check(iterable, false, ts);
+	}
+
+	private static <T> void check(Iterable<T> iterable, boolean anyOrder, T... ts) {
+		Collection<T> all = (anyOrder ? Arrays.asList(ts) : null);
+		int num = 0;
+		for (Iterator<T> it = iterable.iterator(); it.hasNext(); num++) {
+			Assert.assertTrue(num < ts.length);
+			T next = it.next();
+			if (anyOrder) {
+				assertTrue(all.contains(next));
+			} else {
+				Assert.assertEquals(ts[num], next);
+			}
+		}
+		Assert.assertTrue(num == ts.length);
+	}
+
+	private static int size(Iterable<?> iterable) {
+		int size = 0;
+		for (Iterator<?> it = iterable.iterator(); it.hasNext(); it.next()) {
+			size++;
+		}
+		return size;
+	}
+
+	@Test
+	public void testGetGeoLocationNames() {
+		Assert.assertEquals(0, size(owner.getGeoLocationsNames()));
+		owner.addGeolocations(new GeoLocations("test1"));
+		check(owner.getGeoLocationsNames(), "test1");
+		owner.addGeolocations(new GeoLocations("test2"));
+		check(owner.getGeoLocationsNames(), "test1", "test2");
+	}
+	
+	@Test
+	public void testHasGeoLocations() {
+		Assert.assertTrue(owner.hasGeoLocations());
+		Assert.assertFalse(owner.hasGeoLocations("test1"));
+		owner.addGeolocations(new GeoLocations("test1"));
+		Assert.assertTrue(owner.hasGeoLocations());
+		Assert.assertTrue(owner.hasGeoLocations("test1"));
+		owner.addGeolocations(new GeoLocations("test2"));
+		Assert.assertTrue(owner.hasGeoLocations());
+		Assert.assertTrue(owner.hasGeoLocations("test1"));
+		Assert.assertTrue(owner.hasGeoLocations("test2"));
+		Assert.assertTrue(owner.hasGeoLocations("test1", "test2"));
+	}
+	
+	@Test
+	public void testGetGeoLocations1() {
+		Assert.assertNull(owner.getGeoLocations("test1"));
+		GeoLocations geoLocations11 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations11);
+		Assert.assertSame(geoLocations11, owner.getGeoLocations("test1"));
+
+		GeoLocations geoLocations2 = new GeoLocations("test2");
+		owner.addGeolocations(geoLocations2);
+		Assert.assertSame(geoLocations11, owner.getGeoLocations("test1"));
+		Assert.assertSame(geoLocations2, owner.getGeoLocations("test2"));
+
+		GeoLocations geoLocations12 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations12);
+		Assert.assertNotNull(owner.getGeoLocations("test1"));
+		Assert.assertSame(geoLocations2, owner.getGeoLocations("test2"));
+
+	}
+
+	@Test
+	public void testGetGeoLocationsN() {
+		GeoLocations geoLocations11 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations11);
+		Assert.assertEquals(0, owner.getGeoLocations().size());
+		check(owner.getGeoLocations(new String[]{"test1"}), geoLocations11);
+		check(owner.getGeoLocations((String[]) null), geoLocations11);
+		
+		GeoLocations geoLocations2 = new GeoLocations("test2");
+		owner.addGeolocations(geoLocations2);
+		Assert.assertEquals(0, owner.getGeoLocations().size());
+		check(owner.getGeoLocations(new String[]{"test1"}), geoLocations11);
+		check(owner.getGeoLocations(new String[]{"test2"}), geoLocations2);
+		check(owner.getGeoLocations((String[]) null), true, geoLocations11, geoLocations2);
+		
+		GeoLocations geoLocations12 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations12);
+		Assert.assertEquals(0, owner.getGeoLocations().size());
+		check(owner.getGeoLocations(new String[]{"test1"}), true, geoLocations11, geoLocations12);
+		Assert.assertEquals(1, owner.getGeoLocations(new String[]{"test2"}).size());
+		check(owner.getGeoLocations((String[]) null), true, geoLocations11, geoLocations2, geoLocations12);
+	}
+	
+	@Test
+	public void testRemoveGeoLocationsN() {
+		GeoLocations geoLocations11 = new GeoLocations("test1"), geoLocations2 = new GeoLocations("test2"), geoLocations12 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations11);
+		owner.addGeolocations(geoLocations2);
+		owner.addGeolocations(geoLocations12);
+		Assert.assertEquals(3, owner.getGeoLocations((String[]) null).size());
+		owner.removeGeolocations("test1");
+		check(owner.getGeoLocations((String[]) null), true, geoLocations2);
+		owner.removeGeolocations("test2");
+		Assert.assertEquals(0, owner.getGeoLocations((String[]) null).size());
+	}
+	
+	@Test
+	public void testRemoveGeoLocations1() {
+		GeoLocations geoLocations11 = new GeoLocations("test1"), geoLocations2 = new GeoLocations("test2"), geoLocations12 = new GeoLocations("test1");
+		owner.addGeolocations(geoLocations11);
+		owner.addGeolocations(geoLocations2);
+		owner.addGeolocations(geoLocations12);
+		Assert.assertEquals(3, owner.getGeoLocations((String[]) null).size());
+		owner.removeGeolocations(geoLocations11);
+		check(owner.getGeoLocations((String[]) null), true, geoLocations12, geoLocations2);
+		owner.removeGeolocations(geoLocations2);
+		check(owner.getGeoLocations((String[]) null), geoLocations12);
+		owner.removeGeolocations(geoLocations12);
+		Assert.assertEquals(0, owner.getGeoLocations((String[]) null).size());
+	}
+}
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java
similarity index 92%
rename from tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsPersistenceTest.java
rename to tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java
index 7f2fc277192000846ea1eead52fba3e5653b74d0..2eaa8a872919dcf63b5bbb89f03cd0b4cbdfe53d 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsPersistenceTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java
@@ -2,6 +2,8 @@ package tdt4140.gr1800.app.core;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -12,9 +14,9 @@ import org.junit.Test;
 
 import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
 
-public class GeoLocationsPersistenceTest {
+public class GeoLocationsStreamPersistenceTest {
 
-	private GeoLocationsPersistence persistence;
+	private GeoLocationsStreamPersistence persistence;
 	
 	@Before
 	public void setUp() {
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
index 689aaeec74cdba28006ce1c099cc1e6dfbcf419a..c03b9e50612ce3a969a1a42e4c7957b29e3455c3 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java
@@ -67,7 +67,7 @@ public class GeoLocationsTest extends TimedTaggedTest {
 	}
 	
 	@Test
-	public void testFindLocationsNearby() {
+	public void testFindLocationsNearby() { 
 		LatLong latLong = new LatLong(0, 0);
 		Assert.assertTrue(geoLocations.findLocationsNearby(latLong, 0).isEmpty());
 		geoLocations.addLocation(latLong);
diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/TimedTaggedTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/TimedTaggedTest.java
index 1e16bc4c9d548ff5e1f06e070a83bcbc0413f85c..65b1ce6a07942c7c698bded4fe4d57574ef89f02 100644
--- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/TimedTaggedTest.java
+++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/TimedTaggedTest.java
@@ -3,6 +3,7 @@ package tdt4140.gr1800.app.core;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.util.Arrays;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -43,6 +44,42 @@ public class TimedTaggedTest {
 		Assert.assertEquals(now.toLocalTime(), timedTagged.getTime());
 	}
 
+	@Test
+	public void testTaggedValueOf() {		
+		Assert.assertEquals("<en, to>", TimedTaggedImpl.valueOf("en; to", "; ").getTags("<", ", ", ">"));
+		Assert.assertEquals("<en, to>", TimedTaggedImpl.valueOf("en,to").getTags("<", ", ", ">"));
+	}
+
+	@Test
+	public void testTaggedSet() {
+		timedTagged.setTags("en", "to");
+		Assert.assertTrue(timedTagged.hasTags("en"));
+		Assert.assertTrue(timedTagged.hasTags("to"));
+		timedTagged.setTags("tre");
+		Assert.assertFalse(timedTagged.hasTags("en"));
+		Assert.assertFalse(timedTagged.hasTags("to"));
+		Assert.assertTrue(timedTagged.hasTags("tre"));
+	}
+
+	@Test
+	public void testTaggedGet() {
+		Assert.assertEquals(0, timedTagged.getTags().length);
+		timedTagged.setTags("en", "to", "to");
+		String[] tags = timedTagged.getTags();
+		Assert.assertEquals(2, tags.length);
+		Arrays.sort(tags);
+		Arrays.equals(new String[] {"en",  "to"}, tags);
+	}
+	
+	@Test
+	public void testTaggedGet2() {
+		Assert.assertEquals("", timedTagged.getTags(null, null, null));
+		Assert.assertEquals("<>", timedTagged.getTags("<", null, ">"));
+		timedTagged.setTags("en", "to", "to");
+		Assert.assertEquals("ento", timedTagged.getTags(null, null, null));
+		Assert.assertEquals("<en, to>", timedTagged.getTags("<", ", ", ">"));
+	}
+
 	@Test
 	public void testTaggedAdd() {
 		timedTagged.addTags("en", "to");
diff --git a/tdt4140-gr1800/app.ui/src/test/java/tdt4140/gr1800/app/ui/FxAppTest.java b/tdt4140-gr1800/app.ui/src/test/java/tdt4140/gr1800/app/ui/FxAppTest.java
index b31cb41137d1c57885ba42e7d64391ea1828eaee..2cd683e778fff8d640397713ee1bd3475a7770a1 100644
--- a/tdt4140-gr1800/app.ui/src/test/java/tdt4140/gr1800/app/ui/FxAppTest.java
+++ b/tdt4140-gr1800/app.ui/src/test/java/tdt4140/gr1800/app/ui/FxAppTest.java
@@ -7,7 +7,6 @@ import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.junit.Assert;
@@ -18,7 +17,6 @@ import org.testfx.framework.junit.ApplicationTest;
 import org.testfx.util.WaitForAsyncUtils;
 
 import fxmapcontrol.MapBase;
-import fxmapcontrol.MapItemsControl;
 import javafx.collections.ObservableList;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Node;
diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
index 77b0534e907c89b0b89eefc3985dd939d4a3ea7e..9c00904e66aaf7471f0f74c93500c946aa579f1c 100644
--- a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
+++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java
@@ -14,12 +14,12 @@ import javax.servlet.http.HttpServletResponse;
 
 import io.jenetics.jpx.Person;
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsPersistence;
+import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
 import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
 
 public class GeoServlet extends HttpServlet {
 
-	private GeoLocationsPersistence persistence = new GeoLocationsJsonPersistence();
+	private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence();
 
 	private Collection<GeoLocations> allGeoLocations = new ArrayList<GeoLocations>();
 	
@@ -40,6 +40,18 @@ public class GeoServlet extends HttpServlet {
 		super.init();
 	}
 	
+	// REST URL structure, according to https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/
+	// persons/<id>/geoLocations/<num>/geoLocations/<num>
+
+	// GET variants
+	// persons: Get all Person objects. Do we allow that? Should we return a list of <id> values or all the person entities (with some subset of properties) 
+	// persons/<id>: Get a specific Person object
+	// persons/name or email: Get a specific Person object, with the provided name or email (with a '@')
+	// persons/<id>/geoLocations: Get all the GeoLocations objects, with (some subset of) properties
+	// persons/<id>/geoLocations/<num>: Get a specific GeoLocations object
+	// persons/<id>/geoLocations/<num>/geoLocations: Get all GeoLocation objects, with (some subset of) properties
+	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Get a specific GeoLocations object
+	
     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     		String path = request.getPathInfo();
     		Collection<GeoLocations> geoLocations = null;
@@ -66,4 +78,45 @@ public class GeoServlet extends HttpServlet {
 			}
     		}
     }
+
+	// POST variants
+	// persons: Create a new Person object, with properties in the payload
+	// persons/<id>: Not allowed
+    // persons/<id>/geoLocations: Create a new GeoLocations object, with properties in the payload
+	// persons/<id>/geoLocations/<num>: Not allowed
+	// persons/<id>/geoLocations/<num>/geoLocations: Create a new GeoLocation object, with properties in the payload
+	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Not allowed
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+    		super.doPost(req, resp);
+    }
+    
+	// PUT variants
+	// persons: Not allowed
+	// persons/<id>: Update specific Person object
+    // persons/<id>/geoLocations: Not allowed
+	// persons/<id>/geoLocations/<num>: Update specific GeoLocations object
+	// persons/<id>/geoLocations/<num>/geoLocations: Not allowed
+	// persons/<id>/geoLocations/<num>/geoLocations/<num>: Update specific GeoLocations object
+
+    @Override
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+    		// TODO Auto-generated method stub
+    		super.doPut(req, resp);
+    }
+
+    // DELETE variants
+    // persons: Not allowed
+    // persons/<id>: Delete specific Person object
+    // persons/<id>/geoLocations: Delete all GeoLocations objects?
+    // persons/<id>/geoLocations/<num>: Delete specific GeoLocations object
+    // persons/<id>/geoLocations/<num>/geoLocations: Delete all GeoLocation objects?
+    // persons/<id>/geoLocations/<num>/geoLocations/<num>: Delete specific GeoLocation object
+
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+    		// TODO Auto-generated method stub
+    		super.doDelete(req, resp);
+    }
 }
diff --git a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
index 3d00904a41c2caa41be6627c54bd7089bc7e612c..a230c446ad7269721f7913f58b793a31851f129c 100644
--- a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
+++ b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java
@@ -10,13 +10,13 @@ import org.junit.Test;
 
 import tdt4140.gr1800.app.core.GeoLocated;
 import tdt4140.gr1800.app.core.GeoLocations;
-import tdt4140.gr1800.app.core.GeoLocationsPersistence;
+import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence;
 import tdt4140.gr1800.app.core.LatLong;
 import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence;
 
 public class GeoLocationsServerIT {
 
-	private GeoLocationsPersistence persistence = new GeoLocationsJsonPersistence();
+	private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence();
 	
 	private Collection<GeoLocations> get(String path, int size) throws Exception {
 		URL url = new URL("http://localhost:8080/geo" + path);