【问题标题】:Extracting data from CSV file (fusion table and kml workaround)从 CSV 文件中提取数据(融合表和 kml 解决方法)
【发布时间】:2016-08-11 05:42:39
【问题描述】:

在使用 C# 的 Android 版 Xamarin 谷歌地图中,​​您可以基于此 tutorial 创建多边形:

    public void OnMapReady(GoogleMap googleMap)
    {
        mMap = googleMap;
        PolylineOptions geometry = new PolylineOptions()
        .Add(new LatLng(37.35, -37.0123))
        .Add(new LatLng(37.35, -37.0123))
        .Add(new LatLng(37.35, -37.0123));


        Polyline polyline = mMap.AddPolyline(geometry);

    }

但是,我从谷歌地图的 Fusion Table Layer 下载了一个 CSV 文件,因为我认为这可能是处理多边形/折线数据的最简单的选择。输出如下所示:

    description,name,label,geometry
    ,Highland,61,"<Polygon><outerBoundaryIs><LinearRing><coordinates>-5.657018,57.3352 -5.656396,57.334463 -5.655076,57.334556 -5.653439,57.334477 -5.652366,57.334724 -5.650064,57.334477 -5.648096,57.335082 -5.646846,57.335388 -5.644733,57.335539 -5.643309,57.335428 -5.641981,57.335448 -5.640451,57.33578 -5.633217,57.339118 -5.627278,57.338921 -5.617161,57.337649 -5.607948,57.341015 -5.595812,57.343583 -5.586043,57.345373 -5.583581,57.350648 -5.576851,57.353609 -5.570088,57.354017 -5.560732,57.354102 -5.555254,57.354033 -5.549713,57.353146 -5.547766,57.352275 -5.538932,57.352255 -5.525891,57.356217 -5.514888,57.361865 -5.504272,57.366027 -5.494515,57.374515 -5.469829,57.383765 -5.458661,57.389781 -5.453695,57.395033 -5.454057,57.402943 -5.449189,57.40731 -5.440583,57.411447 -5.436133,57.414616 -5.438312,57.415474 -5.438628,57.417955 -5.440956,57.417909 -5.444013,57.414976 -5.450778,57.421362 -5.455035,57.422333 -5.462081,57.420719 -5.468775,57.416975 -5.475205,57.41135 -5.475976,57.409117 -5.47705,57.407092 -5.478101,57.406056 -5.478901,57.40536 -5.479489,57.404534 -5.480051,57.403782 -5.481036,57.403107 -5.484538,57.402102 -5.485647,57.401856 -5.487358,57.401287 -5.488709,57.400962 -5.490175,57.400616 -5.491116,57.400176 -5.493832,57.399318 -5.495279,57.399134 -5.496726,57.39771 -5.498724,57.396836 -5.49974,57.396314 -5.501317,57.39627 -5.502869,57.395426</coordinates></LinearRing></innerBoundaryIs></Polygon>"
    ,Strathclyde,63,"<Polygon><outerBoundaryIs><LinearRing><coordinates>-5.603129,56.313564 -5.603163,56.312536 -5.603643,56.311794 -5.601467,56.311875 -5.601038,56.312481 -5.600697,56.313489 -5.60071,56.31535 -5.60159,56.316107 -5.600729,56.316598 -5.598625,56.316058 -5.596203,56.317477 -5.597024,56.318119 -5.596095,56.318739 -5.595432,56.320116 -5.589343,56.322469 -5.584888,56.325178 -5.582907,56.327169 -5.581414,56.327472 -5.581435,56.326663 -5.582355,56.325602 -5.581515,56.323891 -5.576993,56.331062 -5.57886,56.331475 -5.57676,56.334449 -5.572748,56.335689 -5.569012,56.338143 -5.564802,56.342113 -5.555237,56.346668 -5.551214,56.347448 -5.547651,56.346391 -5.54444,56.344945 -5.541247,56.345945 -5.539099,56.349674 -5.533874,56.34763 -5.525195,56.342888 -5.523518,56.345066 -5.52345,56.346605 -5.526417,56.354361 -5.535455,56.353681 -5.537463,56.35508 -5.536035,56.356271 -5.538923,56.357205 -5.53891,56.359336 -5.539952,56.361491 -5.538102,56.36372 -5.535934,56.36567 -5.53392,56.367705 -5.531369,56.369729 -5.529853,56.371022 -5.532371,56.371274 -5.534177,56.371708 -5.532846,56.373256 -5.529845,56.37496 -5.527675,56.375327 -5.528531,56.375995 -5.526732,56.376343 -5.525442,56.377809 -5.524739,56.379843 -5.526069,56.380561</coordinates></LinearRing></innerBoundaryIs></Polygon>"

我将 KML 文件上传到 Google Maps Fusion Table Layer,然后它创建了 map。然后我去了File&gt;Download&gt;CSV,它给了我上面的例子。

