Hilbert编码 思路和scala 代码

需求:

使用Hilbert 曲线对遥感影像瓦片数据进行编码,获取某个区域的编码值即可

Hilbert 曲线编码方式

思路

大致可以对四个方向的数据进行归类

  1. 左下
  2. 左上
  3. 右上
  4. 右下

这个也对应着编码的顺序

思考在不同Hilbert深度(阶)情况下的四个区域的取值范围

  1. 左下

    [ 0 , 4 n − 1 ) [0,4^{n-1}) [0,4n1)

  2. 左上

    [ 4 n − 1 ,   2   ×   4 n − 1 ) [4^{n-1}, \ 2\ \times \ 4^{n-1}) [4n1, 2 × 4n1)

  3. 右上

    [ 2   ×   4 n − 1 , 3 × 4 n − 1 ) [2\ \times\ 4^{n-1},3 \times4^{n-1}) [2 × 4n1,3×4n1)

  4. 右下

    [ 3   ×   4 n − 1 , 4 n ) [3 \ \times \ 4^{n-1}, 4^n) [3 × 4n1,4n)

因此可以使用递归的方式来进行生成数据

主要问题

左上角和右上角不需要做旋转操作

 // 左上角,待判定点不需要做任何调整,直接递归新子区域即可
 case (true, false)  =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), Point(bounds._1.lat,bounds._2.lon)), maxLevel - 1 ) +   Math.pow(4, maxLevel-2).toInt

// 右上角
      case (false, false) =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), bounds._2), maxLevel - 1) + 2 * Math.pow(4, maxLevel - 2).toInt

左下角和左上角需要考虑旋转问题

可以使用举例子的方式,来找到旋转后的关系

假设空间区域的左下角坐标为(10,20),右上角的坐标为(20,30),中心坐标为(15, 25)

同样以二阶图为例

这里点位置变更主要针对的是待判定的位置属于哪个区域!

Hilbert 编码区域左下区域(左侧为原始点位置,右侧为旋转后的点位置)

(18,22.5)=> (12.5,28)

(17.5,22) => (12,27.5)

Hilbert 编码区域右下区域

(12,27.5)=>(12.5, 28)

(12.5, 22) => (18, 27.5)

左下旋转后坐标生成方式

原始点位置-中心点位置 = (a,b)

结果 = 中心点位置 + (b,a)

例:(18,22.5) - (15,25) = (3,-2.5)

结果 = (15,25 ) + (-2.5, 3) = (12.5, 28)

右下旋转后坐标生成方式

原始点位置-中心点位置 = (a,b)

结果 = 中心点位置 -(b,a)

例: (12, 27.5) - (15,25) = (-3,2.5)

结果 = (15,25) -(2.5,-3) = (12.5,28)

思路捋清之后,可以考虑代码

// 左下角,
 case (true, true)   =>
        recursiveHilbertEncode(Point(lat_half + lon_reverse,lon_half + lat_reverse),   (bounds._1, Point(lat_half, lon_half)),maxLevel - 1)

// 右下角
 case (false, true)  =>
        recursiveHilbertEncode(Point(lat_half - lon_reverse,lon_half - lat_reverse), ((Point(lat_half,bounds._1.lon),Point(bounds._2.lat,lon_half))), maxLevel - 1) + 3 * Math.pow(4, maxLevel-2).toInt

需要对待判定点重新生成,但区域的经纬坐标可以不变,我们不关系点坐标怎么变换,我们只是需要最终的编码结果,保证递归正常运行即可

Hilbert 源码生成

input:

  1. 正方形边界两个对角顶点的坐标
  2. 最大Hilbert 层级(阶、深度)
  3. 待判定的区域两点坐标(或者是一个点)

output:

该区域(区域也是计算区域的中心点位置)、点所在的Hilbert 编码值

递归函数

