草庐IT

mapboxgl加载tiff

GIS兵器库 2023-03-28 原文

缘起

近期在项目中遇到这么一个需求,需要在地图上展示一组格网数据,格网大小为2m*2m,地图api用的mapboxgl。起初拿到这个需要感觉很easy,在地图上添加一个fill图层就好啦。把格网面数据添加到地图上之后,在大比例尺下显示正常,但是当地图层级小于15级时,渲染出的结果会消失。

简单理一下原因,应该是在地图缩小后,每个网格所占的像素太小,所以就消失了。

mapboxgl在处理symbol图层的时候,会遇到点位自动避让问题,导致部分点位不显示。解决方法是把layout中的icon-allow-overlap设置为true,这样就相当于关闭了自动避让功能,所有点图标保持可见状态。但是针对fill图层却没有这么一个属性。

但是这种情况又需要查看数据,要如何实现呢?

首先分析下数据,我的原始数据是通过模型导出的tiff格式的栅格数据,然后在后台根据tiff格式数据中每个像素所在行列号以及其灰度值生成带属性的格网数据,其中像素的灰度值就是在渲染时需要分类展示的值。既然原始tiff数据的灰度值就是所用的属性值,那是不是直接添加到地图就好了。接下来的解决方案就是看是否能直接用mapboxgl直接加载tiff数据,并渲染出自己想要的效果。

mapboxgl加载tiff

查看mapboxgl文档,可以看到mapboxgl支持image图层,只需传入url和coordinates

// 添加至地图
map.addSource('some id', {
  type: 'image',
  url: 'https://www.mapbox.com/images/foo.png',
  coordinates: [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
]});

可是,当我把地址换成tiff数据时却报错了。下面为报错内容:

Could not load image because of The source image could not be decoded.. Please make sure to use a supported image type such as PNG or JPEG. Note that SVGs are not supported

可以简单理解为不支持tiff格式。

tiff文件解析

既然mapboxglimage图层不支持tiff格式,那是不是可以把tiff数据导出成png呢,于是使用arcmap打开了tiff数据,导出数据格式也支持png,但是在保存时又报错了。

经过分析,发现是tiff数据波段数量的原因,我的这份数据波段数为1,从网上下载了一份测试数据,波段数为3,可以成功导出。

在查找相关解决方案的时候,看到这么个工具,geotiff.js,可以通过js解析tiff数据并渲染,leaflet有个扩展就是用的这个工具,https://github.com/stuartmatthews/leaflet-geotiff。查看geotiff.js相关文档,发现其实用起来还是挺方便的,通过简单的代码实现的我的需求。

先使用geotiff.js解析tiff数据,再配合使用canvas绘制图片导出base64格式数据,然后就可以使用添加到mapboxgl图层了。

核心代码如下:

async function getData() {
  GeoTIFF.fromUrl(url).then(tiff => {
    console.log(tiff)
    getImage(tiff)
  });
}
async function getImage(tiff) {
  const image = await tiff.getImage();
  let bbox = await image.getBoundingBox();
  let data = await image.readRasters({
    samples: rgbBands // 波段数量,一个波段:[0],三个波段:[2,1,0]
  });
  let base64Image = getBase64Image(data)
  addToMapboxgl(base64Image)
}
function getBase64Image(data) {
  let thumbnailPixelHeight = data.height
  let thumbnailPixelWidth = data.width
  let canvas = document.createElement('canvas')
  canvas.width = thumbnailPixelWidth
  canvas.height = thumbnailPixelHeight
  let ctx = canvas.getContext("2d")
  let totalPixelCount = 0
  for (let y = 0; y < thumbnailPixelHeight; y++) {
    for (let x = 0; x < thumbnailPixelWidth; x++) {
      let colour = 'rgb(0, 0, 0, 0)' // let the default be no data (transparent)
      // 根据灰度值所在范围渲染颜色
      if (data[0][totalPixelCount] > 0) {
        if (data[0][totalPixelCount] > 50 && data[0][totalPixelCount] <= 55) {
          colour = `rgb(15, 255, 0, 1)`
        } else if (data[0][totalPixelCount] > 55 && data[0][totalPixelCount] <= 60) {
          colour = `rgb(155, 255, 0, 1)`
        } else if (data[0][totalPixelCount] > 60 && data[0][totalPixelCount] <= 65) {
          colour = `rgb(255, 255, 0, 1)`
        } else {
          colour = `rgb(255, 255, 0, 1)`
        }
      }
      ctx.fillStyle = colour
      ctx.fillRect(x, y, 1, 1)
      totalPixelCount++
    }
  }
  let canvasImage = canvas.toDataURL("image/png")
  return canvasImage
}
// 将图片添加到地图
function addToMapboxgl(image) {
  map.addSource('tiff-source', {
    "type": "image",
    "url": image,
    "coordinates": [
      [114.425597191307, 38.1091563484708],
      [114.538187627939, 38.1091563484708],
      [114.538187627939, 37.9627378349512],
      [114.425597191307, 37.9627378349512]
    ]
  });
  map.addLayer({
    id: 'tiff-layer',
    'type': 'raster',
    'source': 'tiff-source',
    'paint': {
      'raster-fade-duration': 0
    }
  });
}

本以为到这里问题已经解决,但是在查看地图时,发现图片图层数据叠加到底图有不小的偏移。

