腾讯位置服务

腾讯位置服务 查看完整档案

北京编辑  |  填写毕业院校腾讯科技(北京)有限公司  |  腾讯位置服务 编辑 lbs.qq.com 编辑
编辑

立足生态,连接未来

个人动态

腾讯位置服务 发布了文章 · 3月5日

腾讯地图手把手教你实现微信小程序路线规划

前言

本文旨在以mpvue框架为基础,探讨地图类小程序的开发思路。 原作者利用mpvue + 腾讯地图的能力做了一个地铁路线规划的小程序,主要提供全球主要城市的地铁线网图及旅游介绍,其中国内城市支持查看地图和路线规划。

目前腾讯位置服务也推出了路线规划插件地铁图插件,实现更加简单便捷,感兴趣的可点击查看。

运行截图

mpvue 介绍 及项目搭建

mpvue = miniprogram + vue framework,说白了就是用vue框架开发小程序。mpvue最近升级为2.x版本,支持微信、支付宝、百度和头条小程序。和传统方式相比,mpvue开发具有以下优点:

彻底的组件化开发能力:提高代码复用性

  • 完整的 Vue.js 开发体验
  • 方便的 Vuex 数据管理方案:方便构建复杂应用
  • 快捷的 webpack 构建机制:自定义构建策略、开发阶段 hotReload
  • 支持使用 npm 外部依赖
  • 使用 Vue.js 命令行工具 vue-cli 快速初始化项目
  • H5 代码转换编译成小程序目标代码的能力

就个人使用体验来看,还是挺丝滑顺畅的,传统web应用开发无缝切换至小程序开发,基本零门槛。要注意的就是小程序的限制及和vue的差异:

  • 小程序使用相对像素 rpx 进行样式布局
  • 部分css选择符不支持,目前只支持 #id | .class | tag | tag,tag | ::after ::before,所以要特别注意
  • 组合式生命周期,mpvue将小程序和vue的生命周期混在一块,详情见 mpvue.com/mpvue/#_3 ,目前这个地方还有很多坑,比如在小程序page unload时,vue实例却没被销毁,导致下次进入页面时,页面状态不变,必须在unLoad时手动重置状态等
  • mpvue 会封装小程序数据对象,通常以$mp开头,如event.$mp.detail.target
  • 小程序的组件和vue组件有差异,不要幻想vue组件的特性都能用,如slot,异步组件等等
  • vue store 和 wx localstorage 最好不要弄混,要根据不同需要选择不同的存储方式
  • 不要用vue路由,要采用小程序原生的导航机制

然后,我们搭建开发环境,mpvue脚手架是开箱即用的:

# 全局安装 vue-cli
# 一般是要 sudo 权限的
$ npm install --global vue-cli@2.9

# 创建一个基于 mpvue-quickstart 模板的新项目
# 新手一路回车选择默认就可以了
$ vue init mpvue/mpvue-quickstart my-project

# 安装依赖,走你
$ cd my-project
$ npm install
$ npm run dev

接着,完善文件结构,增加 config、store、mixins等模块,如图:

app.json是小程序专用文件,也需完善下:

{
  "pages": [
    "pages/citylist/main",
    "pages/citydetail/main"
  ],
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#eee",
    "navigationBarTitleText": "全球地铁,全程为你",
    "navigationBarTextStyle": "black"
  }
}

然后就可以愉快的写Vue代码了,咔咔一个页面,咔咔又是一个页面,组件,store,数据驱动,你喜欢的样子,它都有。

腾讯地图+ 小程序

着重说一下地图的接入,腾讯地图提供了两个对接入口给小程序,1是个性化地图展示,2是专用SDK,二者共同完善了小程序的地图生态。

1、个性地图展示需要开发者自行注册并申请开发者密钥(key),并在管理后台绑定小程序,然后设置个性地图的样式,才能使用:

<map
  id="citymap"
  name="citymap"
  :longitude="lng"
  :latitude="lat"
  :polyline="polyline"
    :markers="markers"
  scale="12"
  :subkey="YOUR_OWN_QQMAP_KEY"
  show-location
  show-compass
  enable-rotate
  style="width: 100%; height: 100%;"
>
  <cover-view class="map-cover-view">
    <button class="explore-btn" type="primary" @tap="exploreCity">查看旅游攻略</button>
  </cover-view>
</map>

其中,map是小程序的原生组件,原生组件脱离在 WebView 渲染流程外,它的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。说白了就是原生组件是微信客户端提供的,它不属于内置浏览器,为此,小程序专门提供了 cover-view 和 cover-image 组件,可以覆盖在部分原生组件上面。这两个组件也是原生组件,但是使用限制与其他原生组件有所不同。

笔者就因为这个坑耽误了不少时间,有时候开发工具可以用,但到了真机上组件就完全乱了,所以还是要以真机调试为准。对于原生组件,不要用太复杂的css,它的很多css属性支持的都不好。

map可以定义多个参数,经纬度不用说,scale指放缩比例,也就是地图比例尺,polyline在地图上绘制折线,markers用于标记地图上的点,show-location用于显示用户所在位置,show-compass显示指北针。