recursiveHilbertEncode

  def recursiveHilbertEncode(point: Point, bounds:(Point,Point), maxLevel:Int): Int = {

    if (maxLevel == 1)
      return 0
    // 边界中心点
    val lat_half = (bounds._1.lat + bounds._2.lat) / 2 //x
    val lon_half = (bounds._1.lon + bounds._2.lon) / 2 //y


    // 反转差值
    val lat_reverse = point.lat - lat_half
    val lon_reverse = point.lon - lon_half


    (point.lat < lat_half, point.lon < lon_half) match {
      // 右上角 , p1,p2 不需要动,需要动的是边界和
      case (false, false) =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), bounds._2), maxLevel - 1) + 2 * Math.pow(4, maxLevel - 2).toInt


      // 右下角 向左旋转
      case (false, true)  =>
        recursiveHilbertEncode(Point(lat_half - lon_reverse,lon_half - lat_reverse), ((Point(lat_half,bounds._1.lon),Point(bounds._2.lat,lon_half))), maxLevel - 1) + 3 * Math.pow(4, maxLevel-2).toInt

      //左上
      case (true, false)  =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), Point(bounds._1.lat,bounds._2.lon)), maxLevel - 1 ) +   Math.pow(4, maxLevel-2).toInt


      // 左下角 向右旋转
      case (true, true)   =>
        recursiveHilbertEncode(Point(lat_half + lon_reverse,lon_half + lat_reverse),   (bounds._1, Point(lat_half, lon_half)),maxLevel - 1)

    }
  }

完整代码

测试代码没整理,有两个main函数,一个是零散的编码区域,一个是从0-15区域的编码结果

import HilbertCurve.getTileCode

object HilbertCurve {

  case class Point(lat: Double, lon: Double)


  // 递归查看Hilbert编码

