├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── logs │ └── .gitignore ├── framework │ ├── testing │ │ └── .gitignore │ ├── views │ │ └── .gitignore │ ├── cache │ │ ├── data │ │ │ └── .gitignore │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ └── .gitignore └── clockwork │ └── .gitignore ├── public ├── robots.txt ├── logo.png ├── favicon.ico ├── background.jpg ├── .htaccess ├── index.php ├── style.css └── primary_area.xml ├── .gitattributes ├── .gitignore ├── .editorconfig ├── app ├── Http │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── TrustHosts.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── TrimStrings.php │ │ ├── Authenticate.php │ │ ├── TrustProxies.php │ │ └── RedirectIfAuthenticated.php │ ├── Controllers │ │ ├── Controller.php │ │ └── ForecastController.php │ └── Kernel.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── Console │ └── Kernel.php ├── Exceptions │ └── Handler.php └── Models │ └── Weather.php ├── .env.example ├── routes ├── api.php ├── console.php └── web.php ├── server.php ├── config ├── cors.php ├── services.php ├── view.php ├── hashing.php ├── broadcasting.php ├── filesystems.php ├── queue.php ├── cache.php ├── mail.php ├── logging.php ├── auth.php ├── database.php ├── session.php └── app.php ├── License.txt ├── artisan ├── composer.json ├── Readme.md └── resources └── views └── index.blade.php /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/clockwork/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.json.gz 3 | index 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/weather-api/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/weather-api/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsukumijima/weather-api/HEAD/public/background.jpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | schedule-* 4 | compiled.php 5 | services.json 6 | events.scanned.php 7 | routes.scanned.php 8 | down 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | .env 7 | .env.backup 8 | .phpunit.result.cache 9 | Homestead.json 10 | Homestead.yaml 11 | npm-debug.log 12 | yarn-error.log 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/PreventRequestsDuringMaintenance.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 18 | return route('login'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | })->describe('Display an inspiring quote'); 19 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\ModelPolicy', 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | $this->registerPolicies(); 27 | 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 19 | } 20 | 21 | /** 22 | * Register the commands for the application. 23 | * 24 | * @return void 25 | */ 26 | protected function commands() 27 | { 28 | $this->load(__DIR__.'/Commands'); 29 | 30 | require base_path('routes/console.php'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | ]; 22 | 23 | /** 24 | * Register any events for your application. 25 | * 26 | * @return void 27 | */ 28 | public function boot() 29 | { 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 26 | return redirect(RouteServiceProvider::HOME); 27 | } 28 | } 29 | 30 | return $next($request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | reportable(function (Throwable $e) { 38 | // 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2022 tsukumi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /app/Http/Controllers/ForecastController.php: -------------------------------------------------------------------------------- 1 | json($weather, Response::HTTP_OK, [], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT); 23 | } 24 | 25 | /** 26 | * クエリパラメータから取得した city_id に基づく天気予報を取得する 27 | * 28 | * @return \Illuminate\Http\Response 29 | */ 30 | public function index_query(Request $request) 31 | { 32 | if (!empty($request->input('city'))) { 33 | 34 | // 天気予報を取得 35 | $weather = Weather::getWeather($request->input('city')); 36 | 37 | // 取得した天気予報を JSON で返す 38 | return response()->json($weather, Response::HTTP_OK, [], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT); 39 | 40 | } else { 41 | 42 | // 404 を返す 43 | return \App::abort(404); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 1024, 48 | 'threads' => 2, 49 | 'time' => 2, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "type": "project", 4 | "description": "The Laravel Framework.", 5 | "keywords": [ 6 | "framework", 7 | "laravel" 8 | ], 9 | "license": "MIT", 10 | "require": { 11 | "php": "^7.3.0|^8.0", 12 | "fruitcake/laravel-cors": "^2.0", 13 | "guzzlehttp/guzzle": "^7.0.1", 14 | "itsgoingd/clockwork": "^5.1", 15 | "laravel/framework": "^8.0", 16 | "laravel/tinker": "^2.5", 17 | "wulfheart/pretty_routes": "^0.3.0" 18 | }, 19 | "require-dev": { 20 | "barryvdh/laravel-ide-helper": "^2.13.0", 21 | "facade/ignition": "^2.5", 22 | "fakerphp/faker": "^1.9.1", 23 | "mockery/mockery": "^1.4.4", 24 | "nunomaduro/collision": "^5.10", 25 | "phpunit/phpunit": "^9.5.10" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "App\\": "app/" 30 | } 31 | }, 32 | "scripts": { 33 | "post-autoload-dump": [ 34 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 35 | "@php artisan package:discover --ansi" 36 | ], 37 | "post-update-cmd": [ 38 | "@php artisan vendor:publish --tag=laravel-assets --ansi", 39 | "@php artisan ide-helper:generate" 40 | ], 41 | "post-root-package-install": [ 42 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 43 | ], 44 | "post-create-project-cmd": [ 45 | "@php artisan key:generate --ansi" 46 | ] 47 | }, 48 | "extra": { 49 | "laravel": { 50 | "dont-discover": [] 51 | } 52 | }, 53 | "config": { 54 | "optimize-autoloader": true, 55 | "preferred-install": "dist", 56 | "sort-packages": true 57 | }, 58 | "minimum-stability": "dev", 59 | "prefer-stable": true 60 | } 61 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | configureRateLimiting(); 39 | 40 | $this->routes(function () { 41 | Route::prefix('api') 42 | ->middleware('api') 43 | ->namespace($this->namespace) 44 | ->group(base_path('routes/api.php')); 45 | 46 | Route::middleware('web') 47 | ->namespace($this->namespace) 48 | ->group(base_path('routes/web.php')); 49 | }); 50 | } 51 | 52 | /** 53 | * Configure the rate limiters for the application. 54 | * 55 | * @return void 56 | */ 57 | protected function configureRateLimiting() 58 | { 59 | RateLimiter::for('api', function (Request $request) { 60 | return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'useTLS' => true, 41 | ], 42 | ], 43 | 44 | 'ably' => [ 45 | 'driver' => 'ably', 46 | 'key' => env('ABLY_KEY'), 47 | ], 48 | 49 | 'redis' => [ 50 | 'driver' => 'redis', 51 | 'connection' => 'default', 52 | ], 53 | 54 | 'log' => [ 55 | 'driver' => 'log', 56 | ], 57 | 58 | 'null' => [ 59 | 'driver' => 'null', 60 | ], 61 | 62 | ], 63 | 64 | ]; 65 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | define('LARAVEL_START', microtime(true)); 11 | 12 | /* 13 | |-------------------------------------------------------------------------- 14 | | Register The Auto Loader 15 | |-------------------------------------------------------------------------- 16 | | 17 | | Composer provides a convenient, automatically generated class loader for 18 | | our application. We just need to utilize it! We'll simply require it 19 | | into the script here so that we don't have to worry about manual 20 | | loading any of our classes later on. It feels great to relax. 21 | | 22 | */ 23 | 24 | require __DIR__.'/../vendor/autoload.php'; 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Turn On The Lights 29 | |-------------------------------------------------------------------------- 30 | | 31 | | We need to illuminate PHP development, so let us turn on the lights. 32 | | This bootstraps the framework and gets it ready for use, then it 33 | | will load up this application so that we can run it and send 34 | | the responses back to the browser and delight our users. 35 | | 36 | */ 37 | 38 | $app = require_once __DIR__.'/../bootstrap/app.php'; 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Run The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once we have the application, we can handle the incoming request 46 | | through the kernel, and send the associated response back to 47 | | the client's browser allowing them to enjoy the creative 48 | | and wonderful application we have prepared for them. 49 | | 50 | */ 51 | 52 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 53 | 54 | $response = $kernel->handle( 55 | $request = Illuminate\Http\Request::capture() 56 | ); 57 | 58 | $response->send(); 59 | 60 | $kernel->terminate($request, $response); 61 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure as many filesystem "disks" as you wish, and you 24 | | may even configure multiple disks of the same driver. Defaults have 25 | | been setup for each driver as an example of the required options. 26 | | 27 | | Supported Drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app'), 36 | ], 37 | 38 | 'public' => [ 39 | 'driver' => 'local', 40 | 'root' => storage_path('app/public'), 41 | 'url' => env('APP_URL').'/storage', 42 | 'visibility' => 'public', 43 | ], 44 | 45 | 's3' => [ 46 | 'driver' => 's3', 47 | 'key' => env('AWS_ACCESS_KEY_ID'), 48 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 49 | 'region' => env('AWS_DEFAULT_REGION'), 50 | 'bucket' => env('AWS_BUCKET'), 51 | 'url' => env('AWS_URL'), 52 | 'endpoint' => env('AWS_ENDPOINT'), 53 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 54 | ], 55 | 56 | ], 57 | 58 | /* 59 | |-------------------------------------------------------------------------- 60 | | Symbolic Links 61 | |-------------------------------------------------------------------------- 62 | | 63 | | Here you may configure the symbolic links that will be created when the 64 | | `storage:link` Artisan command is executed. The array keys should be 65 | | the locations of the links and the values should be their targets. 66 | | 67 | */ 68 | 69 | 'links' => [ 70 | public_path('storage') => storage_path('app/public'), 71 | ], 72 | 73 | ]; 74 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 33 | \App\Http\Middleware\EncryptCookies::class, 34 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 35 | // \Illuminate\Session\Middleware\StartSession::class, 36 | // \Illuminate\Session\Middleware\AuthenticateSession::class, 37 | // \Illuminate\View\Middleware\ShareErrorsFromSession::class, 38 | // \App\Http\Middleware\VerifyCsrfToken::class, 39 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 40 | ], 41 | 42 | 'api' => [ 43 | 'throttle:60,1', 44 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 45 | ], 46 | ]; 47 | 48 | /** 49 | * The application's route middleware. 50 | * 51 | * These middleware may be assigned to groups or used individually. 52 | * 53 | * @var array 54 | */ 55 | protected $routeMiddleware = [ 56 | 'auth' => \App\Http\Middleware\Authenticate::class, 57 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 58 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 59 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 60 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 61 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 62 | 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 63 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 64 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 65 | ]; 66 | } 67 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'sync'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection information for each server that 24 | | is used by your application. A default configuration has been added 25 | | for each back-end shipped with Laravel. You are free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'table' => 'jobs', 40 | 'queue' => 'default', 41 | 'retry_after' => 90, 42 | 'after_commit' => false, 43 | ], 44 | 45 | 'beanstalkd' => [ 46 | 'driver' => 'beanstalkd', 47 | 'host' => 'localhost', 48 | 'queue' => 'default', 49 | 'retry_after' => 90, 50 | 'block_for' => 0, 51 | 'after_commit' => false, 52 | ], 53 | 54 | 'sqs' => [ 55 | 'driver' => 'sqs', 56 | 'key' => env('AWS_ACCESS_KEY_ID'), 57 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 58 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 59 | 'queue' => env('SQS_QUEUE', 'default'), 60 | 'suffix' => env('SQS_SUFFIX'), 61 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 62 | 'after_commit' => false, 63 | ], 64 | 65 | 'redis' => [ 66 | 'driver' => 'redis', 67 | 'connection' => 'default', 68 | 'queue' => env('REDIS_QUEUE', 'default'), 69 | 'retry_after' => 90, 70 | 'block_for' => null, 71 | 'after_commit' => false, 72 | ], 73 | 74 | ], 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | Failed Queue Jobs 79 | |-------------------------------------------------------------------------- 80 | | 81 | | These options configure the behavior of failed queue job logging so you 82 | | can control which database and table are used to store the jobs that 83 | | have failed. You may change them to any database / table you wish. 84 | | 85 | */ 86 | 87 | 'failed' => [ 88 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 89 | 'database' => env('DB_CONNECTION', 'mysql'), 90 | 'table' => 'failed_jobs', 91 | ], 92 | 93 | ]; 94 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "apc", "array", "database", "file", 30 | | "memcached", "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'apc' => [ 37 | 'driver' => 'apc', 38 | ], 39 | 40 | 'array' => [ 41 | 'driver' => 'array', 42 | 'serialize' => false, 43 | ], 44 | 45 | 'database' => [ 46 | 'driver' => 'database', 47 | 'table' => 'cache', 48 | 'connection' => null, 49 | 'lock_connection' => null, 50 | ], 51 | 52 | 'file' => [ 53 | 'driver' => 'file', 54 | 'path' => storage_path('framework/cache/data'), 55 | ], 56 | 57 | 'memcached' => [ 58 | 'driver' => 'memcached', 59 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 60 | 'sasl' => [ 61 | env('MEMCACHED_USERNAME'), 62 | env('MEMCACHED_PASSWORD'), 63 | ], 64 | 'options' => [ 65 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 66 | ], 67 | 'servers' => [ 68 | [ 69 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 70 | 'port' => env('MEMCACHED_PORT', 11211), 71 | 'weight' => 100, 72 | ], 73 | ], 74 | ], 75 | 76 | 'redis' => [ 77 | 'driver' => 'redis', 78 | 'connection' => 'cache', 79 | 'lock_connection' => 'default', 80 | ], 81 | 82 | 'dynamodb' => [ 83 | 'driver' => 'dynamodb', 84 | 'key' => env('AWS_ACCESS_KEY_ID'), 85 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 86 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 87 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 88 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 89 | ], 90 | 91 | 'octane' => [ 92 | 'driver' => 'octane', 93 | ], 94 | 95 | ], 96 | 97 | /* 98 | |-------------------------------------------------------------------------- 99 | | Cache Key Prefix 100 | |-------------------------------------------------------------------------- 101 | | 102 | | When utilizing a RAM based store such as APC or Memcached, there might 103 | | be other applications utilizing the same cache. So, we'll specify a 104 | | value to get prefixed to all our keys so we can avoid collisions. 105 | | 106 | */ 107 | 108 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'), 109 | 110 | ]; 111 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'smtp'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Mailer Configurations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure all of the mailers used by your application plus 24 | | their respective settings. Several examples have been configured for 25 | | you and you are free to add your own as your application requires. 26 | | 27 | | Laravel supports a variety of mail "transport" drivers to be used while 28 | | sending an e-mail. You will specify which one you are using for your 29 | | mailers below. You are free to add additional mailers as required. 30 | | 31 | | Supported: "smtp", "sendmail", "mailgun", "ses", 32 | | "postmark", "log", "array", "failover" 33 | | 34 | */ 35 | 36 | 'mailers' => [ 37 | 'smtp' => [ 38 | 'transport' => 'smtp', 39 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 40 | 'port' => env('MAIL_PORT', 587), 41 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 42 | 'username' => env('MAIL_USERNAME'), 43 | 'password' => env('MAIL_PASSWORD'), 44 | 'timeout' => null, 45 | 'auth_mode' => null, 46 | ], 47 | 48 | 'ses' => [ 49 | 'transport' => 'ses', 50 | ], 51 | 52 | 'mailgun' => [ 53 | 'transport' => 'mailgun', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | ], 59 | 60 | 'sendmail' => [ 61 | 'transport' => 'sendmail', 62 | 'path' => '/usr/sbin/sendmail -bs', 63 | ], 64 | 65 | 'log' => [ 66 | 'transport' => 'log', 67 | 'channel' => env('MAIL_LOG_CHANNEL'), 68 | ], 69 | 70 | 'array' => [ 71 | 'transport' => 'array', 72 | ], 73 | 74 | 'failover' => [ 75 | 'transport' => 'failover', 76 | 'mailers' => [ 77 | 'smtp', 78 | 'log', 79 | ], 80 | ], 81 | ], 82 | 83 | /* 84 | |-------------------------------------------------------------------------- 85 | | Global "From" Address 86 | |-------------------------------------------------------------------------- 87 | | 88 | | You may wish for all e-mails sent by your application to be sent from 89 | | the same address. Here, you may specify a name and address that is 90 | | used globally for all e-mails that are sent by your application. 91 | | 92 | */ 93 | 94 | 'from' => [ 95 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 96 | 'name' => env('MAIL_FROM_NAME', 'Example'), 97 | ], 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Markdown Mail Settings 102 | |-------------------------------------------------------------------------- 103 | | 104 | | If you are using Markdown based email rendering, you may configure your 105 | | theme and component paths here, allowing you to customize the design 106 | | of the emails. Or, you may simply stick with the Laravel defaults! 107 | | 108 | */ 109 | 110 | 'markdown' => [ 111 | 'theme' => 'default', 112 | 113 | 'paths' => [ 114 | resource_path('views/vendor/mail'), 115 | ], 116 | ], 117 | 118 | ]; 119 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Deprecations Log Channel 25 | |-------------------------------------------------------------------------- 26 | | 27 | | This option controls the log channel that should be used to log warnings 28 | | regarding deprecated PHP and library features. This allows you to get 29 | | your application ready for upcoming major versions of dependencies. 30 | | 31 | */ 32 | 33 | 'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Log Channels 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may configure the log channels for your application. Out of 41 | | the box, Laravel uses the Monolog PHP logging library. This gives 42 | | you a variety of powerful log handlers / formatters to utilize. 43 | | 44 | | Available Drivers: "single", "daily", "slack", "syslog", 45 | | "errorlog", "monolog", 46 | | "custom", "stack" 47 | | 48 | */ 49 | 50 | 'channels' => [ 51 | 'stack' => [ 52 | 'driver' => 'stack', 53 | 'channels' => ['single'], 54 | 'ignore_exceptions' => false, 55 | ], 56 | 57 | 'single' => [ 58 | 'driver' => 'single', 59 | 'path' => storage_path('logs/laravel.log'), 60 | 'level' => env('LOG_LEVEL', 'debug'), 61 | ], 62 | 63 | 'daily' => [ 64 | 'driver' => 'daily', 65 | 'path' => storage_path('logs/laravel.log'), 66 | 'level' => env('LOG_LEVEL', 'debug'), 67 | 'days' => 14, 68 | ], 69 | 70 | 'slack' => [ 71 | 'driver' => 'slack', 72 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 73 | 'username' => 'Laravel Log', 74 | 'emoji' => ':boom:', 75 | 'level' => env('LOG_LEVEL', 'critical'), 76 | ], 77 | 78 | 'papertrail' => [ 79 | 'driver' => 'monolog', 80 | 'level' => env('LOG_LEVEL', 'debug'), 81 | 'handler' => SyslogUdpHandler::class, 82 | 'handler_with' => [ 83 | 'host' => env('PAPERTRAIL_URL'), 84 | 'port' => env('PAPERTRAIL_PORT'), 85 | ], 86 | ], 87 | 88 | 'stderr' => [ 89 | 'driver' => 'monolog', 90 | 'level' => env('LOG_LEVEL', 'debug'), 91 | 'handler' => StreamHandler::class, 92 | 'formatter' => env('LOG_STDERR_FORMATTER'), 93 | 'with' => [ 94 | 'stream' => 'php://stderr', 95 | ], 96 | ], 97 | 98 | 'syslog' => [ 99 | 'driver' => 'syslog', 100 | 'level' => env('LOG_LEVEL', 'debug'), 101 | ], 102 | 103 | 'errorlog' => [ 104 | 'driver' => 'errorlog', 105 | 'level' => env('LOG_LEVEL', 'debug'), 106 | ], 107 | 108 | 'null' => [ 109 | 'driver' => 'monolog', 110 | 'handler' => NullHandler::class, 111 | ], 112 | 113 | 'emergency' => [ 114 | 'path' => storage_path('logs/laravel.log'), 115 | ], 116 | ], 117 | 118 | ]; 119 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication drivers have a user provider. This defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | mechanisms used by this application to persist your user's data. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | sources which represent each model / table. These sources may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => App\Models\User::class, 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | You may specify multiple password reset configurations if you have more 80 | | than one user table or model in the application and you want to have 81 | | separate password reset settings based on the specific user types. 82 | | 83 | | The expire time is the number of minutes that the reset token should be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | */ 88 | 89 | 'passwords' => [ 90 | 'users' => [ 91 | 'provider' => 'users', 92 | 'table' => 'password_resets', 93 | 'expire' => 60, 94 | 'throttle' => 60, 95 | ], 96 | ], 97 | 98 | /* 99 | |-------------------------------------------------------------------------- 100 | | Password Confirmation Timeout 101 | |-------------------------------------------------------------------------- 102 | | 103 | | Here you may define the amount of seconds before a password confirmation 104 | | times out and the user is prompted to re-enter their password via the 105 | | confirmation screen. By default, the timeout lasts for three hours. 106 | | 107 | */ 108 | 109 | 'password_timeout' => 10800, 110 | 111 | ]; 112 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # weather-api 3 | 4 | 天気予報 API(livedoor 天気互換)のソースコードです。Laravel 8 で構築されています。 5 | 6 | ## Setup 7 | 8 | 以下は Apache + PHP 7.4 + mod_php で動かす際の手順になります。 9 | 10 | 1. あらかじめ Apache で PHP 7.4 が動く状態にしておく 11 | 2. Laravel に必要な PHP の拡張機能をでインストールする 12 | - Ubuntu なら `sudo apt install php7.4-bcmath php7.4-curl php7.4-mbstring php7.4-xml php7.4-zip` でインストールできる 13 | 3. .env.example を .env にコピーする 14 | 4. .env を手元の環境に合わせて編集する 15 | 5. プロジェクトのディレクトリ内で `composer install` を実行する 16 | 6. Apache の設定で `public/` 以下をドキュメントルートに設定する 17 | 7. アクセスできるか確認する 18 | 19 | ## License 20 | [MIT License](License.txt) 21 | 22 | 23 | ------------------ 24 | 25 |

