地图下载器工具-Java

瓦片下载

  • 要点1

  • 使用瓦片的访问地址直接下载瓦片
    主要用到的是HttpURLConnection去访问,为了避免被封杀ip,设置了两个请求参数referer,User-Agent,目前还未被封杀。
@Overridepublic void run() {// 定义在try外面,用于在finally关闭资源链接HttpURLConnection urlConnection = null;InputStream inputStream = null;OutputStream outputStream = null;URL url = null;try {File file = new File(downloadDir + z + File.separator + x + File.separator + y + ".jpg");url = new URL(String.format(link, (int) (Math.random() * 4), x, y, z));// 如果本地没有图片if (!file.exists()) {urlConnection = (HttpURLConnection) url.openConnection();urlConnection.setConnectTimeout(2000);urlConnection.setRequestMethod("GET");urlConnection.setRequestProperty("referer", "域名来源地址");urlConnection.addRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0");urlConnection.connect();if (urlConnection.getResponseCode() == 200) {if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}file.createNewFile();inputStream = urlConnection.getInputStream();outputStream = new FileOutputStream(file);byte[] bytes = new byte[1024];int len = 0;while ((len = inputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);}outputStream.close();inputStream.close();synchronized (failCount) {successCount++;}} else {synchronized (successCount) {logger.info("下载失败瓦片链接" + url);failCount++;}}} else {synchronized (successCount) {existCount++;}}} catch (Exception e) {synchronized (successCount) {logger.info("下载失败瓦片链接" + url);failCount++;}}}
  • 要点2

  • 根据经纬度与级别计算瓦片的编号。经过多次测试与计算,发现当最大经度为180时,会计算出多余并且不存在的瓦片编号,所以设置为179.99999;当纬度要是为-90或90的时候,由于极地的极大化,也可以认为是投影计算的规则,这里使用tan计算,所以会照成计算数据的不准确,按照正常的经纬度坐标系也是-85~85,所以这里做了限制。
    /*** 根据经纬度计算行列号* * @param lonlat* @param level* @return*/public static TileRowColAndCount getTileColRow(String[] lonlat, Integer level) {double maxlon = Math.max(Double.valueOf(lonlat[0]), Double.valueOf(lonlat[2])) == 180.0 ? 179.99999: Math.max(Double.valueOf(lonlat[0]), Double.valueOf(lonlat[2]));double minlon = Math.min(Double.valueOf(lonlat[0]), Double.valueOf(lonlat[2]));double maxlat = Math.max(Double.valueOf(lonlat[1]), Double.valueOf(lonlat[3])) == 90.0 ? 85.0: Math.max(Double.valueOf(lonlat[1]), Double.valueOf(lonlat[3]));double minlat = Math.min(Double.valueOf(lonlat[1]), Double.valueOf(lonlat[3])) == -90.0 ? -85.0: Math.min(Double.valueOf(lonlat[1]), Double.valueOf(lonlat[3]));long startCol = Math.round(Math.floor((minlon + 180.0) / 360.0 * (1 << level)));long endCol = Math.round(Math.floor((maxlon + 180.0) / 360.0 * (1 << level)));long startRow = Math.round(Math.floor((1.0- Math.log(Math.tan(maxlat * Math.PI / 180.0) + 1.0 / Math.cos(maxlat * Math.PI / 180.0)) / Math.PI)/ 2.0 * (1 << level)));long endRow = Math.round(Math.floor((1.0- Math.log(Math.tan(minlat * Math.PI / 180.0) + 1.0 / Math.cos(minlat * Math.PI / 180.0)) / Math.PI)/ 2.0 * (1 << level)));return new TileRowColAndCount(startRow, endRow, startCol, endCol);}
  • 根据计算的瓦片编号循环拼接成瓦片下载地址进行下载
/*** 执行下载* * @param range       范围* @param link        下载链接* @param downloadDir 下载到本地的目录* @param level       级别*/private void startDownload(TileRowColAndCount tileColRow, String link, String downloadDir, Integer level) {// 创建线程池ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(20, 100, 500, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());// 遍历列行下载for (long col = tileColRow.startCol; col <= tileColRow.endCol; col++) {for (long row = tileColRow.startRow; row <= tileColRow.endRow; row++) {// 线程池执行下载threadPoolExecutor.execute(new DownloadMapTileThread(link, downloadDir, col, row, level));}}threadPoolExecutor.shutdown(); // 关闭线程池while (!threadPoolExecutor.isTerminated()) {} // 所有任务被执行完毕时继续往下执行}

瓦片拼接

  • 原理很简单:使用BufferedImage创建一张新的图片,根据配置的下载信息,拿出对应瓦片编号的图片,添加到新图片的对应位置。
    图片范围大小的限制,因为当图片太大,超过了的Integer范围,就会抛出异常。
    /*** 拼接* * @param tileColRow* @param level* @return*/private BufferedImage executeMergeImage(TileRowColAndCount tileColRow, int level) {// 生成新图片BufferedImage mergeImage = null;try {// 图片范围大小if ((int) ((tileColRow.endCol - tileColRow.startCol + 1) * 256) < 46340&& (int) ((tileColRow.endRow - tileColRow.startRow + 1) * 256) < 46340) {mergeImage = new BufferedImage((int) ((tileColRow.endCol - tileColRow.startCol + 1) * 256),(int) ((tileColRow.endRow - tileColRow.startRow + 1) * 256), BufferedImage.TYPE_INT_RGB);// 合并所有子图片到新图片int wx = 0;int w1 = 0;int h1 = 0;for (long col = tileColRow.startCol; col <= tileColRow.endCol; col++) {int wy = 0;for (long row = tileColRow.startRow; row <= tileColRow.endRow; row++) {File file = new File(downloadDir + level + File.separator + col + File.separator + row + ".jpg");if (file.exists() && (file.length() > 0)) {BufferedImage img = ImageIO.read(file);w1 = img.getWidth();h1 = img.getHeight();// 从图片中读取RGBint[] ImageArrayOne = new int[w1 * h1];ImageArrayOne = img.getRGB(0, 0, w1, h1, ImageArrayOne, 0, w1); // 逐行扫描图像中各个像素的RGB到数组中mergeImage.setRGB(wx, wy, w1, h1, ImageArrayOne, 0, w1); // 设置上半部分或左半部分的RGB} else {continue;}wy += 256;}wx += 256;}} else {logger.info("拼接后的图片超出范围,请小区域拼接再导入数据库融合!");}} catch (IOException e) {e.printStackTrace();}return mergeImage;}

使用gdal给拼接后的图片添加坐标信息

  • gdal的使用,这是一个第三方库,使用的时候非常的坑,相关资料又少,只有多次尝试总结,希望这篇文章能对使用者有所帮助。

    1、下载完整的官网包,这里我使用的是3.0.0版本,直接点击SDKShell.bat文件便可开启工具,主要使用gdalinfo [图片绝对路径] 查看图片的坐标信息

    2、Java程序配置gdal的使用,这一步坑最多,第一个是说需要配置gdal的依赖库,报错指明找不到gdalalljni.dll,则将改文件放到jdk/bin/目录下,发现依然报错,折腾半天,把所有的dll文件全部放到jdk/bin或jdk/lib/下,最后执行,能运行成功,但是还有报错说找不到proj.db文件,琢磨许久配置PROJ.LIB的环境变量,值为D:\map\release-1911-x64-gdal-3-0-0-mapserver-7-4-0\bin\proj6\share,依据自己目录来设置,这个时候再程序才能正常运行。以下是主要的程序代码:

    • 知识要点

      • geoTransform,这是一个仿射变换六参数

        • geoTransform[0]:左上角x坐标
        • geoTransform[1]:东西方向空间分辨率
        • geoTransform[2]:x方向旋转角
        • geoTransform[3]:左上角y坐标
        • geoTransform[4]:y方向旋转角
        • geoTransform[5]:南北方向空间分辨率

        影像中任意一个像素的坐标可以用下式计算:

        默认情况下,a12、a21为0。

      • 根据所下载的瓦片编号,计算出拼接后的大图对应的四个点的经纬度

      • SpatialReference坐标参考类来设置坐标信息

      • 原来使用的是4326坐标系,但是在发布图层的时候发现图片纬度被压扁,照成纬度不对位,使用gdalinfo 查看猜测是有个映射属性导致,但是设置都不见生效,后面还是使用3857坐标系,这样就需要将经纬度转成实际的距离米(m)。

    gdal.AllRegister();Dataset sourceDataset = gdal.Open(targetDir + level + ".tif", gdalconstConstants.GA_Update);setGdalOfMercator(sourceDataset, tileColRow, level);/*** 给拼接后的图片设置位置信息,墨卡托*/public static void setGdalOfMercator(Dataset sourceDataset, TileRowColAndCount tileColRow, Integer level) {// 六参数信息double[] geoTransform = sourceDataset.GetGeoTransform();// 设置要裁剪的起始像元位置,以及各方向的像元数double startX = MercatorAndLatlonExchangeUtil.getMercatorLon((360.0 * (double) tileColRow.startCol) / (1 << level) - 180.0);double endX = MercatorAndLatlonExchangeUtil.getMercatorLon((360.0 * (double) (tileColRow.startCol + (tileColRow.endCol - tileColRow.startCol + 1)))/ (1 << level) - 180.0);double startLat = (360 * Math.atan(Math.pow(Math.E,(1 - Math.pow(2, 1.0 - level) * (tileColRow.startRow - (tileColRow.endRow - tileColRow.startRow + 1)+ sourceDataset.GetRasterBand(1).getYSize() / 256.0)) * Math.PI)))/ Math.PI - 90;double endLat = (360 * Math.atan(Math.pow(Math.E,(1 - Math.pow(2, 1.0 - level)* (tileColRow.startRow + sourceDataset.GetRasterBand(1).getYSize() / 256.0)) * Math.PI)))/ Math.PI - 90;// 转成墨卡托坐标double startY = MercatorAndLatlonExchangeUtil.getMercatorLat(startLat);double endY = MercatorAndLatlonExchangeUtil.getMercatorLat(endLat);// 图片的像素大小int clipX = sourceDataset.GetRasterBand(1).getXSize();int clipY = sourceDataset.GetRasterBand(1).getYSize();// 左上角起始点geoTransform[0] = startX;geoTransform[3] = startY;// 设置空间分辨率geoTransform[1] = (endX - startX) / clipX;geoTransform[5] = (endY - startY) / clipY;// 偏率geoTransform[2] = 0.0;geoTransform[4] = 0.0;SpatialReference ref = new SpatialReference();ref.ImportFromEPSG(3857);sourceDataset.SetGeoTransform(geoTransform);sourceDataset.SetSpatialRef(ref);// 释放资源sourceDataset.delete();}
  • 不出意外,拼接后的图片用gdalinfo验证得出如下信息:

将程序打包

  • 需将gdal的dll跟打包的jar放于同一目录下,设置PROJ.LIB的环境变量,值为D:\map\release-1911-x64-gdal-3-0-0-mapserver-7-4-0\bin\proj6\share,不想设置那么麻烦,我把proj6文件也放到jar所在目录,做了一个bat文件启动,downloadconfig.properties配置文件放于当前目录的上一级目录
@echo offset SDK_ROOT=%~dp0
set SDK_ROOT=%SDK_ROOT:\\=\%:setenv
SET "PROJ_LIB=%SDK_ROOT%proj6\SHARE"java -jar ./downloadTileUtil.core-1.0.0-jar-with-dependencies.jar ../downloadconfig.properties@pause
  • 将jai_imageio-1.1.jar包放置jre/lib/ext/下,这是因为程序中生成tiff文件的时候用到这些依赖,而执行jar的运行环境没有这个jar包,所以需要单独添加jar包到运行环境中(有可能需要三个jai类型的jar),不然打包出来的程序运行拼接的时候会报错

地图下载器工具-Java相关推荐

  1. 【PC工具】离线地图图片地图瓦片下载神器map-download地图下载器

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. 最近玩邻国大神的在M5core2上显示地图,类似手机上的导航软件,可以放大缩小,左右移 ...

  2. 地图旋转_如何使用万能地图下载器计算七参数

    1. 概述 在测绘行业里,坐标转换始终是饶不开的问题,经常会遇到,而坐标转换有相同椭球之间的转换,也有不同椭球之间的转换.如果是不同的椭球体之间的转换,就需要用到七参数,七参数可以通过控制点计算得到, ...

  3. bigemap中下载边界_BIGEMAP地图下载软件-BIGEMAP地图下载器下载v19.2.0.0-西西软件下载...

    BIGEMAP地图下载器是一款全球地图下载软件.用户使用软件可直接选择到卫星图片,电子地图等,方便用户在电脑上进行查看搜寻.有需要的用户欢迎下载使用. BIGEMAP地图下载器介绍 BIGEMAP地图 ...

  4. .NET破解之太乐地图下载器【非暴破】

    不知不觉,接触破解逆向已经三个月了,从当初的门外汉到现在的小白,这个过程只有经历过才知道其中的苦与乐: 有无知.困惑.痛苦.惊喜.彻悟.欣慰-- 有无助的软件脱壳,茫然的代码分析,有无趣的反复测试, ...

  5. 如何解决万能地图下载器下载的地图和选择范围不一致

    有的朋友在下载用万能地图下载器下载地图的时候下载了某个多边形的地图,但是导出地图后发现和自己的下载范围不一致,这是怎么回事呢,今天就给大家讲讲如何解决这个问题. 工具/原料 水经注万能地图下载器 方法 ...

  6. 那些年,我们用过的地图下载器

    作为地信.遥感.地质.测绘等专业的同学,下载电子地图.遥感影像应该都接触过,现在XX地图下载器广告满天飞,虽然各位同学唯唯诺诺,还是被群主踢出了.现在把自己那些年珍藏的地图下载器分享给大家.对各种下载 ...

  7. 万能地图下载器下载谷歌卫星地图在CAD中套合

    手里的矢量图需要和影像进行叠加查看效果,一般国内的矢量数据一般是80.54或者2000坐标系,这时候就需要对下载的影像进行一下坐标转换才可以同矢量地图叠加上.这里以CAD为例,讲解一下如何将万能地图下 ...

  8. 如何使用新版本的万能地图下载器下载谷歌电子地图

    现在基本上所有的朋友都使用的是新版本的万能地图下载器,基本的操作大家应该都会了,今天继续新版本的教程,今天介绍的是如何使用新版本的万能地图下载器下载谷歌电子地图. 工具/原料 万能地图下载器 方法/步 ...

  9. 如何在新版的万能地图下载器上作标注

    新版本的万能地图上线之后,无论从界面还是操作上面都变得比老版本更加的好用,可能有些朋友对新版本有些操作还不是很熟悉,那今天继续系列教程,今天讲解的是如何在新版本的万能地图下载器上作标注. 工具/原料 ...

最新文章

  1. powershell真香
  2. python每个if条件后面都要使用冒号_每一个条件后面都要使用冒号。(2.0分)_学小易找答案...
  3. OpenLayer + Ags 综合应用(一)--OpenLayer 地图展现
  4. LockSupport的源码实现原理以及应用
  5. AGC 019F.Yes or No(思路 组合)
  6. Python3.x爬虫教程:爬网页、爬图片、自己主动登录
  7. Ext JS 6学习文档-第8章-主题和响应式设计
  8. android之Fragment(官网资料翻译)
  9. c#使用私有构造方法
  10. 【软硬链接总结】描述linux下软链接和硬链接的区别(面试题)
  11. MATLAB/Simulink搭建电动汽车整车七自由度模型
  12. win10怎么把c盘锁住_Win10 C盘怎么瘦身?史上最强C盘清理方案
  13. 【GD32F407】 读写内部flash函数
  14. MATLAB——数据类型
  15. SPSS 随机区组秩和检验
  16. Unity3D 太空射击游戏学习笔记
  17. php银行卡号查询接口,银行卡归属地查询
  18. MyEclipse 安装教程
  19. 有氧运动和无氧运动 的能量消耗问题
  20. 【元宵节】中国人民大学与加拿大女王大学金融硕士项目与你的那份“缘”

热门文章

  1. 尚硅谷大数据技术Spark教程-笔记02【SparkCore(核心编程,map、mapPartitions、mapPartitionsWithIndex、flatMap、glom、groupBy)】
  2. oracle可视化图标,Oracle Data Visualization Desktop 试用
  3. 期权定价中的BAW和CRR模型
  4. DaoCloud docker加速器地址获取以及mac系统如何配置镜像加速
  5. action请求_深圳嘉华学校之Action方法返回类型
  6. 震惊科学界!DeepMind AI破解「蛋白质折叠」难题
  7. mPush实战笔记2安装redis
  8. Github上收集微信小程序源码
  9. 资源福利网站「迅雷小站」-资源多无广告!
  10. 怎样用计算机算游戏时间,珍惜时间年龄计算器