MAUI Blazor学习11-百度地图定位

MAUI Blazor学习11-百度地图定位

 

MAUI Blazor系列目录

  1. MAUI Blazor学习1-移动客户端Shell布局 - SunnyTrudeau - 博客园 (cnblogs.com)
  2. MAUI Blazor学习2-创建移动客户端Razor页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  3. MAUI Blazor学习3-绘制ECharts图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  4. MAUI Blazor学习4-绘制BootstrapBlazor.Chart图表 - SunnyTrudeau - 博客园 (cnblogs.com)
  5. MAUI Blazor学习5-BLE低功耗蓝牙 - SunnyTrudeau - 博客园 (cnblogs.com)
  6. MAUI Blazor学习6-扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)
  7. MAUI Blazor学习7-实现登录跳转页面 - SunnyTrudeau - 博客园 (cnblogs.com)
  8. MAUI Blazor学习8-支持多语言 - SunnyTrudeau - 博客园 (cnblogs.com)
  9. MAUI Blazor学习9-VS Code开发调试MAUI入门 - SunnyTrudeau - 博客园 (cnblogs.com)
  10. MAUI Blazor学习10-BarcodeScanner扫描二维码 - SunnyTrudeau - 博客园 (cnblogs.com)

  

地图和定位功能在APP里很常见,原生APP开发可以直接使用配套的第三方地图SDKXamarin.Forms项目要把原生地图SDK转换为C#语言的绑定库,技术要求高,开发效率低下。MAUI Blazor可以采用网页版第三方地图组件,不需要SDK绑定库了,但是要编写一部分JavaScript代码,有得有失,总体上利大于弊。

NetCore官网建议采用隔离的方案操作JavaScript,参见:JavaScript 模块中的 JavaScript 隔离

https://learn.microsoft.com/zh-cn/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-7.0#javascript-isolation-in-javascript-modules

网上有大佬写了隔离方案操作JavaScriptBlazor组件,我把大佬的代码拿过来简化一下,用于MAUI Blazor项目。感谢大佬无私奉献。

Blazor组件自做六 : 使用JS隔离封装Baidu地图 - AlexChow - 博客园 (cnblogs.com)

 

继续沿用之前的MAUI Blazor项目MaBlaApp,增加百度地图定位功能。

 

 

 

编写JavaScript调用地图函数baidumap.js

 

AlexChow大佬代码基础上小改一下,模块主要功能是初始化百度地图控件容器,呈现地图内容,定位等。

 

D:\Software\gitee\mauiblazorapp\MaBlaApp\wwwroot\js\baidumap.js

//百度地图实例
var map = null;

//地图容器控件
var containerId = null;

//初始化百度地图api的js
export function addScript(key, elementId, dotnetRef, backgroundColor, controlSize) {
    if (!key || !elementId) {
        return;
    }

    //设置百度地图容器控件id
    containerId = elementId;
    let url = "https://api.map.baidu.com/api?v=3.0&ak=";
    let scriptsIncluded = false;

    //获取所有js
    let scriptTags = document.querySelectorAll('body > script');
    scriptTags.forEach(scriptTag => {
        if (scriptTag) {
            let srcAttribute = scriptTag.getAttribute('src');

            //查找百度地图api的js是否已经添加到页面
            if (srcAttribute && srcAttribute.startsWith(url)) {
                scriptsIncluded = true;
                return true;
            }
        }
    });

    //如果百度地图的js已经添加到页面,就呈现初始地图到容器控件
    if (scriptsIncluded) {
        initMapsG(dotnetRef);
        return true;
    }

    //把百度地图的js添加到页面
    url = url + key + "&callback=initMapsG";
    let script = document.createElement('script');
    script.src = url;
    document.body.appendChild(script);
    return false;
}

//复位地图
export function resetMaps(elementId) {
    initMaps(elementId);
}

//呈现初始地图到容器控件
function initMapsG(dotnetRef) {
    initMaps(containerId);

    //定位
    geolocation(dotnetRef);
}

