From 0c114bf0b89e427d3ef6c16aa78839cc9f74f209 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 17 Jun 2024 22:41:31 +1000 Subject: [PATCH 1/6] Add support for variable length data types Data type sizes are now represented by `DataTypeSize` instead of usize Also adds `ArraySize`. Both are `Fixed` only for now. Need to support `Variable` throughout the codebase. Change codec API in prep for variable sized data types Enable `{Array,DataType}Size::Variable` Implement `CowArrayBytes::validate()` and add `CodecError::InvalidVariableSizedArrayOffsets` Use `CowArrayBytes::validate()` impl `From` for `CowArrayBytes` for various types Array `_element` methods now use `T: Element` Add `vlen` codec metadata Fix codecs bench Implement an experimental vlen codec Use `impl Into>` in array methods Use `RawBytesCow` consistently Remove various vlen todo's Cleanup `ArrayBytes` Use `ArrayError::InvalidElementValue` for invalid string encodings Add `ArraySubset::contains()` Add `FillValue::new_empty()` Add remaining vlen support to array `store_` methods and improve vlen validation Add remaining vlen support to array `retrieve_` methods Partial decoding in the vlen filter Fix async vlen errors Sharding codec vlen support Add vlen support to sharding partial decoder vlen support for sharded_readable_ext `offsets_u64_to_usize` handle 32-bit system Minor FillValue doc update Remove unused ArraySubset methods and add related convenience functions Add cities test Add `Arrow32` vlen encoding Add support for Interleave32 (Zarr V2) vlen encoding fmt clippy Set minimum version for num-complex Fix `ArrayBytes` from `&[u8; N]` for rust < 1.77 Add `binary` data type Vlen improve docs and test various encodings. Fix `cities.csv` encoding. `vlen` change encoding names Validate `vlen` codec `length32` encoding against `zarr-python` v2 Don't store `zarrs` metadata in cities test output Split `vlen` into `vlen` and `vlen_interleaved` Vlen supports separate index/dat encoding with full codec chains. Fix typesize in vlen `index_codecs` metadata Add support for `String` fill value metadata Add `FillValueMetadata::Unsupported` `ArrayMetadata` can be serialised and deserialised with an unsupported `fill_value`, but `Array` creation will fail. vlen cleanup Change vlen codec identifiers given they are experimental Move duplicate `extract_decoded_regions` fn into `array_bytes` + other minor changes Minor vlen_partial_decoder cleanup Add support for `zarr-python` nonconformant `|O` V2 data type Support conversion of Zarr V2 arrats with `vlen-*` codecs to V3 Update root docs for new vlen related codecs/data types Cleanup `get_vlen_bytes_and_offsets` --- CHANGELOG.md | 34 +- Cargo.toml | 6 +- benches/codecs.rs | 8 +- doc/status/codecs.md | 44 +- doc/status/data_types.md | 9 +- examples/sharded_array_write_read.rs | 12 +- src/array.rs | 25 +- src/array/array_async_readable.rs | 285 +- src/array/array_async_readable_writable.rs | 151 +- src/array/array_async_writable.rs | 121 +- src/array/array_builder.rs | 26 +- src/array/array_bytes.rs | 598 + src/array/array_errors.rs | 11 +- src/array/array_representation.rs | 78 +- src/array/array_sync_readable.rs | 284 +- src/array/array_sync_readable_writable.rs | 145 +- src/array/array_sync_sharded_readable_ext.rs | 211 +- src/array/array_sync_writable.rs | 135 +- src/array/codec.rs | 128 +- .../codec/array_partial_decoder_cache.rs | 40 +- src/array/codec/array_to_array/bitround.rs | 73 +- .../array_to_array/bitround/bitround_codec.rs | 25 +- .../bitround/bitround_partial_decoder.rs | 26 +- src/array/codec/array_to_array/transpose.rs | 81 +- .../transpose/transpose_codec.rs | 146 +- .../transpose/transpose_partial_decoder.rs | 197 +- src/array/codec/array_to_bytes.rs | 2 + src/array/codec/array_to_bytes/bytes.rs | 28 +- .../codec/array_to_bytes/bytes/bytes_codec.rs | 87 +- .../bytes/bytes_partial_decoder.rs | 112 +- src/array/codec/array_to_bytes/codec_chain.rs | 174 +- src/array/codec/array_to_bytes/pcodec.rs | 26 +- .../array_to_bytes/pcodec/pcodec_codec.rs | 28 +- .../pcodec/pcodec_partial_decoder.rs | 45 +- src/array/codec/array_to_bytes/sharding.rs | 58 +- .../array_to_bytes/sharding/sharding_codec.rs | 302 +- .../sharding/sharding_partial_decoder.rs | 620 +- src/array/codec/array_to_bytes/vlen.rs | 198 + .../codec/array_to_bytes/vlen/vlen_codec.rs | 305 + .../vlen/vlen_partial_decoder.rs | 178 + .../codec/array_to_bytes/vlen_interleaved.rs | 83 + .../vlen_interleaved_codec.rs | 157 + .../vlen_interleaved_partial_decoder.rs | 119 + src/array/codec/array_to_bytes/zfp.rs | 33 +- .../codec/array_to_bytes/zfp/zfp_codec.rs | 30 +- .../array_to_bytes/zfp/zfp_partial_decoder.rs | 41 +- .../codec/byte_interval_partial_decoder.rs | 11 +- .../codec/bytes_partial_decoder_cache.rs | 9 +- src/array/codec/bytes_to_bytes/blosc.rs | 18 +- .../codec/bytes_to_bytes/blosc/blosc_codec.rs | 11 +- .../blosc/blosc_partial_decoder.rs | 13 +- src/array/codec/bytes_to_bytes/bz2.rs | 18 +- .../codec/bytes_to_bytes/bz2/bz2_codec.rs | 10 +- .../bytes_to_bytes/bz2/bz2_partial_decoder.rs | 9 +- .../bytes_to_bytes/crc32c/crc32c_codec.rs | 10 +- .../crc32c/crc32c_partial_decoder.rs | 9 +- .../codec/bytes_to_bytes/gzip/gzip_codec.rs | 10 +- .../gzip/gzip_partial_decoder.rs | 9 +- .../test_unbounded/test_unbounded_codec.rs | 12 +- .../test_unbounded_partial_decoder.rs | 9 +- .../codec/bytes_to_bytes/zstd/zstd_codec.rs | 10 +- .../zstd/zstd_partial_decoder.rs | 9 +- src/array/data_type.rs | 128 +- src/array/element.rs | 267 + src/array/fill_value.rs | 25 + src/array_subset.rs | 238 +- src/metadata/array.rs | 54 +- src/metadata/v2/array.rs | 7 +- src/metadata/v2/codec/blosc.rs | 46 +- src/metadata/v3.rs | 4 + src/metadata/v3/codec/vlen.rs | 84 + src/metadata/v3/codec/vlen_interleaved.rs | 37 + src/metadata/v3/fill_value.rs | 77 +- src/storage/storage_async.rs | 2 +- src/storage/store/store_async/opendal.rs | 2 +- src/storage/store/store_sync/opendal.rs | 2 +- tests/array_async.rs | 234 +- tests/array_sync.rs | 264 +- tests/cities.rs | 132 + tests/data/cities.csv | 47868 ++++++++++++++++ tests/data/v2/cities.zarr/.zarray | 18 + tests/data/v2/cities.zarr/0 | Bin 0 -> 11835 bytes tests/data/v2/cities.zarr/1 | Bin 0 -> 12423 bytes tests/data/v2/cities.zarr/10 | Bin 0 -> 13146 bytes tests/data/v2/cities.zarr/11 | Bin 0 -> 13092 bytes tests/data/v2/cities.zarr/12 | Bin 0 -> 13491 bytes tests/data/v2/cities.zarr/13 | Bin 0 -> 13343 bytes tests/data/v2/cities.zarr/14 | Bin 0 -> 13323 bytes tests/data/v2/cities.zarr/15 | Bin 0 -> 13353 bytes tests/data/v2/cities.zarr/16 | Bin 0 -> 13283 bytes tests/data/v2/cities.zarr/17 | Bin 0 -> 13655 bytes tests/data/v2/cities.zarr/18 | Bin 0 -> 13309 bytes tests/data/v2/cities.zarr/19 | Bin 0 -> 13626 bytes tests/data/v2/cities.zarr/2 | Bin 0 -> 12776 bytes tests/data/v2/cities.zarr/20 | Bin 0 -> 13293 bytes tests/data/v2/cities.zarr/21 | Bin 0 -> 13595 bytes tests/data/v2/cities.zarr/22 | Bin 0 -> 13544 bytes tests/data/v2/cities.zarr/23 | Bin 0 -> 13599 bytes tests/data/v2/cities.zarr/24 | Bin 0 -> 13532 bytes tests/data/v2/cities.zarr/25 | Bin 0 -> 13451 bytes tests/data/v2/cities.zarr/26 | Bin 0 -> 13650 bytes tests/data/v2/cities.zarr/27 | Bin 0 -> 13572 bytes tests/data/v2/cities.zarr/28 | Bin 0 -> 13814 bytes tests/data/v2/cities.zarr/29 | Bin 0 -> 13473 bytes tests/data/v2/cities.zarr/3 | Bin 0 -> 12750 bytes tests/data/v2/cities.zarr/30 | Bin 0 -> 13598 bytes tests/data/v2/cities.zarr/31 | Bin 0 -> 13732 bytes tests/data/v2/cities.zarr/32 | Bin 0 -> 13650 bytes tests/data/v2/cities.zarr/33 | Bin 0 -> 13760 bytes tests/data/v2/cities.zarr/34 | Bin 0 -> 13974 bytes tests/data/v2/cities.zarr/35 | Bin 0 -> 13718 bytes tests/data/v2/cities.zarr/36 | Bin 0 -> 13835 bytes tests/data/v2/cities.zarr/37 | Bin 0 -> 13888 bytes tests/data/v2/cities.zarr/38 | Bin 0 -> 13909 bytes tests/data/v2/cities.zarr/39 | Bin 0 -> 13990 bytes tests/data/v2/cities.zarr/4 | Bin 0 -> 12609 bytes tests/data/v2/cities.zarr/40 | Bin 0 -> 13891 bytes tests/data/v2/cities.zarr/41 | Bin 0 -> 13699 bytes tests/data/v2/cities.zarr/42 | Bin 0 -> 13649 bytes tests/data/v2/cities.zarr/43 | Bin 0 -> 14085 bytes tests/data/v2/cities.zarr/44 | Bin 0 -> 13894 bytes tests/data/v2/cities.zarr/45 | Bin 0 -> 13938 bytes tests/data/v2/cities.zarr/46 | Bin 0 -> 13053 bytes tests/data/v2/cities.zarr/47 | Bin 0 -> 11498 bytes tests/data/v2/cities.zarr/5 | Bin 0 -> 12607 bytes tests/data/v2/cities.zarr/6 | Bin 0 -> 13037 bytes tests/data/v2/cities.zarr/7 | Bin 0 -> 13101 bytes tests/data/v2/cities.zarr/8 | Bin 0 -> 13152 bytes tests/data/v2/cities.zarr/9 | Bin 0 -> 13001 bytes tests/data/v2_cities.py | 19 + tests/data/v3/cities.zarr/c/0 | Bin 0 -> 11835 bytes tests/data/v3/cities.zarr/c/1 | Bin 0 -> 12423 bytes tests/data/v3/cities.zarr/c/10 | Bin 0 -> 13146 bytes tests/data/v3/cities.zarr/c/11 | Bin 0 -> 13092 bytes tests/data/v3/cities.zarr/c/12 | Bin 0 -> 13491 bytes tests/data/v3/cities.zarr/c/13 | Bin 0 -> 13343 bytes tests/data/v3/cities.zarr/c/14 | Bin 0 -> 13323 bytes tests/data/v3/cities.zarr/c/15 | Bin 0 -> 13353 bytes tests/data/v3/cities.zarr/c/16 | Bin 0 -> 13283 bytes tests/data/v3/cities.zarr/c/17 | Bin 0 -> 13655 bytes tests/data/v3/cities.zarr/c/18 | Bin 0 -> 13309 bytes tests/data/v3/cities.zarr/c/19 | Bin 0 -> 13626 bytes tests/data/v3/cities.zarr/c/2 | Bin 0 -> 12776 bytes tests/data/v3/cities.zarr/c/20 | Bin 0 -> 13293 bytes tests/data/v3/cities.zarr/c/21 | Bin 0 -> 13595 bytes tests/data/v3/cities.zarr/c/22 | Bin 0 -> 13544 bytes tests/data/v3/cities.zarr/c/23 | Bin 0 -> 13599 bytes tests/data/v3/cities.zarr/c/24 | Bin 0 -> 13532 bytes tests/data/v3/cities.zarr/c/25 | Bin 0 -> 13451 bytes tests/data/v3/cities.zarr/c/26 | Bin 0 -> 13650 bytes tests/data/v3/cities.zarr/c/27 | Bin 0 -> 13572 bytes tests/data/v3/cities.zarr/c/28 | Bin 0 -> 13814 bytes tests/data/v3/cities.zarr/c/29 | Bin 0 -> 13473 bytes tests/data/v3/cities.zarr/c/3 | Bin 0 -> 12750 bytes tests/data/v3/cities.zarr/c/30 | Bin 0 -> 13598 bytes tests/data/v3/cities.zarr/c/31 | Bin 0 -> 13732 bytes tests/data/v3/cities.zarr/c/32 | Bin 0 -> 13650 bytes tests/data/v3/cities.zarr/c/33 | Bin 0 -> 13760 bytes tests/data/v3/cities.zarr/c/34 | Bin 0 -> 13974 bytes tests/data/v3/cities.zarr/c/35 | Bin 0 -> 13718 bytes tests/data/v3/cities.zarr/c/36 | Bin 0 -> 13835 bytes tests/data/v3/cities.zarr/c/37 | Bin 0 -> 13888 bytes tests/data/v3/cities.zarr/c/38 | Bin 0 -> 13909 bytes tests/data/v3/cities.zarr/c/39 | Bin 0 -> 13990 bytes tests/data/v3/cities.zarr/c/4 | Bin 0 -> 12609 bytes tests/data/v3/cities.zarr/c/40 | Bin 0 -> 13891 bytes tests/data/v3/cities.zarr/c/41 | Bin 0 -> 13699 bytes tests/data/v3/cities.zarr/c/42 | Bin 0 -> 13649 bytes tests/data/v3/cities.zarr/c/43 | Bin 0 -> 14085 bytes tests/data/v3/cities.zarr/c/44 | Bin 0 -> 13894 bytes tests/data/v3/cities.zarr/c/45 | Bin 0 -> 13938 bytes tests/data/v3/cities.zarr/c/46 | Bin 0 -> 13053 bytes tests/data/v3/cities.zarr/c/47 | Bin 0 -> 11498 bytes tests/data/v3/cities.zarr/c/5 | Bin 0 -> 12607 bytes tests/data/v3/cities.zarr/c/6 | Bin 0 -> 13037 bytes tests/data/v3/cities.zarr/c/7 | Bin 0 -> 13101 bytes tests/data/v3/cities.zarr/c/8 | Bin 0 -> 13152 bytes tests/data/v3/cities.zarr/c/9 | Bin 0 -> 13001 bytes tests/data/v3/cities.zarr/zarr.json | 28 + 179 files changed, 53040 insertions(+), 2198 deletions(-) create mode 100644 src/array/array_bytes.rs create mode 100644 src/array/codec/array_to_bytes/vlen.rs create mode 100644 src/array/codec/array_to_bytes/vlen/vlen_codec.rs create mode 100644 src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs create mode 100644 src/array/codec/array_to_bytes/vlen_interleaved.rs create mode 100644 src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs create mode 100644 src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs create mode 100644 src/array/element.rs create mode 100644 src/metadata/v3/codec/vlen.rs create mode 100644 src/metadata/v3/codec/vlen_interleaved.rs create mode 100644 tests/cities.rs create mode 100644 tests/data/cities.csv create mode 100644 tests/data/v2/cities.zarr/.zarray create mode 100644 tests/data/v2/cities.zarr/0 create mode 100644 tests/data/v2/cities.zarr/1 create mode 100644 tests/data/v2/cities.zarr/10 create mode 100644 tests/data/v2/cities.zarr/11 create mode 100644 tests/data/v2/cities.zarr/12 create mode 100644 tests/data/v2/cities.zarr/13 create mode 100644 tests/data/v2/cities.zarr/14 create mode 100644 tests/data/v2/cities.zarr/15 create mode 100644 tests/data/v2/cities.zarr/16 create mode 100644 tests/data/v2/cities.zarr/17 create mode 100644 tests/data/v2/cities.zarr/18 create mode 100644 tests/data/v2/cities.zarr/19 create mode 100644 tests/data/v2/cities.zarr/2 create mode 100644 tests/data/v2/cities.zarr/20 create mode 100644 tests/data/v2/cities.zarr/21 create mode 100644 tests/data/v2/cities.zarr/22 create mode 100644 tests/data/v2/cities.zarr/23 create mode 100644 tests/data/v2/cities.zarr/24 create mode 100644 tests/data/v2/cities.zarr/25 create mode 100644 tests/data/v2/cities.zarr/26 create mode 100644 tests/data/v2/cities.zarr/27 create mode 100644 tests/data/v2/cities.zarr/28 create mode 100644 tests/data/v2/cities.zarr/29 create mode 100644 tests/data/v2/cities.zarr/3 create mode 100644 tests/data/v2/cities.zarr/30 create mode 100644 tests/data/v2/cities.zarr/31 create mode 100644 tests/data/v2/cities.zarr/32 create mode 100644 tests/data/v2/cities.zarr/33 create mode 100644 tests/data/v2/cities.zarr/34 create mode 100644 tests/data/v2/cities.zarr/35 create mode 100644 tests/data/v2/cities.zarr/36 create mode 100644 tests/data/v2/cities.zarr/37 create mode 100644 tests/data/v2/cities.zarr/38 create mode 100644 tests/data/v2/cities.zarr/39 create mode 100644 tests/data/v2/cities.zarr/4 create mode 100644 tests/data/v2/cities.zarr/40 create mode 100644 tests/data/v2/cities.zarr/41 create mode 100644 tests/data/v2/cities.zarr/42 create mode 100644 tests/data/v2/cities.zarr/43 create mode 100644 tests/data/v2/cities.zarr/44 create mode 100644 tests/data/v2/cities.zarr/45 create mode 100644 tests/data/v2/cities.zarr/46 create mode 100644 tests/data/v2/cities.zarr/47 create mode 100644 tests/data/v2/cities.zarr/5 create mode 100644 tests/data/v2/cities.zarr/6 create mode 100644 tests/data/v2/cities.zarr/7 create mode 100644 tests/data/v2/cities.zarr/8 create mode 100644 tests/data/v2/cities.zarr/9 create mode 100644 tests/data/v2_cities.py create mode 100644 tests/data/v3/cities.zarr/c/0 create mode 100644 tests/data/v3/cities.zarr/c/1 create mode 100644 tests/data/v3/cities.zarr/c/10 create mode 100644 tests/data/v3/cities.zarr/c/11 create mode 100644 tests/data/v3/cities.zarr/c/12 create mode 100644 tests/data/v3/cities.zarr/c/13 create mode 100644 tests/data/v3/cities.zarr/c/14 create mode 100644 tests/data/v3/cities.zarr/c/15 create mode 100644 tests/data/v3/cities.zarr/c/16 create mode 100644 tests/data/v3/cities.zarr/c/17 create mode 100644 tests/data/v3/cities.zarr/c/18 create mode 100644 tests/data/v3/cities.zarr/c/19 create mode 100644 tests/data/v3/cities.zarr/c/2 create mode 100644 tests/data/v3/cities.zarr/c/20 create mode 100644 tests/data/v3/cities.zarr/c/21 create mode 100644 tests/data/v3/cities.zarr/c/22 create mode 100644 tests/data/v3/cities.zarr/c/23 create mode 100644 tests/data/v3/cities.zarr/c/24 create mode 100644 tests/data/v3/cities.zarr/c/25 create mode 100644 tests/data/v3/cities.zarr/c/26 create mode 100644 tests/data/v3/cities.zarr/c/27 create mode 100644 tests/data/v3/cities.zarr/c/28 create mode 100644 tests/data/v3/cities.zarr/c/29 create mode 100644 tests/data/v3/cities.zarr/c/3 create mode 100644 tests/data/v3/cities.zarr/c/30 create mode 100644 tests/data/v3/cities.zarr/c/31 create mode 100644 tests/data/v3/cities.zarr/c/32 create mode 100644 tests/data/v3/cities.zarr/c/33 create mode 100644 tests/data/v3/cities.zarr/c/34 create mode 100644 tests/data/v3/cities.zarr/c/35 create mode 100644 tests/data/v3/cities.zarr/c/36 create mode 100644 tests/data/v3/cities.zarr/c/37 create mode 100644 tests/data/v3/cities.zarr/c/38 create mode 100644 tests/data/v3/cities.zarr/c/39 create mode 100644 tests/data/v3/cities.zarr/c/4 create mode 100644 tests/data/v3/cities.zarr/c/40 create mode 100644 tests/data/v3/cities.zarr/c/41 create mode 100644 tests/data/v3/cities.zarr/c/42 create mode 100644 tests/data/v3/cities.zarr/c/43 create mode 100644 tests/data/v3/cities.zarr/c/44 create mode 100644 tests/data/v3/cities.zarr/c/45 create mode 100644 tests/data/v3/cities.zarr/c/46 create mode 100644 tests/data/v3/cities.zarr/c/47 create mode 100644 tests/data/v3/cities.zarr/c/5 create mode 100644 tests/data/v3/cities.zarr/c/6 create mode 100644 tests/data/v3/cities.zarr/c/7 create mode 100644 tests/data/v3/cities.zarr/c/8 create mode 100644 tests/data/v3/cities.zarr/c/9 create mode 100644 tests/data/v3/cities.zarr/zarr.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d85801a7..2086bd88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + - Add `ArrayBytes`, `RawBytes`, `RawBytesOffsets`, and `ArrayBytesError` + - These can represent array data with fixed and variable length data types + - Add `array::Element[Owned]` traits representing array elements + - Supports conversion to and from `ArrayBytes` + - Add `array::ElementFixedLength` marker trait + - Add experimental `vlen` and `vlen_interleaved` codec for variable length data types + - `vlen_interleaved` is for legacy support of Zarr V2 `vlen-utf8`/`vlen-bytes`/`vlen-array` codecs + - Add `DataType::{String,Binary}` data types + - These are likely to become standardised in the future and are not feature gated + - Add `ArraySubset::contains()` + - Add `FillValueMetadata::{String,Unsupported}` + - `ArrayMetadata` can be serialised and deserialised with an unsupported `fill_value`, but `Array` creation will fail. + - Implement `From<{[u8; N],&[u8; N],String,&str}>` for `FillValue` + - Add `ArraySize` and `DataTypeSize` + - Add `DataType::fixed_size()` that returns `Option`. Returns `None` for variable length data types. + - Add `ArrayError::IncompatibleElementType` (replaces `ArrayError::IncompatibleElementSize`) + - Add `ArrayError::InvalidElementValue` + ### Changed - Use `[async_]retrieve_array_subset_opt` internally in `Array::[async_]retrieve_chunks_opt` - **Breaking**: Replace `[Async]ArrayPartialDecoderTraits::element_size()` with `data_type()` + - Array `_store` methods now use `impl Into>` instead of `&[u8]` for the input bytes + - **Breaking**: Array `_store_{elements,ndarray}` methods now use `T: Element` instead of `T: bytemuck::Pod` + - **Breaking**: Array `_retrieve_{elements,ndarray}` methods now use `T: ElementOwned` instead of `T: bytemuck::Pod` + - Optimised `Array::[async_]store_array_subset_opt` when the subset is a subset of a single chunk + - Make `transmute_to_bytes` public + - Relax `ndarray_into_vec` from `T: bytemuck:Pod` to `T: Clone` + - **Breaking**: `DataType::size()` now returns a `DataTypeSize` instead of `usize` + - **Breaking**: `ArrayCodecTraits::{encode/decode}` have been specialised into `ArrayTo{Array,Bytes}CodecTraits::{encode/decode}` ### Removed - **Breaking**: Remove `into_array_view` array and codec API - This was not fully utilised, not applicable to variable sized data types, and quite unsafe for a public API - - Remove internal `ChunksPerShardError` and just use `CodecError::Other` + - **Breaking**: Remove internal `ChunksPerShardError` and just use `CodecError::Other` + - **Breaking**: Remove `array_subset::{ArrayExtractBytesError,ArrayStoreBytesError}` + - **Breaking**: Remove `ArraySubset::{extract,store}_bytes[_unchecked]`, they are replaced by methods in `ArrayBytes` + - **Breaking**: Remove `array::validate_element_size` and `ArrayError::IncompatibleElementSize` + - The internal validation in array `_element` methods is now more strict than just matching the element size + - Example: `u16` must match `uint16` data type and will not match `int16` or `float16` ### Fixed - Fix an unnecessary copy in `async_store_set_partial_values` diff --git a/Cargo.toml b/Cargo.toml index c2d450b7..c2bbe2cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ async-lock = { version = "3.2.0", optional = true } async-recursion = { version = "1.0.5", optional = true } async-trait = { version = "0.1.74", optional = true } blosc-sys = { version = "0.3.4", package = "blosc-src", features = ["snappy", "lz4", "zlib", "zstd"], optional = true } -bytemuck = { version = "1.14.0", features = ["extern_crate_alloc", "must_cast"] } +bytemuck = { version = "1.14.0", features = ["extern_crate_alloc", "must_cast", "min_const_generics"] } bytes = "1.6.0" bzip2 = { version = "0.4.4", optional = true, features = ["static"] } crc32c = { version = "0.6.5", optional = true } @@ -75,6 +75,10 @@ zfp-sys = {version = "0.1.15", features = ["static"], optional = true } zip = { version = "2.1.3", optional = true } zstd = { version = "0.13.1", features = ["zstdmt"], optional = true } +[dependencies.num-complex] +version = "0.4.3" +features = ["bytemuck"] + [dev-dependencies] chrono = "0.4" criterion = "0.5.1" diff --git a/benches/codecs.rs b/benches/codecs.rs index 25441c0c..f44bfaff 100644 --- a/benches/codecs.rs +++ b/benches/codecs.rs @@ -8,9 +8,10 @@ use zarrs::array::{ codec::{ array_to_bytes::bytes::Endianness, bytes_to_bytes::blosc::{BloscCompressor, BloscShuffleMode}, - ArrayCodecTraits, BloscCodec, BytesCodec, BytesToBytesCodecTraits, CodecOptions, + ArrayCodecTraits, ArrayToBytesCodecTraits, BloscCodec, BytesCodec, BytesToBytesCodecTraits, + CodecOptions, }, - BytesRepresentation, ChunkRepresentation, DataType, + BytesRepresentation, ChunkRepresentation, DataType, Element, }; fn codec_bytes(c: &mut Criterion) { @@ -35,12 +36,13 @@ fn codec_bytes(c: &mut Criterion) { .unwrap(); let data = vec![0u8; size3.try_into().unwrap()]; + let bytes = Element::into_array_bytes(&DataType::UInt8, &data).unwrap(); group.throughput(Throughput::Bytes(size3)); // encode and decode have the same implementation group.bench_function(BenchmarkId::new("encode_decode", size3), |b| { b.iter(|| { codec - .encode(Cow::Borrowed(&data), &rep, &CodecOptions::default()) + .encode(bytes.clone(), &rep, &CodecOptions::default()) .unwrap() }); }); diff --git a/doc/status/codecs.md b/doc/status/codecs.md index a4587c7c..205aa0f3 100644 --- a/doc/status/codecs.md +++ b/doc/status/codecs.md @@ -1,16 +1,18 @@ -| Codec Type | Codec | ZEP | V3 | V2 | Feature Flag* | -| -------------- | ------------------------- | ----------------- | ------- | ------- | ------------- | -| Array to Array | [transpose] | [ZEP0001] | ✓ | | **transpose** | -| | [bitround] (experimental) | | ✓ | | bitround | -| Array to Bytes | [bytes] | [ZEP0001] | ✓ | | | -| | [sharding_indexed] | [ZEP0002] | ✓ | | **sharding** | -| | [zfp] (experimental) | | ✓ | | zfp | -| | [pcodec] (experimental) | | ✓ | | pcodec | -| Bytes to Bytes | [blosc] | [ZEP0001] | ✓ | ✓ | **blosc** | -| | [gzip] | [ZEP0001] | ✓ | ✓ | **gzip** | -| | [crc32c] | [ZEP0002] | ✓ | | **crc32c** | -| | [zstd] | [zarr-specs #256] | ✓ | | zstd | -| | [bz2] (experimental) | | ✓ | ✓ | bz2 | +| Codec Type | Codec | ZEP | V3 | V2 | Feature Flag* | +| -------------- | -------------------------------------- | ----------------- | ------- | ------- | ------------- | +| Array to Array | [transpose] | [ZEP0001] | ✓ | | **transpose** | +| | [bitround] (experimental) | | ✓ | | bitround | +| Array to Bytes | [bytes] | [ZEP0001] | ✓ | | | +| | [sharding_indexed] | [ZEP0002] | ✓ | | **sharding** | +| | [zfp] (experimental) | | ✓ | | zfp | +| | [pcodec] (experimental) | | ✓ | | pcodec | +| | [vlen] (experimental) | | ✓ | | | +| | V3 [vlen_interleaved] (experimental)
V2 vlen-utf8/vlen-bytes/vlen-array | | ✓ | ✓ | | +| Bytes to Bytes | [blosc] | [ZEP0001] | ✓ | ✓ | **blosc** | +| | [gzip] | [ZEP0001] | ✓ | ✓ | **gzip** | +| | [crc32c] | [ZEP0002] | ✓ | | **crc32c** | +| | [zstd] | [zarr-specs #256] | ✓ | | zstd | +| | [bz2] (experimental) | | ✓ | ✓ | bz2 | \* Bolded feature flags are part of the default set of features.
@@ -31,12 +33,16 @@ [crc32c]: crate::array::codec::bytes_to_bytes::crc32c [zstd]: crate::array::codec::bytes_to_bytes::zstd [bz2]: crate::array::codec::bytes_to_bytes::bz2 +[vlen]: crate::array::codec::array_to_bytes::vlen +[vlen_interleaved]: crate::array::codec::array_to_bytes::vlen_interleaved The `"name"` of of experimental codecs in array metadata links the codec documentation in this crate. -| Experimental Codec | Name / URI | -| ------------------ | ------------------------------------------------- | -| `bitround` | | -| `zfp` | | -| `pcodec` | | -| `bz2` | | +| Experimental Codec | Name / URI | +| ------------------ | -------------------------------------------------------- | +| `bitround` | | +| `zfp` | | +| `pcodec` | | +| `bz2` | | +| `vlen` | | +| `vlen_interleaved` | | diff --git a/doc/status/data_types.md b/doc/status/data_types.md index 05875056..bf66c75a 100644 --- a/doc/status/data_types.md +++ b/doc/status/data_types.md @@ -1,8 +1,12 @@ -| Data Type | ZEP | V3 | V2 | Feature Flag | +| Data Type | ZEP | V3 | V2 | Feature Flag | | --------- | --- | ----- | -- | ------------ | | [bool]
[int8] [int16] [int32] [int64] [uint8] [uint16] [uint32] [uint64]
[float16] [float32] [float64]
[complex64] [complex128] | [ZEP0001] | ✓ | ✓ | | [r* (raw bits)] | [ZEP0001] | ✓ | | | | [bfloat16] | [zarr-specs #130] | ✓ | | | +| [string] (experimental) | [ZEP0007 (draft)] | ✓ | | | +| [binary] (experimental) | [ZEP0007 (draft)] | ✓ | | | + +† Experimental data types are recommended for evaluation only. [bool]: crate::array::data_type::DataType::Bool [int8]: crate::array::data_type::DataType::Int8 @@ -20,6 +24,9 @@ [complex128]: crate::array::data_type::DataType::Complex128 [bfloat16]: crate::array::data_type::DataType::BFloat16 [r* (raw bits)]: crate::array::data_type::DataType::RawBits +[string]: crate::array::data_type::DataType::String +[binary]: crate::array::data_type::DataType::Binary [ZEP0001]: https://zarr.dev/zeps/accepted/ZEP0001.html [zarr-specs #130]: https://github.com/zarr-developers/zarr-specs/issues/130 +[ZEP0007 (draft)]: https://github.com/zarr-developers/zeps/pull/47 diff --git a/examples/sharded_array_write_read.rs b/examples/sharded_array_write_read.rs index d312e8c5..5a05368c 100644 --- a/examples/sharded_array_write_read.rs +++ b/examples/sharded_array_write_read.rs @@ -137,15 +137,15 @@ fn sharded_array_write_read() -> Result<(), Box> { ArraySubset::new_with_start_shape(vec![0, 4], inner_chunk_shape.clone())?, ]; let decoded_inner_chunks_bytes = partial_decoder.partial_decode(&inner_chunks_to_decode)?; - let decoded_inner_chunks_ndarray = decoded_inner_chunks_bytes - .into_iter() - .map(|bytes| bytes_to_ndarray::(&inner_chunk_shape, bytes.to_vec())) - .collect::, _>>()?; println!("Decoded inner chunks:"); for (inner_chunk_subset, decoded_inner_chunk) in - std::iter::zip(inner_chunks_to_decode, decoded_inner_chunks_ndarray) + std::iter::zip(inner_chunks_to_decode, decoded_inner_chunks_bytes) { - println!("{inner_chunk_subset}\n{decoded_inner_chunk}\n"); + let ndarray = bytes_to_ndarray::( + &inner_chunk_shape, + decoded_inner_chunk.into_fixed()?.into_owned(), + )?; + println!("{inner_chunk_subset}\n{ndarray}\n"); } // Show the hierarchy diff --git a/src/array.rs b/src/array.rs index f7f46bdc..4599af0f 100644 --- a/src/array.rs +++ b/src/array.rs @@ -22,6 +22,7 @@ //! The documentation for [`Array`] details how to interact with arrays. mod array_builder; +mod array_bytes; mod array_errors; mod array_metadata_options; mod array_representation; @@ -33,6 +34,7 @@ pub mod codec; pub mod concurrency; pub mod data_type; mod dimension_name; +mod element; mod endianness; mod fill_value; mod nan_representations; @@ -47,9 +49,10 @@ use std::sync::Arc; pub use self::{ array_builder::ArrayBuilder, + array_bytes::{ArrayBytes, ArrayBytesError, RawBytes, RawBytesOffsets}, array_errors::{ArrayCreateError, ArrayError}, array_metadata_options::ArrayMetadataOptions, - array_representation::{ArrayRepresentation, ChunkRepresentation}, + array_representation::{ArrayRepresentation, ArraySize, ChunkRepresentation}, bytes_representation::BytesRepresentation, chunk_grid::ChunkGrid, chunk_key_encoding::{ChunkKeyEncoding, ChunkKeySeparator}, @@ -57,8 +60,9 @@ pub use self::{ codec::ArrayCodecTraits, codec::CodecChain, concurrency::RecommendedConcurrency, - data_type::DataType, + data_type::{DataType, DataTypeSize}, dimension_name::DimensionName, + element::{Element, ElementFixedLength}, endianness::{Endianness, NATIVE_ENDIAN}, fill_value::FillValue, nan_representations::{ZARR_NAN_BF16, ZARR_NAN_F16, ZARR_NAN_F32, ZARR_NAN_F64}, @@ -641,9 +645,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Convert an ndarray into a vec with standard layout -fn ndarray_into_vec( - array: ndarray::Array, -) -> Vec { +fn ndarray_into_vec(array: ndarray::Array) -> Vec { if array.is_standard_layout() { array } else { @@ -695,7 +697,7 @@ pub fn transmute_to_bytes_vec(from: Vec) -> Vec { /// Transmute from `&[T]` to `&[u8]`. #[must_use] -fn transmute_to_bytes(from: &[T]) -> &[u8] { +pub fn transmute_to_bytes(from: &[T]) -> &[u8] { bytemuck::must_cast_slice(from) } @@ -733,17 +735,6 @@ fn iter_u64_to_usize<'a, I: Iterator>(iter: I) -> Vec { .collect::>() } -fn validate_element_size(data_type: &DataType) -> Result<(), ArrayError> { - if data_type.size() == std::mem::size_of::() { - Ok(()) - } else { - Err(ArrayError::IncompatibleElementSize( - data_type.size(), - std::mem::size_of::(), - )) - } -} - #[cfg(feature = "ndarray")] /// Convert a vector of elements to an [`ndarray::ArrayD`]. /// diff --git a/src/array/array_async_readable.rs b/src/array/array_async_readable.rs index 7bb48eb1..e540e59e 100644 --- a/src/array/array_async_readable.rs +++ b/src/array/array_async_readable.rs @@ -13,15 +13,16 @@ use crate::{ }; use super::{ + array_bytes::{merge_chunks_vlen, update_bytes_flen}, codec::{ - options::CodecOptions, ArrayCodecTraits, ArrayToBytesCodecTraits, - AsyncArrayPartialDecoderTraits, AsyncStoragePartialDecoder, + options::CodecOptions, ArrayToBytesCodecTraits, AsyncArrayPartialDecoderTraits, + AsyncStoragePartialDecoder, }, concurrency::concurrency_chunks_and_codec, - transmute_from_bytes_vec, + element::ElementOwned, unsafe_cell_slice::UnsafeCellSlice, - validate_element_size, Array, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV2, - ArrayMetadataV3, + Array, ArrayBytes, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV2, + ArrayMetadataV3, DataTypeSize, }; #[cfg(feature = "ndarray")] @@ -93,14 +94,14 @@ impl Array { pub async fn async_retrieve_chunk_if_exists( &self, chunk_indices: &[u64], - ) -> Result>, ArrayError> { + ) -> Result>, ArrayError> { self.async_retrieve_chunk_if_exists_opt(chunk_indices, &CodecOptions::default()) .await } /// Async variant of [`retrieve_chunk_elements_if_exists`](Array::retrieve_chunk_elements_if_exists). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_elements_if_exists( + pub async fn async_retrieve_chunk_elements_if_exists( &self, chunk_indices: &[u64], ) -> Result>, ArrayError> { @@ -111,7 +112,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_ndarray_if_exists`](Array::retrieve_chunk_ndarray_if_exists). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_ndarray_if_exists( + pub async fn async_retrieve_chunk_ndarray_if_exists( &self, chunk_indices: &[u64], ) -> Result>, ArrayError> { @@ -144,14 +145,17 @@ impl Array { /// Async variant of [`retrieve_chunk`](Array::retrieve_chunk). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk(&self, chunk_indices: &[u64]) -> Result, ArrayError> { + pub async fn async_retrieve_chunk( + &self, + chunk_indices: &[u64], + ) -> Result, ArrayError> { self.async_retrieve_chunk_opt(chunk_indices, &CodecOptions::default()) .await } /// Async variant of [`retrieve_chunk_elements`](Array::retrieve_chunk_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_elements( + pub async fn async_retrieve_chunk_elements( &self, chunk_indices: &[u64], ) -> Result, ArrayError> { @@ -162,7 +166,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_ndarray`](Array::retrieve_chunk_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_ndarray( + pub async fn async_retrieve_chunk_ndarray( &self, chunk_indices: &[u64], ) -> Result, ArrayError> { @@ -172,14 +176,17 @@ impl Array { /// Async variant of [`retrieve_chunks`](Array::retrieve_chunks). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunks(&self, chunks: &ArraySubset) -> Result, ArrayError> { + pub async fn async_retrieve_chunks( + &self, + chunks: &ArraySubset, + ) -> Result, ArrayError> { self.async_retrieve_chunks_opt(chunks, &CodecOptions::default()) .await } /// Async variant of [`retrieve_chunks_elements`](Array::retrieve_chunks_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunks_elements( + pub async fn async_retrieve_chunks_elements( &self, chunks: &ArraySubset, ) -> Result, ArrayError> { @@ -190,7 +197,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunks_ndarray`](Array::retrieve_chunks_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunks_ndarray( + pub async fn async_retrieve_chunks_ndarray( &self, chunks: &ArraySubset, ) -> Result, ArrayError> { @@ -204,14 +211,14 @@ impl Array { &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { self.async_retrieve_chunk_subset_opt(chunk_indices, chunk_subset, &CodecOptions::default()) .await } /// Async variant of [`retrieve_chunk_subset_elements`](Array::retrieve_chunk_subset_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_subset_elements( + pub async fn async_retrieve_chunk_subset_elements( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -227,7 +234,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_subset_ndarray`](Array::retrieve_chunk_subset_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_chunk_subset_ndarray( + pub async fn async_retrieve_chunk_subset_ndarray( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -245,14 +252,14 @@ impl Array { pub async fn async_retrieve_array_subset( &self, array_subset: &ArraySubset, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { self.async_retrieve_array_subset_opt(array_subset, &CodecOptions::default()) .await } /// Async variant of [`retrieve_array_subset_elements`](Array::retrieve_array_subset_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_array_subset_elements( + pub async fn async_retrieve_array_subset_elements( &self, array_subset: &ArraySubset, ) -> Result, ArrayError> { @@ -263,7 +270,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_array_subset_ndarray`](Array::retrieve_array_subset_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_retrieve_array_subset_ndarray( + pub async fn async_retrieve_array_subset_ndarray( &self, array_subset: &ArraySubset, ) -> Result, ArrayError> { @@ -291,7 +298,7 @@ impl Array { &self, chunk_indices: &[u64], options: &CodecOptions, - ) -> Result>, ArrayError> { + ) -> Result>, ArrayError> { if chunk_indices.len() != self.dimensionality() { return Err(ArrayError::InvalidChunkGridIndicesError( chunk_indices.to_vec(), @@ -311,7 +318,7 @@ impl Array { .map_err(ArrayError::StorageError)?; if let Some(chunk_encoded) = chunk_encoded { let chunk_representation = self.chunk_array_representation(chunk_indices)?; - let chunk_decoded = self + let bytes = self .codecs() .decode( Cow::Borrowed(&chunk_encoded), @@ -319,16 +326,11 @@ impl Array { options, ) .map_err(ArrayError::CodecError)?; - let chunk_decoded_size = - chunk_representation.num_elements_usize() * chunk_representation.data_type().size(); - if chunk_decoded.len() == chunk_decoded_size { - Ok(Some(chunk_decoded.into_owned())) - } else { - Err(ArrayError::UnexpectedChunkDecodedSize( - chunk_decoded.len(), - chunk_decoded_size, - )) - } + bytes.validate( + chunk_representation.num_elements(), + chunk_representation.data_type().size(), + )?; + Ok(Some(bytes.into_owned())) } else { Ok(None) } @@ -340,7 +342,7 @@ impl Array { &self, chunk_indices: &[u64], options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { let chunk = self .async_retrieve_chunk_if_exists_opt(chunk_indices, options) .await?; @@ -348,43 +350,49 @@ impl Array { Ok(chunk) } else { let chunk_representation = self.chunk_array_representation(chunk_indices)?; - let fill_value = chunk_representation.fill_value().as_ne_bytes(); - Ok(fill_value.repeat(chunk_representation.num_elements_usize())) + Ok(ArrayBytes::new_fill_value( + chunk_representation.num_elements_usize(), + self.fill_value(), + )) } } /// Async variant of [`retrieve_chunk_elements_if_exists_opt`](Array::retrieve_chunk_elements_if_exists_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_elements_if_exists_opt( + pub async fn async_retrieve_chunk_elements_if_exists_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result>, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self + if let Some(bytes) = self .async_retrieve_chunk_if_exists_opt(chunk_indices, options) - .await?; - Ok(bytes.map(|bytes| transmute_from_bytes_vec::(bytes))) + .await? + { + let elements = T::from_array_bytes(self.data_type(), bytes)?; + Ok(Some(elements)) + } else { + Ok(None) + } } /// Async variant of [`retrieve_chunk_elements_opt`](Array::retrieve_chunk_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_elements_opt( + pub async fn async_retrieve_chunk_elements_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; let bytes = self .async_retrieve_chunk_opt(chunk_indices, options) .await?; - Ok(transmute_from_bytes_vec::(bytes)) + let elements = T::from_array_bytes(self.data_type(), bytes)?; + Ok(elements) } #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_ndarray_if_exists_opt`](Array::retrieve_chunk_ndarray_if_exists_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_ndarray_if_exists_opt( + pub async fn async_retrieve_chunk_ndarray_if_exists_opt( &self, chunk_indices: &[u64], options: &CodecOptions, @@ -407,7 +415,7 @@ impl Array { #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_ndarray_opt`](Array::retrieve_chunk_ndarray_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_ndarray_opt( + pub async fn async_retrieve_chunk_ndarray_opt( &self, chunk_indices: &[u64], options: &CodecOptions, @@ -467,7 +475,7 @@ impl Array { &self, chunks: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { if chunks.dimensionality() != self.dimensionality() { return Err(ArrayError::InvalidArraySubset( chunks.clone(), @@ -482,25 +490,24 @@ impl Array { /// Async variant of [`retrieve_chunks_elements_opt`](Array::retrieve_chunks_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunks_elements_opt( + pub async fn async_retrieve_chunks_elements_opt( &self, chunks: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; let bytes = self.async_retrieve_chunks_opt(chunks, options).await?; - Ok(transmute_from_bytes_vec::(bytes)) + let elements = T::from_array_bytes(self.data_type(), bytes)?; + Ok(elements) } #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunks_ndarray_opt`](Array::retrieve_chunks_ndarray_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunks_ndarray_opt( + pub async fn async_retrieve_chunks_ndarray_opt( &self, chunks: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; let array_subset = self.chunks_subset(chunks)?; let elements = self .async_retrieve_chunks_elements_opt(chunks, options) @@ -515,7 +522,7 @@ impl Array { &self, array_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { if array_subset.dimensionality() != self.dimensionality() { return Err(ArrayError::InvalidArraySubset( array_subset.clone(), @@ -535,10 +542,10 @@ impl Array { // Retrieve chunk bytes let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => Ok(self - .fill_value() - .as_ne_bytes() - .repeat(array_subset.num_elements_usize())), + 0 => Ok(ArrayBytes::new_fill_value( + array_subset.num_elements_usize(), + self.fill_value(), + )), 1 => { let chunk_indices = chunks.start(); let chunk_subset = self.chunk_subset(chunk_indices)?; @@ -557,11 +564,6 @@ impl Array { } } _ => { - // Decode chunks and copy to output - let size_output = - usize::try_from(array_subset.num_elements() * self.data_type().size() as u64) - .unwrap(); - // Calculate chunk/codec concurrency let chunk_representation = self.chunk_array_representation(&vec![0; self.dimensionality()])?; @@ -574,84 +576,109 @@ impl Array { &codec_concurrency, ); - // let mut output = vec![0; size_output]; - // let output_slice = output.as_mut_slice(); - let mut output = Vec::with_capacity(size_output); - { - let output = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); - let retrieve_chunk = |chunk_indices: Vec| { - let options = options.clone(); - async move { - let chunk_subset = self.chunk_subset(&chunk_indices)?; - let chunk_subset_overlap = chunk_subset.overlap(array_subset)?; - let chunk_subset_bytes = self - .async_retrieve_chunk_subset_opt( - &chunk_indices, - &chunk_subset_overlap.relative_to(chunk_subset.start())?, - &options, + match chunk_representation.data_type().size() { + DataTypeSize::Variable => { + let retrieve_chunk = |chunk_indices: Vec| { + let options = options.clone(); + async move { + let chunk_subset = self.chunk_subset(&chunk_indices)?; + let chunk_subset_overlap = chunk_subset.overlap(array_subset)?; + Ok::<_, ArrayError>(( + self.async_retrieve_chunk_subset_opt( + &chunk_indices, + &chunk_subset_overlap.relative_to(chunk_subset.start())?, + &options, + ) + .await?, + chunk_subset_overlap.relative_to(array_subset.start())?, + )) + } + }; + + // TODO: chunk_concurrent_limit + let chunk_bytes_and_subsets = futures::future::try_join_all( + chunks.indices().iter().map(retrieve_chunk), + ) + .await?; + + Ok(merge_chunks_vlen( + chunk_bytes_and_subsets, + array_subset.shape(), + )?) + } + DataTypeSize::Fixed(data_type_size) => { + let size_output = + usize::try_from(array_subset.num_elements() * data_type_size as u64) + .unwrap(); + let mut output = Vec::with_capacity(size_output); + { + let output = + UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); + let retrieve_chunk = |chunk_indices: Vec| { + let options = options.clone(); + async move { + let chunk_subset = self.chunk_subset(&chunk_indices)?; + let chunk_subset_overlap = + chunk_subset.overlap(array_subset)?; + let chunk_subset_bytes = self + .async_retrieve_chunk_subset_opt( + &chunk_indices, + &chunk_subset_overlap + .relative_to(chunk_subset.start())?, + &options, + ) + .await?; + let chunk_subset_bytes = chunk_subset_bytes.into_fixed()?; + let output = unsafe { output.get() }; + update_bytes_flen( + output, + array_subset.shape(), + &chunk_subset_bytes, + &chunk_subset_overlap.relative_to(array_subset.start())?, + data_type_size, + ); + Ok::<_, ArrayError>(()) + } + }; + + futures::stream::iter(&chunks.indices()) + .map(Ok) + .try_for_each_concurrent( + Some(chunk_concurrent_limit), + retrieve_chunk, ) .await?; - let contiguous_indices = unsafe { - chunk_subset_overlap - .relative_to(array_subset.start())? - .contiguous_linearised_indices_unchecked(array_subset.shape()) - }; - let element_size = self.data_type().size(); - let length = - contiguous_indices.contiguous_elements_usize() * element_size; - let mut decoded_offset = 0; - // FIXME: Par iteration? - let output = unsafe { output.get() }; - for (array_subset_element_index, _num_elements) in &contiguous_indices { - let output_offset = usize::try_from(array_subset_element_index) - .unwrap() - * element_size; - debug_assert!((output_offset + length) <= output.len()); - debug_assert!( - (decoded_offset + length) <= chunk_subset_bytes.len() - ); - output[output_offset..output_offset + length].copy_from_slice( - &chunk_subset_bytes[decoded_offset..decoded_offset + length], - ); - decoded_offset += length; - } - Ok::<_, ArrayError>(()) } - }; - futures::stream::iter(&chunks.indices()) - .map(Ok) - .try_for_each_concurrent(Some(chunk_concurrent_limit), retrieve_chunk) - .await?; + unsafe { output.set_len(size_output) }; + Ok(ArrayBytes::from(output)) + } } - unsafe { output.set_len(size_output) }; - Ok(output) } } } /// Async variant of [`retrieve_array_subset_elements_opt`](Array::retrieve_array_subset_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_array_subset_elements_opt( + pub async fn async_retrieve_array_subset_elements_opt( &self, array_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; let bytes = self .async_retrieve_array_subset_opt(array_subset, options) .await?; - Ok(transmute_from_bytes_vec::(bytes)) + let elements = T::from_array_bytes(self.data_type(), bytes)?; + Ok(elements) } #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_array_subset_ndarray_opt`](Array::retrieve_array_subset_ndarray_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_array_subset_ndarray_opt( + pub async fn async_retrieve_array_subset_ndarray_opt( &self, array_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in async_retrieve_array_subset_elements let elements = self .async_retrieve_array_subset_elements_opt(array_subset, options) .await?; @@ -665,7 +692,7 @@ impl Array { chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { let chunk_representation = self.chunk_array_representation(chunk_indices)?; if !chunk_subset.inbounds(&chunk_representation.shape_u64()) { return Err(ArrayError::InvalidArraySubset( @@ -683,52 +710,42 @@ impl Array { data_key(self.path(), chunk_indices, self.chunk_key_encoding()), )); - let partial_decoder = self + let bytes = self .codecs() .async_partial_decoder(input_handle, &chunk_representation, options) - .await?; - let decoded_bytes = partial_decoder + .await? .partial_decode_opt(&[chunk_subset.clone()], options) .await? - .pop() - .unwrap(); - - let expected_size = chunk_subset.num_elements_usize() * self.data_type().size(); - if decoded_bytes.len() == chunk_subset.num_elements_usize() * self.data_type().size() { - Ok(decoded_bytes.into_owned()) - } else { - Err(ArrayError::UnexpectedChunkDecodedSize( - decoded_bytes.len(), - expected_size, - )) - } + .remove(0) + .into_owned(); + bytes.validate(chunk_subset.num_elements(), self.data_type().size())?; + Ok(bytes) } /// Async variant of [`retrieve_chunk_subset_elements_opt`](Array::retrieve_chunk_subset_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_subset_elements_opt( + pub async fn async_retrieve_chunk_subset_elements_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; let bytes = self .async_retrieve_chunk_subset_opt(chunk_indices, chunk_subset, options) .await?; - Ok(transmute_from_bytes_vec::(bytes)) + let elements = T::from_array_bytes(self.data_type(), bytes)?; + Ok(elements) } #[cfg(feature = "ndarray")] /// Async variant of [`retrieve_chunk_subset_ndarray_opt`](Array::retrieve_chunk_subset_ndarray_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_retrieve_chunk_subset_ndarray_opt( + pub async fn async_retrieve_chunk_subset_ndarray_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in async_retrieve_chunk_subset_elements let elements = self .async_retrieve_chunk_subset_elements_opt(chunk_indices, chunk_subset, options) .await?; diff --git a/src/array/array_async_readable_writable.rs b/src/array/array_async_readable_writable.rs index 575c7cd8..bf948731 100644 --- a/src/array/array_async_readable_writable.rs +++ b/src/array/array_async_readable_writable.rs @@ -1,22 +1,22 @@ use futures::{StreamExt, TryStreamExt}; use crate::{ - array::validate_element_size, array_subset::ArraySubset, - storage::AsyncReadableWritableStorageTraits, + array::ArrayBytes, array_subset::ArraySubset, storage::AsyncReadableWritableStorageTraits, }; use super::{ - codec::options::CodecOptions, concurrency::concurrency_chunks_and_codec, Array, ArrayError, + array_bytes::update_array_bytes, codec::options::CodecOptions, + concurrency::concurrency_chunks_and_codec, Array, ArrayError, Element, }; impl Array { /// Async variant of [`store_chunk_subset`](Array::store_chunk_subset). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_store_chunk_subset( + pub async fn async_store_chunk_subset<'a>( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - chunk_subset_bytes: &[u8], + chunk_subset_bytes: impl Into>, ) -> Result<(), ArrayError> { self.async_store_chunk_subset_opt( chunk_indices, @@ -29,7 +29,7 @@ impl Array( + pub async fn async_store_chunk_subset_elements( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -48,7 +48,7 @@ impl Array> + Send, D: ndarray::Dimension, >( @@ -68,10 +68,10 @@ impl Array( &self, array_subset: &ArraySubset, - subset_bytes: &[u8], + subset_bytes: impl Into>, ) -> Result<(), ArrayError> { self.async_store_array_subset_opt(array_subset, subset_bytes, &CodecOptions::default()) .await @@ -79,7 +79,7 @@ impl Array( + pub async fn async_store_array_subset_elements( &self, array_subset: &ArraySubset, subset_elements: &[T], @@ -96,7 +96,7 @@ impl Array> + Send, D: ndarray::Dimension, >( @@ -118,11 +118,11 @@ impl Array( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - chunk_subset_bytes: &[u8], + chunk_subset_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { let chunk_shape = self @@ -138,75 +138,58 @@ impl Array() * self.data_type().size() as u64; - if chunk_subset_bytes.len() as u64 != expected_length { - return Err(ArrayError::InvalidBytesInputSize( - chunk_subset_bytes.len(), - expected_length, - )); - } if chunk_subset.shape() == chunk_shape && chunk_subset.start().iter().all(|&x| x == 0) { // The subset spans the whole chunk, so store the bytes directly and skip decoding self.async_store_chunk_opt(chunk_indices, chunk_subset_bytes, options) .await } else { + let chunk_subset_bytes = chunk_subset_bytes.into(); + chunk_subset_bytes.validate(chunk_subset.num_elements(), self.data_type().size())?; + // Lock the chunk // let key = data_key(self.path(), chunk_indices, self.chunk_key_encoding()); // let mutex = self.storage.mutex(&key).await?; // let _lock = mutex.lock(); // Decode the entire chunk - let mut chunk_bytes = self + let chunk_bytes_old = self .async_retrieve_chunk_opt(chunk_indices, options) .await?; - // Update the intersecting subset of the chunk - let element_size = self.data_type().size(); - let mut offset = 0; - let contiguous_indices = - unsafe { chunk_subset.contiguous_linearised_indices_unchecked(&chunk_shape) }; - let length = contiguous_indices.contiguous_elements_usize() * element_size; - for (chunk_element_index, _num_elements) in &contiguous_indices { - let chunk_offset = usize::try_from(chunk_element_index).unwrap() * element_size; - debug_assert!(chunk_offset + length <= chunk_bytes.len()); - debug_assert!(offset + length <= chunk_subset_bytes.len()); - chunk_bytes[chunk_offset..chunk_offset + length] - .copy_from_slice(&chunk_subset_bytes[offset..offset + length]); - offset += length; - } + // Update the chunk + let chunk_bytes_new = update_array_bytes( + chunk_bytes_old, + chunk_shape, + chunk_subset_bytes, + chunk_subset, + self.data_type().size(), + ); // Store the updated chunk - self.async_store_chunk_opt(chunk_indices, &chunk_bytes, options) + self.async_store_chunk_opt(chunk_indices, chunk_bytes_new, options) .await } } /// Async variant of [`store_chunk_subset_elements_opt`](Array::store_chunk_subset_elements_opt). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub async fn async_store_chunk_subset_elements_opt( + pub async fn async_store_chunk_subset_elements_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, chunk_subset_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunk_subset_elements = crate::array::transmute_to_bytes(chunk_subset_elements); - self.async_store_chunk_subset_opt( - chunk_indices, - chunk_subset, - chunk_subset_elements, - options, - ) - .await + let chunk_subset_bytes = T::into_array_bytes(self.data_type(), chunk_subset_elements)?; + self.async_store_chunk_subset_opt(chunk_indices, chunk_subset, chunk_subset_bytes, options) + .await } /// Async variant of [`store_chunk_subset_ndarray_opt`](Array::store_chunk_subset_ndarray_opt). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub async fn async_store_chunk_subset_ndarray_opt< - T: bytemuck::Pod + Send + Sync, + T: Element + Send + Sync, TArray: Into> + Send, D: ndarray::Dimension, >( @@ -216,7 +199,6 @@ impl Array Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunk_subset_array: ndarray::Array = chunk_subset_array.into(); let subset = ArraySubset::new_with_start_shape( chunk_subset_start.to_vec(), @@ -239,10 +221,10 @@ impl Array( &self, array_subset: &ArraySubset, - subset_bytes: &[u8], + subset_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { // Validation @@ -252,13 +234,6 @@ impl Array Array Array| { - let chunk_subset_in_array = unsafe { - self.chunk_grid() - .subset_unchecked(&chunk_indices, self.shape()) - .unwrap() - }; - let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset_in_array) }; + let chunk_subset = self.chunk_subset(&chunk_indices).unwrap(); // FIXME: unwrap + let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset) }; let chunk_subset_in_array_subset = unsafe { overlap.relative_to_unchecked(array_subset.start()) }; let array_subset_in_chunk_subset = - unsafe { overlap.relative_to_unchecked(chunk_subset_in_array.start()) }; - let chunk_subset_bytes = unsafe { - chunk_subset_in_array_subset.extract_bytes_unchecked( - subset_bytes, + unsafe { overlap.relative_to_unchecked(chunk_subset.start()) }; + let chunk_subset_bytes = subset_bytes + .extract_array_subset( + &chunk_subset_in_array_subset, array_subset.shape(), - self.data_type().size(), + self.data_type(), ) - }; + .unwrap(); // FIXME: unwrap let options = options.clone(); async move { self.async_store_chunk_subset_opt( &chunk_indices, &array_subset_in_chunk_subset, - &chunk_subset_bytes, + chunk_subset_bytes, &options, ) .await @@ -356,15 +313,14 @@ impl Array( + pub async fn async_store_array_subset_elements_opt( &self, array_subset: &ArraySubset, subset_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let subset_elements = crate::array::transmute_to_bytes(subset_elements); - self.async_store_array_subset_opt(array_subset, subset_elements, options) + let subset_bytes = T::into_array_bytes(self.data_type(), subset_elements)?; + self.async_store_array_subset_opt(array_subset, subset_bytes, options) .await } @@ -372,7 +328,7 @@ impl Array> + Send, D: ndarray::Dimension, >( @@ -381,7 +337,6 @@ impl Array Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let subset_array: ndarray::Array = subset_array.into(); let subset = ArraySubset::new_with_start_shape( subset_start.to_vec(), diff --git a/src/array/array_async_writable.rs b/src/array/array_async_writable.rs index 2e4701f4..cb22ee5c 100644 --- a/src/array/array_async_writable.rs +++ b/src/array/array_async_writable.rs @@ -1,9 +1,9 @@ -use std::{borrow::Cow, sync::Arc}; +use std::sync::Arc; use futures::{StreamExt, TryStreamExt}; use crate::{ - array::validate_element_size, + array::ArrayBytes, array_subset::ArraySubset, metadata::MetadataEraseVersion, storage::{ @@ -13,9 +13,9 @@ use crate::{ }; use super::{ - codec::{options::CodecOptions, ArrayCodecTraits}, + codec::{options::CodecOptions, ArrayToBytesCodecTraits}, concurrency::concurrency_chunks_and_codec, - Array, ArrayError, ArrayMetadata, ArrayMetadataOptions, + Array, ArrayError, ArrayMetadata, ArrayMetadataOptions, Element, }; impl Array { @@ -44,10 +44,10 @@ impl Array { /// Async variant of [`store_chunk`](Array::store_chunk). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunk( + pub async fn async_store_chunk<'a>( &self, chunk_indices: &[u64], - chunk_bytes: &[u8], + chunk_bytes: impl Into>, ) -> Result<(), ArrayError> { self.async_store_chunk_opt(chunk_indices, chunk_bytes, &CodecOptions::default()) .await @@ -55,7 +55,7 @@ impl Array { /// Async variant of [`store_chunk_elements`](Array::store_chunk_elements). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunk_elements( + pub async fn async_store_chunk_elements( &self, chunk_indices: &[u64], chunk_elements: &[T], @@ -68,7 +68,7 @@ impl Array { /// Async variant of [`store_chunk_ndarray`](Array::store_chunk_ndarray). #[allow(clippy::missing_errors_doc)] pub async fn async_store_chunk_ndarray< - T: bytemuck::Pod + Send + Sync, + T: Element + Send + Sync, TArray: Into> + Send, D: ndarray::Dimension, >( @@ -83,10 +83,10 @@ impl Array { /// Async variant of [`store_chunks`](Array::store_chunks). #[allow(clippy::missing_errors_doc)] #[allow(clippy::similar_names)] - pub async fn async_store_chunks( + pub async fn async_store_chunks<'a>( &self, chunks: &ArraySubset, - chunks_bytes: &[u8], + chunks_bytes: impl Into>, ) -> Result<(), ArrayError> { self.async_store_chunks_opt(chunks, chunks_bytes, &CodecOptions::default()) .await @@ -94,7 +94,7 @@ impl Array { /// Async variant of [`store_chunks_elements`](Array::store_chunks_elements). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunks_elements( + pub async fn async_store_chunks_elements( &self, chunks: &ArraySubset, chunks_elements: &[T], @@ -107,7 +107,7 @@ impl Array { /// Async variant of [`store_chunks_ndarray`](Array::store_chunks_ndarray). #[allow(clippy::missing_errors_doc)] pub async fn async_store_chunks_ndarray< - T: bytemuck::Pod + Send + Sync, + T: Element + Send + Sync, TArray: Into> + Send, D: ndarray::Dimension, >( @@ -213,24 +213,25 @@ impl Array { /// Async variant of [`store_chunk_opt`](Array::store_chunk_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunk_opt( + pub async fn async_store_chunk_opt<'a>( &self, chunk_indices: &[u64], - chunk_bytes: &[u8], + chunk_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { + let chunk_bytes = chunk_bytes.into(); + // Validation let chunk_array_representation = self.chunk_array_representation(chunk_indices)?; - if chunk_bytes.len() as u64 != chunk_array_representation.size() { - return Err(ArrayError::InvalidBytesInputSize( - chunk_bytes.len(), - chunk_array_representation.size(), - )); - } + chunk_bytes.validate( + chunk_array_representation.num_elements(), + chunk_array_representation.data_type().size(), + )?; - if !options.store_empty_chunks() && self.fill_value().equals_all(chunk_bytes) { + let is_fill_value = + !options.store_empty_chunks() && chunk_bytes.is_fill_value(self.fill_value()); + if is_fill_value { self.async_erase_chunk(chunk_indices).await?; - Ok(()) } else { let storage_handle = Arc::new(StorageHandle::new(self.storage.clone())); let storage_transformer = self @@ -238,11 +239,7 @@ impl Array { .create_async_writable_transformer(storage_handle); let chunk_encoded = self .codecs() - .encode( - Cow::Borrowed(chunk_bytes), - &chunk_array_representation, - options, - ) + .encode(chunk_bytes, &chunk_array_representation, options) .map_err(ArrayError::CodecError)?; let chunk_encoded = AsyncBytes::from(chunk_encoded.to_vec()); crate::storage::async_store_chunk( @@ -252,22 +249,21 @@ impl Array { self.chunk_key_encoding(), chunk_encoded, ) - .await - .map_err(ArrayError::StorageError) + .await?; } + Ok(()) } /// Async variant of [`store_chunk_elements_opt`](Array::store_chunk_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunk_elements_opt( + pub async fn async_store_chunk_elements_opt( &self, chunk_indices: &[u64], chunk_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunk_elements = crate::array::transmute_to_bytes(chunk_elements); - self.async_store_chunk_opt(chunk_indices, chunk_elements, options) + let bytes = T::into_array_bytes(self.data_type(), chunk_elements)?; + self.async_store_chunk_opt(chunk_indices, bytes, options) .await } @@ -275,7 +271,7 @@ impl Array { /// Async variant of [`store_chunk_ndarray_opt`](Array::store_chunk_ndarray_opt). #[allow(clippy::missing_errors_doc)] pub async fn async_store_chunk_ndarray_opt< - T: bytemuck::Pod + Send + Sync, + T: Element + Send + Sync, TArray: Into> + Send, D: ndarray::Dimension, >( @@ -284,7 +280,6 @@ impl Array { chunk_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunk_array: ndarray::Array = chunk_array.into(); let chunk_shape = self.chunk_shape_usize(chunk_indices)?; if chunk_array.shape() == chunk_shape { @@ -302,30 +297,27 @@ impl Array { /// Async variant of [`store_chunks_opt`](Array::store_chunks_opt). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] #[allow(clippy::similar_names)] - pub async fn async_store_chunks_opt( + pub async fn async_store_chunks_opt<'a>( &self, chunks: &ArraySubset, - chunks_bytes: &[u8], + chunks_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => {} + 0 => { + let chunks_bytes = chunks_bytes.into(); + chunks_bytes.validate(0, self.data_type().size())?; + } 1 => { let chunk_indices = chunks.start(); self.async_store_chunk_opt(chunk_indices, chunks_bytes, options) .await?; } _ => { + let chunks_bytes = chunks_bytes.into(); let array_subset = self.chunks_subset(chunks)?; - let element_size = self.data_type().size(); - let expected_size = element_size as u64 * array_subset.num_elements(); - if chunks_bytes.len() as u64 != expected_size { - return Err(ArrayError::InvalidBytesInputSize( - chunks_bytes.len(), - expected_size, - )); - } + chunks_bytes.validate(array_subset.num_elements(), self.data_type().size())?; // Calculate chunk/codec concurrency let chunk_representation = @@ -340,30 +332,17 @@ impl Array { ); let store_chunk = |chunk_indices: Vec| { - let chunk_subset_in_array = unsafe { - self.chunk_grid() - .subset_unchecked(&chunk_indices, self.shape()) - .unwrap() // FIXME: Unwrap - }; - let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset_in_array) }; - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - let chunk_bytes = unsafe { - chunk_subset_in_array_subset.extract_bytes_unchecked( - chunks_bytes, + let chunk_subset = self.chunk_subset(&chunk_indices).unwrap(); // FIXME: unwrap + let chunk_bytes = chunks_bytes + .extract_array_subset( + &chunk_subset.relative_to(array_subset.start()).unwrap(), // FIXME: unwrap array_subset.shape(), - element_size, + self.data_type(), ) - }; - - debug_assert_eq!( - chunk_subset_in_array.num_elements(), - chunk_subset_in_array_subset.num_elements() - ); - + .unwrap(); // FIXME: unwrap let options = options.clone(); async move { - self.async_store_chunk_opt(&chunk_indices, &chunk_bytes, &options) + self.async_store_chunk_opt(&chunk_indices, chunk_bytes, &options) .await } }; @@ -379,15 +358,14 @@ impl Array { /// Async variant of [`store_chunks_elements_opt`](Array::store_chunks_elements_opt). #[allow(clippy::missing_errors_doc)] - pub async fn async_store_chunks_elements_opt( + pub async fn async_store_chunks_elements_opt( &self, chunks: &ArraySubset, chunks_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunks_elements = crate::array::transmute_to_bytes(chunks_elements); - self.async_store_chunks_opt(chunks, chunks_elements, options) + let chunks_bytes = T::into_array_bytes(self.data_type(), chunks_elements)?; + self.async_store_chunks_opt(chunks, chunks_bytes, options) .await } @@ -395,7 +373,7 @@ impl Array { /// Async variant of [`store_chunks_ndarray_opt`](Array::store_chunks_ndarray_opt). #[allow(clippy::missing_errors_doc)] pub async fn async_store_chunks_ndarray_opt< - T: bytemuck::Pod + Send + Sync, + T: Element + Send + Sync, TArray: Into> + Send, D: ndarray::Dimension, >( @@ -404,7 +382,6 @@ impl Array { chunks_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunks_array: ndarray::Array = chunks_array.into(); let chunks_subset = self.chunks_subset(chunks)?; let chunks_shape = chunks_subset.shape_usize(); diff --git a/src/array/array_builder.rs b/src/array/array_builder.rs index cc74e11c..269d7906 100644 --- a/src/array/array_builder.rs +++ b/src/array/array_builder.rs @@ -5,7 +5,8 @@ use crate::{metadata::v3::AdditionalFields, node::NodePath, storage::StorageTran use super::{ chunk_key_encoding::{ChunkKeyEncoding, DefaultChunkKeyEncoding}, codec::{ - ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, BytesCodec, BytesToBytesCodecTraits, + array_to_bytes::vlen::VlenCodec, ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, + BytesCodec, BytesToBytesCodecTraits, }, data_type::IncompatibleFillValueError, Array, ArrayCreateError, ArrayMetadata, ArrayMetadataV3, ArrayShape, ChunkGrid, @@ -93,6 +94,7 @@ impl ArrayBuilder { chunk_grid: ChunkGrid, fill_value: FillValue, ) -> Self { + let is_fixed_size = data_type.fixed_size().is_some(); Self { shape, data_type, @@ -100,7 +102,12 @@ impl ArrayBuilder { chunk_key_encoding: ChunkKeyEncoding::new(DefaultChunkKeyEncoding::default()), fill_value, array_to_array_codecs: Vec::default(), - array_to_bytes_codec: Box::::default(), + array_to_bytes_codec: if is_fixed_size { + Box::::default() + } else { + Box::::default() + // Box::::default() + }, bytes_to_bytes_codecs: Vec::default(), attributes: serde_json::Map::default(), storage_transformers: StorageTransformerChain::default(), @@ -287,12 +294,15 @@ impl ArrayBuilder { )); } } - if self.data_type.size() != self.fill_value.size() { - return Err(IncompatibleFillValueError::new( - self.data_type.name(), - self.fill_value.clone(), - ) - .into()); + + if let Some(data_type_size) = self.data_type.fixed_size() { + if data_type_size != self.fill_value.size() { + return Err(IncompatibleFillValueError::new( + self.data_type.name(), + self.fill_value.clone(), + ) + .into()); + } } let codec_chain = CodecChain::new( diff --git a/src/array/array_bytes.rs b/src/array/array_bytes.rs new file mode 100644 index 00000000..5bed39d7 --- /dev/null +++ b/src/array/array_bytes.rs @@ -0,0 +1,598 @@ +use std::borrow::Cow; + +use itertools::Itertools; +use thiserror::Error; + +use crate::{ + array_subset::{ArraySubset, IncompatibleArraySubsetAndShapeError}, + byte_range::extract_byte_ranges_concat_unchecked, +}; + +use super::{codec::CodecError, ravel_indices, ArrayShape, DataType, DataTypeSize, FillValue}; + +/// Array element bytes. +pub type RawBytes<'a> = Cow<'a, [u8]>; + +/// Array element byte offsets. +pub type RawBytesOffsets<'a> = Cow<'a, [usize]>; + +/// Fixed or variable length array bytes. +/// +/// Offsets are [`None`] if bytes are composed of fixed size data types. +#[derive(Clone, Debug, PartialEq)] +pub enum ArrayBytes<'a> { + /// Bytes for a fixed length array. + Fixed(RawBytes<'a>), + /// Bytes and element byte offsets for a variable length array. + Variable(RawBytes<'a>, RawBytesOffsets<'a>), +} + +/// Errors related to [`ArrayBytes<'_>`] and [`ArrayBytes`]. +#[derive(Debug, Error)] +pub enum ArrayBytesError { + /// Invalid use of a fixed length method. + #[error("Used a fixed length (flen) method on a variable length (vlen) array")] + UsedFixedLengthMethodOnVariableLengthArray, +} + +impl<'a> ArrayBytes<'a> { + /// Create a new fixed length array bytes from `bytes`. + pub fn new_flen(bytes: impl Into>) -> Self { + Self::Fixed(bytes.into()) + } + + /// Create a new variable length array bytes from `bytes` and `offsets`. + pub fn new_vlen( + bytes: impl Into>, + offsets: impl Into>, + ) -> Self { + Self::Variable(bytes.into(), offsets.into()) + } + + /// Create a new [`ArrayBytes`] with `num_elements` composed entirely of the `fill_value`. + #[must_use] + pub fn new_fill_value(num_elements: usize, fill_value: &FillValue) -> Self { + if fill_value.size() == 0 { + Self::new_vlen( + fill_value.as_ne_bytes().repeat(num_elements), + (0..=num_elements) + .map(|i| i * fill_value.size()) + .collect::>(), + ) + } else { + Self::new_flen(fill_value.as_ne_bytes().repeat(num_elements)) + } + } + + /// Convert the array bytes into fixed size bytes. + /// + /// # Errors + /// Returns a [`CodecError::ExpectedFixedLengthBytes`] if the bytes are variable length. + pub fn into_fixed(self) -> Result, CodecError> { + match self { + Self::Fixed(bytes) => Ok(bytes), + Self::Variable(_, _) => Err(CodecError::ExpectedFixedLengthBytes), + } + } + + /// Convert the array bytes into variable sized bytes and element byte offsets. + /// + /// # Errors + /// Returns a [`CodecError::ExpectedVariableLengthBytes`] if the bytes are fixed length. + pub fn into_variable(self) -> Result<(RawBytes<'a>, RawBytesOffsets<'a>), CodecError> { + match self { + Self::Fixed(_) => Err(CodecError::ExpectedVariableLengthBytes), + Self::Variable(bytes, offsets) => Ok((bytes, offsets)), + } + } + + /// Returns the size (in bytes) of the underlying element bytes. + /// + /// This only considers the size of the element bytes, and does not include the elemenet offsets for a variable sized array. + #[must_use] + pub fn size(&self) -> usize { + match self { + Self::Fixed(bytes) | Self::Variable(bytes, _) => bytes.len(), + } + } + + /// Return the byte offsets for variable sized bytes. Returns [`None`] for fixed size bytes. + #[must_use] + pub fn offsets(&self) -> Option<&RawBytesOffsets<'a>> { + match self { + Self::Fixed(_) => None, + Self::Variable(_, offsets) => Some(offsets), + } + } + + /// Convert into owned [`ArrayBytes<'_>`]. + #[must_use] + pub fn into_owned<'b>(self) -> ArrayBytes<'b> { + match self { + Self::Fixed(bytes) => ArrayBytes::<'b>::new_flen(bytes.into_owned()), + Self::Variable(bytes, offsets) => { + ArrayBytes::<'b>::new_vlen(bytes.into_owned(), offsets.into_owned()) + } + } + } + + /// Validate that the array has a valid encoding. + /// + /// For a fixed-length array, check it matches the expected size. + /// For a variable-length array, check that the offsets are monotonically increasing and the largest offset is equal to the array length. + /// Always returns without error for an array with fixed-length data. + /// + /// # Errors + /// Returns an error if the array is not valid. + pub fn validate( + &self, + num_elements: u64, + data_type_size: DataTypeSize, + ) -> Result<(), CodecError> { + validate_bytes(self, num_elements, data_type_size) + } + + /// Returns [`true`] if the array is empty for the given fill value. + #[must_use] + pub fn is_fill_value(&self, fill_value: &FillValue) -> bool { + match self { + Self::Fixed(bytes) => fill_value.equals_all(bytes), + Self::Variable(bytes, _offsets) => fill_value.equals_all(bytes), + } + } + + /// Extract a subset of the array bytes. + /// + /// # Errors + /// Returns a [`CodecError::InvalidArraySubsetError`] if the `array_shape` is incompatible with `subset`. + /// + /// # Panics + /// Panics if indices in the subset exceed [`usize::MAX`]. + pub fn extract_array_subset( + &self, + subset: &ArraySubset, + array_shape: &[u64], + data_type: &DataType, + ) -> Result, CodecError> { + match self { + ArrayBytes::Variable(bytes, offsets) => { + let indices = subset.linearised_indices(array_shape).map_err(|_| { + IncompatibleArraySubsetAndShapeError::new(subset.clone(), array_shape.to_vec()) + })?; + let mut bytes_length = 0; + for index in &indices { + let index = usize::try_from(index).unwrap(); + let curr = offsets[index]; + let next = offsets[index + 1]; + debug_assert!(next >= curr); + bytes_length += next - curr; + } + let mut ss_bytes = Vec::with_capacity(bytes_length); + let mut ss_offsets = Vec::with_capacity(1 + indices.len()); + for index in &indices { + let index = usize::try_from(index).unwrap(); + let curr = offsets[index]; + let next = offsets[index + 1]; + ss_offsets.push(ss_bytes.len()); + ss_bytes.extend_from_slice(&bytes[curr..next]); + } + ss_offsets.push(ss_bytes.len()); + Ok(ArrayBytes::new_vlen(ss_bytes, ss_offsets)) + } + ArrayBytes::Fixed(bytes) => { + let byte_ranges = + subset.byte_ranges(array_shape, data_type.fixed_size().unwrap())?; + let bytes = unsafe { extract_byte_ranges_concat_unchecked(bytes, &byte_ranges) }; + Ok(ArrayBytes::new_flen(bytes)) + } + } + } +} + +/// Validate fixed length array bytes for a given array size. +fn validate_bytes_flen(bytes: &RawBytes, array_size: u64) -> Result<(), CodecError> { + if bytes.len() as u64 == array_size { + Ok(()) + } else { + Err(CodecError::UnexpectedChunkDecodedSize( + bytes.len(), + array_size, + )) + } +} + +/// Validate variable length array bytes for an array with `num_elements`. +fn validate_bytes_vlen( + bytes: &RawBytes, + offsets: &RawBytesOffsets, + num_elements: u64, +) -> Result<(), CodecError> { + if offsets.len() as u64 != num_elements + 1 { + return Err(CodecError::InvalidVariableSizedArrayOffsets); + } + let len = bytes.len(); + let mut offset_last = 0; + for offset in offsets.iter() { + if *offset < offset_last || *offset > len { + return Err(CodecError::InvalidVariableSizedArrayOffsets); + } + offset_last = *offset; + } + if offset_last == len { + Ok(()) + } else { + Err(CodecError::InvalidVariableSizedArrayOffsets) + } +} + +/// Validate array bytes. +fn validate_bytes( + bytes: &ArrayBytes<'_>, + num_elements: u64, + data_type_size: DataTypeSize, +) -> Result<(), CodecError> { + match (bytes, data_type_size) { + (ArrayBytes::Fixed(bytes), DataTypeSize::Fixed(data_type_size)) => { + validate_bytes_flen(bytes, num_elements * data_type_size as u64) + } + (ArrayBytes::Variable(bytes, offsets), DataTypeSize::Variable) => { + validate_bytes_vlen(bytes, offsets, num_elements) + } + (ArrayBytes::Fixed(_), DataTypeSize::Variable) => Err(CodecError::Other( + "Used fixed length array bytes with a variable sized data type.".to_string(), + )), + (ArrayBytes::Variable(_, _), DataTypeSize::Fixed(_)) => Err(CodecError::Other( + "Used variable length array bytes with a fixed length data type.".to_string(), + )), + } +} + +/// This function is used internally by various array/codec methods to write the bytes of a chunk subset into an output with an associated array subset. +/// This approach only works for fixed length data types. +pub(crate) fn update_bytes_flen( + output_bytes: &mut [u8], + output_shape: &[u64], + subset_bytes: &RawBytes, + subset: &ArraySubset, + data_type_size: usize, +) { + debug_assert_eq!( + output_bytes.len(), + usize::try_from(output_shape.iter().product::()).unwrap() * data_type_size + ); + debug_assert_eq!( + subset_bytes.len(), + subset.num_elements_usize() * data_type_size, + ); + + let contiguous_indices = + unsafe { subset.contiguous_linearised_indices_unchecked(output_shape) }; + let length = contiguous_indices.contiguous_elements_usize() * data_type_size; + let mut decoded_offset = 0; + // TODO: Par iteration? + for (array_subset_element_index, _num_elements) in &contiguous_indices { + let output_offset = usize::try_from(array_subset_element_index).unwrap() * data_type_size; + debug_assert!((output_offset + length) <= output_bytes.len()); + debug_assert!((decoded_offset + length) <= subset_bytes.len()); + output_bytes[output_offset..output_offset + length] + .copy_from_slice(&subset_bytes[decoded_offset..decoded_offset + length]); + decoded_offset += length; + } +} + +pub(crate) fn update_bytes_vlen<'a>( + output_bytes: &RawBytes, + output_offsets: &RawBytesOffsets, + output_shape: ArrayShape, + subset_bytes: &RawBytes, + subset_offsets: &RawBytesOffsets, + subset: &ArraySubset, +) -> ArrayBytes<'a> { + // Get the current and new length of the bytes in the chunk subset + let size_subset_new = { + let chunk_subset_indices = subset + .relative_to(subset.start()) + .unwrap() + .linearised_indices(subset.shape()) + .unwrap(); + chunk_subset_indices + .iter() + .map(|index| { + let index = usize::try_from(index).unwrap(); + subset_offsets[index + 1] - subset_offsets[index] + }) + .sum::() + }; + let size_subset_old = { + let chunk_indices = subset.linearised_indices(&output_shape).unwrap(); + chunk_indices + .iter() + .map(|index| { + let index = usize::try_from(index).unwrap(); + output_offsets[index + 1] - output_offsets[index] + }) + .sum::() + }; + + // Populate new offsets and bytes + let mut offsets_new = Vec::with_capacity(output_offsets.len()); + let bytes_new_len = (output_bytes.len() + size_subset_new) + .checked_sub(size_subset_old) + .unwrap(); + let mut bytes_new = Vec::with_capacity(bytes_new_len); + let indices = ArraySubset::new_with_shape(output_shape).indices(); + for (chunk_index, indices) in indices.iter().enumerate() { + offsets_new.push(bytes_new.len()); + if subset.contains(&indices) { + let subset_indices = indices + .iter() + .zip(subset.start()) + .map(|(i, s)| i - s) + .collect::>(); + let subset_index = + usize::try_from(ravel_indices(&subset_indices, subset.shape())).unwrap(); + let start = subset_offsets[subset_index]; + let end = subset_offsets[subset_index + 1]; + bytes_new.extend_from_slice(&subset_bytes[start..end]); + } else { + let start = output_offsets[chunk_index]; + let end = output_offsets[chunk_index + 1]; + bytes_new.extend_from_slice(&output_bytes[start..end]); + } + } + offsets_new.push(bytes_new.len()); + + ArrayBytes::new_vlen(bytes_new, offsets_new) +} + +/// Update the intersecting subset of the chunk +/// This function is used internally by [`store_chunk_subset_opt`] and [`async_store_chunk_subset_opt`] +pub(crate) fn update_array_bytes<'a>( + output_bytes: ArrayBytes, + output_shape: ArrayShape, + subset_bytes: ArrayBytes, + subset: &ArraySubset, + data_type_size: DataTypeSize, +) -> ArrayBytes<'a> { + match (output_bytes, subset_bytes, data_type_size) { + ( + ArrayBytes::Variable(chunk_bytes, chunk_offsets), + ArrayBytes::Variable(chunk_subset_bytes, chunk_subset_offsets), + DataTypeSize::Variable, + ) => update_bytes_vlen( + &chunk_bytes, + &chunk_offsets, + output_shape, + &chunk_subset_bytes, + &chunk_subset_offsets, + subset, + ), + ( + ArrayBytes::Fixed(chunk_bytes), + ArrayBytes::Fixed(chunk_subset_bytes), + DataTypeSize::Fixed(data_type_size), + ) => { + let mut chunk_bytes = chunk_bytes.into_owned(); + update_bytes_flen( + &mut chunk_bytes, + &output_shape, + &chunk_subset_bytes, + subset, + data_type_size, + ); + ArrayBytes::new_flen(chunk_bytes) + } + (_, _, _) => { + unreachable!("Validation should occur outside of this function") + } + } +} + +/// Merge a set of chunks into an array subset. +/// +/// This function is used internally by [`retrieve_array_subset_opt`] and [`async_retrieve_array_subset_opt`]. +pub(crate) fn merge_chunks_vlen<'a>( + chunk_bytes_and_subsets: Vec<(ArrayBytes<'_>, ArraySubset)>, + array_shape: &[u64], +) -> Result, CodecError> { + let num_elements = usize::try_from(array_shape.iter().product::()).unwrap(); + + #[cfg(debug_assertions)] + { + // Validate the input + let mut element_in_input = vec![0; num_elements]; + for (_, chunk_subset) in &chunk_bytes_and_subsets { + // println!("{chunk_subset:?}"); + let indices = chunk_subset.linearised_indices(array_shape).unwrap(); + for idx in &indices { + let idx = usize::try_from(idx).unwrap(); + element_in_input[idx] += 1; + } + } + assert!(element_in_input.iter().all(|v| *v == 1)); + } + + // Get the size of each element + // TODO: Go parallel + let mut element_sizes = vec![0; num_elements]; + for (chunk_bytes, chunk_subset) in &chunk_bytes_and_subsets { + let chunk_offsets = chunk_bytes.offsets().unwrap(); + debug_assert_eq!(chunk_offsets.len() as u64, chunk_subset.num_elements() + 1); + let indices = chunk_subset.linearised_indices(array_shape).unwrap(); + debug_assert_eq!(chunk_offsets.len(), indices.len() + 1); + for (subset_idx, (curr, next)) in indices.iter().zip(chunk_offsets.iter().tuple_windows()) { + debug_assert!(next >= curr); + let subset_idx = usize::try_from(subset_idx).unwrap(); + element_sizes[subset_idx] = next - curr; + } + } + + // Convert to offsets with a cumulative sum + // TODO: Parallel cum sum + let mut offsets = Vec::with_capacity(element_sizes.len() + 1); + offsets.push(0); // first offset is always zero + offsets.extend(element_sizes.iter().scan(0, |acc, &sz| { + *acc += sz; + Some(*acc) + })); + + // Write bytes + // TODO: Go parallel + let mut bytes = vec![0; *offsets.last().unwrap()]; + for (chunk_bytes, chunk_subset) in chunk_bytes_and_subsets { + let (chunk_bytes, chunk_offsets) = chunk_bytes.into_variable()?; + let indices = chunk_subset.linearised_indices(array_shape).unwrap(); + for (subset_idx, (&chunk_curr, &chunk_next)) in + indices.iter().zip(chunk_offsets.iter().tuple_windows()) + { + let subset_idx = usize::try_from(subset_idx).unwrap(); + let subset_curr = offsets[subset_idx]; + let subset_next = offsets[subset_idx + 1]; + bytes[subset_curr..subset_next].copy_from_slice(&chunk_bytes[chunk_curr..chunk_next]); + } + } + + Ok(ArrayBytes::new_vlen(bytes, offsets)) +} + +pub(crate) fn extract_decoded_regions_vlen<'a>( + bytes: &[u8], + offsets: &[usize], + decoded_regions: &[ArraySubset], + array_shape: &[u64], +) -> Result>, CodecError> { + let mut out = Vec::with_capacity(decoded_regions.len()); + for decoded_region in decoded_regions { + let indices = decoded_region.linearised_indices(array_shape)?; + let mut region_bytes_len = 0; + for index in &indices { + let index = usize::try_from(index).unwrap(); + let curr = offsets[index]; + let next = offsets[index + 1]; + debug_assert!(next >= curr); + region_bytes_len += next - curr; + } + let mut region_offsets = Vec::with_capacity(decoded_region.num_elements_usize() + 1); + let mut region_bytes = Vec::with_capacity(region_bytes_len); + for index in &indices { + region_offsets.push(region_bytes.len()); + let index = usize::try_from(index).unwrap(); + let curr = offsets[index]; + let next = offsets[index + 1]; + region_bytes.extend_from_slice(&bytes[curr..next]); + } + region_offsets.push(region_bytes.len()); + out.push(ArrayBytes::new_vlen(region_bytes, region_offsets)); + } + Ok(out) +} + +impl<'a> From> for ArrayBytes<'a> { + fn from(bytes: RawBytes<'a>) -> Self { + Self::new_flen(bytes) + } +} + +// impl<'a, 'b> From<&ArrayBytes<'a>> for ArrayBytes<'b> { +// fn from(bytes: &ArrayBytes<'a>) -> Self { +// match bytes { +// Self::Fixed(bytes) => { +// let bytes = bytes.to_vec(); +// ArrayBytes::<'b>::new_flen(bytes) +// }, +// Self::Variable(bytes, offsets) => { +// let bytes: RawBytes<'b> = bytes.to_vec().into(); +// let offsets: RawBytesOffsets<'b> = offsets.to_vec().into(); +// ArrayBytes::new_vlen(bytes, offsets) +// } +// } +// } +// } + +// impl<'a> From> for ArrayBytes<'a> { +// fn from(bytes: ArrayBytes<'_>) -> Self { +// match bytes { +// ArrayBytes::Fixed(bytes) => ArrayBytes::new_flen(bytes) +// ArrayBytes::Variable(bytes, offsets) => ArrayBytes::new_vlen(bytes, offsets) +// } +// } +// } + +impl<'a> From<&'a [u8]> for ArrayBytes<'a> { + fn from(bytes: &'a [u8]) -> Self { + ArrayBytes::new_flen(bytes) + } +} + +impl From> for ArrayBytes<'_> { + fn from(bytes: Vec) -> Self { + ArrayBytes::new_flen(bytes) + } +} + +impl<'a, const N: usize> From<&'a [u8; N]> for ArrayBytes<'a> { + fn from(bytes: &'a [u8; N]) -> Self { + // NOTE: as_slice() is needed for rust <1.77 + ArrayBytes::new_flen(bytes.as_slice()) + } +} + +#[cfg(test)] +mod tests { + use std::error::Error; + use std::mem::size_of; + + use crate::array::Element; + + use super::*; + + #[test] + fn array_bytes_flen() -> Result<(), Box> { + let data = [0u32, 1, 2, 3, 4]; + let bytes = Element::into_array_bytes(&DataType::UInt32, &data)?; + let ArrayBytes::Fixed(bytes) = bytes else { + panic!() + }; + assert_eq!(bytes.len(), size_of::() * data.len()); + + Ok(()) + } + + #[test] + fn array_bytes_str() -> Result<(), Box> { + let data = ["a", "bb", "ccc"]; + let bytes = Element::into_array_bytes(&DataType::String, &data)?; + let ArrayBytes::Variable(bytes, offsets) = bytes else { + panic!() + }; + assert_eq!(bytes, "abbccc".as_bytes()); + assert_eq!(*offsets, [0, 1, 3, 6]); + + Ok(()) + } + + #[test] + fn test_flen_update_subset() { + let mut bytes_array = vec![0u8; 4 * 4]; + update_bytes_flen( + &mut bytes_array, + &vec![4, 4], + &vec![1u8, 2].into(), + &ArraySubset::new_with_ranges(&[1..2, 1..3]), + 1, + ); + + update_bytes_flen( + &mut bytes_array, + &vec![4, 4], + &vec![3u8, 4].into(), + &ArraySubset::new_with_ranges(&[3..4, 0..2]), + 1, + ); + + debug_assert_eq!( + bytes_array, + vec![0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 3, 4, 0, 0] + ); + } +} diff --git a/src/array/array_errors.rs b/src/array/array_errors.rs index 7d078fac..ff628d6d 100644 --- a/src/array/array_errors.rs +++ b/src/array/array_errors.rs @@ -97,9 +97,16 @@ pub enum ArrayError { #[error("got chunk decoded shape {_0:?}, expected {_1:?}")] UnexpectedChunkDecodedShape(ArrayShape, ArrayShape), /// Incompatible element size. - #[error("got element size {_0}, expected {_1}")] - IncompatibleElementSize(usize, usize), + #[error("the element types does not match the data type")] + IncompatibleElementType, /// Invalid data shape. #[error("data has shape {_0:?}, expected {_1:?}")] InvalidDataShape(Vec, Vec), + /// Invalid element value. + /// + /// For example + /// - a bool array with a value not equal to 0 (false) or 1 (true). + /// - a string with invalid utf-8 encoding. + #[error("Invalid element value")] + InvalidElementValue, } diff --git a/src/array/array_representation.rs b/src/array/array_representation.rs index 51897f29..d74a81ab 100644 --- a/src/array/array_representation.rs +++ b/src/array/array_representation.rs @@ -1,6 +1,9 @@ use std::num::NonZeroU64; -use super::{data_type::IncompatibleFillValueError, ArrayShape, DataType, FillValue}; +use super::{ + data_type::{DataTypeSize, IncompatibleFillValueError}, + ArrayShape, DataType, FillValue, +}; use derive_more::Display; /// The shape, data type, and fill value of an `array`. @@ -24,6 +27,23 @@ pub type ArrayRepresentation = ArrayRepresentationBase; /// The array representation of a chunk, which must have nonzero dimensions. pub type ChunkRepresentation = ArrayRepresentationBase; +/// The size of an array/chunk. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ArraySize { + /// Fixed size. + Fixed { + /// The number of elements. + num_elements: u64, + /// The data type size (in bytes). + data_type_size: usize, + }, + /// Variable sized. + Variable { + /// The number of elements. + num_elements: u64, + }, +} + impl ArrayRepresentationBase where TDim: Into + core::fmt::Debug + Copy, @@ -38,17 +58,26 @@ where data_type: DataType, fill_value: FillValue, ) -> Result { - if data_type.size() == fill_value.size() { - Ok(Self { + match data_type.size() { + DataTypeSize::Fixed(size) => { + if size == fill_value.size() { + Ok(Self { + array_shape, + data_type, + fill_value, + }) + } else { + Err(IncompatibleFillValueError::new( + data_type.name(), + fill_value, + )) + } + } + DataTypeSize::Variable => Ok(Self { array_shape, data_type, fill_value, - }) - } else { - Err(IncompatibleFillValueError::new( - data_type.name(), - fill_value, - )) + }), } } @@ -62,7 +91,9 @@ where data_type: DataType, fill_value: FillValue, ) -> Self { - debug_assert_eq!(data_type.size(), fill_value.size()); + if let Some(data_type_size) = data_type.fixed_size() { + debug_assert_eq!(data_type_size, fill_value.size()); + } Self { array_shape, data_type, @@ -123,23 +154,26 @@ where /// Return the element size. #[must_use] - pub fn element_size(&self) -> usize { - self.fill_value.size() + pub fn element_size(&self) -> DataTypeSize { + self.data_type().size() } - /// Return the total size in bytes. - /// - /// Equal to the product of each element of its shape and the element size. + /// Returns the element size in bytes with a fixed-size data type, otherwise returns [`None`]. #[must_use] - pub fn size(&self) -> u64 { - self.num_elements() * self.element_size() as u64 + pub fn fixed_element_size(&self) -> Option { + self.data_type().fixed_size() } - /// Return the total size in bytes as a [`usize`]. - /// - /// Equal to the product of each element of its shape and the element size. + /// Return the array size. #[must_use] - pub fn size_usize(&self) -> usize { - self.num_elements_usize() * self.element_size() + pub fn size(&self) -> ArraySize { + let num_elements = self.num_elements(); + match self.element_size() { + DataTypeSize::Fixed(data_type_size) => ArraySize::Fixed { + num_elements, + data_type_size, + }, + DataTypeSize::Variable => ArraySize::Variable { num_elements }, + } } } diff --git a/src/array/array_sync_readable.rs b/src/array/array_sync_readable.rs index 8004473e..ecb17187 100644 --- a/src/array/array_sync_readable.rs +++ b/src/array/array_sync_readable.rs @@ -4,7 +4,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_iter_concurrent_limit::iter_concurrent_limit; use crate::{ - array::ArrayMetadataV2, + array::{ArrayBytes, ArrayMetadataV2}, array_subset::ArraySubset, metadata::MetadataRetrieveVersion, node::NodePath, @@ -15,14 +15,15 @@ use crate::{ }; use super::{ + array_bytes::{merge_chunks_vlen, update_bytes_flen}, codec::{ - options::CodecOptions, ArrayCodecTraits, ArrayPartialDecoderTraits, - ArrayToBytesCodecTraits, StoragePartialDecoder, + options::CodecOptions, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, + StoragePartialDecoder, }, concurrency::concurrency_chunks_and_codec, - transmute_from_bytes_vec, + element::ElementOwned, unsafe_cell_slice::UnsafeCellSlice, - validate_element_size, Array, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV3, + Array, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV3, DataTypeSize, }; #[cfg(feature = "ndarray")] @@ -105,7 +106,7 @@ impl Array { pub fn retrieve_chunk_if_exists( &self, chunk_indices: &[u64], - ) -> Result>, ArrayError> { + ) -> Result>, ArrayError> { self.retrieve_chunk_if_exists_opt(chunk_indices, &CodecOptions::default()) } @@ -118,7 +119,7 @@ impl Array { /// - `chunk_indices` are invalid, /// - there is a codec decoding error, or /// - an underlying store error. - pub fn retrieve_chunk_elements_if_exists( + pub fn retrieve_chunk_elements_if_exists( &self, chunk_indices: &[u64], ) -> Result>, ArrayError> { @@ -138,7 +139,7 @@ impl Array { /// /// # Panics /// Will panic if a chunk dimension is larger than `usize::MAX`. - pub fn retrieve_chunk_ndarray_if_exists( + pub fn retrieve_chunk_ndarray_if_exists( &self, chunk_indices: &[u64], ) -> Result>, ArrayError> { @@ -178,7 +179,7 @@ impl Array { /// /// # Panics /// Panics if the number of elements in the chunk exceeds `usize::MAX`. - pub fn retrieve_chunk(&self, chunk_indices: &[u64]) -> Result, ArrayError> { + pub fn retrieve_chunk(&self, chunk_indices: &[u64]) -> Result, ArrayError> { self.retrieve_chunk_opt(chunk_indices, &CodecOptions::default()) } @@ -191,7 +192,7 @@ impl Array { /// - `chunk_indices` are invalid, /// - there is a codec decoding error, or /// - an underlying store error. - pub fn retrieve_chunk_elements( + pub fn retrieve_chunk_elements( &self, chunk_indices: &[u64], ) -> Result, ArrayError> { @@ -211,7 +212,7 @@ impl Array { /// /// # Panics /// Will panic if a chunk dimension is larger than `usize::MAX`. - pub fn retrieve_chunk_ndarray( + pub fn retrieve_chunk_ndarray( &self, chunk_indices: &[u64], ) -> Result, ArrayError> { @@ -264,7 +265,7 @@ impl Array { /// /// # Panics /// Panics if the number of array elements in the chunk exceeds `usize::MAX`. - pub fn retrieve_chunks(&self, chunks: &ArraySubset) -> Result, ArrayError> { + pub fn retrieve_chunks(&self, chunks: &ArraySubset) -> Result, ArrayError> { self.retrieve_chunks_opt(chunks, &CodecOptions::default()) } @@ -275,7 +276,7 @@ impl Array { /// /// # Panics /// Panics if the number of array elements in the chunks exceeds `usize::MAX`. - pub fn retrieve_chunks_elements( + pub fn retrieve_chunks_elements( &self, chunks: &ArraySubset, ) -> Result, ArrayError> { @@ -290,7 +291,7 @@ impl Array { /// /// # Panics /// Panics if the number of array elements in the chunks exceeds `usize::MAX`. - pub fn retrieve_chunks_ndarray( + pub fn retrieve_chunks_ndarray( &self, chunks: &ArraySubset, ) -> Result, ArrayError> { @@ -312,7 +313,7 @@ impl Array { &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { self.retrieve_chunk_subset_opt(chunk_indices, chunk_subset, &CodecOptions::default()) } @@ -324,7 +325,7 @@ impl Array { /// - the chunk subset is invalid, /// - there is a codec decoding error, or /// - an underlying store error. - pub fn retrieve_chunk_subset_elements( + pub fn retrieve_chunk_subset_elements( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -348,7 +349,7 @@ impl Array { /// /// # Panics /// Will panic if the number of elements in `chunk_subset` is `usize::MAX` or larger. - pub fn retrieve_chunk_subset_ndarray( + pub fn retrieve_chunk_subset_ndarray( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -373,7 +374,10 @@ impl Array { /// /// # Panics /// Panics if attempting to reference a byte beyond `usize::MAX`. - pub fn retrieve_array_subset(&self, array_subset: &ArraySubset) -> Result, ArrayError> { + pub fn retrieve_array_subset( + &self, + array_subset: &ArraySubset, + ) -> Result, ArrayError> { self.retrieve_array_subset_opt(array_subset, &CodecOptions::default()) } @@ -386,7 +390,7 @@ impl Array { /// - an array subset is invalid or out of bounds of the array, /// - there is a codec decoding error, or /// - an underlying store error. - pub fn retrieve_array_subset_elements( + pub fn retrieve_array_subset_elements( &self, array_subset: &ArraySubset, ) -> Result, ArrayError> { @@ -404,7 +408,7 @@ impl Array { /// /// # Panics /// Will panic if any dimension in `chunk_subset` is `usize::MAX` or larger. - pub fn retrieve_array_subset_ndarray( + pub fn retrieve_array_subset_ndarray( &self, array_subset: &ArraySubset, ) -> Result, ArrayError> { @@ -432,7 +436,7 @@ impl Array { &self, chunk_indices: &[u64], options: &CodecOptions, - ) -> Result>, ArrayError> { + ) -> Result>, ArrayError> { if chunk_indices.len() != self.dimensionality() { return Err(ArrayError::InvalidChunkGridIndicesError( chunk_indices.to_vec(), @@ -451,7 +455,7 @@ impl Array { .map_err(ArrayError::StorageError)?; if let Some(chunk_encoded) = chunk_encoded { let chunk_representation = self.chunk_array_representation(chunk_indices)?; - let chunk_decoded = self + let bytes = self .codecs() .decode( Cow::Borrowed(&chunk_encoded), @@ -459,16 +463,11 @@ impl Array { options, ) .map_err(ArrayError::CodecError)?; - let chunk_decoded_size = - chunk_representation.num_elements_usize() * chunk_representation.data_type().size(); - if chunk_decoded.len() == chunk_decoded_size { - Ok(Some(chunk_decoded.into_owned())) - } else { - Err(ArrayError::UnexpectedChunkDecodedSize( - chunk_decoded.len(), - chunk_decoded_size, - )) - } + bytes.validate( + chunk_representation.num_elements(), + chunk_representation.data_type().size(), + )?; + Ok(Some(bytes.into_owned())) } else { Ok(None) } @@ -480,50 +479,54 @@ impl Array { &self, chunk_indices: &[u64], options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { let chunk = self.retrieve_chunk_if_exists_opt(chunk_indices, options)?; if let Some(chunk) = chunk { Ok(chunk) } else { let chunk_representation = self.chunk_array_representation(chunk_indices)?; - let fill_value = chunk_representation.fill_value().as_ne_bytes(); - Ok(fill_value.repeat(chunk_representation.num_elements_usize())) + Ok(ArrayBytes::new_fill_value( + chunk_representation.num_elements_usize(), + self.fill_value(), + )) } } /// Explicit options version of [`retrieve_chunk_elements_if_exists`](Array::retrieve_chunk_elements_if_exists). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_elements_if_exists_opt( + pub fn retrieve_chunk_elements_if_exists_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result>, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_chunk_if_exists_opt(chunk_indices, options)?; - Ok(bytes.map(|bytes| transmute_from_bytes_vec::(bytes))) + if let Some(bytes) = self.retrieve_chunk_if_exists_opt(chunk_indices, options)? { + Ok(Some(T::from_array_bytes(self.data_type(), bytes)?)) + } else { + Ok(None) + } } /// Explicit options version of [`retrieve_chunk_elements`](Array::retrieve_chunk_elements). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_elements_opt( + pub fn retrieve_chunk_elements_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_chunk_opt(chunk_indices, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_chunk_opt(chunk_indices, options)?, + ) } #[cfg(feature = "ndarray")] /// Explicit options version of [`retrieve_chunk_ndarray_if_exists`](Array::retrieve_chunk_ndarray_if_exists). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_ndarray_if_exists_opt( + pub fn retrieve_chunk_ndarray_if_exists_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result>, ArrayError> { - // validate_element_size::(self.data_type())?; // in retrieve_chunk_elements_if_exists let shape = self .chunk_grid() .chunk_shape_u64(chunk_indices, self.shape())? @@ -539,12 +542,11 @@ impl Array { #[cfg(feature = "ndarray")] /// Explicit options version of [`retrieve_chunk_ndarray`](Array::retrieve_chunk_ndarray). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_ndarray_opt( + pub fn retrieve_chunk_ndarray_opt( &self, chunk_indices: &[u64], options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in retrieve_chunk_elements let shape = self .chunk_grid() .chunk_shape_u64(chunk_indices, self.shape())? @@ -561,7 +563,7 @@ impl Array { &self, chunks: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { if chunks.dimensionality() != self.dimensionality() { return Err(ArrayError::InvalidArraySubset( chunks.clone(), @@ -575,25 +577,22 @@ impl Array { /// Explicit options version of [`retrieve_chunks_elements`](Array::retrieve_chunks_elements). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunks_elements_opt( + pub fn retrieve_chunks_elements_opt( &self, chunks: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_chunks_opt(chunks, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes(self.data_type(), self.retrieve_chunks_opt(chunks, options)?) } #[cfg(feature = "ndarray")] /// Explicit options version of [`retrieve_chunks_ndarray`](Array::retrieve_chunks_ndarray). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunks_ndarray_opt( + pub fn retrieve_chunks_ndarray_opt( &self, chunks: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in retrieve_chunks_elements_opt let array_subset = self.chunks_subset(chunks)?; let elements = self.retrieve_chunks_elements_opt::(chunks, options)?; elements_to_ndarray(array_subset.shape(), elements) @@ -601,11 +600,12 @@ impl Array { /// Explicit options version of [`retrieve_array_subset`](Array::retrieve_array_subset). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] + #[allow(clippy::too_many_lines)] pub fn retrieve_array_subset_opt( &self, array_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { if array_subset.dimensionality() != self.dimensionality() { return Err(ArrayError::InvalidArraySubset( array_subset.clone(), @@ -625,10 +625,10 @@ impl Array { // Retrieve chunk bytes let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => Ok(self - .fill_value() - .as_ne_bytes() - .repeat(array_subset.num_elements_usize())), + 0 => Ok(ArrayBytes::new_fill_value( + array_subset.num_elements_usize(), + self.fill_value(), + )), 1 => { let chunk_indices = chunks.start(); let chunk_subset = self.chunk_subset(chunk_indices)?; @@ -646,13 +646,10 @@ impl Array { } } _ => { - // Allocate the output - let size_output = array_subset.num_elements_usize() * self.data_type().size(); - let mut output = Vec::with_capacity(size_output); - - // Calculate chunk/codec concurrency let chunk_representation = self.chunk_array_representation(&vec![0; self.dimensionality()])?; + + // Calculate chunk/codec concurrency let codec_concurrency = self.recommended_codec_concurrency(&chunk_representation)?; let (chunk_concurrent_limit, options) = concurrency_chunks_and_codec( @@ -662,73 +659,100 @@ impl Array { &codec_concurrency, ); - { - let output = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); - let retrieve_chunk = |chunk_indices: Vec| { - let chunk_subset = self.chunk_subset(&chunk_indices)?; - let chunk_subset_overlap = chunk_subset.overlap(array_subset)?; - let chunk_subset_bytes = self.retrieve_chunk_subset_opt( - &chunk_indices, - &chunk_subset_overlap.relative_to(chunk_subset.start())?, - &options, - )?; - let contiguous_indices = unsafe { - chunk_subset_overlap - .relative_to(array_subset.start())? - .contiguous_linearised_indices_unchecked(array_subset.shape()) + match chunk_representation.data_type().size() { + DataTypeSize::Variable => { + // Retrieve all the chunks + let retrieve_chunk = |chunk_indices: Vec| -> Result< + (ArrayBytes<'_>, ArraySubset), + ArrayError, + > { + let chunk_subset = self.chunk_subset(&chunk_indices)?; + let chunk_subset_overlap = chunk_subset.overlap(array_subset)?; + Ok(( + self.retrieve_chunk_subset_opt( + &chunk_indices, + &chunk_subset_overlap.relative_to(chunk_subset.start())?, + &options, + )?, + chunk_subset_overlap.relative_to(array_subset.start())?, + )) }; - let element_size = self.data_type().size(); - let length = contiguous_indices.contiguous_elements_usize() * element_size; - let mut decoded_offset = 0; - // FIXME: Par iteration? - let output = unsafe { output.get() }; - for (array_subset_element_index, _num_elements) in &contiguous_indices { - let output_offset = - usize::try_from(array_subset_element_index).unwrap() * element_size; - debug_assert!((output_offset + length) <= output.len()); - debug_assert!((decoded_offset + length) <= chunk_subset_bytes.len()); - output[output_offset..output_offset + length].copy_from_slice( - &chunk_subset_bytes[decoded_offset..decoded_offset + length], - ); - decoded_offset += length; + let chunk_indices = chunks.indices(); + let chunk_bytes_and_subsets = iter_concurrent_limit!( + chunk_concurrent_limit, + chunk_indices, + map, + retrieve_chunk + ) + .collect::, _>>()?; + + Ok(merge_chunks_vlen( + chunk_bytes_and_subsets, + array_subset.shape(), + )?) + } + DataTypeSize::Fixed(data_type_size) => { + // Allocate the output + let size_output = array_subset.num_elements_usize() * data_type_size; + let mut output = Vec::with_capacity(size_output); + + { + let output = + UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); + let retrieve_chunk = |chunk_indices: Vec| { + let chunk_subset = self.chunk_subset(&chunk_indices)?; + let chunk_subset_overlap = chunk_subset.overlap(array_subset)?; + let chunk_subset_bytes = self.retrieve_chunk_subset_opt( + &chunk_indices, + &chunk_subset_overlap.relative_to(chunk_subset.start())?, + &options, + )?; + update_bytes_flen( + unsafe { output.get() }, + array_subset.shape(), + &chunk_subset_bytes.into_fixed()?, + &chunk_subset_overlap.relative_to(array_subset.start())?, + data_type_size, + ); + Ok::<_, ArrayError>(()) + }; + let indices = chunks.indices(); + iter_concurrent_limit!( + chunk_concurrent_limit, + indices, + try_for_each, + retrieve_chunk + )?; } - Ok::<_, ArrayError>(()) - }; - let indices = chunks.indices(); - iter_concurrent_limit!( - chunk_concurrent_limit, - indices, - try_for_each, - retrieve_chunk - )?; + unsafe { output.set_len(size_output) }; + Ok(ArrayBytes::from(output)) + } } - unsafe { output.set_len(size_output) }; - Ok(output) } } } /// Explicit options version of [`retrieve_array_subset_elements`](Array::retrieve_array_subset_elements). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_array_subset_elements_opt( + pub fn retrieve_array_subset_elements_opt( &self, array_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_array_subset_opt(array_subset, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_array_subset_opt(array_subset, options)?, + ) } #[cfg(feature = "ndarray")] /// Explicit options version of [`retrieve_array_subset_ndarray`](Array::retrieve_array_subset_ndarray). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_array_subset_ndarray_opt( + pub fn retrieve_array_subset_ndarray_opt( &self, array_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in retrieve_array_subset_elements_opt let elements = self.retrieve_array_subset_elements_opt::(array_subset, options)?; elements_to_ndarray(array_subset.shape(), elements) } @@ -740,7 +764,7 @@ impl Array { chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result, ArrayError> { let chunk_representation = self.chunk_array_representation(chunk_indices)?; if !chunk_subset.inbounds(&chunk_representation.shape_u64()) { return Err(ArrayError::InvalidArraySubset( @@ -749,7 +773,7 @@ impl Array { )); } - let decoded_bytes = if chunk_subset.start().iter().all(|&o| o == 0) + let bytes = if chunk_subset.start().iter().all(|&o| o == 0) && chunk_subset.shape() == chunk_representation.shape_u64() { // Fast path if `chunk_subset` encompasses the whole chunk @@ -764,51 +788,39 @@ impl Array { data_key(self.path(), chunk_indices, self.chunk_key_encoding()), )); - unsafe { - self.codecs() - .partial_decoder(input_handle, &chunk_representation, options)? - .partial_decode_opt(&[chunk_subset.clone()], options)? - .pop() - .unwrap_unchecked() - .into_owned() - } + self.codecs() + .partial_decoder(input_handle, &chunk_representation, options)? + .partial_decode_opt(&[chunk_subset.clone()], options)? + .remove(0) + .into_owned() }; - - let total_size = decoded_bytes.len(); - let expected_size = chunk_subset.num_elements_usize() * self.data_type().size(); - if total_size == chunk_subset.num_elements_usize() * self.data_type().size() { - Ok(decoded_bytes) - } else { - Err(ArrayError::UnexpectedChunkDecodedSize( - total_size, - expected_size, - )) - } + bytes.validate(chunk_subset.num_elements(), self.data_type().size())?; + Ok(bytes) } /// Explicit options version of [`retrieve_chunk_subset_elements`](Array::retrieve_chunk_subset_elements). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_subset_elements_opt( + pub fn retrieve_chunk_subset_elements_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_chunk_subset_opt(chunk_indices, chunk_subset, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_chunk_subset_opt(chunk_indices, chunk_subset, options)?, + ) } #[cfg(feature = "ndarray")] /// Explicit options version of [`retrieve_chunk_subset_ndarray`](Array::retrieve_chunk_subset_ndarray). #[allow(clippy::missing_errors_doc)] - pub fn retrieve_chunk_subset_ndarray_opt( + pub fn retrieve_chunk_subset_ndarray_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - // validate_element_size::(self.data_type())?; // in retrieve_chunk_subset_elements let elements = self.retrieve_chunk_subset_elements_opt::(chunk_indices, chunk_subset, options)?; elements_to_ndarray(chunk_subset.shape(), elements) diff --git a/src/array/array_sync_readable_writable.rs b/src/array/array_sync_readable_writable.rs index f728c889..28dc1185 100644 --- a/src/array/array_sync_readable_writable.rs +++ b/src/array/array_sync_readable_writable.rs @@ -1,11 +1,10 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use crate::{ - array::validate_element_size, array_subset::ArraySubset, storage::ReadableWritableStorageTraits, -}; +use crate::{array::ArrayBytes, array_subset::ArraySubset, storage::ReadableWritableStorageTraits}; use super::{ - codec::options::CodecOptions, concurrency::concurrency_chunks_and_codec, Array, ArrayError, + array_bytes::update_array_bytes, codec::options::CodecOptions, + concurrency::concurrency_chunks_and_codec, Array, ArrayError, Element, }; impl Array { @@ -23,11 +22,11 @@ impl Array /// # Panics /// Panics if attempting to reference a byte beyond `usize::MAX`. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunk_subset( + pub fn store_chunk_subset<'a>( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - chunk_subset_bytes: &[u8], + chunk_subset_bytes: impl Into>, ) -> Result<(), ArrayError> { self.store_chunk_subset_opt( chunk_indices, @@ -47,7 +46,7 @@ impl Array /// - the size of `T` does not match the data type size, or /// - a [`store_chunk_subset`](Array::store_chunk_subset) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunk_subset_elements( + pub fn store_chunk_subset_elements( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, @@ -70,7 +69,7 @@ impl Array /// # Errors /// Returns an [`ArrayError`] if a [`store_chunk_subset_elements`](Array::store_chunk_subset_elements) error condition is met. pub fn store_chunk_subset_ndarray< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -99,10 +98,10 @@ impl Array /// - there is a codec encoding error, or /// - an underlying store error. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_array_subset( + pub fn store_array_subset<'a>( &self, array_subset: &ArraySubset, - subset_bytes: &[u8], + subset_bytes: impl Into>, ) -> Result<(), ArrayError> { self.store_array_subset_opt(array_subset, subset_bytes, &CodecOptions::default()) } @@ -117,7 +116,7 @@ impl Array /// - the size of `T` does not match the data type size, or /// - a [`store_array_subset`](Array::store_array_subset) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_array_subset_elements( + pub fn store_array_subset_elements( &self, array_subset: &ArraySubset, subset_elements: &[T], @@ -139,7 +138,7 @@ impl Array /// Returns an [`ArrayError`] if a [`store_array_subset_elements`](Array::store_array_subset_elements) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub fn store_array_subset_ndarray< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -156,11 +155,11 @@ impl Array /// Explicit options version of [`store_chunk_subset`](Array::store_chunk_subset). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunk_subset_opt( + pub fn store_chunk_subset_opt<'a>( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, - chunk_subset_bytes: &[u8], + chunk_subset_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { let chunk_shape = self @@ -176,67 +175,55 @@ impl Array chunk_shape, )); } - let expected_length = - chunk_subset.shape().iter().product::() * self.data_type().size() as u64; - if chunk_subset_bytes.len() as u64 != expected_length { - return Err(ArrayError::InvalidBytesInputSize( - chunk_subset_bytes.len(), - expected_length, - )); - } if chunk_subset.shape() == chunk_shape && chunk_subset.start().iter().all(|&x| x == 0) { // The subset spans the whole chunk, so store the bytes directly and skip decoding self.store_chunk_opt(chunk_indices, chunk_subset_bytes, options) } else { + let chunk_subset_bytes = chunk_subset_bytes.into(); + chunk_subset_bytes.validate(chunk_subset.num_elements(), self.data_type().size())?; + // Lock the chunk // let key = data_key(self.path(), chunk_indices, self.chunk_key_encoding()); // let mutex = self.storage.mutex(&key)?; // let _lock = mutex.lock(); // Decode the entire chunk - let mut chunk_bytes = self.retrieve_chunk_opt(chunk_indices, options)?; + let chunk_bytes_old = self.retrieve_chunk_opt(chunk_indices, options)?; + chunk_bytes_old.validate(chunk_shape.iter().product(), self.data_type().size())?; - // Update the intersecting subset of the chunk - let element_size = self.data_type().size(); - let mut offset = 0; - let contiguous_indices = - unsafe { chunk_subset.contiguous_linearised_indices_unchecked(&chunk_shape) }; - let length = contiguous_indices.contiguous_elements_usize() * element_size; - // FIXME: Par iter? - for (chunk_element_index, _num_elements) in &contiguous_indices { - let chunk_offset = usize::try_from(chunk_element_index).unwrap() * element_size; - debug_assert!(chunk_offset + length <= chunk_bytes.len()); - debug_assert!(offset + length <= chunk_subset_bytes.len()); - chunk_bytes[chunk_offset..chunk_offset + length] - .copy_from_slice(&chunk_subset_bytes[offset..offset + length]); - offset += length; - } + // Update the chunk + let chunk_bytes_new = update_array_bytes( + chunk_bytes_old, + chunk_shape, + chunk_subset_bytes, + chunk_subset, + self.data_type().size(), + ); // Store the updated chunk - self.store_chunk_opt(chunk_indices, &chunk_bytes, options) + self.store_chunk_opt(chunk_indices, chunk_bytes_new, options) } } /// Explicit options version of [`store_chunk_subset_elements`](Array::store_chunk_subset_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunk_subset_elements_opt( + pub fn store_chunk_subset_elements_opt( &self, chunk_indices: &[u64], chunk_subset: &ArraySubset, chunk_subset_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunk_subset_elements = crate::array::transmute_to_bytes(chunk_subset_elements); - self.store_chunk_subset_opt(chunk_indices, chunk_subset, chunk_subset_elements, options) + let chunk_subset_bytes = T::into_array_bytes(self.data_type(), chunk_subset_elements)?; + self.store_chunk_subset_opt(chunk_indices, chunk_subset, chunk_subset_bytes, options) } #[cfg(feature = "ndarray")] /// Explicit options version of [`store_chunk_subset_ndarray`](Array::store_chunk_subset_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub fn store_chunk_subset_ndarray_opt< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -246,7 +233,6 @@ impl Array chunk_subset_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunk_subset_array: ndarray::Array = chunk_subset_array.into(); let subset = ArraySubset::new_with_start_shape( chunk_subset_start.to_vec(), @@ -262,10 +248,11 @@ impl Array /// Explicit options version of [`store_array_subset`](Array::store_array_subset). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_array_subset_opt( + #[allow(clippy::too_many_lines)] + pub fn store_array_subset_opt<'a>( &self, array_subset: &ArraySubset, - subset_bytes: &[u8], + subset_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { // Validation @@ -275,13 +262,6 @@ impl Array self.shape().to_vec(), )); } - let expected_size = array_subset.num_elements() * self.data_type().size() as u64; - if subset_bytes.len() as u64 != expected_size { - return Err(ArrayError::InvalidBytesInputSize( - subset_bytes.len(), - expected_size, - )); - } // Find the chunks intersecting this array subset let chunks = self.chunks_in_array_subset(array_subset)?; @@ -294,38 +274,23 @@ impl Array let num_chunks = chunks.num_elements_usize(); if num_chunks == 1 { let chunk_indices = chunks.start(); - let chunk_subset_in_array = unsafe { - self.chunk_grid() - .subset_unchecked(chunk_indices, self.shape()) - .unwrap() - }; - if array_subset == &chunk_subset_in_array { + let chunk_subset = self.chunk_subset(chunk_indices)?; + if array_subset == &chunk_subset { // A fast path if the array subset matches the chunk subset // This skips the internal decoding occurring in store_chunk_subset self.store_chunk_opt(chunk_indices, subset_bytes, options)?; } else { - let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset_in_array) }; - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - let chunk_subset_bytes = unsafe { - chunk_subset_in_array_subset.extract_bytes_unchecked( - subset_bytes, - array_subset.shape(), - self.data_type().size(), - ) - }; - // Store the chunk subset - let array_subset_in_chunk_subset = - unsafe { overlap.relative_to_unchecked(chunk_subset_in_array.start()) }; self.store_chunk_subset_opt( chunk_indices, - &array_subset_in_chunk_subset, - &chunk_subset_bytes, + &array_subset.relative_to(chunk_subset.start())?, + subset_bytes, options, )?; } } else { + let subset_bytes = subset_bytes.into(); + subset_bytes.validate(array_subset.num_elements(), self.data_type().size())?; // Calculate chunk/codec concurrency let chunk_representation = self.chunk_array_representation(&vec![0; self.dimensionality()])?; @@ -338,27 +303,21 @@ impl Array ); let store_chunk = |chunk_indices: Vec| -> Result<(), ArrayError> { - let chunk_subset_in_array = unsafe { - self.chunk_grid() - .subset_unchecked(&chunk_indices, self.shape()) - .unwrap() - }; + let chunk_subset_in_array = self.chunk_subset(&chunk_indices)?; let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset_in_array) }; let chunk_subset_in_array_subset = unsafe { overlap.relative_to_unchecked(array_subset.start()) }; + let chunk_subset_bytes = subset_bytes.extract_array_subset( + &chunk_subset_in_array_subset, + array_subset.shape(), + self.data_type(), + )?; let array_subset_in_chunk_subset = unsafe { overlap.relative_to_unchecked(chunk_subset_in_array.start()) }; - let chunk_subset_bytes = unsafe { - chunk_subset_in_array_subset.extract_bytes_unchecked( - subset_bytes, - array_subset.shape(), - self.data_type().size(), - ) - }; self.store_chunk_subset_opt( &chunk_indices, &array_subset_in_chunk_subset, - &chunk_subset_bytes, + chunk_subset_bytes, &options, ) }; @@ -376,22 +335,21 @@ impl Array /// Explicit options version of [`store_array_subset_elements`](Array::store_array_subset_elements). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_array_subset_elements_opt( + pub fn store_array_subset_elements_opt( &self, array_subset: &ArraySubset, subset_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let subset_elements = crate::array::transmute_to_bytes(subset_elements); - self.store_array_subset_opt(array_subset, subset_elements, options) + let subset_bytes = T::into_array_bytes(self.data_type(), subset_elements)?; + self.store_array_subset_opt(array_subset, subset_bytes, options) } #[cfg(feature = "ndarray")] /// Explicit options version of [`store_array_subset_ndarray`](Array::store_array_subset_ndarray). #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub fn store_array_subset_ndarray_opt< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -400,7 +358,6 @@ impl Array subset_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let subset_array: ndarray::Array = subset_array.into(); let subset = ArraySubset::new_with_start_shape( subset_start.to_vec(), diff --git a/src/array/array_sync_sharded_readable_ext.rs b/src/array/array_sync_sharded_readable_ext.rs index c9207829..048733db 100644 --- a/src/array/array_sync_sharded_readable_ext.rs +++ b/src/array/array_sync_sharded_readable_ext.rs @@ -3,10 +3,13 @@ use std::{collections::HashMap, sync::Arc}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_iter_concurrent_limit::iter_concurrent_limit; +use super::array_bytes::{merge_chunks_vlen, update_bytes_flen}; +use super::element::ElementOwned; use super::{ - codec::CodecOptions, concurrency::concurrency_chunks_and_codec, transmute_from_bytes_vec, - validate_element_size, Array, ArrayError, ArrayShardedExt, ChunkGrid, UnsafeCellSlice, + codec::CodecOptions, concurrency::concurrency_chunks_and_codec, Array, ArrayError, + ArrayShardedExt, ChunkGrid, UnsafeCellSlice, }; +use super::{ArrayBytes, DataTypeSize}; use crate::storage::ReadableStorageTraits; use crate::{array::codec::ArrayPartialDecoderTraits, array_subset::ArraySubset}; @@ -93,13 +96,13 @@ pub trait ArrayShardedReadableExt, inner_chunk_indices: &[u64], options: &CodecOptions, - ) -> Result, ArrayError>; + ) -> Result; /// Read and decode the inner chunk at `chunk_indices` into a vector of its elements. /// /// See [`Array::retrieve_chunk_elements_opt`]. #[allow(clippy::missing_errors_doc)] - fn retrieve_inner_chunk_elements_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunk_elements_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunk_indices: &[u64], @@ -111,7 +114,7 @@ pub trait ArrayShardedReadableExt( + fn retrieve_inner_chunk_ndarray_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunk_indices: &[u64], @@ -127,13 +130,13 @@ pub trait ArrayShardedReadableExt, inner_chunks: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError>; + ) -> Result; /// Read and decode the inner chunks at `inner_chunks` into a vector of their elements. /// /// See [`Array::retrieve_chunks_elements_opt`]. #[allow(clippy::missing_errors_doc)] - fn retrieve_inner_chunks_elements_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunks_elements_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunks: &ArraySubset, @@ -145,7 +148,7 @@ pub trait ArrayShardedReadableExt( + fn retrieve_inner_chunks_ndarray_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunks: &ArraySubset, @@ -161,13 +164,13 @@ pub trait ArrayShardedReadableExt, array_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError>; + ) -> Result; /// Read and decode the `array_subset` of array into a vector of its elements. /// /// See [`Array::retrieve_array_subset_elements_opt`]. #[allow(clippy::missing_errors_doc)] - fn retrieve_array_subset_elements_sharded_opt<'a, T: bytemuck::Pod>( + fn retrieve_array_subset_elements_sharded_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, array_subset: &ArraySubset, @@ -179,7 +182,7 @@ pub trait ArrayShardedReadableExt( + fn retrieve_array_subset_ndarray_sharded_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, array_subset: &ArraySubset, @@ -195,7 +198,7 @@ impl ArrayShardedReadableExt cache: &ArrayShardedReadableExtCache<'a>, inner_chunk_indices: &[u64], options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result { if cache.array_is_sharded() { let array_subset = cache .inner_chunk_grid() @@ -217,29 +220,30 @@ impl ArrayShardedReadableExt let shard_subset = array_subset.relative_to(&shard_origin)?; let partial_decoder = cache.retrieve(self, shard_indices)?; - Ok(partial_decoder + let bytes = partial_decoder .partial_decode_opt(&[shard_subset], options)? - .pop() - .expect("partial_decode_opt called with one subset, returned without error") - .into_owned()) + .remove(0) + .into_owned(); + Ok(bytes) } else { self.retrieve_chunk_opt(inner_chunk_indices, options) } } - fn retrieve_inner_chunk_elements_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunk_elements_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunk_indices: &[u64], options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_inner_chunk_opt(cache, inner_chunk_indices, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_inner_chunk_opt(cache, inner_chunk_indices, options)?, + ) } #[cfg(feature = "ndarray")] - fn retrieve_inner_chunk_ndarray_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunk_ndarray_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunk_indices: &[u64], @@ -260,7 +264,7 @@ impl ArrayShardedReadableExt cache: &ArrayShardedReadableExtCache<'a>, inner_chunks: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result { if cache.array_is_sharded() { let inner_chunk_grid = cache.inner_chunk_grid(); let array_subset = inner_chunk_grid @@ -280,19 +284,20 @@ impl ArrayShardedReadableExt } } - fn retrieve_inner_chunks_elements_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunks_elements_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunks: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_inner_chunks_opt(cache, inner_chunks, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_inner_chunks_opt(cache, inner_chunks, options)?, + ) } #[cfg(feature = "ndarray")] - fn retrieve_inner_chunks_ndarray_opt<'a, T: bytemuck::Pod>( + fn retrieve_inner_chunks_ndarray_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, inner_chunks: &ArraySubset, @@ -315,12 +320,13 @@ impl ArrayShardedReadableExt super::elements_to_ndarray(array_subset.shape(), elements) } + #[allow(clippy::too_many_lines)] fn retrieve_array_subset_sharded_opt<'a>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, array_subset: &ArraySubset, options: &CodecOptions, - ) -> Result, ArrayError> { + ) -> Result { if cache.array_is_sharded() { // Find the shards intersecting this array subset let shards = self.chunks_in_array_subset(array_subset)?; @@ -334,15 +340,11 @@ impl ArrayShardedReadableExt // Retrieve chunk bytes let num_shards = shards.num_elements_usize(); if num_shards == 0 { - Ok(self - .fill_value() - .as_ne_bytes() - .repeat(array_subset.num_elements_usize())) + Ok(ArrayBytes::new_fill_value( + array_subset.num_elements_usize(), + self.fill_value(), + )) } else { - // Allocate the output - let size_output = array_subset.num_elements_usize() * self.data_type().size(); - let mut output = Vec::with_capacity(size_output); - // Calculate chunk/codec concurrency let chunk_representation = self.chunk_array_representation(&vec![0; self.dimensionality()])?; @@ -355,77 +357,106 @@ impl ArrayShardedReadableExt &codec_concurrency, ); - { - let output = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); - let retrieve_shard = |shard_indices: Vec| { - let shard_subset = self.chunk_subset(&shard_indices)?; - let shard_subset_overlap = shard_subset.overlap(array_subset)?; - // let shard_subset_bytes = self.retrieve_chunk_subset_opt( - // &shard_indices, - // &shard_subset_overlap.relative_to(shard_subset.start())?, - // &options, - // )?; - let shard_subset_bytes = cache - .retrieve(self, &shard_indices)? - .partial_decode_opt( - &[shard_subset_overlap.relative_to(shard_subset.start())?], - &options, - )? - .pop() - .unwrap() - .into_owned(); - - let contiguous_indices = unsafe { - shard_subset_overlap - .relative_to(array_subset.start())? - .contiguous_linearised_indices_unchecked(array_subset.shape()) + match self.data_type().size() { + DataTypeSize::Variable => { + let retrieve_inner_chunk = |shard_indices: Vec| -> Result< + (ArrayBytes<'_>, ArraySubset), + ArrayError, + > { + let shard_subset = self.chunk_subset(&shard_indices)?; + let shard_subset_overlap = shard_subset.overlap(array_subset)?; + let bytes = cache + .retrieve(self, &shard_indices)? + .partial_decode_opt( + &[shard_subset_overlap.relative_to(shard_subset.start())?], + &options, + )? + .remove(0) + .into_owned(); + Ok(( + bytes, + shard_subset_overlap.relative_to(array_subset.start())?, + )) }; - let element_size = self.data_type().size(); - let length = contiguous_indices.contiguous_elements_usize() * element_size; - let mut decoded_offset = 0; - // FIXME: Par iteration? - let output = unsafe { output.get() }; - for (array_subset_element_index, _num_elements) in &contiguous_indices { - let output_offset = - usize::try_from(array_subset_element_index).unwrap() * element_size; - debug_assert!((output_offset + length) <= output.len()); - debug_assert!((decoded_offset + length) <= shard_subset_bytes.len()); - output[output_offset..output_offset + length].copy_from_slice( - &shard_subset_bytes[decoded_offset..decoded_offset + length], - ); - decoded_offset += length; + + let indices = shards.indices(); + let chunk_bytes_and_subsets = iter_concurrent_limit!( + chunk_concurrent_limit, + indices, + map, + retrieve_inner_chunk + ) + .collect::, _>>()?; + + Ok(merge_chunks_vlen( + chunk_bytes_and_subsets, + array_subset.shape(), + )?) + } + DataTypeSize::Fixed(data_type_size) => { + let size_output = array_subset.num_elements_usize() * data_type_size; + let mut output = Vec::with_capacity(size_output); + { + let output = + UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut output); + let retrieve_shard_into_slice = |shard_indices: Vec| { + let shard_subset = self.chunk_subset(&shard_indices)?; + let shard_subset_overlap = shard_subset.overlap(array_subset)?; + // let shard_subset_bytes = self.retrieve_chunk_subset_opt( + // &shard_indices, + // &shard_subset_overlap.relative_to(shard_subset.start())?, + // &options, + // )?; + let bytes = cache + .retrieve(self, &shard_indices)? + .partial_decode_opt( + &[shard_subset_overlap + .relative_to(shard_subset.start())?], + &options, + )? + .remove(0) + .into_owned(); + update_bytes_flen( + unsafe { output.get() }, + array_subset.shape(), + &bytes.into_fixed()?, + &shard_subset_overlap.relative_to(array_subset.start())?, + data_type_size, + ); + Ok::<_, ArrayError>(()) + }; + let indices = shards.indices(); + iter_concurrent_limit!( + chunk_concurrent_limit, + indices, + try_for_each, + retrieve_shard_into_slice + )?; } - Ok::<_, ArrayError>(()) - }; - let indices = shards.indices(); - iter_concurrent_limit!( - chunk_concurrent_limit, - indices, - try_for_each, - retrieve_shard - )?; + unsafe { output.set_len(size_output) }; + Ok(ArrayBytes::from(output)) + } } - unsafe { output.set_len(size_output) }; - Ok(output) } } else { self.retrieve_array_subset_opt(array_subset, options) } } - fn retrieve_array_subset_elements_sharded_opt<'a, T: bytemuck::Pod>( + fn retrieve_array_subset_elements_sharded_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, array_subset: &ArraySubset, options: &CodecOptions, ) -> Result, ArrayError> { - validate_element_size::(self.data_type())?; - let bytes = self.retrieve_array_subset_sharded_opt(cache, array_subset, options)?; - Ok(transmute_from_bytes_vec::(bytes)) + T::from_array_bytes( + self.data_type(), + self.retrieve_array_subset_sharded_opt(cache, array_subset, options)?, + ) } #[cfg(feature = "ndarray")] - fn retrieve_array_subset_ndarray_sharded_opt<'a, T: bytemuck::Pod>( + fn retrieve_array_subset_ndarray_sharded_opt<'a, T: ElementOwned>( &'a self, cache: &ArrayShardedReadableExtCache<'a>, array_subset: &ArraySubset, diff --git a/src/array/array_sync_writable.rs b/src/array/array_sync_writable.rs index acbf074b..971dccbd 100644 --- a/src/array/array_sync_writable.rs +++ b/src/array/array_sync_writable.rs @@ -1,10 +1,10 @@ -use std::{borrow::Cow, sync::Arc}; +use std::sync::Arc; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon_iter_concurrent_limit::iter_concurrent_limit; use crate::{ - array::validate_element_size, + array::ArrayBytes, array_subset::ArraySubset, metadata::MetadataEraseVersion, storage::{ @@ -14,9 +14,9 @@ use crate::{ }; use super::{ - codec::{options::CodecOptions, ArrayCodecTraits}, + codec::{options::CodecOptions, ArrayToBytesCodecTraits}, concurrency::concurrency_chunks_and_codec, - Array, ArrayError, ArrayMetadata, ArrayMetadataOptions, + Array, ArrayError, ArrayMetadata, ArrayMetadataOptions, Element, }; impl Array { @@ -58,7 +58,11 @@ impl Array { /// - the length of `chunk_bytes` is not equal to the expected length (the product of the number of elements in the chunk and the data type size in bytes), /// - there is a codec encoding error, or /// - an underlying store error. - pub fn store_chunk(&self, chunk_indices: &[u64], chunk_bytes: &[u8]) -> Result<(), ArrayError> { + pub fn store_chunk<'a>( + &self, + chunk_indices: &[u64], + chunk_bytes: impl Into>, + ) -> Result<(), ArrayError> { self.store_chunk_opt(chunk_indices, chunk_bytes, &CodecOptions::default()) } @@ -71,7 +75,7 @@ impl Array { /// Returns an [`ArrayError`] if /// - the size of `T` does not match the data type size, or /// - a [`store_chunk`](Array::store_chunk) error condition is met. - pub fn store_chunk_elements( + pub fn store_chunk_elements( &self, chunk_indices: &[u64], chunk_elements: &[T], @@ -90,7 +94,7 @@ impl Array { /// - a [`store_chunk_elements`](Array::store_chunk_elements) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub fn store_chunk_ndarray< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -114,10 +118,10 @@ impl Array { /// - an underlying store error. #[allow(clippy::similar_names)] #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunks( + pub fn store_chunks<'a>( &self, chunks: &ArraySubset, - chunks_bytes: &[u8], + chunks_bytes: impl Into>, ) -> Result<(), ArrayError> { self.store_chunks_opt(chunks, chunks_bytes, &CodecOptions::default()) } @@ -129,7 +133,7 @@ impl Array { /// - the size of `T` does not match the data type size, or /// - a [`store_chunks`](Array::store_chunks) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] - pub fn store_chunks_elements( + pub fn store_chunks_elements( &self, chunks: &ArraySubset, chunks_elements: &[T], @@ -146,7 +150,7 @@ impl Array { /// - a [`store_chunks_elements`](Array::store_chunks_elements) error condition is met. #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] pub fn store_chunks_ndarray< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -242,24 +246,25 @@ impl Array { /// Explicit options version of [`store_chunk`](Array::store_chunk). #[allow(clippy::missing_errors_doc)] - pub fn store_chunk_opt( + pub fn store_chunk_opt<'a>( &self, chunk_indices: &[u64], - chunk_bytes: &[u8], + chunk_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { + let chunk_bytes = chunk_bytes.into(); + // Validation let chunk_array_representation = self.chunk_array_representation(chunk_indices)?; - if chunk_bytes.len() as u64 != chunk_array_representation.size() { - return Err(ArrayError::InvalidBytesInputSize( - chunk_bytes.len(), - chunk_array_representation.size(), - )); - } + chunk_bytes.validate( + chunk_array_representation.num_elements(), + chunk_array_representation.data_type().size(), + )?; - if !options.store_empty_chunks() && self.fill_value().equals_all(chunk_bytes) { + let is_fill_value = + !options.store_empty_chunks() && chunk_bytes.is_fill_value(self.fill_value()); + if is_fill_value { self.erase_chunk(chunk_indices)?; - Ok(()) } else { let storage_handle = Arc::new(StorageHandle::new(self.storage.clone())); let storage_transformer = self @@ -267,11 +272,7 @@ impl Array { .create_writable_transformer(storage_handle); let chunk_encoded = self .codecs() - .encode( - Cow::Borrowed(chunk_bytes), - &chunk_array_representation, - options, - ) + .encode(chunk_bytes, &chunk_array_representation, options) .map_err(ArrayError::CodecError)?; crate::storage::store_chunk( &*storage_transformer, @@ -279,29 +280,28 @@ impl Array { chunk_indices, self.chunk_key_encoding(), Bytes::from(chunk_encoded.into_owned()), - ) - .map_err(ArrayError::StorageError) + )?; } + Ok(()) } /// Explicit options version of [`store_chunk_elements`](Array::store_chunk_elements). #[allow(clippy::missing_errors_doc)] - pub fn store_chunk_elements_opt( + pub fn store_chunk_elements_opt( &self, chunk_indices: &[u64], chunk_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunk_elements = crate::array::transmute_to_bytes(chunk_elements); - self.store_chunk_opt(chunk_indices, chunk_elements, options) + let chunk_bytes = T::into_array_bytes(self.data_type(), chunk_elements)?; + self.store_chunk_opt(chunk_indices, chunk_bytes, options) } #[cfg(feature = "ndarray")] /// Explicit options version of [`store_chunk_ndarray`](Array::store_chunk_ndarray). #[allow(clippy::missing_errors_doc)] pub fn store_chunk_ndarray_opt< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -310,12 +310,11 @@ impl Array { chunk_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunk_array: ndarray::Array = chunk_array.into(); let chunk_shape = self.chunk_shape_usize(chunk_indices)?; if chunk_array.shape() == chunk_shape { let chunk_array = super::ndarray_into_vec(chunk_array); - self.store_chunk_elements_opt(chunk_indices, &chunk_array, options) + self.store_chunk_elements_opt(chunk_indices, chunk_array.as_slice(), options) } else { Err(ArrayError::InvalidDataShape( chunk_array.shape().to_vec(), @@ -326,30 +325,27 @@ impl Array { /// Explicit options version of [`store_chunks`](Array::store_chunks). #[allow(clippy::similar_names)] - #[allow(clippy::missing_errors_doc)] - pub fn store_chunks_opt( + #[allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] + pub fn store_chunks_opt<'a>( &self, chunks: &ArraySubset, - chunks_bytes: &[u8], + chunks_bytes: impl Into>, options: &CodecOptions, ) -> Result<(), ArrayError> { let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => {} + 0 => { + let chunks_bytes = chunks_bytes.into(); + chunks_bytes.validate(0, self.data_type().size())?; + } 1 => { let chunk_indices = chunks.start(); self.store_chunk_opt(chunk_indices, chunks_bytes, options)?; } _ => { + let chunks_bytes = chunks_bytes.into(); let array_subset = self.chunks_subset(chunks)?; - let element_size = self.data_type().size(); - let expected_size = element_size as u64 * array_subset.num_elements(); - if chunks_bytes.len() as u64 != expected_size { - return Err(ArrayError::InvalidBytesInputSize( - chunks_bytes.len(), - expected_size, - )); - } + chunks_bytes.validate(array_subset.num_elements(), self.data_type().size())?; // Calculate chunk/codec concurrency let chunk_representation = @@ -364,32 +360,15 @@ impl Array { ); let store_chunk = |chunk_indices: Vec| -> Result<(), ArrayError> { - let chunk_subset_in_array = unsafe { - self.chunk_grid() - .subset_unchecked(&chunk_indices, self.shape()) - .ok_or_else(|| { - ArrayError::InvalidChunkGridIndicesError(chunk_indices.clone()) - })? - }; - let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset_in_array) }; - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - #[allow(clippy::similar_names)] - let chunk_bytes = unsafe { - chunk_subset_in_array_subset.extract_bytes_unchecked( - chunks_bytes, - array_subset.shape(), - element_size, - ) - }; - - debug_assert_eq!( - chunk_subset_in_array.num_elements(), - chunk_subset_in_array_subset.num_elements() - ); - - self.store_chunk_opt(&chunk_indices, &chunk_bytes, &options) + let chunk_subset = self.chunk_subset(&chunk_indices)?; + let chunk_bytes = chunks_bytes.extract_array_subset( + &chunk_subset.relative_to(array_subset.start())?, + array_subset.shape(), + self.data_type(), + )?; + self.store_chunk_opt(&chunk_indices, chunk_bytes, &options) }; + let indices = chunks.indices(); iter_concurrent_limit!(chunk_concurrent_limit, indices, try_for_each, store_chunk)?; } @@ -400,22 +379,21 @@ impl Array { /// Explicit options version of [`store_chunks_elements`](Array::store_chunks_elements). #[allow(clippy::missing_errors_doc)] - pub fn store_chunks_elements_opt( + pub fn store_chunks_elements_opt( &self, chunks: &ArraySubset, chunks_elements: &[T], options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; - let chunks_elements = crate::array::transmute_to_bytes(chunks_elements); - self.store_chunks_opt(chunks, chunks_elements, options) + let chunks_bytes = T::into_array_bytes(self.data_type(), chunks_elements)?; + self.store_chunks_opt(chunks, chunks_bytes, options) } #[cfg(feature = "ndarray")] /// Explicit options version of [`store_chunks_ndarray`](Array::store_chunks_ndarray). #[allow(clippy::missing_errors_doc)] pub fn store_chunks_ndarray_opt< - T: bytemuck::Pod, + T: Element, TArray: Into>, D: ndarray::Dimension, >( @@ -424,13 +402,12 @@ impl Array { chunks_array: TArray, options: &CodecOptions, ) -> Result<(), ArrayError> { - validate_element_size::(self.data_type())?; let chunks_array: ndarray::Array = chunks_array.into(); let chunks_subset = self.chunks_subset(chunks)?; let chunks_shape = chunks_subset.shape_usize(); if chunks_array.shape() == chunks_shape { let chunks_array = super::ndarray_into_vec(chunks_array); - self.store_chunks_elements_opt(chunks, &chunks_array, options) + self.store_chunks_elements_opt(chunks, chunks_array.as_slice(), options) } else { Err(ArrayError::InvalidDataShape( chunks_array.shape().to_vec(), diff --git a/src/array/codec.rs b/src/array/codec.rs index 4207b1c1..212a1c49 100644 --- a/src/array/codec.rs +++ b/src/array/codec.rs @@ -90,6 +90,7 @@ use super::{ concurrency::RecommendedConcurrency, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, DataType, }; +use super::{ArrayBytes, RawBytes}; /// A codec plugin. pub type CodecPlugin = Plugin; @@ -208,28 +209,6 @@ pub trait ArrayCodecTraits: CodecTraits { decoded_representation: &ChunkRepresentation, ) -> Result; - /// Encode a chunk. - /// - /// # Errors - /// Returns [`CodecError`] if a codec fails or `decoded_value` is incompatible with `decoded_representation`. - fn encode<'a>( - &self, - decoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - options: &CodecOptions, - ) -> Result, CodecError>; - - /// Decode a chunk. - /// - /// # Errors - /// Returns [`CodecError`] if a codec fails or the decoded output is incompatible with `decoded_representation`. - fn decode<'a>( - &self, - encoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - options: &CodecOptions, - ) -> Result, CodecError>; - /// Return the partial decode granularity. /// /// This represents the shape of the smallest subset of a chunk that can be efficiently decoded if the chunk were subdivided into a regular grid. @@ -255,7 +234,7 @@ pub trait BytesPartialDecoderTraits: Send + Sync { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError>; + ) -> Result>>, CodecError>; /// Partially decode bytes and concatenate. /// @@ -269,7 +248,7 @@ pub trait BytesPartialDecoderTraits: Send + Sync { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { Ok(self .partial_decode(decoded_regions, options)? .map(|vecs| Cow::Owned(vecs.concat()))) @@ -281,7 +260,7 @@ pub trait BytesPartialDecoderTraits: Send + Sync { /// /// # Errors /// Returns [`CodecError`] if a codec fails. - fn decode(&self, options: &CodecOptions) -> Result>, CodecError> { + fn decode(&self, options: &CodecOptions) -> Result>, CodecError> { Ok(self .partial_decode(&[ByteRange::FromStart(0, None)], options)? .map(|mut v| v.remove(0))) @@ -302,7 +281,7 @@ pub trait AsyncBytesPartialDecoderTraits: Send + Sync { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError>; + ) -> Result>>, CodecError>; /// Partially decode bytes and concatenate. /// @@ -314,8 +293,7 @@ pub trait AsyncBytesPartialDecoderTraits: Send + Sync { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>, CodecError> { - // FIXME: Remove default implementation in the next major release and implement for each codec to reduce internal allocations + ) -> Result>, CodecError> { Ok(self .partial_decode(decoded_regions, options) .await? @@ -328,7 +306,7 @@ pub trait AsyncBytesPartialDecoderTraits: Send + Sync { /// /// # Errors /// Returns [`CodecError`] if a codec fails. - async fn decode(&self, options: &CodecOptions) -> Result>, CodecError> { + async fn decode(&self, options: &CodecOptions) -> Result>, CodecError> { Ok(self .partial_decode(&[ByteRange::FromStart(0, None)], options) .await? @@ -351,7 +329,7 @@ pub trait ArrayPartialDecoderTraits: Send + Sync { fn partial_decode( &self, array_subsets: &[ArraySubset], - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { self.partial_decode_opt(array_subsets, &CodecOptions::default()) } @@ -361,7 +339,7 @@ pub trait ArrayPartialDecoderTraits: Send + Sync { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError>; + ) -> Result>, CodecError>; } #[cfg(feature = "async")] @@ -378,7 +356,7 @@ pub trait AsyncArrayPartialDecoderTraits: Send + Sync { async fn partial_decode( &self, array_subsets: &[ArraySubset], - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { self.partial_decode_opt(array_subsets, &CodecOptions::default()) .await } @@ -388,7 +366,7 @@ pub trait AsyncArrayPartialDecoderTraits: Send + Sync { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError>; + ) -> Result>, CodecError>; } /// A [`ReadableStorage`] store value partial decoder. @@ -409,7 +387,7 @@ impl BytesPartialDecoderTraits for StoragePartialDecoder { &self, decoded_regions: &[ByteRange], _options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(self .storage .get_partial_values_key(&self.key, decoded_regions)? @@ -444,7 +422,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncStoragePartialDecoder { &self, decoded_regions: &[ByteRange], _options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(self .storage .get_partial_values_key(&self.key, decoded_regions) @@ -473,6 +451,28 @@ pub trait ArrayToArrayCodecTraits: decoded_representation: &ChunkRepresentation, ) -> Result; + /// Encode a chunk. + /// + /// # Errors + /// Returns [`CodecError`] if a codec fails or `bytes` is incompatible with `decoded_representation`. + fn encode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError>; + + /// Decode a chunk. + /// + /// # Errors + /// Returns [`CodecError`] if a codec fails or the decoded output is incompatible with `decoded_representation`. + fn decode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError>; + /// Initialise a partial decoder. /// /// `parallel` only affects parallelism on initialisation, which is irrelevant for most codecs. @@ -519,6 +519,28 @@ pub trait ArrayToBytesCodecTraits: decoded_representation: &ChunkRepresentation, ) -> Result; + /// Encode a chunk. + /// + /// # Errors + /// Returns [`CodecError`] if a codec fails or `bytes` is incompatible with `decoded_representation`. + fn encode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError>; + + /// Decode a chunk. + /// + /// # Errors + /// Returns [`CodecError`] if a codec fails or the decoded output is incompatible with `decoded_representation`. + fn decode<'a>( + &self, + bytes: RawBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError>; + /// Initialise a partial decoder. /// /// # Errors @@ -569,9 +591,9 @@ pub trait BytesToBytesCodecTraits: CodecTraits + dyn_clone::DynClone + core::fmt /// Returns [`CodecError`] if a codec fails. fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, options: &CodecOptions, - ) -> Result, CodecError>; + ) -> Result, CodecError>; /// Decode chunk bytes. // @@ -579,10 +601,10 @@ pub trait BytesToBytesCodecTraits: CodecTraits + dyn_clone::DynClone + core::fmt /// Returns [`CodecError`] if a codec fails. fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, decoded_representation: &BytesRepresentation, options: &CodecOptions, - ) -> Result, CodecError>; + ) -> Result, CodecError>; /// Initialises a partial decoder. /// @@ -615,7 +637,7 @@ impl BytesPartialDecoderTraits for std::io::Cursor<&[u8]> { &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -625,12 +647,12 @@ impl BytesPartialDecoderTraits for std::io::Cursor<&[u8]> { } } -impl BytesPartialDecoderTraits for std::io::Cursor> { +impl BytesPartialDecoderTraits for std::io::Cursor> { fn partial_decode( &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -645,7 +667,7 @@ impl BytesPartialDecoderTraits for std::io::Cursor> { &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -662,7 +684,7 @@ impl AsyncBytesPartialDecoderTraits for std::io::Cursor<&[u8]> { &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -674,12 +696,12 @@ impl AsyncBytesPartialDecoderTraits for std::io::Cursor<&[u8]> { #[cfg(feature = "async")] #[async_trait::async_trait] -impl AsyncBytesPartialDecoderTraits for std::io::Cursor> { +impl AsyncBytesPartialDecoderTraits for std::io::Cursor> { async fn partial_decode( &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -696,7 +718,7 @@ impl AsyncBytesPartialDecoderTraits for std::io::Cursor> { &self, decoded_regions: &[ByteRange], _parallel: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(Some( extract_byte_ranges_read_seek(&mut self.clone(), decoded_regions)? .into_iter() @@ -733,9 +755,21 @@ pub enum CodecError { /// Unsupported data type #[error("Unsupported data type {0} for codec {1}")] UnsupportedDataType(DataType, String), + /// Offsets are not [`None`] with a fixed length data type. + #[error("Offsets are invalid or are not compatible with the data type (e.g. fixed-sized data types)")] + InvalidOffsets, /// Other #[error("{_0}")] Other(String), + /// Invalid variable sized array offsets. + #[error("Invalid variable sized array offsets")] + InvalidVariableSizedArrayOffsets, + /// Expected fixed length bytes. + #[error("Expected fixed length array bytes")] + ExpectedFixedLengthBytes, + /// Expected variable length bytes. + #[error("Expected variable length array bytes")] + ExpectedVariableLengthBytes, } impl From<&str> for CodecError { diff --git a/src/array/codec/array_partial_decoder_cache.rs b/src/array/codec/array_partial_decoder_cache.rs index 6ed0858b..d83c37e7 100644 --- a/src/array/codec/array_partial_decoder_cache.rs +++ b/src/array/codec/array_partial_decoder_cache.rs @@ -1,11 +1,6 @@ //! A cache for partial decoders. -use std::{borrow::Cow, marker::PhantomData}; - -use crate::{ - array::{ChunkRepresentation, DataType}, - array_subset::IncompatibleArraySubsetAndShapeError, -}; +use crate::array::{ArrayBytes, ChunkRepresentation, DataType}; use super::{ArrayPartialDecoderTraits, ArraySubset, CodecError, CodecOptions}; @@ -15,8 +10,7 @@ use super::AsyncArrayPartialDecoderTraits; /// A cache for an [`ArrayPartialDecoderTraits`] partial decoder. pub struct ArrayPartialDecoderCache<'a> { decoded_representation: ChunkRepresentation, - cache: Vec, - phantom: PhantomData<&'a ()>, + cache: ArrayBytes<'a>, } impl<'a> ArrayPartialDecoderCache<'a> { @@ -29,7 +23,7 @@ impl<'a> ArrayPartialDecoderCache<'a> { decoded_representation: ChunkRepresentation, options: &CodecOptions, ) -> Result { - let cache = input_handle + let bytes = input_handle .partial_decode_opt( &[ArraySubset::new_with_shape( decoded_representation.shape_u64(), @@ -40,8 +34,7 @@ impl<'a> ArrayPartialDecoderCache<'a> { .into_owned(); Ok(Self { decoded_representation, - cache, - phantom: PhantomData, + cache: bytes, }) } @@ -55,7 +48,7 @@ impl<'a> ArrayPartialDecoderCache<'a> { decoded_representation: ChunkRepresentation, options: &CodecOptions, ) -> Result, CodecError> { - let cache = input_handle + let bytes = input_handle .partial_decode_opt( &[ArraySubset::new_with_shape( decoded_representation.shape_u64(), @@ -67,8 +60,7 @@ impl<'a> ArrayPartialDecoderCache<'a> { .into_owned(); Ok(Self { decoded_representation, - cache, - phantom: PhantomData, + cache: bytes, }) } } @@ -82,21 +74,15 @@ impl<'a> ArrayPartialDecoderTraits for ArrayPartialDecoderCache<'a> { &self, decoded_regions: &[ArraySubset], _options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { let mut out = Vec::with_capacity(decoded_regions.len()); let array_shape = self.decoded_representation.shape_u64(); - let element_size = self.decoded_representation.element_size(); for array_subset in decoded_regions { - out.push(Cow::Owned( - array_subset - .extract_bytes(&self.cache, &array_shape, element_size) - .map_err(|_| { - IncompatibleArraySubsetAndShapeError::from(( - array_subset.clone(), - self.decoded_representation.shape_u64(), - )) - })?, - )); + out.push(self.cache.extract_array_subset( + array_subset, + &array_shape, + self.decoded_representation.data_type(), + )?); } Ok(out) } @@ -113,7 +99,7 @@ impl<'a> AsyncArrayPartialDecoderTraits for ArrayPartialDecoderCache<'a> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { ArrayPartialDecoderTraits::partial_decode_opt(self, decoded_regions, options) } } diff --git a/src/array/codec/array_to_array/bitround.rs b/src/array/codec/array_to_array/bitround.rs index 9da4292f..56f01e39 100644 --- a/src/array/codec/array_to_array/bitround.rs +++ b/src/array/codec/array_to_array/bitround.rs @@ -175,7 +175,7 @@ fn round_bytes(bytes: &mut [u8], data_type: &DataType, keepbits: u32) -> Result< #[cfg(test)] mod tests { - use std::{borrow::Cow, num::NonZeroU64}; + use std::num::NonZeroU64; use array_representation::ChunkRepresentation; use itertools::Itertools; @@ -183,10 +183,8 @@ mod tests { use crate::{ array::{ array_representation, - codec::{ - ArrayCodecTraits, ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, BytesCodec, - CodecOptions, - }, + codec::{ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, BytesCodec, CodecOptions}, + ArrayBytes, }, array_subset::ArraySubset, }; @@ -217,25 +215,24 @@ mod tests { 98765.43210, ]; let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes = ArrayBytes::from(bytes); let codec_configuration: BitroundCodecConfiguration = serde_json::from_str(JSON).unwrap(); let codec = BitroundCodec::new_with_configuration(&codec_configuration); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) .unwrap(); let decoded = codec - .decode( - Cow::Borrowed(&encoded), - &chunk_representation, - &CodecOptions::default(), - ) + .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - let decoded_elements = crate::array::transmute_from_bytes_vec::(decoded.to_vec()); + let decoded_elements = crate::array::transmute_from_bytes_vec::( + decoded.into_fixed().unwrap().into_owned(), + ); assert_eq!(decoded_elements, &[0.0f32, 1.25f32, -8.0f32, 98304.0f32]); } @@ -250,25 +247,24 @@ mod tests { .unwrap(); let elements: Vec = vec![0, 1024, 1280, 1664, 1685, 123145182, 4294967295]; let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes = ArrayBytes::from(bytes); let codec_configuration: BitroundCodecConfiguration = serde_json::from_str(JSON).unwrap(); let codec = BitroundCodec::new_with_configuration(&codec_configuration); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) .unwrap(); let decoded = codec - .decode( - Cow::Borrowed(&encoded), - &chunk_representation, - &CodecOptions::default(), - ) + .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - let decoded_elements = crate::array::transmute_from_bytes_vec::(decoded.to_vec()); + let decoded_elements = crate::array::transmute_from_bytes_vec::( + decoded.into_fixed().unwrap().into_owned(), + ); for element in &decoded_elements { println!("{element} -> {element:#b}"); } @@ -289,25 +285,24 @@ mod tests { .unwrap(); let elements: Vec = vec![0, 3, 7, 15, 17, 54, 89, 128, 255]; let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes = ArrayBytes::from(bytes); let codec_configuration: BitroundCodecConfiguration = serde_json::from_str(JSON).unwrap(); let codec = BitroundCodec::new_with_configuration(&codec_configuration); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) .unwrap(); let decoded = codec - .decode( - Cow::Borrowed(&encoded), - &chunk_representation, - &CodecOptions::default(), - ) + .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - let decoded_elements = crate::array::transmute_from_bytes_vec::(decoded.to_vec()); + let decoded_elements = crate::array::transmute_from_bytes_vec::( + decoded.into_fixed().unwrap().into_owned(), + ); for element in &decoded_elements { println!("{element} -> {element:#b}"); } @@ -327,20 +322,21 @@ mod tests { 0.0f32.into(), ) .unwrap(); - let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = crate::array::transmute_to_bytes_vec(elements).into(); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) - .unwrap(); + .unwrap() + .into_owned(); let decoded_regions = [ ArraySubset::new_with_ranges(&[3..5]), ArraySubset::new_with_ranges(&[17..21]), ]; - let input_handle = Box::new(std::io::Cursor::new(encoded)); + let input_handle = Box::new(std::io::Cursor::new(encoded.into_fixed().unwrap())); let bytes_codec = BytesCodec::default(); let input_handle = bytes_codec .partial_decoder( @@ -361,7 +357,11 @@ mod tests { .unwrap(); let decoded_partial_chunk = decoded_partial_chunk .into_iter() - .map(|bytes| crate::array::convert_from_bytes_slice::(&bytes)) + .map(|bytes| { + crate::array::transmute_from_bytes_vec::( + bytes.into_fixed().unwrap().into_owned(), + ) + }) .collect_vec(); let answer: &[Vec] = &[vec![3.0, 4.0], vec![16.0, 16.0, 20.0, 20.0]]; assert_eq!(answer, decoded_partial_chunk); @@ -382,10 +382,11 @@ mod tests { ) .unwrap(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes = ArrayBytes::from(bytes); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -394,7 +395,7 @@ mod tests { ArraySubset::new_with_ranges(&[3..5]), ArraySubset::new_with_ranges(&[17..21]), ]; - let input_handle = Box::new(std::io::Cursor::new(encoded)); + let input_handle = Box::new(std::io::Cursor::new(encoded.into_fixed().unwrap())); let bytes_codec = BytesCodec::default(); let input_handle = bytes_codec .async_partial_decoder( @@ -418,7 +419,11 @@ mod tests { .unwrap(); let decoded_partial_chunk = decoded_partial_chunk .into_iter() - .map(|bytes| crate::array::convert_from_bytes_slice::(&bytes)) + .map(|bytes| { + crate::array::transmute_from_bytes_vec::( + bytes.into_fixed().unwrap().into_owned(), + ) + }) .collect_vec(); let answer: &[Vec] = &[vec![3.0, 4.0], vec![16.0, 16.0, 20.0, 20.0]]; assert_eq!(answer, decoded_partial_chunk); diff --git a/src/array/codec/array_to_array/bitround/bitround_codec.rs b/src/array/codec/array_to_array/bitround/bitround_codec.rs index 31611eab..c467cbb4 100644 --- a/src/array/codec/array_to_array/bitround/bitround_codec.rs +++ b/src/array/codec/array_to_array/bitround/bitround_codec.rs @@ -1,9 +1,7 @@ -use std::borrow::Cow; - use crate::{ array::{ codec::{ - options::CodecOptions, ArrayCodecTraits, ArrayPartialDecoderTraits, + options::CodecOptions, ArrayBytes, ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToArrayCodecTraits, CodecError, CodecTraits, RecommendedConcurrency, }, ArrayMetadataOptions, ChunkRepresentation, DataType, @@ -76,33 +74,34 @@ impl ArrayCodecTraits for BitroundCodec { // TODO: bitround is well suited to multithread, when is it optimal to kick in? Ok(RecommendedConcurrency::new_maximum(1)) } +} +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToArrayCodecTraits for BitroundCodec { fn encode<'a>( &self, - mut decoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { + let mut bytes = bytes.into_fixed()?; round_bytes( - decoded_value.to_mut(), + bytes.to_mut(), decoded_representation.data_type(), self.keepbits, )?; - Ok(decoded_value) + Ok(ArrayBytes::from(bytes)) } fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, _decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { - Ok(encoded_value) + ) -> Result, CodecError> { + Ok(bytes) } -} -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToArrayCodecTraits for BitroundCodec { fn partial_decoder<'a>( &'a self, input_handle: Box, diff --git a/src/array/codec/array_to_array/bitround/bitround_partial_decoder.rs b/src/array/codec/array_to_array/bitround/bitround_partial_decoder.rs index 678abc99..1d520d27 100644 --- a/src/array/codec/array_to_array/bitround/bitround_partial_decoder.rs +++ b/src/array/codec/array_to_array/bitround/bitround_partial_decoder.rs @@ -1,8 +1,6 @@ -use std::borrow::Cow; - use crate::{ array::{ - codec::{ArrayPartialDecoderTraits, CodecError, CodecOptions}, + codec::{ArrayBytes, ArrayPartialDecoderTraits, CodecError, CodecOptions}, DataType, }, array_subset::ArraySubset, @@ -61,16 +59,19 @@ impl ArrayPartialDecoderTraits for BitroundPartialDecoder<'_> { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { - let mut bytes = self + ) -> Result>, CodecError> { + let bytes = self .input_handle .partial_decode_opt(array_subsets, options)?; - for bytes in &mut bytes { + let mut bytes_out = Vec::with_capacity(bytes.len()); + for bytes in bytes { + let mut bytes = bytes.into_fixed()?; round_bytes(bytes.to_mut(), &self.data_type, self.keepbits)?; + bytes_out.push(bytes.into()); } - Ok(bytes) + Ok(bytes_out) } } @@ -126,16 +127,19 @@ impl AsyncArrayPartialDecoderTraits for AsyncBitroundPartialDecoder<'_> { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { - let mut bytes = self + ) -> Result>, CodecError> { + let bytes = self .input_handle .partial_decode_opt(array_subsets, options) .await?; - for bytes in &mut bytes { + let mut bytes_out = Vec::with_capacity(bytes.len()); + for bytes in bytes { + let mut bytes = bytes.into_fixed()?; round_bytes(bytes.to_mut(), &self.data_type, self.keepbits)?; + bytes_out.push(bytes.into()); } - Ok(bytes) + Ok(bytes_out) } } diff --git a/src/array/codec/array_to_array/transpose.rs b/src/array/codec/array_to_array/transpose.rs index 89dee8ce..33706274 100644 --- a/src/array/codec/array_to_array/transpose.rs +++ b/src/array/codec/array_to_array/transpose.rs @@ -14,7 +14,11 @@ pub use crate::metadata::v3::codec::transpose::{ pub use transpose_codec::TransposeCodec; use crate::{ - array::codec::{Codec, CodecPlugin}, + array::{ + array_bytes::RawBytesOffsets, + codec::{Codec, CodecPlugin}, + ArrayBytes, RawBytes, + }, metadata::v3::{codec::transpose, MetadataV3}, plugin::{PluginCreateError, PluginMetadataInvalidError}, }; @@ -90,17 +94,41 @@ fn permute(v: &[T], order: &TransposeOrder) -> Vec { vec } +fn transpose_vlen<'a>( + bytes: &RawBytes, + offsets: &RawBytesOffsets, + shape: &[usize], + order: Vec, +) -> ArrayBytes<'a> { + debug_assert_eq!(shape.len(), order.len()); + + // Get the transposed element indices + let ndarray_indices = + ndarray::ArrayD::from_shape_vec(shape, (0..shape.iter().product()).collect()).unwrap(); + let ndarray_indices_transposed = ndarray_indices.permuted_axes(order); + + // Collect the new bytes/offsets + let mut bytes_new = Vec::with_capacity(bytes.len()); + let mut offsets_new = Vec::with_capacity(offsets.len()); + for idx in &ndarray_indices_transposed { + offsets_new.push(bytes_new.len()); + let curr = offsets[*idx]; + let next = offsets[idx + 1]; + bytes_new.extend_from_slice(&bytes[curr..next]); + } + offsets_new.push(bytes_new.len()); + + ArrayBytes::new_vlen(bytes_new, offsets_new) +} + #[cfg(test)] mod tests { - use std::{borrow::Cow, num::NonZeroU64}; + use std::num::NonZeroU64; use crate::{ array::{ - codec::{ - ArrayCodecTraits, ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, BytesCodec, - CodecOptions, - }, - ChunkRepresentation, DataType, FillValue, + codec::{ArrayToArrayCodecTraits, ArrayToBytesCodecTraits, BytesCodec, CodecOptions}, + ArrayBytes, ChunkRepresentation, DataType, FillValue, }, array_subset::ArraySubset, }; @@ -118,26 +146,25 @@ mod tests { fill_value, ) .unwrap(); - let bytes: Vec = (0..chunk_representation.size()).map(|s| s as u8).collect(); + let size = chunk_representation.num_elements_usize() + * chunk_representation.data_type().fixed_size().unwrap(); + let bytes: Vec = (0..size).map(|s| s as u8).collect(); + let bytes: ArrayBytes = bytes.into(); let configuration: TransposeCodecConfiguration = serde_json::from_str(json).unwrap(); let codec = TransposeCodec::new_with_configuration(&configuration).unwrap(); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) .unwrap(); let decoded = codec - .decode( - Cow::Borrowed(&encoded), - &chunk_representation, - &CodecOptions::default(), - ) + .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); // let array = ndarray::ArrayViewD::from_shape(array_representation.shape(), &bytes).unwrap(); // let array_representation_transpose = @@ -179,20 +206,17 @@ mod tests { ) .unwrap(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let encoded = codec - .encode( - Cow::Borrowed(&bytes), - &chunk_representation, - &CodecOptions::default(), - ) + .encode(bytes, &chunk_representation, &CodecOptions::default()) .unwrap(); let decoded_regions = [ ArraySubset::new_with_ranges(&[0..4, 0..4]), ArraySubset::new_with_ranges(&[1..3, 1..4]), ArraySubset::new_with_ranges(&[2..4, 0..2]), ]; - let input_handle = Box::new(std::io::Cursor::new(encoded)); + let input_handle = Box::new(std::io::Cursor::new(encoded.into_fixed().unwrap())); let bytes_codec = BytesCodec::default(); let input_handle = bytes_codec .partial_decoder( @@ -213,7 +237,9 @@ mod tests { .unwrap(); let decoded_partial_chunk = decoded_partial_chunk .into_iter() - .map(|bytes| crate::array::convert_from_bytes_slice::(&bytes)) + .map(|bytes| { + crate::array::convert_from_bytes_slice::(&bytes.into_fixed().unwrap()) + }) .collect::>(); let answer: &[Vec] = &[ vec![ @@ -239,10 +265,11 @@ mod tests { ) .unwrap(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -252,7 +279,7 @@ mod tests { ArraySubset::new_with_ranges(&[1..3, 1..4]), ArraySubset::new_with_ranges(&[2..4, 0..2]), ]; - let input_handle = Box::new(std::io::Cursor::new(encoded)); + let input_handle = Box::new(std::io::Cursor::new(encoded.into_fixed().unwrap())); let bytes_codec = BytesCodec::default(); let input_handle = bytes_codec .async_partial_decoder( @@ -276,7 +303,11 @@ mod tests { .unwrap(); let decoded_partial_chunk = decoded_partial_chunk .into_iter() - .map(|bytes| crate::array::convert_from_bytes_slice::(&bytes)) + .map(|bytes| { + crate::array::transmute_from_bytes_vec::( + bytes.into_fixed().unwrap().into_owned(), + ) + }) .collect::>(); let answer: &[Vec] = &[ vec![ diff --git a/src/array/codec/array_to_array/transpose/transpose_codec.rs b/src/array/codec/array_to_array/transpose/transpose_codec.rs index 69aca822..81b40308 100644 --- a/src/array/codec/array_to_array/transpose/transpose_codec.rs +++ b/src/array/codec/array_to_array/transpose/transpose_codec.rs @@ -1,14 +1,12 @@ -use std::borrow::Cow; - use crate::{ array::{ codec::{ - options::CodecOptions, ArrayCodecTraits, ArrayPartialDecoderTraits, + options::CodecOptions, ArrayBytes, ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToArrayCodecTraits, CodecError, CodecTraits, RecommendedConcurrency, }, ArrayMetadataOptions, ChunkRepresentation, }, - metadata::{v3::codec::transpose::TransposeCodecConfigurationV1, v3::MetadataV3}, + metadata::v3::{codec::transpose::TransposeCodecConfigurationV1, MetadataV3}, plugin::PluginCreateError, }; @@ -65,6 +63,90 @@ impl CodecTraits for TransposeCodec { #[cfg_attr(feature = "async", async_trait::async_trait)] impl ArrayToArrayCodecTraits for TransposeCodec { + fn encode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + + match bytes { + ArrayBytes::Variable(bytes, offsets) => { + let order_encode = self.order.0.clone(); + let shape = decoded_representation + .shape() + .iter() + .map(|s| usize::try_from(s.get()).unwrap()) + .collect::>(); + Ok(super::transpose_vlen( + &bytes, + &offsets, + &shape, + order_encode, + )) + } + ArrayBytes::Fixed(bytes) => { + let order_encode = + calculate_order_encode(&self.order, decoded_representation.shape().len()); + let data_type_size = decoded_representation.data_type().fixed_size().unwrap(); + let bytes = transpose_array( + &order_encode, + &decoded_representation.shape_u64(), + data_type_size, + &bytes, + ) + .map_err(|_| CodecError::Other("transpose_array invalid arguments?".to_string()))?; + Ok(ArrayBytes::from(bytes)) + } + } + } + + fn decode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + + match bytes { + ArrayBytes::Variable(bytes, offsets) => { + let mut order_decode = vec![0; decoded_representation.shape().len()]; + for (i, val) in self.order.0.iter().enumerate() { + order_decode[*val] = i; + } + let shape = decoded_representation + .shape() + .iter() + .map(|s| usize::try_from(s.get()).unwrap()) + .collect::>(); + Ok(super::transpose_vlen( + &bytes, + &offsets, + &shape, + order_decode, + )) + } + ArrayBytes::Fixed(bytes) => { + let order_decode = + calculate_order_decode(&self.order, decoded_representation.shape().len()); + let transposed_shape = permute(&decoded_representation.shape_u64(), &self.order); + let data_type_size = decoded_representation.data_type().fixed_size().unwrap(); + let bytes = + transpose_array(&order_decode, &transposed_shape, data_type_size, &bytes) + .map_err(|_| CodecError::Other("transpose_array error".to_string()))?; + Ok(ArrayBytes::from(bytes)) + } + } + } + fn partial_decoder<'a>( &'a self, input_handle: Box, @@ -119,60 +201,4 @@ impl ArrayCodecTraits for TransposeCodec { // TODO: This could be increased, need to implement `transpose_array` without ndarray Ok(RecommendedConcurrency::new_maximum(1)) } - - fn encode<'a>( - &self, - decoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - _options: &CodecOptions, - ) -> Result, CodecError> { - if decoded_value.len() as u64 != decoded_representation.size() { - return Err(CodecError::UnexpectedChunkDecodedSize( - decoded_value.len(), - decoded_representation.size(), - )); - } - - if self.order.0.iter().copied().eq(0..self.order.0.len()) { - // Fast path for identity transform - Ok(decoded_value) - } else { - let len = decoded_value.len(); - let order_encode = - calculate_order_encode(&self.order, decoded_representation.shape().len()); - transpose_array( - &order_encode, - &decoded_representation.shape_u64(), - decoded_representation.element_size(), - &decoded_value, - ) - .map_err(|_| CodecError::UnexpectedChunkDecodedSize(len, decoded_representation.size())) - .map(Cow::Owned) - } - } - - fn decode<'a>( - &self, - encoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - _options: &CodecOptions, - ) -> Result, CodecError> { - if self.order.0.iter().copied().eq(0..self.order.0.len()) { - // Fast path for identity transform - Ok(encoded_value) - } else { - let order_decode = - calculate_order_decode(&self.order, decoded_representation.shape().len()); - let transposed_shape = permute(&decoded_representation.shape_u64(), &self.order); - let len = encoded_value.len(); - transpose_array( - &order_decode, - &transposed_shape, - decoded_representation.element_size(), - &encoded_value, - ) - .map_err(|_| CodecError::UnexpectedChunkDecodedSize(len, decoded_representation.size())) - .map(Cow::Owned) - } - } } diff --git a/src/array/codec/array_to_array/transpose/transpose_partial_decoder.rs b/src/array/codec/array_to_array/transpose/transpose_partial_decoder.rs index 60874482..e7b0ba16 100644 --- a/src/array/codec/array_to_array/transpose/transpose_partial_decoder.rs +++ b/src/array/codec/array_to_array/transpose/transpose_partial_decoder.rs @@ -1,8 +1,6 @@ -use std::borrow::Cow; - use super::{calculate_order_decode, permute, transpose_array, TransposeOrder}; use crate::array::{ - codec::{ArrayPartialDecoderTraits, ArraySubset, CodecError, CodecOptions}, + codec::{ArrayBytes, ArrayPartialDecoderTraits, ArraySubset, CodecError, CodecOptions}, ChunkRepresentation, DataType, }; @@ -31,6 +29,77 @@ impl<'a> TransposePartialDecoder<'a> { } } +fn validate_regions( + decoded_regions: &[ArraySubset], + dimensionality: usize, +) -> Result<(), CodecError> { + for array_subset in decoded_regions { + if array_subset.dimensionality() != dimensionality { + return Err(CodecError::InvalidArraySubsetDimensionalityError( + array_subset.clone(), + dimensionality, + )); + } + } + Ok(()) +} + +fn get_decoded_regions_transposed( + order: &TransposeOrder, + decoded_regions: &[ArraySubset], +) -> Vec { + let mut decoded_regions_transposed = Vec::with_capacity(decoded_regions.len()); + for decoded_region in decoded_regions { + let start = permute(decoded_region.start(), order); + let size = permute(decoded_region.shape(), order); + let decoded_region_transpose = + unsafe { ArraySubset::new_with_start_shape_unchecked(start, size) }; + decoded_regions_transposed.push(decoded_region_transpose); + } + decoded_regions_transposed +} + +/// Reverse the transpose on each subset +fn do_transpose<'a>( + encoded_value: Vec>, + decoded_regions: &[ArraySubset], + order: &TransposeOrder, + decoded_representation: &ChunkRepresentation, +) -> Result>, CodecError> { + let order_decode = calculate_order_decode(order, decoded_representation.shape().len()); + let data_type_size = decoded_representation.data_type().size(); + std::iter::zip(decoded_regions, encoded_value) + .map(|(subset, bytes)| { + bytes.validate(subset.num_elements(), data_type_size)?; + match bytes { + ArrayBytes::Variable(bytes, offsets) => { + let mut order_decode = vec![0; decoded_representation.shape().len()]; + for (i, val) in order.0.iter().enumerate() { + order_decode[*val] = i; + } + Ok(super::transpose_vlen( + &bytes, + &offsets, + &subset.shape_usize(), + order_decode, + )) + } + ArrayBytes::Fixed(bytes) => { + let data_type_size = decoded_representation.data_type().fixed_size().unwrap(); + let bytes = transpose_array( + &order_decode, + &permute(subset.shape(), order), + data_type_size, + &bytes, + ) + .map_err(|_| CodecError::Other("transpose_array error".to_string()))?; + Ok(ArrayBytes::from(bytes)) + } + } + }) + .collect::, CodecError>>() +} + impl ArrayPartialDecoderTraits for TransposePartialDecoder<'_> { fn data_type(&self) -> &DataType { self.decoded_representation.data_type() @@ -40,57 +109,22 @@ impl ArrayPartialDecoderTraits for TransposePartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { - for array_subset in decoded_regions { - if array_subset.dimensionality() != self.decoded_representation.dimensionality() { - return Err(CodecError::InvalidArraySubsetDimensionalityError( - array_subset.clone(), - self.decoded_representation.dimensionality(), - )); - } - } - - // Get transposed array subsets - let mut decoded_regions_transposed = Vec::with_capacity(decoded_regions.len()); - for decoded_region in decoded_regions { - let start = permute(decoded_region.start(), &self.order); - let size = permute(decoded_region.shape(), &self.order); - let decoded_region_transpose = - unsafe { ArraySubset::new_with_start_shape_unchecked(start, size) }; - decoded_regions_transposed.push(decoded_region_transpose); - } + ) -> Result>, CodecError> { + validate_regions( + decoded_regions, + self.decoded_representation.dimensionality(), + )?; + let decoded_regions_transposed = + get_decoded_regions_transposed(&self.order, decoded_regions); let encoded_value = self .input_handle .partial_decode_opt(&decoded_regions_transposed, options)?; - - if self.order.0.iter().copied().eq(0..self.order.0.len()) { - // Fast path for identity transform - Ok(encoded_value) - } else { - // Reverse the transpose on each subset - let order_decode = - calculate_order_decode(&self.order, self.decoded_representation.shape().len()); - let decoded_value = std::iter::zip(decoded_regions, encoded_value) - .map(|(subset, bytes)| { - let len = bytes.len(); - transpose_array( - &order_decode, - &permute(subset.shape(), &self.order), - self.decoded_representation.element_size(), - &bytes, - ) - .map_err(|_| { - CodecError::UnexpectedChunkDecodedSize( - len, - subset.num_elements() - * self.decoded_representation.element_size() as u64, - ) - }) - .map(Cow::Owned) - }) - .collect::, _>>()?; - Ok(decoded_value) - } + do_transpose( + encoded_value, + decoded_regions, + &self.order, + &self.decoded_representation, + ) } } @@ -129,57 +163,22 @@ impl AsyncArrayPartialDecoderTraits for AsyncTransposePartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { - for array_subset in decoded_regions { - if array_subset.dimensionality() != self.decoded_representation.dimensionality() { - return Err(CodecError::InvalidArraySubsetDimensionalityError( - array_subset.clone(), - self.decoded_representation.dimensionality(), - )); - } - } - - // Get transposed array subsets - let mut decoded_regions_transposed = Vec::with_capacity(decoded_regions.len()); - for decoded_region in decoded_regions { - let start = permute(decoded_region.start(), &self.order); - let size = permute(decoded_region.shape(), &self.order); - let decoded_region_transpose = - unsafe { ArraySubset::new_with_start_shape_unchecked(start, size) }; - decoded_regions_transposed.push(decoded_region_transpose); - } + ) -> Result>, CodecError> { + validate_regions( + decoded_regions, + self.decoded_representation.dimensionality(), + )?; + let decoded_regions_transposed = + get_decoded_regions_transposed(&self.order, decoded_regions); let encoded_value = self .input_handle .partial_decode_opt(&decoded_regions_transposed, options) .await?; - - if self.order.0.iter().copied().eq(0..self.order.0.len()) { - // Fast path for identity transform - Ok(encoded_value) - } else { - // Reverse the transpose on each subset - let order_decode = - calculate_order_decode(&self.order, self.decoded_representation.shape().len()); - let decoded_value = std::iter::zip(decoded_regions, encoded_value) - .map(|(subset, bytes)| { - let len = bytes.len(); - transpose_array( - &order_decode, - &permute(subset.shape(), &self.order), - self.decoded_representation.element_size(), - &bytes, - ) - .map(Cow::Owned) - .map_err(|_| { - CodecError::UnexpectedChunkDecodedSize( - len, - subset.num_elements() - * self.decoded_representation.element_size() as u64, - ) - }) - }) - .collect::, _>>()?; - Ok(decoded_value) - } + do_transpose( + encoded_value, + decoded_regions, + &self.order, + &self.decoded_representation, + ) } } diff --git a/src/array/codec/array_to_bytes.rs b/src/array/codec/array_to_bytes.rs index 361df695..765b7544 100644 --- a/src/array/codec/array_to_bytes.rs +++ b/src/array/codec/array_to_bytes.rs @@ -2,6 +2,8 @@ pub mod bytes; pub mod codec_chain; +pub mod vlen; +pub mod vlen_interleaved; #[cfg(feature = "pcodec")] pub mod pcodec; diff --git a/src/array/codec/array_to_bytes/bytes.rs b/src/array/codec/array_to_bytes/bytes.rs index 09562fba..cd465a17 100644 --- a/src/array/codec/array_to_bytes/bytes.rs +++ b/src/array/codec/array_to_bytes/bytes.rs @@ -66,17 +66,19 @@ pub fn reverse_endianness(v: &mut [u8], data_type: &DataType) { }; v.chunks_exact_mut(8).for_each(swap); } + // Variable-sized data types are not supported and are rejected outside of this function + DataType::String | DataType::Binary => unreachable!(), } } #[cfg(test)] mod tests { - use std::{borrow::Cow, num::NonZeroU64}; + use std::num::NonZeroU64; use crate::{ array::{ - codec::{ArrayCodecTraits, ArrayToBytesCodecTraits, CodecOptions, CodecTraits}, - ChunkRepresentation, ChunkShape, Endianness, FillValue, + codec::{ArrayToBytesCodecTraits, CodecOptions, CodecTraits}, + ArrayBytes, ChunkRepresentation, ChunkShape, FillValue, }, array_subset::ArraySubset, }; @@ -126,19 +128,21 @@ mod tests { let chunk_shape = vec![NonZeroU64::new(10).unwrap(), NonZeroU64::new(10).unwrap()]; let chunk_representation = ChunkRepresentation::new(chunk_shape, data_type, fill_value).unwrap(); - let bytes: Vec = (0..chunk_representation.size()).map(|s| s as u8).collect(); + let size = chunk_representation.num_elements_usize() + * chunk_representation.data_type().fixed_size().unwrap(); + let bytes: ArrayBytes = (0..size).map(|s| s as u8).collect::>().into(); let codec = BytesCodec::new(endianness); let encoded = codec.encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), )?; let decoded = codec .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); Ok(()) } @@ -259,13 +263,13 @@ mod tests { ChunkRepresentation::new(chunk_shape.to_vec(), DataType::UInt8, FillValue::from(0u8)) .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u8).collect(); - let bytes = elements; + let bytes: ArrayBytes = elements.into(); let codec = BytesCodec::new(None); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -285,7 +289,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -303,13 +307,13 @@ mod tests { ChunkRepresentation::new(chunk_shape.to_vec(), DataType::UInt8, FillValue::from(0u8)) .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u8).collect(); - let bytes = elements; + let bytes: ArrayBytes = elements.into(); let codec = BytesCodec::new(None); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -331,7 +335,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) diff --git a/src/array/codec/array_to_bytes/bytes/bytes_codec.rs b/src/array/codec/array_to_bytes/bytes/bytes_codec.rs index eddd147c..936be8a2 100644 --- a/src/array/codec/array_to_bytes/bytes/bytes_codec.rs +++ b/src/array/codec/array_to_bytes/bytes/bytes_codec.rs @@ -1,7 +1,5 @@ // Note: No validation that this codec is created *without* a specified endianness for multi-byte data types. -use std::borrow::Cow; - use crate::{ array::{ codec::{ @@ -9,7 +7,8 @@ use crate::{ BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, + ArrayBytes, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, DataTypeSize, + RawBytes, }, metadata::v3::MetadataV3, }; @@ -19,7 +18,7 @@ use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecod use super::{ bytes_partial_decoder, reverse_endianness, BytesCodecConfiguration, BytesCodecConfigurationV1, - Endianness, IDENTIFIER, NATIVE_ENDIAN, + Endianness, NATIVE_ENDIAN, }; /// A `bytes` codec implementation. @@ -64,20 +63,30 @@ impl BytesCodec { fn do_encode_or_decode<'a>( &self, - mut value: Cow<'a, [u8]>, + mut value: RawBytes<'a>, decoded_representation: &ChunkRepresentation, - ) -> Result, CodecError> { - if value.len() as u64 != decoded_representation.size() { - return Err(CodecError::UnexpectedChunkDecodedSize( - value.len(), - decoded_representation.size(), - )); - } else if decoded_representation.element_size() > 1 && self.endian.is_none() { - return Err(CodecError::Other(format!( - "tried to encode an array with element size {} with endianness None", - decoded_representation.size() - ))); - } + ) -> Result, CodecError> { + match decoded_representation.data_type().size() { + DataTypeSize::Variable => { + return Err(CodecError::UnsupportedDataType( + decoded_representation.data_type().clone(), + super::IDENTIFIER.to_string(), + )) + } + DataTypeSize::Fixed(data_type_size) => { + let array_size = decoded_representation.num_elements() * data_type_size as u64; + if value.len() as u64 != array_size { + return Err(CodecError::UnexpectedChunkDecodedSize( + value.len(), + array_size, + )); + } else if data_type_size > 1 && self.endian.is_none() { + return Err(CodecError::Other(format!( + "tried to encode an array with element size {data_type_size} with endianness None" + ))); + } + } + }; if let Some(endian) = &self.endian { if !endian.is_native() { @@ -93,7 +102,10 @@ impl CodecTraits for BytesCodec { let configuration = BytesCodecConfigurationV1 { endian: self.endian, }; - Some(MetadataV3::new_with_serializable_configuration(IDENTIFIER, &configuration).unwrap()) + Some( + MetadataV3::new_with_serializable_configuration(super::IDENTIFIER, &configuration) + .unwrap(), + ) } fn partial_decoder_should_cache_input(&self) -> bool { @@ -124,28 +136,35 @@ impl ArrayCodecTraits for BytesCodec { // } Ok(RecommendedConcurrency::new_maximum(1)) } +} +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for BytesCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { - self.do_encode_or_decode(decoded_value, decoded_representation) + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + let bytes = bytes.into_fixed()?; + self.do_encode_or_decode(bytes, decoded_representation) } fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + bytes: RawBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { - self.do_encode_or_decode(encoded_value, decoded_representation) + ) -> Result, CodecError> { + Ok(ArrayBytes::from( + self.do_encode_or_decode(bytes, decoded_representation)?, + )) } -} -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToBytesCodecTraits for BytesCodec { fn partial_decoder<'a>( &self, input_handle: Box, @@ -179,8 +198,16 @@ impl ArrayToBytesCodecTraits for BytesCodec { &self, decoded_representation: &ChunkRepresentation, ) -> Result { - Ok(BytesRepresentation::FixedSize( - decoded_representation.num_elements() * decoded_representation.element_size() as u64, - )) + match decoded_representation.data_type().size() { + DataTypeSize::Variable => { + return Err(CodecError::UnsupportedDataType( + decoded_representation.data_type().clone(), + super::IDENTIFIER.to_string(), + )) + } + DataTypeSize::Fixed(data_type_size) => Ok(BytesRepresentation::FixedSize( + decoded_representation.num_elements() * data_type_size as u64, + )), + } } } diff --git a/src/array/codec/array_to_bytes/bytes/bytes_partial_decoder.rs b/src/array/codec/array_to_bytes/bytes/bytes_partial_decoder.rs index 75f08fb5..799cbc11 100644 --- a/src/array/codec/array_to_bytes/bytes/bytes_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/bytes/bytes_partial_decoder.rs @@ -6,7 +6,7 @@ use crate::{ ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, CodecOptions, }, - ChunkRepresentation, DataType, + ArrayBytes, ChunkRepresentation, DataType, DataTypeSize, }, array_subset::IncompatibleArraySubsetAndShapeError, }; @@ -47,47 +47,57 @@ impl ArrayPartialDecoderTraits for BytesPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { let mut bytes = Vec::with_capacity(decoded_regions.len()); let chunk_shape = self.decoded_representation.shape_u64(); for array_subset in decoded_regions { - // Get byte ranges - let byte_ranges = array_subset - .byte_ranges(&chunk_shape, self.decoded_representation.element_size()) - .map_err(|_| { - IncompatibleArraySubsetAndShapeError::from(( - array_subset.clone(), - self.decoded_representation.shape_u64(), + match self.decoded_representation.data_type().size() { + DataTypeSize::Variable => { + return Err(CodecError::UnsupportedDataType( + self.data_type().clone(), + super::IDENTIFIER.to_string(), )) - })?; - - // Decode - let decoded = self - .input_handle - .partial_decode_concat(&byte_ranges, options)? - .map_or_else( - || { - Cow::Owned( - self.decoded_representation - .fill_value() - .as_ne_bytes() - .repeat(array_subset.num_elements_usize()), - ) - }, - |mut decoded| { - if let Some(endian) = &self.endian { - if !endian.is_native() { - reverse_endianness( - decoded.to_mut(), - self.decoded_representation.data_type(), - ); - } - } - decoded - }, - ); - - bytes.push(decoded); + } + DataTypeSize::Fixed(data_type_size) => { + // Get byte ranges + let byte_ranges = array_subset + .byte_ranges(&chunk_shape, data_type_size) + .map_err(|_| { + IncompatibleArraySubsetAndShapeError::from(( + array_subset.clone(), + self.decoded_representation.shape_u64(), + )) + })?; + + // Decode + let decoded = self + .input_handle + .partial_decode_concat(&byte_ranges, options)? + .map_or_else( + || { + Cow::Owned( + self.decoded_representation + .fill_value() + .as_ne_bytes() + .repeat(array_subset.num_elements_usize()), + ) + }, + |mut decoded| { + if let Some(endian) = &self.endian { + if !endian.is_native() { + reverse_endianness( + decoded.to_mut(), + self.decoded_representation.data_type(), + ); + } + } + decoded + }, + ); + + bytes.push(ArrayBytes::from(decoded)); + } + } } Ok(bytes) } @@ -128,7 +138,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncBytesPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { for array_subset in decoded_regions { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( @@ -149,14 +159,22 @@ impl AsyncArrayPartialDecoderTraits for AsyncBytesPartialDecoder<'_> { } // Get byte ranges - let byte_ranges = array_subset - .byte_ranges(&chunk_shape, self.decoded_representation.element_size()) - .map_err(|_| { - IncompatibleArraySubsetAndShapeError::from(( - array_subset.clone(), - self.decoded_representation.shape_u64(), + let byte_ranges = match self.decoded_representation.element_size() { + DataTypeSize::Variable => { + return Err(CodecError::UnsupportedDataType( + self.data_type().clone(), + super::IDENTIFIER.to_string(), )) - })?; + } + DataTypeSize::Fixed(data_type_size) => array_subset + .byte_ranges(&chunk_shape, data_type_size) + .map_err(|_| { + IncompatibleArraySubsetAndShapeError::from(( + array_subset.clone(), + self.decoded_representation.shape_u64(), + )) + })?, + }; // Decode let decoded = self @@ -185,7 +203,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncBytesPartialDecoder<'_> { }, ); - bytes.push(decoded); + bytes.push(ArrayBytes::from(decoded)); } Ok(bytes) } diff --git a/src/array/codec/array_to_bytes/codec_chain.rs b/src/array/codec/array_to_bytes/codec_chain.rs index 36c6dae4..fb88ba44 100644 --- a/src/array/codec/array_to_bytes/codec_chain.rs +++ b/src/array/codec/array_to_bytes/codec_chain.rs @@ -1,7 +1,5 @@ //! An array to bytes codec formed by joining an array to array sequence, array to bytes, and bytes to bytes sequence of codecs. -use std::borrow::Cow; - use crate::{ array::{ codec::{ @@ -11,7 +9,8 @@ use crate::{ CodecTraits, }, concurrency::RecommendedConcurrency, - ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, + ArrayBytes, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, + RawBytes, }, metadata::v3::MetadataV3, plugin::PluginCreateError, @@ -225,6 +224,81 @@ impl CodecTraits for CodecChain { #[cfg_attr(feature = "async", async_trait::async_trait)] impl ArrayToBytesCodecTraits for CodecChain { + fn encode<'a>( + &self, + mut bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + + let mut decoded_representation = decoded_representation.clone(); + + // array->array + for codec in &self.array_to_array { + bytes = codec.encode(bytes, &decoded_representation, options)?; + decoded_representation = codec.compute_encoded_size(&decoded_representation)?; + } + + // array->bytes + let mut bytes = self + .array_to_bytes + .encode(bytes, &decoded_representation, options)?; + let mut decoded_representation = self + .array_to_bytes + .compute_encoded_size(&decoded_representation)?; + + // bytes->bytes + for codec in &self.bytes_to_bytes { + bytes = codec.encode(bytes, options)?; + decoded_representation = codec.compute_encoded_size(&decoded_representation); + } + + Ok(bytes) + } + + fn decode<'a>( + &self, + mut bytes: RawBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError> { + let array_representations = + self.get_array_representations(decoded_representation.clone())?; + let bytes_representations = + self.get_bytes_representations(array_representations.last().unwrap())?; + + // bytes->bytes + for (codec, bytes_representation) in std::iter::zip( + self.bytes_to_bytes.iter().rev(), + bytes_representations.iter().rev().skip(1), + ) { + bytes = codec.decode(bytes, bytes_representation, options)?; + } + + // bytes->array + let mut bytes = + self.array_to_bytes + .decode(bytes, array_representations.last().unwrap(), options)?; + + // array->array + for (codec, array_representation) in std::iter::zip( + self.array_to_array.iter().rev(), + array_representations.iter().rev().skip(1), + ) { + bytes = codec.decode(bytes, array_representation, options)?; + } + + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + Ok(bytes) + } + fn partial_decoder<'a>( &'a self, mut input_handle: Box, @@ -427,90 +501,6 @@ impl ArrayCodecTraits for CodecChain { Ok(recommended_concurrency) } - - fn encode<'a>( - &self, - decoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - options: &CodecOptions, - ) -> Result, CodecError> { - if decoded_value.len() as u64 != decoded_representation.size() { - return Err(CodecError::UnexpectedChunkDecodedSize( - decoded_value.len(), - decoded_representation.size(), - )); - } - - let mut decoded_representation = decoded_representation.clone(); - - let mut value = decoded_value; - // array->array - for codec in &self.array_to_array { - value = codec.encode(value, &decoded_representation, options)?; - decoded_representation = codec.compute_encoded_size(&decoded_representation)?; - } - - // array->bytes - value = self - .array_to_bytes - .encode(value, &decoded_representation, options)?; - let mut decoded_representation = self - .array_to_bytes - .compute_encoded_size(&decoded_representation)?; - - // bytes->bytes - for codec in &self.bytes_to_bytes { - value = codec.encode(value, options)?; - decoded_representation = codec.compute_encoded_size(&decoded_representation); - } - - Ok(value) - } - - fn decode<'a>( - &self, - mut encoded_value: Cow<'a, [u8]>, - decoded_representation: &ChunkRepresentation, - options: &CodecOptions, - ) -> Result, CodecError> { - let array_representations = - self.get_array_representations(decoded_representation.clone())?; - let bytes_representations = - self.get_bytes_representations(array_representations.last().unwrap())?; - - // bytes->bytes - for (codec, bytes_representation) in std::iter::zip( - self.bytes_to_bytes.iter().rev(), - bytes_representations.iter().rev().skip(1), - ) { - encoded_value = codec.decode(encoded_value, bytes_representation, options)?; - } - - // bytes->array - encoded_value = self.array_to_bytes.decode( - encoded_value, - array_representations.last().unwrap(), - options, - )?; - - // array->array - for (codec, array_representation) in std::iter::zip( - self.array_to_array.iter().rev(), - array_representations.iter().rev().skip(1), - ) { - encoded_value = codec.decode(encoded_value, array_representation, options)?; - } - - if encoded_value.len() as u64 != decoded_representation.size() { - return Err(CodecError::UnexpectedChunkDecodedSize( - encoded_value.len(), - decoded_representation.size(), - )); - } - - Ok(encoded_value) - } - fn partial_decode_granularity( &self, decoded_representation: &ChunkRepresentation, @@ -612,7 +602,7 @@ mod tests { decoded_regions: &[ArraySubset], decoded_partial_chunk_true: Vec, ) { - let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = crate::array::transmute_to_bytes_vec(elements).into(); let codec_configurations: Vec = vec![ #[cfg(feature = "transpose")] @@ -637,7 +627,7 @@ mod tests { let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -650,9 +640,9 @@ mod tests { ) .unwrap(); if not_just_bytes { - assert_ne!(encoded, decoded); + assert_ne!(encoded, decoded.clone().into_fixed().unwrap()); } - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); // let encoded = codec // .par_encode(bytes.clone(), &chunk_representation) @@ -679,7 +669,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) diff --git a/src/array/codec/array_to_bytes/pcodec.rs b/src/array/codec/array_to_bytes/pcodec.rs index 9c9d71eb..cfe03b86 100644 --- a/src/array/codec/array_to_bytes/pcodec.rs +++ b/src/array/codec/array_to_bytes/pcodec.rs @@ -47,12 +47,13 @@ pub(crate) fn create_codec_pcodec(metadata: &MetadataV3) -> Result = (0..chunk_representation.size()).map(|s| s as u8).collect(); + let size = chunk_representation.num_elements_usize() + * chunk_representation.data_type().fixed_size().unwrap(); + let bytes: Vec = (0..size).map(|s| s as u8).collect(); + let bytes: ArrayBytes = bytes.into(); let max_encoded_size = codec.compute_encoded_size(&chunk_representation)?; let encoded = codec.encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), )?; @@ -93,7 +97,7 @@ mod tests { let decoded = codec .decode(encoded, &chunk_representation, &CodecOptions::default()) .unwrap(); - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); Ok(()) } @@ -228,12 +232,13 @@ mod tests { .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u32).collect(); let bytes = transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let codec = PcodecCodec::new_with_configuration(&serde_json::from_str(JSON_VALID).unwrap()); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -253,7 +258,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().into_owned()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -275,12 +280,13 @@ mod tests { .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u32).collect(); let bytes = transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let codec = PcodecCodec::new_with_configuration(&serde_json::from_str(JSON_VALID).unwrap()); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -302,7 +308,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().into_owned()) .flatten() .collect::>() .chunks(std::mem::size_of::()) diff --git a/src/array/codec/array_to_bytes/pcodec/pcodec_codec.rs b/src/array/codec/array_to_bytes/pcodec/pcodec_codec.rs index 5d9be9f6..b3382d85 100644 --- a/src/array/codec/array_to_bytes/pcodec/pcodec_codec.rs +++ b/src/array/codec/array_to_bytes/pcodec/pcodec_codec.rs @@ -5,8 +5,8 @@ use pco::{standalone::guarantee::file_size, ChunkConfig, ModeSpec, PagingSpec}; use crate::{ array::{ codec::{ - ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, - BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, + ArrayBytes, ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, + BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, RawBytes, RecommendedConcurrency, }, convert_from_bytes_slice, transmute_to_bytes_vec, ArrayMetadataOptions, @@ -108,18 +108,22 @@ impl ArrayCodecTraits for PcodecCodec { // pcodec does not support parallel decode Ok(RecommendedConcurrency::new_maximum(1)) } +} +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for PcodecCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let data_type = decoded_representation.data_type(); + let bytes = bytes.into_fixed()?; macro_rules! pcodec_encode { ( $t:ty ) => { pco::standalone::simple_compress( - &convert_from_bytes_slice::<$t>(&decoded_value), + &convert_from_bytes_slice::<$t>(&bytes), &self.chunk_config, ) .map(Cow::Owned) @@ -164,20 +168,20 @@ impl ArrayCodecTraits for PcodecCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + bytes: RawBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let data_type = decoded_representation.data_type(); macro_rules! pcodec_decode { ( $t:ty ) => { - pco::standalone::simple_decompress(&encoded_value) + pco::standalone::simple_decompress(&bytes) .map(|bytes| Cow::Owned(transmute_to_bytes_vec::<$t>(bytes))) .map_err(|err| CodecError::Other(err.to_string())) }; } - match data_type { + let bytes = match data_type { DataType::UInt16 => { pcodec_decode!(u16) } @@ -209,12 +213,10 @@ impl ArrayCodecTraits for PcodecCodec { data_type.clone(), IDENTIFIER.to_string(), )), - } + }?; + Ok(ArrayBytes::from(bytes)) } -} -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToBytesCodecTraits for PcodecCodec { fn partial_decoder<'a>( &self, input_handle: Box, diff --git a/src/array/codec/array_to_bytes/pcodec/pcodec_partial_decoder.rs b/src/array/codec/array_to_bytes/pcodec/pcodec_partial_decoder.rs index 22a91a1e..a51fee5a 100644 --- a/src/array/codec/array_to_bytes/pcodec/pcodec_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/pcodec/pcodec_partial_decoder.rs @@ -1,14 +1,9 @@ -use std::borrow::Cow; - -use crate::{ - array::{ - codec::{ - ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, - CodecOptions, - }, - ChunkRepresentation, DataType, +use crate::array::{ + codec::{ + ArrayBytes, ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, + CodecOptions, RawBytes, }, - array_subset::IncompatibleArraySubsetAndShapeError, + ChunkRepresentation, DataType, }; #[cfg(feature = "async")] @@ -34,10 +29,10 @@ impl<'a> PcodecPartialDecoder<'a> { } fn do_partial_decode<'a>( - decoded: Option>, + decoded: Option>, decoded_regions: &[ArraySubset], decoded_representation: &ChunkRepresentation, -) -> Result>, CodecError> { +) -> Result>, CodecError> { let mut decoded_bytes = Vec::with_capacity(decoded_regions.len()); let chunk_shape = decoded_representation.shape_u64(); match decoded { @@ -47,7 +42,7 @@ fn do_partial_decode<'a>( .fill_value() .as_ne_bytes() .repeat(array_subset.num_elements_usize()); - decoded_bytes.push(Cow::Owned(bytes_subset)); + decoded_bytes.push(ArrayBytes::from(bytes_subset)); } } Some(decoded_value) => { @@ -56,20 +51,16 @@ fn do_partial_decode<'a>( let decoded_chunk = pco::standalone::simple_decompress(&decoded_value) .map(|bytes| crate::array::transmute_to_bytes_vec::<$t>(bytes)) .map_err(|err| CodecError::Other(err.to_string()))?; + let decoded_chunk: ArrayBytes = decoded_chunk.into(); for array_subset in decoded_regions { - let bytes_subset = array_subset - .extract_bytes( - decoded_chunk.as_slice(), + let bytes_subset = decoded_chunk + .extract_array_subset( + array_subset, &chunk_shape, - decoded_representation.element_size(), - ) - .map_err(|_| { - IncompatibleArraySubsetAndShapeError::from(( - array_subset.clone(), - decoded_representation.shape_u64(), - )) - })?; - decoded_bytes.push(Cow::Owned(bytes_subset)); + decoded_representation.data_type(), + )? + .into_owned(); + decoded_bytes.push(bytes_subset); } }; } @@ -115,7 +106,7 @@ impl ArrayPartialDecoderTraits for PcodecPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { let decoded = self.input_handle.decode(options)?; do_partial_decode(decoded, decoded_regions, &self.decoded_representation) } @@ -153,7 +144,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncPCodecPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { for array_subset in decoded_regions { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( diff --git a/src/array/codec/array_to_bytes/sharding.rs b/src/array/codec/array_to_bytes/sharding.rs index 28fde5ef..da416d5c 100644 --- a/src/array/codec/array_to_bytes/sharding.rs +++ b/src/array/codec/array_to_bytes/sharding.rs @@ -105,6 +105,7 @@ fn decode_shard_index( index_array_representation, options, )?; + let decoded_shard_index = decoded_shard_index.into_fixed()?; Ok(decoded_shard_index .chunks_exact(core::mem::size_of::()) .map(|v| u64::from_ne_bytes(v.try_into().unwrap() /* safe */)) @@ -114,9 +115,12 @@ fn decode_shard_index( #[cfg(test)] mod tests { use crate::{ - array::codec::{ - bytes_to_bytes::test_unbounded::TestUnboundedCodec, ArrayCodecTraits, - BytesToBytesCodecTraits, CodecOptionsBuilder, + array::{ + codec::{ + bytes_to_bytes::test_unbounded::TestUnboundedCodec, BytesToBytesCodecTraits, + CodecOptionsBuilder, + }, + ArrayBytes, }, array_subset::ArraySubset, config::global_config, @@ -199,6 +203,7 @@ mod tests { (0..chunk_representation.num_elements() as u16).collect() }; let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); if unbounded { bytes_to_bytes_codecs.push(Box::new(TestUnboundedCodec::new())) @@ -213,13 +218,13 @@ mod tests { .build(); let encoded = codec - .encode(Cow::Borrowed(&bytes), &chunk_representation, options) + .encode(bytes.clone(), &chunk_representation, options) .unwrap(); let decoded = codec .decode(encoded.clone(), &chunk_representation, options) .unwrap(); - assert_ne!(encoded, decoded); - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); + assert_ne!(encoded, decoded.into_fixed().unwrap()); } #[test] @@ -293,6 +298,7 @@ mod tests { (0..chunk_representation.num_elements() as u16).collect() }; let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); if unbounded { bytes_to_bytes_codecs.push(Box::new(TestUnboundedCodec::new())) @@ -307,13 +313,13 @@ mod tests { .build(); let encoded = codec - .encode(Cow::Borrowed(&bytes), &chunk_representation, options) + .encode(bytes.clone(), &chunk_representation, options) .unwrap(); let decoded = codec .decode(encoded.clone(), &chunk_representation, options) .unwrap(); - assert_ne!(encoded, decoded); - assert_eq!(bytes, decoded.to_vec()); + assert_eq!(bytes, decoded); + assert_ne!(encoded, decoded.into_fixed().unwrap()); } #[cfg(feature = "async")] @@ -361,7 +367,7 @@ mod tests { vec![4, 8] }; - let bytes = elements; + let bytes: ArrayBytes = elements.into(); let bytes_to_bytes_codecs: Vec> = if unbounded { vec![Box::new(TestUnboundedCodec::new())] @@ -378,7 +384,7 @@ mod tests { .build(); let encoded = codec - .encode(Cow::Borrowed(&bytes), &chunk_representation, options) + .encode(bytes.clone(), &chunk_representation, options) .unwrap(); let decoded_regions = [ArraySubset::new_with_ranges(&[1..3, 0..1])]; let input_handle = Box::new(std::io::Cursor::new(encoded)); @@ -391,7 +397,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -442,8 +448,7 @@ mod tests { } else { vec![4, 8] }; - - let bytes = elements; + let bytes: ArrayBytes = elements.into(); let bytes_to_bytes_codecs: Vec> = if unbounded { vec![Box::new(TestUnboundedCodec::new())] @@ -460,7 +465,7 @@ mod tests { .build(); let encoded = codec - .encode(Cow::Borrowed(&bytes), &chunk_representation, options) + .encode(bytes.clone(), &chunk_representation, options) .unwrap(); let decoded_regions = [ArraySubset::new_with_ranges(&[1..3, 0..1])]; let input_handle = Box::new(std::io::Cursor::new(encoded)); @@ -475,7 +480,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -511,8 +516,6 @@ mod tests { #[cfg(feature = "crc32c")] #[test] fn codec_sharding_partial_decode2() { - use crate::array::codec::ArrayCodecTraits; - let chunk_shape: ChunkShape = vec![2, 4, 4].try_into().unwrap(); let chunk_representation = ChunkRepresentation::new( chunk_shape.to_vec(), @@ -522,17 +525,14 @@ mod tests { .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u16).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let codec_configuration: ShardingCodecConfiguration = serde_json::from_str(JSON_VALID2).unwrap(); let codec = ShardingCodec::new_with_configuration(&codec_configuration).unwrap(); let encoded = codec - .encode( - Cow::Borrowed(&bytes), - &chunk_representation, - &CodecOptions::default(), - ) + .encode(bytes, &chunk_representation, &CodecOptions::default()) .unwrap(); let decoded_regions = [ArraySubset::new_with_ranges(&[1..2, 0..2, 0..3])]; let input_handle = Box::new(std::io::Cursor::new(encoded)); @@ -549,7 +549,7 @@ mod tests { println!("decoded_partial_chunk {decoded_partial_chunk:?}"); let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -567,18 +567,14 @@ mod tests { ChunkRepresentation::new(chunk_shape.to_vec(), DataType::UInt8, FillValue::from(0u8)) .unwrap(); let elements: Vec = (0..chunk_representation.num_elements() as u8).collect(); - let bytes = elements; + let bytes: ArrayBytes = elements.into(); let codec_configuration: ShardingCodecConfiguration = serde_json::from_str(JSON_VALID3).unwrap(); let codec = ShardingCodec::new_with_configuration(&codec_configuration).unwrap(); let encoded = codec - .encode( - Cow::Borrowed(&bytes), - &chunk_representation, - &CodecOptions::default(), - ) + .encode(bytes, &chunk_representation, &CodecOptions::default()) .unwrap(); let decoded_regions = [ArraySubset::new_with_ranges(&[1..3, 0..1])]; let input_handle = Box::new(std::io::Cursor::new(encoded)); @@ -595,7 +591,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) diff --git a/src/array/codec/array_to_bytes/sharding/sharding_codec.rs b/src/array/codec/array_to_bytes/sharding/sharding_codec.rs index ed71e36d..757894bf 100644 --- a/src/array/codec/array_to_bytes/sharding/sharding_codec.rs +++ b/src/array/codec/array_to_bytes/sharding/sharding_codec.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, num::NonZeroU64, sync::atomic::AtomicUsize}; use crate::{ array::{ + array_bytes::{merge_chunks_vlen, update_bytes_flen}, chunk_shape_to_array_shape, codec::{ ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, @@ -11,7 +12,8 @@ use crate::{ concurrency::calc_concurrency_outer_inner, transmute_to_bytes_vec, unravel_index, unsafe_cell_slice::UnsafeCellSlice, - ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, FillValue, + ArrayBytes, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, + DataTypeSize, FillValue, RawBytes, }, array_subset::ArraySubset, metadata::v3::MetadataV3, @@ -127,18 +129,23 @@ impl ArrayCodecTraits for ShardingCodec { Ok(RecommendedConcurrency::new_maximum(num_elements.into())) } + fn partial_decode_granularity( + &self, + _decoded_representation: &ChunkRepresentation, + ) -> ChunkShape { + self.chunk_shape.clone() + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for ShardingCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, shard_rep: &ChunkRepresentation, options: &CodecOptions, - ) -> Result, CodecError> { - if decoded_value.len() as u64 != shard_rep.size() { - return Err(CodecError::UnexpectedChunkDecodedSize( - decoded_value.len(), - shard_rep.size(), - )); - } + ) -> Result, CodecError> { + bytes.validate(shard_rep.num_elements(), shard_rep.data_type().size())?; // Get chunk bytes representation, and choose implementation based on whether the size is unbounded or not let chunk_rep = unsafe { @@ -149,34 +156,26 @@ impl ArrayCodecTraits for ShardingCodec { ) }; let chunk_bytes_representation = self.inner_codecs.compute_encoded_size(&chunk_rep)?; - Ok(Cow::Owned(match chunk_bytes_representation { + + let bytes = match chunk_bytes_representation { BytesRepresentation::BoundedSize(size) | BytesRepresentation::FixedSize(size) => { - self.encode_bounded(&decoded_value, shard_rep, &chunk_rep, size, options) + self.encode_bounded(&bytes, shard_rep, &chunk_rep, size, options) } BytesRepresentation::UnboundedSize => { - self.encode_unbounded(&decoded_value, shard_rep, &chunk_rep, options) + self.encode_unbounded(&bytes, shard_rep, &chunk_rep, options) } - }?)) + }?; + Ok(RawBytes::from(bytes)) } #[allow(clippy::too_many_lines)] fn decode<'a>( &self, - encoded_shard: Cow<'a, [u8]>, + encoded_shard: RawBytes<'a>, shard_representation: &ChunkRepresentation, options: &CodecOptions, - ) -> Result, CodecError> { - // Allocate an array for the output - let len = shard_representation.size_usize(); - let mut decoded_shard = Vec::::with_capacity(len); - + ) -> Result, CodecError> { let shard_shape = shard_representation.shape_u64(); - let chunks_per_shard = - calculate_chunks_per_shard(shard_representation.shape(), self.chunk_shape.as_slice())?; - let shard_index = - self.decode_index(&encoded_shard, chunks_per_shard.as_slice(), options)?; - - // Decode chunks let chunk_representation = unsafe { ChunkRepresentation::new_unchecked( self.chunk_shape.as_slice().to_vec(), @@ -184,19 +183,20 @@ impl ArrayCodecTraits for ShardingCodec { shard_representation.fill_value().clone(), ) }; + let chunks_per_shard = + calculate_chunks_per_shard(shard_representation.shape(), chunk_representation.shape())?; + let num_chunks = chunks_per_shard + .as_slice() + .iter() + .map(|i| usize::try_from(i.get()).unwrap()) + .product::(); + + let shard_index = + self.decode_index(&encoded_shard, chunks_per_shard.as_slice(), options)?; let any_empty = shard_index .par_iter() .any(|offset_or_size| *offset_or_size == u64::MAX); - let contiguous_fill_value = if any_empty { - Some(get_contiguous_fill_value( - shard_representation.fill_value(), - &self.chunk_shape, - &shard_shape, - )) - } else { - None - }; // Calc self/internal concurrent limits let (shard_concurrent_limit, concurrency_limit_inner_chunks) = calc_concurrency_outer_inner( @@ -211,42 +211,20 @@ impl ArrayCodecTraits for ShardingCodec { .concurrent_target(concurrency_limit_inner_chunks) .build(); - let chunks_per_shard = - calculate_chunks_per_shard(shard_representation.shape(), chunk_representation.shape())?; - let num_chunks = chunks_per_shard - .as_slice() - .iter() - .map(|i| usize::try_from(i.get()).unwrap()) - .product::(); - let element_size = chunk_representation.element_size() as u64; - - { - let output = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut decoded_shard); - rayon_iter_concurrent_limit::iter_concurrent_limit!( - shard_concurrent_limit, - (0..num_chunks), - try_for_each, - |chunk_index: usize| { + match shard_representation.data_type().size() { + DataTypeSize::Variable => { + let decode_inner_chunk = |chunk_index: usize| { let chunk_subset = self.chunk_index_to_subset(chunk_index as u64, chunks_per_shard.as_slice()); - let output = unsafe { output.get() }; // Read the offset/size let offset = shard_index[chunk_index * 2]; let size = shard_index[chunk_index * 2 + 1]; - if offset == u64::MAX && size == u64::MAX { - if let Some(fv) = &contiguous_fill_value { - let contiguous_iterator = unsafe { - chunk_subset.contiguous_linearised_indices_unchecked(&shard_shape) - }; - for (index, elements) in &contiguous_iterator { - debug_assert_eq!(fv.len() as u64, elements * element_size); - let shard_offset = usize::try_from(index * element_size).unwrap(); - output[shard_offset..shard_offset + fv.len()].copy_from_slice(fv); - } - } else { - unreachable!(); - } + let chunk_bytes = if offset == u64::MAX && size == u64::MAX { + ArrayBytes::new_fill_value( + chunk_representation.num_elements_usize(), + chunk_representation.fill_value(), + ) } else if usize::try_from(offset + size).unwrap() > encoded_shard.len() { return Err(CodecError::Other( "The shard index references out-of-bounds bytes. The chunk may be corrupted." @@ -256,46 +234,112 @@ impl ArrayCodecTraits for ShardingCodec { let offset: usize = offset.try_into().unwrap(); let size: usize = size.try_into().unwrap(); let encoded_chunk = &encoded_shard[offset..offset + size]; - let decoded_chunk = self.inner_codecs.decode( + self.inner_codecs.decode( Cow::Borrowed(encoded_chunk), &chunk_representation, &options, - )?; - let contiguous_iterator = unsafe { - chunk_subset.contiguous_linearised_indices_unchecked(&shard_shape) + )? + }; + Ok((chunk_bytes, chunk_subset)) + }; + + // Decode the inner chunks + let chunk_bytes_and_subsets = rayon_iter_concurrent_limit::iter_concurrent_limit!( + shard_concurrent_limit, + (0..num_chunks), + map, + decode_inner_chunk + ) + .collect::, _>>()?; + + // Convert into an array + merge_chunks_vlen(chunk_bytes_and_subsets, &shard_representation.shape_u64()) + } + DataTypeSize::Fixed(data_type_size) => { + // Allocate an array for the output + let mut decoded_shard = Vec::::with_capacity( + shard_representation.num_elements_usize() * data_type_size, + ); + + let contiguous_fill_value = if any_empty { + Some(get_contiguous_fill_value( + shard_representation.fill_value(), + &self.chunk_shape, + &shard_shape, + )) + } else { + None + }; + + { + let output = + UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut decoded_shard); + let decode_chunk = |chunk_index: usize| { + let chunk_subset = self + .chunk_index_to_subset(chunk_index as u64, chunks_per_shard.as_slice()); + let output = unsafe { output.get() }; + + // Read the offset/size + let offset = shard_index[chunk_index * 2]; + let size = shard_index[chunk_index * 2 + 1]; + if offset == u64::MAX && size == u64::MAX { + if let Some(fv) = &contiguous_fill_value { + let contiguous_iterator = unsafe { + chunk_subset + .contiguous_linearised_indices_unchecked(&shard_shape) + }; + for (index, elements) in &contiguous_iterator { + debug_assert_eq!( + fv.len() as u64, + elements * data_type_size as u64 + ); + let shard_offset = + usize::try_from(index * data_type_size as u64).unwrap(); + output[shard_offset..shard_offset + fv.len()] + .copy_from_slice(fv); + } + } else { + unreachable!(); + } + } else if usize::try_from(offset + size).unwrap() > encoded_shard.len() { + return Err(CodecError::Other( + "The shard index references out-of-bounds bytes. The chunk may be corrupted." + .to_string(), + )); + } else { + let offset: usize = offset.try_into().unwrap(); + let size: usize = size.try_into().unwrap(); + let encoded_chunk = &encoded_shard[offset..offset + size]; + let decoded_chunk = self.inner_codecs.decode( + Cow::Borrowed(encoded_chunk), + &chunk_representation, + &options, + )?; + update_bytes_flen( + output, + &shard_representation.shape_u64(), + &decoded_chunk.into_fixed()?, + &chunk_subset, + data_type_size, + ); }; - let length = usize::try_from( - contiguous_iterator.contiguous_elements() * element_size, - ) - .unwrap(); - let mut data_idx = 0; - for (index, _) in &contiguous_iterator { - let shard_offset = usize::try_from(index * element_size).unwrap(); - output[shard_offset..shard_offset + length] - .copy_from_slice(&decoded_chunk[data_idx..data_idx + length]); - data_idx += length; - } + + Ok::<_, CodecError>(()) }; - Ok::<_, CodecError>(()) + rayon_iter_concurrent_limit::iter_concurrent_limit!( + shard_concurrent_limit, + (0..num_chunks), + try_for_each, + decode_chunk + )?; } - )?; + unsafe { decoded_shard.set_len(decoded_shard.capacity()) }; + Ok(ArrayBytes::from(decoded_shard)) + } } - - unsafe { decoded_shard.set_len(len) }; - Ok(Cow::Owned(decoded_shard)) } - fn partial_decode_granularity( - &self, - _decoded_representation: &ChunkRepresentation, - ) -> ChunkShape { - self.chunk_shape.clone() - } -} - -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToBytesCodecTraits for ShardingCodec { fn partial_decoder<'a>( &'a self, input_handle: Box, @@ -407,13 +451,16 @@ impl ShardingCodec { #[allow(clippy::too_many_lines)] fn encode_bounded( &self, - decoded_value: &[u8], + decoded_value: &ArrayBytes, shard_representation: &ChunkRepresentation, chunk_representation: &ChunkRepresentation, chunk_size_bounded: u64, options: &CodecOptions, ) -> Result, CodecError> { - debug_assert_eq!(decoded_value.len() as u64, shard_representation.size()); // already validated in par_encode + decoded_value.validate( + shard_representation.num_elements(), + shard_representation.data_type().size(), + )?; // Calculate maximum possible shard size let chunks_per_shard = @@ -453,7 +500,6 @@ impl ShardingCodec { .into_builder() .concurrent_target(concurrency_limit_inner_chunks) .build(); - // println!("{shard_concurrent_limit} {concurrency_limit_inner_chunks:?}"); // FIXME: log debug? // Encode the shards and update the shard index { @@ -472,19 +518,15 @@ impl ShardingCodec { |chunk_index: usize| { let chunk_subset = self.chunk_index_to_subset(chunk_index as u64, chunks_per_shard.as_slice()); - let bytes = unsafe { - chunk_subset.extract_bytes_unchecked( - decoded_value, - &shard_shape, - shard_representation.element_size(), - ) - }; - if !chunk_representation.fill_value().equals_all(&bytes) { - let chunk_encoded = self.inner_codecs.encode( - bytes.into(), - chunk_representation, - &options, - )?; + let bytes = decoded_value.extract_array_subset( + &chunk_subset, + &shard_shape, + chunk_representation.data_type(), + )?; + if !bytes.is_fill_value(chunk_representation.fill_value()) { + let chunk_encoded = + self.inner_codecs + .encode(bytes, chunk_representation, &options)?; let chunk_offset = encoded_shard_offset .fetch_add(chunk_encoded.len(), std::sync::atomic::Ordering::Relaxed); @@ -520,8 +562,9 @@ impl ShardingCodec { }; // Encode and write array index + let shard_index_bytes: RawBytes = transmute_to_bytes_vec(shard_index).into(); let encoded_array_index = self.index_codecs.encode( - Cow::Owned(transmute_to_bytes_vec(shard_index)), + shard_index_bytes.into(), &index_decoded_representation, &options, )?; @@ -549,12 +592,15 @@ impl ShardingCodec { #[allow(clippy::too_many_lines)] fn encode_unbounded( &self, - decoded_value: &[u8], + decoded_value: &ArrayBytes, shard_representation: &ChunkRepresentation, chunk_representation: &ChunkRepresentation, options: &CodecOptions, ) -> Result, CodecError> { - debug_assert_eq!(decoded_value.len() as u64, shard_representation.size()); // already validated in par_encode + decoded_value.validate( + shard_representation.num_elements(), + shard_representation.data_type().size(), + )?; let chunks_per_shard = calculate_chunks_per_shard(shard_representation.shape(), chunk_representation.shape())?; @@ -584,26 +630,28 @@ impl ShardingCodec { .into_builder() .concurrent_target(concurrency_limit_inner_chunks) .build(); - // println!("{shard_concurrent_limit} {concurrency_limit_inner_chunks:?}"); // FIXME: log debug? let encode_chunk = |chunk_index| { let chunk_subset = self.chunk_index_to_subset(chunk_index as u64, chunks_per_shard.as_slice()); - let bytes = unsafe { - chunk_subset.extract_bytes_unchecked( - decoded_value, - &shard_shape, - shard_representation.element_size(), - ) + + let bytes = decoded_value.extract_array_subset( + &chunk_subset, + &shard_shape, + chunk_representation.data_type(), + ); + let bytes = match bytes { + Ok(bytes) => bytes, + Err(err) => return Some(Err(err)), }; - if chunk_representation.fill_value().equals_all(&bytes) { + + let is_fill_value = bytes.is_fill_value(chunk_representation.fill_value()); + if is_fill_value { None } else { - let encoded_chunk = self.inner_codecs.encode( - Cow::Owned(bytes), - chunk_representation, - &options_inner, - ); + let encoded_chunk = + self.inner_codecs + .encode(bytes, chunk_representation, &options_inner); match encoded_chunk { Ok(encoded_chunk) => Some(Ok((chunk_index, encoded_chunk.to_vec()))), Err(err) => Some(Err(err)), @@ -662,7 +710,7 @@ impl ShardingCodec { // Write shard index let encoded_array_index = self.index_codecs.encode( - Cow::Owned(transmute_to_bytes_vec(shard_index)), + ArrayBytes::from(transmute_to_bytes_vec(shard_index)), &index_decoded_representation, options, )?; diff --git a/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs b/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs index a55e724d..17ce5bd2 100644 --- a/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs @@ -1,9 +1,10 @@ -use std::{borrow::Cow, num::NonZeroU64}; +use std::num::NonZeroU64; use rayon::prelude::*; use crate::{ array::{ + array_bytes::{merge_chunks_vlen, update_bytes_flen}, chunk_grid::RegularChunkGrid, chunk_shape_to_array_shape, codec::{ @@ -14,7 +15,7 @@ use crate::{ concurrency::{calc_concurrency_outer_inner, RecommendedConcurrency}, ravel_indices, unsafe_cell_slice::UnsafeCellSlice, - ChunkRepresentation, ChunkShape, DataType, + ArrayBytes, ChunkRepresentation, ChunkShape, DataType, DataTypeSize, }, byte_range::ByteRange, }; @@ -128,7 +129,7 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { for array_subset in array_subsets { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( @@ -142,7 +143,7 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { return Ok(array_subsets .iter() .map(|decoded_region| { - Cow::Owned( + ArrayBytes::from( self.decoded_representation .fill_value() .as_ne_bytes() @@ -167,9 +168,6 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { let chunks_per_shard = chunk_shape_to_array_shape(chunks_per_shard.as_slice()); let num_chunks = usize::try_from(chunks_per_shard.iter().product::()).unwrap(); - let element_size = self.decoded_representation.element_size(); - let fill_value = chunk_representation.fill_value().as_ne_bytes(); - // Calculate inner chunk/codec concurrency let (inner_chunk_concurrent_limit, concurrency_limit_codec) = calc_concurrency_outer_inner( options.concurrent_target(), @@ -188,79 +186,157 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { let mut out = Vec::with_capacity(array_subsets.len()); for array_subset in array_subsets { - let array_subset_size = array_subset.num_elements_usize() * element_size; - let mut out_array_subset = vec![0; array_subset_size]; - let out_array_subset_slice = UnsafeCellSlice::new(out_array_subset.as_mut_slice()); - let chunks = unsafe { array_subset.chunks_unchecked(chunk_representation.shape()) }; - rayon_iter_concurrent_limit::iter_concurrent_limit!( - inner_chunk_concurrent_limit, - chunks, - try_for_each, - |(chunk_indices, chunk_subset): (Vec, _)| { - let out_array_subset_slice = unsafe { out_array_subset_slice.get() }; - - let shard_index_idx: usize = - usize::try_from(ravel_indices(&chunk_indices, &chunks_per_shard) * 2) - .unwrap(); - let offset = shard_index[shard_index_idx]; - let size = shard_index[shard_index_idx + 1]; - - // Get the subset of bytes from the chunk which intersect the array - let overlap = unsafe { array_subset.overlap_unchecked(&chunk_subset) }; - let array_subset_in_chunk_subset = - unsafe { overlap.relative_to_unchecked(chunk_subset.start()) }; - - let decoded_bytes = if offset == u64::MAX && size == u64::MAX { - // The chunk is just the fill value - fill_value.repeat(array_subset_in_chunk_subset.num_elements_usize()) - } else { - // Partially decode the inner chunk - let partial_decoder = self.inner_codecs.partial_decoder( - Box::new(ByteIntervalPartialDecoder::new( - &*self.input_handle, - offset, - size, - )), - &chunk_representation, - &options, - ) - .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { - CodecError::Other( - "The shard index references out-of-bounds bytes. The chunk may be corrupted." - .to_string(), + + match self.decoded_representation.element_size() { + DataTypeSize::Variable => { + let decode_inner_chunk_subset = |(chunk_indices, chunk_subset): ( + Vec, + _, + )| { + let shard_index_idx: usize = + usize::try_from(ravel_indices(&chunk_indices, &chunks_per_shard) * 2) + .unwrap(); + let offset = shard_index[shard_index_idx]; + let size = shard_index[shard_index_idx + 1]; + + // Get the subset of bytes from the chunk which intersect the array + let chunk_subset_overlap = + unsafe { array_subset.overlap_unchecked(&chunk_subset) }; + + let chunk_subset_bytes = if offset == u64::MAX && size == u64::MAX { + ArrayBytes::new_fill_value( + chunk_subset_overlap.num_elements_usize(), + chunk_representation.fill_value(), ) } else { - err - })?; - let decoded_bytes = partial_decoder - .partial_decode_opt(&[array_subset_in_chunk_subset], &options)? - .remove(0); - decoded_bytes.to_vec() + // Partially decode the inner chunk + let partial_decoder = self.inner_codecs.partial_decoder( + Box::new(ByteIntervalPartialDecoder::new( + &*self.input_handle, + offset, + size, + )), + &chunk_representation, + &options, + ) + .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { + CodecError::Other( + "The shard index references out-of-bounds bytes. The chunk may be corrupted." + .to_string(), + ) + } else { + err + })?; + partial_decoder + .partial_decode_opt( + &[chunk_subset_overlap + .relative_to(chunk_subset.start()) + .unwrap()], + &options, + )? + .remove(0) + .into_owned() + }; + Ok::<_, CodecError>(( + chunk_subset_bytes, + chunk_subset_overlap + .relative_to(array_subset.start()) + .unwrap(), + )) }; - // Copy decoded bytes to the output - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - let mut decoded_offset = 0; - let contiguous_iterator = unsafe { - chunk_subset_in_array_subset - .contiguous_linearised_indices_unchecked(array_subset.shape()) + // Decode the inner chunk subsets + let chunk_bytes_and_subsets = + rayon_iter_concurrent_limit::iter_concurrent_limit!( + inner_chunk_concurrent_limit, + chunks, + map, + decode_inner_chunk_subset + ) + .collect::, _>>()?; + + // Convert into an array + let out_array_subset = + merge_chunks_vlen(chunk_bytes_and_subsets, array_subset.shape())?; + out.push(out_array_subset); + } + DataTypeSize::Fixed(data_type_size) => { + let array_subset_size = array_subset.num_elements_usize() * data_type_size; + let mut out_array_subset = vec![0; array_subset_size]; + let out_array_subset_slice = + UnsafeCellSlice::new(out_array_subset.as_mut_slice()); + + let decode_inner_chunk_subset_into_slice = |(chunk_indices, chunk_subset): ( + Vec, + _, + )| { + let shard_index_idx: usize = + usize::try_from(ravel_indices(&chunk_indices, &chunks_per_shard) * 2) + .unwrap(); + let offset = shard_index[shard_index_idx]; + let size = shard_index[shard_index_idx + 1]; + + // Get the subset of bytes from the chunk which intersect the array + let chunk_subset_overlap = + unsafe { array_subset.overlap_unchecked(&chunk_subset) }; + + let decoded_bytes = if offset == u64::MAX && size == u64::MAX { + ArrayBytes::new_fill_value( + chunk_subset_overlap.num_elements_usize(), + chunk_representation.fill_value(), + ) + } else { + // Partially decode the inner chunk + let partial_decoder = self.inner_codecs.partial_decoder( + Box::new(ByteIntervalPartialDecoder::new( + &*self.input_handle, + offset, + size, + )), + &chunk_representation, + &options, + ) + .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { + CodecError::Other( + "The shard index references out-of-bounds bytes. The chunk may be corrupted." + .to_string(), + ) + } else { + err + })?; + partial_decoder + .partial_decode_opt( + &[chunk_subset_overlap + .relative_to(chunk_subset.start()) + .unwrap()], + &options, + )? + .remove(0) + .into_owned() + }; + let decoded_bytes = decoded_bytes.into_fixed()?; + update_bytes_flen( + unsafe { out_array_subset_slice.get() }, + array_subset.shape(), + &decoded_bytes, + &chunk_subset_overlap + .relative_to(array_subset.start()) + .unwrap(), + data_type_size, + ); + Ok::<_, CodecError>(()) }; - let length = contiguous_iterator.contiguous_elements_usize() * element_size; - for (array_subset_element_index, _num_elements) in &contiguous_iterator { - let output_offset = - usize::try_from(array_subset_element_index).unwrap() * element_size; - out_array_subset_slice[output_offset..output_offset + length] - .copy_from_slice( - &decoded_bytes[decoded_offset..decoded_offset + length], - ); - decoded_offset += length; - } - Ok::<_, CodecError>(()) + + rayon_iter_concurrent_limit::iter_concurrent_limit!( + inner_chunk_concurrent_limit, + chunks, + try_for_each, + decode_inner_chunk_subset_into_slice + )?; + out.push(ArrayBytes::from(out_array_subset)); } - )?; - out.push(Cow::Owned(out_array_subset)); + } } Ok(out) } @@ -370,7 +446,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncShardingPartialDecoder<'_> { &self, array_subsets: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { for array_subset in array_subsets { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( @@ -384,7 +460,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncShardingPartialDecoder<'_> { return Ok(array_subsets .iter() .map(|decoded_region| { - Cow::Owned( + ArrayBytes::from( self.decoded_representation .fill_value() .as_ne_bytes() @@ -400,180 +476,242 @@ impl AsyncArrayPartialDecoderTraits for AsyncShardingPartialDecoder<'_> { )?; let chunks_per_shard = chunk_shape_to_array_shape(chunks_per_shard.as_slice()); - let element_size = self.decoded_representation.element_size(); + let chunk_representation = unsafe { + ChunkRepresentation::new_unchecked( + self.chunk_grid.chunk_shape().to_vec(), + self.decoded_representation.data_type().clone(), + self.decoded_representation.fill_value().clone(), + ) + }; + let mut out = Vec::with_capacity(array_subsets.len()); - // FIXME: Could go parallel here + // TODO: Could go parallel here? for array_subset in array_subsets { - // shard (subset) - let shard_size = array_subset.num_elements_usize() * element_size; - let mut shard = Vec::with_capacity(shard_size); - let shard_slice = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut shard); - - // Find filled / non filled chunks - let chunk_info = - unsafe { array_subset.chunks_unchecked(self.chunk_grid.chunk_shape()) } - .into_iter() - .map(|(chunk_indices, chunk_subset)| { - let chunk_index = ravel_indices(&chunk_indices, &chunks_per_shard); - let chunk_index = usize::try_from(chunk_index).unwrap(); - - // Read the offset/size - let offset = shard_index[chunk_index * 2]; - let size = shard_index[chunk_index * 2 + 1]; - if offset == u64::MAX && size == u64::MAX { - (chunk_subset, None) - } else { - let offset: usize = offset.try_into().unwrap(); - let size: usize = size.try_into().unwrap(); - (chunk_subset, Some((offset, size))) - } - }) - .collect::>(); - - // Decode unfilled chunks - let results = futures::future::join_all( - chunk_info - .iter() - .filter_map(|(chunk_subset, offset_size)| { - offset_size - .as_ref() - .map(|offset_size| (chunk_subset, offset_size)) - }) - .map(|(chunk_subset, (offset, size))| async move { - let chunk_representation = unsafe { - ChunkRepresentation::new_unchecked( - self.chunk_grid.chunk_shape().to_vec(), - self.decoded_representation.data_type().clone(), - self.decoded_representation.fill_value().clone(), - ) - }; - let partial_decoder = self - .inner_codecs - .async_partial_decoder( - Box::new(AsyncByteIntervalPartialDecoder::new( - &*self.input_handle, - u64::try_from(*offset).unwrap(), - u64::try_from(*size).unwrap(), - )), - &chunk_representation, - options, // FIXME: Adjust options for partial decoding - ) - .await - .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { - CodecError::Other( - "The shard index references out-of-bounds bytes. The chunk may be corrupted." - .to_string(), + match self.decoded_representation.element_size() { + DataTypeSize::Variable => { + let chunks = + unsafe { array_subset.chunks_unchecked(chunk_representation.shape()) }; + + let decode_inner_chunk_subset = |(chunk_indices, chunk_subset): ( + Vec, + _, + )| { + let shard_index_idx: usize = + usize::try_from(ravel_indices(&chunk_indices, &chunks_per_shard) * 2) + .unwrap(); + let chunk_representation = chunk_representation.clone(); + async move { + let offset = shard_index[shard_index_idx]; + let size = shard_index[shard_index_idx + 1]; + + // Get the subset of bytes from the chunk which intersect the array + let chunk_subset_overlap = + unsafe { array_subset.overlap_unchecked(&chunk_subset) }; + + let chunk_subset_bytes = if offset == u64::MAX && size == u64::MAX { + ArrayBytes::new_fill_value( + chunk_subset_overlap.num_elements_usize(), + chunk_representation.fill_value(), ) } else { - err - })?; - let overlap = unsafe { array_subset.overlap_unchecked(chunk_subset) }; - let array_subset_in_chunk_subset = - unsafe { overlap.relative_to_unchecked(chunk_subset.start()) }; - // Partial decoding is actually really slow with the blosc codec! Assume sharded chunks are small, and just decode the whole thing and extract bytes - // TODO: Investigate further - // let decoded_chunk = partial_decoder - // .partial_decode(&[array_subset_in_chunk_subset]) - // .await? - // .remove(0); - let decoded_chunk = partial_decoder - .partial_decode_opt( - &[ArraySubset::new_with_shape(chunk_subset.shape().to_vec())], - options, - ) // FIXME: Adjust options for partial decoding - .await? - .remove(0); - let decoded_chunk = array_subset_in_chunk_subset - .extract_bytes(&decoded_chunk, chunk_subset.shape(), element_size) - .unwrap(); - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - Ok::<_, CodecError>((chunk_subset_in_array_subset, decoded_chunk)) - }), - ) - .await; - // FIXME: Concurrency limit for futures - - if !results.is_empty() { - rayon_iter_concurrent_limit::iter_concurrent_limit!( - options.concurrent_target(), - results, - try_for_each, - |subset_and_decoded_chunk| { - let (chunk_subset_in_array_subset, decoded_chunk): (ArraySubset, Vec) = - subset_and_decoded_chunk?; - let mut data_idx = 0; - let element_size = element_size as u64; - let shard_slice = unsafe { shard_slice.get() }; - let contiguous_iterator = unsafe { - chunk_subset_in_array_subset - .contiguous_linearised_indices_unchecked(array_subset.shape()) - }; - let length = usize::try_from( - contiguous_iterator.contiguous_elements() * element_size, - ) - .unwrap(); - for (index, _num_elements) in &contiguous_iterator { - let shard_offset = usize::try_from(index * element_size).unwrap(); - shard_slice[shard_offset..shard_offset + length] - .copy_from_slice(&decoded_chunk[data_idx..data_idx + length]); - data_idx += length; + // Partially decode the inner chunk + let partial_decoder = self.inner_codecs.async_partial_decoder( + Box::new(AsyncByteIntervalPartialDecoder::new( + &*self.input_handle, + offset, + size, + )), + &chunk_representation, + options, + ).await + .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { + CodecError::Other( + "The shard index references out-of-bounds bytes. The chunk may be corrupted." + .to_string(), + ) + } else { + err + })?; + partial_decoder + .partial_decode_opt( + &[chunk_subset_overlap + .relative_to(chunk_subset.start()) + .unwrap()], + options, + ) + .await? + .remove(0) + .into_owned() + }; + Ok::<_, CodecError>(( + chunk_subset_bytes, + chunk_subset_overlap + .relative_to(array_subset.start()) + .unwrap(), + )) } - Ok::<_, CodecError>(()) - } - )?; - } + }; - // Write filled chunks - let filled_chunks = chunk_info - .iter() - .filter_map(|(chunk_subset, offset_size)| { - if offset_size.is_none() { - Some(chunk_subset) - } else { - None - } - }) - .collect::>(); - if !filled_chunks.is_empty() { - let chunk_array_ss = ArraySubset::new_with_shape(self.chunk_grid.chunk_shape_u64()); - let filled_chunk = self - .decoded_representation - .fill_value() - .as_ne_bytes() - .repeat(chunk_array_ss.num_elements_usize()); - - // Write filled chunks - rayon_iter_concurrent_limit::iter_concurrent_limit!( - options.concurrent_target(), - filled_chunks, - for_each, - |chunk_subset: &ArraySubset| { - let overlap = unsafe { array_subset.overlap_unchecked(chunk_subset) }; - let chunk_subset_in_array_subset = - unsafe { overlap.relative_to_unchecked(array_subset.start()) }; - let mut data_idx = 0; - let element_size = self.decoded_representation.element_size() as u64; - let shard_slice = unsafe { shard_slice.get() }; - let contiguous_iterator = unsafe { - chunk_subset_in_array_subset - .contiguous_linearised_indices_unchecked(array_subset.shape()) - }; - let length = usize::try_from( - contiguous_iterator.contiguous_elements() * element_size, + // Decode the inner chunk subsets + let futures = chunks.iter().map(decode_inner_chunk_subset); + let chunk_bytes_and_subsets = futures::future::try_join_all(futures).await?; + + // Convert into an array + let out_array_subset = + merge_chunks_vlen(chunk_bytes_and_subsets, array_subset.shape())?; + out.push(out_array_subset); + } + DataTypeSize::Fixed(data_type_size) => { + // Find filled / non filled chunks + let chunk_info = + unsafe { array_subset.chunks_unchecked(self.chunk_grid.chunk_shape()) } + .into_iter() + .map(|(chunk_indices, chunk_subset)| { + let chunk_index = ravel_indices(&chunk_indices, &chunks_per_shard); + let chunk_index = usize::try_from(chunk_index).unwrap(); + + // Read the offset/size + let offset = shard_index[chunk_index * 2]; + let size = shard_index[chunk_index * 2 + 1]; + if offset == u64::MAX && size == u64::MAX { + (chunk_subset, None) + } else { + let offset: usize = offset.try_into().unwrap(); + let size: usize = size.try_into().unwrap(); + (chunk_subset, Some((offset, size))) + } + }) + .collect::>(); + + let shard_size = array_subset.num_elements_usize() * data_type_size; + let mut shard = Vec::with_capacity(shard_size); + let shard_slice = UnsafeCellSlice::new_from_vec_with_spare_capacity(&mut shard); + + // Decode unfilled chunks + let results = futures::future::join_all( + chunk_info + .iter() + .filter_map(|(chunk_subset, offset_size)| { + offset_size + .as_ref() + .map(|offset_size| (chunk_subset, offset_size)) + }) + .map(|(chunk_subset, (offset, size))| { + let chunk_representation = chunk_representation.clone(); + async move { + let partial_decoder = self + .inner_codecs + .async_partial_decoder( + Box::new(AsyncByteIntervalPartialDecoder::new( + &*self.input_handle, + u64::try_from(*offset).unwrap(), + u64::try_from(*size).unwrap(), + )), + &chunk_representation, + options, // TODO: Adjust options for partial decoding? + ) + .await + .map_err(|err| if let CodecError::InvalidByteRangeError(_) = err { + CodecError::Other( + "The shard index references out-of-bounds bytes. The chunk may be corrupted." + .to_string(), + ) + } else { + err + })?; + let chunk_subset_overlap = unsafe { array_subset.overlap_unchecked(chunk_subset) }; + // Partial decoding is actually really slow with the blosc codec! Assume sharded chunks are small, and just decode the whole thing and extract bytes + // TODO: Investigate further + // let decoded_chunk = partial_decoder + // .partial_decode(&[chunk_subset_overlap.relative_to(chunk_subset.start())?]) + // .await? + // .remove(0); + let decoded_chunk = partial_decoder + .partial_decode_opt( + &[ArraySubset::new_with_shape(chunk_subset.shape().to_vec())], + options, + ) // TODO: Adjust options for partial decoding + .await? + .remove(0).into_owned(); + let decoded_chunk = decoded_chunk + .extract_array_subset( + &chunk_subset_overlap.relative_to(chunk_subset.start()).unwrap(), + chunk_subset.shape(), + self.decoded_representation.data_type() + )? + .into_fixed()? + .into_owned(); + Ok::<_, CodecError>((decoded_chunk, chunk_subset_overlap)) + }}), ) - .unwrap(); - for (index, _num_elements) in &contiguous_iterator { - let shard_offset = usize::try_from(index * element_size).unwrap(); - shard_slice[shard_offset..shard_offset + length] - .copy_from_slice(&filled_chunk[data_idx..data_idx + length]); - data_idx += length; - } + .await; + // FIXME: Concurrency limit for futures + + if !results.is_empty() { + rayon_iter_concurrent_limit::iter_concurrent_limit!( + options.concurrent_target(), + results, + try_for_each, + |subset_and_decoded_chunk| { + let (chunk_subset_bytes, chunk_subset_overlap): ( + Vec, + ArraySubset, + ) = subset_and_decoded_chunk?; + update_bytes_flen( + unsafe { shard_slice.get() }, + array_subset.shape(), + &chunk_subset_bytes.into(), + &chunk_subset_overlap + .relative_to(array_subset.start()) + .unwrap(), + data_type_size, + ); + Ok::<_, CodecError>(()) + } + )?; } - ); - }; - unsafe { shard.set_len(shard_size) }; - out.push(Cow::Owned(shard)); + + // Write filled chunks + let filled_chunks = chunk_info + .iter() + .filter_map(|(chunk_subset, offset_size)| { + if offset_size.is_none() { + Some(chunk_subset) + } else { + None + } + }) + .collect::>(); + if !filled_chunks.is_empty() { + // Write filled chunks + rayon_iter_concurrent_limit::iter_concurrent_limit!( + options.concurrent_target(), + filled_chunks, + for_each, + |chunk_subset: &ArraySubset| { + let chunk_subset_overlap = + unsafe { array_subset.overlap_unchecked(chunk_subset) }; + let filled_chunk = self + .decoded_representation + .fill_value() + .as_ne_bytes() + .repeat(chunk_subset_overlap.num_elements_usize()); + update_bytes_flen( + unsafe { shard_slice.get() }, + array_subset.shape(), + &filled_chunk.into(), + &chunk_subset_overlap + .relative_to(array_subset.start()) + .unwrap(), + data_type_size, + ); + } + ); + }; + unsafe { shard.set_len(shard_size) }; + out.push(ArrayBytes::from(shard)); + } + } } Ok(out) } diff --git a/src/array/codec/array_to_bytes/vlen.rs b/src/array/codec/array_to_bytes/vlen.rs new file mode 100644 index 00000000..07213411 --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen.rs @@ -0,0 +1,198 @@ +//! The `vlen` array to bytes codec. + +mod vlen_codec; +mod vlen_partial_decoder; + +use std::{mem::size_of, num::NonZeroU64}; + +use itertools::Itertools; +pub use vlen::IDENTIFIER; + +pub use crate::metadata::v3::codec::vlen::{VlenCodecConfiguration, VlenCodecConfigurationV1}; +use crate::{ + array::{ + codec::{ArrayToBytesCodecTraits, CodecError, CodecOptions}, + convert_from_bytes_slice, ChunkRepresentation, CodecChain, DataType, Endianness, FillValue, + RawBytes, NATIVE_ENDIAN, + }, + metadata::v3::codec::vlen, +}; + +pub use vlen_codec::VlenCodec; + +use crate::{ + array::codec::{Codec, CodecPlugin}, + metadata::MetadataV3, + plugin::{PluginCreateError, PluginMetadataInvalidError}, +}; + +use super::bytes::reverse_endianness; + +// Register the codec. +inventory::submit! { + CodecPlugin::new(IDENTIFIER, is_name_vlen, create_codec_vlen) +} + +fn is_name_vlen(name: &str) -> bool { + name.eq(IDENTIFIER) +} + +pub(crate) fn create_codec_vlen(metadata: &MetadataV3) -> Result { + let configuration: VlenCodecConfiguration = metadata + .to_configuration() + .map_err(|_| PluginMetadataInvalidError::new(IDENTIFIER, "codec", metadata.clone()))?; + let codec = Box::new(VlenCodec::new_with_configuration(&configuration)?); + Ok(Codec::ArrayToBytes(codec)) +} + +fn get_vlen_bytes_and_offsets( + index_chunk_representation: &ChunkRepresentation, + bytes: &RawBytes, + index_codecs: &CodecChain, + data_codecs: &CodecChain, + options: &CodecOptions, +) -> Result<(Vec, Vec), CodecError> { + // Get the index length and data start + if bytes.len() < size_of::() { + return Err(CodecError::UnexpectedChunkDecodedSize( + bytes.len(), + size_of::() as u64, + )); + } + let index_len = u64::from_le_bytes(bytes[0..size_of::()].try_into().unwrap()); + let index_len = usize::try_from(index_len) + .map_err(|_| CodecError::Other("index length exceeds usize::MAX".to_string()))?; + let data_start = size_of::() + index_len; + let data_compressed_len = bytes.len() - data_start; + + // Decode the index + let index = &bytes[size_of::()..data_start]; + let mut index_bytes = index_codecs + .decode(index.into(), index_chunk_representation, options)? + .into_fixed()?; + if NATIVE_ENDIAN == Endianness::Big { + reverse_endianness(index_bytes.to_mut(), &DataType::UInt64); + } + let index = match index_chunk_representation.data_type() { + // DataType::UInt8 => { + // let index = convert_from_bytes_slice::(&index_bytes); + // offsets_u8_to_usize(index) + // } + // DataType::UInt16 => { + // let index = convert_from_bytes_slice::(&index_bytes); + // offsets_u16_to_usize(index) + // } + DataType::UInt32 => { + let index = convert_from_bytes_slice::(&index_bytes); + offsets_u32_to_usize(index) + } + DataType::UInt64 => { + let index = convert_from_bytes_slice::(&index_bytes); + offsets_u64_to_usize(index) + } + _ => unreachable!("other data types are not part of VlenIndexDataType"), + }; + + // Get the data length + let Some(&data_len_expected) = index.last() else { + return Err(CodecError::Other( + "Index is empty? It should have at least one element".to_string(), + )); + }; + + // Decode the data + let data = &bytes[data_start..data_start + data_compressed_len]; + let data = if let Ok(data_len_expected) = NonZeroU64::try_from(data_len_expected as u64) { + data_codecs.decode( + data.into(), + &unsafe { + // SAFETY: data type and fill value are compatible + ChunkRepresentation::new_unchecked( + vec![data_len_expected], + DataType::UInt8, + FillValue::from(0u8), + ) + }, + options, + )? + } else { + vec![].into() + } + .into_fixed()? + .into_owned(); + let data_len = data.len(); + + // Check the data length is as expected + if data_len != data_len_expected { + return Err(CodecError::Other(format!( + "Expected data length {data_len_expected} does not match data length {data_len}" + ))); + } + + // Validate the offsets + for (curr, next) in index.iter().tuple_windows() { + if next < curr || *next > data_len { + return Err(CodecError::Other( + "Invalid bytes offsets in vlen Offset64 encoded chunk".to_string(), + )); + } + } + + Ok((data, index)) +} + +// /// Convert u8 offsets to usize +// /// +// /// # Panics if the offsets exceed [`usize::MAX`]. +// fn offsets_u8_to_usize(offsets: Vec) -> Vec { +// if size_of::() == size_of::() { +// bytemuck::allocation::cast_vec(offsets) +// } else { +// offsets +// .into_iter() +// .map(|offset| usize::from(offset)) +// .collect() +// } +// } + +// /// Convert u16 offsets to usize +// /// +// /// # Panics if the offsets exceed [`usize::MAX`]. +// fn offsets_u16_to_usize(offsets: Vec) -> Vec { +// if size_of::() == size_of::() { +// bytemuck::allocation::cast_vec(offsets) +// } else { +// offsets +// .into_iter() +// .map(|offset| usize::from(offset)) +// .collect() +// } +// } + +/// Convert u32 offsets to usize +/// +/// # Panics if the offsets exceed [`usize::MAX`]. +fn offsets_u32_to_usize(offsets: Vec) -> Vec { + if size_of::() == size_of::() { + bytemuck::allocation::cast_vec(offsets) + } else { + offsets + .into_iter() + .map(|offset| usize::try_from(offset).unwrap()) + .collect() + } +} + +/// Convert u64 offsets to usize +/// +/// # Panics if the offsets exceed [`usize::MAX`]. +fn offsets_u64_to_usize(offsets: Vec) -> Vec { + if size_of::() == size_of::() { + bytemuck::allocation::cast_vec(offsets) + } else { + offsets + .into_iter() + .map(|offset| usize::try_from(offset).unwrap()) + .collect() + } +} diff --git a/src/array/codec/array_to_bytes/vlen/vlen_codec.rs b/src/array/codec/array_to_bytes/vlen/vlen_codec.rs new file mode 100644 index 00000000..fc2e6004 --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen/vlen_codec.rs @@ -0,0 +1,305 @@ +use std::{mem::size_of, num::NonZeroU64}; + +use crate::{ + array::{ + codec::{ + ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, BytesCodec, + BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, + RecommendedConcurrency, + }, + transmute_to_bytes_vec, ArrayBytes, ArrayMetadataOptions, BytesRepresentation, + ChunkRepresentation, CodecChain, DataType, DataTypeSize, Endianness, FillValue, RawBytes, + }, + metadata::v3::{codec::vlen::VlenIndexDataType, MetadataV3}, + plugin::PluginCreateError, +}; + +#[cfg(feature = "async")] +use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; + +use super::{vlen_partial_decoder, VlenCodecConfiguration, VlenCodecConfigurationV1}; + +/// A `bytes` codec implementation. +#[derive(Debug, Clone)] +pub struct VlenCodec { + index_codecs: CodecChain, + data_codecs: CodecChain, + index_data_type: VlenIndexDataType, +} + +impl Default for VlenCodec { + fn default() -> Self { + let index_codecs = CodecChain::new( + vec![], + Box::new(BytesCodec::new(Some(Endianness::Little))), + vec![], + ); + let data_codecs = CodecChain::new(vec![], Box::new(BytesCodec::new(None)), vec![]); + Self { + index_codecs, + data_codecs, + index_data_type: VlenIndexDataType::UInt64, + } + } +} + +impl VlenCodec { + /// Create a new `vlen` codec. + #[must_use] + pub fn new( + index_codecs: CodecChain, + data_codecs: CodecChain, + index_data_type: VlenIndexDataType, + ) -> Self { + Self { + index_codecs, + data_codecs, + index_data_type, + } + } + + /// Create a new `vlen` codec from configuration. + /// + /// # Errors + /// Returns a [`PluginCreateError`] if the codecs cannot be constructed from the codec metadata. + pub fn new_with_configuration( + configuration: &VlenCodecConfiguration, + ) -> Result { + let VlenCodecConfiguration::V1(configuration) = configuration; + let index_codecs = CodecChain::from_metadata(&configuration.index_codecs)?; + let data_codecs = CodecChain::from_metadata(&configuration.data_codecs)?; + Ok(Self::new( + index_codecs, + data_codecs, + configuration.index_data_type, + )) + } +} + +impl CodecTraits for VlenCodec { + fn create_metadata_opt(&self, _options: &ArrayMetadataOptions) -> Option { + let configuration = VlenCodecConfigurationV1 { + index_codecs: self.index_codecs.create_metadatas(), + data_codecs: self.data_codecs.create_metadatas(), + index_data_type: self.index_data_type, + }; + Some( + MetadataV3::new_with_serializable_configuration(super::IDENTIFIER, &configuration) + .unwrap(), + ) + } + + fn partial_decoder_should_cache_input(&self) -> bool { + false + } + + fn partial_decoder_decodes_all(&self) -> bool { + true // TODO: Vlen could do partial decoding, but needs coalescing etc + } +} + +impl ArrayCodecTraits for VlenCodec { + fn recommended_concurrency( + &self, + _decoded_representation: &ChunkRepresentation, + ) -> Result { + Ok(RecommendedConcurrency::new_maximum(1)) + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for VlenCodec { + fn encode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + let (data, offsets) = bytes.into_variable()?; + + // Encode offsets + let num_offsets = + NonZeroU64::try_from(decoded_representation.num_elements_usize() as u64 + 1).unwrap(); + let offsets = match self.index_data_type { + // VlenIndexDataType::UInt8 => { + // let offsets = offsets + // .iter() + // .map(|offset| u8::try_from(*offset)) + // .collect::, _>>() + // .map_err(|_| { + // CodecError::Other( + // "index offsets are too large for a uint8 index_data_type".to_string(), + // ) + // })?; + // let offsets = transmute_to_bytes_vec(offsets); + // let index_chunk_rep = ChunkRepresentation::new( + // vec![num_offsets], + // DataType::UInt8, + // FillValue::from(0u8), + // ) + // .unwrap(); + // self.index_codecs + // .encode(offsets.into(), &index_chunk_rep, options)? + // } + // VlenIndexDataType::UInt16 => { + // let offsets = offsets + // .iter() + // .map(|offset| u16::try_from(*offset)) + // .collect::, _>>() + // .map_err(|_| { + // CodecError::Other( + // "index offsets are too large for a uint16 index_data_type".to_string(), + // ) + // })?; + // let offsets = transmute_to_bytes_vec(offsets); + // let index_chunk_rep = ChunkRepresentation::new( + // vec![num_offsets], + // DataType::UInt16, + // FillValue::from(0u16), + // ) + // .unwrap(); + // self.index_codecs + // .encode(offsets.into(), &index_chunk_rep, options)? + // } + VlenIndexDataType::UInt32 => { + let offsets = offsets + .iter() + .map(|offset| u32::try_from(*offset)) + .collect::, _>>() + .map_err(|_| { + CodecError::Other( + "index offsets are too large for a uint32 index_data_type".to_string(), + ) + })?; + let offsets = transmute_to_bytes_vec(offsets); + let index_chunk_rep = ChunkRepresentation::new( + vec![num_offsets], + DataType::UInt32, + FillValue::from(0u32), + ) + .unwrap(); + self.index_codecs + .encode(offsets.into(), &index_chunk_rep, options)? + } + VlenIndexDataType::UInt64 => { + let offsets = offsets + .iter() + .map(|offset| u64::try_from(*offset).unwrap()) + .collect::>(); + let offsets = transmute_to_bytes_vec(offsets); + let index_chunk_rep = ChunkRepresentation::new( + vec![num_offsets], + DataType::UInt64, + FillValue::from(0u64), + ) + .unwrap(); + self.index_codecs + .encode(offsets.into(), &index_chunk_rep, options)? + } + }; + + // Encode data + let data = if let Ok(data_len) = NonZeroU64::try_from(data.len() as u64) { + self.data_codecs.encode( + data.into(), + &ChunkRepresentation::new(vec![data_len], DataType::UInt8, FillValue::from(0u8)) + .unwrap(), + options, + )? + } else { + vec![].into() + }; + + // Pack encoded offsets length, encoded offsets, and encoded data + let mut bytes = Vec::with_capacity(size_of::() + offsets.len() + data.len()); + bytes.extend_from_slice(&u64::try_from(offsets.len()).unwrap().to_le_bytes()); // offsets length as u64 little endian + bytes.extend_from_slice(&offsets); + bytes.extend_from_slice(&data); + Ok(bytes.into()) + } + + fn decode<'a>( + &self, + bytes: RawBytes<'a>, + decoded_representation: &ChunkRepresentation, + options: &CodecOptions, + ) -> Result, CodecError> { + let num_elements = decoded_representation.num_elements_usize(); + let index_shape = vec![NonZeroU64::try_from(num_elements as u64 + 1).unwrap()]; + let index_chunk_rep = match self.index_data_type { + // VlenIndexDataType::UInt8 => { + // ChunkRepresentation::new(index_shape, DataType::UInt8, FillValue::from(0u8)) + // } + // VlenIndexDataType::UInt16 => { + // ChunkRepresentation::new(index_shape, DataType::UInt16, FillValue::from(0u16)) + // } + VlenIndexDataType::UInt32 => { + ChunkRepresentation::new(index_shape, DataType::UInt32, FillValue::from(0u32)) + } + VlenIndexDataType::UInt64 => { + ChunkRepresentation::new(index_shape, DataType::UInt64, FillValue::from(0u64)) + } + } + .unwrap(); + let (data, index) = super::get_vlen_bytes_and_offsets( + &index_chunk_rep, + &bytes, + &self.index_codecs, + &self.data_codecs, + options, + )?; + Ok(ArrayBytes::new_vlen(data, index)) + } + + fn partial_decoder<'a>( + &'a self, + input_handle: Box, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + Ok(Box::new(vlen_partial_decoder::VlenPartialDecoder::new( + input_handle, + decoded_representation.clone(), + &self.index_codecs, + &self.data_codecs, + self.index_data_type, + ))) + } + + #[cfg(feature = "async")] + async fn async_partial_decoder<'a>( + &'a self, + input_handle: Box, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + Ok(Box::new( + vlen_partial_decoder::AsyncVlenPartialDecoder::new( + input_handle, + decoded_representation.clone(), + &self.index_codecs, + &self.data_codecs, + self.index_data_type, + ), + )) + } + + fn compute_encoded_size( + &self, + decoded_representation: &ChunkRepresentation, + ) -> Result { + match decoded_representation.data_type().size() { + DataTypeSize::Variable => Ok(BytesRepresentation::UnboundedSize), + DataTypeSize::Fixed(_) => { + return Err(CodecError::UnsupportedDataType( + decoded_representation.data_type().clone(), + super::IDENTIFIER.to_string(), + )) + } + } + } +} diff --git a/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs b/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs new file mode 100644 index 00000000..d142a8df --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs @@ -0,0 +1,178 @@ +// TODO: Support actual partial decoding, coalescing required + +use std::num::NonZeroU64; + +use crate::{ + array::{ + array_bytes::extract_decoded_regions_vlen, + codec::{ + ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, + CodecOptions, + }, + ArrayBytes, ChunkRepresentation, CodecChain, DataType, FillValue, RawBytes, + }, + metadata::v3::codec::vlen::VlenIndexDataType, +}; + +#[cfg(feature = "async")] +use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; + +/// Partial decoder for the `bytes` codec. +pub struct VlenPartialDecoder<'a> { + input_handle: Box, + decoded_representation: ChunkRepresentation, + index_codecs: &'a CodecChain, + data_codecs: &'a CodecChain, + index_data_type: VlenIndexDataType, +} + +impl<'a> VlenPartialDecoder<'a> { + /// Create a new partial decoder for the `bytes` codec. + pub fn new( + input_handle: Box, + decoded_representation: ChunkRepresentation, + index_codecs: &'a CodecChain, + data_codecs: &'a CodecChain, + index_data_type: VlenIndexDataType, + ) -> Self { + Self { + input_handle, + decoded_representation, + index_codecs, + data_codecs, + index_data_type, + } + } +} + +#[allow(clippy::too_many_arguments)] +fn decode_vlen_bytes<'a>( + index_codecs: &CodecChain, + data_codecs: &CodecChain, + index_data_type: VlenIndexDataType, + bytes: Option, + decoded_regions: &[ArraySubset], + fill_value: &FillValue, + shape: &[u64], + options: &CodecOptions, +) -> Result>, CodecError> { + if let Some(bytes) = bytes { + let num_elements = usize::try_from(shape.iter().product::()).unwrap(); + let index_shape = vec![unsafe { NonZeroU64::new_unchecked(1 + num_elements as u64) }]; + let index_chunk_representation = match index_data_type { + // VlenIndexDataType::UInt8 => { + // ChunkRepresentation::new(index_shape, DataType::UInt8, FillValue::from(0u8)) + // } + // VlenIndexDataType::UInt16 => { + // ChunkRepresentation::new(index_shape, DataType::UInt16, FillValue::from(0u16)) + // } + VlenIndexDataType::UInt32 => { + ChunkRepresentation::new(index_shape, DataType::UInt32, FillValue::from(0u32)) + } + VlenIndexDataType::UInt64 => { + ChunkRepresentation::new(index_shape, DataType::UInt64, FillValue::from(0u64)) + } + } + .expect("all data types/fill values are compatible"); + let (data, index) = super::get_vlen_bytes_and_offsets( + &index_chunk_representation, + &bytes, + index_codecs, + data_codecs, + options, + )?; + extract_decoded_regions_vlen(&data, &index, decoded_regions, shape) + } else { + // Chunk is empty, all decoded regions are empty + let mut output = Vec::with_capacity(decoded_regions.len()); + for decoded_region in decoded_regions { + output.push(ArrayBytes::new_fill_value( + decoded_region.num_elements_usize(), + fill_value, + )); + } + Ok(output) + } +} + +impl ArrayPartialDecoderTraits for VlenPartialDecoder<'_> { + fn data_type(&self) -> &DataType { + self.decoded_representation.data_type() + } + + fn partial_decode_opt( + &self, + decoded_regions: &[ArraySubset], + options: &CodecOptions, + ) -> Result>, CodecError> { + // Get all of the input bytes (cached due to CodecTraits::partial_decoder_decodes_all() == true) + let bytes = self.input_handle.decode(options)?; + decode_vlen_bytes( + self.index_codecs, + self.data_codecs, + self.index_data_type, + bytes, + decoded_regions, + self.decoded_representation.fill_value(), + &self.decoded_representation.shape_u64(), + options, + ) + } +} + +#[cfg(feature = "async")] +/// Asynchronous partial decoder for the `bytes` codec. +pub struct AsyncVlenPartialDecoder<'a> { + input_handle: Box, + decoded_representation: ChunkRepresentation, + index_codecs: &'a CodecChain, + data_codecs: &'a CodecChain, + index_data_type: VlenIndexDataType, +} + +#[cfg(feature = "async")] +impl<'a> AsyncVlenPartialDecoder<'a> { + /// Create a new partial decoder for the `bytes` codec. + pub fn new( + input_handle: Box, + decoded_representation: ChunkRepresentation, + index_codecs: &'a CodecChain, + data_codecs: &'a CodecChain, + index_data_type: VlenIndexDataType, + ) -> Self { + Self { + input_handle, + decoded_representation, + index_codecs, + data_codecs, + index_data_type, + } + } +} + +#[cfg(feature = "async")] +#[async_trait::async_trait] +impl AsyncArrayPartialDecoderTraits for AsyncVlenPartialDecoder<'_> { + fn data_type(&self) -> &DataType { + self.decoded_representation.data_type() + } + + async fn partial_decode_opt( + &self, + decoded_regions: &[ArraySubset], + options: &CodecOptions, + ) -> Result>, CodecError> { + // Get all of the input bytes (cached due to CodecTraits::partial_decoder_decodes_all() == true) + let bytes = self.input_handle.decode(options).await?; + decode_vlen_bytes( + self.index_codecs, + self.data_codecs, + self.index_data_type, + bytes, + decoded_regions, + self.decoded_representation.fill_value(), + &self.decoded_representation.shape_u64(), + options, + ) + } +} diff --git a/src/array/codec/array_to_bytes/vlen_interleaved.rs b/src/array/codec/array_to_bytes/vlen_interleaved.rs new file mode 100644 index 00000000..06d1164d --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen_interleaved.rs @@ -0,0 +1,83 @@ +//! The `vlen_interleaved` array to bytes codec. + +mod vlen_interleaved_codec; +mod vlen_interleaved_partial_decoder; + +use std::mem::size_of; + +pub use vlen_interleaved::IDENTIFIER; + +pub use crate::metadata::v3::codec::vlen_interleaved::{ + VlenInterleavedCodecConfiguration, VlenInterleavedCodecConfigurationV1, +}; +use crate::{ + array::{codec::CodecError, RawBytes}, + metadata::v3::codec::vlen_interleaved, +}; + +pub use vlen_interleaved_codec::VlenInterleavedCodec; + +use crate::{ + array::codec::{Codec, CodecPlugin}, + metadata::MetadataV3, + plugin::{PluginCreateError, PluginMetadataInvalidError}, +}; + +// Register the codec. +inventory::submit! { + CodecPlugin::new(IDENTIFIER, is_name_vlen_interleaved, create_codec_vlen_interleaved) +} + +fn is_name_vlen_interleaved(name: &str) -> bool { + name.eq(IDENTIFIER) +} + +pub(crate) fn create_codec_vlen_interleaved( + metadata: &MetadataV3, +) -> Result { + let configuration: VlenInterleavedCodecConfiguration = metadata + .to_configuration() + .map_err(|_| PluginMetadataInvalidError::new(IDENTIFIER, "codec", metadata.clone()))?; + let codec = Box::new(VlenInterleavedCodec::new_with_configuration(&configuration)); + Ok(Codec::ArrayToBytes(codec)) +} + +fn get_interleaved_bytes_and_offsets( + num_elements: usize, + bytes: &RawBytes, +) -> Result<(Vec, Vec), CodecError> { + // Validate the bytes is long enough to contain header and element lengths + let header_length = size_of::() * (1 + num_elements); + if bytes.len() < header_length { + return Err(CodecError::UnexpectedChunkDecodedSize( + bytes.len(), + header_length as u64, + )); + } + + // Validate the number of elements from the header + let header_num_elements = u32::from_le_bytes((&bytes[0..size_of::()]).try_into().unwrap()); + if u32::try_from(num_elements).unwrap() != header_num_elements { + return Err(CodecError::Other(format!( + "Expected header with {num_elements} elements, got {header_num_elements}" + ))); + } + + let bytes_len = bytes.len() - header_length; + let mut bytes_out = Vec::with_capacity(bytes_len); + let mut offsets_out = Vec::with_capacity(num_elements + 1); + let mut offset = size_of::(); + for _element in 0..num_elements { + let length = + u32::from_le_bytes(bytes[offset..offset + size_of::()].try_into().unwrap()); + offset += size_of::(); + offsets_out.push(bytes_out.len()); + if length != 0 { + bytes_out.extend_from_slice(&bytes[offset..offset + length as usize]); + offset += length as usize; + } + } + offsets_out.push(bytes_out.len()); + + Ok((bytes_out, offsets_out)) +} diff --git a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs new file mode 100644 index 00000000..db610d77 --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs @@ -0,0 +1,157 @@ +use std::mem::size_of; + +use itertools::Itertools; + +use crate::{ + array::{ + codec::{ + ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, + BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, + RecommendedConcurrency, + }, + ArrayBytes, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, DataTypeSize, + RawBytes, + }, + metadata::v3::MetadataV3, +}; + +#[cfg(feature = "async")] +use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; + +use super::{VlenInterleavedCodecConfiguration, VlenInterleavedCodecConfigurationV1}; + +/// The `vlen_interleaved` codec implementation. +#[derive(Debug, Clone, Default)] +pub struct VlenInterleavedCodec {} + +impl VlenInterleavedCodec { + /// Create a new `vlen` codec. + #[must_use] + pub fn new() -> Self { + Self {} + } + + /// Create a new `vlen` codec from configuration. + #[must_use] + pub fn new_with_configuration(_configuration: &VlenInterleavedCodecConfiguration) -> Self { + // let VlenInterleavedCodecConfiguration::V1(configuration) = configuration; + Self {} + } +} + +impl CodecTraits for VlenInterleavedCodec { + fn create_metadata_opt(&self, _options: &ArrayMetadataOptions) -> Option { + let configuration = VlenInterleavedCodecConfigurationV1 {}; + Some( + MetadataV3::new_with_serializable_configuration(super::IDENTIFIER, &configuration) + .unwrap(), + ) + } + + fn partial_decoder_should_cache_input(&self) -> bool { + false + } + + fn partial_decoder_decodes_all(&self) -> bool { + true // TODO: Vlen could do partial decoding, but needs coalescing etc + } +} + +impl ArrayCodecTraits for VlenInterleavedCodec { + fn recommended_concurrency( + &self, + _decoded_representation: &ChunkRepresentation, + ) -> Result { + Ok(RecommendedConcurrency::new_maximum(1)) + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for VlenInterleavedCodec { + fn encode<'a>( + &self, + bytes: ArrayBytes<'a>, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + bytes.validate( + decoded_representation.num_elements(), + decoded_representation.data_type().size(), + )?; + let (bytes, offsets) = bytes.into_variable()?; + + let num_elements = decoded_representation.num_elements(); + debug_assert_eq!(1 + num_elements, offsets.len() as u64); + + let mut data: Vec = Vec::with_capacity(offsets.len() * size_of::() + bytes.len()); + // Number of elements + let num_elements = u32::try_from(num_elements).map_err(|_| { + CodecError::Other("num_elements exceeds u32::MAX in vlen codec".to_string()) + })?; + data.extend_from_slice(num_elements.to_le_bytes().as_slice()); + // Interleaved length (u32, little endian) and element bytes + for (&curr, &next) in offsets.iter().tuple_windows() { + let element_bytes = &bytes[curr..next]; + let element_bytes_len = u32::try_from(element_bytes.len()).unwrap(); + data.extend_from_slice(&element_bytes_len.to_le_bytes()); + data.extend_from_slice(element_bytes); + } + + Ok(data.into()) + } + + fn decode<'a>( + &self, + bytes: RawBytes<'a>, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + let num_elements = decoded_representation.num_elements_usize(); + let (bytes, offsets) = super::get_interleaved_bytes_and_offsets(num_elements, &bytes)?; + Ok(ArrayBytes::new_vlen(bytes, offsets)) + } + + fn partial_decoder<'a>( + &self, + input_handle: Box, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + Ok(Box::new( + super::vlen_interleaved_partial_decoder::VlenInterleavedPartialDecoder::new( + input_handle, + decoded_representation.clone(), + ), + )) + } + + #[cfg(feature = "async")] + async fn async_partial_decoder<'a>( + &'a self, + input_handle: Box, + decoded_representation: &ChunkRepresentation, + _options: &CodecOptions, + ) -> Result, CodecError> { + Ok(Box::new( + super::vlen_interleaved_partial_decoder::AsyncVlenInterleavedPartialDecoder::new( + input_handle, + decoded_representation.clone(), + ), + )) + } + + fn compute_encoded_size( + &self, + decoded_representation: &ChunkRepresentation, + ) -> Result { + match decoded_representation.data_type().size() { + DataTypeSize::Variable => Ok(BytesRepresentation::UnboundedSize), + DataTypeSize::Fixed(_) => { + return Err(CodecError::UnsupportedDataType( + decoded_representation.data_type().clone(), + super::IDENTIFIER.to_string(), + )) + } + } + } +} diff --git a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs new file mode 100644 index 00000000..bb707fb8 --- /dev/null +++ b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs @@ -0,0 +1,119 @@ +// TODO: Support actual partial decoding, coalescing required + +use crate::array::{ + array_bytes::extract_decoded_regions_vlen, + codec::{ + ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, CodecOptions, + }, + ArrayBytes, ChunkRepresentation, DataType, FillValue, RawBytes, +}; + +#[cfg(feature = "async")] +use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; + +/// Partial decoder for the `bytes` codec. +pub struct VlenInterleavedPartialDecoder<'a> { + input_handle: Box, + decoded_representation: ChunkRepresentation, +} + +impl<'a> VlenInterleavedPartialDecoder<'a> { + /// Create a new partial decoder for the `bytes` codec. + pub fn new( + input_handle: Box, + decoded_representation: ChunkRepresentation, + ) -> Self { + Self { + input_handle, + decoded_representation, + } + } +} + +fn decode_vlen_bytes<'a>( + bytes: Option, + decoded_regions: &[ArraySubset], + fill_value: &FillValue, + shape: &[u64], +) -> Result>, CodecError> { + if let Some(bytes) = bytes { + let num_elements = usize::try_from(shape.iter().product::()).unwrap(); + let (bytes, offsets) = super::get_interleaved_bytes_and_offsets(num_elements, &bytes)?; + extract_decoded_regions_vlen(&bytes, &offsets, decoded_regions, shape) + } else { + // Chunk is empty, all decoded regions are empty + let mut output = Vec::with_capacity(decoded_regions.len()); + for decoded_region in decoded_regions { + output.push(ArrayBytes::new_fill_value( + decoded_region.num_elements_usize(), + fill_value, + )); + } + Ok(output) + } +} + +impl ArrayPartialDecoderTraits for VlenInterleavedPartialDecoder<'_> { + fn data_type(&self) -> &DataType { + self.decoded_representation.data_type() + } + + fn partial_decode_opt( + &self, + decoded_regions: &[ArraySubset], + options: &CodecOptions, + ) -> Result>, CodecError> { + // Get all of the input bytes (cached due to CodecTraits::partial_decoder_decodes_all() == true) + let bytes = self.input_handle.decode(options)?; + decode_vlen_bytes( + bytes, + decoded_regions, + self.decoded_representation.fill_value(), + &self.decoded_representation.shape_u64(), + ) + } +} + +#[cfg(feature = "async")] +/// Asynchronous partial decoder for the `bytes` codec. +pub struct AsyncVlenInterleavedPartialDecoder<'a> { + input_handle: Box, + decoded_representation: ChunkRepresentation, +} + +#[cfg(feature = "async")] +impl<'a> AsyncVlenInterleavedPartialDecoder<'a> { + /// Create a new partial decoder for the `bytes` codec. + pub fn new( + input_handle: Box, + decoded_representation: ChunkRepresentation, + ) -> Self { + Self { + input_handle, + decoded_representation, + } + } +} + +#[cfg(feature = "async")] +#[async_trait::async_trait] +impl AsyncArrayPartialDecoderTraits for AsyncVlenInterleavedPartialDecoder<'_> { + fn data_type(&self) -> &DataType { + self.decoded_representation.data_type() + } + + async fn partial_decode_opt( + &self, + decoded_regions: &[ArraySubset], + options: &CodecOptions, + ) -> Result>, CodecError> { + // Get all of the input bytes (cached due to CodecTraits::partial_decoder_decodes_all() == true) + let bytes = self.input_handle.decode(options).await?; + decode_vlen_bytes( + bytes, + decoded_regions, + self.decoded_representation.fill_value(), + &self.decoded_representation.shape_u64(), + ) + } +} diff --git a/src/array/codec/array_to_bytes/zfp.rs b/src/array/codec/array_to_bytes/zfp.rs index 7c461c89..8a72adf1 100644 --- a/src/array/codec/array_to_bytes/zfp.rs +++ b/src/array/codec/array_to_bytes/zfp.rs @@ -271,10 +271,14 @@ fn zfp_decode( #[cfg(test)] mod tests { use num::traits::AsPrimitive; - use std::{borrow::Cow, num::NonZeroU64}; + use std::num::NonZeroU64; use crate::{ - array::codec::{ArrayCodecTraits, ArrayToBytesCodecTraits, CodecOptions}, + array::{ + codec::{ArrayToBytesCodecTraits, CodecOptions}, + element::ElementOwned, + ArrayBytes, + }, array_subset::ArraySubset, }; @@ -304,21 +308,23 @@ mod tests { ] } - fn codec_zfp_round_trip( + fn codec_zfp_round_trip< + T: core::fmt::Debug + std::cmp::PartialEq + ElementOwned + Copy + 'static, + >( chunk_representation: &ChunkRepresentation, configuration: &str, ) where i32: num::traits::AsPrimitive, { let elements: Vec = (0..27).map(|i: i32| i.as_()).collect(); - let bytes = crate::array::transmute_to_bytes_vec(elements.clone()); + let bytes = T::into_array_bytes(chunk_representation.data_type(), &elements).unwrap(); let configuration: ZfpCodecConfiguration = serde_json::from_str(configuration).unwrap(); let codec = ZfpCodec::new_with_configuration(&configuration); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -329,9 +335,10 @@ mod tests { &chunk_representation, &CodecOptions::default(), ) - .unwrap(); - - let decoded_elements = crate::array::convert_from_bytes_slice::(&decoded); + .unwrap() + .into_owned(); + let decoded_elements = + T::from_array_bytes(chunk_representation.data_type(), decoded).unwrap(); assert_eq!(elements, decoded_elements); } @@ -493,13 +500,14 @@ mod tests { ChunkRepresentation::new(chunk_shape, DataType::Float32, 0.0f32.into()).unwrap(); let elements: Vec = (0..27).map(|i| i as f32).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let configuration: ZfpCodecConfiguration = serde_json::from_str(JSON_REVERSIBLE).unwrap(); let codec = ZfpCodec::new_with_configuration(&configuration); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -523,7 +531,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) @@ -548,6 +556,7 @@ mod tests { ChunkRepresentation::new(chunk_shape, DataType::Float32, 0.0f32.into()).unwrap(); let elements: Vec = (0..27).map(|i| i as f32).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); + let bytes: ArrayBytes = bytes.into(); let configuration: ZfpCodecConfiguration = serde_json::from_str(JSON_REVERSIBLE).unwrap(); let codec = ZfpCodec::new_with_configuration(&configuration); @@ -555,7 +564,7 @@ mod tests { let max_encoded_size = codec.compute_encoded_size(&chunk_representation).unwrap(); let encoded = codec .encode( - Cow::Borrowed(&bytes), + bytes.clone(), &chunk_representation, &CodecOptions::default(), ) @@ -582,7 +591,7 @@ mod tests { let decoded_partial_chunk: Vec = decoded_partial_chunk .into_iter() - .map(|v| v.to_vec()) + .map(|bytes| bytes.into_fixed().unwrap().to_vec()) .flatten() .collect::>() .chunks(std::mem::size_of::()) diff --git a/src/array/codec/array_to_bytes/zfp/zfp_codec.rs b/src/array/codec/array_to_bytes/zfp/zfp_codec.rs index 3ea792fd..f98d7c81 100644 --- a/src/array/codec/array_to_bytes/zfp/zfp_codec.rs +++ b/src/array/codec/array_to_bytes/zfp/zfp_codec.rs @@ -11,8 +11,8 @@ use zfp_sys::{ use crate::{ array::{ codec::{ - ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, - BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, + ArrayBytes, ArrayCodecTraits, ArrayPartialDecoderTraits, ArrayToBytesCodecTraits, + BytesPartialDecoderTraits, CodecError, CodecOptions, CodecTraits, RawBytes, RecommendedConcurrency, }, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, DataType, @@ -129,18 +129,21 @@ impl ArrayCodecTraits for ZfpCodec { // TODO: zfp supports multi thread, when is it optimal to kick in? Ok(RecommendedConcurrency::new_maximum(1)) } +} +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ArrayToBytesCodecTraits for ZfpCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + bytes: ArrayBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { - let mut decoded_value_promoted = - promote_before_zfp_encoding(&decoded_value, decoded_representation)?; - let zfp_type = decoded_value_promoted.zfp_type(); + ) -> Result, CodecError> { + let bytes = bytes.into_fixed()?; + let mut bytes_promoted = promote_before_zfp_encoding(&bytes, decoded_representation)?; + let zfp_type = bytes_promoted.zfp_type(); let Some(field) = ZfpField::new( - &mut decoded_value_promoted, + &mut bytes_promoted, &decoded_representation .shape() .iter() @@ -185,22 +188,19 @@ impl ArrayCodecTraits for ZfpCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + bytes: RawBytes<'a>, decoded_representation: &ChunkRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { zfp_decode( &self.mode, - &mut encoded_value.to_vec(), // FIXME: Does zfp **really** need the encoded value as mutable? + &mut bytes.to_vec(), // FIXME: Does zfp **really** need the encoded value as mutable? decoded_representation, false, // FIXME ) - .map(Cow::Owned) + .map(ArrayBytes::from) } -} -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToBytesCodecTraits for ZfpCodec { fn partial_decoder<'a>( &'a self, input_handle: Box, diff --git a/src/array/codec/array_to_bytes/zfp/zfp_partial_decoder.rs b/src/array/codec/array_to_bytes/zfp/zfp_partial_decoder.rs index ac5179ac..7f7103f7 100644 --- a/src/array/codec/array_to_bytes/zfp/zfp_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/zfp/zfp_partial_decoder.rs @@ -1,8 +1,9 @@ -use std::borrow::Cow; - use crate::{ array::{ - codec::{ArrayPartialDecoderTraits, BytesPartialDecoderTraits, CodecError, CodecOptions}, + codec::{ + ArrayBytes, ArrayPartialDecoderTraits, BytesPartialDecoderTraits, CodecError, + CodecOptions, + }, ChunkRepresentation, DataType, }, array_subset::ArraySubset, @@ -51,7 +52,10 @@ impl ArrayPartialDecoderTraits for ZfpPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { + let data_type_size = self.data_type().fixed_size().ok_or_else(|| { + CodecError::UnsupportedDataType(self.data_type().clone(), super::IDENTIFIER.to_string()) + })?; for array_subset in decoded_regions { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( @@ -73,13 +77,9 @@ impl ArrayPartialDecoderTraits for ZfpPartialDecoder<'_> { false, // FIXME )?; for array_subset in decoded_regions { - let byte_ranges = unsafe { - array_subset.byte_ranges_unchecked( - &chunk_shape, - self.decoded_representation.element_size(), - ) - }; - out.push(Cow::Owned(extract_byte_ranges_concat( + let byte_ranges = + unsafe { array_subset.byte_ranges_unchecked(&chunk_shape, data_type_size) }; + out.push(ArrayBytes::from(extract_byte_ranges_concat( &decoded_value, &byte_ranges, )?)); @@ -87,7 +87,7 @@ impl ArrayPartialDecoderTraits for ZfpPartialDecoder<'_> { } None => { for decoded_region in decoded_regions { - out.push(Cow::Owned( + out.push(ArrayBytes::from( self.decoded_representation .fill_value() .as_ne_bytes() @@ -141,7 +141,10 @@ impl AsyncArrayPartialDecoderTraits for AsyncZfpPartialDecoder<'_> { &self, decoded_regions: &[ArraySubset], options: &CodecOptions, - ) -> Result>, CodecError> { + ) -> Result>, CodecError> { + let data_type_size = self.data_type().fixed_size().ok_or_else(|| { + CodecError::UnsupportedDataType(self.data_type().clone(), super::IDENTIFIER.to_string()) + })?; for array_subset in decoded_regions { if array_subset.dimensionality() != self.decoded_representation.dimensionality() { return Err(CodecError::InvalidArraySubsetDimensionalityError( @@ -163,13 +166,9 @@ impl AsyncArrayPartialDecoderTraits for AsyncZfpPartialDecoder<'_> { false, // FIXME )?; for array_subset in decoded_regions { - let byte_ranges = unsafe { - array_subset.byte_ranges_unchecked( - &chunk_shape, - self.decoded_representation.element_size(), - ) - }; - out.push(Cow::Owned(extract_byte_ranges_concat( + let byte_ranges = + unsafe { array_subset.byte_ranges_unchecked(&chunk_shape, data_type_size) }; + out.push(ArrayBytes::from(extract_byte_ranges_concat( &decoded_value, &byte_ranges, )?)); @@ -177,7 +176,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncZfpPartialDecoder<'_> { } None => { for decoded_region in decoded_regions { - out.push(Cow::Owned( + out.push(ArrayBytes::from( self.decoded_representation .fill_value() .as_ne_bytes() diff --git a/src/array/codec/byte_interval_partial_decoder.rs b/src/array/codec/byte_interval_partial_decoder.rs index e0e95eca..33fcffa7 100644 --- a/src/array/codec/byte_interval_partial_decoder.rs +++ b/src/array/codec/byte_interval_partial_decoder.rs @@ -1,6 +1,7 @@ -use std::borrow::Cow; - -use crate::byte_range::{ByteLength, ByteOffset, ByteRange}; +use crate::{ + array::RawBytes, + byte_range::{ByteLength, ByteOffset, ByteRange}, +}; use super::{BytesPartialDecoderTraits, CodecError, CodecOptions}; @@ -36,7 +37,7 @@ impl<'a> BytesPartialDecoderTraits for ByteIntervalPartialDecoder<'a> { &self, byte_ranges: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let byte_ranges: Vec = byte_ranges .iter() .map(|byte_range| match byte_range { @@ -92,7 +93,7 @@ impl<'a> AsyncBytesPartialDecoderTraits for AsyncByteIntervalPartialDecoder<'a> &self, byte_ranges: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let byte_ranges: Vec = byte_ranges .iter() .map(|byte_range| match byte_range { diff --git a/src/array/codec/bytes_partial_decoder_cache.rs b/src/array/codec/bytes_partial_decoder_cache.rs index de42f45a..634866ea 100644 --- a/src/array/codec/bytes_partial_decoder_cache.rs +++ b/src/array/codec/bytes_partial_decoder_cache.rs @@ -2,7 +2,10 @@ use std::{borrow::Cow, marker::PhantomData}; -use crate::byte_range::{extract_byte_ranges, ByteRange}; +use crate::{ + array::RawBytes, + byte_range::{extract_byte_ranges, ByteRange}, +}; use super::{BytesPartialDecoderTraits, CodecError, CodecOptions}; @@ -58,7 +61,7 @@ impl BytesPartialDecoderTraits for BytesPartialDecoderCache<'_> { &self, decoded_regions: &[ByteRange], _options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { Ok(match &self.cache { Some(bytes) => Some( extract_byte_ranges(bytes, decoded_regions) @@ -79,7 +82,7 @@ impl AsyncBytesPartialDecoderTraits for BytesPartialDecoderCache<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { BytesPartialDecoderTraits::partial_decode(self, decoded_regions, options) } } diff --git a/src/array/codec/bytes_to_bytes/blosc.rs b/src/array/codec/bytes_to_bytes/blosc.rs index ef8287b2..16bdc8b7 100644 --- a/src/array/codec/bytes_to_bytes/blosc.rs +++ b/src/array/codec/bytes_to_bytes/blosc.rs @@ -336,7 +336,9 @@ mod tests { let array_representation = ArrayRepresentation::new(vec![2, 2, 2], DataType::UInt16, FillValue::from(0u16)) .unwrap(); - let bytes_representation = BytesRepresentation::FixedSize(array_representation.size()); + let data_type_size = array_representation.data_type().fixed_size().unwrap(); + let array_size = array_representation.num_elements_usize() * data_type_size; + let bytes_representation = BytesRepresentation::FixedSize(array_size as u64); let elements: Vec = (0..array_representation.num_elements() as u16).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); @@ -349,10 +351,7 @@ mod tests { .encode(Cow::Borrowed(&bytes), &CodecOptions::default()) .unwrap(); let decoded_regions: Vec = ArraySubset::new_with_ranges(&[0..2, 1..2, 0..1]) - .byte_ranges( - array_representation.shape(), - array_representation.element_size(), - ) + .byte_ranges(array_representation.shape(), data_type_size) .unwrap(); let input_handle = Box::new(std::io::Cursor::new(encoded)); let partial_decoder = codec @@ -384,7 +383,9 @@ mod tests { let array_representation = ArrayRepresentation::new(vec![2, 2, 2], DataType::UInt16, FillValue::from(0u16)) .unwrap(); - let bytes_representation = BytesRepresentation::FixedSize(array_representation.size()); + let data_type_size = array_representation.data_type().fixed_size().unwrap(); + let array_size = array_representation.num_elements_usize() * data_type_size; + let bytes_representation = BytesRepresentation::FixedSize(array_size as u64); let elements: Vec = (0..array_representation.num_elements() as u16).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); @@ -397,10 +398,7 @@ mod tests { .encode(Cow::Borrowed(&bytes), &CodecOptions::default()) .unwrap(); let decoded_regions: Vec = ArraySubset::new_with_ranges(&[0..2, 1..2, 0..1]) - .byte_ranges( - array_representation.shape(), - array_representation.element_size(), - ) + .byte_ranges(array_representation.shape(), data_type_size) .unwrap(); let input_handle = Box::new(std::io::Cursor::new(encoded)); let partial_decoder = codec diff --git a/src/array/codec/bytes_to_bytes/blosc/blosc_codec.rs b/src/array/codec/bytes_to_bytes/blosc/blosc_codec.rs index f868d30f..c2a79046 100644 --- a/src/array/codec/bytes_to_bytes/blosc/blosc_codec.rs +++ b/src/array/codec/bytes_to_bytes/blosc/blosc_codec.rs @@ -8,7 +8,7 @@ use crate::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, plugin::PluginCreateError, @@ -136,7 +136,6 @@ impl CodecTraits for BloscCodec { cname: self.cname, clevel: self.clevel, shuffle: self.shuffle_mode.unwrap_or_else(|| { - // FIXME: If type size is not known... it must be if self.typesize.unwrap_or_default() > 0 { BloscShuffleMode::BitShuffle } else { @@ -170,9 +169,9 @@ impl BytesToBytesCodecTraits for BloscCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { // let n_threads = std::cmp::min( // options.concurrent_limit(), // std::thread::available_parallelism().unwrap(), @@ -184,10 +183,10 @@ impl BytesToBytesCodecTraits for BloscCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { // let n_threads = std::cmp::min( // options.concurrent_limit(), // std::thread::available_parallelism().unwrap(), diff --git a/src/array/codec/bytes_to_bytes/blosc/blosc_partial_decoder.rs b/src/array/codec/bytes_to_bytes/blosc/blosc_partial_decoder.rs index aa9fd701..c4779b99 100644 --- a/src/array/codec/bytes_to_bytes/blosc/blosc_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/blosc/blosc_partial_decoder.rs @@ -1,8 +1,12 @@ use std::borrow::Cow; use crate::{ - array::codec::{ - bytes_to_bytes::blosc::blosc_nbytes, BytesPartialDecoderTraits, CodecError, CodecOptions, + array::{ + codec::{ + bytes_to_bytes::blosc::blosc_nbytes, BytesPartialDecoderTraits, CodecError, + CodecOptions, + }, + RawBytes, }, byte_range::ByteRange, }; @@ -28,7 +32,7 @@ impl BytesPartialDecoderTraits for BloscPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options)?; let Some(encoded_value) = encoded_value else { return Ok(None); @@ -38,7 +42,6 @@ impl BytesPartialDecoderTraits for BloscPartialDecoder<'_> { let nbytes = blosc_nbytes(&encoded_value); let typesize = blosc_typesize(&encoded_value); if let (Some(nbytes), Some(typesize)) = (nbytes, typesize) { - // FIXME: This needs coalescing to be efficient, for now CodecTraits::partial_decoder_decodes_all is set to true let mut decoded_byte_ranges = Vec::with_capacity(decoded_regions.len()); for byte_range in decoded_regions { let start = usize::try_from(byte_range.start(nbytes as u64)).unwrap(); @@ -81,7 +84,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncBloscPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options).await?; let Some(encoded_value) = encoded_value else { return Ok(None); diff --git a/src/array/codec/bytes_to_bytes/bz2.rs b/src/array/codec/bytes_to_bytes/bz2.rs index 87717eca..09a49182 100644 --- a/src/array/codec/bytes_to_bytes/bz2.rs +++ b/src/array/codec/bytes_to_bytes/bz2.rs @@ -100,7 +100,9 @@ mod tests { let array_representation = ArrayRepresentation::new(vec![2, 2, 2], DataType::UInt16, FillValue::from(0u16)) .unwrap(); - let bytes_representation = BytesRepresentation::FixedSize(array_representation.size()); + let data_type_size = array_representation.data_type().fixed_size().unwrap(); + let array_size = array_representation.num_elements_usize() * data_type_size; + let bytes_representation = BytesRepresentation::FixedSize(array_size as u64); let elements: Vec = (0..array_representation.num_elements() as u16).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); @@ -112,10 +114,7 @@ mod tests { .encode(Cow::Borrowed(&bytes), &CodecOptions::default()) .unwrap(); let decoded_regions: Vec = ArraySubset::new_with_ranges(&[0..2, 1..2, 0..1]) - .byte_ranges( - array_representation.shape(), - array_representation.element_size(), - ) + .byte_ranges(array_representation.shape(), data_type_size) .unwrap(); let input_handle = Box::new(std::io::Cursor::new(encoded)); let partial_decoder = codec @@ -147,7 +146,9 @@ mod tests { let array_representation = ArrayRepresentation::new(vec![2, 2, 2], DataType::UInt16, FillValue::from(0u16)) .unwrap(); - let bytes_representation = BytesRepresentation::FixedSize(array_representation.size()); + let data_type_size = array_representation.data_type().fixed_size().unwrap(); + let array_size = array_representation.num_elements_usize() * data_type_size; + let bytes_representation = BytesRepresentation::FixedSize(array_size as u64); let elements: Vec = (0..array_representation.num_elements() as u16).collect(); let bytes = crate::array::transmute_to_bytes_vec(elements); @@ -159,10 +160,7 @@ mod tests { .encode(Cow::Borrowed(&bytes), &CodecOptions::default()) .unwrap(); let decoded_regions: Vec = ArraySubset::new_with_ranges(&[0..2, 1..2, 0..1]) - .byte_ranges( - array_representation.shape(), - array_representation.element_size(), - ) + .byte_ranges(array_representation.shape(), data_type_size) .unwrap(); let input_handle = Box::new(std::io::Cursor::new(encoded)); let partial_decoder = codec diff --git a/src/array/codec/bytes_to_bytes/bz2/bz2_codec.rs b/src/array/codec/bytes_to_bytes/bz2/bz2_codec.rs index 6bba2f1b..e55a81e2 100644 --- a/src/array/codec/bytes_to_bytes/bz2/bz2_codec.rs +++ b/src/array/codec/bytes_to_bytes/bz2/bz2_codec.rs @@ -9,7 +9,7 @@ use crate::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, }; @@ -74,9 +74,9 @@ impl BytesToBytesCodecTraits for Bz2Codec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let mut encoder = bzip2::read::BzEncoder::new(Cursor::new(decoded_value), self.compression); let mut out: Vec = Vec::new(); encoder.read_to_end(&mut out)?; @@ -85,10 +85,10 @@ impl BytesToBytesCodecTraits for Bz2Codec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let mut decoder = bzip2::read::BzDecoder::new(Cursor::new(encoded_value)); let mut out: Vec = Vec::new(); decoder.read_to_end(&mut out)?; diff --git a/src/array/codec/bytes_to_bytes/bz2/bz2_partial_decoder.rs b/src/array/codec/bytes_to_bytes/bz2/bz2_partial_decoder.rs index c3d7bf89..d0582923 100644 --- a/src/array/codec/bytes_to_bytes/bz2/bz2_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/bz2/bz2_partial_decoder.rs @@ -3,7 +3,10 @@ use std::{borrow::Cow, io::Read}; use bytes::Buf; use crate::{ - array::codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + array::{ + codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + RawBytes, + }, byte_range::{extract_byte_ranges, ByteRange}, }; @@ -26,7 +29,7 @@ impl BytesPartialDecoderTraits for Bz2PartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options)?; let Some(encoded_value) = encoded_value else { return Ok(None); @@ -66,7 +69,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncBz2PartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options).await?; let Some(encoded_value) = encoded_value else { return Ok(None); diff --git a/src/array/codec/bytes_to_bytes/crc32c/crc32c_codec.rs b/src/array/codec/bytes_to_bytes/crc32c/crc32c_codec.rs index 0e93b295..0cf0cfbe 100644 --- a/src/array/codec/bytes_to_bytes/crc32c/crc32c_codec.rs +++ b/src/array/codec/bytes_to_bytes/crc32c/crc32c_codec.rs @@ -6,7 +6,7 @@ use crate::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, }; @@ -63,9 +63,9 @@ impl BytesToBytesCodecTraits for Crc32cCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let checksum = crc32c::crc32c(&decoded_value).to_le_bytes(); let mut encoded_value: Vec = Vec::with_capacity(decoded_value.len() + checksum.len()); encoded_value.extend_from_slice(&decoded_value); @@ -75,10 +75,10 @@ impl BytesToBytesCodecTraits for Crc32cCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { if encoded_value.len() >= CHECKSUM_SIZE { if options.validate_checksums() { let decoded_value = &encoded_value[..encoded_value.len() - CHECKSUM_SIZE]; diff --git a/src/array/codec/bytes_to_bytes/crc32c/crc32c_partial_decoder.rs b/src/array/codec/bytes_to_bytes/crc32c/crc32c_partial_decoder.rs index f8769c0b..4d034849 100644 --- a/src/array/codec/bytes_to_bytes/crc32c/crc32c_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/crc32c/crc32c_partial_decoder.rs @@ -1,7 +1,10 @@ use std::borrow::Cow; use crate::{ - array::codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + array::{ + codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + RawBytes, + }, byte_range::ByteRange, }; @@ -27,7 +30,7 @@ impl BytesPartialDecoderTraits for Crc32cPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let bytes = self.input_handle.partial_decode(decoded_regions, options)?; let Some(bytes) = bytes else { return Ok(None); @@ -80,7 +83,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncCrc32cPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let bytes = self .input_handle .partial_decode(decoded_regions, options) diff --git a/src/array/codec/bytes_to_bytes/gzip/gzip_codec.rs b/src/array/codec/bytes_to_bytes/gzip/gzip_codec.rs index a635315f..af024c25 100644 --- a/src/array/codec/bytes_to_bytes/gzip/gzip_codec.rs +++ b/src/array/codec/bytes_to_bytes/gzip/gzip_codec.rs @@ -11,7 +11,7 @@ use crate::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, }; @@ -78,9 +78,9 @@ impl BytesToBytesCodecTraits for GzipCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let mut encoder = GzEncoder::new( Cursor::new(decoded_value), flate2::Compression::new(self.compression_level.as_u32()), @@ -92,10 +92,10 @@ impl BytesToBytesCodecTraits for GzipCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let mut decoder = GzDecoder::new(Cursor::new(encoded_value)); let mut out: Vec = Vec::new(); decoder.read_to_end(&mut out)?; diff --git a/src/array/codec/bytes_to_bytes/gzip/gzip_partial_decoder.rs b/src/array/codec/bytes_to_bytes/gzip/gzip_partial_decoder.rs index dd86a86b..8ecff296 100644 --- a/src/array/codec/bytes_to_bytes/gzip/gzip_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/gzip/gzip_partial_decoder.rs @@ -6,7 +6,10 @@ use std::{ use flate2::bufread::GzDecoder; use crate::{ - array::codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + array::{ + codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + RawBytes, + }, byte_range::{extract_byte_ranges, ByteRange}, }; @@ -30,7 +33,7 @@ impl BytesPartialDecoderTraits for GzipPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options)?; let Some(encoded_value) = encoded_value else { return Ok(None); @@ -71,7 +74,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncGzipPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options).await?; let Some(encoded_value) = encoded_value else { return Ok(None); diff --git a/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_codec.rs b/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_codec.rs index c7723865..7730b8f6 100644 --- a/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_codec.rs +++ b/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_codec.rs @@ -1,12 +1,10 @@ -use std::borrow::Cow; - use crate::{ array::{ codec::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, }; @@ -56,18 +54,18 @@ impl BytesToBytesCodecTraits for TestUnboundedCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { Ok(decoded_value) } fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { Ok(encoded_value) } diff --git a/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_partial_decoder.rs b/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_partial_decoder.rs index e999809b..9838b28e 100644 --- a/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/test_unbounded/test_unbounded_partial_decoder.rs @@ -1,7 +1,10 @@ use std::borrow::Cow; use crate::{ - array::codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + array::{ + codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + RawBytes, + }, byte_range::{extract_byte_ranges, ByteRange}, }; @@ -25,7 +28,7 @@ impl BytesPartialDecoderTraits for TestUnboundedPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options)?; let Some(encoded_value) = encoded_value else { return Ok(None); @@ -62,7 +65,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncTestUnboundedPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options).await?; let Some(encoded_value) = encoded_value else { return Ok(None); diff --git a/src/array/codec/bytes_to_bytes/zstd/zstd_codec.rs b/src/array/codec/bytes_to_bytes/zstd/zstd_codec.rs index 29268056..187be17a 100644 --- a/src/array/codec/bytes_to_bytes/zstd/zstd_codec.rs +++ b/src/array/codec/bytes_to_bytes/zstd/zstd_codec.rs @@ -8,7 +8,7 @@ use crate::{ BytesPartialDecoderTraits, BytesToBytesCodecTraits, CodecError, CodecOptions, CodecTraits, RecommendedConcurrency, }, - ArrayMetadataOptions, BytesRepresentation, + ArrayMetadataOptions, BytesRepresentation, RawBytes, }, metadata::v3::MetadataV3, }; @@ -76,9 +76,9 @@ impl BytesToBytesCodecTraits for ZstdCodec { fn encode<'a>( &self, - decoded_value: Cow<'a, [u8]>, + decoded_value: RawBytes<'a>, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { let mut result = Vec::::new(); let mut encoder = zstd::Encoder::new(&mut result, self.compression)?; encoder.include_checksum(self.checksum)?; @@ -93,10 +93,10 @@ impl BytesToBytesCodecTraits for ZstdCodec { fn decode<'a>( &self, - encoded_value: Cow<'a, [u8]>, + encoded_value: RawBytes<'a>, _decoded_representation: &BytesRepresentation, _options: &CodecOptions, - ) -> Result, CodecError> { + ) -> Result, CodecError> { zstd::decode_all(std::io::Cursor::new(&encoded_value)) .map_err(CodecError::IOError) .map(Cow::Owned) diff --git a/src/array/codec/bytes_to_bytes/zstd/zstd_partial_decoder.rs b/src/array/codec/bytes_to_bytes/zstd/zstd_partial_decoder.rs index 432a6ab0..5a357b39 100644 --- a/src/array/codec/bytes_to_bytes/zstd/zstd_partial_decoder.rs +++ b/src/array/codec/bytes_to_bytes/zstd/zstd_partial_decoder.rs @@ -3,7 +3,10 @@ use std::borrow::Cow; use bytes::Buf; use crate::{ - array::codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + array::{ + codec::{BytesPartialDecoderTraits, CodecError, CodecOptions}, + RawBytes, + }, byte_range::{extract_byte_ranges, ByteRange}, }; @@ -27,7 +30,7 @@ impl BytesPartialDecoderTraits for ZstdPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options)?; let Some(encoded_value) = encoded_value else { return Ok(None); @@ -66,7 +69,7 @@ impl AsyncBytesPartialDecoderTraits for AsyncZstdPartialDecoder<'_> { &self, decoded_regions: &[ByteRange], options: &CodecOptions, - ) -> Result>>, CodecError> { + ) -> Result>>, CodecError> { let encoded_value = self.input_handle.decode(options).await?; let Some(encoded_value) = encoded_value else { return Ok(None); diff --git a/src/array/data_type.rs b/src/array/data_type.rs index 5b622f7f..1f745d22 100644 --- a/src/array/data_type.rs +++ b/src/array/data_type.rs @@ -22,6 +22,7 @@ use super::FillValue; /// A data type. #[derive(Clone, Debug)] #[non_exhaustive] +#[rustfmt::skip] pub enum DataType { /// `bool` Boolean. Bool, @@ -55,9 +56,13 @@ pub enum DataType { Complex128, /// `r*` raw bits, variable size given by *, limited to be a multiple of 8. RawBits(usize), // the stored usize is the size in bytes + /// A UTF-8 encoded string. + String, + /// Variable-sized binary data. + Binary, - // /// An extension data type. - // Extension(Box), + // /// An extension data type. + // Extension(Box), } /// An unsupported data type error. @@ -102,6 +107,17 @@ impl IncompatibleFillValueError { } } +/// The size of a data type. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum DataTypeSize { + /// Fixed size (in bytes). + Fixed(usize), + /// Variable sized. + /// + /// + Variable, +} + /// Extension data type traits. pub trait DataTypeExtension: dyn_clone::DynClone + core::fmt::Debug + Send + Sync { /// Returns the identifier. @@ -110,8 +126,8 @@ pub trait DataTypeExtension: dyn_clone::DynClone + core::fmt::Debug + Send + Syn /// Returns the name. fn name(&self) -> String; - /// Returns the size in bytes. - fn size(&self) -> usize; + /// Returns the data type size in bytes. + fn size(&self) -> DataTypeSize; /// Returns the data type metadata. fn metadata(&self) -> MetadataV3; @@ -154,6 +170,8 @@ impl DataType { Self::Complex64 => "complex64", Self::Complex128 => "complex128", Self::RawBits(_usize) => "r*", + Self::String => "string", + Self::Binary => "binary", // Self::Extension(extension) => extension.identifier(), } } @@ -178,20 +196,30 @@ impl DataType { // } } - /// Returns the size in bytes. + /// Returns the [`DataTypeSize`]. #[must_use] - pub const fn size(&self) -> usize { + pub const fn size(&self) -> DataTypeSize { match self { - Self::Bool | Self::Int8 | Self::UInt8 => 1, - Self::Int16 | Self::UInt16 | Self::Float16 | Self::BFloat16 => 2, - Self::Int32 | Self::UInt32 | Self::Float32 => 4, - Self::Int64 | Self::UInt64 | Self::Float64 | Self::Complex64 => 8, - Self::Complex128 => 16, - Self::RawBits(size) => *size, + Self::Bool | Self::Int8 | Self::UInt8 => DataTypeSize::Fixed(1), + Self::Int16 | Self::UInt16 | Self::Float16 | Self::BFloat16 => DataTypeSize::Fixed(2), + Self::Int32 | Self::UInt32 | Self::Float32 => DataTypeSize::Fixed(4), + Self::Int64 | Self::UInt64 | Self::Float64 | Self::Complex64 => DataTypeSize::Fixed(8), + Self::Complex128 => DataTypeSize::Fixed(16), + Self::RawBits(size) => DataTypeSize::Fixed(*size), + Self::String | Self::Binary => DataTypeSize::Variable, // Self::Extension(extension) => extension.size(), } } + /// Returns the size in bytes of a fixed-size data type, otherwise returns [`None`]. + #[must_use] + pub const fn fixed_size(&self) -> Option { + match self.size() { + DataTypeSize::Fixed(size) => Some(size), + DataTypeSize::Variable => None, + } + } + /// Create a data type from metadata. /// /// # Errors @@ -216,6 +244,8 @@ impl DataType { "bfloat16" => return Ok(Self::BFloat16), "complex64" => return Ok(Self::Complex64), "complex128" => return Ok(Self::Complex128), + "string" => return Ok(Self::String), + "binary" => return Ok(Self::Binary), _ => {} }; @@ -280,11 +310,26 @@ impl DataType { return Ok(FillValue::new(bytes.clone())); } } - Err(IncompatibleFillValueMetadataError( - self.name(), - fill_value.clone(), - )) - } // Self::Extension(extension) => extension.fill_value_from_metadata(fill_value), + Err(err()) + } + Self::Binary => { + if let FillValueMetadata::ByteArray(bytes) = fill_value { + Ok(FillValue::new(bytes.clone())) + } else { + Err(err()) + } + } + // Self::Extension(extension) => extension.fill_value_from_metadata(fill_value), + Self::String => match fill_value { + FillValueMetadata::String(string) => Ok(FillValue::new(string.as_bytes().to_vec())), + FillValueMetadata::Float(float) => match float { + FillValueFloat::HexString(hex_string) => Ok(String::from(hex_string).into()), + FillValueFloat::NonFinite(non_finite) => Ok(String::from(non_finite).into()), + FillValueFloat::Float(_) => Err(err()), + }, + FillValueMetadata::ByteArray(bytes) => Ok(FillValue::new(bytes.clone())), + _ => Err(err()), + }, } } @@ -346,7 +391,12 @@ impl DataType { Self::RawBits(size) => { debug_assert_eq!(fill_value.as_ne_bytes().len(), *size); FillValueMetadata::ByteArray(fill_value.as_ne_bytes().to_vec()) - } // DataType::Extension(extension) => extension.metadata_fill_value(fill_value), + } + // DataType::Extension(extension) => extension.metadata_fill_value(fill_value), + Self::String => FillValueMetadata::String( + String::from_utf8(fill_value.as_ne_bytes().to_vec()).unwrap(), + ), + Self::Binary => FillValueMetadata::ByteArray(fill_value.as_ne_bytes().to_vec()), } } } @@ -869,7 +919,7 @@ mod tests { assert_eq!(json, serde_json::to_string(&data_type.metadata()).unwrap()); assert_eq!(data_type.identifier(), "r*"); assert_eq!(data_type.name().as_str(), "r8"); - assert_eq!(data_type.size(), 1); + assert_eq!(data_type.size(), DataTypeSize::Fixed(1)); let metadata = serde_json::from_str::("[7]").unwrap(); let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); @@ -885,7 +935,7 @@ mod tests { assert_eq!(json, serde_json::to_string(&data_type.metadata()).unwrap()); assert_eq!(data_type.identifier(), "r*"); assert_eq!(data_type.name().as_str(), "r16"); - assert_eq!(data_type.size(), 2); + assert_eq!(data_type.size(), DataTypeSize::Fixed(2)); let metadata = serde_json::from_str::("[0, 255]").unwrap(); let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); @@ -949,7 +999,7 @@ mod tests { let json = r#""r16""#; let metadata = serde_json::from_str::(json).unwrap(); let data_type: DataType = DataType::from_metadata(&metadata).unwrap(); - assert_eq!(data_type.size(), 2); + assert_eq!(data_type.size(), DataTypeSize::Fixed(2)); } #[test] @@ -960,7 +1010,7 @@ mod tests { }"#; let metadata = serde_json::from_str::(json).unwrap(); let data_type: DataType = DataType::from_metadata(&metadata).unwrap(); - assert_eq!(data_type.size(), 2); + assert_eq!(data_type.size(), DataTypeSize::Fixed(2)); } #[test] @@ -1140,4 +1190,38 @@ mod tests { .fill_value_from_metadata(&metadata) .is_err()); } + + #[test] + fn data_type_string() { + let json = r#""string""#; + let metadata: MetadataV3 = serde_json::from_str(json).unwrap(); + let data_type = DataType::from_metadata(&metadata).unwrap(); + assert_eq!(json, serde_json::to_string(&data_type.metadata()).unwrap()); + assert_eq!(data_type.identifier(), "string"); + assert_eq!(data_type.name().as_str(), "string"); + assert_eq!(data_type.size(), DataTypeSize::Variable); + + let metadata = serde_json::from_str::(r#""hello world""#).unwrap(); + let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); + assert_eq!(fill_value.as_ne_bytes(), "hello world".as_bytes(),); + assert_eq!(metadata, data_type.metadata_fill_value(&fill_value)); + + let metadata = serde_json::from_str::( + r#"[104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]"#, + ) + .unwrap(); + let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); + assert_eq!(fill_value.as_ne_bytes(), "hello world".as_bytes(),); + assert_ne!(metadata, data_type.metadata_fill_value(&fill_value)); // metadata is byte array rep, that is okay + + let metadata = serde_json::from_str::(r#""Infinity""#).unwrap(); + let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); + assert_eq!(fill_value.as_ne_bytes(), "Infinity".as_bytes(),); + assert_ne!(metadata, data_type.metadata_fill_value(&fill_value)); // metadata is float rep, that is okay + + let metadata = serde_json::from_str::(r#""0x7fc00000""#).unwrap(); + let fill_value = data_type.fill_value_from_metadata(&metadata).unwrap(); + assert_eq!(fill_value.as_ne_bytes(), "0x7fc00000".as_bytes(),); + assert_ne!(metadata, data_type.metadata_fill_value(&fill_value)); // metadata is float rep, that is okay + } } diff --git a/src/array/element.rs b/src/array/element.rs new file mode 100644 index 00000000..b4bec745 --- /dev/null +++ b/src/array/element.rs @@ -0,0 +1,267 @@ +use std::mem::ManuallyDrop; + +use itertools::Itertools; +use ArrayError::IncompatibleElementType as IET; + +use super::{convert_from_bytes_slice, transmute_to_bytes, ArrayBytes, ArrayError, DataType}; + +/// A trait representing an array element type. +pub trait Element: Sized + Clone { + /// Validate the data type. + /// + /// # Errors + /// Returns an [`ArrayError`] if the data type is incompatible with [`Element`]. + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError>; + + /// Convert a slice of elements into [`ArrayBytes`]. + /// + /// # Errors + /// Returns an [`ArrayError`] if the data type is incompatible with [`Element`]. + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError>; +} + +/// A trait representing an owned array element type. +pub trait ElementOwned: Element { + /// Convert bytes into a [`Vec`]. + /// + /// # Errors + /// Returns an [`ArrayError`] if the data type is incompatible with [`Element`]. + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError>; +} + +/// A marker trait for a fixed length element. +pub trait ElementFixedLength {} + +impl ElementFixedLength for bool {} +impl ElementFixedLength for u8 {} +impl ElementFixedLength for u16 {} +impl ElementFixedLength for u32 {} +impl ElementFixedLength for u64 {} +impl ElementFixedLength for i8 {} +impl ElementFixedLength for i16 {} +impl ElementFixedLength for i32 {} +impl ElementFixedLength for i64 {} +impl ElementFixedLength for half::f16 {} +impl ElementFixedLength for half::bf16 {} +impl ElementFixedLength for f32 {} +impl ElementFixedLength for f64 {} +impl ElementFixedLength for num::complex::Complex32 {} +impl ElementFixedLength for num::complex::Complex64 {} +impl ElementFixedLength for [u8; N] {} + +impl Element for bool { + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError> { + (data_type == &DataType::Bool).then_some(()).ok_or(IET) + } + + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + Ok(transmute_to_bytes(elements).into()) + } +} + +impl ElementOwned for bool { + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + let bytes = bytes.into_fixed()?; + let elements_u8 = convert_from_bytes_slice::(&bytes); + if elements_u8.iter().all(|&u| u <= 1) { + let length: usize = elements_u8.len(); + let capacity: usize = elements_u8.capacity(); + let mut manual_drop_vec = ManuallyDrop::new(elements_u8); + let vec_ptr: *mut u8 = manual_drop_vec.as_mut_ptr(); + let ptr: *mut Self = vec_ptr.cast::(); + Ok(unsafe { Vec::from_raw_parts(ptr, length, capacity) }) + } else { + Err(ArrayError::InvalidElementValue) + } + } +} + +macro_rules! impl_element_pod { + ($raw_type:ty, $data_type:expr) => { + impl Element for $raw_type { + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError> { + (data_type == &$data_type).then_some(()).ok_or(IET) + } + + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + Ok(transmute_to_bytes(elements).into()) + } + } + + impl ElementOwned for $raw_type { + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + let bytes = bytes.into_fixed()?; + Ok(convert_from_bytes_slice::(&bytes)) + } + } + }; +} + +impl_element_pod!(i8, DataType::Int8); +impl_element_pod!(i16, DataType::Int16); +impl_element_pod!(i32, DataType::Int32); +impl_element_pod!(i64, DataType::Int64); +impl_element_pod!(u8, DataType::UInt8); +impl_element_pod!(u16, DataType::UInt16); +impl_element_pod!(u32, DataType::UInt32); +impl_element_pod!(u64, DataType::UInt64); +impl_element_pod!(half::f16, DataType::Float16); +impl_element_pod!(f32, DataType::Float32); +impl_element_pod!(f64, DataType::Float64); +impl_element_pod!(half::bf16, DataType::BFloat16); +impl_element_pod!(num::complex::Complex32, DataType::Complex64); +impl_element_pod!(num::complex::Complex64, DataType::Complex128); + +impl Element for [u8; N] { + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError> { + if let DataType::RawBits(n) = data_type { + (*n == N).then_some(()).ok_or(IET) + } else { + Err(IET) + } + } + + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + Ok(transmute_to_bytes(elements).into()) + } +} + +impl ElementOwned for [u8; N] { + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + let bytes = bytes.into_fixed()?; + Ok(convert_from_bytes_slice::(&bytes)) + } +} + +macro_rules! impl_element_string { + ($raw_type:ty) => { + impl Element for $raw_type { + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError> { + (data_type == &DataType::String).then_some(()).ok_or(IET) + } + + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + + // Calculate offsets + let mut len: usize = 0; + let mut offsets = Vec::with_capacity(elements.len()); + for element in elements { + offsets.push(len); + len = len.checked_add(element.len()).unwrap(); + } + offsets.push(len); + + // Concatenate bytes + let mut bytes = Vec::with_capacity(usize::try_from(len).unwrap()); + for element in elements { + bytes.extend_from_slice(element.as_bytes()); + } + Ok(ArrayBytes::new_vlen(bytes, offsets)) + } + } + }; +} + +impl_element_string!(&str); +impl_element_string!(String); + +impl ElementOwned for String { + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + let (bytes, offsets) = bytes.into_variable()?; + let mut elements = Vec::with_capacity(offsets.len()); + for (curr, next) in offsets.iter().tuple_windows() { + elements.push( + String::from_utf8(bytes[*curr..*next].to_vec()) + .map_err(|_| ArrayError::InvalidElementValue)?, + ); + } + Ok(elements) + } +} + +macro_rules! impl_element_binary { + ($raw_type:ty) => { + impl Element for $raw_type { + fn validate_data_type(data_type: &DataType) -> Result<(), ArrayError> { + (data_type == &DataType::Binary).then_some(()).ok_or(IET) + } + + fn into_array_bytes<'a>( + data_type: &DataType, + elements: &'a [Self], + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + + // Calculate offsets + let mut len: usize = 0; + let mut offsets = Vec::with_capacity(elements.len()); + for element in elements { + offsets.push(len); + len = len.checked_add(element.len()).unwrap(); + } + offsets.push(len); + + // Concatenate bytes + let bytes = elements.concat(); + + Ok(ArrayBytes::new_vlen(bytes, offsets)) + } + } + }; +} + +impl_element_binary!(&[u8]); +impl_element_binary!(Vec); + +impl ElementOwned for Vec { + fn from_array_bytes( + data_type: &DataType, + bytes: ArrayBytes<'_>, + ) -> Result, ArrayError> { + Self::validate_data_type(data_type)?; + let (bytes, offsets) = bytes.into_variable()?; + let mut elements = Vec::with_capacity(offsets.len()); + for (curr, next) in offsets.iter().tuple_windows() { + elements.push(bytes[*curr..*next].to_vec()); + } + Ok(elements) + } +} diff --git a/src/array/fill_value.rs b/src/array/fill_value.rs index 6d60ec3e..70211940 100644 --- a/src/array/fill_value.rs +++ b/src/array/fill_value.rs @@ -20,6 +20,18 @@ impl From<&[u8]> for FillValue { } } +impl From<[u8; N]> for FillValue { + fn from(value: [u8; N]) -> Self { + Self(value.to_vec()) + } +} + +impl From<&[u8; N]> for FillValue { + fn from(value: &[u8; N]) -> Self { + Self(value.to_vec()) + } +} + impl From> for FillValue { fn from(value: Vec) -> Self { Self(value) @@ -122,6 +134,18 @@ impl From for FillValue { } } +impl From for FillValue { + fn from(value: String) -> Self { + Self(value.into_bytes()) + } +} + +impl From<&str> for FillValue { + fn from(value: &str) -> Self { + Self(value.as_bytes().to_vec()) + } +} + impl FillValue { /// Create a new fill value composed of `bytes`. #[must_use] @@ -146,6 +170,7 @@ impl FillValue { #[must_use] pub fn equals_all(&self, bytes: &[u8]) -> bool { match self.0.len() { + 0 => bytes.is_empty(), 1 => { let fill_value = self.0[0]; let fill_value_128 = u128::from_ne_bytes([self.0[0]; 16]); diff --git a/src/array_subset.rs b/src/array_subset.rs index e743c13b..3abc3352 100644 --- a/src/array_subset.rs +++ b/src/array_subset.rs @@ -6,8 +6,7 @@ //! [`iterators`] includes various types of [`ArraySubset`] iterators. //! //! This module also provides convenience functions for: -//! - computing the byte ranges of array subsets within an array, and -//! - extracting the bytes within subsets of an array. +//! - computing the byte ranges of array subsets within an array with a fixed element size. pub mod iterators; @@ -24,7 +23,6 @@ use thiserror::Error; use crate::{ array::{ArrayIndices, ArrayShape}, byte_range::ByteRange, - vec_spare_capacity_to_mut_slice, }; /// An array subset. @@ -39,25 +37,6 @@ pub struct ArraySubset { shape: ArrayShape, } -/// An array extract bytes error. -#[derive(Debug, Error)] -#[error("array subset {_0} is incompatible with array of shape {_1:?} and element size {_2}")] -pub struct ArrayExtractBytesError(ArraySubset, ArrayShape, usize); - -/// An array store bytes error. -#[derive(Debug, Error)] -pub enum ArrayStoreBytesError { - /// Incompatible array subset and array shape. - #[error(transparent)] - InvalidArrayShape(#[from] IncompatibleArraySubsetAndShapeError), - /// Invalid subset bytes. - #[error("expected subset bytes to have length {_1}, got {_0}")] - InvalidSubsetBytes(usize, usize), - /// Invalid array bytes. - #[error("expected array bytes to have length {_1}, got {_0}")] - InvalidArrayBytes(usize, usize), -} - impl ArraySubset { /// Create a new empty array subset. #[must_use] @@ -287,6 +266,12 @@ impl ArraySubset { usize::try_from(self.num_elements()).unwrap() } + /// Returns [`true`] if the array subset contains `indices`. + #[must_use] + pub fn contains(&self, indices: &[u64]) -> bool { + izip!(indices, &self.start, &self.shape).all(|(&i, &o, &s)| i >= o && i < o + s) + } + /// Return the byte ranges of an array subset in an array with `array_shape` and `element_size`. /// /// # Errors @@ -327,81 +312,11 @@ impl ArraySubset { byte_ranges } - /// Return the bytes in this array subset from an array with shape `array_shape` and `element_size`. - /// - /// # Errors - /// - /// Returns [`ArrayExtractBytesError`] if the length of `array_shape` does not match the array subset dimensionality or the array subset is outside of the bounds of `array_shape`. - /// - /// # Panics - /// - /// Panics if attempting to access a byte index beyond [`usize::MAX`]. - pub fn extract_bytes( - &self, - bytes: &[u8], - array_shape: &[u64], - element_size: usize, - ) -> Result, ArrayExtractBytesError> { - let element_size_u64 = element_size as u64; - if bytes.len() as u64 == array_shape.iter().product::() * element_size_u64 - && array_shape.len() == self.dimensionality() - && self - .end_exc() - .iter() - .zip(array_shape) - .all(|(end, shape)| end <= shape) - { - Ok(unsafe { self.extract_bytes_unchecked(bytes, array_shape, element_size) }) - } else { - Err(ArrayExtractBytesError( - self.clone(), - array_shape.to_vec(), - element_size, - )) - } - } - - /// Return the bytes in this array subset from an array with shape `array_shape` and `element_size`. - /// - /// # Safety - /// The length of `array_shape` must match the array subset dimensionality and the array subset must be within the bounds of `array_shape`. - /// - /// # Panics - /// Panics if attempting to reference a byte beyond `usize::MAX`. - #[must_use] - pub unsafe fn extract_bytes_unchecked( - &self, - bytes: &[u8], - array_shape: &[u64], - element_size: usize, - ) -> Vec { - debug_assert_eq!( - bytes.len() as u64, - array_shape.iter().product::() * element_size as u64 - ); - let num_bytes = self.num_elements_usize() * element_size; - let mut bytes_subset: Vec = Vec::with_capacity(num_bytes); - let bytes_subset_slice = vec_spare_capacity_to_mut_slice(&mut bytes_subset); - let mut subset_offset = 0; - let contiguous_indices = self.contiguous_linearised_indices_unchecked(array_shape); - let byte_length = contiguous_indices.contiguous_elements_usize() * element_size; - for (array_index, _contiguous_elements) in &contiguous_indices { - let byte_offset = usize::try_from(array_index).unwrap() * element_size; - debug_assert!(byte_offset + byte_length <= bytes.len()); - debug_assert!(subset_offset + byte_length <= num_bytes); - bytes_subset_slice[subset_offset..subset_offset + byte_length] - .copy_from_slice(&bytes[byte_offset..byte_offset + byte_length]); - subset_offset += byte_length; - } - unsafe { bytes_subset.set_len(num_bytes) }; - bytes_subset - } - /// Return the elements in this array subset from an array with shape `array_shape`. /// /// # Errors /// - /// Returns [`ArrayExtractBytesError`] if the length of `array_shape` does not match the array subset dimensionality or the array subset is outside of the bounds of `array_shape`. + /// Returns [`IncompatibleArraySubsetAndShapeError`] if the length of `array_shape` does not match the array subset dimensionality or the array subset is outside of the bounds of `array_shape`. /// /// # Panics /// Panics if attempting to access a byte index beyond [`usize::MAX`]. @@ -460,90 +375,6 @@ impl ArraySubset { bytes_subset } - /// Store `bytes_subset` corresponding to the bytes of an array (`array_bytes`) with shape `array_shape` and `element_size`. - /// - /// # Errors - /// - /// Returns [`ArrayStoreBytesError`] if: - /// - the length of `array_shape` does not match the array subset dimensionality or the array subset is outside of the bounds of `array_shape`. - /// - the length of `bytes_array` is not compatible with the `array_shape` and `element size`, or - /// - the length of `bytes_subset` is not compatible with the shape of this subset and `element_size`. - /// - /// # Panics - /// - /// Panics if attempting to reference a byte beyond `usize::MAX`. - pub fn store_bytes( - &self, - bytes_subset: &[u8], - bytes_array: &mut [u8], - array_shape: &[u64], - element_size: usize, - ) -> Result<(), ArrayStoreBytesError> { - let element_size_u64 = element_size as u64; - let expected_subset_size = self.num_elements() * element_size_u64; - let expected_array_size = array_shape.iter().product::() * element_size_u64; - if bytes_subset.len() as u64 != expected_subset_size { - Err(ArrayStoreBytesError::InvalidSubsetBytes( - bytes_subset.len(), - usize::try_from(expected_subset_size).unwrap(), - )) - } else if bytes_array.len() as u64 != expected_array_size { - Err(ArrayStoreBytesError::InvalidSubsetBytes( - bytes_array.len(), - usize::try_from(expected_array_size).unwrap(), - )) - } else { - let mut offset = 0; - let contiguous_indices = self.contiguous_linearised_indices(array_shape)?; - let byte_length = contiguous_indices.contiguous_elements_usize() * element_size; - for (array_index, _contiguous_elements) in &contiguous_indices { - let byte_index = usize::try_from(array_index).unwrap() * element_size; - debug_assert!(byte_index + byte_length <= bytes_array.len()); - debug_assert!(offset + byte_length <= bytes_subset.len()); - bytes_array[byte_index..byte_index + byte_length] - .copy_from_slice(&bytes_subset[offset..offset + byte_length]); - offset += byte_length; - } - Ok(()) - } - } - - /// Store `bytes_subset` corresponding to the bytes of an array (`array_bytes`) with shape `array_shape` and `element_size`. - /// - /// # Safety - /// - /// The length of `array_shape` must match the array subset dimensionality and the array subset must be within the bounds of `array_shape`. - /// The length of `bytes_array` must match the product of the `array_shape` components and `element_size`. - /// The length of `bytes_subset` must match the product of the array subset shape components and `element_size`. - /// - /// # Panics - /// - /// Panics if attempting to reference a byte beyond `usize::MAX`. - pub unsafe fn store_bytes_unchecked( - &self, - bytes_subset: &[u8], - bytes_array: &mut [u8], - array_shape: &[u64], - element_size: usize, - ) { - debug_assert_eq!(bytes_subset.len(), self.num_elements_usize() * element_size); - debug_assert_eq!( - bytes_array.len() as u64, - array_shape.iter().product::() * element_size as u64 - ); - let mut offset = 0; - let contiguous_indices = self.contiguous_linearised_indices_unchecked(array_shape); - let byte_length = contiguous_indices.contiguous_elements_usize() * element_size; - for (array_index, _contiguous_elements) in &contiguous_indices { - let byte_index = usize::try_from(array_index).unwrap() * element_size; - debug_assert!(byte_index + byte_length <= bytes_array.len()); - debug_assert!(offset + byte_length <= bytes_subset.len()); - bytes_array[byte_index..byte_index + byte_length] - .copy_from_slice(&bytes_subset[offset..offset + byte_length]); - offset += byte_length; - } - } - /// Returns an iterator over the indices of elements within the subset. #[must_use] pub fn indices(&self) -> Indices { @@ -812,28 +643,6 @@ mod tests { #[test] fn array_subset_bytes() { let array_subset = ArraySubset::new_with_ranges(&[1..3, 1..3]); - let mut bytes_array = vec![0; 4 * 4]; - array_subset - .store_bytes(&[1, 2, 3, 4], &mut bytes_array, &[4, 4], 1) - .unwrap(); - - assert!(array_subset - .store_bytes(&[1, 2, 3], &mut bytes_array, &[4, 4], 1) - .is_err()); - - assert!(array_subset - .store_bytes(&[1, 2, 3, 4], &mut bytes_array, &[2, 2], 1) - .is_err()); - - assert_eq!( - bytes_array, - vec![0, 0, 0, 0, 0, 1, 2, 0, 0, 3, 4, 0, 0, 0, 0, 0] - ); - unsafe { array_subset.store_bytes_unchecked(&[5, 6, 7, 8], &mut bytes_array, &[4, 4], 1) }; - assert_eq!( - bytes_array, - vec![0, 0, 0, 0, 0, 5, 6, 0, 0, 7, 8, 0, 0, 0, 0, 0] - ); assert!(array_subset.byte_ranges(&[1, 1], 1).is_err()); @@ -852,36 +661,5 @@ mod tests { ByteRange::FromStart(9, Some(2)) ] ); - - assert!(array_subset - .extract_bytes(&bytes_array, &[1, 1], 1) - .is_err()); - - assert_eq!( - array_subset - .extract_bytes(&bytes_array, &[4, 4], 1) - .unwrap(), - vec![5, 6, 7, 8] - ); - - assert_eq!( - unsafe { array_subset.extract_bytes_unchecked(&bytes_array, &[4, 4], 1) }, - vec![5, 6, 7, 8] - ); - - assert!(array_subset - .extract_elements(&bytes_array, &[1, 1]) - .is_err()); - - assert_eq!( - array_subset - .extract_elements(&bytes_array, &[4, 4]) - .unwrap(), - vec![5, 6, 7, 8] - ); - assert_eq!( - unsafe { array_subset.extract_elements_unchecked(&bytes_array, &[4, 4]) }, - vec![5, 6, 7, 8] - ); } } diff --git a/src/metadata/array.rs b/src/metadata/array.rs index f968e785..1573bb61 100644 --- a/src/metadata/array.rs +++ b/src/metadata/array.rs @@ -2,15 +2,18 @@ //! //! See . -use super::v2::{ - array::{ - array_metadata_fill_value_v2_to_v3, data_type_metadata_v2_to_endianness, - data_type_metadata_v2_to_v3_data_type, ArrayMetadataV2, ArrayMetadataV2DataType, - ArrayMetadataV2Order, DataTypeMetadataV2InvalidEndiannessError, FillValueMetadataV2, +pub use super::v3::ArrayMetadataV3; +use super::{ + v2::{ + array::{ + array_metadata_fill_value_v2_to_v3, data_type_metadata_v2_to_endianness, + data_type_metadata_v2_to_v3_data_type, ArrayMetadataV2, ArrayMetadataV2DataType, + ArrayMetadataV2Order, DataTypeMetadataV2InvalidEndiannessError, FillValueMetadataV2, + }, + codec::blosc::{codec_blosc_v2_numcodecs_to_v3, BloscCodecConfigurationNumcodecs}, }, - codec::blosc::{codec_blosc_v2_numcodecs_to_v3, BloscCodecConfigurationNumcodecs}, + v3::codec::vlen_interleaved::VlenInterleavedCodecConfigurationV1, }; -pub use super::v3::ArrayMetadataV3; use thiserror::Error; use derive_more::{Display, From}; @@ -138,22 +141,37 @@ pub fn array_metadata_v2_to_v3( codecs.push(transpose_metadata); } - // Filters (array to array codecs) + // Filters (array to array or array to bytes codecs) + let mut is_vlen = false; if let Some(filters) = &array_metadata_v2.filters { for filter in filters { - codecs.push(MetadataV3::new_with_configuration( - filter.id(), - filter.configuration().clone(), - )); + match filter.id() { + "vlen-utf8" | "vlen-bytes" | "vlen-array" => { + is_vlen = true; + let vlen_interleaved_metadata = + MetadataV3::new_with_serializable_configuration( + super::v3::codec::vlen_interleaved::IDENTIFIER, + &VlenInterleavedCodecConfigurationV1 {}, + )?; + codecs.push(vlen_interleaved_metadata); + } + _ => { + codecs.push(MetadataV3::new_with_configuration( + filter.id(), + filter.configuration().clone(), + )); + } + } } } - // Array-to-bytes codec - let bytes_metadata = MetadataV3::new_with_serializable_configuration( - super::v3::codec::bytes::IDENTIFIER, - &BytesCodecConfigurationV1 { endian: endianness }, - )?; - codecs.push(bytes_metadata); + if !is_vlen { + let bytes_metadata = MetadataV3::new_with_serializable_configuration( + super::v3::codec::bytes::IDENTIFIER, + &BytesCodecConfigurationV1 { endian: endianness }, + )?; + codecs.push(bytes_metadata); + } // Compressor (bytes to bytes codec) if let Some(compressor) = &array_metadata_v2.compressor { diff --git a/src/metadata/v2/array.rs b/src/metadata/v2/array.rs index 1e6426d2..c1872b51 100644 --- a/src/metadata/v2/array.rs +++ b/src/metadata/v2/array.rs @@ -159,7 +159,12 @@ pub fn data_type_metadata_v2_to_v3_data_type( "f8" => Ok(DataType::Float64), "c8" => Ok(DataType::Complex64), "c16" => Ok(DataType::Complex128), - // TODO "|V" + "|O" => Ok(DataType::String), // LEGACY: This is not part of the spec. The dtype for a PyObject, which is what zarr-python 2 uses for string arrays. + // TODO "|mX" timedelta + // TODO "|MX" datetime + // TODO "|SX" string (fixed length sequence of char) + // TODO "|UX" string (fixed length sequence of Py_UNICODE) + // TODO "|VX" other (void * – each item is a fixed-size chunk of memory) _ => Err(DataTypeMetadataV2UnsupportedDataTypeError( data_type.clone(), )), diff --git a/src/metadata/v2/codec/blosc.rs b/src/metadata/v2/codec/blosc.rs index 2d9b6b85..4fd14f8c 100644 --- a/src/metadata/v2/codec/blosc.rs +++ b/src/metadata/v2/codec/blosc.rs @@ -46,22 +46,42 @@ pub fn codec_blosc_v2_numcodecs_to_v3( blosc: &BloscCodecConfigurationNumcodecs, data_type: &DataType, ) -> BloscCodecConfiguration { + let (shuffle, typesize) = match (&blosc.shuffle, data_type.fixed_size()) { + (BloscShuffleModeNumCodecs::NoShuffle, _) => (BloscShuffleMode::NoShuffle, None), + // Fixed + (BloscShuffleModeNumCodecs::Shuffle, Some(data_type_size)) => { + (BloscShuffleMode::Shuffle, Some(data_type_size)) + } + (BloscShuffleModeNumCodecs::BitShuffle, Some(data_type_size)) => { + (BloscShuffleMode::BitShuffle, Some(data_type_size)) + } + (BloscShuffleModeNumCodecs::AutoShuffle, Some(data_type_size)) => { + if data_type_size == 1 { + (BloscShuffleMode::BitShuffle, Some(data_type_size)) + } else { + (BloscShuffleMode::Shuffle, Some(data_type_size)) + } + } + // Variable + ( + BloscShuffleModeNumCodecs::Shuffle + | BloscShuffleModeNumCodecs::BitShuffle + | BloscShuffleModeNumCodecs::AutoShuffle, + None, + ) => { + // FIXME: Check blosc auto behaviour with variable sized data type + // Currently defaulting to "bitshuffle" + // What do other implementations do for a variable sized data type? + // May need to make this function fallible + (BloscShuffleMode::NoShuffle, None) + } + }; + BloscCodecConfiguration::V1(BloscCodecConfigurationV1 { cname: blosc.cname, clevel: blosc.clevel, - shuffle: match blosc.shuffle { - BloscShuffleModeNumCodecs::NoShuffle => BloscShuffleMode::NoShuffle, - BloscShuffleModeNumCodecs::Shuffle => BloscShuffleMode::Shuffle, - BloscShuffleModeNumCodecs::BitShuffle => BloscShuffleMode::BitShuffle, - BloscShuffleModeNumCodecs::AutoShuffle => { - if data_type.size() == 1 { - BloscShuffleMode::BitShuffle - } else { - BloscShuffleMode::Shuffle - } - } - }, - typesize: Some(data_type.size()), + shuffle, + typesize, blocksize: blosc.blocksize, }) } diff --git a/src/metadata/v3.rs b/src/metadata/v3.rs index 396957b7..27b908d5 100644 --- a/src/metadata/v3.rs +++ b/src/metadata/v3.rs @@ -27,6 +27,10 @@ pub mod codec { pub mod sharding; /// `transpose` codec metadata. pub mod transpose; + /// `vlen` codec metadata. + pub mod vlen; + /// `vlen_interleaved` codec metadata. + pub mod vlen_interleaved; #[cfg(feature = "zfp")] /// `zfp` codec metadata. pub mod zfp; diff --git a/src/metadata/v3/codec/vlen.rs b/src/metadata/v3/codec/vlen.rs new file mode 100644 index 00000000..4244f1db --- /dev/null +++ b/src/metadata/v3/codec/vlen.rs @@ -0,0 +1,84 @@ +use derive_more::{Display, From}; +use serde::{Deserialize, Serialize}; + +use crate::metadata::MetadataV3; + +/// The identifier for the `vlen` codec. +pub const IDENTIFIER: &str = "https://codec.zarrs.dev/array_to_bytes/vlen"; + +/// A wrapper to handle various versions of `vlen` codec configuration parameters. +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)] +#[serde(untagged)] +pub enum VlenCodecConfiguration { + /// Version 1.0. + V1(VlenCodecConfigurationV1), +} + +/// Configuration parameters for the `vlen` codec (version 1.0). +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display)] +#[serde(deny_unknown_fields)] +#[display(fmt = "{}", "serde_json::to_string(self).unwrap_or_default()")] +pub struct VlenCodecConfigurationV1 { + /// Encoding for the variable length data indices (offsets). + pub index_codecs: Vec, + /// Encoding for the variable length data. + pub data_codecs: Vec, + /// The indices data type. + pub index_data_type: VlenIndexDataType, +} + +/// Data types for variable length chunk data indices. +#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Debug, Display)] +#[serde(rename_all = "lowercase")] +pub enum VlenIndexDataType { + // /// `uint8` Integer in `[0, 2^8-1]`. + // UInt8, + // /// `uint16` Integer in `[0, 2^16-1]`. + // UInt16, + /// `uint32` Integer in `[0, 2^32-1]`. + UInt32, + /// `uint64` Integer in `[0, 2^64-1]`. + UInt64, +} + +impl VlenCodecConfigurationV1 { + /// Create a new `vlen` codec configuration. + #[must_use] + pub fn new( + index_codecs: Vec, + data_codecs: Vec, + index_data_type: VlenIndexDataType, + ) -> Self { + Self { + index_codecs, + data_codecs, + index_data_type, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn codec_vlen_simple() { + serde_json::from_str::( + r#"{ + "data_codecs": [{"name": "bytes"}], + "index_codecs": [{"name": "bytes","configuration": { "endian": "little" }}], + "index_data_type": "uint32" + }"#, + ) + .unwrap(); + } + + #[test] + fn codec_vlen_compressed() { + serde_json::from_str::(r#"{ + "data_codecs": [{"name": "bytes"},{"name": "blosc","configuration": {"cname": "zstd", "clevel":5,"shuffle": "bitshuffle", "typesize":1,"blocksize":0}}], + "index_codecs": [{"name": "bytes","configuration": { "endian": "little" }},{"name": "blosc","configuration":{"cname": "zstd", "clevel":5,"shuffle": "shuffle", "typesize":4,"blocksize":0}}], + "index_data_type": "uint32" + }"#).unwrap(); + } +} diff --git a/src/metadata/v3/codec/vlen_interleaved.rs b/src/metadata/v3/codec/vlen_interleaved.rs new file mode 100644 index 00000000..fa19a401 --- /dev/null +++ b/src/metadata/v3/codec/vlen_interleaved.rs @@ -0,0 +1,37 @@ +use derive_more::{Display, From}; +use serde::{Deserialize, Serialize}; + +/// The identifier for the `vlen_interleaved` codec. +pub const IDENTIFIER: &str = "https://codec.zarrs.dev/array_to_bytes/vlen_interleaved"; + +/// A wrapper to handle various versions of `vlen_interleaved` codec configuration parameters. +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)] +#[serde(untagged)] +pub enum VlenInterleavedCodecConfiguration { + /// Version 1.0. + V1(VlenInterleavedCodecConfigurationV1), +} + +/// Configuration parameters for the `vlen_interleaved` codec (version 1.0). +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, Default)] +#[serde(deny_unknown_fields)] +#[display(fmt = "{}", "serde_json::to_string(self).unwrap_or_default()")] +pub struct VlenInterleavedCodecConfigurationV1 {} + +impl VlenInterleavedCodecConfigurationV1 { + /// Create a new `vlen_interleaved` codec configuration. + #[must_use] + pub const fn new() -> Self { + Self {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn codec_vlen_interleaved() { + serde_json::from_str::(r#"{}"#).unwrap(); + } +} diff --git a/src/metadata/v3/fill_value.rs b/src/metadata/v3/fill_value.rs index 44dee963..7d5b7957 100644 --- a/src/metadata/v3/fill_value.rs +++ b/src/metadata/v3/fill_value.rs @@ -26,12 +26,16 @@ pub enum FillValueMetadata { Int(i64), /// A float. Float(FillValueFloat), - /// A raw data type. + /// An array of integers. Suitable for raw (`r`) and `binary` data types. #[display(fmt = "{_0:?}")] ByteArray(Vec), /// A complex number. #[display(fmt = "{{re:{_0}, im:{_1}}}")] Complex(FillValueFloat, FillValueFloat), + /// A string. + String(String), + /// An unsupported fill value. + Unsupported(serde_json::Value), } impl TryFrom<&str> for FillValueMetadata { @@ -103,6 +107,12 @@ impl HexString { } } +impl From<&HexString> for String { + fn from(value: &HexString) -> Self { + bytes_to_hex_string(value.as_be_bytes()) + } +} + impl core::fmt::Display for HexString { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}", self.0) @@ -162,6 +172,17 @@ pub enum FillValueFloatStringNonFinite { NaN, } +impl From<&FillValueFloatStringNonFinite> for String { + fn from(value: &FillValueFloatStringNonFinite) -> Self { + match value { + FillValueFloatStringNonFinite::PosInfinity => "Infinity", + FillValueFloatStringNonFinite::NegInfinity => "-Infinity", + FillValueFloatStringNonFinite::NaN => "NaN", + } + .to_string() + } +} + impl FillValueMetadata { /// Convert the fill value to a [`bool`]. #[must_use] @@ -546,4 +567,58 @@ mod tests { _ => unreachable!(), } } + + // Null is not currently supported, so recognise it as unknown fill value metadata + #[test] + fn fill_value_metadata_null() { + let json = r#"null"#; + let metadata: FillValueMetadata = json.try_into().unwrap(); + assert_eq!(json, serde_json::to_string(&metadata).unwrap()); + match metadata { + FillValueMetadata::Unsupported(fill_value) => { + assert!(fill_value.is_null()) + } + _ => unreachable!(), + } + } + + // A negative single byte, so recognise it as unknown fill value metadata + #[test] + fn fill_value_metadata_neg_array1() { + let json = r#"[-5]"#; + let metadata: FillValueMetadata = json.try_into().unwrap(); + assert_eq!(json, serde_json::to_string(&metadata).unwrap()); + match metadata { + FillValueMetadata::Unsupported(fill_value) => { + assert!(fill_value.is_array()) + } + _ => unreachable!(), + } + } + + // Two negative -> complex + #[test] + fn fill_value_metadata_neg_array2() { + let json = r#"[-5, -5]"#; + let metadata: FillValueMetadata = json.try_into().unwrap(); + assert_ne!(json, serde_json::to_string(&metadata).unwrap()); // [-5.0, -5.0] + match metadata { + FillValueMetadata::Complex(_re, _im) => {} + _ => unreachable!(), + } + } + + // Single array element > u8::MAX is currently unknown + #[test] + fn fill_value_metadata_large_array() { + let json = r#"[256]"#; + let metadata: FillValueMetadata = json.try_into().unwrap(); + assert_eq!(json, serde_json::to_string(&metadata).unwrap()); + match metadata { + FillValueMetadata::Unsupported(fill_value) => { + assert!(fill_value.is_array()) + } + _ => unreachable!(), + } + } } diff --git a/src/storage/storage_async.rs b/src/storage/storage_async.rs index f014ca05..e4164261 100644 --- a/src/storage/storage_async.rs +++ b/src/storage/storage_async.rs @@ -313,7 +313,7 @@ where { let prefixes = async_discover_children(storage, path).await?; let mut nodes: Vec = Vec::new(); - // FIXME: Asynchronously get metadata of all prefixes + // TODO: Asynchronously get metadata of all prefixes for prefix in &prefixes { let key = meta_key(&prefix.try_into()?); let child_metadata = match storage.get(&key).await? { diff --git a/src/storage/store/store_async/opendal.rs b/src/storage/store/store_async/opendal.rs index b0d6a1b3..abb4e010 100644 --- a/src/storage/store/store_async/opendal.rs +++ b/src/storage/store/store_async/opendal.rs @@ -63,7 +63,7 @@ impl AsyncReadableStorageTraits for AsyncOpendalStore { key: &StoreKey, byte_ranges: &[ByteRange], ) -> Result>, StorageError> { - // FIXME: Get OpenDAL to return an error if byte range is OOB instead of panic, then don't need to query size + // TODO: Get OpenDAL to return an error if byte range is OOB instead of panic, then don't need to query size let (size, reader) = futures::join!(self.size_key(key), self.operator.reader(key.as_str())); if let (Some(size), Some(reader)) = (size?, handle_result(reader)?) { let mut byte_ranges_fetch = Vec::with_capacity(byte_ranges.len()); diff --git a/src/storage/store/store_sync/opendal.rs b/src/storage/store/store_sync/opendal.rs index 46392e5d..de2d66d0 100644 --- a/src/storage/store/store_sync/opendal.rs +++ b/src/storage/store/store_sync/opendal.rs @@ -58,7 +58,7 @@ impl ReadableStorageTraits for OpendalStore { key: &StoreKey, byte_ranges: &[ByteRange], ) -> Result>, StorageError> { - // FIXME: Get OpenDAL to return an error if byte range is OOB instead of panic + // TODO: Get OpenDAL to return an error if byte range is OOB instead of panic let size = self.size_key(key)?; if let Some(size) = size { let reader = self.operator.reader(key.as_str())?; diff --git a/tests/array_async.rs b/tests/array_async.rs index 777108f2..3129755d 100644 --- a/tests/array_async.rs +++ b/tests/array_async.rs @@ -1,10 +1,13 @@ #![cfg(all(feature = "async", feature = "ndarray", feature = "object_store"))] -use zarrs::array::{ArrayBuilder, DataType, FillValue}; +use zarrs::array::codec::array_to_bytes::vlen::VlenCodec; +use zarrs::array::codec::TransposeCodec; +use zarrs::array::{Array, ArrayBuilder, DataType, FillValue}; use zarrs::array_subset::ArraySubset; use object_store::memory::InMemory; +use zarrs::metadata::v3::codec::transpose::TransposeOrder; use zarrs::storage::store::AsyncObjectStore; #[rustfmt::skip] @@ -48,15 +51,15 @@ async fn array_async_read(shard: bool) -> Result<(), Box> array.async_store_array_subset(&ArraySubset::new_with_ranges(&[1..3, 0..2]), &[5, 6, 9, 10]).await?; assert!(array.async_retrieve_chunk(&[0, 0, 0]).await.is_err()); - assert_eq!(array.async_retrieve_chunk(&[0, 0]).await?, [1, 2, 5, 6]); - assert_eq!(array.async_retrieve_chunk(&[0, 1]).await?, [3, 4, 7, 8]); - assert_eq!(array.async_retrieve_chunk(&[1, 0]).await?, [9, 10, 0, 0]); - assert_eq!(array.async_retrieve_chunk(&[1, 1]).await?, [0, 0, 0, 0]); + assert_eq!(array.async_retrieve_chunk(&[0, 0]).await?, vec![1, 2, 5, 6].into()); + assert_eq!(array.async_retrieve_chunk(&[0, 1]).await?, vec![3, 4, 7, 8].into()); + assert_eq!(array.async_retrieve_chunk(&[1, 0]).await?, vec![9, 10, 0, 0].into()); + assert_eq!(array.async_retrieve_chunk(&[1, 1]).await?, vec![0, 0, 0, 0].into()); assert!(array.async_retrieve_chunk_if_exists(&[0, 0, 0]).await.is_err()); - assert_eq!(array.async_retrieve_chunk_if_exists(&[0, 0]).await?, Some(vec![1, 2, 5, 6])); - assert_eq!(array.async_retrieve_chunk_if_exists(&[0, 1]).await?, Some(vec![3, 4, 7, 8])); - assert_eq!(array.async_retrieve_chunk_if_exists(&[1, 0]).await?, Some(vec![9, 10, 0, 0])); + assert_eq!(array.async_retrieve_chunk_if_exists(&[0, 0]).await?, Some(vec![1, 2, 5, 6].into())); + assert_eq!(array.async_retrieve_chunk_if_exists(&[0, 1]).await?, Some(vec![3, 4, 7, 8].into())); + assert_eq!(array.async_retrieve_chunk_if_exists(&[1, 0]).await?, Some(vec![9, 10, 0, 0].into())); assert_eq!(array.async_retrieve_chunk_if_exists(&[1, 1]).await?, None); assert!(array.async_retrieve_chunk_ndarray::(&[0, 0]).await.is_err()); @@ -72,9 +75,9 @@ async fn array_async_read(shard: bool) -> Result<(), Box> assert!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..2])).await.is_err()); assert!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..3, 0..3])).await.is_err()); - assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, [1, 2, 5, 6]); - assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..1, 0..2])).await?, [1, 2]); - assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 1..2])).await?, [2, 6]); + assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, vec![1, 2, 5, 6].into()); + assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..1, 0..2])).await?, vec![1, 2].into()); + assert_eq!(array.async_retrieve_chunk_subset(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 1..2])).await?, vec![2, 6].into()); assert!(array.async_retrieve_chunk_subset_ndarray::(&[0, 0], &ArraySubset::new_with_ranges(&[0..3, 0..3])).await.is_err()); assert!(array.async_retrieve_chunk_subset_ndarray::(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 0..2])).await.is_err()); @@ -83,11 +86,11 @@ async fn array_async_read(shard: bool) -> Result<(), Box> assert_eq!(array.async_retrieve_chunk_subset_ndarray::(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 1..2])).await?, ndarray::array![[2], [6]].into_dyn()); assert!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2])).await.is_err()); - assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..0, 0..0])).await?, Vec::::new()); - assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 0..1])).await?, [1, 2, 5, 6]); - assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0]); - assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 1..2])).await?, [3, 4, 7, 8, 0, 0, 0, 0]); - assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 1..3])).await?, [3, 4, 0, 0, 7, 8, 0, 0]); + assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..0, 0..0])).await?, vec![].into()); + assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 0..1])).await?, vec![1, 2, 5, 6].into()); + assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0].into()); + assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 1..2])).await?, vec![3, 4, 7, 8, 0, 0, 0, 0].into()); + assert_eq!(array.async_retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 1..3])).await?, vec![3, 4, 0, 0, 7, 8, 0, 0].into()); assert!(array.async_retrieve_chunks_ndarray::(&ArraySubset::new_with_ranges(&[0..2])).await.is_err()); assert!(array.async_retrieve_chunks_ndarray::(&ArraySubset::new_with_ranges(&[0..2, 0..2])).await.is_err()); @@ -96,13 +99,12 @@ async fn array_async_read(shard: bool) -> Result<(), Box> assert_eq!(array.async_retrieve_chunks_ndarray::(&ArraySubset::new_with_ranges(&[0..1, 1..3])).await?, ndarray::array![[3, 4, 0, 0], [7, 8, 0, 0]].into_dyn()); assert!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4])).await.is_err()); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0])).await?, Vec::::new()); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0])).await?, [] as [u8; 0]); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, [1, 2, 5, 6]); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4, 0..4])).await?, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0]); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[1..3, 1..3])).await?, [6, 7, 10 ,0]); - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[5..7, 5..6])).await?, [0, 0]); // OOB -> fill value - assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..5, 0..5])).await?, [1, 2, 3, 4, 0, 5, 6, 7, 8, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // OOB -> fill value + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0])).await?, vec![].into()); + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..2, 0..2])).await?, vec![1, 2, 5, 6].into()); + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4, 0..4])).await?, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0].into()); + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[1..3, 1..3])).await?, vec![6, 7, 10 ,0].into()); + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[5..7, 5..6])).await?, vec![0, 0].into()); // OOB -> fill value + assert_eq!(array.async_retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..5, 0..5])).await?, vec![1, 2, 3, 4, 0, 5, 6, 7, 8, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into()); // OOB -> fill value assert!(array.async_retrieve_array_subset_ndarray::(&ArraySubset::new_with_ranges(&[0..4])).await.is_err()); assert!(array.async_retrieve_array_subset_ndarray::(&ArraySubset::new_with_ranges(&[0..4, 0..4])).await.is_err()); @@ -114,9 +116,9 @@ async fn array_async_read(shard: bool) -> Result<(), Box> assert!(array.async_partial_decoder(&[0]).await.is_err()); assert!(array.async_partial_decoder(&[0, 0]).await?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1])]).await.is_err()); - assert_eq!(array.async_partial_decoder(&[0, 0]).await?.partial_decode(&[]).await?, Vec::>::new()); - assert_eq!(array.async_partial_decoder(&[5, 0]).await?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2])]).await?, [vec![0, 0]]); // OOB -> fill value - assert_eq!(array.async_partial_decoder(&[0, 0]).await?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2]), ArraySubset::new_with_ranges(&[0..2, 1..2])]).await?, [vec![1, 2], vec![2, 6]]); + assert_eq!(array.async_partial_decoder(&[0, 0]).await?.partial_decode(&[]).await?, []); + assert_eq!(array.async_partial_decoder(&[5, 0]).await?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2])]).await?, [vec![0, 0].into()]); // OOB -> fill value + assert_eq!(array.async_partial_decoder(&[0, 0]).await?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2]), ArraySubset::new_with_ranges(&[0..2, 1..2])]).await?, [vec![1, 2].into(), vec![2, 6].into()]); Ok(()) } @@ -132,3 +134,183 @@ async fn array_async_read_uncompressed() -> Result<(), Box Result<(), Box> { array_async_read(true).await } + +async fn array_str_impl( + array: Array>, +) -> Result<(), Box> { + // Store a single chunk + array + .async_store_chunk_elements(&[0, 0], &["a", "bb", "ccc", "dddd"]) + .await?; + assert_eq!( + array + .async_retrieve_chunk_elements::(&[0, 0]) + .await?, + &["a", "bb", "ccc", "dddd"] + ); + + // Write array subset with full chunks + array + .async_store_array_subset_elements( + &ArraySubset::new_with_ranges(&[2..4, 0..4]), + &[ + "1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", + ], + ) + .await?; + assert_eq!( + array + .async_retrieve_chunk_elements::(&[1, 0]) + .await?, + &["1", "22", "55555", "666666"] + ); + assert_eq!( + array + .async_retrieve_chunk_elements::(&[1, 1]) + .await?, + &["333", "4444", "7777777", "88888888"] + ); + + // Write array subset with partial chunks + array + .async_store_array_subset_elements( + &ArraySubset::new_with_ranges(&[1..3, 1..3]), + &["S1", "S22", "S333", "S4444"], + ) + .await?; + assert_eq!( + array + .async_retrieve_chunk_elements::(&[0, 0]) + .await?, + &["a", "bb", "ccc", "S1"] + ); + assert_eq!( + array + .async_retrieve_chunk_elements::(&[0, 1]) + .await?, + &["", "", "S22", ""] + ); + assert_eq!( + array + .async_retrieve_chunk_elements::(&[1, 0]) + .await?, + &["1", "S333", "55555", "666666"] + ); + assert_eq!( + array + .async_retrieve_chunk_elements::(&[1, 1]) + .await?, + &["S4444", "4444", "7777777", "88888888"] + ); + + // Write multiple chunks + array + .async_store_chunks_elements( + &ArraySubset::new_with_ranges(&[0..1, 0..2]), + &["a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333"], + ) + .await?; + assert_eq!( + array + .async_retrieve_chunk_elements::(&[0, 0]) + .await?, + &["a", "bb", "C0", "C11"] + ); + assert_eq!( + array + .async_retrieve_chunk_elements::(&[0, 1]) + .await?, + &["ccc", "dddd", "C222", "C3333"] + ); + assert_eq!( + array + .async_retrieve_chunks_elements::(&ArraySubset::new_with_ranges(&[0..1, 0..2])) + .await?, + &["a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333"] + ); + + // Full chunk requests + assert_eq!( + array + .async_retrieve_array_subset_elements::(&ArraySubset::new_with_ranges(&[ + 0..4, + 0..4 + ])) + .await?, + &[ + "a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333", // + "1", "S333", "S4444", "4444", "55555", "666666", "7777777", "88888888" // + ] + ); + + // Partial chunk requests + assert_eq!( + array + .async_retrieve_array_subset_elements::(&ArraySubset::new_with_ranges(&[ + 1..3, + 1..3 + ])) + .await?, + &["C11", "C222", "S333", "S4444"] + ); + + // Incompatible chunks / bytes + assert!(array + .async_store_chunks_elements(&ArraySubset::new_with_ranges(&[0..0, 0..2]), &["a", "bb"]) + .await + .is_err()); + assert!(array + .async_store_chunks_elements(&ArraySubset::new_with_ranges(&[0..1, 0..2]), &["a", "bb"]) + .await + .is_err()); + + Ok(()) +} + +#[tokio::test] +async fn array_str_async_simple() -> Result<(), Box> { + let store = std::sync::Arc::new(AsyncObjectStore::new(InMemory::new())); + let array_path = "/array"; + let mut builder = ArrayBuilder::new( + vec![4, 4], // array shape + DataType::String, + vec![2, 2].try_into().unwrap(), // regular chunk shape + FillValue::from(""), + ); + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "gzip")] + Box::new(zarrs::array::codec::GzipCodec::new(5)?), + ]); + + let array = builder.build(store, array_path).unwrap(); + array_str_impl(array).await +} + +#[tokio::test] +async fn array_str_async_sharded_transpose() -> Result<(), Box> { + let store = std::sync::Arc::new(AsyncObjectStore::new(InMemory::new())); + let array_path = "/array"; + let mut builder = ArrayBuilder::new( + vec![4, 4], // array shape + DataType::String, + vec![2, 2].try_into().unwrap(), // regular chunk shape + FillValue::from(""), + ); + builder.array_to_array_codecs(vec![Box::new(TransposeCodec::new( + TransposeOrder::new(&[1, 0]).unwrap(), + ))]); + builder.array_to_bytes_codec(Box::new( + zarrs::array::codec::array_to_bytes::sharding::ShardingCodecBuilder::new( + vec![2, 1].try_into().unwrap(), + ) + .array_to_bytes_codec(Box::::default()) + .build(), + )); + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "gzip")] + Box::new(zarrs::array::codec::GzipCodec::new(5)?), + ]); + + let array = builder.build(store, array_path).unwrap(); + array_str_impl(array).await +} diff --git a/tests/array_sync.rs b/tests/array_sync.rs index 5ddcd859..b1263afc 100644 --- a/tests/array_sync.rs +++ b/tests/array_sync.rs @@ -22,15 +22,15 @@ fn array_sync_read(array: Array) -> Result<(), Box(&[0, 0]).is_err()); @@ -46,9 +46,9 @@ fn array_sync_read(array: Array) -> Result<(), Box(&[0, 0], &ArraySubset::new_with_ranges(&[0..3, 0..3])).is_err()); assert!(array.retrieve_chunk_subset_ndarray::(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 0..2])).is_err()); @@ -57,11 +57,11 @@ fn array_sync_read(array: Array) -> Result<(), Box(&[0, 0], &ArraySubset::new_with_ranges(&[0..2, 1..2]))?, ndarray::array![[2], [6]].into_dyn()); assert!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2])).is_err()); - assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..0, 0..0]))?, Vec::::new()); - assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 0..1]))?, [1, 2, 5, 6]); - assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 0..2]))?, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0]); - assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 1..2]))?, [3, 4, 7, 8, 0, 0, 0, 0]); - assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 1..3]))?, [3, 4, 0, 0, 7, 8, 0, 0]); + assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..0, 0..0]))?, vec![].into()); + assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 0..1]))?, vec![1, 2, 5, 6].into()); + assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 0..2]))?, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0].into()); + assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..2, 1..2]))?, vec![3, 4, 7, 8, 0, 0, 0, 0].into()); + assert_eq!(array.retrieve_chunks(&ArraySubset::new_with_ranges(&[0..1, 1..3]))?, vec![3, 4, 0, 0, 7, 8, 0, 0].into()); assert!(array.retrieve_chunks_ndarray::(&ArraySubset::new_with_ranges(&[0..2])).is_err()); assert!(array.retrieve_chunks_ndarray::(&ArraySubset::new_with_ranges(&[0..2, 0..2])).is_err()); @@ -70,13 +70,12 @@ fn array_sync_read(array: Array) -> Result<(), Box(&ArraySubset::new_with_ranges(&[0..1, 1..3]))?, ndarray::array![[3, 4, 0, 0], [7, 8, 0, 0]].into_dyn()); assert!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4])).is_err()); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0]))?, Vec::::new()); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0]))?, [] as [u8; 0]); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..2, 0..2]))?, [1, 2, 5, 6]); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4, 0..4]))?, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0]); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[1..3, 1..3]))?, [6, 7, 10 ,0]); - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[5..7, 5..6]))?, [0, 0]); // OOB -> fill value - assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..5, 0..5]))?, [1, 2, 3, 4, 0, 5, 6, 7, 8, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); // OOB -> fill value + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..0, 0..0]))?, vec![].into()); + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..2, 0..2]))?, vec![1, 2, 5, 6].into()); + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..4, 0..4]))?, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0].into()); + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[1..3, 1..3]))?, vec![6, 7, 10 ,0].into()); + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[5..7, 5..6]))?, vec![0, 0].into()); // OOB -> fill value + assert_eq!(array.retrieve_array_subset(&ArraySubset::new_with_ranges(&[0..5, 0..5]))?, vec![1, 2, 3, 4, 0, 5, 6, 7, 8, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into()); // OOB -> fill value assert!(array.retrieve_array_subset_ndarray::(&ArraySubset::new_with_ranges(&[0..4])).is_err()); assert!(array.retrieve_array_subset_ndarray::(&ArraySubset::new_with_ranges(&[0..4, 0..4])).is_err()); @@ -88,9 +87,9 @@ fn array_sync_read(array: Array) -> Result<(), Box>::new()); - assert_eq!(array.partial_decoder(&[5, 0])?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2])])?, [vec![0, 0]]); // OOB -> fill value - assert_eq!(array.partial_decoder(&[0, 0])?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2]), ArraySubset::new_with_ranges(&[0..2, 1..2])])?, [vec![1, 2], vec![2, 6]]); + assert_eq!(array.partial_decoder(&[0, 0])?.partial_decode(&[])?, []); + assert_eq!(array.partial_decoder(&[5, 0])?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2])])?, [vec![0, 0].into()]); // OOB -> fill value + assert_eq!(array.partial_decoder(&[0, 0])?.partial_decode(&[ArraySubset::new_with_ranges(&[0..1, 0..2]), ArraySubset::new_with_ranges(&[0..2, 1..2])])?, [vec![1, 2].into(), vec![2, 6].into()]); Ok(()) } @@ -122,6 +121,7 @@ fn array_sync_read_uncompressed() -> Result<(), Box> { array_sync_read(array) } +#[cfg(feature = "sharding")] #[test] #[cfg_attr(miri, ignore)] fn array_sync_read_shard_compress() -> Result<(), Box> { @@ -133,7 +133,6 @@ fn array_sync_read_shard_compress() -> Result<(), Box> { vec![2, 2].try_into().unwrap(), // regular chunk shape FillValue::from(0u8), ); - #[cfg(feature = "sharding")] builder.array_to_bytes_codec(Box::new( zarrs::array::codec::array_to_bytes::sharding::ShardingCodecBuilder::new( vec![1, 1].try_into().unwrap(), @@ -159,3 +158,216 @@ fn array_sync_read_shard_compress() -> Result<(), Box> { array_sync_read(array) } + +fn array_str_impl(array: Array) -> Result<(), Box> { + // Store a single chunk + array.store_chunk_elements(&[0, 0], &["a", "bb", "ccc", "dddd"])?; + assert_eq!( + array.retrieve_chunk_elements::(&[0, 0])?, + &["a", "bb", "ccc", "dddd"] + ); + + // Write array subset with full chunks + array.store_array_subset_elements( + &ArraySubset::new_with_ranges(&[2..4, 0..4]), + &[ + "1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", + ], + )?; + assert_eq!( + array.retrieve_chunk_elements::(&[1, 0])?, + &["1", "22", "55555", "666666"] + ); + assert_eq!( + array.retrieve_chunk_elements::(&[1, 1])?, + &["333", "4444", "7777777", "88888888"] + ); + + // Write array subset with partial chunks + array.store_array_subset_elements( + &ArraySubset::new_with_ranges(&[1..3, 1..3]), + &["S1", "S22", "S333", "S4444"], + )?; + assert_eq!( + array.retrieve_chunk_elements::(&[0, 0])?, + &["a", "bb", "ccc", "S1"] + ); + assert_eq!( + array.retrieve_chunk_elements::(&[0, 1])?, + &["", "", "S22", ""] + ); + assert_eq!( + array.retrieve_chunk_elements::(&[1, 0])?, + &["1", "S333", "55555", "666666"] + ); + assert_eq!( + array.retrieve_chunk_elements::(&[1, 1])?, + &["S4444", "4444", "7777777", "88888888"] + ); + + // Write multiple chunks + array.store_chunks_elements( + &ArraySubset::new_with_ranges(&[0..1, 0..2]), + &["a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333"], + )?; + assert_eq!( + array.retrieve_chunk_elements::(&[0, 0])?, + &["a", "bb", "C0", "C11"] + ); + assert_eq!( + array.retrieve_chunk_elements::(&[0, 1])?, + &["ccc", "dddd", "C222", "C3333"] + ); + assert_eq!( + array.retrieve_chunks_elements::(&ArraySubset::new_with_ranges(&[0..1, 0..2]))?, + &["a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333"] + ); + + // Full chunk requests + assert_eq!( + array.retrieve_array_subset_elements::(&ArraySubset::new_with_ranges(&[ + 0..4, + 0..4 + ]))?, + &[ + "a", "bb", "ccc", "dddd", "C0", "C11", "C222", "C3333", // + "1", "S333", "S4444", "4444", "55555", "666666", "7777777", "88888888" // + ] + ); + + // Partial chunk requests + assert_eq!( + array.retrieve_array_subset_elements::(&ArraySubset::new_with_ranges(&[ + 1..3, + 1..3 + ]))?, + &["C11", "C222", "S333", "S4444"] + ); + + // Incompatible chunks / bytes + assert!(array + .store_chunks_elements(&ArraySubset::new_with_ranges(&[0..0, 0..2]), &["a", "bb"]) + .is_err()); + assert!(array + .store_chunks_elements(&ArraySubset::new_with_ranges(&[0..1, 0..2]), &["a", "bb"]) + .is_err()); + + Ok(()) +} + +#[test] +fn array_str_sync_simple() -> Result<(), Box> { + let store = std::sync::Arc::new(MemoryStore::default()); + let array_path = "/array"; + let mut builder = ArrayBuilder::new( + vec![4, 4], // array shape + DataType::String, + vec![2, 2].try_into().unwrap(), // regular chunk shape + FillValue::from(""), + ); + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "gzip")] + Box::new(zarrs::array::codec::GzipCodec::new(5)?), + ]); + + let array = builder.build(store, array_path).unwrap(); + + array_str_impl(array) +} + +#[cfg(feature = "sharding")] +#[test] +fn array_str_sync_sharded_transpose() -> Result<(), Box> { + use zarrs::{ + array::codec::{array_to_bytes::vlen::VlenCodec, TransposeCodec}, + metadata::v3::codec::transpose::TransposeOrder, + }; + + let store = std::sync::Arc::new(MemoryStore::default()); + let array_path = "/array"; + let mut builder = ArrayBuilder::new( + vec![4, 4], // array shape + DataType::String, + vec![2, 2].try_into().unwrap(), // regular chunk shape + FillValue::from(""), + ); + builder.array_to_array_codecs(vec![Box::new(TransposeCodec::new( + TransposeOrder::new(&[1, 0]).unwrap(), + ))]); + builder.array_to_bytes_codec(Box::new( + zarrs::array::codec::array_to_bytes::sharding::ShardingCodecBuilder::new( + vec![2, 1].try_into().unwrap(), + ) + .array_to_bytes_codec(Box::::default()) + .build(), + )); + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "gzip")] + Box::new(zarrs::array::codec::GzipCodec::new(5)?), + ]); + + let array = builder.build(store, array_path).unwrap(); + + array_str_impl(array) +} + +#[rustfmt::skip] +#[test] +fn array_binary() -> Result<(), Box> { + let store = std::sync::Arc::new(MemoryStore::default()); + let array_path = "/array"; + let mut builder = ArrayBuilder::new( + vec![4, 4], // array shape + DataType::Binary, + vec![2, 2].try_into().unwrap(), // regular chunk shape + FillValue::from([]), + ); + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "gzip")] + Box::new(zarrs::array::codec::GzipCodec::new(5)?), + ]); + + let array = builder.build(store, array_path).unwrap(); + + array.store_array_subset_elements::<&[u8]>( + &ArraySubset::new_with_ranges(&[1..3, 1..3]), + &[&[0], &[0, 1], &[0, 1, 2], &[0, 1, 2, 3]], + )?; + assert_eq!( + array.retrieve_chunk_elements::>(&[0, 0])?, + vec![ + vec![], vec![], + vec![], vec![0] + ], + ); + assert_eq!( + array.retrieve_chunk_elements::>(&[0, 1])?, + vec![ + vec![], vec![], + vec![0, 1], vec![] + ], + ); + assert_eq!( + array.retrieve_chunk_elements::>(&[1, 0])?, + vec![ + vec![], vec![0, 1, 2], + vec![], vec![] + ], + ); + assert_eq!( + array.retrieve_chunk_elements::>(&[1, 1])?, + vec![ + vec![0, 1, 2, 3], vec![], + vec![], vec![] + ], + ); + assert_eq!( + array.retrieve_array_subset_elements::>(&ArraySubset::new_with_ranges(&[1..3, 0..4]))?, + vec![ + vec![], vec![0], vec![0, 1], vec![], + vec![], vec![0, 1, 2], vec![0, 1, 2, 3], vec![], + ], + ); + + Ok(()) +} diff --git a/tests/cities.rs b/tests/cities.rs new file mode 100644 index 00000000..0af6afc5 --- /dev/null +++ b/tests/cities.rs @@ -0,0 +1,132 @@ +use std::{ + error::Error, + fs::File, + io::{BufRead, BufReader}, +}; + +use zarrs::{ + array::{ + codec::{ + array_to_bytes::{ + sharding::ShardingCodecBuilder, vlen::VlenCodec, + vlen_interleaved::VlenInterleavedCodec, + }, + ArrayToBytesCodecTraits, ZstdCodec, + }, + ArrayBuilder, ArrayMetadataOptions, DataType, FillValue, + }, + array_subset::ArraySubset, + metadata::v3::codec::vlen::VlenCodecConfiguration, + storage::{ + store::{FilesystemStore, MemoryStore}, + ReadableWritableListableStorage, + }, +}; + +fn read_cities() -> std::io::Result> { + let reader = BufReader::new(File::open("tests/data/cities.csv")?); + let mut cities = Vec::with_capacity(47868); + for line in reader.lines() { + cities.push(line?); + } + Ok(cities) +} + +fn cities_impl( + cities: &[String], + compression_level: Option, + chunk_size: u64, + shard_size: Option, + vlen_codec: Box, + write_to_file: bool, +) -> Result> { + let store: ReadableWritableListableStorage = if write_to_file { + std::sync::Arc::new(FilesystemStore::new("tests/data/v3/cities.zarr")?) + } else { + std::sync::Arc::new(MemoryStore::default()) + }; + store.erase_prefix(&"".try_into().unwrap())?; + + let mut builder = ArrayBuilder::new( + vec![cities.len() as u64], // array shape + DataType::String, + vec![chunk_size].try_into()?, // regular chunk shape + FillValue::from(""), + ); + if let Some(shard_size) = shard_size { + builder.array_to_bytes_codec(Box::new( + ShardingCodecBuilder::new( + vec![shard_size].try_into()?, // inner chunk chape + ) + .array_to_bytes_codec(vlen_codec) + .build(), + )); + } else { + builder.array_to_bytes_codec(vlen_codec); + } + if let Some(compression_level) = compression_level { + builder.bytes_to_bytes_codecs(vec![ + #[cfg(feature = "zstd")] + Box::new(ZstdCodec::new(compression_level, false)), + ]); + } + + let array = builder.build(store.clone(), "/")?; + array.store_metadata_opt(&ArrayMetadataOptions::default().set_include_zarrs_metadata(false))?; + + let subset_all = ArraySubset::new_with_shape(array.shape().to_vec()); + array.store_array_subset_elements(&subset_all, &cities)?; + let cities_out = array.retrieve_array_subset_elements::(&subset_all)?; + assert_eq!(cities, cities_out); + + let last_block = array.retrieve_chunk(&[(cities.len() as u64).div_ceil(chunk_size)])?; + let (_bytes, offsets) = last_block.into_variable()?; + assert_eq!(offsets.len() as u64, chunk_size + 1); + + Ok(store.size_prefix(&"c/".try_into().unwrap())?) // only chunks +} + +#[rustfmt::skip] +#[test] +fn cities() -> Result<(), Box> { + let cities = read_cities()?; + assert_eq!(cities.len(), 47868); + assert_eq!(cities[0], "Tokyo"); + assert_eq!(cities[47862], "Sariwŏn-si"); + assert_eq!(cities[47867], "Charlotte Amalie"); + + let vlen_interleaved = Box::new(VlenInterleavedCodec::default()); + + // let vlen = Box::new(VlenCodec::default()); + let vlen_configuration: VlenCodecConfiguration = serde_json::from_str(r#"{ + "data_codecs": [{"name": "bytes"}], + "index_codecs": [{"name": "bytes","configuration": { "endian": "little" }}], + "index_data_type": "uint32" + }"#)?; + let vlen = Box::new(VlenCodec::new_with_configuration(&vlen_configuration)?); + + let vlen_compressed_configuration: VlenCodecConfiguration = serde_json::from_str(r#"{ + "data_codecs": [{"name": "bytes"},{"name": "blosc","configuration": {"cname": "zstd", "clevel":5,"shuffle": "bitshuffle", "typesize":1,"blocksize":0}}], + "index_codecs": [{"name": "bytes","configuration": { "endian": "little" }},{"name": "blosc","configuration":{"cname": "zstd", "clevel":5,"shuffle": "shuffle", "typesize":4,"blocksize":0}}], + "index_data_type": "uint32" + }"#)?; + let vlen_compressed = Box::new(VlenCodec::new_with_configuration(&vlen_compressed_configuration)?); + + print!("| encoding | compression | size |\n"); + print!("| ---------------- | ----------- | ------ |\n"); + print!("| vlen_interleaved | | {} |\n", cities_impl(&cities, None, 1000, None, vlen_interleaved.clone(), true)?); + print!("| vlen_interleaved | zstd 5 | {} |\n", cities_impl(&cities, Some(5), 1000, None, vlen_interleaved.clone(), false)?); + print!("| vlen | | {} |\n", cities_impl(&cities, None, 1000, None, vlen.clone(), false)?); + print!("| vlen | zstd 5 | {} |\n", cities_impl(&cities, None, 1000, None, vlen_compressed.clone(), false)?); + println!(); + // panic!(); + + // | encoding | compression | size | + // | ---------------- | ----------- | ------ | + // | vlen_interleaved | | 642196 | + // | vlen_interleaved | zstd 5 | 362626 | + // | vlen | | 642580 | + // | vlen | zstd 5 | 346950 | + + Ok(()) +} diff --git a/tests/data/cities.csv b/tests/data/cities.csv new file mode 100644 index 00000000..cfc78d01 --- /dev/null +++ b/tests/data/cities.csv @@ -0,0 +1,47868 @@ +Tokyo +Jakarta +Delhi +Guangzhou +Mumbai +Manila +Shanghai +São Paulo +Seoul +Mexico City +Cairo +New York +Dhaka +Beijing +Kolkāta +Bangkok +Shenzhen +Moscow +Buenos Aires +Lagos +Istanbul +Karachi +Bangalore +Ho Chi Minh City +Ōsaka +Chengdu +Tehran +Kinshasa +Rio de Janeiro +Chennai +Xi’an +Lahore +Chongqing +Los Angeles +Baoding +London +Paris +Linyi +Dongguan +Hyderābād +Tianjin +Lima +Wuhan +Nanyang +Hangzhou +Foshan +Nagoya +Tongshan +Luanda +Zhoukou +Ganzhou +Kuala Lumpur +Heze +Quanzhou +Johannesburg +Chicago +Nanjing +Jining +Hanoi +Pune +Fuyang +Ahmedabad +Bogotá +Shenyang +Dar es Salaam +Khartoum +Shangqiu +Hong Kong +Cangzhou +Riyadh +Santiago +Xingtai +Zhumadian +Chattogram +Surabaya +Zhanjiang +Bijie +Yancheng +Hengyang +Zunyi +Shaoyang +Sūrat +Shangrao +Xinyang +Madrid +Baghdad +Maoming +Jieyang +Miami +Singapore +Houston +Liaocheng +Huanggang +Dalian +Dallas +Qingdao +Yulin +Douala +Qujing +Nangandao +Philadelphia +Pudong +Toronto +Zhengzhou +Dezhou +Nanchong +Jinan +Giza +Nairobi +Guadalajara +Ankara +Tai’an +Langfang +Dazhou +Saint Petersburg +Monterrey +Belo Horizonte +Suzhou +Yongzhou +Changde +Xiangyang +Rangoon +Atlanta +Washington +Zhaotong +Zhangzhou +Melbourne +Yichun +Bozhou +Suqian +Abidjan +Ji’an +Guilin +Pingdingshan +Berlin +Alexandria +Mianyang +Sydney +Huanglongsi +Barcelona +Yuncheng +Cape Town +Changsha +Jeddah +Weinan +Chenzhou +Jiangmen +Jiujiang +Xinpu +Yibin +Huaihua +Yangzhou +Taizhou +Kunming +Yiyang +Changchun +Lu’an +Jiangguanchi +Meizhou +Ürümqi +Suzhou +Boston +İzmir +Guigang +Shantou +Kabul +Xiaoganzhan +Bamako +Luzhou +Hefei +Hengshui +Fortaleza +Anqing +Liuzhou +Zhangjiakou +Zhaoqing +Shijiazhuang +Ningbo +Qiqihar +Phoenix +Fuzhou +Chifeng +Xiaoxita +Amman +Chuzhou +Linfen +Qingyuan +Xianyang +Loudi +Binzhou +Zhuzhou +Taiyuan +Nanning +Harbin +Abuja +Yokohama +Suihua +Zaozhuang +Detroit +Xiamen +Neijiang +Montréal +Fuzhou +Baicheng +Wuhu +Yulinshi +Medan +Wenzhou +Changzhou +Puyang +Jiaozuo +Nanchang +Seattle +Ibadan +Casablanca +Kumasi +Deyang +Busan +Hohhot +Hechi +Algiers +Tangshan +Shiyan +Lucknow +Mashhad +San Francisco +Boankra +Dubai +Anshan +Baojishi +Qinzhou +Guiyang +Bengbu +Bazhou +Suining +Wuxi +Kotla Qasim Khan +Hanzhong +Putian +Zhenjiang +Guang’an +Faisalabad +Changzhi +Tongren +Leshan +Santa Cruz de la Sierra +Qinhuangdao +Jaipur +Xinzhou +Lanzhou +Wuzhou +Athens +San Diego +Addis Ababa +Taichung +Huainan +Guatemala City +Kuwait City +Budapest +Qincheng +Rizhao +Quezon City +Sanaa +Tashkent +Kyiv +Meishan +Incheon +Birmingham +Ningde +Zhongshan +Weihai +Bursa +Minneapolis +Mbuji-Mayi +Haikou +Tongliao +Chaoyang +La Paz +Pyongyang +Tampa +Shaoguan +Heyuan +Brasília +Omdurman +Malang +Stuttgart +Daqing +Rome +Brooklyn +Kaohsiung +Xiangtan +Longyan +Baotou +Handan +Jinzhou +Kanpur +Denver +Nanping +Gāzipura +Shanwei +Chaozhou +Guayaquil +Weifang +Huai’an +Zibo +Ankang +Mogadishu +Munich +Gulou +Taipei +Bekasi +Damascus +Sanming +Yangjiang +Jiamusi +Luohe +Medellín +Dingxi +Shaoxing +Yantai +Huizhou +Lishui +Xuanzhou +Khowrhesht +Mirzāpur +Zigong +Hamburg +Guangyuan +Cali +Huangshi +Xining +Lusaka +Ouagadougou +Yaoundé +Zhuhai +Huludao +Baoshan +Mecca +Vancouver +Lianshan +Beirut +Salvador +Bucharest +Longba +Nāgpur +Queens +Jilin +Tieling +Accra +Yunfu +Bekasi Kota +Daegu +Ghāziābād +Luoyang +Brisbane +Anshun +Riverside +Yingkou +Colombo +Yanjiang +Baku +Antananarivo +Mudanjiang +Fukuoka +Yan’an +Jincheng +Nantong +Lincang +Yuxi +Las Vegas +Caracas +Tangerang +Laibin +Konya +Supaul +Vienna +Eşfahān +Baltimore +Shengli +Dandong +Qinbaling +Gaoping +Awka +Taizhou +Ma’anshan +Harare +Perth +St. Louis +Phnom Penh +Depok +Stockholm +Puning +Huaibei +Kowloon +Córdoba +Haiphong +Zamboanga City +Chongzuo +Rawalpindi +Portland +Kano +Yushan +Havana +Hezhou +Pingliang +Vadodara +Manaus +Qingyang +San Antonio +Rājkot +Shangzhou +Vishākhapatnam +Sanmenxia +Baicheng +Gujranwala +Aleppo +Tijuana +Bamenda +Minsk +Indore +Karaj +Kananga +Peshawar +Sapporo +Sacramento +Tilburg +Pingxiang +Ecatepec +Almaty +Austin +Yinchuan +Santos +Blantyre +Thāne +Orlando +Tainan +Xiping +Multan +Santa Cruz +Port Harcourt +Jixi +Fushun +Warsaw +Beihai +Fuxin +Wuwei +Siping +San Juan +Mersin +Bhopāl +Mosul +Lubumbashi +Davao +Curitiba +San Jose +Shuyangzha +Adana +Quito +Pittsburgh +Brazzaville +Hyderabad City +Diyarbakır +Indianapolis +Pimpri-Chinchwad +Masqaţ +Montevideo +Shuozhou +Manhattan +Cincinnati +Kansas City +Patna +Tegucigalpa +Kampala +Cleveland +Sanzhou +Changshu +Heihe +Conakry +Ximeicun +Caloocan City +Masvingo +Zhongli +Novosibirsk +Bilāspur +Semarang +Jingdezhen +Ludhiāna +Liaoyang +Chengtangcun +Rājshāhi +Balandougou +Jiangyin +Valencia +Āgra +León de los Aldama +Puebla +Columbus +Yopougon +Hebi +Shīrāz +Madurai +Huzhou +Tabrīz +Jamshedpur +Maracaibo +Sofia +San José +Prayagraj +Palembang +Kawasaki +Kōbe +Jiaxing +Kigali +Zhangjiajie +Baiyin +Guiping +Lianjiang +Jianguang +Yucheng +Xushan +Panama City +Nneyi-Umuleri +Leizhou +Gwangju +Katako-Kombe +Recife +Nāsik +Valencia +Onitsha +Abu Dhabi +Zapopan +Daejeon +Bronx +Yekaterinburg +Huazhou +Jinhua +Kyōto +Amsterdam +Pizhou +Kismaayo +Yangshe +Virginia Beach +Dakar +Goiânia +Charlotte +Rui’an +Muscat +Kharkiv +Wenling +Gaozhou +Farīdābād +Medina +Khulna +Ulaanbaatar +Fuqing +Kayseri +Tel Aviv-Yafo +Wuzhong +Pingdu +Sangereng +Yangquan +Samsun +Yutan +Copenhagen +Helsinki +Prague +Milan +Auckland +Santiago +Chizhou +Makassar +Liangshi +Porto Alegre +Huangshan +Barranquilla +Al Başrah +Benxi +Saitama +Guarulhos +Juárez +Mandalay +Xintai +Wusong +Calgary +Meerut +Yushu +Belém +Kuaidamao +Huazhou +Baishan +Adelaide +Haicheng +Milwaukee +Providence +Jacksonville +Yicheng +Cacuaco +Porto +Rosario +Canagatan +Soweto +Bagam +Jabalpur +Rucheng +Huaiyin +Dublin +Kazan +Dayan +Shaoyang +Balıkesir +Comayagüela +Laiwu +Sharjah +Jingling +Kalyān +Nizhniy Novgorod +Yongcheng +Sumedang +Cần Thơ +Brussels +Suwon +Yiwu +Beidao +Vasai-Virar +Xiangshui +Dadukou +Campinas +Lingcheng +Shuangyashan +Mombasa +Najafgarh +Xinyu +Qom +Hargeysa +Baidoa +Zhangye +Vārānasi +Hiroshima +Chiang Mai +Belgrade +Maiduguri +Chelyabinsk +Batam Centre +Rongcheng +Mbandaka +Doha +Ahvāz +Shymkent +Tripoli +Srīnagar +Nashville +Liaoyuan +Aurangābād +Cilacap +Salt Lake City +Omsk +Pikine +Samara +Guankou +Bandar Lampung +Raleigh +Lianyuan +Rongcheng +Dhanbād +Nay Pyi Taw +Aba +Kaiyuan +Zhuji +Yingtan +Edmonton +Leiyang +Ulsan +Yichun +Benin City +Bujumbura +Guyuan +Xiantao +Rostov +Maputo +Bukavu +Amritsar +Shagamu +Yingchuan +Alīgarh +Santo Domingo +Bogor +Bishkek +Tbilisi +Guwāhāti +Ufa +Fès +Mwanza +Biên Hòa +Mexicali +Sevilla +Ikare +Dongtai +Dingzhou +Xibeijie +Tamale +Yuyao +Hanchuan +Gongzhuling +N’Djamena +Ubungo +Cologne +Krasnoyarsk +Zhufeng +Ezhou +Astana +Nezahualcóyotl +Nouakchott +Hāora +Tongjin +Xiashi +Yerevan +Rānchi +Richmond +Ciudad Nezahualcóyotl +Gwalior +Ottawa +Zhongwei +Oslo +Goyang +Sendai +Mizhou +Xishan +Barquisimeto +Hegang +Chandīgarh +Voronezh +Managua +Haldwāni +Vijayavāda +Perm +Fangchenggang +Jiancheng +Cazenga +Kisangani +Shouguang +Memphis +São Luís +Jodhpur +Matola +Ogbomoso +Sanya +Rangapukur +Ashgabat +Wutong +Linhai +Denizli +Niamey +Shubrā al Khaymah +Wafangdian +Zhongxiang +Monrovia +San Cristóbal +Islamabad +Xinyi +Thủ Đức +Morelia +Odesa +Raipur +Changwon +Arequipa +Volgograd +Zaoyang +Xingyi +Shuizhai +Kota +Quetta +Quảng Hà +Domaa-Ahenkro +Bareilly +Oklahoma City +Bordeaux +Xingcheng +Taixing +Xinhualu +Lilongwe +Port-au-Prince +Yingcheng +Al Mijlad +Luocheng +Pekanbaru +Natal +Chiba +Kirkuk +Hartford +Huilong +Wuchuan +Dnipro +Nārāyanganj +Gqeberha +Málaga +Marrakech +Cebu City +Louisville +Asmara +Coimbatore +Maceió +Nada +Taishan +Teresina +Solāpur +Freetown +Santo Domingo Este +Krasnodar +Vientiane +Tangier +Anqiu +Kermānshāh +Feicheng +Kibanseke Première +Seberang Jaya +Buffalo +Hubli +El Alto +Çankaya +Hwasu-dong +Setagaya +Keçiören +Jerusalem +Khartoum North +Meishan +Mushin +Trujillo +Kitakyūshū +Aguascalientes +New Orleans +Fort Worth +Taihe +Riga +Xin’an +Taihecun +Kashgar +Sŏngnam +Trichinopoly +Cartagena +Qingzhou +Naples +Santiago del Estero +Naucalpan de Juárez +Daye +Hengzhou +Padang +Bridgeport +Owerri +Zhuanghe +Bobo-Dioulasso +Ad Dammām +Quzhou +Donetsk +Ashmūn +Bunia +Jiaozhou +Campo Grande +Wuchang +São Gonçalo +Bucaramanga +Mérida +Yangchun +Osmangazi +Esenyurt +Morādābād +Bangui +Abeokuta +Cancún +Antipolo +Dengtalu +Taguig City +Tabūk +Zhoushan +Tucson +As Sulaymānīyah +Chihuahua +Klang +Tiruppūr +Gurgaon +Ar Ramādī +Hai’an +Laiyang +Barinas +Jalandhar +Marseille +Kaifeng Chengguanzhen +Eskişehir +Gaomi +Lhasa +Ipoh +El Paso +Saltillo +Dushanbe +Ikeja +El Dorado +Cochabamba +Portsmouth +Tyumen +Southampton +Hermosillo +Wuxi +Leping +Cheongju +Shache +Sale +Hailun +Macheng +Akure +Ilorin +Erbil +Kathmandu +Saratov +Iguaçu +Zijinglu +Turin +Yuci +Dehui +Pietermaritzburg +Durban +Bhubaneshwar +Denpasar +Tongchuan +João Pessoa +Samarinda +Chengxiang +Rongjiawan +Weichanglu +Sakai +Renqiu +Omaha +Xindi +Wu’an +Qingping +Gaoyou +São Bernardo do Campo +Yiyang +Hejian +Puxi +Bhayandar +Androtsy +Culiacán +Cúcuta +Danyang +Dongyang +Kraków +Pasig City +Thessaloníki +Querétaro +Palermo +Xigazê +Qamdo +McAllen +Libreville +Seyhan +San Pedro Sula +Niigata +Hempstead +Leeds +Hamamatsu +Pointe-Noire +Xiangxiang +Birmingham +Chaohucun +Bucheon +Lubango +Homs +Bilbao +Zouping +Frankfurt +San Luis Potosí +Dali +Fuyang +Khŭjand +Korla +Albuquerque +Hamhŭng +Erzurum +Zagreb +Al ‘Ayn +Songzi +Patiāla +Laixi +Zhongba +Bahawalpur +Qingnian +Kaduna +Winnipeg +Trabzon +Guangshui +Baardheere +Shīshgarh +Nerima +Sizhan +Ciudad Guayana +Natal +Lichuan +Licheng +Santo André +Ōta-ku +Gaalkacyo +Thiruvananthapuram +Osasco +Nampula +Pretoria +Kyaukse +Chengguan +Nehe +Cabinda +Kumamoto +Kermān +Zunhua +Orūmīyeh +Wugang +Bağcılar +Quebec City +Shuangqiao +Umraniye +Yanggok +Tshikapa +Tulsa +Osogbo +Comodoro Rivadavia +Nottingham +Hamilton +Langzhong +Cagayan de Oro +Qian’an +Fresno +An Najaf +Cencheng +Sorocaba +Guli +Sagamihara +Pīshbar +Okayama +Anlu +Concepción +Mississauga +Changsha +Songyang +Lviv +Shihezi +Vilnius +Marka +Enugu +Valenzuela +Yatou +Uberlândia +Xichang +Zaporizhzhia +Bhiwandi +George Town +Bristol +Charleston +Sahāranpur +Dashiqiao +Yenimahalle +Warangal +Nampo +Dasmariñas +Jaboatão +Chisinau +Shiliguri +Boosaaso +Port Moresby +Latakia +Rochester +Ribeirão Prêto +Edogawa +São José dos Campos +General Santos +Ḩamāh +Qianxi +Bauchi +Pendik +Salem +Shishi +Sinnūris +Cocody +Miluo Chengguanzhen +Lokoja +Guadalupe +Gaizhou +Karbalā’ +Borvāyeh-ye Al Bū ‘Azīz +City of Parañaque +Leling +Mamak +Jianshe +Tolyatti +Shizuoka +Jingcheng +Agege +Mar del Plata +Zaragoza +Adachi +Xinmin +Rasht +Shanhu +Zhongshu +Cotonou +Tasikmalaya +Kochi +Soledad +Dhūlia +Acapulco de Juárez +Gorakhpur +Bahār +Kumul +Wrocław +Murcia +Pinghu +Guankou +Łódź +Guntūr +Bhāngar +Dayton +Ch’ŏngjin +Qionghu +Zhaodong +Puyang Chengguanzhen +Bulawayo +Huambo +Aracaju +Bacoor +Wenchang +Rotterdam +Tlaquepaque +Villavicencio +Shulan +Makhachkala +Banjarmasin +Narela +Catia La Mar +Al Hufūf +Jalingo +Sargodha +Karaikandi +Bouaké +Lingbao Chengguanzhen +Brampton +Abomey-Calavi +Durango +Cape Coral +Tondo +Dayrūţ +Tlalnepantla +Ansan +Xiping +Huế +Sanhe +San Jose del Monte +Ch’ŏnan +Cuiabá +Jieshou +Ērer Sātā +Selçuklu +Suohe +Guixi +Izhevsk +Honchō +Wuxue +Jaboatão dos Guararapes +Yıldırım +Bhāvnagar +Jinghong +Tengyue +Mission Viejo +Ruiming +Qufu +Sha Tin +Petaling Jaya +Colorado Springs +Noida +Xinshi +Baton Rouge +Manama +Las Palmas +Chak Forty-one +Jin’e +Benghazi +Chuxiong +Barnaul +Palermo +Makati City +Düsseldorf +Allentown +Xinxing +Ţūkh +Glasgow +Namangan +Bazal’tove +Al Qaţīf +Bhilai +Ulyanovsk +Mangalore +Kaihua +Irkutsk +Meilan +Bazhou +Ogden +Turpan +Jos +Al Manşūrah +Contagem +Jambi +Ḩalwān +Provo +Tân An +Port-Bouët +Pontianak +Meihekou +Jurong +Bihtā +Yuhuan +Joinvile +Feira de Santana +Khabarovsk +Leipzig +Xinji +Knoxville +Ta‘izz +Etimesgut +Changping +Tallinn +Chimalhuacán +Kandahār +Serang +Zhangshu +Grand Rapids +Yukarıkaraman +Gothenburg +Kuantan +Gold Coast +Kawaguchi +Las Piñas City +Cuttack +San Miguel de Tucumán +Ar Rayyān +Salīmpur +Malanje +Columbia +Kryvyi Rih +Djibouti +Zhuozhou +Tianchang +Tunis +Yaroslavl +Bacolod +Garoua +Bafoussam +Haifa +Raurkela +Tumkūr +Balikpapan +Somolu +Vladivostok +Al Ḩillah +Melikgazi +Kagoshima +Sihui +Dortmund +Irapuato +Al Maḩallah al Kubrá +Sialkot City +Luocheng +Albany +Pereira +Gaza +Uvira +Reynosa +Zāhedān +Cimahi +Mbale +Wenlan +Shangzhi +Essen +Itabashi +Shah Alam +Botou +Suginami-ku +Tenkāsi +Kingston +Al Mafraq +Aţ Ţā’if +Port Sudan +Tuxtla +Dehra Dūn +Xiulin +Fu’an +Mymensingh +Hachiōji +Iloilo +Puente Alto +Tomsk +Rabat +Sincan +Bakersfield +Kottayam +Luofeng +Shibīn al Qanāţir +Nakuru +Lingyuan +Tonalá +Bremen +Abū Ḩummuş +Irbid +Macau +Surrey +Ciudad Bolívar +Durgāpur +Shenzhou +New Haven +Orenburg +Kuiju +Zhenzhou +Āsansol +Bokaro Steel City +Dresden +Bello +Kolhāpur +Wencheng +Lanxi +Dangyang +Nava Raipur +Kemerovo +Genoa +Herāt +Londrina +Cuautitlán Izcalli +Uyo +Hamadān +Luanzhou +Chiclayo +Surakarta +Novokuznetsk +Ajmer +Kimhae +Nānded +Wuhai +Palma +Rustenburg +Amrāvati +Des Moines +Lisbon +Yanji +Huanghua +Al Ḩudaydah +Anyang +The Hague +Andijon +Manchester +Nellore +Poznań +Samarkand +Xingcheng +Wancheng +Kaiyuan +Hannover +Sungai Petani +Valledupar +Fengcheng +Muntinlupa City +Ghulja +Ixtapaluca +Fuding +Heroica Matamoros +Akron +Mbeya +An Nāşirīyah +Xiangyang +Ibagué +Al Ḩillah +Juiz de Fora +City of Calamba +El Geneina +Santa Cruz +Ryazan +Chang’an +Florianópolis +Nilüfer +Antwerp +Kassala +Aksu +Salta +Dispur +Palm Bay +Naberezhnyye Chelny +Gulbarga +Nansana +Mingguang +Concord +Beira +Yazd +Ardabīl +Touba +Bīkaner +Gaobeidian +Ndola +Himeji +Ailan Mubage +Bandar ‘Abbās +Skopje +Santa Teresa del Tuy +Port Said +Astrakhan +Kōtō-ku +Ciudad Apodaca +Montería +Nuremberg +Kitchener +Yucheng +Nāgercoil +Agartala +Soacha +Buca +Lyon +Maipú +Arāk +Serra +Tultitlán de Mariano Escobedo +Meknès +Bắc Ninh +Anda +Longzhou +Al Fayyūm +Utsunomiya +Sheffield +Mixco +Suez +Heshan +Penza +Loni +Jiaji +Santa Marta +Ujjain +Beining +Abū Ţisht +Maturín +Liverpool +Macapá +Benguela +Yicheng +Al Fashn +Al ‘Amārah +Carrefour +Campos +Cadaado +Encheng +Bhīlwāra +Bibā +Wichita +Leicester +Newcastle +Hải Dương +Aden +Jhānsi +Matsuyama +Ulhāsnagar +Nagqu +Kitwe +Vellore +Toulouse +Pohang +Mesa +Lipetsk +Duisburg +Jammu +Ile-Ife +Homyel’ +Kirov +Mazatlán +Meicheng +El Fasher +Farāh +Belas +Talatona +Nenjiang +Sukkur +Hsinchu +Harrisburg +Kaliningrad +Hongjiang +Qaraghandy +Lapu-Lapu City +Matsudo +Johor Bahru +Purnea +Imus +Niterói +Beipiao +Dengtacun +Zhijiang +Suoluntun +Staten Island +Chengjiao +Lembok +Likasi +Oujda-Angad +Duyun +Toledo +Pindi Bhattian +Nyala +Bissau +Ichikawa +Kota Bharu +Yuanping +Higashi-ōsaka +Larkana +‘Ajmān +Vinh +Ciudad López Mateos +Cheboksary +Yueqing +Belgaum +Caerdydd +Edinburgh +Brookhaven +Nishinomiya-hama +Karamay +Worcester +Kawachichō +Shahe +Gdańsk +Sevastopol +Garoowe +Gaoping +Villa Nueva +Dunhua +Lianran +Akhmīm +Şanlıurfa +Az Zarqā’ +Mālegaon +São José do Rio Prêto +Valletta +Kolwezi +Jāmnagar +Sylhet +Ananindeua +East London +Berbera +Jiannan +Chiniot +Asunción +Ōita +Nangong +Bārdoli +Eldoret +Bratislava +Kurashiki +Al Jubayl +Worthing +Gaya +Shekhupura +Piura +Vila Velha +Ar Ruşayfah +Jiaojiangcun +Laohekou +San Juan +Mykolaiv +Beian +Fujin +Küçükçekmece +Mazār-e Sharīf +Xiaoyi +Balashikha +Qingzhen +Tula +Ba‘qūbah +Katlehong +Jiangshan +Buraydah +Surab +Kupang +Ambattūr +Nakhon Ratchasima +Tân Uyên +Constantine +Longjiang +Caxias do Sul +Angeles City +Kuqa +Kanazawa +Long Beach +Port St. Lucie +Manado Light +Kartal +Cranbourne +Jalgaon +Porto Velho +Chhatarpur +Fukuyama +Little Rock +Juba +Lanús +Amagasaki +Kikwit +Pyeongtaek +Reno +Kurnool +Spokane +Marikina City +Jian’ou +Huadian +Melaka +Manado +Manizales +Bornova +Minzhu +Demiryol +Erköklü +Kota Kinabalu +Katsushika-ku +Madison +Santiago de Cuba +Udaipur +Kursk +Mogi das Cruzes +Stavropol +General Trias +Sirājganj +Boise +Bonita Springs +Mariupol +Eslāmshahr +Piraeus +Barcelona +Tanbei +Zürich +Pingquan +Ado-Ekiti +Baisha +Batman +Yongji +Esenler +Rodriguez +Ensenada +Danjiangkou +Chauddagram +Kahramanmaraş +San Nicolás de los Garza +Taoyuan District +Ndjili +Mathura +Pasay City +Ning’an +Halifax +Fujisawa +Ulan-Ude +Denton +Laval +Jinchang +Oakland +Springfield +Guangming +Augusta +Kâğıthane +Sunch’ŏn +Sāngli +Avcılar +Jeju +Zhuangyuan +Davangere +Machida +Sanghar +Al Marāghah +Bandung +Kissimmee +Calicut +Kenitra +Windhoek +Huili Chengguanzhen +Sidi Bouzid +Bārāmati +Ţanţā +Ismailia +Cusco +Veracruz +Sokoto +Winston-Salem +Kashiwa +Al Bājūr +Xunyang +Malatya +Yan’an Beilu +Mothīhāri +Aomori +Akola +Mandaluyong City +Aves +Sihŭng +Burco +Tver +Xalapa +Buenaventura +London +Piracicaba +Yogyakarta +Toyota +Daloa +Agadir +Elazığ +Uijeongbu +Hpa-An +Rahimyar Khan +Ugep +Hailin +Mishan +Sarajevo +Seremban +Zhengjiatun +Lecheng +Campina Grande +Xicheng +Pencheng +Kowloon City +Tirana +Kushtia +El Obeid +Mauá +Magnitogorsk +Da’an +Luhansk +Xingren +Takamatsu +Arusha +Fenyang +Ajdābiyā +Callao +Awsīm +Shinagawa-ku +Paju +Santa Rosa +Bettiah +Jhang City +Altındağ +Talā +Stockton +Ţalkhā +Boa Vista +Banjul +Jayapura +Toyama +Sanandaj +Khon Kaen +Fangting +Linghai +Shorāpur +Koumassi +Betim +Sochi +Tinnevelly +Pasto +Syracuse +Bellary +Bhāgalpur +Kisumu +Zhangjiakou Shi Xuanhua Qu +Maringá +Kocasinan +Mataram +Shashemenē +Zaria +Kumi +Wanyuan +Biñan +Chattanooga +Jiexiu +Bağlar +Padiāla +Đà Lạt +Sham Shui Po +Santa Fe +Delhi Cantonment +Cumaná +Barura +Yüreğir +Nagasaki +Mardan +Hat Yai +Salt Lake City +Qazvīn +Etāwa +Lancaster +Sonīpat +Jundiaí +Ivanovo +Greenville +Toyonaka +Bogra +Öskemen +Gifu +Maungdaw +Jiangjiafan +Durham +Bryansk +Dera Ghazi Khan +Anápolis +Pensacola +Miyazaki +Quilon +Mulangodi +Munro Turuttu +Hirakata +Sandakan +Szczecin +Brno +Surgut +Hejin +Fayetteville +Betsiboka +Thiès +Arlington +Al Jahrā’ +Kaunas +Thanh Hóa +Diadema +Lobito +Saurimo +Yola +Zhugang +Tāngāil +Nha Trang +Khayelitsha +Ad Dīwānīyah +Nnewi +Hancheng +San-Pédro +Gujrat +Yokosuka +Tieli +Asyūţ +Gwoza +Sampaloc +Saki +Bologna +Aqtöbe +Cilegon +Uvinza +Aurora +Carapicuíba +Ḩafr al Bāţin +Zanjān +Petrolina +Bairia +Oyo +Taytay +Kisenzi +Bhātpāra +Kūkatpalli +Manisa +Sirūr +Tarlac City +Okazaki +Lianzhou +Ceel Baraf +Yidu +Lingxi +Ilesa +Kākināda +Savar +Nuevo Laredo +Bawshar +Christchurch +Gusau +Zêtang +Palu +Canberra +Minamisuita +Tétouan +Malabon +Neiva +Novi Sad +Huancayo +Celaya +Ichinomiya +Caruaru +Sintra +Hatay +Iquitos +Pānihāti +Cainta +Helixi +Mamou +Manukau City +Itaquaquecetuba +Cantonment +Rohtak +Gaziantep +Māler Kotla +Bhawana +Khorramābād +Lipa City +Butuan +Dikirnis +Stoke-on-Trent +Takasaki +Malakwal +Toyohashi +Chitungwiza +Gebze +Cibinong +Lengshuijiang +Panshi +Az Zubayr +Oxnard +Vinnytsia +Indio +Bharatpur +Petare +Nagano +Huichang +Keelung +Bauru +La Florida +Nicolás Romero +Jinshan +Baguio City +Scranton +Bochum +Sivas +Kolga +Korba +Qardho +Rio Branco +Tecámac +Alanya +Mandaue City +Victorville +Kocaeli +Warri +Victoria +Wŏnsan +Iligan +Anguo +K’ebrī Dehar +Coventry +Kayapınar +Trenton +Cuenca +Blumenau +Nanqiao +Florence +Buurhakaba +Bengkulu +Malmö +Wudalianchi +Shuanghe +Pétion-Ville +Utrecht +Sīkar +Umuahia +Vitsyebsk +Palmira +Wuppertal +Hrodna +Ash Shuhadā’ +Karūr +Ponta Grossa +Sasarām +Taraz +Cubal +Luena +Karnāl +Yong’an +Konak +Minatitlán +Linxia Chengguanzhen +Brahmapur +Chānda +Caucaia +Cuito +Cabuyao +Hongzhai +Gedaref +San Fernando +Kawagoe +Modesto +Nizhniy Tagil +Pokhara +Villahermosa +Van +Mahilyow +Wakayama +Osh +Kita-ku +Gimpo +Corrientes +Franca +Thari Mir Wah +Nara +Vladimir +Yakeshi +Nam Định +Sinŭiju +Oruro +Cabimas +Arkhangelsk +Batangas +Ibb +Ahmadnagar +Sarai Alamgir +Semey +Holguín +Tungi +Yingmen +Sawrān +Chita +Olinda +Praia Grande +Dāsarhalli +Huntsville +Shinjuku +Alicante +Cariacica +Varna +Honolulu +Antofagasta +Ambon +Nice +Cascavel +Pamukkale +Canoas +Takatsuki +António Enes +Greensboro +Anaheim +Plovdiv +Central Coast +Karşıyaka +Shāhjānpur +Gwagwalada +Alamādi +Āvadi +Tanch’ŏn +Khānāpur +Wad Medani +Kūstī +Belfast +Hosūr +Cuddapah +Nakano +Ōtsu +Maseru +Makiivka +Pavlodar +Chon Buri +Naga City +Sarıyer +Brest +Meram +Gómez Palacio +Paulista +Rājahmundry +Koshigaya +Vũng Tàu +Jeonju +Alwar +Tokorozawa +Sumqayıt +Vitória da Conquista +Simferopol +Buôn Ma Thuột +Serekunda +Islip +Cuernavaca +Markham +Bielefeld +Uberaba +Jitpur +Bydgoszcz +Tangdong +Chinju +Corpus Christi +Fort Wayne +Reading +Randburg +Matadi +Bonn +Iwaki +Oshawa +Shah Latif Town +Sambalpur +Fort Collins +Jackson +Yingzhong +Santo Domingo de los Colorados +Uruapan +Lublin +Licheng +Tampere +Belgorod +Muzaffarpur +Viña del Mar +Tepic +Khipro +Hangu +Asan +Chak Jhumra +Myrtle Beach +Soledad de Graciano Sánchez +Şalālah +Santarém +Yanbu +Maebashi +Kaluga +Dumai +Beylikdüzü +Gəncə +Asahikawa +Kendari +Wŏnju +Birkat as Sab‘ +Lafia +Dahūk +Finglas +Kāmārhāti +Thái Nguyên +Bamiantong +Nicosia +São Vicente +Ribeirão das Neves +Guédiawaye +Ciudad Obregón +São José dos Pinhais +Campīernagar +Phatthaya +Fayetteville +Debrecen +Mirpur Mathelo +Sultanbeyli +Bijāpur +Cabanatuan City +Tharād +Antioch +Si Racha +Salamanca +Ratnāgiri +Ulanhot +Koriyama +Yunzhong +Roodepoort +Volzhskiy +Pucallpa +San Pedro +Pelotas +Kōchi +Rāmpur +Kuching +Cotabato +Cimanggis +Gonaïves +Nazrēt +Kikuyu +Córdoba +Kluang +Mekele +Binxian +Nantes +Vaughan +Kaiyuan +Vitória +Shimoga +Lansing +Uíge +Hotan +Camagüey +Taourirt +San Salvador de Jujuy +Kāshīpur +Thủ Dầu Một +Al Kūt +Lexington +Sukabumi +Münster +Menongue +Mobile +Godē +Okene +Jūnāgadh +Smolensk +Guasavito +Asan +Nukus +Kaech’ŏn +Tehuacán +Coatzacoalcos +Muhanga +Youngstown +Zalantun +Saransk +Katsina +Puerto Plata +Henderson +Gwangmyeongni +Geita +Cherepovets +Taubaté +An Nhơn +Fontibón +Hāpur +Kot Radha Kishan +Barueri +San Salvador +Savannah +Naha +Bari +Xiaoli +Trichūr +Mannheim +Bor +Cà Mau +Saint-Denis +San Miguelito +Muar +Kasur +Usme +Barddhamān +Poughkeepsie +Kingston upon Hull +Binangonan +Temara +Attiecoubé +Jiayuguan +Pasir Gudang +Vologda +Gorgān +Tanza +Yakutsk +Panvel +Santa Ana +Guarujá +Nizāmābād +Quy Nhơn +Ḩā’il +Datang +Longquan +Mitchells Plain +Gwangju +Yokkaichi +Chalco +Shahrīār +Shahr-e Qods +Kurgan +Ciudad del Este +Ann Arbor +St. Paul +Karlsruhe +Safi +Ciudad Benito Juárez +Karawang +Sariwŏn +Suzano +Newark +Nghi Sơn +Parbhani +Puerto Princesa +Hisar +Vladikavkaz +Windsor +Kasugai +Ciudad Santa Catarina +Puerto La Cruz +Fatehpur +Ciudad Victoria +Playa del Carmen +Yeşilyurt +Yonghetun +Irvine +Kāshān +Minna +Sumbawanga +Orël +Akita +Kurume +Az Zaqāzīq +Podolsk +Palmas +Montpellier +Vila Nova de Gaia +Bahía Blanca +Al Waqf +San Bernardo +San Juan del Río +Armenia +Augsburg +Qianzhou +Popayán +Al Qunfudhah +Yakou +Newcastle +Oulgaret +Āwasa +Ingrāj Bāzār +Oaxaca +Binjai +Barr Eliâs +Khairpur Tamewah +Sucre +Al ‘Ajamī +Al Maţarīyah +Bada Barabīl +Ash Shāmīyah +Oyster Bay +Ar Raqqah +Chakwal +Ōakashichō +Afyonkarahisar +Dod Ballāpur +Camaçari +Ciudad General Escobedo +Catania +Jember +Al Mubarraz +Pekalongan +Los Mochis +Toshima +Pachuca +Yangsan +Valladolid +Tampico +Bihār +Espoo +Malabo +Pilar +Valparaíso +Cirebon +Tagum +Santa Rosa +Darbhanga +Comilla +Battalgazi +Sorong +Shubrākhīt +Floridablanca +Silang +Eloy Alfaro +Pondokaren +Sikandarābād +Kafr Şaqr +Vila Teixeira da Silva +Pānīpat +Rangpur +Białystok +Canton +Asheville +Flint +Vigo +Coacalco +Āīzawl +Bāli +Bradford +Mabalacat +Dexing +Winter Haven +Graz +Palni +Resistencia +Groznyy +Chimbote +Strasbourg +Bergen +Gatineau +Surajgarha +Tegal +Anchorage +Batna +Aarhus +Morioka +Lincoln +Hulin +Hong’an +Karīmnagar +Santa Maria +Tambov +Dewās +Güngören +Magway +Farg‘ona +Concord +Hugli +Chunchura +Sétif +Sonpur +Meguro +Machala +San Lorenzo +Jersey City +Ichalkaranji +Punto Fijo +Várzea Grande +Tirupati +Pathein +Chernihiv +Sincelejo +Cluj-Napoca +Springfield +Sekondi +Tacna +Tin Shui Wai +Juazeiro do Norte +Al Qurnah +Korhogo +Bhatinda +Katowice +Jālna +Foz do Iguaçu +Bolton +San Pablo +Huixquilucan +Plano +Qillīn +Croix-des-Bouquets +San Juan Sacatepéquez +Ljubljana +Fukushima +Bago +Delmas +Fuquan +Ibaraki +Shreveport +Ostrava +Poltava +Wiesbaden +Satna +Sannai +Huozhou +Temuco +Ica +Tongchuanshi +Jining +Chuncheon +Sakarya +İnegöl +Kaura Namoda +Davenport +Malārd +Lubbock +Lakeland +Sterlitamak +Bukhara +Santa Ana +Sumbe +Mingaora +Çorlu +Kherson +Lucena +Petrópolis +Mamuju +Mau +Nizhnevartovsk +Long Xuyên +Petrozavodsk +Gyeongsan +Bārāsat +South Bend +Pematangsiantar +Maastricht +Việt Trì +Sunderland +Kostroma +Gagnoa +Xingsha +Dire Dawa +Lashkar Gāh +Itagüí +Juliaca +Chula Vista +Posadas +Farrukhābād +Chandler +Kunsan +Yeosu +Qarshi +Saugor +Khmelnytskyi +Nassau +Ratlām +Crato +Yeosu +Shaowu +Pasarkemis +Cotia +Taboão da Serra +San Mateo +Novorossiysk +Tsu +Rockford +Imperatriz +Los Alcarrizos +Soubré +Reading +Székesfehérvár +Majene +Sumida +Chopda +Gabela +Dayr az Zawr +Iaşi +Santa Maria +Sartā +Eugene +Iksan +Mỹ Tho +Nguru +Arnavutköy +Derby +Mito +Kunp’o +Gombe +Bijiao +Cherkasy +Bayat +Handwāra +Kunduz +Drug +Wilmington +Mönchengladbach +Gijón +Brāhmanbāria +Santa Clarita +Thái Bình +Ichihara +Tarija +Shibīn al Kawm +Plymouth +Aswān +Bimo +Murmansk +Gilbert +Maradi +Xiangkhoang +Anantapur +Adıyaman +Kütahya +Yoshkar-Ola +Marabá +Salem +Saskatoon +Sumaré +Killeen +Nagaoka +Djelfa +Sumy +Khwazakhela +Chernivtsi +Suncheon +Kibaha +Nalchik +Sfax +Gent +Gravataí +Antsirabe +Feni +Engels +Imphāl +Taunggyi +Nogales +Ed Daein +Dezfūl +Mossoró +Round Lake Beach +Potosí +Osmaniye +Columbus +Itajaí +North Las Vegas +Tāluqān +Constanţa +Luque +Yao +Jalālābād +Nawabshah +Talisay +Gelsenkirchen +Jagdalpur +Tchibota +Kafr ad Dawwār +Quilmes +Wollongong +Zhytomyr +Volta Redonda +Fukui +Arrah +Malolos +Heroica Nogales +Bariadi +Hong +Oumé +Fuchū +Minato +Boksburg +Olongapo +Quảng Ngãi +Al Ḩamzah +Kennewick +Qo‘qon +Kotri +St. Petersburg +Mişrātah +Aydın +Singa +Manta +Tallahassee +Kakogawachō-honmachi +Isparta +Siverek +Ndulo +Antalya +Huayin +Hiratsuka +Rāniyah +Annaba +Governador Valadares +Khimki +Ondo +Etāwah +Siddhirganj +Horlivka +Indaiatuba +Bloemfontein +Ấp Đa Lợi +Türkmenabat +Malkājgiri +Vitoria-Gasteiz +Germiston +Nonthaburi +Verona +Tuzla +Westminster +Laredo +Kuala Terengganu +San Pedro Carchá +Moçâmedes +Irving +Turmero +Tokushima +São Carlos +Longueuil +Marilao +Tuni +Ash Shaţrah +Sab‘ al Būr +Fort-de-France +Mawlamyine +Peoria +Godomè +Rāpar +Samāstipur +Aksaray +Shinozaki +Parnamirim +Kızıltepe +Jhenida +Turku +Bharatpur +Aachen +Begusarai +Kediri +Kanggye +Chiayi +Çekme +Hakodate +Tacloban +Jūnāgarh +Braunschweig +İskenderun +Pécs +Sōka +Nedumana +Higüey +Los Teques +Montgomery +Jinshi +Wolverhampton +Pointe-à-Pitre +Al Fallūjah +Timişoara +Bata +Rạch Giá +Companiganj +Venice +Taganrog +Bābol +São José +La Paz +Al Bayḑā’ +Natogyi +Kurmuk +Râs el-Barr +Kalār +Bojnūrd +Türkistan +New Delhi +Las Condes +Goma +Rishon LeẔiyyon +Komsomol’sk-na-Amure +Campeche +Manzhouli +Tiruvottiyūr +Palangkaraya +Chesapeake +Burnaby +Rāmnagar +Limeira +Carmen +Gāndhīdhām +Banikoara +Chemnitz +Salūmbar +Kyŏngju +Mérida +Glendale +Sibu +Qā’em Shahr +Paraná +Büyükçekmece +Kiel +Sahiwal +A Coruña +Navotas +Santa Clara +Mehrabpur +Yamagata +York +Khomeynī Shahr +Beykoz +Tsukuba-kenkyūgakuen-toshi +Oruro +Macaé +Győr +Al ‘Āshir min Ramaḑān +Mahajanga +Mount Lavinia +Northampton +Krishnarājpur +Hafizabad +Nelamangala +Beichengqu +Abertawe +Syktyvkar +Rivne +Gdynia +Nashua +Barnsley +Taiping +Rondonópolis +São José de Ribamar +Puducherry +Merlo +Portoviejo +Damanhūr +Garland +Kabinda +Jessore +Kesbewa +Tripoli +Fuji +Eindhoven +Sabzevār +Dourados +Bahir Dar +Yoshiichō-shimobaru +Myitkyina +Rāmgundam +Pālanpur +Tuy Hòa +Sasebo +Sapele +Birāṭnagar +Pandharpur +Qyzylorda +St. Catharines +Chigasaki +Araraquara +Kahama +Halle +Americana +Zhangping +Man +Sete Lagoas +Bānchpār +Haeju +Soyapango +Masaurhi Buzurg +Baruta +Düzce +Marília +Katihār +Scottsdale +Tarapoto +Atushi +Abī al Khaşīb +Jacareí +Anju +Bunkyō-ku +Byatarayanpur +Ahor +Diaobingshancun +Magdeburg +Yato +Matsumoto +Szeged +Chimoio +Kasulu +Elche +Tarsus +Ivano-Frankivsk +Chōfugaoka +González Catán +Nyíregyháza +Wuyishan +Shenmu +Tuticorin +As Sīb +Gangānagar +Braşov +Lafayette +Āmol +Stavanger +Sandnes +Nizhnekamsk +Monclova +Chishui +Djougou +Norfolk +Mirpur Khas +Lille +P’yŏngsŏng-si +P’yŏng-dong +Centurion +North Hempstead +Tinkhang +Rewa +Pākdasht +Kajang +Petaẖ Tiqwa +Abhā +Freiburg im Breisgau +Al Minyā +Iseyin +Central District +Gaborone +Arlington +Juazeiro +Zinder +Bole +Shakhty +Ganda +Uluberiya +Bulandshahr +Banda Aceh +Najafābād +Shibuya-ku +Bayamo +Limassol +Borūjerd +Ibb +García +Arapiraca +Longquan +Miri +Maracanaú +Oral +Craiova +Formosa +Appleton +Chauhanpatti +Bo +Mambéré +Damboa +Groningen +Ipswich +Teluk Intan +San Cristóbal +Cannanore +Rāichūr +Okara +Saga +Saidpur +Lin’an +Colombo +Machilīpatnam +Beni +Nazipur +Purwokerto +Bratsk +Biyalā +Madan +Rancagua +Phú Yên +Neuquén +Divinópolis +Qarchak +Ormoc +Fresnillo +Dzerzhinsk +Granada +Sơn Tây +Singkawang +Zégoua +Kulai +Barishal +Pāli +Songadh +Noginsk +Gadda Madiral +Orsk +Ordu +Košice +Kasukabe +Miriālgūda +Aguadilla +Envigado +Haridwār +Rock Hill +Fremont +Vizianagaram +Cobán +Khénifra +Guantánamo +Krefeld +Şabyā +Fargo +Rāisinghnagar +Uşak +Pāthardi +Magé +Gulfport +Neya +Mel Pālaiyam +Novo Hamburgo +Ipatinga +Sāqultah +Choloma +Kropyvnytskyi +Khatīma +Daşoguz +Khanewal +Petlād +Kamianske +Bremerton +Ageoshimo +Changhua +Regina +Badalona +Dianbu +Kolpino +Rio Verde +Meycauayan +Presidente Prudente +Green Bay +Varāmīn +Padangsidempuan +Uacu Cungo +Enterprise +Hunchun +Tarrasa +Ternopil +Kimberley +Nadiād +Toamasina +Rennes +Mutare +Cikupa +Ratodero +Chuādānga +Carlos Manuel de Céspedes +Blagoveshchensk +Nong’an +Ōta +Itaboraí +Puerto Vallarta +Capiatá +Viamão +Takarazuka +Chapecó +Banfield +Toluca +Atsugichō +Paramaribo +Cox’s Bāzār +Bandar-e Būshehr +Itapevi +Staryy Oskol +Probolinggo +Hialeah +Tanjore +Ji’an Shi +Angarsk +Hachinohe +Ijebu-Ode +Velikiy Novgorod +Barra do Dande +Mezitli +Sandton +Beji +Cork +Dili +Owo +Swindon +Myeik +Cabo Frio +Kichha +Longjiang +Katri +Korolëv +Sarıçam +Guéckédou +Neyshābūr +Sousse +Tabora +Sóc Trăng +Dagarua +Deltona +Tchaourou +Rufisque +San Bernardino +Duekoué +Gainesville +Sambhal +San Felipe +Sāveh +Mainz +Santa Luzia +Chí Linh +La Vega +Singrauliya +El Jadid +Kremenchuk +Ashdod +Prizren +Spring Valley +Thatta +Yenişehir +Al Khubar +Osisioma +Tacoma +Tuluá +Zanzibar +Kafue +Konibodom +Petrel +Messina +Pangkalpinang +Roanoke +Bitung +Santo Tomas +San Miguel +Sabadell +Lübeck +Cartagena +Naihāti +Arakawa +Galaţi +Babylon +Laâyoune +Butembo +Oviedo +Tapachula +Porbandar +São Leopoldo +Apapa +Netanya +Qostanay +Alor Setar +Wayaobu +Zielona Góra +Batu +Gujiao +José C. Paz +Yamunānagar +Banjarbaru +Valencia +Ayacucho +Wellington +Peñalolén +Dongxing +Lutsk +La Ceiba +Ar Ruseris +Hortolândia +Pallāvaram +Marg‘ilon +Jiutepec +Brownsville +Tân An +Golmud +San Rafael +Sidi Aïssa +Khargone +As Samāwah +Maţraḩ +Comalcalco +Cúa +Almere +Vantaa +Mokpo +La Victoria +Warnes +Cascais +Çiğli +Marcory +Chilas +Sikasso +Osan +Oakville +Secunderābād +Sa Đéc +Turbat +El Tigre +Bhuj +Sodo +Isesaki +Jerez de la Frontera +College Station +Norwich +Częstochowa +Monghyr +Luton +Chāpra +Sidi Bel Abbès +Zhubei +Tumaco +Trondheim +Mallawī +Kure +Kuje +Bhadrāvati +Taitō +‘Aqrah +Cao Lãnh +Guadalupe +Mytishchi +Xigujing +Irákleio +Criciúma +Panchkula +Mirpur Bhtoro +Burhānpur +Olympia +Oberhausen +Burgas +Sobral +Clarksville +Trece Martires City +Gangneung +Pagadian +Quilicura +Kirātot +Linz +Arroyo Naranjo +Bukit Mertajam +Dongning +Richmond +Rostock +Kāpra +Pskov +Yonkers +Burutu +Móstoles +Moreno Valley +Thousand Oaks +Legazpi City +Portland +Fontana +Panabo +Puerto Cabello +Ich’ŏn +Beersheba +Bila Tserkva +Santo Agostinho +Oulu +Luziânia +Ciputat +Guarenas +Mohammedia +Babruysk +Taisheng +Sunyani +Lubuklinggau +Ashaiman +Ambāla +Zamora +Chungju +Kasangati +Kharagpur +Farshūţ +Monywa +Nishitōkyō +Robertsonpet +Lyubertsy +Dindigul +Toledo +Milton Keynes +Marawi City +Morogoro +Rānīwāra Kalān +Solihull +Parakou +Damietta +Shimla +Padova +Banjar +Cidade de Nacala +Passo Fundo +Dinājpur +Hospet +Islington +Talca +Hickory +Amarillo +Ifakara +Pamplona +Northcote +Māldah +San Carlos City +Jiutai +Phan Thiết +Puqi +Khān Yūnis +Ongole +Córdoba +Brahmanpara +Biskra +Bingerville +Betigeri +Sioux Falls +Mpanda +Ternate +Kassel +Sakākā +Sejong +Quetzaltenango +Coquimbo +Tekirdağ +Luzhang +Kodumur +Kukawa +Geneva +Huntington +Ellore +Evansville +Kinh Môn +Bīrjand +Santa Ana +Barrancabermeja +La Guaira +Loja +Mandi Burewala +Lauro de Freitas +Mazabuka +Deoghar +Tanjungpinang +Phủ Từ Sơn +Huntington +Chhindwāra +Iringa +Lingampalli +Waterbury +Mokameh +Luxor +Arica +Las Tunas +Qal‘at Bīshah +Frisco +Hà Tĩnh +Richmond Hill +Timayy al Imdīd +Al Khums +Charleroi +Lorain +Matsue +Tarakan +Loures +Pingzhen +Radom +Petropavl +Rio Claro +Maroua +Cajamarca +Qinā +Mai’Adua +Hebron +Puri +Şidfā +Soio +Menemen +Kalamazoo +Haldia +Jacobabad +Khandwa +Aberdeen +Biysk +Huacho +Almería +Yachiyo +Nandyāl +Pulimaddi +Georgetown +Morena +Galveston +Nasīm Shahr +Cosenza +Guacara +Kabankalan +Nagareyama +Araçatuba +Vĩnh Long +Lạng Sơn +Az Zāwīyah +Isanlu +Río Cuarto +Lochau +Karjat +At Tājī +Spartanburg +Lahad Datu +Drabar +Madiun +Santa Tecla +Santa Barbara +Pristina +Uripa +Khowy +Gbadolite +Huangyan +Laqţah +Amroha +Lunga-Lunga +Trieste +Sunrise Manor +Toruń +Martapura +Rānī +Kankan +Chakradharpur +Helong +Segamat +Bhiwāni +Bhind +Huntington Beach +Hobart +Tocuyito +Baure +Higashi-Hiroshima +Maricá +Grand Prairie +Ciudad Madero +Itami +Zākhū +Kodaira +Rajin +Alcalá de Henares +Parma +Overland Park +Huánuco +Kusŏng +Brescia +Zipaquirá +Khammam +Kouribga +McKinney +Madhyamgram +Sinop +Kajo Kaji +Viranşehir +Ghāndīnagar +Rzeszów +Prato +Waco +Acarigua +Myawadi +Mwene-Ditu +Karaköprü +Parachinar +Koronadal +La Serena +Maharagama +San Pedro de Macorís +Liège +Hagerstown +Suzuka +Baharampur +Tchitato +Malema +N’Zérékoré +Mbarara +Morbi +Yuzhno-Sakhalinsk +Cuautla +Anseong +Glendale +Guanajuato +Tébessa +Kamirenjaku +Holon +San Fernando +Pingtung +Peterborough +Tanauan +Nampa +Sūhāj +Pāndhurnā +Bené Beraq +Sosnowiec +Būkān +Muş +Fatehpur +Kumagaya +La Plata +Banī Suwayf +Lae +Quelimane +Hyesan +Béni Mellal +Jalapa +Guanare +Kaesŏng +Nossa Senhora do Socorro +Castanhal +Croydon +Ārba Minch’ +Rio Grande +Gorontalo +Florencia +Fianarantsoa +Tsing Yi Town +Yamaguchi +Iwo +Rāe Bareli +Godoy Cruz +Peoria +Ciudad del Carmen +Bago +Damān +Valera +Ouargla +Cedar Rapids +Manzanillo +Malaybalay +Armavir +Vancouver +Isidro Casanova +Chibia +Leganés +Hino +Orai +Shahr-e Kord +Rybinsk +Jhelum +Mahbūbnagar +Prokopyevsk +Pābna +Cap-Haïtien +Nova Friburgo +Saddiqabad +Balakovo +Ngaoundéré +Hagen +Chŏngju +Paradise +Poza Rica de Hidalgo +Murtazābād +Rājendranagar +Las Heras +Odawara +Abū Ghurayb +Anjōmachi +Araure +Donostia +Fuenlabrada +Pinar del Río +Kenema +Digos +El Progreso +Al Ḩasakah +San Francisco de Macorís +Taranto +Prabumulih +Kishiwada +Iquique +Desē +Gharyān +Kecskemét +Numazu +Ratanpur +Bournemouth +Bhusāval +Tottori +Alvorada +Jōetsu +Chaedŏk +Guatire +Chilpancingo +San Diego +Kōfu +Ucu Seles +Calbayog City +Kırıkkale +Burlington +Kielce +Ocala +Itabuna +Kairouan +Klerksdorp +Pasuruan +Bahraigh +Ed Damazin +Suva +Basildon +Getafe +Cachoeiro de Itapemirim +Potsdam +St. John's +Erie +Umreth +Dunhuang +Semnān +Narsingdi +Newport News +Temirtaū +Banja Luka +Ittikara +Yei +Mahesāna +Frederick +Kuchlagh +Murfreesboro +Ferraz de Vasconcelos +Bolu +‘Unayzah +Pak Kret +Seixal +Al Qāmishlī +Padalarang +Modena +Breda +Toyokawa +Vĩnh Châu +Siguiri +Cuango +Rāiganj +San Cristóbal +Fernando de la Mora +Trảng Bàng +Aqtaū +Santa Bárbara d’Oeste +Batāla +Niš +Oradea +Tumpat +Malumfashi +Isiro +Pinrang +Fort Lauderdale +Jaraguá do Sul +Sirsa +Morsi +Donghua +Odense +Nouméa +Dinapore +Sorsogon +Guarapuava +Tambaram +Bethelsdorp +Saarbrücken +Kāraikkudi +Shrīrāmpur +Abakan +Braga +Epe +Aurora +Kelowna +Plzeň +Fardīs +Himatnagar +Kindia +Tachikawa +Tempe +Thanhlyin +Turbo +La Rioja +Guna +Hamm +Nawāda +Cuauhtémoc +Ploieşti +Inisa +Berazategui +Ikot Ekpene +Funtua +Obuase +Toulon +Jaunpur +Mbanza Kongo +Edirne +Longjing +Geelong +Lhokseumawe +Mahād +Madanapalle +Danbury +Palopo +Petarukan +Guri +Singosari +Quảng Yên +Tongjiang +Longjin +Santander de Quilichao +Wadlakonda +Bhachāu +Shivpuri +Caluquembe +Hosa’ina +Tanjungbalai +Buḍhānilkanṭha +Uji +Mongu +Norilsk +Divo +Calabar +Reims +Dosquebradas +Roxas City +Masan +Purwakarta +Sātāra +El Khroub +Tiaret +Cuautitlán +Spring Hill +Njeru +Torbalı +Ilhéus +Al ‘Arīsh +Langsa +Gastonia +Phillaur +Quevedo +Tebingtinggi +Kandi +Nijmegen +Unnāo +Dundo +Meiktila +Salinas +Sītāpur +La Pintana +Ambato +Almada +Riobamba +Kalamboli +Ciudad Valles +Fredericksburg +Maidstone +San Luis Río Colorado +Letpandan +Hamilton +Al Juwayyidah +Cianjur +Ontario +Ar Raḩmānīyah +Wārāseonī +Castellón de la Plana +León +Mauli +Harnaut +Bejaïa +Elk Grove +Biu +Arafat +Rantau Prapat +Krasnogorsk +Catumbela +Shibirghān +El Bosque +Gainesville +Santa María Texmelucan +Matosinhos +Navadwīp +Salatiga +Marāgheh +Kotdwāra +Amadora +Durrës +Chicoloapan +Khapalu +Bodrum +Yangmei +Pīlibhīt +Sīrjān +Narashino +Cary +Curug +Rancho Cucamonga +Kangqiao +Timon +Burgos +Dagupan City +Ludwigshafen +Bắc Giang +Alleppey +Ada +Habaswein +Gliwice +Piedras Negras +Linjiang +Carúpano +Bida +Sakura +Townsville +Oceanside +Cuddalore +Pátra +Basel +Tlemcen +Lubao +Mexico +Gondal +Shāhīn Shahr +Syzran +Deo +Albacete +Fenglu +Keningau +Akhisar +Podgorica +Sherbrooke +Kamakurayama +Hạ Long +Oldenburg +Silchar +Chīrāla +Gadag +Santander +Saint-Étienne +Mang La +Hitachi +Jabālyā +Polomolok +Ongata Rongai +Sacaba +General Mariano Alvarez +Mülheim +Lagos de Moreno +Tiruvannāmalai +Kindu +Kaolack +Bīdar +Baranavichy +San Miguel +Bade +Talhar +Jalālābād +Izumo +Rafaḩ +Bir el Djir +Ait Melloul +Oeiras +Alcorcón +Cazanga +Garden Grove +Kempton Park +Volgodonsk +Lancaster +Al Kūfah +Araguaína +Metro +Nawsari +Surigao +Erciş +Reggio di Calabria +Makurdi +Bảo Lộc +Kohat +Petite Rivière de l’Artibonite +Ussuriysk +Hemet +Pembroke Pines +Ibirité +Hinthada +Nqutu +Escuintla +Fusagasugá +Malāyer +Olsztyn +Tomakomai +Aplahoué +Valsād +Thingangyun +As Suwayq +Métouia +Concepcion +San Luis +Barra Mansa +Cape Coast +Magwe +Panama City +Urayasu +Puerto Montt +Reggio Emilia +Vallejo +Al Marj +Saḩāb +Caluquembe +Medinīpur +Sŏsan +Talas +Minbya +Chūō-ku +Santa Rita +Manchester +Batumi +Bielsko-Biała +Chetumal +Kamensk-Ural’skiy +Nishio +Medford +Damoh +Kalalé +Kroonstad +Toliara +Bayamón +Calabozo +Baliuag +Harīpur +Mahābād +Neyveli +Bordj Bou Arreridj +Mauldin +Karaman +Moratuwa +Francisco Morato +Hŭich’ŏn +Ilford +Kasama +Gondomar +Novocherkassk +Malanville +Bāramūla +Pôrto Seguro +Jamālpur +Ríohacha +Santa Cruz +Catape +Hirosaki +Villa Canales +Malkāpur +Katha +Oyama +Basuo +Jīnd +Angra dos Reis +Phan Rang-Tháp Chàm +Osnabrück +Itapecerica da Serra +Mogok +Fethiye +Itu +Al Qurayyāt +Jutiapa +Palmdale +Zlatoust +Norwich +Chandannagar +Muskegon +Çerkezköy +Linhares +Tiantoujiao +Sampit +Uppsala +Villanueva +Ādoni +Alasandigutta +Tuguegarao +Siirt +Kuytun +Leesburg +Offa +Ipiales +Le Havre +Sudbury +Ratnapura +Niiza +Takaoka +Leverkusen +Kushiro +Iwata +Obihiro +São Caetano do Sul +Sawangan +Béchar +K’ebrī Beyah +Körfez +Singida +Warrington +Udipi +Saqqez +Fyzābād +Kökshetaū +Ebo +Manavgat +Fukang +Idlib +Sawāi Mādhopur +Lajes +Tenāli +Cienfuegos +Petropavlovsk-Kamchatskiy +High Point +Bocoio +Abengourou +Tuscaloosa +Conjeeveram +Hadano +Chillán +Abū Ḩulayfah +Madhubani +Arnhem +Quibala +Proddatūr +Baubau +San Martin Texmelucan de Labastida +Tunja +Teresópolis +San Marcos +Poços de Caldas +Sultānpur Mazra +Visalia +Wāpi +Blida +Sidon +Wau +Tokat +Garissa +Pocheon +Skikda +Muridke +Muzaffargarh +Kebili +San Fernando +Turgutlu +Jizzax +Ungaran +Kyaunggon +Huddersfield +Ube +Bandar-e Māhshahr +Nazilli +Simao +Anaco +Marysville +Gölcük +Haarlem +Boma +Tuxtepec +Sullana +San Andrés Tuxtla +Heidelberg +Darmstadt +Parnaíba +Mukono +Hala +Godhra +Rafsanjān +Sariaya +Multai +Perugia +Nador +Guadalajara +Tema +Zemun +Ndalatando +Salihli +Daule +Rājapālaiyam +Zhanlicun +Pangzawl +Bontang +Jinggang +Ash Shīḩānīyah +Papantla de Olarte +San Miguel del Padrón +Tulancingo +Tacheng +Tân Phú +Naic +Liancheng +General Roca +Zaranj +Al Ghardaqah +Kidapawan +Chittoor +Bragança Paulista +Solingen +Merced +Pindamonhangaba +Hayward +Split +Lafayette +Ziarat +Girón +Miyakonojō +Đồng Hới +Trà Vinh +Khanpur +Ferkessédougou +Southend +Ciudad Acuña +José María Ezeiza +Khōst +Koudougou +Saint-Marc +Ninh Bình +Bingöl +Dhamār +Sivakāsi +Huaycan +San Nicolás de los Arroyos +Osorno +Milagro +Ceyhan +Moga +Ede +Ekibastuz +Barreiras +Enschede +Springfield +Newport +Chirchiq +Dijon +Jijiga +Patos de Minas +Budaun +Ramat Gan +Uttarpāra +Catamarca +La Laguna +Daltonganj +Quipungo +Jequié +Benoni +Macuspana +Zhengding +Aral +Cadiz +Elektrostal +Matsuzaka +Klaipėda +Ōgaki +Corona +Zabrze +Bharūch +Ilagan +Abaetetuba +Guimarães +Ibarra +Paterson +Gojra +Banhā +Mahlaing +Alexandria +Calama +San Miguel +’s-Hertogenbosch +Grenoble +Amersfoort +Erzincan +Regensburg +George +Herne +Bima +Mandi Bahauddin +Châu Đốc +Severodvinsk +Angers +Itapetininga +Xiaping +Caxias +Villeurbanne +Nāblus +Nan Zhuang +Zaanstad +Guaymas +Enfield +Kamina +Molo +Macon +Saharsa +Chaoshan +Kétou +Rio das Ostras +Adzopé +Portmore +Binghamton +Qalyūb +Danao +Marbella +Ghorāhī +Lakewood +Bạc Liêu +Capas +Cẩm Phả +Almetyevsk +Al Miqdādīyah +Vidisha +Pathānkot +Nowgong +Ravenna +Ar Ramthā +Borāzjān +Tigaraksa +Kisi +Esmeraldas +Kansas City +Horad Barysaw +Souk Ahras +Daiwanishi +Chlef +Pandi +Camarajibe +Odessa +El Eulma +Salzburg +Thānesar +Danzao +Camacupa +Kishangarh +Ŭiwang +Hanam +Paderborn +Zango +Brăila +Barrie +Sunnyvale +Saint-Louis +Edremit +Rudarpur +Kitenkela +Tindwāra +Bandırma +Nalgonda +Jinghai +Hitachi-Naka +Lucapa +Neuss +Noda +Santana de Parnaíba +Ambikāpur +Madīnat as Sādis min Uktūbar +Dibrugarh +Singaraja +Moanda +Seogwipo +Palo Negro +New Bedford +Verāval +Mogi Guaçu +Hoeryŏng +Abbotsford +Kırşehir +Tochigi +Betūl Bazār +Andong +Bālurghāt +Bytom +San Jose +Jorhāt +Poblacion +Ixtlahuaca +Salavat +Kariya +Nevşehir +Krishnanagar +Dutse +Newcastle +Ueda +Livorno +Tete +Vĩnh Yên +Bārākpur +Hollywood +Sinpo +Pouso Alegre +Ciudad Choluteca +Hòa Thành +Alagoinhas +Mudon +Amatitlán +Gulu +Gwangyang +South Lyon +Imabari +Kawashiri +Oxford +Bordj el Kiffan +Gò Công +Erdemli +Gonbad-e Kāvūs +Al Manāqil +Shāntipur +Dīla +Hindupur +Araucária +Ipswich +Matanzas +Beāwar +Long Khánh +Bhālswa Jahangirpur +Aş Şuwayḩirah as Sāḩil +Tauranga +Miass +Erode +Escondido +Lake Charles +Dahuaishu +Minglanilla +Manzanillo +Chichicastenango +Đức Phổ +San Jose +Copiapó +Tafeng +Mahmutlu +Büyük Çakırman +Buğdaylı +Eminabad +Kragujevac +Pasadena +Bellevue +Logroño +Delicias +Talcahuano +Piedecuesta +Toledo +Higashimurayama +Ipokia +Jaranwala +Nāngloi Jāt +Joliet +Kukichūō +Badajoz +Champaign +Fengyicun +Shāhrūd +Mzuzu +Valle de Santiago +Đồng Xoài +Valdivia +Gölbaşı +Naogaon +Kashikishi +El Minié +Borj Hammoud +Auchi +Chauk Azam +Yilan +Sievierodonetsk +Urganch +Ocumare del Tuy +Willemstad +Bávaro +Soro +Pénjamo +Santa Rita +Mariveles +Pomona +Saumlaki +Villa de Álvarez +Lévis +Fairfield +Asaba +Kerch +Concordia +Mesquite +Lashio +Elkhart +Bohicon +Harrow +Rimini +Port Louis +Ağrı +Naperville +Maīmanah +Musashino +Kastamonu +St. George +Sagay +Roseville +‘Ajlūn +Marvdasht +Melitopol +Potchefstroom +Coquitlam +Nek’emtē +Xianshuigu +Nūzvīd +Abbottabad +Santiago +Lárisa +Ramapo +Coro +Sayama +Taza +Jean-Rabel +Al ‘Aqabah +Dundee +Sitārganj +Dongguazhen +Topeka +Al Ḩawīyah +Cagliari +Nîmes +Lüleburgaz +Consolacion +Maridi +Quilpué +Rafael Castillo +Zhangaözen +Nchelenge +Szombathely +Kutaisi +Komaki +Kiambu +Siem Reap +Orekhovo-Borisovo Yuzhnoye +Hājīpur +Pesquería +Kopeysk +Mati +Aix-en-Provence +Malakal +Chingola +Milas +La Lisa +Burlington +Kafr ash Shaykh +Tipitapa +Clermont-Ferrand +Hưng Yên +Tama +Dongsheng +Hābra +Yonago +Kramatorsk +Pageralam +Kalemie +Colima +Dawei +Maquela do Zombo +Wamba +Warner Robins +Cairns +Xinjing +Cam Ranh +Bīr +Florencio Varela +Odintsovo +Keren +Dar‘ā +West Bromwich +Derince +Gandajika +Colina +Dadu +Pleiku +Amreli +Taungdwingyi +Nārnaul +Jyväskylä +Chitaldrug +Pyatigorsk +Calapan +Calapan +Franco da Rocha +Mostaganem +Paço do Lumiar +Etah +Baní +Surprise +Gloucester +Qūchān +Vila Junqueiro +Torrance +Ereğli +Foggia +Iruma +Abohar +Thanatpin +Miskolc +Bhāndāria +Teixeira de Freitas +Villa de Cura +Facatativá +Arad +Fujita +Le Mans +Kisaran +Khairpur Mir’s +Bukoba +Kaithal +Arayat +Poole +Comayagua +Yima +Moshi +Saguenay +Urdaneta +Boca del Rio +Odivelas +Xintang +Barranca +Balasore +Guelph +Zhaxi +Mehtar Lām +Al Mukallā +Kolomna +Dar Naim +Cinere +Pinetown +Salamanca +Asaka +Ramu +Mallapalli +Santa Maria +Kakamigahara +Aalborg +Koiridih +Ciego de Ávila +Bandundu +Disūq +Calabayan +Coimbra +Ghaznī +Zhaoxiang +Ruse +Touggourt +Shillong +Athens +Houma +Cizre +Malasiqui +Lleida +Sannār +Los Ángeles +Rewāri +Paghmān +Birkenhead +Jinjiang +Tinipuka +Cartago +Ashikaga +Stara Zagora +Telford +Nakhodka +Columbia +Huelva +Garanhuns +Hazāribāgh +Moundou +Trindade +Nizip +Nawābganj +Toda +Fullerton +Lichinga +Settat +Bafra +Bhīmavaram +Negombo +Olathe +Okinawa +Namacunde +Boca Chica +Altay +Bahawalnagar +Misato +Olmaliq +Moriguchi +Preston +Thornton +York +Mandsaur +Jahrom +Bondoukou +Lausanne +Bocaue +Khuzdar +Tepatitlán de Morelos +Pemba +Elbistan +Bilbays +Berezniki +Piteşti +Gweru +Khasavyurt +Tân Châu +Numan +Kamālshahr +Rize +Subang +Villupuram +Ingolstadt +Las Maravillas +Greeley +Tabaco +Fukayachō +Beaumont +Nsele +Las Cruces +Porac +Mejicanos +Krugersdorp +Middlesbrough +Shizhaobi +Paranaguá +Sāmarrā’ +Iguala de la Independencia +Ozamiz City +Harchandpur +Yaritagua +Midland +Kumbakonam +Metairie +Torbat-e Ḩeydarīyeh +Xiangcheng +Batu Pahat +Nāḩiyat Ghammās +Peristéri +Darwin +Reykjavík +Atbara +Kanasín +Giá Rai +Payatas +Debre Birhan +La Romana +Aul +Brest +Masaya +Malambo +Rubtsovsk +Momostenango +Pakpattan +Botucatu +Jicheon +Tanay +Blackpool +Carolina +Trois-Rivières +Balneário de Camboriú +Maykop +Balkanabat +Dos Hermanas +Apeldoorn +Pantanal +West Valley City +Ishizaki +Çarşamba +Kuwana +Orange +Warren +Gemena +Sancti Spíritus +Whitby +Cambridge +Kolār +Grand Junction +Médéa +Teluknaga +Tarragona +Koga +Dipolog +Dabou +Tsuchiura +Tyler +Jacmel +Chicacole +Montero +Koutiala +Shūnan +Candelaria +Silivri +Mthatha +Gaziemir +Brusque +Tours +Iğdır +Kovrov +Gunungsitoli +Negage +Pasadena +Teófilo Otoni +La Trinidad +Bānkura +Mandya +Palhoça +Norrköping +Jolo +Kusatsu +Cunduacán +Dehri +Hampton +Sinfra +Myebon +Atibaia +Quillacollo +Medina Estates +Stockport +Kanata +Rangkasbitung +Porto Amboim +Minō +Durgauti +Curicó +Vila Franca de Xira +Igboho +Gingoog +Phủ Lý +Marīvān +Huehuetenango +Barrechid +San Justo +Shizuishan +Mainpuri +Nasugbu +Bloomington +Quimbele +Varginha +Port-Gentil +Carcar +Qabr as Sitt +Chech’ŏn +Campo Largo +San José del Cabo +Cachoeirinha +Termiz +Zinacantepec +Batang +Bacău +Norzagaray +Yaizu +Talaivāsal +Kisarazu +Ağcabədi +Birgañj +Dīsa +Tobruk +Elizabeth +Ebina +Floridablanca +Gitega +Rionegro +Stamford +Nkongsamba +Yuma +Raigarh +Yalova +Maia +Ituzaingó +Tây Ninh +Kigoma +Kent +Miramar +Andīmeshk +Tizi Ouzou +Agboville +Siwān +Maldonado +Ipetumodu +Shahreẕā +Zābol +Inazawa +Caraguatatuba +Pyay +Silopi +Djakotomé +El Oued +Mörön +Bern +Coeur d'Alene +Ashqelon +Minoo +Mabacun +Laghouat +Salto +Sibiu +Brighton +Zafarwal +Londuimbali +Cametá +Vitória de Santo Antão +Dumaguete City +Navoiy +Marianao +Sale +Luuq +Ba Đồn +Gubeng +Daraga +Veszprém +Lakewood +Famalicão +Debre Mark’os +Tiraspol +Coral Springs +Ruda Śląska +Rybnik +Idfū +Tokha +Sterling Heights +Kalol +San Andres +Marituba +Amiens +Yunxian Chengguanzhen +Ferfer +Renca +Ségou +Jaú +Tando Allahyar +Thandwe +Hagonoy +Hassan +Batu Gajah +Matagalpa +Lalitpur +Conchalí +Santa Cruz do Sul +Long Bình +Pitalito +Bibémi +Naga +Porto-Novo +Uppsala +Yuba City +Khorramshahr +Tarime +Crato +Male +Parla +Milton +M’Sila +Srīpur +Kipushi +Gondiā +Zhangmu Touwei +Schaarbeek +Luxembourg +Reẖovot +San Carlos +São Mateus +Dalūpura +Mogaung +Ödemiş +Kingston +San Luis +Centro Habana +Zagnanado +Isahaya +Yakima +Adonara +Talavera +Rustavi +Carrollton +San Juan +Āwarē +Naryāi ka Puri +Blitar +Racine +Karabük +Palwal +Ōme +Chicomba +Johnson City +Annecy +Papeete +Cubatão +Conselheiro Lafaiete +Ji-Paraná +Al Khmissat +Billings +Jijel +Fürth +Buea +Apopa +Itapipoca +Spanish Town +Lam Tin +Pālghāt +Atyraū +Araras +Maijdi +Kuşadası +Vlorë +Quibdó +Marand +Bassila +Thaton +Zama +Chittandikavundanūr +Iowa City +Nanqiaotou +Shuangcheng +Surat Thani +Narita +Lào Cai +Trinidad +Innsbruck +Kozan +Angono +Silay +Ānand +Jīroft +City of Isabela +Nantou +Los Guayos +Inezgane +Arcahaie +Botād +Abiko +Tabarre +Udon Thani +Baiyashi +Lincoln +Mojokerto +Dover +Onomichi +Lucheng +Apucarana +Jamundí +Ergani +Relizane +Polokwane +Aaley +Cili +Battambang +Taldyqorghan +Huejutla de Reyes +Bongaigaon +Bellingham +Mukeriān +Zwolle +Girardot +Vespasiano +Limoges +Charleston +Ponnagyun +Sukrah +Araruama +Çayırova +Hanumāngarh +Jetpur +Arcot +Kokubunji +Amherst +Kānchrāpāra +Parepare +Ciénaga +Sabará +Chinguar +Ferrara +Antsiranana +Tottenham +Mansa +Jamaame +Petapa +Mataró +Cholula de Rivadabia +Idanre +Bamban +Harar +Sarhari +Guadalajara de Buga +Dörtyol +Ulm +Bragança +Mubi +Guagua +Lynchburg +Songnim +Bat Yam +Kislovodsk +Ōsaki +Santo Tomas +Västerås +Puno +Hoshangābād +Táriba +Fengcheng +Saïda +Rosario +Heilbronn +Chās +Apatzingan de la Constitucion +Tuaran +Rudnyy +Nefteyugansk +Khanna +Ahmadpur East +Wazirabad +Santa Clara +Avrankou +Vihari +Guasdualito +Mosquera +Dongsheng +Garut +Votorantim +Buôn Hồ +Abaji +Salmās +Domodedovo +Pforzheim +Edremit +Würzburg +Jāzān +Norman +Santa Lucía Cotzumalguapa +Guimba +Greenville +Simi Valley +Latina +Zhaozhou +Dessalines +San Ignacio +Ciudad Sandino +Allada +Iwakuni +Nagda +Bam +Opole +Kayes +Koforidua +Seto +Ōmiyachō +Missérété +Koganei +Salerno +Torrejón de Ardoz +Saidpur +Nāḩiyat Khān Banī Sa‘d +Angren +Pakokku +Leiria +Sertãozinho +Hardoī +Ñemby +Neftekamsk +Kamëz +Bataysk +Santa Cruz +Ünye +Fuyuan +Duitama +Gurabo al Medio +Ajax +Barcarena Nova +Catabola +Novocheboksarsk +Ksar El Kebir +Örebro +My Drarga +Fort Smith +Suhum +Jandira +Abilene +San Juan +Valinhos +Altamira +Guntakal +Mary +Iizuka +Pithampur +Karatepe +Wolfsburg +Serpukhov +Rosetta +Arecibo +Giresun +Kombolcha +San Fernando +Shchelkovo +Payakumbuh +Pātan +Bonao +Barbacena +Cam Ranh +Zoetermeer +Basīrhat +Resende +Nzega +Villa Alemana +Gashua +Polatlı +Lewisville +Larache +Kristiansand +Az Zulfī +Jilib +Cerro +Hālīsahar +Magelang +Jagādhri +Tychy +Salto +Juticalpa +Novomoskovsk +Jizhou +Leuwiliang +Koidu +Guarapari +Pinsk +Sunām +Ouahigouya +Rishra +Grand-Bassam +Jhārsugra +Leeuwarden +Pearland +Bến Tre +Lehigh Acres +Glazoué +Panevėžys +Chinautla +New Mirpur +Serik +Chía +Kenosha +Noksan +Bajos de Haina +Magalang +Exeter +Bimbo +Pāli +Leiden +Kadirli +Fugu +Savannakhet +Khrustalnyi +Kaspiysk +Magangué +Cambridge +Kirishima +Cai Lậy +Maicao +Pobé +Derbent +Giugliano in Campania +Pervouralsk +Algeciras +Paleng +Santa Cruz +Akçaabat +Djidja +Dūmā +Longtian +Seaside +Punta Arenas +Itaituba +Burlington +Três Lagoas +Nāndgaon +Orizaba +Bento Gonçalves +Honmachi +Baraki +Arvada +Alwāl +Gapan +Kaya +Ed Damer +Munch’ŏn +Gexianzhuang +Kilifi +Zacatecas +Romford +Dimāpur +Patnos +Shiyan +Pati +Kâhta +Yuanlin +Bayawan +Machiques +Hoima +San Pedro Garza García +Bayan Lepas +Ciudad Hidalgo +Behbahān +Itatiba +Cagua +Barretos +Ise +Puerto Cortés +Maina +Indramayu +Cherkessk +Tam Kỳ +Uruma +Sarangāpuram +Mufulira +Waldorf +Ereğli +Independence +Ciudad Ojeda +Bhadrakh +León +Gyumri +Dharmavaram +Rochester +Colchester +Monza +Sivaganga +Chinandega +Hội An +Logan +Farīdpur +Springs +Dorūd +Kashiwara +Kuopio +Ondjiva +Alberton +Temperley +Harlingen +Waterloo +Dordrecht +Kamalia +Berkeley +Tsuruoka +Lianhe +Ar Rass +Hòa Bình +Doğubayazıt +Port-de-Paix +Upington +Wuling +Samandağ +Chơn Thành +Puruliya +Mīt Ghamr +Tabuk +Kırıkhan +Sassari +Ghazīpur +Ch’ungmu +Apartadó +Tumen +Thành Phố Uông Bí +Jīma +Genhe +Navojoa +Porlamar +Anderlecht +Metz +Winterveld +Renala Khurd +Chilapa de Álvarez +Coatepeque +Bagaha +Gudiyāttam +Mahuva +Catchiungo +Fier +Clovis +Kotamobagu +Gurdāspur +Kỳ Anh +Kot Addu +Round Rock +Barcelos +Pueblo +Ramos Mejía +High Wycombe +Gemlik +Temple +Söke +Khurda +Delgado +Poblacion +Kandy +Ban Bang Pu Mai +Gateshead +Guelma +Unwana +Ar Rustāq +San Carlos +Baraka +Ghardaïa +Nokha +Jandrapeta +Hengken +Đông Hòa +Gorzów Wielkopolski +Terrebonne +Lalo +Pelabuhanratu +Yelahanka +Meridian +Malindi +Almirante Tamandaré +Ebetsu +Boulogne-Billancourt +Moncton +Yüksekova +Palma Soriano +An +Jiangna +Perpignan +Pleven +Fulgāzi +Bergamo +Shuixi +Sanxi +Naz̧arābād +Candaba +Parow +Tizayuca +Moḩammad Shahr +Silifke +Port Dickson +Besançon +Debre Tabor +Arapongas +Guaratinguetá +Slough +Aw Dheegle +Ţūz Khūrmātū +Birigui +Totonicapán +Göttingen +Huaraz +Daitōchō +Darjeeling +Piraquara +Bet Shemesh +Bismil +Vihiga +Pescara +Miramar +Sopur +Nowshera +Duluth +Bandar-e Anzalī +Bruges +Yongqing +Calumpit +Bwana Mkubwa +Butwāl +Senador Canedo +The Woodlands +Chikmagalūr +Matamoros +Kadoma +Guelmim +Orekhovo-Zuyevo +Yangliuqing +Xuqiaocun +Soasio +Kampong Cham +Dinalupihan +Malita +Niğde +Gudivāda +Bama +Lahti +Bottrop +Quyang +Ilobu +Ligao +Boulder +Richardson +Trento +Nouadhibou +Dongsheng +Blackburn +Cambridge +Phagwāra +Bagé +Nazran +Aizuwakamatsu +Lodhran +Tahoua +Matsubara +Araguari +Gogounou +Agadez +Pudukkottai +Saanich +Nobeoka +Charallave +Uribia +West Palm Beach +Luanshya +Banyuwangi +Reutlingen +Handa +Kabwe +La Asunción +Salmān Bāk +Catacamas +Tangjin +Midsayap +Port Arthur +Heroica Guaymas +Munūf +East Los Angeles +Uruguaiana +Banfora +Harshin +Adilābād +Redding +Apalit +Yulu +Umuarama +Alcobendas +Cassongue +Clearwater +Fāqūs +Dąbrowa Górnicza +Monroe +Kapaklı +Baripāda +Soreang +Kōnosu +Samal +Datu Odin Sinsuat +Utica +Manpo +Nevinnomyssk +Tatuí +St. Cloud +Mandeville +Chimaltenango +Orléans +La Granja +Erlangen +Yavatmāl +Titāgarh +Ikoma +Lira +Honchō +Barnāla +Cheltenham +Forlì +Chittaurgarh +West Jordan +Xai-Xai +Gabès +Tecomán +Boké +Coronel +Narasaraopet +Siracusa +Himamaylan +Ocaña +Ballarat +Ādīgrat +Dharān +Smithtown +Fatsa +Temixco +Bongao +Ramenskoye +Aïn Beïda +Dimitrovgrad +Karatsu +Nagahama +Târgu-Mureş +Sogamoso +San Tung Chung Hang +Kyzyl +Beppu +Adjaouèrè +Ra’s al Khaymah +Valle de La Pascua +Urasoe +São Gonçalo do Amarante +Sri Jayewardenepura Kotte +Catanduva +Várzea Paulista +North Charleston +San Ildefonso +Linköping +Formosa +Tarogong +Richmond +Nasushiobara +Nusaybin +Pedro Juan Caballero +Ribeirão Pires +Sandvika +Westminster +Bremerhaven +Uzhhorod +Saginaw +Kōenchō +Kovilpatti +Ar Rumaythah +Kasese +Bijnor +Chelmsford +Los Baños +Puerto Madryn +Koblenz +El Pueblito +Kailua +Sliven +Elbląg +Igarassu +Guanabacoa +Aihua +Mendoza +Al Muḑaybī +Obninsk +Swabi +Ciudad Guzmán +Tlapacoyan +Bu’aale +Turbaco +Niihama +Al Manşūrah +Brandon +Amasya +Ponce +Rawajaya +Rawasari +Sano +Dam Dam +Las Delicias +Carlsbad +Lowell +Qiaotou +Hatsukaichi +Bijeljina +Maghnia +Płock +Shacheng +Sach’on +Thimphu +Ariana +Iriga City +Borås +Simões Filho +Plaridel +Catalão +Jaramānā +Kaikkudi +Codó +Yŏju +Broken Arrow +Ambovombe +Sakété +Elgin +Alphen aan den Rijn +Lugazi +Machakos +Rouen +San Juan +Bintulu +Petržalka +Matéri +Kakegawa +Saint-Denis +Oktyabr’skiy +Zhijiang +Kuningan +Helsingborg +Quilengues +Tawau +Poá +Sakiet ed Daier +Īrānshahr +Ifanhim +Euriápolis +Ndali +Gresham +Pul-e Khumrī +Fujimino +Comitán +League City +Mungo +Guiguinto +Midyat +Itabira +Sa-ch’on +Bukittinggi +Sātkhira +Novyy Urengoy +Manolo Fortich +Sloviansk +Akçakale +Hikone +Mangaldan +Hōfu +Mostar +Tōkai +Yessentuki +Downey +Sokodé +Libmanan +Hosan +San Carlos de Bariloche +Paulo Afonso +Tecpán Guatemala +Kazo +Jönköping +Bergisch Gladbach +Tayabas +Remscheid +Carora +Lomas de Zamora +Balāngīr +Shegaon +Macul +Ōshū +Nkayi +Higashiōmi +Otaru +Ciudad de la Costa +Badin +Kisii +Santa Lucía +Goth Tando Sumro +Waterloo +Shkodër +Madrid +Kaposvár +Mahāsamund +Trier +Rājpura +Hezuo +La Libertad +Pochuta +Dassa-Zoumé +Akishima +Bāgalkot +Osmānābād +Estelí +Paarl +Kouandé +Kaliānpur +Shujālpur +Sāhibganj +Passos +Subic +Murrieta +Jaén +Fujimi +Leominster +Longview +Baybay +Jacksonville +Ichinoseki +Cádiz +Pompano Beach +Colatina +Bou Saada +Bou Saâda +Bend +Alkmaar +Recklinghausen +Mawanella +Daet +Nova Lima +Araxá +Laoag +Miami Gardens +Chilón +Chiquimula +Sabara Bangou +Costa Mesa +Baleraja +Montreuil +Villa Mercedes +Sioux City +Ghotki +Jawhar +Santamesa +Kasempa +Champdani +São Lourenço da Mata +Ḩaraḑ +Jena +Gafsa +Ariquemes +Kasuga +Kamyshin +Nsukka +Kintampo +Nandurbar +Purwa Utar +Tuzla +Kaiyun +Namur +Khushab +Everett +Puerto Barrios +Fasā +Rosario +Gilroy +Kiffa +Al Aḩad al Masāriḩah +As Salamīyah +Sorriso +Franceville +San Antonio Enchisi +Quíbor +Socopó +Ahuachapán +Manzini +Masindi +Muktsar +Martínez de la Torre +San Buenaventura +Changbang +Empangeni +Ferozepore +Bāneh +Itele +Rochdale +Jeonghae +Shirayamamachi +Temecula +Tubarão +Sugar Land +Ōmuta +Chico +Msaken +Tinaquillo +Bridgetown +Wythenshawe +Mancherāl +Qal‘at Sukkar +Pisco +Retalhuleu +Bernal +Sutton Coldfield +Vicenza +Doncaster +Winterthur +Dali +Rotherham +Dera Ismail Khan +Esteban Echeverría +Quezon +Aguachica +Naujan +Glan +Bayugan +Eau Claire +Brovary +Gualeguaychú +Delft +Walthamstow +Drammen +Medenine +Nkpor +Kamagaya +Tacurong +Malacatán +Taoyang +Béja +Yŏngju +Labo +Sandachō +Berkane +Sầm Sơn +White Rock +Marugame +Tangjia +Sehore +Murom +Soma +Handeni +Balombo +Talisay +Taitung +Bangaon +Dolgoprudnyy +Thunder Bay +Pulilan +Maxixe +Kasserine +Bagu Na Mohra +Maţrūḩ +Baia Mare +Shihuajie +Tondabayashichō +Bà Rịa +El Monte +Witbank +Khopoli +Ferizaj +Xishancun +Mascara +Khenchela +Taungoo +Móng Cái +Idaho Falls +Melipilla +Komatsu +Islāmābād +Yāsūj +Khardah +Cawayan +Reus +Mopti +Delta +Dearborn +Toowoomba +Mungeli +Bloomington +Alto Hospicio +Habikino +Novoshakhtinsk +Maramag +Yevpatoriia +Caen +Nantang +West Covina +Tādpatri +Birnin Kebbi +Acharnés +Yilong +Sarh +Sparks +Mudanya +An Nuhūd +Žilina +Zhukovskiy +Itumbiara +Rijeka +Douliu +Seversk +South Fulton +Baía Farta +Mazhang +Castelar +Villa Krause +Balsas +Lingayen +Centennial +Labé +Gharbara +Chaman +Sultānpur +Joyabaj +Umm Qaşr +Kogon Shahri +Kawit +Kotmale +Mineshita +Shikohābād +Morales +Liberec +Santana +Shinyanga +Basingstoke +Jalpāiguri +Manokwari +Shāmli +Argenteuil +Sandy Springs +Wa +Cambé +Bhilai Karanja +Chongshan +Ilebo +La Gi +Ejido +Emmen +Edison +Reutov +Banté +Luján +Bagong Silangan +Noyabrsk +Yopal +Erdenet +Inglewood +Suriāpet +Kalmunai +Tajimi +Chābahār +Artëm +Tenggarong +Maipú +Hillsboro +Columbia +Crawley +Açailandia +Roquetas de Mar +As Safīrah +Wardha +La Banda +Catbalogan +Htison +Rānībennur +Burbank +Longjiang +Terni +Mulhouse +Nakhon Si Thammarat +Berdiansk +Toufen +Kŭlob +Kemalpaşa +Ad Dakhla +Tiaong +Tarnów +Carmona +Dagenham +El Limón +Barnoi +Bolzano +Raba +Mingəçevir +Sītāmarhi +Lokossa +Nanaimo +Sơn La +São Pedro da Aldeia +Contramaestre +Khanty-Mansiysk +Bình Hòa +Atebubu +Koszalin +Idkū +Granada +Lo Barnechea +Davie +Kishanganj +Sông Cầu +Temoaya +Kiryū +Jataí +El Cajon +Erechim +Tikrīt +Hindaun +Azare +Semāri +Jurupa Valley +Lerma +Tarīm +Nova Serrana +Japeri +Paragominas +Gangāwati +Bình Long +Pushkino +Maluñgun +Allen +Šabac +Charsadda +Alchevsk +Robāţ Karīm +Auburn +Renton +Nautanwa +Mazyr +Xiva +Moers +Achinsk +Ash Shaykh ‘Uthmān +Lagarto +Jamālpur +Kongolo +Mityana +Bulan +Yozgat +Texcoco +Nikopol +Alaşehir +Holland +Sultan Kudarat +Maranguape +Ndjamba +Makrāna +Al Fāw +Jincheng +Planaltina +Sheopur +Kandhkot +Pergamino +Tagbilaran City +Los Minas +Parral +Lavras +Coronel Fabriciano +Brockton +Włocławek +Brantford +Tenancingo +Presidente Franco +Tuyên Quang +Olanchito +Sergiyev Posad +Salzgitter +Masbate +Gödöllő +Ballia +Wałbrzych +Barika +Rio Rancho +Yelets +Girona +Suruç +Šiauliai +Nancy +Ourense +Aquin +Techiman +Chorzów +Tam Điệp +Balanga +San Mateo +Gillingham +Kanoya +Ikeda +Arzamas +Muriaé +Halifax +Tādepallegūdem +Natitingou +Chatham +Ourinhos +Sindangan +Chicoutimi +Bānsbāria +Tula de Allende +Lida +Toride +Salford +Rialto +Ilopango +Masaka +Spokane Valley +Saijō +Charlottesville +Kilinochchi +Bacabal +Menifee +Orsha +Daly City +Uitenhage +Biak +Wigan +Roncaglia +Itacoatiara +Berdsk +Elista +Yunfu +Musoma +Breves +Buzău +Mubende +Francistown +Lower Hutt +Woodbridge +Tanga +Ubá +Hounslow +Bumba +Būndi +Bergama +Chikushino +Patos +Itanhaém +Aracruz +San Rafael +Inzai +Iguatu +Camboriú +Miryang +Tanjungpandan +Santo Antônio de Jesus +Bendigo +Bouskoura +Paniqui +Amarāvati +Parang +Negapatam +Sangju +Santa Rosa +Buxar +Wembley +Caieiras +Telde +Kurichchi +Hinche +Hōyachō +Guihulñgan +Saint Helens +Gürsu +Jirjā +Noginsk +Kheda +Siegen +Tezpur +Wichita Falls +Riverview +Piacenza +Messaad +Sundarnagar +Houzhuang +Gütersloh +Mayarí +Seoni +Ngong +Ban Mangkon +Mustafakemalpaşa +Kāshmar +Bahrain +Aurangābād +Joünié +Ituiutaba +Mositai +Matehuala +Rishīkesh +Simanggang +Yishi +Isehara +Novokuybyshevsk +Shibuya +Şabrātah +Lahān +Aflou +Al Fqih Ben Çalah +Fugangcun +Al Jumayl +Dhangaḍhi̇̄ +Tavşanlı +Norwalk +Worcester +Shūshtar +Tota +Tando Muhammad Khan +Hildesheim +Kapūrthala +Boryeong +Al Ḩajar al Aswad +Olomouc +Zonguldak +Lee's Summit +Dhamtari +Rāneswar +Chishtian +Longmont +‘Ibrī +Vacaville +Nantou +Ōnojō +Brājarājnagar +Eastbourne +Sūjāngarh +Highlands Ranch +Ciudad Río Bravo +Bhadreswar +Pavlohrad +Clarington +Hengnan +Assis +Klagenfurt +Chilakalūrupet +San Luis de la Paz +Hanau +Hikkaduwa +Sungai Penuh +Kingsport +Chaguanas +Jingping +Novara +Kousséri +Aïn Oussera +Deventer +San Vicente de Baracaldo +Kaiserslautern +KwaDukuza +San Tan Valley +Ngã Bảy +Hồng Ngự +Jeypore +Zomba +Daoukro +Santa Cruz +Itaperuna +Oran +Mporokoso +Quincy +Edinburg +Kāranja +Aboisso +Salavan +Xırdalan +Saint-Jérôme +Red Deer +Sakado +Bỉm Sơn +Kefar Sava +Chanwari +Xinhua +Sungailiat +Sittwe +Zheleznogorsk +Concepción del Uruguay +Leer +Cavite City +Playas de Rosarito +Lynn +Ahar +Yên Bái +Weifen +Sur +Port Blair +Nāḩiyat al Iskandarīyah +Kalyani +Masjed Soleymān +Calasiao +Datia +Kamianets-Podilskyi +Itauguá +Torbat-e Jām +Diourbel +Madhavaram +Kawachinagano +Libertad +Shahrisabz +Gangtok +Vaciamadrid +Boconó +San Felipe del Progreso +Chust +Lamitan +Talipao +San Angelo +Sabanalarga +Zelënodol’sk +Pyinmana +Zahlé +Bāmyān +Mbanza-Ngungu +Banzhuangcun +Ra’s Ghārib +Gwelej +Babīlē +Dzolokpuita +Suramāla +Navalyal +Barod +Vāsco Da Gāma +Khambhāliya +Abbigeri +Kundli +Ad Dujayl +Mumias +Luckeesarai +Songea +Pouytenga +Sololá +Mabai +Debre Zeyit +Dunedin +Hesperia +Nabire +Sundsvall +Kadaiyanallūr +Sayaxché +Ciamis +Putatan +Xiongzhou +Urgut Shahri +Fengning +Bowling Green +Shahdadpur +Federal Way +Talara +Menglang +Kani +Cottbus +Tinsukia +Santa Cruz del Quiché +Hualien +Carmel +Bismarck +Campo Mourão +Alaminos +Xiegang +Tellicherry +Jinotega +Itārsi +Izumisano +Thika +Bislig +Abhar +Viseu +Malate +Ginowan +Wakefield +Sakata +Khamīs Mushayţ +Pili +Pickering +Hasilpur +Trincomalee +Riberalta +Morón +Fishers +Kohīma +Sisophon +Tourcoing +Vereeniging +Mīāneh +Heshan +Paoy Paet +Weldiya +Nyeri +Roubaix +Lafayette +Tobolsk +Burzaco +Espejo +Kontagora +Senahú +Khambhāt +Santiago de Compostela +Ixmiquilpan +Caldas Novas +Schwerin +Salihorsk +Reyhanlı +Itoshima +Zárate +Oton +Manacapuru +Sopron +Koytendag +Abreu e Lima +Lorca +Chipata +Lethbridge +Vista +Chikusei +Ancona +Saku +Silvassa +Santa Cruz do Capibaribe +Babahoyo +Lugo +M’lang +Leme +Las Rozas de Madrid +Río Grande +Boca Raton +Moortebeek +Chitose +Bălţi +Smederevo +Rāmnagar +Trelew +Sarapul +Kamloops +Montego Bay +Catarman +Valença +Saint-Jean-sur-Richelieu +Marmaris +Udine +Janakpur +Serov +Nanxicun +St. Augustine +Paulínia +Kambar +Al Jammālīyah +Itaúna +Ipojuca +Quilenda +Gambēla +Kallithéa +Deoni Buzurg +Radès +Mariano Roque Alonso +Hammamet +Aşağıçinik +Beaverton +Votkinsk +Goodyear +San Cugat del Vallés +Tartu +Tsuyama +Isulan +Kallūru +Kōnan +Lajeado +Adjarra +Dongducheon +Portsmouth +Attock Khurd +Nanterre +Fanyang +Yacuiba +Vị Thanh +Pernik +Andria +Pará de Minas +Orem +Worcester +Modi‘in Makkabbim Re‘ut +Ukhta +Munakata +Maribor +Harnai +Didim +Colón +Gävle +Tumbes +Es Senia +Bodināyakkanūr +Quvasoy +Paletwa +Caotun +Tamazunchale +Arauca +Tela +Paraíso +Do Gonbadān +Sumber +Kāzerūn +Francisco Beltrão +Sangolquí +Iida +Votuporanga +Tiddim +Buin +Bab Ezzouar +Oldham +Zhudong +Sunrise +Ōmura +Half Way Tree +Pejë +Wujiaqu +Harihar +České Budějovice +Tomohon +Parintins +Navapolatsk +Osijek +Greece +Corumbá +Arezzo +Cáceres +Vitry-sur-Seine +Doudian +Lysychansk +Escalante +Portsmouth +Itabaiana +Leninsk-Kuznetskiy +Chārīkār +Arden-Arcade +Harran +Lawrence +Muzaffarabad +Bachhraon +Toms River +Hammersmith +Amalner +Balayan +Sardārshahr +Kalisz +Witten +Longkeng +Longquan +Vanderbijlpark +Vilhena +Río Gallegos +Weiyuan +Caseros +Cesena +Tataouine +Dhuliān +Hadera +Arifwala +Sandy +São Cristóvão +Rayleigh +Paramagudi +Zerakpur +Slidell +Tandwa +Shirē +Kamisu +Çubuk +Burdur +Candelaria +Mezhdurechensk +Aliağa +Pesaro +Erbaa +Tiruchengodu +Hương Thủy +Mons +Pedagādi +Oued Zem +Boli +Nagīna +Bogo +Saint-Michel de l’Atalaye +Chākdaha +Shimada +Closepet +Jiantang +Dovzhansk +Birecik +Emmiganūr +Yalamakūru +Balamban +Siguatepeque +Labuan +Naxçıvan +Rawānduz +Yumbo +Vāniyambādi +Al Jīzah +Buckeye +Rubio +Tiruttani +Mianwali +Dongping +Meizichong +Nāḩiyat al Karmah +Moca +Rincón de Romos +Ocotlán +Sibolga +Esslingen +Cereté +Hemel Hempstead +Livonia +San Ramón +San Martín Jilotepeque +Williamsburg +Bouna +Legnica +Kangan +Suffolk +Los Patios +Compton +Lecce +Bath +Behshahr +Lopez +Lingwala +La Crosse +Bhadohi +Kanuma +Gjakovë +Edmond +San Joaquín +Gerona +Carson +Allinagaram +Gatchina +Bayan Hot +Niagara Falls +Villa Luzuriaga +Marmagao +San Marcos +Pingyuanjie +Greenburgh +Shibata +Ludwigsburg +Gießen +Ashiya +Yishui +Yi Xian +Tracy +Paracatu +Herẕliyya +Monkayo +Ponta Porã +Hayes +Rio Largo +San Fernando +Azumino +Prescott Valley +Dhār +Qormi +San José del Rincón Centro +Middletown +Valongo +Alvand +Lingtang +Đông Hà +Menderes +Chiantla +Simeulu +Ziftá +Cape Breton +Al Fujayrah +Sanjō +Mikhaylovsk +Fall River +Gera +Jaén +Mairiporã +Lawang +San Germán +Hradec Králové +Daanbantayan +Sarov +Michurinsk +Monastir +Wangjia +Yashio +Jinbi +Akşehir +Yotsukaidō +Düren +Chilliwack +Santa Cruz Xoxocotlán +Lemery +Indanan +Santa Fe +Râmnicu Vâlcea +Fenggang +Tual +Plantation +Galle +Mayagüez +Itajubá +Kwekwe +Glazov +Darlington +New Braunfels +Sidi Slimane +Créteil +La Marsa +Ubatuba +Ciudad General Belgrano +Rafaela +Wukari +Guaíba +Barra do Piraí +Palimbang +Nisshin +Hanamaki Onsen +Tübingen +Boundiali +La Reina +Magadan +Roswell +Wimbledon +San Sebastián de los Reyes +Tatvan +Kadugli +Akot +Tamanrasset +Helmond +Inagi +Solikamsk +Mtwara +Rongwo +Sablayan +Azua +Naju +Mogi Mirim +Flensburg +Iserlohn +São João da Boa Vista +Oss +Conroe +Barletta +Bedford +South Gate +Errachidia +Tatakan +Sepatan +Kitakami +Serra Talhada +Contai +Santa Barbara +Ōbu +Teziutlan +Santa Monica +Pardubice +Coatepec +La Spezia +São Roque +Voskresensk +Kirkland +Hoover +Kot Kapūra +Ústí nad Labem +Netrakona +Hove +Cruzeiro do Sul +Satsumasendai +Shabqadar +Pato Branco +O'Fallon +Hamilton +Higashi-Matsuyama +Tultepec +Kakamega +Raub +Ţurayf +Southport +Mijas +Ouidah +Goalundo Ghāt +Sinendé +Linquan +Phú Thọ +Maracay +Çaycuma +Niono +Grahamstown +Myingyan +Alafaya +Brossard +Satu Mare +Pīrānshahr +Yao +Samālūţ +Kārwār +Mīzan Teferī +Arzew +Salaman +Māndvi +San Francisco Solano +Tucuruí +Kalamariá +Jauharabad +Kendu Bay +Hilversum +Rāyachoti +Avaré +Pāloncha +Manhuaçu +Caçapava +Xiancun +Palm Coast +Alessandria +Ambohimangakely +Hastings +Norwalk +Agua Prieta +Lawton +Chino +Lachhmangarh Sīkar +Maple Ridge +Miaoli +Mount Pleasant +Velikiye Luki +Tonacatepeque +Grudziądz +Guercif +Solwezi +Cisauk +Caimbambo +Oudtshoorn +Bauan +Pantukan +Pongotan +Rāmagiri Udayagiri +Cambambe +Gwadar +Mengdingjie +Manteca +Funza +Zhezqazghan +Çanakkale +Katiola +Westminster +Biga +Eslāmābād-e Gharb +Chililabombwe +Vanadzor +Fundación +Ponnāni +Sahagún +Arsuz +Jāmtāra +Florence +Joplin +Pinamalayan +Avignon +Orpington +Valjevo +Julu +Pazardzhik +Vezirköprü +Mandurah +Watford +Poitiers +São João del Rei +Yoshiwara +Puerto Padre +Caucasia +Germantown +Peñaflor +Imizuchō +Nasatta +Abomey +Pollāchi +Gjilan +Hendek +Gujar Khan +Jalalpur Jattan +Wajir +Victorias +Marudi +Kāvali +Aubervilliers +Botoşani +Darnah +Möng Tun +Richard-Toll +Afmadow +Barwaaqo +Ma‘arrat an Nu‘mān +Luau +Necochea +Tangjia +Al Badrashayn +Léogâne +El Ejido +Mihara +Compostela +Presidencia Roque Sáenz Peña +Kiselëvsk +Bayt Lāhyā +Patrocínio +El Puerto de Santa María +Itapeva +San Leandro +Olavarría +Dobrich +Stevenage +San José Pinula +Sankeshwar +Kōka +Glyfáda +Serdar +Saquarema +Cantaura +Los Cerrillos +Conda +Cáceres +Cheektowaga +Ţarţūs +Town 'n' Country +Clifton +Waukegan +Kadiri +Tonghae +Zhunan +Ipil +Prijedor +Oulad Teïma +Níkaia +Mestre +Caracase +Surallah +Ciudad de Atlixco +Pistoia +Akyazı +Torrevieja +Pamekasan +Ségbana +Maladzyechna +Bloomington +Léré +Avondale +Maumere +Phusro +Polangui +Banga +Humpata +Kalibo +San Francisco +Atascocita +Kūhdasht +Jalal-Abad +Kairāna +Jaworzno +Kamensk-Shakhtinskiy +Saundatti +Kansk +Shwebo +Hinigaran +Calabanga +As Salţ +Passi +Colombes +Murcia +Bogo +Aalst +Lucca +Missoula +Hemei +Pisa +Wangqing +Viana do Castelo +Danlí +Chiclana de la Frontera +Fort Myers +Montelíbano +Ben Guerir +Toviklin +Chosica +Paingkyon +Villa María +Rāsipuram +Leping +Najībābād +Podujevë +San Luis +Barra do Corda +Bayramaly +Kimje +Bhakkar +Berisso +Bertoua +Newton +La Grita +Solana +Aïn M’Lila +Nirmal +Nirāla +Ootacamund +Echague +Aroroy +Mobara +Ben Arous +Prosperidad +Alabel +Ban Laem Chabang +Grimsby +Lobnya +Villingen-Schwenningen +Jangipur +Jaffna +Janzūr +Leshou +Lawrence +Muncie +Sangrūr +Damaturu +Qiantangcun +Hartlepool +Al Wakrah +Sassandra +Sakai +Newmarket +Jilotepec +Makilala +Wislane +Maiquetía +Mettupālaiyam +Wakiso +Bromley +Jumri Tilaiyā +Rapid City +Guanambi +Jagüey Grande +Baggao +San Juan de los Morros +Aruppukkottai +Farīdkot +Calauan +Ceylanpınar +Ama +Słupsk +Madgaon +Baras +Gitarama +Ende +Koidu-Bulma +Palangotu Adwār +Sakiet ez Zit +Western Bicutan +Chester +Consolación del Sur +Cipolletti +Kpalimé +Changting +Maasin +San Fabian +Şatrovka +Ratingen +Midrand +Denan +Santa Catarina Pinula +Calaca +Caratinga +Jamūī +Middelburg +Camiling +Nahualá +Limpio +Shuangshuicun +Torrente +Chongoroi +Chimbas +Bhola +Bi’r al ‘Abd +Lorena +Dipalpur +Zwickau +Troy +Fulham +Houndé +Livermore +Citrus Heights +Ad Diwem +Norton +Bama +Wulan +Hawthorne +Heyunkeng +Mardin +Kumārapālaiyam +Heerlen +Mechelen +Cacoal +Takasagochō-takasemachi +Binmaley +Lünen +Tangxing +Campana +Paredes +Fukuroi +Widekum +Winchester +Taohuajiang +Longonjo +Dunkerque +Gubkin +Les Cayes +Hānsi +Salinas Victoria +Kattagan +Tiflet +Springdale +Cárdenas +Shahdol +Yoro +Hamakita +Unaí +Clarkstown +Nuneaton +Anakāpalle +Gravatá +Nabua +Tucupita +Novotroitsk +Tuncheng +Whittier +Deerfield Beach +Nīmbāhera +Nakhon Sawan +Yaofeng +Nachchāndupatti +Hassi Bahbah +Loznica +Kalpitiya +Karanganyar +Navegantes +Yabēlo +Santa Rosa Jauregui +Dingcheng +Guasave +Gotenba +Odienné +Ciudad de Melilla +Bangkalan +Bantayan +San Antonio +Decatur +Batticaloa +Seoni Mālwa +Buzuluk +Ibshawāy +Settsu +Silvan +Sārni +Aulnay-sous-Bois +San Ramon +Repentigny +Shchëkino +Bugulma +Toledo +Khowrāsgān +Kitanagoya +Vineland +Shaoshanzhan +Potiskum +Sharūrah +Kameoka +Keffi +Qaraçuxur +Iga +Chiguayante +Cabiao +Konstanz +Kouvola +Vólos +Guinobatan +Kharian +Mission +Bāsoda +Longhua +Taishan Houcun +San Pedro Sacatepéquez +Ducheng +Ādwa +Sekimachi +Maratturai +Auburn +Fuengirola +Redenção +Karakol +Lake Forest +El Bayadh +Mukacheve +Fusui +Xuddur +Karād +Pariaman +Chinnachauku +Batarasa +Lugang +Junín +Shumen +Colonie +Warder +Caldas +Vélez-Málaga +Pori +Mitrovicë +Jinhe +Garulia +Tagaytay +Apaseo el Grande +General Rodríguez +Chiquinquirá +Krishnagiri +Arona +Upper Darby +Dapitan +Takayama +Grand Bourg +Newport Beach +Harda Khās +Gurupi +Maga +Araripina +Monte Chingolo +Jastrzębie-Zdrój +Puerto Maldonado +Mhow +Derry +Santa Inês +Ealing +Walvisbaai +Bahlā’ +Taxila +Hövsan +Luvungi +Korgas +Melbourne +Atakpamé +Woolwich +Longchuan +Brooklyn Park +Karacabey +Baabda +Larnaca +Prešov +Bryan +Sayhāt +Westland +Ílion +Peterborough +Ciudad Mante +Konotop +Pandacan +Chirundu +Túxpam de Rodríguez Cano +Ijuí +Napa +Mohammadia +Catanzaro +Sumenep +Tshilenge +Worms +Pinheiro +Treviso +Mādabā +Khemis Miliana +Yokotemachi +Dhorāji +Rafḩā +Honiara +Ushiku +Tire +Vīrappanchathiram +Baytown +Komae +Santana do Livramento +Sabanalarga +Dmitrov +New Kru Town +Carpina +Kaizuka +Nabunturan +Marl +Suceava +Bais +Villa Altagracia +Science City of Muñoz +Athurugiriya +Higashiyamato +Ayase +Bilwi +Cicero +Chigorodó +Quixadá +Concepción Tutuapa +Wakō +Al Hindīyah +Luancheng +Dārayyā +Sambava +Kitakōriyamachō +Heṭauḍā +Ath Thawrah +Lāharpur +Diphu +Bolinao +Arujá +El Milia +Channapatna +San Baudilio de Llobregat +Chita +Anderson +Keşan +Kineshma +Ermelo +Zheleznogorsk +Xishancun +Acayucan +Lucas do Rio Verde +Dolisie +Bhawānipatna +Pilkhua +Samadiāla +Ho +Haskovo +Yeysk +Franklin +Matão +Barahona +Tiruppattūr +Hamma Bouziane +Versailles +Moriyama +Farmington Hills +Nizwá +Buena Park +Foumban +Talavera de la Reina +Lafey +Galway +Albany +Aylesbury +Atambua +Nazareth +Taytay +Phúc Yên +Reşiţa +La Piedad +Gokulgarh +São Bento do Sul +Serrinha +Maco +Lqoliaa +Pine Hills +Ashford +Sirsilla +Como +State College +Boukoumbé +Habiganj +Lakshmīpur +La Trinidad +Picos +Siaton +Redwood City +Minden +Kampong Trach +San Antonio +Moju +Ciudad de Ceuta +Lelystad +La Lima +San Fernando +Wutiancun +Mingxing +Mazatenango +Mānsa +Mabinay +Busto Arsizio +Nantingcun +David +Al Ḩayy +Louga +Ufeyn +Ji’an +Wadala Sandhuan +Warwick +Jackson +Bayeux +Stockton-on-Tees +Nakatsu +Brindisi +Cranston +Shīrvān +Tanjay +Chingleput +Jacobina +Mian Channun +Manfalūţ +Rivadavia +Rivadavia +Cruzeiro +Chelghoum el Aïd +Bhalwal +Largo +Calatrava +Pontevedra +Wuyi +Chulucanas +El Estor +Edmonton +Velbert +Cukai +Miami Beach +Chaykovskiy +Sabaneta +Oleksandriia +Owariasahi +Shikokuchūō +Alhambra +Kuznetsk +Deurne +Ōmihachiman +Johns Creek +Puerto Iguazú +Nueva Concepción +Macaíba +Uman +Saint Albans +Mountain View +Quixeramobim +Bekobod +Tacaná +Harlow +Carmen +Burnley +Ust’-Ilimsk +Salisbury +Jepara +Redditch +Saunda +Morgantown +Kongjiazhuangcun +Norderstedt +Ashoknagar +Silver Spring +Ubay +Yurga +Bhaktapur +Banco Filipino International Village +Layton +Bilecik +Yıldız +Gürgenpınarı +Uzungöz +Lucerne +Siasi +Concórdia +Watampone +Springfield +Kātoya +An Khê +Paranavaí +Muroran +Timóteo +Apizaco +Hukou +São Sebastião +Courbevoic +Purmerend +Dar el Beïda +Imam Qasim +Lakewood +Kentaū +Remedios de Escalada +Xicotepec de Juárez +Anapa +Fiumicino +Afşin +Shuibian +Kadi +Valdemoro +Chapadinha +Xiedian +Matalam +Kimitsu +Grosseto +Buhi +Athi River +Nowy Sącz +Florence +San José de las Lajas +Pacatuba +Lambunao +Bulacan +Novouralsk +Mannārgudi +Port of Spain +Orhangazi +Hengkou +Itá +Folsom +Kapalong +Salina Cruz +Panzos +Tecate +Balrāmpur +Baalbek +Hengelo +Kashiwazaki +Tallaght +Khamānon Kalān +La Louvière +Launceston +Madera +Dera Allahyar +Gonder +Campo Limpo +Gaoliying Ercun +New Rochelle +Rafaḩ +Ahenkro +Yonezawa +Orihuela +Shiji +Gobernador Gálvez +San Francisco +Malapatan +Balçova +Barili +Wujindian +Meybod +Yanggao +Bargarh +Curvelo +San Cristóbal Verapaz +Comitancillo +Seropédica +Parma +Terre Haute +San Ramón +Bulanık +Samā’il +Dahegām +Torre del Greco +Randfontein +Batley +Somerville +Ādīgala +Azov +Nagaoka +Calandala +Panjgur +Butterworth +Tala +Antehiroka +Oum el Bouaghi +Cabadbaran +Béziers +Tagoloan +Kāmāreddipet +Zuwārah +Bafut +Tākestān +Arni +Echizen +Maravatío de Ocampo +Lívingston +Aquiraz +Kumbo +Bolpur +Peruvancha +Sint-Niklaas +Flagstaff +Taroudannt +Namsan +Gumlā +Simdega +San Andrés Cholula +Qŭnghirot +Cachoeira do Sul +Boynton Beach +Gamagōri +Manmād +Goiana +Rubí +Nabatîyé +Masallātah +Andahuaylas +Kathri +Jamshoro +Khewra +Zarzis +Puerto Ayacucho +San Carlos del Zulia +Koktokay +Piro +Tall ‘Afar +Homestead +Scunthorpe +Polatsk +Newark +Mīt Salsīl +Bamberg +Plymouth +Turhal +Ben Gardane +Sefrou +Kırklareli +Pátzcuaro +San Jose +Drobeta-Turnu Severin +Sokcho +Paco +Kottagūdem +Idah +Marsala +Poblacion +Anniston +Piatra Neamţ +Ciudad Lerdo +Dessau-Rosslau +Tissamaharama +Akiruno +Texarkana +Patikul +Ezpeleta +Banī Mazār +Mahdia +Vila do Conde +Krasnyi Luch +Cianorte +Tustin +Belo Jardim +Neumünster +Xai +Viana +Eséka +Döşemealtı +Robles +San Martín +Torres Vedras +Langarūd +Pharr +Senhor do Bonfim +Afgooye +Klin +Dudley +San Isidro +Port Huron +Iwamizawa +Yenakiieve +Mangatarem +Ban Talat Rangsit +Sorgun +Caçador +Turlock +Owendo +Schiedam +Yalta +Ban Nong Prue +Drummondville +Natori-shi +Kawartha Lakes +Lisala +Évosmos +Juventino Rosas +Ciudad Lázaro Cárdenas +Jaen +Dobni Para +Hannō +Carmen +Rancho Cordova +Gokāk +Ceará-Mirim +The Villages +Tīkamgarh +Ikom +Milpitas +Ozërsk +Būmahen +Ubon Ratchathani +Bahārestān +Arāria +Cuamba +Huaral +Madīnat as Sādāt +Pèrèrè +Alfenas +Sougueur +Nakatsugawa +New Westminster +Tiko +Sesto San Giovanni +Perris +Bistriţa +Manresa +Daugavpils +Upland +Subulussalam +Tambacounda +Dome +Nakhon Pathom +Toboali +Tierralta +Maizuru +Bury +Alton +Eastleigh +Elbasan +Pagbilao +Villa Celina +Ra’s al Khafjī +Pleasanton +Alīgūdarz +Zaandam +Mooka +Arlit +Skarżysko-Kamienna +Dabakala +Curepipe +Dongchuan +Hengbei +Kuvango +Brixton +La Rochelle +Taher +Gyōda +Sahuayo de Morelos +Aveiro +Bauang +Dinga +Iperu +Varese +Kendall +Arkonam +Delmenhorst +Jonesboro +Bandar Emām +Bellflower +Três Rios +Kashiba +Barreiro +Battle Creek +Denov +Pototan +Qorveh +Queluz +Limay +Bamban +Manbij +Pingyi +Southall +Huolu +San Pedro Pinula +Chino Hills +Péhonko +Viersen +Cheyenne +Argao +Rueil-Malmaison +Al Khānkah +Tanashichō +Chitembo +Chitemo +Ilioúpoli +Macabebe +Kropotkin +Lebanon +Carmichael +South Jordan +Kuacjok +Gandía +Faranah +Mandiraja Kulon +Baracoa +Kizugawa +Godāwari̇̄ +Balqash +Colón +Essaouira +Tanuku +Narra +Koch Bihār +Chengbin +Pakxé +Honjō +Toyomamachi-teraike +Fray Bartolomé de Las Casas +Rheine +Hoofddorp +Davis +Marburg +Villa Victoria +Elizabethtown +Linkou +Birkhadem +Kuniyamuttūr +Kortrijk +Kambam +Bir el Ater +Champigny-sur-Marne +Brikama +Ukunda +Huebampo +Hasselt +Bebedouro +Aksu +Nitra +Phuket +Lipjan +Bodhan +Schaumburg +Alameda +Santa Rosa +Zhongcheng +Santa Catalina +Grand-Lahou +Stellenbosch +Hermosa +Fnidq +Mengla +Usol’ye-Sibirskoye +Katano +Hammond +Tsubame +Tirkākara +Vorkuta +Quatre Bornes +Jelenia Góra +Puli +Caxito +Pasco +Latacunga +Bracknell +Ban Tha Khlong +Cozumel +Paisley +Gelendzhik +Pattoki +Harunabad +Roosendaal +Aş Şuwayrah +Bustos +Evanston +Kabacan +Fukuchiyama +Shūsh +Lehi +Alexandria +Chalándri +Keratsíni +Umingan +Pau +Tecamachalco +Guildford +Zhlobin +Talakag +Nikkō +Nabari +Balagtas +Toyooka +Balkh +Estepona +North Port +Bongabong +Nagua +Berbérati +Santo Ângelo +Ébolowa +Valença +Mao +Lüneburg +Baj Baj +Veliko Tarnovo +Arlington Heights +Chatham +Surt +Shostka +Balashov +El Viejo +Usta Muhammad +Camarillo +Pyapon +Wyoming +Dorsten +Prince George +Mafra +E’erguna +Nipāni +Vinhedo +Tacámbaro de Codallos +Ait Ali +Flower Mound +Ivanteyevka +Aira +Caledon +Bethlehem +Cotuí +Alcalá de Guadaira +Tianguá +Daxincun +Dschang +İdil +Virac +Hattiesburg +Trinidad +Loveland +Abéché +Fāzilka +Khemis el Khechna +Funing +Armant +Al Musayyib +Shinkai +Ibiúna +Paysandú +Pittsburg +Kafr az Zayyāt +Crateús +Ninh Hòa +Siedlce +Sasolburg +Melton +Cedar Park +Palencia +Pozzuoli +Shājāpur +So-Awa +Troisdorf +Tuban +Palmerston North +Padre Hurtado +Anzhero-Sudzhensk +Palo +Keshod +Infanta +East Ham +Kadoma +Daisen +Nahāvand +Bender +Wenatchee +Weston-super-Mare +Montepuez +Esteio +Southfield +Kahror Pakka +Lins +Wilhelmshaven +Fancheng +Molina de Segura +San Ramón de la Nueva Orán +Chintāmani +Ryūgasaki +Manaoag +Rochester Hills +Hammond +Banská Bystrica +Merouana +Yanam +Tailai +Toba Tek Singh +Villa Elisa +Valdosta +Houmt Souk +Rulin +Châteauguay +Gladbeck +Catacaos +Kengtung +Ovalle +Solok +Sankt Gallen +Espinal +Benalmádena +Pilar +Surendranagar +Stakhanov +Sentani +Malappuram +Kargilik +Mérignac +Cadereyta Jiménez +Laiyuan +Xaignabouli +Lod +Sidi Qacem +Maghāghah +Arta +Santa Rosa +Sapiranga +Owensboro +Umeå +San Pedro +Arjona +Apple Valley +Jocotán +Aïn Temouchent +Woodbury +Garhi +Cataguases +Avilés +Joensuu +Jablah +Ramla +Três Corações +Landshut +Saint-Maur-des-Fossés +Tindivanam +Carlisle +Srīvilliputtūr +Kiyose +Xinglong +Bhadarwāh +Rānāghāt +Shadrinsk +Cheria +South Shields +Kai +Bonāb +Villa Carlos Paz +Ciudad Real +Ardakān +Cửa Lô +Acacías +Frontera +Itapira +Tissemsilt +Pawtucket +Lagoa Santa +Dongguan +Dhrāngadhra +Kunitachi +Dayr al Balaḩ +Pinamungahan +Antibes +East Kilbride +Teyateyaneng +Aracati +Detmold +Newcastle under Lyme +Mawatagama +Burton upon Trent +Libon +Chernogorsk +Belleville +Telêmaco Borba +St. Joseph +Gangammapeta +Puqiancun +Dubna +Iju +Wisil +Al Muḩarraq +Khejroli +Tirumangalam +Pardigūda +Warabi +Bugallon +Tocumen +Cherry Hill +Panggezhuang +Jaorā +Doral +Gönen +Fouchana +Amakusa +Tura +Ambājogāi +Palencia +Juchitán de Zaragoza +Dabra +Ubud +Sasagawa +Darhan +Darhan +Pozorrubio +Toffo +Tiznit +Esperanza +Tanguiéta +Tosu +Moulay Abdallah +Cotorro +Drohobych +Dalaguete +Piraçununga +Ouro Prêto +Dover +Cinisello Balsamo +Missouri City +Dayong +Bayreuth +Īṭahari̇̄ +Péda-Houéyogbé +Katori +Kandori +Shancheng +Bozüyük +San Antonio de Los Altos +Assab +Gbawe +Skellefteå +Balingasag +Saratoga Springs +Arnsberg +Wandiwāsh +Ouaké +Rāmhormoz +Pocatello +Bongouanou +San Juan Opico +Miki +Oshkosh +Uspantán +Silao +Brick +New Britain +Çınar +Zlín +Izumiōtsu +Canindé +Mankono +Le Kram +Aprilia +Okegawa +Meshgīn Shahr +Al Qā’im +Airdrie +Blagoevgrad +Castle Rock +Chalchuapa +Gūdūr +Yelabuga +Casoria +Pedro Brand +Gravesend +Ra‘ananna +Brookes Point +Tatebayashi +Lauderhill +Kyōtanabe +Kahan +Tatsunochō-tominaga +Majalengka +Broomfield +Sarnia +Dale City +Mineral’nyye Vody +Vlaardingen +Tamworth +Samundri +Dumangas +Alicia +Dĩ An +Yurihonjō +Aïn Oulmene +Cunhinga +Ajaccio +Hāveri +Castrop-Rauxel +Türkoğlu +Beyşehir +Yegoryevsk +Bolingbrook +Redmond +Caguas +El Kef +Gouda +Mansfield +Tefé +Tarn Tāran +Nandi Hills +Birobidzhan +Hoorn +Mangalagiri +João Monlevade +Brandenburg +Linares +Ellicott City +Vriddhāchalam +Harrogate +Copacabana +Cao Bằng +Târgu Jiu +Sheboygan +Xindian +Kallakkurichchi +Irecê +Kasama +El Hamma +Kumluca +Paterna +Bandar-e Genāveh +Tupi +Mansfield +Novoaltaysk +Lala +Kāsipālaiyam +Glens Falls +Troitsk +Asti +Inuyama +Pančevo +Jose Abad Santos +Bagan Si Api-api +Daytona Beach +Reconquista +Quillota +Cannes +Crewe +Lodi +Redlands +Tan-Tan +Fada Ngourma +Ōtawara +Pacajus +Shakargarh +Harrisonburg +Ragusa +Ciudadela +Bula +Pattukkottai +Almelo +Gobindgarh +Nabeul +Edéa +Brakpan +Néa Smýrni +Usulután +Shibukawa +Luján de Cuyo +Moa +Berdychiv +Dothan +Baghlān +Chaigoubu +Santa Rosa de Cabal +Santa Isabel do Pará +Türkmenbaşy +Naqadeh +Çatalca +San Vicente del Caguán +Jinja +Russas +La Dorada +Mackay +Padre Las Casas +Kostiantynivka +Khomeyn +Cleveland +Jōyō +Vsevolozhsk +Tocoa +Jackson +Mount Vernon +Jingzhou +Caserta +Chapayevsk +Paine +San Martín +Catanauan +Spijkenisse +Södertälje +Centreville +Hương Trà +Amparo +Yafran +Lappeenranta +Carles +Cascavel +Sanwal +Rio do Sul +Yukuhashi +Sremska Mitrovica +Gaspar +Mysłowice +Majadahonda +Zhuolu +Lanxi +San Dionisio +Belovo +Esmeraldas +Weligama +Nepālgañj +Siruguppa +Tangalla +Bacacay +Hekinan +Ede +Sipalay +Aschaffenburg +Dazaifu +Samborondón +Longkoucun +Alegrete +Keshan +Candeias +Altoona +Sangāreddi +Benidorm +Zogbodomé +Maroúsi +Wood Buffalo +Dambulla +Goya +Oroquieta +Virudunagar +Abancay +Penafiel +San Fernando +Palín +Turbaná +Yoshikawa +Santo Antônio do Descoberto +El Banco +Warora +Casa Nova +Tibati +Kirovo-Chepetsk +Saint-Nazaire +Sault Ste. Marie +Camalig +Najrān +Belo Tsiribihina +Myaydo +Al Līth +Bella Vista +Bailongqiaocun +Colón +Chaklāsi +Nilanga +Long Mỹ +Borongan +San Andrés +Wenping +Gumaca +Stara Pazova +Bocholt +Araranguá +Esbjerg +Merzifon +Goianira +Chiryū +Čačak +Quilāndi +Carpi +Lüdenscheid +Mun’gyŏng +Ishioka +Jabuticabal +Calauag +Castro +Cajamar +Framingham +Camden +São Sebastião do Paraíso +Bouaflé +Patzún +Georgetown +Vyborg +Kabarore +Sambrial +Piła +Dondo +Shrewsbury +Mānikganj +Baldwin Park +Florida +Rocklin +Porterville +Ágios Dimítrios +Kayes +Calarcá +Tarīn Kōṯ +Bakıxanov +Kawm Umbū +Mandlā +Tamarac +Palmeira dos Índios +Ostend +Saymayl +Indaial +La Estrella +Sonsonate +Pililla +Santo Tirso +Gosport +Parang +Bartın +Dias d’Ávila +Lomas del Mirador +Nanfengcun +Salamá +Biankouma +Lisburn +Rāyadrug +Mamoudzou +Glen Burnie +Halmstad +Le Bardo +Binalbagan +Shahrixon +Bilāra +Villa Tunari +Huanchaco +Campo Formoso +Goa +Drancy +Rāyagada +Blacksburg +Talibon +Las Piedras +Ganthier +Verkhnyaya Pyshma +Jinsha +Parappanangādi +Konongo +Wausau +Sumter +Gela +Placetas +Janesville +Melong +Fernandópolis +Musashimurayama +Brunswick +Morong +San Francisco del Rincón +Gibara +Ratangarh +Tāndūr +Bakhmut +Terme +Mārkāpur +Bunbury +Sihanoukville +Mauban +Tejupilco +Alabang +Goianésia +Dublin +Warzat +Montecristi +Ayvalık +Nadi +Wilmington +Zográfos +An Nu‘mānīyah +Izalco +Malaut +Lowestoft +Waukesha +Samraong +Kopargo +Phitsanulok +Kodungallūr +Corozal +Neyyāttinkara +Kumanovo +Dondo +Fairbanks +Ejura +Zinjibār +Sesvete +Érd +Zadar +Fatehābād +Bāpatla +Kalamasseri +Tumauini +Izmail +Ostrów Wielkopolski +Lakeville +St. Charles +Şirvan +Al Qurayyā +Gardēz +Cremona +Pavia +Rugby +Badvel +Loulé +Redondo Beach +Yinying +Chiang Rai +Karasu +Poblacion +Shujaabad +Stafford +Vālpārai +Yambol +Esperanza +Djemmal +Chingford +Cabudare +Sankaranayinār Kovil +Xangongo +Uxbridge +Zenica +Chekhov +Bundaberg +Sūratgarh +Spring Hill +Tamana +Kaukhāli +Sig +Bayonne +Coari +Grand Forks +Baiquan +Mindelo +Togoch’alē +Noblesville +Torremolinos +Būr Fu’ād +Capanema +Pavia +Noisy-le-Grand +Yawata-shimizui +Linares +Aliaga +Orani +Dandeli +Santa María La Pila +Minxiong +Huwei +Bopa +Brumado +Havířov +Hujra Shah Muqim +Şa‘dah +Ban Houayxay +Nāndūra Buzurg +Dimbokro +Rizal +Tinambac +Pazarcık +Guaynabo +Celle +San Antonio +Sagunto +El Paso de Robles +Kabirwala +Thaba Nchu +North Richland Hills +Maple Grove +Eniwa +Guzhou +Gaura +Mineiros +Pan’an +Tsurugashima +Grajaú +Cahama +Waingapu +Kempten +Passaic +Blaine +Lubin +Luodong +Thakhèk +Castries +Nansang +Al ‘Āmirāt +Talamba +Badhan +Điện Biên Phủ +Phú Quốc +Longtangwan +Zhanggu +‘Izbat al Burj +Bijaynagar +Satyamangalam +Madhipura +Kodoli +Az Zubaydīyah +Lake Elsinore +Mansfield +Raha Tiga +Raha +San Antonio +Nikki +Fulda +Avaniyāpuram +Rogers +Entebbe +Imerintsiatosika +Eskilstuna +Aigáleo +Rohri +Sagaing +Casas Adobes +Qingquan +Saint John +Farroupilha +Moquegua +Yueshanwan +Altamura +Sherman +Vushtrri +Encarnación +Konin +Novomoskovsk +Kwamhlanga +Ratnanagar +Villa del Rosario +Amstelveen +Garzón +San Miguel de Allende +Walnut Creek +Sanlúcar de Barrameda +San Juan de los Lagos +Los Reyes de Salgado +Basavakalyān +Farīdpur +Kiyosu +Conway +Ziguinchor +Minami-Alps +Uwajima +Roxas +Rioverde +Rittō +Eastvale +Saint-Louis du Nord +Inowrocław +Somasso +L’Aquila +Tournai +Bawku +Samch’ŏk +Rhondda +Union City +Biguaçu +Michigan City +Thohoyandou +Poptún +Sōja +Tripunittura +Toyoake +Al Qūşīyah +Alenquer +Victoria +Aqsū +Chisec +Kirdāsah +Poinciana +Nowrangapur +Welland +Kars +Bitola +Planeta Rica +Don Carlos +Bafia +Cawayan +Tulare +Anan +Limonade +Limbé +Shangchuankou +Barra do Garças +Ongjang +Cuímba +Torbeck +Fedosiia +Rongcheng +Gary +Ad Darb +Imola +Necoclí +Mansehra +Renk +Mila +Mocuba +Dharmasāgaram +Granby +Gaithersburg +San Pascual +Peruíbe +Kireka +Kamsar +Ko Samui +Moriya +Tanabe +Mocuba +Mococa +Piotrków Trybunalski +Varisshiyakuni +Korba +Huishi +La Paz +Yitiaoshan +Bagumbayan +Liuhe +Pālghar +La Chorrera +Buenavista +Lippstadt +East Orange +San José del Guaviare +Queenstown +Yunnanyi +Aparri +Assen +Ixtaczoquitlán +Aalen +Wesley Chapel +Ponta Delgada +Purísima de Bustos +Arcoverde +Jacona de Plancarte +Pakribarawān +Al Aḩmadī +Suwałki +Say’ūn +West Des Moines +Yuriria +Mineral de la Reforma +Indang +Sabae +Alamada +Isnā +Venâncio Aires +Požarevac +Kāyankulam +Velsen-Zuid +Dalton +Dubuque +Jarabacoa +Parādīp Garh +Quartu Sant’Elena +Issy-les-Moulineaux +Valle Hermoso +Bouira +San Leonardo +Ilkal +Zapotlanejo +Doboj +Víctor Larco Herrera +Igbanke +Nihtaur +Schenectady +Mamungan +Cabo San Lucas +Southampton +Kladno +Castelldefels +Ankeny +Sanza Pombo +Tangub +Anjangaon +Maricopa +Şəki +Bardi̇̄bās +Cergy +Puerto San José +Ash Shiḩr +Oriximiná +Adrar +Eagan +Tuymazy +Matara +Lodja +St. Albert +Otukpo +Franklin +Swedru +Nghĩa Lộ +Tynemouth +Sipocot +Tuburan +Villanueva y Geltrú +Hanford +Miagao +Xá Muteba +Bristol +Cuyapo +Mbaké +Yorba Linda +Weston +Watsonville +Hämeenlinna +Levallois-Perret +Minusinsk +Fort McMurray +Lindong +Renukūt +Nābha +Ixtlahuacán de los Membrillos +Putrajaya +Al Buraymī +San Pedro Ayampuc +La Barca +Yanghe +Palo Alto +Castillejos +Antalaha +Kstovo +Ōdate +Siuri +Bodītī +Januária +South Hill +Chengjiao Chengguanzhen +Conceição do Coité +Formiga +Ksar el Boukhari +Kamuli +Rājsamand +Longshan +Bishnupur +Cannock +Dinslaken +Kashiwara +Pesqueira +Colmar +Tepotzotlán +Bayombong +Sant’Eufemia Lamezia +Apac +Shashijie +Pongnam +İslahiye +Shawnee +Modāsa +Santa Barbara +Banepā +Youssoufia +Vaasa +Santa Catarina Otzolotepec +Heroica Caborca +Molepolole +Walsall +Manaure +Kovel +Abnūb +Zigon +Gauravaram +Bergen op Zoom +San Marcos +Areguá +Ayolas +Herford +Sausar +Yuquan +Taman Senai +Omīdīyeh +Huajing +Dhenkānāl +Zihuatanejo +Chicacao +Fuefuki +Urun-Islāmpur +Rolândia +Calais +Stargard Szczeciński +Gopālganj +Itapema +Ankazoabokely +Capelle aan den IJssel +Tomé-Açu +Great Falls +Cuilco +Tôlan̈aro +Lala Musa +Rüsselsheim +Haverhill +Āsela +Sousa +Union City +Séguéla +Shiojiri +Kerpen +Palatine +Longview +Corvallis +Bay +Tabatinga +Wanggezhuang +Washington +Lushar +Rockville +Néa Ionía +Zrenjanin +Szolnok +Old Bridge +Dolores Hidalgo Cuna de la Independencia Nacional +Liepāja +Sa’ada +Skokie +Pikit +Dhārāpuram +Guruvāyūr +Kashima +Cedeño +Jagoniguda +Mīrpeta +Nizhyn +Lupon +Phalodi +Embu-Guaçu +Târgovişte +Middletown +Nueva Guinea +Acilia +Veenendaal +Mount Vernon +Kati +Güines +Beypore +Casper +El Seibo +Grays +Bongabon +Kissidougou +Cosmópolis +Janaúba +Āksum +Çumra +Janiuay +Calimera +Godalming +Pessac +San Mateo Atenco +Botolan +Siddipet +Bulancak +Ilidža +Ames +Rosales +Hihyā +Hīt +Kraljevo +Bolgatanga +Chiyoda-ku +Viladecáns +Karlskrona +Karimama +La Carlota +San Mateo +Focşani +Delray Beach +Nālūt +Katwijk +Sammamish +Walton upon Thames +Ramos Arizpe +Aflao +Karakax +Novi Pazar +Cabedelo +Koratla +Saiki +Weiyuan +Damba +Georgiyevsk +Yachimata +Zacapa +Cuscatancingo +Guangping +Vénissieux +Urla +Lynwood +Hasanpur +Opol +Dundalk +Bethesda +Hashima +Slavyansk-na-Kubani +Växjö +Huquan +Zanhuang +Morristown +Reghaïa +Kampung Tengah +Bengkalis +Virginia +Juana Díaz +Sahaswān +Ocoyoacac +Belogorsk +Arāmbāgh +Kazanlak +Pidugurālla +Massa +Vidnoye +Cazin +Lençóis Paulista +Tsuruga +Ilo +Oberá +Genk +Goz-Beida +Chibok +Ban Suan +Oas +Kankakee +Dangbo +Moita +Agoo +Fajardo +Torreón +El Carmen de Bolívar +Madirovalo +Taman Johor Jaya +Puliyankudi +Stupino +Neuwied +Lantapan +Asenovgrad +Viterbo +Anamur +Lahat +Itapetinga +Alpharetta +Wilde +Tatabánya +Novi +Martínez +Kavála +Karlstad +Coron +Roxas +Zacatecoluca +Finchley +Thornton Heath +Gloucester +Sangamner +Chegutu +Kenner +Kiamba +Fukutsu +Wamena +San Remigio +Gohna +Pulivendla +Bay City +Sakrand +Santo Tomé +Smila +Ina +Collado-Villalba +Victoria +Surubim +Menzel Temime +Lutayan +Tulcea +Arima +Weimar +Angat +Qiryat Gat +Kirtipur +Istaravshan +Rio Negro +South San Francisco +Barreirinhas +Qarqan +Bom Jesus da Lapa +Apex +Parkersburg +Xarardheere +San Francisco El Alto +El Prat de Llobregat +San Antonio Suchitepéquez +Kānhangād +Xiantangcun +Al Minshāh +Beloretsk +Los Amates +Xieqiaocun +Jaisalmer +São Tomé +Singaparna +Malden +Kurabūr +Aïn Defla +Gniezno +Piripiri +Castro Valley +Narok +Rechytsa +Ishimbay +São Félix do Xingu +Ārān Bīdgol +Bāqershahr +Giddarbāha +Täby +Purulhá +Develi +Villa Curuguaty +Tamlūk +Hadjout +Jamjamāl +Sinnar +Vaijāpur +Jocotitlán +Solano +Kungur +Quezon +Pruszków +Bozeman +Birnin Konni +Tigbauan +Ouricuri +Torquay +Jagraon +Alīpur Duār +Pateros +Dhūri +Xibang +Ḩalabjah +Kitamoto +Guider +Zarechnyy +Dormagen +Ishim +San Jose +Villach +Buynaksk +Estância +Itaberaba +Rāth +Villasis +Zeist +Mecheria +Brentwood +Farnborough +Jiquílpan de Juárez +Popondetta +Saidu Sharif +Bāzār-e Yakāwlang +Patuakhāli +Sindelfingen +Matamey +Embu +Caripito +Sumbawa Besar +Bucak +Maasin +Keitumkawn +Autlán de Navarro +Busia +Ioánnina +La Ceja +Kolonnawa +Coyula +Chiquimulilla +Gukovo +Don Torcuato +Nagcarlan +Palaió Fáliro +Ladysmith +Tomigusuku +Clichy +Igarapé-Miri +Bordj Menaïel +Gwacheon +Sabinas +Acaraú +Ostrowiec Świętokrzyski +Soloma +Plauen +As Suwaydā’ +Chipindo +Waltham +Faīẕābād +Sangzishi +Seydişehir +Oued Rhiou +Kensington +Siemianowice Śląskie +Meulaboh +Marinilla +Gerli +Santo Domingo Tehuantepec +Binga +Fredericton +Boston +Grevenbroich +Lechang +Faro +Pirané +Narapalli +Fujioka +Ţūlkarm +Pflugerville +Isabela +Hilongos +Arrecife +Pālitāna +Roeselare +Valence +Rivera +Rahat +Itamaraju +North Little Rock +Al Qurayn +Dharmapuri +Apaseo el Alto +Brentwood +Sokhumi +Idappādi +Hiriyūr +Porto Nacional +Paignton +Potenza +Rosenheim +Chunian +Malvar +Santiago Tianguistenco +Martil +Waterlooville +Sirsi +Sibulan +Póvoa de Varzim +Ash Shaykh Zuwayd +Chervonohrad +Saravia +Seraing +Głogów +Atimonan +Laguna Niguel +Ambarawa +Misantla +Kodād +San Clemente +Alba Iulia +Sangmélima +Ciénaga de Oro +Bertioga +Vacaria +Qinggang +Ghardimaou +Seinäjoki +Tādepalle +Grande Prairie +Welkom +Qal‘ah-ye Now +San Felipe +Yanagawa +Niksar +Pomezia +Siuna +Asbest +Burnsville +Teresa +Randers +Armūr +Simav +Ivry-sur-Seine +Horad Zhodzina +Guiseley +Neubrandenburg +Sodegaura +Tiquisate +Tuao +Bankra +Nawalgarh +Spring +Tupã +Port Charlotte +Camocim +Ferrol +Bognor Regis +Tohāna +Nieuwegein +Most +Penedo +Santa Cruz +Inhambane +Songkhla +Sibalom +Trnava +Kangbao +La Línea de la Concepción +Khagaul +Tenri +Daljā +Ponferrada +Magong +Zarand +Odendaalsrus +Guamúchil +Chakpi Karong +Sundapālaiyam +Fraijanes +Bourges +Jatani +Chokwé +Kurihara +Zvornik +Nawá +Chincha Alta +Jose Pañganiban +Chik Ballāpur +Guiglo +Quimper +Donskoy +Athni +Eden Prairie +Dédougou +Fredrikstad +Paphos +Capão da Canoa +Hornchurch +Assi Bou Nif +Maidenhead +Greenwood +Yangqingcun +Benevides +Lower Merion +At Tall +Midoun +Millcreek +Yattir +Asahi +Slatina +Bhairāhawā +Badr Ḩunayn +Khulayş +Korydallós +Pokrovsk +Rundu +Tajumulco +Akurana +Mitcham +Medicine Hat +Pervomaisk +Feltham +Itō +Dhuburi +Fujiidera +Mirassol +Bellevue +Vittoria +Prilep +Stourbridge +Coon Rapids +Rowlett +Mercedes +Galgamuwa +Ban Lam Sam Kaeo +Abū Qurqāş +Kitale +Cajazeiras +Ouled Djellal +Antony +Banda del Río Salí +Gorno-Altaysk +Volsk +San Marcos +Anuradhapura +Svyetlahorsk +Tartagal +Lugano +Gadwāl +Tetovo +Horsens +Rockhampton +Hamilton +Lakewood +Qurayyāt +Florence-Graham +Teoloyucan +Sīdī Barānī +Granollers +Novaya Balakhna +Nankana Sahib +Visnagar +Puerto Asís +Nago +Commerce City +San Fernando +Vaslui +Pāmūru +Çaldıran +Kasongo +Kaga +Rochester +Batatais +Bossier City +Miyoshi +Pabianice +Halton Hills +Beledweyne +Dewsbury +Taylor +Klintsy +Friedrichshafen +Sheikhpura +Narlıdere +Marina di Carrara +Irún +Māhdāsht +La Habra +Yurimaguas +Campo Bom +Balboa Heights +Sirsi +Wāshīm +Calinog +Mut +Port Orange +Palmela +Rossosh +Moncada +Balad +Itupeva +Gampaha +Woking +Troyes +Nānpāra +Tân Phước Khánh +Castellammare di Stabia +La Seyne-sur-Mer +Bansalan +Champasak +Bāri +Pavlovskiy Posad +Lingshou +Qoryooley +Gusang +Brookline +Revda +Moore +Koro +Council Bluffs +Tandag +Boadilla del Monte +Icó +Carmen de Viboral +Kapatagan +Bensalem +Anse à Galets +Leninogorsk +Sittingbourne +Leander +Acton +Dearborn Heights +Herten +Nagakute +Kilis +Mobārakeh +Portel +Aïn Harrouda +Rovaniemi +Wenxicun +Bergheim +Berekum +Rānipet +Ambilobe +Wundanyi +Reston +Kolding +Schwäbisch Gmünd +Kesamudram +Villa Domínico +Ras Tanura +Puerto Peñasco +Bainet +Riosucio +Shangtangcun +Boryspil +Tuapse +Caràzinho +Kolda +Nagari +Cristalina +Zelenogorsk +Napier +Sherkot +Tighenif +Santarém +Asaka +Camaquã +Panchari Bazar +Bannu +Cyuve +Boituva +Rāghogarh +Chidambaram +Twickenham +Agía Paraskeví +Analavory +Figueira da Foz +Aurora +Narwāna +Vigevano +North Bergen +Tríkala +Cabaret +Aurora +Montebello +Okha +Zaraza +Nova Odessa +Meshkīn Dasht +Naro-Fominsk +San Francisco +Viedma +Sihushan +Bhabhua +Kōshizuka +Manicaragua +Rouiba +Tendō +Borj el Qoblé +Pontiac +Arua +Nanjangūd +Camotán +Mongaguá +Encinitas +Tagajō +Yongbei +Menzel Bourguiba +Montauban +Rayong +Cambanugoy +Sätbayev +Zengqiao +Siddhapur +Plato +Polevskoy +Mitoyo +Derik +Żory +Kotlas +São Miguel dos Campos +Leszno +Runcorn +Nakhyaungcharīpāra +Queen Creek +Nkawkaw +Camabatela +Borisoglebsk +Hita +Lysva +Saraburi +Medford +Springfield +Morada Nova +Naj‘ Ḩammādī +Jhārgrām +Afragola +Santo Amaro +Yangiyŭl +Bezerros +Sungo +Penápolis +Kapchagay +Shiroi +Offenburg +Xiluodu +Cortazar +Praia +Kolea +San Juan Chamelco +San Antonio del Táchira +Plymouth +Karonga +Wrecsam +Sayyid Şādiq +Valle de Bravo +Hendersonville +Palm Harbor +Hato Mayor +Extremoz +Pico Rivera +Cayenne +May Pen +Santa Ana +Výronas +Asker +Ostuncalco +Ilobasco +Itogon +Port Coquitlam +Korosten +Baao +La Democracia +Uzunköprü +Hasuda +Widnes +Taal +Djibo +Euclides da Cunha +Xinmin +Rāmanāthapuram +Kozluk +Candon +Sawahlunto +Gia Nghĩa +Marietta +Hatogaya-honchō +Tunasan +Wellington +Garbsen +Wesel +Budënnovsk +Vejle +Tarnowskie Góry +Trikonavattam +Santa Catarina Ixtahuacán +Sanford +Kolomyia +Sibay +Yala +Salgueiro +Halvad +Mateare +Wels +Barbalha +Woodland +Tauá +Margate +Abuyog +Caldwell +Coyhaique +Maués +Huntersville +Bocaranga +Ar Rastan +Caicó +Udamalpet +Cabatuan +Mirabel +Santo Domingo +Ellesmere Port +Santa Rosa de Copán +Barberena +Velika Gorica +Hashimoto +Idiofa +Bristol +Olbia +Neu-Ulm +Nordre Fåle +La Plata +Ogōshi +Bangor +Iranduba +Nekā +Tulunan +Hürth +Adjohon +Wanparti +Jupiter +Tafo +Aisai +Tsushima +Itoman +Soroti +Unna +San Rafael +La Mesa +Nambuangongo +Los Polvorines +Richland +Jinoba-an +Łomża +Tamba +Chāmrājnagar +Fort Portal +Pantin +Burhaniye +Meihuacun +Yamatotakada +Bethal +Orion +Artemisa +Sarandí +Kresek +El Wak +Villamaría +Shihe +Puerto Limón +Calulo +Galapa +Cubulco +Mbabane +Oyem +Kyustendil +Huatusco +Castilla +Laoang +Oda +Ibitinga +Kukarmunda +Kokawa +Trang +Viareggio +Revere +Trujillo +Yongyang +Meriden +Tigaon +Matanao +Atotonilco el Alto +Taunton +Dumraon +Piscataway +Fryazino +Itapecuru Mirim +Ełk +Monterey Park +Gardena +Slutsk +Vratsa +Euless +Cruz das Almas +Altınözü +Bor +Panruti +Lalmanirhat +Ambanja +Ciudad Arce +Velampālaiyam +Laeken +Gubat +Wallasey +Yisuhe +Rosario +Irvington +Belladère +Mizusawa +Clay +Tirukkoyilūr +San Mateo Ixtatán +Parkent +Paracale +Yame +Qoorlugud +Belebey +Rāzampeta +Kangān +Chistopol +Kumertau +Labinsk +Nedumangād +Berrouaghia +Zamość +Pasaje +San Mariano +Nioro +Ābyek +Samut Sakhon +Mbalmayo +Des Plaines +Jinxing +Malay +Souq Sebt Oulad Nemma +Towada +Pirojpur +Manhiça +Horqueta +Floriano +São Borja +Monte Alegre +Sarāvān +Tyre +Faruka +Buqda Caqable +Oodweyne +Güigüe +Hurlingham +Kribi +Suifenhe +Consuelito +Baba Hassen +Meskiana +Afula +Nahariyya +Gunupur +Mehnājpur +Bayjī +Banane +Kakuma +Kratie +Barneveld +Hamburg +Obando +San Marcos +Çivril +Bābolsar +Union +Thatri +Urus-Martan +Rubizhne +Registro +Chandralapādu +Legnano +Loughborough +San Vicente del Raspeig +Ponnūru +West Allis +Carrara +Aïn Touta +Arankhola +Sigaboy +Kathua +Chambéry +Ilog +North Miami +Özalp +St. Cloud +The Hammocks +Escada +Aranjuez +Blainville +North Lakhimpur +Fano +Andradina +Langenfeld +Skhirate +Dongcun +Euskirchen +Ragay +Khartsyzk +Cupertino +Užice +Lakhdaria +Taylorsville +Vinukonda +Alexandroúpoli +Suharekë +Huehuetoca +Viçosa do Ceará +Gohadi +Kananya +Greifswald +Khagaria +Hardenberg +Matera +Petaluma +Bougouni +Karamürsel +Huajiang +Sanyō-Onoda +Aguacatán +Sennan +Guerra +Date +Pāchora +Ouezzane +Medellin +Maimbung +Lianzhuangcun +Coroatá +Limoeiro do Norte +Chełm +Tiptūr +Altamira +Kokomo +Gopichettipālaiyam +Givatayim +Santee +Alcoy +Itānagar +Esfarāyen +Xo‘jayli Shahri +Hakkari +Mérida +Wangzhuang +Stonecrest +Taunton +Rzhev +White Plains +Montenegro +Yara +Shimotsuke +Druzhkivka +Shirakawa +Tomaszów Mazowiecki +Los Andes +Ouinhri +Morón +Hua Hin +Stralsund +Esperanza +Koga +Aş Şaff +Galátsi +Kesennuma +Garín +Ruhengeri +Aleksandrov +Jōsō +Niort +Alfonso +Zhoujiajing +Gaibandha +União dos Palmares +Hyūga +Dhone +Irosin +Neuilly-sur-Seine +Sérres +Zamora +Irati +Gannan +San Francisco +Trollhättan +Rājgarh +Kateríni +San Simon +Chalkída +Mansalay +Dāmghān +Chichibu +Umm al Qaywayn +Antigua Guatemala +Panna +Palm Beach Gardens +Barking +Chivacoa +Göppingen +Constanza +Saint-Louis du Sud +Yevlax +Mossel Bay +El Palomar +Tahara +Upi +Metapán +Florida +Jiguaní +Petroúpoli +Huauchinango +Kattaqo’rg’on Shahri +Anzio +Motril +Nirgua +Chapel Hill +Santa María Chiquimula +Cruz Alta +Cerro de Pasco +Xikeng +Lac-Brome +Andoharanofotsy +Gattaran +Carvajal +Parobé +Sidi ech Chahmi +Zahirābād +Scheveningen +Roxas +Wani +Binnāguri +Ban Rangsit +Canlaon +Kalamáta +Beccar +Jackson +Manouba +Upleta +El Salvador +Narasapur +Xánthi +Chikuma +Hoboken +Kruševac +Pedro Leopoldo +Parker +Jaguariúna +Blue Springs +Calatagan +Baganga +Faenza +Viseu +Izberbash +Jovellanos +Shoreline +Dosso +Baguinéda +Koja +St. Clair Shores +Kasungu +Sonabedha +Pasrur +Wuyang +Raharpur +Edgware +Xinfeng +Alta Floresta +Lytkarino +Kaş +Mpondwe +Tenkodogo +Horizon West +Ōsakasayama +Frutal +Pipariā +San Luis +Caltanissetta +Gllogovc +Una +Balancán +Ibaan +Kélibia +Sardhana +São Gabriel +Hastināpur +Crotone +Ma’erkang +San Pedro +Tianningcun +Liannong +Liantangcun +Sarcelles +Benevento +Qaskeleng +Trairi +Przemyśl +Margate +Heerhugowaard +San Fernando +Littlehampton +Sandīla +Orland Park +Pebane +Zengcun +Nyagan +Acerra +Ogōri +Limerick +Punta Alta +Palompon +Abington +Tiruvālūr +Sucun +Murakami +Nanjian +Capenda Camulemba +Ishikari +Antsalova +Lambayeque +Doetinchem +Le Blanc-Mesnil +Pursat +Carson City +Rass el Djebel +Mouscron +Frankfurt (Oder) +Sillod +Ruislip +Mielec +Călăraşi +Savona +Naval +Barotac Nuevo +Temascalcingo +Samaná +Chivilcoy +Tikhvin +Halesowen +Kitahiroshima +Midwest City +Mulbāgal +North Vancouver +Rouissat +Bakwa-Kalonji +Dapaong +Maisons-Alfort +Felgueiras +Streatham +Royal Oak +Timargara +Ambahikily +Chibuto +Meleuz +Masantol +Tczew +Pradera +Chornomorsk +Santa Isabel +Kawthoung +Hunsūr +Pānskura +Nanjakkād +Bowie +Kan’onjichō +New Corella +Zempoala +Hameln +Kolondiéba +Allanmyo +Cogan +Aleksin +Berëzovskiy +Prokhladnyy +Oued Lill +Nausori +Glew +Lorient +Rāmpur Hat +Mikhaylovka +Dumanjog +Asingan +Sidi Yahya Zaer +Suileng +Marano di Napoli +Orito +Xiezhou +Royal Tunbridge Wells +Tikhoretsk +Živinice +Villejuif +Ávila +El Attaf +Hervey Bay +Kettering +Bellevue +Khandāla +Lonāvale +St. Peters +Kosai +General Pico +Oak Lawn +Mogoditshane +Grand-Popo +Pavlovo +Salsk +Stalowa Wola +Gengzhuangqiaocun +Towson +Jerez de García Salinas +Yanguancun +New Plymouth +Camajuaní +Taquara +Bilimora +Kothāpet +Coconut Creek +Maduraivayal +Tōgane +Sīra +Monte Plata +Lucan +Diriamba +Milagros +Choshi +Santa Maria +Decatur +Krasnotur’insk +Palāsa +Tokoname +Ejmiatsin +Lenexa +Wiwilí +Guarabira +Bartlett +Humaitá +Santiago Tuxtla +Cosquín +Tanauan +Chintalapalli +Meerbusch +Lohārdagā +Buluan +Sinop +Cuvelai +Ponte Nova +Richards Bay +Acará +South Whittier +Foumbot +Antsinanantsena +Huaniu +Qianwu +Nacaome +Bebington +Molfetta +Tieshansi +Eldorado +Mali +Roermond +Weymouth +Coruripe +Lake Havasu City +Mācherla +Boac +Gümüşhane +Bīmgal +Nova Esperança +Içara +Kandukūr +Tres Arroyos +Saint-Hyacinthe +Keonjhargarh +Krymsk +Riosucio +Villa Hayes +Aldershot +Bilhorod-Dnistrovskyi +Cupang +Alangalang +Samannūd +Belize City +Bulungu +Uson +Boufarik +Ping’an +Kollegāl +Cosamaloapan +Sarapiquí +Moknine +Bel Air South +Unjha +Benslimane +Uki +Nonoichi +Moramanga +Fréjus +Borūjen +Devrek +Maun +Fountainebleau +Wylie +Mafeteng +Bail-Hongal +Jasaan +Shuya +Dehdasht +Semara +Alvarado +Baden-Baden +Lunglei +Neryungri +Minokamo +Ipele +Bura +Zushi +Talagante +Rîbniţa +Nasīrabād +Cerignola +Ródos +Madison +Cuemba +Tezonapa +Ushuaia +Ban Bang Kaeo +Cachoeiras de Macacu +Machang +Huaixiangcun +San Luis +Kędzierzyn-Koźle +Puerto Boyacá +Azzaba +Hagonoy +Mora +Xam Nua +Limbang +Dina +Gūdalur +Ipirá +Caacupé +Miragoâne +Sāmalkot +Karīmganj +Brookhaven +Acambay +Santa Rosa +Pinagkaisahan +Bobbili +Irpin +Bebandem +Chinhoyi +Highland +Fussa +Fountain Valley +Bowmanville +Düziçi +Kālna +Sattenapalle +Tulcán +Al Hoceïma +Lagonoy +Basey +Beauvais +Kudamatsu +Pau d’Alho +Hod HaSharon +Pirapora +Barabai +Muğla +Vrindāvan +Chini +Iskitim +Acámbaro +Diglipur +Khāsh +Beni Enzar +Den Helder +Forest +Macclesfield +Pangantocan +Görlitz +Wellingborough +Tāybād +Mafra +Berwyn +Ar Riqqah +San Pedro Sacatepéquez +Lingen +Ramon +Ixtahuacán +Limoeiro +Porto Feliz +Naugaon Sādāt +Colomba +Sendhwa +Athiémé +El Cerrito +Bartolomé Masó +Stolberg +Moyobamba +Bianyang +Tiwi +Severn +Talegaon Dābhāde +Tama +Rafael Calzada +Wote +Hassa +Rolim de Moura +Mocoa +Boudouaou +Narbonne +Villarrica +Binalonan +Longxing +Sankt Augustin +Sucat +Albany +National City +Placer +Paços de Ferreira +Rosh Ha‘Ayin +Korkuteli +Lian +Narammala +Libungan +Amarante +Magsaysay +Lacey +Bihać +Pèlèngana +Esquipulas +Kūt-e ‘Abdollāh +Klimovsk +Poonamallee +Sarikei +M’diq +Shali +Kettering +Gangārāmpur +Rahovec +Oosterhout +Hohoe +Mount Prospect +Arcadia +Dongola +Mota +Mukōchō +Khlong Luang +Tilakpur +Moncalieri +Castelo Branco +Mizuho +Diffun +Cuizhuangzi +Waiblingen +Tiruvallūr +Mandapeta +San Andrés Villa Seca +Tirur +Mendi +Takizawa +Kimilili +Uman +Eschweiler +Bonga +Pithorāgarh +Kengri +Huaquillas +Myaungmya +Intibucá +Bluefields +Oke-Mesi +Langtang +Ropar +Parsippany-Troy Hills +Abū Za‘bal +Chiapa de Corzo +Lower Bicutan +General Tinio +Takaishi +Acul du Nord +Ajodhya +DeSoto +Poblacion +Smyrna +Bungoma +Longmen +Khān Shaykhūn +Changling +Belampalli +Kharar +Viramgām +Zarrīn Shahr +Sidi Bennour +Hilden +Bradenton +Braintree +Bambang +Biswān +Tsévié +Union +Rengo +Gulariyā +Cuneo +Umm Ruwaba +Kannamangalam +Royal Leamington Spa +Lesosibirsk +Azul +Atchampeta +Oulad Yaïch +New Brunswick +Apatity +Nōgata +Asadābād +Tursunzoda +Paombong +Będzin +Zile +Dzerzhinskiy +Nawucun +Siliancun +Hashtgerd +Kāndi +Nueva Loja +Meaux +Portimão +Hoogeveen +Nyenga +Guliston +Yongqing +Apple Valley +Iba +Mulanay +Kuala Kapuas +Tinley Park +Finote Selam +Hà Giang +Trapani +Urbiztondo +Saravena +Nuevo Casas Grandes +Buthidaung +Wellington +Raxaul +Pulheim +Kidderminster +Shendi +Sankt Pölten +Opava +Agrigento +Chystiakove +San Agustín Acasaguastlán +Sarasota +Barwāni +Porto Alexandre +Barrow in Furness +Batac +Villarrica +Videira +Qiryat Ata +Hacienda Heights +Chicopee +Nansan +Tonekābon +Zalaegerszeg +Biała Podlaska +Xinqing +Monte Mor +Tatalon +Ovar +Sacapulas +Asturias +Toki +Erumaippatti +Khorramdarreh +Langenhagen +Thérmi +Yamasá +Taliwang +West Haven +Mercedes +Winneba +Harpanahalli +Buta +Midalt +Herriman +Umm el Faḥm +Meyerton +Periya Semūr +Moreno +Três Pontas +Wangguanzhuang Sicun +Nordhorn +Madhupur +Foligno +Perth Amboy +Rijswijk +Porirua +Pombal +Chālil +Pinto +Verviers +Colmenar Viejo +Challakere +Casa Grande +Wuhuang +Tigard +Biel/Bienne +Kronjo +Puthiyangādi +Lazarevac +Nāmakkal +Vranje +Ibiporã +Santa Cruz +Hyères +Stryi +Linares +Māngrol +Shijōnawate +Békéscsaba +Novohrad-Volynskyi +Manapla +Chonthrhu +Bhīmunipatnam +Zgierz +Olímpia +Eqbālīyeh +Rangewala +Bobigny +Apopka +Ucuma +Lampang +Fālākāta +União da Vitória +Chino +Canoinhas +Touba +Agbangnizoun +Bilis Qooqaani +Elenga +Polūr +Bhongīr +Tāsgaon +Bad Homburg +Narutochō-mitsuishi +Dompu +Songjiangcun +Catu +Şırnak +La Roche-sur-Yon +Granadilla de Abona +Trani +Oxchuc +Tequisquiapan +Corby +Nāndod +Tivoli +Southaven +Imarichō-kō +Acıpayam +Luquembo +Canterbury +Zhushan +Pithāpuram +Queensburgh +Yenişehir +Jelgava +Minamiuonuma +Bad Salzuflen +Khurai +Świdnica +Ŏjŏk-tong +Tacuarembó +Punganūru +Trenčín +Hidaka +Piedade +Barranqueras +Santa Cruz +Chaiwu +Sarpsborg +Belén de Escobar +Schweinfurt +Phuthaditjhaba +Saruhanlı +Carigara +Sara +Hattingen +Chone +Mikkeli +Pila +Chota +Gabrovo +Annaka +Ghātāl +Terneuzen +Palmares +Santa Elena +Giurgiu +Sāgar +Huntington Park +Mūndka +Diamond Bar +Gus’-Khrustal’nyy +Jinchang +Masinloc +Volzhsk +Bentonville +Pontevedra +Trento +Jalapa +Clamart +Buderim +Ortaca +Douar Ain Chkef +Kampen +Ensenada +Antanifotsy +Nihonmatsu +Kouri +Yucaipa +Capitão Poço +Mbulungu +Tapas +Vannes +Pallíni +Caojia +Konan +San Isidro +Dikwella South +Medianeira +Plainfield +Javānrūd +Azrou +Shidong +Shidongcun +Sakurai +Islāmpur +Umm Qurūn +Ruma +Bełchatów +Morsott +Sidhi +Rāhuri +Chelles +Sado +Hanyū +Zarafshon Shahri +Manhattan +San Manuel +Kızılpınar +Paraćin +Aspen Hill +Rocky Mount +Cornillon +Valle del Guamuez +Bristol +Ankadinondry-Sakay +Christchurch +Rotorua +Peabody +Don Bosco +Frýdek-Místek +Wetzlar +Hashtpar +Kāsaragod +Komotiní +West Sacramento +Frenda +Bir Ali Ben Khalifa +Wayne +Huesca +Ixhuatlán de Madero +Cajicá +Louang Namtha +Puttūr +Mariano Acosta +Jalor +Tarma +San Jacinto +Chèddra +Samāna +Kunnamkulam +Kentwood +Palmaner +Xihu +Jālākāti +Deolāli +Lozova +São Gonçalo do Amarante +Prachuap Khiri Khan +Ribeira do Pombal +Juanjuí +Tagkawayan +Ghōriyān +Jihong +Kennedy Town +Titay +Puerto Libertador +Minalabac +Neustadt +Umred +Şabbāshahr +Bandar-e Torkaman +Hamura +Chenab Nagar +Sohna +Colton +Chilibre +Cholet +Vigan +Manicoré +San Bartolomé +Chaniá +An Nimāş +Passau +Manfredonia +Cabagan +Bacaadweyn +Narva +Bījār +Iganga +Impasugong +Pārvatipuram +Millcreek +Oak Park +Mansāla +Ullāl +Magpet +Juigalpa +Kitaotao +Louangphabang +Dholka +Ottappālam +Westchester +Smyrna +São Francisco do Sul +Zongo +A Yun Pa +Cangola +Funato +Gazipaşa +Upata +Svobodnyy +Wheaton +Hadali +Mora +Manjeri +Alcobaça +Kongoussi +Évry +Krasnokamsk +Al Majāridah +Ashta +Sakon Nakhon +Gisenyi +Lower Paxton +Cambundi Catembo +Évora +Encarnación de Díaz +Beaumont +Ilindu Kothi +Bisceglie +Aloha +Kāliyāganj +Jaggayyapeta +Minnetonka +Cuenca +Brits +Morondava +Liski +Modica +Marechal Cândido Rondon +Ilgın +Épinay-sur-Seine +Travnik +Extrema +Howell +Hongshandian +Ajuy +Pasacao +Marco de Canavezes +Popeşti-Leordeni +Goālpāra +Sibonga +Dilovası +Santa Lucía +Zhigulevsk +Pryluky +Mutsu +Kleve +Liberia +Yaguajay +Shāhpur +Guiuan +Tutóia +Pinellas Park +Ahlen +Granja +Tōkamachi +Keighley +Kannapolis +Gorkhā +Chaozhou +Itabirito +Montesilvano +Tame +Inhumas +Paramount +Dongshan +Amalāpuram +Tokmok +Açu +Dangila +Yangmei +Bakhtiyārpur +Vyksa +Legionowo +Arsikere +San Vicente +La Gomera +Saint-Ouen +Qingan +Estancia +Kabale +Hamilton +Dongsheng +Bitonto +Barbosa +Patancheruvu +Capivari +Pinotepa +Barobo +Rondon do Pará +Vikārābād +Molave +Bāruipur +Beberibe +Deva +Hereford +Dandarah +Dunfermline +Lucban +Zaragoza +Texas City +San Manuel +Sagua la Grande +Itapagé +Puttūr +Novato +Targovishte +Edina +Naka +Mahmutlar +Elda +Piekary Śląskie +Safranbolu +Aurora +At Tawāhī +Salo +San Estanislao +Izumi +Manglaur +Beloeil +Saint-Quentin +Phra Nakhon Si Ayutthaya +Simojovel de Allende +Kadiolo +Bagheria +Hyosha +Normal +Tilhar +Gudermes +Bondy +Xincheng +Buenavista +São Miguel do Guamá +Xinguara +Sultānganj +Congonhas +Tiruvalla +Kandıra +Mandi Dabwāli +Maha Sarakham +Tamiami +‘Alīābād-e Katūl +Clorinda +Grand Island +Xingcheng +Siena +Methuen Town +Frechen +Gallarate +Hrazdan +Timbaúba +San Remo +Elyria +Wheaton +Lorica +Usa +Harrismith +São Francisco +Malkara +Ksar Chellala +Bayonne +Pigcawayan +Ouenza +Jette +Lagoa da Prata +Karak +Madīnat Ḩamad +Woerden +Venkatagiri +Hamtic +Kelaa Kebira +Paracambi +Corbeil-Essonnes +Wolfenbüttel +Bozova +Kobryn +North Bay +Kendale Lakes +Pôrto Ferreira +Quezaltepeque +San José Villa de Allende +Bloomfield +Tramandaí +Kahnūj +Yaozhuangcun +Minami-Sōma +San Joaquin +Zhangjiazhuang +Zhangjiazhuangcun +Bani +Sihor +Tizimín +Brentwood +Imbituba +Roskilde +Cagnes-sur-Mer +Nerkunram +Jihlava +Başkale +Marana +Sertolovo +Guernica +Bad Kreuznach +Neibu +Velletri +Cihanbeyli +President Roxas +Burauen +Tlacotepec +Vyazma +An Nabk +Panjakent +Qabqa +Bamei +Thongwa +Pacora +Badūria +Dauis +El Rama +Claveria +Bârlad +Sironj +Ban Om Noi +San Miguel Ixtahuacán +West New York +Padangpanjang +Ibbenbüren +Tønsberg +Altrincham +Coronel Oviedo +Jangaon +Miyakojima +Charlottetown +Marechal Deodoro +Ibajay +Daudnagar +Paraíso do Tocantins +Jiyyammavalasa +Zalău +Acajutla +Antratsyt +Mandamāri +Lakhminia +Sison +Aïn Sefra +Twin Falls +Krasnokamensk +Marondera +Akbou +Campo Belo +Santo Estêvão +Delmiro Gouveia +Taquaritinga +Tekkeköy +Enerhodar +Lancaster +Óbidos +Cangandala +Lautoka +Pula +Taxco de Alarcón +San Pablo +São José do Rio Pardo +Korāput +Mudhol +Avellino +Pola de Siero +Naguilian +Victoria +Horishni Plavni +Florissant +Ålesund +Uriangato +Bato +El Tumbador +Vaulx-en-Velin +Bhajani +Phaltan +Shiraoka +Buíque +Glendora +Belorechensk +Acatzingo +Diu +Lengquancun +Portici +Ampanihy +Todupulai +Bitlis +Ālbū Kamāl +Chajul +Pontes e Lacerda +Thomazeau +Tangkak +Caetité +Yendi +Gummersbach +Ambohibary +Ad Dabbah +Tejen +Dungu +Takeo +Shiogama +Huamantla +Ibiza +San Onofre +Khattan +Leon +Cathedral City +Vaisampākkal +Kumarankari +Al Kharj +Ntoum +Piaseczno +Eilat +Muban Saeng Bua Thong +Timashevsk +Santa Eulalia +Mengmeng +Aliso Viejo +Vernon +Liaquatpur +Rumia +San Gil +Wangjiazhai +El Bagre +Tomiya +Alytus +Padre Garcia +Villareal +Vila Real +Arwal +Sevran +Choma +Kardzhali +Melo +Mollet +Fontenay-sous-Bois +M.Ə. Rəsulzadə +Palenque +Covilhã +Placentia +São Gabriel da Cachoeira +Crosby +Palmas +Sherpur +Burla +Hoffman Estates +Gādarwāra +Caleta Olivia +Baranoa +Caldas da Rainha +Caramoan +Gradiška +Pordenone +Utrera +Belle-Anse +Isfara +Guapimirim +Tamboril +Qalqīlyah +Kālpi +Aguadulce +Girardota +Civitavecchia +Baião +Santa Ignacia +Shahr-e Bābak +Alaminos +Solnechnogorsk +Xanxerê +Mulongo +Huazangsi +Tijucas +Irinjālakuda +Menggala +Kokstad +Tachilek +Zeralda +Teramo +Tirupparangunram +Maubin +Tulsīpur +İncirliova +Bandō +Zangāreddigūdem +Burien +Barri +Baham +Abdul Hakim +Ravensburg +Râs el Oued +Sombor +Bantacan +Böblingen +Dunwoody +Ensenada Berisso +Viana +Sūsangerd +Mangochi +Willenhall +Peine +Cerca la Source +El Kelaa des Srarhna +Bootle +Stratford +Speyer +Torrelavega +Uzlovaya +Morrinhos +Nentón +Koumantou +Gopālganj +Boukadir +Folkestone +’Aïn Merane +Tadmur +Brandon +Rastatt +Samut Prakan +Kalawana +Sapé +Lənkəran +Rincón de la Victoria +Kyzyl-Kyya +Palm Desert +Puerto Francisco de Orellana +Cutervo +Peranāmpattu +Troy +Guarne +Tamalous +Racibórz +Rongat +Wāliṅ +Dartford +Dabhoi +Montijo +Sartrouville +Severomorsk +Herning +Salekhard +Bargny +Bhavāni +Puthuppariyāram +Mascouche +Collierville +Sidlaghatta +Jūrmala +Korçë +Levittown +Leopoldina +Martin +Tsukubamirai +Fuchū +Barra +Brejo Santo +Marpalli +Bowringpet +Knysna +Sar-e Pul +Ocampo +San Narciso +Aurangābād +Hodal +Snezhinsk +Fatwa +Rosemead +Rosso +Kot Mumin +Shimencun +Weymouth +Segovia +Kalbā +Iormughanlo +Umán +Country Club +Tucurú +Massy +Enid +Horsham +Cumbernauld +Pardwāl +Djamaa +Cuyahoga Falls +Guaxupé +Tobias Barreto +Esher +Metpalli +Kalamansig +Mishawaka +Columbus +Andover +Kirishi +Juruti +Babīlā +Silkeborg +Repalle +Huaiyang +Skenderaj +Mabini +Kyōtango +Miyako +Pontal +Campos do Jordão +Teplice +Summerville +Vigia +Livingston +Nicoya +Banī Walīd +Cheremkhovo +Çermik +Levittown +Tŭrtkŭl +Geyve +Kuchaiburi +Vyāra +Whangarei +Zabīd +Sibsāgar +Irondequoit +Mahmudābād +Elmshorn +Grapevine +Goshogawara +Jarash +Zumpango +Marigot +Covina +Rio Tinto +Ranchuelo +Quirinópolis +Beruniy +Bikramganj +Agrínio +Göksun +Mweka +Chinchiná +Estância Velha +Sunāmganj +Milford city +Neath +Chinnamanūr +Draper +Fafe +Cataingan +Kotka +El Jem +Skien +Lakewood +Haskah Mēnah +Jaru +Metema +Sakaidechō +Catalina Foothills +Arao +Ānaiyūr +Kosamba +La Calera +Clacton-on-Sea +Cava de’ Tirreni +Emden +Northwich +Jaspur +Nichinan +Acireale +Susono +Delano +Gloria +El Nido +Maḩmūd-e Rāqī +Forbesganj +San Ignacio +Tebourba +Hunedoara +Naviraí +Bas Limbé +Tūyserkān +Laksar +Youfangcun +Cabreúva +El Talar de Pacheco +Bar Bigha +Tūndla +Arles +Boa Viagem +Bangued +Aringay +Haverford +Tanjombato +Ampitatafika +Plaisance +Wao +Myrnohrad +Chbar Mon +Ma‘ān +Amulung +Vargem Grande Paulista +Cojutepeque +Chinú +Bāghpat +Floreşti +Peñablanca +Rho +Donsol +Beveren +Rowley Regis +Artur Nogueira +Bhawānīpur Rājdhām +Houten +Yūki +Goslar +Ghŭlakandoz +Gombong +Nes Ẕiyyona +Nakagawa +Palpalá +Hamada +Karviná +Moskovskiy +Adeje +Gopālpur +Borovichi +Willich +Cardona +Qinhe +Bang Bua Thong +Scarborough +Lincoln +Ercolano +Sour el Ghozlane +Déressia +Roslavl +Itararé +Bhadrāchalam +Francisco I. Madero +Sfântu-Gheorghe +Tubod +Biaora +Sojat +Yerba Buena +Wolossébougou +Yasu +Miyoshi +Hassan Abdal +Murray +Mazara del Vallo +Leith +Pamplona +Heidenheim +Mooresville +Eger +Weert +Sakubva +Qabb Eliâs +El Ghâzîyé +Aarsâl +Ikot Okoro +Sechura +Chak Thirty-six North Branch +Rukan +Buni +Chak Sixty-one Gugera Branch +Al Wajh +Abū Ḩamad +Godinlabe +Ceel Dheere +Tukuyu +Kyaliwajjala +Āqchah +Krems an der Donau +Bandar Seri Begawan +Kahemba +Gutao +Xiayang +Dongxishan +Xiazhai +Jieshangya +Saoula +’Aïn el Turk +Semera +Bogoso +Tammampatti +Sujānpur +Shiv +Bhattu Kolān +Downers Grove +Louveira +Florin +Al Khārjah +Sihorā +Chatrā +Rovigo +Parāsia +Satte +Chake Chake +Middelburg +Cypress +Chahār Dangeh +Simraungaḍh +Porvoo +Ejeda +Muktāgācha +Janīn +Shankou +Al Ma‘allā’ +Bigadiç +Erftstadt +Lörrach +Stouffville +Hilsa +Poprad +Cuxhaven +Tepeapulco +Neyrīz +Pananaw +Xankəndi +Boaco +Nohar +Gediz +Gronau +Ain El Aouda +Östersund +Ostrołęka +Cerquilho Velho +Hannan +El Meghaïer +Concepción +Novovolynsk +Jeffersonville +Karapınar +Bhainsa +North Bethesda +Çine +Itupiranga +Pontevedra +Selibe Phikwe +Perintalmanna +Padrauna +Bignay +Albi +Gualán +Azusa +Yeovil +Coral Gables +Ridder +Wagga Wagga +Canguçu +Nanao +Zyryanovsk +Laval +Perambalūr +Chesterfield +Irákleio +Noshiromachi +Coelho Neto +Shawinigan +Aversa +Rosario +Kandangan +Pirot +Cimitarra +Sosúa +McLean +San Juan del Cesar +Huancavelica +Saint-Herblain +Kizhake Chālakudi +Mojo +Leonberg +Dumarao +Sesimbra +Mpigi +Sulleru +St. Louis Park +Araklı +East Honolulu +San Pedro +Rheda-Wiedenbrück +Gao +Shakhtarsk +Bad Oeynhausen +Peddāpuram +Novovyatsk +Payyoli +Tomisato +María la Baja +East Brunswick +Santa Lucía del Camino +Kameyama +Monte Alto +Bedford +Mājalgaon +Noveleta +Catemaco +São Bento do Una +Villaguay +Singen +Battipaglia +Gennevilliers +Özgön +Pen-y-Bont ar Ogwr +Nowshahr +Prudentópolis +Suzaka +Prattipādu +Scandicci +Kiblawan +Karlovac +Ksar Hellal +Santiago +Arifiye +Mulukukú +Freising +Sebeta +Mozhga +Wandan +San Severo +Bagumbayan +Haripur +Mbouda +Goba +Kapadvanj +Mailiao +Edenvale +Odiongan +Euclid +Bergkamen +Asakura +Higashiura +Kizlyar +Joliette +Buguruslan +Zargar +Agustín Codazzi +Tuodian +Lawrence +Tennala +Fangcun +Baicheng +Cintalapa de Figueroa +Carmen +Midori +Ceres +Samaniego +Yong’an +Straubing +Qalāt +Kaédi +University +Notsé +Rāmnagar +Biloxi +Tunuyán +Luleå +San Marcos +Slonim +Saray +Suresnes +Luwero +Guozhen +Hikari +Kefamenanu +Malabang +Sungurlu +Yamaga +El Dorado Hills +Nahuizalco +Pio Duran +Naugachhia +Ardea +Lesnoy +Frankenthal +Karlovy Vary +Vinzons +La Libertad +Port-Vila +Bornheim +Ōamishirasato +Misterbianco +Cerritos +Nizāmpur +Farajok +Sông Đốc +Jaggisettigūdem +Dulag +Rye +Changyŏn +Valladolid +Suwa +Burleson +Libona +Eltham +Fouka +Portage +Rimouski +Al Mayādīn +Barnstable +Courtenay +Alcala +Upplands Väsby +‘Akko +Kangar +Dublin +Panay +Dumingag +Nomimachi +Jiménez +Couva +Hampstead +Godda +Telerghma +Takeochō-takeo +Empoli +Tājūrā’ +Bādurpalle +Washington +Santo Domingo Suchitepéquez +Saint-Priest +Vertientes +Waalwijk +Shimotsuchō-kominami +Aïn Fakroun +Cacongo +Vincennes +Chikugo +Jales +Nikaweratiya +Bastia +Ocotal +Sanare +El‘ad +Sesto Fiorentino +Tunglangan +Tucano +Poway +Lal-lo +Cedar Hill +Harderwijk +Verrettes +Boucan Carré +Dongzhang +Takahama +Everett +Mladenovac +Koduvalli +Garmsār +Guntakal Junction +Gooty +Mantova +Brejo da Madre de Deus +Roman +Stillwater +Barendrecht +Colcapirhua +Amora +Caraballeda +Zaječar +Bantvāl +Barotac Viejo +Angol +Soest +Leiktho +Batajnica +Titusville +Namtu +Yenangyaung +Martigues +São Joaquim da Barra +Omitama +Masagua +Orangetown +Cwmbran +Inabanga +Suzukawa +Nyaungu +Sutton in Ashfield +Siocon +Kīlvishāram +Begoro +Liujiaxia +Niagara Falls +Khairābād +’Aïn Azel +Kyle +Jasdan +Etterbeek +Upperu +Kawkareik +Leesburg +Pedreira +Chieti +Mangalapādi +Kurganinsk +Wakema +Dollard-des-Ormeaux +Asadābād +West Orange +Babaeski +Kozlu +Minalin +Welwyn Garden City +Damāvand +Erlin +Waterford +Stade +Collegno +Beni Yakhlef +Kalima +San Pedro Mixtepec +Alsdorf +Alajuela +Grasse +IJmuiden +Mandeville +Upper Buchanan +Tuba +Levakant +Asunción Mita +Vite +Kara-Balta +Nong Khai +Beypazarı +Jiashizhuangcun +Westfield +Mahayag +Ishigaki +Berastagi +Chhāgalnāiya +Titao +Little Elm +Morden +Dongshan +Çan +Duyên Hải +Santa Rita +Homnābād +Joshīmath +Sanmu +Qulicun +Dachau +Smethwick +Hāgere Hiywet +Shuishang +Florida +Playas +Middletown +Gumia +Alta Gracia +Finike +Kirkcaldy +Shelekhov +Faya +Bokāro +North Highlands +Ami +Durham +Dornbirn +Bacolor +Balimbing +Tuckahoe +Wake Forest +Scafati +Minot +Araci +Roswell +Nettuno +Colonia del Sol +Bocaiúva +Aquidauana +Kanye +Kashira +Monroe +Hennef +Tunduru +Liutuancun +Santangpai +Nāngal Township +Ena +Monopoli +Yongping +Cuetzalan +Ash Shaykh Badr +Kasuya +Iwakura +Okhtyrka +Basoko +Sadāseopet +New Washington +Hoskote +Barras +Clondalkin +Carepa +Bhamo +Wauwatosa +Bothell +Birendranagar +Glenview +Vila Verde +Tibigan +Figueras +Dyero +Chong Nonsi +Rockwall +Báguanos +Golpāyegān +Jalandhar Cantonment +Cornwall +La Reja +Hørsholm +Pativilca +Jiquilisco +Monte Santo +Luwuk +Donetsk +Wilson +Victoriaville +Oranienburg +Bhatkal +Higashine +Güroymak +Águeda +Ad Darwa +Al Madrah Samā’il +Tremembé +Dūngarpur +Mamburao +Diamantina +Rancho Santa Margarita +Chandrāwāda +Kamidani +Zawiercie +Monte Carmelo +Ocosingo +Vale de Cavalos +Montrouge +Amahai +Ambatondrazaka +Pabellón de Arteaga +La Mirada +Georgina +São Benedito +Tonami +Antelope +Hilo +San Lorenzo +Invercargill +Longchamps +Gracias +Landau +Ono +Yumbe +Chetouane +Al Aaroui +Gavá +Hitachi-ota +Dumka +‘Āmūdā +Yecun +Kokkola +Catriel +Helsingør +Chioggia +Sogod +Pampán +Vich +Mairena del Aljarafe +Lamía +San Luis Obispo +Puerto Lempira +Bunawan +Rengam +Gaocheng +Okaya +Buena Vista Tomatlán +Aznā +Egg Harbor +Changanācheri +Dalaman +Poso +Roseville +Newark +Dülmen +Campi Bisenzio +Umarkhed +Serowe +Liuhu +Inđija +Perth +Zutphen +Atarra +La Vega +Altos +Osório +Chaparral +Malinao +Nanto +Lousada +Villa Constitución +Chenalhó +Penco +Voluntari +Vryheid +Sanski Most +Barnet +Sayanogorsk +Wejherowo +Jobabo +General Martín Miguel de Güemes +Sumisip +Qasbat Tadla +Aubagne +Kifisiá +Saint-Malo +Azhikkōd +Azhikkal +Kingisepp +Rio de Mouro +Arsenyev +Loum +Mentor +Talghar +Ocoee +Évreux +São José de Mipibu +San Andrés de Sotavento +Mataquescuintla +Gosen +Snizhne +Cunén +Oued Sly +Ayapel +Vikramasingapuram +Perinton +Waspán +Melle +Jiāganj +Livny +Manappārai +Kasumbalesa +As Sa‘dīyah +Rowland Heights +Zhongtanying +Evesham +Kikugawa +Douz +Otradnyy +Děčín +La Courneuve +Fort Pierce +Albuera +Rivoli +Yabrūd +Urrao +Sidrolândia +Capão Bonito +Cumanayagua +Paderno Dugnano +Ayungon +Pilar +Brea +Cantel +Şowme‘eh Sarā +Mattanūr +Campobasso +Qingyuan +Zarzal +Oro Valley +Chaïdári +Dracena +Hagaribommanahalli +Hà Tiên +Pallipālaiyam +Calimaya +Corato +East Providence +Saranambana +Dahutang +Al Balyanā +Banisilan +Haeryong +Casalnuovo di Napoli +Liulin +Viti +Santo Domingo +Jitaicun +Cuilapa +San Benedetto del Tronto +Nandikotkūr +Herzogenrath +Chomutov +Tomioka +Esplugas de Llobregat +Selu +Stretford +Vyshniy Volochëk +Soest +Maţāy +Mēla Gūdalūr +Martina Franca +Valencia +Boujad +Campo Maior +Borgne +Kabasalan +Neunkirchen +Poções +Lecco +Banbury +Beckenham +Noboribetsu +Blois +Pearl City +João Pinheiro +Lørenskog +Nanzhuangzhen +Greenford +Lohja +Santiago +Isabel +Ayr +Teotihuacan +San Jacinto de Buena Fe +Santa Ana Chiautempan +Al Quşayr +Araioses +Kotelniki +Wokingham +Hyvinkää +Rovenky +Salina +Lukavac +Jesús Menéndez +Sanuki +La Jagua de Ibirico +South Brunswick +Taebaek +Stepnogorsk +Guaramirim +Cologno Monzese +Woodstock +Punalūr +Kampli +Bizerte +Yashan +Ramat HaSharon +La Caleta +Kuilsrivier +Norala +Nellikkuppam +Daxiang +Ridderkerk +Hioki +Dongnanyanfa +Padra +Fujiyoshida +Schwerte +Hof +Muara Teweh +Yalvaç +Wangtan +Libertador General San Martín +Puerto Villarroel +Suluova +Brive-la-Gaillarde +Zarinsk +Bruchsal +Langford Station +Beavercreek +Obra +Faratsiho +Ḩajjah +Nakai +Quinte West +Montevista +Schagen +Lucaya +Aland +Dori +Tuy +Winter Garden +Puñal +Ḩadīthah +Potomac +Lower Tungawan +Świętochłowice +Gryazi +Vuyyūru +Cordon +Kadinamkulam +Corroios +Mānvi +Jendouba +Farmington +San Francisco de los Romo +Jeomchon +Henrietta +Santa Cruz do Rio Pardo +Nelson +Skierniewice +Kita +Rodgau +Albstadt +Río Tercero +Pakenham +Alcira +Sardasht +La Independencia +Quva +Charleville-Mézières +São Bento +Manacor +Lissone +Attleboro +Starogard Gdański +Nilambūr +Shimenzhai +Kilmarnock +Marino +Meudon +Cuihua +Bhālki +Halle-Neustadt +Pagalungan +Bindura +Ban Plai Bua Phatthana +Starachowice +Vidin +Kurayoshi +Farim +Cobija +Zhongzhai +Douera +Anandpur +Capannori +Kouré +Takashima +Nichelino +Pilar +San Lorenzo +Talisay +Narvacan +Aranđelovac +Santana do Ipanema +Carcassonne +Berëzovskiy +Izunokuni +Shimeo +Huntsville +Lawas +Chitré +Tingo María +Megion +Filderstadt +Labutta +Puerto Tejada +Strongsville +Zhob +Seabra +Tlalmanalco +Choisy-le-Roi +União +Manono +Casimiro de Abreu +Eagle Mountain +Noisy-le-Sec +Borča +Sumpango +Kasaoka +Bridgewater +Jammalamadugu +Acaxochitlán +Beringen +Garhwa +Santurce-Antiguo +San Felíu de Llobregat +Prescott +Sikandra Rao +Cavaillon +Eusébio +Nova Venécia +Bothaville +Bünde +Livry-Gargan +Tombôco +Nawāshahr +Trenque Lauquen +Gotha +Rānāvāv +Mairinque +Bihāt +Gahini +Mindat +Bājil +Huilongcun +Camp Perrin +Amboasary +Isiolo +Ḩarastā +Ban Bang Mae Nang +Kikuchi +Dingle +San Rafael +Jaguaquara +Markala +Santiago +Coonoor +Wodzisław Śląski +Thoubāl +Tamagawa +Paraguaçu Paulista +Freeport City +Olive Branch +Ourém +Haymana +Lupao +Donggang +Hokota +Lumba-a-Bayabao +Fastiv +Arlington +Joal-Fadiout +Koupéla +Campo Novo do Parecis +Chepén +Fellbach +Arendal +Stavroúpoli +Dharmaragar +Middletown +Juína +Parys +Goose Creek +Memmingen +Tallbīsah +Sicklerville +Chajarí +Igarapé +Pākaur +Settimo Torinese +Shima +Cheshunt +Jablonec nad Nisou +Shertallai +Havant +Aribinda +Hexiwu +Dodoma +Phan Rí Cửa +Anshan +Kaufbeuren +Nawai +Lubny +Salvador +Altamonte Springs +Badrashni +Haltom City +Ouled Moussa +Borgerhout +Gollalagunta +Hackensack +Dongsu +Tantangan +Dialakorodji +St. Thomas +Maluso +Rājgarh +Muradiye +La Goulette +Changchong +Yambio +Santa María Huatulco +Farrukhnagar +Dendermonde +Puttalam +Hokuto +Shangpa +Rosny-sous-Bois +Elmhurst +Mislata +Torzhok +Ogimachi +Porangatu +Jones +Chattamangalam +Denia +Remedios +Tangdukou +Tindouf +El Valle del Espíritu Santo +Qianjiang Shequ +Urbandale +Kitakata +Caiguantun +Nueva Gerona +Baiji +Ascoli Piceno +Kirkby +Mauriti +Los Banos +Río Cauto +Guacharachi +Talagutong +Kumta +Busaar +Littleton +Cuautepec de Hinojosa +Ortega +Sangarébougou +Veldhoven +Canela +Voi +Kanash +Warud +Ashburn +Salisbury +Kariya +Gaya +Lalian +Bāfq +Surčin +Lábrea +Kerkrade +Birāk +Carnot +Ganapathivattam +East Lansing +Olhão +San Pelayo +Fuxing +West Seneca +Mbulu +Palayan City +Jipijapa +Gobārdānga +Barra Velha +Bountiful +Erith +San Antonio Abad +Nazarovo +San Isidro +Ciudad de Huajuapam de León +Keller +Svitlovodsk +Karmiel +Vallenar +Morgan Hill +Gaoua +Rieti +Saint-Eustache +Weinheim +Puławy +Neustadt am Rübenberge +Hinckley +Ruzayevka +Tayug +Misséni +Radā‘ +Talence +Kelo +Jaraguá +Webster +Chimteppa +Gračanica +Sierra Vista +Cornélio Procópio +Vercelli +Manchester +Ashton +Gaoua +Drachten +Araquari +Medemblik +Paterno +Inongo +Banī ‘Ubayd +Presidente Dutra +Belfort +Garango +Hassi Messaoud +Sayreville +Basud +Surbiton +Numata +Puertollano +Marau +Massigui +Montemorelos +Castleford +Chalungalpādam +Aguilar +Bingmei +Aksay +Nova Kakhovka +Concepción +Inuma +Krosno +Arantāngi +Nāyudupet +Maracaju +Caeté +Alfortville +Haugesund +Bắc Kạn +Sundargarh +Abqaiq +Chalon-sur-Saône +Odenton +Prievidza +Biləcəri +Heusden +Slavonski Brod +Shchūchīnsk +Lapa +Cleveland Heights +Pakxan +Batroûn +Shaḩḩāt +Parma +Kutum +Nimule +Jedeïda +Baba I +Mladá Boleslav +Rabinal +Mahālingpur +Bhāyala +Nalegaon +Muddebihāl +Kedgaon +Sachīn +Ālamūru +Qarabulaq +Dīg +Hekou +Tank +Næstved +Barnagar +Brianka +Marhanets +Tres Valles +Angadanan +Timbó +Acopiara +Maria Aurora +Gallatin +Āstāneh-ye Ashrafīyeh +Plainfield +Nadym +Yatağan +Palm Springs +Mount Laurel +Seregno +Lancaster +Pederneiras +Catford +Tarnobrzeg +Port Loko +Borlänge +Razgrad +Charkhi Dādri +Āz̄arshahr +Buguias +Sagua de Tánamo +Shangshan +Shazhou +Dois Vizinhos +Volkhov +Guledagudda +Al ‘Azīzīyah +Tateyama +Bolívar +Riverton +West Lafayette +Parigi +Rāmeswaram +Santiago Atitlán +Meoqui +Xinji +Port Macquarie +Brentwood +Lehrte +Colotenango +Nasipit +‘Ayn al ‘Arab +Barru +Mochudi +Santa Bárbara +Merksem +Falkensee +Brühl +Santana do Paraíso +Manjuyod +São Lourenço +Balcarce +Jidd Ḩafş +Pemangkat +Sakura +Zwijndrecht +Chichigalpa +Liuquancun +Xique-Xique +Kimry +Cutler Bay +Cascina +Itaberaí +Worksop +Salon-de-Provence +San Pedro Perulapán +Melūr +Terracina +Lake Ridge +Whitney +Dhupgāri +Kafr al Baţţīkh +Sapanca +Sète +Pahrump +Lodi +Flores +Yaopu +Tangjiacun +Soliman +Mount Pleasant +Katipunan +Zamalkā +Bhārella +Kharan +Zouerate +Pyu +Dowlaiswaram +North Lauderdale +Concepcion +Portugalete +Dolores +Conceição do Araguaia +Xiluo +Jacaltenango +Kitgum +Fairfield +Sainthia +Gogrial +Mamanguape +Grabouw +Upper Bicutan +Wentzville +Istres +Augusto Correa +Tori-Bossito +Alcamo +Guisa +Jangamguda +Arboletes +Cayambe +Salem +Mawlaik +Mantes-la-Jolie +Piendamó +Garça +San Vicente +Fond du Lac +Seferhisar +Otwock +Gori +Nsawam +Culasi +Jalacingo +Petatlán +Shekhupur +Masuda +Tsiroanomandidy +Wiener Neustadt +Rohnert Park +Ellenabad +Banaybanay +San Pascual +Mankato +Moorhead +Radomsko +Morley +Karvārakundu +São Sebastião do Passé +Rāpūr +Abū al Maţāmīr +Siyāna +Abovyan +Angul +Bacnotan +Rājaldesar +Robē +Elmadağ +Tongoma +Yaoquan +Santo Antônio da Platina +Sandefjord +Bulalacao +Dalli Rājhara +Houbu +Svishtov +Nandigāma +Vlissingen +Beigangwa +San Jacinto +San Juan y Martínez +Eqlīd +Kołobrzeg +Iwanuma +Kaimana +São Miguel d’Oeste +Pagsanjan +Changtoushang +The Colony +Jisr ash Shughūr +Nanjō +Deinze +General Mamerto Natividad +Çayeli +Barwāh +Chauk +Alaplı +Pinneberg +Labangan +Bom Conselho +Dráma +Sonqor +Medak +Kaarst +Talipparamba +Huatan +Manakara +İznik +Jora +Pattani +Burke +Loon +Saint-Brieuc +Ananipalle +Erkelenz +Majurwa +Freeport +Cateel +Miliana +Tiberias +Maitum +Regla +El Centro +Dikili +Germencik +Al ‘Ayyāţ +Kuybyshev +Rio Grande da Serra +Las Matas de Farfán +Raisen +Albufeira +Harsīn +Turnhout +Shakopee +Central Signal Village +Wilkes-Barre +Mandaon +West Vancouver +Hagi +Daijiazhuang +Tōgō +Tlajomulco de Zúñiga +Roghun +Yartsevo +Zevenaar +Lompoc +Hicksville +Talatamaty +Noordwijk +Jānakammapeta +Georgetown +Oakland Park +Lombard +Otofuke +Concord +Sihora +Dębica +San Vicente de Tagua Tagua +Guinayangan +Al Midhnab +Nocera Inferiore +Murshidābād +Senigallia +Vilvoorde +Boyabat +Korogwe +Ahmadpur +Tinajdad +Limache +Seohāra +Yuchengcun +La Paz +Mucaba +San Jose +Coatbridge +Tarbes +New Tecumseth +Casale +Labason +Yuanli +Bakamune +Goshikichō-aihara-minamidani +Livramento de Nossa Senhora +Comendador +Alès +Wānkāner +Loreto +Wismar +Châlons-en-Champagne +Zongolica +Etten-Leur +Erkrath +Addanki +Berkeley +Hinatuan +Wallsend +Merthyr Tudful +Yirga ‘Alem +Nidadavole +Ciudad Constitución +Beni Saf +Xinjun +Baler +Bellinzona +Varaždin +Anekal +Āzādshahr +Jaro +Rasskazovo +Bietigheim-Bissingen +Sugito +Salto de Pirapora +Himi +Lubuk Sikaping +Shyorongi +Ząbki +Chusovoy +Badian +Thun +Murzuq +Kottangara +Pittsfield +Venray +Kalilangan +North Brunswick +Takāb +Cameron Highlands +Bushenyi +Maştağa +Bagneux +Porsa +Bodupāl +Krasnodon +Tanghin-Dassouri +Alhaurín de la Torre +Puteaux +Rāmachandrapuram +Greenacres +Chembra +Gifhorn +Jinku +Shinshiro +Nijkerk +San Isidro +Santa Elena +Raduzhnyy +Roseller Lim +Villeta +Birsk +Caluire-et-Cuire +Kumatori +Uruará +Cové +Guambog +Prostějov +’Aïn Arnat +Zhexiang +Presidente Epitácio +Shōranūr +Ksar +Rheden +Oakley +Panambi +Kiraz +Jerada +Cimerak +Of +Ponte de Lima +Çeşme +Borken +Bahía Honda +Linden +Heinsberg +Tarauacá +Campbell +Moises Padilla +Paoskoto +Al Kiswah +Hokuto +Danville +Pedro Celestino Negrete +Toboso +Kikuyō +Kilosa +Frosinone +Loreto +Victoria +Bolvadin +Anse Rouge +Puerto del Rosario +Actopan +De Bilt +Nilothi +Dounan +Trelleborg +Yutiancun +Isla +Jambusar +Xihuachi +Sawākin +Innisfil +Turda +Kozáni +Shināş +Hueyapan de Ocampo +El Cuá +General Pacheco +Kōttōppādam +Douar Bni Malek +El Asintal +Villa de Zaachila +Zhongbu +Lota +San Clemente +Andes +North Miami Beach +Vaudreuil-Dorion +Alenquer +Clermont +Silistra +Vargem Grande +San Sebastián Huehuetenango +Aleksinac +Konārak +Paso de los Libres +Ocozocoautla de Espinosa +Maḩallāt +Kula +Khawr Fakkān +San Bruno +Ambāsamudram +Véroia +Laurel +Yeola +Nagaizumi +Channelview +Vahdat +Shanshan +South Upi +Trujillo Alto +Kapan +Nagykanizsa +Aguazul +Darcheh +Berchem +Dock Sur +Vergína +Şān al Ḩajar al Qiblīyah +Sonaguera +Bāniyās +Zhangliangcun +Chicamán +Cumaribo +Maïssade +Khutubi +Ormond Beach +Rantepao +Kāyalpattanam +Kobayashi +Inabe +Dún Dealgan +Vargem Grande do Sul +Sidcup +Macenta +Eṭ Ṭaiyiba +Bakau +Nettetal +Taketoyo +Huber Heights +Rudauli +Dhāka +Lubao +Tešanj +Dunakeszi +Makinohara +Kamen +Alcantarilla +Bron +Yatomi +Barhiya +Woonsocket +Kattivākkam +Kingston upon Thames +Jesús María +Gualeguay +Hillsborough +Santa Cruz +Garbahaarrey +Saoner +Rezé +Yepocapa +Valenciennes +Abucay +Periyakulam +Cuchi +Lai Châu +Middleton +Aurich +Châteauroux +Jatibonico +Heist-op-den-Berg +Santo Antônio +San Cristóbal Totonicapán +Nandongcun +Nandazhang +Buffalo Grove +Badulla +Akdağmadeni +Char Fasson +Zhangcun +Bairāgnia +Sevanagala +Pirthīpur +Bradford West Gwillimbury +Tepalcatepec +Villaba +Shimabara +Mahbūbābād +Santo Tomas +Garges-lès-Gonesse +Fleet +Kalaiyā +Longtoushan Jiezi +Lucena +Puerto Colombia +King’s Lynn +Salaberry-de-Valleyfield +Chefchaouene +Laguna +La Mata +West Babylon +Ban Pet +Nueva Valencia +Al Badārī +Limbdi +Catonsville +Barreiros +Atenco +Mrirt +Itarema +Altadena +Bukama +Naranjo +Ciechanów +Edmonds +Lokeren +Gravina in Puglia +Linton Hall +Thol +Spanish Fork +Hammam Sousse +Newnan +Sarıkamış +Taniyama-chūō +Musina +Orlândia +Meru +Jincheng +Laatzen +Castres +Proper Bansud +Nakano +Quinhámel +Vineyard +Evere +Mengdong +Cabo Bojador +Kafr al Kurdī +Barpeta +Jefferson City +Webuye +Miranda +Kharik +Sidi Mohamed Lahmar +Mondoro +El Affroun +Sukheke Mandi +Manassas +Tecoanapa +Biella +Woodbridge +Zharkent +Puyallup +San Francisco Menéndez +Fianga +Arras +Šibenik +Awaji +Sālār +Nyunzu +Hwange +Simão Dias +Shuilou +Vera Cruz +Machalí +Mata de São João +Panitan +José de Freitas +Thomassique +Jājpur +Sokoura +Tamu +Uruaçu +Allūru +Schertz +Guarda +Amberg +Maroantsetra +Balabac +El Charco +Kōta +Loufan +Kadalur +Coppell +Itapa-Ekiti +Danville +Mariel +North Fort Myers +San Giorgio a Cremano +Jambughoda +Columbia +Dupnitsa +Bairuo +Shebekino +Osinniki +La Orotava +Moline +Seevetal +Kārsiyāng +Dilbeek +Tucuran +Singhanakhon +Sanarate +Carranglan +San Rafael del Sur +Santa Cruz +Beverly +Eisenach +Si Sa Ket +Midland +Xidiancun +Bassano del Grappa +Bang Kruai +Bhatpalli +Santa Rita +Alghero +Balykchy +Melun +Kotabumi +São Mateus do Sul +Thān +Acatlán de Pérez Figueroa +Laungowāl +Akaiwa +Shenjiatun +Annandale +Yaguate +Qā’en +Ben Zakkay +Rouyn-Noranda +Tomé +Iten +Xonobod +Pallisa +Homburg +Kasai +Llanera +Futtsu +Coachella +Coronel Suárez +Alerce +Polanco +Dreieich +Maniwa +Kutno +Hoddesdon +Myanaung +Marki +Akbarpur +President Quirino +Woodlawn +Viborg +Palladam +Uozu +Ansbach +Hilvan +Liloy +Fareham +Meadow Woods +Kārmegh +Aritao +Smederevska Palanka +Machang +Sarakhs +Coram +Qapshaghay +Jocotepec +Thionville +Amontada +Mozdok +Belleville +Nysa +Morarano Chrome +Safonovo +Peachtree Corners +Claveria +Liantang +Ängelholm +Anakaputtur +Diyadin +Cortlandt +Dildārnagar +Cáceres +Jinotepe +Pardés H̱anna Karkur +Kālol +Monchegorsk +Hollister +Villa Bisonó +Sbiba +Bensheim +Esperanza +Pratāpgarh +Bokhtar +Coyuca de Catalán +Puerto Real +Paraíba do Sul +Guaratuba +Quesada +Imperia +Soran +Sarāb +Guaraciaba do Norte +Zhovti Vody +Siegburg +Tineghir +Madridejos +Mampong +San José de Bocay +Miercurea-Ciuc +Holly Springs +Wangyuanqiao +Korenovsk +Nāthdwāra +Dronten +Pā̃chkhāl +Ti̇̄npiple +Santa Rosa del Sur +Štip +Myedu +Songhuajiangcun +Qiryat Moẕqin +Lingquan +Muttayyāpuram +Vawkavysk +Greenock +Matnog +São Lourenço do Sul +Nāikankudi +Mercedes +Puerto Galera +Zhujiacun +Yangcunzai +Yagoua +Nuevo San Carlos +Kolchugino +Hódmezővásárhely +Tactic +Delaware +Hố Nai +Santa María Atzompa +Tiel +Ōizumi +Kirchheim unter Teck +Tororo +Minamishimabara +Amadeo +Qiryat Ono +Schwäbisch Hall +Kibawe +Ban Wat Lak Hok +Larantuka +Barauli +Dunaújváros +Beverwijk +Macaúbas +Montenegro +Kalmar +Marialva +Quezon +Coburg +Yinggen +Tadjenanet +El Aïoun +Urmston +Coyotepec +San Miguel +Miura +Palmares +Rancho Palos Verdes +Diamond Harbour +Waxahachie +Jardinópolis +Satka +Civitanova Marche +Tādif +Köniz +Uden +Sūrampatti +Velasco Ibarra +Kasibu +Paiçandu +Krasnoznamensk +Beitbridge +Dacun +Nārāyanpet +Tôrres +El Consejo +Naga +Boucherville +Santa Bárbara +Fenoarivo Atsinanana +Saidpur Dabra +Shādegān +Dandéresso +Bodø +Álimos +Campechuela +Otavalo +Tavas +Varash +Oued Fodda +Billerica +Tolosa +Kampong Chhnang +Salinas +Ust’-Kut +Wunstorf +Hempfield +Boumerdes +Pueblo Nuevo +Gonzaga +Litian Gezhuang +Blanes +Bayt al Faqīh +Mableton +Jáltipan de Morelos +Přerov +Fitchburg +Gay +Kenge +Daram +Ambohitrimanjaka +Yuzawa +Oqtosh Shahri +Shefar‘am +Le Cannet +Berubāri +Ayagöz +Bullhead City +Su-ngai Kolok +Rājgīr +Socorro +Santa Cruz de Los Taques +Shetou +Sipe Sipe +San Pedro Necta +Dubrovnik +Santa Helena +Lloret de Mar +Indi +Amuntai +Rajaori +Slobozia +Lindi +Anda +Águas Belas +Patulul +Yarumal +Copán +Strezhevoy +Tagudin +San Donà di Piave +Nalhāti +Ruteng +Ventanas +Sānand +Puntarenas +Bourg-en-Bresse +Mission +Puerto Gaitán +Barra dos Coqueiros +Poti +Shimotsuma +Königswinter +Zugdidi +Kodīnar +Sagauli +Golāghāt +Pīleru +Verkhnyaya Salda +Sutton +Marlboro +Karabulak +Sinanpaşa +Llavallol +Desio +Nola +Eberswalde +Memāri +Banī Suhaylā +Tabogon +Teaneck +Svay Rieng +Grove City +Arcos +Lusambo +Mondragon +Yakacık +Maplewood +Kendrāparha +Villa Ángela +Nürtingen +Caerphilly +Marion +Nebbi +Marlborough +Tortuguitas +San +Myski +Naini Tal +Erzin +Santa Cruz +Maulavi Bāzār +Bambari +Germering +Kitaibaraki +Sena Madureira +Tuchín +Narathiwat +Brookfield +Obita +Nordhausen +Tezoyuca +Rāmganj Mandi +El Tocuyo +Santo Antônio de Pádua +Nurdağı +Currais Novos +Mahalapye +Meïganga +Arauquita +Hückelhoven +San Lorenzo +Alcala +Tabarka +Caloundra +Alcalde Díaz +Bridgwater +Leigh +Jayamkondacholapuram +Cherupulasshēri +Mikołów +Guácimo +Hatfield +Wijchen +Panchimalco +Mersa +Niquero +Chaves +Fredericia +Rozzano +French Valley +Santa Eulalia del Río +Schwabach +Schweizer-Reineke +Maayon +Lasam +Hoboken +Mangaratiba +Bacong +Constitución +Shelton +Túquerres +Nossa Senhora da Glória +Trikarpūr North +Shutayil +Agar +San Jose de Urquico +Tanxia +Zákynthos +Antequera +Claypole +Pine Bluff +Peddapalli +Igualada +Kearny +Villa Alsina +Sevilla +Anglet +Geel +Bilāsipāra +Timmins +Halfeti +Guaduas +Toritama +Temryuk +Matiguás +Spassk-Dal’niy +Paliā Kalān +José Mármol +Hālol +Ganta +Kallār +Hallandale Beach +Mambajao +Germantown +Huizen +La Libertad +Angoulême +Ardeşen +Sevilla de Niefang +Rivas +Ambositra +Newbury +Merano +Bujanovac +Komoro +Ishaka +Sumoto +Tagbina +Amami +Pozi +Arraiján +Milton +Tampakan +Unzen +Ciudad Darío +Bazar-Korgon +Ciudad Vieja +Pyt’-Yakh +Upper Hutt +Gatunda +Sarandë +Dongyangshi +Shengaocun +Dongluocun +Segovia +Chambas +Welling +Woburn +Beigang +Hongfengcun +Wujie +Monterotondo +Patti +Esperantina +Marikina Heights +Lancaster +Paranaíba +Drogheda +Bagua Grande +Nanbei +Dowlatābād +Maddela +Lijiaxiang +Reynoldsburg +Yingzhou Linchang +Covington +Salinópolis +Azemmour +Famagusta +Buxtehude +Ciudad Piar +Dhamdāha +Boulogne-sur-Mer +Muaná +Velika Plana +Delījān +Jalalpur Bhattian +Pugachev +Friendswood +Talukkara +Meftah +Penn Hills +Betun +Apóstoles +Weslaco +Malilipot +Patzicía +Santo Tomas +Land O' Lakes +Essex +Tālcher +Soe +Wattrelos +Cuito Cuanavale +Bartlett +Promissão +Anyuan +Qādiān +Fourou +Mobo +Pāmban +Abadan +Buchholz in der Nordheide +Neumarkt +Ahlat +Saint-Germain-en-Laye +Kitob +Westfield +Temsia +Vallehermoso +Swords +Vavveru +Elmalı +Gonābād +Sidi Moussa +Baichigan +Lobo +Sassuolo +Kingswood +Pie de Pató +Buddh Gaya +Shanhe +Katō +Kirkkonummi +Khowrmūj +Annapolis +Villafranca del Panadés +Arys +Ipiaú +Dunstable +DeKalb +Vasto +Mambusao +Maragondon +Cedar Falls +Pirmasens +Itabaianinha +Qeshm +Tomar +Sukuta +Kadūr +Sint-Truiden +Mundakkal +Sherghāti +Nanuque +Veles +Bury Saint Edmunds +Zoumi +Avezzano +Artigas +Ladispoli +Pechora +Barra de São Francisco +Dayr Mawās +Manalapan +Santa Rita do Sapucaí +Nabas +Akyurt +Evren +Yangtangxu +Yatangcun +Lancaster +Njombe +Douar Laouamra +Şəmkir +Pôrto de Moz +Lemgo +Pilāni +Remanso +Siay +Bayındır +Nowgong +Santa Maria da Boa Vista +Metlili Chaamba +Brighton +Bayburt +Ölgiy +Bosconia +Jānjgīr +Uray +Romblon +Andradas +Padre Las Casas +Katsuren-haebaru +Lordegān +San Juan +Villenave-d’Ornon +Gap +Macerata +Cumbal +Guariba +Sovetsk +Freiberg +Erramvāripālem +Corigliano Calabro +Mińsk Mazowiecki +Crystal Lake +Dacun +Halberstadt +Lake Oswego +Severna Park +Pilate +Sosnovoborsk +Krishnarājāsāgara +Rizal +Leinfelden-Echterdingen +Villanueva +Lakeshore +Ramsgate +Findlay +Mārākkara +Huaura +Aracataca +Channarāyapatna +Montélimar +Leyte +Compiègne +Saratoga Springs +Alexandria +New Berlin +Pālampur +Agano +Chebarkul +Hofheim +Reguiba +Zainsk +Almenara +Jhābua +Stains +Vellakkovil +Culver City +Bouar +Beyneū +Curuçá +Balaoan +La Unión +Rubengera +Sabalgarh +Sensuntepeque +Molina +Komono +Kualaserba +Indian Trail +Bañga +Romny +Magburaka +Hellevoetsluis +Payabon +Talas +Colinas +Ampana +Murtajāpur +Kamen’-na-Obi +Peringalam +Autazes +Ōzu +Duncanville +Valley Stream +Yajalón +Belaya Kalitva +Kardítsa +Löhne +Pinamar +Tosya +Ahaus +Afogados da Ingazeira +Majibacoa +Hıdırbey +Fonds Verrettes +Clinton +Camargo +Merta +Uttarkāshi +Boa Esperança +Świnoujście +Schorndorf +Kabuga +Leramatang +Itoigawa +Secunda +El Golea +Havza +Völklingen +Maihar +Begamganj +Gagny +Pedra Branca +Jambe +Santa Quitéria +Salinas +Tiruchendūr +Mokolo +Sejenane +La Rinconada +Myrhorod +The Acreage +Colomiers +Malalag +Balud +Podilsk +Tutayev +Torre Annunziata +Dohazāri +Taysan +Taicheng +Gourcy +Kīlakkarai +Kālimpong +Cihuatlán +Gramado +Romeoville +Oroqen Zizhiqi +Dingras +Heroica Ciudad de Tlaxiaco +Socorro +Phulwāria +Sisak +Luebo +Amursk +Järvenpää +Chiredzi +Soria +Longtang +Raxruhá +Kakrāla +Bragadiru +Santo Domingo +Zagora +Menghan +Hurst +Altagracia de Orituco +Kunigal +Mailapur +Varkkallai +Soavinandriana +Curitibanos +Montana +Mayyanād +Panaji +Poissy +Sieradz +Inverness +Armação dos Búzios +Oraiókastro +Zacualpa +Post Falls +Ihnāsyā al Madīnah +Jbaïl +Matsoandakana +Bukit Gambir +Bandhi +Safdarabad +Jalalabad +Choa Saidan Shah +Ranipur +Lidköping +Kambia +Uchqŭrghon Shahri +Madīnat ‘Īsá +Bamessing +Alashankou +Strood +L’Asile +Chikodi +Rājgarh +Sindgi +Wādegaon +Sardulgarh +Samālkha +Junnar +Salaiya +Tāzah Khūrmātū +Nicastro +Valladolid +Hutchinson +Kasumigaura +Cabatuan +Yalutorovsk +Lishaocun +Herstal +Szigetszentmiklós +Fushë Kosovë +Visoko +Kyaukme +Zimapan +Yian +Santa Cruz del Sur +Jayrūd +Sarāqib +Vyshneve +Xaçmaz +Qiryat Bialik +Brčko +Zhongbai +Chelsea +Santiago Nonualco +Barcellona-Pozzo di Gotto +Ngozi +Acevedo +Waipahu +Lynnwood +Yecapixtla +Sagae +Winslow +Kīsh +Koumra +Northampton +Zvolen +Rāmdurg +Plaridel +Panglao +Braine-l’Alleud +Tago +Matías Romero +Baco +Pinhal +Maintal +Kópavogur +Rauma +Rovereto +Lincoln Park +Huamachuco +Ostfildern +Pamplona +Oshnavīyeh +Villamontes +Sebdou +Amudālavalasa +Fort Lee +’Aïn el Melh +Santo Niño +Cape Girardeau +Atmakūr +Yomitan +Kırkağaç +Palauig +Montclair +Hobbs +Toukountouna +San Nicolas +Carini +Magsaysay +Benenitra +Hangu +Aizawa +Apsheronsk +Massawa +Tivaouane +Maarssen +Draguignan +Shirone +Surin +Novozybkov +Alimodian +Albano Laziale +Kurobeshin +Cantù +Veruela +Pomigliano d’Arco +Caraga +Burjasot +Tianzhong +Ettlingen +Buldon +Masaki +Temascal +Oshakati +Talacogon +Srebrenik +Ţabas +Tulun +Kovvūr +Líbano +Fonseca +Carol Stream +Plant City +Xisa +Douai +Todos Santos Cuchumatán +Wageningen +Nakama +Poblacion +Ninove +Yomra +Capelinha +Aventura +Despatch +Bāsudebpur +Charbagh +Pärnu +Tafí Viejo +Vendrell +Şamaxı +Huanghuajie +La Troncal +Isfisor +Villa del Carbón +Skövde +Manay +Chachoengsao +Villa de San Diego de Ubaté +Jordan +Lebanon +Freital +Djidian Kéniéba +Salcedo +Ihosy +Streamwood +Oviedo +Jiaozishan +Tucumã +Fondi +Kalingalan Caluang +Ḑubā +Écija +Mount Juliet +Farīmān +São Gonçalo dos Campos +Nova Viçosa +Virreyes +Media +Ossining +La Tebaida +Itápolis +Lilio +Pachrūkha +Brant +Siniloan +Alicia +Doğanşehir +San José de Ocoa +Sogod +San Giuliano Milanese +Quincy +Marratxi +Asamankese +Kalu Khan +Lālganj +Islāmpur +Qiryat Yam +Plasencia +Presidente Venceslau +Parral +Whanganui +Xonqa +Issaquah +Sanjiang +Parkland +Guajará-Mirim +Olintepeque +Khāchrod +Żyrardów +Abinsk +Bādepalli +Sijua +Park Ridge +Bagnolet +Morshansk +Kaffrine +Clarin +Marcq-en-Baroeul +Spruce Grove +Placilla de Peñuelas +Seram +Gradačac +Pamplona +Vādāsinor +Abrantes +Amambaí +Naranjal +Damulog +Jacarèzinho +Puerto Berrío +Cocorote +Cesano Maderno +Sadiola +Cacuso +Tangpingcun +Moanda +Marovoay +Istog +Neu Isenburg +Nueva Santa Rosa +Niederkassel +Jogbani +Galkot +Chojnice +Cottage Grove +Guaíra +Takikawa +Bell Gardens +Oliveira +Matinhos +Rio Negrinho +Tarazá +Solan +Hailākāndi +Al Qā‘idah +Magra +Apan +Tamba-Sasayama +Erattukulakkada +Köyceğiz +Nawābganj +Yuzhnoukrainsk +Langen +Yelizovo +Ospino +Ayirāpuram +San Gabriel +Playa Vicente +Warren +Heemskerk +Kampot +Axochiapan +Androka +Guemar +Baikonur +Visconde do Rio Branco +Cacocum +Masamba +Marcos Paz +Mibu +Bayanan +Security-Widefield +Iesi +Manggar +Mettmann +Grants Pass +Ilmenau +Cakung +Keizer +Idangansālai +Çiftlikköy +Ait Ourir +Stendal +Agoncillo +Chittaranjan +Halle +Sual +Penfield +Moatize +Roy +Nueva Rosita +Susurluk +Pirna +Huanren +Narón +Partāpnagar +San Dionisio +Sidi Bibi +Weißenfels +Lluchmayor +Revelganj +Torre-Pacheco +Marinha Grande +Sébékoro +Camaná +Mitsuke +Kaseda-shirakame +Amla +Rtishchevo +Toumodi +Ambatomainty +Al Mālikīyah +Trofa +Ramallah +Bettendorf +Kachkanar +Cleethorpes +Nuevitas +Betamcherla +Ciudad Melchor Múzquiz +Ancud +Sidi Khaled +Gornji Milanovac +Várzea Alegre +Nongzhangjie +Tromsø +Pala Oua +San Fernando de Henares +Sayansk +Sciacca +La Chaux-de-Fonds +Gabaldon +Nabarūh +Goes +Mafamude +São Fidélis +São Raimundo Nonato +Dibaya-Lubwe +Nallūr +Galeana +Königs Wusterhausen +Yangfang +Brumadinho +Staoueli +Penumūr +Westerville +Caluya +Wālājāpet +Huangyoutang +Garalo +Changchunpu +Juan Rodríguez Clara +San Nicolas +San Raimundo +Tuvāgudi +Empalme +Draa el Mizan +Cabugao +Higashimatsushima +Xiwanzi +Royal Palm Beach +Dwārka +Haverstraw +Birmitrapur +Apache Junction +Pehowa +Inashiki +Taşköprü +Saryaghash +Akçakoca +Tshela +Hitachiomiya +São Mateus do Maranhão +Navarre +Ngororero +Aïn Tedeles +Wheeling +Ohrid +Lake Stevens +Skelmersdale +Santa Helena de Goiás +Nūrpur +Rexburg +Pervomaisk +Ermezinde +Ōmagari +Dubbo +Nyköping +Mehidpur +Tipton +Lambaréné +Campina Grande do Sul +Ban Bang Khu Lat +Eccles +Braintree +Gujō +Fasano +Urbana +Aborlan +Shrewsbury +Valencia +Penalva +Los Palacios +Yoshinogawa +Villepinte +Ouro Branco +Rosenberg +Tinnanūr +Dzhankoi +Barbacoas +Cajibío +Los Palacios y Villafranca +Pinner +Monreale +Taibao +Great Yarmouth +Paracuru +Yako +Real +Sakuragawa +Kwai Chung +Vetapālem +Jamindan +Kamp-Lintfort +Margosatubig +Tonbridge +Tianchang +Zacatelco +West Fargo +Pereslavl’-Zalesskiy +Paravūrkambolam +Tuusula +Považská Bystrica +Ilkeston +Shibata +Mariinsk +Voghera +Armavir +Metlaoui +San José de Las Matas +Ábrego +San Fernando +La Presa +Dabola +Kampung Baharu Nilai +Menomonee Falls +Vengat +Santa Maria da Vitória +Arucas +Vestavia Hills +Calexico +Würselen +Ciampino +Valrico +Aketi +Køge +Schio +Leyland +Ibusuki +Champerico +Leribe +Papenburg +La Vergne +Ban Na Pa +Sungandiancun +Vangaindrano +Strängnäs +Achaguas +Khalkhāl +Aziylal +Glenrothes +Alingsås +Santa Clara del Cobre +Bərdə +Klinë +Rājula +Atlantic City +Konakovo +San Andres +Caibarién +Nishiwaki +Rosário +Sarikishty +Al Ghizlānīyah +La Unión +San Felipe Orizatlán +Ingeniero Pablo Nogués +Emîr Abdelkader +Maasin +Saint-Martin-d’Hères +Coto Brus +Clovis +Mahādeopur +Chartres +Szczecinek +Minas +Saronno +Olutanga +Novodvinsk +Lanyi +Genç +Buug +Chorley +Chacabuco +Peachtree City +Phenix City +Sibaté +Melmadai +Buchireddipālem +Shijiazhuangnan +Hammam-Lif +Qiaotouyi +Darāw +Krasnoufimsk +DeLand +Herne Bay +Vredenburg +Kaka +Usinsk +Fribourg +Miyoshidai +Nindirí +Bougara +Misawa +Mtsensk +Atamyrat +Waregem +Ripollet +Kurchatov +Xiaguanying +Marcianise +Steyr +Patnongon +Alekseyevka +Mechanicsville +Itaitinga +Novo Horizonte +Wilrijk +Nigel +Bayan +Şarkışla +Samal +Chuangjian +Khairtal +Mmabatho +Iturama +Krasnyy Sulin +Azzano +Stanton +Maule +Laur +Tāki +Am-Timan +Siyang +Maibara +Antsohihy +Xiushui +Ibara +Barrancas +Zavolzhye +Matale +Brasschaat +Bat Khela +Città di Castello +Dicle +Menglie +Pitangueiras +Holyoke +Greven +Winter Springs +Américo Brasiliense +Bishops Stortford +Xico +Mechelen-aan-de-Maas +Baksan +Wesseling +Borbon +Joué-lés-Tours +Naushahro Firoz +Porur +Pallipram +Oeiras +Tayasan +Zacualtipán +Kehl +Fereydūn Kenār +Karuvambram +Matozinhos +Choybalsan +Bautzen +Owasso +Bradford +Prattville +Cananea +East Point +Trujillo +Campbell River +Navgilem +Vavuniya +Quba +Entre Rios +Orange +San Germán +Shankarpur Khawās +Shengang +Veghel +Sabang +Clifton Park +Savelugu +Thakraha +San Mateo +Mapandan +Prokuplje +Mantingan +Pacifica +Bol’shoy Kamen’ +Bangar +Dubrājpur +Hot Springs +Shambhunāth +Gūdārah +Aristóbulo del Valle +Zhongdong Shequ +Rūdsar +Abū Qīr +Gurais +San Pédro Jocopilas +Bagabag +Dengtangcun +Sidi Yahia El Gharb +Sama +Tlapa de Comonfort +Mucuri +Olot +Yamen +Adelanto +Backnang +Princeton +San Juan Cancuc +Qaratog +Northglenn +Goribidnūr +Kéniéran +Ali Sabieh +Tupelo +Hajīn +Biougra +La Quinta +Ureña +Sampués +San Adrián de Besós +Annemasse +Raalte +Srīnagar +Puri +Rusape +Shakhtīnsk +Pedro II +Bitterfeld +Dhanera +Guayacanes +Obala +Andkhōy +Świdnik +Giyon +Luuk +Celje +Gampola +Ameca +Namlea +Elmont +Tlalixcoyan +Cerveteri +Dibulla +Mission Bend +Tumba +Gaspar Hernández +Pen +Bangkinang +Montclair +Izobil’nyy +Mateus Leme +Güira de Melena +La Puente +Santaluz +Carpentersville +Koboko +Al Bayḑā’ +Al Qaryatayn +Emirdağ +Santa Pola +Pentecoste +Oleiros +Cheltenham +Cheyyār +Pathanāmthitta +Mahanoro +Kaštel Stari +Teijlingen +Nové Zámky +Tudela +Belleville +Ohangaron +Patía +Sillanwali +San Antonio de Padua +Charaut +San Sebastián de Mariquita +Kwidzyn +Arnold +Santo Domingo +Afuá +Marantao +Rāni +Long Eaton +Prince Albert +Güzelbahçe +Calpulalpan +Jalapa +Karuhatan +Manali +Sun City +Mixquiahuala de Juarez +Mamun +Haedo +Coondapoor +Hanumānnagar +Portage +Mombaça +Gomoh +São Francisco do Conde +Yuzhnouralsk +Matina +San Marcelino +Aklera +Umi +Uchaly +Apia +Hilton Head Island +Jacundá +Melchor Ocampo +Yolöten +Mizunami +Falāvarjān +Rāwatbhāta +Satānā +Tōkai +Custódia +Basavana Bāgevādi +Massapê +Galloway +Vasylkiv +Fürstenfeldbruck +Villagarcía de Arosa +Basista +Sanchahe +Les Abricots +Riviera Beach +Venlo +Tietê +Coalville +Huzūrābād +Monrovia +Yanqi +Kolongo-Bozo +Benjamin Constant +Attingal +Hatta +Malbork +Foothill Farms +Bom Jardim +Jeremoabo +Rome +Uryupinsk +Taounate +Warendorf +Stirling +Su’ao +Rio Brilhante +Malavalli +South Valley +Salto del Guairá +Lampa +New Albany +Sirinhaém +Lewiston +Akhtubinsk +Bolesławiec +Rio Branco do Sul +Kranj +Bamendjou +Villaflores +Dubno +Mira +Paraty +Neuilly-sur-Marne +Winchester +Kahrīzak +Gabú +Greenfield +Franconville +Bletchley +Dalnegorsk +Tuxpan +Perote +Atalaia +Georgetown +Tosno +Phônsavan +El Bordo +Allahabad +Pokrov +Baocheng +Turiaçu +Ma‘arratmişrīn +Itaqui +Teboulba +Don Benito +Tambulig +Bonito +Goryachiy Klyuch +Leighton Buzzard +Paracho de Verduzco +San Carlos +Souma +Tuttlingen +Evans +San Andres +Pleasant Grove +Oras +San Antonio +Lovech +Malgobek +Donji Kakanj +Amargosa +Cansanção +Porteirinha +Santa Rosa de Osos +Samobor +Sārangpur +Sandūr +Yugorsk +Kanigiri +Kérou +Oregon City +Magallanes +Gorinchem +Airdrie +Ambatofinandrahana +Trou du Nord +Shiji +Agudos +Mabuhay +Selçuk +Jaito +Argun +Savigny-sur-Orge +Phulbāni +Progreso +Bayur +Tozeur +Conchagua +Grimbergen +Bečej +Villa González +Eboli +Mullaittivu +Blyth +Fengguangcun +Beckum +Besni +Sitangkai +Luna +Ataq +Bartlesville +Santa Cruz +Al Hāshimīyah +Sanankoroba +Lahār +Falun +Dajiecun +São Manuel +Pilibangan +Tibú +Mariano Escobedo +Göygöl +Port Talbot +Rock Island +Landgraaf +Česká Lípa +Lydenburg +Gajendragarh +Jāle +Yamoussoukro +Paravūr Tekkumbhāgam +Bouznika +Jelilyüzi +Mankayan +Mölndal +Andilamena +Katsuragi +Melgar +Kaman +Hanover Park +Alapayevsk +Vettūr +Comapa +Salvatierra +Rukungiri +Leavenworth +Mangai +Moerdijk +Chunār +Wangsicun +Tunzi +Laindon +Kizilyurt +Rezh +Ratia +Kadiyam +Ciudad Manuel Doblado +Minami-Bōsō +Formia +Silves +Bantay +Bahādurganj +Imbatug +Maşyāf +Obburdon +Qulsary +Adjumani +Qo‘ng‘irot Shahri +Binəqədi +Languyan +Boryslav +Martinez +Amarante do Maranhão +Sittard +Long Lama +Kalutara +Redcar +Jagdīspur +Siribala +Chorkŭh +Prijepolje +Porsgrunn +Cloppenburg +Sitalkuchi +Tokār +Kalinkavichy +Llanelli +Pujali +Kālihāti +Lesozavodsk +Kampene +Coesfeld +Beslan +Thonon-les-Bains +Mol +Holstebro +Lagkadás +Greer +Suhl +Bentota +Mandoto +Manazary +Beeston +Dargot +Dāchepalle +Kyshtym +Granadero Baigorria +Tucker +La Ciotat +Villeta +Pennsauken +Baishi Airikecun +Santiago Sacatepéquez +Dom Pedrito +Stara Gora +Nakodar +Bucha +Richmond West +Nanshuicun +Tabligbo +Shaxi +Oświęcim +Lūnāvāda +Ban Doi Suthep +Aourir +Şuhut +Shengli +Port-Margot +Monatélé +Muskogee +Campos Novos +Guilderland +Moalboal +Dālkola +Bindki +Colón +Santa Ana +Kānkuria +Chaiyaphum +Netishyn +Chimboy Shahri +Basilisa +San Enrique +Segrate +Vavur +Pingxiangcheng +Small Heath +Grugliasco +Penticton +Dagua +Moncada +Claremont +Manucan +Siyabuswa +Musiri +Anosiala +Águilas +Kearns +East Meadow +Pāthri +Fāraskūr +Échirolles +San Miguel Chicaj +Nowa Sól +Zuojiawu +Pylaía +Sagnay +Kesavapuram +Maddaloni +Mahē +Missão Velha +Wildomar +Brighton +Caimito +Erding +Richfield +Marijampolė +Magsaysay +Ipueiras +Agogo +Elixku +Qalādizay +Uster +Salima +Camberley +Kanie +La Macarena +Sayula +Châtillon +Tuensang +Mulavana +Coatepeque +Kona +Manoli +Dargaz +Köneürgench +Lakshmeshwar +Sōma +Taxisco +Västervik +San José +Houghton le Spring +Zīra +Wangtuan +Tsubata +Kāramadai +Torres Novas +Mandaguari +Esik +Yasugichō +Or Yehuda +Enrile +Michalovce +Kambove +Yovon +Xinpo +Anserma +Robertsganj +Esquel +Beni Khiar +Estero +Ikongo +Thāna Bhawan +Irbit +Pande +Risalpur Cantonment +Sītākund +Sorel-Tracy +Lierre +Ulan Hua +Beloit +Kulp +Hojāi +José Bonifácio +Matan +Rosário do Sul +Lajedo +Belalcázar +Esch-sur-Alzette +Whitley Bay +Aurora +Las Navas +Demirci +Kannan +Makan +Kidangazhi +Sinsheim +Rossano +Mek’ī +Cantanhede +Denton +Natick +Schaffhausen +Jaral del Progreso +São João da Barra +Ebebiyín +‘Afrīn +Tunduma +Dabutou +Nemmara +Chippenham +Baishan +Punārakh +Barra do Choça +Oakton +Patāmundai +Bulicun +Benāpol +Songo +Horasan +Central Islip +Bindé +Şile +Haomen +Franklin +Neiba +Kodaikānal +Dashtobod +Mamfe +Chelora +Upper Arlington +Knurów +Berat +Cambrils +Nueve de Julio +Zhetisay +Ichchāpuram +West Bridgford +Kaniama +Bossangoa +Guápiles +Lecherías +Sonsón +Arawa +Mazıdağı +Zinzana +Tocancipá +Copperas Cove +Rende +Pūrna +Liwonde +Estahbān +Byumba +Anse d’Hainault +Andover +Holubivske +Simbahan +Porta Westfalica +Banaz +Monaco +Namakgale +Maur +Emsdetten +Cegléd +San Pablo +Ratchaburi +Sandanski +Yahyalı +Fuchūchō +Borehamwood +Barra Bonita +Tomatlán +Bjelovar +Tooele +Danihe +Wołomin +Tomelloso +Jarosław +San Juan Cotzal +Mpessoba +Kajaani +Winsen +Dinas +Nargund +Cabanglasan +Oak Creek +Cumberland +Santiago Juxtlahuaca +Kurtalan +Spoleto +Aketao +Parappur +Coatepec Harinas +Yandian +Melito di Napoli +Teruel +Kotma +Prey Veng +Tuntum +San Miguel Acatán +Beiya +Siayan +Cuenca +Mühlhausen +Yorktown +Modugno +Capalonga +Randallstown +Athis-Mons +Isla de Maipo +Yejituo +Merrillville +Palapye +Canaman +Bollate +Six-Fours-les-Plages +Cuyotenango +Göyçay +San Bernardo del Viento +Gasan +Voerde +Mieres +Onteniente +Taastrup +Ouled Beni Messous +La Vallée de Jacmel +Aparecida +Madingou +Chelmsford +Dagami +Kalāleh +Nyaungdon +San Luis +Temple City +Zacatepec +Cisterna di Latina +Boende +Ban Mueang Na Tai +Rapu-Rapu +Carrollwood +Nellāya +Barira +Petit-Trou de Nippes +Pingshang +Ewing +Pestel +Sareh Mowndeh +Bālarāmpuram +Amancio +Mingjian +Kishmat Dhanbāri +Dīnhāta +Dighwāra +Komatsushimachō +Garhākota +Meppen +Guáimaro +Lunsar +Creil +Hilliard +Girau do Ponciano +Châtelet +Al Jabāyish +Frankston +Apodi +Gutalac +Dunedin +Castricum +Kamenicë +Berriozábal +Sunbury +El Plan +East Kelowna +Kanada +Moorpark +Sokol +Roseville +Maspalomas +Piuí +Pioltello +Limburg +Sibuco +Hillerød +Tamamura +Matanog +Claveria +Masasi +Vempalle +Wai +Virú +Bombardopolis +Mālpura +Saparua +Pihānī +Guayaramerín +Bel-Air +Żary +Shirdi +Ingelheim +San Carlos Sija +Vohipeno +Ampasina-Maningory +Mugumu +Bugiri +Farafenni +Nīm ka Thāna +Taloda +Egypt Lake-Leto +La Libertad +Rāmsar +Naduvattam +Chekkal +Krong Kep +Farmers Branch +Tlokweng +Arroyomolinos +Boyarka +Minas +Pandan +Sarıgöl +Seika +Saint-Raphaël +Uto +Tarikere +Oued Athmenia +Anloga +Hellendoorn +Rumonge +Kamenka +Marion +Conflans-Sainte-Honorine +Az̧ Z̧āhirīyah +Ambohibary +San Lorenzo de Guayubín +Yangshuwa +Lauderdale Lakes +Villefranche-sur-Saône +Mokokchūng +Chillum +Majhaul +Kakhovka +Malmesbury +Daiyue +San Antonio +Hyde +Chernyakhovsk +Dinangorou +Partūr +Chieri +Görükle +Meyzieu +Santa Cruz Verapaz +Lyudinovo +Hendon +Caivano +Dumbéa +Vyazniki +Jalārpet +Dar Chabanne +Maragogipe +Tirkadavūr +Oleśnica +Beja +Orangevale +Falkirk +Huzūrnagar +Sun Prairie +Sainte-Geneviève-des-Bois +Ampelókipoi +Karak +Munro +Jagna +Agualva +Pantnagar +Cedar City +Protvino +Zaïo +Igarapé-Açu +Kāttipparutti +Taozhou +Tambolaka +Fermo +Andújar +Varberg +La Porte +Smarhon +Commack +Caltagirone +Azogues +Crailsheim +Prestea +Riverhead +Bad Vilbel +Kalakkādu +Vavatenina +Burriana +Tam Hiệp +Svetlograd +Aliança +Kharagpur +Nawan Shahr +Pak Chong +Leamington +Guane +Haguenau +Kumo +Camiri +Norristown +Calumet City +South Miami Heights +Vršac +Ẕefat +Numancia +Santa Ana +Addison +Iporá +Macomia +Collo +Usuki +Shiggaon +Mahna +Kinel +Sōsa +Inver Grove Heights +Graaff-Reinet +Hole Narsipur +Santiago de Tolú +Câmara de Lobos +Miyajima +Gürpınar +Leer +Guacarí +Frolovo +Coventry +El Mirage +Goiatuba +Chītāpur +Richmond +Miranda de Ebro +Port Shepstone +Awa +Al ‘Aqīq +Aguelmous +Setouchi +Zhentang +Walkden +Chortoq +Medina +Medchal +Midvale +Kendall West +Casiguran +Kenton +Kula +Cha-am +San Andrés Itzapa +Hoogvliet +Narsampet +Shangzhuangcun +Isumi +Uravakonda +Savanūr +Yerköy +Bariri +Lima +Niamina +Freehold +Pishin +Sanok +Sykiés +Rafiganj +Aravankara +Mahabo +Pambujan +Vitrolles +Yangambi +Sirohi +Belluno +Gahanna +Lökbatan +Savonlinna +Olney +Rifu +Miahuatlán de Porfirio Díaz +Guamá Abajo +Torrington +Şaḩneh +Udhampur +Gigante +Sungaiselam +Kaneohe +Villeneuve-Saint-Georges +Coevorden +North Ridgeville +Umarga +Ringsaker +Hamīrpur +La Concordia +Simiganj +Cartagena del Chairá +Woodley +Cutral-Có +San Juan +Yayladağı +Midlothian +Ténès +Accrington +Pola +Ōfunato +Lugoj +Bôca do Acre +Tamuín +Tallkalakh +Sandino +Salisbury +Savalou +Spišská Nová Ves +Ulliyil +Socorro +Fuquay-Varina +Feijó +Tomobe +Lage +Santuario +Pinerolo +Matanzas +Casalecchio di Reno +Oakville +Boyarka +Dayr Ḩāfir +Kundian +Zorgo +Bou Noura +Angra do Heroísmo +Aïne Draham +Pampatar +Buhriz +Bautista +Ratnapur +Morong +Baohezhuangcun +Hamidiye +Guskhara +Millerovo +Samokov +Pilar +Morohongō +Unnan +Yongjing +Rio Real +Akçadağ +West Hollywood +Pilão Arcado +Dama +Ürgüp +La Palma +Westmont +Bragança +Fruit Cove +Timbuktu +Kilimli +Tonalá +Benton +Yarumal +Saint-Benoît +Conceição de Jacuípe +Villa Adelina +Barra do Bugres +Tahuna +Brunswick +Yasynuvata +Villa Ballester +Newton +Juban +Bedēsa +Kerava +Mörfelden-Walldorf +Ciudad de Allende +Cieza +El Hajeb +Chakapara +Quimbaya +Wildwood +Sūrandai +Goch +Aizumi +Diabali +Bridlington +Czechowice-Dziedzice +Tangancícuaro de Arista +Pansol +Douglasville +Chandanais +Voznesensk +Gandara +San Javier +Evergem +Jieshang +Azuqueca de Henares +Allacapan +Yecla +Cento +Zvishavane +Mirnyy +Roi Et +Usilampatti +Baturité +Sankt Ingbert +Kamata +Bria +Winchester +Tilingzhai +Buenos Aires +San Luis +Springville +Bou Salem +Datteln +San Luis +Zaventem +Praya +Watertown Town +Abra de Ilog +Izkī +Bom Jesus do Itabapoana +Perunād +Deggendorf +Woodstock +Yefremov +Billingham +Aktuluk +Hammam Bou Hadjar +Barsinghausen +Soteapan +Mahasolo +Sulop +Giddalūr +Mananara Avaratra +Lingig +Jaguariaíva +Pāppinisshēri +Fair Oaks +Tāzhakara +Manhattan Beach +Trinidad +Ikalamavony +Pandaul +Adamantina +Bibhutpur +Qazax +Steinfurt +Rāwatsār +San Juan Capistrano +Puerto López +Effia-Kuma +Pulppatta +Cassino +Chrzanów +Lebowakgomo +Al Ḩişn +Plainfield +Kherrata +Confresa +Saint-Chamond +Salinas +Balasan +Vinces +Umarkot +Casilda +Yellandu +Suong +Río Grande +Ouro Preto d’Oeste +Phatthalung +Ieper +Bethlehem +Chur +Tebesbest +Guimbal +San Luis de Sincé +San Pedro de Ycuamandiyú +Unión de Reyes +Munai +Charqueadas +Palotina +Māranchēri +Blagoveshchensk +Kōṯah-ye ‘As̲h̲rō +Maīdān Shahr +Wokha +Prado +Mannar +Awbārī +Firavahana +Fotadrevo +Ibrā’ +Chakdarra +Negotin +Luga +Ḑulay‘ Rashīd +Shaqrā’ +Şalkhad +Tall Salḩab +Bagamoyo +Magu +Caucagua +Guanta +Maojiatang +Cangxi +Dikhil +Winsford +Bāpaura +Chiplūn +Harra +Mel Nāriyappanūr +Dulhanganj +Al Ḩamdānīyah +Shū +Brugherio +Owings Mills +Pimenta Bueno +Talanga +Dimitrovgrad +Meridian +Oranjestad +Maglaj +Ōzu +Ngaoundal +Mlimba +Francavilla Fontana +Padre Bernardo +Cookeville +Niquelândia +Shenjiabang +Camoapa +Ez Zahra +Kaizu +Chipinge +Payao +Northbrook +Karumattampatti +Aţ Ţurrah +Fair Lawn +Tenosique +Foça +Balingen +Ypané +Tarkwa +Kahoku +També +Ilhabela +Eşme +Jiaoxi +Lingsugūr +Dietzenbach +Shīyāli +Iyo +Dundee +Campoalegre +Nyamata +Khodābandeh +Tamura +Slavuta +Ekpé +Posse +Santa Cruz +Armenia +Ikot Abasi +Santa Cruz del Norte +Juara +Madukkarai +Piskent +Châtenay-Malabry +Seiyo +Monroe +Aznakayevo +Palaiseau +Jagodina +Ouled Fares +Victoria +Ville Bonheur +Richmond +Mendez-Nuñez +Alandatte +Ewell +Vadigenhalli +Aguaí +University City +Cernusco sul Naviglio +São José do Belmonte +Kempen +Hīrākud +Munnarkōd +Mannārakkāt +Galapagar +Parkland +Arsin +Villajoyosa +Dragash +Naryn +Āllagadda +Oswego +Yarīm +Oildale +Santa Fé do Sul +Sansanné-Mango +Limbiate +Alatyr +Hammam Dalaa +Khajamahalpur +Uddevalla +Auxerre +Graham +Vernier +Morecambe +Mason +Roanne +São Luís Gonzaga +Montgomery Village +Castelo +Seelze +Wermelskirchen +Yhú +Zhitiqara +Long Beach +Hinesville +Safidon +Shangcaiyuan +Çorum +Leganes +Golden Glades +Del Rio +Kolattupuzha +Saarlouis +Zhujiezhen +Třebíč +Bagre +Ayutuxtepeque +Sion +Goshen +Gladstone +Simpang Renggam +University Place +Randolph +Zelenokumsk +Osimo +Azazga +Dhāmnod +Farafangana +Bugasong +Tienen +Korkino +Naranjito +Augusta +Rio Pardo +Mānāmadurai +Slagelse +Abashiri +Tortosa +Butte +East Gwillimbury +Sarpol-e Z̄ahāb +Marutharōd +Andapa +Anse-à-Veau +Medgidia +Misungwi +Sidi Lakhdar +Dabouziya +Mugnano di Napoli +Falls +Phokeng +Dueñas +Tynaarlo +Espiye +Grantham +Zhangziying +Huntington Station +Qaşr al Qarabūllī +Abulug +Inca +Swadlincote +Yangquan +San Juan Despí +Mildura +Banda +Yeonil +Bugojno +Cativá +Bonifacio +Ofunato +Paithan +Kalaa Srira +Dāpoli +Sabaneta +Wedel +Cañete +Zweibrücken +Viernheim +Kulittalai +Gisborne +Pasni +Koryazhma +Bayog +Bāft +Mogpog +Santa Maria +Skhira +Ahrensburg +Pleasant Hill +Côte-Saint-Luc +Ankola +Aïn Taya +Lębork +Manitowoc +Barugo +Sadda +Akouda +Fairborn +Brzeg +Ballesteros +Ocuilan de Arteaga +Shangzhen +Bodocó +Santa Fe +Curuzú Cuatiá +Gerāsh +Nola +Formigine +Aloguinsan +San Dimas +Smolyan +Nishihara +Schoten +Mazarrón +Stow +Motosu +Banes +Madakalavāripalli +Canicattì +Mâcon +Hatibanda +Bela Vista de Goiás +Sambir +Zaidpur +Sochaczew +Kalasin +Fort Liberté +Exmouth +McMinnville +Ye +Mnasra +Gotō +North Shields +Centenario +Kuji +College Park +Merauke +Itaporanga d’Ajuda +Paxtaobod +Riccione Marina +Năvodari +Vichuga +Meppel +Toretsk +Santa Ana Nextlalpan +Biwong +Corsico +Cherchell +Durazno +Kiryas Joel +Yihezhuang +Houlong +São Joaquim de Bicas +Lakewood Ranch +Kita Chauhāttar +Pueblo West +Betafo +Garibaldi +Merseburg +Heunghae +Phra Phutthabat +Same +Tandubas +Degāna +La Blanca +Třinec +Wavre +Bathurst +Tábor +Swakopmund +Isingiro +Merritt Island +Derry +Geldern +Quilevo +Araçuaí +Conegliano +Nandaime +Licata +São Pedro +Shinjō +Almeirim +Kalarūch +Pinukpuk +Buchanan +Lewiston +Nova Cruz +Lautaro +Fusō +Aiyappan Kōvil +São Desidério +Hitchin +Vedāranniyam +Pomerode +Zhangzhengqiao +Esposende +Lixingcun +Timberwood Park +New Panamao +Simunul +La Huacana +Sibutu +Sahuarita +Nāz̧erābād +Baraidih +Dakota Ridge +Colinas do Tocantins +Atami +Mosonmagyaróvár +General Nakar +Lexington +Santa Maria +Isfana +Gulkevichi +Prairieville +Le Perreux-Sur-Marne +São Sebastião +Pangururan +Úbeda +Badiadka +Shāhīn Dezh +Şereflikoçhisar +Karoi +Toumoukro +Eastchester +Tōon +Valuyki +Redmond +Šid +Korschenbroich +Curaçá +Orangeville +Cariari +Ābīy Ādī +Tiruvūr +Datang +Ágioi Anárgyroi +Lufkin +Bor +Talayan +Tepeji del Río de Ocampo +Tobelo +Znojmo +Villena +Kokrajhar +Risod +Dimona +Caldono +Kornwestheim +Schiltigheim +Laoac East +Takhemaret +Almora +Boğazlıyan +Brejo +San Agustín +Shisō +Spalding +Balarāmpur +Uonuma +Cravinhos +Gyapekurom +Guindulman +Fungurume +Pingtang +Pikesville +Diakon +Qingyang +Argyroúpoli +Radebeul +Leduc +Rainham +Eastpointe +Beaufort West +San Manuel +Çınarcık +Honaz +Portão +Titlāgarh +Cantilan +Wāris Alīganj +Gbarnga +Deer Park +Cooper City +San Remigio +Podgórze +La Unión +Lommel +Pagani +Vác +Sint-Pieters-Leeuw +Palapag +São Domingos do Maranhão +Puqiancun +Westlake +Woodridge +Zeghanghane +Hemer +Jalajala +Jāmkhed +Spanaway +Rūdarpur +San Juan Nepomuceno +Minamishiro +Majagual +Biberach +Dīvāndarreh +Oneşti +Quinchía +Gibraltar +Ankazomiriotra +Maromandia +Mahasoabe +Lushoto +Zhegaozhen +Tamra +Valparaiso +Letchworth +American Fork +Kallidaikurichi +Shariff Aguak +Menen +San Quintin +Les Mureaux +Vassouras +Angri +Peyziwat +Milaor +Kignan +Sharypovo +Cieszyn +Sibagat +Acatlán de Osorio +Stuhr +Korsakov +Lochem +Almendralejo +Kakata +North Providence +Lanciano +Annigeri +Uelzen +San José Poaquil +Uttaradit +San Juan Evangelista +Bassin Bleu +Pulgaon +Sındırgı +City of Orange +Starokostiantyniv +Nizhneudinsk +Oued el Alleug +Impfondo +Zhuqi +Windsor +Curralinho +Initao +Mulavūr +Adrano +Lupi Viejo +Réo +Navan +Guying +Timbío +Gadsden +Zhaitangcun +Toda Bhīm +Byādgi +Itiúba +Lianga +Tabango +Desamparados +Sneek +Palmeira +Imzouren +São Luís de Montes Belos +Selmane +Baclaran +Chaodongcun +Nuoro +Beni Tamou +Oeiras do Pará +Bozdoğan +Dao +Walla Walla +Camiri +New City +Bell Ville +Golungo Alto +Jasło +San Martín Sacatepéquez +Strumica +Harima +Waikabubak +IJsselstein +Parras de la Fuente +Tobias Fornier +Buxin +Lichfield +San Antonio de los Baños +Río Verde Arriba +Kearney +Inhambupe +Kadungapuram +Somoto +Naguilian +Xinzhancun +Crema +Leawood +Beni Slimane +Baldwin +Ghazaouet +Braço do Norte +Martinez +Bangaon +Vechta +Puerto Rico +Narsīpatnam +Bogorodsk +Zhmerynka +Mount Lebanon +Catmon +Várzea da Palma +Tekes +Mankoeng +Zacatlán +Kadingilan +Crown Point +El Ksar +Ojiya +Jaguaribe +Chilecito +Kovin +Capoocan +Kaippakanchēri +Carmel +Āzezo +Karaağaç +Léo +Trappes +Midsalip +Fallbrook +Grodzisk Mazowiecki +Vemalwāda +Xiaotangzhuang +Antiguo Cuscatlán +Irvine +Landi Kotal +Roldanillo +Somma Vesuviana +Palmerston +Tlanchinol +Kasba +Sukhoy Log +Datu Paglas +Kohtla-Järve +Mella +Moose Jaw +Batobato +Upper Merion +Penha +Villa Riva +San Antero +Bārhadashi̇̄ +Oulad Zemam +San Roque +Ōno +Florida +Lianmuqin Kancun +Englewood +East Lake +Aranda de Duero +Macuro +Kōryō +Chascomús +‘Ajab Shīr +Binche +Morro do Chapéu +Batouri +Koh Kong +Atiquizaya +Rheinfelden (Baden) +Masiu +Sapa Sapa +Côtes de Fer +Elmina +Sicuani +Dāganbhuiya +Iriba +Qadsayyā +Maassluis +Pitanga +Goldsboro +Áno Liósia +Aleshtar +Mercedes +Bagh +Chāvakkād +Ixtapan de la Sal +Belen +Aweil +Port Moody +Cabarroguis +Acornhoek +Ayorou +Columbio +Manganam +Saint Helier +Chumphon +Arteijo +Sidi Okba +San Vicente +Comé +Ribeirão +Gorizia +Ken Caryl +Leticia +Niangoloko +Canton +Bacarra +Schwedt (Oder) +Warminster +Manlius +Corinto +Chempalli +Pointe-Claire +Wickford +Batan +Fraiburgo +Hlukhiv +Neuchâtel +Cishan +Nogent-sur-Marne +West Little River +Olkusz +Dhekiajuli +Coxim +Badr +Houilles +Chimbarongo +Bad Nauheim +Mangalam +Poço Redondo +Çukurçayır +Sabanagrande +Santa Ana +Monteiro +Vettam +Paidha +Madhubani +Angamāli +Getafe +Orodara +Darsi +Castro +Sirīpur +Alabaster +Mỹ Hòa +Orillia +Kaçanik +Shōbara +Ban Pak Phun +Butajīra +Date +Geraardsbergen +Paso de Ovejas +Kong +Riachão do Jacuípe +Namhkam +Asakuchi +Madikeri +Bell +Banate +Aldaya +Tulum +Gölbaşı +Kondapalle +Landskrona +Iperó +Tayshet +Kennesaw +South Riding +Tavda +Loughton +Bafang +Montluçon +Glossop +Vintar +Dursunbey +An’gang +Shirakawa-tsuda +Shiroishi +Ronda +Puyo +Batcha +Menlo Park +Nokia +Taishi +Al Mindak +Baltiysk +Amparafaravola +Bāmaur +Ziniaré +Dupax Del Norte +Estreito +Churi +Kidamangalam +Mohnyin +João Câmara +Beidou +Kolín +Shanawān +Buenaventura Lakes +Gojō +Bethel Park +Nowy Targ +Hisuā +Oława +Didouche Mourad +Romainville +Muconda +Petersburg +Radnor +Cottonwood Heights +Perşembe +Mažeikiai +Xincheng +Zamānia +Barguna +Capim Grosso +Stratford +Kampong Speu +Kavundappādi +Pivijay +Castro-Urdiales +Ceres +Shatura +Bragado +Foster City +Ālamat’ā +Ross +Geiro +Guarambaré +Kyrenia +Kartārpur +Obukhiv +Borne +Epe +Andırın +Huyton +Uniondale +Khvāf +Lower Makefield +Statesboro +Furmanov +Katiéna +Pandami +Eirunepé +Puerto Cumarebo +Partizansk +Cranberry +Jülich +Gillette +Totana +Chuhuiv +Shamsābād +Baja +Dipaculao +Palmeira das Missões +Abingdon +Shikārpūr +Salina +Pánuco +Kusapín +Minamikyūshū +Umaria +Brooklyn Center +Trowbridge +Ben Ahmed +Mukdahan +Bugembe +Kaarina +Knokke-Heist +Aosta +Los Gatos +Shima +Glendale Heights +Tekkebhāgam +El Arrouch +Chanderi +Calape +Bungoōno +Temascalapa +Mushie +Tynda +Kamaishi +Victoria Falls +Timimoun +Borba +Kareli +Castelfranco Emilia +Tutin +Ban Ang Sila +Kingman +Taiobeiras +Chester +‘Aïn el Hadjel +Didy +San Luis +Kadınhanı +Wakabadai +Harker Heights +Touros +Puebloviejo +Las Mercedes +Dana Point +Luboń +Jishi +Shingū +Hāngal +Sarıkaya +Marignane +Xinbu +Livingstone +Ambodimanga II +Vohitromby +Zumbo +Forbe Oroya +Lomé +Gumdag +Gyzylgaya +Osvaldo Cruz +Luxitun +Villafranca di Verona +Hinda +Turkauliyā +Boxtel +Hampden +Forchheim +Pôrto União +Taguasco +São Paulo de Olivença +Spring Valley +Sopot +Shalingzicun +Asse +Elesvaram +Bembe +Dellys +Örnsköldsvik +Chernushka +Ventspils +Itamarandiba +Djenné +Rencun +Dara +Dehlorān +Musikoṭ-Khalaṅgā +Carapeguá +Biritiba-Mirim +Castelfranco Veneto +Saint-Georges +Dumalinao +Yamanashi +Massango +Aravan +Romans-sur-Isère +Baraguá +Saintard +Fatehpur Sīkri +Escuque +Fort Erie +Moninnpébougou +Patrātu +Sānchor +Pantelimon +Aksay +Lampertheim +Oued Zenati +Kifrī +Clarence +Tawsalun +Níjar +Lohagaon +Sō +Nikšić +Araban +Watari +Villanueva +San Felipe +Sanana +Nevers +Kollo +Carache +Kakira +Tunceli +Gostivar +Umarkot +Urbano Santos +Sertânia +Pilar +Ārumuganeri +Igrejinha +Tecuci +Baraawe +Syracuse +Teutônia +Ticul +Sighetu Marmaţiei +Slobodskoy +Buco Zau +Parambil +Tit Mellil +Shaliuhe +Franklin +St. Charles +Bela Cruz +Rubino +Delbrück +Ubajara +Bocas de Satinga +Udaipur +Sayram +Xinyuan +Chand Chaur +Fair Oaks +Achim +Val-d’Or +Mankada +Yuzhne +Puerto Escondido +Balzar +Příbram +Barouéli +Ylöjärvi +Kallūr +Sayula de Alemán +LaSalle +Ivaiporã +Rodas +Warwick +Geraldton +Trëkhgornyy +Sherwood +Kaysville +Ansongo +Vadavalli +Nurlat +Jaguarari +Yufu +Debagrām +La Mesa +Falmouth +Silvia +Cota +Arbaoua +Nānjikkottai +São João Batista +Xiaodian +Vilakkudi +Dobryanka +Zhanjia +Des Moines +Iława +Oğuzeli +Itaíba +Herrenberg +City Bell +Fürstenwalde +Jamikunta +Putla Villa de Guerrero +Tārānagar +Goleta +Cibolo +Andover +El Difícil +Lawrence +Belhi +Frederico Westphalen +Lens +San Lazzaro di Savena +Aïn el Bya +Istra +Ōsawa +Ostrogozhsk +Mayantoc +Arris +Kraśnik +Motherwell +Mashiki +Matalom +Diplahan +Businga +Shamsābād +Inzá +Erraguntla +Balindong +Rochester +Payshamba Shahri +Şarköy +Vyatskiye Polyany +Esplanada +Shimanto +Aïn Bessem +Mateur +Saint-Médard-en-Jalles +Chēmanchēri +Badoc +Helena +Hani +Kulebaki +I-n-Salah +Salt +Caçapava do Sul +Dolores +Slantsy +Lobougoula +Minamisatsuma +Elk Grove Village +Spring Valley +Mānwat +Agen +Amés +Shiyeli +Pombal +Baiyan +Garfield +Yinchengpu +Réthymno +Nueva Italia de Ruiz +Ourilândia do Norte +Sotouboua +Fucheng +Correntina +Staryy Beyneu +Ferry Pass +Virei +Chemmaruthi +Savage +Turaiyūr +Deurne +Nouna +Abbiategrasso +Omaezaki +Lower Macungie +Dioungani +Vrilíssia +Bussum +Beverly Hills +Nedroma +Plottier +Desert Hot Springs +Ketti +Kameda-honchō +Pierrefitte-sur-Seine +Dumfries +Oisterwijk +Pires do Rio +Chamtha +Clearfield +Dartmouth +Brighouse +Massantola +Kiranomena +Ingenio +Yankou +Redcliff +Guararapes +Kävlinge +Pattanapuram +Ire +Geesthacht +Lumbang +La Virginia +Sāsthānkotta +Narsinghgarh +San Pedro de Urabá +Kalyandrug +Wigston Magna +Lugo +Bilzen +Ōharu +Namerikawa +Chancay +Sant’Antimo +Nagarote +El Abiodh Sidi Cheikh +Madīnat al Ḩabbānīyah +Tattamangalam +Sibiti +Cachoeira Paulista +Dracut +Naumburg +Curanilahue +Venaria Reale +Komárno +Épinal +Messaména +Concón +Chiché +Peñaranda +Niéna +Wheat Ridge +Bernburg +Zarqān +Zaojiao +Itzehoe +Jimalalud +Bambadinca +Boro +Guanhães +North Olmsted +Ţafas +São Bento +Termoli +Kangasala +Jacqueville +La Calera +Laranjeiras do Sul +Yōkaichiba +Sébaco +Paraipaba +Şāfītā +Mallig +Kindi +Casale Monferrato +Buloqboshi +Cochrane +Igbaras +Piombino +Georgsmarienhütte +Kristianstad +Windsor +Estrêla +Didcot +Nagato +Primorsko-Akhtarsk +Bramsche +Massillon +Gandu +Ibaté +Xangda +Catubig +Matões +Maragogi +Homa Bay +Yorii +Papendrecht +Weatherford +Alubijid +Pasadena +Koungou +Catende +Rokhaty +Sint-Amandsberg +Erāmala +Miguel Alves +Buguey +Nacogdoches +Kāngayam +Utraula +Salug +Shilou +Douar Oulad Hssine +Pojuca +Pervari +Ptolemaḯda +Kōnan +Maputsoe +Termas de Río Hondo +Balimbing +Caramoran +Ribeira Grande +Huixtla +Mizuho +Juneau +Campo Alegre +O'Fallon +Ōdachō-ōda +Rumbek +Waiyuanshan +Zapala +Aguilares +Guaíra +Ouro Fino +Kukshi +Mangūr +Mutki +Serdobsk +Quitilipi +Sami +Kovūr +Antsohimbondrona +Rādhanpur +Pekin +Tamparan +Dhahran +Uglich +Remedios +Florence +Ciudad Sabinas Hidalgo +Radolfzell am Bodensee +São Miguel Arcanjo +Earley +Canyon Lake +Rome +Rahachow +Munnar +Fontana +Chachapoyas +Brasília de Minas +Wernigerode +Malangas +Bauko +Akivīdu +Bizen +Bezons +San Donato Milanese +Aiken +Sakaiminato +Tirebolu +Marrero +Bandarawela +Andriba +Bekoratsaka +Vohilava +Itampolo +Ambano +Ghuenke +Yaguarón +Santo Tomás +Cabaiguán +Makhdumpur +Milot +Aalsmeer +North Cowichan +Września +Youwangjie +Kanzakimachi-kanzaki +Arzano +Levice +Pullman +Franklin Square +Purāini +Mascalucia +Massafra +Tholikuzhi +Needham +Deptford +Cheb +Sitges +La Concepción +Mangalia +Warwick +Sergio Osmeña Sr +Nísia Floresta +Landecy +Ganderkesee +San Carlos Alzatate +Nerópolis +Long Branch +Bexleyheath +South Kingstown +Quintero +Bougaa +Santo Antônio do Tauá +Araguatins +Xaxim +Palamel +Purattūr +Polillo +Southport +Yenice +Nilka +San Pablo +Santa Maria Capua Vetere +Erdek +Cañuelas +Claveria +Cacolo +Avellaneda +Paso del Macho +Bayaguana +Centereach +Turicato +Maryville +Aix-les-Bains +Kampong Thom +Jyväskylän Maalaiskunta +Alengād +Paipa +Dzierżoniów +Oudenaarde +Bửu Long +Bahharet Oulad Ayyad +São Gabriel +Palmeiras de Goiás +Guachavés +Maur Kalān +Cleburne +El Oro de Hidalgo +Domingos Martins +Willingboro +Exu +Camaiore +Atwater +Sidi Ali +Oer-Erkenschwick +Remchi +Pampanito +Sherpur +Namegata +Jhanjhārpur +São Gotardo +Ortaköy +Alamnagar +Palmira +Santa Rita +Newburgh +Velikiy Ustyug +San Lucas Tolimán +Plettenberg Bay +Masi-Manimba +Teykovo +Tongxiao +Stadskanaal +Bagasra +Gurupá +San Pedro de Ribas +Hassi Khelifa +Gallipoli +Kottaikuppam +North Huntingdon +Ferreñafe +Montigny-le-Bretonneux +Salamína +Pantabangan +Tubao +Velliyōd +Komagane +Bocana de Paiwas +Singia +General José de San Martín +Descalvado +Carlsbad +Yıldızeli +Tekkēkara Kizhakku +’Aïn Abid +Duluth +La Maná +Tredyffrin +Farrokh Shahr +Sadābād +Amatenango de la Frontera +Al ‘Aydābī +Laramie +Donggou +La Uruca +Maebara +Kochugaon +Staffanstorp +Bostaniçi +Tuljāpur +Kavār +Santa Catarina Mita +Sitionuevo +El Palmar +Bangor +Oldenzaal +Jaguaruana +Dania Beach +Lālgola +Rushden +Baradères +Sigma +Amecameca de Juárez +Zhaoyu +Garner +Jāsim +West Bend +Bagrāmī +Itapicuru +Kumba +Masur +Vught +Jiming +Naugatuck +Shimizuchō +Barros Blancos +Nautan Dube +Rumuruti +Calubian +Capela +Siquirres +Nueva Imperial +Balyqshy +Morro Agudo +Zentsujichó +Kiryandongo +Tecozautla +Ecclesfield +Kuroishi +Jesús María +Jalpatagua +Jerez +Chum Phae +Favara +Media Luna +Chorbog +Masatepe +Mundelein +Pruszcz Gdański +Žepče +Lloydminster +Nanyuki +Merida +Manises +Wisbech +Fangyuan +Darwen +Holladay +Lidingö +Shenwan +Cururupu +Katagami +Sosnogorsk +Pehuajó +Lawndale +Cueto +Al Khawr +Thazhamel +Hayama +Lake Magdalene +Rāhatgarh +Ayabe +Oak Ridge +I‘zāz +Mae Sot +Milford Mill +İvrindi +Puerto Wilches +Shikārpur +Prosper +Kurivikod +Novovoronezh +Buzovna +Encrucijada +Prestwich +Mechraa Bel Ksiri +Bünyan +Charthāwal +Knjaževac +Epsom +Marks +Oceanside +Dialoubé +Colombia +Qārā +Samdhin +Dej +Sejiyŏn +Kostopil +Mandaguaçu +Bangassou +Airmadidi +Parambu +Savé +Chambly +Kalisizo +Nagdha Simla +Jam +Trebinje +Forquilhinha +Nauāgarhi +Cambrai +Deori Khās +Neuruppin +Renkum +Carballo +Nieuw-Vennep +Lemery +Pindaré-Mirim +Çay +Wevelgem +Āreka +Norak +Kunnatnād +Neira +Kolachel +Cornelius +Sanha +Shentang +L’Haÿ-les-Roses +Shawnee +Halden +Yawatahama-shi +Vellanād +Ílhavo +Draa Ben Khedda +Buyan +Navalcarnero +Courcelles +Plaisir +Myōkō +New Bern +Kudymkar +Colíder +Bolobo +Quţūr +Sri Mādhopur +Bagac +Opelika +Humenné +Ithaca +Hoyerswerda +Villaricca +Dasol +Nicholasville +Point Pedro +Cicero +Guamo +Maralal +Lumding +Cândido Mota +Norwood +Waddinxveen +Dimataling +Mamaroneck +Wenxian Chengguanzhen +Odorheiu Secuiesc +Kléla +Diemen +Hitoyoshi +Monjas +Kandalaksha +Rio das Pedras +Gongguan +Pontoise +Redan +Tanmen +Sherpur Khurd +Araçoiaba da Serra +Ibanda +Altenburg +Netivot +İmişli +Magsingal +Yahşihan +Bel Air North +Río Bravo +Zaqatala +Eyvān +Maḩallat Damanah +Mograne +Shimotoba +Yby Yaú +Port Chester +Burgdorf +Ennery +Châtellerault +Aguada de Pasajeros +Ivisan +Ibaraki +Bandeirantes +President Roxas +Cuauhtémoc +Vadakakarai +Tiquipaya +Boghni +Ōiso +Petrovac na Mlavi +Hendrik-Ido-Ambacht +Inkhil +Siliana +Parnarama +Rillieux-la-Pape +Nāspur +Carangola +La Verne +Unchagao +Esperança +Barrinha +Changuinola +Salgótarján +Valkenswaard +Rosmalen +Kosonsoy +Poconé +Itaperuçu +Bittou +Alba +Bramhapuri +Pontypridd +Duanshan +Shepparton +Kreuztal +Lobos +Rutherglen +Sakaraha +LaGrange +Dar Ould Zidouh +Laguna Hills +Varto +Paracelis +Saint Neots +Siraway +Khānah Sūr +Buenavista +Rusera +Middle River +Rheinberg +Asilah +Ciudad del Plata +Podili +Yaita +Tongeren +Gata +Ambatomiady +Bogoroditsk +Beidaying +Sovetskiy +Orcutt +Shrīgonda +Los Altos +Fomento +Ngudu +Kalliyasshēri +West Falls Church +Rosa Zarate +North Royalton +Tequila +Socorro +Espinosa +Yingyangcun +Lushnjë +Bembèrèkè +Tancítaro +Chitral +Danghara +Khorugh +Sakhnīn +Gevelsberg +Thiais +Sabana Grande de Boyá +Montebelluna +Centre Wellington +Vibo Valentia +Sasaguri +Livingston +Bedworth +Tewksbury +Huangxicun +Tanguá +San Juan Ixcoy +Nantan +Barreirinha +Sterling +Cambita Garabitos +Manaratsandry +Vienne +Vigneux-sur-Seine +Malaikkal +Lagos +Pompeu +Werl +Viry-Châtillon +Petroşani +Hopkinsville +Tairan Camp +Tummapāla +Mushābani +Chantal +Espinho +Karera +Dover +Springfield +Konséguéla +Malakanagiri +Rodolfo Sánchez Taboada +São José do Egito +Vohitrandriana +Andranovory +Wobulenzi +Dashao +San Vicente +Alamogordo +Burlingame +Iguape +Diffa +Zima +Itapemirim +Arraial do Cabo +Santa Vitória do Palmar +Bethany +Gīmbī +Babati +Lucera +Yaese +Lakeside +Dolo Odo +La Paz +Dyurtyuli +Irituia +Arcos de la Frontera +Mocímboa da Praia +Tawaramoto +Ballwin +Tarakeswar +Minas de Matahambre +Rostov +Wanghong Yidui +Bādāmi +Husainābād +Saint-Laurent-du-Var +Ridley +Chagne +Dedza +Alcázar de San Juan +SeaTac +Chichester +Săcele +Mława +Sabirabad +Deal +Jérémie +West Warwick +Itatiaia +Cícero Dantas +Canavieiras +West Odessa +Pichanal +Flores da Cunha +Okagaki +North Chicago +Coria del Río +Kaminokawa +Bairi Chak +Champotón +Pontefract +Dreux +Voorhees +São Luís do Quitunde +Leh +Pattanakkād +Ózd +Culleredo +San Antonio Ilotenango +Sholinghur +Dharampur +Juanacatlán +Bicester +Mānāvadar +Puerto de la Cruz +Palaiya Āyakkudi +North Andover +Careiro da Várzea +Lohmar +Vinkovci +Kalgoorlie +Cholargós +Westfield +Capulhuac +Sarayköy +Nartkala +Zionsville +Beverley +Al Ḩusaynīyah +Chinnālapatti +Walsrode +Pattittara +Arlon +Friedberg +Kórinthos +Lohāgāra +Bègles +Taunusstein +Vitória do Mearim +Ungheni +Nālchiti +Ganassi +Lavras da Mangabeira +Qo‘rg‘ontepa +Jonuta +Careiro +Escuinapa +Nardò +Santa Paula +Río Blanco +Southlake +Barão de Cocais +Busia +Saratoga +Weil am Rhein +Carpentras +Mahemdāvād +Alegre +Paşcani +Andingcun +Tsuruno +San Narciso +Guaranda +Gadarpur +Ban Sai Ma Tai +Písek +Ulliyeri +Ferizli +Tekkali +Chak Five Hundred Seventy-five +Udaypur Gaḍhi̇̄ +Einbeck +San Sebastián +Novoyavorovskoye +San Isidro +Pedana +Mattathūr +Além Paraíba +Agblangandan +Juatuba +Aglipay +Northport +Riacho de Santana +Sekiyado +Dois Irmãos +Tekeli +Pandua +Nova Russas +Bābura +Goussainville +Treviglio +San Ramón +Ciudad Melchor de Mencos +Czeladź +Tepetlaoxtoc +Cruz del Eje +Bannūr +Mont-de-Marsan +Partinico +Moche +Campo Alegre de Lourdes +Balatan +Villiers-sur-Marne +Presidente Figueiredo +Gobernador Virasora +Laḩij +Catolé do Rocha +Chūō +Osterholz-Scharmbeck +Gamu +Newark +Toribío +Chengam +Tabuleiro do Norte +Bad Hersfeld +Ennepetal +San Giuliano Terme +Nazaré da Mata +Juquitiba +Luís Correia +Yunoshima +Niles +Burgess Hill +Eagle +Calafell +Gurnee +Santana do Acaraú +Police +Victoria +Mataas Na Kahoy +Miami Lakes +Jarinu +Carapó +Kaelé +São José da Tapera +Keles +Capim +Taozhuangcun +Diéma +Cachan +Banamba +Uzynaghash +Grottaglie +Talakkād +Devarshola +Birtouta +Aïn Kercha +Buey Arriba +Hafnarfjörður +Sarno +Illescas +Bay Shore +Wallkill +Leusden +Xingang +Zug +Haan +Sokuluk +Osmancık +Olopa +Suzak +Founougo +Liptovský Mikuláš +Hayrabolu +Anilao +Gariadhar +Parkville +Ouatagouna +Aungban +Savigny-le-Temple +Westville +Fārsān +Sankaramangalam +Nāgāwaram +Thung Song +Krabi +Giannitsá +Stratford-upon-Avon +Masho Khel +Hellín +Maināguri +San Carlos +G’ijduvon Shahri +Pamukova +Pittsford +Pau dos Ferros +Gubbio +North Tonawanda +Torrijos +Karuvakulam +Mukumbura +Neuburg +Santa Bárbara +Lawrenceville +Yanai +Afonso Cláudio +Motema +Ar Ruḩaybah +Princeton +Ono +New Smyrna Beach +Trípoli +Erie +Beşiri +Tchindjendje +Ash Shinān +Uran +Maguing +Bandar-e Lengeh +Mandera +Tskhinvali +Magdalena de Kino +Camamu +Pontal do Paraná +Rotterdam +Quillabamba +Bālā Kōh +Carlos Barbosa +Myszków +Menton +Kailahun +Valdepeñas +Friedberg +Karjan +Okotoks +Motomiya +Blagodarnyy +Banlung +Mūvattupula +Austintown +Salyan +Manāwar +Avola +Schönebeck +Samrong +Amagá +Sanford +Ust’-Dzheguta +Oristano +Eldersburg +Kūttānallūr +Dhāri +Salem +Mānsa +Newark upon Trent +Nanyō +Temascaltepec de González +Żywiec +Villemomble +Alma +Rehli +Hirakawachō +Huatabampo +Fengrenxu +Trindade +Graneros +Abū Şuwayr +Shāhpura +Sallimedu +Xinpo +Seguin +Kessel-Lo +Pothuhera +Kurunegala +Al ’Attawia +Télimélé +Ségala Mba +Northolt +San Salvador El Seco +Chimichagua +Dolores +To‘raqo‘rg‘on +Kondopoga +Koni +Liberty +Bella Vista +Cabrobó +Malakoff +Wishaw +Bai Chay +Chimākurti +Andernach +Ometepec +Benguema +San Gregorio de Nigua +Bardejov +Candelaria +Jima Abajo +Majidpur +Drexel Heights +Asprópyrgos +Gaggenau +Jinka +Pantao-Ragat +Zhedao +Kasimov +Las Nieves +Harpenden +Falou +Huanta +Salou +Chabet el Ameur +Tāramangalam +Anse-à-Foleur +Best +Wik’ro +Highland Park +Lewe +Uithoorn +Anjad +Qarataū +Milford +Berriane +Daheba +Whitstable +Tsuru +Crevillente +Calintaan +Mozhaysk +Gorodets +Waterloo +Ndora +Djendel +Gorna Oryahovitsa +Kaipram +Middletown +Kibiti +Kokhma +Bankass +Cláudio +Yverdon-les-Bains +Bowling Green +Periyanāyakkanpālaiyam +Ypacaraí +Liévin +Egra +Inami +Joaçaba +Anse à Pitre +Floresta +Ostuni +Quezaltepeque +Piagapo +Taşova +Kanniparamba +San Giuseppe Vesuviano +Candijay +Marogong +Mussoorie +Pulupandan +Planadas +San Miguel +Mucari +Gukeng +Miramar +Saktī +Dumalag +Rury +Elūr +Balete +Postmasburg +Rahīmpur +Mīnūdasht +Rosignano Marittimo +Oltu +Bretten +Muratlı +Kondarangi Kīranūr +Iguig +Wuyuan +Szekszárd +Monte Patria +Nethirimangalam +Mārgrām +Merzig +Sainte-Julie +Milazzo +Nanchital de Lázaro Cárdenas del Río +Villa Regina +Sakhipur +Zemoura +Degeh Bur +Meschede +Kericho +Granger +Cahul +Dauin +Comiso +Congleton +Kulat +Mahugaon +Jamālpur +Monterey +Santa Maria +Ārda +Balungao +Qahderījān +Chmistâr +El Fanar +Bent Jbaïl +Zghartā +Andranomanelatra +Tsitondroina +Ambinanitelo +Ambalavao +Majuro +Bourèm Guindou +Du Yar +Kuah +Dulmial +Malak Abad +Malème Hodar +Sali +Uar Esgudud +Ikoto +Kafr Nubl +Änew +Mpwapwa +Kayunga +Pop +Kanyobagonga +Gongyefu +Liaojiayuan +Les Palmes +Mundāhal Khurd +Hasanparti +Rānpur +Pātri +Salāya +Edasshēri +Mundi +Padappakara +Sāgwāra +Tirwa +Kaluvāya +Masabdisa +Māgadi +Bāghmāri +Sarpavaram +Dhandhuka +‘Aynkāwah +Jalawlā’ +Mutsamudu +Kotovsk +Saundhonwāli +Salay +Pārakadavu +Eshtehārd +Dala +Soledade +Sombrio +Koratgi +Schwandorf +Askøy +Kamisato +Winter Park +Anchieta +East Niles +Chamblee +Navāpur +Riga +Kaminoyama +Zgorzelec +Birūr +Aarschot +Kottūru +Vero Beach South +San Andrés del Rabanedo +Holbæk +Garopaba +Saint-Constant +Radcliffe +Southgate +Agno +Verbania +Gérakas +Dalin +Pind Dadan Khan +Manduria +La Garenne-Colombes +Marhaura +Banning +Ragan Sur +Santa Rita +Mohyliv-Podilskyi +Imbituva +Galesburg +Subotica +Rietberg +Carshalton +Slavgorod +Vyshhorod +Vestal +Triprangōttūr +General Alvear +Dandenong +Mahón +Plympton +Pānakkudi +Serra Negra +Metamórfosi +Algonquin +Conguaco +Aral +Pinhão +San Roque +Mandalī +Mabole +Campo Magro +Padada +Balabagan +Wellesley +Tacuba +El Idrissia +Villa Dolores +Al Madad +Be’er Ya‘aqov +San Andrés Xecul +Xiaba +Quiapo +El Kseur +Waslala +Ozhūr +Kabalo +Gloucester +Fitchburg +Alacuás +Ris-Orangis +San Luis +Motala +Nutley +Kodayattūr +Petite Rivière de Nippes +Nanga Eboko +Oosterend +Shibancun +Dzyarzhynsk +Shetang +Tiruvēgapra +Fleming Island +Thuận Tiến +Krishnarājpet +Kınık +Bad Zwischenahn +Hakha +Bontoc +Talwāra +Fresno +Kaita +Hengchun +Antsampandrano +East Windsor +Puente-Genil +Madavur +Hengshuicun +Ranai +Dahana +Râmnicu Sărat +Puthupalli +Santa Cruz +Raytown +Bulwell +Durango +Mount Hagen +Bois-Colombes +Mananasy-Tsitakondaza +Charneca +Ancón +Nattam +San Lorenzo +Atascadero +Morrisville +Artëmovskiy +Benito Soliven +Bāzeh Kalāgh +Chahchaheh +Bozmargī +Kalāteh-ye Mīr Āb +Santiago do Cacém +Perico +Rendsburg +Reghin +Sardrūd +Dedovsk +Phú Mỹ +Clichy-sous-Bois +Atbasar +Mbanga +Yong’ancun +Tabio +Décines-Charpieu +Devarkonda +Fridley +Heesch +Igarapava +Saint-Cloud +East Fishkill +Tena +Summerlin South +Karahrūd +Makato +Savanette +Dagestanskiye Ogni +Bayramiç +Bergen +Buenavista +Kloof +Nu‘ayjah +Orchard Park +Kitaakita +Asha +Parsuram +Paragould +Sursand +San Jacinto +Vinaroz +Buriti +Hosdurga +Uychi +Manjo +Nepānagar +Werve +La Rinconada +Pasuquin +Newtownards +Rota +Hazleton +Vlasotince +Alushta +Canguaretama +Zaragoza +Stung Treng +Piraju +Yingshouyingzi +Rahway +Pedras de Fogo +Warrnambool +Trutnov +Toksun +Bonou +Synelnykove +West Rembo +Chatou +Waltrop +Oelde +Monfalcone +Lathrop +Petrich +Cruz +Villa Donato Guerra +Bregenz +Jāmai +Pati do Alferes +Placer +Alquízar +Xinmin +Matthews +Chikuzen +Kalpatta +Mannārgudi +Ağdaş +Idar +Veliyangōd +Villa Gesell +Kendal +San Agustín Chahal +Dayr Abū Sa‘īd +Mangdongshan +Kollengode +Gotse Delchev +South Ubian +Rārōtt +Barbosa +Laurel +Point Fortin +Mashan +Bourgoin-Jallieu +Hajdúböszörmény +Tokmak +Schererville +Hobart +Xalatlaco +Lākheri +Inagawa +Zamboanguita +Limoeiro do Ajuru +Burton +Pedro Betancourt +Eastern Goleta Valley +Antsahalava +Güstrow +Chiknāyakanhalli +Landsberg +Maranga +Johnston +Purificación +Vandœuvre-lès-Nancy +Cambuí +Sumilao +Behror +Olocuilta +Temse +Unterschleißheim +Bra +East Palo Alto +Vallentuna +Central +Périgueux +Nan’ao +Sevenoaks +Demnat +N’Gaous +Dongyuya +Sint-Michielsgestel +Duanzhuang +Hutto +Fountain +Mīnād +Garfield Heights +Ja‘ār +Oak Park +Greenville +Augustów +Lalla Mimouna +Kumalarang +Flandes +Escárcega +Melrose +Northfleet +Arari +Manticao +Sankaridrug +Yangshuling +Fritissa +Nejapa +Brecht +Buenos Aires +Dembī Dolo +Zaltbommel +Charenton-le-Pont +West Windsor +Northeim +Tournefeuille +McCandless +Pirakkād +Juchitepec +Garchitorena +Ibrāhīmpatnam +Mūdbidri +Bujaru +Ātmakūr +Pueblo Nuevo Viñas +Vadakku Valliyūr +Uthal +Krimpen aan den IJssel +Tizi Gheniff +Perafita +Guyancourt +Cramlington +Texarkana +Funchal +Kyegegwa +Golden Gate +Figuil +Espigão D’Oeste +Budaörs +Pontedera +Lavezares +Bagong Pag-Asa +Kostomuksha +Lāmerd +Brownsburg +Likino-Dulevo +L’Arbaa Naït Irathen +Bluffton +Marar +Santiago de Baney +Bāglung +Crofton +Kavajë +Carney +Suisun City +Didiéni +Prainha +Póvoa de Santa Iria +Pilachikare +Ra’s al ‘Ayn +Glória do Goitá +Anderson +Casma +Pattikonda +Nalakadoddi +Castelvetrano +Madang +Jacksonville +Asipovichy +Aralam +Pignon +Glenville +Tönisvorst +Shikharpur +Mioveni +Catarroja +Guotang +Pitou +Yōrō +Kirkwood +Ski +Vaihingen an der Enz +Linden +Swarzędz +Benhao +Emmen +Sun City Center +Arfoud +Carmona +San Rafael +Shuangtian +Magna +Namaacha +Chalatenango +Khunti +Novaya Usman’ +Licab +Riihimäki +Shibushi +Oakleaf Plantation +Oslob +Rasrā +Gates +Garaimāri +Perry Hall +Quivicán +Paratinga +Cachoeira +Divnogorsk +Winnenden +East Lake-Orient Park +Bromsgrove +Kōshū +Udaipur +Fandriana +Drexel Hill +Le Plessis-Robinson +Saalfeld +Actopan +Capitán Bermúdez +Maddagiri +Orchards +Fundão +Tōmi +Cottica +Changzhi +Dougabougou +Cañada de Gómez +Beldānga +Esmeralda +Bulung’ur Shahri +Poás +Ob +Redondela +Zapotiltic +Santa Cruz Cabrália +Poona-Piagapo +La Source +Arniquet +Kamphaeng Phet +Aipe +Irará +Draveil +San Fernando +Oktyabr’sk +Ath +Washington +Blankenfelde +Shaker Heights +Horki +Nieuwkoop +Anadia +Hinunangan +Rāghopur +Yehud +Queensbury +Yomou +Nelson +McDonough +Bühl +Ulundi +Pallijkarani +Mahendragarh +Nakrekal +Takanezawa +São Miguel do Iguaçu +Mégara +Burbank +Culemborg +Sopó +San Cristóbal +Moñitos +Primero de Enero +Ozumba +Sānkrāil +Springe +Kelkheim (Taunus) +Englewood +Bogdanovich +Viacha +Xiaobazi +Desenzano del Garda +Colgong +Agde +Rapallo +Chili +Şūrān +Hisor +Budaka +Sherobod +Cambuslang +Danville +La Oliva +Pālpā +Madīnat Zāyid +Casselberry +Mehlville +Sonāmukhi +Kālappatti +Baixo Guandu +Zhongwangzhuang +Sexmoan +Riesa +Shumerlya +Teapa +Marigliano +Maubeuge +Whitehall +Boxmeer +Pont-y-pŵl +Kitui +Paripiranga +Vattalkundu +Chortkiv +Catandica +Stoughton +Shiqiao +Uttamapālaiyam +Lugus +Mirandópolis +Sendjas +Santo Domingo +Gagarin +Deodrug +Johi +Somotillo +Tocache Nuevo +Vallikunnam +Kudat +Togitsu +Winterswijk +Matouying +San Marcos +Benavente +Reforma +San Javier +Hendījān +Ouled Haddaj +Carmo do Paranaíba +Meißen +Pirri +Ipubi +Vandiyūr +Ponmana +Dapa +Úrsulo Galván +Abasolo +Senhora da Hora +Ahmer el ’Aïn +Ofaqim +Sortöbe +Timizart +Gūdalūr +Câmpina +Humansdorp +Ermont +Pervomaiskyi +Ystad +Cervia +La Dorada +Tameslouht +Cruces +Plachēri +Germī +Sotteville-lès-Rouen +Langley +Ammi Moussa +Lake in the Hills +Laplace +Kailāras +Ḩawţat Sudayr +Hajira +Limoeiro de Anadia +Orange +Bochnia +Tondela +Russellville +Hakmana +Ampatuan +Barabinsk +Humberto de Campos +Sibanicú +Široki Brijeg +Hanawa +Santo Niño +Pucón +Greenville +Haines City +Fabriano +Stafford +Siquijor +Ban Phai +Jimenez +La Calera +Tabuelan +Kuttuparamba +Candelária +Ariyalūr +Dalfsen +Nāhan +Bielawa +Três Marias +Coromandel +Petrovsk +Tazmalt +Rösrath +Borzya +Karippira +Lādwa +Arrentela +Gidri +Easton +Grimsby +Ponot +Munsan +Lacey +Ruskin +Mount Olive +Mondlo +Carinhanha +Balakliia +Welk’īt’ē +Santa Cruz das Palmeiras +Pokhrām +Stockbridge +Emmendingen +Baleno +Mathba +Thabazimbi +Bou Arfa +Izegem +Rehoboth +Kūdligi +Vīrapāndi +Villiers-le-Bel +Śrem +Duzhuang +Ibaiti +Las Rosas +Caetés +Caicedonia +Takahashi +Babatngon +Hirado +Tiruvambadi +San Lucas Sacatepéquez +Narsimlāpet +Curridabat +Kamalāpuram +Saikaichō-kobagō +Garupá +Lepe +Roseaux +Yoshida +Bugarama +Kiskunfélegyháza +Asago +Tarawa +Pingdeng +Bom Jesus +Jeffersontown +Ponmala +Pápa +Landhaura +Metu +Fresnes +Acatenango +Manatí +Hanwell +Premiá de Mar +Stanford le Hope +Zighout Youcef +Umrat +Martorell +Misilmeri +West Springfield +Pilón +Kozhinjampāra +Bella Vista +Novyi Rozdil +Zhonghechang +Kikinda +Carauari +Nongstoin +Rāikot +Sidi Smai’il +Bixby +Schwelm +Independence +Harrison +Tanglou +Tantoucun +Taza +Maki +Lansing +Chahe +Höxter +Andoharanomaitso +Muritiba +Soissons +Krotoszyn +Ballymena +Tibiao +Karakoçan +Paraparaumu +Mabini +Pakwach +Katima Mulilo +Yerres +Leonding +Ferdows +Candelaria +Pilar +Bang Phongphang +Murlīganj +Nauhata +Souk et Tnine Jorf el Mellah +Benicarló +Esztergom +Jamestown +Navolato +Old Harbour +Santiago +Doña Remedios Trinidad +Geislingen an der Steige +Taungup +Nowy Dwór Mazowiecki +Esparza +San Salvador +Mehdya +Pitoa +Oyabe +Pattāmbi +Sarny +Nueva Concepción +Mondragone +Gevaş +Saito +Kombissiri +Fenoarivobe +Pawāyan +Manga +Ciudad Sahagun +Kartaly +Neduvannūr +Edayikunnam +Agawam +İhsaniye +Mirzāpur +Tudela +Jujutla +Bom Jesus +Xizhou +San Carlos +Tracuateua +Laoaoba +Mampikony +Yangiyer +Akkattettar +Bethlehem +Iúna +Rancharia +Simri Bakhriārpur +Sottaiyampālaiyam +Shangluhu +Koprivnica +Zhongtai +Statesville +Moribabougou +Manampizha +Munnalam +Jaciara +Mechernich +Saugus +Frome +Blackrock +Sabra +Mouzaïa +El Copey +Plaine du Nord +Santo Niño +Baradero +Brodnica +Reinbek +Rūdehen +Basay +Bridgewater +Isnos +Aleysk +Hattersheim +Azángaro +Verden +Bamba +Nivelles +Madison Heights +Amaliáda +Ḩarīr +Monroeville +Enterprise +Saint-Étienne-du-Rouvray +Bāgha Purāna +Losal +Dubăsari +Ambatolampy +Bin-Houyé +Longaví +Rajpur +Heerenveen +Pārdi +Cajati +Szentendre +Matsubushi +Samut Songkhram +Izu +Mendefera +Frattamaggiore +San Lorenzo de Esmeraldas +Sue +Pāvugada +Gusev +Ilave +Condega +Nenmem +Elanjivaliseri +Venezuela +Puerto Leguízamo +Zhailuo +Frankfort +Tsushima +Piracuruca +Wood Green +Milton +Oulad Hammou +Conceição da Barra +Selargius +Mpika +Coyaima +Siraha +Douar Oulad Aj-jabri +Tha Yang +Goio-Erê +Kumru +Castel Volturno +Ouled Ben Abd el Kader +Bonito Oriental +Spring +South Laurel +Forest Hills +Ludwigsfelde +Baishaling +Bushey +Saint Bernard +Almus +Cártama +Harūr +Raseborg +Ar Ruţbah +Short Pump +Byaroza +Jaramijó +Allen Park +Valencia +Aberdeen +San Manuel +Ogawa +Yaldā +Bānswāda +Mitú +Conceição das Alagoas +Datu Piang +Qianxucun +Toli +Ciro Redondo +Rāmanayyapeta +Mahārājpur +Voúla +New Iberia +Sidi Lakhdar +Kamateró +Dieppe +Pongoz +Nkhotakota +Aioi +Ban Bang Khu Wat +Villanueva +Guararema +Prata +Passira +General San Martín +Baunatal +Mannanchōri +Clarksburg +Princes Town +Toucheng +Kpandu +Kočani +Arivonimamo +Brāhmana Periya Agrahāram +Henstedt-Ulzburg +Puvali +Tarāna +Kizhakkōtt +Puttankulam +Magog +Boisbriand +Taiwa +Sāho +Mollendo +Higashikagawa +Srungavarapukota +Glen Ellyn +Karanjiā +Oltinko‘l +Aïn Taoujdat +Temerin +Truskavets +Monsey +Supe +Bermejo +Saint-Sébastien-sur-Loire +Ruy Barbosa +Huanghuajing +Guamal +Bakhor +Tablat +Libacao +Herentals +Rio Pardo de Minas +Kolambugan +Adrogue +Safājā +Shakīso +Juangriego +Geilenkirchen +Maravilha +Qiman al ‘Arūs +Vícar +Northampton +Lagdo +Aného +Ans +Köşk +Giżycko +Bardaskan +Altınova +Kevelaer +Königsbrunn +Cuitzeo del Porvenir +Temescal Valley +Hājipur +Edappalli +Bergenfield +Caucete +Antur +Eonyang +Diangouté Kamara +Cocal +Sirsāganj +Walnut +Santo Antônio do Içá +Vakfıkebir +Poggibonsi +Kotagiri +Zacoalco de Torres +Metepec +Bejucal +Grimma +Heywood +Əmircan +Whitehorse +Wappinger +Heshancun +Reda +Kroměříž +Ljubuški +Ayun +Babar +Carmagnola +Ouésso +Pokhvistnevo +Agui +Dobropillia +Shāhpura +Arqalyq +Çiftlik +Lier +Chatan +Eagle Pass +Henderson +Itabela +Guma +Camas +Dar Bel Hamri +Warin Chamrap +Nalayh +Villaviciosa de Odón +Nirasaki +Khrestivka +Gaotan +Saldanha +Obama +Carrickfergus +Tetela de Ocampo +Sønderborg +Chennamangalam +Glen Cove +Pinyahan +Alotenango +Arnstadt +Cabricán +Čapljina +Bearsden +Bhambia Bhai +Udomlya +La Paz Centro +Cabangan +Ridgecrest +Sāngola +Sheohar +San Vicente dels Horts +Neptune +Dieppe +Massamá +Tsinjoarivo +Conchal +Golo-Djigbé +Bom Jardim +Seseña +Kakiri +Tara +Maryland Heights +Kirov +Hazar +Kuli +Nallıhan +Gyula +Xochistlahuaca +Nakhal +Mima +Qujingpu +Sueca +San Vicente de Chucurí +Casa Branca +Pô +Khulm +Guaimaca +Wegberg +Honchō +Dajabón +Djugu +Kalwākurti +East Hampton +Reddish +Converse +Rhennouch +Lincoln +Devanhalli +Gingee +Leichlingen +Villazón +Khust +Åkersberga +Oakdale +Vallauris +Kefar Yona +Bayang +Nunspeet +Navahrudak +Mainit +Vanves +Mchinji +Anajás +Lomme +Twentynine Palms +Douentza +Tainai +Dübendorf +Geldrop +Qornet Chahouâne +Sahavato +Imito +Sandrandahy +Ambohijanahary +Masanwa +Bo‘ka +Qianshanhong Nongchang +Jiaoxiling +Sāila +Limeil-Brévannes +Kārākurisshi +Billericay +Cuartero +Langedijk +Isser +Almazora +Lubok Antu +Montfermeil +San Felipe Jalapa de Díaz +Pacho +Belpasso +Zeitz +Yangyuhe +Orlová +Shaler +San Andrés Sajcabajá +Kasangulu +Kushtagi +Gragnano +Shalqar +San Martín de los Andes +Kanavāikuli +Punceres +Āron +Lake Jackson +Dhabauli +Labasa +Goriar +Hamar +Canosa di Puglia +Vilāngudi +Noé +Douglas +Tsinjoarivo +Aloran +Garden City +Nochistlán de Mejía +Bitam +Ennis +Malmal +Iguaba Grande +Sfizef +Newry +Danvers +Mocímboa +Dahmani +Łuków +Bouka +Ash Shajarah +El Sauce +Reina Mercedes Viejo +Reina Mercedes +Bordj el Bahri +Ambohitompoina +Larreynaga +Majayjay +Caridad +Gülnar +Maple Valley +Gyöngyös +Hiji +Harelbeke +Cecina +Dingalan +Melgaço +Talusan +Orvault +Mytilíni +Chichaoua +San Isidro +Zanandore +Ampasimanolotra +Mahaplag +Quiroga +Daphne +Villa Luvianos +Topki +Sirakorola +Chalhuanca +Umargām +Guaçuí +Sabaneta +Kakuda +Zolotonosha +Hamtramck +Bārughutu +Griesheim +Uchturpan +Muchun +Sestao +Sachse +Traipu +Wāsi +Mizumaki +Darlaston +Belmont +Nibria +Magdalena +Motozintla +Vleuten +Lohne +Rockledge +Māttūl +Tualatin +Wilmette +Vadnagar +Aci Catena +Tlalpujahua de Rayón +Williston +Chato +Ramsey +Desnogorsk +Viñales +Soledad de Doblado +Bongor +Bernards +Ivanjica +Tenares +Ashington +Chaska +Mongo +Fūman +Ramotswa +Charkhāri +Paivalike +Williamsport +Pirapòzinho +Immokalee +Monção +Qibray +Itapissuma +Ōuda-yamaguchi +Khātegaon +As Sarw +Gabasumdo +Havran +Hidrolândia +Sundern +Lapuyan +Ocean +Batken +Tahla +Palmar de Varela +Lebrija +João Alfredo +Itacaré +Tudiyalūr +Parabiago +Mogliano Veneto +Dodge City +San Giovanni in Persiceto +Axim +North Kingstown +Koewarasan +Borşa +Kashima +Santa Bárbara +George Town +San Miniato +Shuiding +Bellaa +Wajimazakimachi +Teltow +Spinea +Santa Rosa de Lima +Alatri +San Pedro del Pinatar +Poruvakara +Porto Belo +Troyan +Le Chesnay +Tabira +Aparecida do Taboado +Amarpur +San Pablo +Brunssum +Argelia +Ban Chang +Kitsuki +Clinton +New Windsor +Atlautla +Tuku +Malargüe +Lagoa Vermelha +Phuntsholing +Tangutūru +Huntley +Niuchangqiao +Zongdi +Mukher +San Juan Guichicovi +Di Linh +Parsa +Hikawa +Mount Pleasant +Leon Postigo +Longtan +Pachor +Pādiyanallūr +Yahaba +Bhimbar +Yemanzhelinsk +Theniet el Had +Quijingue +Shatiancun +Shilan +Sucy-en-Brie +Mont-Dore +Maracás +Baesweiler +Zarechnyy +Lagoa Sêca +Tysons +Veendam +Reisterstown +Pilar do Sul +Contla +Prior Lake +Vila do Conde +Assisi +Soignies +Ambodibonara +New London +Cabrero +Madison +Svendborg +Ouled Fayet +Waddān +Swatara +Laojiezi +Hulst +Câmpulung +Taougrite +Andenne +Ouled Slama Tahta +Narat +Wengtiancun +Wangtang +Karlskoga +Atharan Hazari +Jauja +Mulchén +Homewood +Mazenod +Conner +Pombos +Fortuna Foothills +Wetter (Ruhr) +Kantai +Sorochinsk +Kobuleti +Heemstede +Novo Oriente +Rardhu +Citong +Rāyamangalam +Hacıqabul +Ninomiya +Patjirwa +Teno +Marion +Jarrow +Niimi +Abarkūh +Millville +Witney +Takahagi +Apaxco de Ocampo +Pitogo +Daulatkhān +Marshalltown +Marolambo +Sūlūru +Sitakili +Timaru +Nuwara Eliya +Verkhniy Ufaley +İpsala +Triunfo +Sint-Joost-ten-Node +Tazoult-Lambese +Fichē +McHenry +Shingū +Nāsriganj +Supía +Bourem +Oiapoque +Bandipura +Lemon Grove +Kholmsk +Oulad Yaïch +Tongyangdao +Duayaw-Nkwanta +Piraí +Konnūr +Ukiha +Aleksandrovskoye +San Carlos +Águas Santas +Schloß Holte-Stukenbrock +Feteşti +Purral +Ciudad Bolívar +Los Reyes de Juárez +Toqsu +Villa El Carmen +Arar +Vandikarai +Maloyaroslavets +Algemesí +Mandirituba +Malekān +Sürmene +Crestview +Mu’tah +Mililani Town +Cyangugu +Jardim +Wangen im Allgäu +Heilbron +Ružomberok +Guastatoya +Overath +Caracal +Sébikhotane +Butzbach +Oppegård +Villareal +Shotley Bridge +Sādri +Ayyagarpet +Ibipetuba +Nalbāri +Itaí +Ashford +Penukonda +Leiderdorp +Staraya Russa +Itako +Tuxpan +Macau +Chicago Heights +Ciudad Tecún Umán +Kedu +Balussheri +Chillán Viejo +K’olīto +Panauti +Paouignan +Korgan +Strausberg +Jalpan +Varandarapilli +Kent +Green +King +Blerick +Ikaruga +Parksville +Aytos +Drimmelen +Alvin +Shtime +Palanas +Bihpur +Balch Springs +Castaños +Macalelon +Neenah +Cuerámaro +Pallippurattusēri +Chiavari +Navrongo +Zarraga +Almuñécar +Savur +Deer Park +Nokha +Piešťany +Gurlan +Xovos +Ilchester +Santo Antônio do Monte +Porto Calvo +Bhānder +Behara +Corrente +Ramon Magsaysay +Ehingen an der Donau +Santo Amaro da Imperatriz +Mason City +Sipacapa +Mirnyy +Haliyāl +Bady Bassitt +Citrus Park +Zalingei +Grigny +Pearl +Choba +Novi Ligure +Akitakata +Hamminkeln +Álvares Machado +Talisay +New Lenox +Kushva +Tepehuacán de Guerrero +Gubkinskiy +Debiāpur +Hayang +Kahului +Fremont +Morón de la Frontera +Holbrook +Redenção +Longton +Bourzanga +Calimete +Ezhipram +Garden City +Castelli +Oak Forest +Mocajuba +Ewa Gentry +West Islip +Kimbe +Sihecun +Boralday +Merrimack +Kobyłka +El Abadia +Taraka +Paiporta +Les Anglais +Tauramena +Kernersville +Kupiansk +Conception Bay South +West Linn +Melton Mowbray +Bo’ao +Nguti +Buri Ram +San Juan de Urabá +Dargeçit +Zhongshan +Keswick +Leimen +Hohen Neuendorf +Lindenhurst +Jonava +Pijnacker +Thomasville +Digboi +Piçarras +Dame-Marie +Čakovec +Parimpūdi +Lambersart +Aalten +San Agustín Tlaxiaca +Illkirch-Graffenstaden +Asheboro +Rappang +Novi Grad +Sikonge +Nemuro +Jeffrey’s Bay +Awara +Brétigny-sur-Orge +Haiyang +Rheinbach +Lishanpu +Pershotravensk +Mirano +Ambohijanaka +Pereyaslav-Khmel’nyts’kyy +Afzalpur +Fort Saskatchewan +Tall Rif‘at +Sol’-Iletsk +Long Thành +Liugoucun +Batgram +Quakers Hill +Ichikikushikino +Villagrán +Muchamiel +Vittorio Veneto +Lochearn +Zomin Shaharchasi +El Alia +Minaçu +Irimbiliyam +Ágios Nikólaos +Pedernales +Kuzhittura +Nova Olinda do Norte +Nayoro +Ksebia +Klosterneuburg +Toguéré-Koumbé +Harbiye +Badiangan +Pasinler +Wakefield +Libenge +Guanxi +Nazaré +Wiesloch +San Isidro +Haverhill +Benicia +Rāver +Loutété +Quezon +İmamoğlu +Springettsbury +Märsta +Sens +Leisure City +Plum +Santa Eugenia +Nizao +Thetford +Amatepec +Catamayo +Granite City +Taverny +Malebennūr +Mādhura +Wooster +Ermelo +Āmangal +Bāgepalli +Ticuantepe +Alexânia +Pueblo Bello +Chiriguaná +Marcos Juárez +Pāyakarāopeta +Vohipaho +Tsaravary +Miandrarivo +Mandabe +Andranomavo +Antsiatsiaka +Alakamisy Itenina +Arandu +Jaglot +Haqqulobod +Aragua de Barcelona +Arrecifes +Barbacha +Paracuellos de Jarama +Catió +Woolwich +Estarreja +Kriens +Belmont +Tarragona +Rapperswil-Jona +Oullins +Ourika Wawrmas +Ambinanisakana +Soanierana Ivongo +Anajatuba +Limbe +Aldridge +Basi +Aripuanã +Kapellen +Kadappuram +San Sebastián de Yalí +Ar Rudayyif +Novo Cruzeiro +Gladstone +Nava +Ternivka +Uyuni +Seaford +Lemoore +Bouguirat +Potrerillos +Wassenaar +Zuitou +Heppenheim +Fidenza +São Bernardo +Pfaffenhofen +Werkendam +Tupanatinga +Albania +Farnworth +Moon +Ureshinomachi-shimojuku +Dietikon +Şafāshahr +Nainijor +Tambo +Łowicz +Znamensk +Pemberton +Celendín +Nelliyalam +Qā’emīyeh +Khmilnyk +Margherita +Maner +Pınarbaşı +San Andrés de la Barca +Kulasekharapuram +Atitalaquia +Kaluđerica +Karasuk +San Antonio del Monte +Wahga +Mayskiy +Lobatse +Wheeling +Acapetahua +Bridgeton +Baraúna +Al Qiţena +Albignasego +Pāppākurichchi +Pāmidi +Douglas +Idigny +Miracema +Kadambanād +Alhaurín el Grande +Lukula +Kararān +Sarayönü +Bakhchysarai +San Luis Jilotepeque +Kızılcahamam +Arerāj +Union City +Paducah +Madattukkulam +Kālikāvu +Miki +Feidh el Botma +Gursarāi +Pūnch +Scicli +Chíos +Shoreview +Nsanje +Xima +Bozyazı +Clayton +Canindé de São Francisco +Majagua +Vādippatti +Bankāpur +Shencottah +Villeparisis +Alitagtag +Temple Terrace +Santa Margarita +Rambouillet +Ives Estates +Labrador +Vilyeyka +Wesselsbron +Cangas +Ban Phonla Krang +Khānābād +Bamessi +Magilampupuram +Fortul +Tūvūr +Bolton +Srīnivāspur +Garbagnate Milanese +Trikkunnapuzha +Paracuaro +Margate +Mirassol d’Oeste +Jumilla +Líšeň +Cenon +Solânea +Tamayo +Santa María de Jesús +Sannois +‘Abasān al Kabīrah +Qarah Ẕīā’ od Dīn +Thātha +West Melbourne +Bétera +Tlaxcoapan +Mahina +Gadhada +Bidur +Vernon Hills +Tamorot +Meppāyyūr +Magarao +Malitbog +Goianinha +Cormeilles-en-Parisis +Upper Dublin +Semiluki +Hennigsdorf +Ḩukūmatī Baghrān +Soron +Santa María Ixhuatán +Mégrine +Alattūr +Miacatlán +Saguiaran +Bhuban +Morales +Los Córdobas +Circasia +Kiruna +Ronse +Wanlaweyn +Sapian +Qahā +Māmidālapādu +Palm Springs +Lamego +Pirenópolis +Miyanaga +Piracaia +Shelek +Shangxiao +Wellington +Pitangui +Quarrata +Heiligenhaus +Turrialba +Nediyanad +Ipu +San Juan +Auburn +Ambodiangezoka +Itamaracá +Al Quţayfah +Sacramento +Birine +Bad Neuenahr-Ahrweiler +Nannamukku +Paianía +Talāja +Bien Unido +Segezha +Samayac +Highbury +Kuppādi +Mizque +Bugho +Dalsingh Sarai +South Portland +Kirovsk +Horn Lake +Las Flores +Frías +San Pablo +Ajka +Chettipālaiyam +Manjacaze +Betong +San Felipe +Sun City West +Sibi +Silao +Rājgarh +Buldan +Manihāri +Cuajinicuilapa +Maçka +Edwardsville +Carrollton +Lauri +Plainview +Propriá +Moulay Bousselham +Dar Chioukh +Baytūnyā +Aschersleben +Jaguarão +Chuanliaocun +Newton Mearns +Pantar +Wieliczka +Ibimirim +San Benito Abad +Sidi Akkacha +Voinjama +Ghatāro Chaturbhuj +Okuta +Nova Esperança +South Pasadena +Ratau +Iheddadene +Paramus +Susquehanna +Macrohon +Lauaan +Miguel Pereira +Porto da Folha +Sofiyivs’ka Borshchahivka +Bussy-Saint-Georges +Jarocin +Casiguran +Gioia del Colle +Superior +Iesolo +Tarui +Plainview +La Teste-de-Buch +Shāhganj +Molíns de Rey +Sanger +San Alberto +Lanquín +Manāsa +Horsham +Agía Varvára +Dasūya +Perumbalam +Nyāmti +Severouralsk +Cabot +Zeboudja +Mint Hill +Mantena +Sainte-Thérèse +Étampes +Deogarh +Ituporanga +Kantābānji +Nacimiento +Aleksandrovac +Krasnoarmeysk +Eureka +Novoaleksandrovsk +Dois Córregos +La Unión +Hennaya +Bubong +Ahmadābād +Giarre +Qingshan +Brawley +Clarence-Rockland +Uchinada +Shirley +Charata +Neckarsulm +Karumāndi Chellipālaiyam +Troy +Elmira +Sebeş +Al Karnak +Capão do Leão +Xincun +Timbiras +Upper Macungie +Cheadle Hulme +Sokoura +Madalum +Marapanim +Achern +Kayapa +Langdu +Blagnac +Apam +Quilalí +Água Preta +El Monte +Gördes +Kaisarianí +Mabini +Randolph +Wete +Wall +Pérez +Lido di Iesolo +Kamyshlov +Makubetsu +Apollo Beach +Maski +Oktyabrsk +Tāmarakulam +Nakhon Phanom +Caboolture +Santa Josefa +Pānchla +Tortona +Tecolutla +Peniche +Mandi +Māvelikara +Sélibaby +Lauf +Mauganj +Groß-Gerau +Newton Aycliffe +Beohāri +Madruga +Nādbai +Dinokana +Zemmouri +La Prairie +Gorlice +Miramas +Del Gallego +Blenheim +Owatonna +Estoril +Binə +Mühlacker +Dīnānagar +Cajuru +Jinju +Vīrakeralam +Ouled Mimoun +Rangia +Kirovsk +Camas +Balboa +East Grinstead +Elakādu +Ashton in Makerfield +Rēzekne +Dalnerechensk +San Jose +Zottegem +Bombo +Dongen +Gundlupēt +Lagindingan +East Chicago +Jaltenco +Vicência +Reoti +Panchānandapur +Pandag +Batavia +İncesu +Yuanyangzhen +Poldokhtar +Wyszków +Tougan +Sapouy +Ilha Solteira +Vogošća +Bāgh-e Malek +Pensilvania +Lichtenburg +Ondokuzmayıs +Duptiair +Elói Mendes +Kirzhach +Cheranallūr +Parit Buntar +Bergerac +Tehuipango +Weiterstadt +Navabad +San Pablo +Caririaçu +Clydebank +Windsor +Ulaangom +Ubaidullāhganj +Werota +Siquinalá +Wasco +Walpole +South Salt Lake +Ibotirama +Bingen am Rhein +Nordenham +Cotoca +Koelwār +Khā̃dbāri̇̄ +La Ceiba +Batalha +Nuevo Arraiján +Aru +Bozkır +East Hempfield +Baden +Az Zabadānī +Făgăraş +Narasannapeta +Rasiāri +Sabaa Aiyoun +Beramanja +Saint-Bruno-de-Montarville +Kaboïla +Wright +Yapacani +Aş Şanamayn +Ōra +Teustepe +Pooler +Laranjal Paulista +Semīrom +Zhaicun +Bayanhongor +Carbonia +Karpinsk +Midland +Infanta +Mirganj +Mel Bhuvanagiri +Paz de Ariporo +Bourdoud +Lamut +Kūdlu +San Giovanni Rotondo +Zirndorf +Poro +Colonia del Sacramento +Buesaco +Lainate +Ganjing +Tekkalakote +Chivasso +Rancho Grande +Willebroek +Kalfou +Woodburn +Muñiz +Ksour Essaf +Gedera +Tālīkota +Saumur +Kiskunhalas +Wilmslow +Polysayevo +Labytnangi +Aït Faska +Sogrāha +Cañas +Eṭ Ṭīra +Iradan +Vadakkanandal +Buddayyakota +Kottakota +Guruzāla +Lunel +Marimba +Athens +Liushuquan +Kętrzyn +Rosario de la Frontera +Flémalle-Haute +Puerto Varas +Budhlāda +Montemor-o-Velho +Shuanghe +Sant’Anastasia +Burlington +Tavira +Sanchez-Mira +Suphan Buri +Lice +Selm +Manito +Jirwa +Mankāchar +Iskandar +Baykan +Forest Grove +Beuningen +Mortsel +Imperial Beach +Austin +San Ignacio +Mesagne +Frankfort +Dum Duma +Suār +Gūdūru +Rīngas +Kaous +Oum Hadjer +Cifuentes +Perevalsk +Ouled Rahmoun +Dongfeng +Bijbiāra +Pacasmayo +Çankırı +Wum +Santiago Papasquiaro +Lockport +Bāruni +Vadakku Viravanallur +Montichiari +Gourma Rharous +Tuxpan +Huaquechula +Itapuranga +Skopin +Terlizzi +Kuyucak +Tōno +Desamparados +Anosipatrana +Novopavlovsk +Campos Gerais +Terrytown +Misserghin +Camanducaia +Mzimba +Sierra Bullones +Tianyingcun +Elangunnapuzha +Medina +Phulera +Requínoa +Erice +Terdāl +Tholen +Konobougou +Woodstock +Winona +Élancourt +Dengjiazhuang +Halemba +Tralee +Northdale +Kathu +Key West +Chellalat el Adhaouara +Dancagan +Mahates +Franklin +Thetford Mines +Odemira +Tianwei +Alagoa Grande +La Máquina +Bad Honnef am Rhein +El Hadjar +Casillas +Keystone +Jászberény +Mahitsy +Emmeloord +Dardoq +Gamboma +Burntwood +Zavodoukovsk +Chuimatan +Buenavista +Ridgewood +Hercules +Burgos +Hénin-Beaumont +San Juan Atitán +Renigunta +Bangzha +Lübbecke +Barra da Estiva +Hovd +Skardu +Bajina Bašta +Fort Mill +Cave Spring +Idhnā +Sāndi +Vertou +Bresso +Ambatomborona +Malaimbandy +Mahambo +Ambatofotsy +Tsarazaza +Poytug‘ +Brasiléia +Majiadiancun +Harsewinkel +Sinait +Lakshmīcharīpāra +Gahmar +Gaoya +De Witt +Rosemount +Rich +Dehgolān +Wilsonville +Upper Moreland +Mangaldai +Gonglang +Fairland +Londonderry +Bom Jesus dos Perdões +Montreux +Dharmapuram +Puerto Píritu +Istmina +Tiruppattūr +Queimadas +Curtea de Argeş +Le Grand-Quevilly +Rāmpur +Tekman +Lodi +Santa Lucia +Binidayan +Bobon +Gonesse +Gölköy +Palm City +Giussano +Ōtake +Giyani +Chintalapūdi +Kimovsk +Pando +Big Spring +Gressier +Mathibestad +Socorro +Dolores +Columbine +São Joaquim +Fleetwood +Boscoreale +Santa Ana +Bournville +Masrakh +Elk River +San Sebastián Coatán +Putignano +Gembloux +Mannachchanellūr +Maevatanana +Santa Perpetua de Moguda +Krishnāpuram +Cavaillon +Dano +Jaynagar-Majilpur +Cesenatico +Grandview +Azacualpa +Hjørring +Tubize +La Garde +Grevená +Haoping +Puerto Escondido +Lučenec +Mimoso do Sul +San Martín +El Jícaro +Formosa do Rio Preto +Gilarchāt +Demre +Jodhpur +Teniente Primero Manuel Irala Fernández +Chevella +Bessemer +El Quetzal +Kibeho +Noicattaro +Galatina +Dhrol +Ōmachi +Norfolk +Xishan +Vimercate +Colleyville +Vesala +Jharka +Marmara Ereğlisi +Unterhaching +Boureït +Jenks +Fonte Boa +El Cerrito +Chanhassen +San Juan +Ciempozuelos +Santa Isabel do Rio Negro +Perunkalattu +Ban Pa Sak +Montgomery +Kotal +Port Alfred +Amnat Charoen +Alaçam +Calpe +Gokarn +Ciudad Altamirano +Triggiano +Cəlilabad +Lindau +Dondon +Canarana I +Artvin +Yuanchang +El Progreso +Suitland +Inkster +Vitez +Nagai +Gradignan +Assèmini +Schleswig +Andasibe +San Vicente de Cañete +Polasara +Shenley Brook End +Fort Washington +Kārkala +Lanaken +Kulmbach +Korkut +Aurillac +Vignola +Vayakkalattu +Maracanã +Consett +Rāman +Marysville +Marco +Kadamalaikkundu +Koko +Alboraya +Lādkhed +Nechí +Kunisakimachi-tsurugawa +Le Ray +Vichy +Maintirano +Mungaoli +Chhota Udepur +Tubay +Rockville Centre +Santo Cristo +Florence +Marshfield +Pura +Helmstedt +Karavaram +Conversano +Koba +Arapoti +Wągrowiec +Bishunpur Sundar +Seondha +Natividad +Novelda +Pāveh +Zakopane +Cherukolattur +Biarritz +Talisayan +Chinna Salem +Santa Maria +Firmat +Břevnov +Vorkādi +Halfmoon +Koper +Coffs Harbour +San Antonio del Sur +Horb am Neckar +Ginan +Tecax +Villanueva de la Serena +Béma +Hasköy +Yinajia +Felipe Carrillo Puerto +Kireyevsk +Lapão +Arida +Lourinhã +Tabina +Néa Filadélfeia +Mpophomeni +Bandar-e Deylam +Palm River-Clair Mel +Ban Piang Luang +Rinteln +Lincoln +Kłodzko +New Milton +Tarangnan +Santeramo in Colle +Oxford +Lower Providence +Nanpingcun +Venice +Jardín América +Zhutailing +Mogtédo +Haisyn +Carahue +Champs-Sur-Marne +Coulsdon +Nova Prata +Diébougou +Safīpur +Nāgod +Scandiano +Huejotzingo +Ellwangen +Netāpur Tānda +Pereira Barreto +Fenoarivo +Malanguan +Lamzoudia +Kurchaloy +Fort Beaufort +Santa Lucia La Reforma +Tosa +Batabanó +Friedrichsdorf +Ligang +Villanueva +Tōin +Pace +Sagara +Imatra +Siqbā +Kanchanaburi +Oulad Said +Voorschoten +Doba +Ḩaql +Yolombó +Foughala +São Sebastião da Boa Vista +Altavas +West Chicago +Phulpur +Nanminda +Palma +Keşlə +Otradnoye +Geretsried +Francavilla al Mare +Trussville +Alegria +Busuanga +Tumwater +Moscow +Igaci +Utena +Kāko +Farnham +Valdagno +Migdal Ha‘Emeq +Porto Sant’Elpidio +Inajá +Sliedrecht +Warrington +Velenje +El Tarf +Genet +Mount Gambier +Tarkeshwar +Sirumugai +Missour +Chainpura +Sankt Wendel +Armentières +Staunton +Şarkîkaraağaç +Bilston +Balabanovo +Montbéliard +Nagar +Haiwei +Pszczyna +São João Nepomuceno +Bandiagara +Kami +Raghunāthpur +Oliva +Vijāpur +Jericoacoara +Alençon +Idstein +Wellington +Aracoiaba +Derby +Abay +Stevens Point +Okemos +Ipameri +Onda +Niramaruthūr +Solano +Cliffside Park +Poranki +Shāhedshahr +Karayazı +Oltiariq +Witham +Licey al Medio +Rockaway +Miranda +Tipo-Tipo +Tougué +Vaterstetten +Falconara Marittima +Verl +Salor +Mar de Ajó +Saintes +Ayuquitan +Rottweil +Enna +Brilon +Lopez Jaena +Manyoni +Nettappākkam +Roseto degli Abruzzi +Al ‘Awwāmīyah +Borçka +Xenia +Kocaali +Ciying +Mimasaka +Fujikawaguchiko +Santa Helena +Oldbury +Menaceur +Treinta y Tres +Wetteren +Bungku +Bačka Palanka +Fort Hood +Sobradinho +Astorga +Mimata +Mannadipattu +Kalispell +Salaga +Orosháza +South Bradenton +Mahwah +Tupaciguara +Veenoord +Thatcham +Mercer Island +Exeter +Vistahermosa +Biłgoraj +Āgaro +Bathnāha +Bishop Auckland +Kouhu +Silver Springs Shores +Yany Kapu +Unisan +San Pedro Masahuat +Inta +São José do Norte +West Whittier-Los Nietos +Sangerhausen +Moscháto +Pauri +Tumbao +Anagé +Três Passos +San José +Losino-Petrovskiy +Brunoy +Rocha +Limonar +Kīranūr +Omigawa +Ain Aicha +Nyanza +Szentes +Caloto +Reading +Longbridge +Vnukovo +Leandro N. Alem +Bloxwich +Peekskill +Nagtipunan +Rouached +Craíbas +Vsetín +Chengannūr +Pérama +Belvidere +Zawyat ech Cheïkh +Öhringen +Miyaki +Lamu +Hudson +Demba +Maplewood +Jilikŭl +Showţ +Newberg +Coração de Jesus +Turek +Luzilândia +Tomares +Holt +Eaubonne +De Pere +Zhongcun +Salem +Villeneuve-la-Garenne +Diego Martin +Cherbourg +Roth +Arriaga +Nonāhi +Legnago +Yanaul +Upminster +Louis Trichardt +Ozëry +Ottumwa +San Miguel +Thal +Yanyan +Tarpon Springs +Lennestadt +Galt +Vierzon +Hazelwood +Santo Tomás +Ibirapitanga +San Giovanni Lupatoto +Karunāgapalli +Busselton +Norco +Rakovski +Salamanca +Pinal de Amoles +Yamagata +Timurni +Vynohradiv +Jevargi +Wiehl +Requena +Salzkotten +Lafayette +Harbel +Ulongué +Obertshausen +Mill Creek East +Farkhor +Giv‘at Shemu’él +Lengir +Oupeye +Kovancılar +Suşehri +Ula +Arzignano +Caledonia +Belén de Umbría +San Juan de Alicante +Mednogorsk +Denizciler +La Unión +Bābra +Forney +Camillus +Budai +Sebastian +Tanabi +Kirkby in Ashfield +Kingsville +Seriate +Merkānam +Lüdinghausen +Wādi +Mariano Comense +Chocontá +Weingarten +Bouknadel +Dyatkovo +Kawlin +Albergaria-a-Velha +Elkridge +Acatlán +Topoľčany +Reedley +Cândido Sales +Delitzsch +Chardonnières +Talugtug +Camalaniugan +Fengruncun +Chiromo +Barstow +Ganshoren +Almonte +Ilaiyānkudi +Mudgal +Atlatlahucan +Pfungstadt +Bacoli +Miahuatlán +Allen +Tipasa +Mananjary +Petershagen +Wumayingcun +Sandoná +Sastamala +Avon Lake +Apiaí +Workington +Guéné +Allendale +Uruçuí +Maaseik +Brodósqui +Somoniyon +Saranga +Morozovsk +Taquari +Albany +Adra +Şaydnāyā +Uttaramerūr +Kingswinford +Kola +Villarrobledo +Lātehār +Göle +Chekfa +Sarrat +Erlun +Piranhas +Pāonta Sāhib +Tirorā +Norden +University Park +Fish Hawk +Melrose Park +Banhatti +Walker +Espelkamp +Malyn +El Calafate +Jutaí +Overijse +Aridagawa +Barberton +Raahe +Monreal +El Ghiate +Fatimé +Vāsudevanallūr +Carteret +Álamo +Debila +Bagno a Ripoli +Moses Lake +Bardsīr +Dedham +Rhyl +Gulām +Sovetskaya Gavan’ +Kasrāwad +Ditzingen +Wewak +Panāgar +Brandon +Aimorés +Atkarsk +Candelaria +Campos Sales +Otuzco +Pallappatti +North Tustin +Santa María Tonameca +Sāhibpur Kamāl +Sohwal +Rānia +Conway +Edgewood +Dickinson +Atuntaqui +Torrelodones +Mombin Crochu +Saiha +Novi Travnik +Nāmrup +Corsicana +Āshkhāneh +Malaṅgawā +Mequon +Kadavūr +Baliqchi +Pagudpud +Hastings +Iglesias +Sahjanwa +Rajākheri +Kongsberg +Santo Tomás +Newport +Vicuña +Burē +Ninohe +Ibiapina +Yapqan +Jāfarābād +Safsaf +Correggio +Soccorro +Muskego +Beersel +Duiven +Oegstgeest +Šumperk +Shrīrangapattana +El Karimia +Muret +Armilla +Bombinhas +Romulus +Steenbergen +Seal Beach +Huşi +Waukee +Kochubeyevskoye +Lanling +Nabīnagar +Sohāgpur +Charentsavan +Camaligan +Wolfsberg +San Felipe +Whitehaven +Slavutych +Datça +Barwa Sāgar +Droitwich +Pangil +Daventry +Quirino +Coín +Saint-Ouen-l’Aumône +Boussé +Easton +São João dos Patos +Barki Ballia +Nepomuceno +Simria +Yarmouth +Olpe +Trikodi +Valls +Hukeri +Naubatpur +Rāmshīr +Maywood +Pathiyanikunnu +Sonzacate +Attili +Paoay +Hindley +Khed Brahma +Vangviang +Ehden +Ampahana +Fidirana +Mahatalaky +Ankazomborona +Antsirabe Avaratra +Bidur +Catembe +Nijverdal +Santiago de Chuco +Caballococha +Dainyor +Pinhal Novo +Baxdo +Tall Shihāb +Zaouiet Sousse +Kamonkoli +Kibuku +Sho‘rchi +Araira +Higuerote +Drodro +Dbarwa +Moyalē +Portishead +Pitsea +Tegalbuleud +Ayirūrpāra +Pārner +Lālru +Singhāna +Kalamner +Sojītra +Mādha +Chānasma +Vodurivāndlagūdem +Khiria Jhānsi +Lādol +Kali +Data +Malanvādi +Aş Şaqlāwīyah +Shaqlāwah +Ash Shūnah ash Shamālīyah +Moyale +Simaria +Nossa Senhora das Dores +Tateyama +Calahorra +Mohale’s Hoek +Béthune +Sultepec +Ponta de Pedras +Norton Shores +Kabanga +Sidhaulī +Westhoughton +Tsaratanana +Tapa +Paduvari +Mərdəkan +Sandusky +Gajwel +Baindūr +Plettenberg +Visby +La Esperanza +Sugar Hill +Paete +Springfield +Rio Preto da Eva +Bad Oldesloe +Yuzhang +Niscemi +Traralgon +Cuijk +Portalegre +Marneuli +Ixchiguán +Dalupo +Pinillos +Termini Imerese +Zheleznovodsk +Mundargi +Três de Maio +Koumia +Nainpur +Hunucmá +Elefsína +Morton Grove +Dimasalang +Montalbán +Dugda +Broadstairs +Nogi +Pezinok +Sand +Ixhuatlancillo +Üshtöbe +Westchase +Castelnau-le-Lez +Flores Costa Cuca +Minowa +Quisqueya +Westerlo +Sora +Piracanjuba +Loma Linda +Pedro Carbo +Martinópolis +Quispamsis +Schmallenberg +Sahasoa +Meckenheim +Omurtag +Stein +Iki +Denison +Villa Tapia +Kobo +Wasaga Beach +Wujiaying +Karukurti +Watertown +Pavlovsk +Kirovsk +Kālāvad +Wyandotte +Edavanakad +Ishii +Jabonga +Perrysburg +Solsona +Fort Dodge +Ādīs Zemen +Rangāpāra +Kuna +Madūru +Cherukara +Valambur +Anivorano Avaratra +Liangwu +Sprockhövel +Kérkyra +Rāghopur +Ghoti Budrukh +Santa Rita do Passa Quatro +Sirmatpur +Hamme +Gemerek +Banga +Arnold +Turuttikkara +Carolina Forest +Avon +Aso +Ciudad Barrios +Tamboril +Nazareth +Uherské Hradiště +Collingwood +Syke +Bāsopatti +Piat +Madaoua +General Luna +Riverbank +Derry +Poblacion +Thundersley +Kuttampuzha +West Milford +Świecie +Icatu +Zittau +Khalāri +Baarn +Corralillo +Ouaoula +Voorst +Suchitoto +São Lourenço d’Oeste +Gorleston-on-Sea +Shivganj +Bailey's Crossroads +Ardmore +Salanso +Pamplona +Soledad +Tugaya +Douar Olad. Salem +Nar’yan-Mar +Guaraí +Fontenay-aux-Roses +Zanesville +Pepa +Pweto +Dodola +San Benito +Beifan +Galdácano +San Francisco Zapotitlán +Penistone +Kalayaan +Santana +Jamay +Rudolstadt +Tres Isletas +Ban Khamen +Meiningen +Tuburan +Bintuni +Thornaby on Tees +Frederickson +Villanueva +Scotch Plains +Cambre +Kulachi +Medford +Patian +Taquaritinga do Norte +Owando +Cloverleaf +Bāzārak +Junqueiro +Lutz +Hertford +Zumpango del Río +Brasil Novo +Delfzijl +Bystrc +Moron +Dinuba +Castro Alves +João Lisboa +Algeciras +Ma‘bar +Zhenbeibu +Gouna +Podatūrpeta +Victoria +G’allaorol Shahri +Mairwa +Santa Rosa +North Potomac +Oga +Saltpond +Kakonko +Baroy +Bainbridge Island +Tamilisan +Raška +Hınıs +Jacaré +Pānapur +Kajiado +Narutō +Patnāgarh +Monte Cristi +Stutterheim +Narasingapuram +Shitan +Uauá +Chandili +Miguel Calmon +Kaga Bandoro +Pāsighāt +Ternate +Dix Hills +Naduvannur +Eidsvoll +Larvik +Warstein +Greenbelt +Leoben +Burdeos +Newton in Makerfield +Las Cabras +Pālakodu +Oleshky +Boquim +Shamgarh +Merelbeke +Bielsk Podlaski +Al Qubbah +Bayonet Point +El Milagro +Ban Bang Phun +Santa Maria do Pará +Zwevegem +Ridgeland +Rawson +Navalgund +San Borja +Iaçu +Donggangli +Waldshut-Tiengen +Khashuri +Madougou +Pāta Kalidindi +Rathenow +Coral Terrace +Etajima +Fatoma +Rāmdiri +Oulad Tayeb +Télagh +Auburn Hills +Darende +Condado +Carrascal +Gorantla +Rio Tinto +Irugūr +Selma +Paris +Biharamulo +Reserva +Calamar +Aïn Kechera +Ronnenberg +Zion +Sept-Îles +Panglong +Carlos A. Carrillo +Bad Mergentheim +Païta +Takhli +Isernhagen-Süd +El Retorno +Zernograd +Tuzluca +Karataş +Tapejara +Libourne +Břeclav +Mponela +Guabiruba +Vernon +Stjørdal +Kottapeta +Wierden +Valdivia +Cheruvāranam +Alcobaça +Santo Tomas +Ravānsar +Sirkhandi Bhitha +Clinton +Westford +Markkleeberg +Dungra Chhota +Ain Dfali +Liria +Malinao +Watsa +Chebba +Cordeirópolis +Amatán +Centerville +Homer Glen +Aklvidu +Zdolbuniv +Qualiano +Hulbuk +Bereket +Haren +Andhra Thārhi +Nij Khari +Dongxiaozhai +Tirumuruganpūndi +Parambatt Kavu +Ocara +Waldkraiburg +Tulsīpur +Pindwāra +Ấp Khánh Hòa +Dhāmnagar +Mahdīshahr +Kaynarca +Orly +Riverside +Hanerik +Khajurāho +Traun +Oak Ridge +Leland +Neftekumsk +San Agustín Loxicha +Purwā +Juayúa +Baar +Socastee +Gyál +Sahaspur +Farmington +Cartaxo +Oak Harbor +Sahāwar +Nasukarasuyama +Herndon +Wetzikon +Igaraçu do Tietê +Attendorn +Zungeru +Olesa de Montserrat +Vinnamāla +Būndu +Tamahú +Felixstowe +Ponca City +Qabāţīyah +Arad +Channagiri +’Aïn Boucif +Vilankulo +Zhuangwei +Gaz +Balarāmpur +Garcia Hernandez +Yokadouma +Landover +São Sebastião do Caí +Três Coroas +North Augusta +Żagań +King of Prussia +Decatur +Faribault +Mola di Bari +Armiansk +North Laurel +Erandio +Pedra Azul +Oirase +Bay Point +Ramain +Fray Bentos +Minas Novas +Ashwarāopeta +Herzogenaurach +Tirutturaippūndi +Abim +Varel +Garhara +Panmana +Itambé +Stroitel +Siófok +Medford +Rugeley +Ngorkou +Happy Valley +Le Kremlin-Bicêtre +Kings Norton +Santa Ana +Tbeng Meanchey +Alicia +Eysines +Les Irois +Kazincbarcika +Asan +Buzen +Bacolod +Port St. John +Benbrook +Putten +Valente +Preah Vihear +Kenndié +Hirrīyat Raznah +Pýrgos +Duncan +Birkirkara +Hennenman +Puerto Guzmán +Nagarpāra +Ambohimasina +West Memphis +Taquarituba +Ruvo di Puglia +Asino +Nova Xavantina +Paruthūr +Porto de Mós +Le Bouscat +Saidpur +Grombalia +Sagbayan +Kerrville +Horquetas +Canhotinho +Martos +Las Margaritas +Ballenger Creek +Singarāyakonda +Ladera Ranch +Tadjmout +Buturlinovka +Pallapatti +Iringal +Xinxing +Gose +White Bear Lake +Lucena +Portogruaro +Sarykemer +Ipixuna +La Paz +Bijāwar +Qazi Ahmad +Cedros +Velūr +Echemmaia Est +Cantanhede +Bedburg +Alfreton +Oud-Beijerland +Freudenstadt +Qianwangcun +Koloriāng +Pinheiral +Curacaví +Dakṣiṇkāli̇̄ +Barnegat +Ajim +Collinsville +Staßfurt +Canton +Rădăuţi +Raisio +Oulad Barhil +Kiên Lương +Saint-Laurent-du-Maroni +Saky +Baheri +Almansa +Mahārājgani +Volksrust +Zhuchangba +Elmwood Park +Satuba +Nueva Paz +Starkville +South Plainfield +Borre +Carlow +Katrineholm +Westmont +Wekiwa Springs +Ayanavelikulangara Tekku +Middleborough +Palmetto Bay +Senador Pompeu +Noto +Umga +Zhangshanying +São Geraldo do Araguaia +Gazojak +Saint-Nicolas +Lukaya +Tocopilla +Somerset +Hudson +Mahaiza +Mau +San Juan Nepomuceno +Fairfax +Douar ’Ayn Dfali +El Salto +Valparaíso +Belton +El Factor +Mayāng Imphāl +uMhlanga Rocks +Lebedyn +Carutapera +Mekla +Nova Soure +Ganzhu +Vēttakkāranpudūr +North Lynnwood +Qiaomaichuan +Haaksbergen +Freha +Chester-le-Street +Środa Wielkopolska +Pelham +Jan Kempdorp +El Congo +Calw +Los Barrios +Cabrera +Guatuba +Gotvand +Übach-Palenberg +Farnley +Ambalamanasy II +Great Sankey +Kariba +Debaltseve +Rodez +Canarana +Calliaqua +Ponneri +Soure +Donmatías +Buwenge +Uchquduq Shahri +Boulsa +Gelemso +Aioun +Feriana +Yukon +Marín +Nāravārikuppam +Halikner +Kauswagan +Ortigueira +Quatro Barras +Lagonglong +Fossano +Maksi +Mogalturru +Machachi +Novo Mesto +Magenta +San Enrique +Skawina +Helleland +Forquilha +San Fernando +Golborne +Caibiran +Wałcz +Lop Buri +Les Pavillons-sous-Bois +Ukkāyapalle +Kosigi +Jaruco +Angatuba +Ōguchi +Yoqne‘am ‘Illit +Sehnde +Pingtang +Velingrad +Macia +Liushui +Heiloo +Meyrin +Hingham +Baharly +Rioblanco +Itaporã +Bir Jdid +Zaprešić +Simpsonville +Marsella +Isa +Setti Fatma +Korostyshiv +Salvaterra +Eisenhüttenstadt +Mairena del Alcor +Loha +Tiruvattār +Emporia +Anjozorobe +San Agustin +Marple +Vrbas +Sundararaopeta +Alwaye +Fishkill +Paraopeba +Anastácio +Upper Providence +Saginaw +Ţayyibat al Imām +Bontoc +Wandlitz +Plymstock +Bloomingdale +Espera Feliz +Jaisinghpur +As Sulayyil +Ryde +Qiloane +Areia Branca +Cogua +Monte Sião +Lagoa Grande +Mirandola +Deyr +Montevarchi +Vicente López +Santa María Colotepec +Ormskirk +Wadsworth +Goiás +Semënov +Zaragoza +Sangrāmpur +Skoura +Balete +Quezon +Rolling Meadows +Solon +Boo +Mandan +Senboku +Arttuvāttala +Mina +Tangcun +Rita +Siswa +Sūleswaranpatti +Auburn +Tegina +Guapí +Yanbu +Sakai +Bellview +Columbus +Buritis +Sanando +Jasmine Estates +Saint-Lin--Laurentides +Kourou +Independência +Pontinha +Kościan +Atar +Veranópolis +Laon +Yādiki +Kingston +Papillion +Tehri +Soamanandrariny +Ambatotsipihina +Jequitinhonha +Bcharré +Ambohimandroso +Lopary +Antonibe +Antanimieva +Miarinarivo +Tsiatosika +Itigi +Butaleja +Chinobod +Jalolquduq +Sheghnān +Tchibanga +Maheshwar +Payipira +Caazapá +Quellón +Uvarovo +Zolochiv +Sikandarpur +Jardim +Cachoeira do Arari +Cavinti +Pennādam +San Pedro +Sharya +Huilongping +Barnstaple +Burlington +Laranjeiras +Plainsboro +General Emilio Aguinaldo +Rickmansworth +Kamiamakusa +Icod de los Vinos +Dabaga +Kyonpyaw +Embarcación +Shāhpur +Laguna Salada +São Gonçalo do Sapucaí +Bom Jesus +Littleover +San Fernando +Santa Quitéria do Maranhão +Newport +Wil +La Valette-du-Var +Goirle +Neduvattūr +Ambohimahamasina +Urucurituba +Itaporanga +Jüchen +Arbroath +Tsawwassen +Bulusan +São João da Ponte +Belo Oriente +Sighişoara +Mapoteng +Solin +Wijk bij Duurstede +Rodniki +Puyappalli +Jucás +El Zulia +Douar Bou Tlelis +Ibi +Koulikoro +Manlio Fabio Altamirano +Umbaúba +Irauçuba +Usia +Sendamangalam +Hatonuevo +Kraskovo +São João do Paraíso +Buritizeiro +Pompei +San Nicolas +Alfter +Zwedru +Porto Franco +Fălticeni +Tacaratu +Paxtakor Shahri +Brotas +Mejorada del Campo +Columbus +Tantoyuca +Tecoluca +Gungu +Hadžići +Mātābhānga +Mulgund +Cibitoke +Caversham +Freeport +Vänersborg +Seveso +Tata +Nóqui +Macaparana +Iraquara +Burlington +Peringuzha +Karian +Strathroy-Caradoc +Subaykhān +Acton +Buôn Trấp +Koilkuntla +Matriz de Camarajibe +Dunleary +Johnston +Ourikela +Westerstede +Lihe +Magallanes +Herohalli +Mirandela +Oadby +Hopa +Rosemont +Jurh +Carás +Az Zuwāydah +Ksar el Hirane +South Elgin +Webster Groves +Cranford +Chanthaburi +Mulakumūd +Palu +Limbach-Oberfrohna +Cruzeiro do Oeste +Mansfield +Honda +Annūr +Makhmūr +San Miguel Ocotenco +Ciudad Serdán +Diest +Atotonilco el Grande +Pão de Açúcar +Willoughby +Quesada +Kasongo-Lunda +Altea +Popovo +Encruzilhada do Sul +Squamish +Douar Ouled Ayad +Novo Aripuanã +Thorold +Firuzoba +Kalawit +Carandaí +Kuju +Buy +Dongsheng +Frontignan +Hungund +Kumar Khad +Catigbian +Hodonín +Zawyat an Nwaçer +Montgeron +Sulya +Misato +Bithar +Farragut +Thiene +Highland +Grootfontein +Selydove +El Maknassi +Fada +Gaojiayingcun +Ituango +Dākor +Vallabh Vidyanagar +Stepanavan +El Tejar +Agrestina +Tirumala +Villanueva +Hoh Ereg +Vélingara +Dole +Ishiki +Baliguian +Akwatia +Basankusu +Kuvandyk +Manari +Döbeln +Verukulambu +De Aar +Oldebroek +Kidsgrove +Cullera +Candoni +Santa Apolonia +Burgos +Limbuhan +Hachimantai +Starnberg +Elukone +East Retford +Lālgudi +Fountain Hills +Avanigadda +Vizela +Bīrpur +Nocera Superiore +Husum +Huntingdon +Mummidivaram +Stalybridge +Ōji +Munster +Tatarsk +Fria +Pout +Alice Springs +San Giovanni la Punta +Kościerzyna +Waverly +Lebu +Ayancık +Nørresundby +Puduva +Aurora +Dasungezhuang +Devikolam +Karavalūr +Khenichet-sur Ouerrha +Rishton +Korbach +Tangalan +Salqīn +Chinoz +Stanmore +Nesher +Shama +Sezze +Ayt Mohamed +Southold +Miantso +Maldegem +Droylsden +Marple +San Juan Cotzocón +Gelnhausen +Sīlappādi +Patterson +Tirukkalikkunram +Anūpshahr +Shelbyville +Cazones de Herrera +Sanaur +Santo Antônio do Sudoeste +Muscatine +Dalyoni Bolo +Greenfield +Borne +San Lorenzo +Champlin +Lexington +Orleães +Conil de la Frontera +Lubliniec +Bonito +Independencia +Ālangulam +Yellāpur +Roseburg +Perry Barr +Conde +Piraí do Sul +Raritan +Rajpur +Dunaharaszti +Ahualulco de Mercado +Bang Sao Thong +Chandrakona +Bafilo +Tecali +Pitogo +Taupo +Bogotol +Beni Amrane +Berhoum +Gosforth +Mannamturuttu +Satun +Santa Gertrudes +Kenmore +Fresno +Yesagyo +Gueznaia +Kalach-na-Donu +Mountain House +Aşkale +Tapiales +Ōgawara +Kulgam +Shamsābād +Jacksonville Beach +Truro +Kyeintali +Kébila +Oostkamp +Newton Abbot +Ipatovo +Lustenau +Perungudi +Ðakovo +San Martín Totolán +Cerro Azul +Middletown +Kottūru +Amstetten +San Ignacio de Velasco +Tayga +Monsefú +Tamandaré +Toda Rai Singh +Pēravūr +Montgomery +Bauta +Castiglione delle Stiviere +Heverlee +Gollapūdi +Dhing +Unecha +Ma‘alot Tarshīḥā +Codajás +Luninyets +Belsand +Gerede +Bambuí +Salzwedel +Bishopbriggs +Kaltenkirchen +São Miguel +Khelil +Nyūzen +Xiaojiangcun +Xiaping +El Paso +Jaleshwar +Corinto +Yangi Marg‘ilon +Hannoversch Münden +Brookings +Dumaran +Honāvar +General Pánfilo Natera +Amherstburg +Nixa +Funes +Canalete +Ban Phru +Cupira +Aţ Ţafīlah +Watauga +Marshall +Sonneberg +Loon op Zand +Kaniv +Mujuí dos Campos +Muang Sing +Quaraí +Arukutti +Miyoshi +Davlekanovo +Suonan +Nāyanakulam +Lisle +Ringsted +Pakil +Port Laoise +Jocotenango +Cuyo +Padre Burgos +Gardner +Český Těšín +Farmington +Griffin +Mosbach +Matinha +Dillenburg +Oued el Djemaa +Koili Simra +Yugawara +Lensk +Carmo do Cajuru +Bloemendaal +Chanaur +Gauripur +Francisco Sá +Oulad Hassoune +Hajdúszoboszló +Khārupatia +Maple Heights +Maharlika Village +Monte Caseros +Krychaw +Les Lilas +Kyenjojo +Bonoufla +Ḑank +Tapaktuan +Muggiò +Sanga +Brody +Sinj +Beni Rached +Madangir +Kėdainiai +Minamikarasuyama +Chrudim +Copiague +L’Assomption +Rāmpura +Gjirokastër +Nasu +Bni Frassen +Elburg +Pilar de la Horadada +Kilindoni +Badnāwar +Raul Soares +Pissila +Maychew +Sattahip +Kailāshahar +Vega Baja +Pesochin +San Buenaventura +Zheleznogorsk-Ilimskiy +Srvanampatti +Santa Rosa de Viterbo +Teguise +Veinticinco de Mayo +Senftenberg +Al ‘Azīzīyah +Bình Hòa +Havlíčkŭv Brod +Van Buren +Burhar +Gālīkesh +Ban Thoet Thai +Svilajnac +Kafr Shukr +Clinton +Makouda +Meda +Sluis +Maywood +Bedford +Teorama +Ponda +Almeirim +Albenga +West Springfield +Akkuş +Giulianova +Vanimēl +Gamay +Bultfontein +Severobaykalsk +Netphen +Caniço +Rock Springs +Barbasa +Beni Khalled +Quảng Trị +Pottstown +Sredneuralsk +Dighwa +Sakleshpur +Montecchio Maggiore +Piteå +Takehara +Sāndwa +Çağlayancerit +Pindoretama +Leutkirch im Allgäu +Avellaneda +Tazhava +Madappalli +Sokolo +Maigo +Westerly +Piravanthūr +Saint-Ghislain +Mandāwa +Antanimasaka +Coronel Vivida +Cide +Čadca +Dongshi +Berehove +Zemst +Doctor Mora +Warburg +Collipulli +Ambohimiadana +Horgen +Minja +Sai Mai +Dalmine +Pelitli +Capinzal +Quedlinburg +Jieshang +Ron +Calbiga +Ikeda +North Platte +Bazhajiemicun +Phôngsali +Moskva +Tecumseh +Santo Tomé +Tugatog +Tobatí +Ardahan +Lormont +Bordj Ghdir +Niskayuna +Añatuya +Goumori +Aldeias Altas +La Cruz de Río Grande +Poxoréo +Carambeí +San Rafael Abajo +Whitefield +Kedavūr +Achkhoy-Martan +Piastów +Alhama de Murcia +San José +Rathfarnham +Monteros +Gersthofen +Teodoro Sampaio +Mūlampilli +Camp Springs +Telica +Abaeté +Linda +Cajidiocan +Tekāri +Martha Lake +La Higuerita +La Huerta +Maibog +Alliston +Tsaratanana +Ulukışla +Píritu +Cockeysville +Ozuluama de Mascareñas +Santo Antônio de Posse +Motul +Mine +Phetchaburi +Xunjiansi +Vaikam +Mantua +Cottage Lake +Raymore +Selim +Kūn Puhāl +Pickerington +Calamba +Loanda +Nuku‘alofa +Samrāla +Sasovo +Mizdah +Culion +Kedia +Sheybān +Nakashunbetsu +Cambará +Kolpashevo +Fergus +Kelīshād va Sūdarjān +Pātakākāni +Amarapura +Sevan +Manambūr +Touboro +Wertheim +Montgomery +Wareham +Tranquebar +Wilmington +Kazhukambalam +Capoterra +Itabaiana +Bassar +Rastede +‘Ālī Shahr +Nova Petrópolis +Vilaseca de Solcina +Vukovar +Ponmundam +Kannamangalam Tekku +Union Hill-Novelty Hill +Eastmont +Carmen +Karben +Tototlán +Colón +Pajapita +Kouoro +Gitega +Tūkrah +Ashland +Conceição do Mato Dentro +Bad Soden am Taunus +Vreden +Apatin +Minamata +Puenteareas +Zarautz +Sonepur +Colón +Ribas do Rio Pardo +Ban Bang Rin +Abreus +Białogard +Ravulapalem +New Brighton +Edgewater +Belton +Planalto +Easley +Shuilin +Bananeiras +Douar Imoukkane +Puerto Concordia +Soledad Atzompa +Elgin +Neihuzhai +Trotwood +Litoměřice +Lalganj +São Mamede de Infesta +Manage +Mahthi +Tamiahua +Oakdale +Caba +West Goshen +Ottaviano +Yásica Arriba +Rafael Delgado +Liberty Triangle +Los Arabos +Ōno +Sansanding +Singhāra Buzurg +Fânzeres +Chantilly +Calabasas +Vuliyattara +Cartersville +Sonāri +Engandiyūr +Eustis +Roxbury +Tupiza +Qiryat Mal’akhi +Hombori +Überlingen +Oxkutzkab +Morris +Yunak +Rochefort +Sederot +Mujiayu +Gusinoozërsk +Khirkiyān +Rucphen +Sīwah +Maisons-Laffitte +Fokino +Vilāchcheri +Qiryat Shemona +Tnine Lgharbia +Apolda +Vigonza +Bear +Saint-Dizier +Chester +Pallikondai +Yakınca +Roissy-en-Brie +Orbassano +Cataño +Asafābād +Couëron +Weilheim +Dom Pedro +Nagykőrös +Lunbei +Warragul +Achí +Bloomington +Koussané +Velugodu +Penwortham +Youwarou +Devonport +Hopewell +Dunajská Streda +Auch +Thiotte +Buyende +Upper Allen +Bad Kissingen +Koiri Bigha +Fernley +Mangidy +Copertino +Monte Alegre +Bataguaçu +Anjarkandi +Oliveira do Bairro +San Andrés de Giles +Nova Milanese +Lanester +Frontera +Shiling +Tembagapura +Lysander +Tubungan +Curumaní +Kulattuppālaiyam +Ladner +Nový Jičín +Glassboro +Bad Salzungen +Loos +Khairā Tolā +Ban Bang Krang +Hudson +Taylors +Harborne +Ka-Bungeni +Carletonville +Majdel Aanjar +Ambohitrarivo +Mahela +Farahalana +Analamisampy +Ambohitoaka +Miantsoarivo +Ankaramy +Marovato +Ankilizato +Andalatanosy +Kungsbacka +Xingang +Gyêgu +Zhaoling +Rawtenstall +Dindori +Candiac +Krasnouralsk +Valencia +Despotovac +Wichian Buri +Krasnoarmeysk +Stoneham +Panelas +Wallenhorst +Niamtougou +Manlin +Nagold +Ivoti +Mobetsu +Lisse +Radcliff +Lengerich +Vandiperiyār +Watertown +Lagoa +Yaguachi Nuevo +Tighedouine +Malinalco +Labuleng +Essa +Brent +Latauna +Woodlawn +Sandviken +Manuel B. Gonnet +Minbu +Encantado +Frederikshavn +Searcy +Nikaho +Paradise +Moguer +Dabeiba +Crystal +Las Breñas +Scherpenheuvel +Elektrogorsk +Nocatee +Kamabougou +Manghit +Christiansburg +Friesoythe +Kitzingen +Muttupet +Mékhé +Hollola +Abu +Laguna Beach +Molodohvardiisk +Znamianka +Nijlen +Clifton +Huaibaijie +Loves Park +Blue Island +Swinton +Peters +Manosque +Famaillá +Prairie Village +Bathgate +Itaocara +Biancavilla +Pul-e ‘Alam +Kilkís +Dáfni +Market Harborough +Nyakrom +Wulongpu +Edewecht +Dialakoroba +Dokuchaievsk +Keene +Zduńska Wola +Bhirāha +Isilkul +Shurugwi +Kotido +Vadakarai Kīl Pidāgai +Ayos +El Palmar +Colorado +Senden +Dorohoi +Lermontov +Fontaine +Trebišov +Losser +Vellalūr +Pointe à Raquettes +Ogre +La Democracia +Garoua Boulaï +Zeewolde +Ramos +Kalat +Luruaco +Marcinelle +Rivera +Mahādebnagar +Ekeren +Neusäß +Garden City +Pietrasanta +Viga +Baile Átha Luain +Guadalupe +Bhadās +Genzano di Roma +Vale de Cambra +Laupheim +Araçariguama +Nazca +Allen +Waltham Abbey +Malacatancito +Patacamaya +Oteiza +Olivet +General Luna +Azové +Vagos +Hadyach +Sarandi +Ventimiglia +Blangmangat +Krnov +Arcos de Valdevez +Roselle +Villanueva de la Cañada +Dammarie-lè-Lys +Gainsborough +Machesney Park +Guanajay +Nayāgaon +Tadmaït +Chitarpur +Naolinco de Victoria +Ecoporanga +Selvazzano Dentro +Qaţanā +Xinsi +Ahwa +Erdington +Katy +Millbrae +Nogoyá +Ondangwa +Pawni +Torre del Mar +Ataco +Bressanone +Gaoshu +Hialeah Gardens +Kagadi +Balen +Tadotsu +Siqueira Campos +Yanshanbu +Barbate de Franco +Camilo Ponce Enríquez +Cheriāl +Zunheboto +Winchester +Tomarza +Pulimāthu +Corcoran +Beek en Donk +Fangliao +Beshariq +Haywards Heath +Nāḩiyat al Kifl +Pitogo +Kapfenberg +Brambleton +Pedra +Frascati +Cinco Saltos +Pecan Grove +Aybastı +Achhnera +Lumbatan +Bormujos +Gormi +Velsk +Cartago +Sun Valley +Butig +Kungälv +San José Ojetenam +Amoucha +Pariyāpuram +Kavallemmāvu +Kodarmā +Kaler +Onondaga +Baravāt +Saint-Lambert +Kumano +Duncan +Herdecke +Linstead +Kanchika +Nasrullāhganj +Morrinhos +Villa Park +Vernāg +San Felíu de Guixols +San Miguel de Papasquiaro +Ivato +Quartier Militaire +Ojinaga +Gobō +Vayanūr +Melíssia +Ashtown +Keta +Seva +Cartagena +Junction City +Kalachinsk +Boura +Kulkent +Chunakara Vadakku +Curchorem +Ayagawa +Rāvar +Arroyito +Kerūr +Garmdarreh +Alnif +Idak +Rosita +Asaita +Watertown +Byala Slatina +Madira +Volendam +Sidi Lmokhtar +Zeya +Amalfi +Kittanapalli +Murādpur +Hazel Dell +Vélizy-Villacoublay +Ambriz +Feira Grande +Nakūr +Buenavista +Bulle +Anthem +The Crossings +Candler-McAfee +Kluczbork +Kapolei +Allison Park +Jarqo‘rg‘on +Saint-Louis +Athens +Litvínov +North Plainfield +Goldasht +Inhapim +Burg +Dalān +Shaomi +Candelaria de La Frontera +Patharia +Beixingzhuang +Andoany +Mallasamudram +Tequixquiac +Tassin-la-Demi-Lune +Lucala +Moirāng +Dip +Oytal +Laguna de Duero +Kitajima +Mullingar +Khaw Zar Chaung Wa +Dover +Moncagua +Llaillay +Cudahy +Nanyangcun +East San Gabriel +Lefkáda +Bagulā +Tashtagol +Wednesfield +La Trinidad +Prieto Diaz +Stadthagen +Pullappalli +Okahandja +Røyken +Zedelgem +Olindina +Areia +Beşikdüzü +Valašské Meziříčí +Chincholi +Fucecchio +Manimala +Morombe +Olteniţa +Manzanares +Barakī Barak +Büdingen +Sèvres +Andorra la Vella +Tocantinópolis +Santa Fe de Antioquia +Timmāpur +Fairhope +Montigny-lès-Cormeilles +Lajas +Savanna-la-Mar +Hirpardangal +Manki +Abbeville +Fleurus +Farsley +Parabcan +Bouzeghaia +Bonney Lake +Warrenton +Yahualica de González Gallo +Valmiera +Furukawa +Dawmat al Jandal +Sardinata +Strakonice +Şirvan +Camacan +Cholavandān +Senador José Porfírio +Oshwe +Boriziny +Los Llanos +El Amria +Talayolaparambu +Bronnitsy +Uenohara +Isperih +Haar +Summit +Souk Tlet El Gharb +Bélabo +Rāman Mandi +East Peoria +Pokhuria +Letterkenny +Sinjār +Florida Ridge +Tlacolula de Matamoros +Mentana +Vengattūr +Kenilworth +Horizon City +Lempäälä +Potters Bar +Kafr Baţnā +Medjez el Bab +Melila +Metzingen +Bobleshwar +Greenwood +Zaragoza +Meiwa +Huautla +Katangi +Gölhisar +Sarangani +Sabana de Torres +Jogipet +Melchor Romero +Hulikal +Deuil-la-Barre +Kotelnich +Mutatá +Cherry Hill +Nadvirna +Corinth +Bafatá +Ouani +Kafr Sa‘d +Roselle +Vilcún +Zabrat +Klatovy +Coração de Maria +Challans +Coralville +San Antonio La Paz +Willowbrook +San Pablo Jocopilas +Lazi +La Madeleine +Ivatsevichy +Nkowakowa +Sucre +Chavasshēri +Foleshill +Mount Pearl Park +Staraya Kupavna +Lincoln +Piddig +Yuli +Chatayamangalam +San Juan de Aznalfarache +Emiliano Zapata +Mouila +Khadyzhensk +Kondāzhi +Karnobat +Caculé +Baclayon +Ladyzhyn +Buriti Bravo +Puente Nacional +Gan Yavne +Pullanpallikonam +Naawan +Torcy +Esparraguera +Mihama +Vernon +Biddeford +Aymangala +Bloomingdale +Moyo +Albertville +Pirajuí +Donaueschingen +Naciria +Ottobrunn +Rancho San Diego +Finnkolo +Sarea Khās +Junnārdev +Bristol +Goulburn +Sibut +Formby +Fenyuan +Sipilou +Maying +Kasamatsuchō +Jebba +Betsizaraina +La Porte +Pudsey +Rixensart +Kiboga +Zhengtun +Zurbāţīyah +Gujan-Mestras +Macas +Bermo +Quissamã +Barreira +Freire +Szczytno +Taishituncun +Sevlievo +Gonghe +Hartbeespoort +Káto Polemídia +Mechta Ouled Oulha +Lindsay +Ibatiba +Perundurai +Montville +Arouca +Central Falls +Rumphi +Ivrea +Bad Rappenau +Saint-Lazare +Arbi’a Tighadwiyn +Diriomo +Gif-sur-Yvette +Sek’ot’a +Sabunçu +Cedro +Waynesboro +Loei +Gurmatkāl +Mendeleyevsk +Carouge +Lyndhurst +Gīlān-e Gharb +Kadiria +Sāmbhar +El Tortuguero +Guérou +Taulahā +Natagaima +Bhawānīgarh +Ceccano +Acworth +Owen Sound +Zvenigorod +Lyman +Scarborough +Frauenfeld +Chinameca +Daxin +DeBary +Panniyannūr +El Doncello +Prāntij +Yuli +Gornji Vakuf +Evesham +Guía de Isora +Bundibugyo +Qorako‘l Shahri +Lochristi +Piúma +Phichit +Montilla +March +Roşiori de Vede +Požega +Magallanes +Brockville +Tiverton +Braine-le-Comte +Laja +Nikolskoye +Cayo Mambí +Sanso +Digor +Pale +Ruston +Brushy Creek +Bartoszyce +Valença do Piauí +Monte Aprazível +Cisnădie +Had Sahary +Oyonnax +Zhucaoying +Maracena +Shoeburyness +Versmold +Chankou +Yokoshibahikari +Lordelo do Ouro +Acatlán +Palaw +Ugong +Palmer +Tiruttangal +Senekane +Hillside +Telšiai +Edegem +Aljaraque +Montereau-faut-Yonne +Mountain Brook +Nagla +Ankireddikuntapālem +Bhatpurī +Chausa +Marina +Singampunari +Itsukaichi +El Hermel +Karaçoban +Athiringal +Tervuren +El Colegio +Kihei +Silvânia +Souahlia +Alangāyam +Bahçe +Litherland +Takahata +Koziatyn +Kheïredine +Combs-la-Ville +West Carson +Bignona +Picasent +Huangzhai +Yeadon +Silver Firs +Ibiá +Yahualica +Kundgol +São Filipe +Sokolov +Hérouville-Saint-Clair +Eppingen +Ingabu +Metekora +Radevormwald +Murayama +Shārūnah +Świebodzice +Saint John’s +Dasnāpur +Poggiomarino +Acomb +Abū Şīr Banā +Khorabar +Bahía de Caráquez +Ortona +Topola +Tiruverumbūr +Ilsede +Zirāpur +Sunny Isles Beach +Samtredia +Tirat Karmel +West Deptford +Hillegom +Rioja +Guazacapán +Soroca +Repelón +Medeiros Neto +Simaria +Adalhāt +Yavuzeli +Chalmette +’s-Gravenzande +Surdulica +Sandomierz +McNair +Kondalāmpatti +Phularwan +Pattiyūrgrāmam +Las Torres de Cotillas +Haderslev +Gubbi +Shabestar +Granite Bay +Gümüşhacıköy +Kilkenny +Bakhri +Maribojoc +Santo Antônio +Yabu +Nova Zagora +Sulmona +Guabo +Qarazhal +Flores +Goleniów +Mbaïki +Jiangdi +Chestermere +Les Pennes-Mirabeau +Salvaterra de Magos +Batad +Vellithiruthi +Paraguarí +Takelsa +Ghriss +Sarapāka +Aleksandrów Łódzki +Sant’Arcangelo di Romagna +Ensley +Bavly +Vayalār +Katkol +Bhānvad +Golden Valley +West Rancho Dominguez +Ban Pa Tueng +World Golf Village +Salcedo +Guryevsk +Pīrmed +Waldkirch +Reigate +Lower +Phetchabun +Pingtan +Kingsville +Bourkika +Ramona +Fantino +Bellavista +Sahil +Tabursuq +Talwandi Sābo +Heide +Livadeiá +Salémata +Krolevets +Qapqal +Cunha +Santa Fe +Arroyito +Namayumba +Caojiachuan +Doutou +La Cruz +Madhupur +San Agustin +Essex +Palmital +Evans +Hallim +Clarksville +Smithfield +Árgos +Perāvūrani +Huejúcar +Pailitas +Penarth +Howli +Montclair +Ocatlán +Muelle de los Bueyes +Husnābād +Hoogezand +São José do Rio Preto +Vilnohirsk +Taixi +Timbiquí +Horley +Newquay +Camborne +West Puente Valley +Kālchīni +Estcourt +Eschborn +Karlsfeld +Moncada +Yorosso +Qifţ +Palestrina +Hastings +Schwetzingen +Nevyansk +Imbert +Parsāgarhi +Ḩamīdīyeh +Kommunar +Gardelegen +Ādampur +Chhanera +Itaiópolis +Oxford +Madamba +Lorton +Rimavská Sobota +Polýgyros +Kalýves Polygýrou +Northwood +Honmachi +San Martín +Ceres +Aswāpuram +Khandela +Karaisalı +Tiquisio +Dongta +Mondovì +East Ridge +Carbondale +Ruwa +Jizhuang +Semikarakorsk +Sapulpa +Banbishancun +Woensdrecht +Jāwad +Naregal +Dubnica nad Váhom +Elsdorf +Corail +Silvania +Oxford +Dioumanzana +Anini-y +Taveta +Comacchio +Meixedo +Ostrów Mazowiecka +Tola +Greystones +Uliastay +Paese +Priego de Córdoba +Chillicothe +Épernay +Jugiāl +Ghāt +Manakambahiny +Ambodimotso Atsimo +Ampasimanjeva +Iarintsena +Andranovorivato +Bemanonga +Vohimasina +Andilanatoby +Alakamisy-Ambohimaha +Ambalaroka +Jafaro +Ankiabe-Salohy +Antsakabary +Uch Sharif +Lebane +Huskvarna +Kigumba +Hacı Zeynalabdin +Aoshang +Atherton +Clevedon +Bukkarāyasamudram +Machagai +Unchahra +Göd +Kotovo +Zundert +Drās +Gavimané +Sanford +Taminango +Bilthoven +Al Bāḩah +Pitrufquén +Néma +Vilyuchinsk +Pinhalzinho +Khosrowshahr +Ampanotokana +Vereshchagino +Iracemápolis +San Rafael del Yuma +Djinet +Robbah +Arroio do Meio +Kuppam +Koekelberg +Vertentes +Koksijde +San Jose +Dudelange +Villa de Leyva +Darien +Sêrro +Valinda +Glauchau +Chākia +Pochāram +Diamantino +Mundamāla +New Hartford +Rāmantali +Ḩārim +Ấp Phú Mỹ +Robertson +Svetlyy +Tornio +Nakagusuku +Samba Cango +Mazagão +Chambersburg +Sandbach +Chalchihuitán +Axapusco +Ituberá +Nerja +Makó +Gōtsuchō +St. Andrews +Aichach +Al Fuḩayş +Krathum Baen +Pascagoula +Pak Tin Pa +Shiroishi +Bungotakada +Membakut +São Miguel do Araguaia +Sayada +Kāchhāri +Darreh Shahr +Árta +Béguédo +São Francisco de Paula +Lom +Muzambinho +Vanthli +Kueneng +Póvoa de Lanhoso +Pio XII +Caranavi +Alfafar +Frameries +North Salt Lake +Casablanca +Almoradí +Shinhidaka +Montmorency +Maghalleen +Karlovo +Hillsborough +Middleton +New Castle +Sonthofen +Sainte-Foy-lès-Lyon +Nogales +Montigny-lès-Metz +Renfrew +Komló +Flörsheim +Gigaquit +Binnish +Tongzhou +Kafr Qāsim +Port Hueneme +Rajauli +Massapequa +Mata Grande +Lihuzhuang +Mbandjok +Māngrol +Ōtsuki +Koçarlı +Los Palmitos +Muhlenberg +Machico +Mount Pleasant +Kepsut +Mangalvedha +Yoshioka +Santa Teresa +Danwān +Puttige +Belper +Soyāgaon +Columbia Heights +Hayesville +Horad Smalyavichy +Kizhakkemanād +Ginosa +Sheldon +Mehnatobod +De Meern +Ginebra +Azambuja +Ouardenine +Ma’mūnīyeh +Pallippuram +Amtali +Orobó +Soltau +Pueblo Juárez +Amposta +Muzhakkunnu +Parsippany +Klamath Falls +Kreuzlingen +Płońsk +Looc +Manor +Falmouth +Palmas de Monte Alto +Poço Verde +Siilinjärvi +Santa Lucía Utatlán +Yate +Morinda +Polkowice +Charo +Aḑ Ḑabyah +Aḑ Ḑāli‘ +Avon +Le Petit-Quevilly +Jaynagar +Magdalena +Puruk Cahu +Tūnēri +Had Oulad Issa +Ambatomanoina +Hassi Maameche +Langley Park +Silverdale +Sidi Amrane +Surpur +Bhānpura +Gülşehir +Geertruidenberg +El Callao +Chatra Gobraura +Kayanza +Sedalia +Maniche +Mailavaram +Lakeside +Selden +Seia +South Euclid +Zara +Otopeni +Nøtterøy +Penonomé +Bato +Oberwingert +Shendurjana +Cagwait +Sarai Ranjan +Chapa de Mota +Puerto Triunfo +Eislingen +Hockenheim +Bānapur +Zoersel +Guipos +Bad Harzburg +Birmingham +Chimoré +Anamalais +Shanywathit +Sori +Kurikka +Kalleribhāgam +Massarosa +Vāmanapuram +Miguelópolis +Comalapa +Zabré +Mezdra +Yaojiazhuangcun +Yaojiafen +Erval d’Oeste +Idukki +Tixtla de Guerrero +Vendram +Moularès +Wädenswil +Bankoumana +Caransebeş +Pachino +São João da Madeira +Alhandra +Millau +Shelby +Guapiaçu +Port Alberni +Kozakai-chō +Pampierstad +Tidili Masfiywat +Chāgallu +Sombrerete +Springwater +Eyl +Saint-Jean-de-Braye +Biggleswade +Yucca Valley +Chaumont +Millburn +Assaré +Mühldorf +Paramirim +Kavak +Qādirganj +Brunswick +Duarte +Geseke +Bad Krozingen +Kadiapattanam +Can-Avid +Del City +Pāmpur +Al Karak +Sirat +Ocotlán de Morelos +Tigbao +San Luis Talpa +Gallup +Kangal +Kopřivnice +American Canyon +Senda +Zantiébougou +Kami +Ibicaraí +Lindlar +Bailleston +Gros Islet +Nangavaram +Kobilo +Gökçebey +Nefta +Himora +Pāpanāsam +Lohutí +Jaipur Chuhar +Anūppur +Kānke +Sarzana +Lentini +Ayamonte +San Jacinto +Rāja Pākar +Tiruvankod +Ōkuchi-shinohara +Riachão das Neves +Kumla +Howick +Iisalmi +Tlaltenango de Sánchez Román +São Miguel do Guaporé +Mercato San Severino +Herent +Orzesze +Villeneuve-sur-Lot +Pushkar +Yenice +Zărneşti +San Justo +Mashiko +New Hope +Novopokrovka +Sidi Ifni +Fukagawa +Sainte-Suzanne +Scugog +Bela Vista +Jiuru +Nueva Granada +Nerupperichchal +Lakato +Amarpātan +Savigliano +Xinzhai +Paraguaçu +Vicente Noble +Tamgrout +Isla-Cristina +Alliance +San Fernando +G‘azalkent +Ashtarak +Cuilapan de Guerrero +Chapala +Senaki +Seynod +M’Chedallah +Łaziska Górne +Rosario de Lerma +Alcalá la Real +Unity +Bougado +Qal’acha +Spremberg +Ibirubá +Martí +Xanten +MacArthur +Salcajá +Chilca +Mandapam +Pergine Valsugana +Selouane +Tukwila +Elverum +Seligenstadt +Pākāla +San Nicolás +Mandelieu-la-Napoule +Rancheria Payau +Santa Teresita +Arcueil +Uxbridge +Truro +Kalpakathukonam +Stadtallendorf +Fulshear +Rocky River +Beni Douala +Colle di Val d’Elsa +Pancas +Galeras +Mililani Mauka +Cabucgayan +Ashland +Nāranammālpuram +Kontéla +Güimar +Corciano +Stowmarket +Acatic +Naqādah +Thebes +Payson +Nong Bua Lamphu +Lino Lakes +Wexford +Sermādevi +Ladário +Zerbst +Deán Funes +Cardito +San Martín de las Pirámides +Caombo +Goroka +Villagarzón +Eckernförde +Áno Sýros +Chebli +Bastos +Aarau +Alnāvar +Whyalla +Celina +Parelhas +Hazebrouck +Boquira +North Guwāhāti +Muthutala +Tanki Leendert +Alta Floresta D’Oeste +Holiday +Jawor +Nang Rong +San Bernardo +Allauch +Artémida +Günzburg +Harvey +Chesham +Puerto Natales +Rees +Dickinson +Trezzano sul Naviglio +Takaba +Pandan +Aspe +Kodikuthi +Secaucus +Richmond +Mons-en-Baroeul +Ambohitromby +Hojambaz +Gökdepe +Fort St. John +Madakasīra +Juli +Kotli +El Paujíl +Llorente +Ozark +Sihu +Loncoche +Ryazhsk +Beni Mered +Ganapavaram +Laventille +Paramonga +Senador Guiomard +Palatka +Qalansuwa +East Patchogue +Tarko-Sale +Riehen +Tong’anyi +Paravai +Pendurti +Rio Rico +Osterode +Bariārpur +Amizmiz +Waghäusel +Bogandé +Nhamundá +Volnovakha +Ust’-Katav +Partizánske +Thenia +Fleury-les-Aubrais +Saint-Michel-sur-Orge +Jēkabpils +Chandlers Ford +Tambaú +Āndippatti +Eğil +Mārutūru +Icapuí +Zossen +Omutninsk +Panagyurishte +Trinidad +Shāhbāzpur +Wilmot +Hoogstraten +Noāmundi +Merefa +São João do Piauí +Uruçuca +Ghanzi +Puchheim +Four Square Mile +San Bonifacio +Kutná Hora +Tauragė +Lioua +Grand Island +Neuilly-Plaisance +Péfki +Santa Rosa de Lima +Santa Bárbara +Srīvaikuntam +Jandaia do Sul +Alejandro Korn +Ashland +Bayi +Kant +Świedbodzin +Cumaral +Grajewo +Pāmarru +Shirhatti +Naas +Annecy-le-Vieux +Geneva +Inopacan +Khowai +Mohiuddīnnagar +Perdões +Afourar +Los Vilos +Nogent-sur-Oise +Singur +Kirkintilloch +Gummudipūndi +Nedre Eiker +Allende +Masis +Mantes-la-Ville +Teteven +Almaguer +Palma di Montechiaro +Pleasant Prairie +Seymour +Igreja Nova +Senago +Fortín de las Flores +Achères +Bilohorodka +Mukilteo +Ambinanindrano +Fukuyoshi +Westborough +Iguaí +Stange +Bornem +Silva Jardim +Vecsés +Hafshejān +Galūgāh +Zambrów +Tiruppuvanam +Mountlake Terrace +Khotkovo +Mī’ēso +Metahāra +Mundgod +Dax +South Lake Tahoe +Hammanskraal +Darton +Kasumpti +Chernogolovka +Padmanābhapuram +Doaba +Pāmbādi +Lindenwold +Vicksburg +Moorestown +San Antonio de Areco +Somers +Ulus +Antilla +Terrier Rouge +Oum Drou +Mrągowo +Srīperumbūdūr +Sukhodilsk +Youganning +Yegainnyin +Alcudia +Lyskovo +Mīt Namā +Winter Gardens +Sondershausen +Kelkit +Tubbergen +Naryn +Armidale +São João +Aralık +Aiud +Nirmāli +Narippatta +Wang Nam Yen +Makulubita +Artashat +Akonolinga +Casal di Principe +Luna +Kaduturutti +Huy +Kaikalūr +Cacém +Chāgalamarri +Krus na Ligas +South Holland +Sulphur +Sevilimedu +Royton +Dolton +Clemmons +Mansourah +Perry +Kitcharao +Carthage +Gauting +Mabitac +Belovodskoe +Yorkville +Pájara +Wieluń +Linamon +Aguilares +Baldwin +Mossaka +Nowa Ruda +Agropoli +Vialonga +Almasi +Dronfield +Elmwood Park +Oshoba +Iruttarakonam +Rolante +Traunstein +Nāmagiripettai +Sulejówek +West Pensacola +Tapalpa +Asni +Koduvilārpatti +Carrboro +Ntcheu +Monroe +Sanxing +Suwanee +Aguadas +Manuel Tames +Lealman +Bhairi̇̄ +Ossett +Montecristo +Urk +Narkher +Degollado +Tādikombu +Fierenana +Dupax del Sur +São Sepé +Pillaiyārkuppam +Bobonong +Essex +Kosgi +Zonhoven +Concepción Huista +Jiangjiadong +Kontich +Parasi +Matsuura +Rose Hill +Obock +Tabount +Varennes +Lucélia +Chorwād +Rātu +Saint-Mandé +Milwaukie +Caaporã +Rio Maior +Valle Nacional +Qia’erbagecun +Nāgamangala +Borgomanero +Boca da Mata +Rupauli +Šaľa +Vryburg +Floridia +Hyde Park +Shāzand +Belonia +Gardanne +Lāthi +Belas +Manlleu +Varadero +Tiburon +Penzance +Sarāri +Tinja +Guasipati +Clarin +Bologoye +Khromtaū +Varkaus +Wildeshausen +Hallein +Allschwil +Florencia +Morāsar +Maracaçumé +Hancha +Saponé +Irákleia +Kanmaki +Bayou Cane +Loyola Heights +Kannānendal +Quirima +Saurh +Cortona +Kağızman +Forbach +Villeneuve-le-Roi +Aguinaldo +Cartaya +Skegness +Carlos Casares +Schramberg +Raksaha +Adigaratti +Enköping +Shark +Suaza +Vikhorevka +Kokofata +Ādēt +San Juan de Río Coco +Princesa Isabel +Nogales +Foley +Wipperfürth +Irukanni +Hays +Martellago +Tullahoma +Āmodei +Bānki +Conceição de Macabu +Molde +Chocamán +Traunreut +Altepexi +Wülfrath +Germersheim +Tehata +Gorgonzola +El Pinar +Gardner +San Isidro de Lules +Oued el Aneb +Itapaci +Sadao +Tizi-n-Bechar +São Marcos +Seria +Chopinzinho +Xoxocotla +Furano +Harstad +North Bellmore +Paiganapalli +Karachayevsk +Phulpur +Sondrio +Orhei +Injibara +North Ogden +Toguchin +Imi n’Oulaoun +Xinnongcun +Palmeiras +Behat +Caxambu +Paulistana +San Juan de Vilasar +Makakilo +Saint Austell +Laje +Chinchali +San Francisco +Villapinzón +Palafrugell +Jangy-Nookat +Ukmergė +Saka +Saint Paul’s Bay +Joaquín V. González +South Whitehall +Itambacuri +Park Forest +Nubl +Helena +Oro-Medonte +Józefów +Praia da Vitória +Mirador +Caterham +Sysert +Porto Torres +Wade Hampton +São Jerônimo +Groß-Umstadt +Phalia +Zülpich +Patpāra +Ariano Irpino +Stephenville +Klaukkala +San José de Jáchal +Az Zuwaytīnah +Timmarāsanāyakkanūr +Woodlesford +Nördlingen +San Víctor Abajo +Fatehgarh Chūriān +Mānjhi +Tanhaçu +Sāmba +Willmar +Ronda +Stratton Saint Margaret +Alto-Cuilo +Cuilo +Daddi +Mangghystaū +Merrick +Cranendonck +Sremčica +Nurota +Morafeno +Sitampiky +Ambalavato +Tongobory +Tsarasaotra +Ambohipandrano +Andolofotsy +Soanindrariny +Ankililoaka +Tsiamalao +Fiadanana +Antanambao +Sahamadio +Miorimivalana +Ambohimanambola +Ampasimatera +Karianga +Matanga +La Colonia Tovar +Fengjia +Dabaozi +Sancoale +Kudāl +Anah +Mandza +Sidi Lahssen +Lebanon +Tufānganj +Ţorqabeh +Fredonia +San Antonio Sacatepéquez +Vadakkum +Walia +Laranjeira +Cassilândia +Barcelona +Sokal +Capela do Alto +Agan +Itapecerica +Welench’ītī +Daboh +Nizāmpatam +El Retén +Suzzara +Osmaneli +Zele +Fót +Santa Vitória +Baiheqiao +Thuân An +Saint-Cyr-l’École +Bugalagrande +Ankaraobato +Hualqui +Pushchino +Upper St. Clair +Caracaraí +Carmo do Rio Claro +Mont-Royal +Pacatu +Coreaú +Hirekerūr +Quela +Purranque +Hino +Nizhniy Lomov +Mineola +San Cataldo +Vladičin Han +East Moline +East Pennsboro +Green Valley +Öndörhaan +Chaudfontaine +Cuncolim +Piedra Blanca +Torrox +Damua +Punturin +Saint-Genis-Laval +Renens +Nambiyūr +Chợ Phước Hải +Alagoa Nova +Concepción Chiquirichapa +Louisville +Urussanga +Fort Walton Beach +Argenta +Five Corners +San Guillermo +Vili +Leskovac +Aishō +Guará +Jericho +Bni Rzine +Little Egg Harbor +Mössingen +Esperalvillo +Osa +Vakkam +Lučani +La Unión +Pudupattanam +Kamamaung +Senden +Snellville +Herborn +Voiron +Eeklo +Monte Santo de Minas +Pico Truncado +Battle Ground +Beaumont +Texistepec +Liubotyn +Bālugān +Germantown +Brummen +Ludlow +Murphy +Don Bosco +Koāth +Quixeré +Ambohimanga +Normanton +Mulanje +Sint-Katelijne-Waver +Palavūr +Murree +Zaliohouan +Kyaukpyu +Saint-Gratien +Sotomayor +Sánchez +Bordj Bounaama +Bambalang +Mudākkal +Kara-Suu +Aurād +Luisiana +Shōwa +El Gara +Oliveira do Hospital +Sidi Abdelkader +Buinsk +Birkerød +Beni Haoua +Hyattsville +Amdjarass +Wallington +Hindang +Pogradec +Moniquirá +Olenegorsk +Ovejas +Carlisle +Vinany +Kurovskoye +Sanhe +Wettingen +Cercado Abajo +Letlhakane +Musselburgh +Hārohalli +Arnold +Luckenwalde +Debark’ +Yachiyo +Imi-n-Tanout +Paldorak +Wasquehal +Lajinha +Junqueirópolis +Halluin +Norton +Jindřichŭv Hradec +Bruchköbel +Conselheiro Pena +Elektrougli +Gaura +Castrovillari +West St. Paul +Murrysville +Buckingham +Karmana +Covington +São José da Laje +Miches +Meinerzhagen +Bhainsdehi +Sonāgāzi +Bourg-la-Reine +Beroun +Lockport +Stadtlohn +Devirammanahalli +Mariāni +Grayslake +Malaba +Chelak +Ararat +Conceição da Feira +Valkeakoski +Banting +Schortens +Mill Creek +Ambondro +Alta +Derinkuyu +Ibicoara +Morsang-sur-Orge +Peringanād +Cordeiro +Veresegyház +Coolbaugh +Croix +Jicomé +Carcagente +Wittmund +Sestu +Bronkhorstspruit +Leek +Samacá +Algete +Hörstel +Gavarr +Coudekerque-Branche +Barhampur +Springfield +Bulnes +Colfontaine +Ul +Oliveira de Azemeis +Pālkonda +Puduppalli Kunnam +Chaves +Yermal +Luacano +Sivaslı +Illapel +Valljkkod +Ino +Bryant +Banganapalle +Mino +Supaul +Kuknūr +Sivrihisar +Narwar +Bandar-e Gaz +Mathukumnel +Leigh-on-Sea +Castel San Pietro Terme +Sonbarsa +Tittagudi +Anagni +Puzol +Le Creusot +Monsummano +Zhangatas +Hanahan +Iriona +Hexiang +Gannavaram +St. John +Kiseljak +Achampet +Moḩammadābād +Boscombe +Palestina de los Altos +Dugulubgey +Beruri +Arnaud +Oliveira dos Brejinhos +Hanmayingcun +Goodlands +Lebedinovka +Ennis +Tolosa +Elvas +Parma Heights +Enger +Ménaka +Vranov nad Topľou +Werdau +São João Batista +Llandudno +Guaratinga +Nidderau +Kolāras +Mae Sai +Luna +Campestre +Monte Azul +Ati +Olho d’Água das Flores +Chithara +Ozoir-la-Ferrière +Dolyna +Montecatini Terme +São João de Pirabas +Palma del Río +Baroda +San Julián +Lennox +Baie-Comeau +Mäntsälä +Soye +Bussolengo +Pozantı +Failsworth +Recanati +Cornaredo +South Milwaukee +Kremenets +Kirkstall +Marquette +Wuustwezel +Steinhagen +Działdowo +Profesor Salvador Mazza +Bozoum +Waxhaw +Guadalupe +Lomita +Schwanewede +İsmayıllı +Lagoa do Itaenga +Mesquite +Sidi Chiker +Muttatōdi +Givors +Rosamond +Fougères +Ås +Banaue +Los Llanos de Aridane +Melena del Sur +Montgomery +Bohumín +Piazza Armerina +Denain +Vinjamūr +Kladovo +Nerekhta +Riacho das Almas +El Hammadia +San Isidro +Parramos +Miandrivazo +Lubalo +Bagou +Bad Waldsee +Sidi Rahal +Mitry-Mory +Yanagawamachi-saiwaichō +Sour +Sarreguemines +Kāmalāpuram +Pueblo Nuevo +Arroyo Seco +Longjumeau +Bethany +Chachahuantla +Pahuatlán de Valle +Maraú +Bockum +Isernia +Kleppe +Schifferstadt +Hardi +Manhumirim +Pittsburg +Na Klang +Hurricane +Dingolfing +Concarneau +Crowborough +Adrian +Bobrov +Târnăveni +Tân Phong +Al Qays +Botevgrad +Paraíso +Celbridge +Ghafurov +Popasna +Yakkabog‘ +Sunbāţ +Tepexi de Rodríguez +Patuvilāyi +South St. Paul +Somanya +Aldan +Nan +Goris +Manbengtang +Câmpia Turzii +Neufahrn bei Freising +Jatāra +Kawambwa +Gjøvik +Caravelas +Nanuet +Sāvda +Riverview +Kapay +Wachtberg +Mead Valley +Nuenen +Lillehammer +Loja +Kekem +Baza +Chapulhuacán +Buftea +Rosolini +Toktogul +Palo del Colle +Romano di Lombardia +Nikolsk +Hashtrūd +Mbala +Qagan Us +Foya Tangia +Şā al Ḩajar +Arhavi +Pleasantville +Dumbarton +Lugait +‘Adrā +Chintalapalle +Hlohovec +Mödling +Libertyville +Jefferson +Sultanpur +Forest Lake +Honnāli +La Resolana +Fröndenberg +Zestaponi +Shoreham-by-Sea +Chã Grande +Namioka +Vakon +Arsk +Sunchales +Gandujie +Batī +Colleferro +Codlea +Mazinde +Laboulaye +Guadalupe Nuevo +Sultanhisar +Carmen de Patagones +Quemado de Güines +Aomar +Maniyamturuttu +Torhout +Bothell West +Bladel +Lami +Sonkach +Hernani +Hythe +La Paz +Vadamadurai +Madipakkam +Baião +Chennimalai +Bala +Žďár nad Sázavou +Cobourg +Ōhara +Mustang +Northfield +Atoyac de Álvarez +Station des Essais M.V.A. +Tyrnyauz +Eruh +Elko +Coswig +Colonia General Felipe Ángeles +Cauto Cristo +Carquefou +Trecate +Ijevan +Nishigō +Bibai +Bañolas +Kozmodemyansk +Baskil +Horten +Muhammadābād +Pendembu +Tielt +Shimogamo +Cranbrook +Nellimarla +Lubartów +Grottaferrata +Binondo +San Bernardino +Seydi +Sapang Dalaga +Nahorkatiya +Khvānsār +Sceaux +Limerick +Stevenson Ranch +Capanema +Hatvan +Peritoró +Saint-Pol-sur-Mer +Lower Southampton +Posušje +Becerril +La Celle-Saint-Cloud +Hailsham +Bhojpur Kadīm +Uetze +Kurshab +Reçani +Dülken +Nadikūde +Kaltan +Magsaysay +Timaná +Mindouli +Sant Just Desvern +Golden +Omagh +Kizhāttūr +Aïn Cheggag +Kawayan +Otsego +Bourne +Costas de Cão +Caparica +San Martín +Taşlıçay +Mường Lay +Saco +Paraisópolis +Puerto Quito +Maghull +Piqua +Keevallur +’Aïn el Bell +Fara +Sidi Jaber +Imperial +Susaki +Mealhada +Cinfães +Ostrov +Jarājūs +Vyškov +Pak Phanang +Huércal-Overa +Itatira +Baud +Aígio +Chepo +Sandwich +Roshal +Bezhetsk +Koipādi +Yasothon +Lambari +Joinville-le-Pont +Kuala Pembuang +Bouguenais +Jaboticatubas +Dereli +Pariyāram +San Lorenzo +Reggane +’Aïn el Hammam +Iztapa +Hauppauge +Monroe +Follonica +Greiz +Montrose +Donggou +West Hempstead +North Liberty +Zaghouan +Haßloch +Requena +Old Jamestown +Painesville +Newmarket +Sandhurst +Chokkampatti +Beforona +Koło +Longbangcun +La Cañada Flintridge +Naraura +Jaguaruna +Barki Saria +Rāmnagar Farsāhi +Samboan +Porto Real +Reichenbach/Vogtland +Alatsinainy-Bakaro +Kūttampala +Feltre +San Pablo Tacachico +Kanaya +Yangping +Tewkesbury +Okha +Cormano +Rothwell +Karaiyāmpudūr +Frøyland +Høyland +Marblehead +Sidney +Kihihi +Blankenberge +Sartalillo +Altamont +Lannion +Cugnaux +Harpālpur +Bratunac +Neustrelitz +Middle +Yellowknife +Makhtal +Westbrook +Mundra +Tala Yfassene +Huanuni +North Amityville +Kashkar-Kyshtak +Dan Gorayo +Grande-Synthe +La Chapelle-sur-Erdre +Trentola +Santa María Chilchotla +Chevilly-Larue +Dharmkot +Nesoddtangen +Farāshband +Fangasso +Farum +Baihar +Salmon Creek +Xincheng +Santa Rosa +Rawicz +Katākos +Senta +Santa Rita +Khowrzūq +Las Vigas de Ramírez +Chandragiri +Lynbrook +Gryfino +Kyakhta +Selendi +Bellshill +Tobe +East Northport +Duderstadt +Sherwood +San Martin De Porres +São Felipe +Marion Oaks +Oroville +Lalín +Bīrpur +Salgado +Inírida +Cândido Mendes +Shisui +Chorozinho +Shāmgarh +Nyon +Teculután +Simití +Concordia +Sergach +Tenjo +Minturno +Whitpain +Gopavaram +Çilimli +Lingolsheim +Devrukh +Arlington +Ogden +Kokopo +East Hemet +Pulicat +Rumoi +Frankfort +Wodonga +Luperón +Parkāl +Ely +Ervália +Rovira +Bad Schwartau +Vico Equense +Sbeitla +Chodavaram +Hazlet +Danao +Husainpur +Mar’’ina Horka +Mosta +South Burlington +Blieskastel +Jdour +Aguas Zarcas +Touna +Rāipura +Abano Terme +Palisades Park +Ochtrup +Zhangjiazhuang +Samthar +Āsosa +Belūr +Xionglin +Parnamirim +Telgte +Piñan +Edakkunnam +Le Plessis-Trévise +Khilchipur +Lichtenfels +Gyömrő +Bruges +Kortenberg +Ban Tha Pha +Yaraka +Lower Allen +Gostyń +Azacualpa +San Francisco +Sessa Aurunca +Le Mée-sur-Seine +Chāndpur +Entroncamento +Ypsilanti +Rhenen +Pennāgaram +Mělník +Tondi +Stebnyk +Xo‘jaobod +Ağsu +Ağstafa +Mayluu-Suu +Rivalta di Torino +Pontassieve +Pompéia +Boromo +Cournon-d’Auvergne +Uruburetama +South Frontenac +Douar Lamrabih +Puerto Viejo +Reitz +Pikalëvo +Guácima +Voyenno-Antonovka +Skive +Ripley +Cruz Grande +Ban Klang +Blansko +Taïbet +Kotelnikovo +Polonne +Seaham +Talayāzham +Monte Alegre de Minas +Abou el Hassan +Peterlee +Pargi +Schopfheim +Otegen Batyr +Motatán +Shafter +Diondiori +Sivagiri +Nelkattumseval +Akora +Kubinka +Medina +Frontino +Erragondapālem +Río Grande +Aurād Shāhjahāni +Ayaviri +Pantanaw +Sesheke +Bhitarwār +Regente Feijó +Kherālu +Cahors +Midway +La Trinidad +Rosas +Mattigiri +Alblasserdam +Ālampālaiyam +Dorchester +Teonthar +Arbutus +Giengen an der Brenz +Karukachal +Aquidabã +Portland +Kürten +Del Carmen +Toprakkale +Mayfield Heights +Farakka +Morristown +Turmalina +Pujehun +Belmonte +Holzminden +Bela +Leinefelde +Villa Verde +Vettikkavala +Rivière-du-Loup +Huanímaro +Senec +Halewood +Comrat +Plainview +Archena +Pallazzolo sull’Oglio +Barkly West +Nykøbing Falster +Culpeper +Gálvez +Nkoteng +Westminster +Celorico de Basto +Biləsuvar +Lubań +Kulpahār +Montalto Uffugo +Buckhall +Satsuma +Oberkirch +La Crescenta-Montrose +Medina del Campo +Villa Hidalgo +Agoura Hills +Kwinana +Denderleeuw +Piatã +Yabayo +Şiran +Douglas +Lonār +Vīrarājendrapet +Barroso +Mithi +Porto Real do Colégio +Skanderborg +Killingworth +Depālpur +Sidhapa +Naranjos +Ėsanboy +Dhorīmanna +El Ancer +Cabra +Mahaditra +Temacine +Horwich +Mont-Saint-Aignan +Llallagua +Schmalkalden +Banī Murr +Brezno +Schroeder +Rosedale +Ban Wat Sala Daeng +Casa de Oro-Mount Helix +Nguékhokh +Mesra +Durbat +Pita +Kārtikapalli +Guding +Shakhunya +Diguapo +Buxton +Ferentino +Lohur +Meadowbrook +Bülach +Teopisca +Kannod +Alagir +Kafr Lāhā +Makoua +Castillo de Teayo +Mutuípe +São Joaquim do Monte +Tettnang +Bhopatpur +Whitehall +Harvey +Yumbel +Tolú Viejo +Atescatempa +Port Colborne +Beaune +Bubanza +Hakui +La Esmeralda +Takanabe +Albany +Rolla +Atarfe +Cittadella +Howard +Sidi Merouane +Altoona +Kissa +Oderzo +Jiangjiehe +Dumbrăviţa +Tsotsin-Yurt +Krasnohrad +Pertuis +Etzatlán +Calceta +Presidente Getúlio +Kandakkadava +Rio Formoso +Gorāya +Port Angeles +Tupanciretã +Forest Park +Piove di Sacco +El Ksiba +Bispham +Indwe +Kfar Kiddé +El Bâzoûrîyé +Slobozia +Ampasimena +Mandrosohasina +Ambalakirajy +Rantabe +Mandiavato +Ambatosoratra +Antsampandrano +Tsararafa +Analapatsy +Tsarabaria +Soavina Antanety +Bekatra Maromiandra +Haka +Arvayheer +Cárdenas +Kuala Lipis +Opuwo +Bulolo +Gahi Mammar +Alfena +Kébémer +Mikumi +Muheza +Sultonobod +Sangīn +Sitrah +Zemio +Bamukumbit +Bamumkumbit +Sanjianxiang +Hampton +Minster +Shri Mahāvīrji +Tadas +Asarganj +Rankhandi +Bhaluhār +Mandi Bamora +Bareja +Sikka +Mūnak +Pīrbahora +Hudli +Bāzidpur Madhaul +Suroth +Sujnipur +Marjampād +Sohāna +Chimthāna +Mangapet +Ankalgi +Kothanūru +Tuminkatti +Nāranda +Sonāimukh +Baragoi +Ciudad Miguel Alemán +’Aïn Mabed +Comăneşti +Garhshankar +Donauwörth +Vasiana +Chaville +Salsomaggiore Terme +Dillingen +Evaz +Emet +Antrim +Trostyanets’ +Chirilagua +Sanrh Majhgawan +Palāshi +Sidi Taibi +San Marcos +Kumīl +’Ali Ben Sliman +Shōnai +Shirahama +Vazante +Shīrūru +Valderrama +Valiyakumāramangalam +Kadakola +Vianen +L’Isle-sur-la-Sorgue +Matheu +Tibagi +Itsoseng +Alpinópolis +Usman +Bafoulabé +Altagracia +Korntal-Münchingen +Benicasim +Kampong Thum +Petrinja +Ampefy +Waldbröl +Hudson +Bhasāwar +Coruche +Plaisance-du-Touch +Ban Tha Kham +Carira +Chilly-Mazarin +Acquaviva delle Fonti +Butare +Tarhzirt +Náchod +Irlam +Mapai +Mulundo +Venmani +Dharampur +Aït Tamlil +Koskāpur +Doany +San Martín de la Vega +Had Zraqtane +Sand Springs +La Mornaghia +Hiddenhausen +Chatiā +Andāl +Sīāhkal +Merrifield +Affton +Ulcinj +Dhanaula +Palliman +Äänekoski +Burnie +Merad +Bichena +Yuancun +Novate Milanese +Senapparetti +Pukhrāyān +Plattsburgh +Saint-Augustin-de-Desmaures +Bressuire +Piedade +Ban Tha Ton +Sakae +Nottuln +El Arenal +Cachoeirinha +Zāwiyat Razīn +Oregon +Hunters Creek +Akureyri +Biddulph +Saynshand +Vevey +Vila Rica +Nejo +Tolosa +Mokena +Crest Hill +Namegawa +Maple Shade +Loudima Poste +Málaga +Kisai +Nizhnyaya Tura +El Paraíso +Gil +Guiré +Andirá +Kūysinjaq +Brandsen +Peshtera +Hamina +Miamisburg +Senica +Dagohoy +Karlapālem +Gherla +Castilho +Homewood +Canelones +El Castillo de La Concepción +Opfikon +Sacile +Ibirama +Calella +Orašje +Comapa +Quepos +Bara Malehra +Līkak +Boussu +Umrāpur +El Mansouria +Gerlingen +Zimatlán de Álvarez +Kavieng +Tepetlixpa +Cartavio +San Miguel +Humanes de Madrid +Ratanpur +Moosburg +Campamento +Gubakha +Kuusankoski +Jagatpur +Cantagalo +Rechaïga +Gebre Guracha +Volokolamsk +Stroud +Peto +Masterton +Sint-Andries +Rio Verde de Mato Grosso +Ellamanda +Khizrpur +Kolavallúr +Makurazaki +Spennymoor +Huntsville +Broadview Heights +Pradópolis +Dillingen +Jāmbai +Atlapexco +Albertville +Lincolnia +Garforth +Alamo +Dokolo +Lisieux +Sleaford +Anghad +Mannūr +Kochas +Kāmākhyānagar +Torgau +Ban Na San +Hirokawa +Remígio +Universal City +Sainte-Marthe-sur-le-Lac +Agryz +Stoke Gifford +Wilnsdorf +Kannānkurichchi +Itaparica +Rubiataba +Nosy Varika +Çekerek +Puttūr +Nyandoma +Araripe +Holden +Poperinge +Time +Mosopa +Smørumnedre +Sparta +Oulad Embarek +Massy +Vulcan +Malabuyoc +Cheung Chau +Brandýs nad Labem-Stará Boleslav +Zhaodianzi +Palm Valley +Kattanam +Puerto Armuelles +Ghatkesar +Lackawanna +Nogales +Selby +Bishunpur +Ennigerloh +Ryūyō +San Miguel +Oulad Fraj +Xingangli +Simonésia +Lake Zurich +Prudnik +Poshkent +Três Barras +Pattamadai +Calvillo +Os +Wittlich +Lloydminster +Opoczno +Itatinga +Assebroek +Thimiri +Beldaur +Marsberg +Cherakhera +Mengdan +Sarstedt +Yonabaru +Issaba +Kanekallu +Evergreen Park +Crisópolis +Moineşti +Bachi-Yurt +Yosano +Claremore +Viadana +Nittedal +Pećinci +West Whiteland +Cuité +Hongshui +Gröbenzell +Shinjō +Saavedra +Castelo do Piauí +Pichidegua +Río Segundo +Payimattam +Mayen +Echizen +Schenefeld +Jnane Bouih +Alga +Oschersleben +Beltsville +Kinston +Bückeburg +Stamford +Rybnoye +Babanūsah +Lehāra +South El Monte +Suárez +Colotlán +San Salvo +Taketa +Jimaguayú +Dicholi +Harmanli +Mokēri +Buşrá ash Shām +Silla +Kātpādi +Kanniyākumāri +Shelbyville +Calatayud +Mougins +Nambūru +Orós +Whitemarsh +Sinacaban +Maḩmūdābād Nemūneh +Hajnówka +Guantiankan +Udalguri +Korsholm +Pastores +Būdipuram +Westmount +Andaingo Gara +Chiconcuac +Pichor +Buriti dos Lopes +Komárom +Silver Spring +Bruz +Durağan +Yuanhucun +Grafton +Chipiona +Karuizawa +Kondūr +Nové Mesto nad Váhom +Villa Elisa +Luénoufla +Dayr Abū Ḩinnis +Davyhulme +Villabate +Monroe +Ahfir +Nilakkottai +Urumita +Hanumana +Rāwah +Jussara +Várpalota +Haiger +Bir Kasdali +Ampère +Forest Park +Atasū +Otjiwarongo +Yahotyn +Bad Aibling +Arvin +Rāmjībanpur +Cherepanovo +Lerdo de Tejada +Okhmalynka +Punta Gorda +Bad Pyrmont +Mayahaura +El Coco +Ilijaš +Veroli +Niiyama +Brigham City +Bondo +Kutina +San Juan Zitlaltepec +Petrila +Diinsoor +Sādpur +Tapauá +Ischia +Russell +Turnu Măgurele +Holtsville +Xapuri +Lieto +Rhede +Lexington +Yershov +Buritirama +Lastra a Signa +Hidalgotitlán +Visaginas +Venadillo +Schilde +Engelskirchen +Liuliang +Les Coteaux +Bourg-lès-Valence +Bequimão +Jājarm +Punjai Puliyampatti +Caraúbas +Erlanger +Burnham-on-Sea +Ituzaingó +Barañáin +Heinola +Caoayan +Abbots Langley +Santa Teresita +Suchiapa +Amorebieta +Aysha +Atchoupa +Wenden +Doujing +Mirfield +Acıgöl +Gallipoli +Ansermanuevo +Ballyfermot +Vallam +Dudinka +Morwa +Rizal +Mogiyon +Töle Bī +Hermosa Beach +Caraí +Thamaga +Huseni +Pathrajolhania +Tīrthahalli +Guapó +Jhalidā +Artik +Beloyarskiy +Mihona +Mārahra +Liuguang +Herceg Novi +Crespo +Bagaces +Artik +Rajaudha +Eersel +Eupen +Candelaria +Ipanema +Pauini +Hoxut +Ibigawa +Goole +Bedelē +Parsāhi Sirsia +Arese +Fukude +Yusufeli +Alaca +Kalayapuram +Āmta +Telavi +Mahināwān +Tibba +East Massapequa +Akanavāritota +Āsasa +West Mifflin +Castellana Grotte +Garsfontein +Ait Bousarane +Ait Bouziyane +São Vicente Ferrer +Sjöbo +Sangasso +Hushihacun +Terek +Los Álamos +El Arahal +Teminabuan +Oulad Salmane +Katangi +Callosa de Segura +San Juan de Dios +Hoveyzeh +Deneysville +Nīlgiri +Rhar el Melah +Capinota +Orinda +Albolote +Cugir +Eschwege +Santiago +Saray +Borna +Jesús María +Mölln +Müllheim +Jagalūr +Krishnapur +Orvieto +Lodeynoye Pole +Hawthorne +Sondiha +Gogogogo +Qorasuv +Polonuevo +Setlagode +Rāmchandrapur +Teghra English +Angleton +Narsāpur +Cervantes +Clifton +Marihatag +Kempston +Hechingen +Kemi +Ronchin +Lake Forest +Sabaudia +Madisonville +Kos +Salmon Arm +Cardonal +Adel +Tublay +Someren +Santiago Texacuangos +Ban Thap Kwang +Bartow +Gaeta +Lynn Haven +Kemisē +Liversedge +Siruma +Nova Granada +Kārttigappalli +Vence +Kirkland +Ardon +Dorou +Sāmalāpuram +Salangaippālaiyam +Abrīsham +Xihuangcun +Weil der Stadt +Sulzbach-Rosenberg +Zachary +Los Bajos +Frutillar Alto +Sipoo +Sweetwater +Yandrapalle +Dalanzadgad +Pahsara +Kantharalak +Pariquera-Açu +Kushiro +Bad Driburg +Cabildo +Alto Araguaia +Rayevskiy +Arbaoun +Annaberg-Buchholz +Sartell +Barro +Törökszentmiklós +Englewood +Kalghatgi +Shikrapur +Charef +Saint-Lô +Manjathala +Les Côteaux +Casarano +Prunedale +Middelkerke +Heishanzuicun +Seabrook +Abhayāpuri +Kākdwīp +Boone +Giovinazzo +Canto do Buriti +Burghausen +Tajerouine +Kloten +Kitatajima +Gōdo +Saint-Fons +Pastrana +Juquiá +Rifādpur +Mukaiengaru +Saré-Yamou +Buruanga +Paranapanema +Heishuikeng +Arbatache +Balta +Prichard +Nauen +Kuttyādi +Great Linford +Brookfield +Dudu +Jämsä +Napindan +Ban Bueng +Ban Patong +Mahājerān-e Kamar +Oruvadālkotta +Moulins +José Cardel +Tadian +Wassenberg +Allanridge +Pugo +San Roque +Tangbian +Wolfratshausen +Sorada +Weesp +Bieruń +Cajolá +Corner Brook +Dashtigulho +Pułtusk +Presidente Médici +Bholsar +Ban Fang Tuen +Perūru +Tevāram +Saint-Dié-des-Vosges +Kamiichi +Warrensburg +Faversham +Stillwater +New Glasgow +Moaña +Naples +Palera +Panniperumthalai +Mâncio Lima +San Antonio Huista +Assamannur +Pohādi +Tash-Kömür +Pottanikād +Heusenstamm +Dukinfield +Jirkov +Monte Azul Paulista +Virovitica +Dorval +Qandala +Sélestat +Marechal Taumaturgo +Espiritu +Kapangan +Sirvār +Casamassima +Sukumo +Twinsburg +Ganyesa +Sudley +Dolynska +Seminole +Douar Ait Sidi Daoud +Bouanri +Bhojpur Jadīd +Cutlerville +Iiyama +Wanluan +Kirovgrad +Rovato +Beaconsfield +Tsumeb +Pitimbu +Sint-Gillis-Waas +Mapastepec +Marchena +Peterhead +Ashland +Indargarh +Haldensleben +Highland +Hermiston +Maitland +Alzey +El Viso del Alcor +Kudra +Bakal +Ferndale +Cassano d’Adda +Tillabéri +Balintawak +Bābai +Cabusao +Sherrelwood +Henichesk +Valle Vista +Tonantins +Realeza +Águas Formosas +Gilching +Dračevo +Soure +Söğüt +Araçoiaba +Quartier Morin +Kamwenge +Sirnia +Groveland +Holalkere +Diadi +Al Brouj +Ballitoville +Aberdeen +Roosevelt +Garot +Kassama +Monção +Ghosāi +Magitang +Sühbaatar +Pescia +Gázi +Orange +Vigna di Valle +Atok +Springboro +Amboanana +Khonj +Harrison +Eruvatti +Chetichēri +Kabūdarāhang +Tingloy +Malgrat de Mar +Brie-Comte-Robert +Bandar-e Kong +Ecclesall +Ābomsa +Jakobstad +Zola Predosa +Texcaltitlán +Puerto Morelos +Jiabong +Lumphat +Ystrad Mynach +Andrychów +Lebedyan +Rangra +Kale +Jamiltepec +Devgadh Bāriya +Attapu +Aradíppou +Sligo +Lebbeke +Rurrenabaque +La Corredoría +Binka +Khamaria +Gomīshān +Ermita +Schkeuditz +Venceslau Brás +Ranst +Alcoy +Zerong +Bad Tölz +Dumra +Pokaran +Sibila +Vera +Oued el Abtal +Sirpur +Maumelle +West Manchester +Badger +Maghar +Tinton Falls +La Crau +Gioia Tauro +Norton +Krasnogorskiy +La Paz +Schönefeld +Ghogardīha +Rosedale +Deerfield +Jaicós +Sidi Redouane +Naantali +Meise +Koropí +Chiari +Horseheads +Ambatomirahavavy +Essen +Blankenburg +Seesen +Izra +Gülağaç +Raita +Pauktaw +Vila Real de Santo António +Sömmerda +Tiruvādi +Cocos +Cedar Mill +Papanduva +Guaranésia +Durango +Senguio +Pitt Meadows +Biswanath Chariali +Gerze +Reinach +Ad Dir‘īyah +Schwalmtal +Fukusaki +Diamante +Sirālkoppa +Schneverdingen +Pidhorodne +Billinghurst +Kodumba +Diepenbeek +Passagem Franca +Iramala +Pljevlja +Ampanety +Nikolayevsk-na-Amure +Trzebinia +Zaouïa Aït Ishak +Eitorf +Ainan +Vargem Alta +Contenda +La Ligua +Shemonaīkha +Knightdale +Kufstein +Imouzzer Kandar +Chennīrkara +Māvinpalli +Ban Mon Pin +Ashkezar +Shimokizukuri +Central Point +Midlothian +Baden +San Teodoro +Martín Coronado +Morauna +Shāhgarh +La Unión +Sorgues +Cerqueira César +Cacahoatán +Uwchlan +Montemurlo +Gobabis +White Oak +Bieruń Stary +Bella Unión +Shāhpur +Harpur +Lumberton +Liberal +Stord +Nīlambūr +Lebach +Taka +Frimley +Chautāpal +Lumbayanague +Anguillara Sabazia +Haslett +Paillaco +Molesey +Tavares +Niagara-on-the-Lake +Saugerties +Mumbwa +Prestatyn +Butiá +Pindobaçu +Villefontaine +Cherán +Tazishan +Winchester +Menzel Abderhaman +Aizumisato +Douar Toulal +Rizal +Cirencester +Karlshamn +Ban Bueng Phra +Calne +Perumkulam +Mahisi +Cocoa +Simri +Nalua +Dixon +Ixtlahuacán del Río +Scituate +São José de Piranhas +Sant’Antonio Abate +Pruzhany +Bouansa +Cunco +Malanday +Chinna Ganjām +Murugampālaiyam +La Eliana +Sabanitas +Gentilly +Hindoria +Tanbaichichō +Zverevo +Žatec +Monkseaton +Préveza +Pūranpur +Svay Pak +Sylvania +Itaosy +Cumberland +Lejiangxiang +Cimarron Hills +Guaymango +Warwick +Dharmsāla +Bukungu +Kovūr +Proletarsk +Taquarana +Winthrop +Onega +Royan +Tirmitine +Telpaneca +Rahata +Point Pleasant +Cardedeu +Sarauli +Wervik +Monor +Morlanwelz-Mariemont +Cotija de la Paz +Norwood +Dankov +North Valley Stream +West Chester +Caowotan +Ferguson +Dadeldhurā +Santa Ana Jilotzingo +Sahline +Spoltore +Mānjha +Fond des Blancs +Fayetteville +Fate +El Bolsón +Akune +Água Branca +Gautier +Quaregnon +Pfullingen +Mao +Lagoa de Itaenga +Ellensburg +Zwijndrecht +Befandriana Atsimo +Anjahabe +Ambalanirana +Ambatomena +Mitsinjo +Belamoty +Vohimasy +Ampataka +Ifanirea +Tanambe +Ambodinonoka +Miarinavaratra +Ambatosia +Mahazoarivo +Manantenina +Bezaha +Ranomena +Nootdorp +Bajo Boquete +Shovot +Dūrpalli +Vitré +Greenfield +Cafelândia +Vista Hermosa de Negrete +Kouloum +Haukipudas +Sārangpur +Druten +Chapada dos Guimarães +Bīrpur +Heredia +Kosatarosh +Barão do Grajaú +Nārapala +Sukhāsan +Scorzè +Radeberg +Laukaa +Kāveripatnam +Onex +Ait Ben Daoudi +Caconde +Boiro +Siripur +Koliakkod +Weirton +La Nucía +Sarso +Burscheid +Acqui Terme +Varidhanam +Ban Na Yang +Colwood +Konstantynów Łódzki +Chichiriviche +Southbourne +El Rosal +Saint Peter Port +Carei +Sarsāwa +Krommenie +Utebo +Makronia +Ansonia +San Ignacio Cerro Gordo +Chilkūru +Križevci +Fox Crossing +Pitseng +Atacames +Pinole +Kolbermoor +El Malah +San Gabriel +Tak Bai +Guru Har Sahāi +Motomachi +Beni Zouli +Aikaranad +Augusta +Neviges +Casalgrande +Malacky +Lakhnādon +Harrisburg +Ayvacık +Middlesex Centre +Sudbury +Desavilakku +Monserrato +Bad Berleburg +Eiheiji +Vera +Nakasi +Elamāttūr +Zuidhorn +Pirkkala +Āb Pakhsh +Prenzlau +Ismailpur +Kōtekāra +Ranbīrsinghpura +Tadó +Lagoa Formosa +Franklin Farm +Sironko +Beshkent Shahri +Shirin +Fakfak +Mo i Rana +Matteson +Utazu +Horsforth +Somandepalle +Nayāgarh +Bad Reichenhall +Gātāda +Garðabær +Madalag +Ribeira Grande +Amares +Nanjanād +San Carlos Park +Guareí +Ezhamkulam +Sugaon +Kasterlee +Náousa +Formoso do Araguaia +Jaggampeta +Pozzallo +Hūn +Lagawe +Korsimoro +Ấp Khánh Hưng +Signa +Malazgirt +Ajacuba +Massaguet +Kalmthout +Lake Shore +Coronado +Groesbeek +Hemmingen +Altamira +Kabbūr +Vīrapāndi +Vārapatti +Arita +Alsip +San Sebastián de Buenavista +Araç +Kottakota +Duero +Conceição +Mont-Saint-Hilaire +Naaldwijk +Traiskirchen +Kovylkino +Cinco Ranch +Weißenburg +Larne +Malimono +Horizontina +North Myrtle Beach +Anzoátegui +Fihaonana +Guachucal +Kara-Köl +Oirschot +Varzelândia +Santa Fe Springs +Zahana +Dukli +Ben ’Aknoûn +Ronkonkoma +Chełmno +Nakhl-e Taqī +Onalaska +Acri +Tradate +Lakeway +Kalach +Cusano Milanino +Bradfordville +Barcelos +Qitai +Dzüünharaa +Alzenau in Unterfranken +Las Terrenas +Edam +Nunuñgan +Khetia +Somerset East +São José da Coroa Grande +Republic +Camarate +Berkovitsa +Odžak +Carlos Spegazzini +Brownwood +Milton +Villa Nueva +Bad Dürkheim +Sebaste +Ascención de Guarayos +Lancing +Sabalpur +Marktoberdorf +Vélez +Purushottampur +Sheridan +Syosset +Kalabahi +Udumanthala +Huehuetla +Pastos Bons +Sortavala +Shimomura +Abdurahmoni Jomí +Dŭstí +Hazorasp +Diallassagou +Ware +Xinhua +Hala +Starobilsk +East St. Louis +Marshfield +Owego +Bremervörde +Arilje +Ibirataia +Tocaima +Lukovit +Oulmes +Taft +Jwaneng +Wisconsin Rapids +Terra Santa +Itajuípe +Sompeta +Ban Bo Haeo +Heerde +Springfield +Antotohazo +Lgov +Pastavy +Chowchilla +Camrose +Kabala +Kralupy nad Vltavou +Złotów +Marānchi +Presidente Olegário +Villaquilambre +Doñihue +Novyy Oskol +Karia Ba Mohamed +Villa Rica +Ayyāmpettai +Durant +Birnagar +Bergeijk +Retiro +Rutherford +Hopkins +San Juan Ermita +Nanthankulam +Bonneuil-sur-Marne +Kurumbapālaiyam +Hopkinton +Koporo-Kénié-na +Kiliia +Bayshore Gardens +Thomasville +Ngathainggyaung +Palestine +Harihans +Altônia +Wallingford Center +Melmuri +Riom +Los Lagos +San Rafael La Independencia +Adliswil +Schlieren +Sendamaram +Paglat +Rufino +Lansdale +Gunri +Görele +Alfeld +Phoenixville +Ozorków +Country Walk +South San Jose Hills +Tlalnelhuayocan +Ključ +Hem +Snina +Altus +Ottawa +Dhāriwāl +Aroeiras +Salamina +Djidian +Vadakkangara +Choghādak +Peduasi +Aburi +Qo’shko’pir +Abong Mbang +Lupeni +Tyèlè +Tsivory +Balilihan +Çerkeş +Qaryat al Qī‘ān +Valle +Nefas Mewch’a +Perunturuttu +Andéranboukan +Majdanpek +Areia Branca +Surovikino +Middelburg +Ceglie Messapico +Zapote +Creve Coeur +Leicester +Morales +Martinsburg +Round Lake +Kinzau-Vuete +Doura +Blooming Grove +Togba +Afrânio +Bensenville +São Raimundo das Mangabeiras +Mansa Konko +Trinity +Sonhauli +Lakhna +Almel +Rishivandiyam +Lelydorp +Meerssen +Jasidih +Broughty Ferry +Şammā +Seagoville +Kavalerovo +Siyəzən +Maimón +Soalkuchi +Molakālumuru +Centerton +Santa Catarina Juquila +Selwyn +Cambridge +Elumalai +San Julián +St. Michael +Lauālāgaon +Carnaíba +Kangazha +Shimanovsk +Telwa +Adria +Castaic +Aldama +Futog +Szamotuły +Basatpur +Mūkkanūr +Enkhuizen +Ashland +Bonanza +Bergneustadt +Kīlmangalam +Mānikkal +Le Puy-en-Velay +Lede +Mossendjo +Ribeirão Branco +Chākūr +Manakayi +Ubaíra +Urucará +Sanharó +Tarifa +Ballincollig +Londerzeel +Seara +Knik-Fairview +Vanino +Uetersen +Medjedel +Carlos Chagas +Volketswil +Tillsonburg +Eastwood +Fairwood +Tata +Engenheiro Coelho +Maurepas +Bellwood +East Dereham +Onga +Mullach Íde +Stannington +Reriutaba +Concord +Corinto +Ajnāla +Leposaviq +Saylac +Toshloq +São Gabriel +San Miguel +Miyatoko +Timoktene +Los Alcázares +Renningen +Kalugumalai +Jbabra +Antombana +Felanitx +Five Forks +Ploemeur +Preakness +Majhua +Valavanūr +Al Jawf +Lebanon +Anna +Hatfield +Jalakandāpuram +San Mauro Torinese +Colonia +Skara +Windham +Shika +Rhynern +Tamri +Mohana +Liuma +Clive +Liancourt +Narangba +Meylan +Kasongan +Elizabeth City +Nederland +Bougtob +Tomaszów Lubelski +Crimmitschau +Paluan +San Sebastián Salitrillo +Miracema do Tocantins +Ban Mae Hia Nai +Erumakkuzhi +Zhosaly +Kingsland +Pizarro +Tarrafal +Bhadaur +Shenandoah +Ebbw Vale +Garuva +Boom +Mingjiujie +Leteri +Wheatfield +Morros +Zākīyah +Miracatu +Priozërsk +Lessines +Matipó +Kumbhrāj +Mennzel Bou Zelfa +Mellacheruvu +Berea +Shaogang +Glinde +Ban Mae Kha Tai +Ferguson +Sebt Gzoula +Orhaneli +Hochheim am Main +Kothia +Morro da Fumaça +Kulu +Arcata +Colina +Goyty +Baena +San Diego +Stabroek +Santander +Guadix +Newton +Parole +Sokołów Podlaski +Ba +Rohār +Melville +Oxon Hill +Asunción Nochixtlán +Thalwil +Lodhwe +Tsetserleg +Tessenderlo +Guanzhai +Santa Cruz Zenzontepec +Bilar +Iselin +Brook Park +Härnösand +Galhinna +Huércal de Almería +St. Marys +Tādikonda +Madre de Deus +Anicuns +Tatsuno +Pālaiyampatti +Hässleholm +Buikwe +Appley Bridge +Goffstown +Rawmarsh +Ghedi +Kivsharivka +Potirendaba +Bitkine +Rhaude +Vlotho +Karcag +Misato +Alton +Kalynivka +Chubek +San Lorenzo de El Escorial +Busovača +Schwalmstadt +Staines-upon-Thames +Unquillo +Zagarolo +Piñas +Laurel +San Celoni +Sirvel +Fushë-Krujë +Kherāmeh +Ajaigarh +Foxborough +Tinoc +Giszowiec +Boerne +Vellmar +Tirmalgiri +Qaşr-e Shīrīn +Sharon +Bedlington +Sacavém +Hoppegarten +Éragny +Alcântara +Taft +La Marque +Çatak +Castel Maggiore +Kotanchēri +Kunitomi +Nara +Pio IX +Kannapuram +Oskū +Hessisch Oldendorf +Lagoa da Canoa +Od +Kāriyāpatti +Mill Hill +Matelândia +Cognac +Ngolonianasso +Shimokodanaka +Narlıca +Coalcomán de Vázquez Pallares +Haaltert +Kassaro +Jucuapa +Achocalla +Bakhmach +Princeton +Bönen +Hellemmes-Lille +Chouafa +Zhangshicun +Choi Hung +Arroyo Grande +Astrea +Albert Lea +Cullman +Baliangao +Yur’yev-Pol’skiy +Lahnstein +Dumjor +Ponedera +Johnstown +Orestiáda +Dover +Juvisy-sur-Orge +New River +Abdulino +Mukkūdal +Trenton +Loreto +Vechelde +Montrouis +Pomáz +Castelfidardo +Kreminna +Svatove +San Vicente Pacaya +Ocean Springs +Fatick +Kronberg +Devadānappatti +Basse Santa Su +Unhel +Winder +Hardinxveld-Giessendam +Amvrosiivka +Foum el Anser +Belo Campo +Tallmadge +San Blas Atempa +Tilothu +Nīār +Konz +Nordestina +Fairmont +Korsun-Shevchenkivskyi +Quitandinha +Kumārapuram +Santa Marinella +Langen +Srikhanda +North Babylon +Inhapi +Crowley +North Bay Shore +Aragarças +Hārij +Tinajeros +Longjia +Rio Maria +Bhawānīpur +Itapororoca +Hampton +Sanpaicun +Bracciano +East Goshen +Straşeni +Walcourt +Greene +Cesário Lange +Sigmaringen +Franklin Park +Cuautitlán +Clonmel +Qatlūpur +Illertissen +Mumford +Assa +Filomeno Mata +Chennevières-sur-Marne +Agarpur +Nytva +Baependi +Santiago Ixcuintla +São Benedito do Rio Preto +El Tránsito +Olten +Écully +Noci +Chhoti Sādri +Krasyliv +Newburyport +Campo de la Cruz +Stekene +San Rafael Pie de la Cuesta +Cagdianao +Wörth am Rhein +Louviers +Saladas +Ammāpettai +Almondbury +Redland +Nāri Bhadaun +Maryland City +Cortes +Buenavista +Apastepeque +Sendārappatti +Capoeiras +Mineros +Lake Ronkonkoma +Mirante do Paranapanema +Daryābād +Lamrasla +Xintian +West Haven +Picuí +Anandpur +Rizal +Novopavlovka +Cran-Gévrier +Pembroke +Memuro-minami +Sandaré +Shaoyu +Nuuk +Skýdra +Calverton +Karuppur +Tha Bo +Taió +Melzo +San José Tenango +Lora del Río +Carrières-sous-Poissy +Talata-Volonondry +Riachão do Dantas +Pully +Pontalina +Morretes +Gerd Farāmarz Shāhedīyeh +Abrandābād-e Shāhedīyeh +Kitagata +Nossombougou +Nehbandān +Mangualde +Belém de São Francisco +Lenoir +Do’stlik Shahri +Hadleigh +Beloozërskiy +Sint-Genesius-Rode +Ommen +Volochysk +Püttlingen +Bingley +Rutigliano +Aveiro +Taung +Slyudyanka +Szigethalom +Sakouéba +Navraftor +Şenkaya +Panama City Beach +Pinecrest +Parchim +Palos Hills +Fomboni +Santa Fe +Kadaň +Paraibano +Rāmpur +Kristiansund +Mannara +Rosendaël +Phelan +Aït Bouchta +Bni Bouayach +Tárrega +Kasba Maker +Somerset +Concord +Burlington +Paraibuna +Yupiltepeque +Siloe +Bideford +Luz +Ban Mai +Monte Cristo +Regensdorf +Yoichi +Mūlanūr +Shuangluan +South Orange Village +Bagnols-sur-Cèze +Moissy-Cramayel +Staveley +Asakapalle +Mathurāpur +Pisz +Shaw +Amsterdam +Savaştepe +Nārāyankher +Monforte de Lemos +Sivandipuram +Santa Iria da Azóia +Manchester +Carrigaline +Comodoro +Kānp +Ikniwn +Calera de Tango +Łęczna +Centralia +Fulwood +Antanambao +Ocean Acres +Eski-Nookat +Kingstowne +Kottukal +Júlio de Castilhos +Manga +Budrio +Galaat el Andeless +Mahiari +Bad Segeberg +Kurakhove +Baixa Grande +El Astillero +Pieksämäki +Bronte +Pijijiapan +Zielonka +Ţāqah +Banī Ḩasan ash Shurūq +Nazaré Paulista +Farias Brito +Bettioua +Taku +Gameleira +Sipacate +Stillorgan +Tirunāgeswaram +Sāhna +Filadelfia +Sikhio +Blindio +Serrita +Dagana +Amaraji +Kudatini +Budhni +Kishi +Shaying +Końskie +Santiago de María +Bet She’an +North Massapequa +Kiangara +Ranquitte +Griffith +Palamós +San Sebastián Tutla +Udaipura +Pelham +Plymouth +Washington +Bedēsa +Sarvestān +Taufkirchen +‘Anbarābād +Rikuzen-Takata +Oyama +Concord +Solonópole +Dapi +Oconomowoc +Quibaxi +Aldo Bonzi +Mohiuddinnagar +Steubenville +South Fayette +Warminster +Gossau +Sycamore +Vredendal +Logansport +Bhadrapur +Meishan +Tummalapenta +Redhill +Laives +Åsane +Kottapalle +Corbetta +Petawawa +Colonial Heights +Xochiatipan de Castillo +Zinvié +Starodub +Qishe +Guayama +El Achir +Heckmondwike +Bouarouss +Lavāsān +Ingá +Trujillo +Piatykhatky +Čitluk +Keskin +Heusweiler +Minamishibetsuchō +Dobrush +Pinabacdao +Yoshimi +Schofield Barracks +Palanga +Andalucía +Una +Tominian +Gangwuzhen +Portsmouth +Svilengrad +Beerse +San Pedro +Sangre Grande +Canby +Dumri +Jora Khurd +New Castle +Bourbonnais +Menasha +Łomianki +Ouankoro +Louny +Ciriè +Pottanūr +Feyẕābād +Terra Roxa d’Oeste +Barhampur +Ambiula +Raisāri +Coronda +Poulton le Fylde +Rāmpur +Convención +Polohy +Gāndarbal +Fâches-Thumesnil +Mānikpur +McAlester +Caldas de Montbuy +Pahārpur +Shorewood +Campos Belos +Mure +Niles +White Settlement +Bình Minh +Varjota +Fort Carson +Maoussa +Hybla Valley +Pavullo nel Frignano +San Vito dei Normanni +Großenhain +Borgo San Lorenzo +Kikube +Berkhampstead +Cofradía +Belterra +Dolný Kubín +Southeast +Tora +Chapelle +Antonina +General Juan Madariaga +Madalena +Heguri +Cudahy +Calenzano +Barīkot +Ghouazi +Água Azul do Norte +Kopa +East Riverdale +Arājpur +Wandsworth +Mayorga +Bobingen +Kęty +Ricaurte +Manyas +Pedra Preta +São Tiago de Custoias +Oroszlány +Fuente-Álamo de Murcia +Luganville +Ismaning +Tottiyam +Scarsdale +Bīdestān +Kottá Kalidindi +Santa Ana +Sidi Allal Tazi +Tlacoachistlahuaca +Ambāla +Libjo +Myślenice +San Vicente +Bādkulla +Spanish Lake +Middelharnis +Annakunnu +Vilavūr +Buadiposo-Buntong +Nigrán +Chodzież +Taperoá +Timri +Whitestown +Carmelo +Sunbury +Mwanza +Abangaritos +Frankenberg +Bryn Mawr-Skyway +Keszthely +Târgu Neamţ +Cristinápolis +San Pedro +Porto Novo +Schwechat +Destelbergen +Gidi +Valenza +Piossasco +Zuhres +Matigou +Koratagere +Lakshmaneswaram +Tordera +Ulao +Banbalah +Buchen in Odenwald +Tillmans Corner +Fécamp +Itaberá +Jasien +Fundão +Dumont +Campina Verde +Toyono +Marcos +Marsciano +Rwamagana +Nipomo +Soisy-sous-Montmorency +Biro +Ashtabula +Sakri +Terrell +Dbaïyé +Antsampanimahazo +Antanambe +Manampatrana +Alakamisy +Maroteza +Bemahatazana-Belobaka +Vohilengo +Amboahangibe +Mahazoma +Ambongo +Alatsinainy Ialamarina +Analaiva +Maroaloka +Belobaka +Ankofa +Matsakabanja +Ambovombe Afovoany +Tsianisiha +Beantake +Behenjy +Tranoroa +Qal‘ah-ye Zāl +General Deheza +Al Hamalah +Ad Dirāz +Doruma +Yoboki +Mickleover +Gondar +Tankāra +Ghogaon +Mahraurh +Tall Qaşab +Athār +Gorham +Kamienna Góra +Batié +Kolonodale +Serra Preta +Bombon +Tyāgadurgam +Aci Sant’Antonio +Cameron Park +Wantagh +Stony Plain +Neftegorsk +Oak Bay +Hijuelas +San Cristóbal Cucho +Ariccia +Mount Washington +Sevierville +Kosvik +Lagoa do Carro +Bourne +Cuquío +Sivagiri +Hranice +Halstenbek +Sukhsena +Rāmpur Tilak +Rājmahal +Terenure +Avtury +Kharabali +Konaklı +Dīzīcheh +Antenor Navarro +Melegnano +North Grenville +Sapeaçu +Santo Anastácio +Kegalle +Chaita +Souba +Payyannūr +Cohoes +Ptuj +Towamencin +Jadcherla +Torit +Bodegraven +Almeria +Ranzan +Koula +Bucay +Ben Daoud +Erumāpālaiyam +Barvāla +Haren +Blythe +Mūkondapalli +Doylestown +Flora +Loyalist +Stilfontein +Yeddumailāram +Falköping +Bischheim +Miyauchi +Madeley +Jiadong +Agliana +Kuchinda +Quipapá +Tres de Mayo +Manthani +Burke Centre +Cortes +Texistepeque +Ammon +Gitagum +Palmi +Olho d’Água das Cunhãs +Coltauco +Kapsabet +Shirosato +Alice +Unión de San Antonio +Roulia +Deux-Montagnes +Ituaçu +Narni +Pelaya +Pornic +Mabéhiri +Châteauneuf-les-Martigues +Khokha +Natchitoches +Franconia +Wadowice +Glenvar Heights +Manambaro +Saatlı +Nyborg +Kostrzyn nad Odrą +Mebane +Não-Me-Toque +Garches +Nguigmi +Filadélfia +Fátima +Tiffin +Zapotlán del Rey +Sint-Oedenrode +Murambi +Silvāni +Pudunagaram +Anoka +Puquio +Bafanji +Sanghera +Veldurti +Çal +Mount Eliza +Yuanquan +Munguía +Cary +Guapiara +Steiner Ranch +Gibsonton +Carmópolis de Minas +Gaoniang +Santa Cruz Naranjo +Vilāttikulam +Selma +El Bordj +Ramsbottom +Tolentino +Patar +Bay City +Segué +Vordingborg +Sa Kaeo +Pimentel +Chortiátis +Martūru +Pachmīr +Marwa +Farmington +San Kamphaeng +Cenoví +Manilva +Százhalombatta +Loay +Hidrolândia +Ourém +Forst (Lausitz) +Colne +Sangenjo +Carvin +Vissannapeta +Aci Castello +Ambohitralanana +Bekalta +Shirāli +Montanha +Pontarlier +Pôrto Grande +Édessa +Portsmouth +Sing Buri +Sidi Kada +Pataskala +Marabut +Savignano sul Rubicone +Itapiúna +Kristinehamn +Harper +San Bartolo Tutotepec +Ribera +Pelahiivka +Timbedgha +Policoro +Parede +Goshaingaon +Águas de Lindóia +Bredene +Werdohl +Paks +Yakacık +Bhīkhi +Arcore +Villa Corona +Mengjiacun +Las Heras +São Luís Gonzaga do Maranhão +Carate Brianza +Brownsville +Blaj +Freilassing +Alton +Kukmor +Mazapil +Itanhém +Oberasbach +Pariharpur +Muttenz +Steinbach +Salgar +Seyah Cheshmeh +Santa Fe +Gazantarak +Fontaine-l’Évêque +Feilding +Dhānsāria +Sobinka +Herenfa +Bagahi +Hendaye +Vennandūr +San Rafael +Jovellar +Charleston +Salaspils +Delran +Jucurutu +Ponmana +Shichigahama +Santa Elena +Godfrey +Amīnpur +San Rafael del Norte +Portchester +Idylwood +Berlín +Mankara +Mooirivier +Arakkapādi +Cordenons +Anda +Khaşab +Monthey +Dhamaun +Freudenberg +North Canton +Varvarin +Łańcut +Prachin Buri +Fanipal’ +Tall ‘Aran +Visé +Somma Lombardo +Ash Shaykhān +Agua Blanca +Lake Butler +Klaeng +Weilerswist +Willimantic +Cairu +Auerbach +Or ‘Aqiva +Troy +Lunéville +Schrobenhausen +Māḩiş +Cahokia Heights +Ostermundigen +Arco +North Aurora +Floirac +McKeesport +Eslöv +Dergaon +Douar Oulad Mbarek +Kuttālam +Chitipa +Köping +Toba +Saint-Colomban +Dianópolis +Dniprorudne +Reni +Ekazhevo +Manzanares +Kodiyēri +Nīkshahr +Liulin +Laukāha +Sapucaia +Erjie +Bad Münder am Deister +Estancia Pozo Colorado +Susner +Békés +Herzele +Rockland +Cesson-Sévigné +Ban Mae Ngon Khilek +Pirapemas +Domodossola +Hlybokaye +Live Oak +Broken Hill +Dialafara +Zalţan +Ashton +Ngora +Major Isidoro +Kharhiāl +Miramichi +Kiangan +Vettavalam +Vrede +Miaojiaping +Kalyānpur Bamaiya +Kreuzau +Flórina +Diepholz +Maripād +Palanan +Harwich +East Lampeter +Dourbali +Panórama +Bechem +Dugo Selo +Dumri +Mairi +Avion +Webster +Hope Mills +Levin +Torredembarra +South Ockendon +Moreton +Pūlla +Kitahiroshima +Southbridge +Tufanbeyli +Beni Fouda +Whittlesey +Anacortes +Duffel +Brignoles +Minquan +Biliran +El Omaria +Bad Säckingen +Jaguaripe +Vandalūr +Brenham +Yumurtalık +Pinehurst +Tohoué +Pianoro +Golitsyno +Tunapuna +Nidda +Monzón +Fern Down +Balaguer +Tábara Arriba +Fornaka +Pirapora do Bom Jesus +Kappiyara +Fanandrana +Mount Holly +Bexbach +Arsanjān +Camano +San Jose +Valluvandad +Vettikattiri +Stowbtsy +Gretna +Nelidovo +São Miguel do Tapuio +Abaré +Dingjiagouxiang +Damme +Stuart +Jacksonville +Terenos +El Carmen de Chucurí +Cerrillos +Otrokovice +Cariré +Pupri +Mānullahpatti +Siloam Springs +Woodmere +Saint-Maximin-la-Sainte-Baume +Inza +Stegen +Oborniki +Ban Mae Ka Hua Thung +Daimiel +Asarcık +Pinili +La Primavera +Limay +Eastlake +Gostynin +Lobogo +Longjia +Nakło nad Notecią +Karacasu +Yayas de Viajama +Colbún +Benito Juárez +São Francisco de Assis +Vittal +Ambolomadinika +Sokółka +San Jorge +İskilip +Sam Phran +Totoró +Omallūr +Veliko Gradište +Ubaitaba +Zhongguyue +Restrepo +Barrhead +Capua +Chedaopo +Şavşat +Tijucas do Sul +El Dorado +Storrs +Bel Air +Polyarnyy +Lousã +New Philadelphia +Pirayú +Siderno Marina +Raghunāthpur +Bakouma +Abejorral +Amacuzac +San Alejo +Herve +Boqueirão +Sigus +Arauco +Yondó +Raghunāthpur +Neder-Over-Heembeek +Bad Wildungen +Palau +Para +Marcon +Laatatra +Srbac +Manganj +Putte +Zamora +Santos Reyes Nopala +Inkollu +Eldama Ravine +Ajā +Maesteg +Saint Andrews +Chakkuvarakal +San Jorge +Žiar nad Hronom +Muḩradah +Quanzhang +Peruvanthānam +Rāyappanpatti +Zephyrhills +Krishnapuram +Cortland +Compostela +Água Branca +Aït Majdane +Trossingen +Cerca Carvajal +Boissy-Saint-Léger +Catarman +Alcochete +Rožňava +Sagrada Familia +Wayne +Piritiba +Tak +Ban Thum +Sirari +Barhampur +Kambla +Kisoro +Bistāria +Arroio Grande +Khutāha +Lemont +Clemson +Gömeç +Pālaiyam +Mawkanin +Son en Breugel +Penn +Le Pontet +Çameli +Glencoe +Punnayūr +Colchester +Morarano-Gara +Waalre +Lattes +Aliyābād +Takoma Park +Koriāpatti +Swellendam +Yeniseysk +Al ‘Ashārah +Scarborough +Etacheri +Sona +Mitake +Kempele +Vāyalpād +Oak Grove +Esperanza +Shangjing +Shanglingcun +Harinākunda +Duijiang +Kirksville +Esquimalt +Polignano a Mare +Rio Bananal +Bonnyrigg +Turuvekere +Shirva +Norcross +Mistrató +Dabas +Attimarappatti +Garching bei München +Guidan Roumdji +Boljoon +Yeşilhisar +Villorba +Matagob +Manīn +Gunjur +Krasnystaw +Quedgeley +Malacacheta +Ivanovka +Strzelce Opolskie +Rocca di Papa +St. Matthews +Sulaco +Plast +Ebino +Libertad +Elankūr +Moloacán +Tenango de Doria +Piratini +Mendes +Baxt +San Antonio de Ibarra +Eksambe +Goodlettsville +Cafelândia +Buguda +Kudavāsal +Sahel +Puerto Guzmán +Īlām +Puerto Rico +Stockach +Scott +Coalinga +Nümbrecht +Biyahmū +Ciudad Hidalgo +Karukh +Augustinópolis +Zhoujia +Bhucho Mandi +Ambatolahy +Villa Sarmiento +Croatá +Pajapan +Budva +Kushk +Urucuia +Shin-Kamigotō +South Ogden +El Reno +Fairview +Tepatlaxco +Dharampuri +Umirim +Corbélia +Vallières +Anantāvūr +Saluzzo +Pocinhos +South Venice +Phayao +Cafarnaum +Samaná +Talitay +Vladimirci +Cruz do Espírito Santo +Bluffdale +Gainesville +West Columbia +Guanagazapa +Gautampura +Morganton +Karkamb +Faléa +Marche-en-Famenne +Kiyama +Dar Chaifat +Veternik +Chamalières +Sibutao +Māndvi +Puurs +Los Lunas +Laguna Woods +Bon Air +Tasquillo +Krasnokumskoye +Rojales +Suvorov +Woodhouse +Karachev +Albino +Royston +Peçanha +Abdullahnagar +Rio Pomba +Tabernes de Valldigna +Nedugula +Lakhnaur +Rozdilna +Puerto Aysén +Pātan +Brielle +Nova Era +Nīsang +Lubang +Montemor-o-Novo +Sinzig +Reota +Forio +Bramhall +Mehrān +Kahoku +Derhachi +Seoni Chhapāra +Totutla +Remagen +Pescantina +Anosiarivo +Guaranda +Santa Lucía Milpas Altas +Nahāzāri +Cham +Kolokondé +Camocim de São Félix +Zaslawye +Loboc +Osuna +Bahon +Olivehurst +Conyers +Viradouro +Murray +New Castle +San Isidro +Colón +Calayan +Awlouz +Babenhausen +Karahia +Isola Capo Rizzuto +Buckingham +Ummannūr +Namminikara +Jhagarua +Beinasco +Fraserpet +Borda da Mata +Douétiré +Rio Claro +Yutz +Odaipatti +Międzyrzecz +Bolhrad +Jomboy Shahri +Bry-sur-Marne +Mārtahalli +Wahiawa +Bhagabānpur +Kawa +Curti +Ziracuaretiro +Pyskowice +São Domingos do Prata +Abelardo Luz +Wilton +Çat +Banni +Plungė +Mareth +Balma +Central Saanich +Solrød Strand +Villanueva del Pardillo +Mula +Louth +Néa Erythraía +Kurugodu +Santa Maria di Sala +Streetsboro +Woodcrest +Wangaratta +Gunjāpalle +Kelsterbach +Nizhnyaya Salda +Ban Song +Arpaçay +Raychikhinsk +Paoua +Holmdel +Lalībela +Mujikharf +Barhan +Sānwer +Ayacucho +Gescher +Milla’ab +Oued el Kheïr +Betio +Villa Rica +Viljandi +Glassmanor +Ketama +Bad Langensalza +Sestri Levante +Sainte-Catherine +Lumbreras +Kembhāvi +Ibrāhīmpatan +Skadovsk +Hednesford +Santa Elena +Kadogawa +Tonj +Kamudi +Valenzano +Gothini +Shumikha +Yerrapālem +Sedeh Lanjān +Padre Paraíso +Galaz +Mori +Pécel +Riva del Garda +New Amsterdam +Guardamar del Segura +Mau Dhaneshpur +Betroka +Krasnoslobodsk +Johnstown +Mudukulattūr +Matam +Mannō +Tagana-an +Dijiasuoxiang +Aïn Zaouïa +Uryzhar +Muniz Freire +El Kouif +Miharu +Suesca +Pital +Betulia +Schwarzenbek +Villiers +Mohács +Jenison +As Suqaylibīyah +Chadchan +Pohādi +Mecayapan +Otacílio Costa +Fort Thomas +Cajamarca +Schwalbach +Sierpc +Tíogollo +Maranello +Chāripāra +Almolonga +Namayingo +Dombóvár +Kanrangana +Mundo Novo +Ban Pong +Holzwickede +Sāram +Poyo +Chorhat +Krupanj +Guryevsk +Barbastro +Thorne +Fortim +Port Hope +Keota +Jawkatiā +West Lampeter +Horn-Bad Meinberg +Porciúncula +Pont-à-Celles +Kouka +Santo Antônio do Amparo +Talwandi Bhai +Brand +Toledo +Bad Münstereifel +Bethpage +Houghton Regis +Garwolin +Pollensa +Amesbury +Zébala +Osny +Baie de Henne +Lebanon +Thornton +Kukraun +Norrtälje +Mūlki +Arinos +Sengés +Sanary-sur-Mer +Kulasegaram +Moroni +Shongzhy +Hernando +Staphorst +Benavente +Dehti +Ellinikó +Maigh Nuad +Oskarshamn +Tecolotlán +Rattaphum +Rancho Mirage +San Juan Nonualco +Stanford +Baymak +Capistrano +G‘uzor +Kinel’-Cherkassy +Illizi +Venturosa +Heanor +Friern Barnet +Milton +Khānpur +Dharmāpuri +Mariakerke +Baturbāri +Laurel +North Druid Hills +Mettuppālaiyam +Stafford +Gunzenhausen +Marmande +Shelbyville +Oerlinghausen +Nadugadda +Bawgalegyi +Les Clayes-sous-Bois +Hatfield +Amarante +Inverness +Heilbad Heiligenstadt +Kangaba +Ejutla de Crespo +Jaguaretama +Cipó +Pāvumba +Abadiânia +Aurora +Resplendor +Littau +Souaflia +Minakami +Belmopan +Buford +Bendorf +Monselice +Castelfiorentino +Çukurca +Bakeshiyingcun +Martigny +Belākoba +Pisaflores +Bareggio +Clayton +Bailén +Terzigno +Avenel +Durleşti +Carnaubal +Itacarambi +Kretinga +Port-à-Piment +Marktredwitz +L’Isle-d’Abeau +Raiyam +Piera +Chouafaa +Shahritus +Carmo +Akassato +Lérida +Banabuiú +Anori +Digne-les-Bains +Danville +Badantola +Araruna +Glen Parva +Mont-Organisé +Dormentes +Flowing Wells +Sudipen +Bāţūfah +Forssa +Cherakara +Calhoun +North Decatur +Lyepyel +Trstenik +Bad Wörishofen +Bedford +Cacimba de Dentro +Jouy-le-Moutier +Padugupādu +Neerpelt +Al Ḩārah +Nederweert +Alcantara +Shek Tong Tsui +Villa Isabela +Hinsdale +Zandvoort +Guoxing +Castilleja de la Cuesta +Sumé +Conceição do Almeida +Iati +Traiguén +Cluses +Cottingham +Calnali +Bellaire +Pinewood +Massaranduba +Peka +Poção de Pedras +Tizi Rached +Sìnnai +Konstantinovsk +Itirapina +Kayaralam +Tanque Novo +Malta +Swansea +Konstancin-Jeziorna +Cássia +Tejuçuoca +Tlahuelilpan +Joaquim Gomes +Mayuge +Bom Sucesso +Sake +Settiyārpatti +Monsenhor Tabosa +Majītha +Sawankhalok +Ōi +Grumo Nevano +San Felipe +Ayinīkkād +Tricase +Clitheroe +Grenchen +Berezhany +Saguday +Firminy +Pānchgrām +Fairview Park +Kusa +Ipaba +Figline Valdarno +Graham +Frederikssund +Regeneração +Sallanches +Beckley +Peso da Régua +Banovići +Correntes +Giria +Sa‘ādat Shahr +Marsabit +Pouso Redondo +Ejea de los Caballeros +Manassas Park +Wādī Ḩalfā’ +Barrington +Ilaka Atsinanana +Easton +Ban Phe +Wang Tau Hom +South Hadley +Péruwelz +Damous +Kaukauna +San Bernardino +Groves +Kutiyāna +Manaquiri +Shoufeng +Ignacio de la Llave +Nova Pazova +Bülbülə +Yizhu +Tagounite +Dilasag +Gīnīr +Taxtako‘pir +Xintangcun +Pottasshēri +Mosbrough +Santa Magdalena +Braunau am Inn +Kolaccheri +Melfi +Oswego +Estelle +Mata Roma +San Esteban +Tādigadapa +Chandia +Lake St. Louis +Stockelsdorf +Tolūprpatti +Safety Harbor +Laç +Eutin +Elias Fausto +Usmānpur +Denville +El Segundo +Peragamanna +Sarotar +Condeixa-a-Nova +Zvenyhorodka +Gudipallipādu +San Rafael Cedros +Sibinal +Cinnaminson +Púchov +Xiulin +Svalyava +Mannukara +Āyikudi +Challapalle +Serra Dourada +Crixás +Guadarrama +Künzell +Māvalli +Upper Grand Lagoon +Turinsk +Cañon City +Tifton +Souto Soares +Gavrilov-Yam +Chuarrancho +Paulino Neves +Amfilochía +Sidi Yakoub +Tortum +Condeúba +Ramnagar +Saint-Basile-le-Grand +Kafr Zaytā +Itarantim +La Garriga +West Hempfield +Kibungan +Ruhango +Canovellas +Porteiras +Guernica y Luno +Cáqueza +Ilhota +Knemis Dades +Cheraro +Terre Neuve +Massapequa Park +Kelheim +Lons-le-Saunier +Pernamitta +Norwalk +Carlentini +Kitanakagusuku +Grójec +Bristol +Gennep +Ribeirópolis +San Miguelito +Menzelinsk +Santa María +Arıcak +Gachancipá +Addison +Sinsina +Pokrov +Bellingham +Tsararivotra +Albal +Milledgeville +Rubeho +São Simão +Piranga +Xinying +Huntington +Wadgassen +Heysham +Nāyakanhatti +Hinode +Cariús +Kamenz +Port Washington +Karratha +Macatuba +Sītāmau +Springfield +Middletown +Peñamiller +Juazeirinho +Avitanallur +Wixom +Upper Gwynedd +Pendleton +Brasnorte +Saint-Cyr-sur-Loire +Hannibal +Abington +Baaqlîne +Marosangy +Tolongoina +Etrotroka +Ilakatra +Ambila +Marofoty +Ambohitrolomahitsy +Lazarivo +Milanoa +Isoanala +Amboanjo +Befotaka +Tsarahonenana +Antanimora Atsinanana +Ankarongana +Ambondromisotra +Ambohitsimanova +Anosivelo +Manombo Atsimo +Sahamadio +Anosibe-Ifanja +Beahitse +Antsahavaribe +Ambohipihaonana +Mandritsara +Ambolidibe Atsinanana +Sadabe +Dzitbalché +Ghonchí +Yangi Mirishkor +Jāyal +Matauna +Aḑ Ḑulū‘īyah +Santo Antônio do Leverger +Hindarx +Alauli +Saint-Égrève +Peraía +Naula +Santo Tomás de Jánico +Kakraul +Tomas Oppus +Itajobi +Ban Rawai +Ambalavayal +Analanampotsy +Shankar Saraiyā +Kawai +Clayton +Sikandra +Lodwar +Oulad Amrane +Guisborough +Brunico +Bayt Ūmmar +Ulubey +Fortuna +Arai +Retiro +Srīkūrmam +San Juan Lalana +Gainesville +Andranofasika +Zarumilla +L’Ancienne-Lorette +Ibititá +Kōrōth +Aberdeen +Rāmachandrapuran +Ughara +Geddes +Serafina Corêa +Ginsheim-Gustavsburg +Viroflay +Yanggezhuang +Berga +Meltonakkal +Bishunpura +Qal’at Mgouna +Santana do Cariri +Cercola +Bou Hanifia el Hamamat +Dirba +Puerto Caimito +Kombai +Liuchuan +Mers el Kebir +Balmazújváros +Octeville +Perupālem +Acoyapa +Hohenems +Pozoblanco +Dallas +Myrtle Grove +Garagoa +Upper Saucon +Adelphi +Heber +Namysłów +Podporozhye +San José de Chiquitos +Hünfeld +Hampton Bays +Bridgeview +Ashwaubenon +Sarıoğlan +Chavara Grāmam +Boki-Wéré +Wondelgem +Kottapeta +Powder Springs +Tibau do Sul +Liuguoju +Long’e +Morriston +Aubange +Dalain Hob +Tekanpur +Eggenstein-Leopoldshafen +Guadalupe Victoria +Huruta +Baikatpur +Mālancha +Shingūchō-shingū +Carovigno +Stockerau +Governador Celso Ramos +Glace Bay +Cubellas +McKinleyville +Tobré +Galich +Alcantara +Río Bueno +Ginatilan +Kronach +Zinapécuaro +Carlópolis +Māri‘ +Fındıklı +Massé +Iziaslav +Nāranattenvanpatti +Nidgundi +Sidi Daoud +High Blantyre +Pallikapuzha +Douar Lamjaara +Rāmāyampet +Dehāqān +Laconia +Tubaran +Dazhangzicun +Dazhangzi +Dhulkot +Belle Glade +Aklanpa +Navalmoral de la Mata +Fetromby +Bruckmühl +Khed +Santa Teresa +Colonial Park +Ngara +Morteros +São João dos Poleiros +Farmingville +Vatutine +Soumagne +Vīrapāndi +Ettaiyāpuram +Mājra +New Milford +Miqiao +Heidenau +Azaourissè +Chambellan +Laferrière +Zhukovka +Ighram +Bermeo +Benešov +La Quiaca +Sada +Agaram +Boa Vista do Tupim +Annonay +Pleszew +Bédigoazon +Lamas +Ashmyany +Colesberg +Canutama +Thale +Hosakote +Marion +Le Pré-Saint-Gervais +Bambara-Maoundé +Nerang +Japaratuba +Nerviano +Pampa +Mount Vernon +Jāsk +Alfajayucan +Ban Tha Mai I +Moraga +Latifpur +Sāyarpuram +Linnei +Korem +Maltby +Rāmpur Jalālpur +Rubano +Lerum +Sliema +Pulimel +Janakkala +Bitburg +Defiance +São Pedro do Sul +Pran Buri +Finnentrop +Madanpur +Braunstone +Fiorano Modenese +Bostonia +Haslingden +Auburn +Boden +Azeffoun +Meiti +Damascus +North Hykeham +Shāhkot +Bhawānīpur +Hot Springs Village +Aïn Feka +Xixinzhuangzhen +Belaya Glina +Vallegrande +Wittenberge +Lillerød +Acharipallam +Devizes +Tsuiki +Hrubieszów +Trzcianka +Montceau-les-Mines +Saltash +Vechūr +Mattoon +Upper Chichester +Washougal +Haslemere +Achaljāmu +Ratba +Orăştie +Brixham +Yabuki +Salinas de Hidalgo +Gemert +Bhainsoda +Makīnsk +Asten +Dongjiangshui +Saint Ives +Kadikkād +Iglino +Long Beach +Sidi Lamine +Bad Lippspringe +Vazhani +Baronissi +Minamichita +Daireaux +Altıntaş +Kodikulam +Wyckoff +Cerea +Keetmanshoop +Rijen +Busolwe +Druzhba +Maroochydore +Astara +Montornés del Vallés +Beinan +Ambarès-et-Lagrave +Sinūnī +Kaliro +Puliyankunnu +Marib +San Julián +Famy +Sierre +Roxana +Kāmavarapukota +Weehawken +San Roque +Cestas +Tromsdalen +San Francisco +São Paulo do Potengi +Fazakerley +El Rosario +Sudak +Batuco +Kauhava +Truckee +Orta Nova +Pionki +Villeneuve-Loubet +Tsundupalle +Solothurn +Palmetto Estates +Říčany +Windlesham +Vila Bela da Santíssima Trindade +Latsia +Buntok +Sunland Park +Ouaouzgane +Posoltega +Connahs Quay +’Aïn Abessa +Masamagrell +Maşīf Sarsink +Dhobauli +Bāuria +Puerto Carreño +Humacao +Attappampatti +Eğirdir +Havelock +Yağlıdere +Young +El Piñón +Nandigaon +Bovisio Masciago +Djouab +San Miguel de Salcedo +Maardu +Chiva +Swift Current +Campo do Brito +Chestnuthill +Kadod +Nuevo San Juan Parangaricutiro +Shahbā +Königstein im Taunus +Thandla +Kushima +Santa Lucía +Donna +New Port Richey +Chinampa de Gorostiza +Água Clara +Vimodrone +Centerville +Ribat Al Khayr +Bytów +Diksmuide +Arakkal +Yerbas Buenas +Preganziol +Plumstead +Livinjipuram +Bürstadt +Sobrado de Paiva +Leixlip +Boysun +Dēra +Tengampudūr +Bek’ojī +Kings Park +Penzberg +Takhatgarh +Sendurai +Roche-à-Bateau +Hīrna +Katsuura +Ponte de Sôr +Băicoi +Holzkirchen +Bredbury +Chalāla +Konodimini +Kukës +Iluppur +Fuying +Fuyingzicun +Dour +Kodumudi +Glanerbrug +Klippansbruk +Vincennes +Aston +La Vista +Cabeceiras de Basto +Novomichurinsk +Pisticci +Firestone +Richmond Hill +Povorino +Saalfelden am Steinernen Meer +Kattiyeri +Gikongoro +Balighattam +Westchester +Pôrto Acre +Gonegandla +Ban Wang Nok Aen +Verdun +Pazhayannūr +Pochëp +Piriyāpatna +Jilotepec +East Greenbush +Jalpa de Méndez +Saran +Nässjö +Rotselaar +Erumād +Harborcreek +São Vicente Férrer +Hāvi Bhauār +Holly Springs +La Cruz +Zafra +Murraysville +Deoria +Ocho Rios +Néa Mákri +Châteaurenard +Wellington +Ferndale +Phrae +Kundal +Campagna +Murehwa +São Caetano de Odivelas +Zequ +Banstead +Kumage +Riemst +Przasnysz +Podalakūr +Federación +Bassum +Kruibeke +Hueytown +Kakkalapalle +Madrid +Whickham +Beaver Dam +Agourai +Loxstedt +Zveçan +Berber +Grangemouth +Arani +Sidi Ettiji +Bogatynia +Tāwargeri +Araçagi +Ban Na Sai +Calera +Khirhar +Keynsham +Barangka +Itapiranga +Shiraoi +Lake Mary +Ittiva +Xiaolongtan +Salitre +Glenmont +Millbrook +Mullānwāla +Tadaoka-higashi +Seeheim-Jugenheim +Red Wing +Añisoc +Tantéga +Tiahounkossi +San Isidro +Medicina +Epitacio Huerta +Cleckheaton +Pratteln +Micoud +Bull Run +Brod +Douar Tabouda +El Escorial +La Algaba +Valu lui Traian +Itororó +Shpola +Piquet Carneiro +Ad Dīs +Bánovce nad Bebravou +Leopoldshöhe +Wurzen +Bariārpur +Clearlake +Mariestad +Sedan +Bargūr +Poreč +Zirara +Ceadîr-Lunga +Noordwijkerhout +Shek Wai Kok +Khaniādhāna +Ibipeba +Mangueirinha +Marienberg +Reinheim +Lomas de Sargentillo +Getúlio Vargas +Morton +Kaatsheuvel +Macetown +Burbaliq +Dilijan +The Mumbles +Pannaipuram +Tukums +Mauguio +Three Lakes +Rio Linda +Saint-Jean-de-la-Ruelle +San Juan Bautista +Soumpi +Wailuku +Bijni +Xinyuan +Puerto El Triunfo +Buritama +Cidreira +N’Goussa +Country Club Hills +Łask +Īlkhchī +Īlkhechī +La Sierpe +Mohan Eghu +Ippy +San Giovanni Valdarno +Pembroke +Heumen +Barkot +Warrington +Pirque +Eppelborn +Lille +Miyazu +Tongluo +Paragaticherla +Humble +Menomonie +Lakhipur +Bhai Rupa +Plácido de Castro +Mensora +Cabaceiras do Paraguaçu +Ina +Guaymate +Slaný +Seltso +Itaquitinga +Domchānch +Indiaroba +Coaraci +Santa Maria das Barreiras +Ramón Santana +Hønefoss +West Richland +Middelfart +Tichi +Sujina +Regenstauf +Cattolica +Wentang +Raunheim +Kade +Odumase +Tamallalt +Bogucice +Ada +Parali +Mangrauni +Segbwema +Batán +Marly-le-Roi +Vero Beach +Ahmetli +Ledeberg +Liangyi +Ban Cho Ho +Bayat +Rentachintala +Thị Trấn Ngải Giao +Hövelhof +Brackenheim +Madison +Mapiri +Santa María Petapa +San Estanislao +Puerto Pilón +Pedregulho +Gussago +Horodok +Torre Maggiore +Sunagawa +Uchkeken +Caiapônia +Cassano al Ionio +Thompson +Ciudad Guadalupe Victoria +Haji Shah +Berezan +Langrucun +Espartinas +Sant’Elpidio a Mare +Chinde +Al Ghāţ +Qarqīn +Qusar +Cowley +Pilāppulli +Mudakkiraye +East Highland Park +Amba Icharua +Alpignano +Ukiah +Słubice +Fléron +Shintomi +Adelfia +Zapotlán de Juárez +La Paz +Jinjicun +Jinji +Tafresh +Hebburn +Linbian +Ashby de la Zouch +Milanówek +Ham Lake +Chorbogh +Jasper +Grimari +Bobangui +Kalundborg +Rāsivarai Tottam +Reggello +Antanambao Mahatsara +Ervādi +Braniewo +Rājbalhāi +Hillcrest Heights +Warsaw +Sabotsy +Ban Lam Narai +Barbacoas +Enfield Lock +Ōyodo +Fairburn +Escoublac +Mifune +Dighaun +Ujre +Ban Nong Han +Kirchhain +Dahibhāt Mādhopur +Loma Plata +Kilwinning +Chopadandi +Mohanūr +Mannūr +San Casciano in Val di Pesa +Lake Wales +Fillmore +Les Herbiers +Ojus +Wardenburg +Nanbu +Tōhoku +Taylor +Batalha +Anorí +Sakardih +Encruzilhada +Churriana de la Vega +Glenn Heights +Albemarle +Kozelsk +Katosi +Nakanoto +Piancó +Charqueada +Ciney +Cayetano Germosén +Bordj Mokhtar +Edmundston +Cloverly +Hannut +Greenville +Gurh +Lufeng +Rājnagar +Auburndale +Pupiales +Altena +Rawa Mazowiecka +Belén +Bilohirsk +Vedelago +Gasparillo +Neópolis +Bykhaw +Boshrūyeh +Pearl River +San Miguel +Pelhřimov +Kierspe +Aberdeen +Peer +Adjahomé +Batangafo +Burgdorf +Goulmima +Ambohimanambola +Curacautín +Nilaiyūr +Spanish Springs +Franklin +Hilltown +Westbury +Lugu +Cajari +Hanover +Uherský Brod +Petersberg +Kirsanov +Islām Qal‘ah +Portachuelo +Baro +Dyer +Pudu +Poing +Rāikal +Kurtamysh +Mastchoh +Belém +Vienna +Ntungamo +Las Cabezas de San Juan +Pocking +Dentsville +Bosobolo +Toro +Pamiers +Hatti +Mahesh Khunt +Passa Quatro +Kastoriá +Freienbach +Kesath +Faxinal +Gokavaram +Tahlequah +Outat Oulad Al Haj +Poço Fundo +Nova Resende +Hollins +Middletown +Libiąż +Karema +Bou Hadjar +Stone +Khargāpur +Khirpai +Puan +Zhongzai +Radford +Taohongpozhen +Malnate +Divriği +Caridade +Panukulan +Pasaquina +Brakel +Hude +Aki +Moreau +Angara-Débou +Kingsborough +Žitorađa +Kirchlengern +Barra de Santo Antônio +Straelen +Denby Dale +Phirangipuram +Erba +San Juan +Guben +Ubrique +Payson +Sebt Aït Saghiouchen +East Longmeadow +Mercedes +Juchique de Ferrer +Arteche +Bagnacavallo +Fairview Heights +Fujioka +Bangor +Fullerton +Jussara +Usgao +Nunna +Mahālandi +Chettināyakkanpatti +Cosautlán +Jambaló +Sulzbach +Nova Olímpia +North Arlington +Rolleston +Bārnia +Moussoro +Central +Chinácota +Wemmel +Halver +Ban Thung Tam Sao +Gālivedu +Kakching +Großostheim +Arenys de Mar +Hương Canh +Bad Essen +Novoukrainka +Kargil +Grosse Pointe Woods +Vadugappatti +Villa San José +Piru +Çarşıbaşı +La Falda +Ouled Chebel +Crawfordsville +Sahatavy +Muscle Shoals +Erwitte +Baipingshan +Mount Dora +Westport +Sunnyside +Sananduva +Shuzenji +Bree +Yoshinogari +Tiverton +Elsen +Bandar Murcaayo +Guelendeng +Srbobran +Wallisellen +Poplar Bluff +Yuvileine +Xianxi +Changji +Khunti Dhanaili +Northbridge +Bargteheide +Schmelz +Douglas +Moroto +Lyantonde +Leinì +Birstall +Noniyā +Umbertide +Fort Drum +Shuili +Tremedal +Shahriston +Kodala +Novoanninskiy +Merchtem +Hampton +Arachchalūr +Sohtha +Mālhīpur +Bacuri +’Ayn Bni Mathar +Acarape +Barharwa Kalān +Buffalo +Welby +São Francisco do Guaporé +Birsinghpur +Wiefelstede +Dungannon +Colle Salvetti +Yorkton +Zumarraga +Raposos +Kabo +Omalūr +Center Point +Carcarañá +Utinga +Domoni +Kassorola +Nawnghkio +Berriche +Ripon +San Dionisio +Queensbury +Madukkūr +Ivanava +Sabana de La Mar +Nobsa +General Villegas +Martinsicuro +Simaria +Al Laţāminah +San Antonio Oeste +Acahay +Momil +Brus +Yotoco +Taishachō-kizukikita +Jagdispur +Oyten +Wendlingen am Neckar +Kasumi +Antsahanoro +Goundam +Radzionków Nowy +Ispica +Bad Bentheim +Naama +Ambalabe +Llanquihue +Benjamín Aceval +Montalvo +Éghezée +Bikou +Az Zaydīyah +Jalālpur +Glen Allen +Carhué +Santomera +Ransiki +Progreso +Târgu Secuiesc +Manamelkudi +Bālasamudram +Parkway +Cherān +Khirbat Ghazālah +Longchang +Spárti +Stepney +Massakory +Mukhtārpur Salkani +Kota +Fiano Romano +Artesia +Bethlehem +Garou +Tadikalapūdi +San Martino Buon Albergo +Chickasha +Opa-locka +Künzelsau +Restrepo +South Middleton +El Peñol +Giaveno +Oteapan +Orimattila +Palma Campania +Yunshan +Neratovice +Turgutalp +Ōyamazaki +Freiberg am Neckar +Hermitage +Kabugao +Cotorra +Ikast +Hajdúnánás +Vitthalāpuram +Petrovsk-Zabaykal’skiy +Çaykara +Aranyaprathet +Lemay +Jičín +Al Ḩībah +Walnut Park +Basford +Landerneau +eMuziwezinto +Rožnov pod Radhoštěm +Burrillville +Singhwara +Santa Anita +Altinópolis +Stahnsdorf +Porumāmilla +Šilutė +Do‘stobod +Vodil +Handlová +San Pedro de Lloc +Xiangjiaba +Gokarna +Taree +Khutauna +Saraikela +Izamal +Nakhon Nayok +Ansfelden +Brzesko +Stallings +Kozienice +Bandora +Hostomel +Roşu +Mastic Beach +Bīleh Savār +Kanhauli Manohar +Svitavy +Tocantins +Ampahimanga +Alegria +Attūr +Berea +Itāhri +Aruvāpalam +Griffith +Castellaneta +Harenkarspel +Köprüköy +Chotāla +Alsfeld +Riversdale +Aguadulce +Ségoubougou +Ganeshpur +Monte Carmelo +West Norriton +As Sukhnah +Chorfa +Readington +Ocean Pointe +Timezgana +Huarmey +Dyersburg +South Houston +Banora Point +Kotoura +Hibbing +Talsint +River Falls +Bingawan +Four Corners +Parma +Al Qbab +Baharu +Pujilí +Rāmnagar +Schlüchtern +Alcañiz +Guérande +Milford +Caldera +Pielisjärvi +Loimaa +Āgiripalle +Troutdale +Bni Tajjit +Babayurt +Westwood +Al Awjām +Ban Kao +Sikeston +Rygge +Castenaso +Sayville +Venustiano Carranza +Voss +Kittūr +Tyldesley +Caravaggio +Springfield +Sylvan Lake +Mafuné +Carlet +Upala +Santa Cruz Muluá +Zegzel +East Barnet +Port-de-Bouc +Easthampton +Forks +La Grange +Cêrro Azul +Jaypul +Los Muermos +Markranstädt +Quarteira +Noale +Bang Phae +Upton +’Aïn Babouche +Douar Azla +La Grange +Yeni Suraxanı +Buckley +Jakkampālaiyam +Chennūr +Ranong +Ségou +Bumpe +Rustampur +Simcoe +Math Lohiyār +Ustroń +Kāmayakkavundanpatti +Subachoque +Kodinsk +Jacupiranga +Sultānābād +Kujwa +Parrita +Ambarakaraka +Gubin +Balş +Jalālābād +Medjana +Ubatã +Chaona +Cherukunnu +Khusropur +Saraland +Tarqui +El Ach +Diabigué +Masmouda +Frogn +Pilkha +Bakarpur Ogairah +Nonantola +Bovolone +Prinzapolka +Canudos +Kajur +Nowogard +Sūrappalli +Panajachel +Chinggil +Birhana +Michelstadt +Hazro +Nyahanga +Anantapalle +Ripon +Ait Yaazem +Chai Prakan +Johnstone +Wendelstein +Aurāhi +Aquitania +Bellevue +Horodok +Ottūr +Bouchabel +Viškovo +Fatipura +Ottakkadai +Brüggen +South River +Seymour +Banigbé +Şemdinli +Großenkneten +Kollūru +Rāmewādi +Owase +Santiago Tulantepec +Wohlen +Domont +Mecheraa Asfa +Shāhpur +Olecko +Tankal +Federal +Dalachi +Kalanjūr +Eloy +Quiindy +Bhiloda +Hüyük +Middle Smithfield +Léré +Yaojia +Makhu +Czernica +Fartura +Punta Umbría +Vontimitta +Safo +Sultonobod +Tachiarai +Mount Pleasant +Montbrison +Bādshāhpur +Baléyara +Sulechów +Sérarou +Mikkabi +Ukrainka +Strathroy +Ayyampālaiyam +Darién +Marbach am Neckar +Mārupe +North Fayette +Dickson +Gardendale +Ágios Ioánnis Réntis +Bni Quolla +Grovetown +Stone Ridge +Sivapuram +Lady Lake +Bay Village +Telfs +Al Ḩazm +Mayilūr +Scordia +Ranohira +Duxbury +Tourlaville +Agudo +Penugonda +Fort Mohave +Qarabulaq +Vauréal +Hewitt +Sulphur Springs +Eilenburg +Worcester Park +Bou Djeniba +Doğanhisar +Mahavelona +Kāyanna +Samarate +Mountain Home +Ciudad de Huitzuco +Az Zintān +Cajueiro +Preetz +Azalea Park +Zeulenroda +Americus +Lithia Springs +Qazyan +Grandville +Rāhon +Marahōm +Khat Azakane +Kasli +Giruá +Ya‘bad +Locarno +Aue +Myrza-Ake +Bracebridge +Santa Ana +Fort Hunt +Sátiro Dias +Orsay +Rāni Shakarpura +Sapatgrām +Rylsk +Utnūr +Türkeli +Blanquefort +Kumaranallūr +Exeter +Sidi Azzouz +El Rosario +Degtyarsk +Höganäs +Sughrāin +Summerside +Bethulie +Tsiningia +Antindra +Andapafito +Vohilengo +Manompana +Bevonotra +Betanty +Mandrosonoro +Ankazondandy +Androrangavola +Fiadanana +Alarobia +Inanantonana +Ambalatany +Andemaka +Vohiposa +Anjangoveratra +Leanja +Fenoarivo +Fenoarivo +Bekitro +Ambohibe +Tangainony +Antsenavolo +Analila +Mangabe +Baltit +Tullinge +Kafia Kingi +Manafwa +Buwama +Sardoba +Ishtixon Shahri +Mông Dương +Longji +Koulamoutou +Asāra +Dandu Mailāram +Al Mazār ash Shamālī +Wātrāp +Illingen +Rumilly +Chilgazí +Weener +Zemrane +Neriyamangalam +Dêngka +Dianga +Üsharal +Caidat Sidi Boubker El Haj +Youngsville +Canmore +East Finchley +Ambohimalaza +Guioyo +Lewes +Nanāttupārai +San Cesareo +The Dalles +Washington +Tasīl +Füssen +Rāiganj Bāzār +Ḩammām al ‘Alīl +Dublin +Bathurst +Kamyzyak +Chornobaivka +Yaypan +São Lourenço da Serra +St. Simons +Vicente Guerrero +Balneário do Rincão +Saint-Amand-les-Eaux +Jāwalgeri +Kerman +Saint-Leu-la-Forêt +Kizhūr +Kudali +Cruz Machado +Ouamri +Antadinga +Clarksburg +Frankfort +Este +Barysh +Yurihama +Chittayankottai +Neykkārappatti +Mwingi +Erlensee +Finsterwalde +Hayange +Tuineje +Chertsey +Sukth +Valavakāttumūla +Obra +Mohanpur +Amarwāra +Ambohidrapeto +Bahutāl +Landen +Zhytkavichy +Kyabé +Chivolo +Viera West +Ghomrassen +Gentbrugge +L’Oulja +Pātapatnam +Elbeuf +Tagazhi +Ishidoriyachō-eso +Motru +Ras el Oued +Mandiakui +Coos Bay +South Hayling +Fontainebleau +Valeggio sul Mincio +Tarquinia +Marco Island +Talitsa +Süchteln +Despujols +Summerfield +Alotau +Androy +Stará Ľubovňa +Hongsi +Brahmadesam +Bellmore +Alabat +Schwarzenberg +Campanha +Manatanna +Codru +Königslutter am Elm +Gargždai +Aporá +Sertanópolis +Xiaqiaotou +Băileşti +Bikin +Penicuik +Râşnov +Plavsk +Cumaru +Comarapa +Middleburg Heights +Dunaivtsi +Verneuil-sur-Seine +Ban Dung +Karambakkudi +Lakamané +Petite-Synthe +Adwick le Street +Alpine +Quixelô +Saint-Pierre-des-Corps +Vlagtwedde +Paso de Carrasco +Sāyalkudi +Suchindram +Nyasvizh +Hayes +Alexándreia +San Giovanni in Fiore +Kuršumlija +Lewiston +Calbuco +Brooklyn Park +Marmeleiro +Lower Salford +Schaesberg +Fremont +Heggadadevankote +Fairhaven +Haddada +Aladağ +Piaçabuçu +Bastogne +Ostrov +Zunil +Sint-Kruis +Eilendorf +Walldorf +Floral Park +Greater Napanee +Barentu +Cerejeiras +Luzhou +Skvyra +Ivdel +Turffontein +Vijayapuri North +Quezon +Wadern +Gheorgheni +Addlestone +Vellikulangara +Highland Village +Lam Luk Ka +Dighirpār +Sertã +Concorezzo +Benetúser +Mahires +Esquipulas +Davutlar +Brusciano +Kulhudhuffushi +Beek +Drensteinfurt +Kishundāspur +Warren +Katav-Ivanovsk +Udayagiri +Tapejara +Galván +Bar +Sidi Allal el Bahraoui +Alvarães +Villalbilla +Gryfice +Codroipo +Mario Campos +Prospect Heights +Nidiyanga +Vári +Tupi Paulista +Baghlia +Hammam M’Baïls +Demerval Lobão +Seaford +Valabhīpur +Toma +Sahambala +Jamestown +Toul +Dembeni +Gatesville +Lonigo +Tamazouzt +Xiaozhengzhuang +Pawai +Indianola +La Lucila +Bonito +Osowa +Jataúba +Gandevi +Brofodoumé +Chhapra Bahās +Simmerath +Saint-Julien-en-Genevois +Dassari +Borodino +Taromske +Odaiyakulam +Overland +Bruck an der Mur +Vatomandry +Mandialaza +Hadjadj +Kavarna +Feke +Rypin +Le Pecq +Hugo +Międzyrzec Podlaski +Zhutian +Elkton +Dhusar Tikāpatti +Srīmushnam +Seven Oaks +Kodikkulam +Vallirana +Kadamakudi +Harlingen +Ascheberg +Kalininsk +Ban Samo Khae +Port Orchard +Rutland +Tlaltetela +Santa Rosa +Tapiramutá +Pewaukee +Lake Country +Colônia Leopoldina +Taucha +Ropczyce +Grottammare +Tanakpur +Saviano +Opelousas +João Neiva +Kallūr +Kabayan +Mamadysh +Ash Shaddādah +Kumano +Batalha +Serinyol +Niceville +Haftkel +Cadoneghe +Lentate sul Seveso +Altay +Ixhuatlán del Sureste +Adrasmon +Maliana +Cholai +Tarancón +Victor +Whakatane +Diang +Breisach am Rhein +Salonta +Pūluvappatti +Dhāni Sukhan +Centre de Flacq +Antrim +Belaur +Kunithala +Longmeadow +Dhūmnagar +Westbury +Vardannāpet +Fatehpur +Ōarai +Krapkowice +Palagiano +Waterville +Shāhpura +Palagonia +Altopascio +Kāndla Port +Chethakal +Mahudha +Ras El Oued +San Bartolomé Jocotenango +Macedo de Cavaleiros +Yorito +Rahden +Lara +Ouadhia +Aïn Mediouna +Ramada +Kovdor +Miguelturra +Laurentides +Windham +Goubellat +Garrel +Novo Airão +Gonohe +Kannampālaiyam +Al Mu‘abbadah +Pijiño del Carmen +Mansingha +Sulat +Akambādam +Kalanādu +Rūpbās +Dolores +Kasimkota +Uusikaupunki +San Antonio Palopó +Vieux Fort +Patton +Diafarabé +Vendôme +Iporã +Hennebont +Sonāpur +Herisau +Ardestān +Quimavango +General Pinedo +Northallerton +Titel +Thibodaux +Sered’ +Itatim +Susanville +Wildwood +Santa Juliana +Mirangaba +Hanover +Lindås +Trisshilēri +Usuda +Toyoyama +Hille +Correia Pinto +Guasca +Puduppatti +Minamiminowa +Lübbenau/Spreewald +Lymington +Carugate +Live Oak +Orizona +La Montañita +Dūbacherla +Whitewater +Vynnyky +Campina da Lagoa +Baeza +Lynden +Ribnitz-Damgarten +North Strabane +San Jacinto Amilpas +Zulte +Tarūr +Maruim +Langenberg +Eravattūr +Newberry +Bharanikāvu Tekku +Wolf Trap +Coelemu +Mennecy +Sāhar +Steffisburg +Castillo +Morges +Mwinilunga +Pochampalli +Hayden +North Whitehall +Southern Pines +Kimwanyi +Beko +Dieburg +Taima +Hilvarenbeek +Weston +Niepołomice +Ebersbach an der Fils +Newton +Si Satchanalai +Turkaguda +Damdama +Grain Valley +Buçimas +Beernem +Noto +Aabenraa +Xixucun +’Aïn el Arbaa +Todi +Ifanadiana +Azzano Decimo +San Miguel +Zhirnovsk +Beaucaire +Carterton +Nova Brasilândia d’Oeste +Mount Clemens +Binningen +Yakumo +Tīrān +Copparo +Dhanot +Renai +Melonguane +Namala Guimbala +Bayt Saḩam +Hedongcun +Hartford +Bububu +Novouzensk +Ponders End +Ince-in-Makerfield +Tsukumiura +Colorado do Oeste +Gaillac +Wenwu +Valmontone +Bhit Bhagwānpur +Kājha +Dhemāji +Soavina +La Cruz +Mitchell +Mecitözü +Schiffweiler +Marivorahona +Ərkivan +Mindelheim +Shchuchyn +Ochiai +Zaṟah Sharan +Ban Tha Pho +Northborough +Le Vésinet +Mvurwi +Bengonbeyene +Mezőtúr +Bālkonda +Nioro du Rip +Câmpulung Moldovenesc +Ostroh +Chenlu +Baile an Bhiataigh +Donacarney +Yazu +Capilla de Guadalupe +Porthcawl +Leso +Chubbuck +Molinella +Jiaojiazhuang +Toui +Manūjān +Langenau +Rājāpur +Coto de Caza +Nailsea +Manjīl +Bolotnoye +Boudjima +Longwood +Guinguinéo +Dammāj +Palmitos +Leopoldsburg +Langenthal +Loma de Cabrera +Serpa +Puttalam +Mayoyao +Ashikita +Cheramkod +Solapuram +Pallappatti +Sukhothai +Porto San Giorgio +Tōbetsu +Drăgăşani +Boppard +Kara-Kulja +Frecheirinha +Fort Leonard Wood +Tatahuicapan +Rakvere +Oswestry +Jurāwanpur Karāri +Olmué +Dewangarh +Mangqu +Mölnlycke +Narendrapatnam +Kolakalūru +Antarvedi +Anadyr +Sin-le-Noble +Calumpang +Lucé +Chembagarāmanpudūr +Bilpura +Porto Empedocle +Templin +Ermúa +Julita +Shively +Cumru +Ponsacco +Mouiat Ouennsa +Sundarpur +Crusinallo +Dalanping +Epe +Shizukuishi +Mezőkövesd +Bhagta +Bhagwāngola +Galliate +Pozos +Princeton Meadows +Red Hill +Baynāla +Akdepe +Maullín +Najasa +Zevio +Muhammadābād +Sebt Bni Smith +Cosío +Ban Pang Mu +São Pedro do Sul +Kadaiyam +Belo +Kompalle +Rakovník +Chamgardān +Yığılca +Dixon +Southchase +Cochoapa el Grande +Ernagūdem +Juma Shahri +Ipuã +Kasagi +San Miguel Dueñas +Tung Tau Tsuen +Miyota +Vatlūru +Pequannock +Canapi +Swinton +Bad Neustadt +Frei Paulo +Eppelheim +El Chal +Edakkazhiyūr +Puerto Pimentel +Kežmarok +Machang +Jasol +New Haven +West Ham +Einsiedeln +Chittūr +Sāligrāma +Agrate Brianza +Svetogorsk +Santa Cecília +Ponnamarāvati +Qaryat Sulūq +Scottsboro +La Roda +Ayaş +Mỹ Lương +Palhālan +Shimanto +Codogno +Sovetsk +Greenwood Village +Rakhiv +Souagui +Kukkundūr +Sānātikri +Kumaralingam +Bissegem +Bituruna +Tidjikja +Anār +Kyazanga +Wattignies +Nannestad +Wanda +North Reading +Itariri +Barod +Lwengo +Igaporã +Sainte-Luce-sur-Loire +Bredasdorp +Glória +Dastgerd +Los Osos +Rendon +La Palma +Aleg +El Palmar +Hofgeismar +Edineţ +Matlock +Bokoro +Kučevo +Diapaga +Lahoysk +Certaldo +Ebn Ziad +Aldine +Lezhë +Palanisettipatti +Batavia +Sayō +Sirāli +Meghauna +Morteẕá Gerd +Zhuolan +Teodoro Schmidt +Saffron Walden +Rossville +Kifosso +Bŭston +Cherryland +Pipra Latīf +Kanan +Zelenogradsk +Nobres +Pudtol +Patilār +Daigo +Carthage +White Oak +Kurāwar +Banqiao +Asthānwān +Casalpusterlengo +Vemuladīvi +Maurānwān +Montijo +Dejen +Shipley +Linguère +Ain el Hadid +Ternat +Todmorden +Skhour Rehamna +Tizi-n-Tleta +Tiszaújváros +White Center +Overpelt +Funyan Bīra +Maskanah +Verkhnodniprovsk +Pichhor +Lajkovac +Seekonk +Nkouraba +Budakeszi +Isorana +Ambaguio +Moreni +Skalica +Ban Nong Kathao +Fos-sur-Mer +Hiệp Hòa +Traverse City +Mainburg +Sabana Grande de Palenque +Corbera de Llobregat +Sarai Jattān +Amboavory +Sagala +Baiceng +Neustadt in Holstein +Kyzyl-Suu +Ban Duea +Altunhisar +Noisiel +Guipavas +Andover +Udala +Solec Kujawski +Panorama +Greeneville +Yeşilova +Montivilliers +Differdange +Sandhausen +Turhāpatti +Csongrád +Phulaut +Bilgi +Pianezza +Rosário Oeste +Stapleford +Soliera +Kinna +Mitrapur +Weybridge +Chilamattūru +Madera +Ilovaisk +Kestel +Ventaquemada +Taperoá +Santa Cruz de la Palma +Knaresborough +Kambadūru +Bemidji +Romit +Broxburn +Al Hammam +Arumbāvūr +Bois-d’Arcy +Cipanas +Uthai Thani +Jiblah +Ezzhiliga +Anazzou +Estrela de Alagoas +Dilārpur +Concepción +Nedumudi +Northview +Gehrden +Jamālpur +Ala-Buka +Dacheng +Nymburk +Nuth +Tarascon +Manohisoa +Yankton +Saint-Rambert +Odanāvattam +Vipparla +Machelen +Khao Yoi +Efkarpía +Buggānipalle +Bruntál +Dayton +Clark +Hartselle +Tari +Spittal an der Drau +Robbinsville +Tururu +Corinto +Nancagua +Ezine +Blomberg +Sorrento +Kérouané +Dison +Messias +Glasgow +Żabbar +Meco +Berea +Chokkanāthapuram +Potenza Picena +Grimes +Andrakata +Khampat +Siladon +Sodāg +Lāl Khatangā +Khijri +Nova Olinda +Malhada +Patos +Landázuri +Villa del Rosario +Besana in Brianza +Jackson +Tendrara +Jamao al Norte +Seacombe +Aç-çahrij +Magdiwang +Kürdəmir +Ostashkov +Butiama +Fortuna +Ambongamarina +Ban Khek Noi +Jelcz-Laskowice +Samsikāpuram +Bradley +Raspur Patasia +Fraga +Strzegom +Gopālnagar +Ayutla de los Libres +Robinson +San Policarpo +Homosassa Springs +Cruzília +Discovery Bay +Victoria +Putaendo +Silvi Paese +Thoen +Baildon +Kot Bhāi +Mortara +Krasnovishersk +Trélazé +Talaināyar Agrahāram +Lopare +Kocaköy +Urandi +Rājgaḍh +Frattaminore +Genappe +Tanhuato de Guerrero +Mátészalka +Sava +Vişeu de Sus +Eden +Dvůr Králové nad Labem +Anao-aon +San Luis del Palmar +Clinton +Kumaramputtūr +Bambous +Amatitán +Moul El Bergui +Alta +Uvalde +Bunnik +Manor +Castro Daire +Eldorado +Perivale +Semra +Bennington +Bar +Kanegasaki +Gomboussougou +Daisen +Abaza +Chhājli +Winkler +Chaltyr +Isrāin Kalān +Khamir +Evans +Fruitville +Bad Bramstedt +Haddon +Ratekau +Basārh +Jupi +Maddikera +Odenthal +Amānganj +Undi +Villa Paranacito +Bandar-e Khamīr +Inhuma +Wombwell +São João Evangelista +Burbach +Pihra +Maydolong +Atalaia do Norte +Stanley +Bankheri +Llantrisant +Veys +White +Poronaysk +Itajibá +Mottola +Bhānumukkala +Guerouma +Franklin Park +Belchertown +Broxbourne +Dudhgaon +Uiraúna +Andicun +Elandakuttai +Nagasu +Tenafly +Ladson +Harper Woods +Guamaré +Vennesla +Medrissa +Belāri +Shchigry +Erongarícuaro +Norridge +Shāl +Chaparral +Lagoa da Confusão +Yenice +Maryborough +Clausthal-Zellerfeld +Santa Úrsula +Saint-Charles-Borromée +Chilcuautla +Tēkkampatti +Mātar +Mantua +Lyss +Sidi Ghiles +Yaransk +Atyrá +Ulladulla +Cunit +Sippola +Shedbal +Palmer Ranch +Galanta +Kālikāpur +Motiong +Pereiro +Chemini +Boumahra Ahmed +Peddaboddepalle +Sāhpur +Yasnogorsk +Udayendram +Es Sebt +Vincent +Aïn Jemaa +Beldānga +Privolzhsk +Yasnyy +San Rafael Arriba +Wittenheim +Mangawān +Rātan +Dawlish +Shimohata +Rio Grande City +Puréparo de Echaíz +Project Six +Senlis +Nonkon +Ober-Ramstadt +Penacova +Ovruch +Coulommiers +Boksitogorsk +Bouc-Bel-Air +Vail +Civita Castellana +Volpiano +Springdale +Kinross +Castellarano +Dengshangcun +Palmview +Asbury Park +Pichucalco +Libagon +Featherstone +Mexborough +Melissa +Glenn Dale +Tazert +Anosy Avaratra +La Solana +Marui +Kriel +Aramil +Bétérou +Itanhandu +Metković +Wilmington Island +Kanding +Pimpalgaon Rājā +Mulakaledu +Cowansville +Conchas +Upper Southampton +Johnson City +Rochedale +Billapādu +Vieiro +Neston +Sainte-Anne-des-Plaines +Avsallar +Pinhalzinho +Chervonopartyzansk +Támesis +Charām +Bidston +Rive-de-Gier +Vammala +Bunkeflostrand +Capurso +Shangtianba +Musāpur +Saint-Brice-sous-Forêt +Humayingcun +Macomb +Sumidouro +Phillipsburg +Souk Et-Tleta des Oulad Hamdane +Ljungby +Aravakkurichchi +Tirhassaline +La Apartada +Francheville +Château-Thierry +Barrocas +Česká Třebová +Salesópolis +Monroe +Huasca de Ocampo +Arnedo +Chelsea +Zubin Potok +Hallstahammar +Gagarin Shahri +Northenden +Pyāpali +Kumiyama +Davidson +Chinsali +Chellaston +Ahram +Mahaly +Seshambe +Gombe +Mering +Verwood +Imerimandroso +Adilcevaz +Gölmarmara +Bangassi-Nangou +Longwy +Uchiko +Melouza +Jangīd +Tondangi +Bellavista +Hebli +San Vito al Tagliamento +Dougouni +Nakanojōmachi +Taşucu +Azpeitia +Domaniç +Schwyz +Xiangyuncun +Beech Grove +Dorfen +Adjud +Gulf Shores +Cessnock +Wakuya +Montagu +Fray Luis A. Beltrán +Petlāwad +Espumoso +Seven Pagodas +Salua +Gayéri +Pâ +Niederkrüchten +Redruth +Cumbum +Greenlawn +Waremme +Río San Juan +Puliyūr +Hathīaundha +Moura +Ewa Beach +Sobreda +Douar El Arbaa Bou Quorra +Yauco +Lakhaura +Gaspé +Meruoca +Palmers Green +Totteridge +Nazaré +Gurramkonda +Kontiolahti +Blaydon +Kenmore +San Pedro Tapanatepec +Biatorbágy +Kawaminami +Berkley +Florencia +New Brighton +Biritinga +Kuusamo +Whitman +Mallāpuram +Mashpee +Alice +Vicovu de Sus +Telua +Roanoke Rapids +Depew +Vandalia +América Dourada +Castiglione del Lago +Weißwasser/Oberlausitz +Şefaatlı +Västerhaninge +Horsham +Kawasaki +Pargas +Grodzisk Wielkopolski +Presidente Dutra +Vesoul +Ferndale +Gangāpur +Majiagoucha +Ambohimierambe-Andranofito +New Cassel +Bella Vista +Sansepolcro +Swampscott +Bayserke +Raynham +Ban Na Kham +Warni +Srīvardhan +Kutchan +Jaguapitã +Zacualpan +Klimavichy +Mazagran +Xihuangni +Newport Pagnell +Mahikeng +Vize +Rumst +Koduman +Oulad Bou Rahmoun +Tamaki +Gloversville +Radviliškis +Sakabansi +Miraí +Maesawa +Bhoj +Itki Thākurgaon +Khaira +Piratininga +Chautham +Doumanaba +Tredegar +Kulunda +Bilaua +Hendersonville +Silvino Lobos +Putyvl +Nopala de Villagran +Çifteler +Pacific Grove +Huitzilan +Wülflingen +Tonawanda +Galatone +Brake +Ban Ton Thong Chai +Babhani Bholwa +Tagoloan +Schriesheim +Al Madāmūd +Bhatranha +Kastsyukovichy +Changamkari +Canyon +Cernavodă +Putaparti +Kidal +Capotille +Sooke +Tabhka Khās +Hasanpur +Jadia +Mellila +Bina +Bonheiden +Makapanstad +Sultandağı +Eggertsville +Kohīr +Natividade do Carangola +West Park +Hatton +Durham +Campodarsego +Hasanganj +Talladega +Clevelândia +Ipauçu +Mani +Peddapalle +Devendranagar +Hazel Park +Leninsk +Neustadt bei Coburg +Front Royal +Ut Bulag +Spring Creek +Bad Wurzach +Olonne-sur-Mer +Montargis +Sabou +Alvinópolis +Andondabe +Pedara +Tatarikan +Kanungu +Echuca +Bolivia +Markgröningen +Patnanungan +Rizal +Royse City +Rajni +Mineral Wells +Ekma +Kawagoe +Perchtoldsdorf +Amlash +La Magdalena Tlaltelulco +Pontinia +Boskoop +Saint-Avold +Tubod +Murray Bridge +Langelsheim +Gondomar +Glasgow +Revūr +Kamitonda +Burshtyn +Ylivieska +Ochanthururtha +Carrières-sur-Seine +El Arenal +Villa Ocampo +Chépica +Grenzach-Wyhlen +Mollerusa +Frederick +Contamana +Monroe +Tone +Le Hochet +Corupá +Ritterhude +Capela +Satyāmangala +Zozocolco de Hidalgo +Filiaşi +Fritzlar +Sabbah +Udiyāvara +Ban Ho Mae Salong +South Farmingdale +Ferreiros +Cuevas del Almanzora +Lebanon +Swallownest +Midar +Kutavettūr +Brasilândia +Bangui +Ban Mae Sun Luang +Puerto Salgar +Islāmpur +Boa Esperança do Sul +Netherton +Aruvikkara +Lockhart +Kaintragarh +Hudiksvall +Landsberg +Konārka +Shamsa +Potsdam +Westervoort +Busko-Zdrój +Issoire +Andorinha +Canet de Mar +Cassano delle Murge +Struga +Dionísio Cerqueira +Dembecha +Naduvattam +Saidpur +Alpedrete +Witzenhausen +Wallan +Novyi Buh +Sárvár +Barajor +Pavannur +Verdal +California City +Acala +Kezi +Râs Baalbek +Quthing +Manambondro +Milenaka +Soalala +Ambodiriana +Befasy +Kopoky +Ramainandro +Ambinanindrano +Ambatomanjaka +Andranovelona +Ianantsony +Analamary +Imanombo +Beroy Atsimo +Alarobia Bemaha +Talata Ampano +Ambatoharanana +Sahave +Bevoay +Anahidrano +Ambahive +Ifatsy +Ankisabe +Anjoma-Ramartina +Lokomby +Behisatse +Iharan̈a +Manandona +Antanimenabaka +Marofototra +Tsiatajavona-Ankaratra +Antsoso +Ambesisika +Ankilimivory +Antanifotsy +Wān Long +Kyaukmyaung +Aiyetoro Gbede +Amawom +Rāmechhāp +Bhimphedi +Salyan +Richmond +Mian Sahib +Awan Patti +Koungheul +Bakel +Yufle +Kuljibrīn +Sarmadā +Gammarth +Özdere +Kalongo +Matuga +Zombo +Mutukula +Chaguaramas +Clarines +Fayrōz Kōh +Taywarah +Barakī +Spitak +Villa Ojo de Agua +Bāisāri +Jamaica +Guayos +Villaviciosa +Saint-Avertin +Hessle +Tillor Khurd +Chettikulam +Aivanallur +Shafinagar +Damalcheruvu +Cortalim +Majhgawān +Hombal +Bellatti +Singhānwāla +Hullahalli +Muttamtura +Sathamba +Valattūr +Nedumpura +Turori +Khāndhār +Shirud +Galatge +Vasa +Barţalah +Amirlī +Yinhua +Winkfield +Talpa de Allende +Worthington +Henderson +Buda +Highland Park +Sanyi +Shambu +Huchuan +Anororo +Santa +Şüvəlan +Büttelborn +Antequera +Kisvárda +Shklow +Djambala +La Gloria +Pyryatyn +Kaguchi +Salinas da Margarida +Esbiaat +Sant’Arpino +Calanogas +Pestovo +Lapseki +Yelur +Māndleshwar +Āndipālaiyam +Obando +Kurichchi +Belkheir +Bouchegouf +Texenna +Petersfield +Casalmaggiore +Candói +Távros +Chirpan +Porcia +Castelnuovo Rangone +Laurinburg +Chrysoúpoli +Avanos +Great Falls +Mastic +Kenora +Kenley +Hārohalli +Douar Souk L‘qolla +Santa Brígida +El Hadjira +Tabant +Paralímni +Saint-Servan-sur-Mer +West University Place +Baucau +Cold Lake +’Tlat Bni Oukil +Veliki Preslav +Goluwāli +Lakeland North +Weigelstown +Santa Luzia +Metuchen +Bludenz +Vianópolis +Ādra +Paris +Monkey Bay +Sidi Zouine +Çatalpınar +Dolo +Coconuco +Finale Emilia +Sudbury +Wyke +Ramonville-Saint-Agne +Lambidou +Vallūr +Mitane +Hereford +Mendrisio +Bissendorf +Baiersbronn +Joaíma +Serhetabat +Grande Saline +Restinga Sêca +Majholi +Newtown +Avrillé +Ganjām +Świdwin +Kimyogarlar +La Flèche +Oued Jdida +Schiffdorf +Fairview +Calkiní +Chāndi +Riverdale +Iṭaharā +Jisr ez Zarqā +Malsch +Ahigbé Koffikro +Beckingen +Chemmanam +Gourrama +Nalerigu +Usingen +East Rancho Dominguez +Brooks +Yellāreddi +Lānjī +Sembedu +Lakkundi +Tecuala +Castañuelas +Nakagawa +Nerchinsk +Pursa +Washington +Kunnumēl +Krasnohorivka +Escaldes-Engordany +Lolotique +Lālejīn +Mykolaiv +Kew Green +Hickory Hills +Lagangilang +San Juan +Barra do Sul +Sardinal +Badarpur +Kela Khera +Ammūr +Aghbal +Vinanivao +Sindhnūr +Aizubange +Kentville +Castanet-Tolosan +Morlaix +Poděbrady +Torelló +Aiyampuzha +Holliston +Kurate +Lake Arbor +Vakhsh +Qumqo‘rg‘on +Chapelle-lez-Herlaimont +Beizhou +Quan’ancun +Cumayeri +Tsabit +Sisian +Coquimatlán +Naxxar +Nastola +Valangimān +Dimiao +Stolac +Swansea +Sassenheim +Jhandāpur +Sahakevo +Chemax +West Wickham +Parapatti +Cerro Maggiore +Kizel +Bohodukhiv +Tarumã +Branchburg +Bacuag +Przeworsk +Doukouya +Champāpur +Highland Springs +Ngolobougou +Grefrath +San Tomas +Lidzbark Warmiński +Sera +Le Raincy +Ālampur +Oldsmar +Greensburg +Shepshed +Nisko +Hanamsāgar +Fiorenzuola d’Arda +Sopelana +Breaza +Ravels +Udelnaya +Tayum +Harchoune +Bailleul +Hedehusene +Boulder City +Yuchi +Cujubim +Vellālāpuram +Barharwā +Ayní +Grecia +Tamani +Kondrovo +Chāilāha +Drahichyn +Chegdomyn +Ambérieu-en-Bugey +Nagarūr +Red Oak +Sterling +Qiaoyang +Mori +Forest City +Bromborough +Berlare +Salida +Niquinohomo +Jever +Chīpurupalle +Niedernhausen +Madhuban +Tremelo +Terra Rica +Gabane +Belmont +Avalpūndurai +Koumaïra +Huitán +Nzalat Laadam +Varnsdorf +San Mateo del Mar +Isny im Allgäu +San Vito +Bela Vista do Paraíso +Haga +Le Bourget +Chislehurst +Navashino +Jinta +Taurianova +Timissa +Illzach +Botelhos +Monterrey +Betsukai +Hariharpāra +Grândola +Tall Abyaḑ +Hückeswagen +Mercaderes +Kumāramangalam +Lemon Hill +Cabral +Vuhledar +Tendūkheda +Tadjourah +Nevel +Ponto Novo +Schwabmünchen +Cabañas +Cherrapunji +Port Antonio +Kumārīpur +Nenmini +Fort Payne +Tanque Verde +Kochkor-Ata +Warwick +Huixcolotla +Petrovaradin +Cândido de Abreu +Ilkley +Patrocínio Paulista +Józefosław +Radzyń Podlaski +Benipati +Comox +Astravyets +Ighrem n’Ougdal +Cerritos +Buerarema +Whitefish Bay +Ogíjares +Dehmoí +La Carolina +San Julian +Anta +Gryazovets +Al Mālikīyah +Weizhou +Muthallath al Azraq +Fameck +Sullivan +Devarapalle +Merate +Pālod +Città Sant’Angelo +Baguley +Waunakee +Kapiri Mposhi +Shuichecun +Dęblin +Stony Point +Łapy +Pattensen +Sozopol +Āfdem +Alexander City +New Paltz +Konina +Araputanga +Itamonte +Fiadanana +Lampa +Bni Darkoul +Oulad Dahmane +Armagh +Amasra +Sanquelim +Hilchenbach +Montmagny +Pontivy +Sítio do Quinto +Hanover +Laterza +Williamstown +Tabatinga +Washington +Clarksdale +Romsey +Kamrāwān +Piney Green +Adams +Romilly-sur-Seine +Zítsa +Curtorim +Harhorin +Hasanpur Juned +Shlisselburg +Selb +Hudson +Chitila +Lummen +Krasnogvardeyskoye +Villers-lès-Nancy +Diankabou +Bo’ness +Obanazawa +Tavares +Cherupazhasshi +Much +Luduş +Cumpăna +Haubourdin +Hirono +Matungao +Penrith +Maqat +Deming +Tiltil +El Ateuf +Vohipeno +Briniamaro +Balham +Pincourt +Mozarlândia +Teignmouth +Perunkolattūr +Sinmpérékou +Sasso Marconi +Danilov +Masallı +Entre Rios de Minas +Ijra +Eshowe +Friedland +Banbridge +Cocorná +Havre de Grace +Punata +Dennis +Driouch +San Juan del Sur +Törökbálint +Garhpura +Lilburn +Larkhall +Mount Vernon +Mississippi Mills +Ramsey +Youdiou +Vaddāpalli +Tepetzintla +Breukelen +Sukhinichi +Bodmin +Andūrkonam +Belokurikha +Çiçekdağı +Pilar +Labiod Medjadja +Sakhā +Río Negro +Hammonton +Pindaí +Chaabat el Leham +Pallasovka +Joubb Jannîne +Pine +Rellingen +Roseau +Great Bend +Loreto +Conwy +Neqāb +Matsushige +Arzgir +Greendale +Isla Vista +Muy Muy +Bāladharmāram +San Ġwann +Katyr-Yurt +Prestwick +Tsivilsk +Alagoinha +Douar Oulad Driss +Oulad Driss +Riofrío +Neustadt an der Donau +Sāhit +Antioch +Cēsis +Mediouna +Troon +Chhāpia +Androrangavola +Tarumirim +Paola +San Ramón +Uch-Korgon +Ochakiv +Triunfo +Purísima de la Concepción +Plérin +Shimubi +Santa Croce sull’ Arno +Cuarte de Huerva +Hima +Tirumalaiyampālaiyam +Hooksett +Gladeview +Kōteshwar +Harsefeld +Villa Alegre +Surbo +Ospitaletto +Nishinoomote +Onklou +Guilford +Tlaxcala +Carmen de Areco +Lope de Vega +Kodāngipatti +Baie du Tombeau +Groß-Zimmern +Kurumbalūr +Ankadinandriana +Collecchio +Coremas +Masty +Paramati +Hershey +Rubiera +Owosso +Melksham +Mahatsinjo +Ngoulemakong +Dinan +Ranomafana +Thuin +Laxou +Wibsey +Corridonia +Bar-le-Duc +Fraser +Lake Stickney +Mānantheri +Béré +Bellaa +Pudur +Malvik +Las Guáranas +Santa Marta de Tormes +Saint-Omer +Kushimoto +Dzuunmod +Kysucké Nové Mesto +Condoto +Ifrane +Piraziz +Tapes +Kamalganj +St. Clair +Rasulpur Dhuria +Złotoryja +Capinópolis +Mmopone +Djanet +At-Bashy +Langwedel +Malo +Kujūkuri +North New Hyde Park +Mulakād +Lognes +Great Baddow +Boguszów-Gorce +Kaonke +Sprimont +Ferrier +Mangamila +Mineral del Monte +Vubatalai +Gouré +Sisa +Técpan de Galeana +Limanowa +Néa Alikarnassós +Woudrichem +Mahinog +Bilozerske +Ibitiara +Tiruvāsaladi +Bílina +Sepīdān +Terrace +Ternitz +Cómbita +Farnborough +Rosà +Rio Piracicaba +Teixeira +Flower Hill +Pinia +Djebahia +Misaki +Mercier +Parlier +Lumino +Skipton +Chippewa Falls +Narragansett +Bahçesaray +Dahé +Abony +Alangānallūr +Zontecomatlán de López y Fuentes +Port Glasgow +Magione +Vredefort +Kalocsa +Groveton +Boston +Negreşti-Oaş +Cambuci +Guajiquiro +Kankipādu +Vemulūru +Roncade +Kiri +Plochingen +Paliaturutu +Bartabwa +Batuan +Huasuo +Castellammare del Golfo +Anzegem +Lapua +Langenselbold +Hatten +İspir +Tullamore +Lowton +Carmignano +Suō-Ōshima +Río Branco +Saint-Maurice +Sinha +Sveti Ivan Zelina +Hanover +Agidel +Arroio dos Ratos +Saint-Jean-de-Luz +Uzun +Cabarete +Nova Ponte +Verrières-le-Buisson +Montabaur +Wilbraham +Holalagondi +Jędrzejów +Tonya +Guastalla +Sātyūn +Villa Yapacaní +Yokoshiba +Kelamangalam +Dinmānpur +Hutchinson +Sangota +Palmeirais +Čajetina +Arnouville-lès-Gonesse +Sigulda +Thillangéri +Tostado +Balīgaon +Broadlands +Lucao +Ząbkowice Śląskie +Ágios Athanásios +Lauda-Königshofen +Tostedt +Aleksandrovka +Severínia +Red Bluff +Falls Church +Talya +Port Royal +Kānjikkovil +Key Biscayne +Tzaneen +Jitwārpur Nizāmat +Pineto +Wawizaght +Lakkampatti +Salem Lakes +Burbage +Edenburg +Münster +Palayad +Akora +Benfreha +Paniem +Breza +Kottacheruvu +Naivasha +Qazyqurt +Mocha +Jastrebarsko +Darpa +Debar +Essau +Tiruppāchūr +San Francisco +Relangi +Saint-Genis-Pouilly +Belvedere Park +Ban San Phak Wan Luang +Loviisa +Stein bei Nürnberg +Thorpe Saint Andrew +Andoain +Bijaipur +Bicas +Solila +Ratzeburg +Clarkston +Adekar Kebouche +Anamorós +El Paisnal +Wilsdruff +Biscarrosse +Halwāra +Altınekin +Iakora +Cecil +Marofinaritra +Gundlapelle +Merimandroso +Les Sables-d’Olonne +Ayt ’Attou ou L’Arbi +Stanytsia Luhanska +Ottweiler +Ban Du +Pindorama +Blidet Amor +Australind +Ust’-Ordynskiy +Jun Bel +Bom Jesus do Galho +Arbon +Bakun +Siswa +Abensberg +Nāgireddipalli +Moultrie +Rio Paranaíba +Falagueira +Sarmīn +Aïn Nouissy +Kusāha +Ban Kaeng +Tsimlyansk +Pualas +Kannamanāyakkanūr +Reidsville +Cadaval +Dargahān +Middlesex +Água Boa +Vitomiricë +Sha Kok Mei +Pandhāna +Herselt +Minignan +Corinth +Anapoima +Moston +Sonora +Saint-Fargeau +Pakaryā Harsidhi +Tabuse +Lebanon +Diéramana +Rebouças +Guachochi +Mbuyapey +Neu-Anspach +Immenstadt im Allgäu +Mosina +Montesson +Valea Lupului +Olsberg +McCalla +Quinapundan +Chelsfield +Oria +Aberdare +Suwāsra +San Martín de Loba +Eberbach +Farap +Angus +Nadisāl +Gavinivāripālem +Surajpura +Sadhoa +Sokone +Oyam +Guanduqiao +West Lincoln +Ceres +Yanhewan +Ouled Sidi Brahim +Ludvika +Tenente Portela +Água Fria +San Gabriel +Lingāl +Thornbury +Morton +Chinnavādampatti +Santa Catalina +Poço das Trincheiras +Uckfield +Lākho +Goshen +Uhingen +Barhni +Morris +Deysbrook +Harduli +Ahmed Rachedi +Askim +Murra +Madagh +Boula’wane +Araruna +Cehegín +Burglengenfeld +Krasnyy Kut +Flers +Valenton +San Carlos +Babhangāwān +Fujisaki +Mésa Geitoniá +Eisenstadt +Barroquinha +Nawada +Buggenhout +Turnov +Paraíso +Nampicuan +Qobustan +Haacht +Tlayacapan +Pākala +Markacho +Roh +Huizúcar +Khallikot +Lockhart +Santaquin +Ormeau +Tiruvalanjuli +Sommacampagna +Plumtree +Ichikawamisato +Armthorpe +Lagunia Surajkanth +Ankaramena +Yangasso +Lopik +Sassenberg +Santo Domingo Xenacoj +Larena +Southgate +Tarlapalli +Srīnagar +Scottsbluff +Sopetrán +Jasper +Santa María +Shatrāna +Robbinsdale +Gwanda +Jacaraú +Narahia +Bāra +Neubiberg +Brwinów +Roquebrune-sur-Argens +Asagiri +Short Hills +West Freehold +Tavagnacco +Monticello +Anderson Creek +Tulchyn +Chenôve +Nolensville +Lagoa +Alginet +Alloa +Aït Yaïch +Vinci +Verona +Eggenfelden +Chatham +Black Forest +Claxton Bay +Pahou +Jacaraci +Bokod +Ban Ao Nang +Barranco de Loba +Sirmaur +Araceli +Rauch +Ampohibe +Perai +Sainte-Maxime +Nether Providence +Busembatia +Eching +Kolwāra +Caln +Ascensión +Morroa +Baqiābād +Bagulin +Pilisvörösvár +Szarvas +Satwās +Aartselaar +Davorlim +Alūr +Ulchin +Dollis Hill +Painan +Dornakal +Lavaltrie +Villa Vásquez +Kalývia Thorikoú +Tirkarūr +Tagalft +Kharki +Cayey +Polyarnyye Zori +Mantaly +Korsør +Barhi +Spenge +Vadugapatti +Măgurele +Gorbea +Killarney +Brainerd +General MacArthur +San Sebastián +Saint-Brevin-les-Pins +Château-d’Olonne +Hopatcong +Halawa +Ghora Gali +Skippack +Pomorie +Chik Bānavar +Yéréré +Valbom +Paidiipalli +Itaú de Minas +Mutyālapalle +Minneola +Wymondham +Nagra +Haldībāri +Dumas +North Auburn +Lindenhurst +Baoshan +Hetton le Hole +Bo +Alindao +Tūprān +Wangjiaxian +San Antonio +Alexandria +Choszczno +Sierra Vista Southeast +Gernsbach +Sorso +Muqui +West Bradford +Chiconquiaco +Edgewater +Lubbeek +Lohfelden +Kihoku +Majia +Hargāwān +Üzümlü +Liestal +San Cristóbal +Stäfa +Rheydt +Anomabu +Zaysan +Mohanpur Gaughāta +Yamada +Küsnacht +Washington Court House +Maria +Kārempūdi +Amersham +Vāsad +West Derby +Lake Wylie +East Bridgewater +Ourtzagh +Puraini +Kamatgi +Natchez +Valday +Impruneta +Moorestown-Lenola +New Hamburg +Opwijk +Villa Rica +Saint Joseph +Zolote +Tezu +Ożarów Mazowiecki +Elizabethton +Doria Sonāpur +Penuganchiprolu +Ozark +Sailāna +Cowes +West Drayton +Nacozari Viejo +Rosarno +Churumuco de Morelos +Patiāli +Miena +Mercerville +West Nipissing / Nipissing Ouest +Barela +Riegelsberg +Santa Rosalía +Pineville +Sidi Azzouz +Markdorf +Volgorechensk +Serinhisar +Artsyz +Jitaúna +Miho +Alamo +Löbau +Thondiamannu +Simões +Tornesch +Rehlingen-Siersburg +Breyten +Zaouiat Moulay Bouchta El Khammar +Ihaddadene +Niederzier +Baulia +Bihpuriāgaon +Bradley Gardens +Livingston +Zhutang +Jiaoxiyakou +Arlington +Balangiga +Tianguistengo +Medvezhyegorsk +Narhan +Bhānukumāri +Wilkinsburg +Alto Longá +Strathmore +Pont-à-Mousson +Leno +Hindalgi +Monte Alegre de Sergipe +San Andrés Timilpan +Pazar +Conisbrough +Muttukūru +Belazao +Mittweida +Lakhsetipet +Hasami +Horta +Yanshuiguan +Brookside +Zapolyarnyy +Lakinsk +Mascali +Anpachi +Baia-Sprie +Saldaña +Pālda +Wālājābād +Vil’nyans’k +Klášterec nad Ohří +Rottingdean +High River +Gua +Jori Kalān +Tepecoyo +Polāia Kalān +Chikhli Kalān +Ustka +Sadovoye +Kottavalasa +Tenerife +Méru +Kizhariyūr +Saddle Brook +Balangkas +Estremoz +Saint-Paul-lès-Dax +Doctor Juan Eulogio Estigarribia +Kārkūdalpatti +Vīraghattam +Radzymin +Lotte +Salīmpur +Peumo +Iflissen +Rokycany +Sumbas +Lengede +Mūrak +Māngoli +Tsukawaki +Amay +Sarqan +Santo Antônio dos Lopes +Huliyār +Woippy +Fakola +Novohrodivka +Nagongera +Romitan Shahri +Sariosiyo +Kidbrooke +Roldán +Kambainellūr +Rock Ferry +Baruun-Urt +Coronel Bogado +Dayr al Barshā +Shirley +Neutraubling +Pollokshaws +Iona +Algarrobo +Algarrobo +Bad Camberg +Korangal +Omegna +Eski Īkan +Ambohimanga Atsimo +Orbetello +Našice +Sison +Chiaravalle +Chard +Anantarāzupeta +Bariariyā +Loganville +Bousso +Ban Don Kaeo +Grafing bei München +Brandermill +East Greenwich +Chicago Ridge +Shimada +Gualdo Tadino +Jericho +Ungutūru +Benito Juárez +Biały Kamień +Bikkavolu +Somerton +El Barrio de la Soledad +Puxinanã +Patcham +Santa Maria a Vico +West Lealman +Gantt +Yakhroma +Razan +Nkhata Bay +Mongeri +Ban Nong Kula +Ucar +Ghaura +Vedasandūr +Bedford +Laï +Pak Thong Chai +Ban Tha Thong +Ciénega de Flores +Farmington +Bökönbaev +Manoharpur +Det Udom +Saint-Gilles +Mionica +Bollullos par del Condado +Weston +Mannūr +Marechal Floriano +Bois-Guillaume +Morokweng +Poynton +Ait Yazza +La Carlota +Chikitigarh +Ben Taieb +Langerwehe +Haramachida +Levoča +Mondolfo +Svrljig +Imbaú +Rāje +Aïbongo +Maria da Fé +Prieska +Tafrant +Brazópolis +Seacroft +Kukdeshwar +Opera +Van Buren +Arvika +Kōdanād +Wingene +Horw +Brühl +Kelafo +Greenwood +Neftçala +Padavēdu +Manuel Ribas +Sabinópolis +Toubakoro +Sines +Saren +North Mankato +Knić +Mahavanona +Kāshti +Mapanas +Bainbridge +Ömerli +Pannimadai +Zhabinka +San Giorgio Ionico +Forfar +Nagardevla Budrukh +Little Falls +Bou Izakarn +Al Mu‘aḑḑamīyah +Uxbridge +Douar Hammadi +Jacksonville +Ochër +Borborema +Elk Plain +Kouroussa +Auray +Ishikawa +Pelham +Maldon +Terkuvengānallūr +Barkuhi +Bennane +Lich +Caldas +Crépy-en-Valois +Shichinohe +Aqköl +Wennigsen +Zeven +Chebrolu +Oleggio +Cedar Lake +Willowick +Aḑ Ḑab‘ah +Kearsley +Venëv +Mykolaivka +Mittegroßefehn +Wulong +Luling +Campos Altos +Antas +Tapolca +Kartuzy +Siddāpur +Ālangudi +Rescaldina +Minamiaizu +Fallsburg +Náfplio +Bensville +Pānr +Kaoma +Bochil +Assenede +Adustina +Midlothian +Boa Esperança +Maisach +Mill Valley +Saraykent +Feldkirchen +Tranås +Almoloya de Alquisiras +Miraíma +Kawaii +Haaren +Oschatz +Odlābāri +Shintō +Ayomi +Kalkar +Chesterton +Newport +Covington +Tamarakulam +Hobe Sound +Montelupo Fiorentino +Mankera +Juan de Acosta +Leyton +Puerto Deseado +Hautmont +Pānsemāl +Vinto +Murrhardt +Mackworth +Grenaa +Mnagueur +Huron +Castelsarrasin +Nallagunta +Tālbahat +Coolidge +Uslar +Pirangut +Bhulwāl +Edingen-Neckarhausen +Federal Heights +Bamble +Birqāsh +Sanlúcar la Mayor +Hythe +Feucht +Ashtead +Bua Yai +São José do Cedro +Mers el Hadjad +Orumanayūr +Santol +Mirandiba +Barwon Heads +Raghudebbati +Forest Park +Kātrāvulapalle +Douarnenez +Juan Aldama +Antarvedipālem +Dippoldiswalde +Plewiska +Jitwārpur Kumhra +Taki +Grajales +Staszów +Wombourn +Pulūr +Alto Santo +Shergarh +Buhuşi +Kutavūr +Clearview +Beronono +Narvik +São Martinho do Bispo +Sanjiang Nongchang +Sun Lakes +Nesebar +Rambha +Yuscarán +Lauchhammer +East Whiteland +Boutilimit +Ipiranga +Beekman +Thong Pha Phum +Ústí nad Orlicí +Massalubrense +Kerben +Massaranduba +Omiš +Storozhynets +Astolfo Dutra +Kakunodatemachi +Westmont +Sai Buri +Shepherdsville +Santiago Jocotepec +Collingswood +Uzhur +Sur Singh +Bad Ischl +Ipanguaçu +Villasanta +Tepoztlán +Belison +Kīlattingal +Luino +Valença +Gurinhém +Leirvik +Dallas +El Rodeo +Bovenden +Chāprā +Thônex +Naţanz +Matias Barbosa +Landau an der Isar +Port Lincoln +Lakewood Park +Charcas +Curuá +Nowy Tomyśl +Noya +Kisanuki +Candelaria +College Park +Powell +Siroda +Geldagana +Phulwār +Gudūr +Capitão Enéas +Varde +Fergus Falls +Lagoa Real +Pierre +Sibundoy +Wittstock +Jacksonville +Eraurā +Mŭ’minobod +Pélézi +Baños +Lachute +Trepuzzi +Niška Banja +Valentim Gentil +Kemp Mill +Doranahalli +Byāhatti +Castel San Giovanni +Oneonta +Belm +Bougzoul +Bela +Siderópolis +Titiribí +Hohenstein-Ernstthal +Kinogitan +Athens +Rājim +Rosemère +Beilen +Emerald +Adakplamé +Allouez +El Guetar +Sungai Guntung +Tân Vạn +Sidi Ben Adda +Indiana +Dunmore +Aldenhoven +Tamār +Ershui +Jodoigne +Aiuaba +Koundian +Hidalgo +Ardmore +Oostakker +Italva +Lagoa dos Gatos +Bezou +Neuenkirchen +Jefferson Valley-Yorktown +Ponnampatti +Caudry +Andalgalá +Bishops Cleeve +Ban Mueang Nga +Santiago de Anaya +Rotenburg an der Fulda +East Wenatchee +Nantucket +Sumbal +Tibasosa +Zazafotsy +Arenoso +Talwat +Sjenica +Nina Rodrigues +Montalvânia +Vammanal +Arboga +Grünstadt +Kerugoya +Pedro Afonso +Bēylul +Teploklyuchenka +New Ulm +Sāsan +Ezequiel Montes +Fisciano +Ayni +Moudjbara +Pilas +Thonotosassa +Ahrensfelde +El Socorro +El Carmen de Atrato +Karugamād +Frunze +Gouveia +Chełmża +Karlivka +Khadra +Nantwich +Cojedes +Mazamitla +Río de Oro +Berettyóújfalu +Wauconda +Verona +Santa Catalina +Itakura +Sidi Abdelkarim +Nelas +Águas Vermelhas +Daean +Ostrzeszów +Balassagyarmat +Kottaiyūr +Mbini +Amāri +Beloeil +Cirò Marina +Suknadānga +Kārai +Ūttukkottai +Rio Azul +Mignouré +Barāri +Primrose +Itaguaçu +Sharonville +Hazel Grove +Kīāshahr +Gafanha da Nazaré +Wezembeek-Oppem +Petrolândia +Tierra Amarilla +Fāmenīn +Santa Adélia +Esquipulas Palo Gordo +Morpeth +Circleville +Antohobe +Lakoucun +St. Helens +Uttoxeter +Sibirila +Barjora +Taguatinga +Leuna +Ollioules +Ilampillai +Hawaiian Gardens +Haivoron +Keāl +Hokuei +Turuttiyad +Selmana +Rānāpur +Riposto +Córdoba +Ricaurte +Solhan +Marlow +Alewah +Fujikawa +Martinho Campos +Rājāsūr +Busto Garolfo +Candeias +Beraketa +Ambahita +Mahavelona +Marotsiraka +Mahajamba +Ambatomarina +Antanambaobe +Kalandy +Bekipay +Marokarima +Fiadanana +Mahatsinjony +Vanono +Talata-Vohimena +Vatolatsaka +Belambo +Miarinarivo +Beharona +Ambohimitombo +Tsarahasina +Bevato +Ankiliabo +Amborondra +Bealanana +Sahalanona +Ambodihara +Ilha de Moçambique +O‘nhayot +Shofirkon Shahri +Rafaï +Takerbouzt +Thouars +Brahmānandapuram +Tall Banāt +Milhã +Meilen +Montfoort +Persan +Kamituga +Beach Park +Schneeberg +Annāram +Şaḩnāyā +Cristópolis +Lone Tree +Tulle +Powell +Novoulyanovsk +Coribe +Koundara +Bandraboua +Lorsch +Dubovka +Pathum Thani +Taiyong +Shāndīz +Matane +Donji Vakuf +Mirzāpur +São João d’Aliança +Kongarapalli +Pāli +Ambohimiera +Chumpak +Bebra +Fleury-Mérogis +East Norriton +Pemberton +Kuraymah +Ganguvārpatti +Beiwusidui +Thames Centre +Felixlândia +Mirinzal +Oud-Turnhout +Jardim de Piranhas +Erbach +Sutihār +Boha +Santa Branca +Kambila +Mankal +East Milton +Söğütlü +Raydah +McFarland +Beasain +Ovidiu +Merošina +Thala +Soledade +Drapetsóna +Goianápolis +Sakaki +Lübben (Spreewald) +Canteleu +Rājnagar +Grass Valley +Roselle Park +Pacaembu +Hajdúsámson +Bedfordview +Mjölby +Kargıpınar +Méridjonou +Chaugāin +Detva +Eynesil +Nova Ipixuna +Alfredo Chaves +McPherson +Bolívar +Lyndhurst +Kępno +Belwāra +Amāha +Havelock North +Kanākir +Bambang +La Entrada +Belsandi Tāra +Llanera +Cassina de’ Pecchi +Misano Adriatico +Bothell East +Perdūr +Warrenville +Audincourt +Narasingapuram +Powell River +Furukawamen +Wells Branch +Marostica +Carleton Place +Ichtegem +Castellanza +San José Acatempa +Bang Racham +Attibele +Aracatu +Sesquilé +Camp Pendleton South +Plumstead +Brierley Hill +Tsingoni +Ibicuí +Tarhzout +Montévrain +Streetly +Lower Moreland +Vöhringen +Hunasagi +Waimalu +Höchstadt an der Aisch +Grünberg +Locorotondo +Nachikatsuura +Zhipingxiang +Cossato +Nyzhnia Krynka +Khe Sanh +Abre Campo +Gesuba +Sukma +East Cleveland +Julianadorp +Diego de Almagro +Aßlar +Destin +Çaybaşı +Vīraganūr +Temsamane +Papagaios +Mansidão +Royal Kunia +Oued Taria +Alterosa +Lons +Maracaí +Weißenhorn +Huntington +Berre-l’Étang +Muttunāyakkanpatti +Khergām +Denzlingen +Kelangāh +Chudovo +Alto Paraná +Oberhaching +Sankt Leon-Rot +Debre Werk’ +Melsungen +Midleton +Moralzarzal +Buchloe +Bickley +Kubādupuram +Guelph/Eramosa +Krishnarāyapuram +Wood Dale +Santo Augusto +Tucacas +Grão Mogol +Bukedea +Mogeiro +Hall in Tirol +Orikhiv +Dumri +Bhogpur +Salaverry +Shiloh +Jīran +Olean +Bog Walk +Galmi +Meerane +Tidjelabine +Joaquim Pires +Torrijos +Polukallu +Quakenbrück +Rustington +Tumbippādi +Philippsburg +Eastham +Areado +Simplício Mendes +Makokou +Cefalù +Jabbeke +Lauterbach +Aurora +Sāgar +Caselle Torinese +Hockessin +Sido +Sunset +Matiçan +Friesenheim +Beacon +Tāti +New Franklin +Kettering +Hòa Thượng +Ettenheim +Torre del Campo +Ping’anbao +Villalba +Linganaboyinacherla +Alcanena +Taphan Hin +Hilpoltstein +Ocaña +Binkolo +Tracunhaém +Wakasa +Richland +Oulad Chikh +Billère +Porto Alegre do Norte +Ibipitanga +Tamalameque +Kishanpur Ratwāra +Artondale +Polavaram +Qiblaí +Manoel Vitorino +Tarauna +Avanhandava +Erbach +Shtërpcë +Kingstown +Huangyadong +Guamal +Weno +Waycross +Luozi +Saboeiro +Solaro +Zhashkiv +Carmópolis +Markt Schwaben +Grand Falls +Toulal +Zhovkva +Iskapalli +Millbury +Polotitlán de la Ilustración +San Rafael Las Flores +Amantea +Bellair-Meadowbrook Terrace +Parihāra +Dogāchi +Dryden +Carapebus +Ōki +Itaguara +Beachwood +Batuan +Michendorf +Māndalgarh +Trinitapoli +Fujimi +Mount Barker +Kāuriya +Ribeiro do Amparo +Hīdaj +Khmis Sidi al ’Aydi +Primavera +Worthington +Gangaikondān +Gekhi +Saloá +Barra do Mendes +Tomiño +North Battleford +Paripueira +Valayam +Fara in Sabina +Kamigōri +South Sioux City +Haidarnagar +Vellodu +Cosham +Sauk Rapids +Vembārpatti +Strada +Nules +Mayrtup +Pedro Velho +Olympia Heights +Maumee +Bad Soden-Salmünster +Enniskillen +Khajawa +Seirō +Trbovlje +Pigüé +Clemencia +Wayland +Irikkūr +Haßfurt +Teays Valley +Skoczów +eManzimtoti +Wanzleben +Wörgl +Anadia +Lakeland +Schwaz +Colangute +Forest Hill +Turuvanūr +Dowbarān +Lieusaint +Calafat +Magalhães de Almeida +Meckenbeuren +Krumbach +Littleborough +Ewarton +Channahon +Nawāgarh +Plav +San Antonio de las Vueltas +Pedernales +Passo de Camarajibe +Tako +Braselton +Koppies +Itagi +Gangavalli +Bedford +Graça +Villas +Kamnik +Boyovut +Tachov +Unión de Tula +Horodyshche +Pierrelatte +Kivertsi +Rønne +Achuapa +San Juan +Assaí +Vargem da Roça +Carmo de Minas +Iguatemi +Löningen +Bideipur +Acajutiba +Rixheim +Sarāyān +Tibri +Arbaa Laaounate +Anapurus +Lys-lès-Lannoy +San Ġiljan +Oftringen +Ipswich +Itapetim +Hünxe +Ban Bang Muang +San Rafael Petzal +Ban Tha Luang Lang +Akropong +Bellefontaine +Roncq +Siemiatycze +Couvin +Jinmingsi +Rosyth +Sannicandro Garganico +Mont-Laurier +Kupino +Higashimiyoshi +Yulee +Madānpur +Simri +Ampasinambo +Zhongliao +Caçu +Chaoyangdicun +Auburn +Lomma +Guapé +Torul +Morwell +Santa Luzia +Gräfelfing +Varedo +Arugollu +Berceni +Mariánské Lázně +Saint-Orens-de-Gameville +Kirkwood +Gouka +Feliz +Leverano +Snoqualmie +Tampamolón Corona +Taggia +Kodigenahalli +Willow Grove +Eppstein +Kashin +Singalāndāpuram +McMinnville +São Pedro do Piauí +Gülchö +Cayce +Bandarbeyla +Amrābād +Travagliato +Lipno +Nehrəm +Chityāl +Megarine +Orange Walk +Frankenberg +Holzgerlingen +Kannivādi +Waianae +Douar Lehgagcha +Jardín +Settara +Moissac +Uruoca +Rostamābād +Central Elgin +Itinga +Flores de Goiás +Urupês +Nawāda +Manavālakurichi +Barghāt +Msata +Forster +Hawick +Mata +Badarwās +Tacaimbó +Beverstedt +Gopālpur +Bondeno +Ephrata +Zarghūn Shahr +Sidmouth +Ermoúpoli +Hazel Crest +Ataléia +Futrono +Lyaskovets +Guaraniaçu +Priverno +Enghien +Urbino +Kudachi +Palkūr +Bodagudipādu +St. James +York +Angical +Ibateguara +Taglio +Darihat +Uruana +Saint-Martin-de-Crau +Miami Springs +Malepur +Nova Trento +São João do Triunfo +Alagoinha +Milford Haven +Xireg +Kidlington +Arcadia +Sobrāon +Nonoai +North Fair Oaks +Mistassini +Tāmaraikkulam +Rocky Point +Wezep +Nova Canaã +Lamont +Saugeen Shores +Eichstätt +Sher Chakla +Marienheide +Knottingley +Ipecaetá +Tummalapenta +Moberly +Mions +Shorewood +Kovilūr +Crevalcore +Cêrro Largo +Ndélé +Kaleyānpur +Coldwater +Colesville +Biedenkopf +Quatá +Kingston +South Park +Montataire +Toyloq Qishlog’i +Sariq +Papágos +Mohda +Presidente Kennedy +Tiruppanandāl +Arona +Hinabangan +Ingersoll +Kuleshovka +Auriflama +Angermünde +Valencia West +Diaï Koura +Vempalle +Kraainem +Boa Nova +Itapagipe +Uarini +Las Rosas +Mangha +Bergen +Santa Cruz de Bezana +Castel San Giorgio +Tarumizu +Marshall +San Pedro Pochutla +Radomyshl +Genthin +Chak Husaini +Schmölln +Glenpool +Quatis +Palmito +Motupe +Cambados +Karma +Maribondo +Hettstedt +Kannadiparamba +Copalchí +Kalādgi +Slobozhanske +Madanpur +Sansare +Sale +Sendenhorst +Saint Ann’s Bay +Yongcong +Normandia +Altos del Rosario +Bhīmadolu +Duvergé +Otley +Río Caribe +San Ferdinando di Puglia +Chiran +Mucambo +Poté +Nova Veneza +Svetlogorsk +Aldama +Jindayris +Nikolayevsk +Qantīr +Avon +Latham +Bad Dürrheim +Namīn +Fiesole +Warrensville Heights +Xikou +Isāgarh +Smithfield +El Crucero +Saint-Jacques-de-la-Lande +Chōsei +Piên +Barahari +Ajjampur +Bilaspur +Glen Carbon +Mirabela +Retirolândia +Avalēpalli +San Agustín de Guadalix +University Heights +Jurema +Domkonda +Curiúva +Marshall +Shōdoshima +Speedway +Lanuza +Umburanas +Alexandria +Helensburgh +Childwall +Woudenberg +Pedreiras +Beauharnois +Kostolac +El Kerma +Beeville +Salisbury +Frei Miguelinho +Abadla +Marion +Port Neches +California +Hammam el Rhezez +Milovice +Santa Coloma de Farnés +Bunhe +Manta +Kunjatturu +Dantewāra +Uitgeest +Trzebnica +Pitlam +Spring Garden +Sampaloc +Jiquiriçá +Šamorín +Oak Park +Solonytsivka +Balhāpur +Rockland +Bamafélé +Boninal +Engenheiro Caldas +Allestree +Ottaikkālmantapam +Mahuwa Singhrai +Saimbeyli +Yeşilli +Jaca +Maglie +Babhanganwa +Brecksville +Ihtiman +Santa Luzia do Itanhy +Pfullendorf +Artena +Shamalgan +Serra Branca +Baianópolis +Primeira Cruz +Formello +Trubchevsk +Orocó +Brejo do Cruz +Lumberton +São Félix do Araguaia +Mantenópolis +Verín +Kyzyl-Adyr +Castilla La Nueva +Sóller +Kauhajoki +Yamanobe +Rožaje +Tejutla +Elesbão Veloso +Karpi +Kerepestarcsa +Masangshy +Combarbalá +Bollène +San Javier +Marchtrenk +Qingxicun +Chengara +Amboasary-Gara +Dongshicuo +Barßel +Atner +Mohania +Wyandanch +Marhamat +Vedi +Vázquez +Nighoj +Jannat Shahr +Daniëlskuil +Rāyapalle +Rāzole +Guerrero Negro +Sisai +Duvva +Cañasgordas +Middlewich +Okinoshima +New Providence +Whitby +Amīngarh +Tapilula +Doumé +Ixcatepec +Ráquira +Łęczyca +Nybro +Pirallahı +Camapuã +Atomé-Avégamé +Beaufort +Ak-Dovurak +Rommerskirchen +Sūrān +New Carrollton +Pama +Gonzalez +West End +Zhukovo +San José Villanueva +Wanze +Neckargemünd +Gondalga +Dueville +East Islip +Piquete +Marghita +Cervignano del Friuli +Tokoroa +Upanema +Patālia +Royal Wootton Bassett +South Stormont +Bridport +Gran +Presidente Bernardes +Lappersdorf +Serui +Bardstown +Latiano +Dattapulia +Pegnitz +Sharm ash Shaykh +Dhāna +Bécancour +Winsum +Palestina +Newington +Asperg +Ashiya +Talne +Nālatvād +Seabrook +Rossington +Bandar-e ‘Asalūyeh +Western Springs +Lāla +Echelon +Haselünne +Hilsea +Kurtkoti +Stolin +Nesconset +Plover +Clifton +Bishenpur +Chinique +Mór +Ugo +Oosterzele +Niaogho +Rahui +Karnāwad +Tamanique +Hola Prystan +Kaharlyk +Dinant +Mascote +Alagoinha +Castelfranco di Sotto +Endicott +Wentorf bei Hamburg +Rukhāe +Martinsville +Kheri Naru +Lexington Park +Bexley +Beur +Kharv-e Soflá +Amriswil +Thisted +Wschowa +Hemsworth +Albertirsa +Jānapādu +Spaichingen +Koubel-Koundia +Shangping +‘Alem T’ēna +Rheinfelden +Corabia +South Charleston +Bridgetown +Dārat ‘Izzah +San Jorge +Llanes +Antigua +Pfarrkirchen +Eatontown +Tovuz +Bad Salzdetfurth +Borgaon +Conway +Vaires-sur-Marne +General Belgrano +Novi Iskar +Calolziocorte +Kafr Nabūdah +Tauberbischofsheim +Çayıralan +Adh Dhakhīrah +Nynäshamn +Menaa +São Ludgero +Lourdes +Lewisville +Nova Varoš +Ban Bung Kha +Strabane +Kodriva +Głowno +Hlučín +Avidha +Mqam at Tolba +Ban Mae Kaluang +Corozal +Wiesmoor +Calderara di Reno +Kudrovo +Perungala +Mŭynoq +Siriāri +Karuvellūr +Jefferson +Hard +Andasibe +Wolfhagen +Shawangunk +Ganxi +Chimá +Kenadsa +Sikat +Courcouronnes +Nemocón +Cajabamba +Hantsavichy +Gárdony +Chandla +Khānāpur +Bayt Jālā +Baras +Stezzano +Genas +El Hachimia +Marudūr +Squinzano +Kriva Palanka +Gines +Gudikallu +Vāghodia +Chalma +Schermbeck +Sukhāsan +Severn +Afzala +Fereydūnshahr +Ilamatlán +Strand +Al Ḩā’ir +Caapiranga +Plouzané +Bonita +Itaeté +Madakkathara +Fontenay-le-Comte +King City +Avocado Heights +Little Hulton +Jamira +Udayagiri +Elanad +Kesariyā +Yedapalli +Pālakkuzhi +Stroud +Bad Laasphe +Chembrasshēri +Sterling +São João do Araguaia +Dundigal +Panjgirāin Kalān +General Viamonte +Lake Forest Park +Fruita +Los Alamos +Nagnur +Nallajerla +Ascensión +Manatí +Gullapuram +Moncks Corner +San Juan de Limay +Fontenay-le-Fleury +Villanchirai +Irondale +Callaway +Barro Alto +Valdemorillo +Mamborê +Beesel +Dok Kham Tai +Hévié +Bou Sfer +Bhakua +Kulharia +Taneichi +Sébékoro +Plougastel-Daoulas +Clinton +São Pedro da Água Branca +São Simão +Kadattūr +Fort Lewis +Harwich +San Juan de Betulia +Dakit +Orkney +Péfka +Zábřeh +Englewood +Uibaí +Ouargaye +Tergnier +Mallet +Tucapel +San Jacinto del Cauca +Chinna Mushidivāda +Monteroni di Lecce +Franklin Park +Golyshmanovo +Jucuarán +Ādīs ‘Alem +Avenal +Versoix +Anzin +Germasógeia +Veglie +Neustadt +Dahāria +Ghordaur +Náfpaktos +Sultanpur Mor +Chaddesden +Kotgīr +Hopewell +Dinklage +San Rafael +Talegaon Dhamdhere +Srebrenica +Basāon +Sault Ste. Marie +Azle +Tuchola +Sítio do Mato +Sunjiayan +Vellarivalli +Benaulim +Melenki +Richterswil +Parihārpur +Zawodzie +Dyykan-Kyshtak +Alleroy +Akhty +Seyitgazi +Santa Bárbara +Chahbounia +San Juan +Vieste +Villa Unión +Ruza +Gaundrā +Inawashiro +Ban Don Thong +Heswall +Kalārdasht +Mosgiel +Kaabong +Kaiwen +Dalkeith +San Juan La Laguna +Amarāvati +Kilgore +Arklow +Uppidamangalam +Panapākkam +Velānganni +Testour +Lacombe +Balsa Nova +Connersville +Goudomp +Kokri +Drøbak +Amblecote +Arruda dos Vinhos +Vikravāndi +Joppatowne +Targuist +Kissane Ltouqi +Abhwar +Alsager +Gien +Telmar +Butler +Pottsville +Lislique +Besārh +Palmetto +Bouzina +Sanankoro Djitoumou +Aconibe +Fåberg +San Jacinto +Atlantic Beach +Urziceni +Amondara +Canton +Chīmalapādu +Gołdap +Luís Antônio +Sülüktü +Várzea Nova +San José de Maipo +Ribeira Brava +Paruchūru +Sainte-Sophie +Wake +Urbana +Boechout +Lincoln +Margraten +Kawatana +Feldbach +Saint Budeaux +Corsham +Oxford +Grez-Doiceau +Bāgli +Zollikon +Calle Larga +Marietta +Barka Parbatta +Hatoyama +El Almendro +Arzachena +River Ridge +Yengema +Washington +Jupiter Farms +Zehak +Hösbach +Słupca +Mangalapur +Amity +Qigexingcun +Lake Station +Makwassie +Egg Buckland +Mucur +Tiorpāra +Port-Alfred +Bijai +Sabana Iglesia +Kotor +Cadereyta +Zbarazh +Peabiru +Nattappettai +Sigli +Shetpe +El Carmen +Vemulapūdi +Carnot-Moon +Payyanadam +Człuchów +Andrews +Buli +Bedburg +Barlinek +Oboyan +Karapürçek +Pazhanji +Henderson +Charlton +Mangalāpuram +Rezzato +Serrolândia +Yantzaza +Marungūr +Weilburg +Rājpur +Alzano Lombardo +Mountain Park +Kumlu +Ubstadt-Weiher +Chandi +La Grange Park +Sant’Angelo Lodigiano +Sweden +Hillcrest +Puerto Morazán +Itapuí +Val-des-Monts +Tórshavn +Amarpura +Santa Maria a Monte +Ashton +Dambal +Blue Ash +Okkerneutboom +Tiruvārpu +Pataili +Bacup +Dohta +Beamsville +Hanımçiftliği +Broussard +Saint-Amable +Bachhauta +Kiiminki +Carluke +Kiskőrös +Franconia +Manerbio +Kuurne +Santa Marcela +Morąg +Wąbrzeźno +Dumariā +Farādonbeh +Kingston +Shibām +Auburn +Coatesville +Buşrá al Ḩarīr +Düzköy +Nosivka +Ostwald +Chifubu +Ampthill +Sidi Abd el Moumene +Nārāyangarh +Ban Na Chom Thian +Coomera +White House +Ambodilazana +Grimstad +Velen +Kāgvād +Rosas +Manching +Saarwellingen +Bouskene +Torredonjimeno +Makarska +Kadena +Woodinville +Nishon Tumani +Charxin +Shirebrook +Shiremoor +Mount Vernon +Berck-sur-Mer +Tounfit +Goh +Reeuwijk +Ponte San Giovanni +Elfers +Penrith +Sirhāli Kalān +Pushing +Ratnāpuram +Undrājavaram +Clayton +Dębno +Maní +Argentan +Woodland Park +Soyaniquilpan +Badhoevedorp +San Rafael Oriente +Arendonk +Weinsberg +Cremlingen +Oued Tlélat +Padaivedu +El Tabo +Berezne +Catemu +Reggada +Zehdenick +Aginiparru +Rogers +Aguilar +Bayt Sāḩūr +Bocaiúva do Sul +Hüllhorst +Annāmalainagar +Kūhbil +Pānchgani +Terebovlya +Guillena +Fatehpur +Vetralla +Gura Humorului +Standish +Ang Thong +Carros +Xylókastro +İkizce +Ettāpur +Blytheville +Tummanatti +Montepulciano +Schüttorf +Thame +Ergolding +Ŭrtaowul +West Boldon +Raghunāthpur +Outreau +Portage La Prairie +Joaquim Nabuco +Buttar +Żnin +Sulingen +Modakkurichchi +Lincolnwood +Sečanj +Amilly +Ban Chomphu Nuea +Bugugoucun +Skoghall +Tarhūnah +Tanagura +Rābor +North Greenbush +Los Chiles +Peringom +Naranja +Baş Göynük +Linlithgow +Knutsford +Aldeia de Paio Pires +Torquay +Liangwancun +Karivalamvandanallūr +Doddipatla +El Tablón +Tala +Īnderbor +Orange City +Kings Park West +Olfen +Peshkopi +Arês +Steamboat Springs +Trophy Club +Montespertoli +Muragācha +Khoyniki +La Victoria +Herbrechtingen +Fehmarnsund +New Kingman-Butler +Loveland +Dingle +Sumbha +Empedrado +Paraíso do Norte +Usumatlán +Cornelius +Ad Duraykīsh +Kuttyattur +Puerto San Martín +Pé de Serra +Ortaklar +Chhapra +Östringen +Lubsko +Gangelt +Manivilundān +Šternberk +Dharmaram +Beverungen +Lawang Bato +Ulvila +Daulatnagar +Moody +Aïn Youcef +Nanjai Kilābādi +Dharphari +Casandrino +Casatenovo +Kasumkent +Borodyanka +Damaishan +Andrésy +Perladka +Ivaí +Santa Juana +Gvardeysk +Glendale +Sebt Labrikiyne +Sābang +Pervomaysk +Afir +Mahalpur +Gopālasamudram +Mesudiye +Slobozhanske +Sarria +Chiroqchi +Nioaque +Basseterre +Orangeburg +Beelitz +Jūla Buzarg +Palos Verdes Estates +Yakage +Muriedas +Pácora +Arsali +Mathurāpur +Bichkunda +Shin'onsen +Fátima +Blegny +Lariano +Morangis +Yamato +El Kansera +Kathevaram +Khiria +San Martino di Lupari +Dorado +Walsall Wood +Ambohidronono +Bondāda +Chulumani +Beyla +Værløse +Novellara +Noceto +Douar Sidi Laaroussi +Xaafuun +Çamardı +Benairia +Tonakkal +Guttal +Kuressaare +Trebur +Aïn Lechiakh +Tempio Pausania +Southwick +Las Vegas +Bombarral +Chenggong +Bundāla +Gmunden +Nemours +Hissāramuruvani +Cheval +Liedekerke +Jobat +Vila Pouca de Aguiar +Carpinteria +Ban Khlong +Rio de Contas +Villacidro +Castelnuovo di Verona +Mārathalli +Zayda +San Clemente +Belëv +Oncativo +Parvomay +Fraserburgh +Püspökladány +Cedar Grove +Kingersheim +Sovetskoe +Auria +Portland +Bischofsheim +Hashikami +Cariamanga +Santa Teresa +Bagadó +Mānikpur +Mouvaux +Weddington +Newton +Plaza Huincul +Melilli +Druskininkai +Bou Arada +Penamalūru +La Llagosta +Qal‘eh Ganj +Casale sul Sile +Uyar +Graulhet +Leżajsk +Medway +Hājan +Canals +Santa María de Ipire +Ceyu +Samālsar +Ridgefield Park +Santa Margarita +Miahuatlán +Vestby +San Jerónimo +Ban Chorakhe Samphan +’Aïn Mouilah +Zabaykalsk +Harrison +Villa Juárez +Villa Sandino +Mulungu do Morro +Capaya +Igrapiúna +Mahao +Matsushima +Drochia +Karabanovo +South Glengarry +Hulkoti +Santa María Jalapa del Marqués +Tinglayan +Caerfyrddin +Pell City +Nako +Rio do Antônio +Superior +Autun +Ban Nong Hoi +Singuilucan +Głuchołazy +Chañaral +Vigodarzere +Sharon +Alovera +Balya +Hamworthy +Bajala +Asjen +Mutterstadt +Crispiano +Bålsta +Sali +Pacatuba +Radomir +Kaua Kol +Sainte-Marie +Novi Bečej +Latisana +Trenton +Bom Princípio +Shīnīlē +Gothva +South Daytona +Bensekrane +North Perth +Monteux +Sarezzo +Ketsch +Qulan +Kofelē +Tetāri +Kaspiyskiy +Saktipur +Ksibet el Mediouni +Ba Chúc +Sankt Georgen im Schwarzwald +Sátoraljaújhely +Ayodhyāpattanam +Plattling +Cicuco +Chino Valley +Berkeley Heights +Sidmant al Jabal +San Vicente +General Enrique Mosconi +Brieselang +Dover +Giporlos +Alvorada D’Oeste +Silago +Koregaon +Mahaboboka +Maqsūda +Tubmanburg +Altos +Iraci +Wehr +Betatao +Treuchtlingen +Barhī +Toura +Rosbach vor der Höhe +Miarinarivo +Brig-Glis +Quattro Castella +Phalaborwa +Bergen +Sargūr +Shitāb Diāra +Kanabūr +Kallūr +Gland +Wantage +Rājupālem +Grand Terrace +São Domingos +Tirumakūdal Narsipur +Pavona +Mounds View +Fyzabad +Kozy +Miranda do Corvo +Mazatlán Villa de Flores +Kavitāl +Medapādu +Lake Los Angeles +La Grande +Barsbüttel +San Martín Zapotitlán +Kottāram +Mercedes Umaña +Chos Malal +Albatera +Wollaston +Taishi +North Palm Beach +Güçlükonak +Soddy-Daisy +Stoughton +Wolvega +Joure +Pomfret +Itano +Harrison +Al Jumaylīyah +Bocholt +La Vega +Tōnoshō +Akseki +Küssnacht +San Antonio del Tequendama +New Hanover +Phra Samut Chedi +Betanzos +Darłowo +Ionia +Horst +Andaraí +Caldeirão Grande +Great Driffield +Gex +Gurlapeta +Takkolam +Çavdır +Simijaca +Chandhaus +Udaipur Bithwār +Kamikawa +Krasnozavodsk +Peddavadlapūdi +Ekwāri +Huguan Nongchang +Nasiyanūr +Hungen +Motobu +Oatfield +Gouandé +Fannūj +Cherry Hill Mall +Dărmăneşti +Apostolove +Pendlebury +Goodmayes +Gorakhpur +Makabana +Killai +Scheeßel +Kalikiri +Pallappālaiyam +Cotegipe +Sudbury +Jālhalli +Dinbéla +Adi Keyh +Milnrow +Ribeirão do Pinhal +Paina +West Carrollton +Cambridge +Kuruman +Hajdúhadház +Balua +Mohgaon +Norwalk +Kafr ‘Awān +On Top of the World Designated Place +Lamaçães +Muri +Castlebar +Rabot +Balatonfüred +Nidzica +Alto Garças +Dom Feliciano +Vetlanda +Godella +Gamboula +Forlimpopoli +Kani-Bonzon +Alcácer do Sal +Chernigovka +Afanyangan +Monte Belo +Danga +La Crucecita +Manturovo +Turvo +Pandireddigūdem +Bedford +Hadamar +Fatehpur +Hawaiian Paradise Park +Aberystwyth +Drongen +Rasauli +Mettet +Velykodolynske +Dahu +Charābidya +Thompson +Turbihāl +Palmital +Olivar Bajo +Olivar +Rödental +Tezontepec +Stjørdalshalsen +Lowshān +Shahr-e Herāt +Nova Gorica +Capo d’Orlando +Ajas +Fernandina Beach +Dārāsuram +Neykkārappatti +Missões +Holborn +San Pietro Vernotico +Dayālpur Sāpha +Barāon +Caledon +Shīn +Bolongongo +Neuri +Dinshawāy +Brackley +Claremont +Ubaporanga +Fatehpur +Bārah +Candiba +Tetiiv +Linnich +Leibnitz +Peddakūrapādu +Gangoli +Bohmte +Kalaiyamputtūr +Belpukur +Forrest City +Ampanavoana +Thara +Montgomeryville +Chandauli +Sidhwān +Kaji +Wasserburg am Inn +Lawaan +Quzanlı +Webb City +Kahhalé +Ambararata +Marovandrika +Morafeno +Andreba +Marovato +Ampitasimo +Ambohimahavelona +Isaka-Ivondro +Ambodiampana +Andranomamy +Ihorombe +Mahatsinjo +Antongomena-Bevary +Antsirabe Afovoany +Sahavalanina-Antenina +Belemoka +Mavorano +Evato +Tranovaho +Amborompotsy +Ambalakindresy +Ambahoabe +Vohitrindry +Befandriana +Ampanefena +Ankilivalo +Anjiajia +Ambatondrakalavao +Kirano +Sakoabe +Maroviro +Ambakireny +Tsiately +Ambohitsilaozana +Ambazoa +Ambodisakoana +Bemanevika +Ambondrona +Ambatolampy +Bejofo +Manambolo +Mangindrano +Ankilimalinika +Sandrakatsy +Marojala +Ilafy +Morarano +Mandritsara +Befeta +Amboaboa +Manambidala +Andohajango +Ḩarmah +Sunne +Kumi +Alaverdi +Neunkirchen +Midsomer Norton +Galashiels +Jalam +Ghorādongri +Moviya +Amauna +Dammennu +Timberlake +Divnoye +Lavello +Bhalil +Ksar Sghir +Vieira do Minho +Bocas del Toro +Jennings +Manalalondo +Miami +Sebt Bni Garfett +Anandāpuram +Waconia +Gornalwood +Lohiyār Ujain +Ficarazzi +Baduriātola +Kottapadi +Kara-Kyshtak +Chhāra +Chāwalhāti +Nacozari de García +Noyon +Ridge +West Monroe +Korneuburg +Steinkjer +Paso de los Toros +Zandhoven +Sai Wan Ho +Kibiito +Ivanić-Grad +Kollipara +Vrbovec +Bisee +Chã da Alegria +Antônio Prado +Green Valley +Yiewsley +Chāng +Rochester +Schleiden +Höör +Jiānganj +San Lucas +Kanel +Fouriesburg +Pretoria-Noord +Culaba +Roding +Sitalpur +Ben +Turi +Barwat Pasrāin +Phulwaria +Bachrā +Senmayachō-senmaya +Larkspur +An Thành B +Wanderley +Rovinj +Chapantongo +Casinhas +Wickede +Lingamparti +Itatiaiuçu +Abarán +Uppāda +Troy +Juan de Herrera +Palangarai +Mizil +Garhi +Shenley Church End +Campo de Criptana +Montesarchio +Cardoso Moreira +Kingsbury +Shanyincun +Wicklow +Kūdangulam +Santa Ana +Schodack +Pingtiancun +Jhaua +Montefiascone +Pucioasa +Mogocha +Flint +Saposoa +Mirdaul +Bocşa +Mulakalūru +Libertad +Wächtersbach +Vinings +Jaitpur +Solana Beach +Svirsk +Tartarugalzinho +Sant Sadurní d’Anoia +Lakkavaram +Carpenedolo +Cherutana Tekku +Brejões +Pelham +Maktar +Shiwan +Saint-André +Camponogara +Elandsdoorn +Bou Fekrane +Nossa Senhora do Livramento +Gibraleón +Kent +Sākhmohan +Grantsville +Fokino +Japoatã +Amboise +Rāmpur Shāmchand +North Adams +Matinilla +Trebaseleghe +Gomparou +Morungaba +’Aïn Tolba +Newcastle +Bni Khloug +Isalo +San Pablo +Meadville +Kachavaram +Song Phi Nong +Ātmakūru +Qal‘at al Maḑīq +Lynnfield +Nova Londrina +Fengdeng +Byram +D'Iberville +Paranhos +Woolton +Sint-Gillis-bij-Dendermonde +Tarnos +Amarwā Khurd +Tezze sul Brenta +Valparaíso +Chamical +Holly Hill +Buritirama +Möckern +Castiglion Fiorentino +Bujari +Berja +Statte +St. Ann +Messadine +Kalanaur +Tonoshō +Balbalan +Odder +Schinnen +Anolaima +Leagrave +Besigheim +Châteaudun +Cusset +Český Krumlov +Quierschied +Lahaina +San Mateo +Barra de Santa Rosa +Tashi +Fostoria +Karāhal +Dardenne Prairie +Khomām +Bijelo Polje +Amolatar +Kapchorwa +Goonellabah +Corat +Mayenne +Cliftonville +Kankandighi +Trent Hills +Vellakkinar +Tirmaigiri +Loreto +Chợ Mới +Roßdorf +Gurgunta +Sisauna +Bairia +Kalappālangulam +Monteprandone +Bonneville +Yuza +Petrus Steyn +Sim +Wommelgem +Apuiarés +Saghar Sultānpur +Bāgewādi +Moreira +Buckingham +Vyazemskiy +Esneux +Elavanasūr +Pentapādu Kasba +Misaki +Barja +Mándra +Kalavai +Crissiumal +Uzwil +Ambalarondra +Brønderslev +Highlands +Kapelle +Sangão +El Dorado +Ricaurte +Ayutla +Khombole +Ringnod +Edattirutti +Daru +Neunkirchen +Suzu +Cidelândia +Athens +Suamico +Moorreesburg +Nurkot +Frutillar +Koturkolkara +Keisen +Mountain Home +Căuşeni +Kalush +Lontras +Palombara Sabina +Harrison +Kuala Kurun +Smyrna +Finneytown +Alāyaman +Colares +Purén +Red Bank +Agutaya +Tanakkangulam +Woodhaven +Vejer de la Frontera +Chilwell +Vadnais Heights +Wadersloh +Trail +Múggia +Calcinato +Salisbury +Akayu +Pôrto Murtinho +Mansāpur +Rehti +Mūlaikkaraippatti +Ganapavaram +Lanuvio +Dhutauli +Tehachapi +Mora +Ecublens +Saint-André-de-Cubzac +Chinnasekkadu +Daparkha +Nuevo Ideal +Malaryta +Baláo +Abrera +Saunshi +Cheney +Argentona +Vedappatti +Niandjila +Zhongling +Palmares do Sul +Rahika +Medfield +Aldoar +Bihārīganj +Sidlice +Acasusso +Sugauna +Meriç +Florida City +Dedemsvaart +Guéret +Emeryville +Lansdowne +Auriol +Cavallino +Kallād +Fuldatal +Janāi +Meddappakkam +Puerto Jiménez +Bloomsburg +Seclin +San Pietro in Casale +Terrasini Favarotta +Santa Rosa de Calamuchita +Kādachchinallūr +Quartucciu +Vicente Guerrero +Herzberg am Harz +Sattar +Barhauna +Oxapampa +Sagay +Santuario +Kaul +Palacagüina +San Juan Tecuaco +Spilamberto +Syasstroy +Canelinha +Samé +Raymond Terrace +Mullasshēri +Nieuw Nickerie +Salempur +Fontanafredda +Cornwall +Sugarland Run +Juan L. Lacaze +Tega Cay +Nova Crixás +Frederiksværk +Santa Cruz Amilpas +M’dhilla +Leopold +Arakere +Nartan +Castellbisbal +Antonivka +Bad Urach +Ganga +Spiez +Qazmalar +Buenos Aires +The Nation / La Nation +Bolintin Vale +Nova Timboteua +Ziro +Pueblo Nuevo +Bela Simri +Bhagwānpur Khurd +Douar Oulad Youssef +Aš +Brenes +Syston +Little Lever +Chiatura +Venkatagirikota +Rosario del Tala +Bāzidpur +Ban Krang +Bag‘dod +Yozyovon +Santo Tomás La Unión +Govindgarh +Yelmalla +Rose Belle +Gorokhovets +General Las Heras +Teror +Salzano +Kachchippalli +Spiesen-Elversberg +Chinnakkāmpālaiyam +Milngavie +Tucson Estates +Kalecik +Arohi +Overlea +Toukoroba +Burgos +Kurwa Mathiā +Santa Fe +Springbok +Lokāpur +Florham Park +Kyotera +Rio Casca +Gaura +Santa Maria do Suaçuí +Rāmpurwā +Concepción de Ataco +Hulyaypole +Mallāpur +Lumberton +Yavoriv +Mesolóngi +Kasba Tanora +Santa Ana de Yacuma +Juvignac +Aghbala +Franeker +Peißenberg +Jasauli +Oudenbosch +Twistringen +Kannudaiyāmpatti +Taldom +Sanderstead +Mudhol +Kanbara +Pichilemu +Ocós +Saint-André-les-Vergers +Bayeux +Ashibetsu +Tizgane +Donwari +Nangavalli +Landeh +Festus +Velilla de San Antonio +Chojnów +Loyish Shaharchasi +Gavere +Lagoa Dourada +Sørum +Harpur Bochaha +Nandyālampeta +Greenville +Perez +Huazalingo +Chalfont Saint Peter +Magdalena Milpas Altas +Brzeg Dolny +Agdangan +Novo Lino +Kaufungen +Chodavaram +Zag +Perehinske +San Anselmo +Ban Tat +Dhubaria +Chāhatpur +Sedriano +Sarahandrano +Rodeio +Bariyārpur +Maheswa +Granarolo del l’Emilia +Grover Beach +Anaikal +Antanandava +Mahārājpur +Valbonne +Big Bear City +Casteel +Kaeng Khoi +Villa San Giovanni +Kouti +Vadasseri +Nawāda Gobindganj +Barud +Kolappalūr +Florence +Roudnice nad Labem +Serra Azul +Kāmrej +Maroli +Eufaula +Adygeysk +Accokeek +Rokupr +Keerbergen +Guymon +Beaufort +Wietmarschen +Begampur +Ilicínia +Dharampur Bānde +Cavarzere +Terrabona +Amherst +Ban Sop Tia +Bahādurpur +Kochłowice +Samashki +Majārhāt +Chinna Gollapālem +Calcinaia +Mahābaleshwar +Yemva +Sebiston +Vilanova del Camí +Surkhakhi +Bandlagūda +Oakland +San Salvador +Chodov +Aţ Ţūr +Jāmi +Almirante +Gärtringen +Sonbarsa +Köprübaşı +Sete Barras +Châlette-sur-Loing +Duvvūru +Joanópolis +Les Ponts-de-Cé +Obalāpuram +Sulz am Neckar +Canillá +Ainsdale +Ivanec +Mar de Espanha +Richton Park +Oulad Rahmoun +Staryye Atagi +Khorol +Brus Laguna +Emmiganūru +San Francisco de Mostazal +Laamarna +Alcalá +Debbache el Hadj Douadi +San Antonio Aguas Calientes +Bakhtāwarpur +Mahtha +Kirchheim bei München +Varzaneh +Kyōtamba +Pīrnagar +Bāri +Annapolis Neck +Hvardiiske +Chaiyo +Novi Pazar +Krupka +Playa Grande +Dāmarcherla +Seïada +Kaspi +São Félix da Marinha +Pennsville +Sivalārkulam +Mādugula +Buffelshoek +Woolwich +Miranorte +Nirakpurpāli +Serere +Honggu +Lymm +Flitwick +Zelzate +Umag +Patti +Samāi +Battulapalle +Sason +Puerto Ayora +Caió +Betton +Sante Bennūr +Zelenodolsk +Bāsmenj +Librazhd-Qendër +St. Marys +Brad +Peraiyūr +Gricignano d’Aversa +Altenstadt +Buharkent +Chüy +Mānākondūr +Bamhnī +Sangtŭda +Ālampur Gonpura +Nicosia +Bou Adel +Dianguirdé +Dianké +Challapata +Santiago Amoltepec +Ấp Bình Thành +Feuchtwangen +Mechtras +Northlake +Wawarsing +Delémont +Saint-Hilaire-de-Riez +Hersbruck +Maniyur +Itayanagi +Manorville +Marysville +Derventa +Hedensted +Daharia +Luchong +Cuisnahuat +Brownhills +Amos +Beausoleil +Aït Youssef Ou Ali +Morazán +Paradise Valley +Winterberg +Comines +Olgiate Olona +Neosho +Geneva +Taguaí +Wilmington +Ceuti +Dunkirk +Bad Abbach +Diari +Blaubeuren +Mèze +Shilka +Shirataka +Fords +Vineyard +Herk-de-Stad +Kotturu +Wickliffe +Kelso +Gerpinnes +Pūdūru +Cairo Montenotte +Hornsey +Tamm +Mugutkhan Hubli +Valverde del Camino +Santo Stino di Livenza +Ahermoumou +Kuçovë +Malmédy +Balpyq Bī +Pasadena Hills +Malhador +Lukoyanov +Nakhla +San Biagio di Callalta +Sheopuria +Gaffney +Branson +Waltershausen +Ouzera +Sinincay +Takamori +Bom Repouso +Qiaotouba +Alpen +Sutton on Hull +Tilāri +Schongau +Fulton +Novhorod-Siverskyi +Awantipur +Newington Forest +Robertsville +Market Warsop +Warsop +Bad Windsheim +Nangola +Isla Mujeres +Petrovske +Ziama Mansouria +El Menzel +Al Muzayrīb +Fortaleza dos Nogueiras +Saltcoats +Pondūru +Los Hidalgos +Horndean +Holiday City-Berkeley +Melvindale +Groutville +Rio Vermelho +Koyulhisar +Puttānattam +Kākan +Tenmalai +De Haan +North Walsham +Adukam +Kanniyambram +San Marco in Lamis +Souakene +Artesia +Dores do Indaiá +Tomball +Enumclaw +Baykalsk +Shchastia +Jaroměř +Naduhatti +Costa Marques +Victoria +Knittelfeld +Burj al ‘Arab +Kādiyāmpatti +Ấp Phú Hải +Sirūr +Bonito +Castel Goffredo +Teroual +Maxcanú +Junín de los Andes +Presidente Jânio Quadros +Jalpura +Cunda diá Baze +Simri +Ulster +Cedral +Gürgentepe +Villeneuve-lès-Avignon +Motkūr +Xalpatlahuac +Concepción Las Minas +Ottawa +Summerstrand +Sagopshi +Parigi +Rehoboth +Apía +Isla Ratón +Buchs +Steinheim +Jüterbog +Üllő +Saka +Mamakating +Tamsaout +Santa Bárbara de Pinto +Topliţa +Chariyakulam +Oulad Ouchchih +Yenipazar +Bretzfeld +Levelland +Winnetka +Minooka +Lakeland South +Kharak Kalān +Magsaysay +Road Town +Brunsbüttel +Garh Sisai +Belagal +Muttanampālaiyam +Mendota +Tagoloan +Sarıveliler +Luchenza +Galaosiyo Shahri +Eski Arab +Ələt +Queluz +Santiago +Angādikkal Tekkekara +Hessisch Lichtenau +Puerto López +Yzeure +Canet-en-Roussillon +Hosir +Yuzha +Stupava +Lamphun +Khagra +Grenada +Wetaskiwin +Diéli +Brown Deer +New Germany +Dossenheim +Mīnākshipuram +Sandy +Ibiraçu +Jeppe’s Reef +Chropaczów +Zimnicea +Viotá +Cevicos +Ban Tom Klang +Kortemark +Vilsbiburg +Mahuākherāganj +Koppal +Hayashima +Găeşti +General Acha +Sangar +Wordsley +Granville +Lindsay +Jensen Beach +Chandwā +San Vicente +Lamballe +Vatra Dornei +San Rafael +Tuzdybastaū +Wieringerwerf +Pago Pago +Apricena +Dabat +Dingman +Vidauban +Karedu +Dongyuancun +Osečina +Adré +Saint-Jean-de-Védas +Kamalāpuram +Āppukkudal +Chok Chai +Vasylivka +Duartina +Gueltat Sidi Saad +Tirukkāttuppalli +Kanra +El Fuerte +Campbelltown +Lipari +Tərtər +Quirihue +Niefern-Öschelbronn +Salobreña +Croxley Green +Upper +Ancuabe +Fene +Prymorskyi +Olen +Lucena +Maba +Vohimarina +Birni +Cloquet +Progreso +Wadhraf +Rochefort +Mandrāmo +Rong Kwang +Murud +Nohsa +Parasurāmpūr +Diamou +Calanasan +Cuers +Armadale +Pilich +Marikal +Devāpur +Osiān +Sankt Veit an der Glan +Viera East +Fonsorbes +Pajo +Lakhipur +Dysselsdorp +Sabugal +Petrov Val +Rantoul +Monte Quemado +Hirschaid +Kwīhā +Monóvar +Cascades +Brunswick +Ban Laem +Chai Nat +Conceição do Rio Verde +Makuyu +Baraboo +Lüderitz +La Carlota +Palos de la Frontera +Kawamata +Petushki +Sarare +Emstek +North Smithfield +Avrig +Nakhtarāna +Varazze +Kantang +Jiuduhe +Turki +Hisarcık +Röthenbach an der Pegnitz +Preußisch Oldendorf +Mablethorpe +Chiampo +Betma +Mahrail +Brock Hall +Māyamānkurichchi +Bacalar +Chikura +Fortuna +Lake Norman of Catawba +Villa Sola de Vega +Hansot +Justice +Iconha +Montegranaro +Tola Khadda +Fort Atkinson +Montividiu +Eloxochitlán +Santo Niño +Champādānga +Kewatgāwān +Ereymentaū +West Grey +Ladenburg +Ialoveni +Teulada +Asbury Lake +Zella-Mehlis +Creutzwald +Kłobuck +Gladenbach +Goražde +Buchach +Fort Bliss +Campinorte +Borgo San Dalmazzo +Qārah +Marabella +Kashasha +Beydağ +Pilar +Edemissen +Gumani +Sāvalgi +Borger +Wolsztyn +Pannaikkādu +Falmouth +Sattēgālam +Elkhotovo +Campo Alegre +Stony Brook +Miary +Włodawa +Panj +Culfa +Usmānpur +Dhanupra +Sarasota Springs +Anjahambe +Paithān Kawai +Kameshkovo +Xiaping +Giánnouli +Rionero in Vulture +Goldenrod +Codegua +Nanzhai +Coringa +Pütürge +Wolverton +Zuvvaladinne +Gosaingaon +Hînceşti +Uludere +Canegrate +Baūyrzhan Momyshuly +Georgetown +Kingsburg +Negotino +Devsar +Laarne +Grave +El Adjiba +Saint David’s +Beaumont +Vikāsnagar +Ālwārkurichchi +Xavantes +Alto do Rodrigues +Kotwāpatti Rāmpur +Rozhyshche +Bang Phlat +Ingeniero Maschwitz +Okinawa Número Uno +Neuenburg am Rhein +’Aïn el Assel +Boone +Jerome +Ocna Mureş +Santañy +Domahani +Nāysar +Thair +Poquoson +Mariental +Kamin-Kashyrskyi +Tiruppālai +Chabāl Kalān +Lana +Kathāniān +Pidigan +La Argentina +San Miguel Panán +Arhribs +Upper Uwchlan +Kuttiyēri +Andernos-les-Bains +Shāhpura +Bheja +Zaladanki +Logan +Made +Raamsdonksveer +Robinson +Sedro-Woolley +Rānranagudipeta +Canóvanas +Lakshmīnārāyanapuram +Somerville +Matatiele +Santa Ana Maya +Voerendaal +Ban Bang Non +Parker +Sîngerei +Bryne +Mira +Hacılar +Sylacauga +North St. Paul +Kuhsān +Costessey +Baker +Chaval +Mongat +Barton upon Irwell +Pātiram +Eyvānekey +eXobho +Ounagha +Reshuijie +Xinyingheyan +Cetinje +Sântana +Busogo +Iferhounene +Pont-du-Château +San Bartolo +Putussibau +Guruvarājukuppam +El Tambo +Cromer +Refahiye +Santana do Matos +Nakskov +Bickenhill +Haddonfield +Highland City +Rozenburg +Vernouillet +Engenheiro Beltrão +Macará +Sillamäe +Bad Sassendorf +Graben-Neudorf +Bandi̇̄pur +Uren +Tilbury +Chigwell +Castelnaudary +Anna Regina +Ahirauliyā +Harrai +Fagnano Olona +Bagnolo Mella +Port Washington +Kobiri +Zonnebeke +Boumia +Sátão +Gamarra +Čelákovice +Prairie Ridge +Ozatlán +Bhanas Hivre +Sangān +San Marino +Upper Montclair +Finspång +Kanoni +Hanumantanpatti +Wimauma +Nelmadūr +Villa Literno +Shichuanxiang +Gravatal +Bischwiller +Matsukawa +Kānkon +Gogui +Goiatins +Los Santos +Gohpur +Viterbo +Koszutka +Bandalli +Balchik +Kallupatti +Campiglia Marittima +Sint-Michiels +Bairia +Velten +Dhansura +Oshikango +Guática +Pueblo Rico +Palmer +Ambatomainty +Titara +Massena +Provadia +Jequeri +Chachagüí +Warman +Gulshan +Taiynsha +El Ayote +El Ayote +Mallappulasseri +Los Bellosos +Hersham +Maglód +Huodoushancun +Hizan +Gaildorf +Hullatti +Bayona +Grammichele +Madiama +Meerzorg +Aywaille +Kadiana +Vallejuelo +Escanaba +Boussé +Kolattūr +Mehnatobod +Kegeyli Shahar +Mango +Las Lomitas +Prilly +Hagenow +Maraiyūr +Hinundayan +Siki +Kika +Neerijnen +Edgewood +Koluszki +Xinbocun +Mikashevichy +Xinchangcun +Ponnai +Buttāyagūdem +Arcozelo +Trogir +Sarābleh +Ambinanynony +Khvalynsk +Payyanpalli +Calvizzano +Poço Branco +Titisee-Neustadt +Riverview +Kalingiyam +Brignais +Triel-sur-Seine +Orzinuovi +Amelia +San Pedro Jicayán +Ebersberg +Ammavārikuppam +Kamtaul +El Aouana +Bollnäs +Tendūkheda +Chennampatti +Boortmeerbeek +Quinta de Tilcoco +Kallamalai +Lyelchytsy +Pira +Spondon +Attanūr +Anzola dell’Emilia +Panjīpāra +College +Lewisburg +Las Parejas +Sher +Złocieniec +Estepa +Derby +Santa Ana +Saboyá +Arma +Shamsābād +Conneaut +Asfarvarīn +Tyngsborough +Avigliana +Nanakuli +Martinsville +Ilmajoki +Olagadam +Oissel +Villafranca de los Barros +Dokkum +Ribeirão Claro +San Antonio +Issum +Seravezza +Vardenis +Dharhara +Bad Freienwalde +Aubenas +Mi‘rabah +Sarrebourg +Moka +Essenbach +L’Union +Forécariah +Ichora +Coseley +Sever do Vouga +Fojnica +Torroella de Montgrí +Porto Recanati +Higashiagatsuma +Gonzales +Taraclia +Biliaivka +Pola de Laviana +Ierápetra +Reẕvānshahr +Piano di Sorrento +Arluno +Tüp +Nitte +Bayanaūyl +Sydney Mines +Gaimersheim +Nobeji +Vobkent Shahri +Ovenden +Tenambākkam +Chepstow +Sabinov +Drouin +Dubak +Blackfoot +Kladanj +Koskāpur +Cobh +Lye +Payariq Shahri +Jork +Herrin +Patchogue +Rinconada de Malloa +Alampur +Oulad Fares +San Miguel Xoxtla +Kataysk +Union +Alkhan-Kala +Razlog +Bad Fallingbostel +Capdepera +Mandalgovĭ +Tiszavasvári +Huai Yot +Mutoko +Bourem Inali +Nong Khae +Bandwār +Amersfoort +Kalajoki +Laguna Paiva +Foča +Maqu +Vysoké Mýto +Pontecorvo +Soubala +Mecatlán +Yoko +Oulad Aïssa +Bozkurt +Aphaur +Leonforte +Jaidte Lbatma +Ḩawsh al Baḩdalīyah +Periyakoduveri +Boumalne +Dehqonobod +Valencia +Dragør +Diamniadio +Enkesen +Kibuye +Manchester +El Carmen +Kálymnos +Kāttukkottai +Desri +Bulgan +Capul +Navelim +Dawson Creek +Shuangxianxiang +Teotepeque +Valga +Jānkinagar +Sidney +Harnes +Aubergenville +Bodaybo +Mende +Crestwood +Dasraha Bhogrājpur +Cottonwood +North Castle +Sędziszów Małopolski +Cambuquira +Quimperlé +Cypress Lake +Bou Arkoub +Anklam +Ocean Pines +Kotia +Río Grande +Gravenhurst +Enniscorthy +Emiliano Zapata +Heliópolis +Sūreshjān +Shamaldy-Say +Lake City +Nkheila +Wernau +Chettipulam +Pandalkudi +Frohburg +Biassono +Makeni +Aj Jourf +Susa +Hasanpur +Tonyrefail +Garden City +Madison +Obikiik +Qorao‘zak +G’oliblar Qishlog’i +Giffnock +Prince Rupert +Sacacoyo +Vöcklabruck +La Motte-Servolex +La Chapelle-Saint-Luc +Kakching Khunou +Paikpar +Sendafa +Ar Rommani +Acarlar +Atmākūr +Webster +Lavagna +Jefferson Hills +Veurne +McComb +Bom Retiro do Sul +Nawāda +Kot Shamir +Rivarolo Canavese +Narman +Arbeláez +Sidi Makhlouf +Charmahīn +Leeds +Ain Kansara +Pont-Sainte-Maxence +New Mills +Melavāyi +Guisser +Sala +Cananéia +Pebberu +Gandorhun +Acalá del Río +Lavínia +German Flatts +Claye-Souilly +Lodhīkheda +Eraniel +Kasaishi +Penal +Teningen +Bouca +Haslev +Thatto Heath +Benisa +Hathwān +Streator +Soklogbo +Pendências +Home Gardens +Doctor Phillips +Erumaippatti +Yenkuvārigūdem +Lower Pottsgrove +Fairfax Station +Kafr Rūmā +Baikunthpur +Kulattūr +Wołów +Newman +Gharbia +Polās +Burladingen +Sevūr +Baños +Hatillo de Loba +Ludwigslust +Harrow on the Hill +Béjar +Dagmāra +Spearfish +Dinkelsbühl +Pantepec +Córdoba +Razua +Jerônimo Monteiro +Kamalnagar +Villa Unión +Beyləqan +Cicciano +Doi Lo +Perth East +New Britain +Tzintzuntzán +Godohou +Neuhausen auf den Fildern +Nurhak +Teixeiras +Gandara West +Anderlues +Gavardo +Commerce +Oulad Hamdane +Ban Tap Tao +Belén +Abū Dīs +Juchipila +Marumori +Malloussa +Alucra +Chasiv Yar +Laichingen +Pāiker +Mekra +Mahespur +Khujner +West Deer +West Plains +Senjān +Eral +Pital +Ogulin +Ouénou +Kallanai +Zābolī +Boloso +Effingham +Doddanahalli +Manamodu +Engenheiro Paulo de Frontin +Spresiano +El Campo +Porto Valter +Lila +Selston +Ban Huai So Nuea +Carnaubeira da Penha +Neralakaje +Narala +Scotts Valley +Cuichapa +Nordstemmen +Linkenheim-Hochstetten +Tempoal de Sánchez +Sala Consilina +Beatrice +Itiquira +La Palma +Sidi Aoun +North Saanich +Ramabitsa +Kinrooi +Zetel +Newhaven +Gopālnagar +Chāmarru +Presidencia de la Plaza +Morbegno +Hattem +Steinheim am der Murr +Bhachhi Asli +Karrāpur +Affoltern am Albis +Apen +Emboscada +Barra do Ribeiro +Maxhütte-Haidhof +Mesker-Yurt +Warwick +Sernovodsk +Hassi Fedoul +Bully-les-Mines +Ammāpettai +Dharmastala +Pathra +Újfehértó +Bampūr +Haaren +Obernai +Birstall +Puduvayal +Malone +Currimao +Chêne-Bougeries +Krasnyy Yar +Fómeque +Hanūr +Cedarburg +Bom Lugar +Arara +Tsuruta +Yakoma +Chichli +Okabechō-okabe +Anao +Hengshan +Kumirimora +Ghogha +Lauriyā Nandangarh +Rottofreno +Josefina +Bershad +Vrchlabí +Profondeville +Kolanpāk +Yakouren +Koriukivka +Brejinho +Kottagudi Muttanād +Montecorvino Rovella +Karaburun +Paxtaobod +Kuyganyor +Andijon +Yangiariq +Sabbavaram +Belwa +Lobería +Paranatama +Sogndal +Loiyo +Concepción Batres +Cristais +Santa Leopoldina +Rudrūr +Diamond Springs +Bukkapatnam +Tamalpais-Homestead Valley +Lake Tapps +Te Awamutu +Mariyammanahalli +Dorandā +Lewisboro +Kafr Buhum +Port Victoria +La Magdalena Chichicaspa +Unguía +North Merrick +Adelaide +Hnivan +Hobro +Turki +Namchi +Russi +Châteaubriant +Karimkunnum +Malvinas Argentinas +Saraiya +San Mauro Pascoli +Vulcăneşti +Oftersheim +Richland +Santo Domingo +’Aïn Kerma +Bashtanka +Nevele +Kuttūr +Kewanee +Caledonia +Pianiga +Pasca +Rognac +Bhaisālotan +Katagon +Uppukkottai +Wrentham +Moss Point +Vylgort +Osterhofen +Prévost +Tömük +Rüti +Ghorādal +Sakawa +Tiadiaye +Rafína +Olton +Srīnagar +Tioribougou +Nemili +Villamartín +Cheadle +Bhankarpur +Riedisheim +Bonyhád +New Kensington +Le Grand-Saconnex +Santa Elena +Matāla +Kayattār +Brattleboro +Puerto Nare +Kōttāya +Vannivedu +Na Wa +Sagon +Tholey +Vandithāvalam +Grafton +Botlikh +Khonobod +Huétor Vega +Claiborne +Ebéjico +Raipur +Encamp +Barentin +Choctaw +Dranesville +Gajhara +Błonie +Morab +Oyón +Liperi +Rāghopur +Samādh Bhai +Wasilków +Rielasingen-Worblingen +Marquetalia +Arganil +Gyomaendrőd +Kesarimangalam +Ban Bo Luang +Cocotitlán +Havixbeck +Panzgām +Grumo Appula +Tranent +Eklahra +Barsaun +As Sars +Mucugê +Xiushuicun +Muthuswāmipuram +Minabe +Sainte-Adèle +Ban Bang Sai +Ban Noen Phoem +Sapna +Sainte-Agathe-des-Monts +Justo Daract +Mulug +Marsaskala +Meitingen +Bømlo +Camp Verde +Strzelin +Salgado de São Félix +Pachchaimalaiyankottai +Mangarwāra +Eura +Māndu +El Sobrante +Bela Palanka +Bamber Bridge +Macedonia +Hagfors +Lakeside +Socuéllamos +Ampasimbe +Cherlak +Antri +Pornichet +Velair +Riviera Beach +Ishkāshim +Kuzhippilli +Hassi el Ghella +Kharar +Saclepea +Brzeziny +Kamianka-Dniprovska +Kök-Janggak +Gornyak +North Middleton +Maili +Choró +Chāmpāhāti +San Prisco +Wyndham +Quesnel +Citlaltépec +Yecuatla +Perleberg +Huntington +Gistel +Santa Filomena +Pulakurti +Sonupur +Punnila +Yedtare +New Baltimore +Jericó +Saint-Cyr-sur-Mer +Bharwelī +Mitchellville +Tahannawt +Kinkala +Mayate +San Carlos Yautepec +Datoda +Waikanae +Głubczyce +Gulbahor +Taltal +El Marsa +Montrose +Mānegaon +Lenyenye +Tring +Crossville +Umm ar Rizam +Mattenhof +Benoy +Lehre +Gierałtowice +Münchenstein +Sablé-sur-Sarthe +Kīrippatti +Centralia +Acandí +Bhairāpura +Anndevarapeta +Myrtle Grove +Diré +Villars-sur-Glâne +Sassenburg +Pillānallūr +Ōsako +Kosjerić +Steenokkerzeel +Mostardas +North Tidworth +Narhat +Zlatograd +Si Mustapha +Albox +Sedgley +Baozhong +Mmadinare +Glen Rock +Durmersheim +Portales +Lower Gwynedd +Ban Muang Ngam +Pāpampeta +Ganapatipālaiyam +Paal +Ipaumirim +Miesbach +Louvres +Mugalivakkam +Birdāban +Kodusseri +Dalby +Mettlach +La Queue-en-Brie +Beaconsfield +Motta Sant’Anastasia +Solliès-Pont +Rāmpur Kudarkatti +Nihāl Singhwāla +Borgosesia +Hasbrouck Heights +Bridgnorth +Viswanāthaperi +Bhagwānpur Desua +Unebichō +East Renton Highlands +Iwate +Uzyn +Caimito +Washington +Piketberg +Ugento +Făleşti +Pasłęk +Kalaidasht +Nova Laranjeiras +Kesabpur +Valley Falls +Tábua +Dhaula +Fenton +Liuba +Sinalunga +Woodward +Filandia +Le Chambon-Feugerolles +Khānpur +Al Majma‘ah +Poranga +Curití +Ciudad-Rodrigo +São Francisco do Maranhão +Oiba +Bishnāh +Bharokhara +Pharkia +Ponte da Barca +Hōdatsushimizu +Mays Chapel +Arroio do Tigre +Jacala +Destrehan +Gardnerville Ranchos +Nāttarampalli +Tall Dhahab +Wolgast +Võru +Raismes +Walton-on-the-Naze +Chalgeri +Eiras +Solofra +Nilo Peçanha +Porto +Bangāwān +Ban Pae +Erfelek +Goner +Toropets +Novalukoml’ +Bentley +Rosdorf +Snihurivka +Karagwe +Gbanhi +Lajia +Honeygo +Vălenii de Munte +Alavus +Berwick-Upon-Tweed +Sarmastpur +Vilāngurichchi +North Lebanon +Hwlffordd +Pyrzyce +Rocca Priora +Baxiangshan +Manne Ekeli +Bāgra +St. Peter +Mato Verde +Pittalavānipālem +Steha +Kongsvinger +Imassogo +Mettuppālaiyam +Kārvetnagar +Paramanandal +Iapu +Sabaur +Celebration +Ronneby +Villa Berthet +Edamon +Sławno +Assomada +Gulni +Liberty Lake +Rosaryville +Theux +Marly +Chudamani +Utehia +River Edge +Huité +Kumarkhāli +Nariman +Key Largo +Beyne-Heusay +Velivennu +San José El Ídolo +Yeadon +Occhiobello +Balha +Fort Campbell North +Saddlebrooke +Budakalász +Tomar do Geru +Sahtāh +Monfort Heights +Al Fayd +Les Îles-de-la-Madeleine +Opmeer +Monte San Giovanni Campano +Raffadali +Eureka +Sam +Verde Village +Jardim Alegre +Fuensalida +Warka +Singhbāri +Viyapuram +San Gennaro Vesuviano +Basantpur +Islamey +El Carmen +Keimoes +Hasroûn +El Qâa +Chaqra +Bteghrîne +Qoubaiyat +Ambolomoty +Fanambana +Ivoamba +Ankafina Tsarafidy +Ambohimandroso +Belobaka +Andonabe +Sorombo +Itondy +Iarinarivo +Saharefo +Vohindava +Beandrarezona +Andranovao +Sendrisoa +Maromiandra +Ambalavero +Bekapaika +Anorombato +Ambaliha +Ambohinamboarina +Anjanazana +Andrembesoa +Mahazoarivo +Anjiamangirana I +Nosiarina +Antanamalaza +Andramy +Basibasy +Ampondra +Antaritarika +Befody +Maromby +Omatjete +Saidpur +Umm Badr +Buqkoosaar +Kévé +Bilovodsk +Ḩukūmatī Gīzāb +Jajce +Karrānah +Ngou +Tefam +Mettingen +Wells +Madina do Boé +Princetown +Petit-Goâve +Huvin Hippargi +Khānda +Pinneli +Dahivel +Jai +Rāmachandrapuram +Sadalgi +Halgar +Arni ka Khera +Pramatam +Cecchina +Weatherford +View Park-Windsor Hills +Floreşti +Sokouhoué +Oberschleißheim +Urakawa +Somerset +Barberton +Cimişlia +Ārutla +Talen +Arkansas City +Manuel Urbano +Galvarino +Boskovice +Gladstone +Somersworth +Węgrów +Doranāla +Wang Saphung +Mugdampalli +Krasnousol’skiy +Bolaños de Calatrava +Alajuelita +Mikkelin Maalaiskunta +Volosovo +Martuni +Shiotachō-matsusaki +Rapho +Husepur +Winton +Nagykáta +Tundhul +Msambweni +Yazıhan +Escazú +Benifayó +Kaviti +Arth +Iwanai +Ifs +Minehead +Pāta Putrela +Klazienaveen +Boldājī +Tabocas do Brejo Velho +Oulad Daoud +Ain Beida +Tryavna +Gundelfingen +Arakvāz-e Malekshāhī +Belampona +Siruvāchchūr +Minnāl +Kuangfu +Rāsingapuram +Hathauri +Erbaocun +Santiago del Teide +Marauatpur +Kyzyl-Kyshtak +Star +Hadim +Erkner +Eraclea +Kasaji +Murnau am Staffelsee +Froha +Nueva Era +Mālthone +Bernalda +Mariana +Sremska Kamenica +Tetagunta +Fenton +Souq Jamaa Fdalate +Welver +Stidia +Tāran +Palos Heights +Bissorã +Béna +Tigzirt +Niagadina +Saksohāra +Ballston +Virālippatti +Leominster +Awānkh +Terranuova Bracciolini +Kountouri +Paispamba +Provins +Phước Long +Karaund +Adalaj +Ibaretama +Pudūr +Sidi Yahia +Selimpaşa +Norīa +Vijayāpati +Spencer +Jiajin +Arrigorriaga +Umurlu +Blaricum +Tori-Cada +Jadayāmpālaiyam +Nong Bua +Sondho Dullāh +Konāje +Trezzo sull’Adda +Pecica +Seberi +Amarāpuuram +El Parral +Avesta +Mykhailivka +Darabani +Guidel +Umri +Aït I’yach +Zlaté Moravce +Joaquim Távora +South Miami +Goulds +Alijó +Kozloduy +Caño Martin Peña +Dalmatovo +Governador Dix-Sept Rosado +Sheerness +Khem Karan +Enns +Cinisi +Lienz +Don Sak +Şalpazarı +Distracción +Lagoa do Ouro +Rijkevorsel +Parappukara +Pfäffikon +Richmond +Bačka Topola +Dhanauri +Manchester +Matmata +Tectitán +Pánuco de Coronado +Totogalpa +Çüngüş +Andrelândia +Heddesheim +Saint-Avé +Sai Kung Tuk +Chaital +Pedda Adsarlapalli +Tosashimizu +Schoonhoven +Bueu +Vehkalahti +Mullurkara +Patarrá +Athol +Gisors +Sandridge +Sansalé +Red Bank +Tizi Nisly +‘Anadān +Pacé +Mandasa +Gig Harbor +Bilauri +Hātod +Martinsville +Taché +Poselikha +Brejetuba +Wellington North +Zhdanivka +Paramankurichi +St. Andrews +Fort Bonifacio +Edattala +Grand Baie +Lalmatie +Aheqi +Harrislee +Bairo +Huanian +Kotla +Kronshagen +Tanakallu +Kirikera +Mamarappatti +Halstead +Bound Brook +Williams Lake +Chāndpura +Box Elder +Żebbuġ +Ried im Innkreis +Craponne +Tabontabon +Ambātturai +East Bethel +Oqqo‘rg‘on +Qorashina +Bargoed +Ajjanahalli +Chinna Orampādu +Kampenhout +Bhansia +Baohe +Enamadala +Zemamra +Halásztelek +Arkadak +Wanding +Zumbagua +Belūr +Gundugolanu +Talant +Hamilton Square +Mamqān +Aragoiânia +Shamshernagar +Pakra +Neustadt +Góra Kalwaria +São João +Sultanhanı +Coussé +Dudley +Rutherford +Cachoeira de Minas +Dom Basílio +Nagar +Lauria Inferiore +Kagamino +Ojuelos de Jalisco +Imaruí +Mayilādi +San Severino Marche +Hattian Bala +Gander +Novaya Lyalya +Kamlāpur +Puliyampatti +Benaguacil +Gerstetten +Prattipādu +Jaltenango +Onet Village +Archdale +Maisaka +Badamdar +Rushall +Niles +Comasagua +Linganore +Mahazoarivo +Möhnesee +Épinay-sous-Sénart +Florange +Mehsāri +Bernissart +Snaresbrook +Altavilla Vicentina +Iwaka +San Fernando +Eichenau +Oulad Amrane el Mekki +El Arba Des Bir Lenni +Yuxiaguan +Xincheng +Rudrāngi +Sant’Agata di Militello +Brunn am Gebirge +Monschau +Vanimo +Abasolo +Levashi +La Puebla del Río +Olgiate Comasco +Merāl +Salem +Lake Elmo +Malar +Iacanga +Ban +Laurel +Big Lake +Aguasay +Lissegazoun +Richland +San Juan del Rio del Centauro del Norte +Rankweil +Lauenburg +Eckington +Khajuri +Whistler +Horodnia +Hoyland Nether +Chainpur +Monte Compatri +Çamaş +Ivybridge +Freeport +São Gonçalo do Rio Abaixo +Zriba-Village +Imaculada +Āvadattūr +Miller Place +Recreo +Sixaola +Vendas Novas +Gbéroubouè +L’Isle-Adam +Pedda Vegi +Dendulūru +Sant’Ambrogio di Valpolicella +Myrne +Monte di Procida +Lapinig +Bad Vöslau +Palmilla +Dishāshah +Brighton +Péonga +Pokrovskoye +Vellallūr +Torton +Biltine +Bībīpet +Lizzanello +Kālipatnam +Wallington +Middleburg +Angalakurichchi +Chợ Lách +Trentham +Chero +Bordentown +Wolnzach +Son Servera +Lahra Muhabbat +Guābāri +Itāmāti +Sirsia Hanumānganj +Licínio de Almeida +Paulista +Locri +Andraitx +Bataredh +Spilimbergo +Bibbiena +Mangpa +Raesfeld +Tiddas +San Andrés de Llevaneras +Ratanpur +Hirao +Alum Rock +Premnagar +Bou Merdès +La Tour-de-Peilz +Areal +Hemsbach +San Pedro La Laguna +Korahia +Ōiwa +Pilis +Endwell +Rasūlpur +Vecchiano +Ukrainsk +Poulsbo +Oestrich-Winkel +Baranzate +North Union +Groß-Enzersdorf +Camposampiero +Chilonga +Tapiratiba +Leingarten +Lauffen am Neckar +Wath upon Dearne +Toualet +Barokhar +Sidi Amer El Hadi +Mountougoula +Wargal +Menfi +Jataìzinho +Langgöns +Walldürn +Barrafranca +Befandefa +Tīkar +Holalu +Senirkent +Castle Pines +Bocaina +Dolo Bay +Yakushima +Portela +Mboro +Sonosari +Château-Gontier +Street +Elizabethtown +South Yarmouth +Virgem da Lapa +Oberderdingen +Parsa +Winfield +Picayune +Oswaldtwistle +Kākalūr +Chaplygin +Ghabāghib +Thogapalle +Muscoy +Serra do Salitre +Evanston +Jalalaqsi +Hobyo +Beladi +Little Chute +Portoferraio +Barapiré +Deerlijk +Gulfport +Oued Cheham +Novi Marof +Sânpetru +Neuenrade +Fenoughil +Oued Seguin +Maizières-lès-Metz +Susegana +Le Relecq-Kerhuon +Srīrāmpuram +Khānaqīn +Mountain Top +Santa Cruz Michapa +Axixá +Somain +Zaō +Muping +Minden +Spreitenbach +Kete Krachi +Tortoreto +Biknūr +Tiny +Zwönitz +Ryūō +Comalapa +Cafayate +Aougrout +Vegarai +Roda +Hauzenberg +Ngerengere +Ronchi dei Legionari +Portomaggiore +Oak Hills +Villecresnes +Kehen +Fiume Veneto +Baitoa +Yeniçiftlik +Pritzwalk +Leutenbach +Santa Ana +Chiautla de Tapia +Sidi Ladjel +Olivenza +Tarrytown +Bechloul +Sahasmal +Strunino +Vauvert +Khānsāhibpuram +Market Drayton +Borgaro Torinese +Alamedin +Putnam Valley +Negēlē +Green River +Pragatinagar +Burgthann +São Gonçalo do Pará +Kankaanpää +Allūr +San Pedro +Sīpālakottai +Kotha Gurū +Bitetto +Show Low +Amherst +Baranivka +Brotas de Macaúbas +Guttenberg +Mack +Drolshagen +Campos del Puerto +Cogolin +Hankey +Söderhamn +Tifni +Batočina +Picaña +Owk +Agcogon +Tornquist +Weston +Jaitwār +Kornepādu +Alken +Annakāttumūla +Pastpār +Richfield +Largo +Gillingham +Sher Muhammadpuram +Mapleton +Bopfingen +Hawkesbury +Blace +Elektrėnai +Kuchinarai +Monción +Pößneck +Bad Dürrenberg +Agadi +José María Morelos +Waldniel +Tiruvādānai +Rājāram +Padinjāremuri +Murukondapādu +Ganapavaram +Lawrenceburg +Abhia +Edéia +Chināval +Tolcayuca +Francofonte +Bajiao +Cernay +Konand +Bilopillya +New Baltimore +Rossmoor +Kamianka +Ovidiopol +An Cabhán +Bajestān +Mương Theng +Serravalle Pistoiese +Carignan +Magdagachi +Ban Phan Don +Scharbeutz +Lindenberg im Allgäu +Vasylkivka +Medina Sidonia +Chāltābāria +Vengavasal +Pallejá +Yamamoto +Lunenburg +San Nicolás de los Ranchos +General Cabrera +Trinidad +Septèmes-les-Vallons +Hackney +Chintapalle +Río Colorado +Dināra +Chaumont-Gistoux +Zhetibay +Altstätten +Hardiyā +Sveti Nikole +Mestrino +East Greenwich +Los Alamitos +Mutia +Crowley +Ambatofisaka II +Cossimbāzār +Shady Hills +Irupi +Kőszeg +Golub-Dobrzyń +Baghauni +Appingedam +Guatajiagua +Krujë +Deutschlandsberg +Nellipoyil +Sandy +Carmen +Villefranche-de-Rouergue +Carmiano +Soalandy +Aleksandrów Kujawski +Chūndal +Puente Nacional +Zayukovo +Auta +Brookhaven +Milford +Alegría +Piranhas +Wetherby +Nandimandalam +Sääminki +Spring Lake +Ubaí +Macusani +Costa de Caparica +Sindalakkundu +Malkā +Lantana +Hanover +Bithān +Sowerby Bridge +Semri +Lantana +Rattihalli +Lansing +Lower Burrell +Bol +Nakasongola +Gatumba +Chakai +Geisenheim +Tamezmout +Sengurichchi +Ūjhāna +Rangsdorf +Olhanpur +Douglas +Risca +Tibbar +Elma +Ringwood +Kissing +Vellār +Zārach +Ibirá +Waltikon +Nova Gradiška +Ángel R. Cabada +Óbidos +Hildburghausen +Barkāgaon +James Island +Schwaigern +Rostam Kolā +Dolinsk +Lom Sak +Jacó +Irmo +São Bento do Sapucaí +Luís Alves +Maliāl +Mallikkundam +Izola +Peiting +Hollabrunn +Conceição do Castelo +Billerbeck +Canton +Seven Hills +Saint-Pierre-du-Perray +Venkidanga +Tirodi +Torri di Quartesolo +Ulverston +Sothgaon +Cornedo Vicentino +Signal Hill +Center +Kibi +Bellmawr +Udachnyy +Peligros +Elland +Prudhoe +Kiskunmajsa +Vilandai +Bokākhāt +Alahina +Bolsover +Oliva +Franklin +Randaberg +Škofja Loka +Talakād +Fgura +São Sebastião do Uatumã +Kautālam +Limburgerhof +Keolāri +Nellipāka +Kāranchedu +Vermillion +Befotaka +Bayyanagūdem +Dent +Elavalli +Chittāttukara +Bullas +Tiszakécske +Narasāpuram +Hallbergmoos +Bryn +Lanham +Woodmere +Sassenage +Ponte San Pietro +Marotta +Bagaura +Stokke +Sandy +Berlaar +Bhainsahi +Jardim do Seridó +Zofingen +San Pedro Ixcatlán +Lajosmizse +La Riviera +Raubling +Nandavaram +Panamaram +Yirol +Sukurhutu +Hidaka +Lakri +Jangy-Kyshtak +Lessogou +Mixtla de Altamirano +Samesi +Isaszeg +Fatehābād +Newport East +Emmaus +Norton +Bucyrus +Isaka +Ansião +Kāttakampāla +Isāpur +Mitai +Mendota Heights +Brock +Mount Sinai +Carver +Cowdenbeath +Zaoqiao +Kizhakkanela +Miami Shores +L’Île-Perrot +Durbuy +Dhanaura +Kunkalagunta +Azcoitia +Lundazi +Candelaria +Bolbec +Sonāda +Hemiksem +Karmauli +Siur +Nzeto +Guaiçara +Thiers +Knin +Half Moon Bay +Angicos +Utiel +Diao’ecun +La Esperanza +Motegi +Tlachichilco +Dérassi +Burnham +Ermenek +Dammapeta +Weiz +Pongode +Chebrolu +Kallakkudi +Pepperell +Lakeville +Porecatu +Bayou Blue +Tepetzintla +Ostrhauderfehn +Ichinomiya +Nawā Nagar Nizāmat +Wakefield +Lubaczów +Henley on Thames +Middle Valley +Lenvik +Annappes +Ban Mae Tuen +Māhta +Nallūr +Santa Cruz Itundujia +Moulay Driss Zerhoun +Balaxanı +Ielmo Marinho +Kotra +Summerland +Mala Vyska +Baghambarpur +Saint-Gaudens +Manabo +Ibicuitinga +Xexéu +Castelló de Ampurias +Ziyodin Shaharchasi +Piraúba +Ontario +Veyrier +Rokiškis +Ambohimahasoa +Sint Anthonis +Betmangala +Geisenfeld +Qaşr-e Qand +Kem +Rāyavaram +Kalmiuske +Ascot +Sunninghill +Wolmirstedt +Gararu +Abertillery +Sewāi +Kolnād +Cadale +Cruzeiro do Sul +Tafaraoui +Wallingford +Schiller Park +Fuente de Oro +Devipattinam +Gansbaai +Shuinancun +Ādūru +Santa Comba Dão +Jiquipilas +Cangas de Narcea +Renāpur +Hartsville +Likhoslavl +Mömbris +Guraahai +Chatham +Phon +Bassersdorf +Halsūr +Ankli +Huandacareo +Zero Branco +Markham +Lawrenceburg +Loreto +Santa Lucía +Chambray-lès-Tours +Sablan +Ross-Bétio +Tummalacheruvu +Weeze +Sākib +St. Clements +Khimlāsa +Morūr +Casteldaccia +Gainza +Santa Lucía +Vadakkumbāgam +Mangabe +Kurabalakota +Novoīshīmskīy +Roessleville +Yercaud +Minnehaha +Moss Bluff +Oulad Friha +Santa Genoveva de Docordó +San Josecito +Wichelen +Oulad Hamdane +Patterson +Chegem Vtoroy +Ipaporanga +Town and Country +View Royal +Nazareth +Melle +Gopālapuram +Le Haillan +Naīgarhi +Jethuli +Castenedolo +Bahādarpur +Pettaivāyttalai +Jarābulus +Haradok +Jīdigunta +Okuizumo +Ban Mae Kham Lang Wat +Ban Wiang Phan +Caraguatay +Schwieberdingen +River Forest +Santa Cruz da Baixa Verde +Oñate +Isola della Scala +Pampas +La Unión +Kennebunk +Eijsden +Landsmeer +Nieuwpoort +Gold Canyon +Gardere +Puerto Suárez +Überherrn +Loutráki +Ghal Kalān +Kushmanchi +Severnyy +Navarro +Kopervik +Yangiqo‘rg‘on +Xiaoba +Meßstetten +Ghinda’e +Yorktown +Mistelbach +Silves +Sathmalpur +Khandpara +Forest +Belgrave +Araçás +Perches +Gulgam +Cisneros +Esperanza +Anatolí +Stuarts Draft +Molteno +Kundiawa +Leāma +Espita +Itainópolis +Perumbakkam +Tadapurambākkam +Esil +Saint-Estève +Dąbrowa Tarnowska +Krosūru +Sussex +Derdara +Bugongi +Broome +Carmo da Cachoeira +Amelia +Patera +Downpatrick +Ghambiraopet +Port Lavaca +Wanstead +Muroto-misakicho +Platteville +Den Chai +Chennūr +Khorramābād +Chintalavādi +Nyazepetrovsk +Holešov +Labbaikkudikkādu +Sidi Kasem +Kibungo +Dharmāram +Mangalkot +Port Morant +Matca +Pieve di Soligo +Lamosina +Weinfelden +Sinzheim +Nālwār +Roma +Taïneste +Krapina +Kantilo +Saint Helena Bay +Doda +Mīt Damsīs +Sunkarevu +Bāhāgalpur +Ichhāpur +Smithfield +Norfolk +Vohilava +Moparipālaiyam +Maniago +Okpo +Nandipeta +Ekerö +Quatipuru +Budha Thēh +Golet +Stelle +Nīdāmangalam +Epazoyucan +Sahri +Ingurti +Ramena +Calheta +Noeux-les-Mines +Doddappanāyakkanūr +Shāhpur Baghauni +Giffoni Valle Piana +Cholpon-Ata +Loyalsock +Olintla +Jucati +Velké Meziříčí +Gorey +Kundurpi +Drawsko Pomorskie +Raun +Blundellsands +Cachoeira Alta +Desanagi +Mirik +Grosse Pointe Park +Dolores +San Carlos de Guaroa +Hlaingbwe +Valdivia +Brislington +Orchha +Kathu +Doukombo +Sagada +Nova Odesa +Coronel Dorrego +Belp +San Lázaro +Pākkam +Piprāhi +Żejtun +Newcastle +Sultānpur +Xuân Trùng +Burley +Radnevo +Four Corners +Badou +Hàng Trạm +Slatina +Al M’aziz +Estiva +Gospić +Mexico +Dzouz +Beniel +Fort Morgan +Liubashivka +Dang‘ara +Liman +Strombeek-Bever +Kaboua +Unterföhring +Ostbevern +Elgóibar +Lençóis +’Aïn Roua +Devikāpuram +Taviano +Nova Ubiratã +Rabat +Barhi +Chauny +Baruāri +Lovington +Bouabout +Sonāpur +Kemigawa +Elk City +Kamalāpuram +Cherdakly +Mercogliano +Oskaloosa +Aginskoye +Zeuthen +Atmākūr +Montmagny +Ihumwa +Guayabal +Wittingen +Mount Fletcher +Tawnza +Oxelösund +Coswig +Salisbury +Sathiāla +Farmington +Ridgefield +Moyamba +Lokhvytsya +Tinogasta +Nový Bor +Westerkappeln +San Gregorio di Catania +Leon Valley +Revúca +Diósd +Bānk +Peringōttukurusshi +Yakymivka +Gaurihar Khāliqnagar +Healdsburg +Fatehpur Shāhbāz +Alto Alegre dos Parecis +Myronivka +Jamsaut +Pānāpur Langa +Lindon +Alfonsine +East Glenville +Palestina +Bek-Abad +Rača +Sárbogárd +Satyavedu +Kuriyama +Götzis +Tadley +Kavaratti +Valea Adâncă +Lebon Régis +Vengikkal +Poldasht +Bath +Sun Village +Gonubie +Pirangi +Sinkolo +Kishanganj +Muna +El Ançor +Mios +Hœnheim +Kāri +Arasūr +Rupenaguntla +Shanmukhasundarapuram +Vlasenica +Cidade Gaúcha +Prudente de Morais +Agamé +Bhabānipur +Ridgefield +Fuller Heights +Ḑurumā +Mariehamn +Loran +Ninga +Kalakada +Rengāli +Kashaf +Douar Trougout +Libertad +Vedène +Eningen unter Achalm +Fourmies +Khāngāon +Maruttuvakkudi +Burām +Vitry-le-François +Culcheth +Shingbwiyang +Mogilno +Egelsbach +Pike Creek Valley +Ahlaf +Cocentaina +Chakwai +Wilton Manors +Pontiac +Bellegarde-sur-Valserine +Jódar +Cantley +Esmoriz +Paro +Chitauria +Florestópolis +Ouro Branco +Gräfenhainichen +Sidi Moussa Ben Ali +Kirchhundem +Odatturai +El Khemis des Beni Chegdal +Ban Saeo +Satuek +Montanhas +Trostberg an der Alz +Baroni Khurd +North Wantagh +Covington +Sidéradougou +Cumandá +La Londe-les-Maures +Milići +New Garden +University of California-Santa Barbara +Sonoita +Enghien-les-Bains +Port Salerno +Ajijic +Dhalaa +Erin +Amdel +Varzobkala +Saubara +Vlašim +Harsum +Lendinara +Nikel +Caetanópolis +Gloucester City +Campbellsville +Egersund +Puerto Rico +Barahkurwa +Gandhāri +Woods Cross +Tlacolulan +Hato Corozal +Méricourt +Holyhead +Dobbs Ferry +Greensburg +Er Regueb +Bad Schwalbach +La Libertad +Shāhpur +Ramallo +Notre-Dame-de-l'Île-Perrot +San José de Guaribe +Mogogelo +Kumari +Stradella +Koori +Dorog +Kuse +Pallipattu +Ain Legdah +Malo Crniće +Höhenkirchen-Siegertsbrunn +Bridgeton +Vakhrusheve +Nemyriv +Velimeşe +Laqraqra +Kinattukkadavu +Hanover +Dautphe +Kondhāli +Sorsk +Haydock +Casièr +Sanniquellie +Yuryuzan +Qax +Naliya +Bytča +Cadolzburg +Jimaní +Tourougoumbé +Mahāgama +Kostinbrod +Guinagourou +Sales Oliveira +Pothia +Senduriā +Rāibāri Mahuawa +Ulricehamn +Florennes +Sào Amaro das Brotas +Cabo Verde +Barro Alto +Sociedad +Lopon +Pratāparāmpuram +Tysvær +Lumaco +Majdal Shams +Gänserndorf +Scottburgh +Pelileo +Laren +Buzhum +Dawley +Saint-Cyprien +Rostraver +Plan-de-Cuques +Babayevo +Byerazino +Gundrājukuppam +Wealdstone +San Michele al Tagliamento +Enebakk +São João das Lampas +Montegrotto Terme +Saverne +Yāllūru +Greetland +Higashiizu +Kincardine +Marktheidenfeld +Nyírbátor +Simrāhi +Clawson +Douar Sgarta +Saint-Junien +Newport +Eldorado +Sidi Tabet +Guachetá +Yvetot +Falimāri +Oak Grove +East Grand Rapids +Anjuna +Lakhipur +Chekmagush +Hejiaji +Grünwald +Konakondla +Campobello di Mazara +Lijiacha +Samayanallūr +Halavāgalu +Fulton +Buckhurst Hill +Baghmaria +Harji +Bind +Hirnyk +Onoto +Santa Fé +Sarjāpur +Snyder +Rosario de Mora +Campo do Meio +Sālamedu +Pīr Maker +Santa Clara +Sevilla +Staden +Harrow Weald +Suffern +Gornozavodsk +Mancha Real +Great Bookham +Little Bookham +Lahfayr +Bānsbāri +Hamilton +Boudenib +Buuhoodle +Uchchangidurgam +Kochkor +Morgan City +Tilmi +Musile di Piave +Elliot Lake +Douar Oulad Naoual +Pelsall +Woodbury +Recke +Kamiita +Humahuaca +Alcântaras +Ploufragan +Bhagwatpur +Biddupur +Puyehue +Zambrano +Parora +Okmulgee +Castalla +Baghra +Ātharga +Wells +Resende +Yangirabot +San José del Fragua +Lonate Pozzolo +Progress +Arumbākkam +Meghraj +Altdorf +Jamhor +Pachāhi +Manpaur +Zierikzee +Lyuban +Sidi Baizid +Carnoustie +Gardone Val Trompia +Belén +Bankya +Rudersberg +Phulparās +Manappakkam +Kalinagar +Newtown +Bromont +Engen +Parvatgiri +Perāmpuzha +Medulla +Zaragoza +Bargaon +Bishunpur +Monte Sant’Angelo +Rodenbach +Akola +Catskill +Spencer +Klyetsk +Talaigua Nuevo +Bataiporã +Rothenburg ob der Tauber +Charouine +Maltby +Ochsenfurt +Vaal Reefs +Chkalovsk +Cardoso +Handewitt +Taurisano +Tegueste +Nalambūr +Rheinau +Knowsley +Calatrava +Cherlagandlapālem +Pimenteiras +Lamsabih +Capaci +Ouaklim Oukider +Teniet el Abed +Holbrook +Domneşti +Kurgunta +Somerville +Welzheim +Dharmaj +Cocoa Beach +Yanahuanca +Gustavsberg +Waidhofen an der Ybbs +Machados +Humpolec +Tököl +Besalampy +Gouvêa +Nußloch +Ialysós +Natshal +Pemmpéréna +El Quisco +Santa Rosa de Viterbo +Pāpireddippatti +Dugny +Tiruvennanallūr +Langarivo +Māshyāl +Rio Novo do Sul +Ban Bang Lamung +Bārīgarh +Virton +Tabapuã +Ravenna +Lardero +Masinigudi +Capitán Bado +Varadarājampettai +Taylorville +Tosagua +Capitán Sarmiento +Leatherhead +Gracemere +Bitritto +Mărăşeşti +Oxted +Burslem +Drezna +Pionerskiy +Mülheim-Kärlich +Worb +Ténenkou +Bruay-sur-l’Escaut +Tāzhakudi +Janów Lubelski +Hipperholme +Edasseri +Dimmbal +Zimna Voda +Naryn +Saint-Germain-lès-Arpajon +Arnprior +Tudela +Villa Purificación +Denkendorf +Honmachi +Ikhlāspur +Sabana Yegua +Jem’at Oulad ’Abbou +Periya Soragai +Lafrayta +Sebba +Inhassoro +Lieksa +Caldogno +Miasskoye +Ichinohe +Estiva Gerbi +Gualcince +Batemans Bay +Caotan +Singura +Commune Sidi Youssef Ben Ahmed +El Ghourdane +Arslanbob +Drochtersen +Shiloh +Bad Nenndorf +Panpuli +Teano +Getulina +Chorleywood +Antsambalahy +Golfito +Bátonyterenye +Tikota +Amatenango del Valle +Ban Kat +Stamboliyski +Meru +Svitlodarsk +Pedro de Toledo +Arpajon +Norwell +Dourdan +Limavady +Stanwell +De Doorns +Beldibi +North Dundas +Eichenzell +Tramore +Lakeland Highlands +Pipariya +Somavārappatti +Biri +Majanji +Sajószentpéter +Kanhāipur +Caimanera +Bhilavadi +Seneffe +Dammartin-en-Goële +Iskapālem +Curimatá +Coveñas +Astley +Fallersleben +Riebeeckstad +Simeria +Sinor +Asthal Bohar +Anderson +Caetano +Bicske +Samahuta +Burtonwood +Bānāvar +Trecastagni +Lowes Island +Manvel +Derecik +Kūshk +Kuřim +La Roche-sur-Foron +Sīrpanandal +Santa Cruz +Maryport +Bara Belun +De Witt +Ventersburg +Søgne +Beclean +Largs +Westphalia +Ocean City +Wellesley +Sosenskiy +Edd +Arcachon +Brooklyn +Boultham +Tāmba +Datiāna +Kovūrupalli +Sant’Ilario d’Enza +Hillsborough +Niasso +North Valley +Jādopur Shukul +Lansing +Groton +Poko +Nārāyanavanam +El Playón +Kin +Two Rivers +Jamaat Shaim +Roncador +Deodha +Tetyushi +Lang Suan +Priolo Gargallo +Quebrangulo +Farciennes +Pulpí +Malkanūr +Alburquerque +São João do Manhuaçu +Scalea +Kings Mountain +Sahsaul +Möglingen +Barrington +Willistown +Touama +Prien am Chiemsee +Las Charcas +Saint-Jean +Kingsnorth +Makhmālpur +Alpu +Männedorf +Eranāpuram +Greasley +Cranleigh +Westwood +Retie +Oregon +Kpandae +Chintakunta +Bramhabarada +Bardīha Turki +Attippattu +Gethaura +Kishunpur +San Pablo Atlazalpan +Akjoujt +Sankt Johann im Pongau +Pueblo Viejo +Lavandevīl +Kourouma +Derbisek +Hagen im Bremischen +Beniaján +Tentena +Bora +Boguchany +Betânia +Mineiros do Tietê +Resende Costa +Mountain Ash +Inzago +Werther +Lauingen +Porto-Vecchio +Moyuta +Pasaco +Monteforte Irpino +Sidi Abdellah Ben Taazizt +Anenecuilco +Ortakent +Rahiār Kunchi +Saint Ives +Crayford +West Point +Koog aan de Zaan +Amarchinta +Jagdīshpur +Lincolnton +Aratuba +Antônio Carlos +Herbolzheim +Hormigueros +Gravelines +Belauncha +Vaḩdattīyeh +Korablino +Kassa +Djemmorah +Amala +Granbury +Kaset Wisai +Vanj +Castle Bromwich +Wanaque +Chulym +Kédougou +Hamadānak +Miechów +Kursavka +Şaydā +Sanzana +Holíč +Pinheiro Machado +Mannarai +Hambantota +Marquette-lès-Lille +Dhakaich +Talupula +Abbeville +González +Rānti +Emba +Prachatice +Diez +Montechiarugolo +Nembro +Schöningen +Beni Ounif +Khargrām +Isselburg +Temamatla +Tittachcheri +Kummarapurugupālem +Sonseca +Lathasepura +Carbonera +Ichikai +Hanover +Zvenigovo +Guilherand +Bom Jesus +Laukaria +Lommedalen +Būdili +Tagami +Northbrook +Namli +Kalasa +Nelson +Iygli +Lloró +Sanjiaocheng +Tissa +Valkeala +Sarāb-e Tāveh-ye ‘Olyā +Borjomi +Sirgora +Chamestān +Storm Lake +Ladysmith +Chikni +Bhisho +Pihuamo +Faxinal dos Guedes +Tournon-sur-Rhône +Shahmīrzād +Kazarman +Boiling Springs +Feldkirchen-Westerham +Mpraeso +Busumbala +Tādepalle +Siniscola +Nambour +Guntupalle +Damonojodi +Ārambākkam +Pacoti +Linslade +Berkley +Ilfracombe +Prescot +Bucheya +Barka Gaon +Montopoli in Val d’Arno +Los Almácigos +Hernando +Capilla del Monte +Armstrong +Kranenburg +Parbata +Salaya +Wootton +Malkhaid +Aurelino Leal +Hinwil +Pūngulam +Harsinghpur +Kawadgaon +Baryshivka +Rychnov nad Kněžnou +Chikkārampālaiyam +Satellite Beach +Mhangura +Leek +Moreira Sales +Vaux-le-Pénil +Góra +Arvand Kenār +Arvand Kenār +Hoeilaart +Grobbendonk +Belzig +Kuzuculu +Seringueiras +Westtown +Tucson Mountains +Coldstream +Galbois +Stonehaven +New Port Richey East +Potukonda +Douar Jwalla +Romanshorn +Boujediane +Nāgalāpuram +Douar Ain Maatouf +Rasaunk +Penne +Marginea +Zakamensk +Palukudoddi +Târgu Lăpuş +Wellington +Batesville +San José de Aerocuar +Säter +Kumbadāje +Margherita di Savoia +Piedmont +Elon +Zaouiet Says +Bollullos de la Mitación +Creazzo +Padre Burgos +Vosselaar +Capela de Santana +Areiópolis +Dorking +Grand Rapids +Prymorsk +Cacequi +Bernardino de Campos +Cherry Creek +Sada +Chikha +Honiton +Urbana +Inwood +Mangalmé +Gerasdorf bei Wien +North Lindenhurst +Tambaga +Baley +Kriftel +Aylestone +Bhandārso +Sierra Madre +Zaragoza +Villepreux +Aukštieji Paneriai +Somnāha +Hauterive +Begogo +Antônio Cardoso +Finale Ligure +Planura +Bailin +Mortugaba +Waldkirchen +Balve +Sihaul +Sonbāri +Bannewitz +Mayūreswar +Okhargara +Birch Bay +Atru +Pulsano +Cerese +Lloyd +Barbadanes +Cesson +Dorridge +Olaippatti +Kirensk +Stollberg +Hexham +Cisternino +Enfida +Saādatpur Aguāni +Waipio +Guebwiller +Novska +Banagi +Bālehonnūr +Oberwil +Pongalūr +Kīlkottai +Écaussinnes-Lalaing +Kanamadi +Udawantnagar +Kurman +Fīnch’a’ā +Murapāka +Manatí +Roscoe +Ambinanin’i Sakaleona +Evington +Achankovil +Jamapa +Sheron +Iazizatene +Braunfels +Kowdalli +Valmadrera +Burr Ridge +Fagersta +Palmerston +Kumharsan +De Panne +Brugg +Quintanar de la Orden +Orono +Itanhomi +Panhar +Trofaiach +Southborough +Sultānpur +Camisano Vicentino +Elwood +Congaz +Shahrinav +Mortād +Kachnār +White City +Langeloop +Puerto Caicedo +Uničov +Candelaria +São José do Campestre +Nandayure +Nawalpur +Morant Bay +Matsuo +Mineral de Angangueo +Hasbergen +Iver +Iuiú +Saint-Saulve +Warren +Gengenbach +Kāttupputtūr +La Massana +Phulgāchhi +Plano +Kall +Oporapa +Salcedo +Alto Parnaíba +Ikkādu +Waupun +Vila Frescainha +Tha Chang +An Phú +Caorle +Canyon Lake +Sorontona +Firoza +Murska Sobota +Marilândia +Comarnic +Peri-Mirim +Yamanouchi +Mierlo +’Aïn Taghrout +Ollerton +Port Wentworth +Hatwāns +Kārīz +East Hanover +Chuguyevka +Nazyvayevsk +San Bernardo +Castelvetro di Modena +Capitão de Campos +Georgian Bluffs +Oud-Heverlee +Nāgojanahalli +Parapuã +Duggirāla +Ararendá +Gucheng +Vilyuysk +Illintsi +Antônio Carlos +Ksar Sbahi +Biganos +Yargatti +Bishunpur +Alcoa +Potosí +Fortuna +Raceland +Melsele +Calçado +Fairview +Bang Ban +Chassieu +Bhattiprolu +Ban Wang Daeng +Panguipulli +Wulingshancun +San Luis de La Loma +Si Wilai +Terku Valliyūr +Aydıncık +Herrsching am Ammersee +Kariat Ben Aouda +Céu Azul +Bagamanoc +El Trébol +Divinolândia +Gachetá +Canandaigua +Juruaia +Pedersöre +Congonhal +Şā’īn Qal‘eh +Iguaraci +Rāmanāyakkanpālaiyam +Cleveland +Belén de los Andaquíes +Pāta Ellamilli +Moorslede +Paullo +Narimanov +Piazzola sul Brenta +Konnūr +Pirapetinga +Basaithi +Wen’anyi +Pontchâteau +Río Cuarto +Arenzano +Citrus Springs +Berane +Lālsaraia +Dayālpur +Karebilachi +Codigoro +Nossa Senhora dos Milagres +Hebri +Elchūru +Asahi +Clayton +Merriam +Niémasson +El Águila +Týrnavos +Szprotawa +Passy +Union City +Kać +Dois Riachos +Stiring-Wendel +Selma +Wyomissing +Santa Mariana +Barton upon Humber +Cologno al Serio +Lake Grove +Leicester +Shamunpet +Marinette +Doesburg +Mahīn +Nörvenich +Panazol +Kaniyambādi +Villa La Angostura +Longtaixiang +Planegg +Pēnagam +General Carneiro +La Calamine +Timperley +Whitchurch +Great Wyrley +San Giustino +Great Neck +Jayaque +Korb +Dalippur +Lower Saucon +Mainvilliers +Hāthidāh Buzurg +Bangramanjēshvara +Rutesheim +Devanakavundanūr +La Trinitaria +San Carlos Centro +Floresta Azul +Aba +Shankarpalli +Chhabila +Kadūr Sāhib +Coshocton +Monmouth +Mandal +Zirə +Āwash +Le Luc +Nanpala +Pharāha +Pompton Lakes +Novoazovsk +Thanh Xuân +Lansdowne +Moita Bonita +Wangjiabian +Larbert +Sturgis +Brzeszcze +Fagundes +Glyká Nerá +Boguchar +Montlouis-sur-Loire +Ballymoney +Kisara +Epping +Dumri +Mādhopur +Petal +De Pinte +Riorges +Mukāsi Pidāriyūr +Andacollo +Magnolia +Ráckeve +Puliyara +Joquicingo +Jacinto +Möhlin +Bithlo +Feira +São Sebastião de Lagoa de Roça +La Esperanza +Chornomorske +Sankhavaram +Sakkamapatti +Bottesford +Konen Agrahār +Hikawadai +Yuasa +Lindome +Columbia +Summit +Athy +Koch +Perali +Tezoatlán de Segura y Luna +Aperibé +Kenduadīh +Murgod +Rezina +Phanat Nikhom +Tonse West +Delvāda +Anguera +Hornsby Bend +Fanlu +Riedlingen +Waihee-Waiehu +Cold Springs +Veľký Krtíš +Sosnovka +São Félix +Tekkumbāgam +Bertinoro +Milicz +Botuporã +Gernsheim +Maromme +Amha +Qal‘at an Nakhl +Kongupatti +Ichikawa +Tirumayam +Balingoan +Rolla +Fredericksburg +Brumunddal +Corinda +Carmo da Mata +Weyburn +Ekuvukeni +El Hamel +Tavistock +Allāhdurg +Velddrif +Kalyazin +Nedelišće +Sannieshof +Itatuba +Bir Ghbalou +Greentree +Gloucester Point +Mawu +Mangalam +Antanandava +Kiwoko +Allonnes +La Palma +Waterloo +Chatteris +Sarauni Kalān +Fairview Shores +Xinyaoshang +Vijes +Domažlice +Patu +Waynesboro +Damme +Wālūr +Dolhasca +Franklin Lakes +Chinna Annalūru +Sebring +Regen +Solymár +Saucillo +Shalushka +Siyāna +Älmhult +Lorch +Chauki +Collegedale +La Tuque +Norwich +Abasingammedda +Morafeno +Ambotaka +Kalafotsy +Antsoha +Maroambihy +Voloina +Ambatomasina +Ambodiampana +Antsakanalabe +Antsahabe +Antakotako +Tsararano +Mahazony +Fotsialanana +Ambinanindovoka +Ranomafana +Ankavandra +Manambolosy +Ambohidranandriana +Tsinjomitondraka +Amporoforo +Ambodimangavolo +Analamitsivalana +Bevata +Antsambahara +Androndrona Anava +Sampona +Marolinta +Andranomeva +Miandrarivo +Ambodimanary +Maroamalona +Marovantaza +Marotandrano +Antanandava +Efatsy-Anandroza +Manandroy +Tranomaro +Vinaninkarena +Soaserana +Soamanova +Loikaw +Side +Cerro Corá +Zhamog +Pingcha +Mahād +Al ‘Amādīyah +Santa Flavia +Burlington +Karabash +Itiruçu +Saint-Martin-Boulogne +Byelaazyorsk +Lyndon +Oak Ridge +Edlapādu +Bāgnān +Jagannāthpur +Shagonar +Agua de Dios +Srīpur +Patori +Vengapalli +Farkhâna +Llandybie +Matino +Issoudun +Westview +Dhilwān Kalān +Springdale +Meaford +Komorniki +Churchdown +Guspini +Ribeirão Bonito +Schkopau +Ma’ai +Bardipuram +Mānsinghpur Bijrauli +Summerfield +Myjava +Akyaka +Dayr ‘Aţīyah +Sabalito +Wildau +Nagar Nahusa +Argostóli +Colwyn Bay +Santa Cruz Balanyá +Honwāda +North Logan +Belousovo +Paraúna +Duas Barras +Santa Croce Camerina +Burkburnett +Citrus +Fao Rai +Pingtouchuanxiang +Bargas +Sendurai +Paura Madan Singh +Warden +Cudworth +Singoli +Junín +Pebble Creek +Chicureo Abajo +Oakham +Yelpur +Sesto Calende +Totowa +Easttown +Adjala-Tosorontio +Koronowo +Əhmədli +Piraí do Norte +Xintianfeng +Celina +Manaíra +Staryya Darohi +Rekovac +Punjai Turaiyāmpālaiyam +Cáchira +Aş Şabbūrah +Bīrpur +Mallagunta +Satoshō +Takad Sahel +Tixkokob +Acanceh +Haigerloch +Cabanillas del Campo +Bāwāli +North Branch +Champua +Vochaïkó +Bhangha +Campagnano di Roma +Sarafand +Ongwediva +Guiratinga +Mêdog +Zhydachiv +Três Cachoeiras +Rāmabhadrapuram +Álamos +Vengūr +Masakkavundanchettipālaiyam +Peschiera del Garda +Grand Haven +Volda +Mēmunda +Boekel +Szydłowiec +Rogoźno +Quimilí +Dek’emhāre +Matias Olímpio +Tarrá +El Tarra +Eisenberg +Rüthen +Hellesdon +Nacajuca +Punitaqui +Adendorf +Neuhof +Keles +Highgate +Tirupporūr +Iioka +Little Ferry +Cunha Porã +Burgkirchen an der Alz +Tortolì +Lézignan-Corbières +Longford +Canton +Bishunpur Hakīmābād +Palsud +Rosario +Borsbeek +Devgeri +Satai +Al Ghayz̧ah +Ibiraci +Ūttukkuli +Mahmūda +Völkermarkt +Lichtenstein +Nao Kothi +Cepagatti +Timahdit +Vadamugam Vellodu +Pottireddippatti +Ruisui +Chevigny-Saint-Sauveur +Nallamada +Cacaopera +Ebreichsdorf +Ḩaşşeh +Pleasant View +Hamilton Township +Kalavapūdi +Pompton Plains +Belgrade +Rialma +Ban Yang Hom +Vashon +Rokytne +Quilombo +Itamarati +Rumburk +The Hills +Pedappai +Mena +Semuto +Nādendla +Bni Drar +Texcatepec +Vimmerby +Cantagalo +Kabīrpur +Garmeh +Shōō +Beachwood +Bedford Heights +Corbas +Mazzarino +Sângeorz-Băi +Attard +Malden +Étaples +Kaniyūr +Chatham +Ørsta +Malente +Chāoke +Valley Center +Ingelmunster +Swarna +Donzdorf +Karakthal +West Caldwell +Stratford +Minamiise +Sisia +Hakone +Sankhavaram +Chinnatadāgam +Bariārpur Kāndh +Basbiti +Madna +Memphis +Wustermark +Grandview +Ponteland +Mossley +Ferney-Voltaire +Hollinwood +Kuttappatti +Pellezzano +Barberino di Mugello +Troy +Berriozar +Tibubeneng +Brandon +Wronki +Guéoul +Meliana +Sontha +Altensteig +Karlsdorf-Neuthard +College +Blitta +Tizi +Prineville +Arniya +Venosa +Timberlane +Bueno Brandão +Groaíras +Rincon +Kamen’-Rybolov +Kavaklıdere +Kenzingen +Costeşti +Zasechnoye +Meulebeke +Krosno Odrzańskie +Oued Sebbah +Keila +Ogose +Lehman +Yairipok +Koilakh +Roque Pérez +Chenango +Muswellbrook +Khānjahānpur +Sangrām +Nishi +Pocono +Bobrynets +Osterwieck +Karikād +Chiltiupán +Gages Lake +Passa e Fica +Tarwāra +Minamisanriku +Delhi +Imeni Chapayeva +Bobrovytsia +Xudat +Castel Mella +Nyakosoba +Gilbués +Santoña +Kodmiāl +Manville +Berthoud +Alto Rio Doce +Kiratpur Rājārām +Haysville +Soyaló +Great Harwood +Hardia +Erraguntla +Ghanpur +Qaşr-e Qomsheh +Terryville +Bernardo de Irigoyen +Wisła +Sairé +Yacopí +Langenzenn +Erstein +Fife +Triangle +Kandry +San Roque +Gottmadingen +Glückstadt +Beni Abbès +Inverurie +Garden City +Gentio do Ouro +Coroaci +Yungay +La Puebla de Cazalla +Lincoln Park +Mesyagutovo +Hammelburg +Nova Vodolaha +Itarana +Conceição dos Ouros +Rāmbilli +Chandūr +Węgorzewo +Mae Wang +Bofete +Harri +Garkha +Petrovka +Serravalle +Sahoria Subhai +Khān Bebīn +Nivala +Castelginest +Siversk +Rush +Pallarimangalam +Schönaich +Kothri Kalān +Shing +Santa Rosa de Río Primero +Figuig +Anenii Noi +Herxheim +Estanzuela +Pānetha +Guthrie +Sant Joan de Vilatorrada +Sagarejo +Ankasakasabe +Arouca +Governador Lindenberg +Puck +Arapgir +Neman +Lichtenau +Avelino Lopes +Rāmchandarpur +Rio dos Cedros +Phrai Bueng +Guantingzhan +Bistān +Tanippādi +Amjhera +Mataili Khemchand +Burrel +Etoumbi +Davos +Bondoufle +Peru +Geldermalsen +Khorol +Sanjiangkou +Mogotes +Soham +Whitburn +Wan Tau Tong +Nirpur +Malvern +Worth +Sarmiento +Ukal +Shamsābād +Belmonte Mezzagno +Adda-Douéni +Wantage +Risch +Hambühren +Chirak +Rianxo +Rainhill +Mudki +Jõhvi +Grand Gosier +Venafro +Asahi +Onna +Pīr Bakrān +Estevan +Anuppampattu +Tiruppālaikudi +Gol Tappeh +Laredo +Venmani Padinjāra +Dolbeau +Pālakollu +Maryānaj +Martinópole +Steinbach am Taunus +Sucúa +Aurāhi +Kapaa +Travilah +Brunete +Kyjov +Pātrasāer +Yellayapālem +Phibun Mangsahan +Danville +Villa Corzo +Pattanam +Nauheim +Afonso Bezerra +Ghagga +Kulgo +Avintes +Amarpur +Vargaūr +Ngorongoro +Tatarbunary +Cassá de la Selva +Khlung +Mount Kisco +Urubici +Lake Monticello +Atchison +Pottipuram +South Dundas +Dabiya +Roseira +Centenário do Sul +Pua +Mutlūru +Chiyoda +Khaţţāb +Sítio Novo de Goiás +São Jerônimo da Serra +Igaratinga +Lavaur +Earlestown +Khasanya +Buenavista +Boizenburg +Jitwārpur Chauth +Reguengos de Monsaraz +Amnéville +Jauli +Van Wert +Rājghāt Garail +Probištip +Laranja da Terra +Gamharia +Kurwār +Snovsk +Manevychi +Rāmpur Kalān +Żurrieq +Martin +São Luís do Curu +Bānu Chhapra +Sugarmill Woods +Hudson +Union Park +Sušice +East Cocalico +Amaturá +Sérékali +Oliveirinha +Dário Meira +Stephanskirchen +Biggin Hill +Kurikuppi +Montgomery +Alberique +Cividale del Friuli +Sohwāl +San Valentino Torio +Yuncos +Hejamādi +Alipur +Olivença +Taufkirchen +Mülsen +Keza +Shepton Mallet +Kaurihār +Călăraşi +Bālupur +Ugrinovci +Bileća +Rejiche +Holmen +Le Pont-de-Claix +Chhātāpur +Andhana +Tyāmagondal +Borshchiv +Gympie +Matomou +Darmahā +Eugenópolis +Icononzo +Pālamedu +Māngobandar +Abbigeri +Castano Primo +Amuru +Eski Yakkabog‘ +Tallimarjon Shahri +Bouchagroun +Methil +Bāra +Terra Boa +Wambrechies +Fox Lake +Panaon +Papraur +Kara-Tash +South Union +Guaraciaba +Scorniceşti +Barbosa Ferraz +Capena +Río Segundo +Cornate d’Adda +Muzaffarnagar +Scaggsville +Timonium +Argelès-sur-Mer +Wooburn +Zafarābād +Arakawa +Wald-Michelbach +Binbrook +Totolapan +Elmira +Seonār +Marreddipalli +Paduma +Telkap +Kuppachchipālaiyam +Ambohidanerana +Flixton +Villa Nougues +Los Corrales de Buelna +Nurmo +Sanson +Kondakomarla +Ravutulapūdi +Emiliano Zapata +Ousseltia +Thouaré-sur-Loire +Miyato +Cantillana +Ānavatti +Belle Chasse +Bougoula +Aleksandrovsk +Annoeullin +Tamiang Layang +Sītalpur +Bolivar +Parkstone +Paceco +Ovada +Guaraci +Nekarikallu +Cave +Chiramanangād +Nilavārappatti +Guaitarilla +Matulji +Bhanghi +Nuvem +Raymond +Villamarchante +Nāranāpuram +Itamogi +Nīrkunnam +Akhnūr +Pipalrawān +Bhaurādah +Waasmunster +Errahalli +Punjai Kālāmangalam +Ban Bang Yai +Gounarou +DeForest +Russellville +Suyo +Morro Bay +Valentigney +Mutukūru +Langhirano +Kirchseeon +Qal‘eh Chan‘ān +Bāsht +Gleisdorf +Agouna +Khrystynivka +Vire +Sarsai Nāwar +Kūhbanān +Gori +Épinay-sur-Orge +Kājhi Hridenagar +Jhundo +Burgos +Ñiquén +Santo Domingo +Antanandehibe +Jarjanāz +Kuruvambalam +Rudra Nagar +Del Aire +Lulhaul +Sāmbre +Odugattūr +Pragadavaram +Kīlrājakularāman +Alakamisy Anativato +Ilıcaköy +East Bakersfield +Villa Unión +St. Albans +Bendougouba +Iramaia +Barnoldswick +Tiszafüred +Moda +Sunadkuppi +Lyngdal +Fürth +Suhāgi +Kattāri +Ogano +Doi Saket +Imi Mokorn +Kuttattuppatti +Jaladurgam +Jackson +Kelilalina +Wang Sombun +Kleinblittersdorf +Kingri +Babhnoul +Komarolu +Lieshout +Déville-lès-Rouen +Lehigh +Itacurubí de la Cordillera +Capela do Alto Alegre +Goasi +Placerville +Mogwase +Campo Largo +Na Sceirí +Racale +Juruá +Fondettes +Sárospatak +Karuvelampatti +Pādiyūr +Munhall +Freeport +Selsey +Brdovec +Crestwood +Chanteloup-les-Vignes +Kumāravādi +Isola del Liri +West Hanover +Borovsk +Pia +Chandreru +Riverton +Nandasmo +Bhālpatti +Rāybāg +Vellavādanparappu +Vidalia +Santa María +Martinengo +Motta di Livenza +Middle Island +Linthicum +Tuktukan +Choachí +Surazh +Chapeltique +Le Pradet +Royston +Kerāi +Le Taillan-Médoc +La Palma del Condado +Deh Bakrī +Uropá +Gaohucun +Bad Orb +Panamarattuppatti +Maniamkulam +Monte San Pietro +Westwood Lakes +Douar Bouchfaa +Amesbury +Forestville +Andovoranto +Ankarabato +Taimali +Rogatica +Bāzid Chak Kasturi +Lowell +Zwettl +Rājānagaram +Grezzana +Santa Bárbara +Praskoveya +Zavitinsk +Ripoll +Voisins-le-Bretonneux +Sapahi +Edwards +Săcueni +Asālem +Fujino +Milton +Alagappapuram +Ōtsuchi +Ubalá +Gülbaar +Dharmavaram +Sharg‘un +Campolongo Maggiore +Novomyrhorod +Ymittós +Ormesby +Diss +Īmani +Meiwa +Richmond Heights +Qamīnis +Villers-la-Ville +Xiaozui +Parnera +Tigrāna +Nandiala +Thevūr +Sivrice +Louvain-la-Neuve +Candiota +Ichnia +Frenštát pod Radhoštěm +Enumulapalle +Léognan +Sütçüler +Raeren +Tamarana +Rewtith +Tanant +Chittārkottal +Sidhap Kalān +Pleasanton +Waggaman +Arbaa Sahel +Sesori +Diedorf +Göynücek +Botticino Sera +Santa Catarina Ayotzingo +Sonoma +Morbach +Jyllinge +Bou Khadra +Marudūr +Loano +Shŭrobod +Aduku +Lwakhakha +Bog’ot +Əliabad +Mara Rosa +Ross on Wye +Marcali +Galsi +Tafalla +Sangam +Qal‘eh Tall +Malibu +Pine Hill +Ambohinihaonana +Borgloon +Desborough +Tolna +San Felice sul Panaro +Potomac Park +Rancho Mission Viejo +Sam Ko +Villahermosa +Tarboro +Nadezhda +Şebin Karahisar +Bestwig +Chettipālaiyam +Milattūr +Ville-d’Avray +Kurdi +Madison Heights +Saray +Hatch End +Sangalbahīta +Trevignano +Maryville +Doraville +Bāghūz Fawqānī +Bogalusa +San Juan +Sidi Namane +Chesterfield +Lachhmīpur +Sahuli +Dallgow-Döberitz +Santa Ana Huista +Medesano +Hartford +Lyons +Bischofswerda +Querência do Norte +Darfield +Gairtganj +Corning +Iretama +Orthez +Jhundpura +Taouloukoult +Peralillo +Gambettola +Matsuda-sōryō +Baetov +Darby +Newberry +Agdz +Tisma +Victoria +Tālsur +Kattipūdi +Yampil +Valadares +Trets +Knowle +Cerreto Guidi +Powdersville +Fô-Bouré +Primavera +Uzda +Sebt Ait Ikkou +Mathigeri +Graçanicë +Bela Crkva +Cazzago San Martino +Iwai +Fountain Inn +Huétor-Tájar +Bhaur +Dakhrām +Maynard +Lohariandava +Ardrossan +Konganāpuram +Hemāvati +Darb-e Behesht +Clute +Cricova +Itārhi +Singapperumālkovil +Katra +Besagarahalli +Hosahalli +Anoviara +Jeseník +Kivistö +Kānkōl +Sālotgi +Dakhān +Cameri +Capim Branco +Broomall +São Brás de Alportel +Lexington +Betsiaka +La Homa +Monte Alegre do Piauí +Aylesford +Chuqung +Joutseno +Chettimangurichchi +Ponnampatti +Al Atārib +Gürün +Yeghvard +Plan-les-Ouates +Aulla +Puranāttukara +Kāla Diāra +Malapannanagudi +Leninskoe +Myers Corner +Nakonde +Chantilly +Kuršėnai +Lamhadi +Bharra +Teus +Daganzo de Arriba +Beaumont +Myślibórz +Ponte Serrada +West Haverstraw +Antsaravibe +Nowa Dęba +Santo André +Novotroitske +Corocoro +Rāmāreddi +Cookstown +Essex Junction +Hrebinka +Santa Terezinha de Goiás +Kadwa +Sant’Antìoco +Filipstad +Tinchlik +Coité do Nóia +Gudibanda +Ambato +Veselí nad Moravou +Vienna +Yuzhno-Sukhokumsk +Gobardhanpur Kanāp +Karadichittūr +Galena Park +Atlit +Amāyan +Friendly +Almoloya +Laćarak +Ruppichteroth +Sedaví +Bellefontaine Neighbors +Pernes-les-Fontaines +Kumbhāri +Sirugudi +Maghra +Felsberg +Ngaputaw +Asfour +Hajeb el Aïoun +San Nicolás +Lambton Shores +Delareyville +Nueva Helvecia +Barmstedt +Bel Air +Sânnicolau Mare +Svedala +Lubawa +Emsbüren +Escuintla +Jacinto Machado +Jiwachhpur +Kendall Park +Little Canada +Chinnamandem +Adohoun +Pāma +Monroe +Blackhawk +Dinagat +Mount Airy +Kralendijk +Schleusingen +Paimio +Groot-Brakrivier +Villa Jaragua +Opatija +Hanson +North Dumfries +Hetane +Altamirano +Dießen am Ammersee +Longbenton +Nakayama +Kilibo +Oloron-Sainte-Marie +Usuppūr +Partāp Tānr +Kanur +Mittahalli +Urcos +Bouhmama +Avigliano +Itzer +Lyakhavichy +Cadelbosco di Sopra +Calçoene +Standerton +Jilava +Ouédo-Aguéko +Antardipa +Canápolis +Ghattu +Khilok +Höllviken +Felton +Englefield Green +Sabnima +Munnūru +Toundout +San Ignacio Cohuirimpo +Lunca Cetăţuii +Pires Ferreira +Barharia +Nanticoke +Mantasoa +Küçükkuyu +Aranda +Massa Lombarda +Bajpe +Abadou +Malaya Vishera +Tubará +Aïn Zora +Khovaling +Küçük Dalyan +Urucânia +Pont-Saint-Esprit +Kingsteignton +Killiney +Khokri Kalān +Tangerhütte +Sokhodewara +Douglass +Acari +West Perrine +Ironton +Pinto +İnebolu +Akbarpur +Al Maḩwīt +Peschanokopskoye +La Maddalena +Gölpazarı +Lake Barcroft +Miro Khan +Karpuzlu +Lymanka +Monte Dourado +Ghoswāri +Dasai +Kabira +Kurhani +Sumner +Ninheira +Aït Hani +Hoogland +Kremenki +Rānko +Buşayrā +Camden +Vysokovsk +Murtosa +Tamanar +Iaciara +Linluo +Böhl-Iggelheim +Vellipālaiyam +Cottage Grove +Fairview +Maida Babhangāwān +Sonakhal +San Gaspar Ixchil +Kartāl +Kombai +Had Laaounate +Jablanica +São João de Ver +Songo +Chorrochó +Jaguari +Yacuanquer +Tenango del Aire +Grijó +Kimpese +Trofarello +Tourza +Pasil +Abergele +Sirūr Tājband +Lejanías +Acate +Nakagawa +Wendell +Amtar +Rocafuerte +Raghunāthpur +Bodippatti +Paricônia +’Aïn Fekan +Zapotitlán +Yazoo City +Kiełczów +Kolárovo +Lagoa do Mato +Jurbarkas +Schönwalde-Siedlung +Sawādah +Shāmpur +Tekkāttūr +Aerzen +Alachua +Airway Heights +Mulungu +Pôrto Firme +Lenzburg +Longuenesse +Gajiginhālu +Daşkəsən +Catanduvas +Scottdale +Tarazona de Aragón +Kasavanampatti +Lakeland Village +Vouzela +Monsenhor Gil +Pūliguntā +Yorkshire +Corumbá de Goiás +Flămânzi +Ban Ko +São Tiago +Bangshang +Neuenhaus +Briançon +Flers-lez-Lille +Trabia +Ambohitromby +Ambolotarakely +Ascope +Koppāka +Acushnet +Bad Iburg +Las Matas de Santa Cruz +Kiến Giang +Millington +Kamikawa +Kanagichō +Puente de Piedra +Pachauth +Pella +Campton Hills +Sawla +Damascus +Chitagá +Pélissanne +Lohārda +Darauli +Kuiyibagecun +Fenglin +Tinqueux +Nave +Don Galo +Beverly Hills +Weinböhla +Le Mars +Martensville +‘Utaybah +Sihma +Monument +Máncora +Ada +Paddhari +Usmate Velate +Niel +São José do Calçado +Büdelsdorf +Mathila +Sweetwater +Excelsior Springs +Mokrisset +Sabaneta de Yásica +Tarabha +Sadovoye +Tilougguit +Phaphot +Kulriān +Red Bank +Yoko +Bir Ben Laabed +Urrugne +Paranã +Chervyen +Wendeburg +Sambalhera +Bischofshofen +Ormesson-sur-Marne +Werlte +Lasht-e Neshā +Shiloh +Marotaolana +Grevesmühlen +Torpa +Madathapatti +Montalegre +Saint-Gély-du-Fesc +Arāvelli +Chota Mollakhāli +Jhonkar +Antenetibe +Apt +Pallattūr +San Juanito +Torotoro +Southwick +Maevka +Weingarten +Nāgambhotlapālem +The Pinery +Zmeinogorsk +Mfou +Leninaul +Kākhandiki +River Grove +Pineville +Mouans-Sartoux +Tāmarankottai +North Bellport +Rabta +Sidi Bousber +Garag +Riverdale +Pazaryeri +Reiskirchen +Reading +Mapleton +Cotacachi +Alcora +Tárnok +Skilloúnta +Alderwood Manor +Dhilwan +Tulshia +Karukkalvādi +Saks +Wanderlândia +La Virgen +Frontera +Benedito Novo +Recreio +Flawil +Felida +Rasebetsane +Parczew +Sahuria +Nāhargarh +Buttar Khurd +Montale +Cedro +Xambioá +Paittūr +Simpelveld +Gölyaka +Flöha +New Albany +Sainte-Savine +Pierre-Bénite +Herīs +Naganuma +Fairview +Loudoun Valley Estates +Forest Acres +Shumanay +Perūr +Vargem +Boudouaou el Bahri +Stansbury Park +Plankstadt +Hilter +Al Karak +Neuhausen am Rheinfall +Obuse +Winterville +Burgau +Bagrīnagar +Monmouth +Nallippālaiyam +Surinam +Santiago Chimaltenango +San Josecito +Murata +Morsand +Soquel +Imst +Andonabe Atsimo +Heule +Ertvelde +Cristino Castro +Tierra Colorada +Čelić +Agramonte +Riesi +Jiménez +San Ricardo +Ban Thung Khao Phuang +Usmat Shaharchasi +Dehqonobod +Nuriston +Bibala +Court-Saint-Étienne +Poção +Angostura +Vembūr +Balwa +Sāmākhiāli +Yedappalli +Kamifurano +Rājāpur +Paina +Presidente Vargas +Markt Indersdorf +Candelaria Loxicha +Susuz +Rio do Pires +Sadon +Lighthouse Point +Buriti Alegre +Bad Wildbad +Kibichūō +Hillcrest +Delta +Marck +Bhopālia +Naini +Adalpur +Devanakonda +Nowe Miasto Lubawskie +Krishnamsettipalle +Heath +Santa Maria de Itabira +Griñón +Gudlavalleru +Hōki +Kearney +Olëkminsk +Soledar +Likiškiai +Regidor +Whitnash +Souq Sebt Says +Chencha +Duraiswāmipuram +Begampur +Ingeniero White +Rājpur Kalān +Gommern +Bonham +Ryhope +Shelton +Ouolodo +El Paraíso +Coacoatzintla +Leidschendam +Attleborough +Somers Point +Sidi Abdallah +Alfredo Wagner +Buraan +Ban Lueak +Lake Hopatcong +Bellmead +Pitkyaranta +Folomana +Las Veredas +Saúde +La Escala +Héricourt +Ambhua +Ontario +Bolekhiv +Cacimbinhas +Arkalochóri +Burgstädt +Clermont +Tiruvalam +Machulishchy +Rokunohe +Española +Hulshout +Taormina +Palatka +Nová Dubnica +Corzuela +Luanco +Balua Rāmpur +Tokigawa +Ambahy +Čáslav +Krèmiss +Bordj Okhriss +Al Musayfirah +Dáli +Hartswater +Belāo +Dighāwāni +Santiago Tangamandapio +Novodnistrovsk +Itaipé +Wepangandla +Vaddepalli +Dhanwār +Gemona del Friuli +Bluffton +Macajuba +Vermilion +Floresta +Olbernhau +Friedeburg +Holbeach +Waimea +East Bradford +Karahallı +Vikrutamāla +Burhia Dhanghatta +Lake Morton-Berrydale +Chalco +Teotlaltzingo +Santa Margarita de Mombúy +Pleasant Hill +Gökçe +Palestina +Lollar +Villers-Cotterêts +Kopparam +Pedras de Maria da Cruz +Launceston +Lakhnā +Standish +Khurmi +Phai Sali +Valpoy +Portage +Minanba +Santiago +Sylva +Flat Rock +Zuyevka +Braine-le-Château +Da +Catanduvas +Vieux-Condé +Bakwa +Lonkly +Fervedouro +El Chol +Biržai +Catunda +Burton Latimer +Saidapet +Plaine Magnien +Timrå +Muquém de São Francisco +Wildberg +Sudogda +Valley +Santa Teresinha +São Sebastião da Grama +Litomyšl +Ulft +Thilogne +Mochizuki +Minobu +Wapienica +Ivankiv +Nova Europa +Koth +Laukāha +Abay +Silverton +Sidi Boushab +Yelm +Manakana +Kāmthi +Dumri +Sisai +Bālia +Dnestrovsc +Yeşilköy +Karkamış +Zawyat Ahançal +Antsahadinta +Choyr +Difficult Run +Le Locle +Eccleston +Melito di Porto Salvo +Plattekill +Fatehpur +Vengānellūr +Ibiassucê +Kiso +Lovejoy +Králŭv Dvŭr +Tarare +Awfouss +Estaimpuis +Takon +Suhr +Labin +Warr Acres +Sotkamo +Fatehpur +Kamalāpuram +Portes-lès-Valence +Worcester +Nevelsk +Southborough +South Lebanon +Darling +Rifle +Firminópolis +Nideggen +Valatt +Nagtala +Roca Sales +Coriano +Libānggaon +Nahulingo +Jurbise +Salò +Wabash +Simbach am Inn +Smithville +Rawdon +Bogué +Ciechocinek +Altenberge +Chandera +Kragerø +Trat +Dona Inês +Shenjiaba +Phangnga +Kalanak +Fairmont +Waterford +Oulad Slim +Târgu Ocna +Prestonpans +Kingston +Grafton +Scartho +Māchalpur +Cunha Alta +Aydıncık +Corral de Bustos +Telsang +Bāghīn +Belaya Kholunitsa +Uraí +Planalto +Oued Amlil +Villeneuve-lès-Maguelone +Thorigny-sur-Marne +Bradford-on-Avon +Barhi +Prakhon Chai +Northfield +Kotharpettai +Kihō +Scituate +Opglabbeek +Podstrana +Marlton +Koffiefontein +Nordkirchen +Kodūru +Ban Ueam +Plombières +Braunsbedra +Cislago +Fayzobod +Masindi Port +Terra Alta +Banta +Cadillac +Brikcha +Croissy-sur-Seine +Mount Vista +Waverly +Hithadhoo +Hachīrūd +Montecchio Emilia +Fairmount +Santiago Suchilquitongo +Kamianka-Buzka +Sāīnkhera +Fanjā’ +Great Dunmow +Charlton Kings +Sleepy Hollow +Cuatro Ciénegas de Carranza +Lemmer +Demmin +Mādhopur +Velappādi +Eriyodu +Gateway +Turmānīn +Kangaroo Flat +Uchoa +Narkatpalli +Neustadt +Jhāua +Farmersville +Suchanino +Puerto Quijarro +Palestina +Tripurāntakam +Leones +Santa Clara La Laguna +Nossen +Jigani +Coronel Freitas +Kaufering +Gelves +Mātsavaram +Dougba +Ickenham +Alaçatı +Gokinepalle +Molango +Constantina +Morinville +Senhora dos Remédios +Caém +Paramoti +Telkapalli +Nakasato +Spout Springs +Gayāspur +Kennett +Cholchol +Faradābād +Jālihalli +Truşeni +Zdzieszowice +Capitólio +Akim Swedru +Pāraippatti +Sangrāmpur +Comstock Park +Spa +Belém de Maria +Altusried +Galion +Fasintsara +Steinfeld +Desuri +Nūkān +Ban Non Sombun +Nidamānūru +Pilar +Mercês +Nalgora +Ordubad +Neuenstadt am Kocher +Mādhopur Hazāri +Raghunāthpur +Winchendon +Douar Echbanat +Conceição da Aparecida +Steinau an der Straße +Sidi Brahim +Phek +Millstone +Czarnków +Mānkur +Sarapuí +Villeneuve-Tolosane +Gharyāla +Plymouth +Sarnen +Haikoucun +Puduppatti +Lima +Nova Olinda +Atripalda +Klipphausen +Kottapuram +Cori +Calimesa +Tnine Sidi Lyamani +Libanté +Augustdorf +Kalgi +Bommārbettu +Momanpet +Corleone +Ortaköy +Ambalanūr +Peravali +Itabirinha de Mantena +Nieder-Olm +Fakirtaki +Shasta Lake +Tavriisk +Bassian +Taksimo +Anröchte +Phon Charoen +Itri +Dan +Jaguaribara +Phulmālik +Bonate di Sopra +Blandford Forum +Khawaspur +Banga +Kemin +Arkadelphia +Fairview +Bārah +Kelandis +Scotchtown +Aesch +Siktiāhi +Hisar +Garden City +Krasnoilsk +Rio do Fogo +Kiklah +Nijoní +Sartana +Gourock +Hirehadagalli +Rounia +Hârlău +Ravanusa +El Refugio +Mallan +Coroneo +Vellatūru +Woodlyn +Delportshoop +Māruteru +Kiridh +Srikrishnapur +Villacarrillo +Shahrak-e Enqelāb +María Pinto +Brikama Ba +Mānikpur +Silea +Canatlán +Jeumont +Mokarrampur +Konkavāripalle +Itapé +Cheruvannūr +Northampton +Brewster +Hoek van Holland +Salempur +Ganga Sāgar +Førde +Natonin +Palmeiras +Steinen +Chestnut Ridge +Aulendorf +Cabestany +Sonwān +Atessa +São Luís do Paraitinga +Tirkha +White Marsh +Garrucha +Caspe +Letychiv +Bowral +Trumbull Center +São Miguel das Matas +Wittelsheim +Levanger +Dăbuleni +Magas +Thạnh Phú +Firou +Aydarken +Benalla +Ahogbeya +Matina +Ben Nasseur +Teddington +Oppatavādi +Ban Dan Na Kham +Melres +Gagnef +Santo Antônio do Jacinto +São Domingos +Qārī Kolā-ye Araţeh +Exeter +El Jicaral +Alvorada do Sul +Erlenbach am Main +Ratnagiri +Oneida +Sirakoro +Ananás +Pintadas +Inhangapi +La Riche +Babhniyāwān +Watervliet +Beni Oulid +Vytegra +Meerhout +Weilheim an der Teck +Vila Franca do Campo +Buzdyak +Itaueira +Wallaceburg +Àrvorezinha +El Álamo +Villebon-sur-Yvette +Riano +Alexandria +Astorga +Arimalam +Appenweier +Stranraer +Ranchos +Höchst im Odenwald +Harsola +Dombāchcheri +Hariāna +Kāmepalle +Newport +Caracol +Berwick +Teocuitatlán de Corona +Karuzi +Peñarroya-Pueblonuevo +Yādavolu +Āzamnagar +Chong-Aryk +Geneseo +Etropole +London +Burtonsville +São Romão +Mildenhall +Clay +West Vero Corridor +Blackfalds +Virginópolis +Úmbita +Aghbalou n’Kerdous +Agua Blanca Iturbide +Amurrio +Bacliff +Wood River +General Salgado +Pola de Lena +Rāmchandrapur +Brookdale +Tyukalinsk +Solim +Paris +Bounaamane +Haspra +Qiushanxiang +Auhar Sheikh +Motibennur +Chester +Riolândia +Rodelas +Southampton +Kurşunlu +Buriti do Tocantins +Khiram +South Huntington +Waynesville +Queens +Jaqma +Großburgwedel +Moldava nad Bodvou +Mwaya +Maurilândia +Jordânia +Qanliko‘l +Pyetrykaw +Broadstone +Lakkireddipalle +Trajano de Morais +Merzenich +Limoux +Knezha +Baran +Caparrapí +Bel Imour +Sabana Grande +Raymondville +Örkelljunga +Santa Inês +Kangning +Birsfelden +Rancho Arriba +Kalamūla +Boldeşti-Scăeni +Alto Paraíso de Goiás +Rubim +Nāna +Shahar Telpa +Bad Lauterberg +Locate di Triulzi +Versailles +Murrells Inlet +Armanāz +Bonfinópolis +Bordj Zemoura +Lyuboml’ +Kirchlinteln +Castelli Calepio +Ouroeste +Ratauli +Castelnovo ne’ Monti +Argayash +Morsbach +Balaungi +Zāwal +Fort Madison +Dalgān +Vilkaviškis +Jordbro +Seridó +Lenoir City +Forestville +Santana +Ma‘rabā +Ekinözü +Guimarães +Billdal +Lutry +Międzychód +Srīsailain +Simarwāra Durgāpur +Arraias +Lishuping +Zaozërnyy +Terra de Areia +’Aïn Tellout +Spitalfields +Rāyen +Hecelchakán +Irineópolis +Cheam +Arlöv +Schalksmühle +Jankampet +Tangermünde +Kandrāwān +La Plata +Bandrele +Sidi Bou Ali +San José de Feliciano +São Geraldo +São Carlos +Sant’Agata de’ Goti +Sidi Embarek +Candelaria +Ganvié +Venecia +Shirako +Itapitanga +Bāgh-e Bahādorān +Oppeano +Ban Wisit +Bogen +San Maurizio Canavese +Selkirk +Saint-Félicien +Kambarka +La Trinité +Mallapuram +Dora +Nanbu +Potiraguá +Salvatierra de Miño +Mehdauli +Peddāpuram +Meghaul +Marina del Rey +Tello +Bela +Tremonton +Gafour +Tekkēkara +Kīlminnal +Gaunivāripalle +Tabubil +Guadalupe +Ullūr +Carroll +Estreito de Câmara de Lobos +Bhikkiwind Uttār +Mel Seval +Pine Ridge +Lenguazaque +Somireddipalle +Māli +Dar Si Aissa +Villa Elisa +Ludlow +Reddipalle +Tipp City +Jaltocan +Ertil +Saudade +Alcaudete +Appleton +Rockport +Chivhu +Goldenstedt +Indalvai +Tenente Ananias Gomes +Axixá do Tocantins +Nazária +Lahstedt +North Lakes +Oliveira de Frades +Rio Acima +Ericeira +Tettuppatti +Alpine +Hopetown +Westonaria +Kut Chap +Fronteiras +Jaipur +Chavinda +Gerzat +Hämeenkyrö +Gohuma Bairia +Querfurt +Blumberg +Sint-Lievens-Houtem +Maxaranguape +Popovača +Körmend +Shahmīrpet +Tārar +Barai +Siparia +Enriquillo +Milton +Whitestown +Elkhorn +Bonito de Santa Fé +Kirchzarten +Bougaribaya +Lonquimay +Ostercappeln +Fenggeling +Goldbach +North Bend +Puerto Santander +Süßen +Alberobello +Boaz +East Whittier +Murowana Goślina +Jaqueira +Nayānagar +Rovinari +Davenport +Robstown +Sgamna +Badru Khān +Induno Olona +Galimuyod +Santa Teresinha (2) +Vairampatti +San Marzano sul Sarno +Catarina +Palmácia +Lūgovoy +Santa Isabel Ishuatán +Angelim +Vila Muriqui +Maheshrām +San Diego Country Estates +Jean-Mermoz +Avranches +Khair Khān +Vellur +Sucupira do Norte +Spelle +Brumath +Ksar Lmajaz +Pfastatt +Kīramangalam +Appārāopeta +Königsbach-Stein +Borgampād +New Hyde Park +Marawī +Pūvalūr +East Setauket +Olifantshoek +Agudos do Sul +Bernex +Rotonda +Głogów Małopolski +Santa Maria Madalena +Chantepie +Pārtibanūr +Minatitlán +Gislaved +Igarapé Grande +Treillières +Raibhīr +Vigasio +Concordia Sagittaria +Akbez +Samux +Governador Archer +Lemington +Jītpur +Muttam +Isola Vicentina +Roberval +Canápolis +Villanueva de Arosa +Miribel +Monte San Juan +Potunūru +Duque Bacelar +Betzdorf +Luathaha +Āhiro +Benyahia Abderrahmane +Koila Dewa +São Miguel de Touros +Halfway +Néo Karlovási +Tha Muang +Dinard +Alguazas +La Chapelle-Saint-Mesmin +Vadakku Ariyanāyakipuram +Kowary +Dangriga +Algarrobo +Milford +Alhendín +Năsăud +Campo Redondo +Santa María +Csömör +Multi +Highland +Tanggemu Nongchang +Nāgasamudram +Divisópolis +Lystrup +Igny +Pingree Grove +Moimenta da Beira +Pântano Grande +Duga Resa +Kandulāpuram +Minamiaso +Charalá +Zumaia +Aubière +Snodland +Ramacca +Oignies +Cordele +Tlalixtac de Cabrera +Cedral +Varre-Sai +Centralina +Huittinen +Douchy-les-Mines +Rideau Lakes +Bonthe +Vejen +Borzna +Medina +Pôrto Esperidião +Flowood +Ouangani +Mosquera +Miāni +Columbia +Fort Oglethorpe +Liberty +Pintuyan +Shyroke +Kastav +Rāmannapeta +Oudewater +Çanta +Tefenni +Navabad +Chavkandak +Kazo +Ellon +Alpena +Sechelt +Tetela del Volcán +Rehburg-Loccum +Courrières +Cortês +Garhi +Inkerman +Bosanska Krupa +Florânia +Tarashcha +Beilngries +Douar Mzoura +New Silksworth +Kottapālem +Barāgaon +Tokunoshima +Carencro +Niebüll +Eleşkirt +Arataca +Claymont +Phra Pradaeng +Radyvyliv +Al Jazīrah al Ḩamrā’ +Pescaria Brava +Narasingam +Barāhi +Lake Arrowhead +Jemaat Oulad Mhamed +San Blas +São José do Jacuípe +Mirante +Blain +Gudofredo Viana +Carmo do Rio Verde +Potosí +Venkatāpuram +Celano +Randazzo +Balangkayan +Załęże +Rainbow City +Astoria +Biandanshan +Poirino +Pechea +Chaungtha +Nanfang +Carice +Jimbolia +Münchberg +Amgachia +Birao +Angwāli +Aliganj +Kothi +Darwa +Akil +Kaynaşlı +Vera Cruz +Shirbadgi +Halgūr +Kendallville +Băcioi +Olovo +Gaillard +Fort William +Mikhaylov +Remada +Chikni +Surla +Birkenfeld +Jhakhra +Pānchi +Jandiāla +Kulundu +Kranuan +Jesenice +Madridejos +Ajjipuram +Pipra Naurangiā +Bellamkonda +Svidník +Ban Bong Tai +Marlton +Bela Vista de Minas +Canteras +Bomporto +Evander +Mörlenbach +Cedartown +Korolevo +Fanzhao +Lābhgaon +Katālpur +Ban Bang Toei +Roddam +Biskupiec +Balikumbat +Bleicherode +Foz +Vorsma +Itapiranga +San Pablo +Ware +Mexicaltzingo +Tut +Zelfana +Calasparra +Jādupatti +Port Townsend +Neuville-en-Ferrain +Bordighera +Castleton +Bois-des-Filion +Rogerstone +New Richmond +Ambalavao +Toca +Sobral de Monte Agraço +Pabégou +Togamalai +Penn +Corte Madera +Vūtukūru +Msila +Sidi Abdelaziz +Akhaltsikhe +Batán +Narot Mehra +Shioya +Higashikagura +Aïn Zohra +Valaparla +Kilkunda +Hazle +Opochka +Teplodar +Maur +Tiruchchuli +Kanteru +Telnāl +Arcola +Molalla +Pilar +Choele Choel +Werneck +Lenggries +Masanasa +Caudete +Aizenay +Kāza +Msoga +Busca +Ban Klang +Paris +Gakuch +Zhengdong +Ouled Rabah +Parempuyre +Visbek +Whitchurch +Mungod +Periyanegamam +Vernal +Jefferson +Franklin +Bibbiano +Alella +Ban Pa Hung +Great Missenden +North Gates +Za’roura +Divonne-les-Bains +Néo Psychikó +Villasāgar +San Felice Circeo +Cabo Rojo +Montignies-le-Tilleul +Okondja +Caudebec-lès-Elbeuf +Rignano Flaminio +Hermantown +Elattūr +Iizuna +Liminka +Corfe Mullen +Fílippoi +Dhanur Kalyānwādi +Snohomish +Siswār +Haldipur +Urlaţi +Aigle +Rompicherla +Solindābād +Rāparla +Hire Megalageri +Qovlar +Potavaram +Hobart +Greenville +Plaridel +Kirkel +Serebryansk +Scherpenzeel +Génova +Deūlgaon Māhi +Auterive +Serra Caiada +Bilga +Kondalahalli +Bhagirathpur +Hillsdale +Doorn +Borim +Mawai +Garden Acres +Monaragala +Pocklington +Beccles +Manglūr +Sirigeri +Ankily +Eidsberg +Zavolzhsk +Cajapió +Belagola +Sūlagiri +Yermolino +Oggaz +Meymand +Kabataş +Altötting +Granada +Meuselwitz +Rettanai +Hipparga +Begowāl +Nāthpur +Sedico +Hull +Fuveau +Aroāli +Rāsak +Tolbazy +Torre Santa Susanna +Sylva +Verkhniy Tagil +Vaddādi +Kalakeri +Anandnagar +Horti +Ardal +Corcuera +Komorowice +Struer +Safford +Masdi +Kondakindi Agrahāram +Adigappādi +Hackettstown +Warrenton +Garliava +Marondry +Uchtepa Qishlog’i +Bandixon +Isabela +Baker City +North Glengarry +Türkan +Gunbarrel +Videle +M’Chouneche +Raghunāthpur +Tutzing +Mori +Harrison +Zérizer +Buxerolles +Daruvar +Mashhad Rīzeh +Perumuchchi +Parnaguá +Kittery +Syców +San Simón +Gambissara +Cuapiaxtla de Madero +Havanūr +Nizza Monferrato +Nkokonjeru +Jomasho‘y +Shohimardon +Naftalan +Forres +Chauki Hasan Chauki Makhdum +Glenwood Springs +Rokkasho +Nogliki +Sarız +Roboré +Fouesnant +Padiham +Machchand +Shāhpur Chaumukhi +Muurame +La Misión +Talugai +Korosavāda +Progress Village +South Huron +Guémoukouraba +Kumçatı +Sahidganj +Marieville +Kadalādi +Procida +Cairo +Candelaria +Worsley +Ekamba +Zhatay +Nong Ki +Quartz Hill +Warfield +Jangalapalle +Advi Devalpalli +Brandywine +Mellieħa +Kapyl +Erutukada +Cambridge +Miḩqan +Hardās Bigha +Karapa +Ōnan +Alexandria +Vaals +Kafr Takhārīm +Kele +Padugaipattu +Littleton +Roßtal +Gignac-la-Nerthe +Spanish Fort +Primeiro de Maio +Fiuggi +Cody +Zuidlaren +Bingham +Kilsyth +Thikriwāla +Chichihualco +São Sebastião do Maranhão +Hongliuwan +Tympáki +Pińczów +Umarizal +Weil im Schönbuch +Abergavenny +Ráth Tó +Eumseong +Penetanguishene +Santa Catarina Masahuat +Hozin +Saladoblanco +Kouinine +Cabañaquinta +Mondeville +Mālīnagar +Sapkyo +Rio Vista +Naters +Saint-Max +Kuroshio +La Grange +Williston +Sax +Ghatāwān +Novi di Modena +San Elizario +Çeltik +Khaira +Roelofarendsveen +Bieber +Binéfar +Cabriès +Yaotsu +Atoka +East Rockaway +Villerupt +Bee Ridge +Neves +San Vicente de Castellet +Mesrā +Badia Polesine +Waldwick +Stropkov +La Leonesa +Göytəpə +Ouédo +Porangaba +Kasba +Howell +Antanananivo +Mondaí +Bāgalūr +Vrnjačka Banja +Kidira +Avelgem +Swieqi +Valozhyn +Willstätt +Mazamet +Lake Hiawatha +La’tamna +Ban Bu Sung +Mstsislaw +Heubach +Kwale +West Point +Olaine +Bobenheim-Roxheim +Were Īlu +Auchel +Kosh-Agach +Sung Noen +Struthers +Jafra +Jocoro +Radlett +Browns Mills +Clinton +Orange Lake +Dudhpura +Shāhpur Undi +Bāra Khurd +Mocharim +Alassio +Scenic Oaks +Burgum +Parthenay +Mold +Rupāna +Abjīj +Macerata Campania +Jiji +Călan +Kisújszállás +Dhāmua +Taghbalt +San Ignacio de Moxo +Rüdesheim am Rhein +’Aïn Naga +Dalāwarpur +Begijnendijk +Satipo +Verucchio +Krivodanovka +Sāha +Rangasamudram +Vange +Lysá nad Labem +La Bañeza +Takahama +Blackwells Mills +Namorona +Levski +Méry-sur-Oise +Al Hāmah +Vysokyi +Schotten +Oakbrook +İliç +Francisville +Ciudad Tula +Chapeltown +Lydney +Taiyūr +Gauli Palāsiya +Petmanhalli +Pájaros +Elgin +Büyükorhan +Tinkoni +Lepākshi +Sakuho +Makaha +Mikuszowice +Mengibar +Lipova +Maywood +Tiszaföldvár +Glocester +Zaniéna +Abaiara +Nallikodūr +Sermoneta +Cross Lanes +Parilla +Ikryanoye +Bou’nane +Vembaditālam +Cheat Lake +Mahārājapuram +Baldeogarh +Volpago del Montello +Tay +Zaggota +Olevsk +Houthulst +Suthālia +Puduparambubhāgam +Başmakçı +Sirugamani +Nikaidō-kaminoshōchō +Massi +Hājīpur +Red Hook +Ganapavaram +Castlegar +Lauterach +Munagapāka +Bela +Goito +Benemérito +Novooleksiivka +Tomboutou +Pānrepatti +Lālmunia Munhāra +Kūcheşfahān +Bierbeek +Steinhaus +Kaldenkirchen +Vernouillet +Southwater +Kirundo +Kīl Perambalūr +Harrisonville +Dumont +Canelli +Toukoto +Kingaroy +Airmont +East Rutherford +Kuldīga +Corozal +Reedsburg +An Nayrab +Grosse Pointe Farms +Vernon +Vuktyl +George Mason +Trebbin +Belpāra +Cavan Monaghan +Huejuquilla el Alto +Ramiriquí +Montignoso +Chesapeake Ranch Estates +Pajacuarán +San Pedro Huamelula +Dylym +Dhangaraha +Manahari̇̄ +Pakka Kalān +Sīlamalai +Juripiranga +Mayfield +Roslyn +Fort Meade +Piedrahita +Saint-Laurent-de-la-Salanque +Dagiāpāra +North Versailles +Platón Sánchez +Strasshof an der Nordbahn +Vandamettu +Dores de Campos +Le Beausset +Capodrise +Moba +Suaita +Alajärvi +Senanga +Zaruma +Saint-Loubès +Lowell +Villiersdorp +Horbury +Tiri +Amwa Majhār +Soahany +Haider Khel +Krynica +Shengping +Kolagallu +Srîfa +Chartoûn +Qâna +Amioûn +Râs el Metn +Ed Dâmoûr +Butha-Buthe +Ghadāmis +Belavabary +Mahabako +Boanamary +Morafeno +Esira +Sandravinany +Bedidy +Vohitrafeno +Daraina +Masiaboay +Ambatomivary +Ambodisikidy +Ambohimiarivo +Bekodoka +Maroharatra +Ambararatabe +Ambatomifanongoa +Ambohitrambo +Ebelo +Tsararano +Analalava +Lohafary +Antsoantany +Ambovonomby +Isahara +Ambodivoara +Vodiriana +Ambohimahazo +Ambodimadiro +Andranambolava +Marosakoa +Amborompotsy +Soavimbahoaka +Erada +Mahabe +Mahabo-Mananivo +Miary-Taheza +Ankadindambo +Antaretra +Betrandraka +Amparihy +Tanamarina +Sahanivotry-Manandona +Andranomenatsa +Ambodimandresy +Anontsibe-Sakalava +Amparihitsokatra +Ambatolava +Ankerana +Sihanamaro +Vinanitelo +Vinanitelo +Ifarantsa +Miarinarivo +Ampasimazava +Vohitany +Vohitsaoka +Andranopasy +Beheloka +Ankirondro +Tamponala +Ambatolahy +Katsepy +Vondrozo +Tanambao-Daoud +Sahatona-Tamboharivo +Beanana +Soatanana +Ampitahana +Anosimparihy +Vatana +Ambalanjanakomby +Zoma-Bealoka +Jangany +Ianapera +Ambahatrazo +Fanjakana +Mora +Putao +China +Padang Besar +Závora +Outjo +Al Mazyūnah +Surmon Chogga Grong +Basla +Gadani +Bunji +Ghota Fatehgarh +Setúbal +Diabougou +Koumpentoum +Gadoon +Murgap +Khunays +Kadama +Bukomansimbi +Xishrov +Oyim +Tujg +Boladı +Fatikchari +Puerto America +Mboki +Gar +Ziketan +Sola +Mataguá +Morayra +Karaga +Rebola +Pont Sondé +Haria +Waghāi +Ghargaon +Deh +Medarametla +Hanzviur +Amsin +Muttalakanpatti +Isnapuram +Karumūlaikkal +Dhalai +Nidamalūru +Budwan +Khānpur +Venkatāpuram +Chhimluang +Dhīrwās +Siddarāmpuram +Reha Mota +Paravākkottai +Mevāni +Chhatarpur +Thariāl +Dewal Thal +Lakshmīpuram +Gāndhali +Fatehgarh +Kambhāladinne +Kerwāda +Ulipuram +Kochhor +Betnoti +Bālāgām +Mangalam +Sotik +Kargi +Kourani +Kolno +Rāmamangalam +Bishunpur +Wahlstedt +Pierrelaye +Bomareddipalli +Czersk Pomorski +Aniche +Rakhwāri +Atri +Melendugno +Bad Frankenhausen +Mengen +Soyaux +Digora +Downham Market +Vilpatti +San Marcos +Euxton +Erraballa +Pāikpāra +Fatehābād +Lambesc +Yādwād +Monteriggioni +Marton +Ketugrām +Marshall +Älta +Tarhjicht +Lillers +Praia do Carvoeiro +Stenungsund +Rangamāti +Barkly East +Pavlikeni +Salemi +Kinnelon +Ben N’Choud +Fateha +Kengarai +Pullambādi +Chada +Dilra +Aşağı Ayıblı +Gudensberg +Inungūr +Simri +Budd Lake +Maḩajjah +Lapinlahti +Galleh Dār +Lenīnskīy +Brentwood +Gūlyam +Hosur +Woodbury +Independence +Conway +Chambly +Crikvenica +Itikalapalle +Cape Canaveral +Paināl +Madhuban +Penn Forest +Sulęcin +Wood-Ridge +Fairmount +Yueyaquan +Dettingen an der Erms +Cedar Hills +El Cairo +Shildon +Kanyāna +Maḩalleh-ye Shīrīnū +Ainring +Ringkøbing +Tansandra +Fairfield +Bolsward +Hamilton +Eybens +Bishopstoke +Erenler +Valdobbiadene +Quinto di Treviso +Walker Mill +Marysville +San Nicolas Buenos Aires +São Tomé +Inami +Tsunō +Willoughby Hills +Přelouč +Ottendorf-Okrilla +Marly +Bhawānīpur +Barano d’Ischia +Boundji +Bananal +Dumri +Māmobihāt +Colts Neck +East Liverpool +Casino +Kottaipatti +Tzucacab +Gaolingcun +Sîngera +Tân Sơn +Dujiashigou +Vegachí +Erraguntlakota +Detroit Lakes +Altenholz +Cuesmes +Aberaman +Tokkavādi +Hiranai +Los Ríos +Kishunpur +Kambaliyampatti +Şūfīān +Iwashita +Anamã +Chākicherla +San Calixto +Birchington +Judenburg +Sagurē +Nairn +Makale +Sūndekuppam +Kapasiāwān +Woodburn +Padinjārebāgam +Khāndsa +Bastak +Aībak +Bertem +Friedrichsthal +Mahem +Siechnice +Sankt Andrä +Handsworth +Conselve +Ettimadai +Khurān Milik +Ratne +Kawara +Bastrop +DuPont +Halls +Souama +Saharbani +Muskegon Heights +Koloti +Leppävirta +Kapuvár +Anáhuac +Fallston +Kargopol +Aich +Bad König +Oelsnitz +Sorkheh +Picnic Point +Hooglede +Coal +Fiľakovo +Verkhivtseve +Qufādah +Sellersburg +Arauá +Torrejón de la Calzada +Winfield +Birni Lafia +Puttige +Ennamangalam +Cuicatlan +Heeze +Aboso +Chavusy +Tamzoura +Bhado Khara +Ban Ngao +Payerne +Bad Bevensen +Balatonalmádi +Sparta +Mount Holly +Snezhnogorsk +Kamalasai +Sniatyn +Thị Trấn Mậu A +San Antonio +Consuegra +Aldenham +Bellavista +Ócsa +Erquelinnes +Caraíbas +Nariār +Fino Mornasco +P’yŏngch’ang +Pachchāmpālaiyam +Dubove +Pôrto Xavier +Ammanabrolu +Gossau +Padilla +Kenafif +Coycoyan de las Flores +Bad Ems +Ipupiara +Dongou +La Sierra +Marpingen +Burscough +Kamdoli +Santa Maria +Nohfelden +Kraaipan +Waltenhofen +Mīāndasht +Mulungu +Telwa +Hingyon +La Pointe +Akalāpura +Telpur +Kinālūr +Santa Clara +Fosses +Lālganj +Khāpdeh +Bucine +Morden +Birkenau +Karimunjawa +Kalimala +Kadrābād +Parwāha +Villas +Farsund +Grossos +Ţālkhvoncheh +Heves +Vedurupāvalūru +Rockcreek +Ban Pong Yaeng Nai +Oldenburg in Holstein +Kargahiā Purab +Nanzhou +Maxéville +Bajwāra +Capitola +Welkenraedt +Dongcha +Angalakudūru Malepalle +Rye Brook +Temiskaming Shores +Urzhum +Nové Město na Moravě +Dharmavaram +Minnāmpalli +Jilotlán de los Dolores +Novgorodskoye +Setubinha +Bekkaria +Balderton +Pavittiram +Fair Oaks Ranch +Honwāda +Saarijärvi +Barni +Ellisville +Varzedo +Pipraun +Mussomeli +Uniontown +Strzelce Krajeńskie +Liskeard +Pūdimadaka +Dar El Kebdani +Mandello del Lario +Pokrovske +Manduri +Epanomí +Kunnūr +Karajgi +San Isidro +Polistena +Arroyohondo +Sidi Rahhal +Boufatis +Kaunra +Vaiano +Phak Hai +Holsbeek +Glanmire +Bharhopur +Lomazzo +Coweta +Kaith +Ogdensburg +Maceira +Latifpur +Zefýri +Nāvinipatti +Panjāb +Sursee +Baldock +Kirkland +Matadepera +Carmaux +Suhiya +Kourimat +Maisenhausen +Pachalum +Shankarampet +Tonse East +Nenagh +Wāngi +Kümmersbruck +Bilehra +Mission +Pliezhausen +Wilsele +Huldenberg +Schlitz +Dera Baba Nanak +Ippagūdem +Asola +Golden Hills +Rödinghausen +Brinkmann +Frýdlant nad Ostravicí +Diavatá +Siruvalūr +Urgnano +Miadanandriana +Messíni +Baghānt +Jettihalli +Köflach +Ivančice +Worth +Belmont +Sūknah +Coello +Pizarra +Gudgeri +Souleï +Namakadu +Campi Salentina +River Vale +Qiziltepa +Muthābana +Bhadsara +Saquisilí +St. Augustine Shores +Mohlanapeng +Kaimāti +Kadanganeri +Chintapalle +Gurwaliā Biswās +Hinton +Itapeva +Petorca +Sultānpur +Saint-Sauveur +Ankazotsifantatra +Vardenik +Biot +Beckett Ridge +Ga-Kgapane +Kaiken +Ploërmel +Meldola +Wapakoneta +Trancoso +Hosahalli +Ech Chaïbat +Chupaca +Barahra +Margarita +Baltāra +Kösching +Heilsbronn +Krasnyy Yar +Widhwidh +Ban Pha Bong +Vétraz-Monthoux +Foum Jam’a +Ban Phan Chali +Beneditinos +Nayāgaon +Reddigūdem +Heerlerbaan +Ruoqiang +Santa María de Palautordera +Kanjiža +Saint-Pierre-du-Mont +Qāzigund +Douar Snada +Kushnarënkovo +Metsamor +Alcorta +Ouled Brahim +Montigny-en-Gohelle +Chegūr +Ommangi +Varna +Stocksbridge +San Miguel Sigüilá +Sarkeghāṭ +Krzeszowice +Keokuk +Therwil +Lajes +Pālepalli +Iwamuro-onsen +Greencastle +Berd +Swan Hill +Muttattuteruvu +Boudinar +Ntorosso +Roche-la-Molière +Poggio a Caiano +Richmond Heights +Sauk Village +Ambodiriana +Belomorsk +Laakirchen +Vettaikkāraniruppu +Celldömölk +Lingampet +Mahisānrh +Geylegphug +Independent Hill +Nueva Palmira +Majhgawān +Tanichchiyam +Outa Bouabane +Zinkiv +Paradarāmi +Humlebæk +Abram +Bērikai +Ciudad de Loreto +Aranzazu +Mittenwalde +Dandoli +Huichapan +Ii +Sovicille +Nhandeara +Narasimharājapura +Sweet Home +Grigny +Fundeni +Rodynske +Pailón +Renaico +Norosí +Fuente Palmera +Feyzin +DeRidder +Juru +Achchippatti +Kadoli +Santo Stefano di Magra +Cuencamé de Ceniceros +Altofonte +Plains +Mariluz +Tīgaon +Anjahamana +Lambertville +Eerbeek +Berezivka +Barcs +Surīr +Hochdorf +Bisingen +Tūlin +Boriguma +Mosjøen +Tha Mai +Merchweiler +Katarmāla +Cavriago +Sturbridge +Kobeliaky +Chimay +Hānsa +Dindanko +Masquefa +Khariāl +Kokiladānga +Pedda Kotayalanka +Itamukkala +Kaimūh +Shāhzādpur +Manubolu +Partanna +Qarabalyq +Saint-Grégoire +Patsanda +Philipstown +Makariv +Chandankiāri +Ozieri +Fort Salonga +Carregal do Sal +Touwu +Karghar +Vigonovo +Succasunna +Tekit +Bardmoor +Çıldır +Aramari +Cachipay +Tolmezzo +Sieverne +Parkes +Etchojoa +Iraiyūr +Pushpattūr +Terku Narippaiyūr +Bīrpur Bārāpatti Pindraun +Peru +Mudgee +Bockenem +Kodivalasa +Kamikita-kita +Ribadeo +Couzeix +Basso +Toudja +Gangāpur Athar +Suntar +Berilo +Ilsfeld +Pādarti +Nisarpur +Loudonville +Kusugal +Guia Lopes da Laguna +Alfaro +Nödinge-Nol +Jixian +Pithaura +Baragaon +Mogoşoaia +San Rafael Obrajuelo +Nāgulapādu +Amjhār +Castelletto sopra Ticino +Jesup +San Vicente +Jennings +Nong Wua So +Píllaro +Indūrti +Morrovalle +Oulad ’Azzouz +Gorha +Little River +El Espinar +Plainville +Serafimovskiy +Caldicot +General Alvear +Socotá +Haapsalu +Kiáto +Apiúna +Pasewalk +Vettweiß +Chuhal +Kostrzyń +Santa Bárbara +Kouarfa +Samabouro +Altlandsberg +Köngen +Aurāhi +Basāpatna +Pasupatikovil +Jerissa +Sasaima +Nordwalde +Nūrpur +Kalas +Vlist +Banino +San Sebastián +Coamo +Vidor +Sande +Totma +Portet-sur-Garonne +Arden Hills +Pālkot +San Vendemiano +Grey Highlands +Dumri +Gangaur +Sorab +Ban Mae Sam Laep +Kinhālu +Kalyānpur +Paravāda +Bergambacht +Laubach +Kīl Vālūr +Keshwāri +Alden +Djangoa +Olmos +Drezdenko +Rasht +Hönow +Mānushmuria +Mae O +Bauska +Imlil +Pisac +Mikun +Velpūru +College Place +Reşadiye +Bad Liebenzell +Sukhsena +Boscotrecase +Pleasant Valley +Sabana Larga +Madera Acres +Dorogobuzh +Altmünster +Silvārpatti +Khathjari +Hawera +Łobez +Santa Rosa +Dangcheng +Visselhövede +Alamosa +Bukowno +Veppattūr +Kominato +Lincoln City +Magstadt +Seysses +Avon Park +Chevy Chase +Marathon +Tiztoutine +Trescore Balneario +Bagnolo in Piano +Bay St. Louis +Alcarraz +Choppington +Chak Thāt +Bāg +Beverly Hills +Nyurba +El Roble +Kippax +Bamora +Ghāriyah al Gharbīyah +Valpovo +Kami-kawabe +Alkhan-Yurt +Gökçeada +Tottington +Kotli Ablu +Mora +Jhitkahiyā +Kiban +Albert +Alagarai +Koini +Terrace Heights +Helston +Rute +Neshannock +Štúrovo +Chom Thong +Bueng Khong Long +Ronda Alta +Dodworth +Charne +Gangania +Nueva Toltén +San Fernando +San Fernando +Bavānāt +Muddanūru +Biei +Repatriación +Nallamadu +Rāmnagar Bankat +Jalālpur +Semri +Derazhnia +Oulunsalo +Heath +Miastko +Figeac +Stonegate +Farako +Xincun +Manalūrpettai +Cittanova +Urdorf +Pakri +Vālāntaravai +Tesalia +Pepinster +Sugbongkogon +Perket +Karankot +Garrison +Puerto Tirol +Zell am See +Wölfersheim +Loudéac +Montemarciano +Mahīnāthpur +Keansburg +Plüderhausen +Çamoluk +Barracão +El Molar +Washington +Agatogba +Wald +Voreppe +Kanchanpur +Jaisinghnagar +Kāttāgaram +Green Cove Springs +Gangūru +Salkhua +Fultondale +Ncora +Gaszowice +Pobiedziska +Ingré +Erravaram +Aramangalam +Kolnūr +Xiba +Oberstdorf +Shepperton +Pembroke Dock +Gorom-Gorom +Dhamsāin +Croydon +Mesetas +Sant’Egidio alla Vibrata +Grigiškės +Tadla +Glencoe +Włoszczowa +Sankt Johann in Tirol +Chegurumomadi +Jagatpur +Monmouth Junction +Suzdal +Byalynichy +Sātulūru +Shāhganj +Ambinanintromby +Lakha Nëvre +Ghabrah +Dubliany +Alayor +Tsaramasoandro +Liesveld +Radekhiv +Felpham +Beiuş +Hacarí +Urlāha +Yamaguchi +Puerto Nariño +Dulce Nombre de Jesús +Undavalli +Dhanwāda +Andrainjato +Fandrandava +Ban Mae Chedi +Basavilbaso +Skidal’ +Sirdala +Hunasamaranhalli +Ciudad Cuauhtémoc +Ribeirão do Largo +Bad Breisig +Cobham +Sedona +Brownsville +Ain Karma +Novyye Atagi +Redentora +Twist +Darnétal +Raynes Park +Lykóvrysi +Chausa +Murungattoluvu +Birżebbuġa +Fürstenau +Osthofen +Ghusiya +Motīpur +Eagle Point +Everswinkel +Semmarikulan +Calca +Quiculungo +Giesen +Bou Nouh +Kaithinia +Non Sung +Bom Jesus da Serra +Lágos +Gambolò +Moyogalpa +Aleksandrov Gay +Alto Piquiri +Kitee +Rangasamudram +San Giorgio del Sannio +San Pedro +Grand-Couronne +Cambridge +Kusumha +Hadiāya +East Brandywine +East St. Paul +Nova Floresta +Atlapadu +Rāmpur +Nolinsk +Drăgăneşti-Olt +Chiang Klang +Morrisville +Seforong +Thomaston +Vesele +Karattuppālaiyam +Elambalūr +Zāhed Shahr +Terralba +Decatur +Oudenburg +Idanha-a-Nova +Ledegem +Tayakou +Bareh +Kakkat +Tivim +Aghbalou Aqourar +Mahuver +Carnaubais +La Ferté-sous-Jouarre +Panganiban +Bondues +Tellār +Ingleside +Portland +Estavayer-le-Lac +Litovel +Qahjāvarestān +Shchuchye +Wijnegem +Pine Lake Park +Haraiyā +Ayvacık +Oberhausen-Rheinhausen +Haraiyā +Aqadyr +Fredonia +Lanškroun +Kottampatti +Sundarapāndiyam +Poggio Renatico +Zengjiaba +Worpswede +Lāpangā +Mentone +Wakoro +Vitorino +Serris +Douar Lehouifrat +Ranomafana +Volodarsk +Tiruvengadam +Harvard +Kokologo +Periyapuliyūr +Kannāndahalli +Vanipenta +Gołuchów +Sovata +Lovendegem +Punnavalli +Belsara +Bararam +Rāmpura +Lake Mohawk +Mount Evelyn +Schwaikheim +Pipra Dewās +Madhubani +Āttūrkuppam +Eksjö +Polorós +Purkersdorf +Genemuiden +Huari +Chilanga +Sirka +Wadgira +Tungāvi +Flowery Branch +Mae Ai +Imías +Asuke +Dassel +Zafargarh +Rombas +Pāra +Rolesville +Aklim +Pai Bigha +Sant Julià de Lòria +Neu Bleckede +Rhosllanerchrugog +Kokoszki +Dodóni +Latteri +Cypress Gardens +White Horse +Kola +Imām Şāḩib +Talachyn +Wittenbach +Loreto +Pakhtaobod +Neves Paulista +Belalcázar +Gorgāb +Oostzaan +Sigtuna +Ban Bueng Kok +Sidi Ahmed El Khadir +Santamāgulūru +Mohdra +Malhārgarh +Velakkuttai +Raseiniai +Guifões +Saint-Lys +Pórto Ráfti +Country Club +Hoeselt +Moravská Třebová +Bohechío +Eidson Road +Sävja +Avalūrpet +Santo Tomás de los Plátanos +Pueblo Viejo +Saint-Vith +Tanaina +Stoneham-et-Tewkesbury +Fuli +Ventania +Concordia +Lentvaris +Amritpur +Klötze +Benkovac +Csorna +Sunbury +Hyrum +Alfred and Plantagenet +Alberdi +Großröhrsdorf +Borgo +Pullūru +Pondalūru +Perumbalam +Jibou +Ak-Suu +Thap Khlo +Ban Kang +Ingichka +Brandis +Makri +Piprai +Mońki +Chuy +Lambeth +Pindra +Crosia +Lurate Caccivio +Madanpur +Dināra +Ottappidāram +Castellamonte +Zoudjamé +Tuta +Lindesberg +Canonsburg +San Gregorio Atzompa +Pathāri +Pechory +Inácio Martins +Mākhar +’Aïn Leuh +Kenzhe +Donabate +Lisbon +Tepetlán +Zumárraga +Middleton +Valsequillo de Gran Canaria +Villarrubia de los Ojos +Parmānpur +Laligam +Velpūru +Bithauli +Mount Airy +Langar +Mānrar +Oued Laou +Huachipato +Revel +San Francisco la Unión +Čepin +Orotina +Pinos Puente +Aleşd +Thepaha Rāja Rām +Reddippatti +Pareo +Palafolls +Karīmpur +Kishanpūra Kalān +San Juan del Puerto +Luza +Kadriye +La Farlède +Dundankop +Byarozawka +Bradwell +Kannamangalam +Dahua +Masku +Talapalli +Salmānshahr +Jucuruçu +Arques +Inverell +Bni Boufrah +Barahbatta +Merville +Dandkhora +Pike Road +Palmares Paulista +Sallaumines +Hardia +Tiruvambalapuram +Konidena +Silver City +Comala +Nova Bassano +Dhorgaon +Sangonera la Verde +University of Virginia +Tuam +Simrol +Le Muy +Countryside +Mont-Tremblant +Saint-Doulchard +Ikkarai Boluvāmpatti +Delčevo +Tirúa +Akabira +Glenfield +Nanjundāpuram +Kataha +Torihama +Ban Dong Mada +Immingham +Beltangadi +Ban Lao Yao +Frøn +Willow Street +Pereshchepyne +Bendrahallī +Piploda +Kathūrah +San Lorenzo della Costa +Venecia +Adamankottai +Nueva Esparta +Santa Ana +Bhāgsar +Broni +Argelato +Orange Cove +Veitshöchheim +Racconigi +Dombasle-sur-Meurthe +Brewer +Monett +Morehead City +Godhra +Mādāri Hāt +Filadelfia +Dobre Miasto +El Haouaria +Banská Štiavnica +Bochaha +Kudowa-Zdrój +Pinhel +Itasca +Armutlu +Uracoa +Gundi +Nambutalai +Colonia Nicolich +Kauniainen +Selfoss +Kushijima +St. Stephens +Campo Erê +Bastrop +Aweitancun +Sigatoka +Bernay +Sabangan +Caldas de Reyes +Anantpur +Bilāspur +Sturgeon Bay +Saint-Rémy-de-Provence +Bluefield +Port Elgin +Nowy Dwór Gdański +Flexeiras +Saint-Zotique +Krasnogvardeyskoye +Tadhwa Nandpur +Babhantoli +South Strabane +Widnau +Südlohn +Grado +Neuville-lès-Dieppe +Hockley +Datori +Caernarfon +Boves +Saint-Raymond +Oulad Imloul +Zadonsk +Middleton +Fair Oaks +Kfar Aabîda +Władysławowo +Ohrdruf +Masar +Bowen +Alcanar +Rāmpur +Epalinges +Nersingen +Fernán-Núñez +Gāndlapenta +Agareb +Harīpur +Mangalam +Tila +Ćuprija +Izium +Muyinga +Thogadūru +Kirlampūdi +Tottenham +Cuéllar +Herkimer +Mortágua +Dasso +Butler +Yazıkonak +Postojna +Dessel +Sannicandro di Bari +Sandy Hook +Branquinha +Guateque +Bhawānandpur +Nakaechi +Gibsons +Levokumskoye +Eschenbach +North College Hill +Jessup +Swanage +Hindoli +Nurobod Shahri +Yarm +Hemau +Khesht +Oil City +Hartland +Yalagüina +Târgu Frumos +Sofiivka +Bálsamo +Hlinsko +Petua +Ḩās +Dobhāwān +Szigetvár +Middletown +Xicoténcatl +Ban Nam Dip Luang +Höchberg +Gonikoppal +Chavuttahalli +Trzebiatów +Chilpur +Ferros +Beaumont-sur-Oise +Kudayattūr +Manhattan +Agoué +Cavalcante +’Aïn Kihal +Soanpeta +Jacinto City +Leers +Majali +Wiang Sa +Sūlibele +Casaluce +Grimmen +Sarahs +Henderson +Lüchow +Aire-sur-la-Lys +Pāchhāpur +Banāso +Greenville +Valerik +Wielsbeke +Ratnahalli +Ekma +Puduppattanam +Cingoli +Southport +Bou Zemou +Shiyuan +Ramree +Rellivalasa +Tālavādi +Osterburg +Kensington +Buñol +Perumbālai +Pagidyāla +Avanāshipālaiyam +Lizzano +Bourg-de-Péage +Aytré +Vasiliká +New Square +Saint-Sulpice-la-Pointe +Nāgathān +Photharam +Lago Ranco +Schulzendorf +Gamharia +Bladensburg +Villa Aldama +Petrolina de Goiás +Ézanville +Cuervos +Fauske +Ban Wiang Ka Long +Parūr +Indianola +Sarmera +Jaisinghnagar +Cesa +Aiea +Sānampūdi +Bakaly +Gawān +Ittikelakunta +Mae Rim +Mizhhiria +Bolívar +Kochgāwān +Oulad Ayyad +Karczew +Miltenberg +Nandamūru +Topsham +McKee City +Bilozerka +Daulatpur +Girard +West Glens Falls +Roanoke +Sitebe +Erdőkertes +Parsons +Szubin +Maliaño +Savignano sul Panaro +Sorbolo +Borogani +Šurany +Baretha +LaSalle +Pérols +Chansolme +Kayyngdy +La Paz +Xiada +Adesar +Miānpur Dubauli +Koranampatti +Traversetolo +Lititz +Palankottai +Bisaria +Zymohiria +Paranacity +Semri +Dombarovskiy +Vareš +Parauli +Vīrapāndiyanpattanam +Rathdrum +Pine Castle +Lower Swatara +Basse-Goulaine +Dumra +Kamargani +Velyka Dymerka +Boxley +Vemulanarva +Wepener +Lincoln Village +Port Perry +Bad Gandersheim +Nāzira +Sevilla La Nueva +Kondaparti +Fontoura Xavier +Ekalbehri +Pleasant Grove +Sirsa +Vardhamānkota +Amherst +Teixeira Soares +Gobindpura +Dākpatthar +Kannūlu +Sāngi +Boulder Hill +Fitampito +Sālehpur +Arrapalli +Matawan +Igrim +Mahomet +Elizabethtown-Kitley +Harding +Ashukino +Cunday +Thīkri +Dabhaura +Naruār +Greenwood +Paso Canoas +Kadıköy +Rauco +Canoas +Berchha +Rāmasingavaram +Gangādhar +Bjärred +Chita +Lorgues +Lototla +Nieuw-Lekkerland +Dodarasinakere +Conselice +Ehringshausen +El Cacao +Capitán Mauricio José Troche +Chaplynka +Hohenhameln +Mīnākshipuram +Kanhai +Udburu +Listowel +Kakamas +Mezőberény +Khundāwandpur +Volterra +Bethel +Jinshui +Māmā Khēl +Newburn +Le Rheu +Navani +Ekhari +Khāspur +Maravilha +Wilkau-Haßlau +Colmenarejo +Jalkaura +Hillsborough +Bohemia +Chanal +Piedras Blancas +Barskoon +Menzel Kamel +Jianshi +Crosne +Kaikaram +Vatakemuri +Rāmpur Rajwa +Crystal Beach +Tounfafi +Pagqên +Pedra Badejo +Somvārpet +Yamakita +Falam +Santa Rosa del Peñón +Psychikó +Mallampalli +Xinpi +Meridianville +Tatoufet +Barwādih +Madhopur +Castel Bolognese +Tomah +Ankadimanga +Santa María +Środa Śląska +La Reina +Raun +Middletown +Mooresville +Lescar +Cuorgnè +Esopus +Atāri +Burayevo +Sebnitz +Tadworth +Zawyat Sidi Ben Hamdoun +Eurajoki +Mānbāzār +Valaiyāmpattu +Pahārpur +Bargersville +Águia Branca +Fairview +Cape Elizabeth +Puigcerdá +Madhura +Steger +Tlahualilo de Zaragoza +Basco +Shevington +Smiths Falls +Soldato-Aleksandrovskoye +Worsborough +Castelfranco di Sopra +Seybaplaya +Sztum +Janāpul +Ādivāla +Bni Gmil +Salcea +Byureghavan +Sendamangalam +Ban Nong Tong +Săbăoani +Kalladai +Grinnell +Oosterwolde +Sikandarpur +Manchenahalli +Rāmpur Parhat +Höhr-Grenzhausen +Nagyatád +Khagam +Vəndam +London Colney +Trovagunta +Amityville +Elhovo +Vaprio d’Adda +Bougou +Kujri +Ānjukulippatti +Aleksandrovsk-Sakhalinskiy +Garlasco +Wagner +Dhāntola +Arenápolis +Hickam Housing +Lorraine +Douar Messassa +Mūḩ Ḩasan +Sonsoro +Luckau +San Sebastián de la Gomera +Bisignano +Ngaparou +Tambura +Bulisa +Jingjiazhuang +Bāgor +Nathāna +Rānigaon +Mangrāwān +Louisville +Iowa Colony +Dagbé +Großhansdorf +South Abington +Fochville +Gaada +Muddāda +Ilāmi +Devmaudaldal +Oakwood +Əliabad +Pedro Luro +Saldus +Sokotindji +Tixter +Bāghduma +Kanchanadit +Emsworth +Kaithwār +Valtoha +Saltsjöbaden +Madhuban Bediban +Néa Artáki +Karath +Mehdīpur +Åstorp +Ramara +Ivangorod +Maizal +Llantwit Major +Nārāyanraopet +Carácuaro +Hesarghatta +Malaudh +Berlin +Rudnya +Katigang +Nadimpālem +Luçon +Deh-e Shū +Ekchāri +Virālimalai +Nova Veneza +Tamentit +Tepperumālnallūr +Durgi +Saūmalköl +Pryor Creek +Corella +Cherniakhiv +The Village +Periya Pattanam +Columbia City +Zhangping +Ilsenburg +Quincy-sous-Sénart +Gūdalūr +Bimun +Molagavalli +Santa María Jacatepec +Highfields +Senmanat +Léguevin +Fairfield +Foix +Musāpur +Malaimāchchampatti +Isrāna +Ban Krot +Sundarsi +Notre-Dame-des-Prairies +Puszczykowo +Wertingen +Bewdley +Anan’evo +Helena-West Helena +Berkine +Molbergen +Cervelló +Elūrpatti +Asahni +Vallet +Kemberg +Rafelbuñol +Marne +Alāwalpur +Salem +Woodfield +Casca +Töging am Inn +Cherasco +Leeds and the Thousand Islands +Dényékoro +Nehoiu +Uppūr +Koufália +Halen +Quixabeira +Inole +Bridge City +Brockton +Costeşti +Syurte +Nyzhnohirskyi +Cambira +Saint-Barthélemy-d’Anjou +Saint-Amand-Montrond +Gangāpur +Sultan-Yangiyurt +Legnaro +Runkel +Hohenmölsen +Frouzins +Tabernes Blanques +Mareno di Piave +El Amim +Burela de Cabo +Ban Sathan +Cervera +Gold +Clarksville +Kranídi +Kerap +Shahrak-e Ja‘farīyeh +Dolianova +Winchester +Laurentian Valley +Nittenau +Idumbāvanam +Bijeraghogarh +Nārsingi +Shira +Güney +Khāwad +Erikolam +Kadanādu +Ćićevac +Negrine +South Normanton +Killamarsh +Tissaf +Kommūru +Gonghaur +Novi Banovci +Dachengzicun +Wau +Aksay +Jisrayn +Deruta +Tavarede +Raitar +Monnickendam +Jantho +Eunice +Rorschach +Tarmount +Dhānga +Kankanālapalle +Adolfo Gonzáles Chaves +Cosne sur Loire +Bezliudivka +Pipra +Alma +Assi-Ben Okba +Anaconda +Tissint +Ban Bang Phlap +Villacañas +Dānesfahān +Borovskoy +Banikane +San Juanito de Escobedo +Villa Cañás +Wiener Neudorf +Chewara +Elne +Yutsa +Olivares +Harlākhi +Rāsol +Ghosrāwān +Saidoke +Huinca Renancó +Braslaw +Medleri +Madeira +Ban San Pong +Abra Pampa +Segorbe +Lerici +Dubrovytsya +Mohelnice +Khānpur Khairanti +Bairiyā +Hertzogville +Santa Monica +Odobeşti +Åhus +Soubakaniédougou +Sabiñánigo +Elūrupādu +Lugoff +Carneirinho +Teisendorf +Brockworth +Aurahi +Dibrāghani +Vadakēthara +Ghanīpur Bejha +Fishersville +Sidi El Hattab +Basni +Mono +Ipiranga do Piauí +Tocina +Būdalūr +Anjēhalli +Naurhiya +Andergrove +Embrach +Radstock +Sādiqpur Maraul +Bendarhalli +Simarbani +Sivamalai +Glenshaw +Estrêla d’Oeste +Carqueiranne +Rochelle +San Francisco Libre +An Châu +Jawāsa +Bobil +Sarpamāri +Nirna +Barga +Coral Hills +Bystrzyca Kłodzka +Iāwar +Kharī +Cavriglia +Aschheim +Arenys de Munt +Halachó +Ngọc Sơn +Sa Pa +Muhammadganj +Dharir +Ostróda +Dunblane +Kallayi +Mohanpur +Gümüşova +Benbutucun +Kurort Steinbach-Hallenberg +Orivesi +San Giovanni in Marignano +Anísio de Abreu +Kovilpatti +Siano +Bellinzago Novarese +Chahār Borj-e Qadīm +Elmas +Aniva +Flossmoor +San Juan Bautista +Hani i Elezit +Voitsberg +Danau Kändimarg +Ramdeora +Mechanicsburg +Cusseta +Altdorf +Ponneri +Mutis +Thames Ditton +Hayle +Newmarket +Rāmkali +Recco +Woodway +Dushanovë +Pettāmpālaiyam +Krasnoslobodsk +Devarāpalle +Uppalaguptam +Piombino Dese +Kapelle-op-den-Bos +Makamba +Condé-sur-l’Escaut +Pokotylivka +Negrete +Singhāna +Taisar +Hazrat Shiura +Denham Springs +Manchester +Hsenwi +Novopskov +Resana +Magnago +Cetraro +Sint Willebrord +Capriolo +Gammasa +Tazarka +Saint-Philbert-de-Grand-Lieu +Köse +Närpes +Jamunāmukh +Vempatti +Ja‘farīyeh +Oakville +Mhâjâr +Magny-les-Hameaux +Ukwā +Gaurdah +Cutro +Verkhneyarkeyevo +Horsell +Florence +Middlesborough +Ashland +Kungsängen +Kadimetla +Grigoriopol +Nepi +Curepto +Saint-Jean-le-Blanc +Raunds +Marvast +São Jorge d’Oeste +Nové Město nad Metují +Salzhemmendorf +Jhabrera +Sarakkayhalli +Cermenate +Kharagbani +Kākarāti +Acton +Dymka +Umbrete +San Giorgio di Piano +Rangāpuram +Jarville-la-Malgrange +Tirubhuvane +Helena Valley Southeast +Topoloveni +Le Passage +Broadwater +Pūmalakkundu +Bakhariā +Douar Oulad Bouziane +Pustomyty +Ichenhausen +Ukhāi Purbāri Patti +Maḑāyā +Sierning +Audenge +Zacualpan de Amilpas +Kargat +Pau Brasil +Mēga +Barjhar +Hamīra +Ecatzingo +Zorneding +Rehau +Werneuchen +Hathaura +Chhapera +Tivat +Locust Grove +Frankfort Square +Langnau +Petersberg +Tandarāmpattu +Baisuhalli +Barahpur +Lānghnaj +Shahr-e Majlesī +Eemnes +Punta Indio +Eisenberg +Tiruvāduturai +Muppālla +Burlington +Hastings +Opalenica +Sebastião Laranjeiras +Obernkirchen +Tadangam +Diamondhead +Maraial +Miajadas +Hattula +Molsheim +Mīrjāveh +Ubbergen +Wallerfangen +Tassera +Wilnecote +Southside +Ntchisi +Schlangen +Bāgchīni +Mahālgaon +Canal Winchester +Sutton +Bāyaram +Kirangūr +Awans +Lutterworth +Chinnāmpālaiyam +Ruffano +Warren +Adelsdorf +Aidlingen +Gorē +Ameskroud +Orlu +Savenay +Queensferry +Sherborne +Asudapuram +Igaratá +Aucamville +Chilmil +Kolkwitz +Siteía +Chiman +Oraviţa +Modra +Palhano +Schaafheim +Valréas +Qutubpur +East Stroudsburg +Suan +Barpathār +Kalaīkunda +Laanoussar +Harqalah +San Agustín de las Juntas +Santa Comba +Néa Moudaniá +Lādhuka +Yezhi +Nogent-le-Rotrou +Chandwārā +Bhogāpuram +Verkhneuralsk +Sankt Valentin +Pirpirituba +Wehrheim +Lançon-Provence +Doctor Arroyo +Parol +Barrington +Minano +Meadowbrook +Rum +Tonneins +Nattakkādaiyūr +Kātūria +Takkali +South Amboy +Torrinha +Água Branca +Mallappādi +Santa Teresa di Riva +Crestline +Charter Oak +Blachownia +Ban Khi Lek +Macomer +Rodeo +Zwenkau +St. Anthony +Havre +Tres Ríos +Ouistreham +Kesli +Manteswar +Nūlivedu +Sainte-Julienne +Heiligenhafen +Sheffield +Chinnakkavundanūr +Jāffar Khānpet +Boddikūrapādu +Dowlatābād +Harpur +Tarawān +Pelāgor +Nort-sur-Erdre +Moore +Studénka +Samdrup Jongkhar +Bruchhausen-Vilsen +Irthlingborough +Sujāpur +Vanzago +Gatteo +Concepcion +Chupinguaia +L’Isle-Jourdain +Sosale +Kaleybar +Zafferana Etnea +Fort Stewart +Lubuagan +Pesca +Hagondange +Borna +Hasanpura +Nāgāyalanka +Kopong +Lac des Oiseaux +Unāo +Telkathu +Kalvārpatti +Oued Essalem +Maihma Sarja +Jahāngīrpur Sālkhani +Bull Mountain +Moisei +Peixe +Baisa +Angola +Santa María de Cayón +Laghzawna +Adaklı +Ehningen +Timmāpuram +Boali +Shanhūr +San Pablo Huixtepec +Imielin +Rudraprayāg +Jānpur +Moḩammad Yār +Riverdale +Monserrat +Redon +Chiankī +Bang Khla +Rosario +Ouled Rached +Ralla +Pangunattam +Altınyayla +Dornstadt +Quakertown +East Franklin +Nor Hachn +Torgelow +Rupahi +Choix +Quirino +Sədərək +Pfedelbach +Shimizu +Saint-Jean-d’Illac +Hualañe +Patchūr +Mariyādau +Khajuri +Satravāda +Qualicum Beach +Nieuwleusen +Chechen-Aul +San José Guayabal +Frodsham +Polegate +Pasrāha +Plymouth +Ilvesheim +Drākshārāma +Livron-sur-Drôme +Fársala +Urangānpatti +Gadzhiyevo +Mumaradikop +Barros Cassal +Honganur +Porto de Pedras +Rosário do Catete +Le Crès +Isua +Bo‘z +Magdalena +Shendē +Kanajanahalli +Khandāich +Ikeda +Ecorse +Skidaway Island +Newfane +Malahide +Titu +Poienile de sub Munte +Perwez +Modavāndisatyamangalam +Kharahara +Gassino Torinese +Kißlegg +Ledbury +Síndos +Kruszwica +Juprelle +Tecklenburg +La Jigua +Dahi +Mīlājerd +St. Francis +Tysmenytsia +Betania +Asārhi +Bernalillo +Jandaíra +Marano Vicentino +Ventnor City +Bad Liebenwerda +Lagunia Raghukanth +Kollankulam +Mabeskraal +Analaroa +Kunnattūr +Uppalapādu +Ban Bo Phlap +San Pancrazio Salentino +Jackson +Bethalto +Frickenhausen +Bagnara Calabra +Moldova Nouă +Nakhon Thai +Herculândia +Chicholi +Punta del Este +Pozo Almonte +Laurens +Elsenfeld +Catral +Majhariyā +Bhachhi +Āltūn Kawbrī +Nakoushi +Thakurainia +Almagro +Coleford +Charlotte +Leonia +Mascota +Skwierzyna +Abūz̄ar-e Ghaffārī +Gaggiano +Nakao +Mound +Crigglestone +Teplohirsk +Ānandpur +Gainrha +Goldach +Clermont-l’Hérault +Deokali +Joghtāy +Washington +Rājapūdi +Punnaikkāyal +Wasilla +Güneysınır +Oppicherla +Merrill +Freystadt +Pāppampatti +Tlahuiltepa +Niederhasli +Macedon +Padinska Skela +Didymóteicho +Alukkuli +Chilakhāna +Khotyn +Walworth +Ochsenhausen +Piru +Rāmpur +Douar Oulad Sidi Moussa +Aïn el Hadjar +Launaguet +Sidi Lahsene +Rîşcani +Barei +Rinópolis +Piamonte +Weißenthurm +Edelény +Itiki +Barnāon +Picture Rocks +Aljustrel +Fairfield Glade +Pokrovsk +Sucre +Bojacá +Pedda Tumbalam +Las Tablas +Velykyi Bychkiv +Bridgeport +Risaralda +Bures-sur-Yvette +Lansdowne +Granada +Borgholzhausen +Muro del Alcoy +Escaudain +Augusta +San Pedro Nonualco +Gomaringen +Towcester +Pasian di Prato +Rensselaer +Sugar Grove +Kudelstaart +Keuruu +Ganāram +Algūn +Samalpur +Douar Ezzerarda +Schübelbach +Niesky +San José La Arada +Gandikunta +Vittuone +Manzanares el Real +Bendapūdi +Katteragandla +Sītānagaram +Ban Son Loi +Ratangarh +Mātar +Magalia +Haiku-Pauwela +Diouna +Ħamrun +Capilla del Señor +Benahavís +Southwick +Zavyalovo +Nizhniye Sergi +Smoline +Wunsiedel +Fallon +Otumba +Mels +Ahmadpur +Harbatpur +Loria +Lesquin +Chunakhali +Montescaglioso +Talata-Angavo +Chala +Reshetylivka +Mādhopur +Fatao +Paray-le-Monial +Belley +Barleben +Rain +Iarpur +Raipur Buzurg +Kawai +Hailey +Oulad Cherif +Bandio +Romang +Tanakoub +Santa Isabel Cholula +Gooik +Zoubiria +Māmidipalli +Aranya Kalān +Nūtakki +Tanamarina-Sakay +Momchilgrad +Mirante da Serra +Tasso +Jūraqān +Kahla +Nossa Senhora Aparecida +Dielheim +Sandalpur +Bahābād +Rethen +Giardini +Neuenkirchen +Stevenston +Karsaut +Vallapuram +Ryki +Brugnera +Philippeville +Carrillos +La Victoria de Acentejo +Bowdon +Kondrukota +Vaikuntam +Castelleone +Keşap +San Lorenzo de Descardazar +Kaujalgi +Lesnoy Gorodok +Hlyboka +La Bruyère +São Domingos +Fazendinha +I-n-Amenas +Riverside +Aslanapa +Kalanchak +Baía da Traição +Kondayampālaiyam +Tišnov +Jājireddigūdem +White Meadow Lake +Oromocto +Borja +Clay Cross +Bude +Shannon +Gudimūlakhandrika +Bad Laer +Baluntaicun +Gandhwāni +Vallahbhāpuram +Badagabettu +Gangaura Behra +Washington Terrace +Japaratinga +Antônio Dias +Mejillones +Joigny +Inverigo +Mikhaylovskoye +Badnor +Nonea +Saraunja +Kandiyankovil +Ottappārai +Swāmimalai +Évian-les-Bains +Totnes +Mount Pleasant +Melgaço +Babadag +Nong Kung Si +Ghattupal +Sholaqqorghan +Altenbeken +Kandel +Kharika +Kodakkal +Castrolibero +Qarqaraly +Shawano +Cape St. Claire +Kannavam +Pong Nam Ron +Pudukkottai +Ban Kham Pom +Pohrebyshche +Jambukuttaippatti +Chokkalingapuram +Bādanahatti +Covasna +Aniskino +Llagostera +Sūrak +Waseca +Dhāmnod +O'Hara +Gnarrenburg +Golbāf +Palm Beach +Falan +Elsfleth +Arlesheim +Mont-Saint-Martin +Cullercoats +Vadavālam +Irungalūr +Xalqobod +Qorovulbozor +Mudhol +Gothurutha +Rāmāyipatti +Araújos +Saßnitz +Laheji +Freetown +Paredes de Coura +Tiana +Spáta +Aliquippa +San Fausto de Campcentellas +Bīkē +Dāmargidda +Panthersville +Une +Jamhra +Susāri +Valkurti +La Cruz +Kodigenahalli +Blackstone +Whitecourt +San Lorenzo +Nowra +Tenedla +Leichi +Landivisiau +Ban Chang Phuak +Muli +Vignate +Çobanlar +Støvring +Shirguppi +Chalkāri +Jogaili +Māmidipalli +As Sidrah +Podstepki +Tibaná +Tepe-Korgon +Richmond Heights +Santa María Ajoloapan +Capbreton +Tilehurst +Chikkāla +Sewa +Aberbargoed +Olds +Suwannaphum +Jagannādapuram +Himmatpura +Rājepur +Sukkāmpatti +Roux +Canela Baja +Cistérniga +Tiptree +Conshohocken +Salisbury +Sidi Bou Othmane +Hirehalli +Maidencreek +Guntramsdorf +Harpur Bhindi +Vega Alta +Cêrro Grande +Alsbach-Hähnlein +Douar El Mellaliyine +Arceburgo +Galten +Mādepalli +Nelali +Ḩorr-e Rīāḩī +Grants +Ahuimanu +Willowbrook +Elze +Hacine +Yatton +Budhma +Nashtīfān +Obukhivka +Itapebi +Yaxley +Ivins +Rockingham +Maków Mazowiecki +Murfatlar +Tuskegee +Binisalem +Onchan +Gūdalūr +Waiuku +Ban Wat Chan +Jerez de los Caballeros +Solsona +Sarkad +Ban Yaeng +Trittau +Biblis +Dörverden +Marale +Bangaon +Matelica +Verdejante +Xiangping +Ville-la-Grand +Woodbury +Littleport +Monte Rico +Boekenhouthoek +Foammulah +Pencoed +West Manheim +Wa +Dommasandra +Waterford +Corumbaíba +Waldfeucht +Micco +Flanders +Irshava +Yvoir +Abalessa +Incline Village +Springs +Žabalj +Gədəbəy +Palomares del Río +Reggiolo +Beauraing +Ifigha +Op +Ussel +Banbhāg +Bariariya Tola Rājpur +Entre Ijuís +Beauchamp +Halgeri +Santo Domingo Petapa +Gualaquiza +San Fructuoso de Bagés +Sungal +Pilikōdu +Wysokie Mazowieckie +Yeşilyurt +Fateh Nangal +Mikhaylovka +Zörbig +Županja +Imotski +Piripá +Grünheide +Kyritz +Morubāgalu +Anjūr +Nārāyanpur +Cachoeira dos Índios +Vallendar +Belakvādi +Ražanj +Buenópolis +Chinnāyagūdem +Felino +Lavis +Al Bardīyah +Sângeorgiu de Mureş +Sabie +Gondizalves +Ust’-Nera +Kondūru +Sansa +Veinticinco de Diciembre +Mangasamudram +Bichura +Sauzal +Brandon +Maserada sul Piave +Japurá +Chesterfield +Santa Lucia di Piave +Kaithāhi +Bir Tam Tam +Dāmu +Sibkund +Nerinjippettai +Seven Corners +Audubon +Helotes +Des Peres +Cajvana +Chadan +Caluco +Solita +Oakland +Copceac +Lagbé +Pleternica +Dihri +Bhargaon +Forestdale +Huron East +Stryzhavka +Delta +Bilenke +Laufenburg (Baden) +Pepillo Salcedo +Guano +Gavirate +Ipuiúna +Smithville +Tecoh +Ban Wang Krachae +Nagykálló +Tafersit +Ciudad Insurgentes +Cajobi +Mālingaon +North Codorus +West Athens +Dazhuangzi +Haddington +Yenmangandla +Govindapalle +Malalbergo +Perkasie +Mwaline al Oued +Lanta +Bāsudebpur +Mokri +Mendig +Jandola +San Manuel Chaparrón +Maserà di Padova +Elsmere +Denekamp +Montoro +Umburetama +Crawford +Marlboro Village +Amatlán de los Reyes +Hostivice +Shanklin +Middlebury +Chtiba +Peresecina +Olesno +Raipur +Chitrāda +Nandiyālam +Currumbin +Rothrist +Guatapé +Tettu +Temperance +Sunset Hills +Piranguinho +Palocabildo +Groenlo +Dodji-Bata +Bamaiya Harlāl +Killimangalam +Bad Schussenried +Kongnolli +East Grand Forks +Hurzuf +Alawandi +Kaglipur +Koheda +Dospat +San Jorge +San Jorge +Sauce +Phagu +Sihāli Jāgīr +Eshkanān +Miramar Beach +Tiburon +Kudūru +Istrana +Horodenka +Mata Verde +Nechmeya +Dumri +Pattīswaram +Stillwater +Tunari +Zeydābād +Fujisawachō-niinuma +Nizhniy Odes +Montes Altos +Beuvry +Hawthorn Woods +Zarbdor Shaharchasi +André Fernandes +Reinfeld +Miryal +Odayārpatti +Herenthout +Carmen de Carupa +Chotěboř +Prabhāt Pattan +Fairless Hills +Flemington +Brejolândia +Loeches +Uchti +Satghara +Dallas +Weare +Embalse +San Zenón +Tekpanja +Steynsrus +Rebordosa +Govindāpuram +Kod +Cresskill +Kottapālem +McFarland +Westampton +Chaukhata +Belek +Datian +Rutland +Oborniki Śląskie +Tarusa +Cameron +Benton Harbor +Iguidiy +Hirske +Katakwi +Nalbach +Usworth +Gok +Orosi +Kharovsk +Ouédémè +Consacá +Rivesaltes +Guryongpo +Rada Tilly +Rincão +Park Ridge +McCordsville +Rondon +Ambara +Springfield +Navoloki +Tineo +Urbach +Coatetelco +Fehrbellin +Pithiviers +Sogām +Zeerust +Kenār +Riverside +Bonnievale +La Matanza de Acentejo +Dāvulūru +Nagaoki +Bemarivo +Logatec +Campamento +Calamar +Suances +Chillicothe +Greytown +Ouarégou +Portlethen +Kiskunlacháza +Koila Belwā +Yeldūrti +Huntertown +Marinka +August +Mendon +Dasaut +Umreth +Hokur Badasgom +Haripura +Sidi Ouassay +Paraparaumu Beach +Lynwood +New Scotland +Kożuchów +Bimāwān +Absecon +Olalapādi +Mauji +Yui +Beecher +Gtarna +Majhaulia +Periyamuttūr +Little Falls +Hussepur +Dāita +Midland +Velampatti +Hooper +Vinsady +Brejão +Bāgeshwar +Andiyappanūr +Commerce +Dinnington +Mirzānagar +Banārūyeh +Kanasānapalle +Gurmia +Carignano +Newark +Cedar Hills +Ban Tha Phra +Mainaschaff +Jāmunia +Karayılan +Mendicino +Jumlā +Großbeeren +Pontardulais +Suganwān +Fiumefreddo di Sicilia +Notodden +Kirchberg +Mosciano Sant’Angelo +Sinaia +Sibilia +Pedda Muppāram +Marāi Kalān +Mbuzini +Sergiyevsk +Luís Gomes +Hagaranahalli +Townsend +Fatehpur Bāla +Valley Cottage +Nykøbing Mors +Viravāda +Gidha +Ghāt Borūl +Ālampur +Vinhais +Carregado +Paulo Lopes +Morada Nova de Minas +Auerbach +Chalástra +Chāndpur +Åmål +Pūttai +Orte +Kawara +Berndorf +Adjido +Íquira +Magny-le-Hongre +Venkatādripālem +Oggiono +Economy +Ewo +Campobello di Licata +Sarauni +Wollert +Saraiya +Uppugunduru +Strehaia +Scionzier +Harrodsburg +Country Club Estates +Roxborough Park +Tāla +Lacchiarella +Toccoa +Northwest Harborcreek +Chaponost +São Francisco +Januário Cicco +Udarband +Ayanikkād +Salto Grande +Bonhill +Lanark +Kegen +Bemiss +Colac +Intich’o +Mohon +Mānoke +Poiana Mare +La Florida +Mamnūr +Borgosatollo +Orange Park +Abū Khashab +Khāsbalanda +Livno +Zwiesel +Baniré Koré +Berching +Bolokhovo +Harahan +Andilana Avaratra +La Ravoire +Janhapāra +Fossombrone +Breckerfeld +Rye +Podenzano +San Sebastián +Sanger +Ban Ngio Ngam +Santa Sylvina +Leven +Warkan +Dadrewa +Old Orchard Beach +Verkhnyaya Tura +Chārakunda +Monteroni d’Arbia +Iijima +Méridiala +Travis Ranch +Bérégadougou +Aadorf +Tājpur +Villa Castelli +Nová Paka +Bockhorn +Breinigsville +Ādamī Tulu +Gohi Bishunpur +Lapeer +Waldheim +Ksar Belezma +North Merritt Island +Çayırlı +Westerland +Doberlug-Kirchhain +Furth im Wald +Highland Park +Bougival +Les Sorinières +Porto Tolle +Dunbar +Nanzhuang +Dharampur +Monticello Conte Otto +Tuscumbia +Velyki Luchky +Nhân Trạch +Kurichedu +Kannāl +Katsuyama +Booneville +Pullach im Isartal +Hemmoor +Pivnichne +Sint-Martens-Lennik +Rāmpur Khajuriyā +Douglas +Ampasimpotsy-Gara +Ceelbuur +Coqueiral +Manatuto +Barwell +Sāhāpur +Westwood +Odžaci +Zalishchyky +Beckwith +Arico el Nuevo +Blairgowrie +Cupar +Nādol +Bala Cynwyd +Muhos +Tomblaine +Kaniwāra +Māli +Foiano della Chiana +Gretna +Moman Barodiya +Hampstead +Neustadt +San Francisco +Ban Wang Pradu +Karlıova +San Ignacio +Mandīshah +Bābai Kalān +Aplao +Estanzuelas +Coronel Du Graty +Lakhna +Dhauni +Madhurāpur +Bāsdeopur +Hatti Mattūr +Nārsingi +Akat Amnuai +Treia +Craig +Kaeng Khro +Mburucuyá +Hārua +Gerstungen +Labrador City +Argudan +Brand-Erbisdorf +Morānha +Blacklick Estates +Derby +Kasane +Caxias +Rio del Mar +Burgos +Tutrakan +Mishrikot +Perondi +Seneca Falls +Rafard +Furtwangen im Schwarzwald +Le Teich +Venturina +Lunner +Tha Luang +Chanco +Chanco +Kambaneri Pudukkudi +Crvenka +Mādhavaram +Lago Vista +Aïn el Mediour +Chiţcani +Maropaika +Morafeno +Soamahamanina +Ambodivoanio +Tsimafana +Sahatsiho-Ambohimanjaka +Ranopiso +Ivandrika +Marotolana +Ambatoria +Lanivo +Sarasambo +Antambohobe +Ambalajia +Nato +Mahamaibe +Mitanty +Salobe +Ambariokorano +Vohilava +Vatananto +Iara +Ampary +Ambalaromba +Ambatoharanana +Befotaka +Soamanonga +Bemaharivo +Anteza +Bekopaka +Antaly +Anjialava +Ankarana-Miraihina +Tsaratanana +Antsaidoha-Bebao +Nosibe +Soanierana +Ambatolahy +Soanenga +Mahabo +Manampaneva +Manja +Ambinanin’ Andravory +Belinta +Marovatolena +Morarano +Antsahavaribe +Antseza +Andribavontsona +Ankiliabo +Antanankambano +Alakamisy-Ambohimahazo +Benato-Toby +Ankirihitra +Antsatramidola +Amboronabo +Manevy +Beparasy +Tandrano +Fierenana +Ambarimaninga +Ambodimahabibo +’s-Gravendeel +Dhībān +Dahbed +Qahramon +Lo Miranda +Bovingdon +Diabugu +Jangalapalli +Urpāar +Lohara +Rohera +Ugamedi +Tikar +Gisborne +Carneiros +Marcação +Puerto Octay +Patūt +Hetanpur +Kalicherla +Rarz +San Pedro Atocpan +Turín +Mbamba Bay +Neuötting +Dholbāja +Novyi Svit +Zschopau +Almusafes +Hirehāluhosahalli +Lake Park +Glens Falls North +Guichen +Rājod +Olney +Pomichna +Panasapādu +Druento +Yelnya +Lamorlaye +Settivāripalle +Peddannavāripalle +Ixtapa Zihuatanejo +Podu Iloaiei +Perl +Veauche +Harleysville +Säffle +Icaraíma +Devanāngurichchi +Gacko +Kāndra +Rice Lake +Had Dra +Apahida +Kozova +Iscuandé +Gudlūru +Chundale +Shelburne +Ardooie +Togou +Dulce Nombre de María +Santana do Manhuaçu +Wenzenbach +Kanchanpalli +East Nottingham +Foum Zguid +Urdinarrain +Bassenge +Denbigh +Hirayama +Kosum Phisai +Arboledas +Telaprolu +Yarmouth +Almargem +Tiou +Seydunganallūr +Chilón +Kirs +Puerto Lleras +Hilzingen +Khiriāwān +Pulaski +Yapraklı +Brooksville +Lopatcong +Stanley +San Francisco Ixhuatan +Acobamba +Kattamūru +Bayabas +Akpassi +Hosūru +Sanganakallu +Pulimākkal +Taber +Huasco +Sarzeau +Pedda Pendyāla +Higuera de Zaragoza +Leisure World +Uspenka +Linares +Ponnāda +Bhirua +Barwān +Grabels +Iseo +Saline +Tepechitlán +Dzhalka +Pena Forte +Yekāmbarakuppam +Fitzgerald +Evergreen +Lerma +Thung Sai +Roztoky +Talevad +Mahamda +Pararia +Tulbagh +Valea lui Mihai +Ianca +Vrhnika +Borgoricco +Kelso +Miradouro +Lolokhur +Perkiomen +Al Yādūdah +Neuried +Fort Irwin +Tzitzio +Saint Sampson +Kharsāwān +Siachoque +Cloverdale +Plainedge +Ortenberg +Cheste +Mudgere +Park Forest Village +Aver-o-Mar +Gonfreville-l’Orcher +Grenade +Tovāla +Fredensborg +Sileby +Segni +Nuquí +Ūnagatla +Sheffield Lake +Sarenja +Gāzulapalle +Oak Grove +Hlobyne +Rio das Flores +Wangdue Phodrang +Villamediana de Iregua +Sānchi +San Francisco Chimalpa +Borskoye +Fuldabrück +Zacháro +Bayyavaram +Kusmaul +Tvrdošín +Velden am Wörthersee +Yaguará +Noventa Vicentina +Bazimini +Donnacona +Sucha Beskidzka +Emirgazi +Mnichovo Hradiště +Karadge +Phulhara +Vanavāsi +Groß Kreutz +Ouled Rahou +Sambhu Chak +Bawāna +Aviano +Villa Aberastain +Angelópolis +Ben Chicao +Cébazat +Bāba Bakāla +Bloomingdale +Natuba +Bhelsi +Jasauli Patti +Dalavāypattanam +West Donegal +Haledon +Attnang-Puchheim +Séné +Rāmnagar +Indian Harbour Beach +Czarna Białostocka +Yasenivskyi +Açucena +Chitvel +Winslow +Nanmucun +Ferryhill +Munagāla +Harwood Heights +Colméia +Tena +Plön +Corgao +Bikkatti +Esanai +Mālior +Kotabommāli +Yellanda +Colorno +Peebles +Broughton Astley +Bogota +Almoloya del Río +Viale +Tshabong +Bad Lauchstädt +Borio +Nawāda +Rāmgarha +Belma +Juncos +Montagnana +Ban Nikhom Phatthana +Caputira +Sampgaon +Kafr Sajnah +Khagaur +Ladue +Beaver Falls +Chinna Kalaiyamputtūr +Bestensee +Glastonbury +Monteforte d’Alpone +Thap Than +Seddouk Oufella +Guttikonda +Qashyr +Bni Sidel +La Junta +West Caln +Hürtgenwald +Nunchía +Purcellville +Edgewater +Zhur +Neuhaus am Rennweg +Shahr-e Pīr +Florø +Monastyryshche +Horokhiv +Monistrol-sur-Loire +Kovvali +Gouvieux +Balvādi +Castelnuovo Berardenga +Vila Nova de Cerveira +Sai Ngam +Cholavaram +Kara-Bak +Thị Trấn Đồng Đăng +Libonik +Morières-lès-Avignon +Edgewater Park +Mikhaylovsk +Rāmapattanam +Hope +Chirongui +Zawyat Sidi al Mekki +Suoyarvi +Terra Nova +Eslohe +Mangalpur Gudaria +Nānguneri +Māldah +Somero +Khorāgāchhi +Upper Leacock +Village St. George +Santa María del Tule +Bayport +Fair Lakes +Filottrano +Kui Buri +Florida +Weilmünster +Barhagarh +Asolo +Penaballi +Kilānkundal +Sępólno Krajeńskie +Serafimovich +Redlands +Dashouping +Eceabat +Santa Isabel do Ivaí +Ingenbohl +Brownfield +Argelia +Dayr as Sanqūrīyah +Hernando +Polička +Dumri +Periyapōdu +Kodikkulam +Santa Cruz Atizapán +Osicala +Corman Park No. 344 +Virgínia +Felling +Great Cornard +Segarai +Litchfield Beach +Clinton +Almagro +Basāha +Channubanda +Plymouth +Topchikha +Helsinge +Ayas +Enkakād +Bhadwār +Nueva Guadalupe +Kulu +Devnya +Belvedere Marittimo +Makhambet +St. Pete Beach +Kratovo +Lakhanāpuram +Rock Falls +Palmeira d’Oeste +Árgos Orestikó +Potangal +Tullukuttināyakkanūr +Florstadt +Le Teil +Caister-on-Sea +Wanaka +Amuria +Yangiobod +Vadasīkarambattu +Karor +Sandpoint +Nangis +Orting +Pudozh +Le Portel +Caldwell +San José de Gracia +Ibirapuã +Punjai Lakkāpuram +Kishtwār +Maddūr +Matias Cardoso +Bösel +Wābāgai +Prienai +Crozet +Tepetitlan +San Marzano di San Giuseppe +Kanhauli +Oststeinbek +Sandiacre +Tummalacheruvu +Douar Ait Taleb +Zunilito +Whippany +Napoleon +Tiachiv +Ấp Tân Ngãi +Shoshong +Allāhpur +Barhauna +Tarcento +Tlumach +Holywell +Melpanaikkādu +Bīdkhūn +Ladan Kara +Chachersk +Francisco Caballero Álvarez +Metsemotlhaba +Partick +Belišće +Tharīke +Bargara +North Londonderry +L’Epiphanie +San Antonio +Ifield +Rewāhi +Mansfield +Rio Claro +Carmen de Apicalá +Sucre +Palangavāngudi +Halikko +Majhariyā Sheikh +Edgemere +Liteni +Oberriet +Kurichchi +Polakala +Pomona +Makaya +Gurmaila +Teruel +Hallstadt +Silleda +Alwa Tirunagari +Kulpsville +Zuchwil +Khem Karan Saray +Bālumāth +Nānan +Bhagatpur +Padakanti +Alvarado +Breuillet +Kharsod B +Mosrāh +Camenca +Hohenbrunn +Khandrauli +Thief River Falls +Imilchil +Bhatauliā +Āgadāllanka +Minerbio +Pearsall +Diamou +Tataltepec de Valdés +Quétigny +Ghorbanki +Unterägeri +La Fare-les-Oliviers +Irigny +Mallāram +Gangapatnam +Dāra +Iskourane +Msemrir +Hunduan +Rabo de Peixe +Papara +Schleiz +Kambūr +Golakpur +Chiranellūr +Cumberland Hill +Greenville +Ait Ikkou +Neya +Maipú +Aināpur +Katahra +Kanakpur +Teolo +Collingdale +West Perth +Ayapango +Yakakent +Yelsk +Ratanpur +Kusterdingen +Mannamangalam +Muriyād +Ponta do Sol +Tasso Fragoso +Kéllé +Sorisole +Al Abraq +Pullalacheruvu +Sirikonda +Jogiāra +Gondauli +Viagrande +Rocas de Santo Domingo +Striano +Yanchep +González +Woodmoor +Collier +Japurá +Le Thor +Kuvshinovo +Achacachi +Ak’ordat +Vannikkonendal +Reni +Pandino +Bellheim +El Valle +Bellerive-sur-Allier +Ilarionove +Amtala +Sulahpet +East York +Poteau +Gilgit +Kilchberg +Mandalavādi +Sakaddi +Australia +Bersenbrück +Keora +Kadiyadda +Khetko +Aït Ouaoumana +Hlevakha +Landquart +Golbey +Shankarpur +Agadir Melloul +Fontaine-lès-Dijon +Malangām +Aradeo +Moka +Ban Pong Tao +Jaimalpura +Karariyā +Rhymney +Tirumalaippatti +Sewāri +Kālkuni +Duvvūru +Nimmekal +Casorate Primo +Zoeterwoude +Şabanözü +Volkach +Bansang +Drazinda +Nellutla +Pallippatti +Coffeyville +Caturama +Madnūr +Andalusia +Chak Habib +Pā’īn Chāf +Highland Park +Naunhof +Dayton +Smižany +Ban Noen Kum Nueng +Ban Muang Kham +Vinjam +Psyzh +Sæby +Finestrat +Timmapuram +Madanāncheri +Machadodorp +El Realejo +Povarovo +Armazém +Ospina +Steinheim am Albuch +Seneca +Potengi +Pedrinhas +Urânia +Sānwas +Moslem Ebn-e ‘Aqīl +Signal Mountain +Campbellton +Al Buwayḑah +Bīrpur +Cortez +San Juan de la Costa +Dobříš +Maysville +Piriápolis +Qızılhacılı +Heek +Cumnock +Ablu +Tāmganj +Maria Enzersdorf +Kressbronn am Bodensee +Sanatoga +Dardilly +Hamsāvaram +Tirano +Kaï +Côn Đảo +Memmelsdorf +Gibsonville +Hucclecote +Montmeló +Mahāgaon +Reyes +Baía Formosa +Neuenhof +Satwār +Rāmgarh +Missaglia +Bouati Mahmoud +Sabáudia +Vert-Saint-Denis +Pleasant Hill +Trujillo +Jonnagiri +Chakla Waini +Kodavatipūdi +Sekimachi +Fort Valley +Amarzgane +Entrerríos +Toppenish +Summit +Baciu +Valasa +Kukraun +Carbondale +Ḩīsh +Bāsmanpur +Vadapalanji +Albinea +Adigoppula +Soresina +Mahela +Takua Pa +São Sebastião do Curral +Upper Makefield +Baramandougou +Yacimiento Río Turbio +Rangvāsa +Ban Si Don Chai +Oak Hills Place +San Martín de Valdeiglesias +Sarlat-la-Canéda +Mānsong +Takouta +Hoyo de Manzanares +Paramé +Bārun +Sorala +Jolfā +Porto Santo Stefano +Mokhotlong +Kampong Tunah Jambu +Boucau +Bolszewo +Mühlhausen +Chécy +Bainbridge +Ineu +Hünenberg +Mālipākar +Mousoulou +San Miguel +Lusca +Ala +Tagapul-an +Pitman +Soltsy +Rrëshen +Lovosice +Iklod +Dhobipet +Alvorada +Ranjal +Bernolákovo +Beni Hassane +Angor +Tausa +Obernburg am Main +Tillaivilāgam +Clayton +North Haledon +G‘ozg‘on +Dehri +Taragi +Veľké Kapušany +La Ferté-Bernard +Dunavarsány +Pittsgrove +London Grove +Ulstein +Cristuru Secuiesc +Rouvroy +Penugolanu +Vanduvāncheri +San Ignacio +Madhuban +Bommayapālaiyam +Uta +Santa María Xadani +Gopālpur +Maisaram +Ballenstedt +Gudibanda +Baikunthapur +Ntossoni +Strijen +Haţeg +Lichtervelde +Entraigues-sur-la-Sorgue +Puraini +Chīchkah +Abcoude +Gudivāda +Rautara +Engerwitzdorf +Lontra +Quéven +Jeannette +Bridgewater +Kukrahill +Falla +Nagykovácsi +Miyār +Hassi Berkane +Kryzhopil +Brimington +Varatanapalli +Gholia Kalān +Porcari +Treze Tílias +El Outaya +Karis +Khānāpur +Glencoe +Belozërsk +Tlagasana +Soeda +Toulou +Sowān +Madhubani +La Loggia +Ban Sai Yoi +Koekelare +Dharāwat +Schuylkill +Cofimvaba +Lienen +Costa Volpino +Sodankylä +Essey-lès-Nancy +Belsh +Chahana +Garsekurti +Bhāsaula Dānāpur +Boonton +Saidia +Cherry Hinton +Annan +Nueva Ocotepeque +Kampel +Waldenbuch +Bachchannapet +Yelandūr +Kūnimedu +Moe +Garching an der Alz +La Mujer +Bundehra +Barth +Monforte del Cid +Cinderford +Greenville +Cowley +Adiyakkamangalam +Komījān +Clanton +Néa Michanióna +Deodora +Bath +Alíartos +Somarasampettai +Kusumbe +Mahopac +Tiqqi +Pereiras +Efringen-Kirchen +Poisy +Gessate +Meadow Lakes +Amberomanga +Brevik +Luzzi +Presque Isle +Paratdiha +Bee Cave +Coaldale +Muturkha +Gorodoviki +Minyar +Cunupia +Vicentinópolis +Kirrāyach +Chartiers +Taşkent +Hirni +Chinna Mupparam +Boukhralfa +Srīrāmapuram +Hardiya +Aragona +Pizzo +Großenlüder +Amtala +Īdupugallu +Monmouth +Spring Valley Lake +Bertrix +Rāmpatti +Vidapanakallu +Āshtīān +Wesley Chapel +Marrupa +Zapatoca +Abirāmam +Ōtaki +Perry Heights +Mascoutah +Borgentreich +Cockermouth +Borgo a Buggiano +Galela +Zūlakallu +Pukkulam +Kibaya +Fully +Pa Mok +Ottobeuren +Saint-Paul-Trois-Châteaux +Rāni Sāwargaon +San Pedro de Coche +Riacho dos Machados +Hombrechtikon +Pacuarito +Vohburg an der Donau +Aulnoye-Aymeries +Crest +Hiramandalam +Windsor +Mariinskiy Posad +Dāmal +Būdamangalam +Stony Brook University +Natividade +Lachen +Angichettippālaiyam +Brighton +Habo +Daping +Banak +San Juan Ixcaquixtla +Lenox +Pasivedalajimma +Sremski Karlovci +Tilvalli +Buenavista +Sant’Angelo in Lizzola +Mozzate +Coccaglio +Shopokov +Bouhlou +Afumaţi +Sānrha +Castagneto Carducci +Civitella in Val di Chiana +Summit View +Marlborough +Rudewa +Sonsbeck +Roccastrada +Wervershoof +Tanudan +Stary Sącz +Lamarão +Jabera +Ranod +Franklin +Leopoldo de Bulhões +Kanavāypudūr +Rahta +Pararia +Höshööt +Koror +Uzundere +Biederitz +Notre-Dame-de-Gravenchon +Murillo +Porkhov +Teranikallu +Dhanauli +Nam Som +Río Jiménez +Wingles +Pullānvidudi +Vadakādu +Hoquiam +Corona de Tucson +Comendador Levy Gasparian +Périgny +Saint-Jean-de-Monts +Hakka +Pedda Nindrakolanu +Parabita +Doornkop +Lichana +Rudravaram +Hollymead +Krompachy +Sanjāt +Jackson +Queimada Nova +Hârşova +Meßkirch +Mālaimārpuram +Mirchpur +Aktepe +Wattwil +Bussy +Rauenberg +Moranbah +Igana +Bhui +Ekangar Sarai +Velakalnattam +Bharno +Ellicott +Gundumāl +Folignano +Karuveppampatti +Sātgāchia +Hostotipaquillo +Gremyachinsk +Bad Bergzabern +Löwenberg +Jalālkhera +Maiquinique +Herzberg +Majra +Obertraubling +Lillebonne +Nāgaiyampatti +Faro +San Juan de Arama +Càbras +Carlosama +Roetgen +Saint-Claude +Tsrār Sharīf +Pendekallu +Graham +Galmaarden +Nārona +Montalto di Castro +Port Jervis +Gendou +Stará Turá +Buved +Rāgampet +Pont-Rouge +Volchansk +Güneysu +Pillutla +Siġġiewi +Cutrofiano +Hualaihué +Junín +Vīrapalle +Wyoming +Aransas Pass +Onnaing +La Chapelle d’Armentières +Navappatti +South Londonderry +Ustrzyki Dolne +Punnappatti +Ponte Buggianese +Santanópolis +Margny-lès-Compiègne +Iaboutene +Akālgarh +Angallu +Barbana +Bommagondanahalli +Sarbīsheh +Inékar +Azīzpur Chānde +Tomeşti +Velaux +Uchen +Chanute +Talsi +Gigmoto +Al Quway‘īyah +Ouando +Bishunpur +Besozzo +Hamlin +Kolbuszowa +Perevoz +Califórnia +Sohta +Doiwāla +São José do Cerrito +Choceň +Gangājalghāti +Blanchard +Anthony +Summit Park +Nanzhangcheng +Dunn Loring +Tleta Taghramt +Paola +Gyümai +Bhānuvalli +Roccapiemonte +Champlain +Sidi el Mokhfi +Lázaro Cárdenas +San Sebastian +Janglot +Sidi Dahbi +Havsa +Aşağı Quşçu +Iles +Richterich +Lake Villa +Bududa +Jondor Shaharchasi +Tashir +Siklós +Sini +Freeport +Volodymyrets +Pardanjān +Ogden +Innsbrook +Zawiat Moulay Brahim +Koppunur +Vadacheri +Raia +Pulivalam +Flero +Caprino Veronese +Coaticook +Marcy +Phimai +Ortaköy +Simón Bolívar +Tonk Khurd +Gonzaga +Karadipāra +Roverbella +Audubon +Stăuceni +Pokhraira +Pāppākudi +Torre Boldone +Cullinan +Måløv +Nāttarasankottai +Indūrti +Solebury +Mirdoddi +Conthey +Wietze +Mrakovo +São Pedro do Ivaí +Ergoldsbach +Goworowo +Binfield +Dharhwa +Heusden +Heikendorf +Le Petit-Couronne +Gadaul +Katrīdih +Painkulam +Koshanam +Lequile +Uglegorsk +Limeira d’Oeste +Newstead +Stainz +Titz +Zanica +Aigues-Mortes +Azandarīān +Oxford +Duchcov +Monte Escobedo +Murājpur +Bāgalvād +Gobindpur +Savoy +Cheviot +Kaldsletta +Katueté +Tordesillas +Kambhampādu +Hillview +Pyālakurti +Kursaha +Brandizzo +Sangaree +Embrun +Çamlıyayla +Suttamalli +Rompicherla +Kottūr +Kalinagar +Pibrac +Tecumseh +Aratuípe +Marilândia do Sul +Tilarán +Chetma +Chākand +Guntapalli +Castellabate +Glodeni +Longvic +Teotitlán +River Road +Catuípe +Kilcock +Aberdeen +Briceño +Flieden +Roquevaire +Herseh Chhīna +Bhatkhori +Parasbani +Triuggio +Ter Apel +Al Qardāḩah +Marāveh Tappeh +Minto +Malhada de Pedras +Montlhéry +Atherstone +Olmsted Falls +Bryan +Chełmek +Nörten-Hardenberg +Coutras +Ōwani +Wharton +Heartland +Avabodji +Passo do Sertão +Altavilla Milicia +North Cornwall +Baxter +Meine +Domérat +Amiāwār +Fort Mitchell +Yang Talat +Jujhārpur +Mustafābād +Rodeiro +L’Île-Saint-Denis +Pathrāha +San Sebastiano al Vesuvio +Perryton +Lempdes +Ōhata +Hirson +Harīke +Benner +Gillitts +Kościelisko +Chintakommadinne +Barvynkove +Paula Cândido +Chyhyryn +Nagdah +Clarendon Hills +Abū Ḩardūb +Demirözü +Aars +Tumberi +Dilāwarpur +Karavan +Najrīj +Zabok +Belhatti +Ballina +Tirschenreuth +Fort Knox +Campos Lindos +Eduttavāynattam +Bikrampur Bānde +Park Hills +Ankatafa +Sokyriany +Chak Pahār +Village Green-Green Ridge +Highland Heights +Deutsch-Wagram +Adjarra +Untergruppenbach +Løgten +Onda +Bedum +Farob +Seosaeng +Le Loroux-Bottereau +Isbergues +Vodice +Salar +Lihue +Tudela de Duero +Thāthūpur +Vanukūru +Kottadindulu +Warrenton +Sint-Job-in-’t-Goor +Ḩalāwah +Ōuda-daitō +Gulf Hills +Timmendorfer Strand +La Libertad +Bishamagiri +Pa Sang +Redlynch +Artigues-près-Bordeaux +Dirusumarru +Serramazzoni +Dobele +Nansio +Nandnāwān +Bovalino Marina +Guntersville +Covington +Sollefteå +Saint-Chamas +Chamonix-Mont-Blanc +San Bartolomé Milpas Altas +Lint +Vergiate +Buckie +Sant’Agnello +Budenheim +Sobhāpur +Anykščiai +Rakitovo +Kāoni +Mahadipur +Lourosa +Niederwerrn +Nayāgaon +Bisaul +Panfilovka +La Pêche +’s-Heerenberg +Bouaiche +Chapel en le Frith +Pinjranwān +Digar +Maina +Beibu +Solotvyno +Zuera +Rangwāsa +Manteno +Wiang Haeng +Slateng Dua +Parsād +Maddūr +Tokatippa +Long Hill +Pantelhó +Corella +Ulātu +Gokhulāpur +Gubden +Belo Vale +Monte Alegre do Sul +Kīlakkurichchi +Kattirippulam +Rājhanpur +Cradock +Chop +Salinas +Le Poiré-sur-Vie +Sa‘īdī +East Donegal +Vyetka +Midalam +Khandauli +Shahrak-e Pārs +Smithfield +Sredets +Garešnica +Fort Riley +Ustyuzhna +Beutelsbach +Sande +Basrūr +Mannegudam +Chettiyapatti +Nierstein +Stansted Mountfitchet +Hakubachō +Hamilton +Ouro Verde +Canford Cliffs +Segaon +Martano +Mechanicstown +Ferreira do Zêzere +Tārazu +Panjampatti +Hathiākān +Trissino +Buda-Kashalyova +Rāiparthi +Hire Vadvatti +Soverato Marina +Kanchanpur +Moribila +Tibro +Māgam +Saidābād +Chānp +Petilia Policastro +Templeton +Arealva +Santa Cruz do Monte Castelo +Bremgarten +Mānpur +Pirojgarh +Verkhoturye +Whitwick +Piliscsaba +San Ignacio +Barntrup +Vostochnyy +Corbin +Ammanford +New Ross +Antargangi +Tombos +Aurisina +Kappeln +Kharod +Carneys Point +Oulad Khallouf +Oulad Khallouf +Teghra +Socorro +Laishevo +Achchampeta +Nariño +Karkkila +Khaur +Skowhegan +Amalou +Pasaul +Jainagar +Sīlaiyampatti +Kanavāypatti +Bientina +Mudichchur +Shyamnagar +Zaandijk +Nakaseke +Schwarzenbruck +Nefasīt +Chamba +Velim +Jagannāthpur +Kennedy +Citrus Hills +Ueckermünde +Hāta +Bad Rothenfelde +Mauléon +Seffner +Egg +Egg +La Belleza +Peralta +Bālakrishnanpatti +Serramanna +Aibonito +Sopot +Amingaon +Pariyāri +Kennett +Kozlovka +Koila +Vráble +Miyada +La Huerta +Żuromin +Dobrada +Haibach +Bryans Road +Lancaster +Monona +Quezalguaque +La Cruz +Kanyākulam +Āmudālapalle +Piliv +Rayón +Verkhniye Achaluki +Jalhay +Yellāreddi +Kennedale +Plabennec +Roussillon +Srīrangāpur +Merksplas +Anantasāgaram +San Lorenzo +Swissvale +Eden Isle +Gundūr +Bargaon +Adalar +Rava-Rus’ka +Pallappālaiyam +Mareeba +Viechtach +El Ghomri +Vinica +Ardatov +Vif +Tarxien +Lakhzazra +Gidan Idèr +Heroldsberg +Thorigné-Fouillard +Jagta +Othello +Gering +Kızılcaşar +Dékanmé +Monte Porzio Catone +Gonzales +Tamza +Tougouni +Yasinia +Moss Vale +Vícam Pueblo +Liubymivka +Nuevo Paysandú +Sinimbu +Ouled Abbes +Kalaun +Yalí +Waltham Cross +Trebisacce +Bandamūrlanka +El Espinal +Ahumada +Fällanden +Karpenísi +Kāliganj +Hosuru +Seyyedān +La Grande-Motte +Nunihāt +Hale Dyāmavvanahalli +Vīrapperumānallūr +Torre de Moncorvo +Štětí +Bucksburn +Nettādahalli +Rāni Sāgar +Masandra +Lālam +Lamesa +Reinosa +Janzé +Kirk of Shotts +Charuānwān +Shiddāpūr +Ban Ratchakrut +Babhangaon +Bundāla +Gamail +Lāndupdīh +Locogahoué +Biberist +Castelnuovo di Porto +Coahuitlán +Sèmèrè +Vaucresson +Obersiggenthal +Millis +Anjahamarina +Kamień Pomorski +Na Yung +Youghal +Dharmājigūdem +Castel Gandolfo +Oak Island +Navipet +Vicopisano +Shediac +Loenen +Bushtyno +Hořice +Pöytyä +Midutūru +San Pedro Ixtlahuaca +Verkhniy Mamon +Usiacurí +Sompting +Kings Grant +Reeuwijksebrug +Tanmpègré +Chāpalamadugu +Lambarkiyine +Ayotoxco de Guerrero +Nerubaiske +Pedreguer +Dāla +Raonta +Paloma Creek South +Dougoufé +Brembate +Merrydale +Gretz-Armainvilliers +Buba +Harewa +Lohna +Independence +Closter +Takiéta +Kodaimangalam +Yaragol +Modachchūr +Nerk’in Getashen +Ergué-Gabéric +East Leake +Sakhua +Nesārg +Vairichettipālaiyam +Nūlvi +Arab +Conewago +Ad Darbāsīyah +Staufenberg +Sukand +Troina +Monte San Savino +San Fernando +Dachne +Fürstenfeld +Colindres +Pattanam +Iwaizumi +Dunn +Muzo +Tādināda +Tifra +Pāta Uppāl +West Long Branch +Barton +Ḩadībū +Imsida +Pochinok +Pásztó +Yamkanmardi +Novoselitskoye +Dubovskoye +Wissen +Siegsdorf +Aqsū +Milton +Bad Endorf +Mücheln +Glenwood +Lipki +Privas +Reddiyapatti +Kolumalapalle +Arizona City +Karmaskaly +Taftanāz +Bacobampo +Alfonso Castañeda +Santiponce +Zogno +Bifeng +Ağlasun +Kandern +Tsallagundla +Puran Bigha +Kandanāti +Clusone +Ortuella +Talwandi Chaudhriān +Nāngal Chaudhri +Perryville +Chak Thathi +Tālakulam +Guria +Folsom +Ban Charoen Mueang +Meltham +York +Ban Wat Phrik +Marcolândia +Rottenburg an der Laaber +Karuppūr +Bartica +Dattapāra +Hernani +Sulakyurt +Mangala +Bānki +Poninguinim +Bushkill +Chamusca +Worplesdon +Montecito +Keregodu +Hongtuliang +Khajuri +Montague +Sečovce +Nonnweiler +Penkridge +Severance +Malverne +Ararica +Nossa Senhora dos Remédios +Kasāp +Phopnār Kalān +Southampton +Möckmühl +La Salvetat-Saint-Gilles +Sallisaw +Le Mont-sur-Lausanne +Nijgaon Parānpur +Kosiv +Clayton le Moors +Pokrovka +Lewistown +Lanco +Nawānagar +Ra’s al Ma‘arrah +Qorovul +Woltersdorf +Hundested +Laitila +Cam +Thāndewāla +Khutha Baijnāth +Kandanūr +Medikunda +San Isidro +Pérenchies +Andanappettai +Delavan +Olamzé +Neuenbürg +Kond Rūd +Poniatowa +Reichelsheim +Möser +Hanko +Oakengates +Schnaittach +Perungulam +Dodvad +Silvārpatti +Santa Margherita Ligure +Wieruszów +Hāthāpur +Rāmpatti +Evergreen +Lycksele +Uedem +Thaon-les-Vosges +Sutherlin +Richland Hills +Carbonita +Sautron +Bedwas +Sāgarpur +Darsur +Whitehouse +Hueyotlipan +Takaharu +Kuchai Kot +Boshof +West Auckland +Chermen +El Dovio +Mandalapalle +Pachrukhi +Repala +Tionk Essil +Zhujiagua +Liffré +Pedda Penki +Orşova +Biloziria +Nottampatti +Altoona +Old Forge +Lieşti +Carlton Colville +Dala +Hausjärvi +Kalloní +Campogalliano +Rignano sull’Arno +West Earl +Dobanovci +Staufen im Breisgau +Saint-Vallier +Nālikkalpatti +Chinaur +Munnelli +Nalās +Tarīchar Kalān +Setana +Saruu +Rakai +Biryusinsk +Chapaev +Basarabeasca +Namsos +Joniškis +Orocué +Zərdab +As Sallūm +Gramsh +Būlaevo +Jesenice +Stans +Gadžin Han +Gunnedah +Kaišiadorys +Golubac +Pazin +Sorø +Cowra +Moengo +Olovyannaya +Slovenska Bistrica +Rubirizi +Ayr +Pampa del Infierno +Tweed Heads +Naujoji Akmenė +Shar +Kočevje +Koné +Gleno +Putina +Paide +Aguelhok +Hammerfest +Beočin +Qusmuryn +Osakarovka +Dimitrovgrad +Varėna +Charters Towers +Montpelier +Gżira +Kontcha +Oldeani +Nisporeni +Sokobanja +Ciudad Cortés +Greymouth +Katoomba +Obluchye +Amapá +Sharbaqty +Port Maria +Alebtong +San Julián +Monaghan +Auki +Dilolo +Sembabule +Ch’osan-ŭp +Bentiu +Falmouth +Ertis +Maryborough +Iqaluit +Luba +Kalabo +Young +Grosuplje +Qazaly +Bayghanīn +Lascano +Heyin +Ludza +Yeghegnadzor +Yardımlı +Mtskheta +Guadalupe +Kibale +Jacareacanga +Bairnsdale +San Pablo Villa de Mitla +Castillos +Kemijärvi +Kelmė +Sen Monorom +Gaoual +Zhänibek +Bački Petrovac +Leova +Leeton +Coracora +Kirkwall +Goranboy +Ādaži +Nangan +Aiquile +Ravne na Koroškem +Luân Châu +Atherton +Aračinovo +Briceni +Lerik +Thames +Puerto Baquerizo Moreno +Bossembele +Kičevo +Mongomo +Slovenj Gradec +Tranqueras +Teleneşti +Bestöbe +Obo +Mobaye +Tobyl +Lapovo +Ruyigi +Dowa +Novobërdë +Donduşeni +Debe +Ştefan Vodă +Zambezi +Moree +Skovorodino +Diekirch +In Guezzam +Wick +Thyolo +Rabaul +Ararat +Oğuz +Kapoeta +Krāslava +Kerikeri +Novi Kneževac +Kieta +Aizkraukle +Bongaree +Nicoadala +Librazhd +Santa Venera +Hola +Līvāni +Victoria +Kiruhura +Kiama +Lerwick +Zholymbet +Borgo Maggiore +Obiliq +Brežice +Ajdovščina +Šalčininkai +Forbes +Nata +Khandyga +Gulbene +Charagua +Kishkeneköl +Magugpo Poblacion +Criuleni +Limbaži +Litija +Madona +Trindade +Carnarvon +Awjilah +Seymour +Makarov +Port Augusta +Mazoe +Ros Comáin +Kerema +Northam +Cliza +Mae Hong Son +Roma +Bogatić +Oficina María Elena +Newman +Ingeniero Guillermo N. Juárez +Cooma +Port Saint John’s +Ndendé +Zouar +Deniliquin +Pasvalys +Melut +Comandante Luis Piedra Buena +Siteki +Medveđa +Sal Rei +San Carlos +Dalaba +Yeppoon +Verkhnevilyuysk +Lorengau +Derzhavīnsk +Omaruru +Vanrhynsdorp +Alūksne +Punakha +Ingeniero Jacobacci +Bir Anzarane +Jakar +Phalombe +Queanbeyan +Tumut +Kavadarci +Palikir +Moss +Ub +Kupiškis +Espargos +Gizo +Bella Vista +Veintiocho de Noviembre +Umba +Mengeš +Bač +Junik +Viqueque +Yamba +Kolonia +Alausí +Cəbrayıl +Dinguiraye +San Javier +Sežana +Funafuti +Zagorje +Chepes +Lucea +Maltahöhe +Mitoma +Gyangzê +Schaan +Glarus +Tazovskiy +Radovljica +Veinticinco de Mayo +Preiļi +Luqa +Përmet +Zarasai +Trakai +Şoldăneşti +Echternach +Mundybash +Kaitaia +Rutana +Berovo +Idrija +Širvintos +Ranillug +Lobamba +Aiyomojok +Molėtai +Biloela +Piggs Peak +Appenzell +Stratford +Uncia +Marigot +Tiksi +Xocavənd +Vaduz +Masunga +Cacheu +Balvi +Nieuw Amsterdam +Chonchi +Stawell +Hermanus +Babək +Sisimiut +Muisne +Vossevangen +Okhotsk +Fort-Shevchenko +Mwatate +Põlva +Eenhana +Byron Bay +Mamushë +Kazlų Rūda +Namanga +Narrabri +Črnomelj +General Conesa +Petnjica +San Antonio de los Cobres +Mali +Mali Iđoš +Muramvya +Tura +Šakiai +Goondiwindi +Ouadda +San Quintín +Wiltz +Thaba-Tseka +Richmond +Kratovo +Kovačica +Cospicua +Saint-Pierre +Cobram +San Ramón +Medvode +Witu +San Matías +Rapla +Skuodas +Bajram Curri +Bilibino +Hohenau +Napak +Torghay +Triesen +Albina +Otavi +Tarrafal +Thinadhoo +Jõgeva +Mayumba +Kalangala +Jinzhong +Canillo +Slovenske Konjice +Danilovgrad +Liquiçá +Chernyshevskiy +Karibib +Villa del Rosario +Smiltene +Rogaška Slatina +Roatán +Ķekava +Punta Gorda +McMinns Lagoon +Scone +Palé +Žalec +Puerto Casado +Singleton +Qaşr al Farāfirah +Ignalina +Grevenmacher +Samtse +Igarka +Gevgelija +Wonthaggi +Hrastnik +Sémbé +Lithgow +Valdez +Šentjur +Ust’-Kamchatsk +Bled +Mitzic +Vadsø +Mékambo +Xagħra +Vrapčište +Ordino +Irig +Bolama +Albury +Brownsweg +Turukhansk +Tuzi +Carrick on Shannon +Ponta do Sol +Ilulissat +Bagdarin +Halba +Għaxaq +Fdérik +Čoka +Šilalė +Svolvær +Komatipoort +Radoviš +Klaksvík +Westport +Finnsnes +Balzers +Sangar +Betanzos +Bongandanga +Khatanga +Prevalje +Perito Moreno +Outapi +Valka +Opovo +Otar +Sevnica +Kununurra +Nadur +Camargo +Fuerte Olimpo +Gobernador Gregores +Bueng Kan +Pakruojis +Švenčionys +Bururi +Al Qaşr +Eschen +Victorica +Şuşa +Qıvraq +Susuman +Mauren +Marsa +Karasburg +Samaipata +Magdalena +Saryshaghan +Tepelenë +Ingham +Ilirska Bistrica +Saint George’s +Dehiba +Nwoya +Bekily +Comandante Fontana +Narrogin +Batagay +Black River +Kuala Belait +Victor Harbor +I-n-Amguel +Ruše +La Palma +Omsukchan +Novyy Uoyan +Manjimup +Calheta de São Miguel +Kruševo +Naifaru +Bensonville +Berri +Port Hedland +Las Lajas +Wabag +Pevek +Çorovodë +San Marino +Kalvarija +Pietà +Cerknica +Remich +El Maitén +Avarua +Manica +Aliwal North +Balzan +Qobustan +Robertsport +Karmah an Nuzul +Trebnje +Trzin +Oranjemund +Bethanie +Bîr Mogreïn +Lazdijai +Butalangu +Neiafu +Vitim +P’ungsan +Esperanza +Plandište +Sicasica +Vergara +Miklavž na Dravskem Polju +Deçan +Omuthiya +Piran +Ağdam +Teseney +Ersekë +Ulaan-Uul +Cherskiy +Grand Turk +Padilla +Lavumisa +Šempeter pri Gorici +Massenya +Palana +Tržič +Žiri +Pembroke +Makedonski Brod +Katanning +Imġarr +Zyryanka +Cankuzo +De-Kastri +Tessalit +Ribnica +Dingli +Pukë +São João dos Angolares +Mojkovac +Janjanbureh +Lismore +Villa Ygatimí +Domagnano +Merimbula +Marsaxlokk +Kirkenes +Tolmin +Ceduna +Port Douglas +Mongar +Ligonha +Paita +Kirakira +La Paloma +Srednekolymsk +Wallaroo +Proserpine +Uspallata +Alibunar +Cantemir +Darregueira +Kaberamaido +Zhigansk +Malishevë +Trancas +Bukachacha +Ugol’nyye Kopi +Lukulu +Fish Town +Clare +Turangi +Imqabba +Krasnogorsk +Xewkija +Weipa +Laško +Pofadder +Lenart v Slovenskih Goricah +Smithton +Demir Kapija +The Valley +Mezen +Rietavas +Ljutomer +Juradó +Domžale +Metlika +Rørvik +Ankaran +Brezovica +Għajnsielem +Teeli +Sinnamary +Mežica +Evinayong +Brandfort +Ocniţa +Kudahuvadhoo +Saulkrasti +Iklin +Colonia +Šenčur +Golubovci +Birštonas +Dravograd +Gornja Radgona +Ainaro +Lija +Železniki +Aasiaat +Mopipi +Ust’-Maya +Porto Inglês +Arroyos y Esteros +Qaqortoq +Tearce +Trashigang +Ulbroka +Škofljica +Abaí +Taoudenni +Kärdla +Kalkara +Tifariti +Mahibadhoo +San Lorenzo +Lethem +Gudja +Saranpaul +Al Jaghbūb +Żebbuġ +Lendava +Bogdanci +Rogašovci +Šoštanj +Zreče +Sowa Town +Bopolu +Žitište +Hokitika +Nautla +Tom Price +Radlje ob Dravi +Bordertown +Villalonga +Viligili +Mangbwalu +Groningen +São Domingos +Buala +Entre Ríos +Río Mayo +Cochrane +Senglea +Għargħur +Qrendi +Kerewan +Hlatikulu +Saint-Georges +Longreach +Trashi Yangtse +Barclayville +Kolašin +Vila Velha +Urubamba +Trongsa +Rače +Borovnica +Eydhafushi +Triesenberg +Vittoriosa +Rodeo +Ísafjörður +Donegal +Sauðárkrókur +Tofol +Cestos City +Imtarfa +Mkokotoni +Chumbicha +Mahdia +Kllokot +Rosoman +Charleville +Fiorentino +Provideniya +Baltasar Brum +Cloncurry +Exmouth +Chokurdakh +Nauta +Mariscal José Félix Estigarribia +Capitol Hill +Severo-Kuril’sk +Brokopondo +Vojnik +Aiguá +Tarabuco +Quime +Demir Hisar +Beltinci +Al ‘Alamayn +Höfn +Jaqué +Nida +Merredin +El Dorado +Karungu +Vevčani +Polzela +Bloemhof +Sohano +Zhemgang +Boffa +Egilsstaðir +Coroico +Saskylakh +Krško +Gustavia +Ypejhú +Ruggell +Toltén +Muta +Sveta Ana +Qala +Lehututu +Şahbuz +Štore +Te Anau +Egvekinot +Ig +Marādah +Roura +Onverwacht +Gradsko +Desaguadero +Sorata +Mwenga +El Manteco +Stanley +Kaikoura +Floriana +Ivančna Gorica +José Batlle y Ordóñez +Črna na Koroškem +Puerto Villamil +Kirkop +Laçın +Radenci +Vianden +Totness +Cidade Velha +Acquaviva +Pozo Colorado +Baures +Safi +Apolo +Sannat +Spodnje Hoče +Funadhoo +Vipava +Same +Esperance +Pivka +Omboué +Mozirje +Manadhoo +Evensk +Pukekohe East +Waitakere +Waitangi +Semič +Ambrolauri +Damongo +Konza +Altata +Heydərabad +Djibloho +Sofifi +Afega +Radeče +Valandovo +Lovrenc na Pohorju +Capellen +Tasiilaq +Scottsdale +Ormož +Borgarnes +Katwe +Straža +Kerċem +Žabljak +Abunã +Amudat +Mount Barker +Philipsburg +Maitland +Taedong +Krivogaštani +Mislinja +Beringovskiy +Balakən +Ta’ Xbiex +Novyy Port +Naklo +Queenstown +Bohinjska Bistrica +Nokaneng +Šmarje +Longyearbyen +Nasir +Partesh +Divača +Xgħajra +Cerklje na Gorenjskem +Vodice +Varakļāni +Spodnji Duplek +Pehčevo +Ropaži +Gusinje +Tabor +Gamprin +Pārūn +Puerto Williams +Cuevo +Capitán Pablo Lagerenza +Odranci +Lifford +Prebold +Flying Fish Cove +Zgornja Kungota +Xızı +Bovec +Plasnica +Chiradzulu +Alto Río Senguer +Sierra Colorada +Rogatec +Għarb +Iracoubo +Bourke +Zrnovci +Oktyabr’skiy +Kipili +Ungoofaaru +Vuzenica +Ust’-Kuyga +Eldikan +Tumby Bay +Turnišče +Miren +Chibemba +Alexander Bay +Halls Creek +Tajarhī +Artëmovsk +Nova Crnja +Lokwabe +Clervaux +Dragomer +Munxar +Kranjska Gora +Šentjernej +Peterborough +Cerkno +Oplotnica +Machinga +Port Denison +Tsau +Uummannaq +Xocalı +Šmartno +Penola +Mirna +Kazachye +Nakhodka +Selnica ob Dravi +Kingston South East +Nyimba +Veymandoo +Lubutu +Wagin +Fulacunda +Paamiut +Greytown +Bala Cangamba +Tarutung +Santo António +Novaci +Bosilovo +Safotu +Kalbarri +Villa Rumipal +Dornava +Mogila +Kidričevo +Katherine +Mabaruma +Tulagi +Novo Selo +Barcaldine +Villa Martín Colchak +Ubombo +Regedor Quissico +Çeleken +Isangel +Buluko +Leulumoega +Faetano +Horjul +Ağdam +Črenšovci +Daga +Los Blancos +Kanal +Asau +Gorenja Vas +Lavrentiya +Puerto Acosta +Verkhoyansk +Mirbāţ +Ñacunday +Poljčane +Dikson +Uad Damran +Plužine +Ljubno +Susques +Upernavik +Schellenberg +Chumikan +Innisfail +Klyuchi +Kobarid +Qasigiannguit +Benedikt +Mazatán +Hagåtña +Fulin +Andrijevica +Oranjestad +Mata-Utu +Ouyen +Gornji Grad +Mirna Peč +Hughenden +Haya +Cowell +Yélimané +Središče ob Dravi +General Eugenio A. Garay +Montegiardino +Streaky Bay +Moravče +Príncipe da Beira +Lufilufi +Dobrovnik +Daraj +Fontana +Dobrova +Ayan +Konče +Shamva +Laverton +Veržej +Komenda +Dolenjske Toplice +Nazarje +Rostuša +Hoskins +Velika Polana +Luanza +Meningie +Vitanje +Gorišnica +Winton +Hamilton +Yulara +Gingin +Jegunovce +Ozurgeti +Pesnica +Sodražica +Godhavn +Stari Trg +Preddvor +Vatican City +Charaña +Onslow +Zgornja Hajdina +Bicheno +Omolon +Vailoa +Starše +Muhembo +Sveta Trojica v Slovenskih Goricah +Moravske-Toplice +San Lawrenz +Yaren +Yerëma +Wyndham +Rankovce +Comallo +Velike Lašče +Jamestown +Hvalba +Zhilinda +Satadougou +Lakatoro +Mokronog +Roebourne +Manily +Zhaltyr +Sopište +Kostanjevica na Krki +Pannawonica +Linxi +Ituni +Meekatharra +Obleševo +Qubadlı +Leonora +Massangena +Gawler +Qaanaaq +Komen +Šmartno +Kozje +Puconci +Vasilevo +Calatrava +Bangar +Grad +Uelen +Nkurenkuru +Tigoa +Villa O’Higgins +Kimba +Majšperk +Dibaya +Panda +Gastre +Saleaula +Kəlbəcər +Alofi +Quilpie +Videm +Podčetrtek +Karbinci +Sabaya +Mikhalkino +Oatlands +Zgornje Jezersko +Chiramba +Norseman +Lata +Llica +Mereeg +Telsen +Apače +Kobilje +Wilcannia +Dobrna +Zgornje Gorje +Calenga +Southern Cross +Lozovo +Rečica +Caluula +Tournavista +Felidhoo +Tmassah +Puerto Pinasco +Oymyakon +Tchitado +Yakossi +Križevci +Markovci +Staro Nagoričane +Šmarješke Toplice +Karumba +Planken +Kempsey +Mount Magnet +Vreed-en-Hoop +The Bottom +Richmond +Kullorsuaq +Cirkulane +Videm pri Ptuju +Woomera +Brvenica +Dhuusamarreeb +Skopun +Morawa +Lukovica +Theodore +Crna Trava +Kuzma +Eidsvold +Għasri +Buabidi +Montes Claros +Cankova +Hvannasund +Tsavo +Sherlovaya Gora +Gornji Petrovci +Tišina +Ribnica +Luče +Nizhneyansk +Tandil +Espungabera +Šalovci +Brades +Juršinci +Podlehnik +Braslovče +Toconao +Trnovska Vas +Šavnik +Rinconada +Jurovski Dol +Three Springs +Centar Župa +Hrib-Loški Potok +Ravensthorpe +Scoresbysund +Kingston +Vitomarci +Burubaytal +Dobrovo +Leava +Pine Creek +Keflavík +Šentrupert +Basse-Terre +Umm al ‘Abīd +Dolneni +Araouane +Halfmoon Bay +Bugrino +Shoyna +Buur Gaabo +Podvelka +Put’ Lenina +Cazombo +Belčišta +Enurmino +Porkeri +Nova Vas +Yaupi +Imdina +Ikela +Amderma +Hodoš +Dol +Čučer-Sandevo +Zelenikovo +Melekeok +Ngerulmud +Andamooka +Tomaž pri Ormožu +Tasiusaq +Adelaide River +Kulusuk +Burketown +Bistrica ob Sotli +Škocjan +Kanyato +Amau +Kairaki +Georgetown +Makole +Boulia +Sveti Jurij +Solčava +Carnarvon +Thargomindah +Destrnik +Kraulshavn +Tamworth +Lusanga +Hurdiyo +Port Pirie +Korf +Androka +Cerkvenjak +Ivanhoe +Al Qunayţirah +Camooweal +Bafwasende +Progress +Razkrižje +Buton +Bifoun +Kangersuatsiaq +Narsarsuaq +Bedourie +Petrovec +Mount Isa +Fort Wellington +Dobje +Punta Prieta +Il’pyrskiy +Birdsville +Star Dojran +Želino +Windorah +Al ‘Uqaylah +Lemsid +Mukhomornoye +Vorontsovo +Grytviken +Füzuli +Venado Tuerto +Fámjin +Osilnica +Piso Firme +Studeničani +Pagėgiai +Savissivik +Adamstown +Samamea +Cauquenes +Rocafuerte +Bogovinje +Kovda +Cuya +Vransko +Zillah +Chegga +Djado +Gaigirgordub +Andoas +Puca Urco +Soldado Bartra +Güeppí +Matochkin Shar +Siglan +Omchak +Shalaurova +Khorgo +Tiyerbes +Peregrebnoye +Komsa +Gyda +Khakhar +Menkerya +Ust’-Nyukzha +Zvëzdnyy +Pakhachi +Indiga +Starorybnoye +Laryak +Ulkan +Strelka +Chagda +Sagastyr +Zemlya Bunge +Trofimovsk +Tunguskhaya +Agapa +Podkamennaya Tunguska +Tukchi +Varnek +Numto +Ust’-Olenëk +Bol’sheretsk +Olenëk +Utkholok +Yessey +Karamken +Kostel +Puerto Heath +Lagunas +Barnīs +Gamba +Nord +Timmiarmiut +Ambarchik +Kingoonya +Sabhā +‘Amrān +Al Jabīn +Nelspruit +Lupane +Anouvông +Xékong +Phôn-Hông +Qacha’s Nek +Mersch +Redange-sur-Attert +Idrī +Cocieri +Lipkovo +Ilinden +Resen +Makedonska Kamenica +Dalandzadgad +Tevragh Zeina +Plymouth +Santa Luċija +Rasdhoo +Muli +Dhihdhoo +Fonadhoo +Nilandhoo +Thulusdhoo +Balaka +Neno +Chikwawa +Usakos +Wé +Abakaliki +Yenagoa +Isemi-Ile +Şūr +Haymā’ +Şuḩār +Unión Chocó +Sieyik +Kurumul +Buka +Pili +Tabuk +San Jose +Santa Cruz +Koronadal +Az̧ Z̧a‘āyin +Umm Şalāl ‘Alī +Madīnat ash Shamāl +Bosilegrad +Žagubica +Požega +Doljevac +Boljevac +Ljubovija +Babušnica +Preševo +Ljig +Mali Zvornik +Priboj +Bojnik +Koceljeva +Žabari +Trgovište +Nordvik +Logashkino +Taro +Rabak +El Fula +Edinburgh of the Seven Seas +Georgetown +Žetale +Šentilj +Žužemberk +Zavrč +Chiesanuova +Sédhiou +Ceerigaabo +Laascaanood +Boorama +Nhlangano +Pala +Bardaï +Kara +Ban Huai Hin +Lospalos +Aileu +Pante Macassar +Suai +Aranguez +Banqiao +Mahonda +Vwawa +Koani +Namutumba +Maracha +Namayingo +Luuka Town +Kasanda +Kinoni +Busesa +Bulambuli +Ntoroko +Otuke +Bupoto +Agago +Kitamilo +Nsiika +Kalaki +Kasaali +Nakapiripirit +Pader +Kakumiro +Mparo +Lamwo +Kyankwanzi +Ntara +Bukwo +Butebo +Binyin +Palenga +Kibingo +Kole +Nabilatuk +Rubanda +Kalungu +Kon Tum +Đà Nẵng +Sola +Saratamata +Safotulafai +Mulifanua +Satupa‘itea +Sharan +Nīlī +Dəvəçi +Şərur +Qəbələ +Isale +Dogbo +Tutong +San Rafael +Sarpang +Pemagatshel +Tsimasham +Gasa +Haa +Lhuentse +Tsirang +Loango +Bangolo +Chuquicamata +Panying +Chengde +Nanyangcun +Huinan +Picos +João Teves +Igreja +Ribeira Brava +Nova Sintra +Pombas +Cova Figueira +Erfurt +Sandur +Fuglafjørður +Hov +Vágur +Saltangará +Kvívík +Sumba +Viðareiði +Norðragøta +Toftir +Kirkja +Eiði +Sandavágur +Skúvoy +Skálavík +Sørvágur +Vestmanna +Strendur +Tvøroyri +Húsavík +Kunoy +Oyrarbakki +Goaso +Dambai +Sefwi Wiawso +Kanifing +King Edward Point +Tanjung Selor +Trim +Jaitpura +Navsāri +Tonk +Hubli +Sārī +Īlām +Nyamira +Siaya +Murang’a +Ol Kalou +Sotik Post +Kapenguria +Kabarnet +Migori +Pailin +Ta Khmau +Sariwŏn-si +Munha-dong +Sil-li +Muan +Hongseong +Charlotte Amalie diff --git a/tests/data/v2/cities.zarr/.zarray b/tests/data/v2/cities.zarr/.zarray new file mode 100644 index 00000000..f459a409 --- /dev/null +++ b/tests/data/v2/cities.zarr/.zarray @@ -0,0 +1,18 @@ +{ + "chunks": [ + 1000 + ], + "compressor": null, + "dtype": "|O", + "fill_value": "", + "filters": [ + { + "id": "vlen-utf8" + } + ], + "order": "C", + "shape": [ + 47868 + ], + "zarr_format": 2 +} \ No newline at end of file diff --git a/tests/data/v2/cities.zarr/0 b/tests/data/v2/cities.zarr/0 new file mode 100644 index 0000000000000000000000000000000000000000..a6c19e337339875ebacd4cf518d4e4d0077f7315 GIT binary patch literal 11835 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%0A5+o-TL8|wQ3mbj9oJ6 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 Czq3RD literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/10 b/tests/data/v2/cities.zarr/10 new file mode 100644 index 0000000000000000000000000000000000000000..5e2b2084a0bc9405e556e40701c8ca2bea7e318f GIT binary patch literal 13146 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 PZYIt?&%=VP8^8L0DYwE( literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/11 b/tests/data/v2/cities.zarr/11 new file mode 100644 index 0000000000000000000000000000000000000000..0666249781c50b2872e401648d4923a8a30f662f GIT binary patch literal 13092 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/12 b/tests/data/v2/cities.zarr/12 new file mode 100644 index 0000000000000000000000000000000000000000..f0c7c6184fe338530d33e651c91c93b0b38c6bbe GIT binary patch literal 13491 zcmY+L+j1kxcAh6>%LmCv$(GL%^#b%e03e%ef*?5rr+d2Z5~w69i9#j1s*)V=YN(Jx zS99U;jYo>mxV0VLyQAH)y<>DCjdu73;DsXm5~C|UhQEI$xVIe9(}P2yDsvtF^{;eBas@+Og@l6{;>~z&+_t@OoZ%=(w#x!%k#iuV^y9&*skIld1-PAS3i`F&s z(9L5}`l60qaTQllH#_xlHLqgxk9mFKn>P5qcmF2;s&~bxO%FvGi>nZeOUH`z&~smR zd1=#S@NE;kw(|G+W*mB_E3RU>a7p(c)U&R4Wv`7~Z9m20+P8Dx+Osd+(l?b`=@)}~ z9$KegOnHp!s{UW_`pgwCo6@h@n`iw_&-gsux!>deqsq0lzYBHk4<_3W4`KVW)_289bAoF_W0sltgwyodD1|CHJ#<*@9_wZJXS_UdTI{(` zW)j`#(k)z_nuTlZ($JKtwcQPI+GXuRF-)$wtX$t$>_b-6VjV7%&3}{kuL9wr^^W=J zCU#ZOr-VQ-!;a!C+&X>#7B?@n1Fi2cCZFv+Xk1gSQ>#lxZta>Twsy%lrgr;5A78sA z9tkVE&3TwtzGlOE%q2Ezf14Kyva{G?A~X86t3&5D^5?m0@7&y3C=Uq3-GA|Q09%P#fh6G(%_%+%_U~)6}7jn z+?&6}jQK$dN31%NL8so$T3h~g?TC)X)$^!3jH(#A%>jtC zg;i)RJqNSaCxy$AThzXoL^kGv+3)=MR#QLM4TiDZeUq~$tuWxij6Zgri%VO}unpzH z-@D$oTFa?htsGJ9%Ct!OU@&)IfkvUPx9=Ny3u9v?Z!vuA+UnjZ5AklF$lHTK(}zWh zdw?ij_8D{ye6_B)axsV4og-(XJ?$uU+Yhr~b~-00 zidVkbejw51&|xIz&7ro{?JofVx$!LAwVMMLN7m?R*Ksx5{`uU3;N0Es9=pm_?LXHy z#N)ff;#}f?jpE{1H`U#*c8|^O@s2-Q)x-t%%|-U8K+5)db?mB;<7`4|I{R$6d%O*G zj<~=+7V~5=G;*tTap_y`qdN{(vxpzsjqdZZ3$eBZ{`eO^BYnGm_b9h3%lU6XQ2OGW z7mB`dSSDPe-yPzM+Ur@$uPb-sygm8UEs}eexRZWBL@)8GY#~Plm&0Hb39i`wE+4;+ zrl*}oS0B8f7{+8#20pE~A8Pln`A)`<1tCNRS*7w+J1t~Xc`mXz zAz14f$f=J9L`Laq-4;B(YvcBZb*zK1;E=ikxMK_h{z)jy+V}C^QsOjiKjm#6i~iqVgMm9cWrkX5#)u}e2IpD>`jOTsxTIA>|r z7~W4^edo&9D!vDG?^dzteKB1NS2|f}=zxQ+YRuY{#6GTm9Z{+Fr68~C_88DEd`p-b zmK;a6QWKg0q9vf4!yr2Twqy}5Cq3bsZ4zj?<{SoovZT_Nw^qBS{eLd5D>j-56y>+G z@B|QUaY7~gOem#g*m(ID4ssk>R^MA9;xtlTrBu&+149T=5va^_nPtWtVC>Qa>kNZw~f%A{qgr>reOu43104sd99Fa-Yfo1=PFCCZh8 zWfHpD^@alk$wL*Fy5eF%i~u6C#$_ZyLFOcXof0IST+BXrFi8plaVvdw=$l}1b>&x{ zEHh_e%0-!$G3i+pP&nqzf8^y+UAz*T0sHIXs9SG8wV_XitPqvfp9k06^53GER{ouN z<%@PkC2+NETTF4uy5+`^qo(k~lxtk_6oO+oVotQ73%9{)6*Z*}#U;Eav#oJxm$Qu- zfpiJI?e+|E3#3M4f`o7^Mh>G|rY1~6nqilHauP~_%R;*!r&Z>z!_+xvZ0IbAByxcv zOyV{$(YdPK3H0YSc9uUEZBN~kN!dI;rl7^Nh_>ZdzAmeHm&@r0f>S#>fzMuLA}|Ia zyGjp#?b=joBZID*rPc}vc@#R{SOFPB=Qc)vkHJaB${AlfOF;_K%{%>gnqULLnB>{^ zJ>}TEIri<6Xx>-07HrYa;aE-V<*8#NFKmivn z*~KOUb^3}cWZKuBUrap!WSBphr8?70JE>FW6Ho7(@bsVWZZAl$oWUfM@ zhym1<@ywxkPq<0740MEht34Ab2Em3}3dz9T$ro`T>;H@xPm^V3hCsCvW_t&V$FKtMuSD z{%&bcxpteh&=ur7ys5Doe&w*>-+cYwv}(!?ZBp*UAlYVcE9Kkw`ueq}7xS6uQ$vX6 zwdLP&>tK;hm}i1ZOD8|5>Wd7}Zo%Dh`!&y|*$vXpOm8vQB*ISF<{p%`e4Lb7sGU7cZi6id5cxEZE-{Gq))f0V0=y zc95cSnEK3shQ7Xmm}ry7Ys-+s`>24*ayA*v52E-ieQno{2lfBfO+^J4tt5AUtmPx)9x zyET3{n7wo1oeH~l7Mp`EwFgtT{qx3aLl^Q&mJ%)z67E9zVDU8!^8v)^~+ye|< z4~P3JDm2=y4uRv+kh=e*@0x^*I^1aJi|Rq_Xrg^%Ax2F!pF7(a?EH zHn2k(fA=-jSDKIBe2%+7PWMhbKiqw^!gS)PzWRVtY_&RgUTfpHNFJ%C=1 zD-1jFI|Rb_Mo{U3x?HTSq7Q)E63n+~zYg?H-)rTAd0YpcZ&6LPam66DFb92iEDG6& z=HK#q_qCdr##aX$Uy$mpyV-r*_)5_)!J@eVH%r<5+?5*>tlp4NyYDg^qaduF$zV<( zP@Y-?pEN5?JzZpDogxKjP9e(pe2Jsl#-kBsTJL%#nI3wrN;YdB{9;dbxE&mMeOPzU$ir(IT|I3z7w%j4bG5C8WMTx*@9*^>S^xGq;5$cd`PH7zo1RyQ_! z#>Ls2m4kL!JADX2134dn{AlZZ6>H_PGV55>7Q#`M)>02@q1xbK_mMvvXSDMg1l+*M zEUF@{^_{Z_yQ0lPg|M8lN_$2pxDg{&ViVtCVPqN#yYk&VUR)_>o}0m9(B}qUS5JI= zmke~i3@|jA1A^GYT<2;igQ;Uf*nNK=W2s-9`3j1oEsSVlL2==$jC77@u@1c+rLAn5 z!sSg7#Tj2=zPfON#E{B;ffP+1Zr?)&_uVV}r1LA8^@Xd&=Y8fX?e>>dtqNq+x-peV zo$2;dChSz5+q8-1QIq`65UkJ-erok;i2j0(WQxHlJzSyA`{wC83lfB;W&!=w_NHzL z+t>Eq-iKSH3QwuB-Bu`F0eYOPHAP zBCUJtp=SuG#g&vEyE$@Blj*}@mDUSLR#1yLb~mj&G;~itdHTujQ{*~b^~S}uHJ54)p!gU>Po2Wev8v+96f4wA?>b78Ryzhg z)``GTWWOcC>*elIUkE$N{5sb(k@I|ssgu1IS-#vDlRS*~pi2&JrR6z6TM0P81vM3B z{C%;^_^Yr^xX;+6|Da{jtg+p1e%F;RuE3JCj^V0;9dppi}hOJ5$1^wRcn4trak;h<$Brm ztWicBfIuC}qtt$Hs4NYD77+5$8~a8w2ae>DXvWhYI0VDKo-EkU}S=VR{7j#K(*tlTW0B&(&CM>(JG)Kwx9^&z0F$`R#QKdinWih zODJR3c7W(LDXe@PFk4VQPRPgI<7)TOs`xZ)D6bYzgITq*r!35?27W9|;j!^ifbZh<)iaYoNee5Mhkc z-`rAOEkkh=?s3M(4KCG8xC-gBb z&}zX1hK@2({>|6lUZ^wCRv5MbNVl$0BEn#$EGAX)g%*8Xq2hrN%|@_%8alwTtBPrr z7En@)K=yU}^o>?@NLTmZWy)j4oq2I(lY(>X2=jH;&Ts#SGF=<(g{dJ;Ter`nlXW}; zkiFjh!qyg;cBloE1J8(364}`7Q0f>YnYP#h@37sz&n>_-Ht(W&o;J!^S3gF%=u9f3 z6%4d@FoBt2s}!k>tpvAQ@`bjs``-^uu>3rQ>dB5YVO(eYV3^va^^#`@$x5i)+(I*b zvRoek=wU4t$fAy`Nw5dqWkS!<-W!j37LE0|5&S08yh)IHVHaEnwhhQbp{808nA7UOi4@>!SkZH$;%z65Wz8$B{Y|~Z#PV_;uOAwf#R-i*L zTc=<=jiehbptnbR){CEuCOlIJEQpa^SWZ%Cld-x-L#rFUuw4P9{-QOeDI zC&OGXbqk#Umhh zmH-!hyh%&jvx+V^)f%Ru+K1cW?%Nr7Umb*tprri4@Z^8zMJ4Fc2xziqPX~U@5gUJVDgyO}$ z1@xc-M)y4)%BQ>CC-s?0t9HrMM-dpa$s2|?-ljojW+2rWcumrrn_H}~!&8O@+Q2Bf zerCftT}=-&^PQG`?(d6hKToU9_ttZdTt~S!vvM1TqPqSKF;ScRGn8Xe94g@?+Q}JtO?<~DGpG#e?axfpc#Zp^ zYrvds4EtvO@j7%X*P1q3ZT}45><)Z$0Jt%UT;wVHTUDPL2kZ7n^efH58KVR3=18h! zpNBEDL`|MZ4QU2I5z`9M!4R!8XuZa9WTkQa2{>DA+TuEusDm=vn}9@N?G(gDk2)6s zqb=0NruQnKP?Sa}nV2Gfa6}*!Q+K0nsToRHPQ~0d`9V~o%YQ(Iwt&H$PydHeyRIHI zo{5{{lE#tvqKH(MyQc_|TEG|QYpLr5}CU*LaVEoz;cYhuA+pH*VG{qtjxEEZE8AB9$2;ESODpz<&TIe z)`Kv*s00Nzr9en0AvRg)b5Gxy2q4!F6&~p;!8{zj+qb)jR=daA(3>W|VHVXWx35_N zL)e2uEJ}YD6mJ70Vn&Uh8ijEUN{A!t&J0t$OJ8Y8>V=V6SPTZG!LztHW=h9t5Gb~; zW`4F}X0MmW;f5?Q|DT3tX=)5}Hilj>OE}RKfc@!s!uh`}X}jWXT-7 zpgh8H6fNUDb-$`mTr6$`@7~(kOG-BLF0F;xONO^okhe4&jyQ<{4gL+jy|~e8E^YFM zi-efk|Mrjk(|4=f-O~a4OmJLfBQyF4D`960nR5L_>1#T1A{iElbj)wI@3k%BZ9T)Z z%8m(|3j)_A@lL(fx0f!$(tB$sC=_FLjfSO6otgLOl0(O_DV(8hFgMyGUhw##Io*-Gw~uR0LxZ(?%us3584h{T z|NY4l&CiGvloxx>fS=Nk%UXww)nf25nYcRaE8h3^CmI)mpUOQ zzC^avHaYJxW1M9o(w91rgrK3%r#dFF3`P`QHccS?ZJ+_O$-W%N2k20~mNbA@eWx{_ zB=jkkr@XtEYv!(nilU|Md!fWL8@}X8er`R(`01OzfBI&fHE>O~cl10tu>4Ki3w0=2IgdhVf2 zdxciO$(m3f9O9jP0A)blBIKBXxRb`RnbtuwX~pdb&?^LRzuHqsO_c-y&;8wdKri_! z5iqzVx3JuF$!6SKrXEMEM_QQ6sow@!%qY(S1G-L%>cu=h#Cc@A2$ZpDrl|x~XS8P` zDVy52d+aSaX~vXUzKlED51bgx*=0(rjP=IH z%&^Z9ki$b-nS`jeh%x0IXZoxfxQb&h7BYOHUdf&Y`K^jC);5;CZWHHWYT8LA`34P& z5J4k1-f;LQJap#30aWfGS%ZCuGFN#k{#1NknofL(R!}gD(c>5|Ep*gEyyodo*`+#V zNqC%?(96%mjGhqPG_MB{T<6*JhH+@49@^;CTUNYKtD#dm`gVk)znfU@&6Z5QlO@(mbIs$oTs%?3N@CXwyE;bkAL~8`0+3QmkXOP%e4c} zXFd11Bw%tz>!KI~KN|~xlLTr$4@z{qD})oy@#qq^l9>5f?d zq*aC(!)Fk=nj)?F>l*5qS@5avwm+yn(`gE=i;Bl!B{?gr8ozcryGduhFUXW&9aYux zP^xCGqT6Y`5uquKmd^r-f7pJIt=@2qX-<^ta>l#rBRx@XE$CndUpG+*>6J~`v0H`p zNQbhtmNQOej$ph-5}uC-Wm*V7ncGnxeJ?7)8o$yKPf-PC46WzJ_9$#!e-PKmzRdv% zuuw3O!hc7T5nWeOk6JF>em^7RuPJF7(n<{0Jy9dzNDA!dcQlJcx&UIBZf=;+0a}R4 ziU8zMI!p}qmSZR({%Ff(S zchceyJ%m9+%X~ww)bT64Y`{NEL-= z3?qktV{6F2pQUi-y4-!tzHOzB=*v@ogAhSF6*~4JXL9l)Z7dHb@a8?yrHHp-X#*~f za2FGhmUCCdnL~H57Ma5ZJkA^?espsKnwWMu#&o(G4eJ)D(#t8+VH!e< zTS$^VQj^7LSG^q68nM~SDdE-jCgM%PO}%lkQQMe!UWh*+u8KOjxisGdd8Vp+4MTiF?ddUKo-Q&2^Xt2 zEFC!7AFb2@FzA|VQ- zER32r%Xzg?8caMVeq=o~7S3|4Z1S2X66`V|HXGa1rPnaAMsaH|KZ0a+*Nv>=DYCn!`z^?fVn#8U!OlxnlTRvp z0KtF;uwdBG*nmcD4KO-tGzN_$9LOWRWz~a$`4gij{|tX?2TzZ2fEiX*G9zMN)?Rz< z=>Pj0Uw!otzxwK{AH*syVy=%_Y%CLb?cFw>2?=Ry&;+My@4?ZjlJuq=qcyP8* z*H%&QJdJton>nhvI#MSGdUk=s_S8u!eU+{Ta`zGYRYO}AqP>0G^xQQ_@ z-9x1nUWY}jSNfU`=yJFD2Yh}N_no#isG0>w(1WL;ymythG7e?j>8~$c?F+a0T|Q2{ z%i}|)H#d6O#nWTG@hki0pbo8zzt3-H8=w2y-#2TQv7D>cX3q&rxN4QawQHXqw`{D?35Ic9w66X=K91t9Te>XA zEUtBB|Dm$yJ72dkyY{YBptEDwe_HTZB-l&6{-on~ZMQIYf8F^8FX`218Uz z5lILhkHaE%FCkY9~7;*;`-fYtyI4U;d|Mu`ckJex8S4{?RXgSZ%|$ z^oRYlF1wD63@d!7<)-I9Z^Kp}COe;Zvg?ff7oEE!@>a{P(yzV__0F}~B>w6b|Lt8c zNEox*cWlzmL=?K+4qtZUk{t2cJ*?gRu35`kC&YIZmut%x2fnQSrIzXcB4*==DXRWw z@!-V63B#J4?Q(;eiLeVOlBuRC0!|!&=Csb+qGO-zQl5B|HoSL1E4^{6*tvhsr_0XY z6C$`{*4wu#0+$WRF zW6ggu^CVPm6PhAyh{Xi*gN@65)Q%56xT>PgG+P!A{ZAL6Sr*wTh^pOQkZ=!PFB)>z z8yq%v>+CdIvW|IS)piL9hy2i$K_)%!D%bzwj)T{+kx7Rh@OcP#_AMZjzb>5FjPxhe z{Hl(&nkzR6QaF8ZptgQX((R+=*C;AmWP>+^%F3II<}SbU6(y|IQZLGE(EmJkm7aQ4 zZ<;n90BrYb?3LdpVb%H4qBIaz=Q97vsGpL#)@ z$%r@Zy(@s+;C1$MFfRaC+X#*sX1R9lgv+CFPcb3s%(sA^MZ?o$9&YXI{dZN^TLq)& zDQIWU9|`@fYuliVculx8%b1++D0!$Op3^U2}XL3Nwyk)c`?Oc|dx7VHi91U0X%cN-0u@_*t=~ z*nLS>;yQ9;&^Cmg{*zf|F63l#cI+0l(*iRfCHJeRM?2~SW1;mXZc%zdS2jKG zKa>C}-v;z4{+-)ph(gd@+U6sP6VU<(Qmc^Yt4wR zR#=}^dD{O5^h{j%+TFQWTalQR@aKK1@KmNe=qrONw@m0CSDkAz2%1a2fFhKg!Sfqe zcN=HuIf`3&hJ`zXY+q0_jD61Cib_;{U(t`@H-DbxA)5wRn%sZsO7f$%qI2DWkVX++ zLx#yGTX06^kTiPtRJ*6-IYTINv|b(g>S(BRGKM}0O#|6fqavtL7o7+9(F!cB?$3qIwbYr;n9OjwR|T2719sET0}% zI*rBb(??RVQ#1mq>!RAZoy%sfOw@Nq)jzZclvU_`A}i$eqAQ(Y*-WUib{On-Z>i`UqWtn6o==Z0;6u6|HK_J$boIi`~0j zb{(+49fjB;35aa1HmH{=564~O$Sma{@t(RsP`cSsF4okpRoqK=k7x`e0tntGZ|>e8ChCyIPo+{ zqLy0if^{GX^gd?U8F2`A$>Crj(aAYAqyJ=!T=H03&=$5b=dg}(Z9uC1T`hQ#ehpd9 z3)eN?j5w$Ei@3{cK*weUbn$-DQ9Geb&EvA$eSr|_PSy5*T?N>m&!+u{yt6pD?CRV- z{p9JhfMC=S@}bEt>6nb4Oeo-`+wQ#i(Dy%Qm6d&O0I7K=Q_63r{ogIu>Xz;g1sVVv zi%9Q{y$s4FZ#e`Lt&Y85M3L$g6k6DxU|+{DyL~bNMHzE=Grwz#Hgil_gYoG}UO`1~LHZy$skCp73w5>}s2U2DeGhHkmaw+#Kamblc368Ml|=lw5q93W7|q66?T|$czUF#XT3`9NPX!9GPsFW{Z45b6EL3$T(otb=J8tZ zREI$t({@osiH^j2z!(a!EF+NNYcXj(ryW6K@jG_eIDqS@C3*}C#IEfBD(N5tc>42C ze5$O;O!C)dfXO7O={i6bmF6Nt4P?mnb?F~qPYGWoCRpGOyE{JJyQR4!Zm353bcUjn zxYMkoG)A3^g1*2zRu(q&Td9KX4WkAEn>(tlzB~zvP@Q!yYEMt9@4TuwoP~7ga>&!M zdcP>&58d9?M|A7d6#YztuLoWSf0y)*ov~C(jq%=BTSSTMlq#xSoj^*kuvU{e1;zd- z#D;FHApI+vLRpZkA;CaVOC0^N?DSI#wN<=N$qy*t91ElyWGT>J=ebcwW{(a5FJ7tz zPI<>IFXbgV-3|S;*XFI+t1(%X8}>}q+UN@h35SwrDX9{-=5ux`Ky1xjV_?fk7_zL< zX-ufm+0ZLig-^U}2(u!+by$OzOIIy?(BjAOp_DcRYc)}n8iizGnL7?N{A6*p(u3E2 zSH&`KM5hH=YBl#o>YPE)i%BgMfx76Mx$}i=?Tqstn<@ zv?g}wi6l$6ZT~HeQ0WhW(3HTjUNgZ4W!Oe*!g1_AEGcqYPn983rZ>*?795#pSN|bH zNAULU7qHa#GTR)yP;aDX_|?}(I)sKbb!m_eM+crxU*1ptjsOc6sNx=28> z)}WHyrPqFq!$B%bzn6F=cFN8P1x68$!bMoy9|sGdxB9pIatLLi@b0sqp34eDj>?iJ zZtfw&^}4lWFNi5qo*-52kzrP-#J;d{?#(nMXQB6$Cet|AgK7H0}Xem<+n`xombc7w%O@A9Yw0C8p4mjq3I{pmk?r6UB zzy&dDj#m^-gVi#8)qgA34!b-rGMYz=1lqu=NGEXZ^N+L|V$&K@(os>TPs+%Yep;XWM`Zq_3hDksh?WGR(23L@Z?5*JQgL z0QqwP!?vOTGKgI>LePdrBz;ZOOpxkD(?G?5Hk#mh#w#7sAF9G&`D@=48@*S7X!$hj z)>L^ddf{@)zh$z#IIla+9rVhH@RRH)4?tO4BV#2LWPur)g~&(WsF@p5Lo%ceW)?4U zn#$Y){4Ekenr_^CBT$OF0R(@QD$&^Gp^@<{9?>`;DgRn;OPv`FuVl@avM1VH)X9(C zu7(98*&g_7Mb@Jb{F;efQ`AiWg*~FTebUh{F^L&}u- zarz8?%LrefSGKymV}j(x#*dLgYtoqi+a#Lv{Ad5?$D4I9?S6`uM7C0VCvmUE$9}U) z87GEP*Kp9ben1tsaHfw@sA0Gm3t`)}-S;x4) zOBd<7)M>^hro>MJaA>7|7Lg$Dfgt8O>S!)%G-$*D#2;ez=~0YcQE?siu2ito`&74R zYFP0F<0~XJuquV7!AXVSZoaa*L5dg_U%O4SVo6!%B^nJpTicvqh^NOdRqeEMD7*^g zLG6}UcWFhIXaTi&a7XI2l@%*dl>Qq;M|bGRcCE4Y5h8r(|B{kye3;$0apk_Buqbc` zrGHOpBRusJgc4d?!wU?V@CkG;O-BX*w4pKG9MQ5)s?lk@m5#52Tzi`L)a<+kOOmq( zC2B6q>Vb3Altq_dn&!JQeH0KXfhu^3&DBO-iEqA$6%dM~%nV^S{`sR~jW)RJf7XA{ zD~AL~bcb}$UR7$TwDaq30g*dQMrPngsnMFKD&170p~F^fj#6s^f>jAm;mVca^@aMG z#v>0aT%@23|?Y3A5aSR-YV4dAMZQE*pB*@ zCbT+lYIU<2e8g>JzBhC#w2>yONe4v7qIQKgg(ry*a+jUK$@h{__K7S@5{ zK)#U{TYcZ?w-(~B7_8N6vam=}7(ja9-;5A+(&ztyz8AZ@=l{737W_2rOpZKR?-B$V>tN9sI~XpjKCo%Gb*r!np8{=0I`Hpy@c9T$qkf`@+P zOydzaoVZo$fTi-fY-o%^e>ex_zn6e1bKDOgX~{fAvXOCT9AF-5%_}NZ_Y|RPh>-R= z^J!$Hs2scO8fG8iC{{TpyPD(1Trn^&d^T9(@s-h0LJf`y9%>;NFaO#71+u40H!Wfw z%4V%U&m|%Fd%*=QHYEd!)K%!o2c?=kjgVDj(y z)+x$QSUTXs+DoeTyi`ZDw2FO@=`CxDuGFCTF!<(H$=ZrLgO5bJHywyCv!3@qtcX7S7IVpC>>oo(nW`k?MkgR0gWWF>3B?##P&*; zw9M$}@)D>gSOGGbwjL#ZWAa9_zsz6YjV%9TZc9~A(4XR2IJlQpa-~HXf;r8?V+`k`ayv_ zDgX_X+qo`wyqtWUYY zDWFQy1<^tB&T5!en>i)|<+?DCKJUZ^bR;0earH%8rzhRJBD*Mn5T0#Q<-8YwU>uE!3Y;$Uz3gjx97$$nplrV4$cg7~07gdSn`m)+Zaq#q*jM;#X`9M_ht7dzLjH)fi*&=?A$NbMU6uf0!7 zTy_|<*@X=)cuqU<{4rNH*cFy{VWH2jcW^c+o*wxDLAr4^<2&3M7&4#_M*D{K(~s6E zMse|qzDwVL-jt2E(YGXkK%!=sw2xArt;u#=!eDemNuDAs6aRrq!Td)Ve7R!urnd~1 zY#_Ij-8hDzMW>yOcU2_;Lk@W3mu?&0b3MtR1+cIU-{6HZKInJAXJbKeK_^yQ5j3e6 zN=BWO`QF}Bq6~nEp$)?eLnH(iUBfX7PD-z!ND5p42@UtCR1^ZPT`mKo07i91BpSL$ z!<`sLB-YssK~b%N@XM4zX998Xg7uUa+!S#4ah0gywL@Ca`L1F46m%omV-=^eGLo(H z!e%#!IRKe{x{|5@ZLX8f!;xfBMc9=H_x;nyowuowv1oW1B*F5-FY;*D1qPcg7uKSw zxW-n0i4LBPy;WEpvj2INd60$ChhCfnoy*XFwiAyWGuacE3Js4l?+n_FTaz$4>l>gN zjMVooU@dEgFhr96f`IJ0U}~CJ_mZ45&W-fHNaZ_rq&`%J>m-MDQF5_GYGOw?r?L!( zG&ZWB#vr*G<}2yCRtWH#fyaP8PdS1FcT3Ks$M`Ix|NZh9y_lSTK^P(WR0k zN&#Y{6asx?Mjhiaq{SN5z@E?`X}?=QzLm=Z-B}RRRXhwzRf}xwsQhUFaDfE1K#d3% zc_UJz|KED?Ys4OCq4qiB6SqiGfZ{G=eV1l@+CuDqCwtlq)}gHpqTb5Q2L9c~tGQxT39wvyldx zPwT5Ua4%Gx15>Ye;sjLy0yW9Q~Vgr|9^bk{c6oAsW!}Fi9oA*zj6`thf z;gXw?W@zP{K*7{Z&(fD*T2`o zP&wl<7%DsF>Y&Lhuc#^gH!{LGl0%BoF^wgF*yRL}E%73f z&EX9$_$9P@9bnhHq$N*0CzO+}L?R9}LY}}NuolJk8G-fXau)Z2gB@*(sVRX_0i8#k zhF$yNvH>@S5AtxSIjExhI09l7b0(lbhLu0Q+{(P7P18GOBvUSSroWy9&M6#V{sY1^ z)-cnyR7=yMP_+;g-W$0eFad@0@QikgK|9z-Y7WN0G~$5?u4fG@nj{4|=1`H}IJ+oG zvVmOeRl>`cThu-yaF-sz-T)EFrgnD0^@g|e*3OtYx5nR@0nfli?qa5KC~LC&R1;yy z93dCBAxqscsrqm<;eNJVAil!=2T8zok5GYKQ=NYP+ns6zlWGORfO*;Ba+>&=jX=$8 z;jT**mxZ!x-B~lhqZOn?+jM~|P>R8%i(I(dTTs{x%9xdpBB~g4`A#xC8fx^NO-8v5 z+byY7y2K&BV5a5w88!9&XMwN5+)Qn)wofm`rGXzgyRA9>|4Qp?whVFrItCkqh2kaM ze;zRYgLQw>rZ2&3E?OA?9J`H1Itrh2x5R3H$;FGp3h3cZ4swyEe){4__oBG2C?k)! zajd{`s`05EqZ^_K%8Da;g2oi#IsOszrtj7tCPw!4E=)mnN>{?pkkoApm%hu+2okgw zlMi)c%^3i@Ot*QpO@^i{N&nF#26P@_c9nHCC$3yaOAtDOyDBX0>f;FV23RKxEANSq z{!;?oIpt?fq1B6ZtDVjRx=`5!nxMIt(vfz{5D*;FbJtwrxa4Z6excXRG3UPjkdliS z)HA0vZ%>b?832&_oI#l}@1dh0+Y3DjH>mCQ>;NV#dop)jxFe<_2sRs2k6(~`++x)Q zqcsj!LUGS&xAadx33f~4($VrRlzDmqH|7fDk+oQ0WBhlF?Gt-^L&@1=Zn@y9!5`+E z!7k9vlB>>=!~nr@AJbcFMjRp?y~c>dkiK|BYNqkSsb@~K#tHg0{S@Oh?QZZs(qxF* z%Pa%RQA`8rlaA_cA;~C6qncU+0eS<}4fHV*-cVwfk}&B4wDw+mozaD8gG@QU+>s$=S}qw#jK7XnfKyhG4^&V+J?oJ9 znU*T97&fKb7%Ka?OWP;kDd*ZP=&FB)VE;lwKg;FEaFxIXHSKby!QC*V>svQI^xRoM z{kA(d;}Qb2;YN&&g|BIU4D@9c6OxWptNilNZ74e&DF-Act&YOpf3pTY}`OC9?F literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/14 b/tests/data/v2/cities.zarr/14 new file mode 100644 index 0000000000000000000000000000000000000000..35e157e781b7a8cccd229e122a55bd5279920023 GIT binary patch literal 13323 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

FdKjI97 zc&F~BtLIrt;sqK@tj_?GvmxQqiil5Rsh~o7gi9ZsxL(A%_5FGp=(CrCIyK2a7p7La7n@lJ!2N@Z7*VhuMSb1qPO zPD8N;lsU1RiKFG=l_ri0f*EHTR-u`+uHSgu9XA7m88s(t1a7C9A7Q^{2JKc5%kAOQ zx14EE*3r*p5-2Dm<`#J#U{4doYX(Dk%6bhPteLsRGiV5TrctMExu*Ui9rGqd#)8pJ zSTC)*5Vjx8T{qAF=`kF?UxmsN9clCUq#nWqc1_XFBZ3Yz)o*320bNuu z=>9EGK#+|E+=zziC&Q9dYz9e?Ao^;C%!ve!kn(qKTl7YeuNgYF#m*G#5yfbUc9^nP zi&JyM!I@u*;^TnK}CRxY>=OA-Api^(xS#ky=q26o^a{~>FnAK1whL_{2NWHG{9+72ZAgc5d0Zi z7HF;36kO9gX7j^OHWPW@M@A)&CV$SDhSDXjS|jb^E&it#8HCX=8S&M^M;u@x0rZLsN==fj6zIP+4XXRJfin`Re!`CuK_=|QM#n_dC>wc78TN#{-%yjHK0Hd8X6(Zfctb;8oy z@8X)Kh>-15vX%DlwD%qQh@Lkp4*#Vi`_qxF1=1M&H&q#~P?v63MI|~CvaxLqdG%7f z4o%Z1A(Gq6Oge?-Y0_bND`RRLwrPvXx5Z=1{rU5EmBrG6d?wiR_&1EP*~mk%12m*Z zI#VEudxi9BEk((Z9UIqZ-|tbalM`5M2M?6t7cJw01Y#tWMqi8!RR$KoA1yzr>#Un# z-MJz<$eGpm(SRR7u4SZ0_a^$Hlj4g8pdT%Ha~twiGdTPql5Is<=KK>%rwD?Xp>d7 z*C1HNK{hRHi1Zzd;f(vz(DN#y^VpC$o$?rx7I%k_9Q)CxQKg)T@n0tRr;|x~)&$O( zTNA}oUAa1Zq>`y%!gdR5y9ec72Pb8qx9FUNEVas3H{ig~7rh<75r)=ioqI&ql?5Du z4IfWyppR1!jhPU`^7e({&74wP_+T9qt0Sn5UQhi7rQ`V7f?AxvE*L~>z05Q)o<3c} z>Q>e%a>&Eo-KC?13PyAnSJKIHeD2iu$o_Ut?Nt;w6c-5_`y&S@-?_q z(`|u@Phe;I$nlq+DY`yhd&cWYzX!-fZ%~HygVo>!t}`R736;)#H_yqRIcyooB-oZ@TVIxJ{2t#O_2!+970Gh}dO=13vhKM85jYKpJKqqOTp*o9L26OHELgg}%=a(za_p(bt zx{9BjqynV#wdR)ge2@`@kfVkc9_wg-Tp~j=@=C`yCYtYZ*5Gt%1Ko}at|bjP9%(zB zy8ZD->Hfzb_0;#FX0C3Kxri&IAYA0+F6n^_lIs4a$ST{FGAwwwS8T?S!*-IPz=Akr932v>mR|t7(Nu7J8A?dkbNa z^DGsa407W1_@gd+0rPwQERa>4&}Mjo{4quo(^5bSVmkB1g7RtydQ=yO_k;_&vlhIF zk%5jro#V#BcKbFy|Eb0aS}`~3h(K#hhNO)qCo0mcwootdL$^O2nc!@8OIx3!;+|2y zAq-j|=Sbx2b8Kz+b4`^y!Rg4UpNGSwyd>`s54^WRGQwSn7@h_wi7mhCKX6)#xk5UA z$H5bce-`X>(h=!T8yaxZ%vn53INK7zav$om>hCJMqG+8~gV3x4V)~hW8pfg15!a4K z+4>j0p~z_cZEFYVNin=?DwPXdgvi?x^N!U-Rsi`cCboe zfB1a?4f^GXHBFn8OPO#Q^L|~MZPU{7%;?5(enn?HQqG61=7o%4Uv~ zr0V(3--oWZM_Uz2jjrxY_}GK#x= zF$0u4c!wlOpIxi2*)W`Qy6YBjvXj{tkNlw!#l4c`m&b%o4BIsL7L3sVK=sYx0z z9vQ9cpsl2sC0UFY(c@}SZwO?G91>UFI%~|{Fm3vsB87q2QfVvTvjac+zw9VT{vVT} B3Eltz literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/15 b/tests/data/v2/cities.zarr/15 new file mode 100644 index 0000000000000000000000000000000000000000..88e2b1f2ba9d3b8fa2670c916a0b4a060d81329d GIT binary patch literal 13353 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/16 b/tests/data/v2/cities.zarr/16 new file mode 100644 index 0000000000000000000000000000000000000000..703de528a78a887a89b07592b101fc3c47fe12bb GIT binary patch literal 13283 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 bB+ZX|(hvG!+HujUb#~#1{;ps=7{~a3w@>~@ literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/19 b/tests/data/v2/cities.zarr/19 new file mode 100644 index 0000000000000000000000000000000000000000..7e24a4f3cd8e72a4a6fd5c9dd6af94ac325cfd79 GIT binary patch literal 13626 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/2 b/tests/data/v2/cities.zarr/2 new file mode 100644 index 0000000000000000000000000000000000000000..586ba5f4541a1db7d83276c52987aa851e1f8749 GIT binary patch literal 12776 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/20 b/tests/data/v2/cities.zarr/20 new file mode 100644 index 0000000000000000000000000000000000000000..19336a682b88237bed5deac744f12c7cacc3b96b GIT binary patch literal 13293 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 XQv~Cg8ImqSsIb;Q&v0Rr!utOKb211a literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/21 b/tests/data/v2/cities.zarr/21 new file mode 100644 index 0000000000000000000000000000000000000000..3d27b331a3b6dc8020f7a537f4467e6bd621e7e1 GIT binary patch literal 13595 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/23 b/tests/data/v2/cities.zarr/23 new file mode 100644 index 0000000000000000000000000000000000000000..99fa74b5829664c3f2001924b80d9a4ac8cb0324 GIT binary patch literal 13599 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/24 b/tests/data/v2/cities.zarr/24 new file mode 100644 index 0000000000000000000000000000000000000000..1c2b82693e0a3d5a3dd5b8d5182f13db84b1e268 GIT binary patch literal 13532 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 Rcnx)0|In8hCa<`D{Wr_#SMC4+ literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/25 b/tests/data/v2/cities.zarr/25 new file mode 100644 index 0000000000000000000000000000000000000000..1a150efc69db5ca698aaba97be740f9f710e8656 GIT binary patch literal 13451 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 E0V?G@Pyhe` literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/26 b/tests/data/v2/cities.zarr/26 new file mode 100644 index 0000000000000000000000000000000000000000..54beb664dc6da5fbfbda226956dab683cad3fa4e GIT binary patch literal 13650 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�-fgJF5foW@V>tij+6jg!_=8oNRRVtD(8{(W6js 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/28 b/tests/data/v2/cities.zarr/28 new file mode 100644 index 0000000000000000000000000000000000000000..ac872e6c4443da495314de6ee6d4b98a9d8f1744 GIT binary patch literal 13814 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/29 b/tests/data/v2/cities.zarr/29 new file mode 100644 index 0000000000000000000000000000000000000000..a780160e2c403027c19f5d6552414706861683a0 GIT binary patch literal 13473 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 P3W;5&(Tk^Wy!ihCG;LW9 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/3 b/tests/data/v2/cities.zarr/3 new file mode 100644 index 0000000000000000000000000000000000000000..5cc0ad76dc4e58a23173d37b868c59726e5ed34c GIT binary patch literal 12750 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/30 b/tests/data/v2/cities.zarr/30 new file mode 100644 index 0000000000000000000000000000000000000000..4b53762cc53088deb92e1064de8420b014cd518e GIT binary patch literal 13598 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/32 b/tests/data/v2/cities.zarr/32 new file mode 100644 index 0000000000000000000000000000000000000000..39261e88d97a5e6758b94322ed977d8d68e37a3e GIT binary patch literal 13650 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 MEz-dc>0G-12Lh0uy8r+H literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/33 b/tests/data/v2/cities.zarr/33 new file mode 100644 index 0000000000000000000000000000000000000000..61d0859bfa06574aa642d22812e8c77b29d6d1df GIT binary patch literal 13760 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 WQN*(iMYJADZc@A9A?KR6`u_)C82^R< literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/35 b/tests/data/v2/cities.zarr/35 new file mode 100644 index 0000000000000000000000000000000000000000..296d0d0ebbfd096503d1c161e42c906cb5eea22a GIT binary patch literal 13718 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/37 b/tests/data/v2/cities.zarr/37 new file mode 100644 index 0000000000000000000000000000000000000000..bb2629417c074ef33bbb80d11927e8f4bf2595cc GIT binary patch literal 13888 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 ILs5tS3z^04vH$=8 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/38 b/tests/data/v2/cities.zarr/38 new file mode 100644 index 0000000000000000000000000000000000000000..9f6954519527a446bb9ee49ed58f64df875a8e21 GIT binary patch literal 13909 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 H2`>I0gq8z) literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/39 b/tests/data/v2/cities.zarr/39 new file mode 100644 index 0000000000000000000000000000000000000000..23f36cd3f377faa8cf22ec5274caea80635a16c7 GIT binary patch literal 13990 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 H+p7K_x~(wI literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/4 b/tests/data/v2/cities.zarr/4 new file mode 100644 index 0000000000000000000000000000000000000000..cee2f5c6c23e6cf5545b7f27a351453b4b4f6153 GIT binary patch literal 12609 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 ML}_DK7N7e600B)3(*OVf literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/40 b/tests/data/v2/cities.zarr/40 new file mode 100644 index 0000000000000000000000000000000000000000..c2bc2b6dbe7d9f6aeb34361d937a88f3a64aae76 GIT binary patch literal 13891 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%0As& z)o64flz=Q|(9LN3X$ka({Jg=Tj(hbj{SGROTc$rEkvHNeY*_};KJL(lv7Ro(C~k*b1*d?~z9+<`^c20GLWMq=Z@`}v zRaQ8hg`B8sjEBC~ErVUz+8;fPP=MB9|Gj=HxxWBlwB)J!oVu@~H(yGIVrr`gZu#?% zonevR!q$yjjtTV+s$&3Ib^9MwYS|xHscytvwi_;C`|2@tra0Xr8S~Y#!bLki-trit fhlyrsL9|NEh6c%0Tnoz}mpHCIIp?nyT>t+8;TH3d literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/41 b/tests/data/v2/cities.zarr/41 new file mode 100644 index 0000000000000000000000000000000000000000..7cf3e0a8b1061543b0a254191ee321579236288b GIT binary patch literal 13699 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/42 b/tests/data/v2/cities.zarr/42 new file mode 100644 index 0000000000000000000000000000000000000000..9319880333aea849396b4bcde8a10ccf64053317 GIT binary patch literal 13649 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/43 b/tests/data/v2/cities.zarr/43 new file mode 100644 index 0000000000000000000000000000000000000000..e685e49fb03c145060dc86cc1a3b5294794ddfff GIT binary patch literal 14085 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/45 b/tests/data/v2/cities.zarr/45 new file mode 100644 index 0000000000000000000000000000000000000000..16669856164e450a3989b727a67cf2dae3f992cc GIT binary patch literal 13938 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 eU`3^^TvlnE<|X()BB)Y@MM-=Pcj|` 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/6 b/tests/data/v2/cities.zarr/6 new file mode 100644 index 0000000000000000000000000000000000000000..6307ce3a221bb41369fc36c7edbbe47f0de693d0 GIT binary patch literal 13037 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/7 b/tests/data/v2/cities.zarr/7 new file mode 100644 index 0000000000000000000000000000000000000000..e2d072c815c64acc15cba6fb79becfb53e57219c GIT binary patch literal 13101 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 bLSv-t%FU-RRA(Bq8a_@Lvs20#1^NF2<|or< literal 0 HcmV?d00001 diff --git a/tests/data/v2/cities.zarr/9 b/tests/data/v2/cities.zarr/9 new file mode 100644 index 0000000000000000000000000000000000000000..e4c90e831ab4b3cf26f3b0244da186bd0c9ed214 GIT binary patch literal 13001 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 literal 0 HcmV?d00001 diff --git a/tests/data/v2_cities.py b/tests/data/v2_cities.py new file mode 100644 index 00000000..716a5bef --- /dev/null +++ b/tests/data/v2_cities.py @@ -0,0 +1,19 @@ +import zarr # v2 +import pandas as pd + +print(zarr.__version__) + +df = pd.read_csv("tests/data/cities.csv", header=None) +cities = df[0] + +path_out = 'tests/data/v2/cities.zarr' +array = zarr.open(path_out, mode='w', dtype=str, shape=(len(cities),), chunks=(1000,), compressor = None, fill_value='') +array[:] = cities.values +print(array.info) + +for i in range(48): + v2 = open(f'tests/data/v2/cities.zarr/{i}', 'rb').read() + v3 = open(f'tests/data/v3/cities.zarr/c/{i}', 'rb').read() + assert v2 == v3 + +print("V2 and V3 chunks are identical!") diff --git a/tests/data/v3/cities.zarr/c/0 b/tests/data/v3/cities.zarr/c/0 new file mode 100644 index 0000000000000000000000000000000000000000..a6c19e337339875ebacd4cf518d4e4d0077f7315 GIT binary patch literal 11835 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%0A5+o-TL8|wQ3mbj9oJ6 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 Czq3RD literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/10 b/tests/data/v3/cities.zarr/c/10 new file mode 100644 index 0000000000000000000000000000000000000000..5e2b2084a0bc9405e556e40701c8ca2bea7e318f GIT binary patch literal 13146 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 PZYIt?&%=VP8^8L0DYwE( literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/11 b/tests/data/v3/cities.zarr/c/11 new file mode 100644 index 0000000000000000000000000000000000000000..0666249781c50b2872e401648d4923a8a30f662f GIT binary patch literal 13092 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/12 b/tests/data/v3/cities.zarr/c/12 new file mode 100644 index 0000000000000000000000000000000000000000..f0c7c6184fe338530d33e651c91c93b0b38c6bbe GIT binary patch literal 13491 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/14 b/tests/data/v3/cities.zarr/c/14 new file mode 100644 index 0000000000000000000000000000000000000000..35e157e781b7a8cccd229e122a55bd5279920023 GIT binary patch literal 13323 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

FdKjI97 zc&F~BtLIrt;sqK@tj_?GvmxQqiil5Rsh~o7gi9ZsxL(A%_5FGp=(CrCIyK2a7p7La7n@lJ!2N@Z7*VhuMSb1qPO zPD8N;lsU1RiKFG=l_ri0f*EHTR-u`+uHSgu9XA7m88s(t1a7C9A7Q^{2JKc5%kAOQ zx14EE*3r*p5-2Dm<`#J#U{4doYX(Dk%6bhPteLsRGiV5TrctMExu*Ui9rGqd#)8pJ zSTC)*5Vjx8T{qAF=`kF?UxmsN9clCUq#nWqc1_XFBZ3Yz)o*320bNuu z=>9EGK#+|E+=zziC&Q9dYz9e?Ao^;C%!ve!kn(qKTl7YeuNgYF#m*G#5yfbUc9^nP zi&JyM!I@u*;^TnK}CRxY>=OA-Api^(xS#ky=q26o^a{~>FnAK1whL_{2NWHG{9+72ZAgc5d0Zi z7HF;36kO9gX7j^OHWPW@M@A)&CV$SDhSDXjS|jb^E&it#8HCX=8S&M^M;u@x0rZLsN==fj6zIP+4XXRJfin`Re!`CuK_=|QM#n_dC>wc78TN#{-%yjHK0Hd8X6(Zfctb;8oy z@8X)Kh>-15vX%DlwD%qQh@Lkp4*#Vi`_qxF1=1M&H&q#~P?v63MI|~CvaxLqdG%7f z4o%Z1A(Gq6Oge?-Y0_bND`RRLwrPvXx5Z=1{rU5EmBrG6d?wiR_&1EP*~mk%12m*Z zI#VEudxi9BEk((Z9UIqZ-|tbalM`5M2M?6t7cJw01Y#tWMqi8!RR$KoA1yzr>#Un# z-MJz<$eGpm(SRR7u4SZ0_a^$Hlj4g8pdT%Ha~twiGdTPql5Is<=KK>%rwD?Xp>d7 z*C1HNK{hRHi1Zzd;f(vz(DN#y^VpC$o$?rx7I%k_9Q)CxQKg)T@n0tRr;|x~)&$O( zTNA}oUAa1Zq>`y%!gdR5y9ec72Pb8qx9FUNEVas3H{ig~7rh<75r)=ioqI&ql?5Du z4IfWyppR1!jhPU`^7e({&74wP_+T9qt0Sn5UQhi7rQ`V7f?AxvE*L~>z05Q)o<3c} z>Q>e%a>&Eo-KC?13PyAnSJKIHeD2iu$o_Ut?Nt;w6c-5_`y&S@-?_q z(`|u@Phe;I$nlq+DY`yhd&cWYzX!-fZ%~HygVo>!t}`R736;)#H_yqRIcyooB-oZ@TVIxJ{2t#O_2!+970Gh}dO=13vhKM85jYKpJKqqOTp*o9L26OHELgg}%=a(za_p(bt zx{9BjqynV#wdR)ge2@`@kfVkc9_wg-Tp~j=@=C`yCYtYZ*5Gt%1Ko}at|bjP9%(zB zy8ZD->Hfzb_0;#FX0C3Kxri&IAYA0+F6n^_lIs4a$ST{FGAwwwS8T?S!*-IPz=Akr932v>mR|t7(Nu7J8A?dkbNa z^DGsa407W1_@gd+0rPwQERa>4&}Mjo{4quo(^5bSVmkB1g7RtydQ=yO_k;_&vlhIF zk%5jro#V#BcKbFy|Eb0aS}`~3h(K#hhNO)qCo0mcwootdL$^O2nc!@8OIx3!;+|2y zAq-j|=Sbx2b8Kz+b4`^y!Rg4UpNGSwyd>`s54^WRGQwSn7@h_wi7mhCKX6)#xk5UA z$H5bce-`X>(h=!T8yaxZ%vn53INK7zav$om>hCJMqG+8~gV3x4V)~hW8pfg15!a4K z+4>j0p~z_cZEFYVNin=?DwPXdgvi?x^N!U-Rsi`cCboe zfB1a?4f^GXHBFn8OPO#Q^L|~MZPU{7%;?5(enn?HQqG61=7o%4Uv~ zr0V(3--oWZM_Uz2jjrxY_}GK#x= zF$0u4c!wlOpIxi2*)W`Qy6YBjvXj{tkNlw!#l4c`m&b%o4BIsL7L3sVK=sYx0z z9vQ9cpsl2sC0UFY(c@}SZwO?G91>UFI%~|{Fm3vsB87q2QfVvTvjac+zw9VT{vVT} B3Eltz literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/15 b/tests/data/v3/cities.zarr/c/15 new file mode 100644 index 0000000000000000000000000000000000000000..88e2b1f2ba9d3b8fa2670c916a0b4a060d81329d GIT binary patch literal 13353 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/16 b/tests/data/v3/cities.zarr/c/16 new file mode 100644 index 0000000000000000000000000000000000000000..703de528a78a887a89b07592b101fc3c47fe12bb GIT binary patch literal 13283 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 bB+ZX|(hvG!+HujUb#~#1{;ps=7{~a3w@>~@ literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/19 b/tests/data/v3/cities.zarr/c/19 new file mode 100644 index 0000000000000000000000000000000000000000..7e24a4f3cd8e72a4a6fd5c9dd6af94ac325cfd79 GIT binary patch literal 13626 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/2 b/tests/data/v3/cities.zarr/c/2 new file mode 100644 index 0000000000000000000000000000000000000000..586ba5f4541a1db7d83276c52987aa851e1f8749 GIT binary patch literal 12776 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/20 b/tests/data/v3/cities.zarr/c/20 new file mode 100644 index 0000000000000000000000000000000000000000..19336a682b88237bed5deac744f12c7cacc3b96b GIT binary patch literal 13293 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 XQv~Cg8ImqSsIb;Q&v0Rr!utOKb211a literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/21 b/tests/data/v3/cities.zarr/c/21 new file mode 100644 index 0000000000000000000000000000000000000000..3d27b331a3b6dc8020f7a537f4467e6bd621e7e1 GIT binary patch literal 13595 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/23 b/tests/data/v3/cities.zarr/c/23 new file mode 100644 index 0000000000000000000000000000000000000000..99fa74b5829664c3f2001924b80d9a4ac8cb0324 GIT binary patch literal 13599 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/24 b/tests/data/v3/cities.zarr/c/24 new file mode 100644 index 0000000000000000000000000000000000000000..1c2b82693e0a3d5a3dd5b8d5182f13db84b1e268 GIT binary patch literal 13532 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 Rcnx)0|In8hCa<`D{Wr_#SMC4+ literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/25 b/tests/data/v3/cities.zarr/c/25 new file mode 100644 index 0000000000000000000000000000000000000000..1a150efc69db5ca698aaba97be740f9f710e8656 GIT binary patch literal 13451 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 E0V?G@Pyhe` literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/26 b/tests/data/v3/cities.zarr/c/26 new file mode 100644 index 0000000000000000000000000000000000000000..54beb664dc6da5fbfbda226956dab683cad3fa4e GIT binary patch literal 13650 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�-fgJF5foW@V>tij+6jg!_=8oNRRVtD(8{(W6js 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/28 b/tests/data/v3/cities.zarr/c/28 new file mode 100644 index 0000000000000000000000000000000000000000..ac872e6c4443da495314de6ee6d4b98a9d8f1744 GIT binary patch literal 13814 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/29 b/tests/data/v3/cities.zarr/c/29 new file mode 100644 index 0000000000000000000000000000000000000000..a780160e2c403027c19f5d6552414706861683a0 GIT binary patch literal 13473 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 P3W;5&(Tk^Wy!ihCG;LW9 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/3 b/tests/data/v3/cities.zarr/c/3 new file mode 100644 index 0000000000000000000000000000000000000000..5cc0ad76dc4e58a23173d37b868c59726e5ed34c GIT binary patch literal 12750 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/30 b/tests/data/v3/cities.zarr/c/30 new file mode 100644 index 0000000000000000000000000000000000000000..4b53762cc53088deb92e1064de8420b014cd518e GIT binary patch literal 13598 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/32 b/tests/data/v3/cities.zarr/c/32 new file mode 100644 index 0000000000000000000000000000000000000000..39261e88d97a5e6758b94322ed977d8d68e37a3e GIT binary patch literal 13650 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 MEz-dc>0G-12Lh0uy8r+H literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/33 b/tests/data/v3/cities.zarr/c/33 new file mode 100644 index 0000000000000000000000000000000000000000..61d0859bfa06574aa642d22812e8c77b29d6d1df GIT binary patch literal 13760 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 WQN*(iMYJADZc@A9A?KR6`u_)C82^R< literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/35 b/tests/data/v3/cities.zarr/c/35 new file mode 100644 index 0000000000000000000000000000000000000000..296d0d0ebbfd096503d1c161e42c906cb5eea22a GIT binary patch literal 13718 zcmYM5%Wh*?mYx%^+n09x+84L&$$0_!9Y{(cMUa%3qC%08vrV2Yo~?7)%yW@~8kMRW z=tt8$n z-#_@`i{Ja=i!Uaw-mZLA_3^R!NBlhtTi1qv!l#?R{b}3tuReb{gf27l+< z=4Ah0x^>~JRU4b;clmzls<>+X{+I5b^Vg(}?<&8`&Rg%B-{Fhb+pTZ^F&{?FdT=ZM z5Bd1QH(s|c+Fc#nuG|HEdsX?glws`~r*GzN<#gR`EJDrl8mA|YL)!#BaZ<%^2H$I; zi#Bdr-$i}>#^pup?*1tsuOmyni*2tz=B~-x!*d<=$m`f_vgxC1HhScIXop-ME@RQT zf5C@^Yh5?!^6jS&tt@mJTpb5JXyPi@xX#)BCWCJ~En(EwE_apJwTn=Nb)m(~S$63v zU2y6j4$s@c9(L7*!*laTe7kf_*=M&t^qFh2+_%{|Pq$qM-@5F~b+-B!vFT#F@|_H` zaQ9JElqzS@YQRKKpX; ztUPLG7oo+uF66p!`gr(I9G>+aF5G{7a2692*R5UdJ6%6+ho*ZB>rxkuS^v!WTAwF8 zB)fF|AM0~$);{e2djH>JmdETmM7G=JW~jLiS)jydvvwy?eoI-LsyhRj#%>4*4}hhcfBwF72jPF4@I`D zLtoqlUzy)W4{g}UikDclclf<=TGcK0KRtOf2X-S|^zl^~@}ckwn46(3Lo!|G+V$a= z<-h0K>;13#!Z$0oEFlb?eS^#H^lrkv@_m5=?S+$qh}%XB zdEeKzp1C8mgL!q_#&wxpxT^X$y7%zBb2ZB@hnCP=7nUJowDaE_9LYe$2Dd!`Mq`Fo>76$FbeSjQwV(uBr*SC~JRtcy3*x)dM47 zC(B8}-W862j=PjMz#HxGh zca1i@a82VvSz_ZsFM7dR;?NiRZRz^{@D~`na<#prDvQ|1ho~PX3e;>=c|h14*T-g; z5uvfv%I3kfJ3a6XHp@xXgkjCSew+-|ozoLvcZy%oOjn*3t{JwiQ^G2Vi8`WjOAXi< z@{F2$M>wo}0&8-ado#g6kgcb#_Fw?Zlaam(4qI=`Q-RoOz`r1~gGG&RV-}k1cBuNH z^;*bzR93dpP;e4+zZuNdm#%pN*Yw0m)JkmGi~aZg{(H=+Te0SYZ`r-Y&+vf3T^^cL z?#^T9bXSV~i~auu7eD>1w+Fti1~AW_F>wucbiGyGF|oXBd2N%;W4-H&q1xF#ygaDt zt=CRvme0X`={K<_v=l+q9AHVGX3n*ZLDqTe9_&SDRW{1+!^-_jeqXY*p6DYR|Ncim zwTd?OZm7vndGt0A6@tY_dgBxn8%tY&N8+Tr{Wb6ptXNzP%xadYh^p5{r_q*&vsQJ4NxPFmswtMdu=g$wQgJn5x^zd zjj+ecfYIU{i%_oK{pTP?q?ueV|HNEYGQ2J=NTnVZO^o z@~7J!86?To`q;aNVua4n2aH@R?4}x&O95 z8sXg4dYSfg_;U{l!}?G>y?enLz%|HwzOm@JdU8h7%m84c>#~dR&>H8#nec>Ta|kkl zh)vY`5)O&F!uE;Pq*C$#VFtf zt=;Uu-+w1lym4wEWQy~lf|%OsZd}+k{GpYKY3yQek@NYp+&sq?Zo7qm^Tsx4Su!%2 zNt>5Y;DYuVqNj(?-KN@q%w?a8A?A@T&mk(9!!E*8dhI#wkup=Sy%=~rHnoU%JJLpr zCmiY;(^4$bQ`ui_%bZiRp5RrwceH~_5+=H2DibuRkg#HZPo*_OpV108Ze`{nrMhna zUhz4l?df~sQ`klS&|v6Q%mp7)zwx^&WXl3ZVJu=UuW4N~a%dK5OPzUq(enD8UVq`h ztoW!W6C{}R_~eVw41^faF4$Ry(qxn=agdS(i{1m|<^a<6F~-!m#A@iCN(*o5!zyAz zS$@uqUExXj!f=V-aB{QZXBm)EKy#oyjA0Kd#+y$c=rqh&?HbIjEW&P(Vp49y+YisQ zwAMcmZU9`fBf|}(rv>GY04cTgh1;=mzz3r9iZJ0!9VJDG7JC=^ut=)7y`<|JH^`#7*?H-Gbc;Pp%I6x_6airblT&n z_N1apF?pUX#8JxQ0gA+wo3{52nr=Afbul^ zzv{Ht8*0g?4`tzVL)r;CK?4XVP8HeX2#D7N;>!C{6%`E*98?odL0QMO638bO^j|MVa!Q6u$X)I6o8zN%oxXEjn)0t(mMA3i`#)7=xX zwA9qh{9~~0;&YV3m!N~DL@f(%>eJVBc58@l@M{_Jj2gE!=6K3Z%;8qzMapb#XgwHJ z%av}X2a^#slnF9yQHpBRKsc=V7DhViUqtn-KprmgP{(_19qq zg^-{%feu>sDqu#`h9;O|^Rnv(>ywu#R1cnD)&h`|=q!VESD<&xKUTO!GhVEUBE1+5Y6 zYyX?S`Ol)hKz`9GEf!oziwTmlJX#^co>5y3O_W`3_TO*Cw=b!w$G%eC1I@H`0Lxw8 zpi<8P0ZU4%24xZS3lu%!;=Ghxww^qup5RP!75R+7f)*hbAGC>WwyzS`8 zj}Z>*d63CZiPT_K>U3DS(sobtP~OEPrA@7+y4Uyc2m0(Ik(|Kpk?WP-W^Rza4c{D> zqD{VvZK)SfBcWKjf8l!kOD8C9aSeb|^YY|45F?svM<54pP|jUR`pRCA&epXnhL3&Z zSMgn6+2ypMZr7QP^FvIH5PzH++sY`(wR@`KMqaT%7K}HWmD(PI!in4Fo+*H(Ipq#k zw22Rg?*w-@p?iXzbr1V*)A-?HKskOJd+lpJ^s%%WKyht-oxN;;cI}hK4f(!s#wbLz zq(%q|LGZT2u&s>+v_K`i5ewSP#?hdK2Lq-T@$i%5l;InbA8&VEEcfrT`Tqa(sk0&0 z{W`WuL|R6iS6S@N33+3`a>Co=5|b@a8oJKxHjYooBesvxws3>-&6%fhVR1&PUm>)k zzt7-dwUbi*n_vIjJ$6XO6{z!@U%$sthYvefnD?pXJ6FbhAXTk)i^IDrNlT}`_$D?Y z83|?>rK}TcJ8j`PrFXQbc_t zrfzIq9=!1o#jUue9&$^pSlqlK`K9_GKq)kqK?!WZFI~$PCCHR4VtB6{93wXFR))uO zC9NWdS9iUrkTT@dbqK638iW)Z9;K+bSZ(f zgCX7$I)GAek6E=kRbNWa?qunXayf$mHPo`>C2R$8S?hd5mz87yzz)hH?rzEi!V}?I zqU0z7navcR^~iC6-4mR;op}%SPgjiYvsWbbolo1EZzRFF$7{xS87$h%;4| zI#_juPs3~I8z&Ua2XI0UyRNi>3;q7V2d8IV*ABtnUShdWCjXbplXY)t_?^FFtT z8=2;m+ZnL@5-uU z5yE5m_XYki#=h)ZE{GLO;iXVQZJ1 zwUmi~6i2qIeR}Je5nA7p`Zs+yi;wWX1(Drt==i&b`Z)5>^wHoVq&#_0tDp zQb>g+Q4UQn8AQ&S0tMFfTCB!TKL#KzZ2TjUtuN0Bj8#%G7O^@!TUd+-f}I%&7Sq*G zQNrB}gZv(CR~M`R_9Ff{iz`F3*^obmO-Cnd%ISqj`eQ~CFn6^?uqW)_->0GVsoSvV zB$X_;-nDnQO$Iy-l)?V3Z zJ}Kl8`YFXKPI_pPq+mf5Q1>jTIuwRx{6419& zTN{Pw4J~T_gsGp7+WtA}2P&;P7P?@_1%*6w!yOiCvKI&-ptPBOpv5r4dE>)77Mr3* z=|L7|8jxB#Q&V8h-elKvpoMLl0l*~RCIv9jn+sQ?{#yZ(h<@X?{Aq%zvyFtHEqr}=UJ<$;WI3%EiZ{(sETq}{xI^q zjM?Sj?YIQ&jcTo4l^(z$XfUT&v|U2RD_6Sv&$!NC2IXr(K*oD$47G;IfCHcq7L>8g zsXv5-9bB0yb0nqLCl^fZ`GvaJ2-$ButA@jpuBgi*luy=V@{)|UzN7Oy$|67Fp?Ha#gWcrUP%-+ycfr5^E8*7KGCE0|Ly+H7yI{ZzyGLrj_D|msD0t+ zCXdWaT+vd1BU^fkb%P7e&OFe{rrDQcm|^~(U_QieQ}~!R{i5+F6i;SxA$90eu&dea zgbecQi~Yx1JwkGdv@LTfqFO87sXxXro*8n60mg`PK$bB#sDDFm`*}+{m5$X+G%~16 z!ni7#@2rbYKRH2JNypVjjkAQj@aRtTy)x~T`3dWVacTwU^bduxni-9rMmHHc6ez&k zO|-dIM{$Q?>_M+kUI2{LCQ5d2xfXoeYCyZ%TVu?LwmbAY2(YD zu6JrE?cKwUrfwGvW=BmP?$faQEj#3FNBG8~&8=3d9j+EaQx6_(K~!u?=68lW2!|kJ zPtqEA#f^FfBZ)EWY=jR!to?Sa%1I9l2{Pem(gxxubjpjQBhpCI%0L_JN!LT(5(0{9 zHM!XB^H~w&DKx5JSPu;Aa2BITS8aJ3xWy?E#+vyN@w5xIXR2#kliqH8HsWL%BTl`V z`XyBvHuGC_KP3d3{O~L@C7~2y(jr7;X*?7|Eo8OP+-(p$3)bs{am+K=KD3ecL3M8J zx~*;Jb+Z|;nC?r6HP4PlaCMFm|{BXJ;6->Owz6C(05E0@LfTrC{GAl=C)`vf7>@16R zesu@JnUwP0N zky5Um%p(o^BEr7(=Gz^*RomMZ2^;3*cWJt^Kw*7A?$Sj!`(M@j?@cbd2C7Z10v;#< z_*#xxQg>5k+(?U|*U)X(?v9ScR)59W8B4*X1aEOl1@TQFa#kQ>9L&95!o7B8WRp79 zbHAQMU+(fMNeqBh>DpsQeH}bqvkJE11pRff|JH_5qMK7sVYacc{5+$pP+Zf`A|W>Z zotBU??@SS^7co1A-oE9)8IzlQ zkk>9Dq-KDnJfltC6tvhTP;+5!XS^61IU5GDygTfqg^Xigh0>1RO^c+0S-pe7sTtDQ zMZJu^4g=B{Yx`#_dV}Lk^qTf49m_pK_pi5R9#t`h2g#yzRqj!nK}|!K`9WYhclVS? zv&I-9h_+B0fiSq0K3wg9F>Qp?)f6%<;`;EsDI%}a3YR*unHR`U&VFQcAF7hJNk=nr z7h_Q)znqzASJG4yj{%0LeALqv_#`WlZ?g9Jj)F#q*nav?$7v(oHdU26gxM?)V2R^M zUF17$Xu?3LPbV(5ybA>Qcho;CGDtd44D#*1b20a9WV2e?o=?0xvL6xVCeH z8EvFld29GFAJ)TW{|n{KSAn4|oN_SAN>wU`j+VEU1JW!8E<)K&U_svblTp6^d1Z%L z#Blj{!-LZtlD{t_9nYeO-iprBZ5wAAon#p9We`#L6HY6isOVWg{EC(l48m>e-n=zb zgB=aTtexvhEG0|KIgxD^m;c85U)Slh%Y^tKq8Q^SO6bxVdxqmdZj?%-6=S_jhFWt>KOR4HUc81qm&FEyTF4|tkVfPgRfBo zIoCs}UE}$gg{D~p7&1(5WXVl{3$wF1|_)kOsN5=g^KO-G*eMA%W>z z%gBk>)X6R=$;S@wtuyMRxe0 zJ^I2Wv)$kvHQ zEq1c5`#o1jdd7yUgEo@0@d30QT|RG|Yg0Vr^pl>A>LVw=7Wn z7q(^sYf>A}A30r_TbUBD&sCAK%gu3`aCqkIpe!iwpjSEgdr1g#~3EI7=mCA{2n zEKI$a1r^<*rr=ugEDjtBFR<9NWegV zN`TM{sUbk6>@J`yl}c#}1r>zp-Z`WHN6%`0hQ4zxGSxy?XOeVxbF=$6_uONafAN>T z`s(k0_0?AwVYPoOvstK?s|P;0zrl~gSTAGao8WZInQP{C>=s3K=EHK;Hg-!L@?{n4 zcJ+6;FVsrH=X+fetcQ^d04ETzWzsiId)B(UHPhw zRd&X}rK|GhuW;e~!L|M`>-OEY`fL1s=)yX*QSW`_niX^98I3m$g>UD+Uj9|xz0_Fx z`DFiZO8@jt8~(cPam~{o?|)wC>5(g4$Qh#jdwjcy_bwZ|+EsTx)UMIpBj30=bN*X= zzHw#gx7i7QHG08y|9;*%eL7mXuCz}lHh;}}9J;wJ5?i{mtCmjVy^1k^aCNTFCqYXL z?jP%JpB=8;dLFBT6W=X;9eG!p0S=MX{x)|F%5389*6z=L`k&oOmY7CnyL4uht`2Sg z>F@CCVd!#~A6&(}4$Js2_+{4rGG-^-y9mu9W_e^BzODFKhP@1Po82%pE8Mm+kFH!D z3;wXtr+g@a)9BM!wLZ&THgxsE$=zp9U+1oEI$W_1yY6rD)5L8Xt!U#*-7%=^80@{* zVOe|G?1fukeYuBlXluW6)j#CRDaNUMeIHnVcEyBOvC&&Eh^CgwtT&iH=oQy|$gup% z&9(klv0M^CGW|{ezw1uFUAfS#ee;L9>`H>ib_|E2o<cwFE5(igh zVzpR>G-nb!8+%@z67B4vyuFQeo@>_AZN1wryi(*UE*37E)`7T`kB07Dz)%g&O;J%* zK07Xa88olq{&88S9BKauGq=9hbSAEi*^RGrFUL&-erEy9DQxzSrC-amvzCC-kfX@( z_dr0h^f?P@;xcL>6W_MoM$fpgb!*LUw12GceOtMuvy`4HqFhB9x>AX7?lzrfHgj{= z)f)d0XU=QahG>qyimYJ1l~Ha8QIf!88sF-njz9{?Q#pA)2^(7 zrZEnHRJ+Z_uJC#Zes}fOVqS~8Uit2wK9Hp4Rw;70BBPn8ne8QO4+hLv;ojHTalSM+ zpLGDh2Qx~yCS8T(K@Pfbb>LwQGy=eYxMkEdhJIW5gEQYX&fao^U+#TXb^m~iGeY`B zZ2Aw`ahdgB=Ol#|`P#cYnlmpKL`R(avW%MR(3LBv2Tw?m?2ts&rR+jAK`)!RJeMhB zOluypqQaH^`(`I=USp+Nz;xO$@fQs?$IUR#mV`64A2DmAk#gODQgoyV@6yipZqb z=4u6FW)5F^{qO>m3hQi8deBTUd(!!;;qm0?S6JU1WOZ~-&;`BZhA-n@ew4oOsRaX9i}9aDjz9@nw|wf)RJKk0vAl?Z5i zTc`5f`sJ$b|7YbjiJwI35))}P?Ew|1jO*Ci8&1fCH92mea(*B6MJYI_3U^Bt%dQDi zKIK~>vYrL-$(=ej7VAS_#yL*4xUUEkH#bO}0e{@A@Hq#B^Q~TePEfM=aBq*|ZCCqS zGr$eQl92a?z(cPWaBatMHf4RS@Sxp0^L!0WG%O%p;u+dx%RnE+oGU zpejrWps~5~sSGP85|CCUJ4BmcdgUEP+3|lR*MRZ;dhWlxbmexs_deS56`+h!mQ)ig zcCJ@nM5vQit2`J~Ef?0zY-e`XaBjWTJn%e)xUtWJH4x2}&yP^t8KN-P4lz`21;bW6 zw+AlQuDt_76*V(h4FRMvU#{2L(B%fK!|rVnff`rK6{G&cegDBsLzTOac~htW|L*^M z*A-768EyYDJKcZW?H}jL?-%ZFVF5%fssUrS5@22|V`q@%Z{1pB33P?Vm41B%0$BAX zClx=W_b~P!WK(KHweW3xsI)?`KePv@vH!)rS8Sb;4q}0u4OnFuHWCfW&0^C41}9`Y zaw`z4RI0vk#xt+>k88J}-e5b+wPX)cduiZ##s`fJx#(oOIW6#Ke3ctw~tv1XnuQ;R<-V{JrJE1bn5cB|O+9~2jtAbzu0 z0ZjcjE0R~!dRdb!5BBa^Tyx#G<_=e@Jfuo{2&H=Zrc8x?|I@l_ORFWYA72TI20#ta z1KSF%)uoB6I$vflYgKEFck0S}g6X+cqh(?sBUn@wyC#a?G)347?1r&){r9mEEjc9g z+nV&Y)GFN_vn9B7+awg?230n3+!UGu24m*A0i_yZR8}MA+pY?V)fok-#oJF`AK*C% zs4I*aT`@bm_pEC?QJ`?gSAJq|DyLnvTR^?3X?^GnpRg}Rm={54~$i*(#lm^&q=&3xK z>UF(!_X%iDS(z`vZIV%OJ0RL%QM&_}A;D8LWYlW^F|CR!1Kt?FpM|>PK9Rqtk5+_O zguJiuM;+e3?X(;+tzqz#Wir_rX##69^Bog?anV&OA5^+FxFz(Tu)JobRxxp->zwMG z1{-a6+wx$vr4@69EII@31R-Z2$6BU6t_#?-xubTA(i;+vsFimX3X`s`T$j0Z#*?>D z@Wy*Re^SSlQOt|Z)7~8?zByqz?TWzHyrFP(Xvu!eiUMMlU8?^A^n-s*fauEvF?AV zSr7XUsS49B?Luv^aDfdCMh#qC(L@}WkPGfrDvxE!n*chOLQa5Kw~jqUv1%aKGTuNf zOR=&D^EGqn)b$QA=^{i@&xpz>vc`xj6&zC%aYJS#?5zQh${LyJ>bHOTyI=p8MK*?l zTWrXqRjy?YWQ99DeLkml1FE+AX_pgHHh&C~y#~Dv1!g2Y2=ere-34Xbf3#A26Yku0 z|Hx9T2Fx~|nqd}w5%9Z}4q8i^T>ij1diBe7SC^d`w8WW<&dL%USU5O@;R&K%@HfM* zzqAIr`+`+u!!D%&rFRvxqpqYCvPPRWgUqys;Uyn%;^X-0^L@vPCK&2I+K^{+FG$e|G(Po zoELAwJ(bEggc~G%0z}_0yy)_a=-zg)Q}l`Z=RRx}I~uqN%p?sPyD~QoV^D^>{s*mp z6nACJyRG8+P+$#8)Rc0yzu%`m-Ibs$C@eaC8j0B6ZR1NluD))w=?eZ*v`ugOZ*Oly5i70qipG{k%`kcB_Foq+ zyN(+wlrADDfFreK4wZDV!b>T0Cax7dkRz}C0>09}uhj2M`MZ;-LTQ6l$|iPCY~xak z&;3>r#``%nSh~Rx7+^)*p7h(@Sprisp+JD=m~*?%)y6Jcw1+ zb^l&KGyo+nTu&j{wUHd)9Li14DRc3Kwx|DKcJ(<_vodAvI_yGr5t`DtK6=#vyMR0z zbLtlT2dxGBBEFdkPQx-ggZD%OsUgZ3K2VyR?H}8i%MvLZ>Ge^Dbmf`^Nc?q*I9Udg zNOo7T^esJ(SeFPWH413VtP#}>Z&o?kMY1V-6ezIug15o<6go4c2y@VbF<0wmhf!4@ zfYdT+^G%VE8BYmnEjpMn#+D$#>ds*0-x_?fDjJkBRU_`KpNR=`aAuBCgwkoS5y68} zLbCX>*hR~#CP z7B4na#dkFKc~VZEe(mS;ZvS3py9&4x1Uav&qV~a<%pvcT_@QCMO(=r=p_dNUbht<@ zwKD^njyv>F=28~EeET-y|9Z(EiPHWAP?BnE1qBgt8NE$@aHlp4MXn5*#38p-F1y4* z0CjF*OD(1bK&o5C51MjCXJrM`#g%m@$Lg(AeRRVczjo_nHLN75p_MkLOQOvP5c&wA zr8`;5mTx-h+%k!}bh8vIEl^CVTLKW3i9=9t7>$tj3_O*rn9bgHlsOGHaqG9lx!DEH zE(X#(SSLF}-zCWbO&XAt2SrM_3lx~v<*m$h+W%0CbCjsW&&5&N?Tg5t8g$l`^p7lj z|Gi#!2-8NRQ9c}X=qr|PSAc!$R}z>50TQ%L6U6L|qUb29FKqfxWia^$QtWD(eXPu* zajkBtVze_d%ZQ6`h^XN%#n%&)e?ixboUD1EyVYI)Nl}IL8z`H3_^cw$Jf(uCGcp)F z)!U(pP(pYsrqtX|ae+4?C=q_^vhmiHTMJTL&S-?Jo||^1q`WWOW(a%6U5ijEERl}L zgtql&e%d)+@+EYbEEiy}CVgF2zRjKm9j$P{%!rz27X}&$s|Iw=Zk;;=r7QGpVpE<& z7WRgPhp1MKW}t`X376IJuwFMIkS$IG^2ooL4Ss*1dvZlzUNnvmSLT4w6Is_TL zZH4X!g28&}F?=NdGl0JYy}j zz^g8Y>mYkbk`v<>H(F9jGKk&;c0=ZsrjQKluBt@TPucvbpH~a=(X?B7-GGd13#tKP z2#ufOYYce@GZcVhmrXlAXTj{2;{9T zY{Zc$C34AGY4Jk}|N|P9{aHLW>VT?cg5)XG@D1|n2{uP&G@E2Gp zO;5WXO!E-WFlPFqQe;lPqVhcWVBIIL^wqqp&6>xHB9$$@cC?~LPHokz6-1~^fPDtp z$iWt&##!9z_1T4B*K{B81Z^AXYLY&&40%%$rGsq{ogN4JL2^0@+Rf zBTB5b6*O+3ek$4UQYtFAV3Ns@PGm(nkkh992O0{q#WWUlO~@`=E1vZ6B{;%Fjm2Q? z^aP}bzUx_)=4mpogh+m8u0$o zC2I3bZVp$!`}HrgasPf{6Q-4^=-F*Y<&eO!Emd}VbACpdQ~a~7R_$O~hCHft?9D+$ z6jo!mqNq$dHsDZF6#}Q-{_%_2eU5y;h8Uze5NaxQc18*1H{s-+z-t^9{in)uBpZ}# zsTg|clhmpHCqyp1rtT%y>(W{_prJ`LicunGH zzie*b#%#nuEU3I{yumo683X?dzo6gH8(twBWgXm-YU9XAWe=tUvaKjGIopQT^E0Ui z1C&xQoLI`O%bp~$0wFqa2{vDddrNR%-5Mo*fh+s}u?sLqt#?dVEc+iTE8)tZy#GyY zoPn3I!GSfB+=I{#s`7_r>i#1$)~7Q7tL!uq+Yg~G$QI$(p%XS|$kX1uG(^9n8p}XO zlrhMT?XtWkCDouMJw>J98IEXv%$l=UVcq(f1oyvy+;?c>l=jbR5-8ou5yxfNvB%N^ zIuDgnGWGed*(OrMuV0XZmbBlwVryeOR7x~tOO!XM^z=ZD&60??4J3(XJVnjec9rdR zU2>YE%-aX6Jz6#(L~85c^%K;J5 z(U?(~19GTdcueX^!_ts&HU9TD$pdF`zO{M|A%AxHm_1=E&8}!`bkcU^;j09^IamQr z6sETH2q^-UqtHv(fxT6IQ^H<$G&!Ii_@}v2b|5MQ+pRek+7ABK=4JS~uWGgxbp_Qg zb}Z0z1{-7MmtC;K3lm4%+kXc=J1D_{12#i;dhHAu8pt#8Y3#ErnvliYgORUST@vkP zC|;12HF=z7W7{3>?f#=_To&TQl5`4DAQd;49oF3rY6WlFSbz@Rg?IgX1t?NJcS~(~ zb}R-l7kF2^&NlRRWTueFN(;M38S*`~LJLJQYaz3(mFZz9D*UY>uR~=U zPH!Gaq0sI${MGj9a}i%7GPq9$++zx%F*SY#e05IcHQ1C(c~AV{^s6-4_n7A(f^*o>l`IjS9_8fxpr7NJbM(0I{ym|UxUHjr_TTp3 zDntfGd7f>Ds7S>##DeGSAE7A`QN_R-J!Ivw)2@E{sI?CX(KhYV&Q|dOwfR9m57zLN z=JS485_D8f(LP%3nrM?_;rc(Lg)m&|)D|kf6vA9GpB{0{Dk(1JMk{MZ*J&^ae!b_A z!z3^)tpM^;qy~G|{|JHt946pghuhQ~4P6`eUt=jfC;&w`)n3r$Ly{bp3kFN#POK|5 z+Q!a}yrLo}Ws*J27Jc3natJ`>t$Q`aE5cioz>OjuBGY zdjFr;@6uhPjvOU3ooP}qYSU^hi~1k?Zz{^YrY|miM71To9h$D+<$V? zvn4ZVW>jeU9A7GMuhZsK4u^DxQdh|Gtlg~@OjAax^wqNo5G*yinzL5)6=q3`Zg6hjyZYS#EtxlHcks*5$ z&M@Ur4fj^Rv2g%}kcHs)5vq!;p@m$qiH!Qu((0!Ahj`wsW3=WC%>#9$u(w~{LwY1n zYm7ma9lH4n#o02^-4QyNLIX~I`=`HsFP&H&xowWB9x`jABt%$v#(_iNbIGJM-o&wY zyA$ta8miLY_3!ohwL`H(IoI;W&=2c~ts7-p7hw^l;wif& zLD4?zc%b6y6gX#NMb@eLprNjTKKhAVbJzAn{~bpG4|GmQu!rEFEIk7WI6SAokegdV zgUQIA5*APsB+2B41V@U3+GJ%YPsa`dCl*NI*jY>RhErgWUK*rY=?GgzwQ1JG@VVCPtF zO|#0*&o%c$Vu_Y$|H$DDQ!vpU^KGh)3i9(BI-p@jPyeym`K2=DG?v}`;3&>H%=h$f zHSM7TF{nWncpLEtPvdPl#Kx^+9X-TiyzcJ$k0N$Q1dC2N5c18JVos+LHW5)!uZFth zmKcN~`>OwxpR7_F>E>i6{nAwgeV3h?M|Y75&%D@aU^`GS&;~YNbV7mskvsRmQ3Fl- zkWFjSy_97@aUE5?kaEusuP(nKcRBwMRRsoJYtd+O`O2|FB9ow1>7<8l<|Q0~_>@3a z=Xt2k`u&xuEa?>B1(UEtP|PiD->BdrmKJF@VM7!q1xc%aId}ah4M7Fsv_fS^R`8Z& zGWaafM{AbUVjY2^EOG=QH(3m|3f$KWXZHVdxpEdCd_CW?51?%t zv$|*d@b{jTOX+W04n5(V=L_1+SM*R}DG-!-5ZI2^a4tb!zd%uJX!GcP^u(#72F)!1 z$cG}8fm3!15a`*;GO&bk?9_@_vrU1W*6{P7^keBa@fbUh2Xn2MN`9>m^6f z#?o2rU!gVI+pYt8gHchGJaf(!Gk@S@wVoKE%<)wprL>R$`uK*wfjLF%IjUm<8wyLS zO+ndAs#C^xP9C1#7nbDxBLR=V_(uX2#4HWd|7;Gf*~Sn=Ggr|dMOd*6O4oshmM$92 z?sPDoGc9k_A(GvELrR>f}_^jf{nb32-dv+kY1hmcwHyv#Waw5#? zEr(QZ&MEy$8xOqZ5Pm@|wk3*GQxX=1EO|&ErY#^z{5r1%_qJ)$eY6Kt`qi3@?odD| zMYc5o@)v+I@UH==<}vLPKvZiee#m~pMoP=sy3$I(OP!$j%#LOdLs{3jn%EI@`>c7V zy%E(lgaM{u(^K|aoQ%TULXHlShrX26~*trZ$FAL6u4U1LD1)P@AXn% zdirlgyuEYK+qCnbO(q7;6$U5djnk&PvF#bhu$wkKJLjdcu>YmT8_|T|^JiWI{u7`- zMh=-7C}Mv*ML9$uaL>(+Wb+0eD#)Hs`6|=cz{=A(i8nD(Wb6^zaW>kpASyS(PK_s&}MX^fRpgZ3C9?Ms`Lcio*4Ul6iU~q$O}6LV!{Vp60&a@?Ib>HvWM}($SB&O#(m@BTd22! zXcTr-BhExVAF{JUFBz>PQh;8kT?8E$H&BYUw4<(`cLoH{r$0;$;M4^o7aWIbE5iUIkDNCYo5B~&~tX-^{ehNAHn!6OKb)NRH0EF^XLVn^ep z5IF4|u=|`h*DGSi@?eAxpu$Xk+|n4Kl#+_c)=Ma{+5aW#pjGK3?7$K|hwo$#qVKrj hA7M%0l?f-2)8NMTuj%Ag@XSyyj9wkF+d@i({|jNY*;fDn literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/37 b/tests/data/v3/cities.zarr/c/37 new file mode 100644 index 0000000000000000000000000000000000000000..bb2629417c074ef33bbb80d11927e8f4bf2595cc GIT binary patch literal 13888 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 ILs5tS3z^04vH$=8 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/38 b/tests/data/v3/cities.zarr/c/38 new file mode 100644 index 0000000000000000000000000000000000000000..9f6954519527a446bb9ee49ed58f64df875a8e21 GIT binary patch literal 13909 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 H2`>I0gq8z) literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/39 b/tests/data/v3/cities.zarr/c/39 new file mode 100644 index 0000000000000000000000000000000000000000..23f36cd3f377faa8cf22ec5274caea80635a16c7 GIT binary patch literal 13990 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 H+p7K_x~(wI literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/4 b/tests/data/v3/cities.zarr/c/4 new file mode 100644 index 0000000000000000000000000000000000000000..cee2f5c6c23e6cf5545b7f27a351453b4b4f6153 GIT binary patch literal 12609 zcmYkCTaRN|nwCp~0fy7Szzhh4*mqnd|G{=d#=&-^JM7Bp%)4dp74`~m@8#Twh_tUd zG#YW!Ef|%ip#f2y-Gh2^VhX4!R2o$`SKEQaZLXYjn=ax^W~!Lbi(f9LQ0-nIVk zU;E^fzxBx{pG;#B%B3&=2EWI?TKN2T_%#V-?l!SpxpL{|?r(DG-0kjNmH(FB*%qPs zZGMd0jobOA@p}7haFuKH>nv2=*X7^g*EsClI+pSF=|_K$KM()zyI+1@F0=FRe)*q* z7MNAtPqWvdTEegCDe*1oR0k9lmDu}y-u&B~X#))|C0cX`%ZwB1+kA93L_ z@@zJCRrjUKPWL|Sodg-Ko_CorK$HKD`XLVd?%pmnN=3I)ogD(wTUDtk{{iH8E@g*a;orM5uYx@@a0T|#^{Fed z^|JNF@AB7Q{op6{i*sK!k!+hs9IAEBD(o{aysfk0i#PG;&C_ZhO2SNcu08M6^R`+_ zl`|i!rI%nVS9)2nj}pq8F>-{g?^dqOvF_jH(qPs7tj&~pf3(owQ-9~HnBRr6UTd`; z*S35D@$+jcX*f=Z7nIU(D&`CrDRC6 zx6YL~(1P@gjM=za&!3hSx>|JTHbi1dgd$2GLL`=YYKjI`ZH^eMT&zhLd!zf<#8$z4 z9b%TdI(t<&npKAP_^;U-pQROK^yOrYwa@3~_4^f=eIPKd;qTv^S1QCM)D ztzva}SREd9W8~KUKE_-T+5hg_4~uLRx<706^1y9ft?PsDzWun#Cadqh{kgfY*!X7W z@9MR@(JS!(yZrEwA0BtkYTWCP2ei?%L>R$Tu45y~uHyZYWYW)TcjwELV!bLOHT9g{ z=u5c$EiWl6X5@9{*0ou180w%$PHTe56_`fS4RHZR8iZ!2vUwgWswS0XSG#*XJ&o-; z>#eY${yK5t-j#?KOvP295DyKl3hfa&^v@JIFd+Lh4;?x9` zdcJm*T!*>$$=+IK(QeHKh;1#VGUGh`xdd41qvv>MqeUhz_tl&-s;h6gYXzoX9sYod zs)bc3gVoPzTb8>!SEMp&HGUM^kissn{3ckLyr$|^^ck%_42>_>J~@9z)4P8zd!%}P z_^L`;UAY41K;!F_Q>X_6?Tc0pW7&@128wIcf}=I$T!e`-O4N-Dn- zSCy{&GL@g+W*%rPi1RObYV3%Vl1hxzri4wXGg2h`blniN&Llnw^Qy)1D~Uf>62+QbzxyYloN1q9t9>-+pBR=FblwI`skm~PRGLPnx?zi74edGyup z>E9kJ+i0<$su-m`ohy~6s@y}36??e)%VC*5MX<(D1-FMoS4??{lh~+SP&77 z?9<;haU%&-BWd1NN-kri`{s3OqFaP$)$ihcQ#k6%!t76kxx0ijhOPo%==m#umn4!~ zh80m&Vr^`rkDatlZ2^Mz3mR-;CG%6C{5cADYSMDnjJ_ZRUmrdK3Ep&nW}ioNth7Ex z%y}$|ovMOX#{S!=PflW6$Bag>to%RV?;}7NtYXnNe&J+-^X@NPHd}Q+Em!(TkA@V1 z9tw$pc4S2#3!dY9GH_nt7JO0(1WNov`M!QtfE;f$Puns$P_m1qXtRcR7 zFMp{FQ8x11%oo`(+}t>;9cSHNWP|R1rh0Q)<$hBH$$b{fxyxVpY)D)@Jz5b_6?5g` z;aw=~>&oheUY~4xcq}aD&Pkq@i>6xT+C}$)hNu9!*w*(Ubvqil%7A5L-ya?Tl(Ni- zAjyU+bY3Y}`h20N?_G9D7burO$@(w2-h28Yd-^Z!z1w~Fmmh&ft7DqzuR_5zb8W>7 zmVsmXm09+bW|!bqO>@aZZVppz&uA@G;cErZi2{tUX)P5`Y???K$sN4u|;4>NZI?9o;5!=`PDR>+{={c6|!YMpie z#;vX7q_ArbU*v9A9KO=?r)rhBM)C|RAlR+Ha|_*>xSA4KniKl;F@M(@_!&iVzWd5r z+=(y30)y!7nJX4jwzPsU3Bbxl>*>@cU@Uwd6*T8`7h-9HMR#svH!rqv9W320DLUmn ziKwi{LtcIDP)x;Bbu^GT`tb8K@%`BSG{_V@m4~}vPCEm{&)w4(hp+VM zD+kn02{x2kYjrvKo!$i;2CFKINtD~{+-)`t3rfmfi>IXr;;K>Z-A0$+(0NMPXd3)o z5SWUkZ|b!^^l1gkTW)uN?eO6nq;wL7iznZVQL85Q-r4|lGz$}Ak z9=4W|UDIPE=$IV8^F*uMG>Ob+$9~&x3Lvl!NddVP`3kpTBWyih-!Kb-yB!gMkJuTK`jKP(DBnQ%jaZUq{_xjd#AWF?( z6A)Nt%~#iZhcBwbSJLSUgjwCm3Q4EK{)2xw)kWK8TgaT^PT%p^7pQa6Cc7!6(wqVxo@ zEAOxT8kFug&Ea!>7yMgN5kT;A&DGQ6{_gN-8Q4=lY?P0>^Bj}%|43!8wsg@x;-$^#AP)|W;Om|vTB z`fVk0gxNuB4c$#e$6005mD{atdYgqU>A7{*7YbhyvCRv(3#y~V$@%~OA9OYWiRAQ+ zrLx>tYyOoH&}W{8%QvL-(lVa{4+@A?y;hu090M9cZ%jwv?IsuiA5dmDCi{uI_tw70 z4s#ULfSA3-{x(y6hLJ5eJ_C1p=7~6G^wXyY6ok@r{(dNLQ8Yyio?y(C!2QExCDmRQ z3{xd=EPG8^NnrUE<7Ff79E*rA_4bLYh~UG6a_uvkO4hI9M%`bspLm#yg?aG|12IV? z*epfT9C+gLR+3E$AJ+D(x8Z{ZwrTD#2KR`$v*4udfTLT(u9v&J?oaCVuITCX4WLL44SO|A^kAe*+tQy~@oSyaHJ=sd0b z;fuo3<7HefgZcNOqLaujy?LYnD=22sTyQ8ibt~Tpqw);>Bh@ycdh4 zuJ95*sd2Sjqaog|4QTIr?WJ1)&6;r6eUNHB=0tt(WqZpj`osv@h-#>x%xr4^3Je%K zLud3*{QeV*s(!nUi7_0y(5zfxf(u0$@}EQ?hBO#Z2X+7%8(-fXBN?Nw%)3m<}qkMGR7h%(2%DbQosP-LXf*~;z9!=oO-0ufdxQE=C78j)u3tq`t>P@Jw* z>QkNSiF`FceIAKM1^yuHt79L>!huFA8Ajx8nygTfv7wUNMlg>ZFi`+TVqAL-+U9A> z+X|XBVcr1kTdsWSt4!Lrl23kSHfQ8mn+^jo2$fQodxXoHEY&xrxY1>Axsz0#`Qmxb zWk6rWd~Nok+O!PamQUw|!=2USGllf3Esf(kw*fZH4-ZQ#T;qs=6AL|~gh4$U`*s!f zVXF#rMaO-Zt2@izGe&;d_hk#rf$p=yIMUF-Xd(+uVa!u9^l2%Uf21u)2+-!N$}_L4 zeCy#?g}SJ0_G(R)ytk#V2s7|+g;)Zj+Z3#YC=&D(TL`YE z2o@$|TI^CNSJx*M1uKa=g9j`5Eq&g^YAsun|9Q=XrNx-cDD2X*&rAi2l&mvyfH;Uj zJ<><4)&w7}+C7xZ?{jhHw^SJ$ZxzLS-b0y7?un;O=`*LrJWzQpaQguazccjOC*SrZ zz+KNwfTmAhG{#e%-TTV&i(*u}yB)DBpUk?Sl~@JtQ>$MQvbG@s=XhYaTp_>ZX4sps zQ`q1%T&px{j&&QYav^+xGCQ3t{7AC) zy=#RFOpKbRwXRkgRO%3>I}y8(F~|NEHYI-i|XKXH0#jOnGy1gdT1n5x}B;hv$K#-Z&2->LoBS znY!p zK2V;n?!L67I}6>1?AoQ^#wBz=S?9#h8O!>iLQ82la&&#-!}el&t!+j0l?!s~n4sDv z-7z6asW-0Mxv)>Y)^cNpv1c+sHz#gQkQ2e?{n$VXHm&mOk{aOCj>3$u*$~qQcdxhf zL_Iukl|iB3;rFbHp?-1AINSXxviuV+^~q+h`NBHDK*Cg^`!-3rTSu!!JhWzU9AK(a zciAM6a+XECE!#%yFqmu*yGZE@b#eo1Hf8TD5G2k8#g$zbi!ZndI6py2eQpQ|lQvsk z5q3*UA>bt-QFqV$hSsfB=>Ur+WYcZ;B|+30KKKl9HaFd+0i7+qK8$qSN?CTslDQBP zuO^CX%sycE(JCKByfHUFC$|n$?MKlum#Lc747qeP5j-*z$&32S! z$$9A(8*dv-Rz2CpO9KMQ%1|#0uxkk0P}sUtcnk<1#ohn`0UbwvYZ{7hS6Psa!8`n; zSI^v@p(atJ{i@ZRVjrisS_iy<-GS^eYsNd~6S?cGEdjYT^J;>C^Z~%XmV0J-WT2Vv zzP<_2y056v1}2DLH75G54WuxZf94b@z?7cT@&pY^*a3 zDc@|hnU2B~4i?Xd_Y=QL8p4ij`Knfi{G@x?H+FuWJk@L1!c0{~!y~~BT^c3y*fTJ= z?37~KB+6~tM1_83X{NMoY)rW(pw_OQ6EEdbMiG|)w@S1JqhH`_Qe{2S1dD^@b%1PG zbYH8GP{AEwS2#>P=KH{333;&uJe!thXdR<%QHZhoMKF#X1|N ze)BCu)lx*Tim#M?apobp3^!8|7S0gV_t@1mzB4q99vrL?hVl+YZ-3$!h7(qbq7+R& z+Y=X79*_sJRGJK*7SglSob(#+L_6EGq!p;BBPZFCz!CkuUt6RN&a(GN6$t<#+VPo!TL9v

{Mo+?;-2mJvdCqA4ON(bFfKvM!GCJ6A^SnKAMN?YVDpiJF===&0+5oZ} zt@4H~kvqGIuWZo2VACD9ExWI+taLxyWjYn0?+qCpH>tqj6|KaqE?sVYA4I z158gDocn5V_t~xeJyAS`AJFi32eN7!zq7t*p^RnrFZWZGM&oKlx z@bn-PndHjd#b6^LsS1>Ei93Z7kts%PYnZn0Y`6RK)Qj5rk!S%r9NWfgU+=e7_tAjM z^CquM_Y%KCl=FRc-~+Rhm_7Z+;_!!c;x(>! za{#MMb&=mZJ?h6S!k}7bq{WC^qC3lG1_Ou0W!hq)x)55l{ZvrYJ5%HB!Tpr=@3GRG zw$$(j#6>?sqI_lpp`UKN?af?+@GNJh-A86REA+1W)>;T%LHkwgtLK=WL>+o@Zd~jIG8*))InCS8Od2M;NU+qD8UD%>3P#BBbR}C!D(%b24pBIos^9 z<2F(bB(JwE&iY}FQ-b8YVs&}MYX6HXXeI^Dr`s)6##;Xy3}SJi>NR9qAtw#hD^9=# zVPoxfz=g828`PG3^Fu%YcJQN^N$hUCKAIWZ;5GWNsRC|1qkEX|X? zY*Of45Y12a>3+=KTzL9IzlVynqtIGxMAK2L8c>+3n4OcR$HO&lsqN3)jCN6juy+;* zZ`>N5MH=-~gA!m?{pLhSDw4D`rW>h+q=3<98|ibG1exAF{D5t{!{UHf7sDlOA#G&Y`Wz`6`Ca5c=_2N@euXSmt~V%bgrFjgw} zEa^BO<*n7e?0!`>iS}%L8l|^!Yr%E_#|x@lB$@&CkY-N=+?zQ@Zj-@I$cdA~Lt$Io zX5wa7xu-|z+^Z19z%LEwHXWuIw!mpNDGp!hiPyBb?oqD+4CZ;-Eu{32le*X^4T(@a z3Gqh=$bN6BR%QYKHQhRg2)P5ax$^1J2DgjI7OD&>cV4;jU2yb;W2@0A9j1}AIX@-t znv9i8b0J>EhqCmj`)MR*ZRUOkm{Rnjkwboc>*XewO7 zCC8gaQ6rhMc#{#CKp@i!@eoadiYYqdxKJv%7p32egOxBnpb&iHkz8OuB<<00e!}8h z^EU`hYkW#{Cw25*-!W7kGkF979gEb6<854vpD{eb{(v#HuS!rFC{I@BIEO4Gg?n43 zIJ{w2xrERw^X^OOe5y?uv(Gg=HHI)kYyzHMGa^~Zfy(ncIc&h7An_=rz!eb=3VHG` z-@SwD!~80m0MA=?hOH$s}Flh@@+q}dtQ?g`(u4G0&JeUvO-jlRuE)HRG zTr-s(Iv(ehXcuOwQP^+>&DI^jH$o~9Q5L_EV@Jzfo8W!BW{W);-c>EZCfS(-cZ@CI zC&_;9%2wRnGqSq$>H{(;&Wgu_m+N-kLgZOaY*sKD#-$8-ZLxx)*d899Pux|oLy1)# zup_G}PcKd7*zSWQp;VOgI;+pN&63zNFhh;Ds(Dgl#@-w(qD#|)jzDP{yygHNa@qv4 zVrv(5Ac3>0DUBcok7nnzCzvyFgcoE~B9Hs#G-(G#l7yH7DajU%9RzJoDZS;E&`h60 zIuiK=)Ya@_p>v*}7Kt(w5Gk3&6cpojZb`@MXeRMTI}D@UIa40Tm%&Iu!X|r@XemH8 z5H{xN1mDvSp_+e9Dn}|4XYJ&y*KCS_GrIq3N0BCNY1`)KcARc`c!1xu)SQV^nA^9t z6>mW=GfiFh-xHI|Rx4T>WCgDja8RW|!~wevwH?iX@!+^3YDssjsJaZ%*Xj(9)|%n9 zR3(5(<<1z&D}U4Y9d$vG@>XJ(l&eSpA{r%uyhGZUPyVh7?Z1P>3Kj52#LOhL+sn@ieI zYjkvZ5Su-)w;&X@Rv2|W^>|PfsxR=tu&SvziPhhO(dGXWozSCF zq+MV2@C$$eE)kMB9#P^dgSk=7f`iH57x!-6mPW6F#Bw9Xh<=&k74X-}+K`jq23>{` z-2=8(IKyq*OiXj9>WTwZoq&7hd^V69B2R`G0&q(IY*B_08(Osw=5IMUt{Cc3yY|WC zL7|{1`wfb4sC}-Ct+&HtHg)Vd+J059|H7?hWyHy=!Slt zh^6kw6EE5c3s?n~wRV+ZCa_s^FARWmD#8Yi?l9u359;kL}eeknb+}N>YrNY#6kV`xJ z@~RGR&;|9g2dxt3)|u#8zE0@8_ImW@+X!5&Hr^Xx0%ZYGz*i=quf>sTovS^JgX|Bp ML}_DK7N7e600B)3(*OVf literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/40 b/tests/data/v3/cities.zarr/c/40 new file mode 100644 index 0000000000000000000000000000000000000000..c2bc2b6dbe7d9f6aeb34361d937a88f3a64aae76 GIT binary patch literal 13891 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%0As& z)o64flz=Q|(9LN3X$ka({Jg=Tj(hbj{SGROTc$rEkvHNeY*_};KJL(lv7Ro(C~k*b1*d?~z9+<`^c20GLWMq=Z@`}v zRaQ8hg`B8sjEBC~ErVUz+8;fPP=MB9|Gj=HxxWBlwB)J!oVu@~H(yGIVrr`gZu#?% zonevR!q$yjjtTV+s$&3Ib^9MwYS|xHscytvwi_;C`|2@tra0Xr8S~Y#!bLki-trit fhlyrsL9|NEh6c%0Tnoz}mpHCIIp?nyT>t+8;TH3d literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/41 b/tests/data/v3/cities.zarr/c/41 new file mode 100644 index 0000000000000000000000000000000000000000..7cf3e0a8b1061543b0a254191ee321579236288b GIT binary patch literal 13699 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/42 b/tests/data/v3/cities.zarr/c/42 new file mode 100644 index 0000000000000000000000000000000000000000..9319880333aea849396b4bcde8a10ccf64053317 GIT binary patch literal 13649 zcmZ9T+ioMtmYy5%`D~Am@Co?>@;i`}N~$6$af@nIcijb6`SFx?jYq*Xx;UI#<_DOZ|mw+`?76ul|94b5}*Sp=XR- z)A*IEvOMbJzs5&*v485*`kNIN&~xv@D%NqEjN31luI*W|UG2-z>5`#q@^y@jwsH8q z<9Ahl{I1b$28~;@GF>xr%OY&tPOtA`wf6hH8GRo7);G1+S}{lITU~L{EsOXV%@wa3 z7k1B|8ryi^78}=fy!|cR$Twx5UGs>|CSCNe`E+>v)L`svhz*YKb$Q5U__=X?{a5uE z-}HLiv?of;w!^qAeeKs4S}_k>+}JwVbL`fEUAi;lk#HQeV#dny}TYAwH?>Ca9V>nFCVU;7 zCEYBrtiCZ5<3P7^vDW^laTTAxi7q%jVeabe);&01WjBk)J@&pz_dUK}^_APOWDA(V z`jI$q{sli7xwiAHwd-Qm#!ch0yRZy(l%3xAw%=$^y6=5h`nod*yzzC-GFyFhO@L-^ zdzZ~EKxT(;tBT#oLa*x_&vzlMvuPrZs=R{vhQ;LB|M~4d7!X(eUeCIWP2psNN$9%D zXSabs(=RWo?4sR-+S$8VMXs4~L9mE_}1H>kdEQa)X9JPYM)f zFk;lQp|-$gH9e^j%E;dJqc?cm85TjFyQ~Rld+uf9cU-zE%ow`)dB1Lgey3z4*!9^wg0ugS7ehjD z>U@SNNgcnD6UU(@jH_N@!WMU6J{y)Hz$Uj%=Z~x>8i?Lh>)7hb^TzM_x->i8y0$Q@Ug664q&rVp3+T7* zDRsJ;mVT2A-?ycG^m<(d!|@sDv9yaq*;}^X`)b3IdaDtUq*?il$X{BOy!FrDxIFB< zyf-9|Cc!U!y(f!hlzCqkt*=v+4xp=J&z3#v*`DQIk@W9ho5R7qp=@{3M)4T+GfAPb;Tn9ubp1GC+?(I&+jSh zd)+N%$!%;cT+gxVvhejv`@AQfeN`NO*!mQ|gNiUrAwKL&il-H|dr$GOl421M*C0b@ zAU6s1GFEjq3Y}rW)UDdi<>vFb-*I>I++Z8)Otnk<85MrJ%P7OG0o#57D`cVXYf zz6-Ut+J!lmvGLi^RW)!DVhYMt9jllH?x?udK89|W&AWdG(D%Wr>hD-(^y?H;YtrPObLFLA zJ?vbO0eqJfvc>Te?-cg9d;>AiDpV%VO84XzDQl||9&+|&-*ocP<>80&@Wo?aV9|a-rwd&jB`*khj5LuAZ zP8Ym!6_|9w?zsmQ_31g;v@ckw-cUh%NO5KObLELz`FI+D@zSWxtFZEJsY~Y=6z14j z(7eJ6>4Gmvm%bK;OaTBP;D$2i>`|l;OqY5sOMTu@1s>$snJb&1UkEbUHuT7Cok8}U zXEAAMGf#y7j1c{5fiFrh#6ki#S9%MHF+T=B?KLHYau&_vv!0lVu#hHJ3a)qHS_bIP zy_ZAY9)74EeA5;_tmQq?t;0vRl%=N;f&ztIzU1Q66~|BVPI0OWWAEyA(KnW7v+D2@ zU6) zEO^JRUJ|`LUF&<}Ld#DJ^S3rLW@#N+k15td8EPW+a}>AKxlO~R1|SMh1(og%^)HLw zGV@hm;o00(R++CsCyR_r*Y4vJWKG7K#YOP>K2UPx%GdRZCPhJU!S1$ttgae}_)~I; zNd=?hX>o4z5D+fH$yck@E~!%MYxHS_C56HA|+lcTyFR4%U1>|cx9!mZ&l z!D{KKi96`A1*>m;=h}a%8&Z>CgBPoihX{044!Q|}?F?Qn={Ljkhw}Jejl`h-UyO08Q-tRH7c0Htj+|^-O?92wZu2s zHoHE2+BU|`fM&K=D1y%W1&LEzrwdH(VxtVZqoKelZ3K7{o2JUQ+f(mxOOY;%u$38K z!4lk>g;jjvfV zbuRZA^#Aaq)y&TcGm-a%Ls%7(l(^MEjasN5$pX3lm6pqYTrG3IkT%Son=k;Ybud`i zW;C;v0psF%Ep#3l-Z+8c6`fJbV!0Juy9&yUQRs8X&Zu{N0XnbaQlHY``OuK2>Wf8U zY#Cxj!^V?sE5rJ@I9X`!3BE)M8yH1vorG30AWA@_wQ->9xZUkMouFIUj&ETkbw_>) z1n-`p&=%Jeki*B$8A{!{T}@vnUZLoi?8?LU+h*4q6Qb7ituv@4_((LGt*YPlf`DOY zo`3R-MSuK|vik57nFfC|$>FAjjyxn9dRg=>rCl*IjotARQ7e;QxE1ZvjH24vTew5} z6zbBO8bb9@PhZk-C2sFvBU<4v`mj_DSy{EXB)Ad=4kImk>U8Do+f<2Q)FQX-1 z$?8Muu9`z@WNE?@&6^xPtl|l=)`%vDKh;g9aTkG>jkVc>%9jvOLVBBBI!g_TbA&y8 z)rUm8$D#9H!E-A}A_{sUYOVNG?Y#`KS%DEbEiUitkm87M(0Q6uW5EN?X!orx(Go_m ze-oJ$W@8%ibzxh&0tAllceb(%Dkm|+Pj^_)7GNKV%FSF~X+^|^%U)H$JH*n^pH6N= zA=iCX$Z?5Y(!EmwOJT}+bNt=a@po%Ddl$}bs!l#f+G20n7@w@n^*+z$g)2-MGPXtL zX@Tc-1SoHMz)eqVzf{E6(3irUIrN9ET{t1~SKg|^9r{@rd!+!i(Hn^xJ2bS;vLbB5 zTB$o&6|8|E8hnKZQr}G(?oZwPh@?^f6X;iVoHIGU7Ln9? zht#>=8$-N<4jkXx(y3;ZZ$oY@P7SF#Nv+`m_iWwHH)oYUo4BQJfviATt%T3NaK+>C zJ=G64!eV5Fd9f@2#+}{jZFqz*`L&@Wu>h`6-IUt$&K0H?(>4F^wr+X+uGC&sA`_K3 zyM>%!#}``h@(a0e@RiGV=8sp6uqaqEPGF56$|m-;!U2+vJZVjpc0H6GS zTbRX_vfvh+=+HZ~g?F&u$KV!*^lxLcX`gVOzL^}~Z!qWaz3ea*k|hD}E&fckIgrLmz?7>?QvPK;*7diW>f#Pp2@1CtLg!+jrRo5wbHM%+ar1D8(|hw^@)f zcJ8TrRuHEyFNosW2>$W6tsgB($5e+eN>uS5^u!t4ZVum@Qa0vq2!33|BaG;15duOnfkT(e$ghl3^4P?Y^|$fwqj!$X-rBHR3}d zKH6==OIGwWQdYMEaIy z28iRm;%5SSch)`9%@m!BiAuumWcpT+QY_nX*udzYYT z9#$P}<%R7GQxh6XlVhQ?*`Qbh!*mb~De};0Yofc3MWe}zKx$b;B4y5sfveQS%R73D z6a>ijFP#}u6W9`*#R`J*!{L7?StHFLCc9QEnH|1Y4|RL^@X*;bfocdFMd_&eg_7bb zs+4s9*q%%$(3Xb}+1>LW_(j|vKFHU@Sa_1|TjNs~A*VY-dT_br;Ng$9j}|BcGL9|K zIsO}dqkYl2#r5F>%)n<=#G)1ADxlF?8ufmGTyh`$W13Jv9lJBo$GX=$PzB;_rCt~X zXFEeM08Os~t^(7J@bpPG=Mr32;0TrPiCIWa36{p?cZG!#B<0hazRS zZorUil?3s=GAA2GS-U2e+dBpus@HCliEXf4nozUJv5{LY zsp$4(u+BPI{h9%sFhVhQLr9`!<$Ax?2P1e2#o4XZg)4;K*>q)n_^9XK^(WM3w1YR1 zWMzX9k&znmm+=KH@QPX#Vc+{m5i}G<-rzBY5Hq4OHan%!>-z9v$(2)$VQOOs$gVV6 z#(y+jIQ*}g`qd|{f<#)F_s?ko7AHMq5?b^Lt@h3n#RRKi#=D*vhSsjE%uPd{Ga-rf zzv4P{wARB`D*~Aqzl1VkF2)nV6u5i7fW-8R;9gST<}sR-3WFf&B?cr%Q~_C9`y3K)tq#31dBe5<%8|#zC5%_Us61Nl8K?t@BOKKO*<3JyqmP z`n{#+*yS(>YxgJcM%Pq_UnhcdE`~%#Zxm9r=}p{I)-7~MkNk3$yi`eo6aw_~VqO4bx|9c3#(TTOMIGqozW}9=252 zT3{Z~VKPOoWh(x2zj+R#lmxQH*DSy<1+Jp@SdEVDFV!NJQn?ACd6O1_?(Ly*OsGOvtK~J^HJ*+rc zqcqYM)mg(|$##MCb$r@o=T8vK>@s)Hzhv^RC%vlt*9dXW=x0jlcN7#b((s-y>bO!n z$RMtaX?BT_OOusd1BEOx;Uz*H9I~?ZW^Q6EDMhtDb*v#j31f4l`zjXJJ5hr3sJNu_ zFn~RyGPGysK`ONje1$O0xKlB3jzJsq?a0ZZ%|o<%4B7nuvv%Y>XiP4%rLzhQYe6X8 z>>Upy^0eund4XBLg(kakr4ijR{Z%$_i^KoSP4`2ertfe0_bEQvQG=VJ$(#N`8ARI)K3n~-1l=%L&pjhVy6b70|!CcO+4?iBh^Ls7(@<>NbF%wPJ z#s_c*Z`}*U!^A@?NvCRPC{Fh2v*Um0nLRvOVSK$0y*2ZoIZrdrk0b>eip2`w$DyH` zQp$6S1C0wk+K2I)QGfU-Hl({=G585Gp^!urw1~ zX24&3Oh;wT=?HywA`8lSNr@R<7Lt^C71+y}oW^J}tjIA`jrC{=mp!D9h!iXhY5cKJ z3O#*!?Mer=G>&;07PLF&$O+Y^?AI2viqMM|NVjHEam{%P#s%PlP3SeRm8nriIjte* z&7W}ND#akY_Y~LHD3f%ZNt!cq{w2&%k#NQIDji8FC>}N%y9t#+r`Zs2r)*kQehDhA z4nINJw4J-de{WVjERWHKk&=0Q2w{8TK!*>gA!`e}D^n(|cFla(TWI{*Z+;C*Xm>y^ zl|qKmyZN3OVn;t{x%m?8Pd26dLZ@t)kuF_ViM6Ob0WGuhu*oKHI1?{94zVm=sQx9` z2}L7@wO}Oit(sbsd=y7_&ECVAJ7gYN4vAC)P??k9raiHTcsP&K;izIpVG9xARnJbuf~1i`H6_r!2VZ3B*vyBN2>GTB+36hBc(pxZ_-l{YS_11NDvahoH zqD*9G*5}fxWFcpnJtD6OOxex=cSB6;jRpblJOeR3W6p3NugVIyZVkuL0&gL`r^9zL z@!j!#WruF;UQ8~0BVAcvjj4M)<|Jbedj?KMmvy#pz0mJ0d1uU9a-`MD3Ow3^cFZ|M zy8z|WY!>!=D64G73Y(RcE(X9WV<3ZFcN%csyOvXz5ClE{+C$C0f;#xWD17t2+At+O z!{9bNNrw&KN!DyyOE@c{4g)40$ay^!6>4)j2Y_B&?Qtd}QNtF;OaW#hy9~u*QdcV^ zwvLc92x4qPF7ghROyXMm7&&!ixp(-F?CWQzj+laPqlMiogyhncG$mOIy|K%5#6kX` z-2#i{G0xj*@STREgdAvV;PC&AofQishV}aYAo$cIknkrA*BysIVP)0w(m+!A`kLmB zk&@$pQsSag8gCOfoQ8c-Fuh3)7jvI)DfBYL7&3-tCOpyGR7nTBowG4H9pQ5CpX8ru zs3g&yL;(>3$AR25@xF^2IxwT+)8*JhQprmTP|jrTsVbI*=LmN;$khfeAtH@U0^TC= zXKD&s8phM9G#ag%akVa-qbH^_Fc(e!{QJnWU79jrR8;`GW=}6~bZn{<4uo!!Dz;%o z`QXrGPqRugHHNR8S7Y*(6dS~K%`g@Pw$cSa#~`3@m1cj|7E51khNw zI&-VI8oS-DUgakNfCt%O$u9z(iWWCOOL2|J>6A&9q=k%mvP4Wp-kAPUjwRde{T)gl z;DeG;S;b{05;{AVMAP}wO=w|5oo7vQ+B=AlIahwdEZlH;0!BNTqUTsY0DaUcOs(pk zEk1wHjr0Q+qxSKhbG|cp1LUdW*IznFx9Sf+4+tnI1%XhH5^cJc#rPRb~T>R8s8%P(Ci9PCZ{80 zma?lfh!Xn+r;_BP!SW^Hi51lu>F3wS_j}?^KA3yuFRAG=>&FcGr1@>6Q0W1$s9qY2 zWnZ{0C%qNM7bM%l7wKTl&j<976t}iLW&mk)j8Sv)055ZB#uh$U7p%jY=sQj}i+*SG zi0uteE;;&P#M)B){P@0<%>k(tRy9XFQr|P@mMe=70F^V3jU7DG(q7SvtT)Vt^oGkS z|8kCIfHp=YuPnHycn6{*XS`lf^kfxAesS$PrOHJW_M9~93LStyW4wzSbO}6Ehn=;Y zL0k0YY5p{lm}3`kFzyqIrY<;lGzQqs4DOo#m?nT$2%loi-w1k z!3t;M@aH-8*^U{Yg5n%+{S7l+3YC%Hsoy{UKBx@O+i4%;!sHZu{qpA#3;-$fY5YIL z#pxDrP-BbULW&-rkVVBl{ujGDm6(Sn_CowhiDP_j|RLPpW}kY$ef*_T!6&=%4Q52Yj6ZvC?%YAHV~e1 zMhgEhwW7Q`o% zm_WdM>mHx19*;N|f~b%UIiP1pfcP_n`hkP&3QIW9SBF1IC*icJ49Izzmc%t`n1(G% z2_5FqpE)Qdscky^`dQjVN(G&kkx zs3#}u!>{$}1%J)ZTLm954j)o0oBIt{x7(wp*{cr literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/43 b/tests/data/v3/cities.zarr/c/43 new file mode 100644 index 0000000000000000000000000000000000000000..e685e49fb03c145060dc86cc1a3b5294794ddfff GIT binary patch literal 14085 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/45 b/tests/data/v3/cities.zarr/c/45 new file mode 100644 index 0000000000000000000000000000000000000000..16669856164e450a3989b727a67cf2dae3f992cc GIT binary patch literal 13938 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 eU`3^^TvlnE<|X()BB)Y@MM-=Pcj|` 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/6 b/tests/data/v3/cities.zarr/c/6 new file mode 100644 index 0000000000000000000000000000000000000000..6307ce3a221bb41369fc36c7edbbe47f0de693d0 GIT binary patch literal 13037 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/7 b/tests/data/v3/cities.zarr/c/7 new file mode 100644 index 0000000000000000000000000000000000000000..e2d072c815c64acc15cba6fb79becfb53e57219c GIT binary patch literal 13101 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 bLSv-t%FU-RRA(Bq8a_@Lvs20#1^NF2<|or< literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/c/9 b/tests/data/v3/cities.zarr/c/9 new file mode 100644 index 0000000000000000000000000000000000000000..e4c90e831ab4b3cf26f3b0244da186bd0c9ed214 GIT binary patch literal 13001 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 literal 0 HcmV?d00001 diff --git a/tests/data/v3/cities.zarr/zarr.json b/tests/data/v3/cities.zarr/zarr.json new file mode 100644 index 00000000..90ba194e --- /dev/null +++ b/tests/data/v3/cities.zarr/zarr.json @@ -0,0 +1,28 @@ +{ + "zarr_format": 3, + "node_type": "array", + "shape": [ + 47868 + ], + "data_type": "string", + "chunk_grid": { + "name": "regular", + "configuration": { + "chunk_shape": [ + 1000 + ] + } + }, + "chunk_key_encoding": { + "name": "default", + "configuration": { + "separator": "/" + } + }, + "fill_value": "", + "codecs": [ + { + "name": "https://codec.zarrs.dev/array_to_bytes/vlen_interleaved" + } + ] +} \ No newline at end of file From 495473f939f964f1dce53306716fabf5c635ba18 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Thu, 25 Jul 2024 13:41:10 +1000 Subject: [PATCH 2/6] Fix store value truncation --- CHANGELOG.md | 1 + src/storage/storage_async.rs | 4 +++- src/storage/storage_sync.rs | 4 +++- src/storage/store/store_sync/memory_store.rs | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2086bd88..03a5a2ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix an unnecessary copy in `async_store_set_partial_values` - Fix error when `bytes` metadata is encoded without a configuration, even if empty - Fix an error in `ChunkGrid` docs + - Fixed `[async_]store_set_partial_values` and `MemoryStore::set` to correctly truncate the bytes of store value if they shrink ## [0.15.1] - 2024-07-11 diff --git a/src/storage/storage_async.rs b/src/storage/storage_async.rs index e4164261..66ff26a2 100644 --- a/src/storage/storage_async.rs +++ b/src/storage/storage_async.rs @@ -204,7 +204,9 @@ pub async fn async_store_set_partial_values( vec.resize_with(end_max, Default::default); vec } else { - bytes.to_vec() + let mut bytes = bytes.to_vec(); + bytes.truncate(end_max); + bytes }; // Update the store key diff --git a/src/storage/store/store_sync/memory_store.rs b/src/storage/store/store_sync/memory_store.rs index ec3061d6..e0833e39 100644 --- a/src/storage/store/store_sync/memory_store.rs +++ b/src/storage/store/store_sync/memory_store.rs @@ -66,6 +66,8 @@ impl MemoryStore { let length = usize::try_from(offset + value.len() as u64).unwrap(); if data.len() < length { data.resize(length, 0); + } else { + data.truncate(length); } let offset = usize::try_from(offset).unwrap(); data[offset..offset + value.len()].copy_from_slice(value); From 222d149ef0df7e4c7fffb200e51094c4bc566ec5 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Thu, 25 Jul 2024 14:05:24 +1000 Subject: [PATCH 3/6] Add `ArraySize::new`, fix `ArrayBytes::new_fill_value`, fix `FillValue::equals_all` with vlen data --- CHANGELOG.md | 1 + src/array/array_async_readable.rs | 20 +++++----- src/array/array_bytes.rs | 39 ++++++++++++------- src/array/array_representation.rs | 14 +++++++ src/array/array_sync_readable.rs | 20 +++++----- src/array/array_sync_sharded_readable_ext.rs | 9 ++--- src/array/chunk_shape.rs | 18 ++++++--- .../array_to_bytes/sharding/sharding_codec.rs | 13 ++++--- .../sharding/sharding_partial_decoder.rs | 20 ++++++++-- .../codec/array_to_bytes/vlen/vlen_codec.rs | 4 ++ .../vlen/vlen_partial_decoder.rs | 10 ++--- .../vlen_interleaved_partial_decoder.rs | 11 +++--- src/array/fill_value.rs | 7 ++++ 13 files changed, 121 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03a5a2ea..86788c58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `DataType::fixed_size()` that returns `Option`. Returns `None` for variable length data types. - Add `ArrayError::IncompatibleElementType` (replaces `ArrayError::IncompatibleElementSize`) - Add `ArrayError::InvalidElementValue` + - Add `ChunkShape::num_elements_u64` ### Changed - Use `[async_]retrieve_array_subset_opt` internally in `Array::[async_]retrieve_chunks_opt` diff --git a/src/array/array_async_readable.rs b/src/array/array_async_readable.rs index e540e59e..e037d6c3 100644 --- a/src/array/array_async_readable.rs +++ b/src/array/array_async_readable.rs @@ -22,7 +22,7 @@ use super::{ element::ElementOwned, unsafe_cell_slice::UnsafeCellSlice, Array, ArrayBytes, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV2, - ArrayMetadataV3, DataTypeSize, + ArrayMetadataV3, ArraySize, DataTypeSize, }; #[cfg(feature = "ndarray")] @@ -349,11 +349,10 @@ impl Array { if let Some(chunk) = chunk { Ok(chunk) } else { - let chunk_representation = self.chunk_array_representation(chunk_indices)?; - Ok(ArrayBytes::new_fill_value( - chunk_representation.num_elements_usize(), - self.fill_value(), - )) + let chunk_shape = self.chunk_shape(chunk_indices)?; + let array_size = + ArraySize::new(self.data_type().size(), chunk_shape.num_elements_u64()); + Ok(ArrayBytes::new_fill_value(array_size, self.fill_value())) } } @@ -542,10 +541,11 @@ impl Array { // Retrieve chunk bytes let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => Ok(ArrayBytes::new_fill_value( - array_subset.num_elements_usize(), - self.fill_value(), - )), + 0 => { + let array_size = + ArraySize::new(self.data_type().size(), array_subset.num_elements()); + Ok(ArrayBytes::new_fill_value(array_size, self.fill_value())) + } 1 => { let chunk_indices = chunks.start(); let chunk_subset = self.chunk_subset(chunk_indices)?; diff --git a/src/array/array_bytes.rs b/src/array/array_bytes.rs index 5bed39d7..0cd34ea8 100644 --- a/src/array/array_bytes.rs +++ b/src/array/array_bytes.rs @@ -8,7 +8,9 @@ use crate::{ byte_range::extract_byte_ranges_concat_unchecked, }; -use super::{codec::CodecError, ravel_indices, ArrayShape, DataType, DataTypeSize, FillValue}; +use super::{ + codec::CodecError, ravel_indices, ArrayShape, ArraySize, DataType, DataTypeSize, FillValue, +}; /// Array element bytes. pub type RawBytes<'a> = Cow<'a, [u8]>; @@ -50,17 +52,28 @@ impl<'a> ArrayBytes<'a> { } /// Create a new [`ArrayBytes`] with `num_elements` composed entirely of the `fill_value`. + /// + /// # Panics + /// Panics if the number of elements in `array_size` exceeds [`usize::MAX`]. #[must_use] - pub fn new_fill_value(num_elements: usize, fill_value: &FillValue) -> Self { - if fill_value.size() == 0 { - Self::new_vlen( - fill_value.as_ne_bytes().repeat(num_elements), - (0..=num_elements) - .map(|i| i * fill_value.size()) - .collect::>(), - ) - } else { - Self::new_flen(fill_value.as_ne_bytes().repeat(num_elements)) + pub fn new_fill_value(array_size: ArraySize, fill_value: &FillValue) -> Self { + match array_size { + ArraySize::Fixed { + num_elements, + data_type_size: _, + } => { + let num_elements = usize::try_from(num_elements).unwrap(); + Self::new_flen(fill_value.as_ne_bytes().repeat(num_elements)) + } + ArraySize::Variable { num_elements } => { + let num_elements = usize::try_from(num_elements).unwrap(); + Self::new_vlen( + fill_value.as_ne_bytes().repeat(num_elements), + (0..=num_elements) + .map(|i| i * fill_value.size()) + .collect::>(), + ) + } } } @@ -290,9 +303,7 @@ pub(crate) fn update_bytes_vlen<'a>( ) -> ArrayBytes<'a> { // Get the current and new length of the bytes in the chunk subset let size_subset_new = { - let chunk_subset_indices = subset - .relative_to(subset.start()) - .unwrap() + let chunk_subset_indices = ArraySubset::new_with_shape(subset.shape().to_vec()) .linearised_indices(subset.shape()) .unwrap(); chunk_subset_indices diff --git a/src/array/array_representation.rs b/src/array/array_representation.rs index d74a81ab..175c126a 100644 --- a/src/array/array_representation.rs +++ b/src/array/array_representation.rs @@ -44,6 +44,20 @@ pub enum ArraySize { }, } +impl ArraySize { + /// Create a new [`ArraySize`] from a data type size and number of elements. + #[must_use] + pub fn new(data_type_size: DataTypeSize, num_elements: u64) -> Self { + match data_type_size { + DataTypeSize::Fixed(data_type_size) => Self::Fixed { + num_elements, + data_type_size, + }, + DataTypeSize::Variable => Self::Variable { num_elements }, + } + } +} + impl ArrayRepresentationBase where TDim: Into + core::fmt::Debug + Copy, diff --git a/src/array/array_sync_readable.rs b/src/array/array_sync_readable.rs index ecb17187..4b41d968 100644 --- a/src/array/array_sync_readable.rs +++ b/src/array/array_sync_readable.rs @@ -23,7 +23,7 @@ use super::{ concurrency::concurrency_chunks_and_codec, element::ElementOwned, unsafe_cell_slice::UnsafeCellSlice, - Array, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV3, DataTypeSize, + Array, ArrayCreateError, ArrayError, ArrayMetadata, ArrayMetadataV3, ArraySize, DataTypeSize, }; #[cfg(feature = "ndarray")] @@ -484,11 +484,10 @@ impl Array { if let Some(chunk) = chunk { Ok(chunk) } else { - let chunk_representation = self.chunk_array_representation(chunk_indices)?; - Ok(ArrayBytes::new_fill_value( - chunk_representation.num_elements_usize(), - self.fill_value(), - )) + let chunk_shape = self.chunk_shape(chunk_indices)?; + let array_size = + ArraySize::new(self.data_type().size(), chunk_shape.num_elements_u64()); + Ok(ArrayBytes::new_fill_value(array_size, self.fill_value())) } } @@ -625,10 +624,11 @@ impl Array { // Retrieve chunk bytes let num_chunks = chunks.num_elements_usize(); match num_chunks { - 0 => Ok(ArrayBytes::new_fill_value( - array_subset.num_elements_usize(), - self.fill_value(), - )), + 0 => { + let array_size = + ArraySize::new(self.data_type().size(), array_subset.num_elements()); + Ok(ArrayBytes::new_fill_value(array_size, self.fill_value())) + } 1 => { let chunk_indices = chunks.start(); let chunk_subset = self.chunk_subset(chunk_indices)?; diff --git a/src/array/array_sync_sharded_readable_ext.rs b/src/array/array_sync_sharded_readable_ext.rs index 048733db..7c6ac1b2 100644 --- a/src/array/array_sync_sharded_readable_ext.rs +++ b/src/array/array_sync_sharded_readable_ext.rs @@ -9,7 +9,7 @@ use super::{ codec::CodecOptions, concurrency::concurrency_chunks_and_codec, Array, ArrayError, ArrayShardedExt, ChunkGrid, UnsafeCellSlice, }; -use super::{ArrayBytes, DataTypeSize}; +use super::{ArrayBytes, ArraySize, DataTypeSize}; use crate::storage::ReadableStorageTraits; use crate::{array::codec::ArrayPartialDecoderTraits, array_subset::ArraySubset}; @@ -340,10 +340,9 @@ impl ArrayShardedReadableExt // Retrieve chunk bytes let num_shards = shards.num_elements_usize(); if num_shards == 0 { - Ok(ArrayBytes::new_fill_value( - array_subset.num_elements_usize(), - self.fill_value(), - )) + let array_size = + ArraySize::new(self.data_type().size(), array_subset.num_elements()); + Ok(ArrayBytes::new_fill_value(array_size, self.fill_value())) } else { // Calculate chunk/codec concurrency let chunk_representation = diff --git a/src/array/chunk_shape.rs b/src/array/chunk_shape.rs index 1ee384c0..a2edfd0f 100644 --- a/src/array/chunk_shape.rs +++ b/src/array/chunk_shape.rs @@ -17,7 +17,7 @@ impl ChunkShape { pub fn num_elements(&self) -> NonZeroU64 { unsafe { // Multiplying NonZeroU64 must result in NonZeroU64 - NonZeroU64::new_unchecked(self.0.iter().copied().map(NonZeroU64::get).product::()) + NonZeroU64::new_unchecked(self.num_elements_u64()) } } @@ -30,13 +30,19 @@ impl ChunkShape { #[must_use] pub fn num_elements_nonzero_usize(&self) -> NonZeroUsize { unsafe { - NonZeroUsize::new_unchecked( - usize::try_from(self.0.iter().copied().map(NonZeroU64::get).product::()) - .unwrap(), - ) + // Multiplying NonZeroU64 must result in NonZeroUsize + NonZeroUsize::new_unchecked(usize::try_from(self.num_elements_u64()).unwrap()) } } + /// Return the number of elements as a u64. + /// + /// Equal to the product of the components of its shape. + #[must_use] + pub fn num_elements_u64(&self) -> u64 { + self.0.iter().copied().map(NonZeroU64::get).product::() + } + /// Return the number of elements as a usize. /// /// Equal to the product of the components of its shape. @@ -45,7 +51,7 @@ impl ChunkShape { /// Panics if the number of elements exceeds [`usize::MAX`]. #[must_use] pub fn num_elements_usize(&self) -> usize { - usize::try_from(self.0.iter().copied().map(NonZeroU64::get).product::()).unwrap() + usize::try_from(self.num_elements_u64()).unwrap() } } diff --git a/src/array/codec/array_to_bytes/sharding/sharding_codec.rs b/src/array/codec/array_to_bytes/sharding/sharding_codec.rs index 757894bf..134b6709 100644 --- a/src/array/codec/array_to_bytes/sharding/sharding_codec.rs +++ b/src/array/codec/array_to_bytes/sharding/sharding_codec.rs @@ -12,8 +12,8 @@ use crate::{ concurrency::calc_concurrency_outer_inner, transmute_to_bytes_vec, unravel_index, unsafe_cell_slice::UnsafeCellSlice, - ArrayBytes, ArrayMetadataOptions, BytesRepresentation, ChunkRepresentation, ChunkShape, - DataTypeSize, FillValue, RawBytes, + ArrayBytes, ArrayMetadataOptions, ArraySize, BytesRepresentation, ChunkRepresentation, + ChunkShape, DataTypeSize, FillValue, RawBytes, }, array_subset::ArraySubset, metadata::v3::MetadataV3, @@ -221,10 +221,11 @@ impl ArrayToBytesCodecTraits for ShardingCodec { let offset = shard_index[chunk_index * 2]; let size = shard_index[chunk_index * 2 + 1]; let chunk_bytes = if offset == u64::MAX && size == u64::MAX { - ArrayBytes::new_fill_value( - chunk_representation.num_elements_usize(), - chunk_representation.fill_value(), - ) + let array_size = ArraySize::new( + chunk_representation.data_type().size(), + chunk_representation.num_elements(), + ); + ArrayBytes::new_fill_value(array_size, chunk_representation.fill_value()) } else if usize::try_from(offset + size).unwrap() > encoded_shard.len() { return Err(CodecError::Other( "The shard index references out-of-bounds bytes. The chunk may be corrupted." diff --git a/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs b/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs index 17ce5bd2..0b84c263 100644 --- a/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/sharding/sharding_partial_decoder.rs @@ -15,7 +15,7 @@ use crate::{ concurrency::{calc_concurrency_outer_inner, RecommendedConcurrency}, ravel_indices, unsafe_cell_slice::UnsafeCellSlice, - ArrayBytes, ChunkRepresentation, ChunkShape, DataType, DataTypeSize, + ArrayBytes, ArraySize, ChunkRepresentation, ChunkShape, DataType, DataTypeSize, }, byte_range::ByteRange, }; @@ -205,8 +205,12 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { unsafe { array_subset.overlap_unchecked(&chunk_subset) }; let chunk_subset_bytes = if offset == u64::MAX && size == u64::MAX { + let array_size = ArraySize::new( + chunk_representation.data_type().size(), + chunk_subset_overlap.num_elements(), + ); ArrayBytes::new_fill_value( - chunk_subset_overlap.num_elements_usize(), + array_size, chunk_representation.fill_value(), ) } else { @@ -282,8 +286,12 @@ impl ArrayPartialDecoderTraits for ShardingPartialDecoder<'_> { unsafe { array_subset.overlap_unchecked(&chunk_subset) }; let decoded_bytes = if offset == u64::MAX && size == u64::MAX { + let array_size = ArraySize::new( + chunk_representation.data_type().size(), + chunk_subset_overlap.num_elements(), + ); ArrayBytes::new_fill_value( - chunk_subset_overlap.num_elements_usize(), + array_size, chunk_representation.fill_value(), ) } else { @@ -509,8 +517,12 @@ impl AsyncArrayPartialDecoderTraits for AsyncShardingPartialDecoder<'_> { unsafe { array_subset.overlap_unchecked(&chunk_subset) }; let chunk_subset_bytes = if offset == u64::MAX && size == u64::MAX { + let array_size = ArraySize::new( + self.data_type().size(), + chunk_subset_overlap.num_elements(), + ); ArrayBytes::new_fill_value( - chunk_subset_overlap.num_elements_usize(), + array_size, chunk_representation.fill_value(), ) } else { diff --git a/src/array/codec/array_to_bytes/vlen/vlen_codec.rs b/src/array/codec/array_to_bytes/vlen/vlen_codec.rs index fc2e6004..9284715d 100644 --- a/src/array/codec/array_to_bytes/vlen/vlen_codec.rs +++ b/src/array/codec/array_to_bytes/vlen/vlen_codec.rs @@ -120,6 +120,10 @@ impl ArrayToBytesCodecTraits for VlenCodec { decoded_representation.data_type().size(), )?; let (data, offsets) = bytes.into_variable()?; + assert_eq!( + offsets.len(), + decoded_representation.num_elements_usize() + 1 + ); // Encode offsets let num_offsets = diff --git a/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs b/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs index d142a8df..f004a208 100644 --- a/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/vlen/vlen_partial_decoder.rs @@ -9,7 +9,7 @@ use crate::{ ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, CodecOptions, }, - ArrayBytes, ChunkRepresentation, CodecChain, DataType, FillValue, RawBytes, + ArrayBytes, ArraySize, ChunkRepresentation, CodecChain, DataType, FillValue, RawBytes, }, metadata::v3::codec::vlen::VlenIndexDataType, }; @@ -86,10 +86,10 @@ fn decode_vlen_bytes<'a>( // Chunk is empty, all decoded regions are empty let mut output = Vec::with_capacity(decoded_regions.len()); for decoded_region in decoded_regions { - output.push(ArrayBytes::new_fill_value( - decoded_region.num_elements_usize(), - fill_value, - )); + let array_size = ArraySize::Variable { + num_elements: decoded_region.num_elements(), + }; + output.push(ArrayBytes::new_fill_value(array_size, fill_value)); } Ok(output) } diff --git a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs index bb707fb8..04bac67f 100644 --- a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs @@ -5,7 +5,7 @@ use crate::array::{ codec::{ ArrayPartialDecoderTraits, ArraySubset, BytesPartialDecoderTraits, CodecError, CodecOptions, }, - ArrayBytes, ChunkRepresentation, DataType, FillValue, RawBytes, + ArrayBytes, ArraySize, ChunkRepresentation, DataType, DataTypeSize, FillValue, RawBytes, }; #[cfg(feature = "async")] @@ -33,6 +33,7 @@ impl<'a> VlenInterleavedPartialDecoder<'a> { fn decode_vlen_bytes<'a>( bytes: Option, decoded_regions: &[ArraySubset], + data_type_size: DataTypeSize, fill_value: &FillValue, shape: &[u64], ) -> Result>, CodecError> { @@ -44,10 +45,8 @@ fn decode_vlen_bytes<'a>( // Chunk is empty, all decoded regions are empty let mut output = Vec::with_capacity(decoded_regions.len()); for decoded_region in decoded_regions { - output.push(ArrayBytes::new_fill_value( - decoded_region.num_elements_usize(), - fill_value, - )); + let array_size = ArraySize::new(data_type_size, decoded_region.num_elements()); + output.push(ArrayBytes::new_fill_value(array_size, fill_value)); } Ok(output) } @@ -68,6 +67,7 @@ impl ArrayPartialDecoderTraits for VlenInterleavedPartialDecoder<'_> { decode_vlen_bytes( bytes, decoded_regions, + self.decoded_representation.data_type().size(), self.decoded_representation.fill_value(), &self.decoded_representation.shape_u64(), ) @@ -112,6 +112,7 @@ impl AsyncArrayPartialDecoderTraits for AsyncVlenInterleavedPartialDecoder<'_> { decode_vlen_bytes( bytes, decoded_regions, + self.decoded_representation.data_type().size(), self.decoded_representation.fill_value(), &self.decoded_representation.shape_u64(), ) diff --git a/src/array/fill_value.rs b/src/array/fill_value.rs index 70211940..4de19490 100644 --- a/src/array/fill_value.rs +++ b/src/array/fill_value.rs @@ -2,6 +2,8 @@ //! //! See . +use num::Integer; + /// The fill value of the Zarr array. /// /// Provides an element value to use for uninitialised portions of the Zarr array. @@ -169,6 +171,11 @@ impl FillValue { #[allow(clippy::missing_panics_doc)] #[must_use] pub fn equals_all(&self, bytes: &[u8]) -> bool { + // Special cases for variable length data + if !bytes.len().is_multiple_of(&self.0.len()) || bytes.len() < self.0.len() { + return false; + } + match self.0.len() { 0 => bytes.is_empty(), 1 => { From 71b9d784e4e4e839715e15ad65a60bc7914a59b9 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Thu, 25 Jul 2024 14:14:36 +1000 Subject: [PATCH 4/6] Add `array_write_read_string` example --- Cargo.toml | 5 ++ examples/array_write_read_string.rs | 119 ++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 examples/array_write_read_string.rs diff --git a/Cargo.toml b/Cargo.toml index c2bbe2cc..348470b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,11 @@ name = "array_write_read_ndarray" required-features = ["ndarray"] doc-scrape-examples = true +[[example]] +name = "array_write_read_string" +required-features = ["ndarray"] +doc-scrape-examples = true + [[example]] name = "async_array_write_read" required-features = ["ndarray", "async", "object_store"] diff --git a/examples/array_write_read_string.rs b/examples/array_write_read_string.rs new file mode 100644 index 00000000..97f1444a --- /dev/null +++ b/examples/array_write_read_string.rs @@ -0,0 +1,119 @@ +use itertools::Itertools; +use ndarray::{array, Array2, ArrayD}; +use zarrs::storage::{ + storage_transformer::{StorageTransformerExtension, UsageLogStorageTransformer}, + ReadableWritableListableStorage, +}; + +fn array_write_read() -> Result<(), Box> { + use std::sync::Arc; + use zarrs::{ + array::{DataType, FillValue}, + array_subset::ArraySubset, + storage::store, + }; + + // Create a store + // let path = tempfile::TempDir::new()?; + // let mut store: ReadableWritableListableStorage = Arc::new(store::FilesystemStore::new(path.path())?); + // let mut store: ReadableWritableListableStorage = Arc::new(store::FilesystemStore::new( + // "tests/data/array_write_read.zarr", + // )?); + let mut store: ReadableWritableListableStorage = Arc::new(store::MemoryStore::new()); + if let Some(arg1) = std::env::args().collect::>().get(1) { + if arg1 == "--usage-log" { + let log_writer = Arc::new(std::sync::Mutex::new( + // std::io::BufWriter::new( + std::io::stdout(), + // ) + )); + let usage_log = Arc::new(UsageLogStorageTransformer::new(log_writer, || { + chrono::Utc::now().format("[%T%.3f] ").to_string() + })); + store = usage_log + .clone() + .create_readable_writable_listable_transformer(store); + } + } + + // Create a group + let group_path = "/group"; + let mut group = zarrs::group::GroupBuilder::new().build(store.clone(), group_path)?; + + // Update group metadata + group + .attributes_mut() + .insert("foo".into(), serde_json::Value::String("bar".into())); + + // Write group metadata to store + group.store_metadata()?; + + println!( + "The group metadata is:\n{}\n", + serde_json::to_string_pretty(&group.metadata()).unwrap() + ); + + // Create an array + let array_path = "/group/array"; + let array = zarrs::array::ArrayBuilder::new( + vec![4, 4], // array shape + DataType::String, + vec![2, 2].try_into()?, // regular chunk shape + FillValue::from("_"), + ) + // .bytes_to_bytes_codecs(vec![]) // uncompressed + .dimension_names(["y", "x"].into()) + // .storage_transformers(vec![].into()) + .build(store.clone(), array_path)?; + + // Write array metadata to store + array.store_metadata()?; + + println!( + "The array metadata is:\n{}\n", + serde_json::to_string_pretty(&array.metadata()).unwrap() + ); + + // Write some chunks + array.store_chunk_ndarray( + &[0, 0], + ArrayD::<&str>::from_shape_vec(vec![2, 2], vec!["a", "bb", "ccc", "dddd"]).unwrap(), + )?; + array.store_chunk_ndarray( + &[0, 1], + ArrayD::<&str>::from_shape_vec(vec![2, 2], vec!["4444", "333", "22", "1"]).unwrap(), + )?; + let subset_all = ArraySubset::new_with_shape(array.shape().to_vec()); + let data_all = array.retrieve_array_subset_ndarray::(&subset_all)?; + println!("store_chunk [0, 0] and [0, 1]:\n{data_all}\n"); + + // Write a subset spanning multiple chunks, including updating chunks already written + let ndarray_subset: Array2<&str> = array![["!", "@@"], ["###", "$$$$"]]; + array.store_array_subset_ndarray( + ArraySubset::new_with_ranges(&[1..3, 1..3]).start(), + ndarray_subset, + )?; + let data_all = array.retrieve_array_subset_ndarray::(&subset_all)?; + println!("store_array_subset [1..3, 1..3]:\nndarray::ArrayD\n{data_all}"); + + // Retrieve bytes directly, convert into a single string allocation, create a &str ndarray + // TODO: Add a convenience function for this? + let data_all = array.retrieve_array_subset(&subset_all)?; + let (bytes, offsets) = data_all.into_variable()?; + let string = String::from_utf8(bytes.into_owned())?; + let elements = offsets + .iter() + .tuple_windows() + .map(|(&curr, &next)| &string[curr..next]) + .collect::>(); + let ndarray = ArrayD::<&str>::from_shape_vec(subset_all.shape_usize(), elements)?; + println!("ndarray::ArrayD<&str>:\n{ndarray}"); + + Ok(()) +} + +fn main() { + if let Err(err) = array_write_read() { + println!("{:?}", err); + } +} From 36c6e8051bfb196afe2d4ea49268714fc35cd2f7 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Thu, 25 Jul 2024 14:22:07 +1000 Subject: [PATCH 5/6] Rename `vlen_interleaved` to `vlen_v2` --- CHANGELOG.md | 4 +- doc/status/codecs.md | 34 ++++++++--------- src/array/array_builder.rs | 2 +- src/array/codec/array_to_bytes.rs | 2 +- .../{vlen_interleaved.rs => vlen_v2.rs} | 26 ++++++------- .../vlen_v2_codec.rs} | 24 ++++++------ .../vlen_v2_partial_decoder.rs} | 12 +++--- src/metadata/array.rs | 10 ++--- src/metadata/v3.rs | 4 +- src/metadata/v3/codec/vlen_interleaved.rs | 37 ------------------- src/metadata/v3/codec/vlen_v2.rs | 37 +++++++++++++++++++ tests/cities.rs | 12 +++--- tests/data/v3/cities.zarr/zarr.json | 2 +- 13 files changed, 103 insertions(+), 103 deletions(-) rename src/array/codec/array_to_bytes/{vlen_interleaved.rs => vlen_v2.rs} (73%) rename src/array/codec/array_to_bytes/{vlen_interleaved/vlen_interleaved_codec.rs => vlen_v2/vlen_v2_codec.rs} (85%) rename src/array/codec/array_to_bytes/{vlen_interleaved/vlen_interleaved_partial_decoder.rs => vlen_v2/vlen_v2_partial_decoder.rs} (91%) delete mode 100644 src/metadata/v3/codec/vlen_interleaved.rs create mode 100644 src/metadata/v3/codec/vlen_v2.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 86788c58..2ea5d24d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `array::Element[Owned]` traits representing array elements - Supports conversion to and from `ArrayBytes` - Add `array::ElementFixedLength` marker trait - - Add experimental `vlen` and `vlen_interleaved` codec for variable length data types - - `vlen_interleaved` is for legacy support of Zarr V2 `vlen-utf8`/`vlen-bytes`/`vlen-array` codecs + - Add experimental `vlen` and `vlen_v2` codec for variable length data types + - `vlen_v2` is for legacy support of Zarr V2 `vlen-utf8`/`vlen-bytes`/`vlen-array` codecs - Add `DataType::{String,Binary}` data types - These are likely to become standardised in the future and are not feature gated - Add `ArraySubset::contains()` diff --git a/doc/status/codecs.md b/doc/status/codecs.md index 205aa0f3..f7a3dd93 100644 --- a/doc/status/codecs.md +++ b/doc/status/codecs.md @@ -1,18 +1,18 @@ -| Codec Type | Codec | ZEP | V3 | V2 | Feature Flag* | -| -------------- | -------------------------------------- | ----------------- | ------- | ------- | ------------- | -| Array to Array | [transpose] | [ZEP0001] | ✓ | | **transpose** | -| | [bitround] (experimental) | | ✓ | | bitround | -| Array to Bytes | [bytes] | [ZEP0001] | ✓ | | | -| | [sharding_indexed] | [ZEP0002] | ✓ | | **sharding** | -| | [zfp] (experimental) | | ✓ | | zfp | -| | [pcodec] (experimental) | | ✓ | | pcodec | -| | [vlen] (experimental) | | ✓ | | | -| | V3 [vlen_interleaved] (experimental)
V2 vlen-utf8/vlen-bytes/vlen-array | | ✓ | ✓ | | -| Bytes to Bytes | [blosc] | [ZEP0001] | ✓ | ✓ | **blosc** | -| | [gzip] | [ZEP0001] | ✓ | ✓ | **gzip** | -| | [crc32c] | [ZEP0002] | ✓ | | **crc32c** | -| | [zstd] | [zarr-specs #256] | ✓ | | zstd | -| | [bz2] (experimental) | | ✓ | ✓ | bz2 | +| Codec Type | Codec | ZEP | V3 | V2 | Feature Flag* | +| -------------- | ------------------------------------------------- | ----------------- | ------- | ------- | ------------- | +| Array to Array | [transpose] | [ZEP0001] | ✓ | | **transpose** | +| | [bitround] (experimental) | | ✓ | | bitround | +| Array to Bytes | [bytes] | [ZEP0001] | ✓ | | | +| | [sharding_indexed] | [ZEP0002] | ✓ | | **sharding** | +| | [zfp] (experimental) | | ✓ | | zfp | +| | [pcodec] (experimental) | | ✓ | | pcodec | +| | [vlen] (experimental) | | ✓ | | | +| | [vlen_v2] (experimental)
`vlen-*` in Zarr V2 | | ✓ | ✓ | | +| Bytes to Bytes | [blosc] | [ZEP0001] | ✓ | ✓ | **blosc** | +| | [gzip] | [ZEP0001] | ✓ | ✓ | **gzip** | +| | [crc32c] | [ZEP0002] | ✓ | | **crc32c** | +| | [zstd] | [zarr-specs #256] | ✓ | | zstd | +| | [bz2] (experimental) | | ✓ | ✓ | bz2 | \* Bolded feature flags are part of the default set of features.
@@ -34,7 +34,7 @@ [zstd]: crate::array::codec::bytes_to_bytes::zstd [bz2]: crate::array::codec::bytes_to_bytes::bz2 [vlen]: crate::array::codec::array_to_bytes::vlen -[vlen_interleaved]: crate::array::codec::array_to_bytes::vlen_interleaved +[vlen_v2]: crate::array::codec::array_to_bytes::vlen_v2 The `"name"` of of experimental codecs in array metadata links the codec documentation in this crate. @@ -45,4 +45,4 @@ The `"name"` of of experimental codecs in array metadata links the codec documen | `pcodec` | | | `bz2` | | | `vlen` | | -| `vlen_interleaved` | | +| `vlen_v2` | | diff --git a/src/array/array_builder.rs b/src/array/array_builder.rs index 269d7906..255b8def 100644 --- a/src/array/array_builder.rs +++ b/src/array/array_builder.rs @@ -106,7 +106,7 @@ impl ArrayBuilder { Box::::default() } else { Box::::default() - // Box::::default() + // Box::::default() }, bytes_to_bytes_codecs: Vec::default(), attributes: serde_json::Map::default(), diff --git a/src/array/codec/array_to_bytes.rs b/src/array/codec/array_to_bytes.rs index 765b7544..1d1b75f3 100644 --- a/src/array/codec/array_to_bytes.rs +++ b/src/array/codec/array_to_bytes.rs @@ -3,7 +3,7 @@ pub mod bytes; pub mod codec_chain; pub mod vlen; -pub mod vlen_interleaved; +pub mod vlen_v2; #[cfg(feature = "pcodec")] pub mod pcodec; diff --git a/src/array/codec/array_to_bytes/vlen_interleaved.rs b/src/array/codec/array_to_bytes/vlen_v2.rs similarity index 73% rename from src/array/codec/array_to_bytes/vlen_interleaved.rs rename to src/array/codec/array_to_bytes/vlen_v2.rs index 06d1164d..6c8c9778 100644 --- a/src/array/codec/array_to_bytes/vlen_interleaved.rs +++ b/src/array/codec/array_to_bytes/vlen_v2.rs @@ -1,21 +1,21 @@ -//! The `vlen_interleaved` array to bytes codec. +//! The `vlen_v2` array to bytes codec. -mod vlen_interleaved_codec; -mod vlen_interleaved_partial_decoder; +mod vlen_v2_codec; +mod vlen_v2_partial_decoder; use std::mem::size_of; -pub use vlen_interleaved::IDENTIFIER; +pub use vlen_v2::IDENTIFIER; -pub use crate::metadata::v3::codec::vlen_interleaved::{ - VlenInterleavedCodecConfiguration, VlenInterleavedCodecConfigurationV1, +pub use crate::metadata::v3::codec::vlen_v2::{ + VlenV2CodecConfiguration, VlenV2CodecConfigurationV1, }; use crate::{ array::{codec::CodecError, RawBytes}, - metadata::v3::codec::vlen_interleaved, + metadata::v3::codec::vlen_v2, }; -pub use vlen_interleaved_codec::VlenInterleavedCodec; +pub use vlen_v2_codec::VlenV2Codec; use crate::{ array::codec::{Codec, CodecPlugin}, @@ -25,20 +25,20 @@ use crate::{ // Register the codec. inventory::submit! { - CodecPlugin::new(IDENTIFIER, is_name_vlen_interleaved, create_codec_vlen_interleaved) + CodecPlugin::new(IDENTIFIER, is_name_vlen_v2, create_codec_vlen_v2) } -fn is_name_vlen_interleaved(name: &str) -> bool { +fn is_name_vlen_v2(name: &str) -> bool { name.eq(IDENTIFIER) } -pub(crate) fn create_codec_vlen_interleaved( +pub(crate) fn create_codec_vlen_v2( metadata: &MetadataV3, ) -> Result { - let configuration: VlenInterleavedCodecConfiguration = metadata + let configuration: VlenV2CodecConfiguration = metadata .to_configuration() .map_err(|_| PluginMetadataInvalidError::new(IDENTIFIER, "codec", metadata.clone()))?; - let codec = Box::new(VlenInterleavedCodec::new_with_configuration(&configuration)); + let codec = Box::new(VlenV2Codec::new_with_configuration(&configuration)); Ok(Codec::ArrayToBytes(codec)) } diff --git a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs b/src/array/codec/array_to_bytes/vlen_v2/vlen_v2_codec.rs similarity index 85% rename from src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs rename to src/array/codec/array_to_bytes/vlen_v2/vlen_v2_codec.rs index db610d77..76dba610 100644 --- a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_codec.rs +++ b/src/array/codec/array_to_bytes/vlen_v2/vlen_v2_codec.rs @@ -18,13 +18,13 @@ use crate::{ #[cfg(feature = "async")] use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; -use super::{VlenInterleavedCodecConfiguration, VlenInterleavedCodecConfigurationV1}; +use super::{VlenV2CodecConfiguration, VlenV2CodecConfigurationV1}; -/// The `vlen_interleaved` codec implementation. +/// The `vlen_v2` codec implementation. #[derive(Debug, Clone, Default)] -pub struct VlenInterleavedCodec {} +pub struct VlenV2Codec {} -impl VlenInterleavedCodec { +impl VlenV2Codec { /// Create a new `vlen` codec. #[must_use] pub fn new() -> Self { @@ -33,15 +33,15 @@ impl VlenInterleavedCodec { /// Create a new `vlen` codec from configuration. #[must_use] - pub fn new_with_configuration(_configuration: &VlenInterleavedCodecConfiguration) -> Self { - // let VlenInterleavedCodecConfiguration::V1(configuration) = configuration; + pub fn new_with_configuration(_configuration: &VlenV2CodecConfiguration) -> Self { + // let VlenV2CodecConfiguration::V1(configuration) = configuration; Self {} } } -impl CodecTraits for VlenInterleavedCodec { +impl CodecTraits for VlenV2Codec { fn create_metadata_opt(&self, _options: &ArrayMetadataOptions) -> Option { - let configuration = VlenInterleavedCodecConfigurationV1 {}; + let configuration = VlenV2CodecConfigurationV1 {}; Some( MetadataV3::new_with_serializable_configuration(super::IDENTIFIER, &configuration) .unwrap(), @@ -57,7 +57,7 @@ impl CodecTraits for VlenInterleavedCodec { } } -impl ArrayCodecTraits for VlenInterleavedCodec { +impl ArrayCodecTraits for VlenV2Codec { fn recommended_concurrency( &self, _decoded_representation: &ChunkRepresentation, @@ -67,7 +67,7 @@ impl ArrayCodecTraits for VlenInterleavedCodec { } #[cfg_attr(feature = "async", async_trait::async_trait)] -impl ArrayToBytesCodecTraits for VlenInterleavedCodec { +impl ArrayToBytesCodecTraits for VlenV2Codec { fn encode<'a>( &self, bytes: ArrayBytes<'a>, @@ -118,7 +118,7 @@ impl ArrayToBytesCodecTraits for VlenInterleavedCodec { _options: &CodecOptions, ) -> Result, CodecError> { Ok(Box::new( - super::vlen_interleaved_partial_decoder::VlenInterleavedPartialDecoder::new( + super::vlen_v2_partial_decoder::VlenV2PartialDecoder::new( input_handle, decoded_representation.clone(), ), @@ -133,7 +133,7 @@ impl ArrayToBytesCodecTraits for VlenInterleavedCodec { _options: &CodecOptions, ) -> Result, CodecError> { Ok(Box::new( - super::vlen_interleaved_partial_decoder::AsyncVlenInterleavedPartialDecoder::new( + super::vlen_v2_partial_decoder::AsyncVlenV2PartialDecoder::new( input_handle, decoded_representation.clone(), ), diff --git a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs b/src/array/codec/array_to_bytes/vlen_v2/vlen_v2_partial_decoder.rs similarity index 91% rename from src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs rename to src/array/codec/array_to_bytes/vlen_v2/vlen_v2_partial_decoder.rs index 04bac67f..00b27bca 100644 --- a/src/array/codec/array_to_bytes/vlen_interleaved/vlen_interleaved_partial_decoder.rs +++ b/src/array/codec/array_to_bytes/vlen_v2/vlen_v2_partial_decoder.rs @@ -12,12 +12,12 @@ use crate::array::{ use crate::array::codec::{AsyncArrayPartialDecoderTraits, AsyncBytesPartialDecoderTraits}; /// Partial decoder for the `bytes` codec. -pub struct VlenInterleavedPartialDecoder<'a> { +pub struct VlenV2PartialDecoder<'a> { input_handle: Box, decoded_representation: ChunkRepresentation, } -impl<'a> VlenInterleavedPartialDecoder<'a> { +impl<'a> VlenV2PartialDecoder<'a> { /// Create a new partial decoder for the `bytes` codec. pub fn new( input_handle: Box, @@ -52,7 +52,7 @@ fn decode_vlen_bytes<'a>( } } -impl ArrayPartialDecoderTraits for VlenInterleavedPartialDecoder<'_> { +impl ArrayPartialDecoderTraits for VlenV2PartialDecoder<'_> { fn data_type(&self) -> &DataType { self.decoded_representation.data_type() } @@ -76,13 +76,13 @@ impl ArrayPartialDecoderTraits for VlenInterleavedPartialDecoder<'_> { #[cfg(feature = "async")] /// Asynchronous partial decoder for the `bytes` codec. -pub struct AsyncVlenInterleavedPartialDecoder<'a> { +pub struct AsyncVlenV2PartialDecoder<'a> { input_handle: Box, decoded_representation: ChunkRepresentation, } #[cfg(feature = "async")] -impl<'a> AsyncVlenInterleavedPartialDecoder<'a> { +impl<'a> AsyncVlenV2PartialDecoder<'a> { /// Create a new partial decoder for the `bytes` codec. pub fn new( input_handle: Box, @@ -97,7 +97,7 @@ impl<'a> AsyncVlenInterleavedPartialDecoder<'a> { #[cfg(feature = "async")] #[async_trait::async_trait] -impl AsyncArrayPartialDecoderTraits for AsyncVlenInterleavedPartialDecoder<'_> { +impl AsyncArrayPartialDecoderTraits for AsyncVlenV2PartialDecoder<'_> { fn data_type(&self) -> &DataType { self.decoded_representation.data_type() } diff --git a/src/metadata/array.rs b/src/metadata/array.rs index 1573bb61..09f9ca8c 100644 --- a/src/metadata/array.rs +++ b/src/metadata/array.rs @@ -12,7 +12,7 @@ use super::{ }, codec::blosc::{codec_blosc_v2_numcodecs_to_v3, BloscCodecConfigurationNumcodecs}, }, - v3::codec::vlen_interleaved::VlenInterleavedCodecConfigurationV1, + v3::codec::vlen_v2::VlenV2CodecConfigurationV1, }; use thiserror::Error; @@ -148,12 +148,12 @@ pub fn array_metadata_v2_to_v3( match filter.id() { "vlen-utf8" | "vlen-bytes" | "vlen-array" => { is_vlen = true; - let vlen_interleaved_metadata = + let vlen_v2_metadata = MetadataV3::new_with_serializable_configuration( - super::v3::codec::vlen_interleaved::IDENTIFIER, - &VlenInterleavedCodecConfigurationV1 {}, + super::v3::codec::vlen_v2::IDENTIFIER, + &VlenV2CodecConfigurationV1 {}, )?; - codecs.push(vlen_interleaved_metadata); + codecs.push(vlen_v2_metadata); } _ => { codecs.push(MetadataV3::new_with_configuration( diff --git a/src/metadata/v3.rs b/src/metadata/v3.rs index 27b908d5..4924a09b 100644 --- a/src/metadata/v3.rs +++ b/src/metadata/v3.rs @@ -29,8 +29,8 @@ pub mod codec { pub mod transpose; /// `vlen` codec metadata. pub mod vlen; - /// `vlen_interleaved` codec metadata. - pub mod vlen_interleaved; + /// `vlen_v2` codec metadata. + pub mod vlen_v2; #[cfg(feature = "zfp")] /// `zfp` codec metadata. pub mod zfp; diff --git a/src/metadata/v3/codec/vlen_interleaved.rs b/src/metadata/v3/codec/vlen_interleaved.rs deleted file mode 100644 index fa19a401..00000000 --- a/src/metadata/v3/codec/vlen_interleaved.rs +++ /dev/null @@ -1,37 +0,0 @@ -use derive_more::{Display, From}; -use serde::{Deserialize, Serialize}; - -/// The identifier for the `vlen_interleaved` codec. -pub const IDENTIFIER: &str = "https://codec.zarrs.dev/array_to_bytes/vlen_interleaved"; - -/// A wrapper to handle various versions of `vlen_interleaved` codec configuration parameters. -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)] -#[serde(untagged)] -pub enum VlenInterleavedCodecConfiguration { - /// Version 1.0. - V1(VlenInterleavedCodecConfigurationV1), -} - -/// Configuration parameters for the `vlen_interleaved` codec (version 1.0). -#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, Default)] -#[serde(deny_unknown_fields)] -#[display(fmt = "{}", "serde_json::to_string(self).unwrap_or_default()")] -pub struct VlenInterleavedCodecConfigurationV1 {} - -impl VlenInterleavedCodecConfigurationV1 { - /// Create a new `vlen_interleaved` codec configuration. - #[must_use] - pub const fn new() -> Self { - Self {} - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn codec_vlen_interleaved() { - serde_json::from_str::(r#"{}"#).unwrap(); - } -} diff --git a/src/metadata/v3/codec/vlen_v2.rs b/src/metadata/v3/codec/vlen_v2.rs new file mode 100644 index 00000000..373a6cf8 --- /dev/null +++ b/src/metadata/v3/codec/vlen_v2.rs @@ -0,0 +1,37 @@ +use derive_more::{Display, From}; +use serde::{Deserialize, Serialize}; + +/// The identifier for the `vlen_v2` codec. +pub const IDENTIFIER: &str = "https://codec.zarrs.dev/array_to_bytes/vlen_v2"; + +/// A wrapper to handle various versions of `vlen_v2` codec configuration parameters. +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)] +#[serde(untagged)] +pub enum VlenV2CodecConfiguration { + /// Version 1.0. + V1(VlenV2CodecConfigurationV1), +} + +/// Configuration parameters for the `vlen_v2` codec (version 1.0). +#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, Default)] +#[serde(deny_unknown_fields)] +#[display(fmt = "{}", "serde_json::to_string(self).unwrap_or_default()")] +pub struct VlenV2CodecConfigurationV1 {} + +impl VlenV2CodecConfigurationV1 { + /// Create a new `vlen_v2` codec configuration. + #[must_use] + pub const fn new() -> Self { + Self {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn codec_vlen_v2() { + serde_json::from_str::(r#"{}"#).unwrap(); + } +} diff --git a/tests/cities.rs b/tests/cities.rs index 0af6afc5..d12e0244 100644 --- a/tests/cities.rs +++ b/tests/cities.rs @@ -9,7 +9,7 @@ use zarrs::{ codec::{ array_to_bytes::{ sharding::ShardingCodecBuilder, vlen::VlenCodec, - vlen_interleaved::VlenInterleavedCodec, + vlen_v2::VlenV2Codec, }, ArrayToBytesCodecTraits, ZstdCodec, }, @@ -95,7 +95,7 @@ fn cities() -> Result<(), Box> { assert_eq!(cities[47862], "Sariwŏn-si"); assert_eq!(cities[47867], "Charlotte Amalie"); - let vlen_interleaved = Box::new(VlenInterleavedCodec::default()); + let vlen_v2 = Box::new(VlenV2Codec::default()); // let vlen = Box::new(VlenCodec::default()); let vlen_configuration: VlenCodecConfiguration = serde_json::from_str(r#"{ @@ -114,8 +114,8 @@ fn cities() -> Result<(), Box> { print!("| encoding | compression | size |\n"); print!("| ---------------- | ----------- | ------ |\n"); - print!("| vlen_interleaved | | {} |\n", cities_impl(&cities, None, 1000, None, vlen_interleaved.clone(), true)?); - print!("| vlen_interleaved | zstd 5 | {} |\n", cities_impl(&cities, Some(5), 1000, None, vlen_interleaved.clone(), false)?); + print!("| vlen_v2 | | {} |\n", cities_impl(&cities, None, 1000, None, vlen_v2.clone(), true)?); + print!("| vlen_v2 | zstd 5 | {} |\n", cities_impl(&cities, Some(5), 1000, None, vlen_v2.clone(), false)?); print!("| vlen | | {} |\n", cities_impl(&cities, None, 1000, None, vlen.clone(), false)?); print!("| vlen | zstd 5 | {} |\n", cities_impl(&cities, None, 1000, None, vlen_compressed.clone(), false)?); println!(); @@ -123,8 +123,8 @@ fn cities() -> Result<(), Box> { // | encoding | compression | size | // | ---------------- | ----------- | ------ | - // | vlen_interleaved | | 642196 | - // | vlen_interleaved | zstd 5 | 362626 | + // | vlen_v2 | | 642196 | + // | vlen_v2 | zstd 5 | 362626 | // | vlen | | 642580 | // | vlen | zstd 5 | 346950 | diff --git a/tests/data/v3/cities.zarr/zarr.json b/tests/data/v3/cities.zarr/zarr.json index 90ba194e..f4e5a16e 100644 --- a/tests/data/v3/cities.zarr/zarr.json +++ b/tests/data/v3/cities.zarr/zarr.json @@ -22,7 +22,7 @@ "fill_value": "", "codecs": [ { - "name": "https://codec.zarrs.dev/array_to_bytes/vlen_interleaved" + "name": "https://codec.zarrs.dev/array_to_bytes/vlen_v2" } ] } \ No newline at end of file From 16b6c2f2e66578dc897170c8c80648ce130697d0 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Thu, 25 Jul 2024 14:23:12 +1000 Subject: [PATCH 6/6] Fmt pass --- src/array/codec/array_to_bytes/vlen_v2.rs | 4 +--- src/metadata/array.rs | 9 ++++----- tests/cities.rs | 3 +-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/array/codec/array_to_bytes/vlen_v2.rs b/src/array/codec/array_to_bytes/vlen_v2.rs index 6c8c9778..d4d2a2c5 100644 --- a/src/array/codec/array_to_bytes/vlen_v2.rs +++ b/src/array/codec/array_to_bytes/vlen_v2.rs @@ -32,9 +32,7 @@ fn is_name_vlen_v2(name: &str) -> bool { name.eq(IDENTIFIER) } -pub(crate) fn create_codec_vlen_v2( - metadata: &MetadataV3, -) -> Result { +pub(crate) fn create_codec_vlen_v2(metadata: &MetadataV3) -> Result { let configuration: VlenV2CodecConfiguration = metadata .to_configuration() .map_err(|_| PluginMetadataInvalidError::new(IDENTIFIER, "codec", metadata.clone()))?; diff --git a/src/metadata/array.rs b/src/metadata/array.rs index 09f9ca8c..57d29652 100644 --- a/src/metadata/array.rs +++ b/src/metadata/array.rs @@ -148,11 +148,10 @@ pub fn array_metadata_v2_to_v3( match filter.id() { "vlen-utf8" | "vlen-bytes" | "vlen-array" => { is_vlen = true; - let vlen_v2_metadata = - MetadataV3::new_with_serializable_configuration( - super::v3::codec::vlen_v2::IDENTIFIER, - &VlenV2CodecConfigurationV1 {}, - )?; + let vlen_v2_metadata = MetadataV3::new_with_serializable_configuration( + super::v3::codec::vlen_v2::IDENTIFIER, + &VlenV2CodecConfigurationV1 {}, + )?; codecs.push(vlen_v2_metadata); } _ => { diff --git a/tests/cities.rs b/tests/cities.rs index d12e0244..08a544e0 100644 --- a/tests/cities.rs +++ b/tests/cities.rs @@ -8,8 +8,7 @@ use zarrs::{ array::{ codec::{ array_to_bytes::{ - sharding::ShardingCodecBuilder, vlen::VlenCodec, - vlen_v2::VlenV2Codec, + sharding::ShardingCodecBuilder, vlen::VlenCodec, vlen_v2::VlenV2Codec, }, ArrayToBytesCodecTraits, ZstdCodec, },