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