//呈现初始地图到容器控件
function initMaps(elementId) {
    //创建地图实例
    map = new BMap.Map(elementId, {
        //coordsType指定输入输出的坐标类型,3为gcj02坐标,5为bd0ll坐标,默认为5。指定完成后API将以指定的坐标类型处理您传入的坐标
        coordsType: 5
    });
    //创建默认地点坐标,北京
    var point = new BMap.Point(116.47496, 39.77856);

    //设置中心点坐标和地图级别
    map.centerAndZoom(point, 15);

    //开启鼠标滚轮缩放
    map.enableScrollWheelZoom(true);
    map.addControl(new BMap.NavigationControl());
    map.addControl(new BMap.ScaleControl());
    map.addControl(new BMap.OverviewMapControl());
    //map.addControl(new BMap.MapTypeControl());

    // 仅当设置城市信息时,MapTypeControl的切换功能才能可用
    //map.setCurrentCity("北京");
}

//定位,返回位置信息
export function geolocation(wrapper) {
    var geolocation = new BMap.Geolocation();

    //开启SDK辅助定位
    geolocation.enableSDKLocation();

    //执行定位,回调函数参数r包含位置信息
    geolocation.getCurrentPosition(function (r) {
        let location;
        if (this.getStatus() == BMAP_STATUS_SUCCESS) {
            var mk = new BMap.Marker(r.point);
            map.addOverlay(mk);
            map.panTo(r.point);
            location = r;
            location.Status = BMAP_STATUS_SUCCESS;//0
            console.log(r);

            // 设置中心点坐标和地图级别
            map.centerAndZoom(location.point, 15);
            //map.setCurrentCity(location.address.city);
        }
        else {
            location = {
                "Status": this.getStatus()
            };
        }

        if (wrapper) {
            //回调页面的dotnet函数,传递定位结果
            wrapper.invokeMethodAsync('GetResult', location);
        }

        return location;
    });
}

 

定义百度地图定位结果类BaiduLocation

在浏览器调试运行的时候,打印geolocation(wrapper)的定位结果,复制出来是一个Json,用VS2022的粘贴为类的功能,创建class,然后做一下修正。有的json值为null,自动转换字段类型是object,我去百度地图官网也没有找到定义,只好保留这些object字段。

网上也能够找到类似定位结果类定义,比如https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates,百度地图应该是在这个上边扩展的。

 

D:\Software\gitee\mauiblazorapp\MaBlaApp\Data\BaiduLocation.cs

namespace MaBlaApp.Data;

/// <summary>
/// 百度地图定位返回结果
/// </summary>
public class BaiduLocation
{
    public int Accuracy { get; set; }
    public object Altitude { get; set; }
    public object AltitudeAccuracy { get; set; }
    public object Heading { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public object Speed { get; set; }
    public object Timestamp { get; set; }
    public Point? Point { get; set; }
    public Address? Address { get; set; }
    public int Status { get; set; } = 0;
    public string Error { get; set; } = string.Empty;

    public override string ToString()
    {
        string msg = (Status != 0) ? $"错误码{Status}" : $"{Address}";

        return msg;
    }
}

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

public class Address
{
    public string Country { get; set; } = string.Empty;
    public string City { get; set; } = string.Empty;
    public int City_code { get; set; }
    public string District { get; set; } = string.Empty;
    public string Province { get; set; } = string.Empty;
    public string Street { get; set; } = string.Empty;
    public string Street_number { get; set; } = string.Empty;

