简体   繁体   English

如何在运行时使用 Xamarin.Forms.Maps 创建折线时的 append 位置?

[英]How to append positions when creating a Polyline with Xamarin.Forms.Maps in runtime?

I am creating a Xamarin.Forms.Maps.Polyline in runtime.我正在运行时创建一个Xamarin.Forms.Maps.Polyline How can I append positions dynamically, given that the Polyline.Geopath property is read-only?鉴于Polyline.Geopath属性是只读的,我如何动态定位append?

Createing a polyline in runtime在运行时创建折线

Following the document to create a polyline: Xamarin.Forms Map Polygons and Polylines .按照文档创建折线: Xamarin.Forms Map Polygons and Polylines this link is a tutorial with fixed position in the code.此链接是在代码中固定 position 的教程。 How to assign position in runtime dynamically.如何在运行时动态分配 position。

using Xamarin.Forms.Maps;
// ...
Map map = new Map
{
// ...
};

Create a object to store routing data (data extracted from json)创建一个object来存储路由数据(数据提取自json)

public class MapRoute
{
    //[JsonPropertyName("timestamp")]
    public long timestamp { get; set; }
    //[JsonPropertyName("lat")]
    public double lat { get; set; }
    //[JsonPropertyName("lng")]
    public double lng { get; set; }

    public MapRoute(long v1, double v2, double v3)
    {
        timestamp = v1;
        lat = v2;
        lng = v3;
    }
}

Serialize routing object to JsonString.序列化路由 object 到 JsonString。

public void RouteAppend(MapRoute route)
{
    JsonString.Append(JsonSerializer.Serialize(route));
    JsonString.Append(",");
}

In real life, there are more than 2 elements in jsonString (there are more than 1000 elements which stored in jsonString)在现实生活中,jsonString中有2个以上的元素(jsonString中存储了1000多个元素)

readonly string jsonString = " [ {\"timestamp\": 1514172600000, \"Lat\": 37.33417925, \"Lng\": -122.04153133}, " + "{\"timestamp\": 1514172610000, \"Lat\": 37.33419725, \"Lng\": -122.04151333} ]";

JsonDocument doc;
JsonElement root;

private IList<Position> pos;

doc = Parse(testString);
root = doc.RootElement;
     
var routes = root.EnumerateArray();
while (routes.MoveNext())
{
    var user = routes.Current;
  
    pos.Add(new Position(Convert.ToDouble(user.GetProperty("lat")), Convert.ToDouble(user.GetProperty("lng"))));
           
}

Finally, there is a list pos with a lot of Position , I would assign the pos to Geopath .最后,有一个列表pos有很多Position ,我会将pos分配给Geopath Unfortunately, it isn't allowed.不幸的是,这是不允许的。 It is a readonly property:它是一个只读属性:

// instantiate a polyline
Polyline polyline = new Polyline
{
    StrokeColor = Color.Blue,
    StrokeWidth = 12,
    Geopath = pos // It is a readonly property, cannot assign pos to Geopath
}

// add the polyline to the map's MapElements collection
map.MapElements.Add(polyline);

How can this problem be resolved?如何解决这个问题?

While Polyline.Geopath is a get-only property, the returned IList<Position> is not a read-only collection, so you can add your Position objects to it after construction.虽然Polyline.Geopath是一个仅获取属性,但返回的IList<Position>不是只读集合,因此您可以在构造Position对象添加到它。

Thus you can create the following factory method:因此,您可以创建以下工厂方法:

public static class PolylineFactory
{
    const string latitudeJsonName = "Lat";
    const string longitudeJsonName = "Lng";
    
    public static Polyline FromLatLngJson(string jsonString, float? strokeWidth = default, Color strokeColor = default)
    {
        using var doc = JsonDocument.Parse(jsonString); 
        var query = doc.RootElement.EnumerateArray()
            // GetProperty performs property name matching as an ordinal, case-sensitive comparison.
            .Select(e => new Position(e.GetProperty(latitudeJsonName).GetDouble(), e.GetProperty(longitudeJsonName).GetDouble()));

        var polyline = new Polyline();
        if (strokeColor != default)
            polyline.StrokeColor = strokeColor;
        if (strokeWidth != default)
            polyline.StrokeWidth = strokeWidth.Value;
        foreach (var position in query)
            polyline.Geopath.Add(position);
        
        return polyline;
    }
}

