From 3cbccc1533759f8f49fff6004c84b5e6fd527cff Mon Sep 17 00:00:00 2001 From: hrodmn Date: Wed, 30 Oct 2024 14:49:27 -0500 Subject: [PATCH] handle exact case better --- .../test_timeseries_query.yaml | 385 +++++++++++++----- tests/test_timeseries.py | 6 +- titiler/cmr/timeseries.py | 91 ++--- 3 files changed, 338 insertions(+), 144 deletions(-) diff --git a/tests/cassettes/test_timeseries/test_timeseries_query.yaml b/tests/cassettes/test_timeseries/test_timeseries_query.yaml index 4754923..edf93a3 100644 --- a/tests/cassettes/test_timeseries/test_timeseries_query.yaml +++ b/tests/cassettes/test_timeseries/test_timeseries_query.yaml @@ -17,7 +17,7 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAKtWysgsKVayMtRRKsnPz1aysjS1sNRRyixJzQWKRsfWAgDfiUI2IQAAAA== + H4sIAAAAAAAAAKtWysgsKVayMtRRKsnPz1aysrDQUcosSc0FikXH1gIAPOvJbB8AAAA= headers: Access-Control-Allow-Origin: - '*' @@ -27,33 +27,35 @@ interactions: CMR-Hits: - '1' CMR-Request-Id: - - afaba4fa-c00e-4167-8b5b-d5210233bd45 + - 711e456b-e98d-45e2-984a-2d30c10f13c0 CMR-Took: - - '9590' + - '89' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - c00d9b838651815abd910c87fa7dfec1 + - ec58cb236061ecf56e5cddc88e6c3c38 Content-SHA1: - - 8023545b055340a22510e00a1904899177121112 + - fa98e100c0f7516349449e2ccc61d5efe0304a2a Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:11:01 GMT + - Wed, 30 Oct 2024 19:22:00 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked Vary: - Accept-Encoding, User-Agent Via: - - 1.1 c27b73b84ed8b00262acef150bb27b26.cloudfront.net (CloudFront) + - 1.1 24d5e218dcc2925d4bfa8f6456f56a36.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - metjU3nsFL7UGXCSk_-OJKu1DJpT_0d5G-a0Xm_e_SALw7fSURMSUg== + - cuCF6NqKA-2pw-ZL7KDi5H9ZkkrqIjMWOBIgAZCKJy8uOyp1ytWECA== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -61,7 +63,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - metjU3nsFL7UGXCSk_-OJKu1DJpT_0d5G-a0Xm_e_SALw7fSURMSUg== + - cuCF6NqKA-2pw-ZL7KDi5H9ZkkrqIjMWOBIgAZCKJy8uOyp1ytWECA== X-XSS-Protection: - 1; mode=block status: @@ -85,7 +87,7 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAKtWysgsKVayMtRRKsnPz1ayMjEw01HKLEnNBQpGx9YCANACJz8gAAAA + H4sIAAAAAAAAAKtWysgsKVayMtRRKsnPz1ayMjPXUcosSc0FikXH1gIAiWsigB8AAAA= headers: Access-Control-Allow-Origin: - '*' @@ -95,33 +97,35 @@ interactions: CMR-Hits: - '1' CMR-Request-Id: - - 2828f31d-b82c-4f4f-94f1-0e01f4840393 + - 28bcd1fb-73c6-4085-a46a-989f9698175a CMR-Took: - - '411' + - '68' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - 18b40e51142a287fb8fc4218806009f1 + - 1f60dd8a149e36862ed4697adfdfea62 Content-SHA1: - - b7828daa3781770634c536d5d7c49ef0bd9fc160 + - 05c846731445f40069635406f772866206e4072e Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:11:02 GMT + - Wed, 30 Oct 2024 19:22:00 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked Vary: - Accept-Encoding, User-Agent Via: - - 1.1 c27b73b84ed8b00262acef150bb27b26.cloudfront.net (CloudFront) + - 1.1 24d5e218dcc2925d4bfa8f6456f56a36.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - vjyS8Le3GvID67sPk-HGWLWePRdDemlVw7pAbpNK_a7NBs7bxBQd8A== + - 3gn1TNLw4PHvGrAJ6b4FUJHhLqasYidv-40zSZNbQzoed2b9u0ptMQ== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -129,7 +133,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - vjyS8Le3GvID67sPk-HGWLWePRdDemlVw7pAbpNK_a7NBs7bxBQd8A== + - 3gn1TNLw4PHvGrAJ6b4FUJHhLqasYidv-40zSZNbQzoed2b9u0ptMQ== X-XSS-Protection: - 1; mode=block status: @@ -153,26 +157,26 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAM1WbW/iOBD+K1a+XhPyQiDwLUCWRSqlIqF72lO1MokBXxM7Zzvsdav+9xsnodAu - t6q03EvVqmCPx88z88x4nowdVdIYOleG4vzBGAbBlUEVKWDttyejIAobwycj5SwlpTLVY0mMobEV - mFU5Ma5eNmgGy1PP7XWDfs92AvN2Mb5erCZgIsieSspZbeNeGQwruifNCdd2u47tOI5rw48ZjhZz - 87r7ZfpxGceJCX8blpnTcB7H4Rc3eCjM6fViZO5t17LNzd52LLvGkOckVfqKV3DGru31gsDpe/4J - nFLwPc2IaGyO6xsuCqxgCZdlTlOs/XX2LLMYlthKC2FVRfHL75KzU04ZVqTlYTq26biJHQw9b+i4 - Vm/gfDaerww4pkOYkKLkAufRn4owpVeWmG3JBBwktCB6IWIZZdvjyiu39rD+tSBOnwHBiGwpY39j - 7rwxfwYYc0gloMVxSVK6aQnqW++IkPVHw7F6Vg98r5bX8G2nVCmHnU6aMYtgoXb6dBONLd93gFan - 1UFnfzh5g2sgq/ncnGru08ZgtbxUrm/b7GnWjUSTRpIzJolQYDE5mxF32LWtwBnojLycWZV1+t5x - 5v7KiEuI2Gn6PnJBv3GmcN5uTXiBaR3SKeFQOeJRfx7xqk7rEiQKCc9b2J+IVIetMecC/tcgTKc/ - sIK+DxfySu3OmgStRYTP+3hxccPFeReNh+f7Z60MYI/bRGnAoUh3UKAhyyZUKkHXlVbKjDUFUisF - 8Mf0G1kxqgtmPoIQ6u/G0LYGQWD7fcfr+Z7f63YBxHhH0gdZ1UVwh/NKR5pk3R6xM0wGg6yf+jjo - pt11zx+Q9Tpbuxud6TDfQoDVrtA3THytJn3HjI0e69Q7UNy2771o7uflZbG0Vsd5ahAx1w26ftcP - BkHX6/uR6X8P6bJwrCLza/FN8OMN3e7UhxxvtXCZbKqYZE1NZFXd/863Dsd91QsgIy8Nc0k2RBDo - mW8bgc5AvAP5tHROkbZcDogd7XJJcrg7W4m8kffrFoIbSVklzzBOz3WTZsfkpTTTqqjySprQqBWg - JFnnR5d3LpN3CDGRqaBlI3Bjwr+ynOMMXcp923KmUYImYRLWQmuCJD2I0P+Nf7KjEuWUPaD2vZQo - owLgoOwQGZymREq0pxjFHlIcqR1Bx8ngDWF0NwvRZLaMxgkKx+Mojk9C8HM6qdbwXv8LQaqr8Z8V - SntFG7vo1yS6mUQTNI+S8L2q+U+jcTHZ/Ij6+9UivVSQDN5reJ3ld2BxSRFhWckpUxqIgCebkj1B - qhnVxCM6OY72OKcZglcQSeiJpoABjLMDPem1xI4U7mbRJ7SMrsMEaMxuPiyW8zCZLW7OMOEl4MDl - OQrH4VZ2zgy0hxFMXiCpR+irOEJxtLybjSMU3s70W1Ct29l/cQuJCW+bLvY2pItbUm8K8kcFAw7S - NPWUcf/8FxXb8TlpDAAA + H4sIAAAAAAAAAM1WbW/iOBD+K1a+Hgl5hcC3FLIsUimIhO5pT9XKJAZ8Teyc7bDXrfrfb5ykr8ut + Ki33UrUq2OPx88w8M55740CVNMZOz1Cc3xpjf2D3DKpICYu/3RslUdgY3xsZZxmplKnuKmKMjb3A + rC6I0XvaoDkszzx34IfDge2E5mo5uVxupmAiyJFKyllj4/YMhhU9kvaEa7u+YzuO49rwY0YXy4V5 + 6X+ZfVwnSWrC347l5ixaJEn0xQ1vS3N2ubwwj7Zr2ebuaDuW3WAoCpIpfcUrOBPX9gZh6Ay94AWc + SvAjzYlobZ7Xd1yUWMESrqqCZlj76x9ZbjEssZWVwqrL8pffJWcvOeVYkY6H6dim46Z2OPa8seNa + g5Hz2XjoGXBMhzAlZcUFLuI/FWFKr6wx25MpOEhpSfRCzHLK9s8rr9za4+bXgjh9BgQXZE8Z+xtz + 5435A8BYQCoBLU4qktFdR1Dfek2EbD4ajjWwBuB7s76EbwelKjnu97OcWQQLddCn22js+bEPtPqd + DvrHx5NXuAGyWSzMmeY+aw0263PletVlT7NuJZq2kpwzSYQCi+nJjLhj37ZCZ6Qz8nRmUzXpe8eZ + m56RVBCxl+n7yAX9xpnCRbc15SWmTUhnhEPliDv9+YLXTVrXIFFIeNHB/kSketyacC7gfwPCdIYj + KxwGcCGv1eGkSdhZxPi0jycXV1ycdtF6eLh50MoA9rhLlAYciewABRqxfEqlEnRba6XMWVsgjVIA + f0K/kQ2jumAWFxBC/d0Y29YoDO1g6HiDwAsGvg8gJgeS3cq6KYJrXNQ60iT3B8TOMRmN8mEW4NDP + /O0gGJHtNt+6O53pqNhDgNWh1DdMA60mfcecXdw1qXeguO3Ae9Lcz8vLYlmjjtPUIGKuG/qBH4Sj + 0PeGQWwG30M6LxyrzINGfFN8d0X3B/WhwHstXCbbKiZ5WxN53fS/063DcV/1AsjIU8Nckx0RBHrm + 20agM5AcQD4dnZdIOy6PiB3tck0KuDvfiKKV9+sWgltJWRXPMc5OdZN2x+SVNLO6rItamtCoFaAk + ef9Hl/fPk3cIMZGZoFUrcGPKv7KC4xydy33XcmZxiqZRGjVCa4MkPYjQ/41/eqASFZTdou69lCin + AuCg/DEyOMuIlOhIMUo8pDhSB4KeJ4M3hNH1PELT+TqepCiaTOIkeRGCn9NJvYX3+l8IUlON/6xQ + uiu62MW/pvHVNJ6iRZxG71XNfxqNs8nmR9TfrxbpZYLk8F7D6yy/A4srigjLK06Z0kAEPNmUHAlS + 7agm7tCL4+iIC5ojeAWRhJ5oChjAOHukJ72O2DOF63n8Ca3jyygFGvOrD8v1Ikrny6sTTHgFOHB1 + isLzcCv7JwbaxxFMniGpz9A3SYySeH09n8QoWs31W1Bvu9l/uYLERKu2i70N6XJFmk1B/qhhwEGa + pp4ybh7+Ajx7iQ5qDAAA headers: Access-Control-Allow-Origin: - '*' @@ -182,35 +186,37 @@ interactions: CMR-Hits: - '1' CMR-Request-Id: - - c86fcb1a-6d4d-4ac9-8064-a1d2691e1f27 + - 3b656c8c-8311-4de2-9346-2bb5ac03e28e CMR-Search-After: - '["pocloud",1728604800000,3264876018]' CMR-Took: - - '88' + - '461' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - dbaf0fc959991a43753de187e3c16205 + - 96e0839307861693af1303d6e3a8b2ec Content-SHA1: - - 19bfb669d92ffef5f1d292f080967c41d0b66f87 + - f6e422a23223953528752aef356c3518b6f6aac3 Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:11:02 GMT + - Wed, 30 Oct 2024 19:22:01 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked Vary: - Accept-Encoding, User-Agent Via: - - 1.1 c27b73b84ed8b00262acef150bb27b26.cloudfront.net (CloudFront) + - 1.1 24d5e218dcc2925d4bfa8f6456f56a36.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - 8oopLSC8vDARk3gK10RBxAac5T7WU_pch5x2KJ_JNDnkPRVi9jHxQQ== + - LEmlaTByUz1iRXWLZ927PQz39E0l1C_HBopKKv43m-wo-a60oTc6Gg== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -218,7 +224,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - 8oopLSC8vDARk3gK10RBxAac5T7WU_pch5x2KJ_JNDnkPRVi9jHxQQ== + - LEmlaTByUz1iRXWLZ927PQz39E0l1C_HBopKKv43m-wo-a60oTc6Gg== X-XSS-Protection: - 1; mode=block status: @@ -238,11 +244,11 @@ interactions: authorization: - DUMMY method: GET - uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&bounding_box=1.0,1.0,1.0,1.0&page_size=0 + uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&bounding_box=-100.0,30.0,-90.0,40.0&page_size=0 response: body: string: !!binary | - H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjfWUcosSc0FikXH1gIAvQrnRh8AAAA= + H4sIAAAAAAAAAKtWysgsKVaystBRKsnPz1ayMrPUUcosSc0FikXH1gIAy84O4R8AAAA= headers: Access-Control-Allow-Origin: - '*' @@ -250,35 +256,203 @@ interactions: - CMR-Hits, CMR-Request-Id, X-Request-Id, CMR-Scroll-Id, CMR-Search-After, CMR-Timed-Out, CMR-Shapefile-Original-Point-Count, CMR-Shapefile-Simplified-Point-Count CMR-Hits: - - '0' + - '8' + CMR-Request-Id: + - ac249ff9-4168-4646-b227-14c3395c6b18 + CMR-Took: + - '70' + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-MD5: + - 12cd813c97b1658e5e5895e12b8b7031 + Content-SHA1: + - d182f5f94c351c08feb5891d9a362671e32f3792 + Content-Type: + - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 + Date: + - Wed, 30 Oct 2024 19:22:01 GMT + Server: + - ServerTokens ProductOnly + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding, User-Agent + Via: + - 1.1 eb8a8ec41062dc1c67652a4d3c5cb7ce.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - QwSGIamCLrEnUWeVtXffOzqyMZjWf4U9lpkDT9sCqD1EaCCXkKKDIA== + X-Amz-Cf-Pop: + - MSP50-C1 + X-Cache: + - Miss from cloudfront + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Request-Id: + - QwSGIamCLrEnUWeVtXffOzqyMZjWf4U9lpkDT9sCqD1EaCCXkKKDIA== + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - earthaccess v0.11.0 + authorization: + - DUMMY + method: GET + uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&bounding_box=-100.0,30.0,-90.0,40.0&page_size=0 + response: + body: + string: !!binary | + H4sIAAAAAAAAAKtWysgsKVaystBRKsnPz1ayMjEx1lHKLEnNBQpGx9YCAEFnjGggAAAA + headers: + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - CMR-Hits, CMR-Request-Id, X-Request-Id, CMR-Scroll-Id, CMR-Search-After, CMR-Timed-Out, + CMR-Shapefile-Original-Point-Count, CMR-Shapefile-Simplified-Point-Count + CMR-Hits: + - '8' + CMR-Request-Id: + - 5bedca67-04b9-4b77-8877-abe63a179425 + CMR-Took: + - '444' + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-MD5: + - 2444c1e8541d3f977356691ee319ac5d + Content-SHA1: + - 406dd60e4f04e76f48365d30dc6b03ff01cd5108 + Content-Type: + - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 + Date: + - Wed, 30 Oct 2024 19:22:02 GMT + Server: + - ServerTokens ProductOnly + Strict-Transport-Security: + - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding, User-Agent + Via: + - 1.1 eb8a8ec41062dc1c67652a4d3c5cb7ce.cloudfront.net (CloudFront) + X-Amz-Cf-Id: + - qnt2xej4OET8YcXgpsyz-VrrNoA7GEXaNt5UPisw35GRUynxYz3pGQ== + X-Amz-Cf-Pop: + - MSP50-C1 + X-Cache: + - Miss from cloudfront + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Request-Id: + - qnt2xej4OET8YcXgpsyz-VrrNoA7GEXaNt5UPisw35GRUynxYz3pGQ== + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - earthaccess v0.11.0 + authorization: + - DUMMY + method: GET + uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&bounding_box=-100.0,30.0,-90.0,40.0&page_size=8 + response: + body: + string: !!binary | + H4sIAAAAAAAAAO2aa4+aShiA/wrh61FkGEDwm6vWmlTbo7jN6UmzmQNTnRSBwGi73fjfzzuCsjeT + lRSLho3xMjPvzHvnWfFBXjKeyB2rIfMw/A5v7IbMOF3B2L8P8opyInceZDcMXBrxJr+PqNyRFzEJ + 1j6VG4cJ5sHwEGsmtk0dq2ZzOJjd9UezHqyJ6YYlLAx2i1BDDghnG5qKvO/+DQsn3btPY824u+nd + 9T7OJ84/CuocZjZIUe/w8vkKTdV0pCKYVH8p/CffKeP71OXiqCd69TRTw6atYxM91iuKww3zaJwp + n098C+MV4TBGoshnLhE7tqi7DJH618+V/9gkj3DhEKFME6lNhBzV6GhaR0dKW9e+yNuGvF6thAun + 1Ie13jz2U9fOpx9AcMl5lHRaLdiHKAuaeCxxFUpivtyNBCSB4XCzm2+BS7ozp3XUZy2hRquA45w0 + rMOBI/W7ThdG+jRxYxYJy2GiH/4I/JB40sl7bxt7SxMMZmYWNt31au2vkyaEwBNPHMJGvbMZKN2O + ulJ/NB30HKnb6w1msxcmO0uWSD4LvktZmiSSx2JQU/L2ziCuS5NE2jAizbDEQ4kvqbQvjdzwt4Y4 + wW5MPRpwRiBFDjrfjgafpengQ9cZ9KXR5N3H6bjrjD5OXmhMIibRwItCFnChTUx5zOiGSlDNURiT + +F56dIC0IT7zJEh1KSEr2ozpArbZ25jgzDp5+7UhzyIiZAY/OQiLXH4fxuxXGHDiZ1P9cEVYIKaG + NISmEd+L9zfhOvBYsJjCliRY+DTN/M804fupXhjG8LqroibCSDENjBvygBxZYlhK20a22ZAnIXjw + tTUGVlRs4TboHa5fX6IhBRm6uf263UKBfsr6QB/mUg37r1W12u5gG3JK/ZIHZxQkNE6z/M0y82jX + NIRje4eGNaXfaEyhZwm/zZZg2wSicrxBwna3NE7SyCPRZuB8MsySryPUuZ+wxZK/88lCHBokEXXZ + N0Y9EB3tsgA+xKm9+efHJ769vHL5zEZwqbd2aZwpNPJ25qajwlzhLIetnjhMdZAJ3uro1t5h3dhd + wpWiG3h9lkAy/7cWsqMg7c8720H5zFGTkO9LVZg4Y79gVFVUXYM/u22alm60VZzOzAMm+vv4BvQC + 3zlphTzK8CnkK821hFyG+ggCyKPXVIdYq53dY6/6YJdzR9ZqHcOGR7pWZGDmpvn0d18Rx3D9Fl1m + lgbfzbz2vDW5XvBaR4IrVyvrZy0401RM2DJz93w8bg6fZuFugaio4tzQPic34CpygwbRQxXnBlwi + N+AqcMMxA2tuqLmhCDfo+HRueCFzPdyAa244xBqfwA1G2dyAL5QbED4nN5jV5Aar8txglsgNZhW4 + 4ZiBNTfU3FCIG4wC3PBc5nq4way54RBr8wRusMrmBvNSuQGdkxvsanKD3m5XnBvsErnBrgI3HDOw + 5oaaGwrdp9AK3Kd4LnM93GDX3HCItf12bkCobG6wL5QbVP2M3IC0KnIDUtoarjY35I77/dyw3/uP + csNRA2tuqLmhEDe0C3DDc5mr4Ya8vGpuQNoJ3KCXzA15YC6NG6xzcoNRRW6A/zJQxX8XmTuuBG4w + qsANxwysuaHmhkL3KVCB+xTPZa6HG4yaGw4sYJzADe2yucG4VG6wz8kNVjW5ASGj4txglcgNVhW4 + 4ZiBNTfU3FDo+4YCv294IXM93GBdBDcYyDJtuCoZ8KTaZXGD9XZu0NSyucG6UG5A2hm5QUPV5Aaj + 6r+LzB33+7lhv/cf5YajBtbcUHNDoe8bCtyneCFzNdyQl1fNDRo6gRtwydyQB+aPcMPX7f+WBHFN + 3kEAAA== + headers: + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - CMR-Hits, CMR-Request-Id, X-Request-Id, CMR-Scroll-Id, CMR-Search-After, CMR-Timed-Out, + CMR-Shapefile-Original-Point-Count, CMR-Shapefile-Simplified-Point-Count + CMR-Hits: + - '8' CMR-Request-Id: - - 870bef84-7026-4f45-b00b-9403dc0db237 + - fb43dccd-00cc-466d-9545-e444ef8faa14 + CMR-Search-After: + - '["ges_disc",1728680400000,3263964312]' CMR-Took: - - '74' + - '91' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - b8be39ba010d1477d4e4018d4995b785 + - b61aee4e6edf0646786b630d0ab800fc Content-SHA1: - - b06535c2225cff0f868d85f2800b98325931d1b0 + - b813080869fcf56c37b27d1cf2602d6f4906ec81 Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:14:14 GMT + - Wed, 30 Oct 2024 19:22:02 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked Vary: - Accept-Encoding, User-Agent Via: - - 1.1 40acd4d07ab1c49e82d8bb4b84de3ee6.cloudfront.net (CloudFront) + - 1.1 eb8a8ec41062dc1c67652a4d3c5cb7ce.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - 5kZLjAq8S-DrJm-WldEDfFSuPXMv-OdiQmoAuY2MQlZdYwt20EfFuQ== + - LmH_oV_2N5AbglGfzFq9dr56n_hhBO_3FWnQ1mxEQg9EZ2UaxZgNCA== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -286,7 +460,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - 5kZLjAq8S-DrJm-WldEDfFSuPXMv-OdiQmoAuY2MQlZdYwt20EfFuQ== + - LmH_oV_2N5AbglGfzFq9dr56n_hhBO_3FWnQ1mxEQg9EZ2UaxZgNCA== X-XSS-Protection: - 1; mode=block status: @@ -310,7 +484,7 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjQz01HKLEnNBQpGx9YCAMqSR3UgAAAA + H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjbXUcosSc0FikXH1gIAMO/ynx8AAAA= headers: Access-Control-Allow-Origin: - '*' @@ -320,33 +494,35 @@ interactions: CMR-Hits: - '0' CMR-Request-Id: - - 9bcf59ea-e8d1-4899-9798-de3962a42360 + - 0438f25b-40e9-4d76-80c3-d29bed912d78 CMR-Took: - - '167' + - '38' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - 19a8e525fa883ca3fd044413496b1a16 + - bf3fee0c9550fabcef8e8c380d934d70 Content-SHA1: - - 8a9c079a781ff000b62e801aa66012812fe4aaa7 + - 9e39762171bd6e99d64273dcfe2a3082a0704474 Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:14:14 GMT + - Wed, 30 Oct 2024 19:22:02 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked Vary: - Accept-Encoding, User-Agent Via: - - 1.1 40acd4d07ab1c49e82d8bb4b84de3ee6.cloudfront.net (CloudFront) + - 1.1 5b819722bdc87faf08c0415ffce844c6.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - DqPYZ8SMoUZmz1vdrTxA9EF2KRpWx8ySgRaDmytNikkIR3ii0KxAxQ== + - KTwe1uMFTpnwiLOvRtLZQeUAxFRZYgwdOZNIFz4VUXcUgj51HjR9Hg== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -354,7 +530,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - DqPYZ8SMoUZmz1vdrTxA9EF2KRpWx8ySgRaDmytNikkIR3ii0KxAxQ== + - KTwe1uMFTpnwiLOvRtLZQeUAxFRZYgwdOZNIFz4VUXcUgj51HjR9Hg== X-XSS-Protection: - 1; mode=block status: @@ -378,7 +554,7 @@ interactions: response: body: string: !!binary | - H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjHSUcosSc0FikXH1gIA4/YQ5x8AAAA= + H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjbXUcosSc0FikXH1gIAMO/ynx8AAAA= headers: Access-Control-Allow-Origin: - '*' @@ -388,21 +564,21 @@ interactions: CMR-Hits: - '0' CMR-Request-Id: - - 56af45a0-d961-4ce4-b820-1ba6836d59c4 + - c279c2ca-3027-48ef-8dd8-47b91c3c8581 CMR-Took: - - '43' + - '38' Connection: - keep-alive Content-Encoding: - gzip Content-MD5: - - f842798c1e9639028518b19f829fce08 + - bf3fee0c9550fabcef8e8c380d934d70 Content-SHA1: - - 25d0cf6a642034bf621addece15562bb7d0d0dc0 + - 9e39762171bd6e99d64273dcfe2a3082a0704474 Content-Type: - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:15:08 GMT + - Wed, 30 Oct 2024 19:22:02 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: @@ -412,11 +588,11 @@ interactions: Vary: - Accept-Encoding, User-Agent Via: - - 1.1 79864a2cd51b4f0c47f8279cb5db5dd6.cloudfront.net (CloudFront) + - 1.1 5b819722bdc87faf08c0415ffce844c6.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - TfpiAX-tmCsj4KIY127Jcvyty0LflzMG0R4TOPUeO-viFLfTX3SE1w== + - ON4kK4i6aBP1IfAUmwruz7aXQGPSs7CC4vgAa1zGS-j3a9mu3180EA== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - Miss from cloudfront X-Content-Type-Options: @@ -424,7 +600,7 @@ interactions: X-Frame-Options: - SAMEORIGIN X-Request-Id: - - TfpiAX-tmCsj4KIY127Jcvyty0LflzMG0R4TOPUeO-viFLfTX3SE1w== + - ON4kK4i6aBP1IfAUmwruz7aXQGPSs7CC4vgAa1zGS-j3a9mu3180EA== X-XSS-Protection: - 1; mode=block status: @@ -444,41 +620,60 @@ interactions: authorization: - DUMMY method: GET - uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&page_size=0 + uri: https://cmr.earthdata.nasa.gov/search/granules.umm_json?concept_id%5B%5D=C2623694361-GES_DISC&temporal%5B%5D=2024-10-11T00:00:01Z,2024-10-11T23:59:59Z&bounding_box=1.0,1.0,1.0,1.0&page_size=0 response: body: - string: "\r\n504 Gateway Time-out\r\n\r\n