我已将此 csv 文件添加到我的 xamarin android google map 应用程序的资产文件夹中,我的问题是因为LatLng 需要两个双精度作为输入,有没有办法可以从 csv 文件中输入上述数据进入这个方法,如果是的话怎么做?

不确定如何read the above csv,然后提取&lt;coordinates&gt;,然后在上面的示例代码中将这些坐标添加为新的LatLng

如果您注意到坐标被分成 lat 和 lng,然后下一个 latlng空格 -5.657018,57.3352 -5.656396,57.334463 分隔。

Sudo 代码(这可能需要也可能不需要 xamarin 或 android 经验,可能只需要 C#/Linq)

Read CSV var sr = new StreamReader(Read csv from Asset folder);
Remove description,name,label,geometry
Foreach line in CSV
  Extract Item that contains double qoutes
     Foreach Item Remove Qoutes and <Polygon><outerBoundaryIs><LinearRing><coordinates> from start and end
         Foreach item seperated by a space Extract coordinates
       (This will now leave a long list of 37.35,-37.0123 coordinates for each line)
        Place in something like this maybe?:

    public class Row
    {
        public double Lat { get; set; }
        public double Lng { get; set; }

        public Row(string str)
        {
            string[] separator = { "," };
            var arr = str.Split(separator, StringSplitOptions.None);
            Lat = Convert.ToDouble(arr[0]);
            Lng = Convert.ToDouble(arr[1]);
        }
    }


    private void OnMapReady()
    var rows = new List<Row>();

      Foreach name/new line
         PolylineOptions geometry = new PolylineOptions()
           ForEach (item in rows) //not sure how polyline options will take a foreach
            .Add(New LatLng(item.Lat, item.Lng))
         Polyline polyline = mMap.AddPolyline(geometry);

由于无法在带有 Google Maps API v2 的 Xamarin Android 中使用 Fusion Table Layers,这可能为需要将地图拆分为区域的用户提供了一种快速、更轻松的解决方法。

【问题讨论】:

    标签: c# android linq google-maps xamarin


    【解决方案1】:

    如果我理解正确,问题是如何解析上述 CSV 文件。

    每一行(除了带有标题的第一行)都可以用以下类表示:

    class MapEntry
    {
        public string Description { get; set; }
        public string Name { get; set; }
        public string Label { get; set; }
        public IEnumerable<LatLng> InnerCoordinates { get; set; }
        public IEnumerable<LatLng> OuterCoordinates { get; set; }
    }
    

    注意InnerOuter 坐标。它们在 XML 中由 outerBoundaryIs(必需)和 innerBoundaryIs(可选)元素表示。

    附注:您帖子中的 Highland 行不正确 - 您似乎修剪了部分行,导致 XML 不正确 (&lt;outerBoundaryIs&gt;...&lt;/innerBoundaryIs&gt;)。

    下面是解析的代码:

    static IEnumerable<MapEntry> ParseMap(string csvFile)
    {
        return from line in File.ReadLines(csvFile).Skip(1)
               let tokens = line.Split(new[] { ',' }, 4)
               let xmlToken = tokens[3]
               let xmlText = xmlToken.Substring(1, xmlToken.Length - 2)
               let xmlRoot = XElement.Parse(xmlText)
               select new MapEntry
               {
                   Description = tokens[0],
                   Name = tokens[1],
                   Label = tokens[2],
                   InnerCoordinates = GetCoordinates(xmlRoot.Element("innerBoundaryIs")),
                   OuterCoordinates = GetCoordinates(xmlRoot.Element("outerBoundaryIs")),
               };
    }
    
    static IEnumerable<LatLng> GetCoordinates(XElement node)
    {
        if (node == null) return Enumerable.Empty<LatLng>();
        var element = node.Element("LinearRing").Element("coordinates");
        return from token in element.Value.Split(' ')
               let values = token.Split(',')
               select new LatLng(XmlConvert.ToDouble(values[0]), XmlConvert.ToDouble(values[1]));
    }
    

    我认为代码是不言自明的。唯一需要提及的细节是:

    let tokens = line.Split(new[] { ',' }, 4)
    

    这里我们使用string.Splitoverload,它允许我们指定要返回的最大子字符串数,从而避免处理XML令牌中的逗号的陷阱。

    和:

    let xmlText = xmlToken.Substring(1, xmlToken.Length - 2)
    

    从 XML 标记中去除引号。

    最后,为您的案例提供一个示例用法:

    foreach (var entry in ParseMap(csv_file_full_path))
    {
        PolylineOptions geometry = new PolylineOptions()
        foreach (var item in entry.OuterCoordinates)
            geometry.Add(item)
        Polyline polyline = mMap.AddPolyline(geometry);
    }
    

    更新:为了让 Xamarin 满意(如 cmets 中所述),请将 File.ReadLines 调用替换为对以下帮助程序的调用:

    static IEnumerable<string> ReadLines(string path)
    {
        var sr = new StreamReader(Assets.Open(path));
        try
        {
            string line;
            while ((line = sr.ReadLine()) != null)
                yield return line; 
        }
        finally { sr.Dispose(); }
    }
    

    【讨论】:

    • 感谢 Ivan +1 将尽快尝试。
    • @GarrithGraham 好的,然后修复它:)
    • 我会删除 File. 并执行 return from ReadLines(csvFile).Skip(1)
    • 完全没有差异。如果您需要访问某些状态,只需删除 static
    • Ivan 成功了。纬度和经度在哪里绕错了方向。现在,这是针对 Android Google Maps API v2 中缺少 Fusion Table Layers 的解决方法!!
    【解决方案2】:

    我不确定是否理解您的问题,但我认为这会有所帮助:

    您必须逐行读取 csv 文件,foreach 行提取第三个参数,删除引号并使用 XMLReader 读取结果以提取您想要的行(坐标)。在空格上拆分结果后,您将获得 LatLng 列表,并将每个 LatLng 拆分为“,”,然后您就获得了所需的所有信息。

    System.IO.StreamReader file = new System.IO.StreamReader(@"file.csv");
    string line = file.ReadLine(); //escape the first line
    while((line = file.ReadLine()) != null)
    {
        var xmlString = line.Split({","})[2]; // or 3
        **Update**
        xmlString = xmlString.Replace("\"","")
    
        using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
        {
            reader.ReadToFollowing("coordinate");
            string coordinate = reader.Value;
            PolylineOptions geometry = new PolylineOptions();
            var latLngs = coordinate.Split({' '});
            foreach (var latLng in latLngs)
            {
                double lat = 0;
                double lng = 0;
                var arr = latLng.Split({','});
                double.TryParse(arr[0], out lat);
                double.TryParse(arr[1], out lng);
                geometry.Add(new LatLng(lat, lng));
            }
            Polyline polyline = mMap.AddPolyline(geometry);
            // Do something with your polyline
        }
    }
    
    file.Close();
    

    【讨论】:

    • System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1. 在这条线上reader.ReadToFollowing("coordinate");
    • 确切地说,您必须先删除 xmlString 中的双引号,然后才能使用 xmlString.Replace("\"","") 将其传递给 XmlReader
    • 您的答案中的代码无法编译。我也尝试过没有成功的变通方法,但在同一行仍然遇到同样的问题。
    【解决方案3】:
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Xml.Linq;
    
    namespace ConsoleApplication35
    {
        public class LatLng
        {
            public LatLng(double lat, double lng)
            {
            }
        }
    
        class Program
        {
            private static IEnumerable<LatLng> GetCoordinates(string polygon)
            {
                var xElement = XElement.Parse(polygon);
                //var innerBoundaryCoordinates = xElement.Elements("innerBoundaryIs").FirstOrDefault()?.Value ?? "";
                var outerBoundaryCoordinates = xElement.Elements("outerBoundaryIs").Single()?.Value ?? "";
                return outerBoundaryCoordinates
                    .Split(' ')
                    .Select(latLng =>
                    {
                        var splits = latLng.Split(',');
                        var lat = double.Parse(splits[0], CultureInfo.InvariantCulture);
                        var lng = double.Parse(splits[1], CultureInfo.InvariantCulture);
                        return new LatLng(lat, lng);
                    });
            }
    
            static void Main()
            {
                const string header = "description,name,label,geometry";
                var latLngs = File.ReadLines("file.csv")
                                  .SelectMany(x => x.Split(new[] { header }, StringSplitOptions.RemoveEmptyEntries)) //all geometry`s in one array
                                  .Select(x => x.Split('"'))
                                  .SelectMany(x => GetCoordinates(x[1]))
                                  .ToArray();
            }
        }
    }
    

    【讨论】:

    • 查看更新的问题。您的方法使应用程序崩溃而根本没有错误输出?似乎正在这条线上发生:.SelectMany(x =&gt; GetCoordinates(x[1]))
    • 去掉 GetCoordinates .SelectMany(x =&gt; (x[1])) 并将数组输出到控制台窗口会为每个单独的字符显示一个新行。这是您期望作为string polygon 输入的内容吗?
    • Ok) 我从您的map 链接下载原始资源。代码工作正常。 GetCoordinates(x[1]) 是这种文件格式的安全操作。
    • 我使用简单的 C# console.application。这只是您如何解析文件的示例。例如,您可以使用此实用程序并将您的 csv 文件转换为您的 android 应用程序的其他格式。尝试使用此文件 map 运行此工具,然后为 android 调试和重写它。
    • 您使用 StreamReader 的示例是等效的,它也可以工作。我想您的文件 regions.csvmap 不同
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-18
    • 1970-01-01
    • 2018-05-20
    • 2022-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多