/*
* Copyright (C) 2014 Alec Dhuse
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package co.foldingmap.mapImportExport;
import co.foldingmap.map.vector.VectorLayer;
import co.foldingmap.map.vector.NodeMap;
import co.foldingmap.map.vector.CoordinateList;
import co.foldingmap.map.vector.VectorObjectList;
import co.foldingmap.map.vector.VectorObject;
import co.foldingmap.map.vector.Coordinate;
import co.foldingmap.map.vector.MapPoint;
import co.foldingmap.map.vector.LineString;
import co.foldingmap.map.vector.Polygon;
import co.foldingmap.GUISupport.ProgressIndicator;
import co.foldingmap.Logger;
import co.foldingmap.map.DigitalMap;
import co.foldingmap.map.Layer;
import co.foldingmap.xml.XMLParser;
import co.foldingmap.xml.XMLTag;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* Importer for GeoRss Feeds
* Reference <link>http://georss.org/gml</link>
*
* @author Alec
*/
public class GeoRssImporter implements FormatImporter {
private VectorObjectList<VectorObject> objects;
private String feedDate, feedDescription, feedLanguage;
private String feedLink, feedTitle;
public GeoRssImporter() {
objects = new VectorObjectList<VectorObject>();
}
public GeoRssImporter(NodeMap nodeMap, File rssFile) {
try {
objects = new VectorObjectList<VectorObject>();
parseFile(nodeMap, new XMLParser(new FileReader(rssFile)));
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in GeoRssImporter.constructor - " + e);
}
}
public String getFeedDescription() {
if (feedDescription == null) {
return "";
} else {
return feedDescription;
}
}
public String getFeedTitle() {
if (feedTitle == null) {
return "RSS";
} else {
return feedTitle;
}
}
public VectorObjectList<VectorObject> getObjects() {
return objects;
}
public static VectorObject getItem(NodeMap nodeMap, XMLTag entryTag) {
Coordinate coordinate;
CoordinateList<Coordinate> coordinates;
int subjectIndex;
String coordinateStr, description, lat, lon;
String link, pubDate, title;
String coordinateString, token1, token2;
StringTokenizer st;
VectorObject object;
XMLTag exteriorTag, interiorTag, linkTag;
XMLTag linearTag, pointTag, polygonTag, whereTag;
object = null;
try {
title = entryTag.getSubtagContent("title");
linkTag = entryTag.getSubtagsByName("link").get(0);
description = entryTag.getSubtagContent("description");
pubDate = entryTag.getSubtagContent("pubDate");
if (linkTag != null) {
if (linkTag.containsSubTag("href")) {
link = linkTag.getSubtagContent("href");
} else {
link = linkTag.getPropertyValue("href");
}
}
if (entryTag.containsSubTag("georss:point")) {
coordinateStr = entryTag.getSubtagContent("georss:point");
if (coordinateStr.contains(",")) {
st = new StringTokenizer(coordinateStr, ",");
} else {
st = new StringTokenizer(coordinateStr);
}
token1 = st.nextToken();
token2 = st.nextToken();
coordinate = new Coordinate(0, Float.parseFloat(token1), Float.parseFloat(token2));
object = new MapPoint(title, "(Unspecified Point)", description, coordinate);
nodeMap.put(coordinate);
} else if (entryTag.containsSubTag("gml:Polygon")) {
polygonTag = entryTag.getSubtag("gml:Polygon");
coordinates = new CoordinateList<Coordinate>();
if (polygonTag.containsSubTag("gml:exterior")) {
exteriorTag = polygonTag.getSubtag("gml:exterior");
linearTag = exteriorTag.getSubtag("gml:LinearRing");
coordinates = parsePosList(nodeMap, linearTag.getSubtag("gml:posList"));
object = new Polygon(title, "(Unspecified Polygon)", coordinates);
}
if (polygonTag.containsSubTag("gml:interior")) {
interiorTag = polygonTag.getSubtag("gml:interior");
//TODO add interior
}
} else if (entryTag.containsSubTag("geo:lat")) {
lat = entryTag.getSubtagContent("geo:lat");
lon = entryTag.getSubtagContent("geo:long");
coordinate = new Coordinate(0, Float.parseFloat(lat), Float.parseFloat(lon));
object = new MapPoint(title, "(Unspecified Point)", description, coordinate);
nodeMap.put(coordinate);
} else if (entryTag.containsSubTag("georss:where")) {
whereTag = entryTag.getSubtag("georss:where");
if (whereTag.containsSubTag("gml:Point")) {
pointTag = whereTag.getSubtag("gml:Point");
coordinate = parseCoordinate(pointTag.getSubtag("gml:pos"));
object = new MapPoint(title, "(Unspecified Point)", description, coordinate);
nodeMap.put(coordinate);
object.setDescription(description);
} else if (whereTag.containsSubTag("gml:LineString")) {
linearTag = whereTag.getSubtag("gml:LineString");
coordinates = parsePosList(nodeMap, linearTag.getSubtag("gml:posList"));
object = new LineString(title, "(Unspecified Linestring)", coordinates);
object.setDescription(description);
} else if (whereTag.containsSubTag("gml:Polygon")) {
polygonTag = whereTag.getSubtag("gml:Polygon");
exteriorTag = polygonTag.getSubtag("gml:exterior");
linearTag = exteriorTag.getSubtag("gml:LinearRing");
coordinates = parsePosList(nodeMap, linearTag.getSubtag("gml:posList"));
object = new Polygon(title, "(Unspecified Polygon)", coordinates);
//TODO write exterior
object.setDescription(description);
}
}
if (entryTag.containsSubTag("dc:subject")) {
subjectIndex = 0;
for (XMLTag tag: entryTag.getSubtags("dc:subject")) {
if (object != null) {
object.addCustomDataField("Subject" + subjectIndex, tag.getTagContent());
subjectIndex++;
}
}
}
} catch (Exception e) {
Logger.log(Logger.ERR, "Error in GeoRssImporter.getItem(XMLTag) - " + e);
}
return object;
}
/**
* Imports objects from a given map file and adds objects from the map to
* the given VectorLayer.
*
* @param mapFile The file containing the map to import.
* @param nodeMap The NodeMap to add new Coordinates to.
* @param layer The Layer to add imported objects to.
* @param progressIndicator Optional, to display the progress of the import.
*/
@Override
public void importToLayer(File mapFile, NodeMap nodeMap, Layer layer, ProgressIndicator progressIndicator) throws IOException {
objects = new VectorObjectList<VectorObject>();
parseFile(nodeMap, new XMLParser(new FileReader(mapFile)));
layer.setName(getFeedTitle());
layer.setLayerDescription(getFeedDescription());
if (layer instanceof VectorLayer) {
VectorLayer vecLayer = (VectorLayer) layer;
vecLayer.getObjectList().clear();
for (VectorObject object: objects) {
//Add Coordinates to the NodeMap
for (Coordinate c: object.getCoordinateList())
nodeMap.put(c);
vecLayer.addObject(object);
}
} else {
Logger.log(Logger.ERR, "Error in GeoRssImporter.importToLayer(File, NodeMap, Layer, ProgressIndicator) - Supplied Layer must be a VectorLayer.");
}
}
@Override
public DigitalMap importAsMap(File mapFile, ProgressIndicator progressIndicator) throws IOException {
DigitalMap mapData;
VectorLayer layer;
mapData = new DigitalMap();
layer = new VectorLayer();
mapData.addLayer(layer);
importToLayer(mapFile, mapData.getCoordinateSet(), layer, progressIndicator);
return mapData;
}
private static Coordinate parseCoordinate(XMLTag tag) {
Coordinate coordinate;
String token1, token2;
StringTokenizer st;
st = new StringTokenizer(tag.getTagContent());
if (st.countTokens() >= 2) {
token1 = st.nextToken(); //lat
token2 = st.nextToken(); //lon
coordinate = new Coordinate(0, Float.parseFloat(token1), Float.parseFloat(token2));
} else {
coordinate = null;
}
return coordinate;
}
private void parseFile(NodeMap nodeMap, XMLParser parser) {
ArrayList<XMLTag> itemTags;
VectorObject object;
XMLTag channelTag, rssTag, tag;
tag = parser.parseDocument();
itemTags = tag.getSubtagsByName("feed");
channelTag = null;
if (tag.containsSubTag("feed") || (itemTags.size() > 0)) {
channelTag = itemTags.get(0);
} else if (tag.containsSubTag("rss")) {
rssTag = tag.getSubtag("rss");
channelTag = rssTag.getSubtag("channel");
}
if (channelTag != null) {
feedTitle = channelTag.getSubtagContent("title");
feedLink = channelTag.getSubtagContent("link");
feedDescription = channelTag.getSubtagContent("description");
feedLanguage = channelTag.getSubtagContent("language");
feedDate = channelTag.getSubtagContent("pubDate");
itemTags = channelTag.getSubtagsByName("item");
if (itemTags.isEmpty())
itemTags = channelTag.getSubtagsByName("entry");
//read objects
for (XMLTag itemTag: itemTags) {
object = getItem(nodeMap, itemTag);
if (object != null)
objects.add(getItem(nodeMap, itemTag));
}
}
}
/**
* Returns a Coordinate list from the GeoRSS posList XML tag.
*
* Example:
* <gml:posList>45.256 -110.45 46.46 -109.48 43.84 -109.86</gml:posList>
*
* @param tag
* @return
*/
private static CoordinateList<Coordinate> parsePosList(NodeMap nodeMap, XMLTag tag) {
Coordinate c;
CoordinateList<Coordinate> list;
String token1, token2;
StringTokenizer st;
list = new CoordinateList<Coordinate>();
st = new StringTokenizer(tag.getTagContent());
while (st.countTokens() >= 2) {
token1 = st.nextToken(); //lat
token2 = st.nextToken(); //lon
c = new Coordinate(0, Float.parseFloat(token1), Float.parseFloat(token2));
list.add(c);
nodeMap.put(c);
}
return list;
}
}