root / org.gvsig.gpe / library / trunk / org.gvsig.gpe / org.gvsig.gpe.exportto / org.gvsig.gpe.exportto.kml / src / main / java / org / gvsig / gpe / exportto / kml / ExporttoKMLService.java @ 386
History | View | Annotate | Download (17.7 KB)
1 |
/**
|
---|---|
2 |
* gvSIG. Desktop Geographic Information System.
|
3 |
*
|
4 |
* Copyright (C) 2007-2013 gvSIG Association.
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU General Public License
|
8 |
* as published by the Free Software Foundation; either version 3
|
9 |
* of the License, or (at your option) any later version.
|
10 |
*
|
11 |
* This program is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License
|
17 |
* along with this program; if not, write to the Free Software
|
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 |
* MA 02110-1301, USA.
|
20 |
*
|
21 |
* For any additional information, do not hesitate to contact us
|
22 |
* at info AT gvsig.com, or visit our website www.gvsig.com.
|
23 |
*/
|
24 |
package org.gvsig.gpe.exportto.kml; |
25 |
|
26 |
import java.awt.geom.Rectangle2D; |
27 |
import java.io.File; |
28 |
import java.io.FileOutputStream; |
29 |
import java.io.IOException; |
30 |
import java.util.ArrayList; |
31 |
import java.util.Iterator; |
32 |
import java.util.List; |
33 |
import java.util.Map; |
34 |
|
35 |
import org.cresques.cts.ICoordTrans; |
36 |
import org.cresques.cts.IProjection; |
37 |
import org.slf4j.Logger; |
38 |
import org.slf4j.LoggerFactory; |
39 |
|
40 |
import org.gvsig.exportto.ExporttoService; |
41 |
import org.gvsig.exportto.ExporttoServiceException; |
42 |
import org.gvsig.exportto.ExporttoServiceFinishAction; |
43 |
import org.gvsig.exportto.swing.prov.file.AbstractExporttoFileService; |
44 |
import org.gvsig.fmap.crs.CRSFactory; |
45 |
import org.gvsig.fmap.dal.DALLocator; |
46 |
import org.gvsig.fmap.dal.DataStoreParameters; |
47 |
import org.gvsig.fmap.dal.exception.DataException; |
48 |
import org.gvsig.fmap.dal.exception.InitializeException; |
49 |
import org.gvsig.fmap.dal.exception.ProviderNotRegisteredException; |
50 |
import org.gvsig.fmap.dal.exception.ValidateDataParametersException; |
51 |
import org.gvsig.fmap.dal.feature.Feature; |
52 |
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
53 |
import org.gvsig.fmap.dal.feature.FeatureSet; |
54 |
import org.gvsig.fmap.dal.feature.FeatureStore; |
55 |
import org.gvsig.fmap.dal.feature.FeatureType; |
56 |
import org.gvsig.fmap.dal.feature.NewFeatureStoreParameters; |
57 |
import org.gvsig.fmap.dal.spi.DataManagerProviderServices; |
58 |
import org.gvsig.fmap.geom.DataTypes; |
59 |
import org.gvsig.fmap.geom.Geometry; |
60 |
import org.gvsig.fmap.geom.GeometryLocator; |
61 |
import org.gvsig.fmap.geom.GeometryManager; |
62 |
import org.gvsig.fmap.geom.exception.CreateEnvelopeException; |
63 |
import org.gvsig.fmap.geom.primitive.Envelope; |
64 |
import org.gvsig.fmap.mapcontext.MapContextException; |
65 |
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect; |
66 |
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend; |
67 |
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorLegend; |
68 |
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol; |
69 |
import org.gvsig.gpe.exportto.generic.util.CoordinatesSequenceBbox; |
70 |
import org.gvsig.gpe.exportto.generic.util.GeometryToGPEWriter; |
71 |
import org.gvsig.gpe.exportto.kml.style.KmlStyle; |
72 |
import org.gvsig.gpe.exportto.kml.style.StyleUtils; |
73 |
import org.gvsig.gpe.lib.api.GPELocator; |
74 |
import org.gvsig.gpe.lib.api.writer.IGPEWriterHandler; |
75 |
import org.gvsig.gpe.lib.api.writer.IGPEWriterHandlerImplementor; |
76 |
import org.gvsig.gpe.prov.kml.utils.Kml2_1_Tags; |
77 |
import org.gvsig.gpe.prov.kml.writer.GPEKmlWriterHandlerImplementor; |
78 |
import org.gvsig.tools.dispose.DisposableIterator; |
79 |
import org.gvsig.tools.task.AbstractMonitorableTask; |
80 |
import org.gvsig.tools.task.SimpleTaskStatus; |
81 |
import org.gvsig.xmlpull.lib.api.stream.IXmlStreamWriter; |
82 |
|
83 |
/**
|
84 |
* @author gvSIG Team
|
85 |
* @version $Id$
|
86 |
*
|
87 |
*/
|
88 |
public class ExporttoKMLService extends AbstractMonitorableTask implements |
89 |
ExporttoService { |
90 |
|
91 |
private static Logger logger = LoggerFactory.getLogger(ExporttoKMLService.class); |
92 |
|
93 |
private FeatureStore featureStore = null; |
94 |
private FLyrVect vectorLayer = null; |
95 |
private List<ICoordTrans> transfList = new ArrayList<ICoordTrans>(); |
96 |
|
97 |
private String mimeType = null; |
98 |
private File outFile = null; |
99 |
|
100 |
private boolean useLabels = false; |
101 |
private boolean attsAsBalloon = false; |
102 |
private boolean reprojectTo4326 = false; |
103 |
|
104 |
private IProjection targetproj = null; |
105 |
|
106 |
|
107 |
private NewFeatureStoreParameters newFeatureStoreParameters;
|
108 |
private ExporttoServiceFinishAction exporttoServiceFinishAction;
|
109 |
|
110 |
|
111 |
public ExporttoKMLService(
|
112 |
File kmlFile,
|
113 |
FeatureStore fstore, |
114 |
FLyrVect vlayer, |
115 |
String mtype,
|
116 |
|
117 |
boolean doLabels,
|
118 |
boolean balloon,
|
119 |
boolean reproject4326) {
|
120 |
|
121 |
super("Export to KML"); |
122 |
outFile = kmlFile; |
123 |
featureStore = fstore; |
124 |
vectorLayer = vlayer; |
125 |
mimeType = mtype; |
126 |
|
127 |
useLabels = doLabels; |
128 |
attsAsBalloon = balloon; |
129 |
reprojectTo4326 = reproject4326; |
130 |
|
131 |
ICoordTrans ct = vlayer.getCoordTrans(); |
132 |
if (reprojectTo4326) {
|
133 |
targetproj = CRSFactory.getCRS("EPSG:4326");
|
134 |
if (ct == null) { |
135 |
transfList.add(vlayer.getProjection().getCT(targetproj)); |
136 |
} else {
|
137 |
transfList.add(ct); |
138 |
transfList.add(ct.getPDest().getCT(targetproj)); |
139 |
} |
140 |
} else {
|
141 |
if (ct == null) { |
142 |
targetproj = vectorLayer.getProjection(); |
143 |
} else {
|
144 |
targetproj = ct.getPDest(); |
145 |
transfList.add(ct); |
146 |
} |
147 |
} |
148 |
} |
149 |
|
150 |
public NewFeatureStoreParameters getNewFeatureStoreParameters() {
|
151 |
return newFeatureStoreParameters;
|
152 |
} |
153 |
|
154 |
public void addParameters( |
155 |
NewFeatureStoreParameters newFeatureStoreParameters) { |
156 |
|
157 |
newFeatureStoreParameters.setDynValue("CRS", targetproj);
|
158 |
} |
159 |
|
160 |
public String getFileExtension() { |
161 |
return "kml"; |
162 |
} |
163 |
|
164 |
public void export(FeatureSet featureSet) throws ExporttoServiceException { |
165 |
|
166 |
IGPEWriterHandler wh = null;
|
167 |
FileOutputStream fos = null; |
168 |
String srs = targetproj.getAbrev();
|
169 |
|
170 |
String[] fldNames = null; |
171 |
Envelope env = null;
|
172 |
long count = 0; |
173 |
|
174 |
try {
|
175 |
count = featureSet.getSize(); |
176 |
this.taskStatus.setRangeOfValues(0, count); |
177 |
fldNames = getAttributes(featureStore.getDefaultFeatureType()); |
178 |
env = this.featureStore.getEnvelope();
|
179 |
} catch (DataException e) {
|
180 |
throw new ExporttoServiceException(e); |
181 |
} |
182 |
|
183 |
try {
|
184 |
env = reproject(env); |
185 |
} catch (CreateEnvelopeException cee) {
|
186 |
throw new ExporttoServiceException(cee); |
187 |
} |
188 |
|
189 |
Rectangle2D rect = new Rectangle2D.Double( |
190 |
env.getMinimum(0),
|
191 |
env.getMinimum(1),
|
192 |
env.getLength(0),
|
193 |
env.getLength(1));
|
194 |
|
195 |
File fixedFile = outFile;
|
196 |
try {
|
197 |
|
198 |
if (!outFile.getAbsolutePath().toLowerCase().endsWith(
|
199 |
"." + this.getFileExtension().toLowerCase())) { |
200 |
|
201 |
fixedFile = new File(outFile.getAbsolutePath() + "." + getFileExtension()); |
202 |
} |
203 |
|
204 |
wh = GPELocator.getGPEManager().createWriterByMimeType(mimeType); |
205 |
fos = new FileOutputStream(fixedFile); |
206 |
|
207 |
wh.setOutputStream(fos); |
208 |
wh.initialize(); |
209 |
// ==========================================
|
210 |
wh.startLayer(null, null, fixedFile.getName(), null, srs); |
211 |
// ============== Styles =============
|
212 |
Map<ISymbol, KmlStyle> symsty = null; |
213 |
IXmlStreamWriter xmlw = getXmlStreamWriter(wh); |
214 |
if (true && xmlw != null) { |
215 |
symsty = StyleUtils.getSymbolStyles( |
216 |
vectorLayer, |
217 |
featureSet, |
218 |
attsAsBalloon, |
219 |
fldNames); |
220 |
Iterator<KmlStyle> iter = symsty.values().iterator();
|
221 |
KmlStyle sty = null;
|
222 |
while (iter.hasNext()) {
|
223 |
sty = iter.next(); |
224 |
writeStyle(xmlw, sty); |
225 |
} |
226 |
} |
227 |
// ==========================================
|
228 |
wh.startBbox(null, new CoordinatesSequenceBbox(rect), srs); |
229 |
wh.endBbox(); |
230 |
// ============= Writing feature ============
|
231 |
writeFeatures(wh, xmlw, featureSet, fldNames, symsty, |
232 |
(IVectorLegend) vectorLayer.getLegend()); |
233 |
// ==========================================
|
234 |
wh.endLayer(); |
235 |
// ==========================================
|
236 |
wh.close(); |
237 |
fos.close(); |
238 |
} catch (Exception exc) { |
239 |
throw new ExporttoServiceException(exc); |
240 |
} |
241 |
|
242 |
this.taskStatus.setCurValue(count);
|
243 |
this.taskStatus.terminate();
|
244 |
this.finishAction(fixedFile, this.targetproj); |
245 |
} |
246 |
|
247 |
|
248 |
private Envelope reproject(Envelope env) throws CreateEnvelopeException { |
249 |
|
250 |
int sz = transfList.size();
|
251 |
if (sz == 0) { |
252 |
return env;
|
253 |
} else {
|
254 |
Envelope resp = env; |
255 |
try {
|
256 |
for (int i=0; i<sz; i++) { |
257 |
resp = resp.convert(transfList.get(i)); |
258 |
} |
259 |
} catch (Exception exc) { |
260 |
|
261 |
// If this process fails, we'll use "emergency values":
|
262 |
GeometryManager gm = GeometryLocator.getGeometryManager(); |
263 |
double[] min = new double[2]; |
264 |
double[] max = new double[2]; |
265 |
if (targetproj.isProjected()) {
|
266 |
min = new double[]{-20000000, -20000000}; |
267 |
max = new double[]{ 20000000, 20000000}; |
268 |
} else {
|
269 |
min = new double[]{-180, -90}; |
270 |
max = new double[]{ 180, 90}; |
271 |
} |
272 |
|
273 |
resp = gm.createEnvelope( |
274 |
min[0], min[1], |
275 |
max[0], max[1], |
276 |
Geometry.SUBTYPES.GEOM2D); |
277 |
|
278 |
} |
279 |
return resp;
|
280 |
} |
281 |
} |
282 |
|
283 |
private void writeStyle(IXmlStreamWriter xmlw, KmlStyle sty) throws IOException { |
284 |
|
285 |
xmlw.writeStartElement(Kml2_1_Tags.STYLE); |
286 |
xmlw.writeStartAttribute(Kml2_1_Tags.ID); |
287 |
xmlw.writeValue(sty.getId()); |
288 |
xmlw.writeEndAttributes(); |
289 |
sty.writeXml(xmlw); |
290 |
xmlw.writeEndElement(); |
291 |
} |
292 |
|
293 |
|
294 |
private IXmlStreamWriter getXmlStreamWriter(IGPEWriterHandler wh) {
|
295 |
|
296 |
IGPEWriterHandlerImplementor imple = wh.getImplementor(); |
297 |
if (!(imple instanceof GPEKmlWriterHandlerImplementor)) { |
298 |
/*
|
299 |
* Unexpected class
|
300 |
*/
|
301 |
return null; |
302 |
} |
303 |
GPEKmlWriterHandlerImplementor kmlimple = null;
|
304 |
kmlimple = (GPEKmlWriterHandlerImplementor) imple; |
305 |
IXmlStreamWriter xmlw = kmlimple.getXMLStreamWriter(); |
306 |
return xmlw;
|
307 |
} |
308 |
|
309 |
|
310 |
private void finishAction(File kmlfile, IProjection proj) |
311 |
throws ExporttoServiceException {
|
312 |
|
313 |
if (exporttoServiceFinishAction != null) { |
314 |
|
315 |
/*
|
316 |
* Export is done. We notify with a SHPStoreParameters,
|
317 |
* not with the NewSHPParameters we have used:
|
318 |
*/
|
319 |
DataManagerProviderServices dataman = |
320 |
(DataManagerProviderServices) DALLocator.getDataManager(); |
321 |
|
322 |
DataStoreParameters dsp = null;
|
323 |
try {
|
324 |
dsp = dataman.createStoreParameters("GPE");
|
325 |
} catch (Exception e) { |
326 |
throw new ExporttoServiceException( |
327 |
"Cannot add resulting kml file to view", e);
|
328 |
} |
329 |
|
330 |
dsp.setDynValue("File", kmlfile);
|
331 |
dsp.setDynValue("CRS", proj);
|
332 |
|
333 |
try {
|
334 |
dsp.validate(); |
335 |
} catch (ValidateDataParametersException e) {
|
336 |
throw new ExporttoServiceException(e); |
337 |
} |
338 |
exporttoServiceFinishAction.finished(kmlfile.getName(), dsp); |
339 |
} |
340 |
} |
341 |
|
342 |
private String[] getAttributes(FeatureType ftype) { |
343 |
|
344 |
FeatureAttributeDescriptor[] atts = ftype.getAttributeDescriptors();
|
345 |
FeatureAttributeDescriptor desc = null;
|
346 |
List<String> list = new ArrayList<String>(); |
347 |
for (int i=0; i<atts.length; i++) { |
348 |
desc = atts[i]; |
349 |
if (desc.getDataType().getType() != DataTypes.GEOMETRY) {
|
350 |
list.add(desc.getName()); |
351 |
} |
352 |
} |
353 |
return list.toArray(new String[0]); |
354 |
} |
355 |
|
356 |
private void writeFeatures( |
357 |
IGPEWriterHandler gwh, |
358 |
IXmlStreamWriter xmlw, |
359 |
FeatureSet fset, |
360 |
String[] fieldNames, |
361 |
Map<ISymbol, KmlStyle> symsty,
|
362 |
IVectorLegend lege) throws Exception { |
363 |
|
364 |
GeometryToGPEWriter gw = new GeometryToGPEWriter(gwh);
|
365 |
DisposableIterator diter = fset.fastIterator(); |
366 |
Feature feat = null;
|
367 |
long count = 0; |
368 |
this.taskStatus.setCurValue(count);
|
369 |
ISymbol sym = null;
|
370 |
while (diter.hasNext()) {
|
371 |
feat = (Feature) diter.next(); |
372 |
try {
|
373 |
sym = lege.getSymbolByFeature(feat); |
374 |
} catch (MapContextException mce) {
|
375 |
logger.info("While getting legend symbol.", mce);
|
376 |
} |
377 |
|
378 |
writeFeature(feat, gwh, xmlw, gw, count, fieldNames, symsty.get(sym)); |
379 |
count++; |
380 |
this.taskStatus.setCurValue(count);
|
381 |
} |
382 |
diter.dispose(); |
383 |
} |
384 |
|
385 |
private void writeFeature( |
386 |
Feature feat, |
387 |
IGPEWriterHandler gwh, |
388 |
IXmlStreamWriter xmlw, |
389 |
GeometryToGPEWriter gw, |
390 |
long index,
|
391 |
String[] fieldNames, |
392 |
KmlStyle ksty) throws IOException { |
393 |
|
394 |
String strindex = String.valueOf(index); |
395 |
|
396 |
if (this.useLabels) { |
397 |
String lbl = getLabelForFeature(feat);
|
398 |
gwh.startFeature(strindex, "FEATURE", lbl);
|
399 |
} else {
|
400 |
gwh.startFeature(strindex, "FEATURE", ""); |
401 |
} |
402 |
// =========================
|
403 |
// Style
|
404 |
if (ksty != null) { |
405 |
xmlw.writeStartElement(Kml2_1_Tags.STYLEURL); |
406 |
xmlw.writeValue("#" + ksty.getId());
|
407 |
xmlw.writeEndElement(); |
408 |
} |
409 |
// ===== Balloon ============
|
410 |
if (this.attsAsBalloon) { |
411 |
writeBalloon(xmlw, feat, fieldNames); |
412 |
} |
413 |
// ============= Geometry
|
414 |
Geometry geom = feat.getDefaultGeometry(); |
415 |
/*
|
416 |
* This has no effect if reprojection is not necessary
|
417 |
*/
|
418 |
geom = reproject(geom); |
419 |
gw.writeGeometry(geom, useLabels); |
420 |
// ============= Attributes
|
421 |
Object val = null; |
422 |
String fldname = null; |
423 |
for (int i=0; i<fieldNames.length; i++) { |
424 |
val = feat.get(fieldNames[i]); |
425 |
fldname = fieldNames[i].replace(' ', '_'); |
426 |
gwh.startElement("", fldname, val == null ? "" : val.toString()); |
427 |
gwh.endElement(); |
428 |
} |
429 |
// =========================
|
430 |
gwh.endFeature(); |
431 |
} |
432 |
|
433 |
private void writeBalloon(IXmlStreamWriter xmlw, Feature feat, String[] fieldNames) |
434 |
throws IOException { |
435 |
|
436 |
xmlw.writeStartElement(Kml2_1_Tags.EXTENDED_DATA); |
437 |
String fldrep = null; |
438 |
Object val = null; |
439 |
for (int i=0; i<fieldNames.length; i++) { |
440 |
fldrep = fieldNames[i].replace(' ', '_'); |
441 |
xmlw.writeStartElement(Kml2_1_Tags.DATA); |
442 |
// Att =====================================================
|
443 |
xmlw.writeStartAttribute(null, "name"); |
444 |
xmlw.writeValue(fldrep); |
445 |
xmlw.writeEndAttributes(); |
446 |
// Value =====================================================
|
447 |
xmlw.writeStartElement(Kml2_1_Tags.VALUE); |
448 |
val = feat.get(fieldNames[i]); |
449 |
xmlw.writeValue(val == null ? "" : val.toString()); |
450 |
xmlw.writeEndElement(); |
451 |
// =============================================
|
452 |
xmlw.writeEndElement(); |
453 |
} |
454 |
xmlw.writeEndElement(); |
455 |
|
456 |
/*
|
457 |
*
|
458 |
<ExtendedData>
|
459 |
<Data name="holeNumber">
|
460 |
<value>1</value>
|
461 |
</Data>
|
462 |
<Data name="holeYardage">
|
463 |
<value>234</value>
|
464 |
</Data>
|
465 |
<Data name="holePar">
|
466 |
<value>4</value>
|
467 |
</Data>
|
468 |
</ExtendedData>
|
469 |
*
|
470 |
*/
|
471 |
|
472 |
} |
473 |
|
474 |
private String getLabelForFeature(Feature feat) { |
475 |
|
476 |
if (this.vectorLayer.isLabeled()) { |
477 |
|
478 |
String[] flds = vectorLayer.getLabelingStrategy().getUsedFields(); |
479 |
int n = Math.min(flds.length, 2); |
480 |
if (n == 0) { |
481 |
return ""; |
482 |
} else {
|
483 |
String resp = ""; |
484 |
Object val = null; |
485 |
if (n == 1) { |
486 |
val = feat.get(flds[0]);
|
487 |
resp = (val == null) ? "" : val.toString(); |
488 |
} else {
|
489 |
// n == 2
|
490 |
val = feat.get(flds[0]);
|
491 |
resp = (val == null) ? "" : val.toString(); |
492 |
val = feat.get(flds[1]);
|
493 |
resp = (val == null) ? resp : resp + ", " + val.toString(); |
494 |
} |
495 |
return resp;
|
496 |
} |
497 |
|
498 |
} else {
|
499 |
return ""; |
500 |
} |
501 |
} |
502 |
|
503 |
public void setFinishAction( |
504 |
ExporttoServiceFinishAction exporttoServiceFinishAction) { |
505 |
this.exporttoServiceFinishAction = exporttoServiceFinishAction;
|
506 |
} |
507 |
|
508 |
private Geometry reproject(Geometry geom) {
|
509 |
|
510 |
int sz = transfList.size();
|
511 |
if (sz == 0) { |
512 |
return geom;
|
513 |
} else {
|
514 |
Geometry resp = geom.cloneGeometry(); |
515 |
for (int i=0; i<sz; i++) { |
516 |
resp.reProject(transfList.get(i)); |
517 |
} |
518 |
return resp;
|
519 |
} |
520 |
|
521 |
} |
522 |
|
523 |
} |