4

I'm stuck on a conversion.

I have a KMZ file with some coordinates. I read the file like this:

m=Import["~/Desktop/locations.kmz","Data"]

I get something like this:

{{LayerName->Point Features,
  Geometry->{
    Point[{-120.934,49.3321,372}],
    Point[{-120.935,49.3275,375}],
    Point[{-120.935,49.323,371}]},
  Labels->{},LabeledData->{},ExtendedData->{},
  PlacemarkNames->{1,2,3},
  Overlays->{},NetworkLinks->{}
}}

I want to extract the {x,y,z} from each of the points and also the placemark names {1,2,3} associated with the points. Even if I can just get the points out of Geometry->{} that would be fine because I can extract them into a list with List@@@, but I'm lost at the fundamental part where I can't extract the Geometry "Rule".

Thanks for any help,

Ron

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
Ron
  • 133
  • 5
  • 1
    Ron, would you please update your answer to show actual data you get from `Import`? It should apparently contain strings. – Mr.Wizard Jun 02 '11 at 20:23

4 Answers4

5

While Leonid's answer is correct, you will likely find that it does not work with your code. The reason is that the output of your Import command contains strings, such as "LayerNames", rather than symbols, such as LayerNames. I've uploaded a KML file to my webspace so we can try this using an actual Import command. Try something like the following:

in = Import["http://facstaff.unca.edu/mcmcclur/my.kml", "Data"];
pointList = "Geometry" /.  
    Cases[in, Verbatim[Rule]["Geometry", _], Infinity];
pointList /. Point[stuff_] -> stuff

Again, note that "Geometry" is a string. In fact, the contents of in look like so (in InputForm):

{{"LayerName" -> "Waypoints", 
  "Geometry" -> {Point[{-82.5, 32.5, 0}]}, 
  "Labels" -> {}, "LabeledData" -> {}, 
  "ExtendedData" -> {}, "PlacemarkNames" -> {"asheville"}, 
  "Overlays" -> {}, "NetworkLinks" -> {}}}

Context: KML refers to Keyhole Markup Language. Keyhole was a company that developed tools that ultimately became Google Earth, after they were acquired by Google. KMZ is a zipped version of KML.

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
Mark McClure
  • 4,862
  • 21
  • 34
  • Holy cow, folks, thanks for jumping right on this. I really appreciate how responsive this group is, especially to my somewhat basic questions. – Ron Jun 02 '11 at 17:33
  • Mark, is my suggested simplification correct? My logic is that `"Geometry" /. ...` will return the first value found for a rule `"Geometry" -> ...` and therefore `Cases[..., 1]` should be equivalent, assuming that such a rule exists somewhere within `in`. – Mr.Wizard Jun 02 '11 at 20:21
  • @Mr.Wizard Your code works fine. I've added the correct version of an imported KML file so you can try it. – Mark McClure Jun 03 '11 at 02:14
4

A simplification to Leonid and Mark's answers that I believe can be made safely is to remove the fancy Verbatim construct. That is:

Leonid's first operation can be written:

Join @@ Cases[expr, (Geometry -> x_) :> (x /. Point -> Sequence), Infinity]

Leonid's second operation:

Join @@ Cases[expr, (PlacemarkNames -> x_) :> x, Infinity]

I had trouble importing Mark's data, but from what I can guess, one could write:

pointList = Cases[in, ("Geometry" -> x_) :> x, Infinity, 1]