2、专用SDK,目前提供这些能力:

  • search(options:Object) 地点搜索,搜索周边poi,比如:“酒店” “餐饮” “娱乐” “学校” 等等
  • getSuggestion(options:Object) 用于获取输入关键字的补完与提示,帮助用户快速输入
  • reverseGeocoder(options:Object) 提供由坐标到坐标所在位置的文字描述的转换。输入坐标返回地理位置信息和附近poi列表
  • geocoder(options:Object) 提供由地址描述到所述位置坐标的转换,与逆地址解析的过程正好相反
  • direction(options:Object) 提供驾车,步行,骑行,公交的路线规划能力
  • getCityList() 获取全国城市列表数据
  • getDistrictByCityId(options:Object) 通过城市ID返回城市下的区县
  • calculateDistance(options:Object) 计算一个点到多点的步行、驾车距离

我们以公共交通路线规划为例来看下(以下代码经过简化处理):

第一步,初始化地图SDK对象

import config from '@/config'
import QQMapWX from '../../assets/lib/qqmap-wx-jssdk.js' // 这里用未压缩版的代码
const QQMapSDK = new QQMapWX({
  key: config.qqMapKey || ''
})

第二步,获取起止坐标点,并进行路线查询

// 坐标从上一页query传进来,坐标为浮点数,可通过geocoder接口获取
this.fromLocation = {
  latitude: +query.from.split(',')[0] || -1,
  longitude: +query.from.split(',')[1] || -1
}

this.toLocation = {
  latitude: +query.to.split(',')[0] || -1,
  longitude: +query.to.split(',')[1] || -1
}

// 查询地图路线
queryMapRoutine() {
  QQMapSDK.direction({
    mode: 'transit', // 'transit'(公交路线规划)
    // from参数不填默认当前地址
    from: this.fromLocation,
    to: this.toLocation,
    success: (res) => {
      console.log('路线规划结果', res);
      let routes = res.result.routes;
      this.routes = routes.map(r => {
                // 对每一种路线方案,分别进行解析
        return this.parseRoute(r)
      })
      console.log('parsed routes', this.routes)
    }
  })
}

第三步,路线解析,生成路线描述等

// 解析路线,包括距离,时间,描述,路线,起止点等
parseRoute(route) {
    let result = {}
    // 出发时间
  result.setOutTime = formatTime(new Date())
  result.distance = route.distance < 1000 ?
                    `${route.distance}米` :
                    `${(route.distance / 1000).toFixed(2)}公里`
  result.duration = route.duration < 60 ?
                    `${route.duration}分钟` :
                    `${parseInt(route.duration / 60)}小时${route.duration % 60}分钟`
    result.desc = []
    // 每一个路线分很多步,如先步行,后乘公交,再搭地铁等
  route.steps.forEach(step => {
    // if (step.mode == 'WALKING' && step.distance > 0) {
    //   result.desc.push(`向${step.direction}步行${step.distance}米`)
    // }
    if (step.mode == 'TRANSIT' && step.lines[0]) {
      let line = step.lines[0]
      if (line.vehicle == 'BUS') line.title = `公交车-${line.title}`
      if (line.vehicle == 'RAIL') line.title = `铁路`
      result.desc.push(`${line.title}: ${line.geton.title} —> ${line.getoff.title},途经 ${line.station_count} 站。`)
    }
  })
  result.polyline = []
  result.points = []
  //获取各个步骤的polyline,也就是路线图
  for(let i = 0; i < route.steps.length; i++) {
    let step = route.steps[i]
    let polyline = this.getStepPolyline(step)
    if (polyline) {
      result.points = result.points.concat(polyline.points)
      result.polyline.push(polyline)
    }
    }
    // 标记路线整体显示坐标
  this.getStepPolyline.colorIndex = 0
  let midPointIndex = Math.floor(result.points.length / 2)
  result.latitude = result.points[midPointIndex].latitude
  result.longitude = result.points[midPointIndex].longitude
  // 标记路线起止点
  let startPoint = result.points[0]
  let endPoint = result.points[result.points.length - 1]
  result.markers = [
    {
      iconPath: this.startIcon,
      id: 0,
      latitude: startPoint.latitude,
      longitude: startPoint.longitude,
      width: 28,
      height: 28,
      zIndex: -1,
      anchor: {x: 0.5, y: 1}
    },
    {
      iconPath: this.endIcon,
      id: 1,
      latitude: endPoint.latitude,
      longitude: endPoint.longitude,
      width: 28,
      height: 28,
      zIndex: -1,
      anchor: {x: 0.5, y: 1}
    }
  ]
  return result
},

第四步,getStepPolyline函数 获取路线每一步的路线polyline

