diff --git a/lib/geoxml.dart b/lib/geoxml.dart index bc1410d..e69f7e8 100644 --- a/lib/geoxml.dart +++ b/lib/geoxml.dart @@ -7,6 +7,7 @@ export 'src/kml_writer.dart'; export 'src/model/bounds.dart'; export 'src/model/copyright.dart'; export 'src/model/email.dart'; +export 'src/model/geo_style.dart'; export 'src/model/geoxml.dart'; export 'src/model/link.dart'; export 'src/model/metadata.dart'; @@ -15,4 +16,3 @@ export 'src/model/rte.dart'; export 'src/model/trk.dart'; export 'src/model/trkseg.dart'; export 'src/model/wpt.dart'; -export 'src/model/polygon.dart'; diff --git a/lib/src/kml_reader.dart b/lib/src/kml_reader.dart index 6ab1030..816ac08 100644 --- a/lib/src/kml_reader.dart +++ b/lib/src/kml_reader.dart @@ -5,6 +5,7 @@ import 'package:xml/xml_events.dart'; import 'model/copyright.dart'; import 'model/email.dart'; import 'model/geo_object.dart'; +import 'model/geo_style.dart'; import 'model/geoxml.dart'; import 'model/gpx_tag.dart'; import 'model/kml_tag.dart'; @@ -35,7 +36,7 @@ class KmlReader { Future _fromIterator(StreamIterator iterator) async { // ignore: avoid_as - final gpx = GeoXml(); + final geoXml = GeoXml(); String? kmlName; String? desc; Person? author; @@ -62,39 +63,42 @@ class KmlReader { author = await _readPerson(iterator); break; case KmlTag.extendedData: - gpx.metadata = await _parseMetadata(iterator); + geoXml.metadata = await _parseMetadata(iterator); break; case KmlTag.placemark: - final item = await _readPlacemark(iterator, val.name); + final item = await _readPlacemark(iterator, val.name, geoXml); if (item is Wpt) { - gpx.wpts.add(item); + geoXml.wpts.add(item); } else if (item is Rte) { - gpx.rtes.add(item); + geoXml.rtes.add(item); } break; case KmlTag.folder: - gpx.trks.add(await _readFolder(iterator, val.name)); + geoXml.trks.add(await _readFolder(iterator, val.name, geoXml)); + break; + case KmlTag.style: + geoXml.styles.add(await _readStyle(iterator, val.name)); break; } } } if (kmlName != null) { - gpx.metadata ??= Metadata(); - gpx.metadata!.name = kmlName; + geoXml.metadata ??= Metadata(); + geoXml.metadata!.name = kmlName; } if (author != null) { - gpx.metadata ??= Metadata(); - gpx.metadata!.author = author; + geoXml.metadata ??= Metadata(); + geoXml.metadata!.author = author; } if (desc != null) { - gpx.metadata ??= Metadata(); - gpx.metadata!.desc = desc; + geoXml.metadata ??= Metadata(); + geoXml.metadata!.desc = desc; } - return gpx; + return geoXml; } Future _parseMetadata(StreamIterator iterator) async { @@ -133,9 +137,10 @@ class KmlReader { } Future _readPlacemark( - StreamIterator iterator, String tagName) async { + StreamIterator iterator, String tagName, GeoXml geoXml) async { final item = GeoObject(); final elm = iterator.current; + GeoStyle? style; DateTime? time; Wpt? ext; Wpt? wpt; @@ -183,6 +188,20 @@ class KmlReader { case KmlTag.gxTrack: rte = await _readGxTrack(iterator, val.name); break; + case KmlTag.style: + style = await _readStyle(iterator, val.name); + break; + case KmlTag.styleUrl: + var styleUrl = await _readString(iterator, val.name); + if (styleUrl != null && style == null) { + if (styleUrl.startsWith('#')) { + styleUrl = styleUrl.substring(1); + } + + style = geoXml.styles.firstWhere( + (element) => element.id == styleUrl); + } + break; } } @@ -215,6 +234,10 @@ class KmlReader { wpt.number = ext.number; } + if (style != null) { + wpt.style = style; + } + return wpt; } else if (rte is Rte) { rte.name = item.name; @@ -233,14 +256,22 @@ class KmlReader { rte.number = ext.number; } + if (style != null) { + rte.style = style; + } + return rte; } + if (style != null) { + item.style = style; + } + return item; } Future _readFolder( - StreamIterator iterator, String tagName) async { + StreamIterator iterator, String tagName, GeoXml geoXml) async { final trk = Trk(); final elm = iterator.current; Wpt? ext; @@ -270,7 +301,7 @@ class KmlReader { ext = await _readExtended(iterator); break; case KmlTag.placemark: - final item = await _readPlacemark(iterator, val.name); + final item = await _readPlacemark(iterator, val.name, geoXml); if (item is Wpt) { if (trk.trksegs.isEmpty) { trk.trksegs.add(Trkseg()); @@ -306,9 +337,9 @@ class KmlReader { } Future _readInt( - StreamIterator iterator, String tagName) async { + StreamIterator iterator, String tagName, [int radix=10]) async { final intString = await _readString(iterator, tagName); - return intString != null ? int.parse(intString) : null; + return intString != null ? int.parse(intString, radix: radix) : null; } Future _readDateTime( @@ -346,6 +377,15 @@ class KmlReader { return string.trim(); } + Future _readEnum( + StreamIterator iterator, String tagName, List values) async { + final name = await _readString(iterator, tagName); + if (name == null) { + return null; + } + return values.firstWhere((e) => e.name == name); + } + Future _readData( StreamIterator iterator, Future Function(StreamIterator iterator, String tagName) @@ -641,4 +681,242 @@ class KmlReader { return email; } + + Future _readStyle( + StreamIterator iterator, String tagName) async { + final style = GeoStyle(); + final elm = iterator.current; + + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + for (final attribute in elm.attributes) { + if (attribute.name == KmlTag.id) { + style.id = attribute.value; + } + } + + while (await iterator.moveNext()) { + final val = iterator.current; + + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.lineStyle: + style.lineStyle = await _readLineStyle(iterator, val.name); + break; + case KmlTag.polyStyle: + style.polyStyle = await _readPolyStyle(iterator, val.name); + break; + case KmlTag.iconStyle: + style.iconStyle = await _readIconStyle(iterator, val.name); + break; + case KmlTag.labelStyle: + style.labelStyle = await _readLabelStyle(iterator, val.name); + break; + case KmlTag.balloonStyle: + style.balloonStyle = await _readBalloonStyle(iterator, val.name); + break; + } + } + + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + + return style; + } + + Future _readLineStyle( + StreamIterator iterator, String tagName) async { + final lineStyle = LineStyle(); + final elm = iterator.current; + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.color: + lineStyle.color = await _readInt(iterator, val.name, 16); + break; + case KmlTag.colorMode: + lineStyle.colorMode = await _readEnum( + iterator, val.name, ColorMode.values); + break; + case KmlTag.width: + lineStyle.width = await _readDouble(iterator, val.name); + + } + } + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + return lineStyle; + } + + Future _readPolyStyle( + StreamIterator iterator, String tagName) async { + final polyStyle = PolyStyle(); + final elm = iterator.current; + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.color: + polyStyle.color = await _readInt(iterator, val.name, 16); + break; + case KmlTag.colorMode: + polyStyle.colorMode = await _readEnum( + iterator, val.name, ColorMode.values); + break; + case KmlTag.fill: + polyStyle.fill = await _readInt(iterator, val.name, 16); + break; + case KmlTag.outline: + polyStyle.outline = await _readInt(iterator, val.name, 16); + break; + + } + } + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + return polyStyle; + } + + Future _readIconStyle( + StreamIterator iterator, String tagName) async { + final iconStyle = IconStyle(); + final elm = iterator.current; + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.color: + iconStyle.color = await _readInt(iterator, val.name, 16); + break; + case KmlTag.colorMode: + iconStyle.colorMode = await _readEnum( + iterator, val.name, ColorMode.values); + break; + case KmlTag.scale: + iconStyle.scale = await _readDouble(iterator, val.name); + break; + case KmlTag.heading: + iconStyle.heading = await _readDouble(iterator, val.name); + break; + case KmlTag.icon: + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent && val.name == KmlTag.href) { + iconStyle.iconUrl = await _readString(iterator, val.name); + } + if (val is XmlEndElementEvent && val.name == KmlTag.icon) { + break; + } + } + break; + case KmlTag.hotSpot: + for (final attribute in val.attributes) { + switch (attribute.name) { + case KmlTag.hotSpotX: + iconStyle.x = double.tryParse(attribute.value); + break; + case KmlTag.hotSpotY: + iconStyle.y = double.tryParse(attribute.value); + break; + case KmlTag.xunits: + iconStyle.xunit = HotspotUnits.values.firstWhere( + (element) => element.name == attribute.value); + break; + case KmlTag.yunits: + iconStyle.yunit = HotspotUnits.values.firstWhere( + (element) => element.name == attribute.value); + break; + } + } + } + } + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + return iconStyle; + } + + Future _readLabelStyle( + StreamIterator iterator, String tagName) async { + final labelStyle = LabelStyle(); + final elm = iterator.current; + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.color: + labelStyle.color = await _readInt(iterator, val.name, 16); + break; + case KmlTag.colorMode: + labelStyle.colorMode = await _readEnum( + iterator, val.name, ColorMode.values); + break; + case KmlTag.scale: + labelStyle.scale = await _readDouble(iterator, val.name); + break; + + } + } + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + return labelStyle; + } + + Future _readBalloonStyle( + StreamIterator iterator, String tagName) async { + final balloonStyle = BalloonStyle(); + final elm = iterator.current; + if ((elm is XmlStartElementEvent) && !elm.isSelfClosing) { + while (await iterator.moveNext()) { + final val = iterator.current; + if (val is XmlStartElementEvent) { + switch (val.name) { + case KmlTag.color: + balloonStyle.color = await _readInt(iterator, val.name, 16); + break; + case KmlTag.colorMode: + balloonStyle.colorMode = await _readEnum( + iterator, val.name, ColorMode.values); + break; + case KmlTag.bgColor: + balloonStyle.bgColor = await _readInt(iterator, val.name, 16); + break; + case KmlTag.textColor: + balloonStyle.textColor = await _readInt(iterator, val.name, 16); + break; + case KmlTag.text: + balloonStyle.text = await _readString(iterator, val.name) ?? ''; + break; + case KmlTag.displayMode: + balloonStyle.show = + await _readString(iterator, val.name) == 'default'; + break; + + } + } + if (val is XmlEndElementEvent && val.name == tagName) { + break; + } + } + } + return balloonStyle; + } } diff --git a/lib/src/kml_writer.dart b/lib/src/kml_writer.dart index cb07c2a..3ec491c 100644 --- a/lib/src/kml_writer.dart +++ b/lib/src/kml_writer.dart @@ -6,7 +6,6 @@ import 'model/gpx_tag.dart'; import 'model/kml_tag.dart'; import 'model/link.dart'; import 'model/metadata.dart'; -import 'model/polygon.dart'; import 'model/rte.dart'; import 'model/trk.dart'; import 'model/wpt.dart'; @@ -58,9 +57,9 @@ class KmlWriter { _writePoint(builder, KmlTag.placemark, wpt); } - for (final polygon in gpx.polygons) { - _writePolygon(builder, polygon); - } + // for (final polygon in gpx.polygons) { + // _writePolygon(builder, polygon); + // } for (final rte in gpx.rtes) { _writeTrackRoute(builder, rte); @@ -190,48 +189,48 @@ class KmlWriter { }); } - void _writePolygon(XmlBuilder builder, Polygon polygon) { - builder.element(KmlTag.placemark, nest: () { - _writeElement(builder, KmlTag.name, polygon.name); - _writeElement(builder, KmlTag.desc, polygon.desc); - _writeAtomLinks(builder, polygon.links); - - // Style the polygon. - builder.element(KmlTag.style, nest: () { - builder.element(KmlTag.linestyle, nest: () { - _writeElement(builder, KmlTag.color, - polygon.outlineColor.toRadixString(16)); - _writeElement(builder, KmlTag.width, polygon.outlineWidth); - }); - builder.element(KmlTag.polystyle, nest: () { - _writeElement( - builder, KmlTag.color, polygon.fillColor.toRadixString(16)); - _writeElement(builder, KmlTag.outline, 0); - }); - }); - - builder.element(KmlTag.extendedData, nest: () { - _writeExtendedElement(builder, GpxTag.comment, polygon.cmt); - _writeExtendedElement(builder, GpxTag.type, polygon.type); - - _writeExtendedElement(builder, GpxTag.src, polygon.src); - _writeExtendedElement(builder, GpxTag.number, polygon.number); - }); - - builder.element(KmlTag.polygon, nest: () { - builder.element(KmlTag.outerBoundaryIs, nest: () { - builder.element(KmlTag.linearRing, nest: () { - _writeElement( - builder, - KmlTag.coordinates, - polygon.points - .map((wpt) => [wpt.lon, wpt.lat].join(',')) - .join('\n')); - }); - }); - }); - }); - } + // void _writePolygon(XmlBuilder builder, Polygon polygon) { + // builder.element(KmlTag.placemark, nest: () { + // _writeElement(builder, KmlTag.name, polygon.name); + // _writeElement(builder, KmlTag.desc, polygon.desc); + // _writeAtomLinks(builder, polygon.links); + // + // // Style the polygon. + // builder.element(KmlTag.style, nest: () { + // builder.element(KmlTag.lineStyle, nest: () { + // _writeElement(builder, KmlTag.color, + // polygon.outlineColor.toRadixString(16)); + // _writeElement(builder, KmlTag.width, polygon.outlineWidth); + // }); + // builder.element(KmlTag.polyStyle, nest: () { + // _writeElement( + // builder, KmlTag.color, polygon.fillColor.toRadixString(16)); + // _writeElement(builder, KmlTag.outline, 0); + // }); + // }); + // + // builder.element(KmlTag.extendedData, nest: () { + // _writeExtendedElement(builder, GpxTag.comment, polygon.cmt); + // _writeExtendedElement(builder, GpxTag.type, polygon.type); + // + // _writeExtendedElement(builder, GpxTag.src, polygon.src); + // _writeExtendedElement(builder, GpxTag.number, polygon.number); + // }); + // + // builder.element(KmlTag.polygon, nest: () { + // builder.element(KmlTag.outerBoundaryIs, nest: () { + // builder.element(KmlTag.linearRing, nest: () { + // _writeElement( + // builder, + // KmlTag.coordinates, + // polygon.points + // .map((wpt) => [wpt.lon, wpt.lat].join(',')) + // .join('\n')); + // }); + // }); + // }); + // }); + // } void _writePoint(XmlBuilder builder, String tagName, Wpt wpt) { builder.element(tagName, nest: () { diff --git a/lib/src/model/geo_object.dart b/lib/src/model/geo_object.dart index abf85bb..885efcb 100644 --- a/lib/src/model/geo_object.dart +++ b/lib/src/model/geo_object.dart @@ -1,3 +1,4 @@ +import 'geo_style.dart'; import 'link.dart'; class GeoObject { @@ -27,6 +28,9 @@ class GeoObject { /// here. Map extensions = {}; - // Element tag. + /// Element tag. late String tag; + + /// Kml style + GeoStyle? style; } diff --git a/lib/src/model/geo_style.dart b/lib/src/model/geo_style.dart new file mode 100644 index 0000000..1c6d22f --- /dev/null +++ b/lib/src/model/geo_style.dart @@ -0,0 +1,208 @@ +import 'package:quiver/core.dart'; + +class GeoStyle { + String? id; + LineStyle? lineStyle; + PolyStyle? polyStyle; + IconStyle? iconStyle; + LabelStyle? labelStyle; + BalloonStyle? balloonStyle; + // ListStyle? listStyle; + GeoStyle({ + this.id, + this.lineStyle, + this.polyStyle, + this.iconStyle, + this.labelStyle, + this.balloonStyle, + // this.listStyle, + }); +} + +enum ColorMode { + normal('normal'), + random('random'); + + const ColorMode(this.value); + + final String value; +} + +abstract class ColorStyle { + int? color; + ColorMode? colorMode; +} + +class LineStyle extends ColorStyle { + double? width; + + @override + // ignore: type_annotate_public_apis + bool operator ==(other) { + if (other is LineStyle) { + return other.color == super.color && + other.colorMode == super.colorMode && + other.width == width; + } + + return false; + } + + @override + String toString() => "LineStyle[${[width].join(",")}]"; + + @override + int get hashCode => hashObjects([ + width + ]); +} + +class PolyStyle extends ColorStyle { + int? fill; + int? outline; + + @override + // ignore: type_annotate_public_apis + bool operator ==(other) { + if (other is PolyStyle) { + return other.color == super.color && + other.colorMode == super.colorMode && + other.fill == fill && + other.outline == outline; + } + + return false; + } + + @override + String toString() => "PolyStyle[${[ + color, colorMode, fill, outline,].join(",")}]"; + + @override + int get hashCode => hashObjects([ + color, + colorMode, + fill, + outline, + ]); +} + +enum HotspotUnits { + /// Fraction + fraction('fraction'), + /// Pixels offset from left or bottom + pixels('pixels'), + /// Pixels offset from right or top + insetPixels('insetPixels'); + + const HotspotUnits(this.value); + + final String value; +} + +class IconStyle extends ColorStyle { + String? iconUrl; + double? scale; + double? heading; + num? x; + num? y; + HotspotUnits? xunit; + HotspotUnits? yunit; + + @override + // ignore: type_annotate_public_apis + bool operator ==(other) { + if (other is IconStyle) { + return other.color == super.color && + other.colorMode == super.colorMode && + other.iconUrl == iconUrl && + other.scale == scale && + other.heading == heading && + other.x == x && + other.y == y && + other.xunit == xunit && + other.yunit == yunit; + } + + return false; + } + + @override + String toString() => "IconStyle[${[ + iconUrl, scale, heading, x, y, xunit, yunit,].join(",")}]"; + + @override + int get hashCode => hashObjects([ + iconUrl, + scale, + heading, + x, + y, + xunit, + yunit, + ]); +} + +class LabelStyle extends ColorStyle { + double? scale; + + @override + // ignore: type_annotate_public_apis + bool operator ==(other) { + if (other is LabelStyle) { + return other.color == super.color && + other.colorMode == super.colorMode && + other.scale == scale; + } + + return false; + } + + @override + String toString() => "LabelStyle[${[scale].join(",")}]"; + + @override + int get hashCode => hashObjects([ + scale, + ]); +} + +class BalloonStyle extends ColorStyle { + int? bgColor; + int? textColor = 0xff000000; + String text = ''; + bool show = true; + + @override + // ignore: type_annotate_public_apis + bool operator ==(other) { + if (other is BalloonStyle) { + return other.color == super.color && + other.colorMode == super.colorMode && + other.bgColor == bgColor && + other.textColor == textColor && + other.text == text && + other.show == show; + } + + return false; + } + + @override + String toString() => "BalloonStyle[${[ + bgColor, textColor, text, show].join(",")}]"; + + @override + int get hashCode => hashObjects([ + bgColor, + textColor, + text, + show, + ]); +} + +class ListStyle { + ListStyle() { + throw UnimplementedError(); + } +} diff --git a/lib/src/model/geoxml.dart b/lib/src/model/geoxml.dart index d5de572..a86f373 100644 --- a/lib/src/model/geoxml.dart +++ b/lib/src/model/geoxml.dart @@ -5,8 +5,8 @@ import '../gpx_reader.dart'; import '../gpx_writer.dart'; import '../kml_reader.dart'; import '../kml_writer.dart'; +import 'geo_style.dart'; import 'metadata.dart'; -import 'polygon.dart'; import 'rte.dart'; import 'trk.dart'; import 'wpt.dart'; @@ -35,7 +35,7 @@ class GeoXml { /// A list of tracks. List trks = []; - List polygons = []; + List styles = []; /// You can add extend GPX by adding your own elements from another schema /// here. @@ -51,7 +51,7 @@ class GeoXml { const ListEquality().equals(other.wpts, wpts) && const ListEquality().equals(other.rtes, rtes) && const ListEquality().equals(other.trks, trks) && - const ListEquality().equals(other.polygons, polygons) && + const ListEquality().equals(other.styles, styles) && const MapEquality().equals(other.extensions, extensions); } @@ -66,7 +66,7 @@ class GeoXml { wpts, rtes, trks, - polygons, + styles, extensions ].join(",")}]"; @@ -80,7 +80,7 @@ class GeoXml { ...trks, ...rtes, ...wpts, - ...polygons + ...styles ]); String toGpxString({bool pretty = false}) => diff --git a/lib/src/model/kml_tag.dart b/lib/src/model/kml_tag.dart index 4837254..e9dc692 100644 --- a/lib/src/model/kml_tag.dart +++ b/lib/src/model/kml_tag.dart @@ -19,9 +19,31 @@ class KmlTag { static const linearRing = 'LinearRing'; static const style = 'Style'; - static const polystyle = 'PolyStyle'; - static const linestyle = 'LineStyle'; + static const styleUrl = 'styleUrl'; + static const id = 'id'; + static const styleMap = 'StyleMap'; + static const polyStyle = 'PolyStyle'; + static const lineStyle = 'LineStyle'; + static const iconStyle = 'IconStyle'; + static const labelStyle = 'LabelStyle'; + static const balloonStyle = 'BalloonStyle'; static const color = 'color'; + static const colorMode = 'colorMode'; + static const bgColor = 'bgColor'; + static const textColor = 'textColor'; + static const text = 'text'; + static const displayMode = 'displayMode'; + static const scale = 'scale'; + static const heading = 'heading'; + static const icon = 'Icon'; + static const href = 'href'; + static const hotSpot = 'hotSpot'; + static const hotSpotX = 'x'; + static const hotSpotY = 'y'; + static const xunits = 'xunits'; + static const yunits = 'yunits'; + static const fill = 'fill'; + static const width = 'width'; static const outline = 'outline'; diff --git a/lib/src/model/polygon.dart b/lib/src/model/polygon.dart deleted file mode 100644 index f598efd..0000000 --- a/lib/src/model/polygon.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:geoxml/src/model/kml_tag.dart'; -import 'package:quiver/core.dart'; - -import '../tools/color_converter.dart'; -import 'geo_object.dart'; -import 'link.dart'; -import 'trk.dart'; -import 'wpt.dart'; - -/// Polygon represents a simple polygon - -/// an ordered list of points describing a shape. -class Polygon implements GeoObject { - /// Name of Polygon. - @override - String? name; - - /// GPS comment for track. - @override - String? cmt; - - /// User description of track. - @override - String? desc; - - /// Source of data. Included to give user some idea of reliability and - /// accuracy of data. - @override - String? src; - - /// Links to external information about the track. - @override - List links; - - /// GPS track number. - @override - int? number; - - /// Type (classification) of track. - @override - String? type; - - /// You can add extend GPX by adding your own elements from another schema - /// here. - @override - Map extensions; - - // Element tag. - @override - String tag = KmlTag.polygon; - - /// A Track Segment holds a list of Track Points which are logically connected - /// in order. To represent a single GPS track where GPS reception was lost, or - /// the GPS receiver was turned off, start a new Track Segment for each - /// continuous span of track data. - List points; - - int fillColor; - int outlineColor; - - int outlineWidth; - - /// Construct a new [Trk] object. - Polygon( - {this.name, - this.cmt, - this.desc, - this.src, - List? links, - this.number, - this.type, - Map? extensions, - List? points, - fillColor, - outlineColor, - this.outlineWidth = 1}) - : links = links ?? [], - extensions = extensions ?? {}, - points = points ?? [], - fillColor = _getIntColor(fillColor), - outlineColor = _getIntColor(outlineColor); - - static int _getIntColor(color) { - if (color == null) { - return 0x000000; - } else { - switch (color.runtimeType) { - case num: - return (color as num).toInt(); - case String: - return hexStringToInt(expandColorWithAlpha(color as String)); - default: - return 0x00000000; - } - } - } - - @override - // ignore: type_annotate_public_apis - bool operator ==(other) { - if (other is Polygon) { - return other.name == name && - other.cmt == cmt && - other.desc == desc && - other.src == src && - const ListEquality().equals(other.links, links) && - other.number == number && - other.type == type && - const MapEquality().equals(other.extensions, extensions) && - const ListEquality().equals(other.points, points); - } - - return false; - } - - @override - String toString() => "Polygon[${[name, type, extensions, points].join(",")}]"; - - @override - int get hashCode => hashObjects([ - name, - cmt, - desc, - src, - number, - type, - ...links, - ...extensions.keys, - ...extensions.values, - ...points - ]); -} diff --git a/lib/src/model/rte.dart b/lib/src/model/rte.dart index fedbce6..004bc6f 100644 --- a/lib/src/model/rte.dart +++ b/lib/src/model/rte.dart @@ -1,4 +1,5 @@ import 'package:collection/collection.dart'; +import 'package:geoxml/src/model/geo_style.dart'; import 'package:quiver/core.dart'; import 'geo_object.dart'; @@ -50,6 +51,9 @@ class Rte implements GeoObject { /// A list of route points. List rtepts; + @override + GeoStyle? style; + /// Construct a new [Rte] object. Rte( {this.name, diff --git a/lib/src/model/trk.dart b/lib/src/model/trk.dart index 72f391d..7fd77bb 100644 --- a/lib/src/model/trk.dart +++ b/lib/src/model/trk.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:quiver/core.dart'; import 'geo_object.dart'; +import 'geo_style.dart'; import 'gpx_tag.dart'; import 'link.dart'; import 'trkseg.dart'; @@ -52,6 +53,9 @@ class Trk implements GeoObject { /// continuous span of track data. List trksegs; + @override + GeoStyle? style; + /// Construct a new [Trk] object. Trk( {this.name, diff --git a/lib/src/model/wpt.dart b/lib/src/model/wpt.dart index 611e606..992f92b 100644 --- a/lib/src/model/wpt.dart +++ b/lib/src/model/wpt.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:quiver/core.dart'; import 'geo_object.dart'; +import 'geo_style.dart'; import 'gpx_tag.dart'; import 'link.dart'; @@ -85,6 +86,9 @@ class Wpt implements GeoObject { /// ID of DGPS station used in differential correction. int? dgpsid; + @override + GeoStyle? style; + /// You can add extend GPX by adding your own elements from another schema /// here. @override diff --git a/test/assets/style_test.kml b/test/assets/style_test.kml new file mode 100644 index 0000000..775c2de --- /dev/null +++ b/test/assets/style_test.kml @@ -0,0 +1,75 @@ + + + + Ma an shan + + + + IconStyle + #randomColorIcon + + -122.36868,37.831145,0 + + + + Ma sn shan range + + + 1 + + + 114.45731521,30.51243496,0 114.45449889,30.51281854,0 114.45372105,30.51288324,0 114.45314169,30.51292946,0 114.45195615,30.51302651,0 114.45081890,30.51311432,0 114.45031464,30.51313743,0 114.44967628,30.51313743,0 114.44889307,30.51313743,0 114.44858193,30.51313743,0 114.44766998,30.51309121,0 114.44654882,30.51289249,0 114.44545984,30.51273073,0 114.44430113,30.51257822,0 114.44419384,30.51257360,0 114.44357693,30.51243958,0 114.44304049,30.51232866,0 114.44271863,30.51227782,0 114.44194615,30.51215766,0 114.44088399,30.51186650,0 114.43516552,30.50892714,0 114.43428576,30.50846959,0 114.43385124,30.50828010,0 114.43357766,30.50816455,0 114.43296611,30.50798430,0 114.43251550,30.50790111,0 114.43193614,30.50782254,0 114.43108320,30.50779019,0 114.43031609,30.50783641,0 114.42990303,30.50788725,0 114.42889452,30.51031826,0 114.42871749,30.51087286,0 114.42865312,30.51117326,0 114.42873895,30.51139510,0 114.42888916,30.51160307,0 114.42908764,30.51186650,0 114.42928076,30.51206061,0 114.42948460,30.51224085,0 114.42957580,30.51248117,0 114.42962408,30.51273536,0 114.42964554,30.51298954,0 114.42979574,30.51333615,0 114.42999423,30.51359496,0 114.43019271,30.51376595,0 114.43052530,30.51396468,0 114.43080962,30.51409408,0 114.43102956,30.51416802,0 114.43130314,30.51423272,0 114.43154454,30.51427894,0 114.43197369,30.51495829,0 114.43234921,30.51555445,0 114.43245649,30.51583636,0 114.43245649,30.51607667,0 114.43237066,30.51633546,0 114.43237066,30.51664972,0 114.43252087,30.51723201,0 114.43266034,30.51770800,0 114.43281054,30.51865074,0 114.43284273,30.51942249,0 114.43275690,30.51969976,0 114.43269253,30.52001400,0 114.43267107,30.52038369,0 114.43087399,30.52059627,0 114.43116367,30.52136338,0 114.43188250,30.52142345,0 114.43198442,30.52163140,0 114.43173766,30.52183473,0 114.43111002,30.52188556,0 114.42775190,30.52268039,0 114.42767680,30.52288371,0 114.42791820,30.52331347,0 114.42874968,30.52426078,0 114.42857265,30.52460273,0 114.42826688,30.52583652,0 114.42818642,30.52621081,0 114.42784309,30.52785584,0 114.42676485,30.52903876,0 114.42677021,30.52942228,0 114.42688286,30.52952394,0 114.42807376,30.52956090,0 114.42769825,30.52993056,0 114.42608893,30.53219007,0 114.42610502,30.53232406,0 114.42646980,30.53251351,0 114.42688286,30.53262440,0 114.42744076,30.53267061,0 114.42806840,30.53282309,0 114.42846000,30.53348845,0 114.42930222,30.53485150,0 114.42945778,30.53564160,0 114.42968845,30.53623301,0 114.42987084,30.53633928,0 114.43047702,30.53646865,0 114.43077743,30.53657954,0 114.43123877,30.53659341,0 114.43158746,30.53654720,0 114.43186104,30.53647328,0 + + + + + + route 1 + + + 114.43121195,30.50847883,0 114.43121195,30.50849732,0 114.43123341,30.50849732,0 114.43125486,30.50851580,0 114.43127632,30.50853429,0 114.43129778,30.50853429,0 114.43129778,30.50855278,0 114.43131924,30.50857126,0 114.43134069,30.50858975,0 114.43136215,30.50858975,0 114.43136215,30.50860824,0 114.43138361,30.50862673,0 114.43138361,30.50864521,0 114.43140507,30.50864521,0 114.43140507,30.50866370,0 114.43140507,30.50868219,0 114.43140507,30.50870067,0 114.43140507,30.50871916,0 114.43140507,30.50873765,0 114.43140507,30.50875613,0 114.43142653,30.50877462,0 114.43142653,30.50879311,0 114.43142653,30.50881159,0 114.43144798,30.50881159,0 114.43144798,30.50883008,0 114.43144798,30.50884857,0 114.43144798,30.50886706,0 114.43146944,30.50888554,0 114.43146944,30.50890403,0 114.43149090,30.50890403,0 114.43151236,30.50894100,0 114.43151236,30.50895949,0 114.43151236,30.50897798,0 114.43153381,30.50899646,0 114.43153381,30.50901495,0 114.43153381,30.50903344,0 114.43155527,30.50905192,0 114.43155527,30.50907041,0 114.43155527,30.50908890,0 114.43155527,30.50910738,0 114.43155527,30.50912587,0 114.43155527,30.50914436,0 114.43157673,30.50914436,0 114.43157673,30.50916284,0 114.43157673,30.50918133,0 114.43157673,30.50919982,0 114.43159819,30.50921830,0 114.43159819,30.50923679,0 114.43161964,30.50925528,0 114.43161964,30.50927377,0 114.43161964,30.50931074,0 114.43164110,30.50934771,0 114.43166256,30.50938469,0 114.43168402,30.50940317,0 114.43170547,30.50945863,0 114.43172693,30.50949561,0 114.43174839,30.50953258,0 114.43176985,30.50956955,0 114.43176985,30.50958804,0 114.43179131,30.50960653,0 114.43179131,30.50962501,0 114.43181276,30.50964350,0 114.43181276,30.50966199,0 114.43181276,30.50968047,0 114.43181276,30.50969896,0 114.43183422,30.50971745,0 114.43183422,30.50973593,0 114.43185568,30.50973593,0 114.43185568,30.50977291,0 114.43185568,30.50979139,0 114.43187714,30.50980988,0 114.43187714,30.50982837,0 114.43189859,30.50984685,0 114.43189859,30.50986534,0 114.43189859,30.50988383,0 114.43192005,30.50990231,0 114.43192005,30.50992080,0 114.43192005,30.50995777,0 114.43192005,30.50999475,0 114.43192005,30.51001323,0 114.43194151,30.51001323,0 114.43194151,30.51005021,0 114.43194151,30.51006869,0 114.43194151,30.51010567,0 114.43196297,30.51010567,0 114.43196297,30.51014264,0 114.43198442,30.51016113,0 114.43198442,30.51017961,0 114.43198442,30.51019810,0 114.43198442,30.51021659,0 114.43198442,30.51025356,0 114.43200588,30.51027205,0 114.43202734,30.51029053,0 114.43202734,30.51030902,0 114.43204880,30.51030902,0 114.43204880,30.51032751,0 114.43207026,30.51034599,0 114.43209171,30.51036448,0 114.43211317,30.51036448,0 114.43213463,30.51038297,0 114.43215609,30.51040145,0 114.43217754,30.51040145,0 114.43222046,30.51041994,0 114.43226337,30.51045691,0 114.43234921,30.51047540,0 114.43243504,30.51051237,0 114.43249941,30.51053086,0 114.43252087,30.51054935,0 114.43254232,30.51054935,0 114.43258524,30.51056783,0 114.43262815,30.51056783,0 114.43264961,30.51056783,0 114.43269253,30.51056783,0 114.43273544,30.51056783,0 114.43273544,30.51058632,0 114.43277836,30.51058632,0 114.43279982,30.51058632,0 114.43284273,30.51060481,0 114.43288565,30.51060481,0 114.43290710,30.51060481,0 114.43295002,30.51062329,0 114.43301439,30.51062329,0 114.43307877,30.51062329,0 114.43314314,30.51062329,0 114.43320751,30.51062329,0 + + + + Balloon + #exampleBalloonStyle + + -122.370533,37.823842,0 + + + + diff --git a/test/tests/kml_reader_stream_test.dart b/test/tests/kml_reader_stream_test.dart index 8374ba3..fe517a5 100644 --- a/test/tests/kml_reader_stream_test.dart +++ b/test/tests/kml_reader_stream_test.dart @@ -54,6 +54,20 @@ void main() { expect(kml, src); }); + test('read style kml', () async { + final kml = await KmlReader().fromStream( + File('test/assets/style_test.kml').openRead().transform(utf8.decoder)); + + expect(kml.styles.length, 2); + expect(kml.styles.first.id, equals('randomColorIcon')); + expect(kml.styles.first.iconStyle?.x, 0.5); + expect(kml.rtes.length, 2); + expect(kml.rtes.last.style?.lineStyle?.color, 0xffffff00); + expect(kml.rtes.first.style?.polyStyle?.color, 0xfe00ffff); + expect(kml.wpts.length, 2); + expect(kml.wpts.first.style, equals(kml.styles.first)); + }); + test('read large', () async { final kml = await KmlReader().fromStream( File('test/assets/large.kml').openRead().transform(utf8.decoder));