I'll let the votes on this answer tell me if I am correct.

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • @Gandalf KML import is new in V8. In V7, you could import as XML, as in `Import["file.kml","XML"]`. Of course, you then need to process the result accordingly. – Mark McClure Jun 02 '11 at 19:06
  • @Mr.Wizard Did you see my alternative method at the bottom of my post? I think it is simple enough (although it doesn't matter since, as @Mark pointed out, it won't work. I based it on the OP's info, did not know these are returned as strings) – Leonid Shifrin Jun 02 '11 at 19:07
  • Thanks Lenoid (and everyone else), I used parts from yours (for the placemarks) and also Mark's. I'm trying the simplified version now to understand how it works. – Ron Jun 02 '11 at 20:13
  • @Leonid, yes I did. I just thought it was worth pointing out that (as far as I can tell) `Verbatim` is not required. – Mr.Wizard Jun 02 '11 at 20:15
  • @Mr.Wizard I see your point. You are right. If I would not use this syntax of `Cases`, but instead a briefer one (with just a pattern, like `"Geometry" -> x_`), then `Verbatim` would be needed. – Leonid Shifrin Jun 02 '11 at 20:28
  • @Leonid, I may not understand, but it seems not. Consider: `{"Frog" -> 1, "Cat" -> 2, "Gerbil" -> 3} /. ("Cat" -> x_) :> ("Fox" -> x)` – Mr.Wizard Jun 02 '11 at 20:33
  • @Mr.Wizard I meant something else - that we need it for cases like this: `Cases[{"Frog" -> 1, "Cat" -> 2, "Gerbil" -> 3}, (a_ :> b_)]` will fail, we need `Verbatim` here. – Leonid Shifrin Jun 02 '11 at 21:04
  • @Leonid, I see. So to find any `Rule` with two arguments with `Cases` while not doing transformations, you would use `Verbatim[Rule][_, _]`? I would use `HoldPattern[_ -> _]` or simply `x:(_ -> _)`. Is there a reason to prefer `Verbatim`? – Mr.Wizard Jun 02 '11 at 21:24
  • @Mr.Wizard IMO, `HoldPattern` is a conceptually wrong tool for the job, in this particular case. What we need here is an escape mechanism, to interpret `Rule` not as a part of `Cases` syntax, while `HoldPattern` is used to prevent the possible evaluation of the pattern. It works, but is arguably misused here. I first read this in the book of David Wagner, and over the years I came to totally agree with this and share this viewpoint. – Leonid Shifrin Jun 02 '11 at 21:29
  • @Leonid, I'll trust your judgement on that. What about `x:(_ -> _)`? – Mr.Wizard Jun 02 '11 at 21:38
  • @Mr.Wizard Yes, this looks like an alternative in this particular case. Although, it seems a little inelegant to give a name for the pattern if that name is never used. For someone not fully understanding the reasons for that, your intentions may look unclear, as if something was planned but forgotten. Using `Verbatim` makes things explicit, it is a declarative way to tell the reader of your code about your intent. As Abelson said, "Programs must be written for people to read, and only incidentally for machines to execute". There seems to be a fine borderline between terseness and hackery. – Leonid Shifrin Jun 03 '11 at 10:23
3

Given your expression

expr = {{LayerName -> Point Features, 
       Geometry -> {
         Point[{-120.934, 49.3321, 372}], 
         Point[{-120.935, 49.3275, 375}],
         Point[{-120.935, 49.323, 371}]},
     Labels -> {}, LabeledData -> {}, ExtendedData -> {}, 
     PlacemarkNames -> {1, 2, 3}, Overlays -> {}, NetworkLinks -> {}}}

This will extract the points:

In[121]:= 
   Flatten[Cases[expr, Verbatim[Rule][Geometry, x_] :> (x /. Point -> Sequence),
        Infinity], 1]

Out[121]= {{-120.934, 49.3321, 372}, {-120.935, 49.3275,375}, {-120.935, 49.323, 371}}

And this will extract the placemarks:

In[124]:= Flatten[Cases[expr, Verbatim[Rule][PlacemarkNames, x_] :> x, Infinity], 1]

Out[124]= {1, 2, 3}

Here is a more elegant method exploiting that we are looking for rules, that will extract both:

In[127]:= 
{Geometry, PlacemarkNames} /.Cases[expr, _Rule, Infinity] /. Point -> Sequence

Out[127]= 
{{{-120.934, 49.3321, 372}, {-120.935, 49.3275,375}, {-120.935, 49.323, 371}}, {1, 2, 3}}
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
0

How about Transpose[{"PlacemarkNames", "Geometry"} /. m[[1]]] ?

Joshua Martell
  • 7,074
  • 2
  • 30
  • 37