getStepPolyline(step) {
    let coors = [];
    // 随机颜色
  let colorArr = ['#1aad19', '#10aeff', '#d84e43']
  let _dottedLine = true
  if (step.mode == 'WALKING' && step.polyline) {
    coors.push(step.polyline);
    _dottedLine = false
  } else if (step.mode == 'TRANSIT' && step.lines[0].polyline) {
    coors.push(step.lines[0].polyline);
  } else {
    return null
  }
  //坐标解压(返回的点串坐标,通过前向差分进行压缩)
  let kr = 1000000;
  for (let i = 0 ; i < coors.length; i++){
    for (let j = 2; j < coors[i].length; j++) {
      coors[i][j] = Number(coors[i][j - 2]) + Number(coors[i][j]) / kr;
    }
  }
  //定义新数组,将coors中的数组合并为一个数组
  let coorsArr = [];
  let _points = [];
  for (let i = 0 ; i < coors.length; i ++){
    coorsArr = coorsArr.concat(coors[i]);
  }
  //将解压后的坐标放入点串数组_points中
  for (let i = 0; i < coorsArr.length; i += 2) {
    _points.push({ latitude: coorsArr[i], longitude: coorsArr[i + 1] })
  }
  if (!this.getStepPolyline.colorIndex) {
    this.getStepPolyline.colorIndex = 0
  }
  let colorIndex = this.getStepPolyline.colorIndex % colorArr.length
    this.getStepPolyline.colorIndex++
    // 最终polyline结果
  let polyline = {
    width: 7,
    points: _points,
    color: colorArr[colorIndex],
    dottedLine: _dottedLine,
    arrowLine: true, // 带箭头的线, 开发者工具暂不支持该属性
    borderColor: '#fff',
    borderWidth: 1
  }
  return polyline
}

最后,绑定到地图上并输出,我们可以得到一个大致这样的结果:

广州火车站 -> 广州塔
9.88km 30分钟
地铁5号线 广州火车站 -> 珠江新城,途径7站
地铁3号线 珠江新城 -> 广州塔,途径1站

这样我们就通过direction接口进行了简单的路线规划功能,接着把生成的数据绑定到地图组件上,一个简易的小程序就做好了,是不是很简单?当然如果想做得更好,就要调用其他相似接口,慢慢完善细节。

<map
  id="citymap"
  name="citymap"
  :latitude="currentRoute.latitude"
  :longitude="currentRoute.longitude"
  :polyline="currentRoute.polyline"
  :markers="currentRoute.markers"
  scale="12"
  :subkey="qqMapKey"
  show-location
  show-compass
  enable-rotate
  style="width: 100%; height: 100%;"
></map>

效果

作者:棱镜_jh

链接:https://juejin.cn/post/684490...

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

赞 4 收藏 3 评论 0

腾讯位置服务 发布了文章 · 2月26日

数据可视化API之热力图实现

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

应用场景

热力图以颜色来表现数据强弱大小及分布趋势,可以用在出行、旅游、警务安全、城市规划和研究等多方面。

代码

大家可通过参考手册先了解下参数详情。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>3D经典热力</title>
</head>
<script charset="utf-8"
    data-original="https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&libraries=visualization"></script>
<style type="text/css">
    html,
    body {
        height: 100%;
        margin: 0px;
        padding: 0px;
    }

    #container {
        width: 100%;
        height: 100%;
    }
</style>

<body onload="initMap()">
    <div id="container"></div>
    <script data-original="https://mapapi.qq.com/web/lbs/visualizationApi/demo/data/heat.js"></script>
    <script>
        function initMap() {
            var center = new TMap.LatLng(39.909897147274364, 116.39756310116866);

            //初始化地图
            var map = new TMap.Map("container", {
                zoom: 12,//设置地图缩放级别
                pitch: 45, // 设置地图俯仰角
                center: center,//设置地图中心点坐标
                mapStyleId: "style1" //个性化样式
            });
            //初始化热力图并添加至map图层
            new TMap.visualization.Heat({
                max: 180, // 热力最强阈值
                min: 0, // 热力最弱阈值
                height: 40, // 峰值高度
                radius: 30 // 最大辐射半径
            })
            .addTo(map)
            .setData(heatData);//设置数据
        }
    </script>
</body>

</html>

效果图

在线示例

以上内容只是简单实现,更具体代码示例以及原理讲解,可参考文章:

【硬核干货来了!鹅厂前端工程师手把手教你实现热力图!】

查看原文

赞 3 收藏 3 评论 0

腾讯位置服务 收藏了文章 · 2月24日

基于UE4/Unity绘制地图基础元素-面和体

前言

基于UE4/Unity绘制地图基础元素-线(上篇)

基于UE4/Unity绘制地图基础元素-线(下篇)

搞定地图画线之后,接下来就是绘制面和体了:

面作为地图渲染的基本元素之一,在地图中可以代表各种形式的区域,例如海面、绿地等。面数据通常以离散点串形式存储,因此渲染时最关注的是如何将其展现为闭合的图形。

体可以理解为带有高度的面,在地图中代表各种建筑,通常是由其顶部面数据和高度数据处理得到。

本文记录了绘制面和体的流程以及解决闪烁问题的方案。

绘制多边形区域面

面数据通常以离散点串形式存储,面的绘制与线的绘制原理类似。渲染的基本单位是三角形,线是通过扩展线宽构造三角形后渲染,而面是通过将多边形拆分为多个三角形后渲染。