Alternatively, in .NET 5 JsonSerializer supports deserializing to objects with a single, parameterized constructor, so if you modify your MapRoute class slightly as follows, you can deserialize your jsonString to a List<MapRoute> directly:或者,在 .NET 5 JsonSerializer支持使用单个参数化构造函数反序列化到对象,因此如果您稍微修改MapRoute class 如下所示,您可以将jsonString直接反序列化为List<MapRoute>

public class MapRoute
{
    public long timestamp { get; set; }
    public double lat { get; set; }
    public double lng { get; set; }

    public MapRoute(long timestamp, double lat, double lng)
    {
        // The constructor argument names and property names must match for JsonSerializer to bind to the constructor successfully
        this.timestamp = timestamp;
        this.lat = lat;
        this.lng = lng;
    }
}

public static class PolylineFactory
{
    public static Polyline FromLatLngJson(string jsonString, float? strokeWidth = default, Color strokeColor = default)
    {
        var routes = JsonSerializer.Deserialize<List<MapRoute>>(jsonString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        
        var polyline = new Polyline();
        if (strokeColor != default)
            polyline.StrokeColor = strokeColor;
        if (strokeWidth != default)
            polyline.StrokeWidth = strokeWidth.Value;
        foreach (var route in routes)
            polyline.Geopath.Add(new Position(route.lat, route.lng));
        
        return polyline;
    }
}

(In .NET Core 3.x you would need to add a parameterless constructor to MapRoute and ensure all properties are mutable to deserialize it successfully.) (在 .NET Core 3.x 中,您需要向MapRoute添加一个无参数构造函数并确保所有属性都是可变的才能成功反序列化。)

Either way, you can call the factory method as follows:无论哪种方式,您都可以按如下方式调用工厂方法:

var polyline = PolylineFactory.FromLatLngJson(jsonString, 12, Color.Blue);

Notes:笔记:

  • JsonElement.GetProperty() performs property name matching as an ordinal, case-sensitive comparison , so you need to pass "Lat" and "Lng" rather than "lat" and "lng" , since your jsonString is pascal-cased not camel-cased. JsonElement.GetProperty()将属性名称匹配作为序数、区分大小写的比较执行,因此您需要传递"Lat""Lng"而不是"lat""lng" ,因为您的jsonString是帕斯卡大小写而不是骆驼 -装箱。

    If this is a mistake in your question and your JSON string is really camel-cased, modify latitudeJsonName and longitudeJsonName appropriately.如果这是您的问题中的错误,并且您的 JSON 字符串确实是驼峰式大小写,请适当修改latitudeJsonNamelongitudeJsonName

    If you need to ignore case when fetching a property see Ignore case when using TryGetProperty .如果您在获取属性时需要忽略大小写,请参阅使用 TryGetProperty 时忽略大小写

  • Convert.ToDouble() interprets the incoming value by using the formatting conventions of the current thread culture. Convert.ToDouble()使用当前线程区域性的格式约定来解释传入的值。 Eg in many European locales a , comma is used as the decimal separator when formatting floating values.例如,在许多欧洲语言环境中,格式化浮点值时,逗号用作小数分隔符。 As this is inconsistent with the JSON standard it is better to use the built-in method JsonElement.GetDouble() which will always use the correct decimal separator.由于这与 JSON 标准不一致,因此最好使用内置方法JsonElement.GetDouble() ,它将始终使用正确的小数分隔符。

  • JsonDocument is disposable: JsonDocument是一次性的:

    This class utilizes resources from pooled memory to minimize the impact of the garbage collector (GC) in high-usage scenarios.此 class 使用来自池化 memory 的资源,以最大限度地减少垃圾收集器 (GC) 在高使用情况下的影响。 Failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.未能正确处理此 object 将导致 memory 不会返回到池中,这将增加框架各个部分的 GC 影响。

    In your code you do not dispose of your doc , but you should.在您的代码中,您不会处理您的doc ,但您应该处理。

Demo fiddle #1 here using JsonDocument , and #2 here using MapRoute . Demo fiddle #1这里使用JsonDocument ,#2这里使用MapRoute

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM