当前位置:

首页 > 编程开发 > Springboot集成Tile客户端之Set命令如何实现

Springboot集成Tile客户端之Set命令如何实现

set命令语法SETkeyid[FIELDnamevalue...][EXseconds][NX|XX](OBJECTgeojson)|(POINTlatlonz)|(BOUNDSminlatminlonmaxlatmaxlon)|(HASHgeohash)|(STRINGvalue)set命令就相当于redis中的hash命令的使用,也是一个key和id的组合,但是不同的是,Tile38的set命令还可以携带更多的其他属性,比如可以自定义FIELD字段,还可以设置EX有效期等等,那么我们需要给这个语法设

本文目录

      set命令语法

      SET key id [FIELD name value ...] [EX seconds] [NX|XX] (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

      set命令就相当于redis中的hash命令的使用,也是一个key和id的组合,但是不同的是,Tile38的set命令还可以携带更多的其他属性,比如可以自定义FIELD字段,还可以设置EX有效期等等,那么我们需要给这个语法设计一套好用的java api,以便开发人员可以更好地使用Tile38。

      语法分析

      首先,根据上面提供的语法,我们可以分为三部分:

      1.第一部分就是命令的启示关键字SET,我们把这个关键字单独作为一部分;

      2.第二部分就是key id [FIELD name value ...] [EX seconds] [NX|XX],我们把这些都作为参数;

      3.第三部分就是最后的目标数据对象:

      (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value)

      代码设计

      1.我们把第一部分的命令关键字通过枚举的方式来管理:

      enum Tile38Command implements ProtocolKeyword {
          SET;
          public final byte[] bytes;
          static final String UNDERSCORE = "_";
          static final String SPACE = " ";
          Tile38Command() {
            String name = StringUtils.replace(this.name(), UNDERSCORE, SPACE);
            this.bytes = name.getBytes(StandardCharsets.US_ASCII);
          }
          @Override
          public byte[] getBytes() {
            return this.bytes;
          }
      }

      因为redis客户端工具在发送命令前需要对所有命令进行编码,所以要求所有的命令都必须实现ProtocolKeyword接口。如果命令的起始关键字是两个或多个单词,那么我们会使用下划线连接,转换成bytes的时候我们可以使用空格把下划线替换。

      2.我们把命令的第二部分抽象成一个具体的class,通过相关的字段来进行描述:

      public class SetOpts {
        private String key;
        private String id;
        //字段值必须是双精度浮点型
        private Map fields;
        // 单位秒
        private int ex;
        // 创建方式:
        // NX 不存在的时候创建
        // XX 存在的时候更新
        private NxXx nxXx;
        private SetOpts(Builder builder) {
          this.key = builder.key;
          this.id = builder.id;
          this.fields = builder.fields;
          this.ex = builder.ex;
          this.nxXx = builder.nxXx;
        }
        
        // 把所有的参数按顺序放到列表中
        public List commandLine() {
          List result = new LinkedList<>();
          result.add(this.key);
          result.add(this.id);
          // 添加所有的FIELD
          if (MapUtils.isNotEmpty(this.fields)) {
            for (Map.Entry entry : this.fields.entrySet()) {
              result.add("FIELD");
              result.add(entry.getKey());
              result.add(entry.getValue().toString());
            }
          }
          // 添加`EX`
          if (this.ex >= 0) {
            result.add("EX");
            result.add(String.valueOf(this.ex));
          }
          // 添加NX或XX
          if (Objects.nonNull(this.nxXx)) {
            result.add(this.nxXx.name());
          }
          // 返回结果
          return result;
        }
        
        public enum NxXx {
          NX,
          XX
        }
        // 建造者模式
        public static class Builder {
          private String key;
          private String id;
          //字段值必须是双精度浮点型
          private Map fields;
          // 单位秒
          private int ex = -1;
          // 创建方式:
          // NX 不存在的时候创建
          // XX 存在的时候更新
          private NxXx nxXx;
          public Builder key(String key) {
            this.key = key;
            return this;
          }
          public Builder id(String id) {
            this.id = id;
            return this;
          }
          public Builder field(String field, double value) {
            if (Objects.isNull(this.fields)) {
              this.fields = new LinkedHashMap<>();
            }
            this.fields.put(field, value);
            return this;
          }
          public Builder ex(int seconds) {
            this.ex = seconds;
            return this;
          }
          public Builder nxXx(NxXx nxXx) {
            this.nxXx = nxXx;
            return this;
          }
          public SetOpts build() throws AwesomeException {
            if (StringUtils.isEmpty(this.key)) {
              throw new AwesomeException(500, "key is empty");
            }
            if (StringUtils.isEmpty(this.id)) {
              throw new AwesomeException(500, "id is empty");
            }
            // 创建SetOpts对象
            return new SetOpts(this);
          }
        }
      }

      我们上面通过建造者的设计模式,把所有的参数都转换成了SetOpts这个类当中,开发人员就可以通过SetOpts对象的构建来灵活地控制命令中的参数了。

      3.我们需要把第三部分当中的不同数据对象转换成不同的类型:

      POINT数据类型

      Point关键的字段就是经纬度,除此之外,还有一个额外的字段z,用来存储额外的业务参数,可为空。

      public class Point extends Element implements Serializable {
        // 经度
        private double lng;
        // 维度
        private double lat;
        // 额外的数据
        private double z;
        public Point(double lng, double lat, double z) {
          this.lat = lat;
          this.lng = lng;
          this.z = z;
        }
        public Point(double lng, double lat) {
          this(lng, lat, Integer.MIN_VALUE);
        }
        @Override
        public List commandArgs() {
          List result = new LinkedList<>();
          result.add("POINT");
          result.add(String.valueOf(this.lng));
          result.add(String.valueOf(this.lat));
          if (this.z != Integer.MIN_VALUE) {
            result.add(String.valueOf(this.z));
          }
          return result;
        }
      }

      BOUNDS数据类型

      BOUNDS就是矩形,它的关键字段就是左下角和右上角两个点位,我们使用coordinate1和coordinate2来表示左下角和右上角;

      @AllArgsConstructor
      public class Bounds extends Element {
        private double[] coordinate1;
        private double[] coordinate2;
        @Override
        public List commandArgs() {
          List result = new LinkedList<>();
          result.add("BOUNDS");
          result.add(String.valueOf(coordinate1[0]));
          result.add(String.valueOf(coordinate1[1]));
          result.add(String.valueOf(coordinate2[0]));
          result.add(String.valueOf(coordinate2[1]));
          return result;
        }
      }

      HASH和STRING数据类型

      HASH和STRING其实就是一个单独的字符串,但是我们还是把它封装一下,以便开发人员使用;

      @AllArgsConstructor
      public class Geohash extends Element {
        private String hash;
        @Override
        public List commandArgs() {
          List result = new LinkedList<>();
          result.add("HASH");
          result.add(this.hash);
          return result;
        }
      }
      @AllArgsConstructor
      public class RawString extends Element {
        private String raw;
        @Override
        public List commandArgs() {
          List result = new LinkedList<>();
          result.add("STRING");
          result.add(this.raw);
          return result;
        }
      }

      OBJECT数据类型

      OBJECT其实就是GeoJSON数据,这一类数据比较复杂一点,一共有六种类型,想了解的小伙伴可以看这里geojson.org/

      Point,
      LineString,
      Polygon,
      MultiPoint,
      MultiLineString,
      MultiPolygon

      为了开发人员能够更好的使用这六种类型,我们同样使用建造者模式来设计一下GeoJSON数据类型:

      @Data
      public class GeoJson {
        public static class Builder {
          public Point.Builder point() {
            return new Point.Builder();
          }
          public MultiPoint.Builder multPoint() {
            return new MultiPoint.Builder();
          }
          public LineString.Builder lineString() {
            return new LineString.Builder();
          }
          public MultiLineString.Builder multiLineString() {
            return new MultiLineString.Builder();
          }
          public Polygon.Builder polygon() {
            return new Polygon.Builder();
          }
          public MultiPolygon.Builder multiPolygon() {
            return new MultiPolygon.Builder();
          }
        }
      }

      我们现在一个大类里面创建多个方法,每一个方法都把对应类型的建造者给创造出来,这样的话,就相当于这个类当中有创建六种对象的方式,每个建造者都只负责建造对应的那个对象。

      下面分别是六个建造者的代码,每个对象都基于最基本的BaseGeoJson来构造,BaseGeoJson中把公共的字段type和额外的meta字段抽出来,各个类型不同的点在于坐标点的数量和层次不同,所以根据各自类型的特点,代码设计如下:

      // Point类型
        public static class Point extends BaseGeoJson {
          // 坐标点
          private double[] coordinates;
          Point(Builder builder) {
            super(builder);
            this.type = GeoJsonType.Point;
            this.coordinates = builder.coordinates;
          }
          @Override
          protected Object coordinates() {
            return this.coordinates;
          }
          public static class Builder extends BaseGeoJson.Builder {
            private double[] coordinates;
            public Builder coordinate(double lon, double lat) {
              coordinates = new double[]{lat, lon};
              return this;
            }
            public Point build() {
              return new Point(this);
            }
          }
        }
      // MultiPoint类型
        public static class MultiPoint extends BaseGeoJson {
          private double[][] coordinates;
          MultiPoint(Builder builder) {
            super(builder);
            this.type = GeoJsonType.MultiPoint;
            this.coordinates = builder.convert2Array();
          }
          @Override
          protected Object coordinates() {
            return this.coordinates;
          }
          public static class Builder extends BaseGeoJson.Builder {
            private List coordinates;
            public Builder coordinate(double lon, double lat) {
              if (CollectionUtils.isEmpty(this.coordinates)) {
                this.coordinates = new LinkedList<>();
              }
              this.coordinates.add(new Coordinate(lat, lon));
              return this;
            }
            protected double[][] convert2Array() {
              int length = this.coordinates.size();
              double[][] result = new double[length][];
              for (int i = 0; i < length; i++) {
                result[i] = this.coordinates.get(i).convertToArray();
              }
              return result;
            }
            @Override
            public MultiPoint build() {
              return new MultiPoint(this);
            }
          }
        }
      // LineString类型
        public static class LineString extends MultiPoint {
          private double[][] coordinates;
          LineString(Builder builder) {
            super(builder);
            this.type = GeoJsonType.LineString;
          }
          public static class Builder extends MultiPoint.Builder {
            @Override
            public LineString build() {
              return new LineString(this);
            }
          }
        }
      // MultiLineString类型
        public static class MultiLineString extends BaseGeoJson {
          private double[][][] coordinates;
          MultiLineString(Builder builder) {
            super(builder);
            this.type = GeoJsonType.MultiLineString;
            this.coordinates = builder.convertToArray();
          }
          @Override
          protected Object coordinates() {
            return this.coordinates;
          }
          public static class Builder extends BaseGeoJson.Builder {
            private List lines = new LinkedList<>();
            public Line line() {
              return new Line(this);
            }
            void addLine(Line line) {
              lines.add(line);
            }
            double[][][] convertToArray() {
              int length = this.lines.size();
              double[][][] result = new double[length][][];
              for (int i = 0; i < length; i++) {
                Line line = this.lines.get(i);
                result[i] = line.convert2Array();
              }
              return result;
            }
            @Override
            public BaseGeoJson build() {
              return new MultiLineString(this);
            }
          }
          static class Line {
            private List coordinates;
            private Builder builder;
            Line(Builder builder) {
              this.builder = builder;
              this.builder.addLine(this);
            }
            private double[][] convert2Array() {
              int length = this.coordinates.size();
              double[][] result = new double[length][];
              for (int i = 0; i < length; i++) {
                result[i] = this.coordinates.get(i).convertToArray();
              }
              return result;
            }
            public Line coordinate(double lon, double lat) {
              if (CollectionUtils.isEmpty(this.coordinates)) {
                this.coordinates = new LinkedList<>();
              }
              this.coordinates.add(new Coordinate(lat, lon));
              return this;
            }
            public Line nextLine() {
              return new Line(this.builder);
            }
            public Builder end() {
              return this.builder;
            }
          }
        }
      // Polygon类型
        public static class Polygon extends MultiPoint {
          private double[][][] coordinates;
          Polygon(Builder builder) {
            super(builder);
            this.type = GeoJsonType.Polygon;
            this.coordinates = new double[][][]{builder.convert2Array()};
          }
          public static class Builder extends MultiPoint.Builder {
            @Override
            public Polygon build() {
              return new Polygon(this);
            }
          }
        }
      // MultiPolygon类型
        public static class MultiPolygon extends BaseGeoJson {
          private double[][][][] coordinates;
          MultiPolygon(Builder builder) {
            super(builder);
            this.type = GeoJsonType.MultiPolygon;
            this.coordinates = new double[][][][]{builder.convert2Array()};
          }
          @Override
          protected Object coordinates() {
            return this.coordinates;
          }
          public static class Builder extends BaseGeoJson.Builder {
            private List polygons = new LinkedList<>();
            @Override
            public BaseGeoJson build() {
              return new MultiPolygon(this);
            }
            void addPolygon(Polygon polygon) {
              polygons.add(polygon);
            }
            private double[][][] convert2Array() {
              int length = this.polygons.size();
              double[][][] result = new double[length][][];
              for (int i = 0; i < length; i++) {
                result[i] = this.polygons.get(i).convert2Array();
              }
              return result;
            }
          }
          static class Polygon {
            private List coordinates;
            private Builder builder;
            Polygon(Builder builder) {
              this.builder = builder;
              this.builder.addPolygon(this);
            }
            private double[][] convert2Array() {
              int length = this.coordinates.size();
              double[][] result = new double[length][];
              for (int i = 0; i < length; i++) {
                result[i] = this.coordinates.get(i).convertToArray();
              }
              return result;
            }
            public Polygon coordinate(double lon, double lat) {
              if (CollectionUtils.isEmpty(this.coordinates)) {
                this.coordinates = new LinkedList<>();
              }
              this.coordinates.add(new Coordinate(lat, lon));
              return this;
            }
            public Polygon nextLine() {
              return new Polygon(this.builder);
            }
            public Builder end() {
              return this.builder;
            }
          }
        }
      // 基类BaseGeoJson
        public abstract static class BaseGeoJson extends Element {
          // 公共字段type
          protected GeoJsonType type;
          // 公共字段metadata
          private Map metadata;
          BaseGeoJson(Builder builder) {
            this.metadata = builder.metadata;
          }
          protected abstract Object coordinates();
          // 转换成命令参数
          @Override
          public List commandArgs() {
            List result = new LinkedList<>();
            result.add("OBJECT");
            result.add(toJson());
            return result;
          }
          // 提供统一的转json方法
          protected String toJson() {
            Map map = new LinkedHashMap<>();
            map.put("type", this.type);
            map.put("coordinates", coordinates());
            if (!CollectionUtils.isEmpty(this.metadata)) {
              for (Map.Entry entry : this.metadata.entrySet()) {
                map.put(entry.getKey(), entry.getValue());
              }
            }
            return JsonUtil.obj2String(map);
          }
          abstract static class Builder {
            private Map metadata;
            public Builder meta(String key, String value) {
              if (MapUtils.isEmpty(this.metadata)) {
                this.metadata = new LinkedHashMap<>();
              }
              this.metadata.put(key, value);
              return this;
            }
            public abstract BaseGeoJson build();
          }
          static class Coordinate {
            private double lat;
            private double lon;
            Coordinate(double lat, double lon) {
              this.lat = lat;
              this.lon = lon;
            }
            public double[] convertToArray() {
              return new double[]{this.lat, this.lon};
            }
          }
          // GeoJSON所有的数据类型
          enum GeoJsonType {
            Point,
            LineString,
            Polygon,
            MultiPoint,
            MultiLineString,
            MultiPolygon
          }
        }

      最后,再补充一个基类Element:

      public abstract class Element implements Serializable {
        public abstract List commandArgs();
      }

      如何使用

      我们针对所有的数据类型全部转换成具体的代码设计,下面我们看看如何使用:

      private String setElement(SetOpts setOpts, Element element) {
          List args1 = setOpts.commandLine();
          List commandArgs = element.commandArgs();
          return execute(Tile38Command.SET, args1, commandArgs);
      }
      /**
         * 设置点位
         *
         * @param setOpts
         * @param point
         * @return
         */
        public String setPoint(SetOpts setOpts, Point point) {
          return setElement(setOpts, point);
        }
        /**
         * 设置对象
         *
         * @param setOpts
         * @param geoJson
         * @return
         */
        public String setObject(SetOpts setOpts, GeoJson.BaseGeoJson geoJson) {
          return setElement(setOpts, geoJson);
        }
        /**
         * 设置矩形边界
         *
         * @param setOpts
         * @param bounds
         * @return
         */
        public String setBounds(SetOpts setOpts, Bounds bounds) {
          return setElement(setOpts, bounds);
        }
        /**
         * 设置geohash
         *
         * @param setOpts
         * @param geohash
         * @return
         */
        public String setGeohash(SetOpts setOpts, Geohash geohash) {
          return setElement(setOpts, geohash);
        }
        /**
         * 设置String
         *
         * @param setOpts
         * @param string
         * @return
         */
        public String setString(SetOpts setOpts, RawString string) {
          return setElement(setOpts, string);
        }
      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发
      相关文章 更多
      python删除一个列表里的重复元素的方法与代码示例
      python删除一个列表里的重复元素的方法与代码示例

      学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

      FreeBSD 里面用设置 ADSL宽带上网
      FreeBSD 里面用设置 ADSL宽带上网

      在FreeBSD 6.2上配置PPPoE ADSL拨号连接 第一步:配置PPPoE参数 首先,你需要编辑 /etc/ppp/ppp.conf 这个核心配置文件。在里面加入以下几段配置: 第一段,我们命名为 adsl,用来定义主要的拨号参数: adsl: set device PPPoE: nve0

      C++ std::set与unordered_set怎么选
      C++ std::set与unordered_set怎么选

      需要有序性选std::set,基于红黑树实现,支持排序和范围查询,操作复杂度O(logn);追求平均性能选std::unordered_set,基于哈希表,查找插入删除平均O(1),但无序且最坏情况O(n)。

      C++ set自动排序去重特性解析
      C++ set自动排序去重特性解析

      C++set容器基于红黑树实现,具备自动排序与去重特性,插入、删除、查找时间复杂度为O(logn);可通过自定义比较函数对象或函数指针实现排序规则;与unordered_set相比,后者基于哈希表,平均操作时间复杂度O(1),但无序且最坏情况性能下降;需有序或稳定性能时选set,仅需唯一性且追求平均速度时选unordered_set;批量插入时推荐使用范围insert减少红黑树调整,提升效率。

      C++ set存入自定义对象及排序方法
      C++ set存入自定义对象及排序方法

      C++std::set默认使用std::less作为比较器,依赖operator<实现有序性。自定义对象需提供严格弱序的比较规则,可通过重载operator<或使用自定义比较器(如Functor、Lambda)实现。std::map的键排序机制与std::set完全相同,均要求严格弱序,区别仅在于容器类型和存储结构。

      set与multiset区别详解
      set与multiset区别详解

      set与multiset的主要区别在于唯一性:set不允许重复元素,multiset允许。两者均基于红黑树实现,插入、删除和查找时间复杂度为O(logn)。set在插入时需检查唯一性,可能导致性能略低于multiset,尤其在大量重复数据时;但差异通常可忽略。内存方面,multiset可能更高,因存储所有重复项。选择依据主要是是否需要允许重复:若需唯一性(如用户ID),选set;若允许多次出现(如成绩记录),选multiset。两者都保持元素自动排序,适用于去重、词频统计、有序集合维护等场景。代码使用上,

      C++ STL set自动排序实现方法
      C++ STL set自动排序实现方法

      STLset容器基于红黑树实现,自动排序且去重,插入查找时间复杂度为O(logn),支持自定义排序,不支持随机访问;遍历时元素有序,find用于查找元素,multiset允许重复而set不允许。

      Java集合框架中Map和Set的实现与区别
      Java集合框架中Map和Set的实现与区别

      Map与Set的区别:键值与唯一性:Map存储键值对,Set存储唯一元素。顺序:Map和Set中,HashMap和HashSet是无序集合,LinkedHashMap和LinkedHashSet是有序集合,TreeSet按顺序排序。可变性:Map、LinkedHashMap和TreeSet是可变集合,HashSet和LinkedHashSet是不可变集合。用途:Map用于键值对数据,Set用于唯一元素数据。

      springBoot项目常用目录有哪些
      springBoot项目常用目录有哪些

      springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

      实战经验分享:深入解析Java集合框架应用及问题解决
      实战经验分享:深入解析Java集合框架应用及问题解决

      1.ListList是Java集合框架中的一种有序集合,允许元素重复,并且可以通过索引来访问元素。List的常用实现类包括ArrayList、LinkedList和Vector。以下演示如何使用ArrayList来存储一组整数:importjava.util.ArrayList;publicclassArrayListDemo{publicstaticvoidmain(String[]args){//创建一个ArrayList对象ArrayList<Integer>numbers=newArr

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。