拆分为三角形的过程被称为三角剖分,常用的三角剖分算法是耳切法(Ear Clipping),比较成熟的方案是Mapbox的earcut,对于有 [公式] 个顶点的多边形,其时间复杂度为 [公式] ,值得注意的是,三角剖分的解可能是不唯一的,任何一种剖分方式都能够渲染得到面,但细小的三角形更容易使面中的同一像素绘制多次,造成过度绘制(Overdraw),因此根据多边形特征做一些剖分次序的调整可以作为一个优化点。

剖分完成的多边形区域,在指定了每一个顶点的颜色之后,就能绘制得到纯色的面。和道路线的Z-fighting问题类似,区域面也需要处理同一高度叠加显示的问题。同时,二维的道路线和区域面整体也处于同一个高度上,因此也需要统一考虑层级关系,将所有的道路线置于区域面之上。统一处理完成就可以得到二维的地图底板了。

绘制多边形建筑体

二维地图底板完成后,就轮到地图上的楼块建筑了。为了减少数据量,通常的存储方式是顶面点串和其对应的拔起高度,在渲染时增加顶点构成闭合体。

顶面渲染流程和闭合区域面一致,侧面则是根据楼高进行绘制,在每两个相邻顶点间渲染一个矩形从而构成闭合体的侧面,为了减少绘制次数通常只绘制朝向外侧的侧面,底面在正常视角下看不到,也可以酌情选择是否绘制。

建筑体的渲染只比区域面多了拔高产生的侧面,逻辑比较简单,处理得到所有三角形数据后,配置好顶点颜色即可完成渲染。

奇怪的建筑体Z-fighting问题

理论上来说,建筑体数据的顶面通常不会重合,因此在拔起渲染后不会出现Z-Fighting问题,但奇怪的是,渲染后仍然发现一些体存在侧面闪烁问题。通过全链路的排查,才查出是多边形数据的问题。

三角剖分在使用时有一个前置条件:使用对象必须为简单多边形,即多边形中的任何两条边仅可以在顶点处相交。下图(a)多边形为满足定义的简单多边形,图(b)多边形边01和23在非顶点处相交,因此是非简单多边形。

对于非简单多边形,使用三角剖分只能得出较为满意的结果,但不能保证其正确性。从下图四个顶点构成的非简单多边形的三角剖分结果可以看到,多边形渲染时会丢失顶点并且产生错误的三角形,无法还原数据真实情况。

按照这种想法对现有数据进行了边的相交检测,确实存在一小部分的多边形不是简单多边形。而体元素的立面拔起是按照原始数据在每一组相邻顶点间绘制矩形,因此会产生问题。以上述的非简单多边形(b)为例,边12拔起生成矩形1245,边23拔起生成矩形2364,两个侧面矩形在面1245上完全重合,当外立面贴上不同的纹理后就会产生Z-Fighting现象。同时,因为外立面仅仅绘制朝向外侧一面,面1245在对侧查看时会消失,产生非常诡异的效果。

针对这个问题,比较容易想到的解决方法主要是以下三个:

1、直接过滤,简单粗暴。

2、根据多边形计算外接矩形,减少细节

3、根据三角剖分结果剔除多余顶点,重新生成简单多边形

以上三个方案对于多边形的细节保留由少到多,但并不是完全还原真实数据。尤其对于一些复杂建筑,某一个面的错误会导致最终拼装得到的渲染结果错误。因此比较理想的方式是修复非简单多边形,将其分解为多个简单多边形,分别渲染还原细节。

简单多边形的判定与修复

根据简单多边形的定义,很容易想到采用暴力解法进行判定:一个 边形有 条边,每条边只需要和其他的 条不相邻边判断是否相交即可达到目的,其时间复杂度为 ,对于仅需要进行一遍数据清洗的静态数据来说已经足够。但对于需要实时处理的动态数据来说,其需要遍历所有组合,尤其对于可能仅存在少量相交点的情况,冗余计算太多,因此可以引入时间复杂度更低的相交判定算法进行处理。

对于一个非简单多边形,在分解为多个简单多边形后,绘制所有面积不为0的图形就可以了。这种方案可以最大限度还原原始数据,并且规避闪烁问题。

小结

解决了数据造成的闪烁问题后,就可以在建筑的侧面和顶面使用纯色或者纹理贴图进行装饰,搭配天空盒和一些纯色元素去装饰,已经可以简略模仿出城市的效果。但在当前的建筑拔起渲染方式下,只能通过贴图的形式去表达建筑细节,如果需要更精细的表达效果,例如玻璃窗体结构、楼顶设施等,需要增加额外的三角形去进行呈现。

作者:程序员阿Tu

链接:https://zhuanlan.zhihu.com/p/...

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

腾讯位置服务 赞了文章 · 2月24日

基于UE4/Unity绘制地图基础元素-面和体

前言

基于UE4/Unity绘制地图基础元素-线(上篇)

基于UE4/Unity绘制地图基础元素-线(下篇)