    public override string ToString()
    {
        return $"{Province}{City}{District}{Street}{Street_number}";
    }
}

/*
 选择性粘贴,将json粘贴为类
{
    "accuracy": 1999,
    "altitude": null,
    "altitudeAccuracy": null,
    "heading": null,
    "latitude": 22.527458177915,
    "longitude": 113.92798387312,
    "speed": null,
    "timestamp": null,
    "point": {
        "lng": 113.92798387312,
        "lat": 22.527458177915
    },
    "address": {
        "country": "",
        "city": "深圳市",
        "city_code": 0,
        "district": "南山区",
        "province": "广东省",
        "street": "海德二道",
        "street_number": "479"
    }
}
 */

编写地图页面组件BaiduMap

编写一个地图页面组件BaiduMap。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Shared\BaiduMap.razor

@using Microsoft.Extensions.Configuration;
@implements IAsyncDisposable
@inject IJSRuntime JS
@inject IConfiguration config

<div id="@ContainerId" style="@Style"></div>

@code {

    /// <summary>
    /// BaiduKey<para></para>
    /// 为空则在 IConfiguration 服务获取 "BaiduKey" , 默认在 appsettings.json 文件配置
    /// </summary>
    [Parameter]
    public string? Key { get; set; }

    /// <summary>
    /// 定位结果回调方法
    /// </summary>
    [Parameter]
    public Func<BaiduLocation, Task>? OnResult { get; set; }

    /// <summary>
    /// 容器控件style
    /// </summary>
    public string Style { get; set; } = "height:400px;width:100%;";

    /// <summary>
    /// 容器控件ID
    /// </summary>
    public string ContainerId { get; set; } = "container";

    private IJSObjectReference? module;
    private DotNetObjectReference<BaiduMap>? InstanceGeo { get; set; }

    //百度地图秘钥
    private string key = String.Empty;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            //在 IConfiguration 服务获取 "BaiduKey" , 默认在 appsettings.json 文件配置
            key = Key ?? config["BaiduKey"];

            module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/baidumap.js");
            InstanceGeo = DotNetObjectReference.Create(this);
            while (!(await InitMapScript()))
            {
                await Task.Delay(500);
            }
        }
    }

    //初始化百度地图api的js
    public async Task<bool> InitMapScript() => await module!.InvokeAsync<bool>("addScript", new object?[] { key, ContainerId, InstanceGeo, null, null });

    /// <summary>
    /// 定位完成回调方法
    /// </summary>
    /// <param name="geolocations"></param>
    /// <returns></returns>
    [JSInvokable]
    public async Task GetResult(BaiduLocation geolocations)
    {
        await OnResult?.Invoke(geolocations);

        string json = JsonSerializer.Serialize(geolocations);
        System.Diagnostics.Debug.WriteLine(json);
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            await module.DisposeAsync();
        }
    }
}

 

TestBaiduMap.razor页面引入BaiduMap组件

百度地图KEY要去申请的。我调试的时候遇到过一些错误,后来发现是权限不全导致,比如定位返回结果是要地理编码权限的,不仅仅是定位权限。我申请的KEY权限很多,宁滥勿缺。

新建百度地图测试页面TestBaiduMap.razor,引入BaiduMap组件

D:\Software\gitee\mauiblazorapp\MaBlaApp\Pages\TestBaiduMap.razor

 

@page "/testbaidumap"

<h3>百度地图 Baidu Map</h3>

<p>@message</p>

<BaiduMap Key="百度地图KEY" OnResult="@OnResult" />

@code {

    private string message;
    private BaiduLocation location;

    private Task OnResult(BaiduLocation geolocations)
    {
        location = geolocations;

        message = $"{location}";

        StateHasChanged();

        return Task.CompletedTask;
    }
}

 

 

在安卓平台申请定位权限

如果是在windows平台运行,会提示要求定位,允许定位即可。在安卓平台则需要把浏览器权限转换为安卓APP权限,在MaBlaApp项目中已经做好了框架,参见:MAUI Blazor学习5-BLE低功耗蓝牙。在这个基础上只需要增加定位权限即可。

D:\Software\gitee\mauiblazorapp\MaBlaApp\Platforms\Android\AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

 

测试

使用VS2022调试运行,在windows上面定位不太准确。

在华为手机鸿蒙4(安卓12)上定位准确。 

 

其他方案

MAUI自身也集成了一些跳转第三方地图,定位功能,也可以结合着用,比如调用MAUI的定位函数,得到经纬度,再去调用百度地图api,但是可能要进行坐标系转换,感觉也没啥优势。如果是重度的地图功能,还是要跳到专门的地图APP去的,用人所长,不要在MAUI APP里边解决所有需求,手机APP的生态环境就是这样的,没有大而全的APP,有一些超级APP比如微信,支付宝,也是通过小程序平台去实现各类具体业务需求的。

 

DEMO代码地址:https://gitee.com/woodsun/mauiblazorapp

 

posted on 2023-10-28 22:18  SunnyTrudeau  阅读(369)  评论(0编辑  收藏  举报