26 | 27 |

28 | Build Status 29 | Total Downloads 30 | Latest Stable Version 31 | License 32 |

33 | 34 | ## About Laravel 35 | 36 | Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: 37 | 38 | - [Simple, fast routing engine](https://laravel.com/docs/routing). 39 | - [Powerful dependency injection container](https://laravel.com/docs/container). 40 | - Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. 41 | - Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). 42 | - Database agnostic [schema migrations](https://laravel.com/docs/migrations). 43 | - [Robust background job processing](https://laravel.com/docs/queues). 44 | - [Real-time event broadcasting](https://laravel.com/docs/broadcasting). 45 | 46 | Laravel is accessible, powerful, and provides tools required for large, robust applications. 47 | 48 | ## Learning Laravel 49 | 50 | Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. 51 | 52 | If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. 53 | 54 | ## Laravel Sponsors 55 | 56 | We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell). 57 | 58 | ### Premium Partners 59 | 60 | - **[Vehikl](https://vehikl.com/)** 61 | - **[Tighten Co.](https://tighten.co)** 62 | - **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** 63 | - **[64 Robots](https://64robots.com)** 64 | - **[Cubet Techno Labs](https://cubettech.com)** 65 | - **[Cyber-Duck](https://cyber-duck.co.uk)** 66 | - **[Many](https://www.many.co.uk)** 67 | - **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)** 68 | - **[DevSquad](https://devsquad.com)** 69 | - **[Curotec](https://www.curotec.com/services/technologies/laravel/)** 70 | - **[OP.GG](https://op.gg)** 71 | - **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)** 72 | - **[Lendio](https://lendio.com)** 73 | 74 | ## Contributing 75 | 76 | Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). 77 | 78 | ## Code of Conduct 79 | 80 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). 81 | 82 | ## Security Vulnerabilities 83 | 84 | If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. 85 | 86 | ## License 87 | 88 | The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 89 | -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | 2 | *:focus { 3 | outline: none !important; 4 | } 5 | 6 | html { 7 | position: relative; 8 | min-height: 100%; 9 | padding-bottom: 55px; 10 | box-sizing: border-box; 11 | } 12 | 13 | body { 14 | background-image: url(/background.jpg); 15 | background-repeat: no-repeat; 16 | background-attachment: fixed; 17 | background-size: cover; 18 | line-height: 1.6; 19 | margin-top: 82px; 20 | } 21 | 22 | i { 23 | margin-right: 10px; 24 | } 25 | 26 | li { 27 | margin-bottom: 3px; 28 | } 29 | li:last-of-type { 30 | margin-bottom: 0; 31 | } 32 | 33 | pre { 34 | padding: 1.75em 1.5em; 35 | border: 3px solid #ccc; 36 | color: #f8f8f2; 37 | text-shadow: 0 1px #1a1a1a; 38 | background: #2b2a43eb; 39 | font-size: 13px; 40 | font-family: "Menlo", "Consolas", "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Meiryo", monospace; 41 | } 42 | 43 | table { 44 | display: block; 45 | overflow-x: auto; 46 | white-space: nowrap; 47 | -webkit-overflow-scrolling: touch; 48 | } 49 | table tbody { 50 | display: table; 51 | width: 100%; 52 | } 53 | 54 | h2.card-header { 55 | font-size: 1rem; 56 | } 57 | 58 | .navbar-brand { 59 | display: flex; 60 | align-items: center; 61 | } 62 | 63 | .navbar-brand img { 64 | height: 33px; 65 | margin-right: 7px; 66 | } 67 | 68 | @media (max-width: 450px) { 69 | .navbar-brand { 70 | font-size: 14.7px !important; 71 | margin-right: auto; 72 | } 73 | } 74 | 75 | @media (max-width: 380px) { 76 | .navbar-brand img { 77 | height: 20px; 78 | } 79 | .navbar-brand { 80 | font-size: 13px !important; 81 | margin-right: auto; 82 | } 83 | } 84 | 85 | @media (min-width: 768px) { 86 | .navbar-expand-md .navbar-nav .nav-link { 87 | padding-right: .7rem; 88 | padding-left: .7rem; 89 | } 90 | } 91 | 92 | @media (min-width: 768px) { 93 | .navbar-expand-md .navbar-nav .nav-link { 94 | padding-right: .7rem; 95 | padding-left: .7rem; 96 | } 97 | } 98 | 99 | @media (max-width: 1199px) { 100 | .navbar-brand { 101 | font-size: 18px; 102 | } 103 | .navbar-expand-md .navbar-nav .nav-link { 104 | padding-right: .5rem; 105 | padding-left: .5rem; 106 | font-size: 14.8px; 107 | } 108 | } 109 | 110 | @media (max-width: 961px) { 111 | .navbar-expand-md .navbar-nav .nav-link { 112 | padding-right: .7rem; 113 | padding-left: .7rem; 114 | } 115 | } 116 | 117 | @media (min-width: 768px) { 118 | .navbar-expand-md .navbar-toggler { 119 | display: block; 120 | order: 1; 121 | } 122 | .navbar-expand-md .navbar-collapse { 123 | display: block !important; 124 | -ms-flex-positive: 1; 125 | flex-basis: 100%; 126 | } 127 | .navbar-expand-md .navbar-collapse.collapse:not(.show) { 128 | display: none !important; 129 | } 130 | .navbar-expand-md .navbar-nav { 131 | -ms-flex-direction: column; 132 | flex-direction: column; 133 | } 134 | } 135 | 136 | @media (min-width: 992px) { 137 | .navbar-expand-md .navbar-toggler { 138 | display: none; 139 | } 140 | .navbar-expand-md .navbar-collapse { 141 | display: -ms-flexbox !important; 142 | display: flex !important; 143 | -ms-flex-preferred-size: auto; 144 | flex-basis: auto; 145 | } 146 | .navbar-expand-md .navbar-collapse.collapse:not(.show) { 147 | display: -ms-flexbox!important; 148 | display: flex !important; 149 | } 150 | .navbar-expand-md .navbar-nav { 151 | -ms-flex-direction: row; 152 | flex-direction: row; 153 | } 154 | } 155 | 156 | .card-body ul { 157 | margin-bottom: 5px !important; 158 | padding-left: 30px; 159 | } 160 | 161 | a.text-light, a.text-light { 162 | transition: background-color .15s cubic-bezier(.4,0,.6,1); 163 | border-radius: 4px; 164 | } 165 | 166 | a.text-light:hover, a.text-light:focus { 167 | color: #f8f9fa !important; 168 | } 169 | 170 | a.text-light:hover { 171 | background: rgba(0, 0, 0, 0.08); 172 | } 173 | 174 | #navigation { 175 | background: #2399f6 !important; 176 | overflow: hidden; 177 | } 178 | 179 | table.table tr:last-of-type { 180 | border-bottom: 1px solid #dee2e6; 181 | } 182 | 183 | table > tbody > tr > th{ 184 | background: #dcf3ff; 185 | } 186 | 187 | @media (min-width: 1200px) { 188 | .container, .container-lg, .container-md, .container-sm, .container-xl { 189 | max-width: 1150px; 190 | } 191 | } 192 | 193 | @media screen and (max-width: 500px) { 194 | ol.mb-0 { 195 | padding-inline-start: 20px; 196 | } 197 | } 198 | 199 | .column { 200 | border: 1px solid #dee2e6; 201 | } 202 | 203 | .column div { 204 | margin: 10px 30px 6px; 205 | } 206 | 207 | .column a{ 208 | margin-left: 30px; 209 | } 210 | 211 | #footer { 212 | position: absolute; 213 | left: 0; 214 | bottom: 0; 215 | width: 100%; 216 | } 217 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'mysql'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Database Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here are each of the database connections setup for your application. 26 | | Of course, examples of configuring each database platform that is 27 | | supported by Laravel is shown below to make development simple. 28 | | 29 | | 30 | | All database work in Laravel is done through the PHP PDO facilities 31 | | so make sure you have the driver for your particular database of 32 | | choice installed on your machine before you begin development. 33 | | 34 | */ 35 | 36 | 'connections' => [ 37 | 38 | 'sqlite' => [ 39 | 'driver' => 'sqlite', 40 | 'url' => env('DATABASE_URL'), 41 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 42 | 'prefix' => '', 43 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 44 | ], 45 | 46 | 'mysql' => [ 47 | 'driver' => 'mysql', 48 | 'url' => env('DATABASE_URL'), 49 | 'host' => env('DB_HOST', '127.0.0.1'), 50 | 'port' => env('DB_PORT', '3306'), 51 | 'database' => env('DB_DATABASE', 'forge'), 52 | 'username' => env('DB_USERNAME', 'forge'), 53 | 'password' => env('DB_PASSWORD', ''), 54 | 'unix_socket' => env('DB_SOCKET', ''), 55 | 'charset' => 'utf8mb4', 56 | 'collation' => 'utf8mb4_unicode_ci', 57 | 'prefix' => '', 58 | 'prefix_indexes' => true, 59 | 'strict' => true, 60 | 'engine' => null, 61 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 62 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 63 | ]) : [], 64 | ], 65 | 66 | 'pgsql' => [ 67 | 'driver' => 'pgsql', 68 | 'url' => env('DATABASE_URL'), 69 | 'host' => env('DB_HOST', '127.0.0.1'), 70 | 'port' => env('DB_PORT', '5432'), 71 | 'database' => env('DB_DATABASE', 'forge'), 72 | 'username' => env('DB_USERNAME', 'forge'), 73 | 'password' => env('DB_PASSWORD', ''), 74 | 'charset' => 'utf8', 75 | 'prefix' => '', 76 | 'prefix_indexes' => true, 77 | 'schema' => 'public', 78 | 'sslmode' => 'prefer', 79 | ], 80 | 81 | 'sqlsrv' => [ 82 | 'driver' => 'sqlsrv', 83 | 'url' => env('DATABASE_URL'), 84 | 'host' => env('DB_HOST', 'localhost'), 85 | 'port' => env('DB_PORT', '1433'), 86 | 'database' => env('DB_DATABASE', 'forge'), 87 | 'username' => env('DB_USERNAME', 'forge'), 88 | 'password' => env('DB_PASSWORD', ''), 89 | 'charset' => 'utf8', 90 | 'prefix' => '', 91 | 'prefix_indexes' => true, 92 | ], 93 | 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Migration Repository Table 99 | |-------------------------------------------------------------------------- 100 | | 101 | | This table keeps track of all the migrations that have already run for 102 | | your application. Using this information, we can determine which of 103 | | the migrations on disk haven't actually been run in the database. 104 | | 105 | */ 106 | 107 | 'migrations' => 'migrations', 108 | 109 | /* 110 | |-------------------------------------------------------------------------- 111 | | Redis Databases 112 | |-------------------------------------------------------------------------- 113 | | 114 | | Redis is an open source, fast, and advanced key-value store that also 115 | | provides a richer body of commands than a typical key-value system 116 | | such as APC or Memcached. Laravel makes it easy to dig right in. 117 | | 118 | */ 119 | 120 | 'redis' => [ 121 | 122 | 'client' => env('REDIS_CLIENT', 'phpredis'), 123 | 124 | 'options' => [ 125 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 126 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 127 | ], 128 | 129 | 'default' => [ 130 | 'url' => env('REDIS_URL'), 131 | 'host' => env('REDIS_HOST', '127.0.0.1'), 132 | 'password' => env('REDIS_PASSWORD', null), 133 | 'port' => env('REDIS_PORT', '6379'), 134 | 'database' => env('REDIS_DB', '0'), 135 | ], 136 | 137 | 'cache' => [ 138 | 'url' => env('REDIS_URL'), 139 | 'host' => env('REDIS_HOST', '127.0.0.1'), 140 | 'password' => env('REDIS_PASSWORD', null), 141 | 'port' => env('REDIS_PORT', '6379'), 142 | 'database' => env('REDIS_CACHE_DB', '1'), 143 | ], 144 | 145 | ], 146 | 147 | ]; 148 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'file'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Session Lifetime 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may specify the number of minutes that you wish the session 29 | | to be allowed to remain idle before it expires. If you want them 30 | | to immediately expire on the browser closing, set that option. 31 | | 32 | */ 33 | 34 | 'lifetime' => env('SESSION_LIFETIME', 120), 35 | 36 | 'expire_on_close' => false, 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Session Encryption 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This option allows you to easily specify that all of your session data 44 | | should be encrypted before it is stored. All encryption will be run 45 | | automatically by Laravel and you can use the Session like normal. 46 | | 47 | */ 48 | 49 | 'encrypt' => false, 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Session File Location 54 | |-------------------------------------------------------------------------- 55 | | 56 | | When using the native session driver, we need a location where session 57 | | files may be stored. A default has been set for you but a different 58 | | location may be specified. This is only needed for file sessions. 59 | | 60 | */ 61 | 62 | 'files' => storage_path('framework/sessions'), 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Session Database Connection 67 | |-------------------------------------------------------------------------- 68 | | 69 | | When using the "database" or "redis" session drivers, you may specify a 70 | | connection that should be used to manage these sessions. This should 71 | | correspond to a connection in your database configuration options. 72 | | 73 | */ 74 | 75 | 'connection' => env('SESSION_CONNECTION', null), 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Session Database Table 80 | |-------------------------------------------------------------------------- 81 | | 82 | | When using the "database" session driver, you may specify the table we 83 | | should use to manage the sessions. Of course, a sensible default is 84 | | provided for you; however, you are free to change this as needed. 85 | | 86 | */ 87 | 88 | 'table' => 'sessions', 89 | 90 | /* 91 | |-------------------------------------------------------------------------- 92 | | Session Cache Store 93 | |-------------------------------------------------------------------------- 94 | | 95 | | While using one of the framework's cache driven session backends you may 96 | | list a cache store that should be used for these sessions. This value 97 | | must match with one of the application's configured cache "stores". 98 | | 99 | | Affects: "apc", "dynamodb", "memcached", "redis" 100 | | 101 | */ 102 | 103 | 'store' => env('SESSION_STORE', null), 104 | 105 | /* 106 | |-------------------------------------------------------------------------- 107 | | Session Sweeping Lottery 108 | |-------------------------------------------------------------------------- 109 | | 110 | | Some session drivers must manually sweep their storage location to get 111 | | rid of old sessions from storage. Here are the chances that it will 112 | | happen on a given request. By default, the odds are 2 out of 100. 113 | | 114 | */ 115 | 116 | 'lottery' => [2, 100], 117 | 118 | /* 119 | |-------------------------------------------------------------------------- 120 | | Session Cookie Name 121 | |-------------------------------------------------------------------------- 122 | | 123 | | Here you may change the name of the cookie used to identify a session 124 | | instance by ID. The name specified here will get used every time a 125 | | new session cookie is created by the framework for every driver. 126 | | 127 | */ 128 | 129 | 'cookie' => env( 130 | 'SESSION_COOKIE', 131 | Str::slug(env('APP_NAME', 'laravel'), '_').'_session' 132 | ), 133 | 134 | /* 135 | |-------------------------------------------------------------------------- 136 | | Session Cookie Path 137 | |-------------------------------------------------------------------------- 138 | | 139 | | The session cookie path determines the path for which the cookie will 140 | | be regarded as available. Typically, this will be the root path of 141 | | your application but you are free to change this when necessary. 142 | | 143 | */ 144 | 145 | 'path' => '/', 146 | 147 | /* 148 | |-------------------------------------------------------------------------- 149 | | Session Cookie Domain 150 | |-------------------------------------------------------------------------- 151 | | 152 | | Here you may change the domain of the cookie used to identify a session 153 | | in your application. This will determine which domains the cookie is 154 | | available to in your application. A sensible default has been set. 155 | | 156 | */ 157 | 158 | 'domain' => env('SESSION_DOMAIN', null), 159 | 160 | /* 161 | |-------------------------------------------------------------------------- 162 | | HTTPS Only Cookies 163 | |-------------------------------------------------------------------------- 164 | | 165 | | By setting this option to true, session cookies will only be sent back 166 | | to the server if the browser has a HTTPS connection. This will keep 167 | | the cookie from being sent to you when it can't be done securely. 168 | | 169 | */ 170 | 171 | 'secure' => env('SESSION_SECURE_COOKIE'), 172 | 173 | /* 174 | |-------------------------------------------------------------------------- 175 | | HTTP Access Only 176 | |-------------------------------------------------------------------------- 177 | | 178 | | Setting this value to true will prevent JavaScript from accessing the 179 | | value of the cookie and the cookie will only be accessible through 180 | | the HTTP protocol. You are free to modify this option if needed. 181 | | 182 | */ 183 | 184 | 'http_only' => true, 185 | 186 | /* 187 | |-------------------------------------------------------------------------- 188 | | Same-Site Cookies 189 | |-------------------------------------------------------------------------- 190 | | 191 | | This option determines how your cookies behave when cross-site requests 192 | | take place, and can be used to mitigate CSRF attacks. By default, we 193 | | will set this value to "lax" since this is a secure default value. 194 | | 195 | | Supported: "lax", "strict", "none", null 196 | | 197 | */ 198 | 199 | 'same_site' => 'lax', 200 | 201 | ]; 202 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | your application so that it is used when running Artisan tasks. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | 'asset_url' => env('ASSET_URL', null), 58 | 59 | 'gtag' => env('APP_GTAG', 'UA-000000000-1'), 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Application Timezone 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Here you may specify the default timezone for your application, which 67 | | will be used by the PHP date and date-time functions. We have gone 68 | | ahead and set this to a sensible default for you out of the box. 69 | | 70 | */ 71 | 72 | 'timezone' => 'Asia/Tokyo', 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Application Locale Configuration 77 | |-------------------------------------------------------------------------- 78 | | 79 | | The application locale determines the default locale that will be used 80 | | by the translation service provider. You are free to set this value 81 | | to any of the locales which will be supported by the application. 82 | | 83 | */ 84 | 85 | 'locale' => 'ja', 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Application Fallback Locale 90 | |-------------------------------------------------------------------------- 91 | | 92 | | The fallback locale determines the locale to use when the current one 93 | | is not available. You may change the value to correspond to any of 94 | | the language folders that are provided through your application. 95 | | 96 | */ 97 | 98 | 'fallback_locale' => 'en', 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Faker Locale 103 | |-------------------------------------------------------------------------- 104 | | 105 | | This locale will be used by the Faker PHP library when generating fake 106 | | data for your database seeds. For example, this will be used to get 107 | | localized telephone numbers, street address information and more. 108 | | 109 | */ 110 | 111 | 'faker_locale' => 'ja_JP', 112 | 113 | /* 114 | |-------------------------------------------------------------------------- 115 | | Encryption Key 116 | |-------------------------------------------------------------------------- 117 | | 118 | | This key is used by the Illuminate encrypter service and should be set 119 | | to a random, 32 character string, otherwise these encrypted strings 120 | | will not be safe. Please do this before deploying an application! 121 | | 122 | */ 123 | 124 | 'key' => env('APP_KEY'), 125 | 126 | 'cipher' => 'AES-256-CBC', 127 | 128 | /* 129 | |-------------------------------------------------------------------------- 130 | | Autoloaded Service Providers 131 | |-------------------------------------------------------------------------- 132 | | 133 | | The service providers listed here will be automatically loaded on the 134 | | request to your application. Feel free to add your own services to 135 | | this array to grant expanded functionality to your applications. 136 | | 137 | */ 138 | 139 | 'providers' => [ 140 | 141 | /* 142 | * Laravel Framework Service Providers... 143 | */ 144 | Illuminate\Auth\AuthServiceProvider::class, 145 | Illuminate\Broadcasting\BroadcastServiceProvider::class, 146 | Illuminate\Bus\BusServiceProvider::class, 147 | Illuminate\Cache\CacheServiceProvider::class, 148 | Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, 149 | Illuminate\Cookie\CookieServiceProvider::class, 150 | Illuminate\Database\DatabaseServiceProvider::class, 151 | Illuminate\Encryption\EncryptionServiceProvider::class, 152 | Illuminate\Filesystem\FilesystemServiceProvider::class, 153 | Illuminate\Foundation\Providers\FoundationServiceProvider::class, 154 | Illuminate\Hashing\HashServiceProvider::class, 155 | Illuminate\Mail\MailServiceProvider::class, 156 | Illuminate\Notifications\NotificationServiceProvider::class, 157 | Illuminate\Pagination\PaginationServiceProvider::class, 158 | Illuminate\Pipeline\PipelineServiceProvider::class, 159 | Illuminate\Queue\QueueServiceProvider::class, 160 | Illuminate\Redis\RedisServiceProvider::class, 161 | Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, 162 | Illuminate\Session\SessionServiceProvider::class, 163 | Illuminate\Translation\TranslationServiceProvider::class, 164 | Illuminate\Validation\ValidationServiceProvider::class, 165 | Illuminate\View\ViewServiceProvider::class, 166 | 167 | /* 168 | * Package Service Providers... 169 | */ 170 | 171 | /* 172 | * Application Service Providers... 173 | */ 174 | App\Providers\AppServiceProvider::class, 175 | App\Providers\AuthServiceProvider::class, 176 | // App\Providers\BroadcastServiceProvider::class, 177 | App\Providers\EventServiceProvider::class, 178 | App\Providers\RouteServiceProvider::class, 179 | 180 | ], 181 | 182 | /* 183 | |-------------------------------------------------------------------------- 184 | | Class Aliases 185 | |-------------------------------------------------------------------------- 186 | | 187 | | This array of class aliases will be registered when this application 188 | | is started. However, feel free to register as many as you wish as 189 | | the aliases are "lazy" loaded so they don't hinder performance. 190 | | 191 | */ 192 | 193 | 'aliases' => [ 194 | 195 | 'App' => Illuminate\Support\Facades\App::class, 196 | 'Arr' => Illuminate\Support\Arr::class, 197 | 'Artisan' => Illuminate\Support\Facades\Artisan::class, 198 | 'Auth' => Illuminate\Support\Facades\Auth::class, 199 | 'Blade' => Illuminate\Support\Facades\Blade::class, 200 | 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 201 | 'Bus' => Illuminate\Support\Facades\Bus::class, 202 | 'Cache' => Illuminate\Support\Facades\Cache::class, 203 | 'Config' => Illuminate\Support\Facades\Config::class, 204 | 'Cookie' => Illuminate\Support\Facades\Cookie::class, 205 | 'Crypt' => Illuminate\Support\Facades\Crypt::class, 206 | 'Date' => Illuminate\Support\Facades\Date::class, 207 | 'DB' => Illuminate\Support\Facades\DB::class, 208 | 'Eloquent' => Illuminate\Database\Eloquent\Model::class, 209 | 'Event' => Illuminate\Support\Facades\Event::class, 210 | 'File' => Illuminate\Support\Facades\File::class, 211 | 'Gate' => Illuminate\Support\Facades\Gate::class, 212 | 'Hash' => Illuminate\Support\Facades\Hash::class, 213 | 'Http' => Illuminate\Support\Facades\Http::class, 214 | 'Js' => Illuminate\Support\Js::class, 215 | 'Lang' => Illuminate\Support\Facades\Lang::class, 216 | 'Log' => Illuminate\Support\Facades\Log::class, 217 | 'Mail' => Illuminate\Support\Facades\Mail::class, 218 | 'Notification' => Illuminate\Support\Facades\Notification::class, 219 | 'Password' => Illuminate\Support\Facades\Password::class, 220 | 'Queue' => Illuminate\Support\Facades\Queue::class, 221 | 'RateLimiter' => Illuminate\Support\Facades\RateLimiter::class, 222 | 'Redirect' => Illuminate\Support\Facades\Redirect::class, 223 | // 'Redis' => Illuminate\Support\Facades\Redis::class, 224 | 'Request' => Illuminate\Support\Facades\Request::class, 225 | 'Response' => Illuminate\Support\Facades\Response::class, 226 | 'Route' => Illuminate\Support\Facades\Route::class, 227 | 'Schema' => Illuminate\Support\Facades\Schema::class, 228 | 'Session' => Illuminate\Support\Facades\Session::class, 229 | 'Storage' => Illuminate\Support\Facades\Storage::class, 230 | 'Str' => Illuminate\Support\Str::class, 231 | 'URL' => Illuminate\Support\Facades\URL::class, 232 | 'Validator' => Illuminate\Support\Facades\Validator::class, 233 | 'View' => Illuminate\Support\Facades\View::class, 234 | 235 | ], 236 | 237 | ]; 238 | -------------------------------------------------------------------------------- /public/primary_area.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1次細分区定義表 - livedoor 天気情報 5 | http://weather.livedoor.com/?r=rss 6 | livedoor 天気情報で使用されている1次細分区の定義表。それぞれの地点のRSSフィードURLと、お天気Webサービスで対応するidが定義されています。 7 | Mon, 11 May 2020 11:00:00 +0900 8 | 9 | livedoor Weather Team. 10 | ja 11 | 天気情報 12 | http://weather.livedoor.com/ 13 | (C) LINE Corporation 14 | 15 | livedoor 天気情報 16 | http://weather.livedoor.com/ 17 | http://weather.livedoor.com/img/cmn/livedoor.gif 18 | 118 19 | 26 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | [ PR ] ブログでお天気を簡単ゲット! 275 | http://weather.livedoor.com/weather_hacks/plugin.html?pref=01a 276 | PR 277 | 278 | livedoor 天気情報「Weather Hacks」では一般のブロガーの皆さん向けにブログでお天気を表示できる、お天気プラグインを公開しました。使い方はとってもカンタン!手順に沿って作成したHTMLソースを自分のブログに貼り付けるだけです! 279 | 280 | 281 | お天気プラグイン - livedoor 天気情報 282 | http://weather.livedoor.com/weather_hacks/plugin.html?pref=01a 283 | http://weather.livedoor.com/img/weather_hacks/news_title.gif 284 | 151 285 | 50 286 | 287 | Mon, 11 May 2020 11:00:00 +0900 288 | 289 | 290 | 291 | 292 | 293 | -------------------------------------------------------------------------------- /resources/views/index.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 天気予報 API(livedoor 天気互換) 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 61 | 62 | 63 | 64 | 65 | 105 | 106 |
107 |
108 |