搞定地图画线之后,接下来就是绘制面和体了:

面作为地图渲染的基本元素之一,在地图中可以代表各种形式的区域,例如海面、绿地等。面数据通常以离散点串形式存储,因此渲染时最关注的是如何将其展现为闭合的图形。

体可以理解为带有高度的面,在地图中代表各种建筑,通常是由其顶部面数据和高度数据处理得到。

本文记录了绘制面和体的流程以及解决闪烁问题的方案。

绘制多边形区域面

面数据通常以离散点串形式存储,面的绘制与线的绘制原理类似。渲染的基本单位是三角形,线是通过扩展线宽构造三角形后渲染,而面是通过将多边形拆分为多个三角形后渲染。

拆分为三角形的过程被称为三角剖分,常用的三角剖分算法是耳切法(Ear Clipping),比较成熟的方案是Mapbox的earcut,对于有 [公式] 个顶点的多边形,其时间复杂度为 [公式] ,值得注意的是,三角剖分的解可能是不唯一的,任何一种剖分方式都能够渲染得到面,但细小的三角形更容易使面中的同一像素绘制多次,造成过度绘制(Overdraw),因此根据多边形特征做一些剖分次序的调整可以作为一个优化点。

剖分完成的多边形区域,在指定了每一个顶点的颜色之后,就能绘制得到纯色的面。和道路线的Z-fighting问题类似,区域面也需要处理同一高度叠加显示的问题。同时,二维的道路线和区域面整体也处于同一个高度上,因此也需要统一考虑层级关系,将所有的道路线置于区域面之上。统一处理完成就可以得到二维的地图底板了。

绘制多边形建筑体

二维地图底板完成后,就轮到地图上的楼块建筑了。为了减少数据量,通常的存储方式是顶面点串和其对应的拔起高度,在渲染时增加顶点构成闭合体。

顶面渲染流程和闭合区域面一致,侧面则是根据楼高进行绘制,在每两个相邻顶点间渲染一个矩形从而构成闭合体的侧面,为了减少绘制次数通常只绘制朝向外侧的侧面,底面在正常视角下看不到,也可以酌情选择是否绘制。

建筑体的渲染只比区域面多了拔高产生的侧面,逻辑比较简单,处理得到所有三角形数据后,配置好顶点颜色即可完成渲染。

奇怪的建筑体Z-fighting问题

理论上来说,建筑体数据的顶面通常不会重合,因此在拔起渲染后不会出现Z-Fighting问题,但奇怪的是,渲染后仍然发现一些体存在侧面闪烁问题。通过全链路的排查,才查出是多边形数据的问题。

三角剖分在使用时有一个前置条件:使用对象必须为简单多边形,即多边形中的任何两条边仅可以在顶点处相交。下图(a)多边形为满足定义的简单多边形,图(b)多边形边01和23在非顶点处相交,因此是非简单多边形。

对于非简单多边形,使用三角剖分只能得出较为满意的结果,但不能保证其正确性。从下图四个顶点构成的非简单多边形的三角剖分结果可以看到,多边形渲染时会丢失顶点并且产生错误的三角形,无法还原数据真实情况。

按照这种想法对现有数据进行了边的相交检测,确实存在一小部分的多边形不是简单多边形。而体元素的立面拔起是按照原始数据在每一组相邻顶点间绘制矩形,因此会产生问题。以上述的非简单多边形(b)为例,边12拔起生成矩形1245,边23拔起生成矩形2364,两个侧面矩形在面1245上完全重合,当外立面贴上不同的纹理后就会产生Z-Fighting现象。同时,因为外立面仅仅绘制朝向外侧一面,面1245在对侧查看时会消失,产生非常诡异的效果。

针对这个问题,比较容易想到的解决方法主要是以下三个:

1、直接过滤,简单粗暴。

2、根据多边形计算外接矩形,减少细节

3、根据三角剖分结果剔除多余顶点,重新生成简单多边形

以上三个方案对于多边形的细节保留由少到多,但并不是完全还原真实数据。尤其对于一些复杂建筑,某一个面的错误会导致最终拼装得到的渲染结果错误。因此比较理想的方式是修复非简单多边形,将其分解为多个简单多边形,分别渲染还原细节。

简单多边形的判定与修复

根据简单多边形的定义,很容易想到采用暴力解法进行判定:一个 边形有 条边,每条边只需要和其他的 条不相邻边判断是否相交即可达到目的,其时间复杂度为 ,对于仅需要进行一遍数据清洗的静态数据来说已经足够。但对于需要实时处理的动态数据来说,其需要遍历所有组合,尤其对于可能仅存在少量相交点的情况,冗余计算太多,因此可以引入时间复杂度更低的相交判定算法进行处理。

对于一个非简单多边形,在分解为多个简单多边形后,绘制所有面积不为0的图形就可以了。这种方案可以最大限度还原原始数据,并且规避闪烁问题。

小结