经过一番对比分析,发现原来是tiff数据的坐标系与地图坐标系不一致的导致的。我拿到的tiff数据坐标系为西安80的投影坐标系,在展示时配置的为wgs84地理坐标系,所以会有偏差。既然是坐标系问题,那就通过工具对tiff文件做下投影转换。这里用的是arcmap,打开ArcToolbox–>Data Management Tools–>Projections and Transformations–>Raster–>Project Raster

转换之后会发现,数据的行列值也会发生变化,也就是tiff图片的大小和形状都有所变化。

转换前:

转换后:

使用转换后的数据再次解析,然后叠加到地图,位置完全匹配。

最终展示方案

通过尝试发现,单独的图片展示时,由于图片分辨率固定,当地图等级放大到一定程度图片会被放大很多导致图片模糊不清,展示效果不理想;单独的格网面展示时,当地图等级缩小到一定程度,面图层则会消失,也就是文章开头提到的问题。

综上,根据自己的格网数据大小,判断在哪个等级格网面数据会消失,小于这个等级使用图片展示,大于这个等级用格网面展示,就可以完美的展示出想要的效果。

处理前效果:

处理后效果:

以上为有tiff栅格数据情况的解决方案,针对于只有格网面数据,而没有tiff栅格数据的情况要怎么解决呢?

如果在这组格网数据中,每个网格的属性中有他所在原始tiff数据的像素位置,以及原始tiff数据像素大小,就可以写一个类似上文中的getBase64Image方法,遍历每个网格,在网格对应的像素位置上绘制颜色,然后再通过canvas导出图片添加到地图。

总结

  1. mapboxglimage图层无法直接添加tiff栅格数据
  2. mapboxgl添加fill图层时,地图层级缩小到一定程度,面数据所占像素值过小无法显示
  3. tiff数据可以使用geotiff.js+canvas解析,得到base64的图片,添加到mapboxglimage图层
  4. 在解析tiff数据时,需注意它的坐标系、波段个数等信息
  5. 在做展示时可以image图层和fill图层结合展示,效果较好

参考资料:

  1. https://geotiffjs.github.io/geotiff.js/
  2. https://github.com/stuartmatthews/leaflet-geotiff
  3. https://www.cnblogs.com/arxive/p/6746570.html

原文地址:http://gisarmory.xyz/blog/index.html?blog=mapboxgl-geotiff

欢迎关注《GIS兵器库

本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名《GIS兵器库》(包含链接:  http://gisarmory.xyz/blog/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

有关mapboxgl加载tiff的更多相关文章

  1. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  4. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  5. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  6. ruby-on-rails - 是否可以让 ActiveRecord 为使用 :joins option? 加载的行创建对象 - 2

    我需要做这样的事情classUser'User',:foreign_key=>'abuser_id'belongs_to:gameendclassGame['JOINabuse_reportsONusers.id=abuse_reports.abuser_id','JOINgamesONgames.id=abuse_reports.game_id'],:group=>'users.id',:select=>'users.*,count(distinctgames.id)ASgame_count,count(abuse_reports.id)asabuse_report_count',:

  7. ruby - 运行 rackup private_pub.ru -s thin -E production 命令时无法加载此类文件 -- thin (LoadError) - 2

    我指的是pubrailscasttutorial并已正确执行所有步骤,但在运行最后一个命令时,即rackupprivate_pub.ru-sthin-Eproduction为了架设faye服务器,我收到以下错误:/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in`require':cannotloadsuchfile--thin(LoadError)from/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in`require'from/var/lib/gems/1.9.1/gems

  8. ruby - libxml-ruby 无法在 x86_64 上加载 - 2

    我们在服务器端遇到libxml-rubygem的问题可能是因为它使用x86_64架构:$uname-aLinuxip-10-228-171-642.6.21.7-2.fc8xen-ec2-v1.0#1SMPTueSep110:25:30EDT2009x86_64GNU/Linuxrequire'libxml'LoadError:/usr/local/ruby-enterprise/lib/ruby/gems/1.8/gems/libxml-ruby-1.1.4/lib/libxml_ruby.so:invalidELFheader-/usr/local/ruby-enterprise/

  9. Ruby 不从 stdlib 加载 CSV - 2

    我不太确定为什么这不起作用,我一直在寻找解决方案。很简单,我正在运行一个执行require'CSV'的小脚本。,它在我的Mac1.9.3-p327上运行良好,但在p374上的服务器上无法运行。我得到的错误是/home/deployer/.rbenv/versions/1.9.3-p374/lib/ruby/1.9.1/rubygems/custom_require.rb:36:inrequire':cannotloadsuchfile--CSV(LoadError)from/home/deployer/.rbenv/versions/1.9.3-p374/lib/ruby/1.9.1/

  10. ruby - 无法加载此类文件——脚本/rails : Getting this error while remote debugging through RubyMine - 2

    我在通过RubyMineIDE进行远程调试时遇到以下错误。$bundleexecrdebug-ide--port1234--script/railsserverFastDebugger(ruby-debug-ide0.4.9)listenson:1234/home/amit/.rvm/gems/ruby-1.9.3-p125/gems/ruby-debug-ide19-0.4.12/lib/ruby-debug-ide.rb:123:in`debug_load'/home/amit/.rvm/gems/ruby-1.9.3-p125/gems/ruby-debug-ide19-0.4.

随机推荐