Skip to content

Commit 34d1445

Browse files
committed
bring back proper kmz export
1 parent 44a5d8f commit 34d1445

File tree

4 files changed

+291
-6
lines changed

4 files changed

+291
-6
lines changed

lib/com/hydrologis/smash/import_export_plugins/export/kml/kml_export.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ class _KmlExportWidgetState extends State<KmlExportWidget>
9999
var exportsFolder = await Workspace.getExportsFolder();
100100
var ts = HU.TimeUtilities.DATE_TS_FORMATTER.format(DateTime.now());
101101
outFilePath =
102-
HU.FileUtilities.joinPaths(exportsFolder.path, "smash_export_$ts.kml");
102+
HU.FileUtilities.joinPaths(exportsFolder.path, "smash_export_$ts.kmz");
103103

104-
await GpxExporter.exportDb(widget.projectDb, File(outFilePath), true);
104+
await KmlExporter().exportDb(widget.projectDb, File(outFilePath));
105+
// await GpxExporter.exportDb(widget.projectDb, File(outFilePath), true);
105106

106107
setState(() {
107108
building = false;

lib/com/hydrologis/smash/import_export_plugins/utils/gpx_kml_utilities.dart

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,282 @@ class GpxExporter {
148148
}
149149
}
150150
}
151+
152+
class KmlExporter {
153+
String IMAGES_SEPARATOR = ";";
154+
late String dbName;
155+
late bool useFiltered;
156+
157+
Future<void> exportDb(ProjectDb db, File outputFile) async {
158+
dbName = HU.FileUtilities.nameFromFile(db.getPath(), false);
159+
160+
useFiltered = GpPreferences().getBooleanSync(
161+
SmashPreferencesKeys.KEY_GPS_USE_FILTER_GENERALLY, false);
162+
163+
var kmlString = "";
164+
kmlString += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
165+
kmlString +=
166+
"<kml xmlns=\"http://www.opengis.net/kml/2.2\" xmlns:gx=\"http://www.google.com/kml/ext/2.2\"\n";
167+
kmlString +=
168+
"xmlns:kml=\"http://www.opengis.net/kml/2.2\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n";
169+
kmlString += "<Document>\n";
170+
kmlString += "<name>";
171+
kmlString += dbName;
172+
kmlString += "</name>\n";
173+
kmlString += getMarker("red-pushpin",
174+
"http://maps.google.com/mapfiles/kml/pushpin/red-pushpin.png", 20, 2);
175+
kmlString += getMarker("yellow-pushpin",
176+
"http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png", 20, 2);
177+
kmlString += getMarker("bookmark-icon",
178+
"http://maps.google.com/mapfiles/kml/pal4/icon39.png", 16, 16);
179+
kmlString += getMarker("camera-icon",
180+
"http://maps.google.com/mapfiles/kml/pal4/icon38.png", 16, 16);
181+
kmlString += getMarker("info-icon",
182+
"http://maps.google.com/mapfiles/kml/pal3/icon35.png", 16, 16);
183+
184+
var notes = db.getNotes();
185+
for (Note note in notes) {
186+
kmlString += noteToKmlString(db, note);
187+
}
188+
var logs = db.getLogs();
189+
for (Log log in logs) {
190+
kmlString += logToKmlString(db, log);
191+
}
192+
var simpleImages = db.getImages(onlySimple: true);
193+
for (DbImage image in simpleImages) {
194+
kmlString += simpleImageToKmlString(db, image);
195+
}
196+
kmlString += "</Document>\n";
197+
kmlString += "</kml>\n";
198+
199+
final archive = Archive();
200+
archive.addFile(ArchiveFile.string("kml.kml", kmlString));
201+
202+
var images = db.getImages(onlySimple: false);
203+
for (var image in images) {
204+
var imgBytes = db.getImageDataBytes(image.imageDataId!);
205+
archive.addFile(
206+
ArchiveFile.noCompress(image.text, imgBytes!.length, imgBytes));
207+
}
208+
var zipData = ZipEncoder().encode(archive, modified: DateTime.now())!;
209+
await outputFile.writeAsBytes(zipData);
210+
}
211+
212+
String logToKmlString(ProjectDb db, Log log) {
213+
String name = makeXmlSafe(log.text);
214+
var sB = "";
215+
sB += "<Placemark>\n";
216+
sB += "<name>${name}</name>\n";
217+
sB += "<visibility>1</visibility>\n";
218+
sB += "<LineString>\n";
219+
sB += "<tessellate>1</tessellate>\n";
220+
sB += "<coordinates>\n";
221+
222+
List<LogDataPoint> logDataPoints = db.getLogDataPointsById(log.id!);
223+
for (LogDataPoint point in logDataPoints) {
224+
sB += "${point.lon},${point.lat},1 \n";
225+
}
226+
sB += "</coordinates>\n";
227+
sB += "</LineString>\n";
228+
sB += "<Style>\n";
229+
sB += "<LineStyle>\n";
230+
// int parsedColor = ColorUtilities.toColor(color);
231+
LogProperty? logProperties = db.getLogProperties(log.id!);
232+
String hexColor = logProperties!.color ?? "#FF0000";
233+
sB += "<color>${hexColor}</color>\n";
234+
sB += "<width>${logProperties.width}</width>\n";
235+
sB += "</LineStyle>\n";
236+
sB += "</Style>\n";
237+
sB += "</Placemark>\n";
238+
239+
return sB.toString();
240+
}
241+
242+
String noteToKmlString(ProjectDb db, Note note) {
243+
var images = [];
244+
String name = makeXmlSafe(note.text);
245+
if (note.hasForm()) {
246+
name = FormUtilities.getFormItemLabel(note.form!, name);
247+
}
248+
var sB = "<Placemark>\n";
249+
sB += "<styleUrl>#red-pushpin</styleUrl>\n";
250+
// sB += "<styleUrl>#info-icon</styleUrl>\n";
251+
sB += "<name>${name}</name>\n";
252+
sB += "<description>\n";
253+
254+
if (note.hasForm()) {
255+
sB += "<![CDATA[\n";
256+
257+
Map<String, dynamic> sectionObject = jsonDecode(note.form!);
258+
if (sectionObject.containsKey(ATTR_SECTIONNAME)) {
259+
String sectionName = sectionObject[ATTR_SECTIONNAME];
260+
sB += "<h1>${sectionName}</h1>\n";
261+
}
262+
263+
List<String> formsNames = TagsManager.getFormNames4Section(sectionObject);
264+
for (String formName in formsNames) {
265+
sB += "<h2>${formName}</h2>\n";
266+
267+
sB +=
268+
"<table style=\"text-align: left; width: 100%;\" border=\"1\" cellpadding=\"5\" cellspacing=\"2\">";
269+
sB += "<tbody>";
270+
271+
Map<String, dynamic>? form4Name =
272+
TagsManager.getForm4Name(formName, sectionObject);
273+
List<Map<String, dynamic>> formItems =
274+
TagsManager.getFormItems(form4Name);
275+
for (int i = 0; i < formItems.length; i++) {
276+
Map<String, dynamic> formItem = formItems[i];
277+
if (!formItem.containsKey(TAG_KEY)) {
278+
continue;
279+
}
280+
281+
String type = formItem[TAG_TYPE];
282+
String key = formItem[TAG_KEY];
283+
String value = formItem[TAG_VALUE];
284+
285+
String label = key;
286+
if (formItem.containsKey(TAG_LABEL)) {
287+
label = formItem[TAG_LABEL];
288+
}
289+
290+
if (type == TYPE_PICTURES) {
291+
if (value.trim().length == 0) {
292+
continue;
293+
}
294+
var imageIdsSplit = value.split(IMAGES_SEPARATOR);
295+
for (String imageId in imageIdsSplit) {
296+
DbImage image = db.getImageById(int.parse(imageId));
297+
String imgName = image.text;
298+
sB += "<tr>";
299+
sB +=
300+
"<td colspan=\"2\" style=\"text-align: left; vertical-align: top; width: 100%;\">";
301+
sB += "<img src=\"${imgName}\" width=\"300\">";
302+
sB += "</td>";
303+
sB += "</tr>";
304+
305+
images.add(imageId);
306+
}
307+
} else if (type == TYPE_MAP) {
308+
if (value.trim().length == 0) {
309+
continue;
310+
}
311+
sB += "<tr>";
312+
String imageId = value.trim();
313+
DbImage image = db.getImageById(int.parse(imageId));
314+
String imgName = image.text;
315+
sB +=
316+
"<td colspan=\"2\" style=\"text-align: left; vertical-align: top; width: 100%;\">";
317+
sB += "<img src=\"${imgName}\" width=\"300\">";
318+
sB += "</td>";
319+
sB += "</tr>";
320+
images.add(imageId);
321+
} else if (type == TYPE_SKETCH) {
322+
if (value.trim().length == 0) {
323+
continue;
324+
}
325+
var imageIdsSplit = value.split(IMAGES_SEPARATOR);
326+
for (String imageId in imageIdsSplit) {
327+
DbImage image = db.getImageById(int.parse(imageId));
328+
String imgName = image.text;
329+
sB += "<tr>";
330+
sB +=
331+
"<td colspan=\"2\" style=\"text-align: left; vertical-align: top; width: 100%;\">";
332+
sB += "<img src=\"${imgName}\" width=\"300\">";
333+
sB += "</td>";
334+
sB += "</tr>";
335+
336+
images.add(imageId);
337+
}
338+
} else {
339+
sB += "<tr>";
340+
sB +=
341+
"<td style=\"text-align: left; vertical-align: top; width: 50%;\">";
342+
sB += label;
343+
sB += "</td>";
344+
sB +=
345+
"<td style=\"text-align: left; vertical-align: top; width: 50%;\">";
346+
sB += value;
347+
sB += "</td>";
348+
sB += "</tr>";
349+
}
350+
}
351+
sB += "</tbody>";
352+
sB += "</table>";
353+
}
354+
sB += "]]>\n";
355+
} else {
356+
String description = makeXmlSafe(note.description);
357+
sB += description;
358+
sB += "\n";
359+
sB += TimeUtilities.ISO8601_TS_FORMATTER_MILLIS
360+
.format(DateTime.fromMillisecondsSinceEpoch(note.timeStamp));
361+
}
362+
363+
sB += "</description>\n";
364+
sB += "<gx:balloonVisibility>1</gx:balloonVisibility>\n";
365+
sB += "<Point>\n";
366+
sB += "<coordinates>";
367+
sB += note.lon.toString();
368+
sB += ",";
369+
sB += note.lat.toString();
370+
sB += ",0</coordinates>\n";
371+
sB += "</Point>\n";
372+
sB += "</Placemark>\n";
373+
374+
return sB.toString();
375+
}
376+
377+
String simpleImageToKmlString(ProjectDb db, DbImage image) {
378+
String name = makeXmlSafe(image.text);
379+
var kml = """
380+
<Placemark>
381+
<styleUrl>#camera-icon</styleUrl>
382+
<name>${name}</name>
383+
<description>
384+
<![CDATA[
385+
<h1>${name}</h1>
386+
<table style="text-align: left; width: 100%;" border="1" cellpadding="5" cellspacing="2">
387+
<tbody>
388+
<tr>
389+
<td colspan="2" style="text-align: left; vertical-align: top; width: 100%;">
390+
<img src="${name}" width="300">
391+
</td>
392+
</tr>
393+
</tbody>
394+
</table>
395+
]]>
396+
</description>
397+
<gx:balloonVisibility>1</gx:balloonVisibility>
398+
<Point>
399+
<coordinates>${image.lon.toString()},${image.lat.toString()},0</coordinates>
400+
</Point>
401+
</Placemark>
402+
""";
403+
404+
return kml;
405+
}
406+
407+
String makeXmlSafe(String? string) {
408+
if (string == null) return "";
409+
string = string.replaceAll("&", "&amp;");
410+
return string;
411+
}
412+
413+
String getMarker(String alias, String url, int x, int y) {
414+
var sb = """
415+
<Style id="${alias}">
416+
<IconStyle>
417+
<scale>1.1</scale>
418+
<Icon>
419+
<href>${url}</href>
420+
</Icon>
421+
<hotSpot x="${x.toString()}" y="${y.toString()}" xunits="pixels" yunits="pixels" />
422+
</IconStyle>
423+
<ListStyle>
424+
</ListStyle>
425+
</Style>
426+
""";
427+
return sb;
428+
}
429+
}