解决了数据造成的闪烁问题后,就可以在建筑的侧面和顶面使用纯色或者纹理贴图进行装饰,搭配天空盒和一些纯色元素去装饰,已经可以简略模仿出城市的效果。但在当前的建筑拔起渲染方式下,只能通过贴图的形式去表达建筑细节,如果需要更精细的表达效果,例如玻璃窗体结构、楼顶设施等,需要增加额外的三角形去进行呈现。

作者:程序员阿Tu

链接:https://zhuanlan.zhihu.com/p/...

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

赞 2 收藏 2 评论 0

腾讯位置服务 发布了文章 · 2月5日

UE4/Unity绘制地图基础元素-面和体

前言

基于UE4/Unity绘制地图基础元素-线(上篇)

基于UE4/Unity绘制地图基础元素-线(下篇)

搞定地图画线之后,接下来就是绘制面和体了:

面作为地图渲染的基本元素之一,在地图中可以代表各种形式的区域,例如海面、绿地等。面数据通常以离散点串形式存储,因此渲染时最关注的是如何将其展现为闭合的图形。

体可以理解为带有高度的面,在地图中代表各种建筑,通常是由其顶部面数据和高度数据处理得到。

本文记录了绘制面和体的流程以及解决闪烁问题的方案。

绘制多边形区域面

面数据通常以离散点串形式存储,面的绘制与线的绘制原理类似。渲染的基本单位是三角形,线是通过扩展线宽构造三角形后渲染,而面是通过将多边形拆分为多个三角形后渲染。

拆分为三角形的过程被称为三角剖分,常用的三角剖分算法是耳切法(Ear Clipping),比较成熟的方案是Mapbox的earcut,对于有 [公式] 个顶点的多边形,其时间复杂度为 [公式] ,值得注意的是,三角剖分的解可能是不唯一的,任何一种剖分方式都能够渲染得到面,但细小的三角形更容易使面中的同一像素绘制多次,造成过度绘制(Overdraw),因此根据多边形特征做一些剖分次序的调整可以作为一个优化点。

剖分完成的多边形区域,在指定了每一个顶点的颜色之后,就能绘制得到纯色的面。和道路线的Z-fighting问题类似,区域面也需要处理同一高度叠加显示的问题。同时,二维的道路线和区域面整体也处于同一个高度上,因此也需要统一考虑层级关系,将所有的道路线置于区域面之上。统一处理完成就可以得到二维的地图底板了。

绘制多边形建筑体

二维地图底板完成后,就轮到地图上的楼块建筑了。为了减少数据量,通常的存储方式是顶面点串和其对应的拔起高度,在渲染时增加顶点构成闭合体。

顶面渲染流程和闭合区域面一致,侧面则是根据楼高进行绘制,在每两个相邻顶点间渲染一个矩形从而构成闭合体的侧面,为了减少绘制次数通常只绘制朝向外侧的侧面,底面在正常视角下看不到,也可以酌情选择是否绘制。

建筑体的渲染只比区域面多了拔高产生的侧面,逻辑比较简单,处理得到所有三角形数据后,配置好顶点颜色即可完成渲染。

奇怪的建筑体Z-fighting问题

理论上来说,建筑体数据的顶面通常不会重合,因此在拔起渲染后不会出现Z-Fighting问题,但奇怪的是,渲染后仍然发现一些体存在侧面闪烁问题。通过全链路的排查,才查出是多边形数据的问题。

三角剖分在使用时有一个前置条件:使用对象必须为简单多边形,即多边形中的任何两条边仅可以在顶点处相交。下图(a)多边形为满足定义的简单多边形,图(b)多边形边01和23在非顶点处相交,因此是非简单多边形。

对于非简单多边形,使用三角剖分只能得出较为满意的结果,但不能保证其正确性。从下图四个顶点构成的非简单多边形的三角剖分结果可以看到,多边形渲染时会丢失顶点并且产生错误的三角形,无法还原数据真实情况。

按照这种想法对现有数据进行了边的相交检测,确实存在一小部分的多边形不是简单多边形。而体元素的立面拔起是按照原始数据在每一组相邻顶点间绘制矩形,因此会产生问题。以上述的非简单多边形(b)为例,边12拔起生成矩形1245,边23拔起生成矩形2364,两个侧面矩形在面1245上完全重合,当外立面贴上不同的纹理后就会产生Z-Fighting现象。同时,因为外立面仅仅绘制朝向外侧一面,面1245在对侧查看时会消失,产生非常诡异的效果。

针对这个问题,比较容易想到的解决方法主要是以下三个:

1、直接过滤,简单粗暴。

2、根据多边形计算外接矩形,减少细节

3、根据三角剖分结果剔除多余顶点,重新生成简单多边形

以上三个方案对于多边形的细节保留由少到多,但并不是完全还原真实数据。尤其对于一些复杂建筑,某一个面的错误会导致最终拼装得到的渲染结果错误。因此比较理想的方式是修复非简单多边形,将其分解为多个简单多边形,分别渲染还原细节。

简单多边形的判定与修复

