diff --git a/project/build.properties b/project/build.properties index 10fd9ee..22af262 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.5 +sbt.version=1.7.1 diff --git a/src/main/scala/com/phasmidsoftware/kmldoc/KML.scala b/src/main/scala/com/phasmidsoftware/kmldoc/KML.scala index bea0b34..5464fb3 100644 --- a/src/main/scala/com/phasmidsoftware/kmldoc/KML.scala +++ b/src/main/scala/com/phasmidsoftware/kmldoc/KML.scala @@ -34,7 +34,7 @@ case class KML_Binding(kml: KML, binding: NamespaceBinding) * @param StyleMaps a sequence of StyleMap elements. * @param Folders a sequence of Folder elements. */ -case class Document(name: Text, description: Text, Styles: Seq[Style], StyleMaps: Seq[StyleMap], Folders: Seq[Folder]) +case class Document(name: Text, maybeOpen: Option[Int], description: Text, Styles: Seq[Style], StyleMaps: Seq[StyleMap], Folders: Seq[Folder]) /** * Case class to represent a Scale which is represented in XML as, for example: 1.1 @@ -48,6 +48,7 @@ case class Icon(href: Text) case class HotSpot(_x: Int, _xunits: String, _y: Int, _yunits: String) case class IconStyle(scale: Scale, Icon: Icon, hotSpot: HotSpot) +//case class IconStyle(maybeScale: Option[Scale], Icon: Icon, hotSpot: HotSpot) case class LabelStyle(scale: Scale) @@ -117,6 +118,7 @@ object KmlExtractors extends Extractors { implicit val extractorCoordinates: Extractor[Coordinates] = (node: Node) => Success(Coordinates.parse(node.text)) implicit val extractorScale: Extractor[Scale] = extractor10(Scale) + // implicit val extractMaybeScale: Extractor[Option[Scale]] = extractorOption implicit val extractorIcon: Extractor[Icon] = extractor10(Icon) implicit val extractorColor: Extractor[Color] = extractor10(Color) implicit val extractorWidth: Extractor[Width] = extractor10(Width) @@ -145,7 +147,8 @@ object KmlExtractors extends Extractors { implicit val extractorMultiStyleMap: MultiExtractor[Seq[StyleMap]] = multiExtractor[StyleMap] implicit val extractorMultiStyle: MultiExtractor[Seq[Style]] = multiExtractor[Style] implicit val extractorMultiFolder: MultiExtractor[Seq[Folder]] = multiExtractor[Folder] - implicit val extractorDocument: Extractor[Document] = extractor23(Document) + implicit val extractMaybeOpen: Extractor[Option[Int]] = extractorOption + implicit val extractorDocument: Extractor[Document] = extractor33(Document) implicit val extractorMultiDocument: MultiExtractor[Seq[Document]] = multiExtractor[Document] implicit val extractorKml: Extractor[KML] = extractor01(KML) implicit val extractorMultiKml: MultiExtractor[Seq[KML]] = multiExtractor[KML] @@ -172,6 +175,7 @@ trait KmlRenderers extends Renderers { import Renderers._ implicit val rendererScale: Renderable[Scale] = renderer1(Scale) + // implicit val rendererOptionScale: Renderable[Option[Scale]] = optionRenderer implicit val rendererIcon: Renderable[Icon] = renderer1(Icon) implicit val rendererColor: Renderable[Color] = renderer1(Color) implicit val rendererWidth: Renderable[Width] = renderer1(Width) @@ -204,7 +208,8 @@ trait KmlRenderers extends Renderers { implicit val rendererFolders: Renderable[Seq[Folder]] = sequenceRenderer[Folder] implicit val rendererStyles: Renderable[Seq[Style]] = sequenceRenderer[Style] implicit val rendererStyleMaps: Renderable[Seq[StyleMap]] = sequenceRenderer[StyleMap] - implicit val rendererDocument: Renderable[Document] = renderer5(Document) + implicit val renderOptionOpen: Renderable[Option[Int]] = optionRenderer + implicit val rendererDocument: Renderable[Document] = renderer6(Document) implicit val rendererDocuments: Renderable[Seq[Document]] = sequenceRenderer[Document] implicit val rendererKml: Renderable[KML] = renderer1(KML) implicit val rendererKml_Binding: Renderable[KML_Binding] = (t: KML_Binding, format: Format, stateR: StateR) => diff --git a/src/main/scala/com/phasmidsoftware/render/Renderers.scala b/src/main/scala/com/phasmidsoftware/render/Renderers.scala index 5377cb3..04ef15a 100644 --- a/src/main/scala/com/phasmidsoftware/render/Renderers.scala +++ b/src/main/scala/com/phasmidsoftware/render/Renderers.scala @@ -115,6 +115,27 @@ trait Renderers { doNestedRender(format, stateR, wInner, wOuter, r.productElementName(4)) } + /** + * Method to create a renderer fpr a Product (e.g., case class) with five members. + * + * @param construct a function which takes a P0, P1, P2, P3, P4 and yields an R (this is usually the apply method of a case class). + * @tparam P0 the (Renderable) type of the first member of Product type R. + * @tparam P1 the (Renderable) type of the second member of Product type R. + * @tparam P2 the (Renderable) type of the third member of Product type R. + * @tparam P3 the (Renderable) type of the fourth member of Product type R. + * @tparam P4 the (Renderable) type of the fifth member of Product type R. + * @tparam R the (Renderable) type of Renderable to be returned (must be a Product). + * @return a function which takes an R, a Format, and a StateR as parameters and yields a Renderable[R]. + */ + def renderer6[P0: Renderable, P1: Renderable, P2: Renderable, P3: Renderable, P4: Renderable, P5: Renderable, R <: Product : ClassTag](construct: (P0, P1, P2, P3, P4, P5) => R): Renderable[R] = (r: R, format: Format, stateR: StateR) => { + val objectOuter = r.productElement(5).asInstanceOf[P5] + val constructorInner: (P0, P1, P2, P3, P4) => R = construct(_, _, _, _, _, objectOuter) + val objectInner = constructorInner(r.productElement(0).asInstanceOf[P0], r.productElement(1).asInstanceOf[P1], r.productElement(2).asInstanceOf[P2], r.productElement(3).asInstanceOf[P3], r.productElement(4).asInstanceOf[P4]) + val wInner = renderer5(constructorInner).render(objectInner, format.indent, stateR.recurse) + val wOuter = renderOuter(r, objectOuter, 4, format) + doNestedRender(format, stateR, wInner, wOuter, r.productElementName(4)) + } + /** * Method to yield a renderer of Option[R]. * diff --git a/src/main/scala/com/phasmidsoftware/xml/Extractors.scala b/src/main/scala/com/phasmidsoftware/xml/Extractors.scala index d4a8840..4a7c5f8 100644 --- a/src/main/scala/com/phasmidsoftware/xml/Extractors.scala +++ b/src/main/scala/com/phasmidsoftware/xml/Extractors.scala @@ -28,6 +28,19 @@ trait Extractors { case Failure(x) => Failure(x) // TESTME } + /** + * Method to yield an Extractor which can choose from alternate extractors. + * + * TESTME + * + * @tparam R result type. + * @tparam P0 first extractor type. + * @tparam P1 second extractor type. + * @return an Extractor[R]. + */ + def extractorAlt[R, P0 <: R : Extractor, P1 <: R : Extractor]: Extractor[R] = + (node: Node) => implicitly[Extractor[P0]].extract(node) orElse implicitly[Extractor[P1]].extract(node) + /** * Extractor which will convert an Xml Node into a sequence of P objects where there is evidence of Extractor[P]. * @@ -476,6 +489,130 @@ trait Extractors { } yield t case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) } + + /** + * Extractor which will convert an Xml Node into an instance of a case class with six members. + * + * @param construct a function (P0,P1,P2,P3,P4,P5) => T, usually the apply method of a case class. + * @tparam P0 the (Extractor) type of the first member of the Product type T. + * @tparam P1 the (Extractor) type of the second member of the Product type T. + * @tparam P2 the (Extractor) type of the third member of the Product type T. + * @tparam P3 the (Extractor) type of the fourth member of the Product type T. + * @tparam P4 the (Extractor) type of the fifth member of the Product type T. + * @tparam P5 the (Extractor) type of the sixth member of the Product type T. + * @tparam T the underlying type of the result, a Product with five members. + * @return an Extractor[T] whose method extract will convert a Node into a T. + */ + def extractor60[P0: Extractor, P1: Extractor, P2: Extractor, P3: Extractor, P4: Extractor, P5: Extractor, T <: Product : ClassTag](construct: (P0, P1, P2, P3, P4, P5) => T, fields: Seq[String] = Nil): Extractor[T] = + (node: Node) => + fieldNames(fields) match { + case member0 :: fs => + for { + p0 <- extractField[P0](member0)(node) + t <- extractor50[P1, P2, P3, P4, P5, T](construct(p0, _, _, _, _, _), fs).extract(node) + } yield t + case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) // TESTME + } + + /** + * Extractor which will convert an Xml Node into an instance of a case class with five members. + * + * TESTME + * + * @param construct a function (P0,P1,P2,P3,P4) => T, usually the apply method of a case class. + * @tparam PE0 the (Extractor) type of the first member of the Product type T. + * @tparam PE1 the (Extractor) type of the second member of the Product type T. + * @tparam PE2 the (Extractor) type of the third member of the Product type T. + * @tparam PE3 the (Extractor) type of the fourth member of the Product type T. + * @tparam PM0 the (MultiExtractor) type of the fourth member of the Product type T. + * @tparam PM1 the (MultiExtractor) type of the fifth member of the Product type T. + * @tparam T the underlying type of the result, a Product with five members. + * @return an Extractor[T] whose method extract will convert a Node into a T. + */ + def extractor42[PE0: Extractor, PE1: Extractor, PE2: Extractor, PE3: Extractor, PM0: MultiExtractor, PM1: MultiExtractor, T <: Product : ClassTag](construct: (PE0, PE1, PE2, PE3, PM0, PM1) => T, fields: Seq[String] = Nil): Extractor[T] = + (node: Node) => + fieldNames(fields) match { + case member0 :: fs => + for { + p0 <- extractField[PE0](member0)(node) + t <- extractor32[PE1, PE2, PE3, PM0, PM1, T](construct(p0, _, _, _, _, _), fs).extract(node) + } yield t + case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) + } + + /** + * Extractor which will convert an Xml Node into an instance of a case class with five members. + * + * @param construct a function (P0,P1,P2,P3,P4) => T, usually the apply method of a case class. + * @tparam PE0 the (Extractor) type of the first member of the Product type T. + * @tparam PE1 the (Extractor) type of the second member of the Product type T. + * @tparam PE2 the (MultiExtractor) type of the third member of the Product type T. + * @tparam PM0 the (MultiExtractor) type of the fourth member of the Product type T. + * @tparam PM1 the (MultiExtractor) type of the fifth member of the Product type T. + * @tparam T the underlying type of the result, a Product with five members. + * @return an Extractor[T] whose method extract will convert a Node into a T. + */ + def extractor33[PE0: Extractor, PE1: Extractor, PE2: Extractor, PM0: MultiExtractor, PM1: MultiExtractor, PM2: MultiExtractor, T <: Product : ClassTag](construct: (PE0, PE1, PE2, PM0, PM1, PM2) => T, fields: Seq[String] = Nil): Extractor[T] = + (node: Node) => + fieldNames(fields) match { + case member0 :: fs => + for { + p0 <- extractField[PE0](member0)(node) + t <- extractor23[PE1, PE2, PM0, PM1, PM2, T](construct(p0, _, _, _, _, _), fs).extract(node) + } yield t + case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) // TESTME + } + + /** + * Extractor which will convert an Xml Node into an instance of a case class with five members. + * + * TESTME + * + * @param construct a function (P0,P1,P2,P3,P4) => T, usually the apply method of a case class. + * @tparam PE0 the (Extractor) type of the first member of the Product type T. + * @tparam P1 the (MultiExtractor) type of the second member of the Product type T. + * @tparam P2 the (MultiExtractor) type of the third member of the Product type T. + * @tparam P3 the (MultiExtractor) type of the fourth member of the Product type T. + * @tparam P4 the (MultiExtractor) type of the fifth member of the Product type T. + * @tparam T the underlying type of the result, a Product with five members. + * @return an Extractor[T] whose method extract will convert a Node into a T. + */ + def extractor24[PE0: Extractor, PE1: Extractor, P1: MultiExtractor, P2: MultiExtractor, P3: MultiExtractor, P4: MultiExtractor, T <: Product : ClassTag](construct: (PE0, PE1, P1, P2, P3, P4) => T, fields: Seq[String] = Nil): Extractor[T] = + (node: Node) => + fieldNames(fields) match { + case member0 :: fs => + for { + p0 <- extractField[PE0](member0)(node) + t <- extractor14[PE1, P1, P2, P3, P4, T](construct(p0, _, _, _, _, _), fs).extract(node) + } yield t + case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) + } + + /** + * Extractor which will convert an Xml Node into an instance of a case class with five members. + * + * TESTME + * + * @param construct a function (P0,P1,P2,P3,P4) => T, usually the apply method of a case class. + * @tparam P0 the (MultiExtractor) type of the first member of the Product type T. + * @tparam P1 the (MultiExtractor) type of the second member of the Product type T. + * @tparam P2 the (MultiExtractor) type of the third member of the Product type T. + * @tparam P3 the (MultiExtractor) type of the fourth member of the Product type T. + * @tparam P4 the (MultiExtractor) type of the fifth member of the Product type T. + * @tparam T the underlying type of the result, a Product with five members. + * @return an Extractor[T] whose method extract will convert a Node into a T. + */ + def extractor06[P0: MultiExtractor, P1: MultiExtractor, P2: MultiExtractor, P3: MultiExtractor, P4: MultiExtractor, P5: MultiExtractor, T <: Product : ClassTag](construct: (P0, P1, P2, P3, P4, P5) => T, fields: Seq[String] = Nil): Extractor[T] = + (node: Node) => + fieldNames(fields) match { + case member0 :: fs => + for { + p0 <- extractChildren[P0](node, member0) + t <- extractor05[P1, P2, P3, P4, P5, T](construct(p0, _, _, _, _, _), fs).extract(node) + } yield t + case fs => Failure(XmlException(s"extractor5: insufficient field names: $fs")) + } + } /** @@ -683,6 +820,8 @@ object Extractors { * @tparam T the type to be constructed. */ trait Extractor[T] { + self => + /** * Method to convert a Node into a Try[T]. * @@ -690,6 +829,16 @@ trait Extractor[T] { * @return a Try[T]. */ def extract(node: Node): Try[T] + + /** + * Method to combine this Extractor[T] with alt as a disjunctive expression. + * + * TESTME + * + * @param alt an alternative Extractor which will be invoked if this Extractor fails. + * @return an Extractor based on this and alt. + */ + def |(alt: Extractor[T]): Extractor[T] = (node: Node) => self.extract(node) orElse alt.extract(node) } /** diff --git a/src/test/resources/KML_Samples.kml b/src/test/resources/KML_Samples.kml new file mode 100644 index 0000000..47ae59a --- /dev/null +++ b/src/test/resources/KML_Samples.kml @@ -0,0 +1,915 @@ + + + + KML Samples + 1 + Unleash your creativity with the help of these examples! + + + + + + + + + + + + + + Placemarks + These are just some of the different kinds of placemarks with + which you can mark your favorite places + + -122.0839597145766 + 37.42222904525232 + 0 + -148.4122922628044 + 40.5575073395506 + 500.6566641072245 + + + Simple placemark + Attached to the ground. Intelligently places itself at the + height of the underlying terrain. + + -122.0822035425683,37.42228990140251,0 + + + + Floating placemark + 0 + Floats a defined distance above the ground. + + -122.0839597145766 + 37.42222904525232 + 0 + -148.4122922628044 + 40.5575073395506 + 500.6566641072245 + + #downArrowIcon + + relativeToGround + -122.084075,37.4220033612141,50 + + + + Extruded placemark + 0 + Tethered to the ground by a customizable + "tail" + + -122.0845787421525 + 37.42215078737763 + 0 + -148.4126684946234 + 40.55750733918048 + 365.2646606980322 + + #globeIcon + + 1 + relativeToGround + -122.0857667006183,37.42156927867553,50 + + + + + Styles and Markup + 0 + With KML it is easy to create rich, descriptive markup to + annotate and enrich your placemarks + + -122.0845787422371 + 37.42215078726837 + 0 + -148.4126777488172 + 40.55750733930874 + 365.2646826292919 + + #noDrivingDirections + + Highlighted Icon + 0 + Place your mouse over the icon to see it display the new + icon + + -122.0856552124024 + 37.4224281311035 + 0 + 0 + 0 + 265.8520424250024 + + + + + + normal + #normalPlacemark + + + highlight + #highlightPlacemark + + + + Roll over this icon + 0 + #exampleStyleMap + + -122.0856545755255,37.42243077405461,0 + + + + + Descriptive HTML + 0 +
+Placemark descriptions can be enriched by using many standard HTML tags.
+For example: +
+Styles:
+Italics, +Bold, +Underlined, +Strike Out, +subscriptsubscript, +superscriptsuperscript, +Big, +Small, +Typewriter, +Emphasized, +Strong, +Code +
+Fonts:
+red by name, +leaf green by hexadecimal RGB +
+size 1, +size 2, +size 3, +size 4, +size 5, +size 6, +size 7 +
+Times, +Verdana, +Arial
+
+Links: +
+Google Earth! +
+ or: Check out our website at www.google.com +
+Alignment:
+