504 - Gateway Time-out

\r\n\r\n\r\n" + string: !!binary | + H4sIAAAAAAAAAKtWysgsKVayMtBRKsnPz1ayMjPVUcosSc0FikXH1gIAFjQNex8AAAA= headers: + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - CMR-Hits, CMR-Request-Id, X-Request-Id, CMR-Scroll-Id, CMR-Search-After, CMR-Timed-Out, + CMR-Shapefile-Original-Point-Count, CMR-Shapefile-Simplified-Point-Count + CMR-Hits: + - '0' + CMR-Request-Id: + - 9f076db6-f821-478b-ab00-125ec846934e + CMR-Took: + - '66' Connection: - keep-alive - Content-Length: - - '132' + Content-Encoding: + - gzip + Content-MD5: + - f54bebd6ea9e4c90e3ac93c18af6f9b0 + Content-SHA1: + - e1a43d971f0207160ce02664a1326fe4f9f7f6ad Content-Type: - - text/html + - application/vnd.nasa.cmr.umm_results+json;version=1.6.6; charset=utf-8 Date: - - Wed, 30 Oct 2024 00:18:04 GMT + - Wed, 30 Oct 2024 19:22:02 GMT Server: - ServerTokens ProductOnly Strict-Transport-Security: - max-age=31536000; includeSubDomains; preload + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding, User-Agent Via: - - 1.1 e04ec6b091fdb111272443ef65179798.cloudfront.net (CloudFront) + - 1.1 5b819722bdc87faf08c0415ffce844c6.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - a8AXtQ53Pvn1SEP9UOs0TyDCYrTISLJogN92-kLp5B5JbSzRnHHiVA== + - jlojcBrvzf-TdhBOfkKxVlAFWBA2hxmS_CIW6g7QFogl_ldFlbttyQ== X-Amz-Cf-Pop: - - ORD53-C2 + - MSP50-C1 X-Cache: - - Error from cloudfront + - Miss from cloudfront X-Content-Type-Options: - nosniff X-Frame-Options: - SAMEORIGIN X-Request-Id: - - a8AXtQ53Pvn1SEP9UOs0TyDCYrTISLJogN92-kLp5B5JbSzRnHHiVA== + - jlojcBrvzf-TdhBOfkKxVlAFWBA2hxmS_CIW6g7QFogl_ldFlbttyQ== X-XSS-Protection: - 1; mode=block status: - code: 504 - message: Gateway Time-out + code: 200 + message: OK version: 1 diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index eb73e01..8f242a5 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -92,7 +92,7 @@ def test_generate_datetime_ranges(): start, exact_end_datetime, "P1M", exact=True ) assert len(exact_ranges) == 5 - assert exact_ranges[-1] == (exact_end_datetime, exact_end_datetime) + assert exact_ranges[-1] == (exact_end_datetime) exact_end_datetime = datetime(2023, 10, 25) exact_ranges = generate_datetime_ranges( @@ -233,6 +233,10 @@ def test_timeseries_query( start_datetime=start_datetime, end_datetime=end_datetime, ), + minx=-100, + miny=30, + maxx=-90, + maxy=40, ) assert len(query) == 8 diff --git a/titiler/cmr/timeseries.py b/titiler/cmr/timeseries.py index 5624a2a..63e7fdb 100644 --- a/titiler/cmr/timeseries.py +++ b/titiler/cmr/timeseries.py @@ -90,13 +90,13 @@ class TimeseriesParams(DefaultDependency): Query( description="Start datetime for timeseries request", ), - ] + ] = None end_datetime: Annotated[ Optional[str], Query( description="End datetime for timeseries request", ), - ] + ] = None step: Annotated[ Optional[str], Query( @@ -112,13 +112,13 @@ class TimeseriesParams(DefaultDependency): Query( description="If true, queries will be made for a point-in-time at each step. If false, queries will be made for the entire interval between steps" ), - ] = False - # intervals: Annotated[ - # Optional[List[List[str]]], - # Query( - # description="Optional list of specific time points or time intervals to summarize over" - # ), - # ] = None + ] = None + datetimes: Annotated[ + Optional[str], + Query( + description="Optional list of comma-separated specific time points or time intervals to summarize over" + ), + ] = None def parse_duration(duration: str) -> relativedelta: @@ -147,13 +147,13 @@ def parse_duration(duration: str) -> relativedelta: def generate_datetime_ranges( start_datetime: datetime, end_datetime: datetime, step: str, exact: bool = False -) -> List[Tuple[datetime, datetime]]: +) -> List[Union[Tuple[datetime], Tuple[datetime, datetime]]]: """Generate datetime ranges""" start = start_datetime end = end_datetime step_delta = parse_duration(step) - ranges = [] + ranges: List[Union[Tuple[datetime], Tuple[datetime, datetime]]] = [] current = start step_timedelta = (current + step_delta) - current @@ -161,9 +161,9 @@ def generate_datetime_ranges( while current < end: if exact: - # For exact ranges, start and end are the same + # For exact case, return a tuple with just one exact datetime next_step = current + step_delta - ranges.append((current, current)) + ranges.append((current,)) else: next_step = min(current + step_delta, end) if next_step == end: @@ -180,7 +180,7 @@ def generate_datetime_ranges( current = next_step if current == end: - ranges.append((end, end)) + ranges.append((end,)) if not ranges: return [(start, end)] @@ -200,7 +200,7 @@ class CMRQueryParameters(BaseModel): def timeseries_query( concept_id: ConceptID, - timeseries_params=Depends(TimeseriesParams), + timeseries_params: TimeseriesParams = Depends(TimeseriesParams), minx: Optional[float] = None, miny: Optional[float] = None, maxx: Optional[float] = None, @@ -211,8 +211,12 @@ def timeseries_query( If no step is provided with timeseries_params, a query will be sent to CMR to identify all unique timesteps in granules between the provided start/stop_datetime. """ - # Validate and process timeseries parameters - if ( + # if a comma-separated list of datetimes is provided, just use those + if timeseries_params.datetimes: + datetime_params = timeseries_params.datetimes.split(",") + + # if a start, end, and step are provided use the generate_datetime_ranges function + elif ( timeseries_params.start_datetime and timeseries_params.end_datetime and timeseries_params.step @@ -225,12 +229,18 @@ def timeseries_query( timeseries_params.end_datetime.replace("Z", "+00:00") ), step=timeseries_params.step, - exact=timeseries_params.exact, + exact=timeseries_params.exact + if timeseries_params.exact is not None + else False, ) + datetime_params = [ - "/".join([start.isoformat(), end.isoformat()]) - for start, end in datetime_ranges + "/".join([t.isoformat() for t in datetime_range]) + for datetime_range in datetime_ranges ] + + # if a start and end are provided but no step, query CMR to identify unique datetimes from + # a granule search elif ( timeseries_params.start_datetime and timeseries_params.end_datetime @@ -255,6 +265,7 @@ def timeseries_query( concept_id=concept_id, **search_params, ) + # if there are no results we get an IndexError which we should just treat as an empty list except IndexError: return [] @@ -273,7 +284,7 @@ def timeseries_query( else: raise HTTPException( status_code=400, - detail="you must provide at least start_datetime and end_datetime", + detail="you must provide at least start_datetime and end_datetime, or specific datetimes", ) return [ @@ -312,8 +323,6 @@ async def timestep_request( raise ValueError(f"{method} must be one of GET or POST") response = await _method(url, **kwargs) - # if response.status_code != 200: - # raise HTTPException(status_code=response.status_code, detail=response.text) return response @@ -337,6 +346,7 @@ def register(self, factory: Endpoints): "for a timeseries query" } }, + tags=["Timeseries"], ) def get_timeseries_parameters( query=Depends(timeseries_query), @@ -376,16 +386,13 @@ async def timeseries_geojson_statistics( histogram_params=Depends(factory.histogram_dependency), image_params=Depends(factory.img_part_dependency), ): - # Construct the base URL for the original endpoint base_url = str(factory.url_for(request, "geojson_statistics")) - # Create a list of URLs for each time interval urls = [] for timeseries_query_params in query: - url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params})}" + url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params.model_dump()})}" urls.append(url) - # Fetch all URLs concurrently timestep_requests = await asyncio.gather( *[ timestep_request( @@ -398,8 +405,7 @@ async def timeseries_geojson_statistics( ] ) - # Combine results into a single response - datetime_strs = [d["datetime"] for d in query] + datetime_strs = [d.datetime for d in query] geojson.properties["statistics"] = {} for r, datetime_str in zip(timestep_requests, datetime_strs): if r.status_code == 200: @@ -427,6 +433,7 @@ async def timeseries_tilejson( Literal[tuple(factory.supported_tms.list())], Path(description="Identifier for a supported TileMatrixSet"), ], + query=Depends(timeseries_query), tile_format: Annotated[ Optional[ImageType], Query( @@ -455,7 +462,6 @@ async def timeseries_tilejson( color_formula=Depends(factory.color_formula_dependency), colormap=Depends(factory.colormap_dependency), render_params=Depends(factory.render_dependency), - query=Depends(timeseries_query), ) -> TimeseriesTileJSON: # Construct the base URL for the original endpoint base_url = str( @@ -464,21 +470,18 @@ async def timeseries_tilejson( ) ) - # Create a list of URLs for each time interval urls = [] for timeseries_query_params in query: - url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params})}" + url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params.model_dump()})}" urls.append(url) - # Fetch all URLs concurrently timestep_requests = await asyncio.gather( *[timestep_request(url, method="GET") for url in urls] ) results = [request.json() for request in timestep_requests] - # Combine results into a single response - datetime_strs = [d["datetime"] for d in query] + datetime_strs = [d.datetime for d in query] return dict(zip(datetime_strs, results)) @@ -523,7 +526,6 @@ async def bbox_timeseries_image( render_params=Depends(factory.render_dependency), ): """Create image from a bbox.""" - # Construct the base URL for the original endpoint path_params = { "minx": minx, "miny": miny, @@ -548,31 +550,26 @@ async def bbox_timeseries_image( ) ) - # Create a list of URLs for each time interval urls = [] for timeseries_query_params in query: - url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params})}" - # url = url.replace( - # "http://localhost:8081", - # "https://9ox7r6pi8c.execute-api.us-west-2.amazonaws.com", - # ) + url = f"{base_url}?{urlencode({**request.query_params, **timeseries_query_params.model_dump()})}" urls.append(url) - # Fetch all URLs concurrently timestep_requests = await asyncio.gather( *[timestep_request(url, method="GET", timeout=None) for url in urls] ) - print("n requests returned:", len(timestep_requests)) pngs = [] for r in timestep_requests: if r.status_code == 200: pngs.append(Image.open(io.BytesIO(r.content))) + elif r.status_code == 204: + continue + else: + r.raise_for_status() - # Create a BytesIO object to hold the GIF gif_bytes = io.BytesIO() - # Save images as a GIF pngs[0].save( gif_bytes, format="GIF", @@ -582,8 +579,6 @@ async def bbox_timeseries_image( duration=1000 // fps, ) - # Seek to the start gif_bytes.seek(0) - # Create a streaming response to return the gif return StreamingResponse(gif_bytes, media_type=TimeseriesMediaType.gif)