Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- Dockerfile +18 -11
- ac_availability.csv +0 -0
- ac_contract_list_geocoded.csv +0 -0
- ac_guard_master_geocoded.csv +89 -0
- app.R +475 -45
Dockerfile
CHANGED
|
@@ -1,14 +1,21 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
dplyr \
|
| 8 |
-
ggplot2 \
|
| 9 |
-
readr \
|
| 10 |
-
ggExtra
|
| 11 |
-
|
| 12 |
-
COPY . .
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Spaces用 R Shiny Dockerfile
|
| 2 |
+
# rocker/geospatial: leaflet, dplyr等が事前インストール済み
|
| 3 |
+
FROM rocker/geospatial:4.4.1
|
| 4 |
|
| 5 |
+
# shinyをインストール
|
| 6 |
+
RUN R -e "install.packages('shiny', repos='https://cloud.r-project.org/')"
|
| 7 |
|
| 8 |
+
# パッケージ確認
|
| 9 |
+
RUN R -e "library(shiny); library(dplyr); library(leaflet); cat('All packages OK\n')"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
+
# アプリディレクトリ
|
| 12 |
+
WORKDIR /app
|
| 13 |
+
|
| 14 |
+
# ファイルをコピー(新しいデータファイル名に対応)
|
| 15 |
+
COPY app.R ac_contract_list_geocoded.csv ac_guard_master_geocoded.csv ac_availability.csv /app/
|
| 16 |
+
|
| 17 |
+
# ポート設定
|
| 18 |
+
EXPOSE 7860
|
| 19 |
+
|
| 20 |
+
# 実行
|
| 21 |
+
CMD ["R", "-e", "shiny::runApp('/app', host='0.0.0.0', port=7860)"]
|
ac_availability.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
ac_contract_list_geocoded.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
ac_guard_master_geocoded.csv
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ユーザー番号,従業員番号,ユーザーID,苗字,名前,苗字カナ,名前カナ,所属支店,生年月日,年齢,性別,血液型,最低血圧,最高血圧,郵便番号,住所,本籍,最寄り駅,交通手段,電話番号1,電話番号2,緊急連絡先(氏名),続柄,緊急連絡先(電話番号),確認日,緊急連絡先(氏名)2,続柄2,緊急連絡先(電話番号)2,確認日2,緊急連絡先(氏名)3,続柄3,緊急連絡先(電話番号)3,確認日3,扶養家族人数,配偶者の有無,メールアドレス,入社日,home_lat,home_lng
|
| 2 |
+
375,u00016,u00016,,,,,栃木・群馬,,,,,,,321-0152,栃木県宇都宮市西川田6-4-18,,西川田,車,,,,,,,,,,,,,,,,,,42761,36.5551,139.8828
|
| 3 |
+
391,u00033,u00033,,,,,栃木・群馬,,,,,,,321-0974,栃木県宇都宮市竹林町123 クレセントレジデンスC202,秋田県大館市釈迦内字山神台1-2,駅東公園前,車,,,,,028-627-6584,,,,028-627-6584,,,,,,,,,43498,36.5551,139.8828
|
| 4 |
+
397,u00039,u00039,,,,,栃木・群馬,,,,,,,322-0045,栃木県鹿沼市上殿町373-1,栃木県鹿沼市上殿町373-1,新鹿沼,車,,,,,090-2245-5253,,,,090-2245-5253,,,,,,,,,43655,36.567,139.746
|
| 5 |
+
401,u00043,u00043,,,,,栃木・群馬,,,,,,,321-0139,栃木県宇都宮市若松原2-18-9,,雀宮,車,,,,,080-6548-1226,,,,080-6548-1226,,,,,,,,,43783,36.5551,139.8828
|
| 6 |
+
412,u00054,u00054,,,,,栃木・群馬,,,,,,,324-0031,栃木県大田原市藤沢363,,片岡,車,,,,,080-1683-8271,,,,080-1683-8271,,,,,,,,,44096,36.8717,140.015
|
| 7 |
+
422,u00064,u00064,,,,,栃木・群馬,,,,,,,321-0952,栃木県宇都宮市泉が丘6-2-17 アザレヤマンション206,栃木県宇都宮市泉が丘6-2-17 アザレヤマンション206,峰,車,,,,,090-9675-7861,,,,090-9675-7861,,,,,,,,,44324,36.5551,139.8828
|
| 8 |
+
423,u00065,u00065,,,,,栃木・群馬,,,,,,,321-0952,栃木県宇都宮市泉が丘6-2-17 アザレアマンション206,栃木県宇都宮市泉が丘6-2-17 アザレアマンション206,峰,車,,,,,095-844-6043,,,,095-844-6043,,,,,,,,,44324,36.5551,139.8828
|
| 9 |
+
440,u00082,u00082,,,,,栃木・群馬,,,,,,,329-1233,栃木県塩谷郡高根沢町宝積寺2248-32,,宝積寺,車,,,,,028-675-2766,,,,028-675-2766,,,,,,,,,44629,36.6333,139.9833
|
| 10 |
+
447,u00089,u00089,,,,,栃木・群馬,,,,,,,322-0036,栃木県鹿沼市下田町2-1403-16,栃木県鹿沼市下田町2-1403-16,新鹿沼,車,,,,,0289-62-3256,,,,0289-62-3256,,,,,,,,,44819,36.567,139.746
|
| 11 |
+
449,U00091,u00091,,,,,栃木・群馬,,,,,,,321-0111,栃木県宇都宮市川田町1261-3 沢村ハイツ101,,南宇都宮,車,,,,,090-8814-0030,,,,090-8814-0030,,,,,,,,,44887,36.5551,139.8828
|
| 12 |
+
450,u00092,u00092,,,,,栃木・群馬,,,,,,,321-3426,栃木県芳賀郡市貝町赤羽418-27,,多田羅,車,,,,,090-2620-7084,,,,090-2620-7084,,,,,,,,,45026,36.5167,140.0833
|
| 13 |
+
451,u00093,u00093,,,,,栃木・群馬,,,,,,,321-0982,栃木県宇都宮市御幸ケ原町143-19 サンビレッジ御幸ヶ原A203,,峰,車,,,,,080-2305-7654,,,,080-2305-7654,,,,,,,,,45178,36.5551,139.8828
|
| 14 |
+
1022,u00108,k000162,,,,,栃木・群馬,,,,,,,321-0932,栃木県宇都宮市平松本町364 県住541,,駅東公園前,車,,,,,080-6111-5952,,,,080-6111-5952,,,,,,,,,45541,36.5551,139.8828
|
| 15 |
+
1050,u00109,k000190,,,,,栃木・群馬,,,,,,,321-0925,栃木県宇都宮市東簗瀬1丁目42番地4 レクエルドユーミー301,埼玉県秩父市上影森31-1,宇都宮,車,,,,,0494-24-5432,,,,0494-24-5432,,,,,,,,,45545,36.5551,139.8828
|
| 16 |
+
1147,u00114,k000267,,,,,栃木・群馬,,,,,,,321-0236,栃木県下都賀郡壬生町上稲葉1976-2,,東武金崎,車,,,,,080-6757-6348,,,,080-6757-6348,,,,,,,,,45591,36.4258,139.8083
|
| 17 |
+
1148,u00115,k000268,,,,,栃木・群馬,,,,,,,321-4337,栃木県真岡市上高間木1-7-2 スカイヒルズC-102,,真岡駅,車,,,,,090-5337-9153,,,,090-5337-9153,,,,,,,,,45474,36.4403,140.0148
|
| 18 |
+
1241,u00116,k000361,,,,,栃木・群馬,,,,,,,329-1104,栃木県宇都宮市下岡本町1949番地5,栃木県宇都宮市下岡本町1949番地5,岡本駅,車,,,,,090-4535-8389,45682.0,,,090-4535-8389,,,,,,,,,45600,36.5551,139.8828
|
| 19 |
+
1350,u00061,k000458,,,,,栃木・群馬,,,,,,,322-0031,栃木県鹿沼市睦町320睦町市営住宅3-5,栃木県鹿沼市茂呂1858番地18,鹿沼,車,,,,,090-2918-3572,,,,090-2918-3572,,,,,,,,,45772,36.567,139.746
|
| 20 |
+
1353,E00406,k000461,,,,,栃木・群馬,,,,,,,321-3423,栃木県芳賀郡市貝町市塙1215-1,栃木県宇都宮市屋板町529番地1,市塙,車,,,,,090-9016-3608,,,,090-9016-3608,,,,,,,,,45785,36.5167,140.0833
|
| 21 |
+
1415,E00413,k000523,,,,,栃木・群馬,,,,,,,322-0344,栃木県鹿沼市西沢町324番地,栃木県鹿沼市西沢町324番地,樅山,車,,,,,090-8894-0993,,,,090-8894-0993,,,,,,,,,45930,36.567,139.746
|
| 22 |
+
1425,F00408,k000533,,,,,栃木・群馬,,,,,,,321-0152,栃木県宇都宮市西川田5丁目27番13号 シャトルハイツA102,栃木県宇都宮市川田町884番地,西川田,車,,,,,090-6658-0525,,,,090-6658-0525,,,,,,,,,45916,36.5551,139.8828
|
| 23 |
+
1488,F00411,k000568,,,,,栃木・群馬,,,,,,,321-0932,栃木県宇都宮市平松本町364番地 県営平松本町住宅5棟41号 ,栃木県宇都宮市平松本町364番地1 ,駅東公園前,車,,,,,080-5984-9707,,,,080-5984-9707,,,,,,,,,45973,36.5551,139.8828
|
| 24 |
+
476,E00113,e00113,,,,,栃木・群馬,,,,,,,307-0042,茨城県結城市江川新宿1988番地,,間々田,車,,,,,0296-35-2701,,,,0296-35-2701,,,,,,,,,38844,36.3058,139.8767
|
| 25 |
+
479,E00347,e00347,,,,,栃木・群馬,,,,,,,329-4412,栃木県栃木市大平町北武井670番地,,栃木,車,,,,,080-1248-7855,,,,080-1248-7855,,,,,,,,,41699,36.3817,139.73
|
| 26 |
+
482,E00384,e00384,,,,,栃木・群馬,,,,,,,328-0075,栃木県栃木市箱森町11-11,,栃木,車,,,,,0282-24-6701,,,,0282-24-6701,,,,,,,,,44512,36.3817,139.73
|
| 27 |
+
487,E00391,e00391,,,,,栃木・群馬,,,,,,,307-0037,茨城県結城市東茂呂1639,,間々田,車,,,,,0296-48-0621,,,,0296-48-0621,,,,,,,,,45204,36.3058,139.8767
|
| 28 |
+
488,F00349,f00349,,,,,栃木・群馬,,,,,,,323-0829,栃木県小山市東城南3丁目4番地2,,小山,車,,,,,080-1041-6718,,,,080-1041-6718,,,,,,,,,42010,36.3147,139.8012
|
| 29 |
+
494,F00400,f00400,,,,,栃木・群馬,,,,,,,327-0004,栃木県佐野市赤坂町200-6 レジデンス赤坂B棟,,佐野駅,電車,,,,,090-9011-4893,,,,090-9011-4893,,,,,,,,,45204,36.3142,139.5783
|
| 30 |
+
506,E00349,e00349,,,,,栃木・群馬,,,,,,,329-0205,栃木県小山市間々田871番地2,,間々田,電車,,,,,080-3540-4550,,,,080-3540-4550,,,,,,,,,41706,36.3147,139.8012
|
| 31 |
+
509,F00306,f00306,,,,,栃木・群馬,,,,,,,328-0054,栃木県栃木市平井町88番地7,,栃木,電車,,,,,090-3576-7883,,,,090-3576-7883,,,,,,,,,40579,36.3817,139.73
|
| 32 |
+
510,F00326,f00326,,,,,栃木・群馬,,,,,,,307-0001,茨城県結城市大木2377番地1,,小田林,車,,,,,0296-35-1180,,,,0296-35-1180,,,,,,,,,41343,36.3058,139.8767
|
| 33 |
+
1156,F00407,k000276,,,,,栃木・群馬,,,,,,,323-0826,栃木県小山市雨ケ谷827番地3,,小山駅,車,,,,,090-1552-0266,45604.0,,,090-1552-0266,,,,,,,,,45598,36.3147,139.8012
|
| 34 |
+
1447,E00416,k000555,,,,,栃木・群馬,,,,,,,329-4304,栃木県栃木市岩舟町静和306番地1,栃木県栃木市岩舟町静和856番地1,静和,車,,,,,090-4598-3000,,,,090-4598-3000,,,,,,,,,45959,36.3817,139.73
|
| 35 |
+
1454,E00417,k000562,,,,,栃木・群馬,,,,,,,323-0818,栃木県小山市大字塚崎1389番地,栃木県小山市大字塚崎1389番地,間々田,車,,,,,070-3666-5345,,,,070-3666-5345,,,,,,,,,45966,36.3147,139.8012
|
| 36 |
+
478,E00342,e00342,,,,,栃木・群馬,,,,,,,324-0012,栃木県大田原市南金丸381番地2,,那須塩原,車,,,,,080-1347-5090,,,,080-1347-5090,,,,,,,,,41529,36.8717,140.015
|
| 37 |
+
480,E00352,e00352,,,,,栃木・群馬,,,,,,,329-1311,栃木県さくら市氏家1806-8,,氏家,車,,,,,,,,,,,,,,,,,,41825,36.6858,139.9673
|
| 38 |
+
483,E00385,e00385,,,,,栃木・群馬,,,,,,,371-0015,群馬県前橋市三河町2-4-1 ライオンズマンション前橋316号,,前橋,電車,,,,,027-288-2687,,,,027-288-2687,,,,,,,,,44384,36.3911,139.0608
|
| 39 |
+
490,F00386,f00386,,,,,栃木・群馬,,,,,,,366-0002,埼玉県深谷市下手計1059番地 コスモリヴェール3-B号,,深谷,電車,,,,,080-4141-0426,,,,080-4141-0426,,,,,,,,,44354,36.1975,139.2814
|
| 40 |
+
491,F00390,f00390,,,,,栃木・群馬,,,,,,,320-0056,栃木県宇都宮市戸祭3-8-17 グリーンプラザ202,,東武宇都宮,車,,,,,090-4941-5349,,,,090-4941-5349,,,,,,,,,44798,36.5551,139.8828
|
| 41 |
+
496,F00402,f00402,,,,,栃木・群馬,,,,,,,370-0844,群馬県高崎市和田多中町13-19,,南高崎,徒歩,,,,,027-325-3623,,,,027-325-3623,,,,,,,,,45331,36.3219,139.0032
|
| 42 |
+
497,M00017,m00017,,,,,栃木・群馬,,,,,,,370-0852,群馬県高崎市中居町2丁目22番地1 県住F棟151号,,倉賀野,車,,,,,027-253-4464,,,,027-253-4464,,,,,,,,,40088,36.3219,139.0032
|
| 43 |
+
498,M00021,m00021,,,,,栃木・群馬,,,,,,,370-0004,群馬県高崎市井野町16番地4 吉田アパート104号,,井野(群馬県),車,,,,,0273-64-3485,,,,0273-64-3485,,,,,,,,,40138,36.3219,139.0032
|
| 44 |
+
499,M00026,m00026,,,,,栃木・群馬,,,,,,,375-0014,群馬県藤岡市下栗須45番地13,,群馬藤岡,車,,,,,080-6625-1498,,,,080-6625-1498,,,,,,,,,40325,36.2556,139.0736
|
| 45 |
+
500,M00056,m00056,,,,,栃木・群馬,,,,,,,370-3103,群馬県高崎市箕郷町下芝395番地3,,群馬八幡,車,,,,,090-2629-8848,,,,090-2629-8848,,,,,,,,,41913,36.3219,139.0032
|
| 46 |
+
501,M00083,m00083,,,,,栃木・群馬,,,,,,,367-0055,埼玉県本庄市若泉2丁目7番10号,,本庄,電車,,,,,090-8586-6388,,,,090-8586-6388,,,,,,,,,43357,36.2433,139.1903
|
| 47 |
+
502,M00087,m00087,,,,,栃木・群馬,,,,,,,370-1201,群馬県高崎市倉賀野町6387 エトワールナイン201号,,倉賀野,車,,,,,027-327-9065,,,,027-327-9065,,,,,,,,,43613,36.3219,139.0032
|
| 48 |
+
503,E00355,e00355,,,,,栃木・群馬,,,,,,,321-0132,栃木県宇都宮市雀の宮5丁目6番17号,,雀宮,車,,,,,028-655-3367,,,,028-655-3367,,,,,,,,,41890,36.5551,139.8828
|
| 49 |
+
504,M00042,m00042,,,,,栃木・群馬,,,,,,,370-0851,群馬県高崎市上中居町263番地1 ハーベスト上中居103,,高崎,徒歩,,,,,011-836-0353,,,,011-836-0353,,,,,,,,,41221,36.3219,139.0032
|
| 50 |
+
505,E00191,e00191,,,,,栃木・群馬,,,,,,,963-1411,福島県郡山市湖南町舟津849,,上戸(福島県),車,,,,,090-9371-7612,,,,090-9371-7612,,,,,,,,,39284,37.4,140.3597
|
| 51 |
+
507,M00075,m00075,,,,,栃木・群馬,,,,,,,370-1111,群馬県佐波郡玉村町南玉1012番地6,,新町(群馬県),車,,,,,0270-64-0525,,,,0270-64-0525,,,,,,,,,42980,36.3,139.11
|
| 52 |
+
508,M00076,m00076,,,,,栃木・群馬,,,,,,,370-0046,群馬県高崎市江木町1142-1 ロイヤル88 2B,,高崎問屋町,徒歩,,,,,080-4129-3321,,,,080-4129-3321,,,,,,,,,43088,36.3219,139.0032
|
| 53 |
+
511,F00356,f00356,,,,,栃木・群馬,,,,,,,320-0011,栃木県宇都宮市富士見が丘4-4-8 ニュー富士見マンション101,,宇都宮,車,,,,,080-3368-3845,,,,080-3368-3845,,,,,,,,,42567,36.5551,139.8828
|
| 54 |
+
512,E00393,e00393,,,,,栃木・群馬,,,,,,,329-1104,栃木県宇都宮市下岡本町3766-9,,岡本(栃木県),車,,,,,080-5079-5634,,,,080-5079-5634,,,,,,,,,45371,36.5551,139.8828
|
| 55 |
+
516,M00096,m00096,,,,,栃木・群馬,,,,,,,370-0873,群馬県高崎市豊岡町1581,,北高崎,車,,,,,090-5430-1906,,,,090-5430-1906,,,,,,,,,45312,36.3219,139.0032
|
| 56 |
+
1141,E00399,k000261,,,,,栃木・群馬,,,,,,,329-0618,栃木県河内郡上三川町しらさぎ1-23-8,,石橋(栃木県),車,,,,,,,,,,,,,,,,,,45595,36.4417,139.9117
|
| 57 |
+
1242,E00402,k000362,,,,,栃木・群馬,,,,,,,321-0973,栃木県宇都宮市岩曽町807番地11,静岡県御殿場市川島田698番地4,宇都宮駅,車,,,,,090-2420-3087,45685.0,,,090-2420-3087,,,,,,,,,45682,36.5551,139.8828
|
| 58 |
+
1252,E00403,k000372,,,,,栃木・群馬,,,,,,,370-0816,群馬県高崎市常盤町127番地1 エトワール高崎常盤町402号,青森県つがる市木造筒木坂松本27番地,高崎駅,徒歩,,,,,046-281-2671,45704.0,,,046-281-2671,,,,,,,,,45700,36.3219,139.0032
|
| 59 |
+
1273,E00404,k000393,,,,,栃木・群馬,,,,,,,379-0111,群馬県安中市板鼻618番地52,群馬県安中市板鼻618番地52,安中,車,,,,,090-2569-9618,45717.0,,,090-2569-9618,,,,,,,,,45700,36.3306,138.8881
|
| 60 |
+
1328,E00405,k000436,,,,,栃木・群馬,,,,,,,321-4363,栃木県真岡市亀山104番地12,栃木県真岡市東大島1242番地2,真岡,車,,,,,090-2201-1335,45756.0,,,090-2201-1335,,,,,,,,,45744,36.4403,140.0148
|
| 61 |
+
1494,E00419,k000574,,,,,栃木・群馬,,,,,,,370-1201,群馬県高崎市倉賀野町1815 シオン桜木201,群馬県高崎市倉賀野町1815番地1 ,倉賀野,電車,,,,,090-9208-9116,,,,090-9208-9116,,,,,,,,,45958,36.3219,139.0032
|
| 62 |
+
979,W00003,k000119,,,,,栃木・群馬,,,,,,,379-2311,群馬県みどり市笠懸町阿左美484番地10,,阿左美,車,,,,,090-3474-5581,,,,090-3474-5581,,,,,,,,,45503,36.3958,139.2817
|
| 63 |
+
1128,X00012,k000248,,,,,栃木・群馬,,,,,,,370-3501,群馬県北群馬郡榛東村長岡209,,八木原,車,,,,,090-8493-2106,,,,090-8493-2106,,,,,,,,,45556,36.45,139.0
|
| 64 |
+
1138,X00013,k000258,,,,,栃木・群馬,,,,,,,373-0832,群馬県太田市富沢町172-6,,細谷(群馬県),車,,,,,0276-38-3754,,,,0276-38-3754,,,,,,,,,45563,36.2917,139.3756
|
| 65 |
+
1167,W00007,k000287,,,,,栃木・群馬,,,,,,,370-0511,群馬県邑楽郡大泉町北小泉1丁目8−14,群馬県邑楽郡大泉町北小泉1丁目8番,小泉町(群馬県),車,,,,,0276-61-0683,45607.0,,,0276-61-0683,,,,,,,,,45600,36.25,139.4
|
| 66 |
+
1175,X00015,k000295,,,,,栃木・群馬,,,,,,,370-0522,群馬県邑楽郡大泉町富士1丁目6番21号,群馬県邑楽郡大泉町1丁目1688番地27,西小泉,車,,,,,0276-63-6193,45615.0,,,0276-63-6193,,,,,,,,,45615,36.25,139.4
|
| 67 |
+
1176,X00017,k000296,,,,,栃木・群馬,,,,,,,360-0831,埼玉県熊谷市久保島811番地1 熊谷玉井住宅B-612,埼玉県熊谷市久保島811番地1,籠原,車,,,,,070-1426-4750,45619.0,,,070-1426-4750,,,,,,,,,45619,36.1472,139.3886
|
| 68 |
+
1282,W00009,k000402,,,,,栃木・群馬,,,,,,,379-2313,群馬県みどり市笠懸町鹿615番地19,群馬県みどり市笠懸町鹿615番地19,岩宿,車,,,,,090-3405-0272,,,,090-3405-0272,,,,,,,,,45720,36.3958,139.2817
|
| 69 |
+
1367,X00018,k000475,,,,,栃木・群馬,,,,,,,326-0338,栃木県足利市福居町1511番地8,群馬県桐生市相生町2丁目518番地10,福居,車,,,,,080-3215-0091,,,,080-3215-0091,,,,,,,,,45839,36.3408,139.4496
|
| 70 |
+
1388,E00408,k000496,,,,,栃木・群馬,,,,,,,326-0824,栃木県足利市八幡町600番地20 県営八幡住宅243,栃木県足利市若草町3番地3 ,野州山辺,車,,,,,080-125-17584,,,,080-125-17584,,,,,,,,,45887,36.3408,139.4496
|
| 71 |
+
1397,E00410,k000505,,,,,栃木・群馬,,,,,,,377-0003,群馬県渋川市八木原1280番地2 油屋アパート1号室,群馬県渋川市北橘町真壁1634番地58 ,八木原,車,,,,,090-4412-7342,,,,090-4412-7342,,,,,,,,,45894,36.4892,139.0
|
| 72 |
+
1428,F00414,k000536,,,,,栃木・群馬,,,,,,,370-0883,群馬県高崎市剣崎町414番地10,群馬県高崎市剣崎町414番地10,群馬八幡,車,,,,,090-1100-3025,,,,090-1100-3025,,,,,,,,,45944,36.3219,139.0032
|
| 73 |
+
392,u00034,u00034,,,,,栃木・群馬,,,,,,,320-0855,栃木県宇都宮市上欠町1231-342,栃木県宇都宮市上欠町1231-342,鶴田,車,,,,,028-648-1660,,,,028-648-1660,,,,,,,,,43517,36.5551,139.8828
|
| 74 |
+
431,u00073,u00073,,,,,栃木・群馬,,,,,,,320-0075,栃木県宇都宮市宝木本町1148-31,,東武宇都宮,車,,,,,090-5767-9202,,,,090-5767-9202,,,,,,,,,44343,36.5551,139.8828
|
| 75 |
+
454,u00096,u00096,,,,,栃木・群馬,,,,,,,321-3222,栃木県宇都宮市野高谷町515-2,栃木県さくら市下河戸770,ゆいの杜東,車,,,,,090-7329-1217,,,,090-7329-1217,,,,,,,,,45264,36.5551,139.8828
|
| 76 |
+
492,F00398,f00398,,,,,栃木・群馬,,,,,,,323-0811,栃木県小山市犬塚3-26-20 ルミエールV102,,,,,,,,090-2022-5821,,,,090-2022-5821,,,,,,,,,45109,36.3147,139.8012
|
| 77 |
+
493,F00399,f00399,,,,,栃木・群馬,,,,,,,306-0212,茨城県古河市久能1139-1,,,,,,,,090-5339-2813,,,,090-5339-2813,,,,,,,,,45159,36.1947,139.6994
|
| 78 |
+
1018,k000158,k000158,,,,,栃木・群馬,,,,,,,326-0822,栃木県足利市田中町942-3 サクラマンション301,,足利市,車,,,,,080-5194-8108,,,,080-5194-8108,,,,,,,,,45519,36.3408,139.4496
|
| 79 |
+
1020,X00010,k000160,,,,,栃木・群馬,,,,,,,326-0141,栃木県足利市小俣町382-3,,小俣(栃木県),車,,,,,090-1460-0858,,,,090-1460-0858,,,,,,,,,45519,36.3408,139.4496
|
| 80 |
+
1177,X00016,k000297,,,,,栃木・群馬,,,,,,,326-0335,栃木県足利市上渋垂町119番地1,栃木県足利市福富町2013番地1,福居,車,,,,,090-5550-8568,45619.0,,,090-5550-8568,,,,,,,,,45619,36.3408,139.4496
|
| 81 |
+
1225,E00401,k000345,,,,,栃木・群馬,,,,,,,323-0027,栃木県小山市花垣町1丁目6番29号,栃木県小山市花垣町1丁目6番,小山駅,車,,,,,080-6785-1219,45670.0,,,080-6785-1219,,,,,,,,,45311,36.3147,139.8012
|
| 82 |
+
1358,E00407,k000466,,,,,栃木・群馬,,,,,,,3200075,栃木県宇都宮市宝木本町1246番地7 アムールアドニスⅡ 201,栃木県日光市土沢1810番地14,東武宇都宮,車,,,,,090-4923-8916,,,,090-4923-8916,,,,,,,,,45821,36.5551,139.8828
|
| 83 |
+
1391,E00409,k000499,,,,,栃木・群馬,,,,,,,322-0526,栃木県鹿沼市楡木町661番地4,栃木県鹿沼市下奈良部町307番地,楡木,車,,,,,090-7191-7239,,,,090-7191-7239,,,,,,,,,45895,36.567,139.746
|
| 84 |
+
1412,F00409,k000520,,,,,栃木・群馬,,,,,,,322-0026,栃木県鹿沼市茂呂1175−2 K.U.3A 102,韓国,鹿沼,車,,,,,070-5545-4903,,,,070-5545-4903,,,,,,,,,45922,36.567,139.746
|
| 85 |
+
1432,E00415,k000540,,,,,栃木・群馬,,,,,,,307-0001,茨城県結城市大字結城10753番地6,茨城県結城市大字結城10753番地6,小田林,車,,,,,080-5505-8698,,,,080-5505-8698,,,,,,,,,45947,36.3058,139.8767
|
| 86 |
+
1491,E00418,k000571,,,,,栃木・群馬,,,,,,,321-0904,栃木県宇都宮市陽東4-14-40 ひがしハイツワンA101,秋田県鹿角郡小坂町小坂字下前田35-6,陽東3丁目,車,,,,,090-4606-8700,,,,090-4606-8700,,,,,,,,,45978,36.5551,139.8828
|
| 87 |
+
481,E00362,e00362,,,,,栃木・群馬,,,,,,,323-0014,栃木県小山市喜沢1148番地18,栃木県下野市1657番地24,小山,車,,,,,070-2797-1224,,,,070-2797-1224,,,,,,,,,42786,36.3147,139.8012
|
| 88 |
+
486,E00389,e00389,,,,,栃木・群馬,,,,,,,320-0053,栃木県宇都宮市戸祭町3018-2 むぎくら第4ハイツB203号室,,,,,,,,090-4636-6540,,,,090-4636-6540,,,,,,,,,45054,36.5551,139.8828
|
| 89 |
+
763,A01036,a01036,,,,,栃木・群馬,,,,,,,310-0913,茨城県水戸市見川町2131番地952 メゾン見川103号,茨城県水戸市見川町2131番地952,赤塚駅,車,,,,,090-6115-0195,,,,090-6115-0195,,,,,,,,,41366,36.3419,140.4467
|
app.R
CHANGED
|
@@ -1,58 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
library(shiny)
|
| 2 |
-
library(bslib)
|
| 3 |
library(dplyr)
|
| 4 |
-
library(
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
),
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
)
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
server <- function(input, output, session) {
|
| 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 |
shinyApp(ui, server)
|
|
|
|
| 1 |
+
# app.R - 警備マッチング可視化アプリ(改良版)
|
| 2 |
+
# 要件: 充足率表示、日付期間選択、要営業強化エリア識別
|
| 3 |
+
|
| 4 |
library(shiny)
|
|
|
|
| 5 |
library(dplyr)
|
| 6 |
+
library(leaflet)
|
| 7 |
+
|
| 8 |
+
# =========================================================
|
| 9 |
+
# 0) 設定
|
| 10 |
+
# =========================================================
|
| 11 |
+
BBOX <- list(
|
| 12 |
+
lat_min = 36.0,
|
| 13 |
+
lat_max = 36.9,
|
| 14 |
+
lng_min = 139.3,
|
| 15 |
+
lng_max = 140.3
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
# 充足率の閾値
|
| 19 |
+
THRESHOLD_LOW <- 80 # 80%未満 = 要営業強化(赤)
|
| 20 |
+
THRESHOLD_HIGH <- 100 # 100%以上 = 充足(緑)
|
| 21 |
+
|
| 22 |
+
# =========================================================
|
| 23 |
+
# 1) ユーティリティ
|
| 24 |
+
# =========================================================
|
| 25 |
+
read_csv_bom <- function(path) {
|
| 26 |
+
if (!file.exists(path)) stop(paste("ファイルが見つからない:", path))
|
| 27 |
+
df <- tryCatch(
|
| 28 |
+
read.csv(path, fileEncoding = "UTF-8-BOM", stringsAsFactors = FALSE, check.names = FALSE),
|
| 29 |
+
error = function(e) read.csv(path, stringsAsFactors = FALSE, check.names = FALSE)
|
| 30 |
+
)
|
| 31 |
+
df
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
to_date <- function(x) {
|
| 35 |
+
if (inherits(x, "Date")) return(x)
|
| 36 |
+
x <- as.character(x)
|
| 37 |
+
x <- trimws(x)
|
| 38 |
+
x[x == ""] <- NA
|
| 39 |
+
x2 <- gsub("/", "-", x, fixed = TRUE)
|
| 40 |
+
as.Date(x2)
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
haversine_km <- function(lat1, lon1, lat2, lon2) {
|
| 44 |
+
r <- 6371.0
|
| 45 |
+
to_rad <- function(x) x * pi / 180
|
| 46 |
+
dlat <- to_rad(lat2 - lat1)
|
| 47 |
+
dlon <- to_rad(lon2 - lon1)
|
| 48 |
+
a <- sin(dlat/2)^2 + cos(to_rad(lat1)) * cos(to_rad(lat2)) * sin(dlon/2)^2
|
| 49 |
+
2 * r * asin(pmin(1, sqrt(a)))
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
extract_city <- function(addr) {
|
| 53 |
+
if (is.na(addr) || trimws(addr) == "") return(NA_character_)
|
| 54 |
+
a <- gsub(" ", " ", addr, fixed = TRUE)
|
| 55 |
+
a <- gsub("\\(.*?\\)", "", a)
|
| 56 |
+
a <- gsub("\\(.*?\\)", "", a)
|
| 57 |
+
a2 <- sub(".*県", "", a)
|
| 58 |
+
m <- regexpr("(市|町|村|区)", a2)
|
| 59 |
+
if (m[1] == -1) return(NA_character_)
|
| 60 |
+
endpos <- m[1] + attr(m, "match.length") - 1
|
| 61 |
+
substr(a2, 1, endpos)
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
# 充足率に応じた色を返す
|
| 65 |
+
get_fill_color <- function(rate) {
|
| 66 |
+
case_when(
|
| 67 |
+
is.na(rate) ~ "gray",
|
| 68 |
+
rate < THRESHOLD_LOW ~ "#e74c3c", # 赤 - 要営業強化
|
| 69 |
+
rate < THRESHOLD_HIGH ~ "#f39c12", # オレンジ - 注意
|
| 70 |
+
TRUE ~ "#27ae60" # 緑 - 充足
|
| 71 |
+
)
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
# =========================================================
|
| 75 |
+
# 2) データ読み込み&前処理
|
| 76 |
+
# =========================================================
|
| 77 |
+
load_all_data <- function() {
|
| 78 |
+
# ファイル名を新しいデータに変更
|
| 79 |
+
contract_raw <- read_csv_bom("ac_contract_list_geocoded.csv")
|
| 80 |
+
guard_raw <- read_csv_bom("ac_guard_master_geocoded.csv")
|
| 81 |
+
avail_raw <- read_csv_bom("ac_availability.csv")
|
| 82 |
+
|
| 83 |
+
# ---- contract_list ----
|
| 84 |
+
contract <- contract_raw %>%
|
| 85 |
+
mutate(
|
| 86 |
+
`案件予定日(開始)` = to_date(`案件予定日(開始)`),
|
| 87 |
+
`案件予定日(終了)` = to_date(`案件予定日(終了)`),
|
| 88 |
+
現場住所 = paste0(
|
| 89 |
+
ifelse(is.na(`現場住所1`), "", `現場住所1`),
|
| 90 |
+
ifelse(is.na(`現場住所2`) | trimws(`現場住所2`) == "", "", paste0(" ", `現場住所2`))
|
| 91 |
+
),
|
| 92 |
+
required_guards = `必要人数`,
|
| 93 |
+
site_city = `市区町村`
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
# ---- guard_master ----
|
| 97 |
+
# 「従業員番号」の代わりに「ユーザー番号」を使用
|
| 98 |
+
guard <- guard_raw %>%
|
| 99 |
+
mutate(
|
| 100 |
+
ユーザー番号 = suppressWarnings(as.integer(`ユーザー番号`)),
|
| 101 |
+
guard_city = vapply(`住所`, extract_city, character(1))
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
# ---- availability ----
|
| 105 |
+
# 「従業員番号」の代わりに「ユーザー番号」を使用
|
| 106 |
+
avail <- avail_raw %>%
|
| 107 |
+
mutate(
|
| 108 |
+
日付 = to_date(`日付`),
|
| 109 |
+
ユーザー番号 = suppressWarnings(as.integer(`ユーザー番号`)),
|
| 110 |
+
available_flag = as.integer(`対応可否`)
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
list(contract = contract, guard = guard, avail = avail)
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
DATA <- NULL
|
| 117 |
+
LOAD_ERROR <- NULL
|
| 118 |
+
tryCatch({
|
| 119 |
+
DATA <- load_all_data()
|
| 120 |
+
}, error = function(e) {
|
| 121 |
+
LOAD_ERROR <<- e$message
|
| 122 |
+
})
|
| 123 |
+
|
| 124 |
+
# =========================================================
|
| 125 |
+
# 3) UI
|
| 126 |
+
# =========================================================
|
| 127 |
+
ui <- fluidPage(
|
| 128 |
+
tags$head(
|
| 129 |
+
tags$link(rel = "icon", href = "data:,"),
|
| 130 |
+
tags$style(HTML("
|
| 131 |
+
.legend-box { padding: 10px; background: white; border-radius: 5px; }
|
| 132 |
+
.legend-item { display: flex; align-items: center; margin: 5px 0; }
|
| 133 |
+
.legend-color { width: 20px; height: 20px; border-radius: 50%; margin-right: 8px; }
|
| 134 |
+
.summary-header { background-color: #f8f9fa; padding: 10px; border-radius: 5px; margin-bottom: 10px; }
|
| 135 |
+
"))
|
| 136 |
),
|
| 137 |
+
titlePanel("警備マッチング可視化(充足率・期間選択対応)"),
|
| 138 |
+
sidebarLayout(
|
| 139 |
+
sidebarPanel(
|
| 140 |
+
if (!is.null(LOAD_ERROR)) {
|
| 141 |
+
tags$div(
|
| 142 |
+
style = "color:#b00020; font-weight:600;",
|
| 143 |
+
paste0("データ読込エラー: ", LOAD_ERROR),
|
| 144 |
+
tags$br(),
|
| 145 |
+
"同じフォルダに ac_contract_list_geocoded.csv / ac_guard_master_geocoded.csv / ac_availability.csv を置いているか確認する。"
|
| 146 |
+
)
|
| 147 |
+
} else {
|
| 148 |
+
tagList(
|
| 149 |
+
h4("📅 期間選択"),
|
| 150 |
+
dateRangeInput(
|
| 151 |
+
"date_range",
|
| 152 |
+
"対象期間",
|
| 153 |
+
start = min(DATA$contract$`案件予定日(開始)`, na.rm = TRUE),
|
| 154 |
+
end = min(DATA$contract$`案件予定日(開始)`, na.rm = TRUE) + 7,
|
| 155 |
+
min = min(DATA$contract$`案件予定日(開始)`, na.rm = TRUE),
|
| 156 |
+
max = max(DATA$contract$`案件予定日(終了)`, na.rm = TRUE),
|
| 157 |
+
format = "yyyy/mm/dd",
|
| 158 |
+
language = "ja",
|
| 159 |
+
separator = " ~ "
|
| 160 |
+
),
|
| 161 |
+
hr(),
|
| 162 |
+
h4("🗺️ 表示設定"),
|
| 163 |
+
checkboxInput("show_guards", "アベイラブル隊員を表示", TRUE),
|
| 164 |
+
radioButtons(
|
| 165 |
+
"match_mode",
|
| 166 |
+
"マッチング方法",
|
| 167 |
+
choices = c("市区町村一致" = "city", "距離(半径km)" = "dist"),
|
| 168 |
+
selected = "city"
|
| 169 |
+
),
|
| 170 |
+
conditionalPanel(
|
| 171 |
+
condition = "input.match_mode == 'dist'",
|
| 172 |
+
numericInput("radius_km", "半径(km)", value = 20, min = 1, max = 200, step = 1)
|
| 173 |
+
),
|
| 174 |
+
hr(),
|
| 175 |
+
h4("📊 凡例"),
|
| 176 |
+
tags$div(
|
| 177 |
+
class = "legend-box",
|
| 178 |
+
tags$div(class = "legend-item",
|
| 179 |
+
tags$div(class = "legend-color", style = "background-color: #27ae60;"),
|
| 180 |
+
tags$span("充足(100%以上)")
|
| 181 |
+
),
|
| 182 |
+
tags$div(class = "legend-item",
|
| 183 |
+
tags$div(class = "legend-color", style = "background-color: #f39c12;"),
|
| 184 |
+
tags$span("注意(80-99%)")
|
| 185 |
+
),
|
| 186 |
+
tags$div(class = "legend-item",
|
| 187 |
+
tags$div(class = "legend-color", style = "background-color: #e74c3c;"),
|
| 188 |
+
tags$span("要営業強化(80%未満)")
|
| 189 |
+
),
|
| 190 |
+
tags$div(class = "legend-item",
|
| 191 |
+
tags$div(class = "legend-color", style = "background-color: #3498db; opacity: 0.7;"),
|
| 192 |
+
tags$span("アベイラブル隊員")
|
| 193 |
+
)
|
| 194 |
+
)
|
| 195 |
+
)
|
| 196 |
+
},
|
| 197 |
+
width = 3
|
| 198 |
+
),
|
| 199 |
+
mainPanel(
|
| 200 |
+
# サマリー統計
|
| 201 |
+
fluidRow(
|
| 202 |
+
column(3,
|
| 203 |
+
tags$div(class = "summary-header",
|
| 204 |
+
h5("稼働現場数"),
|
| 205 |
+
textOutput("stat_sites", inline = TRUE)
|
| 206 |
+
)
|
| 207 |
+
),
|
| 208 |
+
column(3,
|
| 209 |
+
tags$div(class = "summary-header",
|
| 210 |
+
h5("必要人数合計"),
|
| 211 |
+
textOutput("stat_required", inline = TRUE)
|
| 212 |
+
)
|
| 213 |
+
),
|
| 214 |
+
column(3,
|
| 215 |
+
tags$div(class = "summary-header",
|
| 216 |
+
h5("対応可能人数"),
|
| 217 |
+
textOutput("stat_available", inline = TRUE)
|
| 218 |
+
)
|
| 219 |
+
),
|
| 220 |
+
column(3,
|
| 221 |
+
tags$div(class = "summary-header",
|
| 222 |
+
h5("平均充足率"),
|
| 223 |
+
textOutput("stat_avg_rate", inline = TRUE)
|
| 224 |
+
)
|
| 225 |
+
)
|
| 226 |
+
),
|
| 227 |
+
leafletOutput("map", height = 480),
|
| 228 |
+
br(),
|
| 229 |
+
h4("現場別 需給状況(選択期間)"),
|
| 230 |
+
tableOutput("summary"),
|
| 231 |
+
width = 9
|
| 232 |
+
)
|
| 233 |
+
)
|
| 234 |
)
|
| 235 |
|
| 236 |
+
# =========================================================
|
| 237 |
+
# 4) Server
|
| 238 |
+
# =========================================================
|
| 239 |
server <- function(input, output, session) {
|
| 240 |
+
if (!is.null(LOAD_ERROR)) {
|
| 241 |
+
output$map <- renderLeaflet({ leaflet() %>% addTiles() })
|
| 242 |
+
output$summary <- renderTable({
|
| 243 |
+
data.frame(エラー = LOAD_ERROR, stringsAsFactors = FALSE)
|
| 244 |
+
})
|
| 245 |
+
return()
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
contract <- DATA$contract
|
| 249 |
+
guard <- DATA$guard
|
| 250 |
+
avail <- DATA$avail
|
| 251 |
+
|
| 252 |
+
# 選択期間のデータを取得
|
| 253 |
+
daily <- reactive({
|
| 254 |
+
date_range <- input$date_range
|
| 255 |
+
d_start <- date_range[1]
|
| 256 |
+
d_end <- date_range[2]
|
| 257 |
+
|
| 258 |
+
# 期間内に稼働中の現場
|
| 259 |
+
active_sites <- contract %>%
|
| 260 |
+
filter(!is.na(`案件予定日(開始)`), !is.na(`案件予定日(終了)`)) %>%
|
| 261 |
+
filter(`案件予定日(開始)` <= d_end, `案件予定日(終了)` >= d_start)
|
| 262 |
+
|
| 263 |
+
# 期間内で「対応可」の日が1日でもある隊員
|
| 264 |
+
# 「従業員番号」→「ユーザー番号」に変更
|
| 265 |
+
available_emp <- avail %>%
|
| 266 |
+
filter(!is.na(日付), 日付 >= d_start, 日付 <= d_end, available_flag == 1L) %>%
|
| 267 |
+
distinct(ユーザー番号)
|
| 268 |
+
|
| 269 |
+
available_guards <- guard %>%
|
| 270 |
+
inner_join(available_emp, by = "ユーザー番号")
|
| 271 |
+
|
| 272 |
+
list(
|
| 273 |
+
date_start = d_start,
|
| 274 |
+
date_end = d_end,
|
| 275 |
+
active_sites = active_sites,
|
| 276 |
+
available_guards = available_guards
|
| 277 |
+
)
|
| 278 |
})
|
| 279 |
+
|
| 280 |
+
# 需給サマリー計算
|
| 281 |
+
summary_tbl <- reactive({
|
| 282 |
+
dd <- daily()
|
| 283 |
+
sites <- dd$active_sites
|
| 284 |
+
gds <- dd$available_guards
|
| 285 |
+
|
| 286 |
+
if (nrow(sites) == 0) {
|
| 287 |
+
return(data.frame(メッセージ = "選択期間に稼働中の現場がない", stringsAsFactors = FALSE))
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
if (input$match_mode == "city") {
|
| 291 |
+
# gdsが空の場合は空のデータフレームを作成(型を明示)
|
| 292 |
+
if (nrow(gds) == 0) {
|
| 293 |
+
g_by_city <- data.frame(
|
| 294 |
+
guard_city = character(0),
|
| 295 |
+
available_guards = integer(0),
|
| 296 |
+
stringsAsFactors = FALSE
|
| 297 |
)
|
| 298 |
+
} else {
|
| 299 |
+
g_by_city <- gds %>%
|
| 300 |
+
mutate(guard_city = as.character(ifelse(is.na(guard_city), "不明", guard_city))) %>%
|
| 301 |
+
count(guard_city, name = "available_guards")
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
result <- sites %>%
|
| 305 |
+
mutate(site_city = as.character(ifelse(is.na(site_city), "不明", site_city))) %>%
|
| 306 |
+
left_join(g_by_city, by = c("site_city" = "guard_city")) %>%
|
| 307 |
+
mutate(
|
| 308 |
+
available_guards = ifelse(is.na(available_guards), 0L, available_guards),
|
| 309 |
+
shortage = pmax(required_guards - available_guards, 0L),
|
| 310 |
+
fulfillment_rate = round(available_guards / required_guards * 100, 0)
|
| 311 |
)
|
| 312 |
+
} else {
|
| 313 |
+
radius <- input$radius_km
|
| 314 |
+
if (is.null(radius) || is.na(radius) || radius <= 0) radius <- 20
|
| 315 |
+
|
| 316 |
+
if (nrow(gds) == 0) {
|
| 317 |
+
result <- sites %>%
|
| 318 |
+
mutate(
|
| 319 |
+
available_guards = 0L,
|
| 320 |
+
shortage = required_guards,
|
| 321 |
+
fulfillment_rate = 0
|
| 322 |
+
)
|
| 323 |
+
} else {
|
| 324 |
+
result <- sites %>%
|
| 325 |
+
rowwise() %>%
|
| 326 |
+
mutate(
|
| 327 |
+
available_guards = {
|
| 328 |
+
dist <- haversine_km(site_lat, site_lng, gds$home_lat, gds$home_lng)
|
| 329 |
+
sum(dist <= radius, na.rm = TRUE)
|
| 330 |
+
}
|
| 331 |
+
) %>%
|
| 332 |
+
ungroup() %>%
|
| 333 |
+
mutate(
|
| 334 |
+
shortage = pmax(required_guards - available_guards, 0L),
|
| 335 |
+
fulfillment_rate = round(available_guards / required_guards * 100, 0)
|
| 336 |
+
)
|
| 337 |
}
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
# 色と状態を追加
|
| 341 |
+
result <- result %>%
|
| 342 |
+
mutate(
|
| 343 |
+
fill_color = get_fill_color(fulfillment_rate),
|
| 344 |
+
status = case_when(
|
| 345 |
+
fulfillment_rate < THRESHOLD_LOW ~ "⚠️ 要営業強化",
|
| 346 |
+
fulfillment_rate < THRESHOLD_HIGH ~ "△ 注意",
|
| 347 |
+
TRUE ~ "○ 充足"
|
| 348 |
+
)
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
result
|
| 352 |
+
})
|
| 353 |
+
|
| 354 |
+
# 表示用テーブル
|
| 355 |
+
output$summary <- renderTable({
|
| 356 |
+
tbl <- summary_tbl()
|
| 357 |
+
if ("メッセージ" %in% colnames(tbl)) return(tbl)
|
| 358 |
+
|
| 359 |
+
tbl %>%
|
| 360 |
+
transmute(
|
| 361 |
+
契約ID = contract_id,
|
| 362 |
+
契約No = `契約No`,
|
| 363 |
+
顧客 = `顧客`,
|
| 364 |
+
件名 = paste0(`件名1`, ifelse(is.na(`件名2`) | trimws(`件名2`) == "", "", paste0(" ", `件名2`))),
|
| 365 |
+
市区町村 = site_city,
|
| 366 |
+
必要人数 = required_guards,
|
| 367 |
+
対応可能 = as.integer(available_guards),
|
| 368 |
+
充足率 = paste0(fulfillment_rate, "%"),
|
| 369 |
+
状態 = status
|
| 370 |
+
)
|
| 371 |
+
})
|
| 372 |
+
|
| 373 |
+
# サマリー統計
|
| 374 |
+
output$stat_sites <- renderText({
|
| 375 |
+
dd <- daily()
|
| 376 |
+
paste0(nrow(dd$active_sites), " 件")
|
| 377 |
+
})
|
| 378 |
+
|
| 379 |
+
output$stat_required <- renderText({
|
| 380 |
+
tbl <- summary_tbl()
|
| 381 |
+
if ("メッセージ" %in% colnames(tbl)) return("-")
|
| 382 |
+
paste0(sum(tbl$required_guards, na.rm = TRUE), " 人")
|
| 383 |
+
})
|
| 384 |
+
|
| 385 |
+
output$stat_available <- renderText({
|
| 386 |
+
dd <- daily()
|
| 387 |
+
paste0(nrow(dd$available_guards), " 人")
|
| 388 |
+
})
|
| 389 |
+
|
| 390 |
+
output$stat_avg_rate <- renderText({
|
| 391 |
+
tbl <- summary_tbl()
|
| 392 |
+
if ("メッセージ" %in% colnames(tbl)) return("-")
|
| 393 |
+
avg_rate <- mean(tbl$fulfillment_rate, na.rm = TRUE)
|
| 394 |
+
paste0(round(avg_rate, 0), "%")
|
| 395 |
+
})
|
| 396 |
+
|
| 397 |
+
# 地図(初回のみ)
|
| 398 |
+
output$map <- renderLeaflet({
|
| 399 |
+
center_lat <- mean(c(BBOX$lat_min, BBOX$lat_max))
|
| 400 |
+
center_lng <- mean(c(BBOX$lng_min, BBOX$lng_max))
|
| 401 |
+
|
| 402 |
+
leaflet() %>%
|
| 403 |
+
addTiles() %>%
|
| 404 |
+
setView(lng = center_lng, lat = center_lat, zoom = 9) %>%
|
| 405 |
+
addLayersControl(
|
| 406 |
+
overlayGroups = c("現場(充足率)", "アベイラブル隊員"),
|
| 407 |
+
options = layersControlOptions(collapsed = FALSE)
|
| 408 |
+
)
|
| 409 |
+
})
|
| 410 |
+
|
| 411 |
+
# 地図更新(マーカー)
|
| 412 |
+
observe({
|
| 413 |
+
dd <- daily()
|
| 414 |
+
sites_data <- summary_tbl()
|
| 415 |
+
gds <- dd$available_guards
|
| 416 |
+
|
| 417 |
+
proxy <- leafletProxy("map")
|
| 418 |
+
proxy %>%
|
| 419 |
+
clearGroup("現場(充足率)") %>%
|
| 420 |
+
clearGroup("アベイラブル隊員")
|
| 421 |
+
|
| 422 |
+
# 現場マーカー(充足率付き)
|
| 423 |
+
if (!("メッセージ" %in% colnames(sites_data)) && nrow(sites_data) > 0) {
|
| 424 |
+
# CircleMarkersを追加
|
| 425 |
+
proxy %>%
|
| 426 |
+
addCircleMarkers(
|
| 427 |
+
data = sites_data,
|
| 428 |
+
lng = ~site_lng, lat = ~site_lat,
|
| 429 |
+
radius = 14,
|
| 430 |
+
color = ~fill_color,
|
| 431 |
+
fillColor = ~fill_color,
|
| 432 |
+
fillOpacity = 0.85,
|
| 433 |
+
weight = 2,
|
| 434 |
+
label = ~paste0(
|
| 435 |
+
"【", `契約No`, "】", `件名1`,
|
| 436 |
+
" | 充足率: ", fulfillment_rate, "%",
|
| 437 |
+
" | 必要: ", required_guards, "人",
|
| 438 |
+
" | 対応可: ", available_guards, "人"
|
| 439 |
+
),
|
| 440 |
+
group = "現場(充足率)"
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
# 充足率ラベルを個別に追加(色を動的に設定するため)
|
| 444 |
+
for (i in seq_len(nrow(sites_data))) {
|
| 445 |
+
row <- sites_data[i, ]
|
| 446 |
+
proxy %>%
|
| 447 |
+
addLabelOnlyMarkers(
|
| 448 |
+
lng = row$site_lng,
|
| 449 |
+
lat = row$site_lat,
|
| 450 |
+
label = paste0(row$fulfillment_rate, "%"),
|
| 451 |
+
labelOptions = labelOptions(
|
| 452 |
+
noHide = TRUE,
|
| 453 |
+
direction = "top",
|
| 454 |
+
textOnly = TRUE,
|
| 455 |
+
style = list(
|
| 456 |
+
"font-weight" = "bold",
|
| 457 |
+
"font-size" = "11px",
|
| 458 |
+
"color" = "white",
|
| 459 |
+
"background-color" = row$fill_color,
|
| 460 |
+
"padding" = "2px 5px",
|
| 461 |
+
"border-radius" = "4px"
|
| 462 |
+
)
|
| 463 |
+
),
|
| 464 |
+
group = "現場(充足率)"
|
| 465 |
+
)
|
| 466 |
+
}
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
# アベイラブル隊員マーカー
|
| 470 |
+
# 「従業員番号」→「ユーザー番号」に変更
|
| 471 |
+
if (isTRUE(input$show_guards) && nrow(gds) > 0) {
|
| 472 |
+
proxy %>%
|
| 473 |
+
addCircleMarkers(
|
| 474 |
+
data = gds,
|
| 475 |
+
lng = ~home_lng, lat = ~home_lat,
|
| 476 |
+
radius = 6,
|
| 477 |
+
color = "#3498db",
|
| 478 |
+
fillColor = "#3498db",
|
| 479 |
+
fillOpacity = 0.6,
|
| 480 |
+
weight = 1,
|
| 481 |
+
label = ~paste0("ユーザー番号: ", ユーザー番号, " | ", 苗字, " ", 名前, " | ", guard_city),
|
| 482 |
+
group = "アベイラブル隊員"
|
| 483 |
+
)
|
| 484 |
+
}
|
| 485 |
+
})
|
| 486 |
}
|
| 487 |
|
| 488 |
shinyApp(ui, server)
|