Skip to content

Commit 2ea761e

Browse files
committed
expose plpgsql integer encoding and conversion functions
1 parent 2bd6c06 commit 2ea761e

2 files changed

Lines changed: 145 additions & 42 deletions

File tree

plpgsql/pluscode_functions.sql

Lines changed: 136 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,79 @@ BEGIN
9090
WHILE (lng >= 180) LOOP
9191
lng := lng - 360;
9292
END LOOP;
93-
return lng;
93+
RETURN lng;
94+
END;
95+
$BODY$;
96+
97+
98+
-- pluscode_latitudeToInteger ####
99+
-- Convert latitude to an integer representation.
100+
-- PARAMETERS
101+
-- latitude numeric // latitude in degrees
102+
-- EXAMPLE
103+
-- select pluscode_latitudeToInteger(149.18);
104+
CREATE OR REPLACE FUNCTION public.pluscode_latitudeToInteger(
105+
latitude numeric)
106+
RETURNS numeric
107+
LANGUAGE 'plpgsql'
108+
COST 100
109+
IMMUTABLE
110+
AS $BODY$
111+
DECLARE
112+
CODE_ALPHABET_ text := '23456789CFGHJMPQRVWX';
113+
ENCODING_BASE_ int := char_length(CODE_ALPHABET_);
114+
LATITUDE_MAX_ int := 90;
115+
MAX_DIGIT_COUNT_ int := 15;
116+
PAIR_CODE_LENGTH_ int := 10;
117+
PAIR_PRECISION_ decimal := power(ENCODING_BASE_, 3);
118+
GRID_ROWS_ int := 5;
119+
FINAL_LAT_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_ROWS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
120+
latVal decimal := 0;
121+
BEGIN
122+
latVal := round(latitude * FINAL_LAT_PRECISION_);
123+
latVal := latVal + LATITUDE_MAX_ * FINAL_LAT_PRECISION_;
124+
IF (latVal < 0) THEN
125+
latVal := 0;
126+
ELSIF (latVal > 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_) THEN
127+
latVal := 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_ - 1;
128+
END IF;
129+
RETURN latVal;
130+
END;
131+
$BODY$;
132+
133+
134+
-- pluscode_longitudeToInteger ####
135+
-- Convert longitude to an integer representation.
136+
-- PARAMETERS
137+
-- longitude numeric // longitude in degrees
138+
-- EXAMPLE
139+
-- select pluscode_longitudeToInteger(149.18);
140+
CREATE OR REPLACE FUNCTION public.pluscode_longitudeToInteger(
141+
longitude numeric)
142+
RETURNS numeric
143+
LANGUAGE 'plpgsql'
144+
COST 100
145+
IMMUTABLE
146+
AS $BODY$
147+
DECLARE
148+
CODE_ALPHABET_ text := '23456789CFGHJMPQRVWX';
149+
ENCODING_BASE_ int := char_length(CODE_ALPHABET_);
150+
LONGITUDE_MAX_ int := 180;
151+
MAX_DIGIT_COUNT_ int := 15;
152+
PAIR_CODE_LENGTH_ int := 10;
153+
PAIR_PRECISION_ decimal := power(ENCODING_BASE_, 3);
154+
GRID_COLUMNS_ int := 4;
155+
FINAL_LNG_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_COLUMNS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
156+
lngVal decimal := 0;
157+
BEGIN
158+
lngVal := round(longitude * FINAL_LNG_PRECISION_);
159+
lngVal := lngVal + LONGITUDE_MAX_ * FINAL_LNG_PRECISION_;
160+
IF (lngVal < 0) THEN
161+
lngVal := lngVal % (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_) + 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_;
162+
ELSIF (lngVal > 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_) THEN
163+
lngVal := lngVal % (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_);
164+
END IF;
165+
RETURN lngVal;
94166
END;
95167
$BODY$;
96168

@@ -302,17 +374,16 @@ END;
302374
$BODY$;
303375

304376

