批量绘图 EXCEL绘制基站扇区地图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了批量绘图 EXCEL绘制基站扇区地图相关的知识,希望对你有一定的参考价值。

参考技术A 通信行业网管和网优部门日常工作,有时需要在地图上标注基站,并根据基站的参数在地图上绘制基站扇区。

数据源格式如下:

1)【经度和纬度列】:基站坐标,为经纬度。

2)【半径】:基站信号辐射半径,基站信号的最大半径,如果基站有多个设备,可分别设置,用 | 分割。例如:200|300|200。

3)【方位角】:基站设备信号角度,如多个设备可用 | 分割。

4)【幅宽角】:基站设备信号的辐射宽度,以角度表示,如多个设备可用 | 分割。

以上信息为必填项,其他列为可选。在EXCEL表中准备好需求数据后,下面使用小O地图EXCEL版制作基站地图。

启动【小O地图EXCEL版】,在EXCEL中打开整理好的数据表格。接下来使用小O地图的【地图绘图】标注地图。

1、新建绘图图层

新建绘图图层,用于标注基站点位,如果不需要标注基站位置,可跳过本章节内容。

2、批量创建点图形

在绘图图层的菜单上,选择【批量创建】,在弹出界面上设置数据所在行列号。

3、批量导入设置

选择【导入点】对照表格,设置数据所在行列号,点击【导入】按钮

注意:

1)导入行号,可选择行号区间导入数据。

2)为保证地图标注显示效率,单个绘图图层最多导入300个点,如点数超过此限制,可新建绘图图层按行号区间导入点。

4、导入成功后的效果图

1、新建扇区绘图图层

新建绘图图层,选择【批量创建】菜单,在弹出界面中选择【扇区】,并设置数据所在行列号。

2、导入扇区数据

设置行列号后,点击【导入】按钮,正常情况下,地图会出现下述效果。

默认情况,标注的扇区填充色会使用侧边栏的【面样式】,用户可以在导入前,先设置样式,这样导入时就会按设定的样式填充。

高级做法,可根据数据分类填充样式,如下图,我们根据表格中的 【信号类型】 字段设置不同颜色。

扇区创建完毕后,我们可以保存数据,将数据导出GIS格式文件。

1、保存绘图记录

先保存新记录,将当前绘图记录保存至【地图绘图管理器】窗口中。

2、导出绘图记录

保存至绘图管理器窗口的记录,可以导出成GIS格式文件,包括ShapeFile、Mapinfo Tab、AutoCAD Dxf、GeoJSON、KML等格式文件。

按点、线、面类型导出数据,导入数据在QGIS软件中打开。

以上就是使用小O地图EXCEL版进行扇区地图制作的操作。

使用的是【地图绘图】功能,绘图均以图层方式操作。

几点事项:

1、新建绘图图层,标注基站点位和基站扇区分别新建两个图层

2、绘图数量限制,大量数据可新建多个绘图图层,分别行数区间导入数据。

3、设置绘图样式,导入前先在侧边栏设置,导入数据将按设置效果显示。也可按单元格颜色设置。

4、绘图可保存,保存至绘图管理器,并可以导出GIS格式。

按上述步骤可以标注基站点位和基站扇形图形。

小O地图提供了很多EXCEL表格与地图结合的功能,篇幅有限,更多消息大家可以关注专栏或公众号。

最后放几张效果。

-- 本文完 --

Mapbox 地图画布上的自定义绘图

【中文标题】Mapbox 地图画布上的自定义绘图【英文标题】:Custom Drawing on Mapbox Map Canvas 【发布时间】:2017-01-26 14:12:16 【问题描述】:

我希望能够使用 android sdk 在 mapbox 地图上手动绘制复杂的形状。我继承了地图视图类并覆盖了 ondraw 事件,但不幸的是,我绘制的任何内容都会被地图本身覆盖。

例如,我需要能够在其他复杂形状中绘制具有菱形边框的多边形。这我可以在 GoogleMaps 中使用自定义图块提供程序并覆盖 ondraw 来解决问题。

这是我目前唯一的 mapbox 代码:

    @Override
    public void onDraw(Canvas canvas)         
        super.onDraw(canvas);

        Paint stroke = new Paint();
        stroke.setColor(Color.BLACK);
        stroke.setStyle(Paint.Style.STROKE);
        stroke.setStrokeWidth(5);
        stroke.setAntiAlias(true); 

        canvas.drawLine(0f,0f,1440f,2464f,stroke);
    

【问题讨论】:

【参考方案1】:

你可以通过两种方式做你想做的事:

1) 如您所建议:“继承MapView 类并覆盖onDraw() 事件”。MapView 扩展FrameLayoutViewGroup,所以您应该覆盖dispatchDraw() 而不是 onDraw()

这种方法需要自定义视图,它扩展MapView并实现:

MapView上画图;

自定义线条样式(“菱形而不是简单线条”);

Lat/LonMapView 坐标的绑定路径。

要覆盖MapView,您应该覆盖dispatchDraw(),例如这样:

@Override
public void dispatchDraw(Canvas canvas) 
    super.dispatchDraw(canvas);
    canvas.save();
    drawDiamondsPath(canvas);
    canvas.restore();