About

109 |
110 | 111 |

天気予報 API(livedoor 天気互換)は、気象庁が配信している全国各地の今日・明日・明後日の天気予報・天気詳細・予想気温・降水確率と都道府県の天気概況情報を JSON データで提供しています。

112 | 113 |

114 | 去る2020年7月、livedoor 天気がサービス終了となりました。livedoor 天気の API はわかりやすく、認証も不要でとても利用しやすかったのですが、突然の終了となりとても残念です。
115 | 代替として使えそうな API を探しましたが、OpenWeatherMap は API キーが必要な上に予報自体が正確でなかったり、気象庁のサイトはそもそも API がなかったりなど livedoor 天気のように手軽に使える API は見つからず、こうして自作することとなりました。
116 | (気象庁 HP のリニューアルにより現在は各種 API が存在しますが、公に公開されているものではないためドキュメントはなく、またデータが非常に取りづらい構造になっており、手軽に使えるかというと微妙…)
117 |

118 | 119 |

120 | この API は、気象庁 から配信されている全国各地の天気予報を取得し、終了した livedoor 天気 の API と互換性のある JSON 形式のデータで提供するものです。URL を差し替えるだけで極力既存のコードを変えることなく利用できるようにしているつもりですが、livedoor 天気の API からの変更点や注意点もあります。利用される際は下記の 変更点・注意事項 をよく読んだ上でご利用ください。
121 |

