diff --git a/src/Benzina/TerritoryPumpTrait.php b/src/Benzina/TerritoryPumpTrait.php index 3cfe9ca..f9924de 100644 --- a/src/Benzina/TerritoryPumpTrait.php +++ b/src/Benzina/TerritoryPumpTrait.php @@ -12,76 +12,72 @@ trait TerritoryPumpTrait */ public static function cleanLocation(string $location, int $detailLevel = 3): string { - $location = trim($location); - - // 1. Skip URLs + // Skip web addresses if ( - str_starts_with($location, 'http://') - || str_starts_with($location, 'https://') - || str_starts_with($location, 'www.') + \str_starts_with($location, 'www.') + || \str_starts_with($location, 'http://') + || \str_starts_with($location, 'https://') ) { return ''; } - // 2. Normalize separators ONLY when clearly semantic (not street hyphens) - $location = preg_replace('/\s+(\/|\||&|\by\b|\bi\b|and)\s+/iu', ',', $location); + // Remove secondary conjoined places from locations + // e.g: "España y el mundo" -> "España" + foreach ([' / ', ' | ', ' - ', ' y ', ' i ', ' and ', ' & '] as $conjoinment) { + if (\str_contains($location, $conjoinment)) { + $location = \explode($conjoinment, $location)[0]; + } + } - // 3. Normalize parentheses -> commas - $location = str_replace(['(', ')'], [',', ''], $location); + // Normalize parenthesis + if (\str_contains($location, '(') || \str_contains($location, ')')) { + $location = \str_replace('(', ',', $location); + $location = \str_replace(')', '', $location); + } - // 4. Remove leading labels like "University X:" - $location = preg_replace('/^[^:]{1,80}:\s*/u', '', $location); + // Remove colon specifications + // e.g: "Universidad Carlos III de Madrid: Campus de Getafe, Calle Madrid, Getafe, España" -> "Campus de Getafe, Calle Madrid, Getafe, España" + $location = \preg_replace('/^[\w ]+:/', '', $location); - // 5. Tokenize - $parts = array_map( - fn($p) => trim($p), - explode(',', $location) - ); + // Process comma-separated address pieces + $location = \explode(',', $location); - // 6. Clean tokens - $parts = array_values(array_filter($parts, function ($p) { - if ($p === '') { + // Clean non desired location pieces + $location = \array_filter($location, function ($l) { + if (empty($l)) { return false; } - // Remove pure noise like empty numbers or coordinates - if (preg_match('/^\s*[-+]?\d+(\.\d+)?\s*$/', $p)) { + // Skip numeric only pieces: coordinates, street numbers, etc + if (\preg_match('/^[-\d.]*$/', $l) || \str_contains($l, 'º')) { return false; } return true; - })); + }); - // 7. Normalize tokens - $parts = array_map(function ($p) { - $p = trim($p); + // Normalize location pieces + $location = \array_map(function ($l) { + // Trim spaces and numbers at start of piece + $l = \preg_replace('/^[\d ]+/', '', \trim($l)); - // remove leading house numbers but keep street names - $p = preg_replace('/^\d+\s*/u', '', $p); - - $upper = mb_strtoupper($p, 'UTF-8'); - foreach (self::COMMON_VARIATIONS as $standard => $variants) { - if (in_array($upper, $variants, true)) { - return $standard; + // Normalize typos and name variations + foreach (self::COMMON_VARIATIONS as $standard => $variations) { + if (in_array(\mb_strtoupper($l), $variations)) { + $l = $standard; } } - return $p; - }, $parts); + return $l; + }, $location); - // 8. Keep only last N components (most specific context) - if (count($parts) > $detailLevel) { - $parts = array_slice($parts, -$detailLevel); - } + $location = \join(', ', \array_slice($location, -1 * $detailLevel)); - // 9. Join deterministically - $result = implode(', ', $parts); + // Trim remaining numbers and punctuation marks + $location = \preg_replace('/^[\d\.\,\-;]+/', '', $location); + $location = \preg_replace('/[\d\.\,\-;]+$/', '', $location); - // 10. Final cleanup - $result = preg_replace('/\s+/u', ' ', $result); - $result = trim($result, " \t\n\r\0\x0B,.-;"); - - return mb_strtoupper($result, 'UTF-8'); + return \mb_strtoupper(\trim($location)); } /**