精华内容
下载资源
问答
  • uniapp写的基于腾讯地图的微信小程序的电子围栏(服务范围),里面的key换成自己再腾讯地图api官网申请的,如果哪里有写的不好的地方,请多多谅解!
  • 基于微信小程序做的车辆电子围栏demo,实现手动点击添加删除标记点、标记点连接生成多边形区域、保存和删除已绘制的多边形区域
  • 微信管理电子围栏

    2018-12-01 18:34:47
    周界报警通过网络传输进行管理方案设计思考,微信管理脉冲主机传输过来的报警信号并且处理报警信号
  • 脉冲电子围栏系统由脉冲主机、避雷器、防护箱、终端杆、终端杆绝缘子、终端杆绝缘子固定夹、承力杆、承力杆绝缘子、中间杆、中间杆绝缘子、合金导线、高压绝缘导线、螺纹式线-线连结器、警示牌、声光报警器等前端...
  • 电子围栏说明书.pdf

    2021-04-20 17:06:14
    电子围栏配置说明
  • 高德web端实现多边形电子围栏demo 地图上点三个点形成多边形区域,可以通过拉拽添加点的形式添加边的数量 可判断marker是否在多边形区域区域内(jQuery需要自己添加)
  • 微信小程序-实现电子围栏-半径-经纬度-是否在围栏内-画圆等
  • 基于GPS的电子围栏设计,朱安里,杜谦,本文阐述了GIS系统的电子围栏的设计与开发,主要内容有电子围栏的研究背景和意义、关键算法的改进、电子围栏的整体设计、数据库实
  • 电子围栏主机采用了先进的“阻挡为主,报警为辅”的周界安防理念,集“威慑、阻挡、报警、安全”于一身。下面一起来学习一下
  • 距离测量、ip定位(浏览器定位)、行政区域查询、单击地图获取位置信息、获取marker点处位置信息、简易圆形电子围栏(圆可编辑大小、可移动位置)、右键菜单、中英对照、卫星地图 *简易电子围栏显示圆+开启路线回放+...
  • 练手项目 无参考价值 ...腾讯地图,百度地图api研究,地址解析,路线计算,电子围栏判断等,涉及到的密钥可以去百度或者腾讯的官网申请,腾讯的地图做的还是不如百度,电子围栏是用百度的api算法实现的。
  • 电子围栏教程

    2018-12-03 12:59:14
    电子围栏教程 !
  • 电子围栏

    2019-01-04 15:28:00
    1、找了个电子围栏算法,也就是多边形找点,在图形内还是图形外 #!/usr/bin/env python # -*- coding: utf-8 -*- import json import math lnglatlist = [] data = '[{"name":"武汉市三环","points":[{"lng...

     1、找了个电子围栏算法,也就是多边形找点,在图形内还是图形外

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import json
    import math
    lnglatlist = []
    data = '[{"name":"武汉市三环","points":[{"lng":114.193437,"lat":30.513069},{"lng":114.183376,"lat":30.509211},{"lng":114.188191,"lat":30.505291},{"lng":114.187975,"lat":30.504731},{"lng":114.201773,"lat":30.492782},{"lng":114.213559,"lat":30.48855},{"lng":114.239143,"lat":30.484006},{"lng":114.248341,"lat":30.470062},{"lng":114.267888,"lat":30.470062},{"lng":114.286286,"lat":30.46309},{"lng":114.294335,"lat":30.459105},{"lng":114.298934,"lat":30.459105},{"lng":114.305833,"lat":30.459105},{"lng":114.341478,"lat":30.453128},{"lng":114.422613,"lat":30.462591},{"lng":114.424337,"lat":30.453688},{"lng":114.444316,"lat":30.456303},{"lng":114.466809,"lat":30.466078},{"lng":114.473708,"lat":30.549713},{"lng":114.443813,"lat":30.624326},{"lng":114.407593,"lat":30.683478},{"lng":114.388621,"lat":30.703352},{"lng":114.3616,"lat":30.704843},{"lng":114.311582,"lat":30.678466999999998},{"lng":114.241442,"lat":30.64123},{"lng":114.201773,"lat":30.63079},{"lng":114.182226,"lat":30.63427},{"lng":114.165553,"lat":30.626812},{"lng":114.162679,"lat":30.6109},{"lng":114.170153,"lat":30.59598},{"lng":114.167853,"lat":30.552201},{"lng":114.179351,"lat":30.529309}],"type":0}]'
    data = json.loads(data)
    if 'points' in data[0]:
        for point in data[0]['points']:
            #print(str(point['lng'])+" "+str(point['lat']))
            lnglat = []
            lnglat.append(float(str(point['lng'])))
            lnglat.append(float(str(point['lat'])))
            lnglatlist.append(lnglat)
    
    def windingNumber(point, poly):
        poly.append(poly[0])
        px = point[0]
        py = point[1]
        sum = 0
        length = len(poly)-1
    
        for index in range(0,length):
            sx = poly[index][0]
            sy = poly[index][1]
            tx = poly[index+1][0]
            ty = poly[index+1][1]
    
            #点与多边形顶点重合或在多边形的边上
            if((sx - px) * (px - tx) >= 0 and (sy - py) * (py - ty) >= 0 and (px - sx) * (ty - sy) == (py - sy) * (tx - sx)):
                return "on"
            #点与相邻顶点连线的夹角
            angle = math.atan2(sy - py, sx - px) - math.atan2(ty - py, tx - px)
    
            #确保夹角不超出取值范围(-π 到 π)
            if(angle >= math.pi):
                angle = angle - math.pi * 2
            elif(angle <= -math.pi):
                angle = angle + math.pi * 2
            sum += angle
    
            #计算回转数并判断点和多边形的几何关系
        result = 'out' if int(sum / math.pi) == 0 else 'in'
        return result
    
    point = [114.193437,30.513068]
    print(windingNumber(point,lnglatlist))

    转自

    http://blog.51cto.com/yixianwei/2089075

     

    2、我的项目json文件比较复杂,需要用个二维数据来存储坐标数据

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import json
    import math
    
    def windingNumber(point, poly):
        poly.append(poly[0])
        px = point[0]
        py = point[1]
        sum = 0
        length = len(poly)-1
    
        for index in range(0,length):
            sx = poly[index][0]
            sy = poly[index][1]
            tx = poly[index+1][0]
            ty = poly[index+1][1]
    
            #点与多边形顶点重合或在多边形的边上
            if((sx - px) * (px - tx) >= 0 and (sy - py) * (py - ty) >= 0 and (px - sx) * (ty - sy) == (py - sy) * (tx - sx)):
                return "on"
            #点与相邻顶点连线的夹角
            angle = math.atan2(sy - py, sx - px) - math.atan2(ty - py, tx - px)
    
            #确保夹角不超出取值范围(-π 到 π)
            if(angle >= math.pi):
                angle = angle - math.pi * 2
            elif(angle <= -math.pi):
                angle = angle + math.pi * 2
            sum += angle
    
            #计算回转数并判断点和多边形的几何关系
        result = 'out' if int(sum / math.pi) == 0 else 'in'
        return result
    
    
    if __name__ == "__main__":
    
        with open("railwayFence.json", 'r') as f:
            data = json.loads(f.read())
            data = data['railwayFence']
            #print(len(data))
            #print(data[65]['areaName'])
    
        lnglatlist = [[]]*len(data)
        point_test = [115.259161584,38.813623816]
        point_test1 = [115.243922249,38.836012517]
    
        for i in range(0,len(data)):
            for point in data[i]['coordinates']:
                #print(str(point['L'])+" "+str(point['B']))
                lnglat = []
                lnglat.append(float(str(point['L'])))
                lnglat.append(float(str(point['B'])))
                lnglatlist[i].append(lnglat)
            ret = windingNumber(point_test1,lnglatlist[i])
    
            if ret == 'in' or ret == 'on':
                break
             
        print ret
    
        

     

    3、解析出json中的坐标点,可以描出电子围栏,打点测试就很方便了

    test.txt

    截取几个点,格式如下

    38.836013 115.243822
    38.836012 115.243818
    38.836013 115.243813
    38.836013 115.243809
    38.836015 115.243805
    38.836017 115.243801
    38.836019 115.243898
    38.836022 115.243895
    38.836023 115.243895
    38.836092 115.243850
    38.836160 115.243806
    38.836189 115.243788
    38.836218 115.243769
    38.836416 115.243642
    38.836613 115.243515
    38.837036 115.243243

     

    plot.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import matplotlib.pyplot as plt
    import numpy as np
    import json
    
    with open("railwayFence.json", 'r') as f:
        data = json.loads(f.read())
        data = data['railwayFence']
        fo = open("tmp", "w")
        for i in range(0,len(data)):
            for point in data[i]['coordinates']:
                d = '%(x).9f %(y).9f\n'%{'x':float(str(point['B'])),'y':float(str(point['L']))}
                fo.write( d ) 
        fo.close()
    
    data = np.loadtxt('tmp')
    plt.plot(data[:,0],data[:,1])
    
    #plt.annotate('test point', xy=(39.82775139,116.250658818),arrowprops=dict(facecolor='red', shrink=0))
    #plt.annotate('test point', xy=(39.823400546,116.25345992),arrowprops=dict(facecolor='red', shrink=0))
    plt.annotate('test point', xy=(39.813623816,116.259161584),arrowprops=dict(facecolor='red', shrink=0))
    plt.show()

     

    python plot.py

     

    4、计算单个围栏

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import json
    import math
    lnglatlist = []
    
    with open("65.json", 'r') as f:
        data = json.loads(f.read())
        #print(data)
        print(data['areaName'])
    
    #data = json.loads(data)
    if 'coordinates' in data:
        for point in data['coordinates']:
            #print(str(point['L'])+" "+str(point['B']))
            lnglat = []
            lnglat.append(float(str(point['L'])))
            lnglat.append(float(str(point['B'])))
            lnglatlist.append(lnglat)
    
    def windingNumber(point, poly):
        poly.append(poly[0])
        px = point[0]
        py = point[1]
        sum = 0
        length = len(poly)-1
    
        for index in range(0,length):
            sx = poly[index][0]
            sy = poly[index][1]
            tx = poly[index+1][0]
            ty = poly[index+1][1]
    
            #点与多边形顶点重合或在多边形的边上
            if((sx - px) * (px - tx) >= 0 and (sy - py) * (py - ty) >= 0 and (px - sx) * (ty - sy) == (py - sy) * (tx - sx)):
                return "on"
            #点与相邻顶点连线的夹角
            angle = math.atan2(sy - py, sx - px) - math.atan2(ty - py, tx - px)
    
            #确保夹角不超出取值范围(-π 到 π)
            if(angle >= math.pi):
                angle = angle - math.pi * 2
            elif(angle <= -math.pi):
                angle = angle + math.pi * 2
            sum += angle
    
            #计算回转数并判断点和多边形的几何关系
        result = 'out' if int(sum / math.pi) == 0 else 'in'
        return result
    
    point = [115.259161584,38.813623816]
    print(windingNumber(point,lnglatlist))

     

    5、展示单个围栏

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import matplotlib.pyplot as plt
    import numpy as np
    import json
    
    '''
    with open("railwayFence.json", 'r') as f:
        data = json.loads(f.read())
        data = data['railwayFence']
        fo = open("tmp", "w")
        for i in range(0,len(data)):
            for point in data[i]['coordinates']:
                d = '%(x).9f %(y).9f\n'%{'x':float(str(point['B'])),'y':float(str(point['L']))}
                fo.write( d ) 
        fo.close()
    '''
    
    data = np.loadtxt('65.txt')
    plt.plot(data[:,0],data[:,1])
    
    #plt.annotate('test point', xy=(38.82775139,115.250658818),arrowprops=dict(facecolor='red', shrink=0))
    #plt.annotate('test point', xy=(38.823400546,115.25345992),arrowprops=dict(facecolor='red', shrink=0))
    plt.annotate('test point', xy=(38.813623816,115.259161584),arrowprops=dict(facecolor='red', shrink=0))
    plt.show()

     

    6、用c语言解析json也行

    main.c

    /*
            reference documentation
            https://blog.csdn.net/stsahana/article/details/79638992
            https://blog.csdn.net/fengxinlinux/article/details/53121287
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <string.h>
    #include "cJSON.h"
    
    char *json_file_path = "./railwayFence.json";
    
    char *json_loader(char *path)
    {
      FILE *f;
      long len;
      char *content;
      f=fopen(path,"rb");
      fseek(f,0,SEEK_END);
      len=ftell(f);
      fseek(f,0,SEEK_SET);
      content=(char*)malloc(len+1);
      fread(content,1,len,f);
      fclose(f);
      return content;
    }
    
    int main(void)
    {
        FILE *fp1;
        fp1=fopen("test.txt","w+");
    
        char *json_str = json_loader(json_file_path);
    
            cJSON *root=cJSON_Parse(json_str);
            if (!root) {
                printf("Error before: [%s]\n",cJSON_GetErrorPtr());
            }
    
            //railwayFence array
            cJSON *railwayFenceArray = cJSON_GetObjectItem(root, "railwayFence");  
            if(!railwayFenceArray){  
                printf("Error before: [%s]\n",cJSON_GetErrorPtr()); 
            }
            int railwayFence_array_size = cJSON_GetArraySize(railwayFenceArray);  
            printf("railwayFence array size is %d\n",railwayFence_array_size);
            int i = 0;
            char *p  = NULL; 
            cJSON *it; 
            for(i=0; i< railwayFence_array_size; i++) {  
                    FILE *fp;
                    char  buffer[32];
                    sprintf( buffer, "./data/%d.txt", i );
                    fp=fopen(buffer,"w+");
                cJSON *railwayFenceItem = cJSON_GetArrayItem(railwayFenceArray,i);
                p = cJSON_PrintUnformatted(railwayFenceItem);  
                it = cJSON_Parse(p); 
                if(!it)  
                continue ;
    
                cJSON *areaName,*dangerType;
                areaName = cJSON_GetObjectItem(it, "areaName");  
                printf("areaName is %s\n",areaName->valuestring);  
                dangerType = cJSON_GetObjectItem(it, "dangerType");  
                printf("dangerType is %s\n",dangerType->valuestring);
    
                //Coordinate array
                cJSON *CoordinateArray = cJSON_GetObjectItem(railwayFenceItem, "coordinates");  
                if(!CoordinateArray){  
                    printf("Error before: [%s]\n",cJSON_GetErrorPtr()); 
                }
                int Coordinate_array_size = cJSON_GetArraySize(CoordinateArray);  
                printf("Coordinate array size is %d\n",Coordinate_array_size);
                int j = 0;
                char *q  = NULL; 
                cJSON *jt;  
                for(j=0; j< Coordinate_array_size; j++) {
                    cJSON *CoordinateItem = cJSON_GetArrayItem(CoordinateArray,j);
                    q = cJSON_PrintUnformatted(CoordinateItem);  
                    jt = cJSON_Parse(q); 
                    if(!jt)  
                        continue ;
    
                    cJSON *B,*L;
                    B = cJSON_GetObjectItem(jt, "B");  
                    printf("B is %f\n",B->valuedouble);  
                    L = cJSON_GetObjectItem(jt, "L");  
                    printf("L is %f\n",L->valuedouble);
                    fprintf(fp1,"%f %f\n",B->valuedouble,L->valuedouble);
                    fprintf(fp,"%f %f\n",B->valuedouble,L->valuedouble);
                
                    free(q); cJSON_Delete(jt);                
                }
                free(p); cJSON_Delete(it);
            }
            if(root) { 
                cJSON_Delete(root);  
                    //return 0;  
            }
    
      return 0;
    }

     

    Makefile

    复制代码
    OBJ= main 
    all: ${OBJ}
    main:
        gcc -g -o main main.c cJSON.c -lm
    
    clean:
        rm -f ${OBJ}
    .PHONY: ${OBJ}
    复制代码

     

    7、nodejs解析json文件更简单

    main.js

    复制代码
    var fs = require("fs");
    var contents = fs.readFileSync("railwayFence.json");
    var obj = JSON.parse(contents); //console.log("<<<<<<<<<<<<<<<<<<<<"+JSON.stringify(obj)); for(var i in obj.railwayFence){ console.log('>>>>>>>>>>>>>>>>>>>>>>>>put areaName: ' + obj.railwayFence[i].areaName) console.log('>>>>>>>>>>>>>>>>>>>>>>>>put dangerTypeName: ' + obj.railwayFence[i].danggerTypeName) for(var j in obj.railwayFence[i].coordinates){ console.log('>>>>>>>>>>>>>>>>>>>>>>>>put B: ' + obj.railwayFence[i].coordinates[j].B) console.log('>>>>>>>>>>>>>>>>>>>>>>>>put L: ' + obj.railwayFence[i].coordinates[j].L) } }
    复制代码

     

    8、同事找的c算法,记录一下

    /**
     * 功能:判断点是否在多边形内
     * 方法:求解通过该点的水平线(射线)与多边形各边的交点
     * 结论:单边交点为奇数,成立!
     * 参数:p 指定的某个点
             ptPolygon 多边形的各个顶点坐标(首末点可以不一致) 
             nCount 多边形定点的个数
     * 说明:
     */
    // 注意:在有些情况下x值会计算错误,可把double类型改为long类型即可解决。
    int PtInPolygon(Point_t* p, Point_t* ptPolygon, int nCount) 
    { 
        int nCross = 0, i;
        double x;
        Point_t p1, p2;
        
        for (i = 0; i < nCount; i++) 
        { 
            p1 = ptPolygon[i]; 
            p2 = ptPolygon[(i + 1) % nCount];
            // 求解 y=p->y 与 p1p2 的交点
            if ( p1.y == p2.y ) // p1p2 与 y=p->y平行 
                continue;
            if ( p->y < min(p1.y, p2.y) ) // 交点在p1p2延长线上 
                continue; 
            if ( p->y >= max(p1.y, p2.y) ) // 交点在p1p2延长线上 
                continue;
            // 求交点的 X 坐标 -------------------------------------------------------------- 
            x = (double)(p->y - p1.y) * (double)(p2.x - p1.x) / (double)(p2.y - p1.y) + p1.x;
            if ( x > p->x ) 
            {
                nCross++; // 只统计单边交点 
            }
        }
        // 单边交点为偶数,点在多边形之外 --- 
        return (nCross % 2 == 1); 
    }
    
    /*******************************************************************************
     * 名称: pointInPolygon
     * 功能: 当前平面坐标点是否在区域面内
     * 形参: point:需判断点
     *      points:区域面点集合
     *      count:区域面点个数
     * 返回: 判断结果 1在区域内 0在区域外
     * 说明: 无
     ******************************************************************************/
    int pointInPolygon(Point_t *point, Point_t *points,int count) 
    {
     int   i,j=count-1 ;
     int  oddNodes = 0 ;
     double  x,y;  
    
     if(point == NULL || points == NULL)
      return -1;
    
     x = point->x, y = point->y; 
     for (i=0;i<count; i++)
     {
      if((points[i].y< y && points[j].y>=y
       || points[j].y<y && points[i].y>=y)
       && (points[i].x<=x || points[j].x<=x)) 
      {
       oddNodes^=(points[i].x+(y-points[i].y)/(points[j].y-points[i].y)*(points[j].x-points[i].x)<x);
      }
      j=i;
     }
    
      return oddNodes;
    }

     

    怎么处理都行,怎么方便怎么用。

     

    end

     

    转载于:https://www.cnblogs.com/dong1/p/10220116.html

    展开全文
  • 百度地图历史轨迹绘制,动画播放,暂停、加速、减速,车头实时角度偏移,HTML页面点开即可使用
  • 电子围栏系统技术方案讲解.pdf
  • 行业资料-电子功用-移动目标无线定位电子围栏系统及其操作方法.pdf
  • 脉冲电子围栏是传统的普通围墙与报警系统的完美结合,在具有普通围墙的阻挡作用的基础上,增加报警功能,误报率极低,同时又具有威慑入侵者的作用,因此,脉冲电子围栏系统弥补了传统周界防护报警系统的弱点,具有...
  • Core Location 电子围栏:入门

    千次阅读 2018-08-14 13:14:56
    原文:Geofencing with Core Location: Getting Started 作者:Andy Pereira ...Geofencing 会在设备进入/离开指定的电子围栏时通知应用程序。它可以让你写出一些很酷的应用程序,当你从家里出来时...

    原文:Geofencing with Core Location: Getting Started
    作者:Andy Pereira
    译者:kmyhy

    更新说明:Andy Pereira 将本教程升级至 Xcode 9.3 和 Swift 4.1。

    Geofencing 会在设备进入/离开指定的电子围栏时通知应用程序。它可以让你写出一些很酷的应用程序,当你从家里出来时触发通知,或者在附近出现最爱的商店时,用最近的、最多的订单提示给用户。在这个 Geofencing 教程中,你将学习如何在 iOS 和 swift 中使用区域检测——即 Core Location 的 Region Monitoring API。

    另外,你将创建一个基于定位提醒的 app,Geotify,它允许用户基于物理位置创建提醒。让我们开始吧!

    开始

    使用底部的 Dowload Materials 按钮下载开始项目。它包含了一个用于在地图上添加/删除大头钉简单 UI。每个大头钉表示一个指定位置的提醒项,我把它叫做 geotification。

    Build & run,你会看到一张空空的地图。

    点击 + 按钮,添加一个 geotification。app 会另外显示一张视图,允许你设置 geotification 的各种属性。

    在本教程中,你将在苹果的新总部卡布基诺添加一个大头钉。如果不知道位置,打开谷歌地图,用它找到正确的位置。要让大头钉定位精确,请放大地图。

    注:要在模拟器上使用 pinch 手势,请按下 option 键,然后用 shift 键移动手指的中心点,然后放开 shift 键,拖动鼠标进行捏放。

    Radius 表示从指定位置开始距离多少米,在这个位置上 iOS 将触发通知。该通知可以是您希望在通知中显示的任何消息。这个 app 还可以让用户通过顶部的 segment control 来指定在圆圈内是用进入还是退出来触发通知。

    在 Radius 上输入 1000,在 Note 上输入 Say Hi to Tim!,把第一个电子围栏通知置于 Upon Entry 上。

    填完后点击 Add 按钮。你会看到地图上显示了一个新的大头钉,外面还围了一个圆圈表示的电子围栏:

    点击大头钉,你会看到通知详情,比如提醒内容和事件类型。不要点击那个小叉叉,除非你想删除它!

    您可以随意添加或删除任意多的地理位置。由于该应用程序使用 UserDefaults 进行持久化,所以在重启后的位置列表仍然会存在。

    配置 Core Location 和权限

    现在,你添加到地图上的电子围栏通知还没有真正的功能,只能看看而已。要解决这个问题,你需要遍历每个位置,使用 Core Location 监听它的保护范围。

    在开始监控电子围栏之前,你需要创建 CLLocationManager 实例并请求相应的权限。

    打开 GeotificationsViewController.swift 声明一个 CLLocationManager 常量。可以将它放在 var geotifications: [Geotification] = []: 一句之后。

    let locationManager = CLLocationManager()
    

    然后修改 viewDidLoad() 代码:

    override func viewDidLoad() {
      super.viewDidLoad()
      // 1
      locationManager.delegate = self
      // 2
      locationManager.requestAlwaysAuthorization()
      // 3
      loadAllGeotifications()
    }

    代码分为 3 步:

    1. 将 view controller 设置为 locationManager 的 delegate,这样 view controller 就会通过代理方法接收到通知。
    2. 调用 requestAlwaysAuthorization() 方法,它会显示一个对话框,向用户请求总是使用定位服务的全新。地理围栏 app 必须请求一直使用定位服务的权限,因为哪怕是 app 不运行的期间也要监听电子围栏。Info.plist 的 NSLocationAlwaysAndWhenInUseUsageDescription 键中包含了要向用户显示的信息。从 iOS 11 开始,所有请求一直允许权限的 app 同时也要允许用户选择“仅在应用期间使用”。Info.plist also 的 NSLocationWhenInUseUsageDescription 键用于指定它的信息。重要的是尽可能简单地向用户说明为什么要使用“一直允许”权限。
    3. 调用 loadAllGeotifications() 方法,读取之前保存到 UserDefaults 中的 geotification 列表并加载到 geotifications 数组中。这个方法还会将 geotification 加载到地图上的大头钉中。

    当应用程序提示用户进行授权时,它将显示 NSLocationAlwaysAndWhenInUseUsageDescription 字符串,它向用户进行了很好的解释,说明为什么该应用需要访问用户的位置。当你请求授权时,这个 key 是必需的。如果缺失这个 key,系统会忽略请求,并禁用定位服务。

    Build & run,你会看到:

    你已经完成了 app 的请求授权许可。好了,请点击允许,确保 location manager 能够在适当的时机收到委托回调。

    在继续下一步之前,还有一个小问题:用户当前的位置没有显示在地图图上!默认情况下,map view 会禁用这个特性,同时,在导航栏左上角的 zoom 按钮也不会起作用。

    幸好这个问题很容易解决——你只需要在用户同意授权之后开启 current location属性。

    在 GeotificationsViewController.swift 的CLLocationManagerDelegate 扩展中添加委托方法:

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
      mapView.showsUserLocation = (status == .authorizedAlways)
    }

    location manager 在授权状态发生改变时调用 locationManager(_:didChangeAuthorizationStatus:) 方法,如果用户同意授权,location manager 会在你初始化了 location mananger 并设置其 delegate 之后调用这个方法。

    因此这个方法是检查 app 是否被授权的理想场所。如果用户已授权,你就可以启用 mapview 的 showsUserLocation。

    Build & run。如果你在真机上运行,你会看到 mapview 上显示出大头钉。如果在模拟器运行,请点击 Debug ▸ Location ▸ Apple 菜单,就可以看见定位大头钉了:

    另外,现在导航栏上的 zoom 按钮也可以使用了。

    注册电子围栏

    配置好 location manager 之后,你现在必须让 app 注册用户要监控的电子围栏范围。

    用户的电子围栏信息保存在 Geotification 模型中。但是,要对电子围栏进行监控,Core Location 需要你将它们表示为 CLCircularRegion 对象。要满足这个条件,你需要创建一个助手方法,将一个 Geonotification 映射为 CLCircularRegion。

    打开 GeotificationsViewController.swift 添加方法:

    func region(with geotification: Geotification) -> CLCircularRegion {
      // 1
      let region = CLCircularRegion(center: geotification.coordinate, 
        radius: geotification.radius, 
        identifier: geotification.identifier)
      // 2
      region.notifyOnEntry = (geotification.eventType == .onEntry)
      region.notifyOnExit = !region.notifyOnEntry
      return region
    }

    这个方法做了这些事情:

    1. 以电子围栏的中心坐标、半径、以及识别 app 中已注册的不同电子围栏的 ID 来初始化一个 CLCircularRegion。初始化过程很简单,因为在 Geotification 中已经包含了这些信息。
    2. CLCircularRegion 有两个 boolean 属性: notifyOnEntry 和 notifyOnExit。它们代表在设备进入/离开围栏时所对应不同的事件。因为 app 只允许一个围栏监控一种类型的事件,你需要针对 Geotificaiton 对象的 eventType 值来设置这两个值。

    接着,当用户添加了 geotification 之后,需要用一个方法来启动对指定 geotification 的监控。

    在 GeotificationsViewController 中加入方法:

    func startMonitoring(geotification: Geotification) {
      // 1
      if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
        showAlert(withTitle:"Error", message: "Geofencing is not supported on this device!")
        return
      }
      // 2
      if CLLocationManager.authorizationStatus() != .authorizedAlways {
        let message = """
          Your geotification is saved but will only be activated once you grant 
          Geotify permission to access the device location.
          """
        showAlert(withTitle:"Warning", message: message)
      }
      // 3
      let fenceRegion = region(with: geotification)
      // 4
      locationManager.startMonitoring(for: fenceRegion)
    }

    上述代码的执行步骤简单说明如下:

    1. isMonitoringAvailableForClass(_:) 方法判断设备是否支持电子围栏监控功能。如果不支持,退出并提示用户。showAlert() 是 Utilities.swift 中的一个助手方法,需要一个显示时用到的 title 参数和一个 message 参数。
    2. 然后,判断用户是否授权 app 使用定位服务。如果未授权,app 不会接收任何电子围栏通知。但是,你仍然需要用户能够保存围栏信息,因为用 Core Location 注册电子围栏不需要授权。当用户后面对 app 进行授权后,电子围栏监控会自动开启。
    3. 用工具方法为指定的 geotification 创建 CLCircularRegion 对象。
    4. 然后,注册该 CLCircularRegion,让 Core Location 的 location manager 开始监控。

    写完这个方法,你需要另一个方法,当用户从 app 中删除 geotification 时停止监控它。

    在 GeotificationsViewController.swift,在 startMonitoring(geotificiation:) 方法下添加:

    func stopMonitoring(geotification: Geotification) {
      for region in locationManager.monitoredRegions {
        guard let circularRegion = region as? CLCircularRegion, 
          circularRegion.identifier == geotification.identifier else { continue }
        locationManager.stopMonitoring(for: circularRegion)
      }
    }

    这个方法简单停止 locationManager 对指定 geotification 的监控。

    开始和停止方法完成后,你可以用在添加、删除 geotification 时调用它们了。首先是添加部分。

    首先打开 GeotificationsViewController.swift,找到 addGeotificationViewController(_:didAddCoordinate:radius:identifier:note:eventType:) 方法。

    addGeotificationViewController 在创建 geotification时调用这个委托方法。它负责创建一个新的 Geotification对 象并刷新地图和 geotifications 数组。最后,它会调用 saveAllGeotifications(),这个个方法用新的 geotifications 数组作为参数,将它存到 UserDefaults 中。

    现在,修改 addGeotificationViewController(_:didAddCoordinate:radius:identifier:note:eventType:) 方法为:

    func addGeotificationViewController(
      _ controller: AddGeotificationViewController, didAddCoordinate coordinate: CLLocationCoordinate2D, 
      radius: Double, identifier: String, note: String, eventType: Geotification.EventType
    ) {
      controller.dismiss(animated: true, completion: nil)
      // 1
      let clampedRadius = min(radius, locationManager.maximumRegionMonitoringDistance)
      let geotification = Geotification(coordinate: coordinate, radius: clampedRadius, 
        identifier: identifier, note: note, eventType: eventType)
      add(geotification)
      // 2
      startMonitoring(geotification: geotification)
      saveAllGeotifications()
    }

    主要修改有两处:

    1. 防止 radius 的值不大于 locationManager 的 maximumRegionMonitoringDistance 属性,这个属性是以米为单位的电子围栏最大半径。这是重要的,因为如果超过了这个最大值,会导致监控失败。
    2. 调用 startMonitoring(geotification:) 以向 Core Location 注册新添加的 geotification。

    这样,app 就能够注册电子围栏的监控了。但是有一个限制:因为电子围栏是系统共享资源,Core Location 限制每个 app 的最大监控电子围栏数是 20。

    出于本教程的目的,对这个限制的解决办法(在最后面“接下来去哪儿”会有一些讨论),是限制用户能够添加 geotification 的数目。

    在 updateGeotificationCount() 最后添加代码:

    navigationItem.rightBarButtonItem?.isEnabled = (geotifications.count < 20)

    这行代码在添加书达到上限时禁用 Add 按钮。

    最后,是删除 geotification。这个功能是在mapView(_:annotationView:calloutAccessoryControlTapped:) 中进行的,当用户点击大头钉上的 delete 按钮会调用这个方法。

    在 mapView(_:annotationView:calloutAccessoryControlTapped:)的 remove(geotification) 前面添加一句:

    stopMonitoring(geotification: geotification)
    

    这会停止电子围栏监控,然后删除 geotification,保存修改到 UserDefaults。

    这样,你的 app 已经能够监控和停止监控用户的电子围栏了。太好了!

    Build & run。你发现没有任何改变,但 app 已经能够注册监控区域了。但是,它还不能响应电子围栏事件。别急——这是接下来的工作!

    响应电子围栏事件

    需要实现几个关于错误处理的委托方法。这会用在万一有错误出现的时候。

    在 GeotificationsViewController.swift, 在 CLLocationManagerDelegate 扩展中添加:

    func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, 
                         withError error: Error) {
      print("Monitoring failed for region with identifier: \(region!.identifier)")
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
      print("Location Manager failed with the following error: \(error)")
    }

    这些委托方法简单地输出错误信息以便调试。

    注:在你的生产项目中,你肯定想在错误处理时更多做些事情。例如,你不想安静地输出日志,而想通知用户发生了什么。

    然后,打开 AppDelegate.swift,你将在这里监听并处理电子围栏的进入/退出事件。

    在头部 import Core Location 框架:

    import CoreLocation
    

    在 var window: UIWindow? 下添加新属性:

    let locationManager = CLLocationManager()
    

    将 application(_:didFinishLaunchingWithOptions:) 修改为:

    func application(
      _ application: UIApplication, 
      didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil
    ) -> Bool {
      locationManager.delegate = self
      locationManager.requestAlwaysAuthorization()
      return true
    }

    你让 AppDelegate 接收电子围栏事件。先不管 Xcode 在这里提示的错误警告,等会我们会解决它。你可能奇怪,“为什么要在 AppDelegate 而不是 view controller 中干这件事呢?”

    iOS 会无时不刻地监控 app 注册的电子围栏, 哪怕 app 没有运行了。如果设备在 app 不在运行的情况下触发电子围栏事件,iOS 会自动打开后台中的 app。因此 AppDelegate 是处理这类事件的理想场所,因为 view controller 可能并没有加载或者就绪。

    你还可能奇怪,“为什么刚刚才创建的 CLLocationMananger 就知道要监控什么电子围栏呢?”

    你的 app 所注册的一切电子围栏都能被 app 中所有的 location mananger 访问,因此无论你在哪里初始化 location mananger 都无所谓。很爽吧?:]

    接下来就是实现电子围栏事件的相关委托方法了。在此之前,需要创建一个处理电子围栏事件的方法。

    在 AppDelegate.swift 添加方法:

    func handleEvent(for region: CLRegion!) {
      print("Geofence triggered!")
    }

    这个方法有一个 CLRegion 参数,它只是简单地打印一个信息。别急——后面你会实现事件的处理。

    然后,在 AppDelegate 的最后添加一个扩展:

    extension AppDelegate: CLLocationManagerDelegate {
      func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        if region is CLCircularRegion {
          handleEvent(for: region)
        }
      }
    
      func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        if region is CLCircularRegion {
          handleEvent(for: region)
        }
      }
    }

    正如方法名所示,当设备进入某个区域时调用 locationManager(_:didEnterRegion:) 方法,当设备退出某个区域时调用 locationManager(_:didExitRegion:) 方法。

    两个方法都会收到一个 CLRegion 参数。你需要判断它是不是 CLCircularRegion,因为如果 app 监控的是 iBeacon 时它有可能是一个 CLBeaconReion。如果 region 就是一个 CLCircularRegion,再调用 handleEvent(for:)。

    注:iOS 在判断到有穿过边界的行为时触发电子围栏事件。如果用户已经位于某个区域内部,iOS 不产生事件。如果你需要知道设备是否位于指定区域以内或者以外,你需要使用苹果提供的 requestStateForRegion(_:) 方法。

    现在 app 已经能够监控电子围栏事件了,你需要来测试一下。无论它能不能挑动你的神经,这都是一件值得兴奋的事情,因为在本教程中,你终于能够看到点结果了。

    最真实的测试方式是在设备上运行你的 app,添加几个 geotification 然后拿着手机到处走或者开车溜溜。但是,这并不明智,因为你无法在设备不插线的情况下,查看电子围栏事件触发的打印日志。此外,在你提交测试之前,确保这个 app 工作正常是一个好的做法。

    幸好,有一个简单的法子,不需要你离开你温暖的小窝。Xcode 允许你在项目中使用 GPX 文件模拟测试位置。在开始项目中已经包含了一个!

    打开 Supporting Files 组下的 SimulatedLocations.gpx,你会看到:

    <?xml version="1.0"?>
    <gpx version="1.1" creator="Xcode">
      <wpt lat="37.3349285" lon="-122.011033">
        <name>Apple</name>
        <time>2014-09-24T14:00:00Z</time>
      </wpt>
      <wpt lat="37.422" lon="-122.084058">
        <name>Google</name>
        <time>2014-09-24T14:00:05Z</time>
      </wpt>
    </gpx>

    这其实是一个 XML 文件,包含了两个路径点(wpt,waypoint):Google 的山景城和丘珀蒂诺的苹果公园。你会看到每个路径点有一个 time 节点。它们之间相差有 5 秒,因此用这个文件模拟位置时,它会花 5 秒钟从苹果走到谷歌。还有两个 gpx 文件:Apple.gpx 和 Google.gpx。这是固定位置,你可以它们创建电子围栏。

    要模拟 GPX 文件中的位置,请 Build & run。当 app 进入主视图控制器后,回到 Xcode,选择调试工具栏的 Location 图标,然后选择 SimulatedLocations:

    沿着两个路径点之间的路径添加几个 geotification。如果在注册电子围栏之前添加过 geotification,那么这些 geotification 不会有效,你可以删除它们重新开始。

    关于测试位置,一种好的做法是在每个路径点上防止一个 geotification。这是可能的一种测试场景:

    Google: Radius: 1000m, Message: "Say Bye to Google!", Notify on Exit
    Apple: Radius: 1000m, Message: "Say Hi to Apple!", Notify on Entry

    注:为了让添加位置的时候更容易,你可以用额外提供的测试地点。

    一旦添加了 geotification,你会在进入/离开电子围栏时看到控制台打印消息。如果你按下 Home 键或者锁屏,让 app 转入后台,每当你穿过电子围栏时仍然能看到打印消息,尽管你无法虚拟地验证这种行为。

    注:模拟位置既可以在模拟器也可以在设备上运行。但是,模拟器不是很精确,无论进入还是退出电子围栏,地触发对应的事件的时机不是很一致。在设备上要好得多,或者更流畅,拿起手机来起来逛逛!

    通知用户电子围栏事件

    app 已经完成大半。这里,当设备穿过电子围栏时通知用户的工作就留给你了,因此请自行完成这个功能。

    要通过委托回调获取和 CLCircularRegion 相关的 note 描述字串,你必须从 UserDefaults 中检索对应的 geotification。这也不足为道,因你您可以使用注册时分配给 CLCircularRegion 的唯一 ID 来找到正确的 geotification。

    在 AppDelegate.swift 添加一句导入语句:

    import UserNotifications
    

    接着,添加一个工具方法:

    func note(from identifier: String) -> String? {
      let geotifications = Geotification.allGeotifications()
      guard let matched = geotifications.filter {
        $0.identifier == identifier
      }
      .first else { return nil }
      return matched.note
    }

    这个工具方法根据 ID 从持久存储中查找 geotification 的 note,然后返回。

    现在你已经能返回电子围栏的 note 了,可以编写当电子围栏事件触发的代码并将 note 用于提示的消息。

    在 application(_:didFinishLaunchingWithOptions:) 方法 returns 之前添加:

    let options: UNAuthorizationOptions = [.badge, .sound, .alert]
    UNUserNotificationCenter.current()
      .requestAuthorization(options: options) { success, error in
      if let error = error {
        print("Error: \(error)")
      }
    }

    最后,添加这个方法:

    func applicationDidBecomeActive(_ application: UIApplication) {
      application.applicationIconBadgeNumber = 0
      UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
      UNUserNotificationCenter.current().removeAllDeliveredNotifications()
    }

    这段代码提示用户打开远程通知。此外,还清空了已有的通知。

    然后,修改 handleEvent(for:) 方法为:

    func handleEvent(for region: CLRegion!) {
      // 如果 app 是 active 的,显示一个 alert
      if UIApplication.shared.applicationState == .active {
        guard let message = note(from: region.identifier) else { return }
        window?.rootViewController?.showAlert(withTitle: nil, message: message)
      } else {
        // 否则显示本地通知
        guard let body = note(from: region.identifier) else { return }
        let notificationContent = UNMutableNotificationContent()
        notificationContent.body = body
        notificationContent.sound = UNNotificationSound.default()
        notificationContent.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let request = UNNotificationRequest(identifier: "location_change",
                                            content: notificationContent,
                                            trigger: trigger)
        UNUserNotificationCenter.current().add(request) { error in
          if let error = error {
            print("Error: \(error)")
          }
        }
      }
    }

    如果 app 是激活(active)的,上面的代码会用 alert controller 方式显示 note。否则,显示本地为通知。

    Build & run,重复上一节所述的测试过程。当电子围栏事件触发,你会看到 alert 显示出 note 上的备注:

    按 Home 键或者锁屏,将 app 切换至后台。你仍然会继续收到电子围栏事件的通知。

    这样,你就拥有一个完整的、基于定位的提醒 app 了。好,请离开座位,把你的 app 带出去 show 一下吧!

    注:在测试 app 时,可能会发现通知发出的时间并不是刚好在你穿过边界的时候。

    这是因为 iOS 在判断是否穿过边界之前,有一个缓冲距离,和设备必须在新位置停留的最短时间。iOS 在内部定义了这些阈值,目的是减少用户在极度接近电子围栏边界的情况下发出的错误通知。

    此外,这些阈值好像受定位硬件性能的限制。从经验上讲,当在设备上启用 Wi-Fi 时,电子围栏会更加精确。

    接下来去哪里?

    恭喜你!你已经可以编写你自己的电子围栏 app 了!

    你可以从页尾的 Download Materials 按钮处下载完成后的项目。

    电子围栏是一种很有用的技术,在市场营销、资源管理、安全、家长控制、甚至游戏等领域都有许多实用而深远的应用,你能达到什么样的目标完全取决于你的想象力。更多信息可以阅读苹果的区域监控

    我希望你喜欢这篇教程。欢迎在下面留言或提问。

    Download Materials

    展开全文
  • 电子围栏的实现原理

    千次阅读 2021-07-29 18:31:47
    整理下电子围栏的算法. 先看下经纬度的含义(^_^),与地球的关系 经线(英文:longitude,简写long): 连接南北极的圆 纬线(英文:latitude,简写lat): 以赤道为中心的圆 ...

    序言

    整理下电子围栏的算法.

    先看下经纬度的含义(^_^),与地球的关联.这个会在算法中有影响,你在考虑算法的过程中应该会联想到相关的问题.cuiyaonan2000@163.com

    1. 经线(英文:longitude,简写long): 连接南北极的圆
    2. 纬线(英文:latitude,简写lat): 以赤道为中心的圆

    另外关于地理坐标的值(算法会考虑这些):

    • 经度:东经为正数,西经为负数。
    • 纬度:北纬为正数,南纬为负数。

    规则的多边形

    这种的处理比较简单具体思路如下.

    在坐标系中需要计算在规则多边形中T点是是否在范围内只需要满足如下的2点需求(这里算法需要考虑坐标横跨北纬南纬以及东经西经的问题cuiyaonan2000@163.com):

    1. C-lat <= T-lat <= B-lat
    2. C-Long <= T-Long <= B-Long

    如上的是以BC为对角线的判断,当然我们可以同时增加AD的对角线的判断,来使结果更加正确. 

    规则的圆形

    圆形电子围栏的实现有种简单的方式,就是根据半径来计算目标点与中心原点的直径是否超过了它的半径.这里介绍的是稍微复杂的一种算法.

    算法太复杂研究中

    不规则的区域

    不规则计算规则也是比较简单的.

    如下图所示:红色的点是目标点. 以红色的点为中心点画一条垂直于Longitude的线.

    如果该红色的点左右两侧的交点数量是奇数,则表示该红色的点在电子围栏之内.

    如果该红色的点左右两侧的交点数量是偶数,则表示该红色的点不在电子围栏之内.

    其实如上的规则利用了现实的情况,我们的电子围栏边界都是直线,且都是一个封闭的区域.必然会产生奇数或者偶数的交点cuiyaonan2000@163.com

     交点的生成规则

    结合上图所示.我们需要知道 交点只能跟一条边线产生.

    如下图所示最右边有一个交点 正好在A边线和B边线的合并处.

    该交点属于B边线,因为(同时也是交点产生的必要条件cuiyaonan2000@163.com):

    • 边线必须有一端在交点的上方,可以覆盖
    • 边线必须有一端在交点的下方,不可以覆盖,即交点不能跟边线低点重合

     

     更复杂的一个情况

    可以看到如下图所示: 横线直接与B边线重合

    但是这里只有C边线有交点,因为只有C边线满足如上的规则,

    因为:有一点在水平线上有一点在点在水平线下.

    展开全文
  • java电子围栏

    2021-11-08 09:49:43
    java版的电子围栏违规判断,经过测试,准确率接近百分之百,偏差在几米范围以内, 本工具类支持圆形,矩形,多边形,采用射线穿透算法,性能与准确率都有保障,实测判断多边形围栏,单线程执行百万次只需要十几秒,...

    java版的电子围栏违规判断,经过测试,准确率接近百分之百,偏差在几米范围以内,

    本工具类支持圆形,矩形,多边形,采用射线穿透算法,性能与准确率都有保障,实测判断多边形围栏,单线程执行百万次只需要十几秒,多线程只需要2秒

    废话不说,直接上代码,先贴工具类代码

    import com.core.entity.Point;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @描述: 电子围栏计算
     * @公司:
     * @作者: 高斯林
     * @版本: 1.0.0
     * @日期: 2021-11-08 09:44:54
     */
    public class GeofencingUtils {
    
        /**
         * 地球半径(米)
         */
        private static final double EARTH_RADIUS = 6378137.0;
    
    
        private static double rad(double d) {
            return d * Math.PI / 180.0;
        }
    
        /**
         * 计算是否在圆内
         * @param radius 半径(单位/米)
         * @param p1 圆心坐标
         * @param p2 判断点坐标
         * @return: boolean true:在圆内,false:在圆外
         * @date: 2021-11-08 09:44:54
         */
        public static boolean isInCircle(double radius, Point p1, Point p2) {
            double radLat1 = rad(p1.getLat());
            double radLat2 = rad(p2.getLat());
            double a = radLat1 - radLat2;
            double b = rad(p1.getLng()) - rad(p2.getLng());
            double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                    Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
            s = s * EARTH_RADIUS;
            s = Math.round(s * 10000) / 10000;
            return !(s > radius);
        }
    
        /**
         * 是否在矩形区域内
         *  @param lng    测试点经度
         * @param lat    测试点纬度
         * @param minLng 矩形四个点中最小经度
         * @param maxLng 矩形四个点中最大经度
         * @param minLat 矩形四个点中最小纬度
         * @param maxLat 矩形四个点中最大纬度
         * @return boolean true:在矩形内, false:在矩形外
         * @Title: isInArea
         */
        public static boolean isInRectangleArea(double lng, double lat,  double minLng, double maxLng,
                                                double minLat, double maxLat) {
            if (isInRange(lat, minLat, maxLat)) {//如果在纬度的范围内
                if (minLng * maxLng > 0) {
                    return isInRange(lng, minLng, maxLng);
                } else {
                    if (Math.abs(minLng) + Math.abs(maxLng) < 180) {
                        return isInRange(lng, minLng, maxLng);
                    } else {
                        double left = Math.max(minLng, maxLng);
                        double right = Math.min(minLng, maxLng);
                        return isInRange(lng, left, 180) || isInRange(lng, right, -180);
                    }
                }
            } else {
                return false;
            }
        }
    
        /**
         * 是否在矩形区域内
         * @param point    测试点
         * @param gpsPoints 矩形GPS四个坐标点
         * @return boolean true:在矩形内, false:在矩形外
         * @Title: isInArea
         */
        public static boolean isInRectangleArea(Point point, Point[] gpsPoints) {
            if (gpsPoints.length != 4) {
                return false;
            }
            double[] lats = new double[4];
            double[] lngs = new double[4];
            for (int i = 0; i < gpsPoints.length; i++) {
                lats[i] = gpsPoints[i].getLat();
                lngs[i] = gpsPoints[i].getLng();
            }
            Arrays.sort(lats);
            Arrays.sort(lngs);
            return isInRectangleArea(point.getLat(), point.getLng(), lats[0], lats[3], lngs[0], lngs[3]);
        }
    
    
        /**
         * 判断是否在经纬度范围内
         * @param point
         * @param left
         * @param right
         * @return boolean
         */
        public static boolean isInRange(double point, double left, double right) {
            return point >= Math.min(left, right) && point <= Math.max(left, right);
        }
    
        /**
         * 判断点是否在多边形内
         * @param point 测试点
         * @param pts   多边形的点
         * @return boolean true:在多边形内, false:在多边形外
         * @throws
         * @Title: IsPointInPoly
         */
        public static boolean isInPolygon(Point point, List<Point> pts) {
    
            int N = pts.size();
            boolean boundOrVertex = true;
            int intersectCount = 0;//交叉点数量
            double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
            Point p1, p2;//临近顶点
            Point p = point; //当前点
    
            p1 = pts.get(0);
            for (int i = 1; i <= N; ++i) {
                if (p.equals(p1)) {
                    return boundOrVertex;
                }
    
                p2 = pts.get(i % N);
                if (p.getLng() < Math.min(p1.getLng(), p2.getLng()) || p.getLng() > Math.max(p1.getLng(), p2.getLng())) {
                    p1 = p2;
                    continue;
                }
    
                //射线穿过算法
                if (p.getLng() > Math.min(p1.getLng(), p2.getLng()) && p.getLng() < Math.max(p1.getLng(), p2.getLng())) {
                    if (p.getLat() <= Math.max(p1.getLat(), p2.getLat())) {
                        if (p1.getLng() == p2.getLng() && p.getLat() >= Math.min(p1.getLat(), p2.getLat())) {
                            return boundOrVertex;
                        }
    
                        if (p1.getLat() == p2.getLat()) {
                            if (p1.getLat() == p.getLat()) {
                                return boundOrVertex;
                            } else {
                                ++intersectCount;
                            }
                        } else {
                            double xinters = (p.getLng() - p1.getLng()) * (p2.getLat() - p1.getLat()) / (p2.getLng() - p1.getLng()) + p1.getLat();
                            if (Math.abs(p.getLat() - xinters) < precision) {
                                return boundOrVertex;
                            }
    
                            if (p.getLat() < xinters) {
                                ++intersectCount;
                            }
                        }
                    }
                } else {
                    if (p.getLng() == p2.getLng() && p.getLat() <= p2.getLat()) {
                        Point p3 = pts.get((i + 1) % N);
                        if (p.getLng() >= Math.min(p1.getLng(), p3.getLng()) && p.getLng() <= Math.max(p1.getLng(), p3.getLng())) {
                            ++intersectCount;
                        } else {
                            intersectCount += 2;
                        }
                    }
                }
                p1 = p2;
            }
            return intersectCount % 2 != 0;
        }
    }

    再贴实体类

    import lombok.Data;
    import lombok.ToString;
    
    
    @Data
    @ToString
    public class Point {
        /**
         * 经度(-180~180,东经正数,西经负数)
         */
        private double lng;
        /**
         * 维度(-90~90,北纬正数,南纬负数)
         */
        private double lat;
    
        public Point(double lng, double lat) {
            this.lng = lng;
            this.lat = lat;
        }
    }

    说明:用哪种坐标系不重要,但是围栏的经纬度与要判断的坐标点必须保持是同一种坐标系!!!!!!!!!!!

    展开全文
  • 该 3D 场景从正面展示了一个现代化工厂的现实场景,包括工厂工人的实时位置、电子围栏的范围、现场的安全情况等等,帮助我们直观的了解当前工厂人员的安全状况。 本篇文章通过对工厂可视化场景的搭建和模型的加载,...
  • 第2章 LoRa无线通信技术开发2.6 LoRa电子围栏系统开发与实现项目场景安防程序逻辑分析安防类程序接口分析项目实践项目场景畜牧牲畜对于农户来说是高价值产品同时是保证收益的重要来源因此为了保证牲畜在放养过程中不...
  • 国家公园安防特点 脉冲电子围栏的定义 发展演变 组成结构 技术原理 防控作用 未来发展
  • 在项目中,通常会需要对禁止进入的区域进行报警提示,本范例即实现了模拟监控数据和围栏区域,并监控报警的功能。
  • 电子围栏系统知识大全

    千次阅读 2018-08-23 15:18:37
    电子围栏系统具有强大的阻挡作用和威慑作用、具有误报率极低的智能报警功能、备有报警接口,能与别的安防系统联动,提高系统的安全防范等级。   智能型周界安防脉冲电子围栏的概念 全新的周界报警系统概念: ?...
  • (3) 电子围栏。在地图上拖拽一个矩形框,当车辆(暂时用标注marker来模拟)在矩形框内的时候,处于正常状态;当车辆驶出矩形框外的时候,报警。 (4)实时路况。提供部分城市的实时路况信息。这个功能51dituAPI支持。 ...
  • '正常:在围栏里面':'异常:在围栏外面', icon:'none' }) }, creatPolygons() { //创建多边形围栏/服务范围 if (this.data.markers.length < 3){ debugger return wx.showToast({ title: '请先在地图上标记点...
  • 用户自定义电子围栏,记录车辆进出电子围栏的时间。 技术点 checkpoint stateBackend watermark Broadcast State 模式 数据流连接广播流 state 实现 checkpoint Flink中的每个方法或算子都能够是有状态的。...
  • 一种实时监测超越电子围栏的算法在智能巡检中的应用,汤琳琳,张永军,伴随着智能巡检概念的兴起,基于手持智能终端的智能巡检已被行业广泛接受与应用。智能巡检并不是手工巡检的简单取代,利用它我们

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,031
精华内容 1,212
关键字:

电子围栏