122 | 123 |

124 | 2021年2月の気象庁 HP のリニューアルに対応しました。また、気象庁 HP の API を活用して 気象台名・見出し/本文のみの天気概況文・天気詳細・一時細分区域名 のプロパティを新たに追加し、より利用しやすくなりました。
125 | 気象データの取得処理が事実上すべて書き直しになった関係で、細かな挙動やこの API で追加されたプロパティが変更・統一されています(livedoor 天気との互換性は保っているはず)。詳細は下記の 変更点・注意事項レスポンスフィールド をご参照ください。
126 |

127 | 128 |
129 |
130 |
131 | 132 |
133 |
134 |

変更点・注意事項

135 |
136 | 137 |
    138 |
  • 天気予報は気象庁 HP の公に公開されていない API から取得しているため、レスポンスに予期しないデータが入っていた場合や API のデータ構造が変更された場合などに、500 エラーで取得できなくなる可能性があります。
  • 139 |
      140 |
    • 気象庁 HP の API は仕様の継続性や運用状況のお知らせが保証されていません(参考)。
    • 141 |
    • 万全は期しているつもりですが、アクセスする時間によってデータが変わるため、修正できていない不具合があるかもしれません。
    • 142 |
    • また、API の不具合などの要因で、正確でない天気予報が取得されてしまう可能性もないわけではありません。
    • 143 |
    • サービスの利用は無料ですが、この API を利用したことで何らかの不具合が発生しても責任は負えません。自己責任にてお願いします。
    • 144 |
    • このため、天気アプリなどの天気を「正確に」「確実に」取得する必要がある用途での利用は推奨しません。 有料の API サービスなども検討してください。
    • 145 |
    146 |
  • API は HTTP 接続と HTTPS 接続両方に対応していますが、できるだけ HTTPS でアクセスすることを推奨します(証明書云々など HTTPS 接続が難しい環境向けに一応残しています)。
  • 147 |
  • ピンポイント予報の発表地点を表す pinpointLocations は気象庁 HP の API からは取得できないため、廃止としました。
  • 148 |
  • link はリクエストされたデータの地域に該当する気象庁 HP の天気予報の URL を返すようになりました。
  • 149 |
  • copyright 内の項目は現状に合わせて変更しています。provider は気象庁としていますが、非公式です。
  • 150 |
      151 |
    • API のレスポンス内 ( copyright => provider => note ) に JSON データへ編集している旨を明記しています。
    • 152 |
    153 |
  • 明後日の降水確率や気温など、3日間天気予報から取得できなかった情報は週間天気予報から取得します。
  • 154 |
      155 |
    • 週間天気予報を発表している観測所は離島がある場合を除いて各都道府県ごとに一つのため、週間天気予報の観測が行われていない地点の ID が指定された場合、同じ都道府県内の週間天気予報を発表している観測所から代わりにデータを取得します。
    • 156 |
    • 奄美地方 (ID: 460040) のように県庁所在地でないにも関わらず週間天気予報の観測が行われている場合は、ID で指定された方の観測所からデータを取得します。
    • 157 |
    • 以前は週間天気予報からのデータ取得を行っていなかったため、特に明後日の天気では null になるプロパティが目立っていましたが、これにより天気詳細(0時~5時の明後日分)・予想気温(今日終了分)・降水確率(今日終了分)以外のプロパティでは常にデータが返されるようになります。
    • 158 |
    159 |
  • 気象庁から配信されているデータの関係上、今日の最低気温は取得できません( null になります)。
  • 160 |
      161 |
    • 17時発表の予報では今日の最高気温も取得できないようです。今日の予想気温は取得できない場合も考慮して実装してください。
    • 162 |
    • 明後日の予想気温は常に週間天気予報から取得します。明日の予想気温も、0時~5時の間は週間天気予報から取得します。
    • 163 |
    • 以前の最高気温/最低気温が取得できなかった際に max/min の要素ごと null になる挙動は削除され、celsius・fahrenheit それぞれの要素に null が設定されるようになりました。
    • 164 |
    165 |
  • 日付が変わってから今日分の天気が配信されるまでの0時~5時の間は、気象庁 HP の API で明日分として配信されている天気を今日の天気、明後日分として配信されている天気を明日の天気として返します。
  • 166 |
      167 |
    • 0時~5時の間、明後日の天気は週間天気予報から取得します。
    • 168 |
    169 |
  • forecasts => image は気象庁 HP にてホストされている各天気アイコンの URL を返します。
  • 170 |
      171 |
    • 以前は livedoor 天気からサルベージした天気アイコンを使用していましたが、気象庁側で定義されている天気の種類があまりにも多く livedoor 天気の天気アイコンだけでは表現しきれないため、気象庁 HP の天気アイコン(SVG 画像)へ変更しました。
    • 172 |
    • SVG はベクター形式の画像のため、API クライアントによっては別途対応が必要かもしれません。
    • 173 |
    • これにより、天気アイコンの画像サイズも 50 × 31 から 80 × 60 へ変更になっています。
    • 174 |
    175 |
  • publicTimeFormatted と description => publicTimeFormatted を追加しました。publicTime を 年/月/日 時間:分:秒 の形式で取得できます。
  • 176 |
      177 |
    • 以前は publicTime_format というプロパティ名でしたが、API 全体での命名規則(lowerCamelCase)に合わせました。
    • 178 |
    179 |
  • publishingOffice を追加しました。予報を発表した気象台(例・福岡管区気象台)を取得できます。
  • 180 |
  • description => headlineText・description => bodyText を追加しました。見出し/本文のみの天気概況文を取得できます。
  • 181 |
      182 |
    • description => headlineText は見出しのみの天気概況文です。
    • 183 |
    • description => bodyText は本文のみの天気概況文です。
    • 184 |
    • description => text は今まで通り見出しと本文の両方を含んだ天気概況文です。
    • 185 |
    186 |
  • 天気詳細を表す detail を追加しました。詳細な天気情報・風の強さ・波の高さを取得できます。
  • 187 |
      188 |
    • 週間天気予報に存在しないデータのため、0時~5時の間、明後日の天気詳細はいずれのプロパティも null になります。
    • 189 |
    • 波の高さ (detail => wave) は海に面している地域のみです。海に面していない地域では null になります。
    • 190 |
    191 |
  • 降水確率を表す chanceOfRain を追加しました。今日と明日の 0時~6時・6時~12時・12時~18時・18時~24時 の降水確率を取得できます。
  • 192 |
      193 |
    • プロパティ名は T00_06(00時~06時)・T06_12(06時~12時)・T12_18(12時~18時)・T18_24(18時~24時)へ統一しました。
    • 194 |
    • 今日分の降水確率のうち、過ぎた分(例・12時に API にアクセスしたときの0時~6時の降水確率)は --% になります。
    • 195 |
    • 明後日の降水確率は常に週間天気予報から取得します。明日の降水確率も、0時~5時の間は週間天気予報から取得します。
    • 196 |
    • 週間天気予報から取得する場合は時間帯ごとの降水確率のデータが週間天気予報にないため、すべての時間帯で同じ降水確率になります。
    • 197 |
    198 |
  • location => district を追加しました。「八幡」のような地域名に加え、「北九州地方」のような一次細分区域名を取得できます。
  • 199 |
  • API の CORS (Access-Control-Allow-Origin) は許可しているので、ブラウザから JavaScript でこの API を利用することも可能です。
  • 200 |
      201 |
    • ただし、前述した通り天気を確実に取得する必要のある用途での利用は推奨しません。天気ウィジェットなど、動かなくても問題がない程度のものにとどめておくのが無難だと思います。
    • 202 |
    203 |
  • テストアプリや開発用途以外で利用される場合は独自のユーザーエージェント(例・WeatherApp/1.0)を設定してください。
  • 204 |
      205 |
    • どのアプリケーションからどれくらいアクセスがあるかをサーバーログで把握するためです。
    • 206 |
    • あまり強いサーバーではないので、API に連続してアクセスする場合は最低でも 0.5 秒以上間隔を空けてから行ってください。
    • 207 |
    • 短時間に連続してアクセスした場合、この API だけでなく気象庁 HP にも負荷をかけてしまうことになるため、絶対にやめてください。
    • 208 |
    209 |
  • この API はできる限り維持するつもりですが、サーバーの負荷が高くなったり気象庁 HP の API 構造が大幅に変更された場合など、やむを得ず終了する可能性があります。あらかじめご了承ください。
  • 210 |
  • コードは GitHub にて公開しています。なにか不具合があれば Issues へお願いします。
  • 211 |
      212 |
    • 未検証ですが、自分のサイトでこの API をホストすることも可能です。
    • 213 |
    214 |