305-
-- pluscode_encode ####
306-
-- Encode lat lng to get pluscode
377+
-- pluscode_encodeIntegers ####
378+
-- Encode lat lng in their integer representation to get an Open Location Code.
379+
-- This function is for testing purposes only.
307380
-- PARAMETERS
308381
-- latitude numeric // latitude ref
309382
-- longitude numeric // longitude ref
310383
-- codeLength int// How long must be the pluscode
311-
-- EXAMPLE
312-
-- select pluscode_encode(49.05,-0.108,12);
313-
CREATE OR REPLACE FUNCTION public.pluscode_encode(
314-
latitude numeric,
315-
longitude numeric,
384+
CREATE OR REPLACE FUNCTION public.pluscode_encodeIntegers(
385+
latVal numeric,
386+
lngVal numeric,
316387
codeLength int DEFAULT 10)
317388
RETURNS text
318389
LANGUAGE 'plpgsql'
@@ -325,51 +396,17 @@ DECLARE
325396
PADDING_CHARACTER_ text := '0';
326397
CODE_ALPHABET_ text := '23456789CFGHJMPQRVWX';
327398
ENCODING_BASE_ int := char_length(CODE_ALPHABET_);
328-
LATITUDE_MAX_ int := 90;
329-
LONGITUDE_MAX_ int := 180;
330-
MIN_DIGIT_COUNT_ int := 2;
331399
MAX_DIGIT_COUNT_ int := 15;
332400
PAIR_CODE_LENGTH_ int := 10;
333-
PAIR_PRECISION_ decimal := power(ENCODING_BASE_, 3);
334401
GRID_CODE_LENGTH_ int := MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_;
335402
GRID_COLUMNS_ int := 4;
336403
GRID_ROWS_ int := 5;
337-
FINAL_LAT_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_ROWS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
338-
FINAL_LNG_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_COLUMNS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
339404
code text := '';
340-
latVal decimal := 0;
341-
lngVal decimal := 0;
342405
latDigit smallint;
343406
lngDigit smallint;
344407
ndx smallint;
345408
i_ smallint;
346409
BEGIN
347-
IF ((codeLength < MIN_DIGIT_COUNT_) OR ((codeLength < PAIR_CODE_LENGTH_) AND (codeLength % 2 = 1))) THEN
348-
RAISE EXCEPTION 'Invalid Open Location Code length - %', codeLength
349-
USING HINT = 'The Open Location Code length must be 2, 4, 6, 8, 10, 11, 12, 13, 14, or 15.';
350-
END IF;
351-
352-
codeLength := LEAST(codeLength, MAX_DIGIT_COUNT_);
353-
354-
latVal := floor(round((latitude + LATITUDE_MAX_) * FINAL_LAT_PRECISION_, 6));
355-
lngVal := floor(round((longitude + LONGITUDE_MAX_) * FINAL_LNG_PRECISION_, 6));
356-
357-
latVal := round(latitude * FINAL_LAT_PRECISION_);
358-
latVal := latVal + LATITUDE_MAX_ * FINAL_LAT_PRECISION_;
359-
IF (latVal < 0) THEN
360-
latVal := 0;
361-
ELSIF (latVal > 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_) THEN
362-
latVal := 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_ - 1;
363-
END IF;
364-
365-
lngVal := round(longitude * FINAL_LNG_PRECISION_);
366-
lngVal := lngVal + LONGITUDE_MAX_ * FINAL_LNG_PRECISION_;
367-
IF (lngVal < 0) THEN
368-
lngVal := lngVal % (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_) + 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_;
369-
ELSIF (lngVal > 2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_) THEN
370-
lngVal := lngVal % (2 * LONGITUDE_MAX_ * FINAL_LNG_PRECISION_);
371-
END IF;
372-
373410
IF (codeLength > PAIR_CODE_LENGTH_) THEN
374411
i_ := 0;
375412
WHILE (i_ < (MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_)) LOOP
@@ -406,6 +443,63 @@ END;
406443
$BODY$;
407444

408445

446+
-- pluscode_encode ####
447+
-- Encode lat lng to get pluscode
448+
-- PARAMETERS
449+
-- latitude numeric // latitude ref
450+
-- longitude numeric // longitude ref
451+
-- codeLength int// How long must be the pluscode
452+
-- EXAMPLE
453+
-- select pluscode_encode(49.05,-0.108,12);
454+
CREATE OR REPLACE FUNCTION public.pluscode_encode(
455+
latitude numeric,
456+
longitude numeric,
457+
codeLength int DEFAULT 10)
458+
RETURNS text
459+
LANGUAGE 'plpgsql'
460+
COST 100
461+
IMMUTABLE
462+
AS $BODY$
463+
DECLARE
464+
SEPARATOR_ text := '+';
465+
SEPARATOR_POSITION_ int := 8;
466+
PADDING_CHARACTER_ text := '0';
467+
CODE_ALPHABET_ text := '23456789CFGHJMPQRVWX';
468+
ENCODING_BASE_ int := char_length(CODE_ALPHABET_);
469+
LATITUDE_MAX_ int := 90;
470+
LONGITUDE_MAX_ int := 180;
471+
MIN_DIGIT_COUNT_ int := 2;
472+
MAX_DIGIT_COUNT_ int := 15;
473+
PAIR_CODE_LENGTH_ int := 10;
474+
PAIR_PRECISION_ decimal := power(ENCODING_BASE_, 3);
475+
GRID_CODE_LENGTH_ int := MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_;
476+
GRID_COLUMNS_ int := 4;
477+
GRID_ROWS_ int := 5;
478+
FINAL_LAT_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_ROWS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
479+
FINAL_LNG_PRECISION_ decimal := PAIR_PRECISION_ * power(GRID_COLUMNS_, MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_);
480+
code text := '';
481+
latVal decimal := 0;
482+
lngVal decimal := 0;
483+
latDigit smallint;
484+
lngDigit smallint;
485+
ndx smallint;
486+
i_ smallint;
487+
BEGIN
488+
IF ((codeLength < MIN_DIGIT_COUNT_) OR ((codeLength < PAIR_CODE_LENGTH_) AND (codeLength % 2 = 1))) THEN
489+
RAISE EXCEPTION 'Invalid Open Location Code length - %', codeLength
490+
USING HINT = 'The Open Location Code length must be 2, 4, 6, 8, 10, 11, 12, 13, 14, or 15.';
491+
END IF;
492+
493+
codeLength := LEAST(codeLength, MAX_DIGIT_COUNT_);
494+
495+
latVal := pluscode_latitudeToInteger(latitude);
496+
lngVal := pluscode_longitudeToInteger(longitude);
497+
498+
RETURN pluscode_encodeIntegers(latVal, lngVal, codeLength);
499+
END;
500+
$BODY$;
501+
502+
409503
-- pluscode_decode ####
410504
-- Decode a pluscode to get the corresponding bounding box and the center
411505
-- PARAMETERS

plpgsql/tests_script_l.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
--Test script for openlocationcode functions
22

3+
4+
--###############################################################
5+
--pluscode_latitudeToInteger
6+
select pluscode_latitudeToInteger(149.18);
7+
-- pluscode_latitudeToInteger
8+
-----------------
9+
-- 4499999999
10+
-- (1 row)
11+
312
--###############################################################
413
--pluscode_cliplatitude
514
select pluscode_cliplatitude(149.18);

0 commit comments

Comments
 (0)