11<?php
22
3+ error_reporting (E_ALL );
4+ ini_set ('display_errors ' , '1 ' );
5+
6+ //die(minPostal('FR 54311 CEDEX', 'FR'));
7+
38$ cwd = dirname (realpath (__FILE__ ));
4- $ zipfile = $ cwd . ' /zip-codes-database-FREE.csv ' ;
5- if (! file_exists ( $ zipfile )) {
6- echo " Error: Take the latest free zip code database from zip-codes.com and extract the CSV next to this file and try again. \n" ;
7- exit ( 1 ) ;
8- }
9+ $ zipfile = $ cwd. ' /allCountries.zip ' ;
10+ $ txtfile = $ cwd . ' /allCountries.txt ' ;
11+ $ statsOnly = false ;
12+ $ regenKey = false ;
13+ $ keyArr = $ regenKey ? [] : json_decode ( file_get_contents ( $ cwd . ' /key.json ' ), true );
914
15+ // Download http://download.geonames.org/export/zip/allCountries.zip
16+ if (!file_exists ($ txtfile )) {
17+ echo "Downloading zipcodes... " ;
18+ $ ch = curl_init ();
19+ curl_setopt ($ ch , CURLOPT_URL , 'http://download.geonames.org/export/zip/allCountries.zip ' );
20+ curl_setopt ($ ch , CURLOPT_RETURNTRANSFER , 1 );
21+ $ data = curl_exec ($ ch );
22+ curl_close ($ ch );
23+ $ file = fopen ($ zipfile , "w+ " );
24+ fputs ($ file , $ data );
25+ fclose ($ file );
26+ $ zip = new ZipArchive ;
27+ $ res = $ zip ->open ($ zipfile );
28+ if ($ res === true ) {
29+ $ zip ->extractTo ('. ' );
30+ $ zip ->close ();
31+ unlink ($ zipfile );
32+ } else {
33+ die ('Unable to unzip. ' );
34+ }
35+ }
1036
11- echo "Cleaning any previous build... \n" ;
12- clean ($ cwd );
37+ // echo "Cleaning up from previous build...\n";
38+ // clean($cwd);
1339
1440$ row = 0 ;
15- $ headers = [];
16- if (($ handle = fopen ($ zipfile , 'r ' )) !== false ) {
17- while (($ data = fgetcsv ($ handle , 1000 , ', ' )) !== false ) {
18- if ($ row == 0 ) {
19- $ headers = $ data ;
20- } else {
21- $ zip = new stdClass ();
22- foreach ($ data as $ key => $ value ) {
23- $ keyname = $ headers [$ key ];
24- $ zip ->{$ keyname } = $ value ;
41+ $ headers = [
42+ 'countryCode ' , // iso country code, 2 characters
43+ 'postalCode ' , // varchar(20)
44+ 'placeName ' , // varchar(180)
45+ 'adminName1 ' , // 1. order subdivision (state) varchar(100)
46+ 'adminCode1 ' , // 1. order subdivision (state) varchar(20)
47+ 'adminName2 ' , // 2. order subdivision (county/province) varchar(100)
48+ 'adminCode2 ' , // 2. order subdivision (county/province) varchar(20)
49+ 'adminName3 ' , // 3. order subdivision (community) varchar(100)
50+ 'adminCode3 ' , // 3. order subdivision (community) varchar(20)
51+ 'latitude ' , // estimated latitude (wgs84)
52+ 'longitude ' , // estimated longitude (wgs84)
53+ 'accuracy ' , // accuracy of lat/lng from 1=estimated to 6=centroid
54+ ];
55+ $ stats = [];
56+ if (($ handle = fopen ($ txtfile , 'r ' )) !== false ) {
57+ while (($ data = fgetcsv ($ handle , 1000 , "\t" )) !== false ) {
58+ $ place = new stdClass ();
59+ foreach ($ data as $ key => $ value ) {
60+ $ keyname = $ headers [$ key ];
61+ $ place ->{$ keyname } = $ value ;
62+ }
63+ if (!empty ($ place ->countryCode ) && !empty ($ place ->postalCode )) {
64+ $ countryCode = strtoupper ($ place ->countryCode );
65+
66+ if ($ statsOnly ) {
67+ $ minCode = minPostal ($ place ->postalCode );
68+ // Two countries have too much numerical variance to list all zip-codes independently
69+ // given Github's current repository limits. For these we will only include the min JSON/JSONP.
70+ $ length = strlen ($ place ->postalCode );
71+
72+ if (!isset ($ stats [$ countryCode ])) {
73+ $ stats [$ countryCode ] = [
74+ 'count ' => 1 ,
75+ 'lowest ' => $ minCode ,
76+ 'highest ' => $ minCode ,
77+ ];
78+ } else {
79+ $ stats [$ countryCode ]['count ' ]++;
80+ if ($ minCode < $ stats [$ countryCode ]['lowest ' ]) {
81+ $ stats [$ countryCode ]['lowest ' ] = $ minCode ;
82+ } elseif ($ minCode > $ stats [$ countryCode ]['highest ' ]) {
83+ $ stats [$ countryCode ]['highest ' ] = $ minCode ;
84+ }
85+ }
86+ continue ;
2587 }
26- if (!empty ($ zip ->ZipCode )) {
27- echo "Creating files for " . $ zip ->ZipCode . "\n" ;
28- file_put_contents ($ cwd . '/ ' . $ zip ->ZipCode . '.json ' , json_encode ($ zip ));
29- file_put_contents ($ cwd . '/ ' . $ zip ->ZipCode . '.jsonp ' , 'zipCodeCallback( ' . json_encode ($ zip ) . '); ' );
88+
89+ $ minOnly = in_array ($ countryCode , ['JP ' , 'PT ' ]);
90+ echo 'Creating entry for ' .$ place ->countryCode .' ' .$ place ->postalCode .($ minOnly ? ' (min only) ' : '' ).' in index ' .$ minCodeRounded ."\n" ;
91+ if (!is_dir ($ cwd .'/ ' .$ countryCode )) {
92+ mkdir ($ cwd .'/ ' .$ countryCode );
93+ }
94+
95+ if (!file_exists ($ cwd .'/ ' .$ countryCode .'/test.jsonp ' )) {
96+ $ test = new stdClass ();
97+ $ test ->success = true ;
98+ file_put_contents ($ cwd .'/ ' .$ countryCode .'/test.jsonp ' , 'zipCodesTestCallback( ' .json_encode ($ test ).'); ' );
99+ }
100+ if (!$ minOnly ) {
101+ file_put_contents (
102+ $ cwd .'/ ' .$ countryCode .'/ ' .$ place ->postalCode .'.json ' ,
103+ json_encode ($ place )
104+ );
105+ }
106+
107+ if (!is_dir ($ cwd .'/ ' .$ countryCode .'/min ' )) {
108+ mkdir ($ cwd .'/ ' .$ countryCode .'/min ' );
30109 }
110+
111+ $ minCodeRounded = minPostalRounded ($ place ->postalCode , $ place ->countryCode , $ keyArr );
112+ $ codes = [];
113+ if (file_exists ($ cwd .'/ ' .$ countryCode .'/min/ ' .$ minCodeRounded .'.json ' )) {
114+ $ codes = json_decode (file_get_contents ($ cwd .'/ ' .$ countryCode .'/min/ ' .$ minCodeRounded .'.json ' ), true );
115+ }
116+ $ codes [$ place ->postalCode ] = $ data ;
117+ $ codesJSON = json_encode ($ codes );
118+ file_put_contents (
119+ $ cwd .'/ ' .$ countryCode .'/min/ ' .$ minCodeRounded .'.json ' ,
120+ $ codesJSON
121+ );
122+ file_put_contents (
123+ $ cwd .'/ ' .$ countryCode .'/min/ ' .$ minCodeRounded .'.jsonp ' ,
124+ 'zipCodesCallback( ' .$ codesJSON .'); '
125+ );
31126 }
32127 $ row ++;
33128 }
34129 fclose ($ handle );
35130}
36131
132+ // floor((X - lowest) / ((highest - lowest) / ceil(count / 1000))),
133+ // if (a > 1) { file = floor((X - b) / c); }
134+
135+ if ($ regenKey ) {
136+ $ key = [];
137+ foreach ($ stats as $ country => $ stat ) {
138+ $ key [$ country ] = [];
139+ if ($ stat ['count ' ] > 100000 ) {
140+ $ chars = 3 ;
141+ } elseif ($ stat ['count ' ] > 10000 ) {
142+ $ chars = 2 ;
143+ } elseif ($ stat ['count ' ] > 1000 ) {
144+ $ chars = 1 ;
145+ } else {
146+ $ chars = 0 ;
147+ }
148+ $ key [$ country ] = $ chars ;
149+
150+ }
151+ file_put_contents (
152+ $ cwd .'/key.json ' ,
153+ json_encode ($ key )
154+ );
155+ file_put_contents (
156+ $ cwd .'/key.js ' ,
157+ 'var key = ' .json_encode ($ key ).'; '
158+ );
159+ }
160+
161+ die (var_export ($ key , true ));
162+
37163/**
38- * Recursively delete files and folders within a directory based on type.
164+ * Recursively delete files within a directory based on type.
39165 * @param $dir
40- * @param null $root
41166 * @param array $types
42167 */
43- function clean ($ dir , $ root = null , $ types = ['json ' , 'jsonp ' ])
168+ function clean ($ dir , $ types = ['json ' , 'jsonp ' ])
44169{
45- if (!$ root ) {
46- $ root = $ dir ;
47- }
48170 if (is_dir ($ dir )) {
49171 $ objects = scandir ($ dir );
50172 foreach ($ objects as $ object ) {
51173 if ($ object != ". " && $ object != ".. " ) {
52174 if (is_dir ($ dir ."/ " .$ object )) {
53- clean ($ dir ."/ " .$ object , $ root , $ types );
175+ clean ($ dir ."/ " .$ object , $ types );
54176 } else {
55177 $ ext = pathinfo ($ object , PATHINFO_EXTENSION );
56178 if (in_array ($ ext , $ types )) {
@@ -59,11 +181,36 @@ function clean($dir, $root = null, $types = ['json', 'jsonp'])
59181 }
60182 }
61183 }
62- if ($ dir !== $ root ) {
63- try {
64- rmdir ($ dir );
65- } catch (Exception $ e ) {
66- }
67- }
68184 }
185+ }
186+
187+ function minPostal ($ postalCode )
188+ {
189+ // Only allow uppercase.
190+ $ postalCode = strtoupper ($ postalCode );
191+ // Remove non-alphanumeric characters.
192+ $ postalCode = preg_replace ("/[^A-Z0-9]/ " , '' , $ postalCode );
193+
194+ // Convert from base 36 to an integer.
195+ $ postalCode = base_convert ($ postalCode , 36 , 10 );
196+
197+ return $ postalCode ;
198+ }
199+
200+ function minPostalRounded ($ postalCode , $ country , $ keyArr = [])
201+ {
202+ $ result = 0 ;
203+
204+ if ($ keyArr [$ country ] > 1 ) {
205+
206+ // Only allow uppercase.
207+ $ postalCode = strtoupper ($ postalCode );
208+ // Remove non-alphanumeric characters.
209+ $ postalCode = preg_replace ("/[^A-Z0-9]/ " , '' , $ postalCode );
210+
211+ $ result = substr ($ postalCode , 0 , $ keyArr [$ country ]);
212+ }
213+
214+ return $ result ;
215+
69216}
0 commit comments