ewkb.erl 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. -module(ewkb).
  2. -export([parse_geometry/1, format_geometry/1]).
  3. -type point_type() :: '2d' | '3d' | '2dm' | '3dm'.
  4. -record(point,{
  5. point_type :: point_type(),
  6. x :: float(),
  7. y :: float(),
  8. z :: float(),
  9. m :: float()
  10. }).
  11. -type point(PointType) :: #point{ point_type :: PointType }.
  12. -record(multi_point,{
  13. point_type :: PointType,
  14. points :: [point(PointType)]
  15. }).
  16. -type multi_point(PointType) :: #multi_point{ point_type :: PointType }.
  17. -record(line_string,{
  18. point_type :: PointType,
  19. points :: [point(PointType)]
  20. }).
  21. -type line_string(PointType) :: #line_string{ point_type :: PointType }.
  22. -record(multi_line_string,{
  23. point_type :: PointType,
  24. line_strings :: [line_string(PointType)]
  25. }).
  26. -type multi_line_string(PointType) :: #multi_line_string{ point_type :: PointType }.
  27. -record(circular_string,{
  28. point_type :: PointType,
  29. points :: [point(PointType)]
  30. }).
  31. -type basic_string(PointType) :: #circular_string{ point_type :: PointType } | #line_string{ point_type :: PointType }.
  32. -record(compound_curve,{
  33. point_type :: PointType,
  34. lines :: [basic_string(PointType)]
  35. }).
  36. -type curve(PointType) :: #circular_string{ point_type :: PointType } | #line_string{ point_type :: PointType } | #compound_curve{ point_type :: PointType }.
  37. -record(multi_curve,{
  38. point_type :: PointType,
  39. curves :: [curve(PointType)]
  40. }).
  41. -type multi_curve(PointType) :: #multi_curve{ point_type :: PointType }.
  42. -record(polygon,{
  43. point_type :: PointType,
  44. rings :: [line_string(PointType)]
  45. }).
  46. -type polygon(PointType) :: #polygon{ point_type :: PointType }.
  47. -record(multi_polygon,{
  48. point_type :: PointType,
  49. polygons :: [polygon(PointType)]
  50. }).
  51. -type multi_polygon(PointType) :: #multi_polygon{ point_type :: PointType }.
  52. -record(triangle,{
  53. point_type :: PointType,
  54. rings :: [line_string(PointType)]
  55. }).
  56. -type triangle(PointType) :: #triangle{ point_type :: PointType }.
  57. -record(curve_polygon,{
  58. point_type :: PointType,
  59. rings :: [curve(PointType)]
  60. }).
  61. -type curve_polygon(PointType) :: #curve_polygon{ point_type :: PointType }.
  62. -record(polyhedral_surface,{
  63. point_type :: PointType,
  64. polygons :: [polygon(PointType)]
  65. }).
  66. -type polyhedral_surface(PointType) :: #polyhedral_surface{ point_type :: PointType }.
  67. -type surface(PointType) :: polygon(PointType) | curve_polygon(PointType) | polyhedral_surface(PointType).
  68. -record(multi_surface,{
  69. point_type :: PointType,
  70. surfaces :: [surface(PointType)]
  71. }).
  72. -type multi_surface(PointType) :: #multi_surface{ point_type :: PointType }.
  73. -record(tin,{
  74. point_type :: PointType,
  75. triangles :: [triangle(PointType)]
  76. }).
  77. -type tin(PointType) :: #tin{ point_type :: PointType }.
  78. -type geometry(PointType) :: point(PointType) |
  79. line_string(PointType) |
  80. triangle(PointType) |
  81. tin(PointType) |
  82. curve(PointType) |
  83. surface(PointType) |
  84. multi_point(PointType) |
  85. multi_line_string(PointType) |
  86. multi_polygon(PointType) |
  87. multi_curve(PointType) |
  88. multi_surface(PointType) |
  89. geometry_collection(PointType).
  90. -type geometry() :: geometry(point_type()).
  91. -type geometry_collection(PointType) :: [geometry(PointType)].
  92. -type geom_type() :: geometry
  93. | point %
  94. | line_string%
  95. | polygon%
  96. | multi_point%
  97. | multi_line_string%
  98. | multi_polygon%
  99. | geometry_collection%
  100. | circular_string%
  101. | compound_curve%
  102. | curve_polygon%
  103. | multi_curve%
  104. | multi_surface%
  105. | curve%
  106. | surface%
  107. | polyhedral_surface%
  108. | tin%
  109. | triangle.%
  110. parse_geometry(Binary) ->
  111. {Geometry, <<>>} = parse_geometry_data(Binary),
  112. Geometry.
  113. format_geometry(Geometry) ->
  114. Type = format_type(Geometry),
  115. PointType = format_point_type(Geometry),
  116. Data = format_geometry_data(Geometry),
  117. <<"01", Type/binary, PointType/binary, Data/binary>>.
  118. format_geometry_data(#point{ point_type = '2d', x = X, y = Y }) ->
  119. Xbin = format_float64(X),
  120. Ybin = format_float64(Y),
  121. <<Xbin/binary, Ybin/binary>>;
  122. format_geometry_data(#point{ point_type = '2dm', x = X, y = Y, m = M }) ->
  123. Xbin = format_float64(X),
  124. Ybin = format_float64(Y),
  125. Mbin = format_float64(M),
  126. <<Xbin/binary, Ybin/binary, Mbin/binary>>;
  127. format_geometry_data(#point{ point_type = '3d', x = X, y = Y, z = Z }) ->
  128. Xbin = format_float64(X),
  129. Ybin = format_float64(Y),
  130. Zbin = format_float64(Z),
  131. <<Xbin/binary, Ybin/binary, Zbin/binary>>;
  132. format_geometry_data(#point{ point_type = '3dm', x = X, y = Y, z = Z, m = M }) ->
  133. Xbin = format_float64(X),
  134. Ybin = format_float64(Y),
  135. Zbin = format_float64(Z),
  136. Mbin = format_float64(M),
  137. <<Xbin/binary, Ybin/binary, Zbin/binary, Mbin/binary>>;
  138. format_geometry_data({SimpleCollection, _, Data})
  139. when SimpleCollection == line_string;
  140. SimpleCollection == circular_string;
  141. SimpleCollection == polygon;
  142. SimpleCollection == triangle ->
  143. format_collection(Data);
  144. format_geometry_data({TypedCollection, _, Data})
  145. when
  146. TypedCollection == multi_point;
  147. TypedCollection == multi_line_string;
  148. TypedCollection == multi_curve;
  149. TypedCollection == multi_polygon;
  150. TypedCollection == multi_surface;
  151. TypedCollection == compound_curve;
  152. TypedCollection == curve_polygon;
  153. TypedCollection == geometry_collection;
  154. TypedCollection == polyhedral_surface;
  155. TypedCollection == tin ->
  156. format_typed_collection(Data).
  157. format_collection(Collection) when is_list(Collection) ->
  158. Length = length(Collection),
  159. LengthBin = format_int32(Length),
  160. CollectionBin = lists:foldl(
  161. fun(Element, Acc) ->
  162. ElementBin = format_geometry_data(Element),
  163. <<Acc/binary, ElementBin/binary>>
  164. end,
  165. <<>>,
  166. Collection),
  167. <<LengthBin/binary, CollectionBin/binary>>.
  168. format_typed_collection(Collection) when is_list(Collection) ->
  169. Length = length(Collection),
  170. LengthBin = format_int32(Length),
  171. CollectionBin = lists:foldl(
  172. fun(Element, Acc) ->
  173. ElementBin = format_geometry(Element),
  174. <<Acc/binary, ElementBin/binary>>
  175. end,
  176. <<>>,
  177. Collection),
  178. <<LengthBin/binary, CollectionBin/binary>>.
  179. format_int32(Int) when is_integer(Int) ->
  180. Bin = <<Int:1/little-integer-unit:32>>,
  181. bin_to_hex(Bin).
  182. format_float64(Int) when is_number(Int) ->
  183. Bin = <<Int:1/little-float-unit:64>>,
  184. bin_to_hex(Bin).
  185. -spec parse_geometry_data(binary()) -> {geometry(), binary()}.
  186. parse_geometry_data(Binary) ->
  187. <<"01", TypeCode:4/binary, SubtypeCode:4/binary, Data/binary>> = Binary,
  188. Type = parse_type(TypeCode),
  189. Subtype = parse_point_type(SubtypeCode),
  190. parse_geometry_data(Type, Subtype, Data).
  191. -spec parse_geometry_data(geom_type(), point_type(), binary()) -> {geometry(), binary()}.
  192. parse_geometry_data(curve, _, _) -> error({curve, not_supported});
  193. parse_geometry_data(surface, _, _) -> error({surface, not_supported});
  194. parse_geometry_data(geometry, _, _) -> error({geometry, not_supported});
  195. parse_geometry_data(point, PointType, Data) ->
  196. parse_point(PointType, Data);
  197. parse_geometry_data(LineType, PointType, Data)
  198. when LineType == line_string;
  199. LineType == circular_string ->
  200. {Points, Rest} = parse_collection(point, PointType, Data),
  201. {{LineType, PointType, Points}, Rest};
  202. parse_geometry_data(polygon, PointType, Data) ->
  203. {Lines, Rest} = parse_collection(line_string, PointType, Data),
  204. {#polygon{ point_type = PointType, rings = Lines }, Rest};
  205. parse_geometry_data(triangle, PointType, Data) ->
  206. {#polygon{ rings = Rings }, Rest} = parse_geometry_data(polygon, PointType, Data),
  207. {#triangle{ point_type = PointType, rings = Rings }, Rest};
  208. parse_geometry_data(Collection, PointType, Data)
  209. when
  210. Collection == multi_point;
  211. Collection == multi_line_string;
  212. Collection == multi_curve;
  213. Collection == multi_polygon;
  214. Collection == multi_surface;
  215. Collection == compound_curve;
  216. Collection == curve_polygon;
  217. Collection == geometry_collection;
  218. Collection == polyhedral_surface;
  219. Collection == tin ->
  220. {Lines, Rest} = parse_typed_collection(Data),
  221. {{Collection, PointType, Lines}, Rest}.
  222. -spec parse_collection(geom_type(), point_type(), binary()) -> {[geometry()], binary()}.
  223. parse_collection(Type, PointType, Data) ->
  224. {Length, CountRest} = parse_int32(Data),
  225. lists:foldl(
  226. fun(_, {Geoms, Rest}) ->
  227. {Geom, R} = parse_geometry_data(Type, PointType, Rest),
  228. {Geoms ++ [Geom], R}
  229. end,
  230. {[], CountRest},
  231. lists:seq(1, Length)).
  232. -spec parse_typed_collection(binary()) -> {[geometry()], binary()}.
  233. parse_typed_collection(Data) ->
  234. {Length, CountRest} = parse_int32(Data),
  235. lists:foldl(
  236. fun(_, {Geoms, Rest}) ->
  237. {Geom, R} = parse_geometry_data(Rest),
  238. {Geoms ++ [Geom], R}
  239. end,
  240. {[], CountRest},
  241. lists:seq(1, Length)).
  242. -spec parse_int32(binary()) -> {integer(), binary()}.
  243. parse_int32(<<Hex:8/binary, Rest/binary>>) ->
  244. <<Int:1/little-integer-unit:32>> = hex_to_bin(Hex),
  245. {Int, Rest}.
  246. -spec parse_float64(binary()) -> {float(), binary()}.
  247. parse_float64(<<Hex:16/binary, Rest/binary>>) ->
  248. <<Float:1/little-float-unit:64>> = hex_to_bin(Hex),
  249. {Float, Rest}.
  250. parse_point(PointType, Data) ->
  251. {Values, Rest} = lists:foldl(
  252. fun(_, {Values, Rest}) ->
  253. {Value, R} = parse_float64(Rest),
  254. {Values ++ [Value], R}
  255. end,
  256. {[], Data},
  257. lists:seq(1, point_size(PointType))),
  258. Point = case {PointType, Values} of
  259. {'2d', [X,Y]} ->
  260. #point{ point_type = PointType, x = X, y = Y };
  261. {'2dm', [X,Y,M]} ->
  262. #point{ point_type = PointType, x = X, y = Y, m = M };
  263. {'3d', [X,Y,Z]} ->
  264. #point{ point_type = PointType, x = X, y = Y, z = Z };
  265. {'3dm', [X,Y,Z,M]} ->
  266. #point{ point_type = PointType, x = X, y = Y, z = Z, m = M }
  267. end,
  268. {Point, Rest}.
  269. -spec point_size(point_type()) -> 2..4.
  270. point_size('2d') -> 2;
  271. point_size('2dm') -> 3;
  272. point_size('3d') -> 3;
  273. point_size('3dm') -> 4.
  274. -spec parse_type(binary()) -> geom_type().
  275. parse_type(<<"0000">>) -> geometry;
  276. parse_type(<<"0100">>) -> point;
  277. parse_type(<<"0200">>) -> line_string;
  278. parse_type(<<"0300">>) -> polygon;
  279. parse_type(<<"0400">>) -> multi_point;
  280. parse_type(<<"0500">>) -> multi_line_string;
  281. parse_type(<<"0600">>) -> multi_polygon;
  282. parse_type(<<"0700">>) -> geometry_collection;
  283. parse_type(<<"0800">>) -> circular_string;
  284. parse_type(<<"0900">>) -> compound_curve;
  285. parse_type(<<"0A00">>) -> curve_polygon;
  286. parse_type(<<"0B00">>) -> multi_curve;
  287. parse_type(<<"0C00">>) -> multi_surface;
  288. parse_type(<<"0D00">>) -> curve;
  289. parse_type(<<"0E00">>) -> surface;
  290. parse_type(<<"0F00">>) -> polyhedral_surface;
  291. parse_type(<<"1000">>) -> tin;
  292. parse_type(<<"1100">>) -> triangle.
  293. -spec format_type(geometry() | geom_type()) -> binary().
  294. format_type(Geometry) when is_tuple(Geometry) ->
  295. format_type(element(1, Geometry));
  296. format_type(geometry) -> <<"0000">>;
  297. format_type(point) -> <<"0100">>;
  298. format_type(line_string) -> <<"0200">>;
  299. format_type(polygon) -> <<"0300">>;
  300. format_type(multi_point) -> <<"0400">>;
  301. format_type(multi_line_string) -> <<"0500">>;
  302. format_type(multi_polygon) -> <<"0600">>;
  303. format_type(geometry_collection) -> <<"0700">>;
  304. format_type(circular_string) -> <<"0800">>;
  305. format_type(compound_curve) -> <<"0900">>;
  306. format_type(curve_polygon) -> <<"0A00">>;
  307. format_type(multi_curve) -> <<"0B00">>;
  308. format_type(multi_surface) -> <<"0C00">>;
  309. format_type(curve) -> <<"0D00">>;
  310. format_type(surface) -> <<"0E00">>;
  311. format_type(polyhedral_surface) -> <<"0F00">>;
  312. format_type(tin) -> <<"1000">>;
  313. format_type(triangle) -> <<"1100">>.
  314. -spec parse_point_type(binary()) -> point_type().
  315. parse_point_type(<<"0000">>) -> '2d';
  316. parse_point_type(<<"0080">>) -> '3d';
  317. parse_point_type(<<"0040">>) -> '2dm';
  318. parse_point_type(<<"00c0">>) -> '3dm'.
  319. -spec format_point_type(geometry() | point_type()) -> binary().
  320. format_point_type(Geometry) when is_tuple(Geometry) ->
  321. format_point_type(element(2, Geometry));
  322. format_point_type('2d') -> <<"0000">>;
  323. format_point_type('3d') -> <<"0080">>;
  324. format_point_type('2dm') -> <<"0040">>;
  325. format_point_type('3dm') -> <<"00c0">>.
  326. hex_to_bin(<<C:2/binary>>) ->
  327. Int = binary_to_integer(C, 16),
  328. << Int >>;
  329. hex_to_bin(<<C:2/binary, Rest/binary>>) ->
  330. Int = binary_to_integer(C, 16),
  331. RestBin = hex_to_bin(Rest),
  332. << Int, RestBin/binary >>.
  333. bin_to_hex(<<C>>) ->
  334. Int = int_to_hex(C),
  335. << Int/binary >>;
  336. bin_to_hex(<<C, Rest/binary>>) ->
  337. Int = int_to_hex(C),
  338. RestBin = bin_to_hex(Rest),
  339. << Int/binary, RestBin/binary >>.
  340. int_to_hex(C) ->
  341. case integer_to_binary(C, 16) of
  342. <<_,_>> = Val -> Val;
  343. <<Byte>> -> <<"0", Byte>>
  344. end.