ewkb.erl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. %% https://en.wikipedia.org/wiki/Well-known_text
  2. %% http://postgis.net/docs/manual-2.4/using_postgis_dbmanagement.html#EWKB_EWKT
  3. -module(ewkb).
  4. -export([decode_geometry/1, encode_geometry/1]).
  5. -export_type([point_type/0, point/1, multi_point/1, line_string/1,
  6. multi_line_string/1, basic_string/1, curve/1, multi_curve/1,
  7. polygon/1, multi_polygon/1, triangle/1, curve_polygon/1,
  8. polyhedral_surface/1, surface/1, multi_surface/1, tin/1,
  9. geometry/1, geometry/0, geometry_collection/1, geom_type/0]).
  10. -include("epgsql_geometry.hrl").
  11. -type point_type() :: '2d' | '3d' | '2dm' | '3dm'.
  12. -type point(PointType) :: #point{ point_type :: PointType }.
  13. -type multi_point(PointType) :: #multi_point{ point_type :: PointType }.
  14. -type line_string(PointType) :: #line_string{ point_type :: PointType }.
  15. -type multi_line_string(PointType) :: #multi_line_string{ point_type :: PointType }.
  16. -type basic_string(PointType) :: #circular_string{ point_type :: PointType }
  17. | #line_string{ point_type :: PointType }.
  18. -type curve(PointType) :: #circular_string{ point_type :: PointType }
  19. | #line_string{ point_type :: PointType }
  20. | #compound_curve{ point_type :: PointType }.
  21. -type multi_curve(PointType) :: #multi_curve{ point_type :: PointType }.
  22. -type polygon(PointType) :: #polygon{ point_type :: PointType }.
  23. -type multi_polygon(PointType) :: #multi_polygon{ point_type :: PointType }.
  24. -type triangle(PointType) :: #triangle{ point_type :: PointType }.
  25. -type curve_polygon(PointType) :: #curve_polygon{ point_type :: PointType }.
  26. -type polyhedral_surface(PointType) :: #polyhedral_surface{ point_type :: PointType }.
  27. -type surface(PointType) :: polygon(PointType)
  28. | curve_polygon(PointType)
  29. | polyhedral_surface(PointType).
  30. -type multi_surface(PointType) :: #multi_surface{ point_type :: PointType }.
  31. -type tin(PointType) :: #tin{ point_type :: PointType }.
  32. -type geometry(PointType) :: point(PointType) |
  33. line_string(PointType) |
  34. triangle(PointType) |
  35. tin(PointType) |
  36. curve(PointType) |
  37. surface(PointType) |
  38. multi_point(PointType) |
  39. multi_line_string(PointType) |
  40. multi_polygon(PointType) |
  41. multi_curve(PointType) |
  42. multi_surface(PointType) |
  43. geometry_collection(PointType).
  44. -type geometry() :: geometry(point_type()).
  45. -type geometry_collection(PointType) :: [geometry(PointType)].
  46. -type geom_type() :: geometry
  47. | point %
  48. | line_string%
  49. | polygon%
  50. | multi_point%
  51. | multi_line_string%
  52. | multi_polygon%
  53. | geometry_collection%
  54. | circular_string%
  55. | compound_curve%
  56. | curve_polygon%
  57. | multi_curve%
  58. | multi_surface%
  59. | curve%
  60. | surface%
  61. | polyhedral_surface%
  62. | tin%
  63. | triangle.%
  64. -spec decode_geometry(binary()) -> geometry().
  65. decode_geometry(Binary) ->
  66. {Geometry, <<>>} = decode_geometry_data(Binary),
  67. Geometry.
  68. -spec encode_geometry(geometry()) -> binary().
  69. encode_geometry(Geometry) ->
  70. Type = encode_type(Geometry),
  71. PointType = encode_point_type(Geometry),
  72. Data = encode_geometry_data(Geometry),
  73. <<1, Type/binary, PointType/binary, Data/binary>>.
  74. encode_geometry_data(#point{ point_type = '2d', x = X, y = Y }) ->
  75. Xbin = encode_float64(X),
  76. Ybin = encode_float64(Y),
  77. <<Xbin/binary, Ybin/binary>>;
  78. encode_geometry_data(#point{ point_type = '2dm', x = X, y = Y, m = M }) ->
  79. Xbin = encode_float64(X),
  80. Ybin = encode_float64(Y),
  81. Mbin = encode_float64(M),
  82. <<Xbin/binary, Ybin/binary, Mbin/binary>>;
  83. encode_geometry_data(#point{ point_type = '3d', x = X, y = Y, z = Z }) ->
  84. Xbin = encode_float64(X),
  85. Ybin = encode_float64(Y),
  86. Zbin = encode_float64(Z),
  87. <<Xbin/binary, Ybin/binary, Zbin/binary>>;
  88. encode_geometry_data(#point{ point_type = '3dm', x = X, y = Y, z = Z, m = M }) ->
  89. Xbin = encode_float64(X),
  90. Ybin = encode_float64(Y),
  91. Zbin = encode_float64(Z),
  92. Mbin = encode_float64(M),
  93. <<Xbin/binary, Ybin/binary, Zbin/binary, Mbin/binary>>;
  94. encode_geometry_data({SimpleCollection, _, Data})
  95. when SimpleCollection == line_string;
  96. SimpleCollection == circular_string;
  97. SimpleCollection == polygon;
  98. SimpleCollection == triangle ->
  99. encode_collection(Data);
  100. encode_geometry_data({TypedCollection, _, Data})
  101. when
  102. TypedCollection == multi_point;
  103. TypedCollection == multi_line_string;
  104. TypedCollection == multi_curve;
  105. TypedCollection == multi_polygon;
  106. TypedCollection == multi_surface;
  107. TypedCollection == compound_curve;
  108. TypedCollection == curve_polygon;
  109. TypedCollection == geometry_collection;
  110. TypedCollection == polyhedral_surface;
  111. TypedCollection == tin ->
  112. encode_typed_collection(Data).
  113. encode_collection(Collection) when is_list(Collection) ->
  114. Length = length(Collection),
  115. LengthBin = encode_int32(Length),
  116. CollectionBin = lists:foldl(
  117. fun(Element, Acc) ->
  118. ElementBin = encode_geometry_data(Element),
  119. <<Acc/binary, ElementBin/binary>>
  120. end,
  121. <<>>,
  122. Collection),
  123. <<LengthBin/binary, CollectionBin/binary>>.
  124. encode_typed_collection(Collection) when is_list(Collection) ->
  125. Length = length(Collection),
  126. LengthBin = encode_int32(Length),
  127. CollectionBin = lists:foldl(
  128. fun(Element, Acc) ->
  129. ElementBin = encode_geometry(Element),
  130. <<Acc/binary, ElementBin/binary>>
  131. end,
  132. <<>>,
  133. Collection),
  134. <<LengthBin/binary, CollectionBin/binary>>.
  135. encode_int32(Int) when is_integer(Int) ->
  136. <<Int:1/little-integer-unit:32>>.
  137. encode_float64(Int) when is_number(Int) ->
  138. <<Int:1/little-float-unit:64>>.
  139. -spec decode_geometry_data(binary()) -> {geometry(), binary()}.
  140. decode_geometry_data(Binary) ->
  141. <<1, TypeCode:2/binary, SubtypeCode:2/binary, Data/binary>> = Binary,
  142. Type = decode_type(TypeCode),
  143. Subtype = decode_point_type(SubtypeCode),
  144. decode_geometry_data(Type, Subtype, Data).
  145. -spec decode_geometry_data(geom_type(), point_type(), binary()) -> {geometry(), binary()}.
  146. decode_geometry_data(curve, _, _) -> error({curve, not_supported});
  147. decode_geometry_data(surface, _, _) -> error({surface, not_supported});
  148. decode_geometry_data(geometry, _, _) -> error({geometry, not_supported});
  149. decode_geometry_data(point, PointType, Data) ->
  150. decode_point(PointType, Data);
  151. decode_geometry_data(LineType, PointType, Data)
  152. when LineType == line_string;
  153. LineType == circular_string ->
  154. {Points, Rest} = decode_collection(point, PointType, Data),
  155. {{LineType, PointType, Points}, Rest};
  156. decode_geometry_data(polygon, PointType, Data) ->
  157. {Lines, Rest} = decode_collection(line_string, PointType, Data),
  158. {#polygon{ point_type = PointType, rings = Lines }, Rest};
  159. decode_geometry_data(triangle, PointType, Data) ->
  160. {#polygon{ rings = Rings }, Rest} = decode_geometry_data(polygon, PointType, Data),
  161. {#triangle{ point_type = PointType, rings = Rings }, Rest};
  162. decode_geometry_data(Collection, PointType, Data)
  163. when
  164. Collection == multi_point;
  165. Collection == multi_line_string;
  166. Collection == multi_curve;
  167. Collection == multi_polygon;
  168. Collection == multi_surface;
  169. Collection == compound_curve;
  170. Collection == curve_polygon;
  171. Collection == geometry_collection;
  172. Collection == polyhedral_surface;
  173. Collection == tin ->
  174. {Lines, Rest} = decode_typed_collection(Data),
  175. {{Collection, PointType, Lines}, Rest}.
  176. -spec decode_collection(geom_type(), point_type(), binary()) -> {[geometry()], binary()}.
  177. decode_collection(Type, PointType, Data) ->
  178. {Length, CountRest} = decode_int32(Data),
  179. lists:foldl(
  180. fun(_, {Geoms, Rest}) ->
  181. {Geom, R} = decode_geometry_data(Type, PointType, Rest),
  182. {Geoms ++ [Geom], R}
  183. end,
  184. {[], CountRest},
  185. lists:seq(1, Length)).
  186. -spec decode_typed_collection(binary()) -> {[geometry()], binary()}.
  187. decode_typed_collection(Data) ->
  188. {Length, CountRest} = decode_int32(Data),
  189. lists:foldl(
  190. fun(_, {Geoms, Rest}) ->
  191. {Geom, R} = decode_geometry_data(Rest),
  192. {Geoms ++ [Geom], R}
  193. end,
  194. {[], CountRest},
  195. lists:seq(1, Length)).
  196. -spec decode_int32(binary()) -> {integer(), binary()}.
  197. decode_int32(<<Hex:4/binary, Rest/binary>>) ->
  198. <<Int:1/little-integer-unit:32>> = Hex,
  199. {Int, Rest}.
  200. -spec decode_float64(binary()) -> {float(), binary()}.
  201. decode_float64(<<Hex:8/binary, Rest/binary>>) ->
  202. <<Float:1/little-float-unit:64>> = Hex,
  203. {Float, Rest}.
  204. decode_point(PointType, Data) ->
  205. {Values, Rest} = lists:foldl(
  206. fun(_, {Values, Rest}) ->
  207. {Value, R} = decode_float64(Rest),
  208. {Values ++ [Value], R}
  209. end,
  210. {[], Data},
  211. lists:seq(1, point_size(PointType))),
  212. Point = case {PointType, Values} of
  213. {'2d', [X, Y]} ->
  214. #point{ point_type = PointType, x = X, y = Y };
  215. {'2dm', [X, Y, M]} ->
  216. #point{ point_type = PointType, x = X, y = Y, m = M };
  217. {'3d', [X, Y, Z]} ->
  218. #point{ point_type = PointType, x = X, y = Y, z = Z };
  219. {'3dm', [X, Y, Z, M]} ->
  220. #point{ point_type = PointType, x = X, y = Y, z = Z, m = M }
  221. end,
  222. {Point, Rest}.
  223. -spec point_size(point_type()) -> 2..4.
  224. point_size('2d') -> 2;
  225. point_size('2dm') -> 3;
  226. point_size('3d') -> 3;
  227. point_size('3dm') -> 4.
  228. -spec decode_type(binary()) -> geom_type().
  229. decode_type(<<0, 0>>) -> geometry;
  230. decode_type(<<1, 0>>) -> point;
  231. decode_type(<<2, 0>>) -> line_string;
  232. decode_type(<<3, 0>>) -> polygon;
  233. decode_type(<<4, 0>>) -> multi_point;
  234. decode_type(<<5, 0>>) -> multi_line_string;
  235. decode_type(<<6, 0>>) -> multi_polygon;
  236. decode_type(<<7, 0>>) -> geometry_collection;
  237. decode_type(<<8, 0>>) -> circular_string;
  238. decode_type(<<9, 0>>) -> compound_curve;
  239. decode_type(<<10, 0>>) -> curve_polygon;
  240. decode_type(<<11, 0>>) -> multi_curve;
  241. decode_type(<<12, 0>>) -> multi_surface;
  242. decode_type(<<13, 0>>) -> curve;
  243. decode_type(<<14, 0>>) -> surface;
  244. decode_type(<<15, 0>>) -> polyhedral_surface;
  245. decode_type(<<16, 0>>) -> tin;
  246. decode_type(<<17, 0>>) -> triangle.
  247. -spec encode_type(geometry() | geom_type()) -> binary().
  248. encode_type(Geometry) when is_tuple(Geometry) ->
  249. encode_type(element(1, Geometry));
  250. encode_type(geometry) -> <<00, 0>>;
  251. encode_type(point) -> <<01, 0>>;
  252. encode_type(line_string) -> <<02, 0>>;
  253. encode_type(polygon) -> <<03, 0>>;
  254. encode_type(multi_point) -> <<04, 0>>;
  255. encode_type(multi_line_string) -> <<05, 0>>;
  256. encode_type(multi_polygon) -> <<06, 0>>;
  257. encode_type(geometry_collection) -> <<07, 0>>;
  258. encode_type(circular_string) -> <<08, 0>>;
  259. encode_type(compound_curve) -> <<09, 0>>;
  260. encode_type(curve_polygon) -> <<10, 0>>;
  261. encode_type(multi_curve) -> <<11, 0>>;
  262. encode_type(multi_surface) -> <<12, 0>>;
  263. encode_type(curve) -> <<13, 0>>;
  264. encode_type(surface) -> <<14, 0>>;
  265. encode_type(polyhedral_surface) -> <<15, 0>>;
  266. encode_type(tin) -> <<16, 0>>;
  267. encode_type(triangle) -> <<17, 0>>.
  268. -spec decode_point_type(binary()) -> point_type().
  269. decode_point_type(<<0, 0>>) -> '2d';
  270. decode_point_type(<<0, 64>>) -> '2dm';
  271. decode_point_type(<<0, 128>>) -> '3d';
  272. decode_point_type(<<0, 192>>) -> '3dm'.
  273. -spec encode_point_type(geometry() | point_type()) -> binary().
  274. encode_point_type(Geometry) when is_tuple(Geometry) ->
  275. encode_point_type(element(2, Geometry));
  276. encode_point_type('2d') -> <<0, 0>>;
  277. encode_point_type('2dm') -> <<0, 64>>;
  278. encode_point_type('3d') -> <<0, 128>>;
  279. encode_point_type('3dm') -> <<0, 192>>.