lib/smash_import_export_plugins.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import 'package:smashlibs/com/hydrologis/flutterlibs/utils/logging.dart';
2525
import 'package:smashlibs/smashlibs.dart';
2626
import 'package:http/http.dart' hide MultipartFile, Response;
2727
import 'package:provider/provider.dart';
28+
import 'package:archive/archive.dart';
29+
import 'package:archive/archive_io.dart';
2830

2931
part 'com/hydrologis/smash/import_export_plugins/export.dart';
3032
part 'com/hydrologis/smash/import_export_plugins/export/geopackage/geopackage_export.dart';

pubspec.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ dependencies:
2121
# git:
2222
# url: https://github.com/moovida/dart_hydrologis_db.git
2323
smashlibs:
24-
# path: ../smashlibs/
25-
git:
26-
url: https://github.com/moovida/smashlibs.git
27-
ref: "9222198"
24+
path: ../smashlibs/
25+
# git:
26+
# url: https://github.com/moovida/smashlibs.git
27+
# ref: "9222198"
2828

2929

3030
# apache 2
@@ -48,6 +48,9 @@ dependencies:
4848
pdf: ^3.8.3
4949
# pdf: '>=2.1.0 <=3.6.0'
5050

51+
# MIT
52+
archive: ^3.3.6
53+
5154
# MIT
5255
provider: ^6.0.4
5356

0 commit comments

Comments
 (0)