栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于uni-app与百度地图API实现简易前后端分离的地理围栏应用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于uni-app与百度地图API实现简易前后端分离的地理围栏应用

一、引言

地理围栏(Geo-fencing)是LBS 的一种新应用,就是用一个虚拟的栅栏围出一个虚拟地理边界。当手机进入、离开某个特定地理区域,或在该区域内活动时,后台可以感知到这一变化,同时手机可以接收自动通知和警告。地理围栏技术融入生活,可以使得生活效率更加高效同时可以使得生活更加安全。有了地理围栏技术,位置社交网站就可以帮助用户在进入某一地区时自动登记。现阶段的地理围栏应用十分有限,相关资料与资源也不充足,本次实习致力于打造一个能够供个人使用的简单的地理围栏应用,并且能够在此基础上升级为企业、人员管理等多人使用的移动应用。

二、关键技术方案

2.1 uni-app开发

uni-app 是一个使用 Vue.js (opens new window)开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。本次实习使用Hbuilder X(一个轻如编辑器、强如IDE的合体版本)进行uni-app的开发。

2.2 GPS定位技术

通过Android/iOS/windows phone 提供的API 直接获取位置。也可以调用百度地图api的接口直接获取当前的地理位置。不过需要注意的是由于各大坐标系不一样,百度地理api定位的坐标系和手机系统定位有些许差别。本次实习中手机系统定位使用的坐标系是wgs84坐标系。

2.4 地理围栏的设计与管理

地理围栏的设计主要是通过百度地图api的多边形进行实现。多边形由大于等于三个点构成,可以增加、删除、编辑边界点,除此之外,可以直接在地图上拖动边界点进行编辑,点击边界虚化中点可以在边界中点添加一个边界点。这样可以实现十分简单便捷地设计各种形状的地理围栏。

2.5 判断点是否在多边形内的算法设计

判断点在多边形内部的方法有很多,不过有的只适用于凸多边形,但是对于地理围栏应用,围栏不一定宗室凸多边形,所以算法需要能够适用于所有多边形,本次实习使用的方法是引射线法。引射线法:从目标点出发引一条射线,看这条射线和多边形所有边的交点数目。如果有奇数个交点,则说明在内部,如果有偶数个交点,则说明在外部。

三、系统分析与设计

3.1 需求定义

1、功能需求

·地理围栏的构建

功能描述:用户能够构建并管理任意形状的地理围栏。

基本流程:在区域管理处用户可以管理区域的边界点,包括修改位置,删除位置和添加位置,添加点的默认位置为用户当前的定位。同时在地图上也可以拖动边界点,点击边界线中间的虚拟点也会添加一个边界点,实现更为便利的围栏管理

·定位

功能描述:用户授权后能够实时对用户定位。

基本流程:系统后台会通过系统GPS实时定位并显示,地图上也会有定位按钮,用户点击后地图会跳转到用户当前位置,同时弹出详细信息窗口供用户复制。

·进入/离开地理围栏通知

功能描述:提示用户进出地理围栏。

基本流程:当用户进入或者离开地理围栏的时候,能够弹出通知告知用户。

·时刻显示用户当前状态

功能描述:用户能够实时看到自己当前的状态。

基本流程:用户能够直接查看自己是在地理围栏内部还是外部。

·其他功能

功能描述:比例尺、缩放、卫星地图、全景地图等。

基本流程:调用百度地图api的多种接口能够使地图更加实用。

四、实现与实验

4.1 软件实现

uni-app开发

为了实现多端兼容,综合考虑编译速度、运行性能等因素,uni-app 约定了如下开发规范:

(1)页面文件遵循 Vue 单文件组件 (SFC) 规范(opens new window)

(2)组件标签靠近小程序规范,详见uni-app 组件规范

(3)接口能力(JS API)靠近微信小程序规范,但需将前缀 wx 替换为 uni

(4)数据绑定及事件处理同 Vue.js 规范,同时补充了App及页面的生命周期

(5)为兼容多端运行,建议使用flex布局进行开发

开发过程类似于vue的开发流程。在main.js中配置相关-创建vue页面并边界-在pages中注册页面。开发使用Hbuilder X界面如下:

