· 2 years ago · Dec 29, 2022, 08:10 PM
1<?php
2
3namespace App\Support\Geocoders;
4
5use App\Support\MapInformationAbstract;
6use Illuminate\Support\Facades\Cache;
7use Excel;
8
9/**
10 * United States Census Bureau Free Geocoder.
11 * @see https://geocoding.geo.census.gov/geocoder/
12 */
13class USCB extends MapInformationAbstract implements GeocoderInterface
14{
15 /**
16 * Methods:
17 * - 'address' parse by array parts
18 * - 'onelineaddress' parse by a one-line address string
19 *
20 * @var string
21 */
22 protected $method = 'address';
23
24 /**
25 * Max rows of batch file.
26 *
27 * @var int
28 */
29 protected const BATCH_SIZE = 600;
30
31 /**
32 * Geocode an address.
33 *
34 * @param array|string $address
35 * @return array
36 */
37 public function process($address)
38 {
39 if (is_array($address)) {
40 $address = [
41 'street' => $address['address1'] . ' ' . $address['address2'],
42 'city' => $address['city'],
43 'state' => $address['state'],
44 'zip' => $address['zip'],
45 ];
46 $address_string = implode(' ', $address);
47
48 $query = $address;
49 } elseif (is_string($address)) {
50 $address_string = $address;
51
52 $this->method = 'onelineaddress';
53
54 $query = ['address' => $address];
55 }
56
57 if (Cache::has('geo:address:' . snake_case($address_string))) {
58 return Cache::get('geo:address:' . snake_case($address_string));
59 }
60
61 $query['format'] = 'json';
62 $query['benchmark'] = 'Public_AR_Current';
63
64 // make API request
65 $response = (new \GuzzleHttp\Client())->request(
66 'GET',
67 "https://geocoding.geo.census.gov/geocoder/locations/$this->method",
68 ['query' => $query]
69 );
70
71 $geocoder = json_decode($response->getBody()->getContents())->result;
72
73 if (isset($geocoder->addressMatches) && count($geocoder->addressMatches)) {
74 $coordinates = $geocoder->addressMatches[0]->coordinates;
75 $address_string = $geocoder->addressMatches[0]->matchedAddress;
76
77 $params = [
78 'coords' => ['x' => $coordinates->x, 'y' => $coordinates->y],
79 'address_string' => $address_string,
80 ];
81
82 Cache::put('geo:address:' . snake_case($address_string), $params, $this->cache);
83
84 return $params;
85 }
86
87 return [];
88 }
89
90 /**
91 * Geocode a batch of addresses.
92 *
93 * @param array $addresses
94 * @return array
95 */
96 public function batch($addresses)
97 {
98 $output_path = storage_path('tmp/Output.csv');
99
100 // if (!file_exists($output_path)) {
101 $addresses = collect($addresses)->unique(0)->values()->chunk(self::BATCH_SIZE);
102
103 if (!$addresses->count()) {
104 return [];
105 }
106
107 $addresses->each(function ($address_set, $key) use ($output_path) {
108 $filename = 'Addresses';
109 Excel::create($filename, function ($excel) use ($address_set) {
110 $excel->sheet('Sheet1', function ($sheet) use ($address_set) {
111 $sheet->fromArray($address_set, null, 'A1', false, false);
112 });
113 })->store('csv', storage_path('tmp'));
114
115 // make API request
116 $response = (new \GuzzleHttp\Client())->request(
117 'POST',
118 'https://geocoding.geo.census.gov/geocoder/locations/addressbatch',
119 [
120 'multipart' => [
121 [
122 'name' => 'benchmark',
123 'contents' => 'Public_AR_Current',
124 ],
125 [
126 'name' => 'addressFile',
127 'contents' => fopen(storage_path('tmp/' . $filename . '.csv'), 'r'),
128 ],
129 ],
130 'sink' => fopen($output_path, 'a'),
131 ]
132 );
133
134 sleep(3);
135 });
136 // }
137
138 $items = Excel::load($output_path, function ($reader) {
139 return $reader->noHeading();
140 })->toArray();
141
142 return $items;
143 }
144}
145
146?>
147<?php
148
149namespace App\Support\Geocoders;
150
151use Illuminate\Support\Collection;
152
153interface GeocoderInterface
154{
155 /**
156 * Geocode an address
157 * @param Collection $original_address
158 * @return Collection
159 */
160 public function process($original_address): Collection;
161}
162?>
163