为了账号安全,请及时绑定邮箱和手机立即绑定

使用戈朗更新 KML 文件中的节点

使用戈朗更新 KML 文件中的节点

Go
萧十郎 2022-09-12 15:47:17
我正在使用一个KML文件,我用它来在谷歌地球内绘制线串。我正在从 USB 适配器接收 GPS 数据,并将坐标馈送到 Go 频道。我正在尝试读取通道并更新KML文件中的节点以添加到线弦中(从而绘制我的移动)。以下是 KML 结构:<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"><Folder>    <name>Foo</name>    <open>1</open>    <Document>        <name>Bar</name>        <Placemark>            <Style>                <LineStyle>                    <color>ff00ff00</color>                    <width>5</width>                </LineStyle>                <PolyStyle>                    <color>ff4080ff</color>                    <fill>1</fill>                    <outline>1</outline>                </PolyStyle>            </Style>            <LineString>                <tessellate>1</tessellate>                <coordinates>                    1.23411166666667,10.12345678901234,0                     1.23421166666667,10.12345678901234,0                     1.23431166666667,10.12345678901234,0                     1.23431166666667,10.32345678901234,0                 </coordinates>            </LineString>        </Placemark>    </Document></Folder></kml>我希望追加到坐标节点。我在想两种方法之一。首先解析文件并使用正则表达式查找并插入数据。其次,解析 XML 并查找更新节点中的值。后者似乎是更明智的选择,但是我在Google上搜索的所有内容都向我展示了如何将新节点添加到XML树中,而不是附加到现有条目中。</coordinates>到目前为止,我所尝试的感觉就像一团糟,每次从频道读取时都低效地打开文件,最终不起作用。type LineString struct {    coordinates string `xml:"coordinates"`}    func plotLocation(c chan data.GpsPos) {    /*        continuously read from the channel        use the location data to plot a breadcrumb trail    */    defer wg.Done()    for currentCoords := range c {        file, err := os.Open("/Users/me/foo.kml")        if err != nil {            log.Fatal(err)        }我是否做了一些明显错误的事情,是否有更好的方法将这些数据写入文件(每秒钟左右发生一次)?
查看完整描述

1 回答

?
慕沐林林

TA贡献2016条经验 获得超9个赞

文件是否在应用程序之外发生更改?如果没有,那么您可以在循环之前解析一次文件,维护坐标列表,并在每次更改时将其写出,以便外部应用程序可以看到中间结果。如果您计划执行更多转换,或者想要在开始时从头开始生成整个文件,这也将非常有用。


首先,您需要一个具有适当标记的结构(请参见 xml.Unmarshal)。我通常从在线生成器开始进行此类操作:


// type definition adapted from https://www.onlinetool.io/xmltogo/


type KML struct {

    XMLName xml.Name `xml:"kml"`

    Text    string   `xml:",chardata"`

    XMLNS   string   `xml:"xmlns,attr"`

    GX      string   `xml:"gx,attr"`

    KML     string   `xml:"kml,attr"`

    Atom    string   `xml:"atom,attr"`

    Folder  struct {

        Text     string `xml:",chardata"`

        Name     string `xml:"name"`

        Open     string `xml:"open"`

        Document struct {

            Text      string `xml:",chardata"`

            Name      string `xml:"name"`

            Placemark struct {

                Text  string `xml:",chardata"`

                Style struct {

                    Text      string `xml:",chardata"`

                    LineStyle struct {

                        Text  string `xml:",chardata"`

                        Color string `xml:"color"`

                        Width string `xml:"width"`

                    } `xml:"LineStyle"`

                    PolyStyle struct {

                        Text    string `xml:",chardata"`

                        Color   string `xml:"color"`

                        Fill    string `xml:"fill"`

                        Outline string `xml:"outline"`

                    } `xml:"PolyStyle"`

                } `xml:"Style"`

                LineString struct {

                    Text        string `xml:",chardata"`

                    Tessellate  string `xml:"tessellate"`

                    Coordinates string `xml:"coordinates"`

                } `xml:"LineString"`

            } `xml:"Placemark"`

        } `xml:"Document"`

    } `xml:"Folder"`

我会为此做一些帮助:


func readKML(filename string) (*KML, error) {

  f, err := os.Open(filename)

  if err != nil {

    return nil, fmt.Errorf("opening KML file: %w", err) // contains filename

  }

  defer f.Close() // reading, ignoring error is acceptable

  var kml KML

  if err := xml.NewDecoder(f).Decode(&kml); err != nil {

    return nil, fmt.Errorf("decoding XML from %q as KML: %w", filename, err)

  }

  return &kml, nil

}


func writeKML(filename string, kml *KML) error {

  f, err := os.Create(filename)

  if err != nil {

    return fmt.Errorf("creating KML file: %w", err) // contains filename

  }

  defer f.Close() // double close is OK for *os.File

  enc := xml.NewEncoder(f)

  enc.Indent("", "    ")

  if err := enc.Encode(kml); err != nil {

    return nil, fmt.Errorf("encoding KML to %q: %w", filename, err)

  }

  return nil

}

然后你的循环可以看起来像这样:


kml, err := readKML(filename)

if err != nil {

  return err // contains context

}


coordinates := strings.Fields(kml.Folder.Document.Placemark.LineString.Coordinates)


for coord := range incoming {

  line := fmt.Sprintf("%f,%f,%d\n", coord.Lat, coord.Long, 0)

  coordinates = append(coordinates, coord)

  

  kml.Folder.Document.Placemark.LineString.Coordinates = strings.Join(coordinates, "\n")

  if err := writeKML(filename, kml); err != nil {

    log.Printf("Warning: failed to update %q: %s", filename, err)

  }

}

在查看您拥有的代码时,我怀疑问题在于您正在延迟文件关闭,这将在函数返回时执行,而不是在循环继续时执行。您也可以使此方法正常工作,为此,我建议将逻辑分解为函数,以便可以独立测试每个部分,这也可能意味着您的延迟现在已在函数中正确限定。


查看完整回答
反对 回复 2022-09-12
  • 1 回答
  • 0 关注
  • 102 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信