Include types from gonavitia/navitia
This commit is contained in:
parent
82769f923e
commit
24b1102761
5
go.mod
5
go.mod
@ -4,6 +4,10 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/mb0/wkt v0.0.0-20170420051526-a30afd545ee1
|
||||
github.com/paulmach/go.geojson v1.5.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/twpayne/go-geom v1.5.2
|
||||
golang.org/x/text v0.11.0
|
||||
)
|
||||
|
||||
@ -30,6 +34,5 @@ require (
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
126
go.sum
126
go.sum
@ -1,12 +1,9 @@
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -14,38 +11,15 @@ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
|
||||
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
|
||||
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
@ -54,144 +28,66 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/mb0/wkt v0.0.0-20170420051526-a30afd545ee1 h1:VCgV+ng800r1/AChRHzHbWCtQI06cPxoZQUljQHTyXc=
|
||||
github.com/mb0/wkt v0.0.0-20170420051526-a30afd545ee1/go.mod h1:IhobDa5AIyiMAsnH/qkytD0NbG0JMOJ2ihQqe1NdXyg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||
github.com/paulmach/go.geojson v1.5.0 h1:7mhpMK89SQdHFcEGomT7/LuJhwhEgfmpWYVlVmLEdQw=
|
||||
github.com/paulmach/go.geojson v1.5.0/go.mod h1:DgdUy2rRVDDVgKqrjMe2vZAHMfhDTrjVKt3LmHIXGbU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/twpayne/go-geom v1.5.2 h1:LyRfBX2W0LM7XN/bGqX0XxrJ7SZc3XwmxU4aj4kSoxw=
|
||||
github.com/twpayne/go-geom v1.5.2/go.mod h1:3z6O2sAnGtGCXx4Q+5nPOLCA5e8WI2t3cthdb1P2HH8=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
4
types/.gitignore
vendored
Normal file
4
types/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.zip
|
||||
Fuzz*Dir/
|
||||
*.prof
|
||||
*.test
|
78
types/README.md
Normal file
78
types/README.md
Normal file
@ -0,0 +1,78 @@
|
||||
# navitia/types is a library for working with types returned by the [Navitia](navitia.io) API. [![GoDoc](https://godoc.org/github.com/govitia/navitia/types?status.svg)](https://godoc.org/github.com/govitia/navitia/types)
|
||||
|
||||
Package types implements support for the types used in the Navitia API (see doc.navitia.io), simplified and modified for idiomatic Go use.
|
||||
|
||||
This is navitia/types v0.2. It is not API-Stable, and won't be until the v1 release of navitia, but it's getting closer !
|
||||
This package was and is developped as a supporting library for the [navitia API client](https://github.com/govitia/navitia) but can be used to build other navitia API clients.
|
||||
|
||||
## Install
|
||||
|
||||
Simply run `go get -u github.com/govitia/navitia/types`.
|
||||
|
||||
## Coverage
|
||||
|
||||
Preview of the supported types, see [the doc](https://godoc.org/github.com/govitia/navitia-types) for more information, and the [navitia.io doc](http://doc.navitia.io) for information about the remote API.
|
||||
|
||||
|Type Name|Description|Navitia Name|
|
||||
|---|---|---|
|
||||
|[`Journey`](https://godoc.org/github.com/govitia/navitia-types#Journey)|A journey (X-->Y)|"journey"|
|
||||
|[`Section`](https://godoc.org/github.com/govitia/navitia-types#Section)|A section of a `Journey`|"section"|
|
||||
|[`Region`](https://godoc.org/github.com/govitia/navitia-types#Region)|A region covered by the API|"region"|
|
||||
|[`Isochrone`](https://godoc.org/github.com/govitia/navitia-types#Region)|A region covered by the API|"isochrone"|
|
||||
|[`Container`](https://godoc.org/github.com/govitia/navitia-types#Container)|This contains a Place or a PTObject|"place"/"pt_object"|
|
||||
|[`Place`](https://godoc.org/github.com/govitia/navitia-types#Place)|Place is an empty interface, by convention used to identify an `Address`, [`StopPoint`](https://godoc.org/github.com/govitia/navitia-types#StopPoint), [`StopArea`](https://godoc.org/github.com/govitia/navitia-types#StopArea), [`POI`](https://godoc.org/github.com/govitia/navitia-types#POI), [`Admin`](https://godoc.org/github.com/govitia/navitia-types#Admin) & [`Coordinates`](https://godoc.org/github.com/govitia/navitia-types#Coordinates).|
|
||||
|[`PTObject`](https://godoc.org/github.com/govitia/navitia-types#Place)|PTObject is an empty interface by convention used to identify a Public Transportation object|
|
||||
|[`Line`](https://godoc.org/github.com/govitia/navitia-types#Line)|A public transit line.|"line"|
|
||||
|[`Route`](https://godoc.org/github.com/govitia/navitia-types#Route)|A specific route within a `Line`.|"route"|
|
||||
|
||||
And others, such as [`Display`](https://godoc.org/github.com/govitia/navitia-types#Display) ["display_informations"], [`PTDateTime`](https://godoc.org/github.com/govitia/navitia-types#PTDateTime) ["pt-date-time"], [`StopTime`](https://godoc.org/github.com/govitia/navitia-types#StopTime) ["stop_time"]
|
||||
|
||||
## Getting started
|
||||
|
||||
```golang
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/govitia/navitia/types"
|
||||
)
|
||||
|
||||
func main() {
|
||||
data := []byte{"some journey's json"}
|
||||
var j types.Journey
|
||||
_ = j.UnmarshalJSON(data)
|
||||
}
|
||||
```
|
||||
|
||||
### Going further
|
||||
|
||||
Obviously, this is a very simple example of what navitia/types can do, [check out the documentation !](https://godoc.org/github.com/govitia/navitia/types)
|
||||
|
||||
## What's new in v0.2
|
||||
|
||||
- Merge back into the `navitia` tree !
|
||||
- `Container` is now a type that can be used as a Place Container or as a PTObject Container, which helps everyone!
|
||||
- No more `String` methods
|
||||
- Better unmarshalling, including better error handling, along with better testing
|
||||
- Benchmarks !
|
||||
- `Disruption` support, along with what it entails.
|
||||
- Rename `JourneyStatus` to `Effect`
|
||||
- And others ! See `git log` for more information !
|
||||
|
||||
## TODO
|
||||
|
||||
### Documentation
|
||||
|
||||
- Update `readme.md` to reflect new changes
|
||||
- Add links to the doc.navitia.io documentation to every type
|
||||
|
||||
### Testing
|
||||
|
||||
- `(*PTDateTime).UnmarshalJSON`
|
||||
- `ErrInvalidPlaceContainer.Error`
|
||||
- `Equipment.Known`
|
||||
- Every Type should have at least one file to be tested against
|
||||
- Globalise mutual code in unmarshal testers
|
||||
|
||||
## Footnotes
|
||||
|
||||
I made this project as I wanted to explore and push my go skills, and I'm really up for you to contribute ! Send me a pull request and/or contact me if you have any questions! ( [@aabizri](https://twitter.com/aabizri) on twitter)
|
6
types/activePeriod.go
Normal file
6
types/activePeriod.go
Normal file
@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type ActivePeriod struct {
|
||||
Begin string `json:"begin"`
|
||||
End string `json:"end"`
|
||||
}
|
9
types/calendar.go
Normal file
9
types/calendar.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// Calendar is returned on vehicle journey message and indicates periodicity informations
|
||||
// about transport schedules.
|
||||
type Calendar struct {
|
||||
ActivePeriods []ActivePeriod `json:"active_periods"`
|
||||
WeekPattern WeekPattern `json:"week_pattern"`
|
||||
Exceptions []Exception `json:"exceptions"`
|
||||
}
|
9
types/channel.go
Normal file
9
types/channel.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// A Channel is a destination media for a message.
|
||||
type Channel struct {
|
||||
ID ID `json:"id"` // ID of the address
|
||||
ContentType string `json:"content_type"` // Content Type (text/html etc.) RFC1341.4
|
||||
Name string `json:"name"` // Name of the channel
|
||||
Types []string `json:"types,omitempty"` // Types ?
|
||||
}
|
65
types/common_test.go
Normal file
65
types/common_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testUnmarshal is a helper to test unmarshalling for any value implementing results.
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func testUnmarshal(t *testing.T, data typeTestData, resultsType reflect.Type) {
|
||||
t.Helper()
|
||||
// Create the run function generator, allowing us to run this in parallel
|
||||
rgen := func(data []byte, correct bool) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
// Declare this test to be run in parallel
|
||||
t.Parallel()
|
||||
|
||||
// Create a pointer to a new value of the type indicated in resultsType
|
||||
res := reflect.New(resultsType).Interface()
|
||||
|
||||
// We use encoding/json's unmarshaller, as we don't have one for this type
|
||||
err := json.Unmarshal(data, res)
|
||||
|
||||
// We check that the result is what we expect:
|
||||
// If we expect no errors (correct == true) but we get one, the test has failed
|
||||
// If we expect an error (correct == false) but we don't get one, the test has failes
|
||||
// In all other cases, the test is successful !
|
||||
if err != nil && correct {
|
||||
t.Errorf("expected no errors but got one: %v", err)
|
||||
} else if err == nil && !correct {
|
||||
t.Errorf("expected an error but didn't get one !")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the sub functions (those will be the correct and incorrect version of this test)
|
||||
sub := func(data map[string][]byte, correct bool) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
// Declare this test to be run in parallel
|
||||
t.Parallel()
|
||||
|
||||
// If we have no data, we skip
|
||||
if len(data) == 0 {
|
||||
t.Skip("no data provided, skipping...")
|
||||
}
|
||||
|
||||
// For all files provided
|
||||
for name, datum := range data {
|
||||
// Get the run function
|
||||
rfunc := rgen(datum, correct)
|
||||
|
||||
// Run !
|
||||
t.Run(name, rfunc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run !
|
||||
t.Run("correct", sub(data.correct, true))
|
||||
t.Run("incorrect", sub(data.incorrect, false))
|
||||
}
|
9
types/company.go
Normal file
9
types/company.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// A Company is a provider of transport
|
||||
// Example: the RATP in Paris
|
||||
// See http://doc.navitia.io/#public-transport-objects
|
||||
type Company struct {
|
||||
ID string `json:"id"` // Identifier of the company
|
||||
Name string `json:"name"` // Name of the company
|
||||
}
|
280
types/container.go
Normal file
280
types/container.go
Normal file
@ -0,0 +1,280 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// these are the types that can be embedded.
|
||||
const (
|
||||
EmbeddedStopArea = "stop_area" // This is a Place & a PT Object
|
||||
EmbeddedPOI = "poi" // This is a place
|
||||
EmbeddedAddress = "address" // This is a place
|
||||
EmbeddedStopPoint = "stop_point" // This is a place
|
||||
EmbeddedAdmin = "administrative_region" // This is a place
|
||||
EmbeddedLine = "line" // This is a PT Object
|
||||
EmbeddedRoute = "route" // This is a PT Object
|
||||
EmbeddedNetwork = "network" // This is a PT Object
|
||||
EmbeddedCommercialMode = "commercial_mode" // This is a PT Object
|
||||
EmbeddedTrip = "trip" // This is a PT Object
|
||||
)
|
||||
|
||||
// EmbeddedTypes lists all the possible embedded types you can find in a Container
|
||||
var EmbeddedTypes = [...]string{
|
||||
EmbeddedStopArea,
|
||||
EmbeddedPOI,
|
||||
EmbeddedAddress,
|
||||
EmbeddedStopPoint,
|
||||
EmbeddedAdmin,
|
||||
EmbeddedLine,
|
||||
EmbeddedRoute,
|
||||
EmbeddedNetwork,
|
||||
EmbeddedCommercialMode,
|
||||
EmbeddedTrip,
|
||||
}
|
||||
|
||||
// embeddedTypesPlace stores a list of embedded types you can find in a container containing a Place.
|
||||
var embeddedTypesPlace = [...]string{
|
||||
EmbeddedStopArea,
|
||||
EmbeddedPOI,
|
||||
EmbeddedAddress,
|
||||
EmbeddedStopPoint,
|
||||
EmbeddedAdmin,
|
||||
}
|
||||
|
||||
// embeddedTypesPTObject stores a list of embedded types you can find in a container containing a PTObject.
|
||||
var embeddedTypesPTObject = [...]string{
|
||||
EmbeddedStopArea,
|
||||
EmbeddedLine,
|
||||
EmbeddedRoute,
|
||||
EmbeddedNetwork,
|
||||
EmbeddedCommercialMode,
|
||||
EmbeddedTrip,
|
||||
}
|
||||
|
||||
// An Object is what is contained by a Container
|
||||
type Object interface{}
|
||||
|
||||
// A Container holds an Object, which can be a Place or a PT Object
|
||||
type Container struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
EmbeddedType string `json:"embedded_type"`
|
||||
Quality int `json:"quality"`
|
||||
|
||||
embeddedJSON json.RawMessage
|
||||
|
||||
// embeddedObject acts as a cache, it is the only element guarded by the RWMutex
|
||||
embeddedObject Object
|
||||
|
||||
// If multiple goroutines try to access embeddedObject while one is writing it, results are undefined.
|
||||
// So we guard against it through a mutex.
|
||||
mu *sync.RWMutex
|
||||
}
|
||||
|
||||
// IsPlace returns true if the container's content is a Place
|
||||
func (c *Container) IsPlace() bool {
|
||||
t := c.EmbeddedType
|
||||
return t == EmbeddedStopArea ||
|
||||
t == EmbeddedPOI ||
|
||||
t == EmbeddedAddress ||
|
||||
t == EmbeddedStopPoint ||
|
||||
t == EmbeddedAdmin
|
||||
}
|
||||
|
||||
// IsPTObject returns true if the container's content is a PTObject
|
||||
func (c *Container) IsPTObject() bool {
|
||||
t := c.EmbeddedType
|
||||
return t == EmbeddedStopArea ||
|
||||
t == EmbeddedLine ||
|
||||
t == EmbeddedRoute ||
|
||||
t == EmbeddedNetwork ||
|
||||
t == EmbeddedCommercialMode ||
|
||||
t == EmbeddedTrip
|
||||
}
|
||||
|
||||
// ErrInvalidContainer is returned after a check on a Container
|
||||
type ErrInvalidContainer struct {
|
||||
// If the Container has a zero ID.
|
||||
NoID bool
|
||||
|
||||
// If the PlaceContainer has an EmbeddedType yet non-empty embedded content.
|
||||
NoEmbeddedType bool
|
||||
|
||||
// If the PlaceContainer has an unknown EmbeddedType
|
||||
UnknownEmbeddedType bool
|
||||
}
|
||||
|
||||
// Error satisfies the error interface
|
||||
func (err ErrInvalidContainer) Error() string {
|
||||
// Count the number of anomalies
|
||||
var anomalies uint
|
||||
|
||||
msg := "Error: Invalid non-empty PlaceContainer (%d anomalies):"
|
||||
|
||||
if err.NoID {
|
||||
msg += "\n\tNo ID specified"
|
||||
anomalies++
|
||||
}
|
||||
if err.NoEmbeddedType {
|
||||
msg += "\n\tEmpty EmbeddedType yet non-empty embedded content"
|
||||
anomalies++
|
||||
}
|
||||
if err.UnknownEmbeddedType {
|
||||
msg += "\n\tUnknown EmbeddedType"
|
||||
anomalies++
|
||||
}
|
||||
|
||||
return fmt.Sprintf(msg, anomalies)
|
||||
}
|
||||
|
||||
// Empty returns true if the container is empty (zero value)
|
||||
func (c *Container) Empty() bool {
|
||||
return c.ID == "" && c.Name == "" && c.EmbeddedType == "" && c.Quality == 0 && len(c.embeddedJSON) == 0 && c.embeddedObject == nil
|
||||
}
|
||||
|
||||
// Check checks the validity of the Container. Returns an ErrInvalidContainer.
|
||||
//
|
||||
// An empty Container is valid. But those cases aren't:
|
||||
// - If the Container has an empty ID.
|
||||
// - If the Container has an empty EmbeddedType & a non-empty embedded types inside.
|
||||
// - If the Container has an unknown EmbeddedType.
|
||||
func (c *Container) Check() error {
|
||||
// Check if the container is empty
|
||||
if c.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the error to be populated
|
||||
err := ErrInvalidContainer{}
|
||||
|
||||
// Check for zero ID
|
||||
err.NoID = c.ID == ""
|
||||
|
||||
// Check if the embedded type is empty & there is a non-empty embedded content inside, that's an error
|
||||
if c.EmbeddedType == "" && (len(c.embeddedJSON) != 0 || c.embeddedObject != nil) {
|
||||
err.NoEmbeddedType = true
|
||||
return err
|
||||
} else if c.EmbeddedType == "" { // Else, if the embedded type indicator is empty, the rest is useless
|
||||
return nil
|
||||
}
|
||||
|
||||
// Else, check if the declared EmbeddedType is known.
|
||||
var known bool
|
||||
for _, ket := range EmbeddedTypes {
|
||||
if c.EmbeddedType == ket {
|
||||
known = true
|
||||
break
|
||||
}
|
||||
}
|
||||
err.UnknownEmbeddedType = !known
|
||||
|
||||
// Check if there's any change
|
||||
emptyErr := ErrInvalidContainer{}
|
||||
if err != emptyErr {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object returns the Object contained in a Container.
|
||||
// If the Container is empty, Object returns an error.
|
||||
// Check() is run on the Container.
|
||||
func (c *Container) Object() (Object, error) {
|
||||
if c.EmbeddedType == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If we already have an embedded object, return it
|
||||
c.mu.RLock()
|
||||
o := c.embeddedObject
|
||||
c.mu.RUnlock()
|
||||
if o != nil {
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// Create the receiver
|
||||
var obj Object
|
||||
|
||||
// Switch through
|
||||
switch c.EmbeddedType {
|
||||
case EmbeddedStopArea:
|
||||
obj = &StopArea{}
|
||||
case EmbeddedPOI:
|
||||
obj = &POI{}
|
||||
case EmbeddedAddress:
|
||||
obj = &Address{}
|
||||
case EmbeddedStopPoint:
|
||||
obj = &StopPoint{}
|
||||
case EmbeddedAdmin:
|
||||
obj = &Admin{}
|
||||
case EmbeddedLine:
|
||||
obj = &Line{}
|
||||
case EmbeddedRoute:
|
||||
obj = &Route{}
|
||||
case EmbeddedNetwork:
|
||||
obj = &Network{}
|
||||
case EmbeddedCommercialMode:
|
||||
obj = &CommercialMode{}
|
||||
case EmbeddedTrip:
|
||||
obj = &Trip{}
|
||||
default:
|
||||
return nil, errors.Errorf("no known embedded type indicated (we have \"%s\"), can't return a place !", c.EmbeddedType)
|
||||
}
|
||||
|
||||
// Unmarshal into the receiver
|
||||
err := json.Unmarshal(c.embeddedJSON, obj)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "couldn't unmarshal the embedded type (%s)", c.EmbeddedType)
|
||||
}
|
||||
|
||||
// Let's add it to the container
|
||||
c.mu.Lock()
|
||||
c.embeddedObject = obj
|
||||
c.mu.Unlock()
|
||||
|
||||
// Let's return it
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// Place returns the Place contained in the container if that is what's inside
|
||||
//
|
||||
// If the Object isn't a Place or the Container is empty or invalid, Place returns an error
|
||||
func (c *Container) Place() (Place, error) {
|
||||
// Check if its a Place
|
||||
if !c.IsPlace() {
|
||||
return nil, errors.Errorf("container's content isn't a Place")
|
||||
}
|
||||
|
||||
// Return it then
|
||||
obj, err := c.Object()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Type assert
|
||||
return obj.(Place), nil
|
||||
}
|
||||
|
||||
// PTObject returns the PTObject contained in the container if that is what's inside
|
||||
//
|
||||
// If the Object isn't a PTObject or the Container is empty or invalid, Place returns an error
|
||||
func (c *Container) PTObject() (PTObject, error) {
|
||||
// Check if its a Place
|
||||
if !c.IsPTObject() {
|
||||
return nil, errors.Errorf("container's content isn't a PTObject")
|
||||
}
|
||||
|
||||
// Return it then
|
||||
obj, err := c.Object()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Type assert
|
||||
return obj.(PTObject), nil
|
||||
}
|
21
types/container_fuzz.go
Normal file
21
types/container_fuzz.go
Normal file
@ -0,0 +1,21 @@
|
||||
// +build gofuzz
|
||||
|
||||
package types
|
||||
|
||||
func FuzzContainer(data []byte) int {
|
||||
c := &Container{}
|
||||
|
||||
// Let's unmarshal, this is not our job so "bleh"
|
||||
err := c.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Let's check it !
|
||||
err = c.Check()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
57
types/container_json.go
Normal file
57
types/container_json.go
Normal file
@ -0,0 +1,57 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// UnmarshalJSON satisfies the json.Unmarshaller interface
|
||||
func (c *Container) UnmarshalJSON(b []byte) error {
|
||||
// Set up a mutex
|
||||
c.mu = &sync.RWMutex{}
|
||||
|
||||
// Unmarshal into a map
|
||||
data := map[string]json.RawMessage{}
|
||||
err := json.Unmarshal(b, &data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Couldn't unmarshal into a map")
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Container", b}
|
||||
|
||||
// From a map, extract the ID, Name & EmbeddedType
|
||||
if id, ok := data["id"]; ok {
|
||||
err := json.Unmarshal(id, &c.ID)
|
||||
if err != nil {
|
||||
return gen.err(err, "ID", "id", id, "error while unmarshalling")
|
||||
}
|
||||
}
|
||||
if name, ok := data["name"]; ok {
|
||||
err := json.Unmarshal(name, &c.Name)
|
||||
if err != nil {
|
||||
return gen.err(err, "Name", "name", name, "error while unmarshalling")
|
||||
}
|
||||
}
|
||||
if embeddedType, ok := data["embedded_type"]; ok {
|
||||
err := json.Unmarshal(embeddedType, &c.EmbeddedType)
|
||||
if err != nil {
|
||||
return gen.err(err, "EmbeddedType", "embedded_type", embeddedType, "error while unmarshalling")
|
||||
}
|
||||
}
|
||||
if quality, ok := data["quality"]; ok {
|
||||
err := json.Unmarshal(quality, &c.Quality)
|
||||
if err != nil {
|
||||
return gen.err(err, "Quality", "quality", quality, "error while unmarshalling")
|
||||
}
|
||||
}
|
||||
|
||||
// Now, assign the embedded content to the Container
|
||||
if embedded, ok := data[c.EmbeddedType]; ok {
|
||||
c.embeddedJSON = embedded
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
259
types/container_test.go
Normal file
259
types/container_test.go
Normal file
@ -0,0 +1,259 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var containers map[string]*Container
|
||||
|
||||
// loadContainers loads the containers in their final form for testing
|
||||
func loadContainers() error {
|
||||
// Get the input
|
||||
corpus := testData["container"].correct
|
||||
if len(corpus) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cs := make(map[string]*Container, len(corpus))
|
||||
// For each of them, unmarshal and add to containers
|
||||
for name, datum := range corpus {
|
||||
c := &Container{}
|
||||
|
||||
err := c.UnmarshalJSON(datum)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling: %v", err)
|
||||
}
|
||||
|
||||
cs[name] = c
|
||||
}
|
||||
|
||||
containers = cs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestContainer_Object_NoCompare tests the Container.Object method
|
||||
func TestContainer_Object_NoCompare(t *testing.T) {
|
||||
// Get the input
|
||||
data := containers
|
||||
if len(data) == 0 {
|
||||
t.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Create the run function generator, allowing us to run it in parallel
|
||||
rgen := func(c *Container) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
// Get the object
|
||||
obj, err := c.Object()
|
||||
if err != nil {
|
||||
t.Errorf("Error while calling .Object(): %v", err)
|
||||
}
|
||||
|
||||
// Log it
|
||||
t.Logf("Object: %#v", obj)
|
||||
}
|
||||
}
|
||||
|
||||
// For each of them, let's run a subtest
|
||||
for name, datum := range data {
|
||||
// Create the run function
|
||||
rfunc := rgen(datum)
|
||||
|
||||
// Run !
|
||||
t.Run(name, rfunc)
|
||||
}
|
||||
}
|
||||
|
||||
// TestContainer_Check_NoCompare tests the Container.Check method
|
||||
func TestContainer_Check_NoCompare(t *testing.T) {
|
||||
// Get the input
|
||||
data := containers
|
||||
if len(data) == 0 {
|
||||
t.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Create the run function generator, allowing us to run it in parallel
|
||||
rgen := func(c *Container) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
err := c.Check()
|
||||
if err != nil {
|
||||
t.Errorf("Check gave us invalid results: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each of them, let's run a subtest
|
||||
for name, datum := range data {
|
||||
// Create the run function
|
||||
rfunc := rgen(datum)
|
||||
|
||||
// Run !
|
||||
t.Run(name, rfunc)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkContainer_UnmarshalJSON benchmarks Container.UnmarshalJSON through benchmarks
|
||||
func BenchmarkContainer_UnmarshalJSON(b *testing.B) {
|
||||
// Get the bench data
|
||||
data := testData["container"].bench
|
||||
if len(data) == 0 {
|
||||
b.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Run function generator, allowing parallel run
|
||||
// Returns three functions: one without .Object() being called, one with, one one with only the call to .Object being recorded
|
||||
runGen := func(in []byte) (without func(*testing.B), with func(*testing.B), only func(*testing.B)) {
|
||||
without = func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Unmarshal a Journey
|
||||
c := &Container{}
|
||||
_ = c.UnmarshalJSON(in)
|
||||
}
|
||||
}
|
||||
|
||||
with = func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Unmarshal a Journey
|
||||
c := &Container{}
|
||||
_ = c.UnmarshalJSON(in)
|
||||
|
||||
_, _ = c.Object()
|
||||
}
|
||||
}
|
||||
|
||||
only = func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Unmarshal a Journey
|
||||
b.StopTimer()
|
||||
c := &Container{}
|
||||
_ = c.UnmarshalJSON(in)
|
||||
b.StartTimer()
|
||||
_, _ = c.Object()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Loop over all corpus
|
||||
for name, datum := range data {
|
||||
// Get run function
|
||||
without, with, only := runGen(datum)
|
||||
|
||||
// Run it !
|
||||
b.Run(name+"_without", without)
|
||||
b.Run(name+"_with", with)
|
||||
b.Run(name+"_only", only)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkContainer_Check benchmarks Container.Check through subbenchmarks
|
||||
func BenchmarkContainer_Check(b *testing.B) {
|
||||
// Get the bench data
|
||||
data := testData["container"].bench
|
||||
if len(data) == 0 {
|
||||
b.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Run function generator, allowing parallel run
|
||||
runGen := func(in Container) func(*testing.B) {
|
||||
return func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Call .Check
|
||||
_ = in.Check()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all corpus
|
||||
for name, datum := range data {
|
||||
c := Container{}
|
||||
|
||||
err := json.Unmarshal(datum, &c)
|
||||
if err != nil {
|
||||
b.Errorf("Error while unmarshalling: %v", err)
|
||||
}
|
||||
|
||||
// Get run function
|
||||
runFunc := runGen(c)
|
||||
|
||||
// Run it !
|
||||
b.Run(name, runFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// TestContainer_IsXXX tests (*Container).IsPlace and (*Container).IsPTObject
|
||||
func TestContainer_IsXXX(t *testing.T) {
|
||||
t.Run("IsPlace", func(t *testing.T) {
|
||||
for _, et := range embeddedTypesPlace {
|
||||
c := &Container{EmbeddedType: et}
|
||||
if !c.IsPlace() {
|
||||
t.Errorf("IsPlace for embedded type %s: expected true got false", et)
|
||||
}
|
||||
}
|
||||
for _, et := range embeddedTypesPTObject {
|
||||
if et != EmbeddedStopArea {
|
||||
c := &Container{EmbeddedType: et}
|
||||
if c.IsPlace() {
|
||||
t.Errorf("IsPlace for embedded type %s: expected false got true", et)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IsPTObject", func(t *testing.T) {
|
||||
for _, et := range embeddedTypesPTObject {
|
||||
c := &Container{EmbeddedType: et}
|
||||
if !c.IsPTObject() {
|
||||
t.Errorf("IsPTObject for embedded type %s: expected true got false", et)
|
||||
}
|
||||
}
|
||||
for _, et := range embeddedTypesPlace {
|
||||
if et != EmbeddedStopArea {
|
||||
c := &Container{EmbeddedType: et}
|
||||
if c.IsPTObject() {
|
||||
t.Errorf("IsPTObject for embedded type %s: expected false got true", et)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestContainer_Empty tests that (*Container).Empty reports correctly whether or not the container is truly empty
|
||||
func TestContainer_Empty(t *testing.T) {
|
||||
emptyContainer := Container{}
|
||||
nonEmptyContainers := []Container{
|
||||
{
|
||||
ID: "test",
|
||||
},
|
||||
{
|
||||
Name: "test",
|
||||
},
|
||||
{
|
||||
EmbeddedType: "test",
|
||||
},
|
||||
{
|
||||
Quality: 10,
|
||||
},
|
||||
{
|
||||
embeddedObject: new(Object),
|
||||
},
|
||||
{
|
||||
embeddedJSON: json.RawMessage("that's not very raw"),
|
||||
},
|
||||
}
|
||||
|
||||
// If the empty container isn't reported as such, error
|
||||
if !emptyContainer.Empty() {
|
||||
t.Errorf("Calling (*Container).Empty on an empty container returned with false when we expected true")
|
||||
}
|
||||
|
||||
// Iterate through the test non-empty containers
|
||||
for _, c := range nonEmptyContainers {
|
||||
if c.Empty() {
|
||||
t.Errorf("Calling (*Container).Empty on a non-empty container returned true when we expected false. Container: %#v", c)
|
||||
}
|
||||
}
|
||||
}
|
51
types/coord.go
Normal file
51
types/coord.go
Normal file
@ -0,0 +1,51 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Coordinates code for coordinates used throughout the API.
|
||||
// This is the Go representation of "Coordinates". It implements Place.
|
||||
// See http://doc.navitia.io/#standard-objects.
|
||||
type Coordinates struct {
|
||||
Longitude float64 `json:"lon"`
|
||||
Latitude float64 `json:"lat"`
|
||||
}
|
||||
|
||||
// jsonCoordinates define the JSON implementation of Coordinates types
|
||||
type jsonCoordinates struct {
|
||||
Latitude string `json:"lat"`
|
||||
Longitude string `json:"lon"`
|
||||
}
|
||||
|
||||
// ID formats coordinates for use in queries as an ID.
|
||||
func (c Coordinates) ID() ID {
|
||||
return ID(fmt.Sprintf("%3.3f;%3.3f", c.Longitude, c.Latitude))
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Coordinates
|
||||
func (c *Coordinates) UnmarshalJSON(b []byte) error {
|
||||
var data jsonCoordinates
|
||||
|
||||
err := json.Unmarshal(b, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Coordinates types : %w", err)
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Coordinates", b}
|
||||
|
||||
// Now parse the values
|
||||
c.Longitude, err = strconv.ParseFloat(data.Longitude, 64)
|
||||
if err != nil {
|
||||
return gen.err(err, "Longitude", "lon", data.Longitude, "error in strconv.ParseFloat")
|
||||
}
|
||||
c.Latitude, err = strconv.ParseFloat(data.Latitude, 64)
|
||||
if err != nil {
|
||||
return gen.err(err, "Latitude", "lat", data.Latitude, "error in strconv.ParseFloat")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
18
types/departure.go
Normal file
18
types/departure.go
Normal file
@ -0,0 +1,18 @@
|
||||
package types
|
||||
|
||||
type Departure struct {
|
||||
DisplayInformations Display `json:"display_informations"`
|
||||
StopPoint StopPoint `json:"stop_point"`
|
||||
Route Route `json:"route"`
|
||||
Links []Link `json:"links"`
|
||||
StopDateTime
|
||||
}
|
||||
|
||||
type StopDateTime struct {
|
||||
Links []Link `json:"links"`
|
||||
ArrivalDateTime string `json:"arrival_date_time"`
|
||||
DepartureDateTime string `json:"departure_date_time"`
|
||||
BaseArrivalDateTime string `json:"base_arrival_date_time"`
|
||||
BaseDepartureDateTime string `json:"base_departure_date_time"`
|
||||
DataFreshness string `json:"data_freshness"`
|
||||
}
|
89
types/display.go
Normal file
89
types/display.go
Normal file
@ -0,0 +1,89 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// A Display holds informations useful to display.
|
||||
type Display struct {
|
||||
Headsign string `json:"headsign"` // The headsign associated with the object
|
||||
Network string `json:"network"` // The name of the belonging network
|
||||
Direction string `json:"direction"` // A direction to take
|
||||
CommercialMode ID `json:"commercial_mode"` // The commercial mode in ID Form
|
||||
PhysicalMode ID `json:"physical_mode"` // The physical mode in ID Form
|
||||
Label string `json:"label"` // The label of the object
|
||||
Color color.Color `json:"color"` // Hexadecimal color of the line
|
||||
TextColor color.Color `json:"text_color"` // The text color for this section
|
||||
Code string `json:"code"` // The code of the line
|
||||
Description string `json:"description"` // Description
|
||||
Equipments []Equipment `json:"equipments"` // Equipments on this object
|
||||
Name string `json:"name"` // Name of object
|
||||
TripShortName string `json:"trip_short_name"` // TripShoerName short name of the current trip
|
||||
}
|
||||
|
||||
// jsonDisplay define the JSON implementation of Display types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonDisplay struct {
|
||||
// Pointers to the corresponding real values
|
||||
Headsign *string `json:"headsign"`
|
||||
Network *string `json:"network"`
|
||||
Direction *string `json:"direction"`
|
||||
CommercialMode *ID `json:"commercial_mode"`
|
||||
PhysicalMode *ID `json:"physical_mode"`
|
||||
Label *string `json:"label"`
|
||||
Code *string `json:"code"`
|
||||
Description *string `json:"description"`
|
||||
Equipments *[]Equipment `json:"equipments"`
|
||||
|
||||
// Values to process
|
||||
Color string `json:"color"`
|
||||
TextColor string `json:"text_color"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Display
|
||||
func (d *Display) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
// We define some of the value as pointers to the real values, allowing us to bypass copying in cases where we don't need to process the data
|
||||
data := &jsonDisplay{
|
||||
Headsign: &d.Headsign,
|
||||
Network: &d.Network,
|
||||
Direction: &d.Direction,
|
||||
CommercialMode: &d.CommercialMode,
|
||||
PhysicalMode: &d.PhysicalMode,
|
||||
Label: &d.Label,
|
||||
Code: &d.Code,
|
||||
Description: &d.Description,
|
||||
Equipments: &d.Equipments,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Display: %w", err)
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Display", b}
|
||||
|
||||
// Now process the values
|
||||
// We expect a color string length of 6 because it should be coded in hexadecimal
|
||||
if str := data.Color; len(str) == 6 {
|
||||
clr, err := parseColor(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "Color", "color", str, "error in parseColor")
|
||||
}
|
||||
d.Color = clr
|
||||
}
|
||||
if str := data.TextColor; len(str) == 6 {
|
||||
clr, err := parseColor(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "TextColor", "text_color", str, "error in parseColor")
|
||||
}
|
||||
d.TextColor = clr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
137
types/disruption.go
Normal file
137
types/disruption.go
Normal file
@ -0,0 +1,137 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Effect codes for known journey status information
|
||||
// For example, reduced service, detours or moved stops.
|
||||
//
|
||||
// See https://developers.google.com/transit/gtfs-realtime/reference/Effect for more information
|
||||
type Effect string
|
||||
|
||||
// JourneyStatusXXX are known JourneyStatuse
|
||||
const (
|
||||
// Service suspended.
|
||||
EffectNoService Effect = "NO_SERVICE"
|
||||
|
||||
// Service running at lowered capacity.
|
||||
JourneyStatusReducedService = "REDUCED_SERVICE"
|
||||
|
||||
// Service running but with substantial delays expected.
|
||||
JourneyStatusSignificantDelay = "SIGNIFICANT_DELAY"
|
||||
|
||||
// Service running on alternative routes to avoid problem.
|
||||
JourneyStatusDetour = "DETOUR"
|
||||
|
||||
// Service above normal capacity.
|
||||
JourneyStatusAdditionalService = "ADDITIONAL_SERVICE"
|
||||
|
||||
// Service different from normal capacity.
|
||||
JourneyStatusModifiedService = "MODIFIED_SERVICE"
|
||||
|
||||
// Miscellaneous, undefined Effect.
|
||||
JourneyStatusOtherEffect = "OTHER_EFFECT"
|
||||
|
||||
// Default setting: Undetermined or Effect not known.
|
||||
JourneyStatusUnknownEffect = "UNKNOWN_EFFECT"
|
||||
|
||||
// Stop not at previous location or stop no longer on route.
|
||||
JourneyStatusStopMoved = "STOP_MOVED"
|
||||
)
|
||||
|
||||
type DisruptionStatus string
|
||||
|
||||
const (
|
||||
StatusPast DisruptionStatus = "past"
|
||||
StatusActive = "active"
|
||||
StatusFuture = "future"
|
||||
)
|
||||
|
||||
// A Disruption reports the specifics of a Disruption
|
||||
type Disruption struct {
|
||||
ID ID `json:"id"` // ID of the Disruption
|
||||
|
||||
// State of the disruption.
|
||||
// The state is computed using the application_periods of the disruption and the current time of the query.
|
||||
// It can be either "Past", "Active" or "Future"
|
||||
Status DisruptionStatus `json:"status"`
|
||||
Tags []string `json:"tags"`
|
||||
|
||||
InputDisruptionID ID // For traceability, ID of original input disruption
|
||||
InputImpactID ID // For traceability: Id of original input impact
|
||||
Severity Severity `json:"severity"` // Severity gives some categorization element
|
||||
Periods []Period // Dates where the current disruption is active
|
||||
Messages []Message // Text to provide to the traveller
|
||||
LastUpdated time.Time // Last Update of that disruption
|
||||
Impacted []ImpactedObject `json:"impacted_stops"` // Objects impacted
|
||||
Cause string // The cause of that disruption
|
||||
Category string // The category of the disruption, optional.
|
||||
DisruptionID string `json:"disruption_id"`
|
||||
}
|
||||
|
||||
// jsonDisruption define the JSON implementation of Disruption types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonDisruption struct {
|
||||
// The references
|
||||
ID *ID `json:"id"`
|
||||
Status *DisruptionStatus `json:"status"`
|
||||
Tags *[]string `json:"tags"`
|
||||
InputDisruptionID *ID `json:"disruption_id"`
|
||||
InputImpactID *ID `json:"impact_id"`
|
||||
Severity *Severity `json:"severity"`
|
||||
Periods *[]Period `json:"application_periods"`
|
||||
Messages *[]Message `json:"messages"`
|
||||
Impacted *[]ImpactedObject `json:"impacted_objects"`
|
||||
Cause *string `json:"cause"`
|
||||
Category *string `json:"category"`
|
||||
|
||||
// Those we will process
|
||||
LastUpdated string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type PTObjectDisruptions struct {
|
||||
EmbeddedType string `json:"embedded_type"`
|
||||
ID string `json:"id"`
|
||||
Quality int `json:"quality"`
|
||||
Name string `json:"name"`
|
||||
Trip Trip `json:"trip"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Disruption
|
||||
func (d *Disruption) UnmarshalJSON(b []byte) error {
|
||||
data := &jsonDisruption{
|
||||
ID: &d.ID,
|
||||
Status: &d.Status,
|
||||
Tags: &d.Tags,
|
||||
InputDisruptionID: &d.InputDisruptionID,
|
||||
InputImpactID: &d.InputImpactID,
|
||||
Severity: &d.Severity,
|
||||
Periods: &d.Periods,
|
||||
Messages: &d.Messages,
|
||||
Impacted: &d.Impacted,
|
||||
Cause: &d.Cause,
|
||||
Category: &d.Category,
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Disruption", b}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Disruption: %w", err)
|
||||
}
|
||||
|
||||
// Now we process the Update time
|
||||
d.LastUpdated, err = parseDateTime(data.LastUpdated)
|
||||
if err != nil {
|
||||
return gen.err(err, "LastUpdated", "updated_at", data.LastUpdated, "parseDateTime failed")
|
||||
}
|
||||
|
||||
// Finished !
|
||||
return nil
|
||||
}
|
16
types/disruption_test.go
Normal file
16
types/disruption_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Disruption_Unmarshal tests unmarshalling for Disruption.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Disruption_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["disruption"], reflect.TypeOf(Disruption{}))
|
||||
}
|
48
types/equipment.go
Normal file
48
types/equipment.go
Normal file
@ -0,0 +1,48 @@
|
||||
package types
|
||||
|
||||
// An Equipment codes for specific equipment the public transport object has
|
||||
type Equipment string
|
||||
|
||||
// EquipmentWheelchairAccessibility are known equipments
|
||||
const (
|
||||
EquipmentWheelchairAccessibility Equipment = "has_wheelchair_accessibility"
|
||||
EquipmentBikeAccepted Equipment = "has_bike_accepted"
|
||||
EquipmentAirConditioned Equipment = "has_air_conditioned"
|
||||
EquipmentVisualAnnouncement Equipment = "has_visual_announcement"
|
||||
EquipmentAudibleAnnouncement Equipment = "has_audible_announcement"
|
||||
EquipmentAppropriateEscort Equipment = "has_appropriate_escort"
|
||||
EquipmentAppropriateSignage Equipment = "has_appropriate_signage"
|
||||
EquipmentSchoolVehicle Equipment = "has_school_vehicle"
|
||||
EquipmentWheelchairBoarding Equipment = "has_wheelchair_boarding"
|
||||
EquipmentSheltered Equipment = "has_sheltered"
|
||||
EquipmentElevator Equipment = "has_elevator"
|
||||
EquipmentEscalator Equipment = "has_escalator"
|
||||
EquipmentBikeDepot Equipment = "has_bike_depot"
|
||||
)
|
||||
|
||||
// knownEquipments lists all the known equipments
|
||||
var knownEquipments = [...]Equipment{
|
||||
EquipmentWheelchairAccessibility,
|
||||
EquipmentBikeAccepted,
|
||||
EquipmentAirConditioned,
|
||||
EquipmentVisualAnnouncement,
|
||||
EquipmentAudibleAnnouncement,
|
||||
EquipmentAppropriateEscort,
|
||||
EquipmentAppropriateSignage,
|
||||
EquipmentSchoolVehicle,
|
||||
EquipmentWheelchairBoarding,
|
||||
EquipmentSheltered,
|
||||
EquipmentElevator,
|
||||
EquipmentEscalator,
|
||||
EquipmentBikeDepot,
|
||||
}
|
||||
|
||||
// Known reports whether an equipment is known
|
||||
func (eq Equipment) Known() bool {
|
||||
for _, k := range knownEquipments {
|
||||
if eq == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
6
types/exception.go
Normal file
6
types/exception.go
Normal file
@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type Exception struct {
|
||||
Type string `json:"type"`
|
||||
Datetime string `json:"datetime"`
|
||||
}
|
54
types/fare.go
Normal file
54
types/fare.go
Normal file
@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/text/currency"
|
||||
)
|
||||
|
||||
// Fare is the fare of some thing
|
||||
type Fare struct {
|
||||
Total currency.Amount
|
||||
Found bool
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Fare
|
||||
func (f *Fare) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
// We define some of the value as pointers to the real values, allowing us to bypass copying in cases where we don't need to process the data
|
||||
data := &struct {
|
||||
Found *bool `json:"found"`
|
||||
Cost struct {
|
||||
Value string `json:"value"`
|
||||
Currency string `json:"currency"`
|
||||
} `json:"cost"`
|
||||
}{
|
||||
Found: &f.Found,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
return errors.Wrap(err, "Error while unmarshalling journey")
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Fare", b}
|
||||
|
||||
// Let's convert the cost now
|
||||
// If we have no defined fare, let's skip that part
|
||||
if data.Cost.Value == "" || data.Cost.Currency == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First get the currency unit
|
||||
unit, err := currency.ParseISO(data.Cost.Currency)
|
||||
if err != nil {
|
||||
return gen.err(err, "Total", "cost.currency", data.Cost.Currency, "error while retrieving currency unit via currency.ParseISO")
|
||||
}
|
||||
|
||||
// Now let's create the correct amount
|
||||
f.Total = unit.Amount(data.Cost.Value)
|
||||
|
||||
return nil
|
||||
}
|
5
types/fareZone.go
Normal file
5
types/fareZone.go
Normal file
@ -0,0 +1,5 @@
|
||||
package types
|
||||
|
||||
type FareZone struct {
|
||||
Name string `json:"name"`
|
||||
}
|
43
types/fuzz.sh
Executable file
43
types/fuzz.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
shopt -s extglob
|
||||
declare -A functionNames
|
||||
functionNames=(["FuzzJourney"]="journey" ["FuzzPlaceCountainer"]="place")
|
||||
|
||||
echo "Functions that will be built:"
|
||||
printf "\t- %s\n" "${!functionNames[@]}"
|
||||
|
||||
workdirPath="/tmp/fuzz"
|
||||
echo "Creating global workdir: $workdirPath"
|
||||
mkdir $workdirPath
|
||||
|
||||
for i in "${!functionNames[@]}"
|
||||
do
|
||||
path="$workdirPath/$i"
|
||||
mkdir $path
|
||||
mkdir "$path/corpus"
|
||||
binpath="$path/$i.zip"
|
||||
|
||||
corpuspath="./testdata/${functionNames[$i]}"
|
||||
echo "Copying corpus for $i from $corpuspath"
|
||||
cp $corpuspath/known/* "$path/corpus/"
|
||||
cp $corpuspath/corpus/* "$path/corpus/"
|
||||
|
||||
echo "Building $i"
|
||||
go-fuzz-build -func $i -o $binpath github.com/aabizri/gonavitia/types
|
||||
|
||||
echo "Running $i ($binpath)"
|
||||
go-fuzz -bin $binpath -workdir=$path
|
||||
|
||||
echo "Copying back corpus from $corpuspath"
|
||||
destination="./testdata/${functionNames[$i]}/corpus"
|
||||
rsync --exclude="*.json" $path/corpus/* $destination/
|
||||
|
||||
echo "Copying crashers"
|
||||
destination="./testdata/${functionNames[$i]}/crasher"
|
||||
commit=`git rev-parse HEAD`
|
||||
for j in $path/crashers/*
|
||||
do
|
||||
filename=$commit-${j##*/}
|
||||
cp $j $destination/$filename
|
||||
done
|
||||
done
|
50
types/id.go
Normal file
50
types/id.go
Normal file
@ -0,0 +1,50 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// An ID is used throughout the library, it is something used by the navitia API and useful to communicate with it.
|
||||
type ID string
|
||||
|
||||
// Check for ID validity
|
||||
func (id ID) Check() error {
|
||||
if len(id) == 0 {
|
||||
return errors.Errorf("ID invalid: an empty string \"\" is not a valid ID")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// typeNames stores navitia-side name of types that may appear in IDs
|
||||
var typeNames = map[string]bool{
|
||||
"network": true,
|
||||
"line": true,
|
||||
"route": true,
|
||||
"stop_area": true,
|
||||
"commercial_mode": true,
|
||||
"physical_mode": true,
|
||||
"company": true,
|
||||
"admin": true,
|
||||
"stop_point": true,
|
||||
}
|
||||
|
||||
// Type gets the type of object this ID refers to.
|
||||
//
|
||||
// Possible types: network, line, route, stop_area, commercial_mode, physical_mode, company, admin, stop_point.
|
||||
//
|
||||
// This is just guessing, if no type is found, type returns an empty string.
|
||||
func (id ID) Type() string {
|
||||
splitted := strings.Split(string(id), ":")
|
||||
if len(splitted) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
possible := splitted[0]
|
||||
if typeNames[possible] {
|
||||
return possible
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
11
types/id_test.go
Normal file
11
types/id_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestIDCheck checks if ID.Check returns an error when given an empty ID
|
||||
func TestIDCheck(t *testing.T) {
|
||||
id := ID("")
|
||||
if id.Check() == nil {
|
||||
t.Errorf("Received no error even though we expect one")
|
||||
}
|
||||
}
|
13
types/impactedObject.go
Normal file
13
types/impactedObject.go
Normal file
@ -0,0 +1,13 @@
|
||||
package types
|
||||
|
||||
// An ImpactedObject describes a PTObject impacted by a Disruption with some additional info.
|
||||
type ImpactedObject struct {
|
||||
// The impacted public transport object
|
||||
Object Container `json:"pt_object"`
|
||||
|
||||
// Only for line section impact, the impacted section
|
||||
ImpactedSection ImpactedSection `json:"impacted_section"`
|
||||
|
||||
// Only for Trip delay, the list of delays, stop by stop
|
||||
ImpactedStops []ImpactedStop `json:"impacted_stops"`
|
||||
}
|
9
types/impactedSection.go
Normal file
9
types/impactedSection.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// An ImpactedSection records the impact to a section
|
||||
type ImpactedSection struct {
|
||||
// The start of the disruption, spatially
|
||||
From Container `json:"from"`
|
||||
// Until this point
|
||||
To Container `json:"to"`
|
||||
}
|
42
types/impactedStop.go
Normal file
42
types/impactedStop.go
Normal file
@ -0,0 +1,42 @@
|
||||
package types
|
||||
|
||||
// An ImpactedStop records the impact to a stop
|
||||
type ImpactedStop struct {
|
||||
// The impacted stop point of the trip
|
||||
Point StopPoint `json:"stop_point"`
|
||||
|
||||
// New departure hour (format HHMMSS) of the trip on this stop point
|
||||
NewDeparture string
|
||||
|
||||
// New arrival hour (format HHMMSS) of the trip on this stop point
|
||||
NewArrival string
|
||||
|
||||
// Base departure hour (format HHMMSS) of the trip on this stop point
|
||||
BaseDeparture string
|
||||
|
||||
// Base arrival hour (format HHMMSS) of the trip on this stop point
|
||||
BaseArrival string
|
||||
|
||||
// Cause of the modification
|
||||
Cause string `json:"cause"`
|
||||
|
||||
// Effect on that StopPoint
|
||||
// Can be "added", "deleted", "delayed"
|
||||
Effect string
|
||||
|
||||
AmendedArrivalTime string `json:"amended_arrival_time"`
|
||||
|
||||
StopTimeEffect string `json:"stop_time_effect"`
|
||||
|
||||
DepartureStatus string `json:"departure_status"`
|
||||
|
||||
IsDetour bool `json:"is_detour"`
|
||||
|
||||
AmendedDepartureTime string `json:"amended_departure_time"`
|
||||
|
||||
BaseArrivalTime string `json:"base_arrival_time"`
|
||||
|
||||
BaseDepartureTime string `json:"base_departure_time"`
|
||||
|
||||
ArrivalStatus string `json:"arrival_status"`
|
||||
}
|
10
types/isochrone.go
Normal file
10
types/isochrone.go
Normal file
@ -0,0 +1,10 @@
|
||||
package types
|
||||
|
||||
import "github.com/paulmach/go.geojson"
|
||||
|
||||
// An Isochrone is sent back by the /isochrones service, it gives you a multi-polygon geojson response which represent a same time travel zone.
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/Isochrone_map for what is an isochrone.
|
||||
//
|
||||
// See http://doc.navitia.io/#isochrones-currently-in-beta
|
||||
type Isochrone geojson.Geometry
|
9
types/journeyPattern.go
Normal file
9
types/journeyPattern.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// JourneyPattern A journey pattern is an ordered list of stop points.
|
||||
// Two vehicles that serve exactly the same stop points in
|
||||
// exactly the same order belong to to the same journey pattern.
|
||||
type JourneyPattern struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
195
types/journeys.go
Normal file
195
types/journeys.go
Normal file
@ -0,0 +1,195 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A JourneyQualification qualifies a Journey, see const declaration.
|
||||
type JourneyQualification string
|
||||
|
||||
// JourneyXXX qualify journeys
|
||||
const (
|
||||
JourneyBest JourneyQualification = "best"
|
||||
JourneyRapid JourneyQualification = "rapid"
|
||||
JourneyComfort JourneyQualification = "comfort"
|
||||
JourneyCar JourneyQualification = "car"
|
||||
JourneyLessWalk JourneyQualification = "less_fallback_walk"
|
||||
JourneyLessBike JourneyQualification = "less_fallback_bike"
|
||||
JourneyLessBikeShare JourneyQualification = "less_fallback_bss"
|
||||
JourneyFastest JourneyQualification = "fastest"
|
||||
JourneyNoPTWalk JourneyQualification = "non_pt_walk"
|
||||
JourneyNoPTBike JourneyQualification = "non_pt_bike"
|
||||
JourneyNoPTBikeShare JourneyQualification = "non_pt_bss"
|
||||
)
|
||||
|
||||
// JourneyQualifications is a user-friendly slice of all journey qualification
|
||||
// As it might be used in requests, this is exported
|
||||
var JourneyQualifications = []JourneyQualification{
|
||||
JourneyBest,
|
||||
JourneyRapid,
|
||||
JourneyComfort,
|
||||
JourneyCar,
|
||||
JourneyLessWalk,
|
||||
JourneyLessBike,
|
||||
JourneyLessBikeShare,
|
||||
JourneyFastest,
|
||||
JourneyNoPTWalk,
|
||||
JourneyNoPTBike,
|
||||
JourneyNoPTBikeShare,
|
||||
}
|
||||
|
||||
// A Journey holds information about a possible journey
|
||||
type Journey struct {
|
||||
Duration time.Duration
|
||||
Transfers uint
|
||||
|
||||
Departure time.Time
|
||||
Requested time.Time
|
||||
Arrival time.Time
|
||||
|
||||
CO2Emissions CO2Emissions
|
||||
|
||||
Sections []Section
|
||||
|
||||
From Container
|
||||
To Container
|
||||
|
||||
Type JourneyQualification
|
||||
|
||||
Fare Fare
|
||||
|
||||
// Status from the whole journey taking into acount the most disturbing information retrieved on every object used
|
||||
Status Effect
|
||||
}
|
||||
|
||||
// jsonJourney define the JSON implementation of Journey types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonJourney struct {
|
||||
Duration int64 `json:"duration"`
|
||||
Transfers *uint `json:"nb_transfers"`
|
||||
|
||||
Departure string `json:"departure_date_time"`
|
||||
Requested string `json:"requested_date_time"`
|
||||
Arrival string `json:"arrival_date_time"`
|
||||
|
||||
Sections *[]Section `json:"sections"`
|
||||
|
||||
From *Container `json:"from"`
|
||||
To *Container `json:"to"`
|
||||
|
||||
Type *JourneyQualification `json:"type"`
|
||||
|
||||
Fare *Fare `json:"fare"`
|
||||
|
||||
Status *Effect `json:"status"`
|
||||
}
|
||||
|
||||
// CO2Emissions holds how much CO2 is emitted.
|
||||
type CO2Emissions struct {
|
||||
Unit string
|
||||
Value float64
|
||||
}
|
||||
|
||||
// jsonCO2Emissions define the JSON implementation of CO2Emissions types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonCO2Emissions struct {
|
||||
Unit *string `json:"unit"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// TravelerType is a Traveler's type
|
||||
// Defines speeds & accessibility values for different types of people
|
||||
type TravelerType string
|
||||
|
||||
// The defined types of the api
|
||||
const (
|
||||
// A standard Traveler
|
||||
TravelerStandard TravelerType = "standard"
|
||||
|
||||
// A slow walker
|
||||
TravelerSlowWalker TravelerType = "slow_walker"
|
||||
|
||||
// A fast walker
|
||||
TravelerFastWalker TravelerType = "fast_walker"
|
||||
|
||||
// A Traveler with luggage
|
||||
TravelerWithLuggage TravelerType = "luggage"
|
||||
|
||||
// A Traveler in a wheelchair
|
||||
TravelerInWheelchair TravelerType = "wheelchair"
|
||||
)
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Journey.
|
||||
// Behaviour:
|
||||
// - If "from" is empty, then don't populate the From field.
|
||||
// - Same for "to"
|
||||
func (j *Journey) UnmarshalJSON(b []byte) error {
|
||||
data := &jsonJourney{
|
||||
Transfers: &j.Transfers,
|
||||
Sections: &j.Sections,
|
||||
From: &j.From,
|
||||
To: &j.To,
|
||||
Type: &j.Type,
|
||||
Fare: &j.Fare,
|
||||
Status: &j.Status,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Journey: %w", err)
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Journey", b}
|
||||
|
||||
// As the given duration is in second, let's multiply it by one second to have the correct value
|
||||
j.Duration = time.Duration(data.Duration) * time.Second
|
||||
|
||||
var err error
|
||||
// For departure, requested and arrival, we use parseDateTime
|
||||
j.Departure, err = parseDateTime(data.Departure)
|
||||
if err != nil {
|
||||
return gen.err(err, "Departure", "departure_date_time", data.Departure, "parseDateTime failed")
|
||||
}
|
||||
j.Requested, err = parseDateTime(data.Requested)
|
||||
if err != nil {
|
||||
return gen.err(err, "Requested", "requested_date_time", data.Requested, "parseDateTime failed")
|
||||
}
|
||||
j.Arrival, err = parseDateTime(data.Arrival)
|
||||
if err != nil {
|
||||
return gen.err(err, "Arrival", "arrival_date_time", data.Arrival, "parseDateTime failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for CO2Emissions
|
||||
func (c *CO2Emissions) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
// We define some of the value as pointers to the real values, allowing us to bypass copying in cases where we don't need to process the data
|
||||
data := &jsonCO2Emissions{
|
||||
Unit: &c.Unit,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
return fmt.Errorf("error while unmarshalling CO2Emissions: %w", err)
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"CO2Emissions", b}
|
||||
|
||||
// Now parse the value
|
||||
f, err := strconv.ParseFloat(data.Value, 64)
|
||||
if err != nil {
|
||||
return gen.err(err, "Value", "value", data.Value, "error in strconv.ParseFloat")
|
||||
}
|
||||
c.Value = f
|
||||
|
||||
return nil
|
||||
}
|
15
types/journeys_fuzz.go
Normal file
15
types/journeys_fuzz.go
Normal file
@ -0,0 +1,15 @@
|
||||
// +build gofuzz
|
||||
|
||||
package types
|
||||
|
||||
func FuzzJourney(data []byte) int {
|
||||
j := &Journey{}
|
||||
|
||||
// Let's unmarshal
|
||||
err := j.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
45
types/journeys_test.go
Normal file
45
types/journeys_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Journey_Unmarshal tests unmarshalling for Journey.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Journey_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["journey"], reflect.TypeOf(Journey{}))
|
||||
}
|
||||
|
||||
// BenchmarkJourney_UnmarshalJSON benchmarks Journey unmarshalling via subbenchmarks
|
||||
func BenchmarkJourney_UnmarshalJSON(b *testing.B) {
|
||||
// Get the bench data
|
||||
data := testData["journey"].bench
|
||||
if len(data) == 0 {
|
||||
b.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Run function generator, allowing parallel run
|
||||
runGen := func(in []byte) func(*testing.B) {
|
||||
return func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Unmarshal a Journey
|
||||
j := &Journey{}
|
||||
_ = j.UnmarshalJSON(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all corpus
|
||||
for name, datum := range data {
|
||||
// Get run function
|
||||
runFunc := runGen(datum)
|
||||
|
||||
// Run it !
|
||||
b.Run(name, runFunc)
|
||||
}
|
||||
}
|
110
types/json.go
Normal file
110
types/json.go
Normal file
@ -0,0 +1,110 @@
|
||||
// json.go provides types & functions for json unmarshalling
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// DateTimeFormat is the format used by the Navitia Api for use with time pkg.
|
||||
DateTimeFormat string = "20060102T150405" // YYYYMMDDThhmmss
|
||||
// DateFormat is when there is no time info
|
||||
DateFormat string = "20060102"
|
||||
)
|
||||
|
||||
// parseDateTime parses a time formatted under iso-date-time as indicated in the Navitia api.
|
||||
// This is simply parsing a date formatted under the standard ISO 8601.
|
||||
// If the given string is empty (i.e ""), then the zero value of time.Time will be returned
|
||||
func parseDateTime(datetime string) (time.Time, error) {
|
||||
// If there's no datetime given, just return the zero value
|
||||
if datetime == "" || datetime == "not-a-date-time" {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
|
||||
// If the datetime doesn't countain a "T", then it does not have time info
|
||||
var format string
|
||||
if strings.Contains(datetime, "T") {
|
||||
format = DateTimeFormat
|
||||
} else {
|
||||
format = DateFormat
|
||||
}
|
||||
|
||||
// Parse it
|
||||
res, err := time.Parse(format, datetime)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "parseDateTime: error while parsing datetime")
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// UnmarshalError is returned when unmarshalling fails
|
||||
// It implements both error and github.com/pkg/errors's causer
|
||||
type UnmarshalError struct {
|
||||
// Type on which the unmarshaller where the error occurred works
|
||||
Type string
|
||||
|
||||
// JSON Key where failure occurred
|
||||
Key string
|
||||
|
||||
// Name of the key in package
|
||||
Name string
|
||||
|
||||
// Value associated with the key
|
||||
Value interface{}
|
||||
|
||||
// Message of the error
|
||||
Message string
|
||||
|
||||
// Underlying error
|
||||
Underlying error
|
||||
|
||||
// Full JSON data
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// Cause implements github.com/pkg/error's causer
|
||||
func (err UnmarshalError) Cause() error {
|
||||
return err.Underlying
|
||||
}
|
||||
|
||||
// Error implements error
|
||||
func (err UnmarshalError) Error() string {
|
||||
msg := fmt.Sprintf(
|
||||
"(*%s).UnmarshalJSON: Unmarshalling %s (json: \"%s\") with value \"%v\" failed",
|
||||
err.Type,
|
||||
err.Name,
|
||||
err.Key,
|
||||
err.Value)
|
||||
|
||||
if err.Message != "" {
|
||||
msg += ": " + err.Message
|
||||
}
|
||||
if err.Underlying != nil {
|
||||
msg += " [" + err.Cause().Error() + "]"
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// unmarshalErrorer allows us to make better error messages
|
||||
type unmarshalErrorMaker struct {
|
||||
Type string
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// err creates a new UnmarshalError
|
||||
func (gen unmarshalErrorMaker) err(underlyingErr error, name string, key string, value interface{}, message string) error {
|
||||
return UnmarshalError{
|
||||
Type: gen.Type,
|
||||
Key: key,
|
||||
Name: name,
|
||||
Value: value,
|
||||
Message: message,
|
||||
Underlying: underlyingErr,
|
||||
JSON: gen.JSON,
|
||||
}
|
||||
}
|
128
types/line.go
Normal file
128
types/line.go
Normal file
@ -0,0 +1,128 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// A Line codes for a public transit line.
|
||||
// Warning: a Line isn't a route, it has no direction information, and can have several embranchments.
|
||||
// See http://doc.navitia.io/#public-transport-objects.
|
||||
type Line struct {
|
||||
ID ID `json:"id"` // ID is the navitia identifier of the line, eg: "line:RAT:M6"
|
||||
Name string `json:"name"` // Name of the line eg: "Nation - Charles de Gaule Etoile"
|
||||
Code string `json:"code"` // Code is the codename of the line
|
||||
Color color.Color `json:"color"` // Color of the Line, eg "FFFFFF"
|
||||
|
||||
// OpeningTime is the opening time of the line
|
||||
OpeningTime struct {
|
||||
Hours uint8 `json:"hours"`
|
||||
Minutes uint8 `json:"minutes"`
|
||||
Seconds uint8 `json:"seconds"`
|
||||
} `json:"opening_time"`
|
||||
|
||||
// ClosingTime is the closing time of the line
|
||||
ClosingTime struct {
|
||||
Hours uint8 `json:"hours"`
|
||||
Minutes uint8 `json:"minutes"`
|
||||
Seconds uint8 `json:"seconds"`
|
||||
} `json:"closing_time"`
|
||||
|
||||
Routes []Route `json:"routes"` // Routes contains the routes of the line
|
||||
CommercialMode CommercialMode `json:"commercial_mode"` // CommercialMode of the line
|
||||
PhysicalModes []PhysicalMode `json:"physical_modes"` // PhysicalModes of the line
|
||||
}
|
||||
|
||||
// jsonLine define the JSON implementation of Line types.
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonLine struct {
|
||||
ID *ID `json:"id"` // ID is the navitia identifier of the line, eg: "line:RAT:M6"
|
||||
Name *string `json:"name"` // Name of the line eg: "Nation - Charles de Gaule Etoile"
|
||||
Code *string `json:"code"` // Code is the codename of the line
|
||||
Routes *[]Route `json:"routes"` // Routes contains the routes of the line
|
||||
CommercialMode *CommercialMode `json:"commercial_mode"` // CommercialMode of the line
|
||||
PhysicalModes *[]PhysicalMode `json:"physical_modes"` // PhysicalModes of the line
|
||||
|
||||
// Value to process
|
||||
Color string `json:"color"` // Color of the Line, eg "FFFFFF"
|
||||
OpeningTime string `json:"opening_time"` // OpeningTime is the opening time of the line
|
||||
ClosingTime string `json:"closing_time"` // ClosingTime is the closing time of the line
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Line
|
||||
func (l *Line) UnmarshalJSON(b []byte) error {
|
||||
data := jsonLine{
|
||||
ID: &l.ID,
|
||||
Name: &l.Name,
|
||||
Code: &l.Code,
|
||||
Routes: &l.Routes,
|
||||
CommercialMode: &l.CommercialMode,
|
||||
PhysicalModes: &l.PhysicalModes,
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Line types : %w", err)
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Line", b}
|
||||
|
||||
// Now process the values
|
||||
|
||||
// For Color: we expect a color string length of 6 because it should be coded in hexadecimal
|
||||
if str := data.Color; len(str) == 6 {
|
||||
clr, err := parseColor(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "Color", "color", str, "error in parseColor")
|
||||
}
|
||||
l.Color = clr
|
||||
}
|
||||
|
||||
// For OpeningTime and ClosingTime: we define a function to help us
|
||||
parseTime := func(str string) (h, m, s uint8, err error) {
|
||||
if len(str) != 6 {
|
||||
err = errors.Errorf("time string not to standard: len=%d instead of 6", len(str))
|
||||
return
|
||||
}
|
||||
h64, err := strconv.ParseUint(str[:2], 10, 8)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error while parsing hours")
|
||||
return
|
||||
}
|
||||
m64, err := strconv.ParseUint(str[2:4], 10, 8)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error while parsing minutes")
|
||||
return
|
||||
}
|
||||
s64, err := strconv.ParseUint(str[4:], 10, 8)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error while parsing seconds")
|
||||
return
|
||||
}
|
||||
return uint8(h64), uint8(m64), uint8(s64), nil
|
||||
}
|
||||
// We expect as well a 6-character long value
|
||||
if str := data.OpeningTime; len(str) == 6 {
|
||||
t := &l.OpeningTime
|
||||
var err error
|
||||
t.Hours, t.Minutes, t.Seconds, err = parseTime(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "OpeningTime", "opening_time", str, "error in parseTime")
|
||||
}
|
||||
}
|
||||
if str := data.ClosingTime; len(str) == 6 {
|
||||
t := &l.ClosingTime
|
||||
var err error
|
||||
t.Hours, t.Minutes, t.Seconds, err = parseTime(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "ClosingTime", "closing_time", str, "error in parseTime")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
44
types/line_test.go
Normal file
44
types/line_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Line_Unmarshal tests unmarshalling for Line.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Line_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["line"], reflect.TypeOf(Line{}))
|
||||
}
|
||||
|
||||
// BenchmarkLineUnmarshal benchmarks Line unmarshalling via subbenchmarks
|
||||
func BenchmarkLineUnmarshal(b *testing.B) {
|
||||
// Get the bench data
|
||||
data := testData["line"].bench
|
||||
if len(data) == 0 {
|
||||
b.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Run function generator, allowing parallel run
|
||||
runGen := func(in []byte) func(*testing.B) {
|
||||
return func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
l := &Line{}
|
||||
_ = l.UnmarshalJSON(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all corpus
|
||||
for name, datum := range data {
|
||||
// Get run function
|
||||
runFunc := runGen(datum)
|
||||
|
||||
// Run it !
|
||||
b.Run(name, runFunc)
|
||||
}
|
||||
}
|
11
types/linereport.go
Normal file
11
types/linereport.go
Normal file
@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
type LineReports struct {
|
||||
Disruptions []Disruption `json:"disruptions"`
|
||||
LineReports []LineReport `json:"line_reports"`
|
||||
}
|
||||
|
||||
type LineReport struct {
|
||||
Line Line `json:"line"`
|
||||
PTObjects []PTObject `json:"pt_objects"`
|
||||
}
|
8
types/link.go
Normal file
8
types/link.go
Normal file
@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
type Link struct {
|
||||
Href string `json:"href"`
|
||||
Type string `json:"type"`
|
||||
Rel string `json:"rel"`
|
||||
Templated bool `json:"templated"`
|
||||
}
|
7
types/message.go
Normal file
7
types/message.go
Normal file
@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
// A Message contains the text to be provided to the traveler.
|
||||
type Message struct {
|
||||
Text string `json:"text"` // The message to bring to the traveler
|
||||
Channel *Channel `json:"channel"` // The destination media for this Message.
|
||||
}
|
70
types/misc.go
Normal file
70
types/misc.go
Normal file
@ -0,0 +1,70 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// DataFreshness codes for a specific data freshness requirement: realtime or base_schedule
|
||||
type DataFreshness string
|
||||
|
||||
// parseColor, given a hex code #RRGGBB returns a color.NRGBA
|
||||
func parseColor(str string) (color.NRGBA, error) {
|
||||
var clr color.NRGBA
|
||||
|
||||
if len(str) != 6 {
|
||||
return clr, errors.Errorf("parseColor: can't parse, invalid length (len=%d instead of 6)", len(str))
|
||||
}
|
||||
|
||||
r, err := strconv.ParseUint(str[:2], 16, 8)
|
||||
if err != nil {
|
||||
return clr, errors.Wrapf(err, "parseColor: red component parsing failed (str: %s)", str[:2])
|
||||
}
|
||||
g, err := strconv.ParseUint(str[2:4], 16, 8)
|
||||
if err != nil {
|
||||
return clr, errors.Wrapf(err, "parseColor: green component parsing failed (str: %s)", str[2:4])
|
||||
}
|
||||
b, err := strconv.ParseUint(str[4:], 16, 8)
|
||||
if err != nil {
|
||||
return clr, errors.Wrapf(err, "parseColor: blue component parsing failed (str: %s)", str[4:])
|
||||
}
|
||||
|
||||
return color.NRGBA{
|
||||
R: uint8(r),
|
||||
G: uint8(g),
|
||||
B: uint8(b),
|
||||
}, nil
|
||||
}
|
||||
|
||||
const (
|
||||
// DataFreshnessRealTime means you'll get undisrupted journeys
|
||||
DataFreshnessRealTime DataFreshness = "realtime"
|
||||
// DataFreshnessBaseSchedule means you can get disrupted journeys in the response.
|
||||
DataFreshnessBaseSchedule = "base_schedule"
|
||||
)
|
||||
|
||||
// A PTDateTime (pt stands for “public transport”) is a complex date time object to manage the difference between stop and leaving times at a stop.
|
||||
// It is used by:
|
||||
// - Row in Schedule
|
||||
// - StopSchedule
|
||||
// - StopDatetime
|
||||
type PTDateTime struct {
|
||||
// Date/Time of departure
|
||||
Departure time.Time `json:"departure"`
|
||||
|
||||
// Date/Time of arrival
|
||||
Arrival time.Time `json:"arrival"`
|
||||
}
|
||||
|
||||
// A Code is associated to a dataset
|
||||
//
|
||||
// Every object managed by Navitia comes with its own list of ids.
|
||||
// You will find some source ids, merge ids, etc. in “codes” list in json responses.
|
||||
// Be careful, these codes may not be unique. The navitia id is the only unique id.
|
||||
type Code struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
66
types/mode.go
Normal file
66
types/mode.go
Normal file
@ -0,0 +1,66 @@
|
||||
package types
|
||||
|
||||
// ModeXXX are known non-public transportation mode
|
||||
const (
|
||||
ModeWalking = "walking"
|
||||
ModeBike = "bike"
|
||||
ModeCar = "car"
|
||||
|
||||
// Not used in Section
|
||||
ModeBikeShare = "bss"
|
||||
)
|
||||
|
||||
// A CommercialMode codes for a commercial method of transportation.
|
||||
//
|
||||
// Note that in contrast with physical modes, commercial modes aren't normalised, if you want to query with them, it is best to use a PhysicalMode.
|
||||
//
|
||||
// See http://doc.navitia.io/#public-transport-objects
|
||||
type CommercialMode struct {
|
||||
// A CommercialMode ID is in the form of "commercial_mode:something"
|
||||
ID ID `json:"id"`
|
||||
|
||||
// Name of the commercial mode
|
||||
Name string `json:"name"`
|
||||
|
||||
// Physical modes of this commercial modes
|
||||
// Example: []PhysicalMode{PhysicalMode{ID: "physical_mode:Tramway", Name: "Tramway"}}
|
||||
PhysicalModes []PhysicalMode `json:"physical_modes"`
|
||||
}
|
||||
|
||||
// A PhysicalMode codes for a physical method of transportation
|
||||
// For example, air travel, bus, metro and train.
|
||||
//
|
||||
// As well, note that physical modes are normalised and fastened, see the list in PhysicalModes
|
||||
//
|
||||
// See http://doc.navitia.io/#public-transport-objects
|
||||
type PhysicalMode struct {
|
||||
// Identifier of the physical mode
|
||||
// For example: "physical_mode:Tramway"
|
||||
ID ID `json:"id"`
|
||||
|
||||
// Name of the physical mode
|
||||
// For example: "Tramway"
|
||||
Name string `json:"name"`
|
||||
|
||||
// Commercial modes of this physical mode
|
||||
CommercialModes []CommercialMode `json:"commercial_mode"`
|
||||
}
|
||||
|
||||
// PhysicalModeXXX are the possible physical modes in ID form
|
||||
const (
|
||||
PhysicalModeAir ID = "physical_mode:Air"
|
||||
PhysicalModeBoat ID = "physical_mode:Boat"
|
||||
PhysicalModeBus ID = "physical_mode:Bus"
|
||||
PhysicalModeBusRapidTransit ID = "physical_mode:BusRapidTransit"
|
||||
PhysicalModeCoach ID = "physical_mode:Coach"
|
||||
PhysicalModeFerry ID = "physical_mode:Ferry"
|
||||
PhysicalModeFunicular ID = "physical_mode:Funicular"
|
||||
PhysicalModeLocalTrain ID = "physical_mode:LocalTrain"
|
||||
PhysicalModeLongDistanceTrain ID = "physical_mode:LongDistanceTrain"
|
||||
PhysicalModeMetro ID = "physical_mode:Metro"
|
||||
PhysicalModeRapidTransit ID = "physical_mode:RapidTransit"
|
||||
PhysicalModeShuttle ID = "physical_mode:Shuttle"
|
||||
PhysicalModeTaxi ID = "physical_mode:Taxi"
|
||||
PhysicalModeTrain ID = "physical_mode:Train"
|
||||
PhysicalModeTramway ID = "physical_mode:Tramway"
|
||||
)
|
9
types/network.go
Normal file
9
types/network.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
// Network represents a specific network.
|
||||
// They are fed by the agencies in GTFS format.
|
||||
// See http://doc.navitia.io/#public-transport-objects.
|
||||
type Network struct {
|
||||
ID string `json:"id"` // ID is the identifier of the network
|
||||
Name string `json:"name"` // Name is the name of the network
|
||||
}
|
54
types/pathSegment.go
Normal file
54
types/pathSegment.go
Normal file
@ -0,0 +1,54 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A PathSegment (called Path item in the Navitia API) is a part of a path
|
||||
type PathSegment struct {
|
||||
Length uint `json:"length"` // The Length of the segment
|
||||
Name string `json:"name"` // The Name of the way corresponding to the segment
|
||||
Duration time.Duration `json:"duration"` // The duration in seconds of the segment
|
||||
|
||||
// The angle in degree between the previous segment and this segment
|
||||
// = 0 Means going straight
|
||||
// < 0 Means turning left
|
||||
// > 0 Means turning right
|
||||
Direction int `json:"direction"`
|
||||
}
|
||||
|
||||
// jsonPathSegment define the JSON implementation of PathSegment types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonPathSegment struct {
|
||||
// Pointers to the corresponding real values
|
||||
Length *uint
|
||||
Name *string
|
||||
Direction *int
|
||||
|
||||
// Value to process
|
||||
Duration int64
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a PathSegment
|
||||
func (ps *PathSegment) UnmarshalJSON(b []byte) error {
|
||||
data := &jsonPathSegment{
|
||||
Length: &ps.Length,
|
||||
Name: &ps.Name,
|
||||
Direction: &ps.Direction,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling PathSegment: %w", err)
|
||||
}
|
||||
|
||||
// Now process the value
|
||||
// As the given duration is in second, let's multiply it by one second to have the correct value
|
||||
ps.Duration = time.Duration(data.Duration) * time.Second
|
||||
|
||||
return nil
|
||||
}
|
47
types/period.go
Normal file
47
types/period.go
Normal file
@ -0,0 +1,47 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Period of effect
|
||||
type Period struct {
|
||||
Begin time.Time `json:"begin"`
|
||||
End time.Time `json:"end"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Period
|
||||
func (p *Period) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
// We define some of the value as pointers to the real values, allowing us to bypass copying in cases where we don't need to process the data
|
||||
data := &struct {
|
||||
// Those we will process
|
||||
Begin string `json:"begin"`
|
||||
End string `json:"end"`
|
||||
}{}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Period", b}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error while unmarshalling Period")
|
||||
}
|
||||
|
||||
// Now we process the times.
|
||||
p.Begin, err = parseDateTime(data.Begin)
|
||||
if err != nil {
|
||||
return gen.err(err, "Begin", "begin", data.Begin, "parseDateTime failed")
|
||||
}
|
||||
p.End, err = parseDateTime(data.End)
|
||||
if err != nil {
|
||||
return gen.err(err, "End", "end", data.End, "parseDateTime failed")
|
||||
}
|
||||
|
||||
// Finished !
|
||||
return nil
|
||||
}
|
128
types/place.go
Normal file
128
types/place.go
Normal file
@ -0,0 +1,128 @@
|
||||
package types
|
||||
|
||||
// A Place isn't something directly used by the Navitia.io api.
|
||||
//
|
||||
// However, it allows the library user to use idiomatic go when working with the library.
|
||||
// If you want a countainer, see Container
|
||||
//
|
||||
// Place is held by these types:
|
||||
// - StopArea
|
||||
// - POI
|
||||
// - Address
|
||||
// - StopPoint
|
||||
// - Admin
|
||||
type Place interface{}
|
||||
|
||||
// A StopArea represents a stop area: a nameable zone, where there are some stop points.
|
||||
type StopArea struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Label of the stop area.
|
||||
// The name is directly taken from the data whereas the label is something computed by navitia for better traveler information.
|
||||
// If you don't know what to display, display the label
|
||||
Label string `json:"label"`
|
||||
|
||||
// Coordinates of the stop area
|
||||
Coord Coordinates `json:"coord"`
|
||||
|
||||
// Administrative regions of the stop area in which is placed the stop area
|
||||
Admins []Admin `json:"administrative_regions"`
|
||||
|
||||
// Stop points countained in this stop area
|
||||
StopPoints []StopPoint `json:"stop_points"`
|
||||
|
||||
Timezone string `json:"timezone"`
|
||||
}
|
||||
|
||||
// A POIType codes for the type of the point of interest
|
||||
type POIType struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// A POI is a Point Of Interest. A loosely-defined place.
|
||||
type POI struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// The name is directly taken from the data whereas the label is something computed by navitia for better traveler information.
|
||||
// If you don't know what to display, display the label
|
||||
Label string `json:"label"`
|
||||
|
||||
// The type of the POI
|
||||
Type POIType `json:"poi_type"`
|
||||
}
|
||||
|
||||
// An Address codes for a real-world address: a point located in a street.
|
||||
type Address struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Label of the address
|
||||
// The name is directly taken from the data whereas the label is something computed by navitia for better traveler information.
|
||||
// If you don't know what to display, display the label
|
||||
Label string `json:"label"`
|
||||
|
||||
// Coordinates of the address
|
||||
Coord Coordinates `json:"coord"`
|
||||
|
||||
// House number of the address
|
||||
HouseNumber uint `json:"house_number"`
|
||||
|
||||
// Administrative regions of the stop area in which is placed the stop area
|
||||
Admins []Admin `json:"administrative_regions"`
|
||||
}
|
||||
|
||||
// A StopPoint codes for a stop point in a line: a location where vehicles can pickup or drop off passengers.
|
||||
type StopPoint struct {
|
||||
ID ID `json:"id"`
|
||||
|
||||
// Name of the stop point
|
||||
Name string `json:"name"`
|
||||
|
||||
Label string `json:"label"`
|
||||
|
||||
// Coordinates of the stop point
|
||||
Coord Coordinates `json:"coord"`
|
||||
|
||||
// Administrative regions of the stop point
|
||||
Admins []Admin `json:"administrative_regions"`
|
||||
|
||||
// List of equipments of the stop point
|
||||
Equipments []Equipment `json:"equipment"`
|
||||
|
||||
// Stop Area countaining the stop point
|
||||
StopArea *StopArea `json:"stop_area"`
|
||||
|
||||
CommercialModes []CommercialMode `json:"commercial_modes"`
|
||||
|
||||
Links []Link `json:"links"`
|
||||
|
||||
PhysicalModes []PhysicalMode `json:"physical_modes"`
|
||||
|
||||
FareZone FareZone `json:"fare_zone"`
|
||||
}
|
||||
|
||||
// An Admin represents an administrative region: a region under the control/responsibility of a specific organisation.
|
||||
// It can be a city, a district, a neightborhood, etc.
|
||||
type Admin struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
||||
// Label of the address
|
||||
// The name is directly taken from the data whereas the label is something computed by navitia for better traveler information.
|
||||
// If you don't know what to display, display the label
|
||||
Label string `json:"label"`
|
||||
|
||||
// Coordinates of the administrative region
|
||||
Coord Coordinates `json:"coord"`
|
||||
|
||||
// Level of the administrative region
|
||||
Level int `json:"level"`
|
||||
|
||||
// Zip code of the administrative region
|
||||
ZipCode string `json:"zip_code"`
|
||||
|
||||
Insee string `json:"insee"`
|
||||
}
|
12
types/ptobject.go
Normal file
12
types/ptobject.go
Normal file
@ -0,0 +1,12 @@
|
||||
package types
|
||||
|
||||
// A PTObject is a Public Transport object: StopArea, Trip, Line, Route, Network, etc.
|
||||
type PTObject interface{}
|
||||
|
||||
// A Trip corresponds to a scheduled vehicle circulation (and all its linked real-time and disrupted routes).
|
||||
//
|
||||
// An example : a train, routing a Paris to Lyon itinerary every day at 06h29, is the “Trip” named “6641”.
|
||||
type Trip struct {
|
||||
ID ID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
149
types/region.go
Normal file
149
types/region.go
Normal file
@ -0,0 +1,149 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mb0/wkt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/twpayne/go-geom"
|
||||
)
|
||||
|
||||
// A Region holds information about a geographical region, including its ID, name & shape.
|
||||
type Region struct {
|
||||
ID ID `json:"id"` // Identifier of the region
|
||||
Name string `json:"name"` // Name of the region
|
||||
Status string `json:"status"` // Status of the dataset
|
||||
|
||||
// Shape of the region.
|
||||
// You can use it to check if a particular coordinate is within that MultiPolygon
|
||||
Shape *geom.MultiPolygon `json:"shape"`
|
||||
|
||||
DatasetCreation time.Time `json:"dataset_creation"` // When was the DataSet created ?
|
||||
LastLoaded time.Time `json:"last_loaded"` // When was it last loaded at navitia.io's end ?
|
||||
ProductionStart time.Time `json:"production_start"` // When did production start ?
|
||||
ProductionEnd time.Time `json:"production_end"` // When did or when will it stop ?
|
||||
|
||||
// An error in the dataset.
|
||||
// This comes from the server, not from this package.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// jsonRegion define the JSON implementation of Region types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonRegion struct {
|
||||
ID *ID `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Status *string `json:"status"`
|
||||
|
||||
// This is mind-fuckery of the highest level.
|
||||
// While EVERY other geojson value returned by navitia is in standard format, THIS ONE, for NO GOOD REASON is coded in wkt...
|
||||
// See (http://en.wikipedia.org/wiki/Well-known_text).
|
||||
Shape string `json:"shape"`
|
||||
|
||||
DatasetCreation string `json:"dataset_created_at"`
|
||||
LastLoaded string `json:"last_load_at"`
|
||||
|
||||
ProductionStart string `json:"start_production_date"`
|
||||
ProductionEnd string `json:"end_production_date"`
|
||||
|
||||
Error *string `json:"error"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Region
|
||||
func (r *Region) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
data := &jsonRegion{
|
||||
ID: &r.ID,
|
||||
Name: &r.Name,
|
||||
Status: &r.Status,
|
||||
Error: &r.Error,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Region: %w", err)
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Region", b}
|
||||
|
||||
// Now let's process the values
|
||||
// First the times
|
||||
r.DatasetCreation, err = parseDateTime(data.DatasetCreation)
|
||||
if err != nil {
|
||||
return gen.err(err, "DatasetCreation", "dataset_created_at", data.DatasetCreation, "Error while parsing datetime")
|
||||
}
|
||||
r.LastLoaded, err = parseDateTime(data.LastLoaded)
|
||||
if err != nil {
|
||||
return gen.err(err, "LastLoaded", "last_load_at", data.LastLoaded, "Error while parsing datetime")
|
||||
}
|
||||
r.ProductionStart, err = parseDateTime(data.ProductionStart)
|
||||
if err != nil {
|
||||
return gen.err(err, "ProductionStart", "start_production_date", data.ProductionStart, "Error while parsing datetime")
|
||||
}
|
||||
r.ProductionEnd, err = parseDateTime(data.ProductionEnd)
|
||||
if err != nil {
|
||||
return gen.err(err, "ProductionEnd", "end_production_date", data.ProductionEnd, "Error while parsing datetime")
|
||||
}
|
||||
|
||||
// And now let's have some FUN, deal with the "shape" key.
|
||||
// First, let's check if the string isn't empty, cause that would be so awesome...
|
||||
if data.Shape != "" {
|
||||
// Parse the MKT
|
||||
out, err := wkt.Parse([]byte(data.Shape))
|
||||
if err != nil {
|
||||
return gen.err(err, "Shape", "shape", out, "error in wkt.Parse")
|
||||
}
|
||||
|
||||
// Now, out should be a wkt.MultiPolygon
|
||||
wktmp, ok := out.(*wkt.MultiPolygon)
|
||||
if !ok {
|
||||
return gen.err(nil, "Shape", "shape", out, "expected out to be of type wkt.MultiPolygon, but it isn't !")
|
||||
}
|
||||
|
||||
// Call our funny little function to convert that to a geom format
|
||||
mp, err := convertWktMPtoGeomMP(wktmp)
|
||||
if err != nil {
|
||||
return gen.err(err, "Shape", "shape", wktmp, "error while converting *wkt.MultiPolygon to *geom.MultiPolygon via convertWktMPtoGeomMP")
|
||||
}
|
||||
|
||||
r.Shape = mp
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertWktMPtoGeomMP converts a wkt MultiPolygon to a geom MultiPolygon
|
||||
func convertWktMPtoGeomMP(in *wkt.MultiPolygon) (*geom.MultiPolygon, error) {
|
||||
// Now let's convert it to a geom format
|
||||
// First let's create the geom.MultiPolygon
|
||||
mp := geom.NewMultiPolygon(geom.XY)
|
||||
|
||||
// Then let's iterate through the polygons, and convert each of them from wkt.Coord to geom.Coord
|
||||
multipolygonCoords := make([][][]geom.Coord, len(in.Polygons))
|
||||
for i, k := range in.Polygons {
|
||||
polygonCoords := make([][]geom.Coord, len(k))
|
||||
for j, l := range k {
|
||||
coords := make([]geom.Coord, len(l))
|
||||
for n, m := range l {
|
||||
coord := make(geom.Coord, 2)
|
||||
coord[0] = m.X
|
||||
coord[1] = m.Y
|
||||
coords[n] = coord
|
||||
}
|
||||
polygonCoords[j] = coords
|
||||
}
|
||||
multipolygonCoords[i] = polygonCoords
|
||||
}
|
||||
|
||||
// Now assign it !
|
||||
mp, err := mp.SetCoords(multipolygonCoords)
|
||||
if err != nil {
|
||||
return mp, errors.Wrapf(err, "Error while setting coordinates")
|
||||
}
|
||||
return mp, err
|
||||
}
|
15
types/region_fuzz.go
Normal file
15
types/region_fuzz.go
Normal file
@ -0,0 +1,15 @@
|
||||
// +build gofuzz
|
||||
|
||||
package types
|
||||
|
||||
func FuzzRegion(data []byte) int {
|
||||
r := &Region{}
|
||||
|
||||
// Let's unmarshal
|
||||
err := r.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
71
types/region_test.go
Normal file
71
types/region_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Region_Unmarshal tests unmarshalling for Region.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Region_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["region"], reflect.TypeOf(Region{}))
|
||||
}
|
||||
|
||||
// TestRegionUnmarshal_ShapeInvalidMKT tests known invalid MKT (well-known text) -encoded Region.Shape inputs for (*Region).UnmarshalJSON
|
||||
func TestRegionUnmarshal_ShapeInvalidMKT(t *testing.T) {
|
||||
// Shapes
|
||||
shapes := [...]string{
|
||||
"MULTIPOLYGON(((-11.33535 51.29165,0,0,-11.33535 51.29165))",
|
||||
"MULTIPOLYGON((",
|
||||
"MULTIPOLYGON(((-11.33535 51.29165,0,0,-11.33535 51.29165)",
|
||||
"MULTIPOLYGON(",
|
||||
"POLYGON(",
|
||||
"MULTIPOLYGON(((0",
|
||||
}
|
||||
|
||||
// Run
|
||||
for i, s := range shapes {
|
||||
in := []byte(fmt.Sprintf(`{"shape": "%s"}`, s))
|
||||
r := &Region{}
|
||||
err := r.UnmarshalJSON(in)
|
||||
if err == nil {
|
||||
t.Errorf("No error in run #%d even though we expected one", i)
|
||||
} else if !strings.Contains(err.Error(), "EOF") {
|
||||
t.Errorf("Unexpected error in run #%d with [%s]: %v", i, in, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRegionUnmarshal benchmarks Region unmarshalling via subbenchmarks
|
||||
func BenchmarkRegionUnmarshal(b *testing.B) {
|
||||
// Get the bench data
|
||||
data := testData["region"].bench
|
||||
if len(data) == 0 {
|
||||
b.Skip("No data to test")
|
||||
}
|
||||
|
||||
// Run function generator, allowing parallel run
|
||||
runGen := func(in []byte) func(*testing.B) {
|
||||
return func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r := &Region{}
|
||||
_ = r.UnmarshalJSON(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop over all corpus
|
||||
for name, datum := range data {
|
||||
// Get run function
|
||||
runFunc := runGen(datum)
|
||||
|
||||
// Run it !
|
||||
b.Run(name, runFunc)
|
||||
}
|
||||
}
|
67
types/route.go
Normal file
67
types/route.go
Normal file
@ -0,0 +1,67 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A Route represents a route: a Line can have several routes,
|
||||
// that is several directions with potential junctions and different frequency for each.
|
||||
// See http://doc.navitia.io/#public-transport-objects.
|
||||
type Route struct {
|
||||
ID ID `json:"id"` // Identifier of the route, eg: "route:RAT:M6"
|
||||
Name string `json:"name"` // Name of the route
|
||||
Frequence bool `json:"is_frequence"` // If the route has frequency or not. Can only be “False”, but may be “True” in the future
|
||||
Line Line `json:"line"` // Line is the line it is connected to
|
||||
Direction Container `json:"direction"` // Direction is the direction of the route (Place or POI)
|
||||
PhysicalModes []PhysicalMode `json:"physical_modes"` // PhysicalModes of the line
|
||||
GeoJSON GeoJSON `json:"geo_json"`
|
||||
}
|
||||
|
||||
// jsonRoute define the JSON implementation of Route types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonRoute struct {
|
||||
ID *ID `json:"id"`
|
||||
Name *string `json:"name"`
|
||||
Line *Line `json:"line"`
|
||||
Direction *Container `json:"direction"`
|
||||
|
||||
// Value to process
|
||||
Frequence string `json:"is_frequence"`
|
||||
}
|
||||
|
||||
type GeoJSON struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for Route
|
||||
func (r *Route) UnmarshalJSON(b []byte) error {
|
||||
data := &jsonRoute{
|
||||
ID: &r.ID,
|
||||
Name: &r.Name,
|
||||
Line: &r.Line,
|
||||
Direction: &r.Direction,
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Route", b}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Line")
|
||||
}
|
||||
|
||||
// Now process the value
|
||||
switch {
|
||||
case data.Frequence == "true" || data.Frequence == "True":
|
||||
r.Frequence = true
|
||||
case data.Frequence == "false" || data.Frequence == "False":
|
||||
r.Frequence = false
|
||||
default:
|
||||
return gen.err(nil, "Frequence", "is_frequency", data.Frequence, `String is neither True, true, False or false`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
16
types/route_test.go
Normal file
16
types/route_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Route_Unmarshal tests unmarshalling for Route.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Route_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["route"], reflect.TypeOf(Route{}))
|
||||
}
|
235
types/section.go
Normal file
235
types/section.go
Normal file
@ -0,0 +1,235 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/twpayne/go-geom"
|
||||
"github.com/twpayne/go-geom/encoding/geojson"
|
||||
)
|
||||
|
||||
// A Section holds information about a specific section
|
||||
type Section struct {
|
||||
Type SectionType
|
||||
ID ID
|
||||
Mode string
|
||||
From Container
|
||||
To Container
|
||||
Departure time.Time // Departure time
|
||||
Arrival time.Time // Arrival time
|
||||
Duration time.Duration // Duration of travel
|
||||
Path []PathSegment // The path taken by this section
|
||||
Geo *geom.LineString // The path in geojson format
|
||||
StopTimes []StopTime // List of the stop times of this section
|
||||
Display Display // Information to display
|
||||
Additional []PTMethod // Additional informations, from what I can see this is always a PTMethod
|
||||
}
|
||||
|
||||
// jsonSection define the JSON implementation of Section types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonSection struct {
|
||||
// Pointers to the corresponding real values
|
||||
Type *SectionType `json:"type"`
|
||||
ID *ID `json:"id"`
|
||||
From *Container `json:"from"`
|
||||
To *Container `json:"to"`
|
||||
Mode *string `json:"mode"`
|
||||
StopTimes *[]StopTime `json:"stop_date_times"`
|
||||
Display *Display `json:"display_informations"`
|
||||
Additional *[]PTMethod `json:"additional_informations"`
|
||||
Path *[]PathSegment `json:"path"`
|
||||
|
||||
// Values to process
|
||||
Departure string `json:"departure_date_time"`
|
||||
Arrival string `json:"arrival_date_time"`
|
||||
Duration int64 `json:"duration"`
|
||||
Geo *geojson.Geometry `json:"geojson"`
|
||||
}
|
||||
|
||||
// A SectionType codifies the type of section that can be encountered
|
||||
type SectionType string
|
||||
|
||||
// These are the types of sections that can be returned from the API
|
||||
const (
|
||||
// Public transport section
|
||||
SectionPublicTransport SectionType = "public_transport"
|
||||
|
||||
// Street section
|
||||
SectionStreetNetwork SectionType = "street_network"
|
||||
|
||||
// Waiting section between transport
|
||||
SectionWaiting SectionType = "waiting"
|
||||
|
||||
// This “stay in the vehicle” section occurs when the traveller has to stay in the vehicle when the bus change its routing.
|
||||
SectionStayIn SectionType = "stay_in"
|
||||
|
||||
// Transfer section
|
||||
SectionTransfer SectionType = "transfer"
|
||||
|
||||
// Teleportation section. Used when starting or arriving to a city or a stoparea (“potato shaped” objects) Useful to make navitia idempotent.
|
||||
// Warning: Be careful: no Path nor Geo items in this case
|
||||
SectionCrowFly SectionType = "crow_fly"
|
||||
|
||||
// Vehicle may not drive along: traveler will have to call agency to confirm journey
|
||||
// Also sometimes called ODT
|
||||
SectionOnDemandTransport SectionType = "on_demand_transport"
|
||||
|
||||
// Taking a bike from a bike sharing system (bss)
|
||||
SectionBikeShareRent SectionType = "bss_rent"
|
||||
|
||||
// Putting back a bike from a bike sharing system (bss)
|
||||
SectionBikeSharePutBack SectionType = "bss_put_back"
|
||||
|
||||
// Boarding on plane
|
||||
SectionBoarding SectionType = "boarding"
|
||||
|
||||
// Landing off the plane
|
||||
SectionLanding SectionType = "landing"
|
||||
)
|
||||
|
||||
// SectionTypes is the type of a section
|
||||
var SectionTypes = map[SectionType]string{
|
||||
SectionPublicTransport: "Public transport section",
|
||||
SectionStreetNetwork: "Street section",
|
||||
SectionWaiting: "Waiting section between transport",
|
||||
SectionStayIn: "This “stay in the vehicle” section occurs when the traveller has to stay in the vehicle when the bus change its routing.",
|
||||
SectionTransfer: "Transfer section",
|
||||
SectionCrowFly: "Teleportation section. Used when starting or arriving to a city or a stoparea (“potato shaped” objects) Useful to make navitia idempotent",
|
||||
SectionOnDemandTransport: "Vehicle may not drive along: traveler will have to call agency to confirm journey",
|
||||
SectionBikeShareRent: "Taking a bike from a bike sharing system (bss)",
|
||||
SectionBikeSharePutBack: "Putting back a bike from a bike sharing system (bss)",
|
||||
SectionBoarding: "Boarding on plane",
|
||||
SectionLanding: "Landing off the plane",
|
||||
}
|
||||
|
||||
// A StopTime stores info about a stop in a route: when the vehicle comes in, when it comes out, and what stop it is.
|
||||
type StopTime struct {
|
||||
// The PTDateTime of the stop, this stores the info about the arrival & departure
|
||||
PTDateTime PTDateTime
|
||||
StopPoint StopPoint `json:"stop_point"` // The stop point in question
|
||||
DropOffAllowed bool `json:"drop_off_allowed"`
|
||||
UTCDepartureTime string `json:"utc_departure_time"`
|
||||
Headsign string `json:"headsign"`
|
||||
UTCArrivalTime string `json:"utc_arrival_time"`
|
||||
PickupAllowed bool `json:"pickup_allowed"`
|
||||
DepartureTime string `json:"departure_time"`
|
||||
}
|
||||
|
||||
// A PTMethod is a Public Transportation method: it can be regular, estimated times or ODT (on-demand transport)
|
||||
type PTMethod string
|
||||
|
||||
// PTMethodXXX codes for known PTMethod
|
||||
const (
|
||||
// PTMethodRegular: No on-demand transport. Line does not contain any estimated stop times, nor zonal stop point location. No need to call too.
|
||||
PTMethodRegular PTMethod = "regular"
|
||||
|
||||
// PTMethodDateTimeEstimated: No on-demand transport. However, line has at least one estimated date time.
|
||||
PTMethodDateTimeEstimated PTMethod = "had_date_time_estimated"
|
||||
|
||||
// PTMethodODTStopTime: Line does not contain any estimated stop times, nor zonal stop point location. But you will have to call to take it.
|
||||
PTMethodODTStopTime PTMethod = "odt_with_stop_time"
|
||||
|
||||
// PTMethodODTStopPoint: Line can contain some estimated stop times, but no zonal stop point location. And you will have to call to take it.
|
||||
PTMethodODTStopPoint PTMethod = "odt_with_stop_point"
|
||||
|
||||
// PTMethodODTZone: Line can contain some estimated stop times, and zonal stop point location. And you will have to call to take it. Well, not really a public transport line, more a cab…
|
||||
PTMethodODTZone PTMethod = "odt_with_zone"
|
||||
)
|
||||
|
||||
/*
|
||||
UnmarshalJSON implements json.Unmarshaller for a Section
|
||||
|
||||
Behaviour:
|
||||
- If "from" is empty, then don't populate the From field.
|
||||
- Same for "to"
|
||||
*/
|
||||
func (s *Section) UnmarshalJSON(b []byte) error {
|
||||
data := &jsonSection{
|
||||
Type: &s.Type,
|
||||
ID: &s.ID,
|
||||
From: &s.From,
|
||||
To: &s.To,
|
||||
Mode: &s.Mode,
|
||||
Display: &s.Display,
|
||||
Additional: &s.Additional,
|
||||
StopTimes: &s.StopTimes,
|
||||
Path: &s.Path,
|
||||
}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Section: %w", err)
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"Section", b}
|
||||
|
||||
// For departure and arrival, we use parseDateTime
|
||||
s.Departure, err = parseDateTime(data.Departure)
|
||||
if err != nil {
|
||||
return gen.err(err, "Departure", "departure_date_time", data.Departure, "parseDateTime failed")
|
||||
}
|
||||
s.Arrival, err = parseDateTime(data.Arrival)
|
||||
if err != nil {
|
||||
return gen.err(err, "Arrival", "arrival_date_time", data.Arrival, "parseDateTime failed")
|
||||
}
|
||||
|
||||
// As the given duration is in second, let's multiply it by one second to have the correct value
|
||||
s.Duration = time.Duration(data.Duration) * time.Second
|
||||
|
||||
// Now let's deal with the geom
|
||||
if data.Geo != nil {
|
||||
// Catch an error !
|
||||
if data.Geo.Coordinates == nil {
|
||||
return gen.err(nil, "Geo", "geojson", data.Geo, "Geo.Coordinates is nil, can't continue as that will cause a panic")
|
||||
}
|
||||
|
||||
// Let's decode it
|
||||
geot, err := data.Geo.Decode()
|
||||
if err != nil {
|
||||
return gen.err(err, "Geo", "geojson", data.Geo, "Geo.Decode() failed")
|
||||
}
|
||||
// And let's assert the type
|
||||
geo, ok := geot.(*geom.LineString)
|
||||
if !ok {
|
||||
return gen.err(err, "Geo", "geojson", data.Geo, "Geo type assertion failed!")
|
||||
}
|
||||
// Now let's assign it
|
||||
s.Geo = geo
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a PTDateTime
|
||||
func (ptdt *PTDateTime) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
data := &struct {
|
||||
Departure string `json:"departure_date_time"`
|
||||
Arrival string `json:"arrival_date_time"`
|
||||
}{}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling PTDateTime: %w", err)
|
||||
}
|
||||
|
||||
// Create the error generator
|
||||
gen := unmarshalErrorMaker{"PTDateTime", b}
|
||||
|
||||
// Now we use parseDateTime
|
||||
ptdt.Departure, err = parseDateTime(data.Departure)
|
||||
if err != nil {
|
||||
return gen.err(err, "Departure", "departure_date_time", data.Departure, "parseDateTime failed")
|
||||
}
|
||||
ptdt.Arrival, err = parseDateTime(data.Arrival)
|
||||
if err != nil {
|
||||
return gen.err(err, "Arrival", "arrival_date_time", data.Arrival, "parseDateTime failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
16
types/section_test.go
Normal file
16
types/section_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test_Section_Unmarshal tests unmarshalling for Section.
|
||||
// As the unmarshalling is done in-house, this allows us to check that the custom UnmarshalJSON function correctly
|
||||
//
|
||||
// This launches both a "correct" and "incorrect" subtest, allowing us to test both cases.
|
||||
// If we expect no errors but we get one, the test fails
|
||||
// If we expect an error but we don't get one, the test fails
|
||||
func Test_Section_Unmarshal(t *testing.T) {
|
||||
testUnmarshal(t, testData["section"], reflect.TypeOf(Section{}))
|
||||
}
|
66
types/severity.go
Normal file
66
types/severity.go
Normal file
@ -0,0 +1,66 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
// Severity object can be used to make visual grouping.
|
||||
type Severity struct {
|
||||
// Name of severity
|
||||
Name string `json:"name"`
|
||||
|
||||
// Priority of the severity. Given by the agency. 0 is the strongest priority, a nil Priority means its undefined (duh).
|
||||
Priority *int `json:"priority"`
|
||||
|
||||
// HTML color for classification
|
||||
Color color.Color `json:"color"`
|
||||
|
||||
// Effect: Normalized value of the effect on the public transport object
|
||||
Effect Effect `json:"effect"`
|
||||
}
|
||||
|
||||
// jsonSeverity define the JSON implementation of Severity types
|
||||
// We define some of the value as pointers to the real values,
|
||||
// allowing us to bypass copying in cases where we don't need to process the data.
|
||||
type jsonSeverity struct {
|
||||
// The references
|
||||
Name *string `json:"name"`
|
||||
Priority *int `json:"priority,omitempty"` // As priority can be null, and 0 is the highest priority.
|
||||
Effect *Effect `json:"effect"`
|
||||
|
||||
// Those we will process
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaller for a Severity
|
||||
func (s *Severity) UnmarshalJSON(b []byte) error {
|
||||
// First let's create the analogous structure
|
||||
// We define some of the value as pointers to the real values, allowing us to bypass copying in cases where we don't need to process the data
|
||||
data := &jsonSeverity{
|
||||
Name: &s.Name,
|
||||
Priority: s.Priority,
|
||||
Effect: &s.Effect,
|
||||
}
|
||||
|
||||
// Let's create the error generator
|
||||
gen := unmarshalErrorMaker{"Severity", b}
|
||||
|
||||
// Now unmarshall the raw data into the analogous structure
|
||||
err := json.Unmarshal(b, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while unmarshalling Severity: %w", err)
|
||||
}
|
||||
|
||||
// Process the color
|
||||
if str := data.Color; len(str) == 6 {
|
||||
clr, err := parseColor(str)
|
||||
if err != nil {
|
||||
return gen.err(err, "Color", "color", str, "error in parseColor")
|
||||
}
|
||||
s.Color = clr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
41
types/testdata/container/bench/regular.json
vendored
Normal file
41
types/testdata/container/bench/regular.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"embedded_type": "stop_area",
|
||||
"quality": 70,
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "RATRDBAC"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "RDBAC"
|
||||
}
|
||||
],
|
||||
"name": "Rue du Bac",
|
||||
"links": [],
|
||||
"coord": {
|
||||
"lat": "48.855756",
|
||||
"lon": "2.325569"
|
||||
},
|
||||
"label": "Rue du Bac (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"timezone": "Europe\/Paris",
|
||||
"id": "stop_area:RAT:SA:RDBAC"
|
||||
},
|
||||
"name": "Rue du Bac (Paris)",
|
||||
"id": "stop_area:RAT:SA:RDBAC"
|
||||
}
|
41
types/testdata/container/correct/a0.json
vendored
Normal file
41
types/testdata/container/correct/a0.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"embedded_type": "stop_area",
|
||||
"quality": 70,
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "RATRDBAC"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "RDBAC"
|
||||
}
|
||||
],
|
||||
"name": "Rue du Bac",
|
||||
"links": [],
|
||||
"coord": {
|
||||
"lat": "48.855756",
|
||||
"lon": "2.325569"
|
||||
},
|
||||
"label": "Rue du Bac (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"timezone": "Europe\/Paris",
|
||||
"id": "stop_area:RAT:SA:RDBAC"
|
||||
},
|
||||
"name": "Rue du Bac (Paris)",
|
||||
"id": "stop_area:RAT:SA:RDBAC"
|
||||
}
|
41
types/testdata/container/correct/a1.json
vendored
Normal file
41
types/testdata/container/correct/a1.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"embedded_type": "stop_area",
|
||||
"quality": 60,
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "RATMKFDO"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "MKFDO"
|
||||
}
|
||||
],
|
||||
"name": "Malakoff \u2014 Rue Etienne Dolet",
|
||||
"links": [],
|
||||
"coord": {
|
||||
"lat": "48.814668",
|
||||
"lon": "2.296999"
|
||||
},
|
||||
"label": "Malakoff \u2014 Rue Etienne Dolet (Malakoff)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "92046",
|
||||
"name": "Malakoff",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.817406",
|
||||
"lon": "2.297158"
|
||||
},
|
||||
"label": "Malakoff (92240)",
|
||||
"id": "admin:fr:92046",
|
||||
"zip_code": "92240"
|
||||
}
|
||||
],
|
||||
"timezone": "Europe\/Paris",
|
||||
"id": "stop_area:RAT:SA:MKFDO"
|
||||
},
|
||||
"name": "Malakoff \u2014 Rue Etienne Dolet (Malakoff)",
|
||||
"id": "stop_area:RAT:SA:MKFDO"
|
||||
}
|
51
types/testdata/container/correct/a2.json
vendored
Normal file
51
types/testdata/container/correct/a2.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 80,
|
||||
"id": "poi:n682262148",
|
||||
"name": "Rue Chabanais (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue Chabanais",
|
||||
"coord": {
|
||||
"lat": "48.8669921",
|
||||
"lon": "2.3366321"
|
||||
},
|
||||
"label": "Rue Chabanais (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 1,
|
||||
"id": "2.3366321;48.8669921",
|
||||
"name": "Rue Chabanais",
|
||||
"coord": {
|
||||
"lat": "48.8669921",
|
||||
"lon": "2.3366321"
|
||||
},
|
||||
"label": "1 Rue Chabanais (Paris)"
|
||||
},
|
||||
"id": "poi:n682262148",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"name": "Rue Chabanais",
|
||||
"source": "cadastre-dgi-fr source : Direction G\u00e9n\u00e9rale des Imp\u00f4ts - Cadastre. Mise \u00e0 jour : 2010",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "02007",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a3.json
vendored
Normal file
51
types/testdata/container/correct/a3.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 80,
|
||||
"id": "poi:n639606894",
|
||||
"name": "Rue Moncey (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue Moncey",
|
||||
"coord": {
|
||||
"lat": "48.8801859",
|
||||
"lon": "2.3312932"
|
||||
},
|
||||
"label": "Rue Moncey (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 2,
|
||||
"id": "2.3312932;48.8801859",
|
||||
"name": "Rue Moncey",
|
||||
"coord": {
|
||||
"lat": "48.8801859",
|
||||
"lon": "2.3312932"
|
||||
},
|
||||
"label": "2 Rue Moncey (Paris)"
|
||||
},
|
||||
"id": "poi:n639606894",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "N\/A",
|
||||
"name": "Rue Moncey",
|
||||
"wheelchair": "no",
|
||||
"operator": "JCDecaux",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a4.json
vendored
Normal file
51
types/testdata/container/correct/a4.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 80,
|
||||
"id": "poi:n597860967",
|
||||
"name": "Rue montgallet (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue montgallet",
|
||||
"coord": {
|
||||
"lat": "48.844328",
|
||||
"lon": "2.3896931"
|
||||
},
|
||||
"label": "Rue montgallet (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 39,
|
||||
"id": "2.3896931;48.844328",
|
||||
"name": "Rue Montgallet",
|
||||
"coord": {
|
||||
"lat": "48.844328",
|
||||
"lon": "2.3896931"
|
||||
},
|
||||
"label": "39 Rue Montgallet (Paris)"
|
||||
},
|
||||
"id": "poi:n597860967",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "16",
|
||||
"name": "Rue montgallet",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "12013",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a5.json
vendored
Normal file
51
types/testdata/container/correct/a5.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n439912307",
|
||||
"name": "Hittorf - Rue Hittorf - 75010 Paris (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Hittorf - Rue Hittorf - 75010 Paris",
|
||||
"coord": {
|
||||
"lat": "48.8720809",
|
||||
"lon": "2.3576446"
|
||||
},
|
||||
"label": "Hittorf - Rue Hittorf - 75010 Paris (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 14,
|
||||
"id": "2.3576446;48.8720809",
|
||||
"name": "Rue Hittorf",
|
||||
"coord": {
|
||||
"lat": "48.8720809",
|
||||
"lon": "2.3576446"
|
||||
},
|
||||
"label": "14 Rue Hittorf (Paris)"
|
||||
},
|
||||
"id": "poi:n439912307",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "17",
|
||||
"name": "Hittorf - Rue Hittorf - 75010 Paris",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "10009",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
52
types/testdata/container/correct/a6.json
vendored
Normal file
52
types/testdata/container/correct/a6.json
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n272853107",
|
||||
"name": "Rue de Siam (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue de Siam",
|
||||
"coord": {
|
||||
"lat": "48.861679",
|
||||
"lon": "2.2753896"
|
||||
},
|
||||
"label": "Rue de Siam (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 1,
|
||||
"id": "2.2753896;48.861679",
|
||||
"name": "Rue de Siam",
|
||||
"coord": {
|
||||
"lat": "48.861679",
|
||||
"lon": "2.2753896"
|
||||
},
|
||||
"label": "1 Rue de Siam (Paris)"
|
||||
},
|
||||
"id": "poi:n272853107",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "16",
|
||||
"name": "Rue de Siam",
|
||||
"source": "survey",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "16017",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a7.json
vendored
Normal file
51
types/testdata/container/correct/a7.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n340402115",
|
||||
"name": "Rue des Boulets (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue des Boulets",
|
||||
"coord": {
|
||||
"lat": "48.8521875",
|
||||
"lon": "2.3889688"
|
||||
},
|
||||
"label": "Rue des Boulets (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 45,
|
||||
"id": "2.3889688;48.8521875",
|
||||
"name": "Rue des Boulets",
|
||||
"coord": {
|
||||
"lat": "48.8521875",
|
||||
"lon": "2.3889688"
|
||||
},
|
||||
"label": "45 Rue des Boulets (Paris)"
|
||||
},
|
||||
"id": "poi:n340402115",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "23",
|
||||
"name": "Rue des Boulets",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "11009",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a8.json
vendored
Normal file
51
types/testdata/container/correct/a8.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n272852792",
|
||||
"name": "Rue Fran\u00e7ois Ponsard (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Rue Fran\u00e7ois Ponsard",
|
||||
"coord": {
|
||||
"lat": "48.8583046",
|
||||
"lon": "2.2742742"
|
||||
},
|
||||
"label": "Rue Fran\u00e7ois Ponsard (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 4,
|
||||
"id": "2.2742742;48.8583046",
|
||||
"name": "Chauss\u00e9e de la Muette",
|
||||
"coord": {
|
||||
"lat": "48.8583046",
|
||||
"lon": "2.2742742"
|
||||
},
|
||||
"label": "4 Chauss\u00e9e de la Muette (Paris)"
|
||||
},
|
||||
"id": "poi:n272852792",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "23",
|
||||
"name": "Rue Fran\u00e7ois Ponsard",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "16021",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/a9.json
vendored
Normal file
51
types/testdata/container/correct/a9.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 60,
|
||||
"id": "poi:n439919694",
|
||||
"name": "Beaubourg - 46 Rue Beaubourg - 75003 Paris (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Beaubourg - 46 Rue Beaubourg - 75003 Paris",
|
||||
"coord": {
|
||||
"lat": "48.8610006",
|
||||
"lon": "2.353484"
|
||||
},
|
||||
"label": "Beaubourg - 46 Rue Beaubourg - 75003 Paris (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 26,
|
||||
"id": "2.353484;48.8610006",
|
||||
"name": "Rue Geoffroy l'Angevin",
|
||||
"coord": {
|
||||
"lat": "48.8610006",
|
||||
"lon": "2.353484"
|
||||
},
|
||||
"label": "26 Rue Geoffroy l'Angevin (Paris)"
|
||||
},
|
||||
"id": "poi:n439919694",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "18",
|
||||
"name": "Beaubourg - 46 Rue Beaubourg - 75003 Paris",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "3010",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b0.json
vendored
Normal file
51
types/testdata/container/correct/b0.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 80,
|
||||
"id": "poi:n2025403733",
|
||||
"name": "avenue Marceau (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "avenue Marceau",
|
||||
"coord": {
|
||||
"lat": "48.8652776",
|
||||
"lon": "2.3001217"
|
||||
},
|
||||
"label": "avenue Marceau (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 4,
|
||||
"id": "2.3001217;48.8652776",
|
||||
"name": "Avenue Marceau",
|
||||
"coord": {
|
||||
"lat": "48.8652776",
|
||||
"lon": "2.3001217"
|
||||
},
|
||||
"label": "4 Avenue Marceau (Paris)"
|
||||
},
|
||||
"id": "poi:n2025403733",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "30",
|
||||
"name": "avenue Marceau",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "8046",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b1.json
vendored
Normal file
51
types/testdata/container/correct/b1.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n4037878821",
|
||||
"name": "13, Avenue Foch (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Parking v\u00e9lo",
|
||||
"id": "poi_type:amenity:bicycle_parking"
|
||||
},
|
||||
"name": "13, Avenue Foch",
|
||||
"coord": {
|
||||
"lat": "48.845978",
|
||||
"lon": "2.440875"
|
||||
},
|
||||
"label": "13, Avenue Foch (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 11,
|
||||
"id": "2.440875;48.845978",
|
||||
"name": "Avenue Foch",
|
||||
"coord": {
|
||||
"lat": "48.845978",
|
||||
"lon": "2.440875"
|
||||
},
|
||||
"label": "11 Avenue Foch"
|
||||
},
|
||||
"id": "poi:n4037878821",
|
||||
"properties": {
|
||||
"amenity": "bicycle_parking",
|
||||
"name": "13, Avenue Foch",
|
||||
"access": "permissive",
|
||||
"operator": "10",
|
||||
"covered": "no",
|
||||
"bicycle_parking": "rack"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b2.json
vendored
Normal file
51
types/testdata/container/correct/b2.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n340398579",
|
||||
"name": "Avenue de Gravelle (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue de Gravelle",
|
||||
"coord": {
|
||||
"lat": "48.8243871",
|
||||
"lon": "2.4184684"
|
||||
},
|
||||
"label": "Avenue de Gravelle (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 0,
|
||||
"id": "2.4184684;48.8243871",
|
||||
"name": "Rue du Bac",
|
||||
"coord": {
|
||||
"lat": "48.8243871",
|
||||
"lon": "2.4184684"
|
||||
},
|
||||
"label": "Rue du Bac"
|
||||
},
|
||||
"id": "poi:n340398579",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "50",
|
||||
"name": "Avenue de Gravelle",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "12126",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
52
types/testdata/container/correct/b3.json
vendored
Normal file
52
types/testdata/container/correct/b3.json
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n1490011488",
|
||||
"name": "Avenue des Gobelins (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue des Gobelins",
|
||||
"coord": {
|
||||
"lat": "48.8371959",
|
||||
"lon": "2.3514837"
|
||||
},
|
||||
"label": "Avenue des Gobelins (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 22,
|
||||
"id": "2.3514837;48.8371959",
|
||||
"name": "Avenue des Gobelins",
|
||||
"coord": {
|
||||
"lat": "48.8371959",
|
||||
"lon": "2.3514837"
|
||||
},
|
||||
"label": "22 Avenue des Gobelins (Paris)"
|
||||
},
|
||||
"id": "poi:n1490011488",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "40",
|
||||
"name": "Avenue des Gobelins",
|
||||
"wheelchair": "no",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "0527",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b4.json
vendored
Normal file
51
types/testdata/container/correct/b4.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n489537569",
|
||||
"name": "Avenue des Portugais (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue des Portugais",
|
||||
"coord": {
|
||||
"lat": "48.8711878",
|
||||
"lon": "2.2937312"
|
||||
},
|
||||
"label": "Avenue des Portugais (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 0,
|
||||
"id": "2.2937312;48.8711878",
|
||||
"name": "Avenue des Portugais",
|
||||
"coord": {
|
||||
"lat": "48.8711878",
|
||||
"lon": "2.2937312"
|
||||
},
|
||||
"label": "Avenue des Portugais (Paris)"
|
||||
},
|
||||
"id": "poi:n489537569",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "26",
|
||||
"name": "Avenue des Portugais",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "16001",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
50
types/testdata/container/correct/b5.json
vendored
Normal file
50
types/testdata/container/correct/b5.json
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n1379439290",
|
||||
"name": "Avenue des Ternes (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue des Ternes",
|
||||
"coord": {
|
||||
"lat": "48.8794462",
|
||||
"lon": "2.2915207"
|
||||
},
|
||||
"label": "Avenue des Ternes (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 2,
|
||||
"id": "2.2915207;48.8794462",
|
||||
"name": "Place Tristan Bernard",
|
||||
"coord": {
|
||||
"lat": "48.8794462",
|
||||
"lon": "2.2915207"
|
||||
},
|
||||
"label": "2 Place Tristan Bernard (Paris)"
|
||||
},
|
||||
"id": "poi:n1379439290",
|
||||
"properties": {
|
||||
"operator": "JCDecaux",
|
||||
"amenity": "bicycle_rental",
|
||||
"ref": "17036",
|
||||
"name": "Avenue des Ternes",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b6.json
vendored
Normal file
51
types/testdata/container/correct/b6.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n272853313",
|
||||
"name": "Avenue Henri Martin (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue Henri Martin",
|
||||
"coord": {
|
||||
"lat": "48.864084",
|
||||
"lon": "2.2768625"
|
||||
},
|
||||
"label": "Avenue Henri Martin (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 71,
|
||||
"id": "2.2768625;48.864084",
|
||||
"name": "Avenue Henri Martin",
|
||||
"coord": {
|
||||
"lat": "48.864084",
|
||||
"lon": "2.2768625"
|
||||
},
|
||||
"label": "71 Avenue Henri Martin (Paris)"
|
||||
},
|
||||
"id": "poi:n272853313",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "34",
|
||||
"name": "Avenue Henri Martin",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "16013",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b7.json
vendored
Normal file
51
types/testdata/container/correct/b7.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n1406238646",
|
||||
"name": "Avenue Rene Coty (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Avenue Rene Coty",
|
||||
"coord": {
|
||||
"lat": "48.8248017",
|
||||
"lon": "2.3362648"
|
||||
},
|
||||
"label": "Avenue Rene Coty (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 48,
|
||||
"id": "2.3362648;48.8248017",
|
||||
"name": "Avenue Reille",
|
||||
"coord": {
|
||||
"lat": "48.8248017",
|
||||
"lon": "2.3362648"
|
||||
},
|
||||
"label": "48 Avenue Reille (Paris)"
|
||||
},
|
||||
"id": "poi:n1406238646",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"name": "Avenue Rene Coty",
|
||||
"wheelchair": "no",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "14016",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/b8.json
vendored
Normal file
51
types/testdata/container/correct/b8.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 60,
|
||||
"id": "poi:n2304319864",
|
||||
"name": "10 Avenue des Minimes (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Parking v\u00e9lo",
|
||||
"id": "poi_type:amenity:bicycle_parking"
|
||||
},
|
||||
"name": "10 Avenue des Minimes",
|
||||
"coord": {
|
||||
"lat": "48.8411291",
|
||||
"lon": "2.4320878"
|
||||
},
|
||||
"label": "10 Avenue des Minimes (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 0,
|
||||
"id": "2.4320878;48.8411291",
|
||||
"name": "Rue Louis Besquel",
|
||||
"coord": {
|
||||
"lat": "48.8411291",
|
||||
"lon": "2.4320878"
|
||||
},
|
||||
"label": "Rue Louis Besquel"
|
||||
},
|
||||
"id": "poi:n2304319864",
|
||||
"properties": {
|
||||
"amenity": "bicycle_parking",
|
||||
"capacity": "20",
|
||||
"name": "10 Avenue des Minimes",
|
||||
"supervised": "no",
|
||||
"covered": "no",
|
||||
"bicycle_parking": "stands"
|
||||
}
|
||||
}
|
||||
}
|
48
types/testdata/container/correct/b9.json
vendored
Normal file
48
types/testdata/container/correct/b9.json
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 60,
|
||||
"id": "poi:n4689211908",
|
||||
"name": "32 Avenue Anatole France (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Parking v\u00e9lo",
|
||||
"id": "poi_type:amenity:bicycle_parking"
|
||||
},
|
||||
"name": "32 Avenue Anatole France",
|
||||
"coord": {
|
||||
"lat": "48.8420506",
|
||||
"lon": "2.4316558"
|
||||
},
|
||||
"label": "32 Avenue Anatole France (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 47,
|
||||
"id": "2.4316558;48.8420506",
|
||||
"name": "Rue des Vignerons",
|
||||
"coord": {
|
||||
"lat": "48.8420506",
|
||||
"lon": "2.4316558"
|
||||
},
|
||||
"label": "47 Rue des Vignerons"
|
||||
},
|
||||
"id": "poi:n4689211908",
|
||||
"properties": {
|
||||
"source": "Mairie de Vincennes",
|
||||
"amenity": "bicycle_parking",
|
||||
"name": "32 Avenue Anatole France"
|
||||
}
|
||||
}
|
||||
}
|
18
types/testdata/container/correct/c0.json
vendored
Normal file
18
types/testdata/container/correct/c0.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"embedded_type": "administrative_region",
|
||||
"quality": 70,
|
||||
"administrative_region": {
|
||||
"insee": "7511559",
|
||||
"name": "Quartier de Grenelle",
|
||||
"level": 10,
|
||||
"coord": {
|
||||
"lat": "48.850168",
|
||||
"lon": "2.29184"
|
||||
},
|
||||
"label": "Quartier de Grenelle (75015)",
|
||||
"id": "admin:fr:7511559",
|
||||
"zip_code": "75015"
|
||||
},
|
||||
"id": "admin:fr:7511559",
|
||||
"name": "Quartier de Grenelle (75015)"
|
||||
}
|
41
types/testdata/container/correct/c1.json
vendored
Normal file
41
types/testdata/container/correct/c1.json
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"embedded_type": "stop_area",
|
||||
"quality": 50,
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "RATLMPGR"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "LMPGR"
|
||||
}
|
||||
],
|
||||
"name": "La Motte-Picquet \u2014 Grenelle",
|
||||
"links": [],
|
||||
"coord": {
|
||||
"lat": "48.84916",
|
||||
"lon": "2.297949"
|
||||
},
|
||||
"label": "La Motte-Picquet \u2014 Grenelle (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"timezone": "Europe\/Paris",
|
||||
"id": "stop_area:RAT:SA:LMPGR"
|
||||
},
|
||||
"name": "La Motte-Picquet \u2014 Grenelle (Paris)",
|
||||
"id": "stop_area:RAT:SA:LMPGR"
|
||||
}
|
52
types/testdata/container/correct/c2.json
vendored
Normal file
52
types/testdata/container/correct/c2.json
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:n305103675",
|
||||
"name": "Grenelle Violet (prop3) (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Grenelle Violet (prop3)",
|
||||
"coord": {
|
||||
"lat": "48.8499385",
|
||||
"lon": "2.2945842"
|
||||
},
|
||||
"label": "Grenelle Violet (prop3) (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 88,
|
||||
"id": "2.2945842;48.8499385",
|
||||
"name": "Boulevard de Grenelle",
|
||||
"coord": {
|
||||
"lat": "48.8499385",
|
||||
"lon": "2.2945842"
|
||||
},
|
||||
"label": "88 Boulevard de Grenelle (Paris)"
|
||||
},
|
||||
"id": "poi:n305103675",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "53",
|
||||
"name": "Grenelle Violet (prop3)",
|
||||
"wheelchair": "no",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "15106",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
50
types/testdata/container/correct/c3.json
vendored
Normal file
50
types/testdata/container/correct/c3.json
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 70,
|
||||
"id": "poi:w99577109",
|
||||
"name": "Square des Gr\u00e8s (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Parc, espace vert",
|
||||
"id": "poi_type:leisure:park"
|
||||
},
|
||||
"name": "Square des Gr\u00e8s",
|
||||
"coord": {
|
||||
"lat": "48.85956934",
|
||||
"lon": "2.406174677"
|
||||
},
|
||||
"label": "Square des Gr\u00e8s (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 14,
|
||||
"id": "2.406174677;48.85956934",
|
||||
"name": "Rue Riblette",
|
||||
"coord": {
|
||||
"lat": "48.85956934",
|
||||
"lon": "2.406174677"
|
||||
},
|
||||
"label": "14 Rue Riblette (Paris)"
|
||||
},
|
||||
"id": "poi:w99577109",
|
||||
"properties": {
|
||||
"wikipedia": "fr:Square des Gr\u00e8s",
|
||||
"wikidata": "Q3494997",
|
||||
"name": "Square des Gr\u00e8s",
|
||||
"leisure": "park",
|
||||
"start_date": "1983"
|
||||
}
|
||||
}
|
||||
}
|
52
types/testdata/container/correct/c4.json
vendored
Normal file
52
types/testdata/container/correct/c4.json
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 50,
|
||||
"id": "poi:n672727214",
|
||||
"name": "Commissariat de police Javel-Grenelle (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Police, gendarmerie",
|
||||
"id": "poi_type:amenity:police"
|
||||
},
|
||||
"name": "Commissariat de police Javel-Grenelle",
|
||||
"coord": {
|
||||
"lat": "48.8433337",
|
||||
"lon": "2.2768334"
|
||||
},
|
||||
"label": "Commissariat de police Javel-Grenelle (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 34,
|
||||
"id": "2.2768334;48.8433337",
|
||||
"name": "Rue Balard",
|
||||
"coord": {
|
||||
"lat": "48.8433337",
|
||||
"lon": "2.2768334"
|
||||
},
|
||||
"label": "34 Commissariat de police Javel-Grenelle (Paris)"
|
||||
},
|
||||
"id": "poi:n672727214",
|
||||
"properties": {
|
||||
"addr:housenumber": "34",
|
||||
"amenity": "police",
|
||||
"addr:city": "Paris",
|
||||
"addr:postcode": "75015",
|
||||
"name": "Commissariat de police Javel-Grenelle",
|
||||
"addr:country": "FR",
|
||||
"addr:street": "Rue Balard"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/c5.json
vendored
Normal file
51
types/testdata/container/correct/c5.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 50,
|
||||
"id": "poi:n60751375",
|
||||
"name": "Sebastopol Grenata - 12 Rue Grenata - 75002 Paris (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Sebastopol Grenata - 12 Rue Grenata - 75002 Paris",
|
||||
"coord": {
|
||||
"lat": "48.8652569",
|
||||
"lon": "2.3516674"
|
||||
},
|
||||
"label": "Sebastopol Grenata - 12 Rue Grenata - 75002 Paris (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 12,
|
||||
"id": "2.3516674;48.8652569",
|
||||
"name": "Rue Greneta",
|
||||
"coord": {
|
||||
"lat": "48.8652569",
|
||||
"lon": "2.3516674"
|
||||
},
|
||||
"label": "12 Rue Greneta (Paris)"
|
||||
},
|
||||
"id": "poi:n60751375",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "34",
|
||||
"name": "Sebastopol Grenata - 12 Rue Grenata - 75002 Paris",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "2001",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/c6.json
vendored
Normal file
51
types/testdata/container/correct/c6.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 40,
|
||||
"id": "poi:n439924298",
|
||||
"name": "Grenier Saint-Lazare - 34 Rue Grenier Saint-Lazare - 75003 Paris (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Grenier Saint-Lazare - 34 Rue Grenier Saint-Lazare - 75003 Paris",
|
||||
"coord": {
|
||||
"lat": "48.8631013",
|
||||
"lon": "2.3527609"
|
||||
},
|
||||
"label": "Grenier Saint-Lazare - 34 Rue Grenier Saint-Lazare - 75003 Paris (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 34,
|
||||
"id": "2.3527609;48.8631013",
|
||||
"name": "Rue du Grenier Saint-Lazare",
|
||||
"coord": {
|
||||
"lat": "48.8631013",
|
||||
"lon": "2.3527609"
|
||||
},
|
||||
"label": "34 Rue du Grenier Saint-Lazare (Paris)"
|
||||
},
|
||||
"id": "poi:n439924298",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "31",
|
||||
"name": "Grenier Saint-Lazare - 34 Rue Grenier Saint-Lazare - 75003 Paris",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "3014",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
51
types/testdata/container/correct/c7.json
vendored
Normal file
51
types/testdata/container/correct/c7.json
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"embedded_type": "poi",
|
||||
"quality": 40,
|
||||
"id": "poi:n310350237",
|
||||
"name": "Square Bela Bartok - Quai Grenelle - 75015 Paris (Paris)",
|
||||
"poi": {
|
||||
"poi_type": {
|
||||
"name": "Station VLS",
|
||||
"id": "poi_type:amenity:bicycle_rental"
|
||||
},
|
||||
"name": "Square Bela Bartok - Quai Grenelle - 75015 Paris",
|
||||
"coord": {
|
||||
"lat": "48.8511757",
|
||||
"lon": "2.2845792"
|
||||
},
|
||||
"label": "Square Bela Bartok - Quai Grenelle - 75015 Paris (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"address": {
|
||||
"house_number": 2,
|
||||
"id": "2.2845792;48.8511757",
|
||||
"name": "Place de Brazzaville",
|
||||
"coord": {
|
||||
"lat": "48.8511757",
|
||||
"lon": "2.2845792"
|
||||
},
|
||||
"label": "2 Place de Brazzaville (Paris)"
|
||||
},
|
||||
"id": "poi:n310350237",
|
||||
"properties": {
|
||||
"amenity": "bicycle_rental",
|
||||
"capacity": "23",
|
||||
"name": "Square Bela Bartok - Quai Grenelle - 75015 Paris",
|
||||
"operator": "JCDecaux",
|
||||
"ref": "15102",
|
||||
"network": "V\u00e9lib'"
|
||||
}
|
||||
}
|
||||
}
|
30
types/testdata/container/correct/c8.json
vendored
Normal file
30
types/testdata/container/correct/c8.json
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"embedded_type": "address",
|
||||
"quality": 80,
|
||||
"id": "2.397900446545659;48.86088080214612",
|
||||
"name": "Avenue Greffulhe (Paris)",
|
||||
"address": {
|
||||
"name": "Avenue Greffulhe",
|
||||
"house_number": 0,
|
||||
"coord": {
|
||||
"lat": "48.8608808021",
|
||||
"lon": "2.39790044655"
|
||||
},
|
||||
"label": "Avenue Greffulhe (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"id": "2.397900446545659;48.86088080214612"
|
||||
}
|
||||
}
|
30
types/testdata/container/correct/c9.json
vendored
Normal file
30
types/testdata/container/correct/c9.json
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"embedded_type": "address",
|
||||
"quality": 80,
|
||||
"id": "2.35;48.8652835",
|
||||
"name": "Cour Greneta (Paris)",
|
||||
"address": {
|
||||
"name": "Cour Greneta",
|
||||
"house_number": 0,
|
||||
"coord": {
|
||||
"lat": "48.8652835",
|
||||
"lon": "2.35"
|
||||
},
|
||||
"label": "Cour Greneta (Paris)",
|
||||
"administrative_regions": [
|
||||
{
|
||||
"insee": "75056",
|
||||
"name": "Paris",
|
||||
"level": 8,
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"label": "Paris",
|
||||
"id": "admin:fr:75056",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"id": "2.35;48.8652835"
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{}
|
@ -0,0 +1,11 @@
|
||||
panic: runtime error: invalid memory address or nil pointer dereference
|
||||
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x4f1cdb]
|
||||
|
||||
goroutine 1 [running]:
|
||||
github.com/aabizri/gonavitia/types.FuzzPlaceCountainer(0x7fa709ccf000, 0x2, 0x200000, 0x3)
|
||||
/tmp/go-fuzz-build662575377/gopath/src/github.com/aabizri/gonavitia/types/place_fuzz.go:22 +0x18b
|
||||
go-fuzz-dep.Main(0x53b308)
|
||||
/tmp/go-fuzz-build662575377/goroot/src/go-fuzz-dep/main.go:49 +0xde
|
||||
main.main()
|
||||
/tmp/go-fuzz-build662575377/gopath/src/github.com/aabizri/gonavitia/types/go.fuzz.main/main.go:10 +0x2d
|
||||
exit status 2
|
@ -0,0 +1 @@
|
||||
"{}"
|
202
types/testdata/create.go
vendored
Normal file
202
types/testdata/create.go
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type journeyRequest struct {
|
||||
Journeys []json.RawMessage `json:"journeys"`
|
||||
}
|
||||
|
||||
func (r journeyRequest) messages() []json.RawMessage {
|
||||
return r.Journeys
|
||||
}
|
||||
|
||||
type placeRequest struct {
|
||||
Places []json.RawMessage `json:"places"`
|
||||
}
|
||||
|
||||
func (r placeRequest) messages() []json.RawMessage {
|
||||
return r.Places
|
||||
}
|
||||
|
||||
type coverageRequest struct {
|
||||
Regions []json.RawMessage `json:"regions"`
|
||||
}
|
||||
|
||||
func (r coverageRequest) messages() []json.RawMessage {
|
||||
return r.Regions
|
||||
}
|
||||
|
||||
type request interface {
|
||||
messages() []json.RawMessage
|
||||
}
|
||||
|
||||
var (
|
||||
originFlag = flag.String("from", "../../testdata", "Original directory")
|
||||
destinationFlag = flag.String("to", "./", "Destination directory")
|
||||
originPath string
|
||||
destinationPath string
|
||||
)
|
||||
|
||||
var equivalence = map[string]string{
|
||||
"journeys": "journey",
|
||||
"places": "place",
|
||||
"coverage": "region",
|
||||
}
|
||||
|
||||
// load loads the origin directory and files
|
||||
func load(path string) (map[string][]*os.File, error) {
|
||||
subDirs, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error while listing subdirs !: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
originFiles := make(map[string][]*os.File, len(subDirs))
|
||||
|
||||
for _, dinfo := range subDirs {
|
||||
dirName := dinfo.Name()
|
||||
if !dinfo.IsDir() {
|
||||
fmt.Printf("Skipping %s...\n", dirName)
|
||||
} else {
|
||||
fmt.Printf("Processing %s directory...\n", dirName)
|
||||
files, err := ioutil.ReadDir(filepath.Join(originPath, dirName))
|
||||
if err != nil {
|
||||
fmt.Printf("Error while reading %s directory !: %v\n", dirName, err)
|
||||
return originFiles, err
|
||||
}
|
||||
for _, finfo := range files {
|
||||
fmt.Printf("\tProcessing %s...\n", finfo.Name())
|
||||
fname := finfo.Name()
|
||||
if fname[len(fname)-4:] != "json" {
|
||||
fmt.Printf("\t\tSkipping\n")
|
||||
} else {
|
||||
path := filepath.Join(originPath, dirName, fname)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Printf("\t\tError while opening file %s! : %v\n", path, err)
|
||||
}
|
||||
originFiles[dirName] = append(originFiles[dirName], f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return originFiles, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if filepath.IsAbs(*originFlag) {
|
||||
originPath = *originFlag
|
||||
} else {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("Error while retrieving working directory, please retry with an absolute path: %v\n", err)
|
||||
return
|
||||
}
|
||||
originPath = filepath.Join(wd, *originFlag)
|
||||
}
|
||||
|
||||
if filepath.IsAbs(*destinationFlag) {
|
||||
destinationPath = *destinationFlag
|
||||
} else {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("Error while retrieving working directory, please retry with an absolute path: %v\n", err)
|
||||
return
|
||||
}
|
||||
destinationPath = filepath.Join(wd, *destinationFlag)
|
||||
}
|
||||
|
||||
originFiles, err := load(originPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
}
|
||||
|
||||
// For each of them, process them
|
||||
for cat, files := range originFiles {
|
||||
cat = equivalence[cat]
|
||||
fmt.Printf("Printing %s...\n", cat)
|
||||
for _, file := range files {
|
||||
fmt.Printf("Dealing with one origin-file...\n")
|
||||
// Prepare decoder
|
||||
dec := json.NewDecoder(file)
|
||||
|
||||
// Create hosting structure & decode to it
|
||||
var req request
|
||||
switch cat {
|
||||
case "journey":
|
||||
tmp := &journeyRequest{}
|
||||
// Decode to it
|
||||
err := dec.Decode(tmp)
|
||||
if err != nil {
|
||||
stat, _ := file.Stat()
|
||||
fmt.Printf("Error while decoding %s: %v\n", stat.Name(), err)
|
||||
return
|
||||
}
|
||||
req = tmp
|
||||
case "place":
|
||||
tmp := &placeRequest{}
|
||||
// Decode to it
|
||||
err := dec.Decode(tmp)
|
||||
if err != nil {
|
||||
stat, _ := file.Stat()
|
||||
fmt.Printf("Error while decoding %s: %v\n", stat.Name(), err)
|
||||
return
|
||||
}
|
||||
req = tmp
|
||||
case "region":
|
||||
tmp := &coverageRequest{}
|
||||
// Decode to it
|
||||
err := dec.Decode(tmp)
|
||||
if err != nil {
|
||||
stat, _ := file.Stat()
|
||||
fmt.Printf("Error while decoding %s: %v\n", stat.Name(), err)
|
||||
return
|
||||
}
|
||||
req = tmp
|
||||
default:
|
||||
fmt.Printf("Incorrect category")
|
||||
return
|
||||
}
|
||||
|
||||
// Get file stat
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
fmt.Printf("Error while retrieving file stat: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Now for each Journey, create a new file and write to it
|
||||
for i, message := range req.messages() {
|
||||
// Create the file name
|
||||
nname := fmt.Sprintf("%s%d.json", stat.Name()[:len(stat.Name())-5], i)
|
||||
npath := filepath.Join(destinationPath, cat, nname)
|
||||
|
||||
// Create the file
|
||||
nfile, err := os.Create(npath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error while creating file %s: %v\n", npath, err)
|
||||
}
|
||||
|
||||
// Write to it
|
||||
enc := json.NewEncoder(nfile)
|
||||
enc.SetIndent("", "\t")
|
||||
err = enc.Encode(message)
|
||||
if err != nil {
|
||||
fmt.Printf("Error while writing to file %s: %v\n", npath, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
23
types/testdata/disruption/correct/doc.json
vendored
Normal file
23
types/testdata/disruption/correct/doc.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"id": "ce7e265d-5762-45b6-ab4d-a1df643dd48d",
|
||||
"status": "active",
|
||||
"disruption_id": "ce7e265d-5762-45b6-ab4d-a1df643dd48d",
|
||||
"impact_id": "ce7e265d-5762-45b6-ab4d-a1df643dd48d",
|
||||
"severity": {
|
||||
"name": "trip delayed",
|
||||
"effect": "SIGNIFICANT_DELAYS"
|
||||
},
|
||||
"application_periods": [
|
||||
{
|
||||
"begin": "20160608T215400",
|
||||
"end": "20160608T230959"
|
||||
}
|
||||
],
|
||||
"messages": [
|
||||
{"text": "Strike"}
|
||||
],
|
||||
"updated_at": "20160617T132624",
|
||||
"impacted_objects": [],
|
||||
"cause": "Cause...",
|
||||
"category": "incident"
|
||||
}
|
1821
types/testdata/journey/bench/heavy-nogeojson.json
vendored
Normal file
1821
types/testdata/journey/bench/heavy-nogeojson.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2135
types/testdata/journey/bench/heavy.json
vendored
Normal file
2135
types/testdata/journey/bench/heavy.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
898
types/testdata/journey/bench/light-nogeojson.json
vendored
Normal file
898
types/testdata/journey/bench/light-nogeojson.json
vendored
Normal file
@ -0,0 +1,898 @@
|
||||
{
|
||||
"arrival_date_time": "20170413T141146",
|
||||
"calendars": [
|
||||
{
|
||||
"active_periods": [
|
||||
{
|
||||
"begin": "20170326",
|
||||
"end": "20170417"
|
||||
}
|
||||
],
|
||||
"exceptions": [
|
||||
{
|
||||
"datetime": "20170402",
|
||||
"type": "remove"
|
||||
},
|
||||
{
|
||||
"datetime": "20170409",
|
||||
"type": "remove"
|
||||
},
|
||||
{
|
||||
"datetime": "20170415",
|
||||
"type": "add"
|
||||
}
|
||||
],
|
||||
"week_pattern": {
|
||||
"friday": true,
|
||||
"monday": true,
|
||||
"saturday": false,
|
||||
"sunday": true,
|
||||
"thursday": true,
|
||||
"tuesday": true,
|
||||
"wednesday": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"co2_emission": {
|
||||
"unit": "gEC",
|
||||
"value": 39.556
|
||||
},
|
||||
"departure_date_time": "20170413T133945",
|
||||
"duration": 1921,
|
||||
"durations": {
|
||||
"total": 1921,
|
||||
"walking": 1081
|
||||
},
|
||||
"fare": {
|
||||
"found": false,
|
||||
"links": [],
|
||||
"total": {
|
||||
"currency": "",
|
||||
"value": "0.0"
|
||||
}
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "https://api.navitia.io/v1/coverage/fr-idf/journeys?allowed_id%5B%5D=stop_area%3AOIF%3ASA%3A8739305\u0026allowed_id%5B%5D=stop_area%3AOIF%3ASA%3A8754700\u0026to=2.2922926%3B48.8583736\u0026from=2.3749036%3B48.8467927\u0026min_nb_journeys=5",
|
||||
"rel": "same_journey_schedules",
|
||||
"templated": false,
|
||||
"type": "journeys"
|
||||
}
|
||||
],
|
||||
"nb_transfers": 0,
|
||||
"requested_date_time": "20170413T133734",
|
||||
"sections": [
|
||||
{
|
||||
"arrival_date_time": "20170413T135400",
|
||||
"co2_emission": {
|
||||
"unit": "",
|
||||
"value": 0.0
|
||||
},
|
||||
"departure_date_time": "20170413T133945",
|
||||
"duration": 855,
|
||||
"from": {
|
||||
"address": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
},
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.8467927",
|
||||
"lon": "2.3749036"
|
||||
},
|
||||
"house_number": 9,
|
||||
"id": "2.3749036;48.8467927",
|
||||
"label": "9 Rue Abel (Paris)",
|
||||
"name": "Rue Abel"
|
||||
},
|
||||
"embedded_type": "address",
|
||||
"id": "2.3749036;48.8467927",
|
||||
"name": "9 Rue Abel (Paris)",
|
||||
"quality": 0
|
||||
},
|
||||
"id": "section_12_0",
|
||||
"links": [],
|
||||
"mode": "walking",
|
||||
"path": [
|
||||
{
|
||||
"direction": 0,
|
||||
"duration": 90,
|
||||
"length": 101,
|
||||
"name": "Rue Abel"
|
||||
},
|
||||
{
|
||||
"direction": 22,
|
||||
"duration": 14,
|
||||
"length": 16,
|
||||
"name": "Boulevard Diderot"
|
||||
},
|
||||
{
|
||||
"direction": -7,
|
||||
"duration": 328,
|
||||
"length": 367,
|
||||
"name": "Rue Van Gogh"
|
||||
},
|
||||
{
|
||||
"direction": 8,
|
||||
"duration": 40,
|
||||
"length": 45,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": -4,
|
||||
"duration": 183,
|
||||
"length": 205,
|
||||
"name": "Pont Charles de Gaulle"
|
||||
},
|
||||
{
|
||||
"direction": -6,
|
||||
"duration": 13,
|
||||
"length": 15,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 90,
|
||||
"duration": 161,
|
||||
"length": 180,
|
||||
"name": "Quai d'Austerlitz"
|
||||
},
|
||||
{
|
||||
"direction": -87,
|
||||
"duration": 26,
|
||||
"length": 29,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 0,
|
||||
"duration": 0,
|
||||
"length": 0,
|
||||
"name": "Cour Seine"
|
||||
}
|
||||
],
|
||||
"to": {
|
||||
"embedded_type": "stop_point",
|
||||
"id": "stop_point:OIF:SP:8754702:800:C",
|
||||
"name": "Gare d'Austerlitz RER C (Paris)",
|
||||
"quality": 0,
|
||||
"stop_point": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41333"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754702:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8754702:800:C"
|
||||
}
|
||||
],
|
||||
"commercial_modes": [
|
||||
{
|
||||
"id": "commercial_mode:rapidtransit",
|
||||
"name": "RER"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.842528",
|
||||
"lon": "2.365433"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8754702:800:C",
|
||||
"label": "Gare d'Austerlitz RER C (Paris)",
|
||||
"links": [],
|
||||
"name": "Gare d'Austerlitz RER C",
|
||||
"physical_modes": [
|
||||
{
|
||||
"id": "physical_mode:RapidTransit",
|
||||
"name": "Train de banlieue / RER"
|
||||
}
|
||||
],
|
||||
"stop_area": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754700"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopArea:8754700"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.843578",
|
||||
"lon": "2.364651"
|
||||
},
|
||||
"id": "stop_area:OIF:SA:8754700",
|
||||
"label": "Gare d'Austerlitz (Paris)",
|
||||
"links": [],
|
||||
"name": "Gare d'Austerlitz",
|
||||
"timezone": "Europe/Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "street_network"
|
||||
},
|
||||
{
|
||||
"additional_informations": [
|
||||
"regular"
|
||||
],
|
||||
"arrival_date_time": "20170413T140800",
|
||||
"base_arrival_date_time": "20170413T140800",
|
||||
"base_departure_date_time": "20170413T135400",
|
||||
"co2_emission": {
|
||||
"unit": "gEC",
|
||||
"value": 39.556
|
||||
},
|
||||
"departure_date_time": "20170413T135400",
|
||||
"display_informations": {
|
||||
"code": "C",
|
||||
"color": "FCD946",
|
||||
"commercial_mode": "RER",
|
||||
"description": "",
|
||||
"direction": "Gare de Versailles Ch\u00e2teau - Rive Gauche (Versailles)",
|
||||
"equipments": [],
|
||||
"headsign": "VICK",
|
||||
"label": "C",
|
||||
"links": [],
|
||||
"network": "RER",
|
||||
"physical_mode": "Train de banlieue / RER",
|
||||
"text_color": "FFFFFF"
|
||||
},
|
||||
"duration": 840,
|
||||
"from": {
|
||||
"embedded_type": "stop_point",
|
||||
"id": "stop_point:OIF:SP:8754702:800:C",
|
||||
"name": "Gare d'Austerlitz RER C (Paris)",
|
||||
"quality": 0,
|
||||
"stop_point": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41333"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754702:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8754702:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.842528",
|
||||
"lon": "2.365433"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8754702:800:C",
|
||||
"label": "Gare d'Austerlitz RER C (Paris)",
|
||||
"links": [],
|
||||
"name": "Gare d'Austerlitz RER C",
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754700"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopArea:8754700"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.843578",
|
||||
"lon": "2.364651"
|
||||
},
|
||||
"id": "stop_area:OIF:SA:8754700",
|
||||
"label": "Gare d'Austerlitz (Paris)",
|
||||
"links": [],
|
||||
"name": "Gare d'Austerlitz",
|
||||
"timezone": "Europe/Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "section_13_0",
|
||||
"links": [
|
||||
{
|
||||
"id": "vehicle_journey:OIF:82209457-1_354848-1_dst_2",
|
||||
"type": "vehicle_journey"
|
||||
},
|
||||
{
|
||||
"id": "line:OIF:800:COIF741",
|
||||
"type": "line"
|
||||
},
|
||||
{
|
||||
"id": "route:OIF:800:C_R",
|
||||
"type": "route"
|
||||
},
|
||||
{
|
||||
"id": "commercial_mode:rapidtransit",
|
||||
"type": "commercial_mode"
|
||||
},
|
||||
{
|
||||
"id": "physical_mode:RapidTransit",
|
||||
"type": "physical_mode"
|
||||
},
|
||||
{
|
||||
"id": "network:RER",
|
||||
"type": "network"
|
||||
}
|
||||
],
|
||||
"stop_date_times": [
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T135300",
|
||||
"base_arrival_date_time": "20170413T135300",
|
||||
"base_departure_date_time": "20170413T135400",
|
||||
"departure_date_time": "20170413T135400",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41333"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754702:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8754702:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.842528",
|
||||
"lon": "2.365433"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8754702:800:C",
|
||||
"label": "Gare d'Austerlitz RER C (Paris)",
|
||||
"links": [],
|
||||
"name": "Gare d'Austerlitz RER C"
|
||||
}
|
||||
},
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T135700",
|
||||
"base_arrival_date_time": "20170413T135700",
|
||||
"base_departure_date_time": "20170413T135800",
|
||||
"departure_date_time": "20170413T135800",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41335"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754731:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8754731:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.853336",
|
||||
"lon": "2.346035"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8754731:800:C",
|
||||
"label": "Saint-Michel Notre-Dame RER C (Paris)",
|
||||
"links": [],
|
||||
"name": "Saint-Michel Notre-Dame RER C"
|
||||
}
|
||||
},
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T140000",
|
||||
"base_arrival_date_time": "20170413T140000",
|
||||
"base_departure_date_time": "20170413T140100",
|
||||
"departure_date_time": "20170413T140100",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41334"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8754730:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8754730:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.860708",
|
||||
"lon": "2.32562"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8754730:800:C",
|
||||
"label": "Mus\u00e9e d'Orsay (Paris)",
|
||||
"links": [],
|
||||
"name": "Mus\u00e9e d'Orsay"
|
||||
}
|
||||
},
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T140300",
|
||||
"base_arrival_date_time": "20170413T140300",
|
||||
"base_departure_date_time": "20170413T140400",
|
||||
"departure_date_time": "20170413T140400",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41208"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739303:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739303:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.862902",
|
||||
"lon": "2.313911"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8739303:800:C",
|
||||
"label": "Invalides (Paris)",
|
||||
"links": [],
|
||||
"name": "Invalides"
|
||||
}
|
||||
},
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T140600",
|
||||
"base_arrival_date_time": "20170413T140600",
|
||||
"base_departure_date_time": "20170413T140600",
|
||||
"departure_date_time": "20170413T140600",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41209"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739304:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739304:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.862662",
|
||||
"lon": "2.30099"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8739304:800:C",
|
||||
"label": "Pont de l'Alma (Paris)",
|
||||
"links": [],
|
||||
"name": "Pont de l'Alma"
|
||||
}
|
||||
},
|
||||
{
|
||||
"additional_informations": [],
|
||||
"arrival_date_time": "20170413T140800",
|
||||
"base_arrival_date_time": "20170413T140800",
|
||||
"base_departure_date_time": "20170413T140900",
|
||||
"departure_date_time": "20170413T140900",
|
||||
"links": [],
|
||||
"stop_point": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41210"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739305:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.857293",
|
||||
"lon": "2.290392"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8739305:800:C",
|
||||
"label": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"links": [],
|
||||
"name": "Champ de Mars Tour Eiffel"
|
||||
}
|
||||
}
|
||||
],
|
||||
"to": {
|
||||
"embedded_type": "stop_point",
|
||||
"id": "stop_point:OIF:SP:8739305:800:C",
|
||||
"name": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"quality": 0,
|
||||
"stop_point": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41210"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739305:800:C"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.857293",
|
||||
"lon": "2.290392"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8739305:800:C",
|
||||
"label": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"links": [],
|
||||
"name": "Champ de Mars Tour Eiffel",
|
||||
"stop_area": {
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopArea:8739305"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.8572",
|
||||
"lon": "2.293234"
|
||||
},
|
||||
"id": "stop_area:OIF:SA:8739305",
|
||||
"label": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"links": [],
|
||||
"name": "Champ de Mars Tour Eiffel",
|
||||
"timezone": "Europe/Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "public_transport"
|
||||
},
|
||||
{
|
||||
"arrival_date_time": "20170413T141146",
|
||||
"co2_emission": {
|
||||
"unit": "",
|
||||
"value": 0.0
|
||||
},
|
||||
"departure_date_time": "20170413T140800",
|
||||
"duration": 226,
|
||||
"from": {
|
||||
"embedded_type": "stop_point",
|
||||
"id": "stop_point:OIF:SP:8739305:800:C",
|
||||
"name": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"quality": 0,
|
||||
"stop_point": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
},
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41210"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739305:800:C"
|
||||
},
|
||||
{
|
||||
"type": "ZDEr_ID_REF_A",
|
||||
"value": "41210"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305:800:C"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopPoint:8739305:800:C"
|
||||
}
|
||||
],
|
||||
"commercial_modes": [
|
||||
{
|
||||
"id": "commercial_mode:rapidtransit",
|
||||
"name": "RER"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.857293",
|
||||
"lon": "2.290392"
|
||||
},
|
||||
"equipments": [],
|
||||
"id": "stop_point:OIF:SP:8739305:800:C",
|
||||
"label": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"links": [],
|
||||
"name": "Champ de Mars Tour Eiffel",
|
||||
"physical_modes": [
|
||||
{
|
||||
"id": "physical_mode:RapidTransit",
|
||||
"name": "Train de banlieue / RER"
|
||||
}
|
||||
],
|
||||
"stop_area": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"codes": [
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopArea:8739305"
|
||||
},
|
||||
{
|
||||
"type": "external_code",
|
||||
"value": "OIF8739305"
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"value": "StopArea:8739305"
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.8572",
|
||||
"lon": "2.293234"
|
||||
},
|
||||
"id": "stop_area:OIF:SA:8739305",
|
||||
"label": "Champ de Mars Tour Eiffel (Paris)",
|
||||
"links": [],
|
||||
"name": "Champ de Mars Tour Eiffel",
|
||||
"timezone": "Europe/Paris"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "section_14_0",
|
||||
"links": [],
|
||||
"mode": "walking",
|
||||
"path": [
|
||||
{
|
||||
"direction": 0,
|
||||
"duration": 19,
|
||||
"length": 21,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 0,
|
||||
"duration": 14,
|
||||
"length": 16,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 3,
|
||||
"duration": 5,
|
||||
"length": 6,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 13,
|
||||
"duration": 28,
|
||||
"length": 31,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": -101,
|
||||
"duration": 3,
|
||||
"length": 3,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": -4,
|
||||
"duration": 5,
|
||||
"length": 6,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 3,
|
||||
"duration": 3,
|
||||
"length": 3,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": -89,
|
||||
"duration": 17,
|
||||
"length": 19,
|
||||
"name": ""
|
||||
},
|
||||
{
|
||||
"direction": 93,
|
||||
"duration": 132,
|
||||
"length": 148,
|
||||
"name": "Quai Branly"
|
||||
}
|
||||
],
|
||||
"to": {
|
||||
"address": {
|
||||
"administrative_regions": [
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
},
|
||||
{
|
||||
"coord": {
|
||||
"lat": "48.856609",
|
||||
"lon": "2.351499"
|
||||
},
|
||||
"id": "admin:fr:75056",
|
||||
"insee": "75056",
|
||||
"label": "Paris",
|
||||
"level": 8,
|
||||
"name": "Paris",
|
||||
"zip_code": ""
|
||||
}
|
||||
],
|
||||
"coord": {
|
||||
"lat": "48.8583736",
|
||||
"lon": "2.2922926"
|
||||
},
|
||||
"house_number": 69,
|
||||
"id": "2.2922926;48.8583736",
|
||||
"label": "69 Quai Branly (Paris)",
|
||||
"name": "Quai Branly"
|
||||
},
|
||||
"embedded_type": "address",
|
||||
"id": "2.2922926;48.8583736",
|
||||
"name": "69 Quai Branly (Paris)",
|
||||
"quality": 0
|
||||
},
|
||||
"type": "street_network"
|
||||
}
|
||||
],
|
||||
"status": "",
|
||||
"tags": [
|
||||
"walking",
|
||||
"ecologic"
|
||||
],
|
||||
"type": "best"
|
||||
}
|
1112
types/testdata/journey/bench/light.json
vendored
Normal file
1112
types/testdata/journey/bench/light.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1565
types/testdata/journey/bench/regular-nogeojson.json
vendored
Normal file
1565
types/testdata/journey/bench/regular-nogeojson.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1863
types/testdata/journey/bench/regular.json
vendored
Normal file
1863
types/testdata/journey/bench/regular.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2135
types/testdata/journey/correct/a0.json
vendored
Normal file
2135
types/testdata/journey/correct/a0.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1863
types/testdata/journey/correct/a1.json
vendored
Normal file
1863
types/testdata/journey/correct/a1.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1564
types/testdata/journey/correct/a2.json
vendored
Normal file
1564
types/testdata/journey/correct/a2.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1112
types/testdata/journey/correct/b0.json
vendored
Normal file
1112
types/testdata/journey/correct/b0.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user