根据简单多边形的定义,很容易想到采用暴力解法进行判定:一个 边形有 条边,每条边只需要和其他的 条不相邻边判断是否相交即可达到目的,其时间复杂度为 ,对于仅需要进行一遍数据清洗的静态数据来说已经足够。但对于需要实时处理的动态数据来说,其需要遍历所有组合,尤其对于可能仅存在少量相交点的情况,冗余计算太多,因此可以引入时间复杂度更低的相交判定算法进行处理。

对于一个非简单多边形,在分解为多个简单多边形后,绘制所有面积不为0的图形就可以了。这种方案可以最大限度还原原始数据,并且规避闪烁问题。

小结

解决了数据造成的闪烁问题后,就可以在建筑的侧面和顶面使用纯色或者纹理贴图进行装饰,搭配天空盒和一些纯色元素去装饰,已经可以简略模仿出城市的效果。但在当前的建筑拔起渲染方式下,只能通过贴图的形式去表达建筑细节,如果需要更精细的表达效果,例如玻璃窗体结构、楼顶设施等,需要增加额外的三角形去进行呈现。

作者:程序员阿Tu

链接:https://zhuanlan.zhihu.com/p/...

来源:知乎

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

赞 3 收藏 3 评论 0

腾讯位置服务 收藏了文章 · 2月4日

最简单的迁徙图实现demo

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

应用场景

弧线图用以展示两点之间的关联,可以用在迁徙图等表示流向的场景中。

代码

大家可通过参考手册
先了解下参数详情。

创建弧线图

// 创建地图
var map = new TMap.Map('container', {
    center: new TMap.LatLng(40.040934, 116.272677)
});
 
// 创建弧线图
var arc = new TMap.visualization.Arc({
    pickStyle: function(arcLine) { // 样式映射函数
        var red = {
            color: '#DD0000', 
            animateColor:'#FF0000'
        };
        var blue = {
            color: '#0000DD',
            animateColor: '#0000FF'
        };
        return arcLine.id == 'red' ? red : blue;
    },
}).addTo(map); // 通过addTo()添加到指定地图实例
 
// 设置数据,若需要更新数据则再次调用setData方法即可
arc.setData([
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 }, // 弧线起点
        to: { lat: 38.040934, lng: 116.272677 }  // 弧线终点
    },
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 },
        to: { lat: 38.040934, lng: 116.272677 }
    }
]);

获取点击的弧线

如果您希望获取鼠标点击的弧线图形的具体信息,可以监听弧线图的click事件。

arc.on('click', function(evt) {
    if (evt.detail.arc) {
        console.log('点击了:', evt.detail.arc); // 用户所点击的图形所对应的数据
    } else {
        console.log('未点击到图形');
    }
});

效果图

在线示例

作者:腾讯位置服务

链接:https://segmentfault.com/a/11...

来源:segmentfault

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

腾讯位置服务 赞了文章 · 2月4日

最简单的迁徙图实现demo

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

应用场景

弧线图用以展示两点之间的关联,可以用在迁徙图等表示流向的场景中。

代码

大家可通过参考手册
先了解下参数详情。

创建弧线图

// 创建地图
var map = new TMap.Map('container', {
    center: new TMap.LatLng(40.040934, 116.272677)
});
 
// 创建弧线图
var arc = new TMap.visualization.Arc({
    pickStyle: function(arcLine) { // 样式映射函数
        var red = {
            color: '#DD0000', 
            animateColor:'#FF0000'
        };
        var blue = {
            color: '#0000DD',
            animateColor: '#0000FF'
        };
        return arcLine.id == 'red' ? red : blue;
    },
}).addTo(map); // 通过addTo()添加到指定地图实例
 
// 设置数据,若需要更新数据则再次调用setData方法即可
arc.setData([
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 }, // 弧线起点
        to: { lat: 38.040934, lng: 116.272677 }  // 弧线终点
    },
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 },
        to: { lat: 38.040934, lng: 116.272677 }
    }
]);

获取点击的弧线

如果您希望获取鼠标点击的弧线图形的具体信息,可以监听弧线图的click事件。

arc.on('click', function(evt) {
    if (evt.detail.arc) {
        console.log('点击了:', evt.detail.arc); // 用户所点击的图形所对应的数据
    } else {
        console.log('未点击到图形');
    }
});

效果图

在线示例

作者:腾讯位置服务

链接:https://segmentfault.com/a/11...

来源:segmentfault

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

赞 3 收藏 3 评论 0

腾讯位置服务 收藏了文章 · 2月1日

腾讯位置服务数据可视化之弧线图

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

应用场景

弧线图用以展示两点之间的关联,可以用在迁徙图等表示流向的场景中。

代码

大家可通过参考手册
先了解下参数详情。

创建弧线图

// 创建地图
var map = new TMap.Map('container', {
    center: new TMap.LatLng(40.040934, 116.272677)
});
 
// 创建弧线图
var arc = new TMap.visualization.Arc({
    pickStyle: function(arcLine) { // 样式映射函数
        var red = {
            color: '#DD0000', 
            animateColor:'#FF0000'
        };
        var blue = {
            color: '#0000DD',
            animateColor: '#0000FF'
        };
        return arcLine.id == 'red' ? red : blue;
    },
}).addTo(map); // 通过addTo()添加到指定地图实例
 
