/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.data.gx.xml;
import com.vividsolutions.jts.geom.Coordinate;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import org.geotoolkit.data.gx.GxUtilities;
import org.geotoolkit.data.gx.model.AbstractTourPrimitive;
import org.geotoolkit.data.gx.model.Angles;
import org.geotoolkit.data.gx.model.AnimatedUpdate;
import org.geotoolkit.data.gx.model.EnumFlyToMode;
import org.geotoolkit.data.gx.model.EnumPlayMode;
import org.geotoolkit.data.gx.model.FlyTo;
import org.geotoolkit.data.gx.model.GxModelConstants;
import org.geotoolkit.data.gx.model.LatLonQuad;
import org.geotoolkit.data.gx.model.MultiTrack;
import org.geotoolkit.data.gx.model.PlayList;
import org.geotoolkit.data.gx.model.SoundCue;
import org.geotoolkit.data.gx.model.TourControl;
import org.geotoolkit.data.gx.model.Track;
import org.geotoolkit.data.gx.model.Wait;
import org.geotoolkit.data.kml.KmlUtilities;
import org.geotoolkit.data.kml.model.AltitudeMode;
import org.geotoolkit.data.kml.model.Extensions;
import org.geotoolkit.data.kml.model.Extensions.Names;
import org.geotoolkit.data.kml.model.KmlException;
import org.geotoolkit.data.kml.model.TimeSpan;
import org.geotoolkit.data.kml.model.TimeStamp;
import org.geotoolkit.data.kml.xml.KmlExtensionWriter;
import org.geotoolkit.data.kml.xml.KmlWriter;
import org.geotoolkit.data.kml.xsd.SimpleTypeContainer;
import org.geotoolkit.xml.StaxStreamWriter;
import org.opengis.feature.Feature;
import org.geotoolkit.data.kml.xml.KmlConstants;
import static org.geotoolkit.data.gx.xml.GxConstants.*;
import static java.util.Collections.*;
/**
*
* @author Samuel Andrés
* @module
*/
public class GxWriter extends StaxStreamWriter implements KmlExtensionWriter {
private final KmlWriter kmlWriter;
public Map<Object, List<Extensions.Names>> complexTable = new HashMap<>();
public Map<String, List<Extensions.Names>> simpleTable = new HashMap<>();
/**
*
* @param w Kmlwriter used by GxWriter.
*/
public GxWriter(KmlWriter w) {
super();
initComplexTable();
initSimpleTable();
kmlWriter = w;
}
/**
* @{@inheritDoc }
*/
@Override
public void setOutput(Object output)
throws XMLStreamException, IOException{
super.setOutput(output);
}
public void writeTour(Feature tour) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_TOUR);
kmlWriter.writeCommonAbstractFeature(tour);
final Object value = tour.getPropertyValue(KmlConstants.ATT_PLAYLIST);
if (value instanceof Iterable<?>) {
for (final Object p : (Iterable<?>) value) {
writePlayList((PlayList) p);
}
}
writer.writeEndElement();
}
private void writePlayList(PlayList playlist) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_PLAYLIST);
for (AbstractTourPrimitive tourPrimitive : playlist.getTourPrimitives()) {
writeAbstractTourPrimitive(tourPrimitive);
}
writer.writeEndElement();
}
private void writeAbstractTourPrimitive(AbstractTourPrimitive tourPrimitive)
throws XMLStreamException, KmlException {
if (tourPrimitive instanceof FlyTo) {
writeFlyTo((FlyTo) tourPrimitive);
} else if (tourPrimitive instanceof AnimatedUpdate) {
writeAnimatedUpdate((AnimatedUpdate) tourPrimitive);
} else if (tourPrimitive instanceof TourControl) {
writeTourControl((TourControl) tourPrimitive);
} else if (tourPrimitive instanceof Wait) {
writeWait((Wait) tourPrimitive);
} else if (tourPrimitive instanceof SoundCue) {
writeSoundCue((SoundCue) tourPrimitive);
}
}
private void writeCommonAbstractTourPrimitive(AbstractTourPrimitive tourPrimitive)
throws XMLStreamException, KmlException
{
kmlWriter.writeCommonAbstractObject(tourPrimitive);
}
private void writeFlyTo(FlyTo flyTo) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_FLY_TO);
writeCommonAbstractTourPrimitive(flyTo);
if (KmlUtilities.isFiniteNumber(flyTo.getDuration())) {
writeDuration(flyTo.getDuration());
}
if (flyTo.getFlyToMode() != null) {
writeFlyToMode(flyTo.getFlyToMode());
}
kmlWriter.writeAbstractView(flyTo.getView());
writer.writeEndElement();
}
private void writeAnimatedUpdate(AnimatedUpdate animatedUpdate) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_ANIMATED_UPDATE);
writeCommonAbstractTourPrimitive(animatedUpdate);
if (KmlUtilities.isFiniteNumber(animatedUpdate.getDuration())) {
writeDuration(animatedUpdate.getDuration());
}
kmlWriter.writeUpdate(animatedUpdate.getUpdate());
writer.writeEndElement();
}
private void writeTourControl(TourControl tourControl) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_TOUR_CONTROL);
writeCommonAbstractTourPrimitive(tourControl);
if (tourControl.getPlayMode() != null) {
writePlayMode(tourControl.getPlayMode());
}
writer.writeEndElement();
}
private void writeWait(Wait wait) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_WAIT);
writeCommonAbstractTourPrimitive(wait);
if (KmlUtilities.isFiniteNumber(wait.getDuration())) {
writeDuration(wait.getDuration());
}
writer.writeEndElement();
}
private void writeSoundCue(SoundCue soundCue) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_SOUND_CUE);
writeCommonAbstractTourPrimitive(soundCue);
kmlWriter.writeHref(soundCue.getHref());
writer.writeEndElement();
}
public void writeLatLonQuad(LatLonQuad latLonQuad) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_LAT_LON_QUAD);
kmlWriter.writeCommonAbstractObject(latLonQuad);
kmlWriter.writeCoordinates(latLonQuad.getCoordinates());
writer.writeEndElement();
}
public void writeTimeSpan(TimeSpan timeSpan) throws XMLStreamException, KmlException{
writer.writeStartElement(URI_GX, TAG_TIME_SPAN);
kmlWriter.writeCommonAbstractTimePrimitive(timeSpan);
kmlWriter.writeBegin(timeSpan.getBegin());
kmlWriter.writeEnd(timeSpan.getEnd());
if (timeSpan.extensions().simples(Names.TIME_SPAN) != null) {
// TODO
}
if (timeSpan.extensions().complexes(Names.TIME_SPAN) != null) {
// TODO
}
writer.writeEndElement();
}
public void writeTimeStamp(TimeStamp timeStamp) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_TIME_STAMP);
kmlWriter.writeCommonAbstractTimePrimitive(timeStamp);
kmlWriter.writeWhen(timeStamp.getWhen());
if (timeStamp.extensions().simples(Names.TIME_STAMP) != null) {
// TODO
}
if (timeStamp.extensions().complexes(Names.TIME_STAMP) != null) {
// TODO
}
writer.writeEndElement();
}
public void writeTrack(Track track) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_TRACK);
kmlWriter.writeCommonAbstractGeometry(track);
kmlWriter.writeAltitudeMode(track.getAltitudeMode());
for (Calendar when : track.getWhens()) {
kmlWriter.writeWhen(when);
}
if (track.getCoord() != null) {
for (Coordinate coord : track.getCoord().toCoordinateArray()) {
writeCoord(coord);
}
}
for (Angles angles : track.getAngles()) {
writeAngles(angles);
}
if (track.getModel() != null) {
kmlWriter.writeModel(track.getModel());
}
kmlWriter.writeExtendedData(track.getExtendedData());
writer.writeEndElement();
}
public void writeMultiTrack(MultiTrack multiTrack) throws XMLStreamException, KmlException {
writer.writeStartElement(URI_GX, TAG_MULTI_TRACK);
kmlWriter.writeCommonAbstractGeometry(multiTrack);
kmlWriter.writeAltitudeMode(multiTrack.getAltitudeMode());
writeInterpolate(multiTrack.getInterpolate());
for(Track track : multiTrack.getTracks()) {
writeTrack(track);
}
writer.writeEndElement();
}
public void writeCoord(Coordinate coord) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_COORD);
writer.writeCharacters(GxUtilities.toString(coord));
writer.writeEndElement();
}
public void writeAngles(Angles angles) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_ANGLES);
writer.writeCharacters(GxUtilities.toString(angles));
writer.writeEndElement();
}
private void writeDuration(double duration) throws XMLStreamException {
if (DEF_DURATION != duration) {
writer.writeStartElement(URI_GX, TAG_DURATION);
writer.writeCharacters(Double.toString(duration));
writer.writeEndElement();
}
}
private void writeFlyToMode(EnumFlyToMode enumFlyToMode) throws XMLStreamException {
if (!DEF_FLY_TO_MODE.equals(enumFlyToMode)) {
writer.writeStartElement(URI_GX, TAG_FLY_TO_MODE);
writer.writeCharacters(enumFlyToMode.getFlyToMode());
writer.writeEndElement();
}
}
private void writePlayMode(EnumPlayMode playMode) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_PLAY_MODE);
writer.writeCharacters(playMode.getPlayMode());
writer.writeEndElement();
}
public void writeAltitudeMode(AltitudeMode altitudeMode) throws XMLStreamException {
if (DEF_ALTITUDE_MODE != altitudeMode) {
writer.writeStartElement(URI_GX, TAG_ALTITUDE_MODE);
writer.writeCharacters(altitudeMode.getAltitudeMode());
writer.writeEndElement();
}
}
public void writeBalloonVisibility(Boolean bv) throws XMLStreamException {
if (DEF_BALLOON_VISIBILITY != bv.booleanValue()) {
writer.writeStartElement(URI_GX, TAG_BALLOON_VISIBILITY);
if (bv) {
writer.writeCharacters(SimpleTypeContainer.BOOLEAN_TRUE);
} else {
writer.writeCharacters(SimpleTypeContainer.BOOLEAN_FALSE);
}
writer.writeEndElement();
}
}
public void writeInterpolate(Boolean inter) throws XMLStreamException{
if (DEF_INTERPOLATE != inter.booleanValue()) {
writer.writeStartElement(URI_GX, TAG_INTERPOLATE);
if (inter) {
writer.writeCharacters(SimpleTypeContainer.BOOLEAN_TRUE);
} else {
writer.writeCharacters(SimpleTypeContainer.BOOLEAN_FALSE);
}
writer.writeEndElement();
}
}
public void writeX(int abs) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_X);
writer.writeCharacters(String.valueOf(abs));
writer.writeEndElement();
}
public void writeY(int abs) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_Y);
writer.writeCharacters(String.valueOf(abs));
writer.writeEndElement();
}
public void writeW(int abs) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_W);
writer.writeCharacters(String.valueOf(abs));
writer.writeEndElement();
}
public void writeH(int abs) throws XMLStreamException {
writer.writeStartElement(URI_GX, TAG_H);
writer.writeCharacters(String.valueOf(abs));
writer.writeEndElement();
}
/**
* @{@inheritDoc }
*/
@Override
public void writeComplexExtensionElement(String kmlVersionUri,
Extensions.Names ext, Object contentsElement)
throws XMLStreamException, KmlException
{
// Feature element
if (contentsElement instanceof Feature) {
Feature feature = (Feature) contentsElement;
if (feature.getType().equals(GxModelConstants.TYPE_TOUR)) {
writeTour(feature);
}
}
// non-Feature element
else if (contentsElement instanceof LatLonQuad) {
writeLatLonQuad((LatLonQuad) contentsElement);
} else if (contentsElement instanceof TimeSpan) {
writeTimeSpan((TimeSpan) contentsElement);
} else if (contentsElement instanceof TimeStamp) {
writeTimeStamp((TimeStamp) contentsElement);
} else if (contentsElement instanceof AltitudeMode) {
writeAltitudeMode((AltitudeMode) contentsElement);
} else if (contentsElement instanceof Track) {
writeTrack((Track) contentsElement);
} else if (contentsElement instanceof MultiTrack) {
writeMultiTrack((MultiTrack) contentsElement);
}
}
/**
* @{@inheritDoc }
*/
@Override
public void writeSimpleExtensionElement(String kmlVersionUri,
Extensions.Names ext, SimpleTypeContainer contentsElement)
throws XMLStreamException, KmlException
{
if (TAG_BALLOON_VISIBILITY.equals(contentsElement.getTagName())) {
writeBalloonVisibility((Boolean) contentsElement.getValue());
} else if (TAG_X.equals(contentsElement.getTagName())) {
writeX((Integer) contentsElement.getValue());
} else if (TAG_Y.equals(contentsElement.getTagName())) {
writeY((Integer) contentsElement.getValue());
} else if (TAG_W.equals(contentsElement.getTagName())) {
writeW((Integer) contentsElement.getValue());
} else if (TAG_H.equals(contentsElement.getTagName())) {
writeH((Integer) contentsElement.getValue());
}
}
/**
* @{@inheritDoc }
*/
@Override
public boolean canHandleComplex(String kmlVersionUri,
Extensions.Names ext, Object contentObject)
{
Boolean reponse = false;
List<Extensions.Names> liste = null;
if (contentObject instanceof Feature) {
liste = complexTable.get(((Feature) contentObject).getType());
} else {
for (Object c : complexTable.keySet()) {
if (c instanceof Class
&& ((Class) c).isInstance(contentObject)) {
liste = complexTable.get(c);
break;
}
}
}
if (liste != null) {
if (EMPTY_LIST.equals(liste)
|| liste.contains(ext)) {
reponse = true;
}
}
return reponse;
}
/**
* @{@inheritDoc }
*/
@Override
public boolean canHandleSimple(String kmlVersionUri, Names ext, String elementTag) {
List<Names> liste = simpleTable.get(elementTag);
Boolean reponse = false;
if (liste != null
&& liste.contains(ext)) {
reponse = true;
}
return reponse;
}
/**
* Initializes Complex table. This table contains mapping between
* Gx extensions classes and their extensions names (i.e. : candidate places
* in KML file).
*/
private void initComplexTable() {
List<Names> latLonQuadContainersList = new ArrayList<Names>();
latLonQuadContainersList.add(Names.GROUND_OVERLAY);
List<Names> timeSpanContainers = new ArrayList<Names>();
timeSpanContainers.add(Names.VIEW);
List<Names> timeStampContainers = new ArrayList<Names>();
timeStampContainers.add(Names.VIEW);
complexTable.put(GxModelConstants.TYPE_TOUR, EMPTY_LIST);
complexTable.put(Track.class, EMPTY_LIST);
complexTable.put(MultiTrack.class, EMPTY_LIST);
complexTable.put(TimeSpan.class, timeSpanContainers);
complexTable.put(TimeStamp.class, timeStampContainers);
complexTable.put(LatLonQuad.class, latLonQuadContainersList);
complexTable.put(AltitudeMode.class, EMPTY_LIST);
}
/**
* Initializes Complex table. This table contains mapping between
* Gx extensions simple values and their extensions names (i.e. : candidate
* places in KML file).
*/
private void initSimpleTable() {
List<Names> balloonVisibilityList = new ArrayList<>();
balloonVisibilityList.add(Extensions.Names.FEATURE);
List<Names> hList = new ArrayList<>();
hList.add(Extensions.Names.BASIC_LINK);
List<Names> wList = new ArrayList<>();
wList.add(Extensions.Names.BASIC_LINK);
List<Names> xList = new ArrayList<>();
xList.add(Extensions.Names.BASIC_LINK);
List<Names> yList = new ArrayList<>();
yList.add(Extensions.Names.BASIC_LINK);
simpleTable.put(GxConstants.TAG_BALLOON_VISIBILITY, balloonVisibilityList);
simpleTable.put(GxConstants.TAG_H, hList);
simpleTable.put(GxConstants.TAG_W, wList);
simpleTable.put(GxConstants.TAG_X, xList);
simpleTable.put(GxConstants.TAG_Y, yList);
}
}