  /**
   * 瓦片中心点坐标
   * 新bounds边界(其实也是用来定义边界的中心点的)
   * maxLevel
   * @return
   */
  def recursiveHilbertEncode(point: Point, bounds:(Point,Point), maxLevel:Int): Int = {

    if (maxLevel == 1)
      return 0
    // 边界中心点
    val lat_half = (bounds._1.lat + bounds._2.lat) / 2 //x
    val lon_half = (bounds._1.lon + bounds._2.lon) / 2 //y
//
//    println("边界中心点  :(" + lat_half + ", " + lon_half + ")")
//
//    println("瓦片中心点:(" + point.lat + ", " + point.lon + ")")
    // point 为瓦片的中心点

    // 反转差值
    val lat_reverse = point.lat - lat_half
    val lon_reverse = point.lon - lon_half


    (point.lat < lat_half, point.lon < lon_half) match {
      // 右上角 , p1,p2 不需要动,需要动的是边界和
      case (false, false) =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), bounds._2), maxLevel - 1) + 2 * Math.pow(4, maxLevel - 2).toInt
      //TODO: 问题
      // 右下角 向左旋转
      // 此处需要也对point 点进行旋转吗?,好像不需要,这里是沿着中轴线旋转180度
      case (false, true)  =>
        recursiveHilbertEncode(Point(lat_half - lon_reverse,lon_half - lat_reverse), ((Point(lat_half,bounds._1.lon),Point(bounds._2.lat,lon_half))), maxLevel - 1) + 3 * Math.pow(4, maxLevel-2).toInt

      //左上
      case (true, false)  =>
        recursiveHilbertEncode(point, (Point(lat_half, lon_half), Point(bounds._1.lat,bounds._2.lon)), maxLevel - 1 ) +   Math.pow(4, maxLevel-2).toInt


      //TODO: 左下角也有问题
      // 左下角 向右旋转
      case (true, true)   =>
        recursiveHilbertEncode(Point(lat_half + lon_reverse,lon_half + lat_reverse),   (bounds._1, Point(lat_half, lon_half)),maxLevel - 1)

    }
  }
  // 第一步函数:获取瓦片编码
  def getTileCode(p1: Point, p2: Point, bounds: (Point, Point), maxLevel: Int): Int = {

    // TODO:此处也默认 bounds 第一个点位于左下角
    // TODO: 此处默认p1 位于p2的左下角

    // 定义中心点 x,y值
    val x_half = (p1.lat + p2.lat)/2
    val y_half = (p1.lon + p2.lon)/2

    // 边界的中心点
    val encoder = recursiveHilbertEncode(Point(x_half,y_half), bounds, maxLevel)
    encoder
  }

  /**
   * 测试左下角的16个区域
   * @param args
   */
  def main2(args: Array[String]): Unit = {
    val bounds = (Point(10.0, 10.0), Point(20.0, 20.0))

    val maxLevel4 = 4

    // TODO: 左下角
    //  15 Error

//     最左下角的四个,但是此时如果是旋转过,再去查看右上角和左下角,就会有旋转问题
//
    val p1 = Point(10,10)
    val p2 = Point(11.25,11.25)

    val tileCode0 = getTileCode(p1,p2,bounds,maxLevel4)
    println(s"TIle Code0 is given region :$tileCode0")

    val p3 = Point(10,11.25)
    val p4 = Point(11.25,12.5)

    val tileCode1 = getTileCode(p3,p4,bounds,maxLevel4)
    println(s"TIle Code1 is given region :$tileCode1")

    val p5 = Point(11.25,11.25)
    val p6 = Point(12.5,12.5)
    val tileCode2 = getTileCode(p5,p6,bounds,maxLevel4)
    println(s"TIle Code2 is given region :$tileCode2")

    val p7 = Point(11.25,10)
    val p8 = Point(12.5,11.25)
    val tileCode3 = getTileCode(p7, p8, bounds, maxLevel4)
    println(s"TIle Code3 is given region :$tileCode3")

    println("==================")
    val a = Point(12.5,10)
    val b = Point(13.75,11.25)
    val tileCode4 = getTileCode(a, b, bounds, maxLevel4)
    println(s"TIle Code4 is given region :$tileCode4")

    val t1 = Point(13.75,10)
    val t2 = Point(15.0, 11.25)
    val tileCode5 = getTileCode(t1,t2,bounds,maxLevel4)
    println(s"Tile Code5 is given region:$tileCode5")

    val c = Point(13.75,11.25)
    val d = Point(15,12.5)
    val tileCode6 = getTileCode(c,d,bounds,maxLevel4)
    println(s"TIle Code6 is given region :$tileCode6")


    val b3 = Point(12.5,11.25)
    val b4 = Point(13.75,12.5)
    val tileCode7 = getTileCode(b3,b4,bounds,maxLevel4)
    println(s"TIle Code7 is given region :$tileCode7")

    println("=================")
    val e = Point(12.5 ,12.5)
    val f = Point(13.75, 13.75)
    val tileCode8 = getTileCode(e,f,bounds,maxLevel4)
    println(s"TIle Code8 is given region :$tileCode8")


    val g = Point(13.75, 12.5)
    val h = Point(15,13.75)
    val tileCode9 = getTileCode(g, h,bounds, maxLevel4)
    println(s"TIle Code9 is given region :$tileCode9")

    val c3 = Point(12.5,13.75)
    val c4 = Point(13.75,15)
    val tileCode11 = getTileCode(c3,c4, bounds, maxLevel4)
    println(s"TIle Code11 is given region :$tileCode11")
//     左下角的左上角的右上角有问题
    println("==========")
    val c1 = Point(11.25,13.75)
    val c2 = Point(12.5,15)
    val tileCode12 = getTileCode(c1,c2,bounds,maxLevel4)
    println(s"TIle Code12 is given region :$tileCode12")


    val d1 = Point(11.25,12.5)
    val d2 = Point(12.5,13.75)
    val tileCode13 = getTileCode(d1,d2,bounds,maxLevel4)
    println(s"TIle Code13 is given region :$tileCode13")

    val d3 = Point(10,12.5)
    val d4 = Point(11.25,13.75)
    val tileCode14 = getTileCode(d3,d4,bounds,maxLevel4)
    println(s"TIle Code14 is given region :$tileCode14")



    val d5 = Point(10,13.75)
    val d6 = Point(11.25,15)
    val tileCode15 = getTileCode(d5,d6,bounds,maxLevel4)
    println(s"TIle Code15 is given region :$tileCode15")



    // TODO; 右下角TODO Error
    // 58
//    val p74 = Point(15,10)
//    val p84 = Point(16.25,11.25)
//    val tileCode44 = getTileCode(p74, p84, bounds, maxLevel4)
//    println(s"Tile Code for given region: $tileCode44")


  }
  def main(args: Array[String]): Unit = {
    val bounds = (Point(10.0, 10.0), Point(20.0, 20.0))
    // 右上角  42
    val p14 = Point(18.75, 18.75)
    val p24 = Point(20, 20)
    val maxLevel4 = 4
    val tileCode14 = getTileCode(p14, p24, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode14")

    // 左上角 //21
    val p34 = Point(10, 18.75)
    val p44 = Point(11.25, 20)
    val tileCode24 = getTileCode(p34, p44, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode24")


    //   5
    val p54 = Point(13.75, 10)
    val p64 = Point(15, 11.25)

    val tileCode34 = getTileCode(p54, p64, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode34")

    // TODO 15 Error
    val p545 = Point(10, 13.75)
    val p645 = Point(11.25, 15)

    val tileCode344 = getTileCode(p545, p645, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode344")

    // 右下角

    // TODO Error
    // 58
    val p74 = Point(15,10)
    val p84 = Point(16.25,11.25)
    val tileCode44 = getTileCode(p74, p84, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode44")



    // 34
    val p1 = Point(16.25, 16.25)
    val p2 = Point(17.5, 17.5)
    val maxLevel = 3
    val tileCode = getTileCode(p1, p2, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode")

    // 10
    val p3 = Point(13.75, 13.75)
    val p4 = Point(15.0, 15.0)

    val tileCode2 = getTileCode(p3, p4, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode2")

    // 8
    val p5 = Point(12.5, 12.5)
    val p6 = Point(13.75, 13.75)

    val tileCode3 = getTileCode(p5, p6, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode3")

    // 32
    val p7 = Point(15.0, 15.0)
    val p8 = Point(16.25, 16.25)

    val tileCode4 = getTileCode(p7, p8, bounds, maxLevel4)
    println(s"Tile Code for given region: $tileCode4")


  }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/775784.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AutoX.js某音自动评论(一个函数,5秒完成)

背景 某音自动化评论&#xff0c;步骤简单&#xff0c;对版本兼容性要高&#xff08;不用节点id定位&#xff09; 思路 通过Intent直接跳转到视频*利用文字&#xff0c;描述&#xff08;正则&#xff09;等匹配输入框&#xff0c;发表评论 效果 某音自动化评论 已经封装成一…

专题二:Spring源码编译

目录 下载源码 配置Gradle 配置环境变量 配置setting文件 配置Spring源码 配置文件调整 问题解决 完整配置 gradel.properties build.gradle settiings.gradel 在专题一&#xff1a; Spring生态初探中我们从整体模块对Spring有个整体的印象&#xff0c;现在正式从最…

C盘扩容/扩大C盘的12个有效操作方法

对于许多计算机用户来说&#xff0c;C盘空间可能会成为一个问题&#xff0c;尤其是那些将计算机广泛用于工作、游戏和多媒体目的的用户。如果您发现C驱动器上的空间不足&#xff0c;则需要对其进行扩展以提高系统的整体性能。在这篇文章中&#xff0c;我们将探讨C盘扩展的12种操…

【计算智能】遗传算法(二):基本遗传算法在优化问题中的应用【实验】

前言 本系列文章架构概览&#xff1a; 本文将介绍基本遗传算法在解决优化问题中的应用,通过实验展示其基本原理和实现过程&#xff1a;选取一个简单的二次函数作为优化目标&#xff0c;并利用基本遗传算法寻找其在指定范围内的最大值。 2. 基本遗传算法&#xff08;SGA&#x…

Laravel 谨慎使用Storage::append()

在 driver 为 local 时&#xff0c;Storage::append()在高并发下&#xff0c;会存在丢失数据问题&#xff0c;文件被覆写&#xff0c;而非尾部添加&#xff0c;如果明确是本地文件操作&#xff0c;像日志写入&#xff0c;建议使用 Illuminate\Filesystem\Filesystem或者php原生…

跨境干货|最新注册Google账号方法分享

谷歌账号对做跨境外贸业务的人来说是刚需&#xff0c;目前来说大部分的海外社媒平台、工具都可以用谷歌账号来注册。但是仍然有很多朋友并不知道如何注册这个谷歌账号&#xff0c;今天就来给大家分享2个注册谷歌账号的方法&#xff0c;一个是手机号注册&#xff0c;一个是如何跳…

品牌营销新趋势:独立站如何与TikTok达人互动共赢

在当今数字化营销时代&#xff0c;独立站与TikTok达人的互动营销已成为一种高效的推广方式。通过精心设计的互动环节&#xff0c;独立站不仅能够增强用户参与感&#xff0c;还能显著提升带货效果。本文Nox聚星将和大家探讨独立站如何与TikTok达人进行高效互动&#xff0c;吸引用…

如何有效管理你的Facebook时间线?

Facebook作为全球最大的社交平台之一&#xff0c;每天都有大量的信息和内容在用户的时间线上展示。有效管理你的Facebook时间线&#xff0c;不仅可以提升用户体验&#xff0c;还能够帮助你更好地控制信息流和社交互动。本文将探讨多种方法和技巧&#xff0c;帮助你有效管理个人…

开发者评测|操作系统智能助手OS Copilot

操作系统智能助手OS Copilot 文章目录 操作系统智能助手OS CopilotOS Copilot 是什么优势功能 操作步骤创建实验重置密码创建Access Key配置安全组安装 os-copilot环境变量配置功能评测命令行模式多轮交互模式 OS Copilot 产品体验评测反馈OS Copilot 产品功能评测反馈 参考文档…

C++基础(七):类和对象(中-2)

上一篇博客学的默认成员函数是类和对象的最重要的内容&#xff0c;相信大家已经掌握了吧&#xff0c;这一篇博客接着继续剩下的内容&#xff0c;加油&#xff01; 目录 一、const成员&#xff08;理解&#xff09; 1.0 引入 1.1 概念 1.2 总结 1.2.1 对象调用成员函数 …

使用 mongo2neo4j 和 SemSpect 通过各种方式进行图探索

用于可视化和探索每个 MEAN 堆栈背后的数据图的 ETL 您是否正在努力回答有关 MEANS Web 服务数据的紧急问题&#xff1f;哪里有 BI 可以快速回答“上个季度哪些亚洲的artisan.plus 用户触发了订单&#xff1f;”这个问题&#xff0c;而无需编写查询&#xff1f;使用 mongo2neo4…

Linux:进程间通信(一.初识进程间通信、匿名管道与命名管道、共享内存)

上次结束了基础IO&#xff1a;Linux&#xff1a;基础IO&#xff08;三.软硬链接、动态库和静态库、动精态库的制作和加载&#xff09; 文章目录 1.认识进程间通信2.管道2.1匿名管道2.2pipe()函数 —创建匿名管道2.3匿名管道的四种情况2.4管道的特征 3.基于管道的进程池设计4.命…

FineBI在线学习资源-数据处理

FineBI在线学习资源汇总&#xff1a; 学习资源 视频课程 帮助文档 问答 数据处理学习文档&#xff1a; 相关资料&#xff1a; 故事背景概述-https://help.fanruan.com/finebi6.0/doc-view-1789.html 基础表处理-https://help.fanruan.com/finebi6.0/doc-view-1791.html …

软件设计之Java入门视频(11)

软件设计之Java入门视频(11) 视频教程来自B站尚硅谷&#xff1a; 尚硅谷Java入门视频教程&#xff0c;宋红康java基础视频 相关文件资料&#xff08;百度网盘&#xff09; 提取密码&#xff1a;8op3 idea 下载可以关注 软件管家 公众号 学习内容&#xff1a; 该视频共分为1-7…

Ubuntu 24.04 上安装 Kubernetes,超级详细的教程!

Kubernetes 是一个免费的开源容器编排工具&#xff0c;它允许基于容器的应用程序的自动化部署、扩展和管理。 我们将介绍如何使用 Kubeadm 逐步在 Ubuntu 24.04 上安装 Kubernetes 此次演示中&#xff0c;我们将使用以下三个 Ubuntu 24.04 实例 Instance 1 : Master Node (k…

计算机视觉——opencv快速入门(二) 图像的基本操作

前言 上一篇文章中我们介绍了如何配置opencv&#xff0c;而在这篇文章我们主要介绍的是如何使用opencv来是实现一些常见的图像操作。 图像的读取&#xff0c;显示与存储 读取图像文件 在opencv中我们利用imread函数来读取图像文件,函数语法如下&#xff1a; imagecv2.imre…

植物大战僵尸融合版最新版1.0下载及安装教程

《植物大战僵尸融合版》最新版1.0已经发布&#xff0c;为粉丝们带来了全新的游戏体验。这个版本由B站UP主蓝飘飘fly精心打造&#xff0c;引入了创新的植物融合玩法&#xff0c;让玩家可以享受策略和创意的结合。以下是游戏的详细介绍和安装指南&#xff1a; 游戏特色介绍 全新…

TF-IDF计算过程一步步推导详解含代码演示

相关概念 TF-IDF TF-IDF&#xff08;Term Frequency–Inverse Document Frequency&#xff09;是一种用于资讯检索与文本挖掘的常用加权技术。TF-IDF是一种统计方法&#xff0c;用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在…

lua入门(2) - 数据类型

前言 本文参考自: Lua 数据类型 | 菜鸟教程 (runoob.com) 希望详细了解的小伙伴还请查看上方链接: 八个基本类型 type - 函数查看数据类型: 测试程序: print(type("Hello world")) --> string print(type(10.4*3)) --> number print(t…

pdf可以删除其中一页吗?6个软件教你快速进行pdf编辑

pdf可以删除其中一页吗&#xff1f;6个软件教你快速进行pdf编辑 编辑PDF文件并删除特定页面是处理文档时常见的需求&#xff0c;特别是在需要定制或精简文件内容时。以下是几款广受欢迎的PDF编辑软件&#xff0c;它们提供了强大的页面删除功能&#xff0c;帮助用户轻松管理和修…