// 设置数据,若需要更新数据则再次调用setData方法即可
arc.setData([
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 }, // 弧线起点
        to: { lat: 38.040934, lng: 116.272677 }  // 弧线终点
    },
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 },
        to: { lat: 38.040934, lng: 116.272677 }
    }
]);

获取点击的弧线

如果您希望获取鼠标点击的弧线图形的具体信息,可以监听弧线图的click事件。

arc.on('click', function(evt) {
    if (evt.detail.arc) {
        console.log('点击了:', evt.detail.arc); // 用户所点击的图形所对应的数据
    } else {
        console.log('未点击到图形');
    }
});

效果图

在线示例

作者:腾讯位置服务

链接:https://segmentfault.com/a/11...

来源:segmentfault

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

腾讯位置服务 发布了文章 · 1月29日

数据可视化API之弧线图实现

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

应用场景

弧线图用以展示两点之间的关联,可以用在迁徙图等表示流向的场景中。

代码

大家可通过参考手册
先了解下参数详情。

创建弧线图

// 创建地图
var map = new TMap.Map('container', {
    center: new TMap.LatLng(40.040934, 116.272677)
});
 
// 创建弧线图
var arc = new TMap.visualization.Arc({
    pickStyle: function(arcLine) { // 样式映射函数
        var red = {
            color: '#DD0000', 
            animateColor:'#FF0000'
        };
        var blue = {
            color: '#0000DD',
            animateColor: '#0000FF'
        };
        return arcLine.id == 'red' ? red : blue;
    },
}).addTo(map); // 通过addTo()添加到指定地图实例
 
// 设置数据,若需要更新数据则再次调用setData方法即可
arc.setData([
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 }, // 弧线起点
        to: { lat: 38.040934, lng: 116.272677 }  // 弧线终点
    },
    {
        id: 'red',
        from: { lat: 40.040934, lng: 116.272677 },
        to: { lat: 38.040934, lng: 116.272677 }
    }
]);

获取点击的弧线

如果您希望获取鼠标点击的弧线图形的具体信息,可以监听弧线图的click事件。

arc.on('click', function(evt) {
    if (evt.detail.arc) {
        console.log('点击了:', evt.detail.arc); // 用户所点击的图形所对应的数据
    } else {
        console.log('未点击到图形');
    }
});

效果图

在线示例

查看原文

赞 3 收藏 3 评论 0

腾讯位置服务 收藏了文章 · 1月28日

一看就懂的动态轨迹图实现示例

前言

数据可视化API(Web),是基于腾讯位置服务JavaScript API GL实现的专业地理空间数据可视化渲染引擎。
通过这套API,可以实现轨迹数据、坐标点数据、热力、迁徙、航线等空间数据的可视化展现。

使用步骤

1、注册成为腾讯位置服务开发者,并进入控制台 -> key管理界面创建key;

2、数据可视化API(以下简称可视化API),所提供的可视化效果是以图层的方式叠加在JavaScript API GL之上,图层中所显示的数据由实例化的对象统一管理。

3、加载可视化API

可视化API是以 Javascript API GL 的附加库的形式加载的,请确保:
引入时须传入&libraries=visualization参数(查看: Javascript API GL加载参数说明

<script data-original="https://map.qq.com/api/gljs?v=1.exp&key=YOUR_KEY&libraries=visualization"></script>

轨迹图实现

大家可通过参考手册
先了解下参数详情,以下是代码示例。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>动态轨迹图</title>
</head>
<script charset="utf-8" data-original="https://map.qq.com/api/gljs?v=1.exp&key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&libraries=visualization"></script>
<style type="text/css">
    html,
    body {
        height: 100%;
        margin: 0px;
        padding: 0px;
    }

    #container {
        width: 100%;
        height: 100%;
    }
</style>

<body onload="initMap()">
    <div id="container"></div>
    <script data-original="https://mapapi.qq.com/web/lbs/visualizationApi/demo/data/trail.js"></script>
    <script>
        function initMap() {
            var center = new TMap.LatLng(39.984104, 116.307503);

            //初始化地图
            var map = new TMap.Map("container", {
                zoom:12,//设置地图缩放级别
                center: center,//设置地图中心点坐标
                mapStyleId: "style1" //个性化样式
            });
            //初始化轨迹图并添加至map图层
            new TMap.visualization.Trail({
                pickStyle:function(item){ //轨迹图样式映射函数
                    return {
                        width: 2
                    }
                },
                startTime: 0,//动画循环周期的起始时间戳
                showDuration: 120,//动画中轨迹点高亮的持续时间
                playRate:30 // 动画播放倍速

            })
            .addTo(map)
            .setData(trailData);//设置数据
        }
    </script>
</body>

</html>

效果图

在线示例

作者:腾讯位置服务

链接:https://segmentfault.com/a/11...

来源:segmentfault

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

查看原文

认证与成就

  • 获得 223 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-09-10
个人主页被 6.4k 人浏览