Apache Sedona和Spark将geojson瓦片化例子

news2025/1/13 8:13:20

Apache Sedona很方便读取geojson、ShapeFile、geopackage等文件,提供了很多spark sql函数和rdd算子。下面例子主要用于熟悉spark和sedona的使用。

引入的maven包

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>GeoJsonToMvt</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <scala.version>2.13.8</scala.version>
        <geotrellis.version>3.7.1</geotrellis.version>
        <spark.version>3.3.4</spark.version>
        <spray.json.version>1.3.6</spray.json.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.13</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.13</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.13</artifactId>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>io.spray</groupId>
            <artifactId>spray-json_2.13</artifactId>
            <version>${spray.json.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.13.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.13.4</version>
        </dependency>
        <dependency>
            <groupId>org.locationtech.jts</groupId>
            <artifactId>jts-core</artifactId>
            <version>1.19.0</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-main</artifactId>
            <version>25.4</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>25.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.sedona</groupId>
            <artifactId>sedona-viz-3.0_2.13</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.sedona</groupId>
            <artifactId>sedona-core-3.0_2.13</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.sedona</groupId>
            <artifactId>sedona-sql-3.0_2.13</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.sedona</groupId>
            <artifactId>sedona-spark-3.0_2.13</artifactId> <!-- 替换为你的 Spark 和 Scala 版本 -->
            <version>1.6.1</version> <!-- 替换为你的 Sedona 版本 -->
        </dependency>
        <dependency>
            <groupId>org.locationtech.proj4j</groupId>
            <artifactId>proj4j</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>osgeo</id>
            <name>OSGeo Release Repository</name>
            <url>https://repo.osgeo.org/repository/release/</url>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

下面是java实现的spark代码 部分是由AI(Gemini2.0)生成代码

import org.apache.sedona.spark.SedonaContext;
import org.apache.sedona.viz.core.Serde.SedonaVizKryoRegistrator;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.Metadata;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.io.WKTReader;
import scala.Tuple3;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.lang.Math.cos;
import static java.lang.Math.floor;
import static java.lang.Math.pow;
import static java.lang.Math.toRadians;
import static org.apache.spark.sql.functions.*;

public class GeoJsonToTilesJava {

    public static void main(String[] args) throws Exception {
        System.setProperty("org.geotools.referencing.forceXY", "true");
        // 创建SparkSession
        SparkSession config = SedonaContext.builder()
                .master("local[*]") // Delete this if run in cluster mode
                .appName("readTestJava") // Change this to a proper name
                .config("spark.kryo.registrator", SedonaVizKryoRegistrator.class.getName())
                .config("spark.sql.extensions", "org.apache.sedona.viz.sql.SedonaVizExtensions,org.apache.sedona.sql.SedonaSqlExtensions")
                .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
                .getOrCreate();

        SparkSession sedona = SedonaContext.create(config);


        Dataset<Row> df = sedona.read().format("geojson")
                .option("multiPolygon", "true")
                .load("src/main/resources/guangdong.json")
                .selectExpr("explode(features) as features") // Explode the envelope to get one feature per row.
                .select("features.*") // Unpack the features struct.
                .withColumn("name", expr("properties['name']")).drop("properties").drop("type");
        Dataset<Row> df1 = df;
        df1.show();

        //Dataset<Row> filteredDF = df.filter(expr("ST_Y(ST_Centroid(ST_GeomFromGeoJSON(geometry))) BETWEEN -85.05 AND 85.05"));
        // 创建坐标转换器
        try {
            Dataset<Row> geomDF = df.withColumn("sedona_geom",
                    expr("ST_AsEWKT(ST_Transform(geometry,'epsg:4326','epsg:3857',true))"));

            // 使用 UDF 创建 Sedona Geometry 列
            //Dataset<Row> geomDF = df.withColumn("sedona_geom", callUDF("createSedonaGeometry", col("geometry")));
            geomDF.show();

            // 修改: getTileIndexUDF 的返回值类型, 并保证如果坐标为空,则返回一个 null struct
            org.apache.spark.sql.api.java.UDF2<String, Integer, Row> getTileIndexUDF = (centroidStr, zoom)->{
                if (centroidStr == null || centroidStr.isEmpty()) {
                    return RowFactory.create(null, null, null);
                } else {
                    String[] str = centroidStr.replace("POINT (", "").replace(")", "").split(" ");
                    List<Double> coordinates = new ArrayList<>();
                    coordinates.add(Double.parseDouble(str[0]));
                    coordinates.add(Double.parseDouble(str[1]));
                    Tuple3<Integer,Integer,Integer> tileIndex = getTileIndex(coordinates,zoom);
                    return RowFactory.create(tileIndex._1(), tileIndex._2(), tileIndex._3());
                }
            };
            //CRS.decode("EPSG:4326", true);
            // 注册 UDF
            StructType tileIndexSchema = new StructType(new StructField[]{
                    new StructField("x", DataTypes.IntegerType, true, Metadata.empty()),
                    new StructField("y", DataTypes.IntegerType, true, Metadata.empty()),
                    new StructField("z", DataTypes.IntegerType, true, Metadata.empty())
            });
            sedona.udf().register("getTileIndexUDF", getTileIndexUDF, tileIndexSchema);



            org.apache.spark.sql.api.java.UDF1<String,String> getCentroid = (geomStr)->{
                if (geomStr == null || geomStr.isEmpty()) {
                    return null;
                } else {
                    GeometryFactory factory = new GeometryFactory();
                    WKTReader wktReader = new WKTReader(factory);
                    org.locationtech.jts.geom.Geometry geom = wktReader.read(geomStr);
                    return geom.getCentroid().toString();
                }
            };
            // 注册 UDF
            sedona.udf().register("getCentroid", getCentroid, DataTypes.StringType);

            List<Integer> zooms = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14);

            //  关键修改:直接在原始的 geomDF 上面进行循环操作
            for (Integer zoom : zooms){
                Dataset<Row> tilesDF = geomDF.withColumn("centroid",callUDF("getCentroid", col("sedona_geom")))
                        .withColumn("tile", callUDF("getTileIndexUDF", col("centroid"), lit(zoom)));
                tilesDF.show();
                Dataset<Row>  groupedTiles = tilesDF
                        .filter(col("tile").isNotNull())
                        .groupBy(col("tile"))
                        .agg(collect_list("sedona_geom").alias("features"));
                groupedTiles.write().format("json").mode("overwrite").save("D:/temp/output/zoom_" + zoom);
            }
            sedona.stop();

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static Tuple3<Integer, Integer, Integer> getTileIndex(List<Double> coordinates, int zoom) {
        if (coordinates == null || coordinates.isEmpty()) {
            return null;
        } else {
            double x = coordinates.get(0);
            double y = coordinates.get(1);
            double res = 156543.03392 * cos(toRadians(0)) / pow(2, zoom);
            int tileX = (int) floor((x + 20037508.34) / (res * 256));
            int tileY = (int) floor((20037508.34 - y) / (res * 256));

            return new Tuple3<>(tileX,tileY,zoom);
        }
    }
}

参考链接
https://sedona.apache.org/1.7.0/tutorial/sql/
https://sedona.apache.org/1.7.0/api/sql/Function/
谷歌双子座AIhttps://aistudio.google.com/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker 自建rustdesk服务器测试

参考https://blog.csdn.net/tootsy_you/article/details/130010564 注意&#xff1a; docker-compose.yml version: 3networks:rustdesk-net:external: falseservices:hbbs:container_name: hbbsports:- 21115:21115- 21116:21116- 21116:21116/udp- 21118:21118image: rust…

检验统计量与p值笔记

一、背景 以雨量数据为例&#xff0c;当获得一个站点一年的日雨量数据后&#xff0c;我们需要估计该站点的雨量的概率分布情况&#xff0c;因此我们利用有参估计的方式如极大似然法估计得到了假定该随机变量服从某一分布的参数&#xff0c;从而得到该站点的概率密度函数&#x…

每日十题八股-2025年1月12日

1.为什么四次挥手之后要等2MSL? 2.服务端出现大量的timewait有哪些原因? 3.TCP和UDP区别是什么&#xff1f; 4.TCP为什么可靠传输 5.怎么用udp实现http&#xff1f; 6.tcp粘包怎么解决&#xff1f; 7.TCP的拥塞控制介绍一下&#xff1f; 8.描述一下打开百度首页后发生的网络过…

制造企业“数字化转型”典型场景参考

聚焦产业链上下游企业研发设计、生产制造、运维服务、经营管理、供应链管理等场景&#xff0c;以场景为切入点梳理数字化转型痛点需求&#xff0c;绘制重点行业、重点产业链数字化转型场景图谱&#xff08;简称“一图谱”&#xff09;&#xff0c;明确企业数字化转型路径&#…

Web渗透测试之XSS跨站脚本 防御[WAF]绕过手法

目录 XSS防御绕过汇总 参考这篇文章绕过 XSS payload XSS防御绕过汇总 服务端知道有网络攻击或者xss攻 Html

《机器学习》——sklearn库中CountVectorizer方法(词频矩阵)

CountVectorizer方法介绍 CountVectorizer 是 scikit-learn 库中的一个工具&#xff0c;它主要用于将文本数据转换为词频矩阵&#xff0c;而不是传统意义上的词向量转换&#xff0c;但可以作为词向量转换的一种基础形式。用于将文本数据转换为词频矩阵&#xff0c;它是文本特征…

session-manager-plugin: command not found 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Linux之读者写者模型与特殊锁的学习

目录 读者写者模型 特殊锁 悲观锁 自旋锁 在前几期&#xff0c;我们学习了多线程的生产者和消费者模型&#xff0c;生产者和消费者模型中&#xff0c;有三种关系&#xff0c;两个角色&#xff0c;一个场所&#xff0c;那么读者写者模型和生产者消费者模型有什么关联吗&…

期刊(中英),期刊分区,期刊所在数据库(中英),出版商区别和联系

目录 对期刊、分区、数据库、出版商整体了解期刊&#xff08;中英&#xff09;期刊分区期刊所在数据库总结 出版商 对期刊、分区、数据库、出版商整体了解 下图是我对这四部分的一个理解&#xff0c;其中期刊根据论文使用语言分为中英两种&#xff0c;期刊分区是用来评判论文质…

数学函数的参数和返回值探秘

数学函数的参数和返回值探秘 一、数学函数的参数1.1 隐式类型转换1.2 隐式类型转换的秘密 二、数学函数的返回值 本文所说的数学函数单指<math.h>中的系统函数&#xff0c;这些函数对参数和返回值的要求与其他类函数是有一点不同的。尤其是参数部分&#xff0c;是有值得深…

炸砖块游戏的最终图案

描述 小红正在玩一个“炸砖块”游戏,游戏的规则如下:初始有一个 n * m 的砖块矩阵。小红会炸 k 次,每次会向一个位置投炸弹,如果这个位置有一个砖块,则砖块消失,上方的砖块向下落。小红希望你画出最终砖块的图案。 输入描述 第一行输入三个正整数 n, m, k,代表矩阵的行…

代码随想录算法训练营第 4 天(链表 2)| 24. 两两交换链表中的节点19.删除链表的倒数第N个节点 -

一、24. 两两交换链表中的节点 题目&#xff1a;24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 视频&#xff1a;帮你把链表细节学清楚&#xff01; | LeetCode&#xff1a;24. 两两交换链表中的节点_哔哩哔哩_bilibili 讲解&#xff1a;代码随想录 dummy-…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

【网络】:网络编程套接字

目录 源IP地址和目的IP地址 源MAC地址和目的MAC地址 源端口号和目的端口号 端口号 VS 进程ID TCP协议和UDP协议 网络字节序 字符串IP和整数IP相互转换 查看当前网络的状态 socket编程接口 socket常见API 创建套接字 绑定端口号 发送数据 接收数据 sockaddr结构…

UnityDemo-TheBrave-制作笔记

这是我跟着b站up主MStudio的视频学习制作的&#xff0c;大体上没有去做一些更新的东西&#xff0c;这里只是一个总的总结。在文章的最后&#xff0c;我会放上可以游玩该游戏的链接和exe可执行文件&#xff0c;不过没有对游戏内容进行什么加工&#xff0c;只有基本的功能实现罢了…

力扣经典二分题:4. 寻找两个正序数组的中位数

题目链接&#xff1a;4. 寻找两个正序数组的中位数 - 力扣&#xff08;LeetCode&#xff09; 一、题目分析 这道题目是让我们在 两个正序的数组中寻找中位数已知两个数组的大小分别是&#xff1a;int m nums1.size(),n nums2.size();中位数性质1&#xff1a;中位数左侧元素 …

C++ 文字识别OCR

一.引言 文字识别&#xff0c;也称为光学字符识别&#xff08;Optical Character Recognition, OCR&#xff09;&#xff0c;是一种将不同形式的文档&#xff08;如扫描的纸质文档、PDF文件或数字相机拍摄的图片&#xff09;中的文字转换成可编辑和可搜索的数据的技术。随着技…

SpringBoot:SaToken的options预检请求鉴权失败

问题描述 使用如下sa-token配置&#xff0c;前端通过IP端口号的方式访问后端服务&#xff0c;会存在options预检请求鉴权失败的问题。 问题分析 http-options请求 HTTP OPTIONS 方法请求给定的 URL 或服务器的允许通信选项。客户端可以用这个方法指定一个 URL&#xff0c;或者…

UE材质函数

材质函数是可在不同材质中重复使用的材质表达式的一个集合 相当于把常用的功能封装到一个集合里&#xff0c;需要用到的时候调用 输入input可以添加输入节点 如果勾上公开到库&#xff0c;就可以在材质面板直接搜索到材质函数 材质函数可以直接做成一个输出

51c~Pytorch~合集5

我自己的原文哦~ https://blog.51cto.com/whaosoft/13059544 一、PyTorch DDP 正在郁闷呢 jetson nx 的torchvision安装~~ 自带就剩5g 想弄到ssd 项目中的 venv中又 cuda.h没有... 明明已经装好什么都对 算了说今天主题 啊对 还是搬运啊 学习之工具人而已 勿怪 Distrib…