215 | 216 |
217 |
218 |
219 | 220 |
221 |
222 |

リクエストパラメータ

223 |
224 |
225 |

226 | JSON データをリクエストする際のベースとなる URL は以下になります。
227 | {{ config('app.url') }}/api/forecast
228 | この URL に下の表のパラメータを加え、実際にリクエストします。 229 |

230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 242 | 243 |
パラメータ名説明
city 239 | 地域別に定義された ID 番号を表します。
240 | リクエストする地域と ID の対応は、livedoor 天気で使われていた 全国の地点定義表 内で
「一次細分区域(cityタグ)」の ID をご参照ください。(例・佐賀県 伊万里 = 410020 ) 241 |
244 | 245 |
246 | (例)「福岡県・久留米の天気」を取得する場合
247 |
248 | 下記 URL にアクセスして JSON データを取得します。http:// でのアクセスも可能です。
249 | 基本 URL + 久留米の ID (400040) 250 |
251 | {{ config('app.url') }}/api/forecast/city/400040 252 |
253 | クエリで取得することもできます。 254 |
255 | {{ config('app.url') }}/api/forecast?city=400040 256 |
257 |
258 |
259 |
260 |
261 | 262 |
263 |
264 |

レスポンスフィールド

265 |
266 | 267 |

268 | 取得した JSON データは以下の定義に基づいて構成されています(プロパティ名は順不同)。
269 | 下線 の項目はこの API で新たに追加されたプロパティです。
270 |

271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 328 | 329 | 330 | 331 | 466 | 467 | 468 | 469 | 494 | 495 | 496 | 497 | 521 | 522 |
プロパティ名内容
publicTime予報の発表日時( ISO8601 形式 / 例・2020-09-01T05:00:00+09:00 )
publicTimeFormatted予報の発表日時(例・2020/09/01 05:00:00 )
publishingOffice予報を発表した気象台(例・福岡管区気象台)
titleタイトル・見出し(例・福岡県 久留米 の天気)
linkリクエストされたデータの地域に該当する気象庁 HP の天気予報の URL
description 300 |
天気概況文
301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 |
プロパティ名内容
publicTime天気概況文の発表時刻( ISO8601 形式 / 例・2020-09-01T04:52:00+09:00 )
publicTimeFormatted天気概況文の発表時刻(例・2020/09/01 04:52:00 )
headlineText天気概況文(見出しのみ)
bodyText天気概況文(本文のみ)
text天気概況文
327 |
forecasts 332 |
都道府県天気予報の予報日毎の配列
333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 377 | 378 | 379 | 380 | 381 | 401 | 402 | 403 | 404 | 405 | 432 | 433 | 434 | 435 | 436 | 463 | 464 |
プロパティ名内容
date予報日
dateLabel予報日(今日・明日・明後日のいずれか)
telop天気(晴れ、曇り、雨など)
detail 355 |
356 | 天気詳細
357 |
358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 |
プロパティ名内容
weather詳細な天気情報
wind風の強さ
wave波の高さ(海に面している地域のみ)
376 |
temperature 382 |
383 | max・・・最高気温
384 | min・・・最低気温 385 |
386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 |
プロパティ名内容
celsius摂氏 (°C)
fahrenheit華氏 (°F)
400 |
chanceOfRain 406 |
407 | 降水確率
408 |
409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 |
プロパティ名内容
T00_060 時から 6 時までの降水確率
T06_126 時から 12 時までの降水確率
T12_1812 時から 18 時までの降水確率
T18_2418 時から 24 時までの降水確率
431 |
image 437 |
438 | 天気アイコン
439 |
440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 |
プロパティ名内容
title天気(晴れ、曇り、雨など)
url天気アイコンの URL(SVG 画像)
width天気アイコンの幅
height天気アイコンの高さ
462 |
465 |
location 470 |
予報を発表した地域を定義
471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 |
プロパティ名内容
area地方名(例・九州)
prefecture都道府県名(例・福岡県)
district一次細分区域名(例・北九州地方)
city地域名(気象観測所名)(例・八幡)
493 |
copyright 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 |
プロパティ名内容
titleコピーライトの文言
link天気予報 API(livedoor 天気互換)の URL
image天気予報 API(livedoor 天気互換)のアイコン
provider天気予報 API(livedoor 天気互換)で使用している気象データの配信元(気象庁)
520 |
523 | 524 |
525 |
526 |
527 | 528 |
529 |
530 |

JSON データサンプル

531 |
532 | 533 |

livedoor 天気の API では ASCII の範囲外の文字はすべてエスケープされていましたが、この API ではエスケープは行いません。