对于自定义线条样式您可以使用Paint 类的setPathEffect() 方法。为此,您应该为“钻石印章”(以像素为单位)创建路径,它将重复每个“前进”(也以像素为单位):

        mPathDiamondStamp = new Path();
        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD);

        mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDiamondPaint.setColor(Color.BLUE);
        mDiamondPaint.setStrokeWidth(2);
        mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mDiamondPaint.setStyle(Paint.Style.STROKE);
        mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE));

(在这种情况下,有 2 个Path - 第一个(顺时针)用于外边框,第二个(逆时针)用于“钻石”透明“孔”的内边框)。

为了将屏幕上的路径绑定到MapViewLat/Lon 坐标,您应该拥有MapViewMapboxMap 对象——因为getMapAsync()onMapReady() 应该被覆盖:

@Override
public void getMapAsync(OnMapReadyCallback callback) 
    mMapReadyCallback = callback;
    super.getMapAsync(this);


@Override
public void onMapReady(MapboxMap mapboxMap) 
    mMapboxMap = mapboxMap;
    if (mMapReadyCallback != null) 
        mMapReadyCallback.onMapReady(mapboxMap);
    

您可以在“纬度/经度到屏幕”转换中使用它:

        mBorderPath = new Path();
        LatLng firstBorderPoint = mBorderPoints.get(0);
        PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint);
        mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y);

        for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) 
            PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint));
            mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y);
        

完整源代码:

自定义 DrawMapView.java

public class DrawMapView extends MapView implements OnMapReadyCallback

    private float DIAMOND_WIDTH = 42;
    private float DIAMOND_HEIGHT = 18;
    private float DIAMOND_ADVANCE = 1.5f * DIAMOND_WIDTH;       // spacing between each stamp of shape
    private float DIAMOND_PHASE = DIAMOND_WIDTH / 2;            // amount to offset before the first shape is stamped
    private float DIAMOND_BORDER_WIDTH = 6;                     // width of diamond border

    private Path mBorderPath;
    private Path mPathDiamondStamp;
    private Paint mDiamondPaint;
    private OnMapReadyCallback mMapReadyCallback;
    private MapboxMap mMapboxMap = null;
    private List<LatLng> mBorderPoints;

    public DrawMapView(@NonNull Context context) 
        super(context);
        init();
    

    public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
        init();
    

    public DrawMapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init();
    

    public DrawMapView(@NonNull Context context, @Nullable MapboxMapOptions options) 
        super(context, options);
        init();
    

    public void setBorderPoints(List<LatLng> borderPoints) 
        mBorderPoints = borderPoints;
    

    @Override
    public void getMapAsync(OnMapReadyCallback callback) 
        mMapReadyCallback = callback;
        super.getMapAsync(this);
    

    @Override
    public void onMapReady(MapboxMap mapboxMap) 
        mMapboxMap = mapboxMap;
        if (mMapReadyCallback != null) 
            mMapReadyCallback.onMapReady(mapboxMap);
        
    

    @Override
    public void dispatchDraw(Canvas canvas) 
        super.dispatchDraw(canvas);
        canvas.save();
        drawDiamondsPath(canvas);
        canvas.restore();
    

    private void drawDiamondsPath(Canvas canvas) 
        if (mBorderPoints == null || mBorderPoints.size() == 0) 
            return;
        

        mBorderPath = new Path();

        LatLng firstBorderPoint = mBorderPoints.get(0);
        PointF firstScreenPoint = mMapboxMap.getProjection().toScreenLocation(firstBorderPoint);
        mBorderPath.moveTo(firstScreenPoint.x, firstScreenPoint.y);

        for (int ixPoint = 1; ixPoint < mBorderPoints.size(); ixPoint++) 
            PointF currentScreenPoint = mMapboxMap.getProjection().toScreenLocation(mBorderPoints.get(ixPoint));
            mBorderPath.lineTo(currentScreenPoint.x, currentScreenPoint.y);
        

        mPathDiamondStamp = new Path();
        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.moveTo(-DIAMOND_WIDTH / 2 + DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, -DIAMOND_HEIGHT / 2 + DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.lineTo(DIAMOND_WIDTH / 2 - DIAMOND_BORDER_WIDTH, 0);
        mPathDiamondStamp.lineTo(0, DIAMOND_HEIGHT / 2 - DIAMOND_BORDER_WIDTH / 2);
        mPathDiamondStamp.close();

        mPathDiamondStamp.setFillType(Path.FillType.EVEN_ODD);

        mDiamondPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDiamondPaint.setColor(Color.BLUE);
        mDiamondPaint.setStrokeWidth(2);
        mDiamondPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mDiamondPaint.setStyle(Paint.Style.STROKE);
        mDiamondPaint.setPathEffect(new PathDashPathEffect(mPathDiamondStamp, DIAMOND_ADVANCE, DIAMOND_PHASE, PathDashPathEffect.Style.ROTATE));

        canvas.drawPath(mBorderPath, mDiamondPaint);
    

    private void init() 
        mBorderPath = new Path();
        mPathDiamondStamp = new Path();
    

ActivityMain.java

public class MainActivity extends AppCompatActivity 

    private DrawMapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        MapboxAccountManager.start(this, getString(R.string.access_token));
        setContentView(R.layout.activity_main);

        mapView = (DrawMapView) findViewById(R.id.mapView);
        mapView.onCreate(savedInstanceState);
        mapView.getMapAsync(new OnMapReadyCallback() 
            @Override
            public void onMapReady(MapboxMap mapboxMap) 

                mapView.setBorderPoints(Arrays.asList(new LatLng(-36.930129, 174.958843),
                        new LatLng(-36.877860, 174.978108),
                        new LatLng(-36.846373, 174.901841),
                        new LatLng(-36.829215, 174.814659),
                        new LatLng(-36.791326, 174.779337),
                        new LatLng(-36.767680, 174.823242)));
            
        );
    

    @Override
    public void onResume() 
        super.onResume();
        mapView.onResume();
    

    @Override
    public void onPause() 
        super.onPause();
        mapView.onPause();
    

    @Override
    public void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    

    @Override
    public void onLowMemory() 
        super.onLowMemory();
        mapView.onLowMemory();
    

    @Override
    protected void onDestroy() 
        super.onDestroy();
        mapView.onDestroy();
    


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:mapbox="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    tools:context="ua.com.omelchenko.mapboxlines.MainActivity">

    <ua.com.omelchenko.mapboxlines.DrawMapView
        android:id="@+id/mapView"
        android:layout_
        android:layout_
        mapbox:center_latitude="-36.841362"
        mapbox:center_longitude="174.851110"
        mapbox:style_url="@string/style_mapbox_streets"
        mapbox:zoom="10"/>
</RelativeLayout>

最后,你应该得到这样的结果:

并且您应该考虑一些“特殊情况”,例如,如果所有路径点都在地图的当前视图之外,则上面没有线,甚至线应该穿过地图视图并且应该是可见的。

2) (更好的方法)创建和发布带有您的附加线的地图并为它们提供custom style(特别是看看“带图像的线型”部分)。您可以为此使用Mapbox Studio。在这种方法中,所有“特殊情况”和性能问题都在 Mabpox 方面得到解决。

