Hi,
based on the previous discussion I’ve developed a prototype of a new KML module
with limited functionality, but enough meat to foster further discussion on the
design of the module.
The module can be found in community, named kml2:
https://github.com/geoserver/geoserver/tree/master/src/community/kml2
If you want to build it as a replacement of the kml module, you can by doing so:
mvn eclipse:eclipse -P!kml,kml2
The \ is there to escape the ! which is otherwise interpreted as a signal by the
linux shell, if you are on another operating system you might not need it
(and oh, the ! is there to exclude the kml profile).
The module as suggested in the past is based on an object model of OGC
KML, derived from the official schemas.
At first I wanted to created it via jaxb and then strip the annotations, but then
Alessio pointed me at JAK (https://code.google.com/p/javaapiforkml/),
which already has the object model, and can also encode/decode KML
via JAXB.
Now, in the past we had issues including JAXB in the classpath so I paid
attention in two ways:
- first, I’ve setup the code to use the JAXB version included in the JDK.
It generates namespace prefixes that are not good looking, but works
and spares us an extra dependency - I’ve started the GeoServer for the CITE tests for WFS 1.1, configured styles
for the layers, generated a bit of KML to kick the JAXB machinery, and
then run the CITE tests themselves, getting a full pass
So, by the above, it seems the conflict is no more there.
The only drawback I can see in JAK is that it’s a 600KB dependency, but
99% of it is the KML model itself, so… we’d have it anyways one way or the
other.
The current kml2 prototype can respond to two types of requests:
- a network links generation
- a download mode KML generation out of vector feature
The network link generation shows how to build a in memory representation
of KML for small amounts of data. It’s really simple, the kml, document,
folder and network links are built using the JAK api:
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/NetworkLinkMapOutputFormat.java
The serialization of the KML document happens here, and it’s equally simple:
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/KMLMapResponse.java#L39
Now, the problem with the object model is that theoretically one would have
to build the whole kml tree in memory before serializing it.
Following Justin’s suggestion I’ve created some “streaming list” support
that tricks JAXB into serializing a large document without actually keeping it in
memory.
The base of the work is the Sequence, which is generated by a SequenceFactory
and then wrapped into a SequenceList to turn the Sequence into a “fake” list:
https://github.com/geoserver/geoserver/tree/master/src/community/kml2/src/main/java/org/geoserver/kml/sequence
The two sequences are around at the moment are one generating a list of Folder based on the
layers in the GetMap request, and one that generates a list of Placemark based on the
features contained in a layer:
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/sequence/FolderSequenceFactory.java
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/sequence/FeatureSequenceFactory.java
Both sequences are mostly concerned with the iteration itself, and they just generate a dummy/empty Folder/Placemark.
What fills them are the KmlDecorator, classes that receive the kml object (a Feature subclass), a kml encoding context
providing all the objects that normally encoders would need, and add some portions of the Feature into it:
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/KmlDecoratorFactory.java
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/KmlEncodingContext.java
This allows to have classes that do the encoding of a significant, but isolated part of the KML, such as geometry encoding,
or name/description encoding:
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/PlacemarkGeometryDecoratorFactory.java
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/PlacemarkNameDecoratorFactory.java
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/PlacemarkDescriptionDecoratorFactory.java
https://github.com/geoserver/geoserver/blob/master/src/community/kml2/src/main/java/org/geoserver/kml/decorator/PlacemarkStyleDecoratorFactory.java
The separation between the decorator factory and the decoration is setup so that the factory can be registered in the Spring
app context as a plugin, but the KmlDecorator itself does not need to concern itself with being thread safe and can keep
state.
Also, the factory can decide not to generate the decorator in case some options disable the generation of some bits of KML.
One interesting example of the above concept is that we could have a plugin encoding extended data, have a flag to control
style/lookat/description generation, and make up a bridge so that the KML generator would end up building KML as data + attributes,
and use it as a WFS output format.
Also, given that the decorator factories are pluggable, I believe most of the GeoSearch work can be done in a pure plugin
fashion (and same goes for Collada model inclusion as part of W3DS).
One thing that I still haven’t quite figured out is decorator ordering… all of the ones I’ve written so far are orthogonal,
they can be run in whatever order, but for example a geosearch plugin should run after the Placemark description
generation.
A simple (lame?) idea could be to just use ExtensionPriority, which may work if we have really few extension
that need ordering.
Another approach that could scale up better with a large set of dependencies could be pairwise ordering of
some sort, something like:
boolean dependsOn(KmlDecorator other)
and then the code would use those relationships to build a dependency tree… which sounds quite a bit more complicated.
Well this is what I have so far. One further step that I’d like to look at is to make the filtering of feature collections
pluggable, so that regionation can be managed as a plugin.
Ah, the idea of a classification by attribute is still lingering in my head, that might be tackled somehow by creating
a tree out of the MapContent… think of having some Layer classes that are actually groups, containers, that
the KML generator could understand, and then just use the MapContent mangling extension to rebuild the
map content along some classification.
Anyways, this is not really on the target, what I have on the plate is functional compatibility, not really new functionality.
We’ll see how much time remains towards the end.
Anyways, what do you think?
Cheers
Andrea
–
==
Our support, Your Success! Visit http://opensdi.geo-solutions.it for more information.
Ing. Andrea Aime
@geowolf
Technical Lead
GeoSolutions S.A.S.
Via Poggio alle Viti 1187
55054 Massarosa (LU)
Italy
phone: +39 0584 962313
fax: +39 0584 1660272
mob: +39 339 8844549
http://www.geo-solutions.it
http://twitter.com/geosolutions_it