开发结束并测试无误后可以进行APP打包。在发行-APP云打包中配置相关信息打包即可生成apk文件。

Springboot开发

Spring框架是Java平台上的一种开源应用框架,提供具有控制反转特性的容器。Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供大量优秀的Web框架方便开发等等。

SpringBoot所具备的特征有:

(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;

(2)内嵌Tomcat或Jetty等Servlet容器;

(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;

(4)尽可能自动配置Spring容器;

(5)提供准备好的特性,如指标、健康检查和外部化配置;

(6)绝对没有代码生成,不需要XML配置。

开发springboot需要的编写的目录如下。其中common中为一些公共资源,controller中为后端接口,entity中为一些实体类,function中为一些函数方法。DemoApplication为springboot的启动类。

4.2 实验步骤

(1)设计界面原型

首先用Axure绘制APP的原型图。本APP主打简单易操作,因为用户需要操作的地方主要就是编辑区域,其他检测定位等操作都是系统后台进行,所以界面并不复杂。APP主页面最上方就是地图,这里能够显示用户创建的区域,并且能够对多边形进行编辑,同时还能够手动定位。在中部是区域管理,更详细的区域修改在这个地方,能够更详细地编辑边界点的信息。在下方是当前的状态信息,告知用户当前位置以及当前位置相较于区域的状态。然后就是帮助按钮,点击后跳转到帮助页面。当用户进入/离开设置的区域的时候,能够弹出提示。

 

(2)Uni-app开发

Uni-app项目构成

Uni-app项目构成如下

pages文件夹中为编辑的页面,uview-ui是使用的第三方组件库,main.js是所有页面公共的配置,所有页面需要使用的元素需要在这个文件中全局配置,App.vue中可以配置全局css,在pages.json中配置并注册所有页面,只有注册的页面才能够被访问,其中页面数组第一项为应用启动页面。Uni.scss为uni-app内置的常用样式变量。

第三方库的注册使用

首先要使用第三方库就要对相应的库进行注册引入。对于百度地图api,首先使用命令$ npm install vue-baidu-map –save 对百度地图api进行引入。这里和html开发不一样,html直接在页面中的

helpInfo.vue






judgeController.java

package com.example.demo.controller;
import com.example.demo.common.Result;
import com.example.demo.entity.Form;
import org.springframework.web.bind.annotation.*;
import com.example.demo.function.judgeCurrentLocation;

@RestController
@CrossOrigin
@RequestMapping("/judge")
public class judgeController {
    @PostMapping("/location")
    public Result judgeLocation(@RequestBody Form form){
        int status = judgeCurrentLocation.judgeStatue(form);
        if(status == 0){
            // 之前在区域外,但现在进入区域了
            System.out.println("用户已进入区域");
            return Result.success("100");
        }
        else if(status == 1){
            // 状态为在区域内
            return Result.success("200");
        }
        else if(status == 2){
            // 原来在区域内,现在在区域外
            System.out.println("用户已离开区域");
            return Result.success("300");
        }
        else{
            // 状态为在区域外
            return Result.success("400");
        }
    }
}

Form.java

package com.example.demo.entity;
import lombok.Data;
import java.util.List;

@Data
public class Form {
    public String status;  // 前面的状态 -1为初始状态,0在区域内,1在区域外
    public List polygonPath;
    public String myposition_lng;
    public String myposition_lat;
}

Point.java

package com.example.demo.entity;

public class Point {
    public String lng;
    public String lat;

    public Point(String lng, String lat) {
        this.lng = lng;
        this.lat = lat;
    }
}

judgeCurrentLocation.java

package com.example.demo.function;
import com.example.demo.entity.Form;
import com.example.demo.entity.Point;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

public class judgeCurrentLocation {

    public static boolean isPtInPoly(Point2D.Double point, List pts) {
        int polygonSidesCount = pts.size();  // 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
        int intersectCount = 0;  // 射线与多边形相交的次数
        double precision = 2e-10;  //浮点类型计算时候与0比较时候的容差
        Point2D.Double p1, p2;  // 相邻两点
        p1 = pts.get(0);  // 线段起点
        for (int i = 1; i <= polygonSidesCount; ++i) {
            // 按照点录入的顺序依次检查相邻两点组成的线段和当前的点的关系。
            if (point.equals(p1)) {
                // 如果当前点就是多边形的顶点之一,直接返回。
                return true;
            }
            p2 = pts.get(i % polygonSidesCount);  // 线段终点
            if (point.x < Math.min(p1.x, p2.x) || point.x > Math.max(p1.x, p2.x)) {
                // 点在x轴上的映射,明显超出了线段在x轴上的投影
                p1 = p2;
                continue;
            }
            if (point.x > Math.min(p1.x, p2.x) && point.x < Math.max(p1.x, p2.x)) {
                // 当前点在线段于x轴上的投影内
                if (point.y <= Math.max(p1.y, p2.y)) {
                    //当前点的 y 坐标小于 线段对y轴投影的最大值
                    if (p1.x == p2.x && point.y >= Math.min(p1.y, p2.y)) {
                        // 若线段同样是平行于y轴,则可以断定,当前点,在多边形的这条垂直于x轴的边上。
                        return true;
                    }
                    if (p1.y == p2.y) {
                        // 若线段为平行于x轴的水平线
                        if (p1.y == point.y) {
                            // 当前点正好位于该水平线上,直接返回该点位于多边形的一条边上。
                            return true;
                        } else {
                            // 如果当前点在水平线以下,增加一个交点。
                            ++intersectCount;
                        }
                    } else {
                        // 如果不是水平线,则用两点式求当前点的x带入多边形线段的直线方程后,对应的y的坐标
                        double xInSideLineFormulaResultY = (point.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(point.y - xInSideLineFormulaResultY) < precision) {
                            // 误差允许范围内,该点就在线段上,则表明,该点位于多边形的一个边上。
                            return true;
                        }

                        if (point.y < xInSideLineFormulaResultY) {
                            // 如果线段上取得的y比当前点的y要大,当前做向上的射线,肯定交于上方的一个点。
                            ++intersectCount;
                        }
                    }
                }
            } else {
                // 当前点不在线段投影到x轴的区间中
                if (point.x == p2.x && point.y <= p2.y) {
                    // 但恰好位于线段终点的x坐标对应的平行于y轴上的线上的低于终点y的一点
                    // 此时检查下一点能否将其x边界包含。
                    Point2D.Double p3 = pts.get((i + 1) % polygonSidesCount);
                    if (point.x >= Math.min(p1.x, p3.x) && point.x <= Math.max(p1.x, p3.x)) {
                        // 若当前点的x坐标位于 p1和p3组成的线段关于x轴的投影中,则记为该点的射线只穿过端点一次。
                        ++intersectCount;
                    } else {
                        // 若当前点的x坐标不能包含在p1和p3组成的线段关于x轴的投影中,则点射线通过的两条线段组成了一个弯折的部分,
                        // 此时我们记射线穿过该端点两次
                        intersectCount += 2;
                    }
                    // 此判断的核心思路是由点在两条线段的内部还是外部去思考得出的
                }
            }
            // 进行下一个线段的判断
            p1 = p2;
        }
        // 奇数在多边形内,偶数在多边形外
        return intersectCount % 2 != 0;
    }

    public static boolean judgePosition(List polygonPath, Point position){
        Point2D.Double point = new Point2D.Double(Double.parseDouble(position.lng), Double.parseDouble(position.lat));
        List pts = new ArrayList<>();
        for (Point value : polygonPath) {
            pts.add(new Point2D.Double(Double.parseDouble(value.lng), Double.parseDouble(value.lat)));
        }
        return isPtInPoly(point,pts);
    }

    public static int judgeStatue(Form form){
        Point point = new Point(form.myposition_lng,form.myposition_lng);
        if(judgePosition(form.polygonPath,point)){
            // 判断结果为在区域内
            if(form.status.equals("1")){
                // 之前在区域外,但现在进入区域了
                return 0;
            }
            else{
                // 状态为在区域内
                return 1;
            }
        }
        else{
            // 判断结果为在区域外
            if(form.status.equals("0")){
                // 原来在区域内,现在在区域外
                return 2;
            }
            else{
                // 状态为在区域外
                return 3;
            }
        }
    }

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1024305.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号