From cfce8f05136a1bc601ad396ac329fe7ccb05ecb2 Mon Sep 17 00:00:00 2001 From: forestmvey Date: Wed, 25 Sep 2024 10:50:13 -0700 Subject: [PATCH] Initial commit for the InfluxDB Timestream Connector. Signed-off-by: forestmvey --- .gitignore | 4 - .../influxdb-timestream-connector/Cargo.lock | 2549 +++++++++++++++++ .../influxdb-timestream-connector/Cargo.toml | 33 + .../influxdb-timestream-connector/README.md | 257 ++ .../docs/adr/require-line-protocol-tags.md | 27 + ...estream_connector_lambda_function_arch.png | Bin 0 -> 146934 bytes .../influxdb-timestream-connector/src/lib.rs | 119 + .../src/line_protocol_parser.rs | 78 + .../src/line_protocol_parser/tests.rs | 748 +++++ .../influxdb-timestream-connector/src/main.rs | 16 + .../src/metric.rs | 60 + .../src/records_builder.rs | 136 + .../multi_table_multi_measure_builder.rs | 114 + .../src/records_builder/tests.rs | 391 +++ .../src/timestream_utils.rs | 165 ++ .../tests/integration_test.rs | 1171 ++++++++ .../sample-influxdb-clients/README.md | 7 + .../data/bird-migration.line | 2002 +++++++++++++ .../sample-influxdb-clients/go/README.md | 43 + .../sample-influxdb-clients/go/go.mod | 29 + .../sample-influxdb-clients/go/go.sum | 119 + .../go/line-protocol-client-demo.go | 117 + 22 files changed, 8181 insertions(+), 4 deletions(-) create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/Cargo.lock create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/Cargo.toml create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/README.md create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/docs/adr/require-line-protocol-tags.md create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/docs/img/influxdb_timestream_connector_lambda_function_arch.png create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/lib.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser/tests.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/main.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/metric.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/multi_table_multi_measure_builder.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/tests.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/src/timestream_utils.rs create mode 100644 integrations/influxdb_connector/influxdb-timestream-connector/tests/integration_test.rs create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/README.md create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/data/bird-migration.line create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/go/README.md create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/go/go.mod create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/go/go.sum create mode 100644 integrations/influxdb_connector/sample-influxdb-clients/go/line-protocol-client-demo.go diff --git a/.gitignore b/.gitignore index 28883687..82d44953 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,6 @@ bin/ obj/ -# Ignore common go files -go.mod -go.sum - # Ignore common Java files .idea/ *.iml diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.lock b/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.lock new file mode 100644 index 00000000..806f2c74 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.lock @@ -0,0 +1,2549 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "aws-config" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2424565416eef55906f9f8cece2072b6b6a76075e3ff81483ebe938a89a4c05f" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abf69a87be33b6f125a93d5046b5f7395c26d1f449bf8d3927f5577463b6de0" +dependencies = [ + "ahash", + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac", + "http 0.2.12", + "http-body 0.4.6", + "lru", + "once_cell", + "percent-encoding", + "regex-lite", + "sha2", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-timestreamwrite" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87050ae54cd21499f85cb7192adf71295c1a34c2d0ce9b823f784cb0be770561" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "once_cell", + "regex-lite", + "tokio", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint 0.5.5", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "p256", + "percent-encoding", + "ring", + "sha2", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.60.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598b1689d001c4d4dc3cb386adb07d37786783aee3ac4b324bcadac116bf3d23" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http 0.2.12", + "http-body 0.4.6", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6363078f927f612b970edf9d1903ef5cef9a64d1e8423525ebb1f0a1633c858" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01dbcb6e2588fd64cfb6d7529661b06466419e4c54ed1c62d6510d2d0350a728" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.0", + "httparse", + "hyper 0.14.29", + "hyper-rustls", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273dcdfd762fae3e1650b8024624e7cd50e484e37abdab73a7a706188ad34543" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "aws_lambda_events" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7319a086b79c3ff026a33a61e80f04fd3885fbb73237981ea080d21944e1cb1c" +dependencies = [ + "base64 0.22.1", + "bytes", + "http 1.1.0", + "http-body 1.0.0", + "http-serde", + "query_map", + "serde", + "serde_json", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint 0.4.9", + "der", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "http-serde" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" +dependencies = [ + "http 1.1.0", + "serde", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.29", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "influxdb-line-protocol" +version = "1.0.0" +source = "git+https://github.com/influxdata/influxdb3_core?rev=d81f63ddc10e3cf1c28b05e6c1cef03b71da7f8a#d81f63ddc10e3cf1c28b05e6c1cef03b71da7f8a" +dependencies = [ + "bytes", + "log", + "nom", + "smallvec", + "snafu", +] + +[[package]] +name = "influxdb-timestream-connector" +version = "0.1.0" +dependencies = [ + "anyhow", + "aws-config", + "aws-credential-types", + "aws-runtime", + "aws-sdk-s3", + "aws-sdk-timestreamwrite", + "aws-types", + "chrono", + "influxdb-line-protocol", + "lambda_http", + "rand", + "serde", + "tokio", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lambda_http" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fe279be7f89f5f72c97c3a96f45c43db8edab1007320ecc6a5741273b4d6db" +dependencies = [ + "aws_lambda_events", + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.1", + "lambda_runtime", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio-stream", + "url", +] + +[[package]] +name = "lambda_runtime" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed49669d6430292aead991e19bf13153135a884f916e68f32997c951af637ebe" +dependencies = [ + "anyhow", + "async-stream", + "base64 0.22.1", + "bytes", + "futures", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "http-serde", + "hyper 1.4.1", + "hyper-util", + "lambda_runtime_api_client", + "pin-project", + "serde", + "serde_json", + "serde_path_to_error", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tracing", +] + +[[package]] +name = "lambda_runtime_api_client" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c90a10f094475a34a04da2be11686c4dcfe214d93413162db9ffdff3d3af293a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "tokio", + "tower", + "tower-service", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "query_map" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eab6b8b1074ef3359a863758dae650c7c0c6027927a085b7af911c8e0bf3a15" +dependencies = [ + "form_urlencoded", + "serde", + "serde_derive", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b835cb902660db3415a672d862905e791e54d306c6e8189168c7f3d9ae1c79d" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1e02fca405f6280643174a50c942219f0bbf4dbf7d480f1dd864d6f211ae5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.toml b/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.toml new file mode 100644 index 00000000..aac6e30f --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "influxdb-timestream-connector" +version = "0.1.0" +edition = "2021" + +[dependencies.lambda_http] +version = "0.13.0" +default-features = false +features = ["apigw_http", "anyhow", "tracing"] + +[dependencies] +anyhow = "1.0.86" +aws-config = { version = "1.5.5", features = ["behavior-version-latest"] } +aws-credential-types = "1.2.1" +aws-runtime = "1.4.2" +aws-sdk-s3 = "1.46.0" +aws-sdk-timestreamwrite = "1.39.0" +aws-types = "1.3.3" +chrono = "0.4.38" +influxdb-line-protocol = { git = "https://github.com/influxdata/influxdb3_core", rev = "d81f63ddc10e3cf1c28b05e6c1cef03b71da7f8a" } +rand = "0.5.0" +serde = { version = "1.0.208", features = ["derive"] } +tokio = { version = "1.39.3", features = ["full"] } + +[package.metadata.lambda.env] +region = "us-east-1" +database_name = "influxdb-line-protocol" +measure_name_for_multi_measure_records = "influxdb-measure" +enable_database_creation = "true" +enable_table_creation = "true" +enable_mag_store_writes = "true" +mag_store_retention_period = "8000" +mem_store_retention_period = "12" diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/README.md b/integrations/influxdb_connector/influxdb-timestream-connector/README.md new file mode 100644 index 00000000..905deecd --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/README.md @@ -0,0 +1,257 @@ +# InfluxDB Timestream Connector + +## Overview + +The InfluxDB Timestream connector allows [line protocol](https://docs.influxdata.com/influxdb/v2/reference/syntax/line-protocol/) to be ingested to [Amazon Timestream for LiveAnalytics](https://aws.amazon.com/timestream/). The connector parses ingested line protocol and maps the data to multi-measure records for ingestion into Timestream for LiveAnalytics using the [Timestream Write API](https://docs.aws.amazon.com/timestream/latest/developerguide/API_Operations_Amazon_Timestream_Write.html). + +### Architecture + +The following diagram shows a high-level overview of the connector's architecture when deployed as an [AWS Lambda](https://aws.amazon.com/lambda/) function. + + + +## Table Schema + +### Multi-Table Multi-Measure + +The following table shows how the connector maps line protocol elements to Timestream for LiveAnalytics record elements. + +| Line Protocol Element | Timestream Record Attribute | +|-----------------------|---------------------------| +| Timestamp | Time | +| Tags | Dimensions | +| Fields | Measures | +| Measurements | Table names | + +A Timestream record's `measure_name` field is not derived from any element of ingested line protocol. Due to the multi-measure record translation, the connector sets the `measure_name` for each multi-measure record to the value of a Lambda environment variable. + +The following example shows the translation of a single line protocol point into a Timestream for LiveAnalytics table, using a Timestamp with second precision and a Lambda environment variable configured to `influxdb-measure`: + +#### Line Protocol Point + +``` +cpu_load_short,host=server01,region=us-west value=0.64,average=1.24, 1725059274 +``` + +#### Resulting cpu_load_short Timestream for LiveAnalytics Table + +| host | region | measure_name | time | value | average | +|----------|---------|------------------|-------------------------------|-------|---------| +| server01 | us-west | influxdb-measure | 2024-08-30 23:07:54.000000000 | 0.64 | 1.24 | + +## Deployment Options + +### AWS CloudFormation Deployment + +The InfluxDB Timestream connector can be deployed within an AWS CloudFormation stack as an AWS Lambda function with an accompanying Amazon REST API Gateway. The API Gateway mimics the InfluxDB v2 API and provides the `/api/v2/write` endpoint for ingestion. + +#### Deploying a CloudFormation Stack Using SAM CLI + +The stack can be deployed using the [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) and `template.yml`. + +1. Run the following command replacing `` with the AWS region you want to deploy in and provide parameter overrides as desired: + + ```shell + sam --region --parameter-overrides ParameterKey=exampleKey,ParameterValue=exampleValue deploy template.yml + ``` +2. Once the stack has finished deploying, take note of the output "Endpoint" value. This value will be used as the endpoint for all write requests, for example, `/api/v2/write`. + +#### Stack Logs + +##### Viewing REST API Gateway Logs + +By default, execution and access logging is enabled for the REST API Gateway. + +To view logs: + +1. Visit the [AWS CloudFormation console](https://console.aws.amazon.com/cloudformation/home). +2. In the navigation pane, choose **Stacks**. +3. Choose your deployed stack from the list of stacks. +4. In the **Resources** tab choose the **Physical ID** of the REST API Gateway. +5. In the navigation pane, under **Monitor**, choose **Logging**. +6. From the dropdown menu, select the currently deployed stage. +7. Choose **View logs in CloudWatch**. + +##### Viewing Lambda Logs + +By default, logging is enabled for the deployed connector. + +To view logs: + +1. Visit the [AWS CloudFormation console](https://console.aws.amazon.com/cloudformation/home). +2. In the navigation pane, choose **Stacks**. +3. Choose your deployed stack from the list of stacks. +4. In the **Resources** tab choose the **Physical ID** of the Lambda function. +5. In the **Monitor** tab, choose **View CloudWatch logs**. + +### Local Deployment + +The connector can be run locally using [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html). + +1. [Download and install Rust](https://www.rust-lang.org/tools/install). +2. [Download and install Cargo Lambda](https://www.cargo-lambda.info/guide/installation.html). +3. Configure the following environment variables: + - `region` string: the AWS region to use. Defaults to `us-east-1`. + - `database_name` string: the Timestream for LiveAnalytics database name to use. Defaults to `influxdb-line-protocol`. + - `measure_name_for_multi_measure_records` string: the value to use in records as the measure name. Defaults to `influxdb-measure`. +4. To run the connector on `http://localhost:9000` execute the following command: + + ```shell + cargo lambda watch + ``` +5. Send all requests to `http://localhost:9000/api/v2/write`. + +## Security + +### Encryption + +When the connector is deployed as part of a CloudFormation stack, the stack's API Gateway ensures all communication is protected by TLS 1.2+. + +### Authentication + +The REST API Gateway ensures all requests are authenticated with SigV4. Any InfluxDB user tokens or credentials are discarded. This authentication method cannot be correlated to IAM authorization. + +## IAM Permissions + +### IAM Deployment Permissions + +[//]: # (TODO: Update deployment policy with least privilege once REST API Gateway deployment has been tested.) + +The following is the least privileged IAM permissions for deploying the connector. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "iam:CreateRole", + "iam:AttachRolePolicy", + "iam:UpdateAssumeRolePolicy", + "iam:PassRole", + "iam:PutRolePolicy" + ], + "Resource": [ + "arn:aws:iam::{account-id}:role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::{account-id}:role/cargo-lambda-role*" + ] + }, + { + "Effect": "Allow", + "Action": [ + "lambda:CreateFunction", + "lambda:UpdateFunctionCode", + "lambda:GetFunction", + "lambda:UpdateFunctionConfiguration", + "lambda:GetFunctionConfiguration", + "lambda:CreateFunctionUrlConfig" + ], + "Resource": "arn:aws:lambda:{region}:{account-id}:function:influxdb-timestream-connector" + } + ] +} +``` + +### IAM Execution Permissions + +The following is the least privileged IAM permissions for executing the connector. + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "timestream:WriteRecords", + "timestream:Select", + "timestream:DescribeTable", + "timestream:CreateTable" + ], + "Resource": "arn:aws:timestream:{region}:{account-id}:database/influxdb-line-protocol/table/*" + }, + { + "Effect": "Allow", + "Action": [ + "timestream:DescribeEndpoints" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "timestream:DescribeDatabase", + "timestream:CreateDatabase" + ], + "Resource": "arn:aws:timestream:{region}:{account-id}:database/influxdb-line-protocol" + } + ] +} +``` + +## Example Application with Go Client + +A demo Go client application is available under `sample-code/aws-timestream/sample-influxdb-clients/go/line-protocol-client-demo.go` that sends line protocol data to a local or deployed instance of the connector. A line protocol sample dataset is included in `sample-code/aws-timestream/sample-influxdb-clients/data/bird-migration.line`, which the demo application uses for ingestion. + +To configure the sample application and ingest all line protocol data contained in `bird-migration.line` to Timestream for LiveAnalytics, perform the following steps: + +1. [Download and install Go](https://go.dev/doc/install). +2. [Configure your AWS credentials for use by the AWS SDK for Rust](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html). +3. [Deploy the Timestream Line Protocol connector locally](#local-deployment). +4. Navigate to `sample-code/aws-timestream/sample-influxdb-clients/go/`. +5. Run the sample Go client: + - With the connector deployed in a CloudFormation stack, replacing `` with the AWS region you deployed your stack in and `` with the endpoint of your deployed REST API Gateway: + ```shell + go run line-protocol-client-demo.go --region --service execute-api --endpoint + ``` + - With the connector deployed locally: + ```shell + go run line-protocol-client-demo.go + ``` +6. Run the following [AWS CLI](https://aws.amazon.com/cli/) command to verify data has been ingested to Timestream for LiveAnalytics, replacing `` with the region you used for ingesting data: + ```shell + aws timestream-query query \ + --region \ + --query-string 'SELECT * FROM "influxdb-line-protocol"."migration1" LIMIT 10' + ``` + +## Troubleshooting + +### Amazon Timestream Write API Errors + +| Error | Status Code | Description | Solution | +|-------|-------------|-------------|----------| +| `AccessDeniedException` | 400 | You are not authorized to perform this action. | Verify that your user has the [correct permissions](#iam-permissions). | +| `InternalServerException` | 500 | Timestream was unable to fully process this request because of an internal server error. | Try ingesting records at another time. | +| `RejectedRecordsException` | 400 | The records sent to Timestream were invalid. | Check the [line protocol limitations](#line-protocol-limitations) and [caveats](#caveats) and ensure your line protocol points abide by expected formatting. +| `ResourceNotFoundException` | 400 | The operation tried to access a nonexistent resource. The resource might not be specified correctly, or its status might not be ACTIVE. | Verify that your database exists in Timestream for LiveAnalytics. Consider setting `enable_database_creation` to `true` to allow the connector to create the database for you. | +| `ServiceQuotaExceededException` | 400 | The instance quota of resource exceeded for this account. | Confirm you are not exceeding Timestream for LiveAnalytics' quotas. If you are not exceeding any quotas, check the list of [line protocol limitations](#line-protocol-limitations) below, specifically the number of unique measurement names. | +| `ThrottlingException` | 400 | Too many requests were made by a user and they exceeded the service quotas. The request was throttled. | Decrease the rate of your requests. | + + +## Limitations + +### Line Protocol Limitations + +Due to the connector translating line protocol to Timestream records, line protocol must satisfy [Timestream for LiveAnalytics' quotas](https://docs.aws.amazon.com/timestream/latest/developerguide/ts-limits.html). + +| Line Protocol Component | Limitation | +|-------------------------|------------| +| Maximum size of a line protocol point, with `measure_name` included. | 2 Kilobytes | +| Number of unique tag keys per table. | 128 | +| Maximum tag key size. | 60 bytes | +| Maximum measurement name size. | 256 bytes | +| Maximum unique measurement names per database (using multi-table multi-measure schema). | 50,000 | +| Maximum field key size. | 256 bytes | +| Maximum number of fields per point. | 256 | +| Maximum field value size. | 2048 bytes | +| Maximum unique field key values. | 1024 | +| Latest valid timestamp. | Fifteen minutes in the future from the current time. | +| Oldest valid timestamp. | `mag_store_retention_period` days before the current time. | + +## Caveats + +### Line Protocol Tag Requirement + +In order to ingest to Timestream for LiveAnalytics, every line protocol point must include at least one tag. diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/docs/adr/require-line-protocol-tags.md b/integrations/influxdb_connector/influxdb-timestream-connector/docs/adr/require-line-protocol-tags.md new file mode 100644 index 00000000..83cc3ae1 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/docs/adr/require-line-protocol-tags.md @@ -0,0 +1,27 @@ +# Requirement of Line Protocol to Include at Least One Tag + +## Status + +Accepted. + +## Context + +Ingesting data to Timestream using the AWS SDK for Rust requires building [records](https://docs.aws.amazon.com/timestream/latest/developerguide/API_Record.html). A record includes various fields. Among them is an array of dimensions, "metadata attributes of the time series." All records require at least one dimension. + +## Decision + +Line protocol uses the following format: +``` +,= = +``` +for example, using a timestamp in seconds: +``` +readings,fleet=Alberta velocity=52.13 1725050416 +``` +All components of a line protocol point are required except for tags. + +We have decided that all line protocol points ingested to Timestream using the InfluxDB Timestream connector must have at least one tag. This is due to the fact that we map tags to dimensions. + +## Consequences + +This means that some tweaking of ingested data is required on the user's side. Users must ensure all line protocol points have at least one tag. diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/docs/img/influxdb_timestream_connector_lambda_function_arch.png b/integrations/influxdb_connector/influxdb-timestream-connector/docs/img/influxdb_timestream_connector_lambda_function_arch.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2d1a76f028397f032a66c8fa0b5705323c8a94 GIT binary patch literal 146934 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 literal 0 HcmV?d00001 diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/lib.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/lib.rs new file mode 100644 index 00000000..95b85769 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/lib.rs @@ -0,0 +1,119 @@ +use anyhow::{anyhow, Error, Result}; +use aws_sdk_timestreamwrite as timestream_write; +use lambda_http::{Body, IntoResponse, Request, RequestExt, Response}; +use line_protocol_parser::*; +use records_builder::*; +use std::collections::HashMap; +use std::io::prelude::*; +use std::{str, thread, time}; +use timestream_utils::*; + +pub mod line_protocol_parser; +pub mod metric; +pub mod records_builder; +pub mod timestream_utils; + +// The maximum number of database/table creation/delete API calls +// that can be made per second is 1. +pub static TIMESTREAM_API_WAIT_SECONDS: u64 = 1; + +async fn handle_body( + client: ×tream_write::Client, + body: &[u8], + precision: ×tream_write::types::TimeUnit, +) -> Result<(), Error> { + // Handle parsing body in request + + let line_protocol = str::from_utf8(body).unwrap(); + let metric_data = parse_line_protocol(line_protocol)?; + let multi_measure_builder = get_builder(SchemaType::MultiTableMultiMeasure(std::env::var( + "measure_name_for_multi_measure_records", + )?)); + + // Only currently supports multi-measure multi-table + let multi_table_batch = build_records(&multi_measure_builder, &metric_data, precision)?; + handle_multi_table_ingestion(client, multi_table_batch).await?; + Ok(()) +} + +async fn handle_multi_table_ingestion( + client: ×tream_write::Client, + records: HashMap>, +) -> Result<(), Error> { + // Ingestion for multi-measure schema type + + let database_name = std::env::var("database_name")?; + + match database_exists(client, &database_name).await { + Ok(true) => (), + Ok(false) => { + if database_creation_enabled()? { + thread::sleep(time::Duration::from_secs(TIMESTREAM_API_WAIT_SECONDS)); + create_database(client, &database_name).await?; + } else { + return Err(anyhow!( + "Database {} does not exist and database creation is not enabled", + database_name + )); + } + } + Err(error) => return Err(anyhow!(error)), + } + + for (table_name, _) in records.iter() { + match table_exists(client, &database_name, table_name).await { + Ok(true) => (), + Ok(false) => { + if table_creation_enabled()? { + thread::sleep(time::Duration::from_secs(TIMESTREAM_API_WAIT_SECONDS)); + create_table(client, &database_name, table_name, get_table_config()?).await? + } else { + return Err(anyhow!( + "Table {} does not exist and database creation is not enabled", + table_name + )); + } + } + Err(error) => println!("error checking table exists: {:?}", error), + } + } + + for (table_name, mut records) in records.into_iter() { + ingest_records(client, &database_name, &table_name, &mut records).await? + } + + Ok(()) +} + +pub async fn lambda_handler( + client: ×tream_write::Client, + event: Request, +) -> Result { + // Handler for lambda runtime + + let precision = match event + .query_string_parameters_ref() + .and_then(|params| params.first("precision")) + { + Some("ms") => timestream_write::types::TimeUnit::Milliseconds, + Some("us") => timestream_write::types::TimeUnit::Microseconds, + Some("s") => timestream_write::types::TimeUnit::Seconds, + _ => timestream_write::types::TimeUnit::Nanoseconds, + }; + + let data: Result, _> = event.body().bytes().collect(); + let data = data?; + + match handle_body(client, &data, &precision).await { + Ok(_) => Ok(Response::builder() + .status(200) + .header("content-type", "text/html") + .body(Body::Empty) + .map_err(Box::new)?), + Err(error) => Ok(Response::builder() + .status(400) + .header("content-type", "text/html") + .body(Body::Text(error.to_string())) + .map_err(Box::new)?), + } +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser.rs new file mode 100644 index 00000000..24af9d66 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser.rs @@ -0,0 +1,78 @@ +use crate::metric::{self, Metric}; +use anyhow::{anyhow, Error}; +use influxdb_line_protocol::{self, parse_lines, ParsedLine}; + +pub fn parse_line_protocol(line_protocol: &str) -> Result, Error> { + // Parses a string of line protocol to a vector of Metric structs, + + let parsed_lines = parse_lines(line_protocol); + let mut output_metrics: Vec = Vec::new(); + for line_result in parsed_lines { + match line_result { + Ok(line) => { + let new_metric = parsed_line_to_metric(line)?; + output_metrics.push(new_metric); + } + + Err(error) => { + return Err(anyhow!("Failed to parse line: {}", error.to_string())); + } + } + } + Ok(output_metrics) +} + +pub fn parsed_line_to_metric(parsed_line: ParsedLine) -> Result { + // Converts an influxdb_line_protocol ParsedLine struct to a Metric struct. + + let mut new_tags: Vec<(String, String)> = Vec::new(); + if let Some(tag_set) = parsed_line.series.tag_set.as_ref() { + for (tag_key, tag_value) in tag_set { + new_tags.push((tag_key.to_string(), tag_value.to_string())); + } + } + + let mut new_fields: Vec<(String, metric::FieldValue)> = Vec::new(); + for (field_key, field_value) in parsed_line.field_set.as_ref() { + match field_value { + influxdb_line_protocol::FieldValue::I64(int_value) => { + new_fields.push((field_key.to_string(), metric::FieldValue::I64(*int_value))); + } + + influxdb_line_protocol::FieldValue::U64(uint_value) => { + new_fields.push((field_key.to_string(), metric::FieldValue::U64(*uint_value))); + } + + influxdb_line_protocol::FieldValue::F64(float_value) => { + new_fields.push((field_key.to_string(), metric::FieldValue::F64(*float_value))); + } + + influxdb_line_protocol::FieldValue::String(string_value) => { + new_fields.push(( + field_key.to_string(), + metric::FieldValue::String(string_value.to_string()), + )); + } + + influxdb_line_protocol::FieldValue::Boolean(bool_value) => { + new_fields.push(( + field_key.to_string(), + metric::FieldValue::Boolean(*bool_value), + )); + } + } + } + + match parsed_line.timestamp { + Some(timestamp) => Ok(Metric::new( + parsed_line.series.measurement.to_string(), + Some(new_tags), + new_fields, + timestamp, + )), + None => Err(anyhow!("Failed to parse timestamp")), + } +} + +#[cfg(test)] +pub mod tests; diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser/tests.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser/tests.rs new file mode 100644 index 00000000..69f430fa --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/line_protocol_parser/tests.rs @@ -0,0 +1,748 @@ +use super::parse_line_protocol; +use crate::metric::{self, Metric}; + +fn metrics_are_equal(actual_metric: &Metric, expected_metric: &Metric) -> bool { + // Determines whether two Metric structs have equal values for all struct fields. + if actual_metric.name() != expected_metric.name() { + println!("Metric names are not equal"); + return false; + } + + if actual_metric.timestamp() != expected_metric.timestamp() { + println!("Metric timestamps are not equal"); + return false; + } + + if actual_metric.tags() != expected_metric.tags() { + println!("Metric Option value of tags did not match") + } + + if let (Some(actual_tags), Some(expected_tags)) = + (&actual_metric.tags(), &expected_metric.tags()) + { + if actual_tags.len() != expected_tags.len() { + println!("Metric number of tags are not equal"); + return false; + } + + for (i, (tag_key, tag_value)) in actual_tags.iter().enumerate() { + if *tag_key != *expected_tags[i].0 { + println!("Metric tag keys are not equal"); + return false; + } + + if *tag_value != *expected_tags[i].1 { + println!("Metric tag values are not equal"); + return false; + } + } + } + + if actual_metric.fields().len() != expected_metric.fields().len() { + println!("Metric number of fields are not equal"); + return false; + } + + for (i, (field_key, field_value)) in actual_metric.fields().iter().enumerate() { + if *field_key != *expected_metric.fields()[i].0 { + println!("Metric field keys are not equal"); + return false; + } + + if field_value != &expected_metric.fields()[i].1 { + println!("Metric field values are not equal"); + return false; + } + } + + true +} + +#[test] +fn test_parse_field_integer() -> Result<(), String> { + // Tests parsing a single valid line with an integer field value. + let lp = String::from("readings incline=125i 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![("incline".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_field_float() -> Result<(), String> { + // Tests parsing a single valid line with a float field value. + let lp = String::from("readings incline=125 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![("incline".to_string(), metric::FieldValue::F64(125.0))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_field_string_double_quote() -> Result<(), String> { + // Tests parsing a single valid line with a string field value using double quotes. + let lp = String::from("readings incline=\"125\" 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![( + "incline".to_string(), + metric::FieldValue::String("125".to_string()), + )], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_field_string_single_quote() -> Result<(), String> { + // Tests parsing a single valid line with a string field value using single quotes. + let lp = String::from("readings incline=\"\'125\'\" 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![( + "incline".to_string(), + metric::FieldValue::String("\'125\'".to_string()), + )], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_field_boolean() -> Result<(), String> { + // Tests parsing a single valid line with a boolean field value. + let lp = String::from("readings incline=true 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![("incline".to_string(), metric::FieldValue::Boolean(true))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_field_boolean_invalid() -> Result<(), String> { + // Tests parsing a single invalid line with an invalid boolean field value. + let lp = String::from("readings incline=tree 1577836800000"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_measurement_unescaped_equals() -> Result<(), String> { + // Tests parsing a single valid line where the measurement name includes an unescaped equals sign. + let lp = String::from("read=ings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000"); + + let expected_metric = Metric::new( + "read=ings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_measurement_underscore_begin() -> Result<(), String> { + // Tests parsing a single valid line where the measurement name begins with an underscore. + let lp = String::from("_readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000"); + let expected_metric = Metric::new( + "_readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_no_fields() -> Result<(), String> { + // Tests parsing a single invalid line where fields are missing. + let lp = String::from("readings,fleet=Alberta 1577836800000"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_multiple_fields() -> Result<(), String> { + // Tests parsing a single valid line with multiple fields. + let lp = String::from("readings incline=125i,fuel_usage=21.30 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[ignore] +#[test] +fn test_parse_multiple_measurements() -> Result<(), String> { + // Tests parsing a single invalid line with multiple measurement names. + let lp = String::from( + "readings,readings2,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000", + ); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_no_timestamp() -> Result<(), String> { + // Tests parsing a single invalid line without a timestamp. + let lp = String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_non_unix_timestamp() -> Result<(), String> { + // Tests parsing a single invalid line with a non-unix timestamp. + let lp = + String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 2020-01-01T00:00:00Z"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_timestamp_with_quotes() -> Result<(), String> { + // Tests parsing a single invalid line with the timestamp in double quotes. + let lp = String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 \"1577836800000\""); + + let output_metrics = parse_line_protocol(&lp); + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_no_whitespace() -> Result<(), String> { + // Tests parsing a single invalid line with no whitespace between components. + let lp = String::from("readings,fuel_usage=21.30,2020-01-01T00:00:00Z"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_multiple_timestamps() -> Result<(), String> { + // Tests parsing a single invalid line with multiple timestamps. + let lp = String::from("readings incline=125i 1577836800000000000 1577836800000"); + + let output_metrics = parse_line_protocol(&lp); + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_batch() -> Result<(), String> { + // Tests parsing multiple valid lines with integer field values. + let lp = String::from( + "readings incline=125i 1577836800000 + readings incline=125i 1577836800000 + readings incline=125i 1577836800000 + readings incline=125i 1577836800000 + readings incline=125i 1577836800000 + readings incline=125i 1577836800000", + ); + + let expected_metric = Metric::new( + "readings".to_string(), + None, + vec![("incline".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_emojis() -> Result<(), String> { + // Tests parsing a single valid line with emojis included in the measurement name, tag key, + // tag value, field key, and field value. + let lp = String::from("re😎dings,fl☕️et=🤙 in🤠line=\"😀\" 1577836800000"); + + let expected_metric = Metric::new( + "re😎dings".to_string(), + Some(vec![("fl☕️et".to_string(), "🤙".to_string())]), + vec![( + "in🤠line".to_string(), + metric::FieldValue::String("😀".to_string()), + )], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_comma() -> Result<(), String> { + // Tests parsing a single valid line with escaped commas in the measurement name, tag key, + // tag value, and field key. + let lp = String::from(r"\,readings,fleet\,=A\,lberta inc\,line=125i 1577836800000"); + + let expected_metric = Metric::new( + ",readings".to_string(), + Some(vec![("fleet,".to_string(), "A,lberta".to_string())]), + vec![("inc,line".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_equals() -> Result<(), String> { + // Tests parsing a single valid line with escaped equals signs in the tag key, tag value, + // and field key. + let lp = String::from(r"readings,fleet\==A\=lberta inc\=line=125i 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet=".to_string(), "A=lberta".to_string())]), + vec![(r"inc=line".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_unescaped_equals_measurement() -> Result<(), String> { + // Tests parsing a single valid line with an unescaped equals sign in the measurement name. + let lp = String::from(r"rea=dings,fleet=Alberta incline=125i 1577836800000"); + + let expected_metric = Metric::new( + "rea=dings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![(r"incline".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_equals_measurement() -> Result<(), String> { + // Tests parsing a single valid line with an escaped equals sign in the measurement name. + let lp = String::from(r"rea\=dings,fleet=Alberta incline=125i 1577836800000"); + + let expected_metric = Metric::new( + "rea\\=dings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![(r"incline".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_space() -> Result<(), String> { + // Tests parsing a single valid line with an escaped space in the tag key, tag value, + // and field key. + let lp = String::from(r"readings,fleet\ =A\ lberta inc\ line=125i 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet ".to_string(), "A lberta".to_string())]), + vec![(r"inc line".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_measurement_escaped_space() -> Result<(), String> { + // Tests parsing a single valid line with an escaped space in measurement name. + let lp = String::from(r"read\ ings,fleet=Alberta incline=125i 1577836800000"); + + let expected_metric = Metric::new( + "read ings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![(r"incline".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_double_quote_field_value() -> Result<(), String> { + // Tests parsing a single valid line with escaped quotes in the field value. + let lp = String::from("readings,fleet=Alberta incline=\"\\\"test\\\"\" 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![( + r"incline".to_string(), + metric::FieldValue::String(String::from("\"test\"")), + )], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_non_escaped_double_quote_field_value() -> Result<(), String> { + // Tests parsing a single invalid line with unescaped quotes in the field value. + let lp = String::from("readings,fleet=Alberta incline=\"\"test\"\" 1577836800000"); + + let output_metrics = parse_line_protocol(&lp); + + assert!(output_metrics.is_err()); + Ok(()) +} + +#[test] +fn test_parse_escaped_backslash_field_value() -> Result<(), String> { + // Tests parsing a single valid line with escaped backslashes in the field value. + let lp = String::from("readings,fleet=Alberta incline=\"\\\\test\\\\\" 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![( + r"incline".to_string(), + metric::FieldValue::String(String::from("\\test\\")), + )], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_escaped_backslash_not_field_value() -> Result<(), String> { + // Tests parsing a single valid line with escaped backslashes in the measurement name, tag key, + // tag value, and field key. + let lp = String::from("read\\\\ings,fl\\\\eet=Al\\\\berta inc\\\\line=125i 1577836800000"); + + let expected_metric = Metric::new( + "read\\ings".to_string(), + Some(vec![("fl\\eet".to_string(), "Al\\berta".to_string())]), + vec![("inc\\line".to_string(), metric::FieldValue::I64(125))], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_single_point_single_comment() -> Result<(), String> { + // Tests parsing a single valid line with one comment included. + let lp = String::from( + "# This is a comment + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000", + ); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_single_point_multiple_comments() -> Result<(), String> { + // Tests parsing a single valid line with two comments included. + let lp = String::from( + "# This is a comment + # This is another comment + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000", + ); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + assert!(metrics_are_equal(&output_metrics[0], &expected_metric)); + Ok(()) +} + +#[test] +fn test_parse_multiple_points_single_comment() -> Result<(), String> { + // Tests parsing two valid lines with one comment included. + let lp = String::from( + "# This is a comment + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000 + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000", + ); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_multiple_points_multiple_comments() -> Result<(), String> { + // Tests parsing two valid lines with two comments included. + let lp = String::from( + "# This is a comment + # This is another comment + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000 + readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000", + ); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_nanoseconds_timestamp() -> Result<(), String> { + // Tests parsing one valid line with a nanosecond timestamp. + let lp = + String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000000000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000000000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_microseconds_timestamp() -> Result<(), String> { + // Tests parsing one valid line with a microsecond timestamp. + let lp = String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_milliseconds_timestamp() -> Result<(), String> { + // Tests parsing one valid line with a millisecond timestamp. + let lp = String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800000"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800000, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_seconds_timestamp() -> Result<(), String> { + // Tests parsing one valid line with a second timestamp. + let lp = String::from("readings,fleet=Alberta incline=125i,fuel_usage=21.30 1577836800"); + + let expected_metric = Metric::new( + "readings".to_string(), + Some(vec![("fleet".to_string(), "Alberta".to_string())]), + vec![ + ("incline".to_string(), metric::FieldValue::I64(125)), + ("fuel_usage".to_string(), metric::FieldValue::F64(21.30)), + ], + 1577836800, + ); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + for metric in output_metrics { + assert!(metrics_are_equal(&metric, &expected_metric)); + } + Ok(()) +} + +#[test] +fn test_parse_empty() -> Result<(), String> { + // Tests parsing empty line protocol. + let lp = String::new(); + + let output_metrics = parse_line_protocol(&lp).expect("Failed to parse line protocol"); + + // Should return an empty Vec. + assert!(output_metrics.is_empty()); + Ok(()) +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/main.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/main.rs new file mode 100644 index 00000000..aace484a --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/main.rs @@ -0,0 +1,16 @@ +use influxdb_timestream_connector::{ + lambda_handler, records_builder::validate_env_variables, timestream_utils::get_connection, +}; +use lambda_http::{run, service_fn, tracing, Error as lambda_error, Request}; + +#[tokio::main] +async fn main() -> Result<(), lambda_error> { + validate_env_variables()?; + let region = std::env::var("region")?; + let timestream_client = get_connection(®ion).await?; + tracing::init_default_subscriber(); + run(service_fn(|event: Request| { + lambda_handler(×tream_client, event) + })) + .await +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/metric.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/metric.rs new file mode 100644 index 00000000..a51f7c63 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/metric.rs @@ -0,0 +1,60 @@ +#[derive(Debug)] +pub struct Metric { + name: String, + tags: Option>, + fields: Vec<(String, FieldValue)>, + timestamp: i64, +} + +#[derive(Debug, PartialEq)] +pub enum FieldValue { + Boolean(bool), + I64(i64), + U64(u64), + F64(f64), + String(String), +} + +impl std::fmt::Display for FieldValue { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + FieldValue::Boolean(v) => v.fmt(f), + FieldValue::I64(v) => v.fmt(f), + FieldValue::U64(v) => v.fmt(f), + FieldValue::F64(v) => v.fmt(f), + FieldValue::String(v) => v.fmt(f), + } + } +} + +impl Metric { + pub fn new( + name: String, + tags: Option>, + fields: Vec<(String, FieldValue)>, + timestamp: i64, + ) -> Self { + Metric { + name, + tags, + fields, + timestamp, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn tags(&self) -> &Option> { + &self.tags + } + + pub fn fields(&self) -> &Vec<(String, FieldValue)> { + &self.fields + } + + pub fn timestamp(&self) -> i64 { + self.timestamp + } +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder.rs new file mode 100644 index 00000000..a257ff29 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder.rs @@ -0,0 +1,136 @@ +use crate::metric::Metric; +use anyhow::{anyhow, Error}; +use aws_sdk_timestreamwrite as timestream_write; +use std::collections::HashMap; + +mod multi_table_multi_measure_builder; + +pub enum SchemaType { + MultiTableMultiMeasure(String), +} + +impl std::fmt::Display for SchemaType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SchemaType::MultiTableMultiMeasure(v) => v.fmt(f), + } + } +} + +pub fn get_builder(schema: SchemaType) -> impl BuildRecords { + // Currently only supported schema is multi-table multi-measure + multi_table_multi_measure_builder::MultiTableMultiMeasureBuilder { + measure_name: schema.to_string(), + } +} + +pub fn build_records( + records_builder: &impl BuildRecords, + metrics: &[Metric], + precision: ×tream_write::types::TimeUnit, +) -> Result>, Error> { + records_builder.build_records(metrics, precision) +} + +pub struct TableConfig { + pub mag_store_retention_period: i64, + pub mem_store_retention_period: i64, + pub enable_mag_store_writes: bool, +} + +pub fn get_table_config() -> Result { + // Get the populated table_config struct + + Ok(TableConfig { + mag_store_retention_period: std::env::var("mag_store_retention_period")?.parse()?, + mem_store_retention_period: std::env::var("mem_store_retention_period")?.parse()?, + enable_mag_store_writes: matches!( + std::env::var("enable_mag_store_writes")? + .to_lowercase() + .as_str(), + "true" | "t" | "1" + ), + }) +} + +pub fn table_creation_enabled() -> Result { + // Convert the env var table_creation_enabled to bool + + match std::env::var("enable_table_creation") { + Ok(enabled) => Ok(env_var_to_bool(enabled)), + Err(_) => Err(anyhow!( + "enable_table_creation environment variable is not defined" + )), + } +} + +pub fn database_creation_enabled() -> Result { + // Convert the env var database_creation_enabled to bool + + match std::env::var("enable_database_creation") { + Ok(enabled) => Ok(env_var_to_bool(enabled)), + Err(_) => Err(anyhow!( + "enable_database_creation environment variable is not defined" + )), + } +} + +pub fn env_var_to_bool(env_var: String) -> bool { + // Convert the env var to bool + + matches!(env_var.as_str(), "true" | "t" | "1") +} + +pub fn validate_env_variables() -> Result<(), Error> { + // Validate environment variables for all schema types + + if std::env::var("region").is_err() { + return Err(anyhow!("region environment variable is not defined")); + } + if std::env::var("database_name").is_err() { + return Err(anyhow!("database_name environment variable is not defined")); + } + if std::env::var("enable_database_creation").is_err() { + return Err(anyhow!( + "enable_database_creation environment variable is not defined" + )); + } + let enable_table_creation = std::env::var("enable_table_creation"); + + if enable_table_creation.is_err() { + return Err(anyhow!( + "enable_table_creation environment variable is not defined" + )); + } + + if env_var_to_bool(enable_table_creation?) { + if std::env::var("enable_mag_store_writes").is_err() { + return Err(anyhow!( + "enable_mag_store_writes environment variable is not defined" + )); + } + if std::env::var("mag_store_retention_period").is_err() { + return Err(anyhow!( + "mag_store_retention_period environment variable is not defined" + )); + } + if std::env::var("mem_store_retention_period").is_err() { + return Err(anyhow!( + "mem_store_retention_period environment variable is not defined" + )); + } + } + + Ok(()) +} + +pub trait BuildRecords { + fn build_records( + &self, + metrics: &[Metric], + precision: ×tream_write::types::TimeUnit, + ) -> Result>, Error>; +} + +#[cfg(test)] +pub mod tests; diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/multi_table_multi_measure_builder.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/multi_table_multi_measure_builder.rs new file mode 100644 index 00000000..14062683 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/multi_table_multi_measure_builder.rs @@ -0,0 +1,114 @@ +use super::{validate_env_variables, BuildRecords}; +use crate::metric::{FieldValue, Metric}; +use anyhow::{anyhow, Error, Result}; +use aws_sdk_timestreamwrite as timestream_write; +use std::collections::HashMap; + +pub struct MultiTableMultiMeasureBuilder { + pub measure_name: String, +} + +impl BuildRecords for MultiTableMultiMeasureBuilder { + // trait implementation to support multi-measure multi-table schema with Timestream + + fn build_records( + &self, + metrics: &[Metric], + precision: ×tream_write::types::TimeUnit, + ) -> Result>, Error> { + validate_env_variables()?; + validate_multi_measure_env_variables()?; + build_multi_measure_records(metrics, &self.measure_name, precision) + } +} + +fn validate_multi_measure_env_variables() -> Result<(), Error> { + // Validate environment variables for multi-measure schema types + + if std::env::var("measure_name_for_multi_measure_records").is_err() { + return Err(anyhow!( + "measure_name_for_multi_measure_records environment variable is not defined" + )); + } + + Ok(()) +} + +fn build_multi_measure_records( + metrics: &[Metric], + measure_name: &str, + precision: ×tream_write::types::TimeUnit, +) -> Result>, Error> { + // Builds multi-measure multi-table records hashmap + + let mut multi_table_batch: HashMap> = + HashMap::new(); + for metric in metrics.iter() { + let new_record = metric_to_timestream_record(measure_name, metric, precision)?; + let table_name = metric.name(); + if let Some(record_vec) = multi_table_batch.get_mut(table_name) { + record_vec.push(new_record); + } else { + multi_table_batch.insert(table_name.to_string(), vec![new_record]); + } + } + + Ok(multi_table_batch) +} + +pub fn metric_to_timestream_record( + measure_name: &str, + metric: &Metric, + precision: ×tream_write::types::TimeUnit, +) -> Result { + // Converts the metric struct to a timestream multi-measure record + + let mut dimensions: Vec = Vec::new(); + for tag in metric.tags().iter().flatten() { + dimensions.push( + timestream_write::types::Dimension::builder() + .name(tag.0.to_owned()) + .value(tag.1.to_owned()) + .build() + .expect("Failed to build dimension"), + ) + } + + let mut measure_values: Vec = Vec::new(); + for field in metric.fields() { + let measure_type = get_timestream_measure_type(&field.1)?; + measure_values.push( + timestream_write::types::MeasureValue::builder() + .name(field.0.to_owned()) + .value(field.1.to_string()) + .r#type(measure_type) + .build() + .expect("Failed to build measure"), + ); + } + + let new_record = timestream_write::types::Record::builder() + .measure_name(measure_name) + .set_measure_values(Some(measure_values)) + .set_measure_value_type(Some(timestream_write::types::MeasureValueType::Multi)) + .set_time_unit(Some(precision.clone())) + .time(metric.timestamp().to_string()) + .set_dimensions(Some(dimensions)) + .build(); + + Ok(new_record) +} + +pub fn get_timestream_measure_type( + field_value: &FieldValue, +) -> Result { + // Converts a metric struct type to a timestream measure value type + + match field_value { + FieldValue::Boolean(_) => Ok(timestream_write::types::MeasureValueType::Boolean), + FieldValue::I64(_) => Ok(timestream_write::types::MeasureValueType::Bigint), + FieldValue::U64(_) => Ok(timestream_write::types::MeasureValueType::Bigint), + FieldValue::F64(_) => Ok(timestream_write::types::MeasureValueType::Double), + FieldValue::String(_) => Ok(timestream_write::types::MeasureValueType::Varchar), + } +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/tests.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/tests.rs new file mode 100644 index 00000000..9a9d0015 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/records_builder/tests.rs @@ -0,0 +1,391 @@ +use super::build_records; +use crate::metric::{FieldValue, Metric}; +use anyhow::Error; +use aws_sdk_timestreamwrite as timestream_write; +use std::env; + +#[test] +fn test_mtmm_single_record() -> Result<(), Error> { + // Single measure for multi-measure record + + setup_minimal_env_vars(); + setup_multi_measure_env_vars(); + let multi_table_multi_measure_schema = + super::SchemaType::MultiTableMultiMeasure(String::from("influxdb-connector-measure")); + let multi_table_multi_measure_builder = super::get_builder(multi_table_multi_measure_schema); + let metrics = [Metric::new( + "readings".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("incline"), FieldValue::I64(125))], + 1577836800000, + )]; + + let records = build_records( + &multi_table_multi_measure_builder, + &metrics, + ×tream_write::types::TimeUnit::Nanoseconds, + )?; + assert_eq!(records.len(), 1); + let first_record = records + .get("readings") + .expect("Failed to unwrap") + .first() + .expect("Failed to unwrap"); + assert_eq!(first_record.time, Some(String::from("1577836800000"))); + + assert_eq!( + first_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + first_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert!(first_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("125")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(first_record.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + + Ok(()) +} + +#[test] +fn test_mtmm_single_destination() -> Result<(), Error> { + // Dataset all going to same table + + setup_minimal_env_vars(); + setup_multi_measure_env_vars(); + let multi_table_multi_measure_schema = + super::SchemaType::MultiTableMultiMeasure(String::from("influxdb-connector-measure")); + let multi_table_multi_measure_builder = super::get_builder(multi_table_multi_measure_schema); + let metrics = [ + Metric::new( + "readings".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("incline"), FieldValue::I64(125))], + 1577836800000, + ), + Metric::new( + "readings".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("incline"), FieldValue::I64(150))], + 1577836900032, + ), + ]; + + let records = build_records( + &multi_table_multi_measure_builder, + &metrics, + ×tream_write::types::TimeUnit::Nanoseconds, + )?; + assert_eq!(records.len(), 1); + let readings = records.get("readings").expect("Failed to unwrap"); + let first_record = &readings[0]; + let second_record = &readings[1]; + assert_eq!(first_record.time, Some(String::from("1577836800000"))); + assert_eq!(second_record.time, Some(String::from("1577836900032"))); + + assert_eq!( + first_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + second_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + first_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert_eq!( + second_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert!(first_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("125")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(first_record.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + assert!(second_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("150")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(second_record.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + + Ok(()) +} + +#[test] +fn test_mtmm_multi_record() -> Result<(), Error> { + // Dataset going to multiple table destinations + + setup_minimal_env_vars(); + setup_multi_measure_env_vars(); + let multi_table_multi_measure_schema = + super::SchemaType::MultiTableMultiMeasure(String::from("influxdb-connector-measure")); + let multi_table_multi_measure_builder = super::get_builder(multi_table_multi_measure_schema); + let metrics = [ + Metric::new( + "readings".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("incline"), FieldValue::I64(125))], + 1577836800000, + ), + Metric::new( + "velocity".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("km/h"), FieldValue::F64(4.6))], + 1577836911132, + ), + ]; + + let records = build_records( + &multi_table_multi_measure_builder, + &metrics, + ×tream_write::types::TimeUnit::Nanoseconds, + )?; + assert_eq!(records.len(), 2); + let readings = records + .get("readings") + .expect("Failed to unwrap") + .first() + .expect("Failed to unwrap"); + let velocity = records + .get("velocity") + .expect("Failed to unwrap") + .first() + .expect("Failed to unwrap"); + assert_eq!(readings.time, Some(String::from("1577836800000"))); + assert_eq!(velocity.time, Some(String::from("1577836911132"))); + + assert_eq!(readings.measure_name(), Some("influxdb-connector-measure")); + assert_eq!(velocity.measure_name(), Some("influxdb-connector-measure")); + assert_eq!( + readings.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert_eq!( + velocity.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert!(readings.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("125")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(readings.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + assert!(velocity.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("km/h")) + .value(String::from("4.6")) + .r#type(timestream_write::types::MeasureValueType::Double) + .build() + .expect("Failed to build measure") + )); + assert!(velocity.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + + Ok(()) +} + +#[test] +fn test_mtmm_empty_dimensions() -> Result<(), Error> { + // Dataset with empty dimensions + + setup_minimal_env_vars(); + setup_multi_measure_env_vars(); + let multi_table_multi_measure_schema = + super::SchemaType::MultiTableMultiMeasure(String::from("influxdb-connector-measure")); + let multi_table_multi_measure_builder = super::get_builder(multi_table_multi_measure_schema); + let metrics = [Metric::new( + "readings".to_string(), + None, + vec![(String::from("incline"), FieldValue::I64(125))], + 1577836800000, + )]; + + let records = build_records( + &multi_table_multi_measure_builder, + &metrics, + ×tream_write::types::TimeUnit::Nanoseconds, + )?; + assert_eq!(records.len(), 1); + let readings = records.get("readings").expect("Failed to unwrap"); + let first_record = &readings[0]; + assert_eq!(first_record.time, Some(String::from("1577836800000"))); + + assert_eq!( + first_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + first_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert!(first_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("125")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(first_record.dimensions().is_empty()); + + Ok(()) +} + +#[test] +fn test_mtmm_varying_timestamp_records() -> Result<(), Error> { + // Varying timestamp parsing + + setup_minimal_env_vars(); + setup_multi_measure_env_vars(); + let multi_table_multi_measure_schema = + super::SchemaType::MultiTableMultiMeasure(String::from("influxdb-connector-measure")); + let multi_table_multi_measure_builder = super::get_builder(multi_table_multi_measure_schema); + let metrics = [ + Metric::new( + "readings".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("incline"), FieldValue::I64(125))], + 1577836866658, + ), + Metric::new( + "velocity".to_string(), + vec![(String::from("goal"), String::from("baseline"))].into(), + vec![(String::from("km/h"), FieldValue::F64(4.6))], + 1577836911132, + ), + ]; + + let records = build_records( + &multi_table_multi_measure_builder, + &metrics, + ×tream_write::types::TimeUnit::Nanoseconds, + )?; + assert_eq!(records.len(), 2); + + let first_record = records + .get("readings") + .expect("Failed to unwrap") + .first() + .expect("Failed to unwrap"); + + assert_eq!(first_record.time, Some(String::from("1577836866658"))); + assert_eq!( + first_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + first_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + assert!(first_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("incline")) + .value(String::from("125")) + .r#type(timestream_write::types::MeasureValueType::Bigint) + .build() + .expect("Failed to build measure") + )); + assert!(first_record.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + + let second_record = records + .get("velocity") + .expect("Failed to unwrap") + .first() + .expect("Failed to unwrap"); + + assert_eq!(second_record.time, Some(String::from("1577836911132"))); + assert_eq!( + second_record.measure_name(), + Some("influxdb-connector-measure") + ); + assert_eq!( + second_record.measure_value_type(), + Some(×tream_write::types::MeasureValueType::Multi) + ); + + assert!(second_record.measure_values().contains( + ×tream_write::types::MeasureValue::builder() + .name(String::from("km/h")) + .value(String::from("4.6")) + .r#type(timestream_write::types::MeasureValueType::Double) + .build() + .expect("Failed to build measure") + )); + + assert!(second_record.dimensions().contains( + ×tream_write::types::Dimension::builder() + .name(String::from("goal")) + .value(String::from("baseline")) + .build() + .expect("Failed to build dimension") + )); + + Ok(()) +} + +fn setup_multi_measure_env_vars() { + env::set_var("measure_name_for_multi_measure_records", "influxdb-measure"); +} + +fn setup_minimal_env_vars() { + env::set_var("enable_table_creation", "false"); + env::set_var("region", "us-west-2"); + env::set_var("database_name", "test-database"); + env::set_var("enable_database_creation", "false"); + env::set_var("enable_mag_store_writes", "false"); +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/src/timestream_utils.rs b/integrations/influxdb_connector/influxdb-timestream-connector/src/timestream_utils.rs new file mode 100644 index 00000000..63c810f3 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/src/timestream_utils.rs @@ -0,0 +1,165 @@ +use super::records_builder::TableConfig; +use anyhow::{anyhow, Error, Result}; +use aws_sdk_timestreamwrite as timestream_write; +use aws_types::region::Region; +use std::io::Write; + +pub async fn get_connection( + region: &str, +) -> Result { + // Get a connection to Timestream + + let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .region(Region::new(region.to_owned())) + .load() + .await; + let (client, reload) = timestream_write::Client::new(&config) + .with_endpoint_discovery_enabled() + .await + .expect("Failed to get the write client connection with Timestream"); + + tokio::task::spawn(reload.reload_task()); + println!("Initialized connection to Timestream in region {}", region); + Ok(client) +} + +pub async fn create_database( + client: ×tream_write::Client, + database_name: &str, +) -> Result<(), timestream_write::Error> { + // Create a new Timestream database + + println!("Creating new database {}", database_name); + client + .create_database() + .set_database_name(Some(database_name.to_owned())) + .send() + .await?; + + Ok(()) +} + +pub async fn create_table( + client: ×tream_write::Client, + database_name: &str, + table_name: &str, + table_config: TableConfig, +) -> Result<(), timestream_write::Error> { + // Create a new Timestream table + + println!( + "Creating new table {} for database {}", + table_name, database_name + ); + let retention_properties = timestream_write::types::RetentionProperties::builder() + .set_magnetic_store_retention_period_in_days(Some(table_config.mag_store_retention_period)) + .set_memory_store_retention_period_in_hours(Some(table_config.mem_store_retention_period)) + .build()?; + + let magnetic_store_properties = + timestream_write::types::MagneticStoreWriteProperties::builder() + .set_enable_magnetic_store_writes(Some(table_config.enable_mag_store_writes)) + .build()?; + + client + .create_table() + .set_table_name(Some(table_name.to_owned())) + .set_database_name(Some(database_name.to_owned())) + .set_retention_properties(Some(retention_properties)) + .set_magnetic_store_write_properties(Some(magnetic_store_properties)) + .send() + .await?; + + Ok(()) +} + +pub async fn table_exists( + client: ×tream_write::Client, + database_name: &str, + table_name: &str, +) -> Result { + // Check if table already exists + + match client + .describe_table() + .table_name(table_name) + .database_name(database_name) + .send() + .await + { + Ok(_) => Ok(true), + Err(error) => match error + .as_service_error() + .map(|e| e.is_resource_not_found_exception()) + { + Some(true) => Ok(false), + _ => Err(anyhow!(error)), + }, + } +} + +pub async fn database_exists( + client: ×tream_write::Client, + database_name: &str, +) -> Result { + // Check if database already exists + + match client + .describe_database() + .database_name(database_name) + .send() + .await + { + Ok(_) => Ok(true), + Err(error) => match error + .as_service_error() + .map(|e| e.is_resource_not_found_exception()) + { + Some(true) => Ok(false), + _ => Err(anyhow!(error)), + }, + } +} + +pub async fn ingest_records( + client: ×tream_write::Client, + database_name: &str, + table_name: &str, + records: &mut [timestream_write::types::Record], +) -> Result<(), Error> { + // Ingest records to Timestream in batches of 100 (Max supported Timestream batch size) + + let mut records_ingested: usize = 0; + const MAX_TIMESTREAM_BATCH_SIZE: usize = 100; + + let mut records_chunked: Vec> = records + .chunks(MAX_TIMESTREAM_BATCH_SIZE) + .map(|sub_records| sub_records.to_vec()) + .collect(); + for chunk in records_chunked.iter_mut() { + records_ingested += chunk.len(); + match client + .write_records() + .database_name(database_name) + .table_name(table_name) + .set_records(Some(std::mem::take(chunk))) + .send() + .await + { + Ok(_) => { + println!("{} records ingested", records_ingested); + std::io::stdout().flush()?; + } + Err(error) => { + println!("SdkError: {:?}", error.raw_response().unwrap()); + return Err(anyhow!(error)); + } + } + } + println!( + "{} records ingested total for table {} in database {}", + records_ingested, table_name, database_name + ); + + Ok(()) +} diff --git a/integrations/influxdb_connector/influxdb-timestream-connector/tests/integration_test.rs b/integrations/influxdb_connector/influxdb-timestream-connector/tests/integration_test.rs new file mode 100644 index 00000000..656e1390 --- /dev/null +++ b/integrations/influxdb_connector/influxdb-timestream-connector/tests/integration_test.rs @@ -0,0 +1,1171 @@ +use anyhow::Error; +use aws_credential_types::Credentials; +use aws_sdk_timestreamwrite as timestream_write; +use aws_types::region::Region; +use lambda_http::http::StatusCode; +use lambda_http::{http::Request, IntoResponse}; +use lambda_http::{Body, RequestExt}; +use rand::{distributions::uniform::SampleUniform, distributions::Alphanumeric, Rng}; +use std::collections::HashMap; +use std::{env, thread, time}; + +static DATABASE_NAME: &str = "influxdb_timestream_connector_integ_db"; +static REGION: &str = "us-west-2"; +static MAX_TIMESTREAM_TABLE_NAME_LENGTH: usize = 256; + +// A batch of resources to be deleted at the end of a test. +struct CleanupBatch { + client: timestream_write::Client, + database_name: String, + table_names_to_delete: Vec, +} + +impl CleanupBatch { + pub fn new( + client: timestream_write::Client, + database_name: String, + table_names_to_delete: Vec, + ) -> CleanupBatch { + CleanupBatch { + client, + database_name, + table_names_to_delete, + } + } + + async fn cleanup(&mut self) { + for table_name_to_delete in self.table_names_to_delete.iter() { + println!( + "Deleting table {} in database {}", + table_name_to_delete, self.database_name + ); + thread::sleep(time::Duration::from_secs( + influxdb_timestream_connector::TIMESTREAM_API_WAIT_SECONDS, + )); + let result = self + .client + .delete_table() + .database_name(&self.database_name) + .table_name(table_name_to_delete) + .send() + .await; + match result { + Ok(_) => (), + + Err(error) => { + println!( + "Table deletion failed for table {}: {:?}", + table_name_to_delete, + error.raw_response() + ); + } + } + } + } +} + +fn set_environment_variables() { + env::set_var("database_name", DATABASE_NAME); + env::set_var("enable_database_creation", "true"); + env::set_var("enable_table_creation", "true"); + env::set_var("enable_mag_store_writes", "true"); + // A value of 30,000 allows single-digit timestamps to be ingested. + env::set_var("mag_store_retention_period", "30000"); + env::set_var("mem_store_retention_period", "12"); + env::set_var("region", REGION); + env::set_var( + "measure_name_for_multi_measure_records", + "test_measure_name", + ); +} + +fn random_string(n: usize) -> String { + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(n) + .map(char::from) + .collect() +} + +fn random_number(low: T, high: T) -> T { + rand::thread_rng().gen_range(low, high) +} + +// These integration tests use the InfluxDB Timestream connector as a library +// instead of deploying the connector as a lambda and then making +// requests to the connector. Each test builds a lambda_http::http::Request and +// passes it to the connector's lambda_handler function. This means integration +// testing has minimal overhead. + +#[tokio::test] +async fn test_mtmm_basic() -> Result<(), Error> { + // Tests ingesting a single point. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_multiple_timestamps() -> Result<(), Error> { + // Tests ingesting a single point with two timestamps. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {} {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis(), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_many_tags_many_fields() -> Result<(), Error> { + // Tests ingesting a single point with 50 tags and 50 fields. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let mut point = format!("{},", lp_measurement_name); + + for i in 0..50 { + point.push_str(&format!("tag{}={}", i, random_string(9))); + if i != 49 { + point.push(','); + } + } + point.push(' '); + for i in 0..50 { + point.push_str(&format!("field{}={}i", i, random_number(0, 100001))); + if i != 49 { + point.push(','); + } + } + point.push_str(&format!(" {}\n", chrono::Utc::now().timestamp_millis())); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_float() -> Result<(), Error> { + // Tests ingesting a single point with a float value for the field. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={} {}\n", + lp_measurement_name, + random_string(9), + random_number(0.0, 100001.0), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_string() -> Result<(), Error> { + // Tests ingesting a single point with a string value for the field. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1=\"{}\" {}\n", + lp_measurement_name, + random_string(9), + random_string(9), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_bool() -> Result<(), Error> { + // Tests ingesting a single point with a bool value for the field. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={} {}\n", + lp_measurement_name, + random_string(9), + true, + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_max_tag_length() -> Result<(), Error> { + // Tests ingesting a single point with a tag where the tag's key + // is 60, the maximum allowed dimension name length, and its value + // is 1988 characters long. The length of the tag key and tag value + // together amount to the maximum size for a dimension pair, 2 kilobytes. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},{}={} field1={}i {}\n", + lp_measurement_name, + random_string(60), + random_string(1988), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_beyond_max_tag_length() -> Result<(), Error> { + // Tests ingesting a single point with a tag where the tag's key + // is 60, the maximum allowed dimension name length, and its value + // is 1989 characters long. The length of the tag key and tag value + // together exceed the maximum size for a dimension pair, 2 kilobytes. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},{}={} field1={}i {}\n", + lp_measurement_name, + random_string(60), + random_string(1989), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_max_field_length() -> Result<(), Error> { + // Tests ingesting a single point with a field where the length + // of the field key is the maximum measure name, 256, and the length + // of the field value is the maximum measure value size, 2048. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},field1={} {}=\"{}\" {}\n", + lp_measurement_name, + random_string(9), + random_string(256), + random_string(2048), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_beyond_max_field_length() -> Result<(), Error> { + // Tests ingesting a single point with a field where the length + // of the field key is the maximum measure name, 256, and the length + // of the field value is beyond the maximum measure value size, 2048. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},field1={} {}=\"{}\" {}\n", + lp_measurement_name, + random_string(9), + random_string(256), + random_string(2049), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_max_unique_field_keys() -> Result<(), Error> { + // Tests ingesting a batch of points where the number of unique field keys + // in the batch equals the maximum number of unique measures for a single + // table, 1024. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let mut lp_batch = String::new(); + for i in 0..1024 { + let point = format!( + "{},tag1={} field{}={}i {}\n", + lp_measurement_name, + random_string(9), + i, + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_beyond_max_unique_field_keys() -> Result<(), Error> { + // Tests ingesting a batch of points where the number of unique field keys + // in the batch exceeds the maximum number of unique measures for a single + // table, 1024. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let mut lp_batch = String::new(); + for i in 0..1025 { + let point = format!( + "{},tag1={} field{}={}i {}\n", + lp_measurement_name, + random_string(9), + i, + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_max_unique_tag_keys() -> Result<(), Error> { + // Tests ingesting a batch of points where the number of unique tag keys + // in the batch equals the maximum number of unique dimensions for a single + // table, 128. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let mut lp_batch = String::new(); + for i in 0..128 { + let point = format!( + "{},tag{}={} field1={}i {}\n", + lp_measurement_name, + i, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_beyond_max_unique_tag_keys() -> Result<(), Error> { + // Tests ingesting a batch of points where the number of unique tag keys + // in the batch exceeds the maximum number of unique dimensions for a single + // table, 128. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let mut lp_batch = String::new(); + for i in 0..129 { + let point = format!( + "{},tag{}={} field1={}i {}\n", + lp_measurement_name, + i, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_max_table_name_length() -> Result<(), Error> { + // Tests ingesting a single point with measurement with length + // equal to the maximum number of bytes a Timestream table name can + // have. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = random_string(MAX_TIMESTREAM_TABLE_NAME_LENGTH); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_beyond_max_table_name_length() { + // Tests ingesting a single point with measurement with length + // exceeding the maximum number of bytes a Timestream table name can + // have. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = random_string(MAX_TIMESTREAM_TABLE_NAME_LENGTH + 1); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point)) + .expect("Failed to create request") + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await + .expect("Failed to send request to lambda handler") + .into_response() + .await; + + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() != StatusCode::OK); +} + +#[tokio::test] +async fn test_mtmm_nanosecond_precision() -> Result<(), Error> { + // Tests ingesting a single point with nanosecond precision. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now() + .timestamp_nanos_opt() + .expect("Failed to create nanosecond timestamp") + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ns".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_microsecond_precision() -> Result<(), Error> { + // Tests ingesting a single point with microsecond precision. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_micros() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "us".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_second_precision() -> Result<(), Error> { + // Tests ingesting a single point with second precision. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "s".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_no_precision() -> Result<(), Error> { + // Tests ingesting a single point without precision supplied. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + // Without a precision provided, the connector should assume the precision + // is nanoseconds. + chrono::offset::Utc::now() + .timestamp_nanos_opt() + .expect("Failed to create nanosecond timestamp") + ); + + let request = Request::builder() + .method("POST") + .body(Body::Text(point))?; + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_empty_point() -> Result<(), Error> { + // Tests ingesting an empty string. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let point = String::new(); + + let request = Request::builder() + .method("POST") + .body(Body::Text(point))?; + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + Ok(()) +} + +#[tokio::test] +pub async fn test_mtmm_small_timestamp() -> Result<(), Error> { + // Tests ingesting with a single-digit millisecond timestamp. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + 3 + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_5_measurements() -> Result<(), Error> { + // Tests ingesting a batch with five measurements. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let mut table_names_to_delete = Vec::::new(); + let lp_measurement_name = String::from("readings"); + + let mut lp_batch = String::new(); + for i in 0..5 { + let lp_measurement_name = format!("{lp_measurement_name}{i}").to_string(); + table_names_to_delete.push(lp_measurement_name.clone()); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), table_names_to_delete); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_100_measurements() -> Result<(), Error> { + // Tests ingesting a batch with 100 measurements. + set_environment_variables(); + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let mut table_names_to_delete = Vec::::new(); + + let lp_measurement_name = String::from("readings"); + let mut lp_batch = String::new(); + for i in 0..100 { + let lp_measurement_name = format!("{lp_measurement_name}{i}").to_string(); + table_names_to_delete.push(lp_measurement_name.clone()); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), table_names_to_delete); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +async fn test_mtmm_5000_batch() -> Result<(), Error> { + // Tests ingesting a batch of 5000 points with a single measurement. + // 5000 is the recommended batch size for InfluxDB v2 OSS. + set_environment_variables(); + // Cleanup + let client = influxdb_timestream_connector::timestream_utils::get_connection(REGION) + .await + .expect("Failed to get client"); + + let lp_measurement_name = String::from("readings"); + let mut lp_batch = String::new(); + + for _ in 0..5000 { + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + lp_batch.push_str(&point); + } + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(lp_batch))? + .with_query_string_parameters(query_parameters); + + let response = influxdb_timestream_connector::lambda_handler(&client, request) + .await? + .into_response() + .await; + println!("Response: {}: {:?}", response.status(), response.body()); + assert!(response.status() == StatusCode::OK); + + let mut cleanup_batch = + CleanupBatch::new(client, DATABASE_NAME.to_string(), vec![lp_measurement_name]); + cleanup_batch.cleanup().await; + + Ok(()) +} + +#[tokio::test] +#[should_panic] +async fn test_mtmm_no_credentials() { + // Tests ingesting without AWS credentials. This test should panic. + set_environment_variables(); + + let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .credentials_provider(Credentials::new("", "", None, None, "test")) + .region(Region::new(REGION)) + .load() + .await; + + let (client, reload) = timestream_write::Client::new(&config) + .with_endpoint_discovery_enabled() + .await + .expect("Failed to get the write client connection with Timestream"); + tokio::task::spawn(reload.reload_task()); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point)) + .expect("Failed to created request") + .with_query_string_parameters(query_parameters); + + let _response = influxdb_timestream_connector::lambda_handler(&client, request).await; +} + +#[tokio::test] +#[should_panic] +async fn test_mtmm_incorrect_credentials() { + // Tests ingesting with incorrect AWS credentials. This test should panic. + set_environment_variables(); + + let config = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .credentials_provider(Credentials::new( + "ANOTREAL", + "notrealrnrELgWzOk3IfjzDKtFBhDby", + None, + None, + "test", + )) + .region(Region::new(REGION)) + .load() + .await; + + let (client, reload) = timestream_write::Client::new(&config) + .with_endpoint_discovery_enabled() + .await + .expect("Failed to get the write client connection with Timestream"); + tokio::task::spawn(reload.reload_task()); + + let lp_measurement_name = String::from("readings"); + + let point = format!( + "{},tag1={} field1={}i {}\n", + lp_measurement_name, + random_string(9), + random_number(0, 100001), + chrono::offset::Utc::now().timestamp_millis() + ); + + let query_parameters = HashMap::from([("precision".to_string(), "ms".to_string())]); + let request = Request::builder() + .body(Body::Text(point)) + .expect("Failed to created request") + .with_query_string_parameters(query_parameters); + + let _response = influxdb_timestream_connector::lambda_handler(&client, request).await; +} diff --git a/integrations/influxdb_connector/sample-influxdb-clients/README.md b/integrations/influxdb_connector/sample-influxdb-clients/README.md new file mode 100644 index 00000000..00cdd726 --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/README.md @@ -0,0 +1,7 @@ +# Amazon sample InfluxDB client applications + +## Getting started + +Below is a list of sample applications to use with the InfluxDB Timestream Connector. The sample applications are implementations of the [InfluxDB client libraries](https://docs.influxdata.com/influxdb/v1/tools/api_client_libraries/) that ingest a sample line protocol dataset and use SigV4 authentication for each request. Each sample is fully functional and can ingest data to Timestream for LiveAnalytics with the InfluxDB Timestream Connector running locally, or deployed as a Lambda function with a REST API Gateway. + +* [Go](https://github.com/awslabs/amazon-timestream-tools/blob/mainline/integrations/influxdb_connector/sample-influxdb-clients/go) diff --git a/integrations/influxdb_connector/sample-influxdb-clients/data/bird-migration.line b/integrations/influxdb_connector/sample-influxdb-clients/data/bird-migration.line new file mode 100644 index 00000000..480e77ee --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/data/bird-migration.line @@ -0,0 +1,2002 @@ +migration1,id=91752A,s2_cell_id=164b35c lat=8.3495,lon=39.01233 1554123600000000000 +migration1,id=91752A,s2_cell_id=164b3dc lat=8.56067,lon=39.08883 1554102000000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.86233,lon=38.81167 1547557200000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.883,lon=38.7975 1553065200000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.86233,lon=38.81217 1553670000000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.862,lon=38.81233 1553929200000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.86217,lon=38.81217 1554382800000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.86183,lon=38.81233 1554706800000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.86233,lon=38.81217 1554728400000000000 +migration1,id=91752A,s2_cell_id=17b4854 lat=7.8675,lon=38.819 1554782400000000000 +migration1,id=91752A,s2_cell_id=17b48fc lat=7.87067,lon=38.82233 1554750000000000000 +migration1,id=91752A,s2_cell_id=17b4944 lat=7.97617,lon=38.91567 1553778000000000000 +migration1,id=91752A,s2_cell_id=17b4944 lat=7.97617,lon=38.91567 1553842800000000000 +migration1,id=91752A,s2_cell_id=17b4944 lat=7.97967,lon=38.92467 1554274800000000000 +migration1,id=91752A,s2_cell_id=17b495c lat=7.99767,lon=38.93017 1558530000000000000 +migration1,id=91752A,s2_cell_id=17b495c lat=8.0015,lon=38.91317 1559890800000000000 +migration1,id=91752A,s2_cell_id=17b4964 lat=8.01367,lon=38.88867 1550818800000000000 +migration1,id=91752A,s2_cell_id=17b4964 lat=8.00583,lon=38.88833 1553864400000000000 +migration1,id=91752A,s2_cell_id=17b4964 lat=7.9915,lon=38.86633 1554955200000000000 +migration1,id=91752A,s2_cell_id=17b4964 lat=8.0065,lon=38.87583 1559113200000000000 +migration1,id=91752A,s2_cell_id=17b4974 lat=7.9695,lon=38.8615 1553918400000000000 +migration1,id=91752A,s2_cell_id=17b497c lat=8.002,lon=38.83383 1553540400000000000 +migration1,id=91752A,s2_cell_id=17b497c lat=8.00467,lon=38.82667 1553659200000000000 +migration1,id=91752A,s2_cell_id=17b497c lat=7.99117,lon=38.85767 1559912400000000000 +migration1,id=91752A,s2_cell_id=17b4984 lat=8.00467,lon=38.779 1553713200000000000 +migration1,id=91752A,s2_cell_id=17b4984 lat=8.00583,lon=38.78117 1560744000000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.00883,lon=38.75417 1553691600000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.0115,lon=38.7555 1555138800000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.03267,lon=38.75467 1555646400000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.02833,lon=38.755 1555678800000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.0285,lon=38.75767 1558152000000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=7.99967,lon=38.75317 1560344400000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.01383,lon=38.76267 1561262400000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.03117,lon=38.76433 1568260800000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.00483,lon=38.74467 1570345200000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.03233,lon=38.76233 1572667200000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.01033,lon=38.75083 1573995600000000000 +migration2,id=91752A,s2_cell_id=17b498c lat=8.01617,lon=38.77833 1575291600000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.98467,lon=38.7635 1556866800000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.97217,lon=38.77233 1556953200000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.985,lon=38.75 1557126000000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.98267,lon=38.76433 1557385200000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.9825,lon=38.757 1560754800000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.99067,lon=38.75317 1561964400000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.9905,lon=38.75133 1562677200000000000 +migration2,id=91752A,s2_cell_id=17b4994 lat=7.96817,lon=38.7715 1576587600000000000 +migration2,id=91752A,s2_cell_id=17b49a4 lat=7.94583,lon=38.79917 1553626800000000000 +migration2,id=91752A,s2_cell_id=17b49ac lat=7.8905,lon=38.81433 1554836400000000000 +migration2,id=91752A,s2_cell_id=17b49ac lat=7.90283,lon=38.81817 1555009200000000000 +migration2,id=91752A,s2_cell_id=17b49b4 lat=7.90683,lon=38.753 1554966000000000000 +migration2,id=91752A,s2_cell_id=17b49b4 lat=7.91617,lon=38.772 1555041600000000000 +migration2,id=91752A,s2_cell_id=17b49bc lat=7.94683,lon=38.7705 1560322800000000000 +migration2,id=91752A,s2_cell_id=17b49bc lat=7.95533,lon=38.745 1577106000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94433,lon=38.72983 1554210000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94217,lon=38.73033 1554404400000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9525,lon=38.73433 1554793200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9475,lon=38.72767 1555398000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9475,lon=38.72767 1556197200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94633,lon=38.727 1556974800000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94183,lon=38.73033 1559998800000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1560495600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1560517200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94433,lon=38.72983 1560582000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1561035600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94233,lon=38.73067 1561057200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1561208400000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94233,lon=38.73067 1561230000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94417,lon=38.72967 1561294800000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1561381200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94283,lon=38.731 1561402800000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94217,lon=38.7305 1561489200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94283,lon=38.731 1561748400000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94283,lon=38.731 1561780800000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94217,lon=38.7305 1561921200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.944,lon=38.72933 1561953600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.942,lon=38.73033 1562007600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1562569200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94283,lon=38.731 1562742000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9475,lon=38.72767 1562763600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94183,lon=38.73033 1562785200000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.94433,lon=38.72983 1562817600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.942,lon=38.7305 1562871600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9445,lon=38.73 1562904000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.9475,lon=38.72767 1568271600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.95,lon=38.73167 1574082000000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.93067,lon=38.73567 1574859600000000000 +migration2,id=91752A,s2_cell_id=17b49c4 lat=7.93633,lon=38.72867 1575378000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04433,lon=38.766 1546326000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.06183,lon=38.77617 1555830000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05933,lon=38.775 1556856000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04633,lon=38.77667 1560571200000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05417,lon=38.7735 1562828400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05317,lon=38.77383 1562914800000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05567,lon=38.77317 1562936400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.0545,lon=38.77333 1564113600000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05817,lon=38.77767 1564718400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04917,lon=38.77867 1565323200000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.03933,lon=38.76617 1565841600000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.06783,lon=38.774 1566360000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04667,lon=38.76783 1566457200000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.0515,lon=38.77333 1567569600000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.044,lon=38.77017 1571490000000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.05183,lon=38.77833 1571684400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.0365,lon=38.76083 1572332400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04183,lon=38.7615 1572872400000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04683,lon=38.77583 1574319600000000000 +migration2,id=91752A,s2_cell_id=17b4a24 lat=8.04133,lon=38.76533 1575118800000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.05767,lon=38.78033 1555052400000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.04267,lon=38.80733 1556607600000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.05183,lon=38.78083 1560312000000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.06633,lon=38.78367 1560484800000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.03233,lon=38.7885 1560841200000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.0635,lon=38.7885 1562385600000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.06417,lon=38.80233 1563076800000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.0615,lon=38.78733 1567742400000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.062,lon=38.7795 1568952000000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.0425,lon=38.78117 1572505200000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.0635,lon=38.806 1576825200000000000 +migration2,id=91752A,s2_cell_id=17b4a2c lat=8.03883,lon=38.817 1577170800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09433,lon=38.80567 1548442800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07617,lon=38.8145 1548907200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07383,lon=38.7805 1550516400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09167,lon=38.81417 1551877200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.072,lon=38.77883 1552222800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.073,lon=38.7805 1552330800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09383,lon=38.79083 1552395600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.089,lon=38.81917 1552482000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.091,lon=38.7925 1554177600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09483,lon=38.80667 1556942400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08367,lon=38.79933 1558076400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.06867,lon=38.77967 1558119600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09833,lon=38.79667 1558324800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07,lon=38.7895 1559275200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07633,lon=38.79383 1559372400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0695,lon=38.78633 1560085200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07633,lon=38.8035 1561348800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07333,lon=38.79217 1562050800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07917,lon=38.79367 1562212800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07933,lon=38.79367 1562731200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.074,lon=38.80033 1563163200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09617,lon=38.80783 1563422400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.086,lon=38.79283 1563508800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07167,lon=38.79583 1563595200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.072,lon=38.81983 1563606000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0715,lon=38.7975 1563768000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0845,lon=38.79267 1563854400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.072,lon=38.781 1563951600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09733,lon=38.805 1564027200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0755,lon=38.7925 1564200000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09517,lon=38.79433 1564470000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09283,lon=38.81083 1564815600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07483,lon=38.808 1564891200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09883,lon=38.80583 1564977600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09833,lon=38.81 1565150400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.06867,lon=38.80583 1565236800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0945,lon=38.813 1565409600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0915,lon=38.80283 1565582400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09967,lon=38.80067 1565755200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09767,lon=38.805 1566014400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07067,lon=38.78417 1566100800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09333,lon=38.81483 1566187200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09083,lon=38.82033 1566975600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08233,lon=38.8085 1567656000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.098,lon=38.79333 1568206800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.10017,lon=38.79867 1568520000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08167,lon=38.786 1569049200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0745,lon=38.77917 1570334400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09183,lon=38.81267 1571641200000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80867 1571716800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80867 1571770800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0915,lon=38.80567 1571900400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08467,lon=38.78783 1571976000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.093,lon=38.81333 1572408000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0895,lon=38.79667 1572440400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07817,lon=38.80333 1572580800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09617,lon=38.80117 1572753600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07817,lon=38.8 1572840000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.09483,lon=38.80933 1573012800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08183,lon=38.80767 1573239600000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80883 1573498800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.0815,lon=38.81583 1573736400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80867 1573758000000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08133,lon=38.791 1573822800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80867 1573844400000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.08867,lon=38.80967 1573930800000000000 +migration2,id=91752A,s2_cell_id=17b4a34 lat=8.07717,lon=38.81817 1574168400000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07067,lon=38.77717 1550343600000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07483,lon=38.77633 1552546800000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07483,lon=38.77633 1552935600000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07383,lon=38.77783 1553086800000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07167,lon=38.77017 1555819200000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07117,lon=38.775 1556078400000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.072,lon=38.77183 1560150000000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.06783,lon=38.77567 1562126400000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.076,lon=38.777 1564632000000000000 +migration2,id=91752A,s2_cell_id=17b4a3c lat=8.07,lon=38.777 1568088000000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.11233,lon=38.80733 1555992000000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.102,lon=38.80283 1556002800000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.11183,lon=38.807 1556089200000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.11183,lon=38.807 1556596800000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.103,lon=38.80467 1564059600000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.103,lon=38.7975 1566964800000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.1005,lon=38.8005 1571403600000000000 +migration2,id=91752A,s2_cell_id=17b4a4c lat=8.10183,lon=38.7955 1572494400000000000 +migration3,id=91752A,s2_cell_id=17b4a4c lat=8.10283,lon=38.79933 1573218000000000000 +migration3,id=91752A,s2_cell_id=17b4b74 lat=8.1615,lon=38.905 1554091200000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.08583,lon=38.939 1553227200000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.087,lon=38.938 1553238000000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.08767,lon=38.943 1553583600000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.08983,lon=38.93283 1554015600000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.10483,lon=38.92283 1555214400000000000 +migration3,id=91752A,s2_cell_id=17b4b94 lat=8.0895,lon=38.90833 1556175600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86583 1546315200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.068,lon=38.88583 1546498800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86517 1546542000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86567 1546574400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86583 1546585200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1546660800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1546671600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86583 1546747200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.057,lon=38.87433 1546758000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1546833600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06,lon=38.8665 1546844400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.866 1546920000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86567 1546930800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86567 1546952400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86583 1547017200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547038800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.866 1547060400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86617 1547092800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06,lon=38.86683 1547103600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86567 1547125200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86583 1547179200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05983,lon=38.86617 1547190000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547211600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86583 1547265600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05983,lon=38.86633 1547298000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86567 1547352000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547362800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1547384400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547449200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547470800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86583 1547535600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.86617 1547611200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86533 1547643600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86517 1547697600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1547708400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.8665 1547730000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.86633 1547794800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86617 1547816400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86567 1547870400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86633 1547881200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05633,lon=38.8795 1547956800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06017,lon=38.8665 1547967600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86533 1547989200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.865 1548043200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.86667 1548054000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86483 1548075600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.865 1548162000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.865 1548313200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.865 1548334800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86533 1548388800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.865 1548421200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.865 1548475200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86483 1548507600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86633 1548561600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.8665 1548572400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1548594000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.86667 1548648000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86483 1548658800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.865 1548680400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1548734400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.86667 1548745200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86583 1548766800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.866 1548853200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1548918000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.865 1548939600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.8665 1548961200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.86667 1548993600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0675,lon=38.88617 1549026000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86583 1549090800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86583 1549198800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.86633 1549263600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86617 1549285200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1549350000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1549371600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.8665 1549436400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.866 1549458000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.866 1549512000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86633 1549544400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86583 1549598400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.8665 1549609200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1549630800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.8665 1549695600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.866 1549717200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.8665 1549771200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.86683 1549782000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.866 1549803600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86633 1549857600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.86617 1549868400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86517 1549890000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1549944000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.059,lon=38.866 1549954800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.866 1549976400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1550030400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1550062800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1550149200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86667 1550214000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86567 1550235600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86467 1550300400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1550322000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.86667 1550376000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86533 1550408400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0655,lon=38.88767 1550462400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1550473200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0575,lon=38.86783 1550548800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86483 1550559600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86567 1550581200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1550646000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1550667600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05783,lon=38.86667 1550721600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1550732400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86583 1550754000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1550840400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1550894400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1550905200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.865 1550926800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1550991600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86483 1551067200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86483 1551078000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1551153600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1551164400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1551186000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1551240000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1551250800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1551337200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1551358800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86517 1551326400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86667 1551412800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1551423600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.865 1551445200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1551510000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1551531600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86683 1551585600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1551618000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05933,lon=38.86667 1551672000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1551704400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0595,lon=38.867 1551758400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06,lon=38.867 1551769200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1551790800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05967,lon=38.86683 1551855600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86533 1551942000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1552028400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1552104000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86517 1552136400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05817,lon=38.8655 1552190400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.865 1552276800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86483 1552287600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86483 1552654800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06117,lon=38.8835 1552827600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1552914000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86467 1553000400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.8655 1553173200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86533 1553324400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05817,lon=38.8655 1553346000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1553432400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.8655 1553518800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1553605200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1553950800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06367,lon=38.86367 1554436800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06517,lon=38.864 1554534000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06633,lon=38.863 1554577200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0565,lon=38.89817 1554922800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86467 1555225200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86483 1555246800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.07,lon=38.89 1555311600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05817,lon=38.86533 1555333200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05867,lon=38.86517 1555506000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1555743600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1556110800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1556262000000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1556348400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.0585,lon=38.86517 1556434800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05833,lon=38.86417 1557061200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86483 1557633600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05917,lon=38.8875 1559804400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.07133,lon=38.8925 1560862800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06533,lon=38.89833 1561694400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.8625 1563022800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.86267 1563109200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05883,lon=38.868 1563249600000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06567,lon=38.89317 1563260400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.058,lon=38.863 1568444400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.075,lon=38.89217 1569589200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.05983,lon=38.8795 1569826800000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06367,lon=38.88233 1571198400000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.06133,lon=38.901 1575961200000000000 +migration3,id=91752A,s2_cell_id=17b4bc4 lat=8.061,lon=38.86817 1577797200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.859 1546347600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85633 1546369200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85883 1546401600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.85867 1546412400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85883 1546434000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85883 1546455600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85883 1546488000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.859 1546520400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85867 1546606800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85883 1546628400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.859 1546693200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85883 1546714800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85983 1546779600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85883 1546801200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.859 1546866000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85883 1546887600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85883 1546974000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08583,lon=38.8215 1547006400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.859 1547146800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85883 1547233200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06583,lon=38.85 1547276400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85917 1547319600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85383 1547578800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85583 1547838000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.859 1547902800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.857 1547924400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85633 1548010800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85583 1548183600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0685,lon=38.82867 1548248400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85617 1548529200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85867 1548615600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85867 1548702000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.85567 1548788400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.84717 1548820800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85867 1548874800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85867 1549047600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.83967 1549080000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85867 1549134000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85867 1549220400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85867 1549252800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85867 1549306800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85867 1549393200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.859 1549425600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.8585 1549479600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85867 1549566000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85867 1549652400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8585 1549738800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.8585 1549825200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.8585 1549911600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85517 1550084400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85483 1550170800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.84917 1550203200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85517 1550257200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.858 1550289600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85467 1550602800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.8545 1550775600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85417 1550862000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85833 1550948400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85833 1551034800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.8525 1551099600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8585 1551294000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8585 1551326400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.8585 1551380400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8585 1551466800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85817 1551553200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.065,lon=38.8585 1551639600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.8585 1551726000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.8585 1551812400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.8585 1551844800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.8585 1551898800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08033,lon=38.82433 1551963600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06533,lon=38.85833 1551985200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85867 1552017600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06717,lon=38.83383 1552050000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.8585 1552071600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.8585 1552158000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.85833 1552244400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.8585 1552417200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.85833 1552503600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.07083,lon=38.83267 1552568400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85833 1552762800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06517,lon=38.85817 1552795200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.855 1553022000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85633 1553108400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85867 1553400000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06517,lon=38.858 1553486400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1553497200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85817 1554188400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8585 1554264000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85867 1554350400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85867 1554361200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06517,lon=38.85867 1554447600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85883 1554469200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.85867 1554490800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06667,lon=38.86017 1554555600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.8585 1554642000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.8585 1554814800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06767,lon=38.82583 1554868800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.85867 1554901200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85417 1555074000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1555160400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85883 1555387200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85917 1555419600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85983 1555484400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1555560000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85883 1555570800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85833 1555592400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1555657200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85883 1555765200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1555851600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85883 1556024400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.8585 1556046000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85933 1556251200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85867 1556370000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1556456400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.07,lon=38.834 1556510400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0795,lon=38.82783 1556521200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08467,lon=38.8255 1556629200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1556715600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1556802000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1556888400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1556996400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1557028800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1557082800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.071,lon=38.83483 1557115200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85817 1557147600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.07917,lon=38.82733 1557201600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1557234000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85883 1557298800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.8615 1557320400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85883 1557374400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.86217 1557406800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.86217 1557471600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1557579600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.8615 1557644400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.86233 1557666000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1557687600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557720000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557730800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557752400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1557806400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1557817200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557892800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557903600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1557925200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1557990000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1558011600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1558065600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1558098000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1558162800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06767,lon=38.82867 1558184400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85783 1558238400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85583 1558249200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1558270800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1558335600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1558357200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.855 1558411200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85783 1558422000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1558443600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.858 1558497600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1558508400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1558584000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1558594800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85833 1558616400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.827 1558638000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1558670400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1558681200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85833 1558702800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08117,lon=38.82917 1558756800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1558767600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1558789200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1558843200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1558854000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.86183 1558875600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1558929600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1558940400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85783 1558962000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85783 1558983600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.082,lon=38.82883 1559016000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.07983,lon=38.823 1559026800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85817 1559048400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85783 1559102400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85833 1559134800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1559156400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0755,lon=38.8295 1559199600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1559221200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559286000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.858 1559307600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8585 1559361600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.858 1559394000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85867 1559415600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1559448000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559458800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559480400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.85817 1559502000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559545200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85833 1559588400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06567,lon=38.84333 1559620800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1559631600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85867 1559653200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85817 1559674800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1559718000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559739600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.858 1559761200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8585 1559793600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559826000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1559880000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1559977200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1560139200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1560225600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.858 1560236400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85817 1560258000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06683,lon=38.86083 1560398400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85833 1560603600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1560657600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.86133 1560690000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.8585 1560776400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85867 1560830400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85867 1560916800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.858 1560949200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.8585 1561014000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.8585 1561089600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.067,lon=38.86017 1561100400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.86183 1561359600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.09167,lon=38.82417 1561446000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.86183 1561467600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.86183 1561521600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.86217 1561618800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.862 1561640400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.862 1561791600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.86217 1561813200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.8615 1561834800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.83967 1561867200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05817,lon=38.862 1561878000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.86167 1561986000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.858 1562094000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05867,lon=38.862 1562137200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.86217 1562158800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.85867 1562180400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.862 1562223600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.86217 1562245200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.862 1562310000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.862 1562331600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.86217 1562396400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.862 1562418000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.858 1562439600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.86183 1562482800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0585,lon=38.86233 1562504400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05833,lon=38.862 1562590800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05817,lon=38.86167 1562644800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.8565 1562850000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85567 1563087600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85583 1563130800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85567 1563174000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85567 1563195600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8555 1563217200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.8555 1563282000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85567 1563303600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.8555 1563346800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.84433 1563368400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.8555 1563433200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.8555 1563454800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85583 1563476400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.8565 1563519600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85533 1563541200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85617 1563562800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.8555 1563627600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85583 1563649200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.8565 1563681600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.83683 1563692400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85567 1563714000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.8555 1563735600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.8555 1563778800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85467 1563800400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85533 1563822000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85483 1563865200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08483,lon=38.82483 1563886800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85567 1563908400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85533 1563940800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85617 1563973200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85533 1563994800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.84917 1564038000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85717 1564081200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.856 1564124400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85617 1564146000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8555 1564167600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85583 1564210800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.856 1564232400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.856 1564254000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85567 1564286400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.856 1564297200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06817,lon=38.83033 1564318800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85583 1564340400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06783,lon=38.8255 1564372800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85533 1564383600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.856 1564405200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85617 1564426800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08817,lon=38.82333 1564459200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.855 1564491600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85583 1564513200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.067,lon=38.8235 1564545600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.856 1564556400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85483 1564578000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85567 1564599600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85583 1564642800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.8555 1564664400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85833 1564686000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.8555 1564729200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.8575 1564750800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85833 1564772400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.856 1564804800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85567 1564837200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85817 1564858800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85733 1564902000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1564923600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85583 1564945200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85583 1564988400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565010000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85667 1565031600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85617 1565064000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.856 1565074800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85717 1565096400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85633 1565118000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1565161200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85667 1565182800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85633 1565204400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1565247600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.857 1565269200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85683 1565334000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.857 1565355600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.8565 1565377200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1565420400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1565442000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85617 1565463600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08617,lon=38.825 1565496000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85683 1565506800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1565528400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565550000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565593200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1565614800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85633 1565636400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06667,lon=38.84383 1565668800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85633 1565679600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85633 1565701200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565722800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85717 1565766000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1565787600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565809200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85633 1565852400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85633 1565874000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85717 1565895600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0855,lon=38.82267 1565928000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.8565 1565938800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8575 1565960400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85667 1565982000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1566025200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85883 1566046800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85717 1566068400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85733 1566111600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85883 1566133200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1566198000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85617 1566219600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.8565 1566241200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85633 1566273600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1566284400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85717 1566306000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85717 1566327600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85767 1566370800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8565 1566392400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85717 1566414000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1566446400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85667 1566478800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.8575 1566500400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85733 1566532800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85717 1566543600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.083,lon=38.82183 1566565200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.8575 1566586800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8515 1566619200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.8585 1566630000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85867 1566673200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.84517 1566705600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85633 1566716400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85633 1566738000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85717 1566759600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0755,lon=38.82983 1566792000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1566802800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85633 1566824400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.857 1566846000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.83117 1566878400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.8565 1566889200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85617 1566910800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85633 1566932400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85667 1566997200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85667 1567018800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0905,lon=38.822 1567051200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.8565 1567062000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85667 1567083600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.857 1567105200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08333,lon=38.82633 1567137600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1567148400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85617 1567170000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85667 1567191600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85667 1567224000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85717 1567234800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.8565 1567256400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85733 1567278000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06783,lon=38.83617 1567310400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.8565 1567321200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85683 1567342800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.8575 1567364400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85733 1567396800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.07467,lon=38.82917 1567407600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.858 1567450800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.08967,lon=38.82317 1567483200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.858 1567494000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.858 1567515600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85717 1567537200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.8575 1567580400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85717 1567602000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.8575 1567623600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85817 1567666800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1567688400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.858 1567710000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.858 1567753200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85817 1567774800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.858 1567796400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.848 1567828800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.858 1567839600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1567861200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.858 1567882800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85717 1567915200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85817 1567926000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85767 1567947600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85733 1567969200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85833 1568001600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85817 1568012400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.8585 1568034000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85717 1568055600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85867 1568098800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06517,lon=38.857 1568120400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85733 1568142000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85817 1568174400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1568185200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1568228400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85767 1568293200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85767 1568347200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8575 1568358000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85733 1568379600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1568401200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1568433600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85067 1568466000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85767 1568487600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.85767 1568552400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.857 1568574000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.8575 1568606400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85767 1568617200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85767 1568638800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85767 1568660400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85767 1568692800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.858 1568703600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85767 1568725200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85817 1568779200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85817 1568790000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85783 1568811600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1568833200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.85417 1568876400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85783 1568898000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85767 1568919600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.8575 1568962800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.8575 1568984400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85817 1569006000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.071,lon=38.82233 1569070800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85833 1569092400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85783 1569135600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0645,lon=38.8575 1569157200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85833 1569178800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1569211200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1569222000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.8575 1569243600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.858 1569265200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85767 1569297600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1569308400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.8575 1569330000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.8585 1569351600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85767 1569384000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8575 1569394800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1569416400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85767 1569438000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85783 1569470400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85817 1569481200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85767 1569502800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8585 1569524400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8575 1569556800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85233 1569567600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85767 1569610800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.8535 1569643200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85767 1569654000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.8475 1569675600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.858 1569697200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.85833 1569729600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85767 1569740400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.858 1569762000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85767 1569783600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85767 1569816000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.858 1569848400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85883 1569870000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85783 1569902400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.8575 1569913200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1569934800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85767 1569956400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.8465 1569988800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85767 1569999600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.85767 1570021200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1570042800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85833 1570075200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85733 1570086000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85783 1570107600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.858 1570129200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85783 1570161600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.85817 1570172400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85783 1570194000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8585 1570215600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.858 1570248000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.855 1570258800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.8575 1570280400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.858 1570302000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.858 1570366800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85783 1570388400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8575 1570420800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.065,lon=38.8575 1570453200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1570474800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06467,lon=38.85817 1570507200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85867 1570518000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85783 1570539600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85817 1570561200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.8585 1570593600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06483,lon=38.85767 1570604400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.858 1570626000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8585 1570647600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0625,lon=38.8585 1570680000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85767 1570690800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85767 1570712400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85833 1570734000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1570766400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85833 1570777200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85833 1570798800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85817 1570820400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8575 1570852800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.8585 1570863600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85817 1570906800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85783 1570939200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.8565 1570950000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.8575 1570971600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85817 1570993200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85767 1571025600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85767 1571036400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1571058000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1571079600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1571112000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1571166000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85833 1571230800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85817 1571252400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.074,lon=38.82767 1571284800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.858 1571295600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85817 1571317200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85817 1571338800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1571382000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85817 1571425200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1571468400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85833 1571511600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85767 1571544000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1571576400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85833 1571598000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.8575 1571662800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.8575 1571727600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1571749200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85817 1571803200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.858 1571835600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85817 1571857200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85733 1571889600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85767 1571922000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85817 1571943600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85767 1571986800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85783 1572008400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1572030000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1572062400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1572073200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1572094800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.8575 1572116400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1572181200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1572202800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.84983 1572235200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85767 1572246000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85767 1572267600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85783 1572289200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1572321600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1572354000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85783 1572375600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1572418800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.858 1572462000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.8575 1572526800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85783 1572548400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1572613200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85783 1572634800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85767 1572678000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1572699600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.858 1572721200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06033,lon=38.85733 1572786000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85767 1572807600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.858 1572894000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06567,lon=38.83333 1572926400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1572958800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85783 1573045200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85767 1573066800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.062,lon=38.8575 1573099200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.8575 1573131600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85767 1573153200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85817 1573304400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85783 1573326000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85783 1573369200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1573390800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85783 1573477200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.858 1573563600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1573617600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85733 1573650000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1573704000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.857 1573876800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85733 1573887600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85783 1573909200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06167,lon=38.85683 1573963200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.85733 1573974000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8575 1574017200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06717,lon=38.8345 1574049600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06083,lon=38.85783 1574060400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85733 1574103600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06183,lon=38.85683 1574136000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8575 1574190000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06233,lon=38.85717 1574254800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8575 1574276400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85733 1574308800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85783 1574341200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06117,lon=38.85733 1574362800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1574395200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.8285 1574427600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.8575 1574449200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06133,lon=38.85733 1574514000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.858 1574535600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85817 1574600400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.858 1574622000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1574654400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1574686800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85817 1574708400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1574740800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85883 1574773200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85817 1574794800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06533,lon=38.83033 1574827200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.858 1574881200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85867 1574913600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85867 1574946000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0605,lon=38.85733 1574967600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1575000000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85717 1575054000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.061,lon=38.85717 1575086400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.8575 1575140400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85767 1575172800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85783 1575205200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05983,lon=38.85783 1575226800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1575270000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06,lon=38.85767 1575313200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1575345600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.85683 1575442800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06067,lon=38.85817 1575464400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575486000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06017,lon=38.857 1575518400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.857 1575529200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85683 1575550800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575572400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.857 1575604800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06217,lon=38.857 1575615600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85683 1575637200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575658800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0635,lon=38.85683 1575691200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06333,lon=38.85683 1575702000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85683 1575723600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575745200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06567,lon=38.82133 1575810000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575831600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06267,lon=38.85617 1575864000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85667 1575896400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1575918000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06283,lon=38.85633 1575950400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06383,lon=38.85667 1575982800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85667 1576047600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06433,lon=38.85667 1576069200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576090800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06367,lon=38.85667 1576123200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85667 1576155600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576177200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.063,lon=38.85667 1576209600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.064,lon=38.85667 1576242000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576263600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06317,lon=38.85633 1576296000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.06417,lon=38.85667 1576328400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576350000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.8585 1576382400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0615,lon=38.85467 1576414800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576436400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85833 1576468800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.8585 1576479600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05967,lon=38.85817 1576501200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85683 1576555200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.8585 1576566000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85817 1576609200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85633 1576641600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85867 1576652400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.8585 1576674000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.8585 1576695600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85917 1576760400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.8585 1576782000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85867 1576868400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.85867 1576933200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85867 1576954800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85883 1576987200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.0595,lon=38.8585 1577019600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85917 1577041200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.859 1577084400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85933 1577127600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85883 1577192400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85783 1577214000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.059,lon=38.85917 1577246400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.859 1577278800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.85917 1577300400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.859 1577365200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.859 1577386800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05933,lon=38.85867 1577451600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.85883 1577473200000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.859 1577505600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.85917 1577559600000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85867 1577624400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.8575 1577646000000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85933 1577710800000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05883,lon=38.85767 1577732400000000000 +migration3,id=91752A,s2_cell_id=17b4bcc lat=8.05917,lon=38.85733 1577818800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05767,lon=38.85367 1547406000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0515,lon=38.85583 1547438400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.85283 1547492400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86117 1547524800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05767,lon=38.85367 1547665200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.85317 1547751600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.85517 1548097200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05367,lon=38.84533 1548140400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03717,lon=38.839 1548216000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04933,lon=38.84683 1548226800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05883,lon=38.85167 1548270000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05467,lon=38.86133 1548302400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.84767 1548356400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.055,lon=38.8515 1548399600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.048,lon=38.8495 1548486000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.84583 1548831600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0555,lon=38.86033 1549112400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03817,lon=38.84483 1549166400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05167,lon=38.85283 1549177200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.02983,lon=38.837 1549339200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0285,lon=38.84783 1549522800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0395,lon=38.847 1549684800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05883,lon=38.854 1549998000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05467,lon=38.85467 1550041200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0565,lon=38.85367 1550386800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05517,lon=38.84383 1550430000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85433 1550689200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05783,lon=38.85867 1550980800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.8465 1551121200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.85 1551207600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.06,lon=38.83767 1551272400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1551499200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04267,lon=38.84733 1552114800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.034,lon=38.84317 1552201200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.86117 1552309200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.8615 1552363200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04017,lon=38.85133 1552374000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0545,lon=38.83867 1552460400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1552536000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86183 1552590000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85067 1552676400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0405,lon=38.86233 1552708800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.8575 1552741200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1552849200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.86133 1552881600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86117 1552968000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.043,lon=38.84617 1552978800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1553054400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.86067 1553140800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.858 1553194800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.85933 1553259600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.85967 1553313600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.02883,lon=38.841 1553454000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04167,lon=38.84217 1553745600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1553799600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1553886000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1553972400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03967,lon=38.86033 1554004800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1554058800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86033 1554145200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1554231600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1554318000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04867,lon=38.86217 1554620400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1554663600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03783,lon=38.86183 1554696000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05483,lon=38.8555 1554987600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05483,lon=38.85967 1555095600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05817,lon=38.85133 1555128000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555182000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555268400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.8605 1555300800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05833,lon=38.85817 1555354800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555441200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85833 1555527600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555614000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555700400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555786800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05267,lon=38.84267 1555873200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1555938000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.84067 1555959600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556132400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556218800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556305200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05283,lon=38.84533 1556337600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556391600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05117,lon=38.862 1556424000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556478000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0475,lon=38.85633 1556542800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556564400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556650800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.035,lon=38.8525 1556683200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.059,lon=38.84533 1556694000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556737200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1556769600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.058,lon=38.85317 1556780400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556823600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1556910000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.861 1557039600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05733,lon=38.86033 1557169200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05517,lon=38.85183 1557212400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1557255600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.86167 1557288000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05733,lon=38.86017 1557342000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.046,lon=38.839 1557428400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557514800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.85717 1557558000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557601200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557774000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05,lon=38.859 1557838800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557860400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557946800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1557979200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1558033200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1558206000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0555,lon=38.86 1558292400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05833,lon=38.85817 1558465200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1558551600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.8605 1558724400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.86067 1558810800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.861 1558897200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1559070000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86033 1559188800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86067 1559242800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05733,lon=38.86033 1559329200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.85317 1559534400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05217,lon=38.857 1559566800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.06017,lon=38.84267 1559707200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.8615 1559847600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.844 1559966400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0485,lon=38.83317 1560020400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86083 1560052800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.86133 1560063600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.86083 1560106800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.8595 1560171600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1560193200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0565,lon=38.86083 1560279600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1560366000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.06,lon=38.84333 1560430800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.86067 1560452400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0565,lon=38.86083 1560538800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1560625200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86067 1560711600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.86083 1560798000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.86067 1560884400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05833,lon=38.83833 1560970800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.86133 1561122000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1561143600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.861 1561176000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.86133 1561186800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1561273200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1561316400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.86133 1561532400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05667,lon=38.8605 1561554000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05617,lon=38.86083 1561575600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.86117 1561608000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.861 1561662000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05783,lon=38.86133 1561705200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05767,lon=38.86133 1561899600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86133 1562040000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86167 1562072400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86133 1562266800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86117 1562353200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05483,lon=38.862 1562472000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86083 1562526000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05717,lon=38.84133 1562558400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.8605 1562612400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.86167 1562655600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.8605 1562698800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1562958000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.058,lon=38.85783 1562990400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05683,lon=38.86167 1563001200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05833,lon=38.85817 1563044400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.032,lon=38.8485 1563336000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0585,lon=38.85817 1563390000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.048,lon=38.85283 1565290800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05067,lon=38.85583 1568314800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.8555 1568530800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04583,lon=38.84717 1568746800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04933,lon=38.845 1568865600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04683,lon=38.84617 1570885200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04533,lon=38.844 1571122800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05583,lon=38.845 1571144400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04983,lon=38.84367 1571209200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05633,lon=38.85417 1571371200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04217,lon=38.83867 1571457600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05417,lon=38.849 1571554800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04767,lon=38.84017 1571630400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05033,lon=38.858 1571814000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0595,lon=38.84833 1572159600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04617,lon=38.85933 1572591600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0495,lon=38.84717 1572764400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04817,lon=38.848 1572850800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04867,lon=38.8475 1572937200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05083,lon=38.84967 1572980400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04817,lon=38.847 1573023600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04067,lon=38.84817 1573110000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.049,lon=38.84717 1573196400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04733,lon=38.8385 1573272000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.044,lon=38.846 1573282800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04383,lon=38.8525 1573412400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0395,lon=38.8485 1573455600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0565,lon=38.8215 1573531200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04017,lon=38.84833 1573542000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0485,lon=38.84917 1573585200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.056,lon=38.86167 1573628400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0455,lon=38.85267 1573671600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.036,lon=38.84383 1573714800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03067,lon=38.83083 1573790400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03967,lon=38.8425 1573801200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0395,lon=38.84233 1574146800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03183,lon=38.84083 1574222400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0385,lon=38.84367 1574233200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04417,lon=38.8365 1574481600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03833,lon=38.844 1574492400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04,lon=38.8485 1574665200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04733,lon=38.84317 1574838000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0345,lon=38.84917 1574924400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04383,lon=38.847 1575010800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05867,lon=38.8565 1575032400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0425,lon=38.8455 1575097200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0375,lon=38.844 1575183600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0465,lon=38.84717 1575356400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05233,lon=38.84483 1575399600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05967,lon=38.83933 1575432000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0425,lon=38.84767 1575874800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04883,lon=38.84917 1576004400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0475,lon=38.845 1576393200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0495,lon=38.84167 1576522800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0515,lon=38.85117 1576728000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03917,lon=38.8345 1576738800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03967,lon=38.84183 1576846800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05783,lon=38.84683 1576900800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04617,lon=38.84717 1576911600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04733,lon=38.84683 1576998000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0545,lon=38.846 1577073600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04033,lon=38.85483 1577160000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0385,lon=38.844 1577257200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.04183,lon=38.8365 1577332800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05383,lon=38.84283 1577343600000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0525,lon=38.842 1577419200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.02583,lon=38.84817 1577430000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0355,lon=38.8445 1577516400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05917,lon=38.83417 1577538000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.0575,lon=38.8545 1577592000000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.044,lon=38.8425 1577678400000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.057,lon=38.84333 1577689200000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.05983,lon=38.845 1577764800000000000 +migration3,id=91752A,s2_cell_id=17b4bd4 lat=8.03767,lon=38.83383 1577775600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05517,lon=38.86417 1547784000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05733,lon=38.86567 1548129600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05533,lon=38.86367 1549004400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0545,lon=38.8655 1550116800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.02167,lon=38.87217 1550127600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.054,lon=38.88883 1550494800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.047,lon=38.86867 1550635200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.036,lon=38.86833 1550808000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.02767,lon=38.895 1551013200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.045,lon=38.86983 1551596400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.03933,lon=38.868 1551682800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.041,lon=38.86417 1551931200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.032,lon=38.88617 1552449600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04667,lon=38.86417 1552622400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0565,lon=38.868 1552633200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04133,lon=38.87667 1552806000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04967,lon=38.87833 1553281200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04283,lon=38.88933 1553367600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05667,lon=38.86383 1553410800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05117,lon=38.88517 1553756400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0475,lon=38.8755 1554296400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0565,lon=38.86867 1554523200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0445,lon=38.8655 1554609600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04583,lon=38.89267 1555473600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0545,lon=38.86783 1555732800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04583,lon=38.86333 1556283600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0555,lon=38.87917 1557547200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.044,lon=38.86717 1558378800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0465,lon=38.86667 1559934000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.047,lon=38.866 1560409200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.033,lon=38.8645 1560668400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0565,lon=38.86783 1561003200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05383,lon=38.88033 1561435200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0535,lon=38.86633 1561726800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0535,lon=38.86283 1562299200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.042,lon=38.86583 1566154800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0525,lon=38.87617 1566651600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.02817,lon=38.88883 1567429200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0565,lon=38.863 1569124800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04383,lon=38.86417 1570431600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04433,lon=38.86733 1572148800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04567,lon=38.87617 1573185600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04617,lon=38.86683 1573358400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04967,lon=38.87633 1573444800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04733,lon=38.86467 1574568000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.0455,lon=38.88683 1574751600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.044,lon=38.86867 1575259200000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.05233,lon=38.86717 1575777600000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.02067,lon=38.9005 1576036800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.04467,lon=38.8795 1576134000000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.052,lon=38.86417 1576220400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.03667,lon=38.89433 1576306800000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.034,lon=38.873 1576814400000000000 +migration3,id=91752A,s2_cell_id=17b4bdc lat=8.03067,lon=38.86533 1577602800000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.05083,lon=38.913 1547622000000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.0435,lon=38.91017 1560927600000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.04333,lon=38.92067 1569038400000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.0465,lon=38.9115 1574406000000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.04833,lon=38.91033 1574578800000000000 +migration3,id=91752A,s2_cell_id=17b4be4 lat=8.042,lon=38.91367 1575788400000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.05333,lon=38.92117 1552719600000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.055,lon=38.91683 1552892400000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.0765,lon=38.90433 1553151600000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.07983,lon=38.91667 1553572800000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.05233,lon=38.93433 1553832000000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.0595,lon=38.93767 1554037200000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.08683,lon=38.91367 1554879600000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.08117,lon=38.93567 1555905600000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.06133,lon=38.93333 1555916400000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.0695,lon=38.9185 1556164800000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.06333,lon=38.91117 1557460800000000000 +migration3,id=91752A,s2_cell_id=17b4bec lat=8.06533,lon=38.93633 1557493200000000000 +migration3,id=91761A,s2_cell_id=140317c lat=21.5525,lon=25.26917 1554537600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51633,lon=24.32467 1554580800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554613200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554624000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554645600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554667200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554699600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554710400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554732000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554753600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554786000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554796800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554818400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554840000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554872400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1554883200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554904800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33233 1554926400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554969600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1554991200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555012800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555045200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555056000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1555077600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555099200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33233 1555131600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555142400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1555164000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33233 1555185600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555218000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33233 1555228800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33233 1555250400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555272000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555304400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555315200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33217 1555336800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.51217,lon=24.33233 1555358400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555390800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555401600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555423200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555477200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555488000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555531200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555563600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555574400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555617600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555650000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555660800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555704000000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555736400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555747200000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555790400000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555822800000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555833600000000000 +migration3,id=91761A,s2_cell_id=140419c lat=22.512,lon=24.33217 1555876800000000000 +migration3,id=91761A,s2_cell_id=140473c lat=22.28717,lon=24.599 1554559200000000000 +migration3,id=91761A,s2_cell_id=141d314 lat=21.0285,lon=25.79083 1554526800000000000 +migration3,id=91761A,s2_cell_id=141d47c lat=20.86767,lon=25.8685 1554494400000000000 +migration3,id=91761A,s2_cell_id=141d9e4 lat=20.75517,lon=26.46067 1554472800000000000 +migration3,id=91761A,s2_cell_id=141e1c4 lat=20.3645,lon=27.42183 1554451200000000000 +migration3,id=91761A,s2_cell_id=1693324 lat=14.39117,lon=30.7105 1554148800000000000 +migration3,id=91761A,s2_cell_id=1694714 lat=15.33133,lon=30.03733 1554181200000000000 +migration3,id=91761A,s2_cell_id=1696fac lat=16.34217,lon=29.57933 1554192000000000000 +migration3,id=91761A,s2_cell_id=16977d4 lat=17.17033,lon=29.5845 1554213600000000000 +migration3,id=91761A,s2_cell_id=16992a4 lat=18.50633,lon=29.44517 1554300000000000000 +migration3,id=91761A,s2_cell_id=169939c lat=18.313,lon=29.7385 1554278400000000000 +migration3,id=91761A,s2_cell_id=1699a74 lat=17.84,lon=29.69133 1554267600000000000 +migration3,id=91761A,s2_cell_id=1699b74 lat=17.60133,lon=29.58417 1554235200000000000 +migration3,id=91761A,s2_cell_id=169f1ec lat=18.98767,lon=29.33583 1554354000000000000 +migration3,id=91761A,s2_cell_id=169f32c lat=18.62167,lon=29.20383 1554321600000000000 +migration3,id=91761A,s2_cell_id=169f72c lat=19.182,lon=29.00267 1554364800000000000 +migration3,id=91761A,s2_cell_id=16a0424 lat=19.7845,lon=28.2055 1554386400000000000 +migration3,id=91761A,s2_cell_id=16a084c lat=20.12833,lon=27.74283 1554440400000000000 +migration3,id=91761A,s2_cell_id=16a08bc lat=19.918,lon=27.873 1554408000000000000 +migration3,id=91761A,s2_cell_id=16e2364 lat=9.71217,lon=30.5255 1553544000000000000 +migration3,id=91761A,s2_cell_id=16ec574 lat=13.58367,lon=30.71417 1554127200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.687,lon=30.63533 1553587200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.6875,lon=30.635 1553608800000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69467,lon=30.634 1553630400000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68383,lon=30.638 1553662800000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68583,lon=30.63383 1553695200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69467,lon=30.63633 1553716800000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68567,lon=30.635 1553749200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68567,lon=30.63517 1553781600000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69567,lon=30.63317 1553803200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69367,lon=30.6495 1553846400000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69617,lon=30.63 1553868000000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.6965,lon=30.631 1553889600000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69383,lon=30.63533 1553922000000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68533,lon=30.63883 1553932800000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69633,lon=30.6315 1553954400000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69767,lon=30.63267 1553976000000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.69367,lon=30.64917 1554008400000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68567,lon=30.63517 1554019200000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68567,lon=30.63517 1554040800000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.6935,lon=30.63567 1554062400000000000 +migration3,id=91761A,s2_cell_id=16ee934 lat=12.68633,lon=30.63967 1554094800000000000 +migration3,id=91761A,s2_cell_id=16ee944 lat=12.72883,lon=30.61567 1553673600000000000 +migration3,id=91761A,s2_cell_id=16ee944 lat=12.74033,lon=30.60267 1554105600000000000 +migration3,id=91761A,s2_cell_id=16ee94c lat=12.707,lon=30.61717 1553760000000000000 +migration3,id=91761A,s2_cell_id=16ee94c lat=12.70783,lon=30.619 1553835600000000000 +migration3,id=91761A,s2_cell_id=16ef414 lat=11.975,lon=30.45917 1553576400000000000 +migration3,id=91761A,s2_cell_id=170d92c lat=5.1515,lon=31.82783 1553490000000000000 +migration3,id=91761A,s2_cell_id=171a85c lat=6.80267,lon=30.998 1553500800000000000 +migration3,id=91761A,s2_cell_id=171c424 lat=8.241,lon=30.6485 1553522400000000000 +migration3,id=91761A,s2_cell_id=177a6cc lat=1.88433,lon=32.69133 1553457600000000000 +migration3,id=91761A,s2_cell_id=177fbac lat=0.34317,lon=33.98167 1553436000000000000 +migration3,id=91761A,s2_cell_id=177fcdc lat=0.12467,lon=33.88033 1546545600000000000 +migration3,id=91761A,s2_cell_id=177fcdc lat=0.12667,lon=33.88117 1546718400000000000 +migration3,id=91761A,s2_cell_id=177fcdc lat=0.13017,lon=33.8825 1546804800000000000 +migration3,id=91761A,s2_cell_id=177fce4 lat=0.12783,lon=33.89283 1550779200000000000 +migration3,id=91761A,s2_cell_id=177fcfc lat=0.14467,lon=33.93433 1546318800000000000 +migration3,id=91761A,s2_cell_id=177fd04 lat=0.122,lon=33.9335 1550822400000000000 +migration3,id=91761A,s2_cell_id=177fd0c lat=0.07733,lon=33.9355 1547193600000000000 +migration3,id=91761A,s2_cell_id=177fd0c lat=0.06983,lon=33.93083 1547366400000000000 +migration3,id=91761A,s2_cell_id=177fd0c lat=0.0655,lon=33.93117 1548316800000000000 +migration3,id=91761A,s2_cell_id=177fd0c lat=0.06417,lon=33.937 1553241600000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.0725,lon=33.9225 1546610400000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.07,lon=33.92383 1546869600000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.06383,lon=33.92567 1548230400000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.06567,lon=33.88783 1548738000000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.062,lon=33.89783 1549774800000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.064,lon=33.905 1549947600000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.06467,lon=33.93017 1550520000000000000 +migration3,id=91761A,s2_cell_id=177fd14 lat=0.07033,lon=33.9225 1553328000000000000 +migration3,id=91761A,s2_cell_id=177fd24 lat=0.12317,lon=33.8835 1546372800000000000 +migration3,id=91761A,s2_cell_id=177fd24 lat=0.11883,lon=33.87983 1546459200000000000 +migration3,id=91761A,s2_cell_id=177fd24 lat=0.12,lon=33.88317 1546632000000000000 +migration3,id=91761A,s2_cell_id=177fd24 lat=0.11617,lon=33.88333 1550811600000000000 +migration3,id=91761A,s2_cell_id=177fd44 lat=0.0385,lon=33.88083 1546675200000000000 +migration3,id=91761A,s2_cell_id=177fd44 lat=0.05167,lon=33.8795 1546750800000000000 +migration3,id=91761A,s2_cell_id=177fd44 lat=0.033,lon=33.85633 1547107200000000000 +migration3,id=91761A,s2_cell_id=177fd4c lat=0.042,lon=33.8195 1547701200000000000 +migration3,id=91761A,s2_cell_id=177fd54 lat=0.01417,lon=33.82983 1547280000000000000 +migration3,id=91761A,s2_cell_id=177fd54 lat=0.01583,lon=33.834 1551340800000000000 +migration3,id=91761A,s2_cell_id=177fd54 lat=0.02417,lon=33.82583 1551427200000000000 +migration3,id=91761A,s2_cell_id=177fd5c lat=0.02083,lon=33.87583 1547269200000000000 +migration3,id=91761A,s2_cell_id=177fd5c lat=0.02517,lon=33.8415 1551416400000000000 +migration3,id=91761A,s2_cell_id=177fd64 lat=0.0295,lon=33.92117 1548910800000000000 +migration3,id=91761A,s2_cell_id=177fd64 lat=0.02933,lon=33.91283 1549170000000000000 +migration3,id=91761A,s2_cell_id=177fd64 lat=0.02833,lon=33.91217 1549180800000000000 +migration3,id=91761A,s2_cell_id=177fd64 lat=0.0215,lon=33.90767 1551070800000000000 +migration3,id=91761A,s2_cell_id=177fd64 lat=0.01767,lon=33.9145 1551157200000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05067,lon=33.892 1546502400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.06,lon=33.9 1546578000000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05367,lon=33.89017 1546588800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03417,lon=33.8905 1546848000000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.06183,lon=33.89783 1546934400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05867,lon=33.90017 1547010000000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05933,lon=33.888 1547096400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03517,lon=33.923 1547884800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05517,lon=33.90567 1548489600000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.05533,lon=33.89983 1548662400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03583,lon=33.92733 1548748800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.04367,lon=33.92533 1548835200000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03483,lon=33.91533 1548997200000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03567,lon=33.92433 1549094400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.04683,lon=33.914 1549688400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.041,lon=33.91217 1550206800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.0365,lon=33.91433 1550379600000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.03333,lon=33.90883 1550466000000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.044,lon=33.91017 1550908800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.04367,lon=33.92067 1550995200000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.04833,lon=33.9155 1553068800000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.04517,lon=33.91783 1553144400000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.0465,lon=33.92967 1553317200000000000 +migration3,id=91761A,s2_cell_id=177fd6c lat=0.059,lon=33.92717 1553371200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1546329600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94933 1546351200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05133,lon=33.94933 1546405200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94933 1546416000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05433,lon=33.94733 1546437600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.946 1546491600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0545,lon=33.94733 1546524000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1546664400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05133,lon=33.94933 1546696800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94617 1546761600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05433,lon=33.94733 1546783200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05233,lon=33.9505 1546837200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1546891200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05267,lon=33.95067 1546923600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05133,lon=33.94933 1546956000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94617 1546977600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1547020800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1547042400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94617 1547064000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1547128800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1547150400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1547182800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0545,lon=33.94733 1547215200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05883,lon=33.94617 1547236800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1547323200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04483,lon=33.96983 1547355600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1547388000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05867,lon=33.94633 1547409600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.9495 1547442000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03817,lon=33.94617 1547452800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1547474400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05883,lon=33.94633 1547496000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1547528400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94933 1547539200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.94733 1547560800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04083,lon=33.9545 1547582400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.94683 1547614800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.947 1547625600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1547647200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94617 1547668800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94617 1547733600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1547755200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03617,lon=33.9605 1547787600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05267,lon=33.95067 1547798400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.94733 1547820000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1547841600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.94733 1547874000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1547906400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1547928000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1547960400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.9475 1547971200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.94733 1547992800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1548014400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.052,lon=33.93833 1548046800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.9475 1548057600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.9475 1548079200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05383,lon=33.947 1548100800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1548133200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05267,lon=33.95067 1548144000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94933 1548165600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1548187200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05267,lon=33.95067 1548219600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.9475 1548252000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1548273600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05767,lon=33.94683 1548306000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.94733 1548338400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1548360000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.9475 1548392400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1548403200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05767,lon=33.94683 1548424800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1548446400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.94767 1548478800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1548511200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1548532800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03817,lon=33.945 1548565200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94783 1548576000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.94733 1548597600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1548619200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04117,lon=33.94583 1548651600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.94733 1548684000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05333,lon=33.94767 1548705600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1548770400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1548792000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1548824400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.94733 1548856800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94783 1548878400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05833,lon=33.946 1548921600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05333,lon=33.94783 1548943200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94767 1548964800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94783 1549008000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.94733 1549029600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.053,lon=33.94783 1549051200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.041,lon=33.93333 1549083600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94783 1549116000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1549137600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1549202400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04183,lon=33.94317 1549224000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05233,lon=33.9505 1549256400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05283,lon=33.95083 1549267200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1549288800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94717 1549310400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05183,lon=33.943 1549342800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04517,lon=33.95067 1549353600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.947 1549375200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.949 1549396800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0355,lon=33.97133 1549429200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05333,lon=33.94767 1549461600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94833 1549483200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.9485 1549515600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05317,lon=33.94767 1549526400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.053,lon=33.94767 1549548000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05283,lon=33.94783 1549569600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94833 1549602000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05833,lon=33.94583 1549612800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1549634400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.053,lon=33.948 1549656000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05833,lon=33.946 1549699200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1549720800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05283,lon=33.94783 1549742400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.947 1549785600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0605,lon=33.945 1549807200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.053,lon=33.948 1549828800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94833 1549861200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05267,lon=33.94783 1549872000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94933 1549893600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94917 1549915200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.947 1549958400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05383,lon=33.947 1549980000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0515,lon=33.94833 1550001600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0525,lon=33.95017 1550034000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04317,lon=33.95283 1550044800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1550066400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05383,lon=33.947 1550088000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03817,lon=33.95617 1550120400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.032,lon=33.94433 1550131200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.94733 1550152800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05383,lon=33.94683 1550174400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05733,lon=33.947 1550217600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.947 1550239200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.9485 1550260800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03217,lon=33.967 1550293200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0525,lon=33.95017 1550304000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.9495 1550325600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.9485 1550347200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05183,lon=33.9495 1550390400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1550412000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.94833 1550433600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.947 1550476800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1550498400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.94717 1550552400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1550584800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.055,lon=33.94117 1550606400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.94717 1550638800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1550649600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05883,lon=33.9465 1550671200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1550692800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.03967,lon=33.95667 1550725200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1550736000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1550757600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05683,lon=33.94733 1550844000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1550865600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.947 1550898000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1550930400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1550952000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04617,lon=33.93783 1550984400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1551016800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.94633 1551038400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.04067,lon=33.93167 1551081600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05917,lon=33.9465 1551103200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05933,lon=33.94633 1551124800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0575,lon=33.9475 1551168000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05183,lon=33.9495 1551189600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05933,lon=33.94633 1551211200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05367,lon=33.9475 1551276000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05183,lon=33.9485 1551297600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05933,lon=33.94633 1551384000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05167,lon=33.9485 1551330000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.059,lon=33.94633 1551362400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0595,lon=33.9465 1553025600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.057,lon=33.947 1553058000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.94717 1553090400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0595,lon=33.9465 1553112000000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05667,lon=33.947 1553155200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05783,lon=33.9465 1553176800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05967,lon=33.9465 1553198400000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05667,lon=33.94717 1553230800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05983,lon=33.94667 1553263200000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05983,lon=33.9465 1553284800000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.0585,lon=33.94617 1553349600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05133,lon=33.95017 1553403600000000000 +migration3,id=91761A,s2_cell_id=177fd74 lat=0.05717,lon=33.947 1553414400000000000 +migration3,id=91761A,s2_cell_id=177fd7c lat=0.027,lon=33.9415 1549440000000000000 +migration3,id=91761A,s2_cell_id=177fd7c lat=0.03083,lon=33.96617 1550563200000000000 +migration3,id=91761A,s2_cell_id=19d483c lat=-0.954,lon=33.91517 1551945600000000000 +migration3,id=91761A,s2_cell_id=19d4844 lat=-0.93283,lon=33.907 1551762000000000000 +migration3,id=91761A,s2_cell_id=19d4844 lat=-0.92933,lon=33.893 1551772800000000000 +migration3,id=91761A,s2_cell_id=19d4844 lat=-0.94033,lon=33.91 1551859200000000000 +migration3,id=91761A,s2_cell_id=19d488c lat=-0.87867,lon=33.86833 1552982400000000000 +migration3,id=91761A,s2_cell_id=19d48e4 lat=-0.84483,lon=33.95267 1552021200000000000 +migration3,id=91761A,s2_cell_id=19d48ec lat=-0.8445,lon=33.92367 1552464000000000000 +migration3,id=91761A,s2_cell_id=19d48ec lat=-0.84567,lon=33.89667 1552550400000000000 +migration3,id=91761A,s2_cell_id=19d48f4 lat=-0.85033,lon=33.91533 1552539600000000000 +migration3,id=91761A,s2_cell_id=19d48f4 lat=-0.86633,lon=33.922 1552723200000000000 +migration3,id=91761A,s2_cell_id=19d48f4 lat=-0.86783,lon=33.9 1552809600000000000 +migration3,id=91761A,s2_cell_id=19d48f4 lat=-0.878,lon=33.89033 1552896000000000000 +migration3,id=91761A,s2_cell_id=19d48f4 lat=-0.861,lon=33.91217 1552971600000000000 +migration3,id=91761A,s2_cell_id=19d48fc lat=-0.84833,lon=33.93717 1552032000000000000 +migration3,id=91761A,s2_cell_id=19d48fc lat=-0.86417,lon=33.97317 1552107600000000000 +migration3,id=91761A,s2_cell_id=19d48fc lat=-0.85683,lon=33.95017 1552118400000000000 +migration3,id=91761A,s2_cell_id=19d48fc lat=-0.86133,lon=33.97267 1552204800000000000 +migration3,id=91761A,s2_cell_id=19d48fc lat=-0.85917,lon=33.96 1552453200000000000 +migration3,id=91761A,s2_cell_id=19d4904 lat=-0.868,lon=33.99933 1552194000000000000 +migration3,id=91761A,s2_cell_id=19d4904 lat=-0.86867,lon=33.991 1552291200000000000 +migration3,id=91761A,s2_cell_id=19d4904 lat=-0.86433,lon=33.98517 1552366800000000000 +migration3,id=91761A,s2_cell_id=19d4904 lat=-0.853,lon=33.97583 1552377600000000000 +migration3,id=91761A,s2_cell_id=19d497c lat=-0.87617,lon=34.1255 1552680000000000000 +migration3,id=91761A,s2_cell_id=19d497c lat=-0.87483,lon=34.132 1552766400000000000 +migration3,id=91761A,s2_cell_id=19d497c lat=-0.84533,lon=34.128 1552852800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90433,lon=34.143 1551535200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.8925,lon=34.12933 1551556800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90517,lon=34.14283 1551621600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90567,lon=34.14283 1551643200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90583,lon=34.14283 1551675600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90433,lon=34.14317 1551686400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90383,lon=34.1435 1551708000000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.88467,lon=34.11367 1551729600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90383,lon=34.1435 1551794400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.89633,lon=34.13133 1551816000000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.907,lon=34.14317 1551848400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90517,lon=34.14283 1551880800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.905,lon=34.14283 1551902400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1551967200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.89833,lon=34.13667 1551988800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90483,lon=34.14283 1552053600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.89,lon=34.13567 1552075200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90433,lon=34.143 1552140000000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90417,lon=34.14317 1552161600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1552226400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.89267,lon=34.1235 1552248000000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90417,lon=34.14333 1552312800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.89083,lon=34.12783 1552334400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1552399200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.88533,lon=34.13217 1552420800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1552485600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.888,lon=34.1405 1552507200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90417,lon=34.14333 1552572000000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.88433,lon=34.12283 1552593600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1552658400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.9035,lon=34.14383 1552712400000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.904,lon=34.14333 1552744800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.9035,lon=34.14383 1552798800000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90467,lon=34.14283 1552831200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90333,lon=34.144 1552885200000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.90417,lon=34.14333 1552917600000000000 +migration3,id=91761A,s2_cell_id=19d4984 lat=-0.87983,lon=34.13867 1552939200000000000 +migration3,id=91761A,s2_cell_id=19d49a4 lat=-0.884,lon=34.02817 1552280400000000000 +migration3,id=91761A,s2_cell_id=19d49ac lat=-0.88583,lon=34.00333 1551934800000000000 +migration3,id=91761A,s2_cell_id=19d49ac lat=-0.87967,lon=34.0075 1552626000000000000 +migration3,id=91761A,s2_cell_id=19d49ac lat=-0.89017,lon=33.99583 1552636800000000000 +migration3,id=91761A,s2_cell_id=19d49b4 lat=-0.9115,lon=34.01917 1551589200000000000 +migration3,id=91761A,s2_cell_id=19d49dc lat=-0.988,lon=34.0385 1551600000000000000 +migration3,id=91761A,s2_cell_id=19d4e04 lat=-0.50533,lon=34.12883 1551470400000000000 +migration3,id=91761A,s2_cell_id=19d4e54 lat=-0.62267,lon=33.99333 1551502800000000000 +migration3,id=91761A,s2_cell_id=19d4f2c lat=-0.72417,lon=33.94417 1551513600000000000 +migration3,id=91761A,s2_cell_id=19d5634 lat=-0.061,lon=34.0085 1551254400000000000 +migration3,id=91761A,s2_cell_id=19d5634 lat=-0.0475,lon=33.99333 1551330000000000000 +migration3,id=91761A,s2_cell_id=19d5634 lat=-0.06117,lon=33.97883 1551340800000000000 +migration3,id=91761A,s2_cell_id=19d5634 lat=-0.046,lon=34.00633 1551362400000000000 +migration3,id=91761A,s2_cell_id=19d568c lat=-0.14483,lon=34.08133 1553004000000000000 +migration3,id=91761A,s2_cell_id=19d5774 lat=-0.145,lon=33.87783 1547301600000000000 +migration3,id=91761A,s2_cell_id=19d57a4 lat=-0.0965,lon=33.9005 1551448800000000000 +migration3,id=91761A,s2_cell_id=19d57c4 lat=-0.04367,lon=33.91083 1551243600000000000 +migration3,id=91761A,s2_cell_id=19d590c lat=-0.16683,lon=33.58783 1547712000000000000 +migration3,id=91763A,s2_cell_id=19d1924 lat=-1.76517,lon=32.897 1559073600000000000 +migration3,id=91763A,s2_cell_id=19d22d4 lat=-1.53267,lon=33.24083 1559106000000000000 +migration3,id=91763A,s2_cell_id=19d23ec lat=-1.7405,lon=33.37783 1559052000000000000 +migration3,id=91763A,s2_cell_id=19d23f4 lat=-1.72767,lon=33.41917 1556006400000000000 +migration3,id=91763A,s2_cell_id=19d23f4 lat=-1.73167,lon=33.421 1556028000000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.32267,lon=33.841 1547442000000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.32167,lon=33.816 1550044800000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.31817,lon=33.81083 1551416400000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.318,lon=33.81083 1551448800000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.3095,lon=33.82717 1551859200000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.314,lon=33.8175 1552226400000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.31833,lon=33.81083 1552896000000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.32317,lon=33.83917 1553058000000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.2985,lon=33.80367 1555920000000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.3165,lon=33.81317 1556611200000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.30567,lon=33.8185 1559019600000000000 +migration3,id=91763A,s2_cell_id=19d30a4 lat=-1.305,lon=33.8185 1574409600000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.2745,lon=33.8205 1547971200000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.27583,lon=33.8255 1549180800000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.27583,lon=33.8135 1569830400000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.28133,lon=33.8025 1574236800000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.27817,lon=33.83133 1574841600000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.274,lon=33.826 1575003600000000000 +migration3,id=91763A,s2_cell_id=19d30ac lat=-1.27267,lon=33.79833 1576310400000000000 +migration3,id=91763A,s2_cell_id=19d30b4 lat=-1.28367,lon=33.86383 1553576400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85067 1546696800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1546923600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.8535 1547128800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1547452800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85117 1547474400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1547560800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85317 1547820000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1547874000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1547884800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1547906400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1547960400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3205,lon=33.85483 1547992800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32117,lon=33.85367 1548046800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32117,lon=33.85367 1548057600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1548079200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32117,lon=33.85367 1548133200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31283,lon=33.84317 1548144000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.321,lon=33.85383 1548165600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31183,lon=33.85283 1548219600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32217,lon=33.85133 1548230400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.85067 1548252000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85067 1548306000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.322,lon=33.85133 1548316800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1548338400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31183,lon=33.85267 1548478800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1548489600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32033,lon=33.85483 1548511200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31183,lon=33.85267 1548565200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1548576000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1548597600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.321,lon=33.85367 1548651600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.85067 1548662400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1548684000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85067 1548738000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1548748800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1548770400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3165,lon=33.86017 1548910800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1548921600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1548943200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1549202400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3095,lon=33.85383 1549256400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1549267200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.8505 1549288800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1549375200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1549612800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.85317 1549634400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85067 1550066400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85067 1550206800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.85067 1550217600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31317,lon=33.85183 1550239200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31817,lon=33.851 1550293200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1550304000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1550325600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3165,lon=33.86017 1550379600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32117,lon=33.85367 1550390400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85317 1550412000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1550584800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31617,lon=33.86083 1551157200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.8535 1551168000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31617,lon=33.86067 1551189600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31233,lon=33.8525 1551362400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1551535200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.85067 1551600000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32117,lon=33.85367 1551621600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3165,lon=33.86017 1551675600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32167,lon=33.8505 1551686400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3215,lon=33.85067 1551762000000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1551772800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.32183,lon=33.85367 1551794400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31583,lon=33.86083 1551848400000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31633,lon=33.86083 1551880800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31617,lon=33.86067 1551934800000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31583,lon=33.86083 1551945600000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.31617,lon=33.86083 1551967200000000000 +migration3,id=91763A,s2_cell_id=19d30bc lat=-1.3165,lon=33.86017 1552021200000000000 + diff --git a/integrations/influxdb_connector/sample-influxdb-clients/go/README.md b/integrations/influxdb_connector/sample-influxdb-clients/go/README.md new file mode 100644 index 00000000..69007b1d --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/go/README.md @@ -0,0 +1,43 @@ +# InfluxDB Timestream Connector Sample Client + +## Overview + +This directory provides a sample Go client for use with the [InfluxDB Timestream Connector](../../influxdb-timestream-connector/README.md). + +By default, the client reads data from `../data/bird-migration.line` and sends [line protocol data](https://docs.influxdata.com/influxdb/v2/reference/syntax/line-protocol/) to the InfluxDB Timestream Connector. The client authenticates all requests with [AWS Signature Version 4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html) (SigV4) authentication. Its code provides an example of how SigV4 authentication can be implemented, since, when deployed as part of a CloudFormation stack, the InfluxDB Timestream Connector requires all requests use SigV4 authentication. + +## Configuration + +### Prerequisites + +1. [Install Go](https://go.dev/doc/install). +2. [Configure credentials to be used for SigV4 authentication](https://docs.aws.amazon.com/sdkref/latest/guide/creds-config-files.html). +3. [Deploy the InfluxDB Timestream Connector](../../influxdb-timestream-connector/README.md#deployment-options). + +### Command-Line Options + +The following command-line options are available: + +- `dataset`: The path to the line protocol dataset being ingested. Defaults to `../data/bird-migration.line`. +- `endpoint`: Endpoint for InfluxDB Timestream Connector. Defaults to `http://127.0.0.1:9000`. +- `precision`: Precision for line protocol: nanoseconds=`ns`, milliseconds=`ms`, microseconds=`us`, seconds=`s`. Defaults to `ns`. +- `region`: AWS region for InfluxDB Timestream Connector. Defaults to `us-east-1`. +- `service`: Service value for SigV4 header. Defaults to `lambda`. + +## Example + +### With Connector Deployed in a CloudFormation Stack + +When the connector is [deployed as a Lambda function within a CloudFormation stack](../../influxdb-timestream-connector/README.md#aws-cloudformation-deployment), run the following, replacing `` with the AWS region you deployed your stack in and `` with the endpoint of your deployed REST API Gateway: + +```shell +go run line-protocol-client-demo.go --region --service execute-api --endpoint +``` + +### With the Connector Deployed Locally + +When the connector is [deployed locally](../../influxdb-timestream-connector/README.md#local-deployment), the default command-line option values suffice: + +```shell +go run line-protocol-client-demo.go +``` diff --git a/integrations/influxdb_connector/sample-influxdb-clients/go/go.mod b/integrations/influxdb_connector/sample-influxdb-clients/go/go.mod new file mode 100644 index 00000000..d07f4dcf --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/go/go.mod @@ -0,0 +1,29 @@ +module line-protocol-client-demo + +go 1.22.1 + +require ( + github.com/aws/aws-sdk-go-v2 v1.27.1 + github.com/aws/aws-sdk-go-v2/config v1.27.17 + github.com/influxdata/influxdb-client-go/v2 v2.3.1-0.20210518120617-5d1fff431040 +) + +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.17.17 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.10 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.28.11 // indirect + github.com/aws/smithy-go v1.20.2 // indirect + github.com/deepmap/oapi-codegen v1.6.0 // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/stretchr/testify v1.8.1 // indirect + golang.org/x/net v0.23.0 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect +) diff --git a/integrations/influxdb_connector/sample-influxdb-clients/go/go.sum b/integrations/influxdb_connector/sample-influxdb-clients/go/go.sum new file mode 100644 index 00000000..24d6f5c4 --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/go/go.sum @@ -0,0 +1,119 @@ +github.com/aws/aws-sdk-go-v2 v1.27.1 h1:xypCL2owhog46iFxBKKpBcw+bPTX/RJzwNj8uSilENw= +github.com/aws/aws-sdk-go-v2 v1.27.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/config v1.27.17 h1:L0JZN7Gh7pT6u5CJReKsLhGKparqNKui+mcpxMXjDZc= +github.com/aws/aws-sdk-go-v2/config v1.27.17/go.mod h1:MzM3balLZeaafYcPz8IihAmam/aCz6niPQI0FdprxW0= +github.com/aws/aws-sdk-go-v2/credentials v1.17.17 h1:b3Dk9uxQByS9sc6r0sc2jmxsJKO75eOcb9nNEiaUBLM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.17/go.mod h1:e4khg9iY08LnFK/HXQDWMf9GDaiMari7jWPnXvKAuBU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.4 h1:0cSfTYYL9qiRcdi4Dvz+8s3JUgNR2qvbgZkXcwPEEEk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.4/go.mod h1:Wjn5O9eS7uSi7vlPKt/v0MLTncANn9EMmoDvnzJli6o= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.8 h1:RnLB7p6aaFMRfyQkD6ckxR7myCC9SABIqSz4czYUUbU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.8/go.mod h1:XH7dQJd+56wEbP1I4e4Duo+QhSMxNArE8VP7NuUOTeM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.8 h1:jzApk2f58L9yW9q1GEab3BMMFWUkkiZhyrRUtbwUbKU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.8/go.mod h1:WqO+FftfO3tGePUtQxPXM6iODVfqMwsVMgTbG/ZXIdQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.10 h1:7kZqP7akv0enu6ykJhb9OYlw16oOrSy+Epus8o/VqMY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.10/go.mod h1:gYVF3nM1ApfTRDj9pvdhootBb8WbiIejuqn4w8ruMes= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.10 h1:ItKVmFwbyb/ZnCWf+nu3XBVmUirpO9eGEQd7urnBA0s= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.10/go.mod h1:5XKooCTi9VB/xZmJDvh7uZ+v3uQ7QdX6diOyhvPA+/w= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.4 h1:QMSCYDg3Iyls0KZc/dk3JtS2c1lFfqbmYO10qBPPkJk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.4/go.mod h1:MZ/PVYU/mRbmSF6WK3ybCYHjA2mig8utVokDEVLDgE0= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.11 h1:HYS0csS7UJxdYRoG+bGgUYrSwVnV3/ece/wHm90TApM= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.11/go.mod h1:QXnthRM35zI92048MMwfFChjFmoufTdhtHmouwNfhhU= +github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= +github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +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= +github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/influxdata/influxdb-client-go/v2 v2.3.1-0.20210518120617-5d1fff431040 h1:MBLCfcSsUyFPDJp6T7EoHp/Ph3Jkrm4EuUKLD2rUWHg= +github.com/influxdata/influxdb-client-go/v2 v2.3.1-0.20210518120617-5d1fff431040/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= diff --git a/integrations/influxdb_connector/sample-influxdb-clients/go/line-protocol-client-demo.go b/integrations/influxdb_connector/sample-influxdb-clients/go/line-protocol-client-demo.go new file mode 100644 index 00000000..85ef1800 --- /dev/null +++ b/integrations/influxdb_connector/sample-influxdb-clients/go/line-protocol-client-demo.go @@ -0,0 +1,117 @@ +package main + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "flag" + "fmt" + "io" + "net/http" + "time" + "os" + "bufio" + + influxdbhttp "github.com/influxdata/influxdb-client-go/v2/api/http" + "github.com/influxdata/influxdb-client-go/v2" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" +) + +// UserAgentSetter is the implementation of Doer interface for setting the SigV4 headers +type SigV4HeaderSetter struct { + RequestDoer influxdbhttp.Doer +} + +var( + region string + service string + endpoint string + dataset string + precision string +) + +func main() { + flag.StringVar(®ion, "region", "us-east-1", "AWS region for InfluxDB Timestream Connector") + flag.StringVar(&service, "service", "lambda", "Service value for SigV4 header") + flag.StringVar(&endpoint, "endpoint", "http://127.0.0.1:9000", "Endpoint for InfluxDB Timestream Connector") + flag.StringVar(&dataset, "dataset", "../data/bird-migration.line", "Line protocol dataset being ingested") + flag.StringVar(&precision, "precision", "ns", "Precision for line protocol: nanoseconds=ns, milliseconds=ms, microseconds=us, seconds=s") + flag.Parse() + + opts := influxdb2.DefaultOptions() + opts.HTTPOptions().SetHTTPDoer(&SigV4HeaderSetter{RequestDoer: opts.HTTPClient(),}) + + switch { + case precision == "ns": + opts.WriteOptions().SetPrecision(time.Nanosecond) + case precision == "ms": + opts.WriteOptions().SetPrecision(time.Millisecond) + case precision == "us": + opts.WriteOptions().SetPrecision(time.Microsecond) + case precision == "s": + opts.WriteOptions().SetPrecision(time.Second) + default: + fmt.Println("Invalid precision value, valid values include: nanoseconds=ns, milliseconds=ms, microseconds=us, seconds=s") + return + } + + bucket := "" + org := "" + token := "" + + file, err := os.Open(dataset) + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + + sc := bufio.NewScanner(file) + lines := make([]string, 0) + + for sc.Scan() { + lines = append(lines, sc.Text()) + } + + if err := sc.Err(); err != nil { + fmt.Println(err) + return + } + + client := influxdb2.NewClientWithOptions(endpoint, token, opts) + writeAPI := client.WriteAPIBlocking(org, bucket) + err = writeAPI.WriteRecord(context.Background(), lines[0:]...) + if err != nil { + panic(err) + } + + // Ensures background processes finishes + client.Close() +} + +// Do is called before each request is made +func (u *SigV4HeaderSetter) Do(req *http.Request) (*http.Response, error) { + ctx := context.Background() + signer := v4.NewSigner() + + cfg, _ := config.LoadDefaultConfig(context.TODO()) + credentialsValue, _ := cfg.Credentials.Retrieve(context.TODO()) + + bodyBytes, _ := io.ReadAll(req.Body) + reader1 := io.NopCloser(bytes.NewBuffer(bodyBytes)) + reader2 := io.NopCloser(bytes.NewBuffer(bodyBytes)) + bodyBytes, _ = io.ReadAll(reader1) + + hash := sha256.New() + hash.Write([]byte(string(bodyBytes))) + hashedAndEncodedBody := hex.EncodeToString(hash.Sum(nil)) + + signer.SignHTTP(ctx, credentialsValue, req, hashedAndEncodedBody, service, region, time.Now()) + req.Body = reader2 + + // Call original Doer to proceed with request + return u.RequestDoer.Do(req) +} +