【讨论】:

Gidday Andriy,在发布问题后不久,我在最后一刻遇到了意外的商务旅行,所以我很抱歉没有尽快回复您。你的回答正是我所需要的。非常感谢您的帮助。 谢谢你,@Kevin :) 希望回答对你有帮助。 这在平移和缩放地图时是否也有效?这里,onDraw() 和 dispatchDraw() 只被调用一次 @j3App 有效:折线将在正确的位置,线图案的大小没有改变。【参考方案2】:

如果我理解正确,您是在尝试向地图添加菱形(用户没有绘制形状)?如果是这种情况,您有几个选择:

    使用多边形,只需添加点列表,它就会绘制形状(在本例中为菱形)。这将是最简单的,但我假设您已经尝试过并且它不适合您。

    List<LatLng> polygon = new ArrayList<>();
    polygon.add(<LatLng Point 1>);
    polygon.add(<LatLng Point 2>);
    
    ...
    
    mapboxMap.addPolygon(new PolygonOptions()
      .addAll(polygon)
      .fillColor(Color.parseColor("#3bb2d0")));
    

    使用 4.2.0(仍处于测试阶段)中引入的新样式 API 添加填充层。这样做首先需要您创建一个带有点的 GeoJSON 对象,然后将其添加到地图中。我必须这样做的最接近的例子是this example,在演示应用程序中找到。

    使用 onDraw,它只需要将画布转换为 GeoJSON 对象并像步骤 2 中解释的那样添加为图层。我只建议您在运行时让用户绘制形状,在这种情况下坐标是不确定的。

如果你正在寻找不同的东西,我会编辑这个答案。

【讨论】:

感谢您的回复。我不是想画一个菱形多边形。我试图绘制一个多边形,它的边框由小钻石而不是一条简单的线组成。请参阅图片以澄清我的问题。选项 3 是我正在尝试做的事情,但无法实现,因为一旦地图从 ondraw 事件呈现异步,我在画布上绘制的任何内容都会被覆盖。我也不认为将画布转换为 geojson 会起作用,因为 geojson 无法定义复杂的多边形菱形。 您还有什么建议吗?我很想切换到 mapbox,因为自定义地图要好得多,但是因为我无法绘制我需要的形状,所以我坚持使用 googlemaps,直到找到解决方案。非常感谢您的帮助。 你知道这是否可能吗?如果不可能,想继续前进。再次感谢您的帮助。 "你知道这是否可能吗?" - 可以通过多种方式实现。

以上是关于批量绘图 EXCEL绘制基站扇区地图的主要内容,如果未能解决你的问题,请参考以下文章

5G NR — 基站的扇区小区跟踪区注册区服务区

5G NR — 基站的扇区小区跟踪区注册区服务区

如何用高德地图发定位

5G无线技术基础自学系列 | 基站数量计算

基站定位LAC,CID转经纬度

android 版百度地图如何通过定位功能获得当前的位置所在的城市?