ewkb.erl 9.2 KB

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