Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions provider/postgis/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ type ErrInvalidSSLMode string
func (e ErrInvalidSSLMode) Error() string {
return fmt.Sprintf("postgis: invalid ssl mode (%v)", string(e))
}

type ErrUnclosedToken string

func (e ErrUnclosedToken) Error() string {
return fmt.Sprintf("postgis: unclosed token in (%v)", string(e))
}
50 changes: 47 additions & 3 deletions provider/postgis/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"strconv"
"strings"
"unicode"

"github.com/go-spatial/tegola/basic"
"github.com/go-spatial/tegola/provider"
Expand Down Expand Up @@ -67,14 +68,20 @@ func genSQL(l *Layer, pool *pgx.ConnPool, tblname string, flds []string) (sql st
}

const (
bboxToken = "!BBOX!"
zoomToken = "!ZOOM!"
bboxToken = "!BBOX!"
zoomToken = "!ZOOM!"
scaleDenominatorToken = "!SCALE_DENOMINATOR!"
pixelWidthToken = "!PIXEL_WIDTH!"
pixelHeightToken = "!PIXEL_HEIGHT!"
)

// replaceTokens replaces tokens in the provided SQL string
//
// !BBOX! - the bounding box of the tile
// !ZOOM! - the tile Z value
// !SCALE_DENOMINATOR! - scale denominator, assuming 90.7 DPI (i.e. 0.28mm pixel size)
// !PIXEL_WIDTH! - the pixel width in m, assuming 256x256 tiles
// !PIXEL_HEIGHT! - the pixel height in m, assuming 256x256 tiles
func replaceTokens(sql string, srid uint64, tile provider.Tile) (string, error) {

bufferedExtent, _ := tile.BufferedExtent()
Expand All @@ -95,14 +102,51 @@ func replaceTokens(sql string, srid uint64, tile provider.Tile) (string, error)

bbox := fmt.Sprintf("ST_MakeEnvelope(%g,%g,%g,%g,%d)", minPt.X(), minPt.Y(), maxPt.X(), maxPt.Y(), srid)

extent, _ := tile.Extent()
// TODO: Always convert to meter if we support different projections
pixelWidth := (extent.MaxX() - extent.MinX()) / 256
pixelHeight := (extent.MaxY() - extent.MinY()) / 256
scaleDenominator := pixelWidth / 0.00028 /* px size in m */

// replace query string tokens
z, _, _ := tile.ZXY()
tokenReplacer := strings.NewReplacer(
bboxToken, bbox,
zoomToken, strconv.FormatUint(uint64(z), 10),
scaleDenominatorToken, strconv.FormatFloat(scaleDenominator, 'f', -1, 64),
pixelWidthToken, strconv.FormatFloat(pixelWidth, 'f', -1, 64),
pixelHeightToken, strconv.FormatFloat(pixelHeight, 'f', -1, 64),
)

return tokenReplacer.Replace(sql), nil
uppercaseTokenSQL, err := uppercaseTokens(sql)
if err != nil {
return "", err
}

return tokenReplacer.Replace(uppercaseTokenSQL), nil
}

// uppercaseTokens will sniff for ! chars and uppercase everything between them
// if an odd number of ! are found an error is thrown
func uppercaseTokens(str string) (string, error) {
rs := []rune(str)

uppercase := false
for i := range rs {
if rs[i] == '!' {
uppercase = !uppercase
continue
}
if uppercase {
rs[i] = unicode.ToUpper(rs[i])
}
}

if uppercase {
return str, ErrUnclosedToken(str)
}

return string(rs), nil
}

func transformVal(valType pgtype.OID, val interface{}) (interface{}, error) {
Expand Down
56 changes: 56 additions & 0 deletions provider/postgis/util_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func TestReplaceTokens(t *testing.T) {
tile: slippy.NewTile(16, 11241, 26168, 64, tegola.WebMercator),
expected: "SELECT id, scalerank=16 FROM foo WHERE geom && ST_MakeEnvelope(-1.3163688815956049e+07,4.0352540420407774e+06,-1.3163058210472783e+07,4.035884647524042e+06,3857)",
},
"replace pixel_width/height and scale_denominator": {
sql: "SELECT id, !pixel_width! as width, !pixel_height! as height, !scale_denominator! as scale_denom FROM foo WHERE geom && !BBOX!",
srid: tegola.WebMercator,
tile: slippy.NewTile(11, 1070, 676, 64, tegola.WebMercator),
expected: "SELECT id, 76.43702827453626 as width, 76.43702827453671 as height, 272989.3866947724 as scale_denom FROM foo WHERE geom && ST_MakeEnvelope(899816.6968478388,6.789748347570495e+06,919996.0723123164,6.809927723034973e+06,3857)",
},
}

for name, tc := range tests {
Expand All @@ -61,6 +67,56 @@ func TestReplaceTokens(t *testing.T) {
}
}

func TestUppercaseTokens(t *testing.T) {
type tcase struct {
str string
expected string
expectedErr error
}

fn := func(tc tcase) func(t *testing.T) {
return func(t *testing.T) {
out, err := uppercaseTokens(tc.str)
if err != nil {
if tc.expectedErr.Error() != err.Error() {
t.Errorf("unexpected error, expected %v got %v", tc.expectedErr, err)
return
}

return
}

if out != tc.expected {
t.Errorf("expected \n \t%v\n out \n \t%v", tc.expected, out)
return
}
}
}

tests := map[string]tcase{
"uppercase tokens": {
str: "this !lower! case !STrInG! should uppercase !TOKENS!",
expected: "this !LOWER! case !STRING! should uppercase !TOKENS!",
},
"no tokens": {
str: "no token",
expected: "no token",
},
"empty string": {
str: "",
expected: "",
},
"unclosed token": {
str: "unclosed !token",
expectedErr: ErrUnclosedToken("unclosed !token"),
},
}

for name, tc := range tests {
t.Run(name, fn(tc))
}
}

func TestDecipherFields(t *testing.T) {
ttools.ShouldSkip(t, TESTENV)

Expand Down