left

+

center

+

right

+
+Ordered Lists:
+
  1. First
  2. Second
  3. Third
+
  1. First
  2. Second
  3. Third
+
  1. First
  2. Second
  3. Third
+
+Unordered Lists:
+
  • A
  • B
  • C
+
  • A
  • B
  • C
+
  • A
  • B
  • C
+
+Definitions:
+
+
Google:
The best thing since sliced bread
+
+
+Centered:
+Time present and time past
+Are both perhaps present in time future,
+And time future contained in time past.
+If all time is eternally present
+All time is unredeemable.
+
+
+Block Quote: +
+
+We shall not cease from exploration
+And the end of all our exploring
+Will be to arrive where we started
+And know the place for the first time.
+-- T.S. Eliot +
+
+
+Headings:
+

Header 1

+

Header 2

+

Header 3

+

Header 4

+

Header 5

+
+Images:
+Remote image
+
+Scaled image
+
+
+Simple Tables:
+ + + +
12345
abcde
+
+[Did you notice that double-clicking on the placemark doesn't cause the viewer to take you anywhere? This is because it is possible to directly author a "placeless placemark". If you look at the code for this example, you will see that it has neither a point coordinate nor a LookAt element.]]]>
+
+
+ + Ground Overlays + 0 + Examples of ground overlays + + Large-scale overlay on terrain + 0 + Overlay shows Mount Etna erupting on July 13th, 2001. + + 15.02468937557116 + 37.67395167941667 + 0 + -16.5581842842829 + 58.31228652890705 + 30350.36838438907 + + + http://developers.google.com/kml/documentation/images/etna.jpg + + + 37.91904192681665 + 37.46543388598137 + 15.35832653742206 + 14.60128369746704 + -0.1556640799496235 + + + + + Screen Overlays + 0 + Screen overlays have to be authored directly in KML. These + examples illustrate absolute and dynamic positioning in screen space. + + Simple crosshairs + 0 + This screen overlay uses fractional positioning to put the + image in the exact center of the screen + + http://developers.google.com/kml/documentation/images/crosshairs.png + + + + + + + + Absolute Positioning: Top left + 0 + + http://developers.google.com/kml/documentation/images/top_left.jpg + + + + + + + + Absolute Positioning: Top right + 0 + + http://developers.google.com/kml/documentation/images/top_right.jpg + + + + + + + + Absolute Positioning: Bottom left + 0 + + http://developers.google.com/kml/documentation/images/bottom_left.jpg + + + + + + + + Absolute Positioning: Bottom right + 0 + + http://developers.google.com/kml/documentation/images/bottom_right.jpg + + + + + + + + Dynamic Positioning: Top of screen + 0 + + http://developers.google.com/kml/documentation/images/dynamic_screenoverlay.jpg + + + + + + + + Dynamic Positioning: Right of screen + 0 + + http://developers.google.com/kml/documentation/images/dynamic_right.jpg + + + + + + + + + Paths + 0 + Examples of paths. Note that the tessellate tag is by default + set to 0. If you want to create tessellated lines, they must be authored + (or edited) directly in KML. + + Tessellated + 0 + tag has a value of 1, the line will contour to the underlying terrain]]> + + -112.0822680013139 + 36.09825589333556 + 0 + 103.8120432044965 + 62.04855796276328 + 2889.145007690472 + + + 1 + -112.0814237830345,36.10677870477137,0 + -112.0870267752693,36.0905099328766,0 + + + + Untessellated + 0 + tag has a value of 0, the line follow a simple straight-line path from point to point]]> + + -112.0822680013139 + 36.09825589333556 + 0 + 103.8120432044965 + 62.04855796276328 + 2889.145007690472 + + + 0 + -112.080622229595,36.10673460007995,0 + -112.085242575315,36.09049598612422,0 + + + + Absolute + 0 + Transparent purple line + + -112.2719329043177 + 36.08890633450894 + 0 + -106.8161545998597 + 44.60763714063257 + 2569.386744398339 + + #transPurpleLineGreenPoly + + 1 + absolute + -112.265654928602,36.09447672602546,2357 + -112.2660384528238,36.09342608838671,2357 + -112.2668139013453,36.09251058776881,2357 + -112.2677826834445,36.09189827357996,2357 + -112.2688557510952,36.0913137941187,2357 + -112.2694810717219,36.0903677207521,2357 + -112.2695268555611,36.08932171487285,2357 + -112.2690144567276,36.08850916060472,2357 + -112.2681528815339,36.08753813597956,2357 + -112.2670588176031,36.08682685262568,2357 + -112.2657374587321,36.08646312301303,2357 + + + + Absolute Extruded + 0 + Transparent green wall with yellow outlines + + -112.2643334742529 + 36.08563154742419 + 0 + -125.7518698668815 + 44.61038665812578 + 4451.842204068102 + + #yellowLineGreenPoly + + 1 + 1 + absolute + -112.2550785337791,36.07954952145647,2357 + -112.2549277039738,36.08117083492122,2357 + -112.2552505069063,36.08260761307279,2357 + -112.2564540158376,36.08395660588506,2357 + -112.2580238976449,36.08511401044813,2357 + -112.2595218489022,36.08584355239394,2357 + -112.2608216347552,36.08612634548589,2357 + -112.262073428656,36.08626019085147,2357 + -112.2633204928495,36.08621519860091,2357 + -112.2644963846444,36.08627897945274,2357 + -112.2656969554589,36.08649599090644,2357 + + + + Relative + 0 + Black line (10 pixels wide), height tracks terrain + + -112.2580438551384 + 36.1072674824385 + 0 + 4.947421249553717 + 44.61324882043339 + 2927.61105910266 + + #thickBlackLine + + 1 + relativeToGround + -112.2532845153347,36.09886943729116,645 + -112.2540466121145,36.09919570465255,645 + -112.254734666947,36.09984998366178,645 + -112.255493345654,36.10051310621746,645 + -112.2563157098468,36.10108441943419,645 + -112.2568033076439,36.10159722088088,645 + -112.257494011321,36.10204323542867,645 + -112.2584106072308,36.10229131995655,645 + -112.2596588987972,36.10240001286358,645 + -112.2610581199487,36.10213176873407,645 + -112.2626285262793,36.10157011437219,645 + + + + Relative Extruded + 0 + Opaque blue walls with red outline, height tracks terrain + + -112.2683594333433 + 36.09884362144909 + 0 + -72.24271551768405 + 44.60855445139561 + 2184.193522571467 + + #redLineBluePoly + + 1 + 1 + relativeToGround + -112.2656634181359,36.09445214722695,630 + -112.2652238941097,36.09520916122063,630 + -112.2645079986395,36.09580763864907,630 + -112.2638827428817,36.09628572284063,630 + -112.2635746835406,36.09679275951239,630 + -112.2635711822407,36.09740038871899,630 + -112.2640296531825,36.09804913435539,630 + -112.264327720538,36.09880337400301,630 + -112.2642436562271,36.09963644790288,630 + -112.2639148687042,36.10055381117246,630 + -112.2626894973474,36.10149062823369,630 + + + + + Polygons + 0 + Examples of polygon shapes + + Google Campus + 0 + A collection showing how easy it is to create 3-dimensional + buildings + + -122.084120030116 + 37.42174011925477 + 0 + -34.82469740081282 + 53.454348562403 + 276.7870053764046 + + + Building 40 + 0 + #transRedPoly + + 1 + relativeToGround + + + -122.0848938459612,37.42257124044786,17 + -122.0849580979198,37.42211922626856,17 + -122.0847469573047,37.42207183952619,17 + -122.0845725380962,37.42209006729676,17 + -122.0845954886723,37.42215932700895,17 + -122.0838521118269,37.42227278564371,17 + -122.083792243335,37.42203539112084,17 + -122.0835076656616,37.42209006957106,17 + -122.0834709464152,37.42200987395161,17 + -122.0831221085748,37.4221046494946,17 + -122.0829247374572,37.42226503990386,17 + -122.0829339169385,37.42231242843094,17 + -122.0833837359737,37.42225046087618,17 + -122.0833607854248,37.42234159228745,17 + -122.0834204551642,37.42237075460644,17 + -122.083659133885,37.42251292011001,17 + -122.0839758438952,37.42265873093781,17 + -122.0842374743331,37.42265143972521,17 + -122.0845036949503,37.4226514386435,17 + -122.0848020460801,37.42261133916315,17 + -122.0847882750515,37.42256395055121,17 + -122.0848938459612,37.42257124044786,17 + + + + + + Building 41 + 0 + #transBluePoly + + 1 + relativeToGround + + + -122.0857412771483,37.42227033155257,17 + -122.0858169768481,37.42231408832346,17 + -122.085852582875,37.42230337469744,17 + -122.0858799945639,37.42225686138789,17 + -122.0858860101409,37.4222311076138,17 + -122.0858069157288,37.42220250173855,17 + -122.0858379542653,37.42214027058678,17 + -122.0856732640519,37.42208690214408,17 + -122.0856022926407,37.42214885429042,17 + -122.0855902778436,37.422128290487,17 + -122.0855841672237,37.42208171967246,17 + -122.0854852065741,37.42210455874995,17 + -122.0855067264352,37.42214267949824,17 + -122.0854430712915,37.42212783846172,17 + -122.0850990714904,37.42251282407603,17 + -122.0856769818632,37.42281815323651,17 + -122.0860162273783,37.42244918858722,17 + -122.0857260327004,37.42229239604253,17 + -122.0857412771483,37.42227033155257,17 + + + + + + Building 42 + 0 + #transGreenPoly + + 1 + relativeToGround + + + -122.0857862287242,37.42136208886969,25 + -122.0857312990603,37.42136935989481,25 + -122.0857312992918,37.42140934910903,25 + -122.0856077073679,37.42138390166565,25 + -122.0855802426516,37.42137299550869,25 + -122.0852186221971,37.42137299504316,25 + -122.0852277765639,37.42161656508265,25 + -122.0852598189347,37.42160565894403,25 + -122.0852598185499,37.42168200156,25 + -122.0852369311478,37.42170017860346,25 + -122.0852643957828,37.42176197982575,25 + -122.0853239032746,37.42176198013907,25 + -122.0853559454324,37.421852864452,25 + -122.0854108752463,37.42188921823734,25 + -122.0854795379357,37.42189285337048,25 + -122.0855436229819,37.42188921797546,25 + -122.0856260178042,37.42186013499926,25 + -122.085937287963,37.42186013453605,25 + -122.0859428718666,37.42160898590042,25 + -122.0859655469861,37.42157992759144,25 + -122.0858640462341,37.42147115002957,25 + -122.0858548911215,37.42140571326184,25 + -122.0858091162768,37.4214057134039,25 + -122.0857862287242,37.42136208886969,25 + + + + + + Building 43 + 0 + #transYellowPoly + + 1 + relativeToGround + + + -122.0844371128284,37.42177253003091,19 + -122.0845118855746,37.42191111542896,19 + -122.0850470999805,37.42178755121535,19 + -122.0850719913391,37.42143663023161,19 + -122.084916406232,37.42137237822116,19 + -122.0842193868167,37.42137237801626,19 + -122.08421938659,37.42147617161496,19 + -122.0838086419991,37.4214613409357,19 + -122.0837899728564,37.42131306410796,19 + -122.0832796534698,37.42129328840593,19 + -122.0832609819207,37.42139213944298,19 + -122.0829373621737,37.42137236399876,19 + -122.0829062425667,37.42151569778871,19 + -122.0828502269665,37.42176282576465,19 + -122.0829435788635,37.42176776969635,19 + -122.083217411188,37.42179248552686,19 + -122.0835970430103,37.4217480074456,19 + -122.0839455556771,37.42169364237603,19 + -122.0840077894637,37.42176283815853,19 + -122.084113587521,37.42174801104392,19 + -122.0840762473784,37.42171341292375,19 + -122.0841447047739,37.42167881534569,19 + -122.084144704223,37.42181720660197,19 + -122.0842503333074,37.4218170700446,19 + -122.0844371128284,37.42177253003091,19 + + + + + + + Extruded Polygon + A simple way to model a building + + The Pentagon + + -77.05580139178142 + 38.870832443487 + 59.88865561738225 + 48.09646074797388 + 742.0552506670548 + + + 1 + relativeToGround + + + -77.05788457660967,38.87253259892824,100 + -77.05465973756702,38.87291016281703,100 + -77.05315536854791,38.87053267794386,100 + -77.05552622493516,38.868757801256,100 + -77.05844056290393,38.86996206506943,100 + -77.05788457660967,38.87253259892824,100 + + + + + -77.05668055019126,38.87154239798456,100 + -77.05542625960818,38.87167890344077,100 + -77.05485125901024,38.87076535397792,100 + -77.05577677433152,38.87008686581446,100 + -77.05691162017543,38.87054446963351,100 + -77.05668055019126,38.87154239798456,100 + + + + + + + Absolute and Relative + 0 + Four structures whose roofs meet exactly. Turn on/off + terrain to see the difference between relative and absolute + positioning. + + -112.3348969157552 + 36.14845533214919 + 0 + -86.91235037566909 + 49.30695423894192 + 990.6761201087104 + + + Absolute + 0 + #transBluePoly + + 1 + absolute + + + -112.3372510731295,36.14888505105317,1784 + -112.3356128688403,36.14781540589019,1784 + -112.3368169371048,36.14658677734382,1784 + -112.3384408457543,36.14762778914076,1784 + -112.3372510731295,36.14888505105317,1784 + + + + + + Absolute Extruded + 0 + #transRedPoly + + 1 + 1 + absolute + + + -112.3396586818843,36.14637618647505,1784 + -112.3380597654315,36.14531751871353,1784 + -112.3368254237788,36.14659596244607,1784 + -112.3384555043203,36.14762621763982,1784 + -112.3396586818843,36.14637618647505,1784 + + + + + + Relative + 0 + + -112.3350152490417 + 36.14943123077423 + 0 + -118.9214100848499 + 37.92486261093203 + 345.5169113679813 + + #transGreenPoly + + 1 + relativeToGround + + + -112.3349463145932,36.14988705767721,100 + -112.3354019540677,36.14941108398372,100 + -112.3344428289146,36.14878490381308,100 + -112.3331289492913,36.14780840132443,100 + -112.3317019516947,36.14680755678357,100 + -112.331131440106,36.1474173426228,100 + -112.332616324338,36.14845453364654,100 + -112.3339876620524,36.14926570522069,100 + -112.3349463145932,36.14988705767721,100 + + + + + + Relative Extruded + 0 + + -112.3351587892382 + 36.14979247129029 + 0 + -55.42811560891606 + 56.10280503739589 + 401.0997279712519 + + #transYellowPoly + + 1 + 1 + relativeToGround + + + -112.3348783983763,36.1514008468736,100 + -112.3372535345629,36.14888517553886,100 + -112.3356068927954,36.14781612679284,100 + -112.3350034807972,36.14846469024177,100 + -112.3358353861232,36.1489624162954,100 + -112.3345888301373,36.15026229372507,100 + -112.3337937856278,36.14978096026463,100 + -112.3331798208424,36.1504472788618,100 + -112.3348783983763,36.1514008468736,100 + + + + + + +
+
diff --git a/src/test/scala/com/phasmidsoftware/kmldoc/KmlSpec.scala b/src/test/scala/com/phasmidsoftware/kmldoc/KmlSpec.scala index e67ce2d..b5e5a01 100644 --- a/src/test/scala/com/phasmidsoftware/kmldoc/KmlSpec.scala +++ b/src/test/scala/com/phasmidsoftware/kmldoc/KmlSpec.scala @@ -3520,7 +3520,7 @@ class KmlSpec extends AnyFlatSpec with should.Matchers { ks.size shouldBe 1 val kml: KML = ks.head val w = kml.toString - w.length shouldBe 80808 + w.length shouldBe 80813 case Failure(x) => fail(x) } } @@ -3533,7 +3533,7 @@ class KmlSpec extends AnyFlatSpec with should.Matchers { ks.size shouldBe 1 val kml: KML = ks.head val w = kml.toString - w.length shouldBe 4977 + w.length shouldBe 4982 case Failure(x) => fail(x) } } @@ -3584,5 +3584,28 @@ class KmlSpec extends AnyFlatSpec with should.Matchers { case Failure(x) => fail(x) } } + + ignore should "extract and render sample kml as XML from Google sample" in { + val renderer = new KmlRenderers {}.rendererKml_Binding + val url = KML.getClass.getResource("/KML_Samples.kml") + val xml: Elem = XML.loadFile(url.getFile) + extractorMultiKml.extract(xml) match { + case Success(ks) => + ks.size shouldBe 1 + val kml = KML_Binding(ks.head, xml.scope) + val w = renderer.render(kml, FormatXML(0), StateR().setName("kml")) + val filename = "xmlOutput.kml" + val fw = new FileWriter(filename) + fw.write( + """ + |""".stripMargin) + fw.write(w) + fw.close() + val copy: Elem = parseUnparsed(w) + val ksy: Try[scala.Seq[KML]] = extractorMultiKml.extract(copy) + ksy should matchPattern { case Success(_ :: Nil) => } + case Failure(x) => fail(x) + } + } }