534 | 535 |
{
536 |     "publicTime": "2021-03-03T05:00:00+09:00",
537 |     "publicTimeFormatted": "2021/03/03 05:00:00",
538 |     "publishingOffice": "福岡管区気象台",
539 |     "title": "福岡県 久留米 の天気",
540 |     "link": "https://www.jma.go.jp/bosai/forecast/#area_type=offices&area_code=400000",
541 |     "description": {
542 |         "publicTime": "2021-03-03T04:43:00+09:00",
543 |         "publicTimeFormatted": "2021/03/03 04:43:00",
544 |         "headlineText": "福岡、北九州地方では、3日夕方まで高波に注意してください。福岡県では、4日まで空気の乾燥した状態が続くため、火の取り扱いに注意してください。",
545 |         "bodyText": " 福岡県は、寒気の影響により曇りとなっている所がありますが、高気圧に覆われて概ね晴れています。\n\n 3日は、寒気の影響によりはじめ曇りとなる所がありますが、高気圧に覆われて概ね晴れとなるでしょう。\n\n 4日は、高気圧に覆われて晴れとなる所もありますが、気圧の谷や湿った空気の影響により概ね曇りで、夜遅くは雨となるでしょう。",
546 |         "text": "福岡、北九州地方では、3日夕方まで高波に注意してください。福岡県では、4日まで空気の乾燥した状態が続くため、火の取り扱いに注意してください。\n\n 福岡県は、寒気の影響により曇りとなっている所がありますが、高気圧に覆われて概ね晴れています。\n\n 3日は、寒気の影響によりはじめ曇りとなる所がありますが、高気圧に覆われて概ね晴れとなるでしょう。\n\n 4日は、高気圧に覆われて晴れとなる所もありますが、気圧の谷や湿った空気の影響により概ね曇りで、夜遅くは雨となるでしょう。"
547 |     },
548 |     "forecasts": [
549 |         {
550 |             "date": "2021-03-03",
551 |             "dateLabel": "今日",
552 |             "telop": "晴れ",
553 |             "detail": {
554 |                 "weather": "晴れ",
555 |                 "wind": "北の風",
556 |                 "wave": "0.5メートル"
557 |             },
558 |             "temperature": {
559 |                 "min": {
560 |                     "celsius": null,
561 |                     "fahrenheit": null
562 |                 },
563 |                 "max": {
564 |                     "celsius": "14",
565 |                     "fahrenheit": "57.2"
566 |                 }
567 |             },
568 |             "chanceOfRain": {
569 |                 "T00_06": "--%",
570 |                 "T06_12": "0%",
571 |                 "T12_18": "0%",
572 |                 "T18_24": "0%"
573 |             },
574 |             "image": {
575 |                 "title": "晴れ",
576 |                 "url": "https://www.jma.go.jp/bosai/forecast/img/100.svg",
577 |                 "width": 80,
578 |                 "height": 60
579 |             }
580 |         },
581 |         {
582 |             "date": "2021-03-04",
583 |             "dateLabel": "明日",
584 |             "telop": "曇のち一時雨",
585 |             "detail": {
586 |                 "weather": "くもり 時々 晴れ 夜遅く 雨",
587 |                 "wind": "北の風 後 北東の風",
588 |                 "wave": "0.5メートル"
589 |             },
590 |             "temperature": {
591 |                 "min": {
592 |                     "celsius": "4",
593 |                     "fahrenheit": "39.2"
594 |                 },
595 |                 "max": {
596 |                     "celsius": "18",
597 |                     "fahrenheit": "64.4"
598 |                 }
599 |             },
600 |             "chanceOfRain": {
601 |                 "T00_06": "10%",
602 |                 "T06_12": "10%",
603 |                 "T12_18": "20%",
604 |                 "T18_24": "60%"
605 |             },
606 |             "image": {
607 |                 "title": "曇のち一時雨",
608 |                 "url": "https://www.jma.go.jp/bosai/forecast/img/212.svg",
609 |                 "width": 80,
610 |                 "height": 60
611 |             }
612 |         },
613 |         {
614 |             "date": "2021-03-05",
615 |             "dateLabel": "明後日",
616 |             "telop": "雨のち曇",
617 |             "detail": {
618 |                 "weather": null,
619 |                 "wind": null,
620 |                 "wave": null
621 |             },
622 |             "temperature": {
623 |                 "min": {
624 |                     "celsius": "10",
625 |                     "fahrenheit": "50"
626 |                 },
627 |                 "max": {
628 |                     "celsius": "20",
629 |                     "fahrenheit": "68"
630 |                 }
631 |             },
632 |             "chanceOfRain": {
633 |                 "T00_06": "70%",
634 |                 "T06_12": "70%",
635 |                 "T12_18": "70%",
636 |                 "T18_24": "70%"
637 |             },
638 |             "image": {
639 |                 "title": "雨のち曇",
640 |                 "url": "https://www.jma.go.jp/bosai/forecast/img/313.svg",
641 |                 "width": 80,
642 |                 "height": 60
643 |             }
644 |         }
645 |     ],
646 |     "location": {
647 |         "area": "九州",
648 |         "prefecture": "福岡県",
649 |         "district": "筑後地方",
650 |         "city": "久留米"
651 |     },
652 |     "copyright": {
653 |         "title": "(C) 天気予報 API(livedoor 天気互換)",
654 |         "link": "{{ config('app.url') }}/",
655 |         "image": {
656 |             "title": "天気予報 API(livedoor 天気互換)",
657 |             "link": "{{ config('app.url') }}/",
658 |             "url": "{{ config('app.url') }}/logo.png",
659 |             "width": 120,
660 |             "height": 120
661 |         },
662 |         "provider": [
663 |             {
664 |                 "link": "https://www.jma.go.jp/jma/",
665 |                 "name": "気象庁 Japan Meteorological Agency",
666 |                 "note": "気象庁 HP にて配信されている天気予報を JSON データへ編集しています。"
667 |             }
668 |         ]
669 |     }
670 | }
671 | 672 |
673 |
674 |
675 | 676 | 685 | 686 | 687 | 688 | -------------------------------------------------------------------------------- /app/Models/Weather.php: -------------------------------------------------------------------------------- 1 | 'The specified city ID is invalid.']; 31 | } 32 | 33 | // 地域ID / 一時細分区域名 34 | $city_id = (string) $id; 35 | $district_name = WeatherDefinition::Areas['class10s'][$city_id]['name']; 36 | 37 | // 地域 ID から配列のインデックスを抽出 38 | $city_index = intval(substr($city_id, 4, 1)) - 1; 39 | if ($city_index < 0) $city_index = 0; // 大東島など一部の地域用 40 | 41 | // 都道府県ID / 都道府県名 42 | $prefecture_id = (string) WeatherDefinition::Areas['class10s'][$id]['parent']; 43 | $prefecture_name = WeatherDefinition::Areas['offices'][$prefecture_id]['name']; 44 | 45 | // 地方名(九州、東北 など) 46 | $area_name = Weather::getAreaName($prefecture_id); 47 | 48 | // 1.十勝地方と奄美地方は測候所という気象台の下部施設が気象情報を発表しているため、 49 | // 十勝地方:014030・奄美地方:460040 のように一見普通の地域 ID のように見えるが、気象庁 HP 上は独立した都道府県/地方の扱いになっている 50 | // 一方 API では 014100・460100 のように同じ都道府県扱いのため、気象庁 HP へのリンクに使う ID だけ別個書き換える必要がある 51 | // 2.また、根室 (014010) と釧路 (014020) はなぜか API レスポンス上の配列の順序が反対で天気も反対に取得されてしまうため、これも別個書き換える 52 | // このコードは area が $city_index の順で並んで返ってくる事を期待して作られているので、そうでなかった場合別の地点の天気を取得してしまう 53 | $prefecture_url_id = $prefecture_id; 54 | switch ($city_id) { 55 | case '014010': 56 | $city_index = 1; 57 | break; 58 | case '014020': 59 | $city_index = 0; 60 | break; 61 | case '014030': 62 | $prefecture_url_id = '014030'; 63 | break; 64 | case '460040': 65 | $prefecture_url_id = '460040'; 66 | break; 67 | } 68 | 69 | 70 | /**** API リクエスト ****/ 71 | 72 | // 気象庁 HP の API 73 | $jma_api_forecast = "https://www.jma.go.jp/bosai/forecast/data/forecast/{$prefecture_id}.json"; 74 | $jma_api_overview = "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/{$prefecture_id}.json"; 75 | 76 | // API から気象データを取得 77 | $retry_count = 5; 78 | while ($retry_count > 0) { 79 | try { 80 | $forecast_response = HTTP::withHeaders(['User-Agent' => 'weather-api/1.0']) 81 | ->withOptions(['verify' => false]) 82 | ->get($jma_api_forecast); 83 | break; 84 | } catch (\Throwable $e) { 85 | $retry_count--; 86 | if ($retry_count === 0) throw $e; // 3回失敗したら例外を投げる 87 | sleep(1); // 1秒待機 88 | } 89 | } 90 | if ($forecast_response->status() === 200) { 91 | $forecast_data = $forecast_response->json(); 92 | } else { 93 | return ['error' => "Request to JMA API failed (HTTP Error {$forecast_response->status()})"]; 94 | } 95 | 96 | // API から気象概況を取得 97 | $retry_count = 5; 98 | while ($retry_count > 0) { 99 | try { 100 | $overview_response = HTTP::withHeaders(['User-Agent' => 'weather-api/1.0']) 101 | ->withOptions(['verify' => false]) 102 | ->get($jma_api_overview); 103 | break; 104 | } catch (\Throwable $e) { 105 | $retry_count--; 106 | if ($retry_count === 0) throw $e; // 3回失敗したら例外を投げる 107 | sleep(1); // 1秒待機 108 | } 109 | } 110 | if ($overview_response->status() === 200){ 111 | $overview = $overview_response->json(); 112 | } else { 113 | return ['error' => "Request to JMA API failed (HTTP Error {$forecast_response->status()})"]; 114 | } 115 | 116 | // アメダスは鹿児島のように地域に複数存在する場合があり、また愛媛県の新居浜 (380020) のように 117 | // 0 番目に存在するアメダス ID と実際に運用されているアメダス ID が異なる場合があるため、念のためこっちも回す 118 | $city_amedas_index = 0; 119 | foreach (WeatherDefinition::ForecastArea[$prefecture_id][$city_index]['amedas'] as $amedas_id) { 120 | 121 | // 地域のアメダスID 122 | $city_amedas_id = $amedas_id; 123 | 124 | // 地域のアメダスのインデックス 125 | // なんでもかんでも配列のインデックス探さないといけないの心底つらい 126 | foreach ($forecast_data[0]['timeSeries'][2]['areas'] as $area_key => $area) { 127 | // アメダス ID と一致したら、そのアメダス ID があるインデックスを取得して終了 128 | if ($area['area']['code'] === $city_amedas_id) { 129 | $city_amedas_index = $area_key; 130 | break 2; // 親ループも抜ける 131 | } 132 | } 133 | } 134 | 135 | // 地域の週間天気予報用のインデックス 136 | // ほとんどは県あたりの観測地点は1箇所しかないが、小笠原諸島がある東京、奄美地方のある鹿児島などでは複数存在する 137 | // なんでもかんでも配列のインデックス探さないといけないの心底つらい 138 | foreach ($forecast_data[1]['timeSeries'][1]['areas'] as $area_key => $area) { 139 | // アメダス ID と一致したら、そのアメダス ID があるインデックスを取得して終了 140 | if ($area['area']['code'] === $city_amedas_id) { 141 | $city_weekly_index = $area_key; 142 | break; 143 | } 144 | } 145 | // 取得できなかった場合(3日間天気でしか観測を行っていない地点)は 0 とし、その都道府県のメインの観測地点を利用する 146 | if (!isset($city_weekly_index)) { 147 | $city_weekly_index = 0; 148 | } 149 | 150 | // 地域名(気象観測所名)を取得 151 | // livedoor天気では気象観測所の名前が地域名として使われており、それに合わせるため気象データの方から取得している 152 | $city_name = $forecast_data[0]['timeSeries'][2]['areas'][$city_amedas_index]['area']['name']; 153 | 154 | 155 | /**** 気象データから今日・明日・明後日の天気予報を取得 ****/ 156 | 157 | // API のデータは日付が変わっても 5 時までは更新されないため、自力で昨日の情報を削除したり整形する作業が必要になる 158 | 159 | // 天気予報 160 | $forecast = Weather::getForecast($forecast_data, $city_index, $city_weekly_index); 161 | 162 | // 最高気温・最低気温 163 | $temperature = Weather::getTemperature($forecast_data, $city_amedas_index, $city_weekly_index); 164 | 165 | // 降水確率 166 | $chanceofrain = Weather::getChanceOfRain($forecast_data, $city_index, $city_weekly_index); 167 | 168 | 169 | /**** 出力する JSON データ ****/ 170 | 171 | $forecast_json = [ 172 | 'publicTime' => $forecast_data[0]['reportDatetime'], 173 | 'publicTimeFormatted' => (new DateTimeImmutable($forecast_data[0]['reportDatetime']))->format('Y/m/d H:i:s'), 174 | 'publishingOffice' => $forecast_data[0]['publishingOffice'], 175 | 'title' => "{$prefecture_name} {$city_name} の天気", 176 | 'link' => "https://www.jma.go.jp/bosai/forecast/#area_type=offices&area_code={$prefecture_url_id}", 177 | 'description' => [ 178 | 'publicTime' => $overview['reportDatetime'], 179 | 'publicTimeFormatted' => (new DateTimeImmutable($overview['reportDatetime']))->format('Y/m/d H:i:s'), 180 | // 天気概況の見出し 181 | 'headlineText' => $overview['headlineText'], 182 | // 天気概況の本文 183 | 'bodyText' => $overview['text'], 184 | // 見出しが空でなければ見出しと本文を改行で連結し、空であれば本文のみを返す 185 | 'text' => ($overview['headlineText'] !== '' ? "{$overview['headlineText']}\n\n{$overview['text']}": $overview['text']), 186 | ], 187 | 'forecasts' => [ 188 | [ 189 | // 今日の天気 190 | 'date' => $forecast[0]['date'], 191 | 'dateLabel' => "今日", 192 | 'telop' => $forecast[0]['telop'], 193 | 'detail' => [ 194 | 'weather' => $forecast[0]['detail']['weather'], 195 | 'wind' => $forecast[0]['detail']['wind'], 196 | 'wave' => $forecast[0]['detail']['wave'], 197 | ], 198 | 'temperature' => [ 199 | 'min' => [ 200 | 'celsius' => $temperature[0]['min']['celsius'], 201 | 'fahrenheit' => $temperature[0]['min']['fahrenheit'], 202 | ], 203 | 'max' => [ 204 | 'celsius' => $temperature[0]['max']['celsius'], 205 | 'fahrenheit' => $temperature[0]['max']['fahrenheit'], 206 | ] 207 | ], 208 | 'chanceOfRain' => [ 209 | 'T00_06' => $chanceofrain[0]['T00_06'], 210 | 'T06_12' => $chanceofrain[0]['T06_12'], 211 | 'T12_18' => $chanceofrain[0]['T12_18'], 212 | 'T18_24' => $chanceofrain[0]['T18_24'], 213 | ], 214 | 'image' => [ 215 | 'title' => $forecast[0]['image']['title'], 216 | 'url' => $forecast[0]['image']['url'], 217 | 'width' => 80, 218 | 'height' => 60, 219 | ] 220 | ], 221 | [ 222 | // 明日の天気 223 | 'date' => $forecast[1]['date'], 224 | 'dateLabel' => "明日", 225 | 'telop' => $forecast[1]['telop'], 226 | 'detail' => [ 227 | 'weather' => $forecast[1]['detail']['weather'], 228 | 'wind' => $forecast[1]['detail']['wind'], 229 | 'wave' => $forecast[1]['detail']['wave'], 230 | ], 231 | 'temperature' => [ 232 | 'min' => [ 233 | 'celsius' => $temperature[1]['min']['celsius'], 234 | 'fahrenheit' => $temperature[1]['min']['fahrenheit'], 235 | ], 236 | 'max' => [ 237 | 'celsius' => $temperature[1]['max']['celsius'], 238 | 'fahrenheit' => $temperature[1]['max']['fahrenheit'], 239 | ] 240 | ], 241 | 'chanceOfRain' => [ 242 | 'T00_06' => $chanceofrain[1]['T00_06'], 243 | 'T06_12' => $chanceofrain[1]['T06_12'], 244 | 'T12_18' => $chanceofrain[1]['T12_18'], 245 | 'T18_24' => $chanceofrain[1]['T18_24'], 246 | ], 247 | 'image' => [ 248 | 'title' => $forecast[1]['image']['title'], 249 | 'url' => $forecast[1]['image']['url'], 250 | 'width' => 80, 251 | 'height' => 60, 252 | ] 253 | ], 254 | [ 255 | // 明後日の天気 256 | 'date' => $forecast[2]['date'], 257 | 'dateLabel' => "明後日", 258 | 'telop' => $forecast[2]['telop'], 259 | 'detail' => [ 260 | 'weather' => $forecast[2]['detail']['weather'], 261 | 'wind' => $forecast[2]['detail']['wind'], 262 | 'wave' => $forecast[2]['detail']['wave'], 263 | ], 264 | 'temperature' => [ 265 | 'min' => [ 266 | 'celsius' => $temperature[2]['min']['celsius'], 267 | 'fahrenheit' => $temperature[2]['min']['fahrenheit'], 268 | ], 269 | 'max' => [ 270 | 'celsius' => $temperature[2]['max']['celsius'], 271 | 'fahrenheit' => $temperature[2]['max']['fahrenheit'], 272 | ] 273 | ], 274 | 'chanceOfRain' => [ 275 | 'T00_06' => $chanceofrain[2]['T00_06'], 276 | 'T06_12' => $chanceofrain[2]['T06_12'], 277 | 'T12_18' => $chanceofrain[2]['T12_18'], 278 | 'T18_24' => $chanceofrain[2]['T18_24'], 279 | ], 280 | 'image' => [ 281 | 'title' => $forecast[2]['image']['title'], 282 | 'url' => $forecast[2]['image']['url'], 283 | 'width' => 80, 284 | 'height' => 60, 285 | ] 286 | ] 287 | ], 288 | 'location' => [ 289 | 'area' => $area_name, 290 | 'prefecture' => $prefecture_name, 291 | 'district' => $district_name, 292 | 'city' => $city_name, 293 | ], 294 | 'copyright' => [ 295 | 'title' => "(C) 天気予報 API(livedoor 天気互換)", 296 | 'link' => "{$url}/", 297 | 'image' => [ 298 | 'title' => "天気予報 API(livedoor 天気互換)", 299 | 'link' => "{$url}/", 300 | 'url' => "{$url}/logo.png", 301 | 'width' => 120, 302 | 'height' => 120, 303 | ], 304 | 'provider' => [ 305 | [ 306 | 'link' => "https://www.jma.go.jp/jma/", 307 | 'name' => "気象庁 Japan Meteorological Agency", 308 | 'note' => "気象庁 HP にて配信されている天気予報を JSON データへ編集しています。", 309 | ] 310 | ] 311 | ] 312 | ]; 313 | 314 | // json を返す 315 | return $forecast_json; 316 | } 317 | 318 | 319 | /** 320 | * 都道府県 ID からその都道府県が属する地方名を取得する 321 | * WeatherDefinition では「九州北部地方(山口県を含む)」のように一般的な分類がされていないため 322 | * 323 | * @param string $prefecture_id 都道府県ID 324 | * @return string 地方名 325 | */ 326 | private static function getAreaName(string $prefecture_id): string 327 | { 328 | $prefecture_id = intval(substr($prefecture_id, 0, 2)); 329 | if ($prefecture_id === 1) { 330 | $area = '北海道'; 331 | } else if ($prefecture_id >= 2 and $prefecture_id <= 7) { 332 | $area = '東北'; 333 | } else if ($prefecture_id >= 8 and $prefecture_id <= 14) { 334 | $area = '関東'; 335 | } else if ($prefecture_id >= 15 and $prefecture_id <= 23) { 336 | $area = '中部'; 337 | } else if ($prefecture_id >= 24 and $prefecture_id <= 30) { 338 | $area = '近畿'; 339 | } else if ($prefecture_id >= 31 and $prefecture_id <= 35) { 340 | $area = '中国'; 341 | } else if ($prefecture_id >= 36 and $prefecture_id <= 39) { 342 | $area = '四国'; 343 | } else if ($prefecture_id >= 40 and $prefecture_id <= 46) { 344 | $area = '九州'; 345 | } else if ($prefecture_id === 47) { 346 | $area = '沖縄'; 347 | } else { 348 | $area = '日本'; 349 | } 350 | return $area; 351 | } 352 | 353 | 354 | /** 355 | * 取得した生の気象データから、今日・明日・明後日の天気予報・気温・降水確率を取得する 356 | * 357 | * @param array $forecast_data API から取得した気象データ 358 | * @param int $city_index 取得する地域の配列のインデックス 359 | * @param int $city_weekly_index 取得する地域の配列のインデックス(週間天気予報用) 360 | * @return array 整形された気象データ 361 | */ 362 | private static function getForecast(array $forecast_data, int $city_index, int $city_weekly_index): array 363 | { 364 | $forecast = []; 365 | 366 | $days_datetime = [ 367 | (new DateTimeImmutable('now')), // 現在の時刻 368 | (new DateTimeImmutable('now'))->modify('+1 days'), // 明日の時刻 369 | (new DateTimeImmutable('now'))->modify('+2 days'), // 明後日の時刻 370 | ]; 371 | 372 | // 今日・明日・明後日の天気予報が載っている配列のインデックスを格納 373 | $forecast_index = [null, null, null]; 374 | 375 | // timeDefines の中から今日・明日・明後日の日付を見つけ、インデックスを手に入れる 376 | // 見つからなかったら既定値の null になる 377 | foreach ($forecast_data[0]['timeSeries'][0]['timeDefines'] as $timedefine_index => $timedefine) { 378 | 379 | // 比較対象(処理対象)の時刻 380 | $compare_datetime = new DateTimeImmutable($timedefine); 381 | 382 | foreach ($days_datetime as $day_index => $day_datetime) { 383 | 384 | // 同じ日付ならインデックスを取得して抜ける 385 | if ($compare_datetime->setTime(0,0) == $day_datetime->setTime(0,0)) { 386 | $forecast_index[$day_index] = $timedefine_index; 387 | break; 388 | } 389 | } 390 | } 391 | 392 | // インデックスを手に入れたので、インデックスが null でなければアクセスしてデータを取りに行く 393 | foreach ($days_datetime as $day_index => $day_datetime) { 394 | 395 | // 天気コード 396 | $weathercodes = $forecast_data[0]['timeSeries'][0]['areas'][$city_index]['weatherCodes']; 397 | $weathercode = ($forecast_index[$day_index] !== null ? $weathercodes[$forecast_index[$day_index]] : null); 398 | 399 | // 詳細な天気情報 400 | $weathers = $forecast_data[0]['timeSeries'][0]['areas'][$city_index]['weathers']; 401 | $weather = ($forecast_index[$day_index] !== null ? $weathers[$forecast_index[$day_index]] : null); 402 | 403 | // 風の強さ 404 | $winds = $forecast_data[0]['timeSeries'][0]['areas'][$city_index]['winds']; 405 | $wind = ($forecast_index[$day_index] !== null ? $winds[$forecast_index[$day_index]] : null); 406 | 407 | // 波の高さ 408 | // 海沿いの地域以外では存在しない 409 | if (isset($forecast_data[0]['timeSeries'][0]['areas'][$city_index]['waves'])) { 410 | $waves = $forecast_data[0]['timeSeries'][0]['areas'][$city_index]['waves']; 411 | $wave = ($forecast_index[$day_index] !== null ? $waves[$forecast_index[$day_index]] : null); 412 | } else { 413 | $wave = null; 414 | } 415 | 416 | // データを入れる 417 | $forecast[$day_index] = [ 418 | 'date' => $day_datetime->format('Y-m-d'), 419 | // WeatherDefinition::Telops から天気コードに当てはまるテロップや画像のファイル名を取得する 420 | 'telop' => ($weathercode !== null ? WeatherDefinition::Telops[$weathercode][3]: null), 421 | 'detail' => [ 422 | 'weather' => $weather, 423 | 'wind' => $wind, 424 | 'wave' => $wave, 425 | ], 426 | 'image' => [ 427 | // テロップと共通 428 | 'title' => ($weathercode !== null ? WeatherDefinition::Telops[$weathercode][3]: null), 429 | // 気象庁の SVG にリンク 430 | 'url' => ($weathercode !== null ? 'https://www.jma.go.jp/bosai/forecast/img/' . WeatherDefinition::Telops[$weathercode][0]: null), 431 | ] 432 | ]; 433 | } 434 | 435 | // 明後日の天気が3日間予報から取得できない場合(多くの場合、0時~5時の間)は、週間天気予報からデータを持ってくる 436 | // 以下は0時~5時の明後日専用の処理 437 | foreach ($forecast as $forecast_key => $forecast_value) { 438 | 439 | // その日の天気予報がすべて存在しない 440 | if ($forecast_value['telop'] === null) { 441 | 442 | // 週間天気予報の時刻 443 | $weekly_datetime = $days_datetime[$forecast_key]; 444 | 445 | // 週間天気予報から目当ての日付を見つけ、インデックスを手に入れる 446 | foreach ($forecast_data[1]['timeSeries'][0]['timeDefines'] as $key => $value) { 447 | 448 | // 比較対象の時刻 449 | $compare_datetime = new DateTimeImmutable($value); 450 | 451 | // 同じ日付ならインデックスを取得して抜ける 452 | if ($compare_datetime->setTime(0,0) == $weekly_datetime->setTime(0,0)) { 453 | $weekly_index = $key; 454 | break; 455 | } 456 | } 457 | 458 | // インデックスが定義されている場合だけ 459 | if (isset($weekly_index)) { 460 | 461 | // 天気コード 462 | $weathercode = $forecast_data[1]['timeSeries'][0]['areas'][$city_weekly_index]['weatherCodes'][$weekly_index]; 463 | 464 | // データを入れる 465 | $forecast[$day_index] = [ 466 | 'date' => $days_datetime[$forecast_key]->format('Y-m-d'), 467 | // WeatherDefinition::Telops から天気コードに当てはまるテロップや画像のファイル名を取得する 468 | 'telop' => WeatherDefinition::Telops[$weathercode][3], 469 | 'detail' => [ // 週間天気予報では以下の情報は取得できない 470 | 'weather' => null, 471 | 'wind' => null, 472 | 'wave' => null, 473 | ], 474 | 'image' => [ 475 | // テロップと共通 476 | 'title' => WeatherDefinition::Telops[$weathercode][3], 477 | // 気象庁の SVG にリンク 478 | 'url' => 'https://www.jma.go.jp/bosai/forecast/img/' . WeatherDefinition::Telops[$weathercode][0], 479 | ] 480 | ]; 481 | } 482 | } 483 | } 484 | 485 | clock()->debug($forecast); 486 | 487 | return $forecast; 488 | } 489 | 490 | 491 | /** 492 | * 取得した生の気象データから、今日・明日・明後日の気温を取得する 493 | * 494 | * @param array $forecast_data API から取得した気象データ 495 | * @param int $city_amedas_index 取得する地域のアメダス ID の配列のインデックス 496 | * @param int $city_weekly_index 取得する地域の配列のインデックス(週間天気予報用) 497 | * @return array 整形された気象データ 498 | */ 499 | private static function getTemperature(array $forecast_data, int $city_amedas_index, int $city_weekly_index): array 500 | { 501 | $temperature = []; 502 | 503 | $days_datetime = [ 504 | (new DateTimeImmutable('now')), // 現在の時刻 505 | (new DateTimeImmutable('now'))->modify('+1 days'), // 明日の時刻 506 | (new DateTimeImmutable('now'))->modify('+2 days'), // 明後日の時刻 507 | ]; 508 | 509 | // 今日・明日・明後日の最高気温/最低気温が載っている配列のインデックスを格納 510 | $temperature_index_min = [null, null, null]; 511 | $temperature_index_max = [null, null, null]; 512 | 513 | // timeDefines の中から今日・明日・明後日の日付を見つけ、インデックスを手に入れる 514 | // 見つからなかったら既定値の null になる 515 | foreach ($forecast_data[0]['timeSeries'][2]['timeDefines'] as $timedefine_index => $timedefine) { 516 | 517 | // 比較対象(処理対象)の時刻 518 | $compare_datetime = new DateTimeImmutable($timedefine); 519 | 520 | foreach ($days_datetime as $day_index => $day_datetime) { 521 | 522 | // 最低気温 523 | // 同じ日付ならインデックスを取得して抜ける 524 | if ($compare_datetime == $day_datetime->setTime(0,0)) { 525 | $temperature_index_min[$day_index] = $timedefine_index; 526 | break; 527 | } 528 | 529 | // 最高気温 530 | // 同じ日付ならインデックスを取得して抜ける 531 | if ($compare_datetime == $day_datetime->setTime(9,0)) { 532 | $temperature_index_max[$day_index] = $timedefine_index; 533 | break; 534 | } 535 | } 536 | } 537 | 538 | // インデックスを手に入れたので、インデックスが null でなければアクセスしてデータを取りに行く 539 | foreach ($days_datetime as $day_index => $day_datetime) { 540 | 541 | // ネスト長過ぎる 542 | $temps = $forecast_data[0]['timeSeries'][2]['areas'][$city_amedas_index]['temps']; 543 | 544 | // 現在のループのインデックスの気温のインデックスが null じゃなければ取得を実行(ややこしい) 545 | // 目的の情報が取り出しにくすぎる… 546 | $temperature[$day_index] = [ 547 | 'min' => [ 548 | 'celsius' => ($temperature_index_min[$day_index] !== null ? 549 | $temps[$temperature_index_min[$day_index]] : null), // 摂氏はそのまま 550 | 'fahrenheit' => ($temperature_index_min[$day_index] !== null ? 551 | strval($temps[$temperature_index_min[$day_index]] * 1.8 + 32) : null), // 華氏に変換 552 | ], 553 | 'max' => [ 554 | 'celsius' => ($temperature_index_max[$day_index] !== null ? 555 | $temps[$temperature_index_max[$day_index]] : null), // 摂氏はそのまま 556 | 'fahrenheit' => ($temperature_index_max[$day_index] !== null ? 557 | strval($temps[$temperature_index_max[$day_index]] * 1.8 + 32) : null), // 華氏に変換 558 | ] 559 | ]; 560 | } 561 | 562 | // 今日の最低気温は常に存在しないのが正しいらしい🤔ので弾く 563 | // ダミーの最低気温が入っているとき、最低気温は最高気温と同じ値かそれ以上になるのを利用する( HP 上では - になってる) 564 | // いっそ API に今日の最低気温を含めないでくれ…って気持ち 565 | if (intval($temperature[0]['min']['celsius']) >= intval($temperature[0]['max']['celsius'])) { 566 | $temperature[0]['min']['celsius'] = null; 567 | $temperature[0]['min']['fahrenheit'] = null; 568 | } 569 | 570 | // 明後日の最高気温・最低気温は常に取得できないはずなので、週間天気予報から持ってくる 571 | // 明日分も0時~5時の間は取得できないと思われる 572 | foreach ($temperature as $temperature_key => $temperature_value) { 573 | 574 | // その日の最高気温・最低気温ともに存在しない 575 | if ($temperature_value['min']['celsius'] === null and $temperature_value['max']['celsius'] === null) { 576 | 577 | // 週間天気予報の時刻 578 | $weekly_datetime = $days_datetime[$temperature_key]; 579 | 580 | // 週間天気予報から目当ての日付を見つけ、インデックスを手に入れる 581 | foreach ($forecast_data[1]['timeSeries'][1]['timeDefines'] as $key => $value) { 582 | 583 | // 比較対象の時刻 584 | $compare_datetime = new DateTimeImmutable($value); 585 | 586 | // 同じ日付ならインデックスを取得して抜ける 587 | if ($compare_datetime->setTime(0,0) == $weekly_datetime->setTime(0,0)) { 588 | $weekly_index = $key; 589 | break; 590 | } 591 | } 592 | 593 | // インデックスが定義されている場合だけ 594 | if (isset($weekly_index)) { 595 | 596 | // 最高気温・最低気温 597 | $weekly_tempmin = $forecast_data[1]['timeSeries'][1]['areas'][$city_weekly_index]['tempsMin'][$weekly_index]; 598 | $weekly_tempmax = $forecast_data[1]['timeSeries'][1]['areas'][$city_weekly_index]['tempsMax'][$weekly_index]; 599 | 600 | // データを入れる 601 | $temperature[$temperature_key] = [ 602 | 'min' => [ 603 | 'celsius' => $weekly_tempmin, // 摂氏はそのまま 604 | 'fahrenheit' => strval($weekly_tempmin * 1.8 + 32), // 華氏に変換 605 | ], 606 | 'max' => [ 607 | 'celsius' => $weekly_tempmax, // 摂氏はそのまま 608 | 'fahrenheit' => strval($weekly_tempmax * 1.8 + 32), // 華氏に変換 609 | ] 610 | ]; 611 | } 612 | } 613 | } 614 | 615 | clock()->debug($temperature); 616 | 617 | return $temperature; 618 | } 619 | 620 | 621 | /** 622 | * 取得した生の気象データから、今日・明日・明後日の降水確率を取得する 623 | * 624 | * @param array $forecast_data API から取得した気象データ 625 | * @param int $city_index 取得する地域の配列のインデックス 626 | * @param int $city_weekly_index 取得する地域の配列のインデックス(週間天気予報用) 627 | * @return array 整形された気象データ 628 | */ 629 | private static function getChanceOfRain(array $forecast_data, int $city_index, int $city_weekly_index): array 630 | { 631 | $chanceofrain = []; 632 | 633 | $days_datetime = [ 634 | (new DateTimeImmutable('now')), // 現在の時刻 635 | (new DateTimeImmutable('now'))->modify('+1 days'), // 明日の時刻 636 | (new DateTimeImmutable('now'))->modify('+2 days'), // 明後日の時刻 637 | ]; 638 | 639 | // 今日・明日・明後日の降水確率が載っている配列のインデックスを格納 640 | $chanceofrain_index = [ 641 | 'T00_06' => [null, null, null], 642 | 'T06_12' => [null, null, null], 643 | 'T12_18' => [null, null, null], 644 | 'T18_24' => [null, null, null], 645 | ]; 646 | 647 | // timeDefines の中から今日・明日・明後日の日付を見つけ、インデックスを手に入れる 648 | // 最高気温/最低気温同様、降水確率は今日分明日分しかないし今日分の降水確率も過去のは存在しないのでこうせざるを得ない 649 | // 見つからなかったら既定値の null になる 650 | foreach ($forecast_data[0]['timeSeries'][1]['timeDefines'] as $timedefine_index => $timedefine) { 651 | 652 | // 比較対象(処理対象)の時刻 653 | $compare_datetime = new DateTimeImmutable($timedefine); 654 | 655 | foreach ($days_datetime as $day_index => $day_datetime) { 656 | 657 | // 00時~06時の降水確率 658 | if ($compare_datetime == $day_datetime->setTime(0,0)) { 659 | $chanceofrain_index['T00_06'][$day_index] = $timedefine_index; 660 | break; 661 | } 662 | 663 | // 06時~12時の降水確率 664 | if ($compare_datetime == $day_datetime->setTime(6,0)) { 665 | $chanceofrain_index['T06_12'][$day_index] = $timedefine_index; 666 | break; 667 | } 668 | 669 | // 12時~18時の降水確率 670 | if ($compare_datetime == $day_datetime->setTime(12,0)) { 671 | $chanceofrain_index['T12_18'][$day_index] = $timedefine_index; 672 | break; 673 | } 674 | 675 | // 18時~24時の降水確率 676 | if ($compare_datetime == $day_datetime->setTime(18,0)) { 677 | $chanceofrain_index['T18_24'][$day_index] = $timedefine_index; 678 | break; 679 | } 680 | } 681 | } 682 | 683 | // インデックスを手に入れたので、インデックスが null でなければアクセスしてデータを取りに行く 684 | foreach ($days_datetime as $day_index => $day_datetime) { 685 | 686 | // ネスト長過ぎる 687 | $pops = $forecast_data[0]['timeSeries'][1]['areas'][$city_index]['pops']; 688 | 689 | // データを入れる 690 | $chanceofrain[$day_index] = [ 691 | 'T00_06' => ($chanceofrain_index['T00_06'][$day_index] !== null ? $pops[$chanceofrain_index['T00_06'][$day_index]].'%' : '--%'), 692 | 'T06_12' => ($chanceofrain_index['T06_12'][$day_index] !== null ? $pops[$chanceofrain_index['T06_12'][$day_index]].'%' : '--%'), 693 | 'T12_18' => ($chanceofrain_index['T12_18'][$day_index] !== null ? $pops[$chanceofrain_index['T12_18'][$day_index]].'%' : '--%'), 694 | 'T18_24' => ($chanceofrain_index['T18_24'][$day_index] !== null ? $pops[$chanceofrain_index['T18_24'][$day_index]].'%' : '--%'), 695 | ]; 696 | } 697 | 698 | // 明後日の降水確率は常に取得できないはずなので、週間天気予報から持ってくる 699 | // 明日分も0時~5時の間は取得できないと思われる 700 | foreach ($chanceofrain as $chanceofrain_key => $chanceofrain_value) { 701 | 702 | // その日の降水確率がすべて存在しない 703 | if ($chanceofrain_value['T00_06'] === '--%' and 704 | $chanceofrain_value['T06_12'] === '--%' and 705 | $chanceofrain_value['T12_18'] === '--%' and 706 | $chanceofrain_value['T18_24'] === '--%') { 707 | 708 | // 週間天気予報の時刻 709 | $weekly_datetime = $days_datetime[$chanceofrain_key]; 710 | 711 | // 週間天気予報から目当ての日付を見つけ、インデックスを手に入れる 712 | foreach ($forecast_data[1]['timeSeries'][0]['timeDefines'] as $key => $value) { 713 | 714 | // 比較対象の時刻 715 | $compare_datetime = new DateTimeImmutable($value); 716 | 717 | // 同じ日付ならインデックスを取得して抜ける 718 | if ($compare_datetime->setTime(0,0) == $weekly_datetime->setTime(0,0)) { 719 | $weekly_index = $key; 720 | break; 721 | } 722 | } 723 | 724 | // インデックスが定義されている場合だけ 725 | if (isset($weekly_index)) { 726 | 727 | // 降水確率 728 | // 週間天気予報だと時間ごとの詳細な降水確率は取得できないので、全て同じ値に設定する 729 | $weekly_chanceofrain = $forecast_data[1]['timeSeries'][0]['areas'][$city_weekly_index]['pops'][$weekly_index]; 730 | 731 | // 降水確率が空でなければ 732 | // 最初の要素の降水確率は '' になるらしい 733 | if ($weekly_chanceofrain !== '') { 734 | 735 | // データを入れる 736 | $chanceofrain[$chanceofrain_key] = [ 737 | 'T00_06' => $weekly_chanceofrain.'%', 738 | 'T06_12' => $weekly_chanceofrain.'%', 739 | 'T12_18' => $weekly_chanceofrain.'%', 740 | 'T18_24' => $weekly_chanceofrain.'%', 741 | ]; 742 | } 743 | } 744 | } 745 | } 746 | 747 | clock()->debug($chanceofrain); 748 | 749 | return $chanceofrain; 750 | } 751 | } 752 | --------------------------------------------------------------------------------