実践・連作障害回避スケジューリングツールを作る④

2021-06-19

GAS ツール プログラミング

B!

限られた農地を上手に活用する方法をプログラム側から考える。

ここまでの記事 プログラムの続きを書いてとりあえずの完成を目指します!




今回のソースファイル全文

今回の追加分です。

それぞれ、前回作成したコードファイルに追加していきます。
「~以下に追加」となっていますが、基本どこでもOKです。

define.gsに追加
  1. // -------------------------------------------------------------------------------------------------------
  2. // tmpシート.
  3. // -------------------------------------------------------------------------------------------------------
  4. var TmpDataStartX = 3; // 開始 X.
  5. var TmpDataStartY = 3; // 開始 Y.
  6. var TmpYearX = 2; // 年度 X.
main.gsに追加
  1. // -------------------------------------------------------------------------------------------------------
  2. // エントリーポイント以下に追加.
  3. // -------------------------------------------------------------------------------------------------------
  4. // -------------------------------------------------------------------------------------------------------
  5. // 「作物決定」ボタンを押した.
  6. // -------------------------------------------------------------------------------------------------------
  7. function PushEntry() {
  8. // シートの取得.
  9. var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  10. var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );
  11. var defineSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameDefine );
  12. if ( mngSheet === null ) { return; }
  13. if ( dataSheet === null ) { return; }
  14. if ( defineSheet === null ) { return; }
  15. // 作物名が存在するかどうかチェック.
  16. var dataID = SearchDataSheet( dataSheet );
  17. if ( dataID === -1 ) { return; }
  18. var dataIdx = GetLastRow( mngSheet, MngListIDX ) + 1;
  19. var num, tmp;
  20. // ID.
  21. mngSheet.getRange( dataIdx, MngListIDX ).setValue( dataID );
  22. // 作物名.
  23. mngSheet.getRange( dataIdx, MngListNameX ).setValue(
  24. dataSheet.getRange( dataID, DataNameX ).getValue() );
  25. // 科目.
  26. mngSheet.getRange( dataIdx, MngListSubjectX ).setValue(
  27. GetDataToSubject( dataSheet, defineSheet, dataID, DefineVarietySubjectX ) );
  28.  
  29. // 作業開始.
  30. num = dataSheet.getRange( dataID, DataSowingSeedStartX ).getValue();
  31. tmp = dataSheet.getRange( dataID, DataSowingSeedResultX ).getValue();
  32. if ( num === 0 ) {
  33. // 種まき作業がないので定植で考える.
  34. num = dataSheet.getRange( dataID, DataPlantingStartX ).getValue();
  35. tmp = dataSheet.getRange( dataID, DataPlantingResultX ).getValue();
  36. mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( false );
  37. } else {
  38. mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( true );
  39. }
  40. mngSheet.getRange( dataIdx, MngListWorkStartX ).setValue( num + '月' );
  41.  
  42. // 収穫.
  43. mngSheet.getRange( dataIdx, MngListWorkEndX ).setValue(
  44. dataSheet.getRange( dataID, DataHarvestStartX ).getValue() + '月' );
  45.  
  46. // 作業終了週.
  47. mngSheet.getRange( dataIdx, MngListWorkEndWeekX ).setValue(
  48. dataSheet.getRange( dataID, DataCultivationPeriodEndX ).getValue() );
  49.  
  50. // 輪作年限BASE.
  51. mngSheet.getRange( dataIdx, MngListCropRotX ).setValue(
  52. GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  53. // 輪作年限残り.
  54. mngSheet.getRange( dataIdx, MngListCropRotRestX ).setValue(
  55. GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  56. // カラ期間.
  57. if ( dataIdx !== MngListStartY ) {
  58. // 1個前の作業終了週をget.
  59. num = mngSheet.getRange( (dataIdx - 1), MngListWorkEndWeekX ).getValue();
  60. // 選択した作物の作業開始週と比較.
  61. if ( num < tmp ) {
  62. // カラ期間計算.
  63. num = tmp - num;
  64. } else {
  65. num = OneYearToWeeks + (tmp - num);
  66. }
  67.  
  68. // カラ期間.
  69. mngSheet.getRange( dataIdx, MngListIntervalX ).setValue( num );
  70. // 前の輪作年限を全部更新.
  71. num = num + dataSheet.getRange( dataID, DataCultivationPeriodResultX ).getValue(); // カラ期間+栽培期間(ざっくり).
  72. for ( var y = MngListStartY; y < dataIdx; ++y ) {
  73. tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
  74. if ( tmp === 0 ) { continue; }
  75. tmp = tmp - num;
  76. if ( tmp < 0 ) { tmp = 0; }
  77. mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  78. }
  79. }
  80. return;
  81. }
  82.  
  83. // -------------------------------------------------------------------------------------------------------
  84. // 「スケジュール作成」ボタンを押した.
  85. // -------------------------------------------------------------------------------------------------------
  86. function PushCreateSchedule() {
  87. // シートの取得.
  88. var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  89. var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );
  90. var defineSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameDefine );
  91. if ( mngSheet === null ) { return; }
  92. if ( dataSheet === null ) { return; }
  93. if ( defineSheet === null ) { return; }
  94. // 指定シート名が存在するか.
  95. var newSheetName = mngSheet.getRange( MngConfigSheetName ).getValue();
  96. var newSheet = GetSheet( newSheetName );
  97. if ( newSheet !== null ) {
  98. // シートを削除する.
  99. DeleteSheet( newSheet );
  100. }
  101. // 新たに作成.
  102. newSheet = CopySheet( SheetNameTmp, newSheetName );
  103. // 作物リストを頭から解釈してく.
  104. var dataEnd = GetLastRow( mngSheet, MngListIDX ) + 1;
  105. var year = 0, ID, isSeed, start, end, index = 1, col, name;
  106. // ~年目入力.
  107. DrawYear( newSheet, (year + 1) );
  108. for ( var y = MngListStartY; y < dataEnd; ++y ) {
  109. // ID.
  110. ID = mngSheet.getRange( y, MngListIDX ).getValue();
  111. // 種まきか否か.
  112. isSeed = mngSheet.getRange( y, MngListIsSowingSeedX ).getValue();
  113. // 作業終了週.
  114. end = dataSheet.getRange( ID, DataCultivationPeriodEndX ).getValue();
  115. // 色.
  116. col = GetBGColorToSubject( defineSheet, mngSheet.getRange( y, MngListSubjectX ).getValue() );
  117. // 名前.
  118. name = mngSheet.getRange( y, MngListNameX ).getValue();
  119. if ( isSeed === true ) {
  120. // 種まき開始週.
  121. start = dataSheet.getRange( ID, DataSowingSeedResultX ).getValue();
  122. } else {
  123. // 定植開始週.
  124. start = dataSheet.getRange( ID, DataPlantingResultX ).getValue();
  125. }
  126. // 色塗り開始.
  127. if ( y !== MngListStartY ) {
  128. if ( index >= start ) {
  129. // 1年ずらす.
  130. ++year;
  131. // ~年目入力.
  132. DrawYear( newSheet, (year + 1) );
  133. }
  134. }
  135.  
  136. index = start;
  137.  
  138. // 名前入力.
  139. newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setValue( name );
  140. while ( true ) {
  141. // 終了チェック.
  142. if ( index === end ) { break; }
  143. // 色塗り.
  144. newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setBackground( col );
  145. ++index;
  146. if ( index > OneYearToWeeks ) {
  147. // 年数を増やす.
  148. ++year;
  149. // ~年目入力.
  150. DrawYear( newSheet, (year + 1) );
  151. index = 1;
  152. }
  153. }
  154. }
  155. return;
  156. }
  157.  
  158. // -------------------------------------------------------------------------------------------------------
  159. // 「1つ消す」ボタンを押した.
  160. // -------------------------------------------------------------------------------------------------------
  161. function PushOnePrev() {
  162. // シートの取得.
  163. var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  164. var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );
  165.  
  166. if ( mngSheet === null ) { return; }
  167. if ( dataSheet === null ) { return; }
  168. var endY = GetLastRow( mngSheet, MngListIDX );
  169. if ( endY < MngListStartY ) { return; }
  170. // 消してしまうカラ期間 + 栽培期間.
  171. var baseNum, tmp;
  172. var num =
  173. mngSheet.getRange( endY, MngListIntervalX ).getValue() +
  174. dataSheet.getRange( mngSheet.getRange( endY, MngListIDX ).getValue(), DataCultivationPeriodResultX ).getValue();
  175. RangeClear( mngSheet, MngListIDX, endY, 10, 1 );
  176. // 輪作年限を再設定.
  177. for ( var y = MngListStartY; y < endY; ++y ) {
  178. baseNum = mngSheet.getRange( y, MngListCropRotX ).getValue();
  179. if ( baseNum === 0 ) { continue; }// 輪作年限がもともとない作物はスキップ.
  180. tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
  181. tmp = tmp + num;
  182. // base以上にならないようにする.
  183. if ( tmp > baseNum ) {
  184. tmp = baseNum;
  185. }
  186. mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  187. }
  188. return;
  189. }
  190.  
  191. // -------------------------------------------------------------------------------------------------------
  192. // 「全部消す」ボタンを押した.
  193. // -------------------------------------------------------------------------------------------------------
  194. function PushAllDelete() {
  195. // シートの取得.
  196. var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  197.  
  198. if ( mngSheet === null ) { return; }
  199. RangeClear( mngSheet, MngListIDX, MngListStartY, 10, 0 );
  200. return;
  201. }
  202.  
  203. // -------------------------------------------------------------------------------------------------------
  204. // private以下に追加.
  205. // -------------------------------------------------------------------------------------------------------
  206. // -------------------------------------------------------------------------------------------------------
  207. // 現在のスプレッドシートの中の指定の名前のシートを取得.
  208. // -------------------------------------------------------------------------------------------------------
  209. function GetSheet( sheetName ) {
  210. return SpreadsheetApp.getActive().getSheetByName( sheetName );
  211. }
  212. // -------------------------------------------------------------------------------------------------------
  213. // スプレッドシートを削除.
  214. // -------------------------------------------------------------------------------------------------------
  215. function DeleteSheet( sheet ) {
  216. SpreadsheetApp.getActive().deleteSheet( sheet );
  217. return;
  218. }
  219. // -------------------------------------------------------------------------------------------------------
  220. // スプレッドシートをコピー作成&リネーム.
  221. // -------------------------------------------------------------------------------------------------------
  222. function CopySheet( copySheetName, newSheetName ) {
  223. var copy = GetSheet( copySheetName );
  224. if ( copy === null ) { return null; }// 失敗.
  225. var newSheet = copy.copyTo( SpreadsheetApp.getActive() );
  226. if ( newSheet === null ) { return null; }//失敗.
  227. // リネーム.
  228. newSheet.setName( newSheetName );
  229. return newSheet;
  230. }
  231. // -------------------------------------------------------------------------------------------------------
  232. // ~年目入力.
  233. // -------------------------------------------------------------------------------------------------------
  234. function DrawYear( tmpSheet, year ) {
  235. tmpSheet.getRange( ((TmpDataStartY - 1) + year), TmpYearX ).setValue( year + '年目' );
  236. return;
  237. }
  238. // -------------------------------------------------------------------------------------------------------
  239. // 科目名の背景カラーを取得.
  240. // -------------------------------------------------------------------------------------------------------
  241. function GetBGColorToSubject( defineSheet, subjectName ) {
  242. var endIdx = GetLastRow( defineSheet, DefineSubjectX ) + 1;
  243.  
  244. var ranges = defineSheet.getRange( DefineDataStartY, DefineSubjectX, endIdx, 1 ).createTextFinder( subjectName ).findAll();
  245.  
  246. if ( ranges.length === 0 ) { return '#ffffff'; }
  247. return ranges[0].getBackground();
  248. }

【解説】「作物決定」ボタンの処理実装

18~62行目

  1. // 作物名が存在するかどうかチェック.
  2. var dataID = SearchDataSheet( dataSheet );
  3. if ( dataID === -1 ) { return; }
  4. var dataIdx = GetLastRow( mngSheet, MngListIDX ) + 1;
  5. var num, tmp;
  6. // ID.
  7. mngSheet.getRange( dataIdx, MngListIDX ).setValue( dataID );
  8. // 作物名.
  9. mngSheet.getRange( dataIdx, MngListNameX ).setValue(
  10. dataSheet.getRange( dataID, DataNameX ).getValue() );
  11. // 科目.
  12. mngSheet.getRange( dataIdx, MngListSubjectX ).setValue(
  13. GetDataToSubject( dataSheet, defineSheet, dataID, DefineVarietySubjectX ) );
  14.  
  15. // 作業開始.
  16. num = dataSheet.getRange( dataID, DataSowingSeedStartX ).getValue();
  17. tmp = dataSheet.getRange( dataID, DataSowingSeedResultX ).getValue();
  18. if ( num === 0 ) {
  19. // 種まき作業がないので定植で考える.
  20. num = dataSheet.getRange( dataID, DataPlantingStartX ).getValue();
  21. tmp = dataSheet.getRange( dataID, DataPlantingResultX ).getValue();
  22. mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( false );
  23. } else {
  24. mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( true );
  25. }
  26. mngSheet.getRange( dataIdx, MngListWorkStartX ).setValue( num + '月' );
  27.  
  28. // 収穫.
  29. mngSheet.getRange( dataIdx, MngListWorkEndX ).setValue(
  30. dataSheet.getRange( dataID, DataHarvestStartX ).getValue() + '月' );
  31.  
  32. // 作業終了週.
  33. mngSheet.getRange( dataIdx, MngListWorkEndWeekX ).setValue(
  34. dataSheet.getRange( dataID, DataCultivationPeriodEndX ).getValue() );
  35.  
  36. // 輪作年限BASE.
  37. mngSheet.getRange( dataIdx, MngListCropRotX ).setValue(
  38. GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  39. // 輪作年限残り.
  40. mngSheet.getRange( dataIdx, MngListCropRotRestX ).setValue(
  41. GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
選択中のセルの作物名から「data」シートを検索し、縦列をIDとして利用しています。
(別途IDをつけたい場合はdataシートを改造)

その後IDを使用し必要なデータを取り出しています。

64~77行目

  1. // カラ期間.
  2. if ( dataIdx !== MngListStartY ) {
  3. // 1個前の作業終了週をget.
  4. num = mngSheet.getRange( (dataIdx - 1), MngListWorkEndWeekX ).getValue();
  5. // 選択した作物の作業開始週と比較.
  6. if ( num < tmp ) {
  7. // カラ期間計算.
  8. num = tmp - num;
  9. } else {
  10. num = OneYearToWeeks + (tmp - num);
  11. }
  12.  
  13. // カラ期間.
  14. mngSheet.getRange( dataIdx, MngListIntervalX ).setValue( num );
このツールでは「カラ期間」を計算しています。

…勝手に命名してますので好きに呼んでやってください。

ここで言うカラ期間は選択した作物間の空きの期間(週数)です。
スケジュールの作成がしやすいように保持しています。

79~87行目

  1. // 前の輪作年限を全部更新.
  2. num = num + dataSheet.getRange( dataID, DataCultivationPeriodResultX ).getValue(); // カラ期間+栽培期間(ざっくり).
  3. for ( var y = MngListStartY; y < dataIdx; ++y ) {
  4. tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
  5. if ( tmp === 0 ) { continue; }
  6. tmp = tmp - num;
  7. if ( tmp < 0 ) { tmp = 0; }
  8. mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  9. }
  10.  
カラ期間の計算ののち、現在選択中の作物の輪作年限数の消化をしています。
コメントにも書かれていますが、(ざっくり)してます。

【解説】「スケジュール作成」ボタンの処理実装

126~179行目

  1. for ( var y = MngListStartY; y < dataEnd; ++y ) {
  2. // ID.
  3. ID = mngSheet.getRange( y, MngListIDX ).getValue();
  4. // 種まきか否か.
  5. isSeed = mngSheet.getRange( y, MngListIsSowingSeedX ).getValue();
  6. // 作業終了週.
  7. end = dataSheet.getRange( ID, DataCultivationPeriodEndX ).getValue();
  8. // 色.
  9. col = GetBGColorToSubject( defineSheet, mngSheet.getRange( y, MngListSubjectX ).getValue() );
  10. // 名前.
  11. name = mngSheet.getRange( y, MngListNameX ).getValue();
  12. if ( isSeed === true ) {
  13. // 種まき開始週.
  14. start = dataSheet.getRange( ID, DataSowingSeedResultX ).getValue();
  15. } else {
  16. // 定植開始週.
  17. start = dataSheet.getRange( ID, DataPlantingResultX ).getValue();
  18. }
  19. // 色塗り開始.
  20. if ( y !== MngListStartY ) {
  21. if ( index >= start ) {
  22. // 1年ずらす.
  23. ++year;
  24. // ~年目入力.
  25. DrawYear( newSheet, (year + 1) );
  26. }
  27. }
  28.  
  29. index = start;
  30.  
  31. // 名前入力.
  32. newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setValue( name );
  33. while ( true ) {
  34. // 終了チェック.
  35. if ( index === end ) { break; }
  36. // 色塗り.
  37. newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setBackground( col );
  38. ++index;
  39. if ( index > OneYearToWeeks ) {
  40. // 年数を増やす.
  41. ++year;
  42. // ~年目入力.
  43. DrawYear( newSheet, (year + 1) );
  44. index = 1;
  45. }
  46. }
  47. }
このツールのキモと言っても過言ではない部分です。

作業期間部分のみを色塗りしていきます。色は「define」シートのセルの背景色から取得しています。

【解説】「1つ消す」「全部消す」ボタンの処理実装

200~223行目

  1. // 消してしまうカラ期間 + 栽培期間.
  2. var baseNum, tmp;
  3. var num =
  4. mngSheet.getRange( endY, MngListIntervalX ).getValue() +
  5. dataSheet.getRange( mngSheet.getRange( endY, MngListIDX ).getValue(), DataCultivationPeriodResultX ).getValue();
  6. RangeClear( mngSheet, MngListIDX, endY, 10, 1 );
  7. // 輪作年限を再設定.
  8. for ( var y = MngListStartY; y < endY; ++y ) {
  9. baseNum = mngSheet.getRange( y, MngListCropRotX ).getValue();
  10. if ( baseNum === 0 ) { continue; }// 輪作年限がもともとない作物はスキップ.
  11. tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
  12. tmp = tmp + num;
  13. // base以上にならないようにする.
  14. if ( tmp > baseNum ) {
  15. tmp = baseNum;
  16. }
  17. mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  18. }
作物リストから1つ消す場合は残り輪作年限をもとに戻さないといけません。

改善点など

ソースを見直して、軽くできそうな箇所は軽くしたいですね(;'∀')

ざっくりスケジュールなので、リアルベースでツールを考えてみる?

スケジュールを作成する時の文字色も「define」シートから引っ張っても良いと思います。

…などなど、色々手を加えられそうな部分は山ほどありそうです。

これで一旦完成という事になります!長丁場お疲れ様です。
こんな感じでプログラムと農業、色々とできそうな気がして、考えるだけでワクワクしませんか?(`・ω・´)ノ
私だけか…?



ブログランキング・にほんブログ村へ

Translate

魔王と愉快な仲間

このブログを検索

ラベル

ブログ アーカイブ

メッセージ

名前

メール *

メッセージ *

Powered by Blogger.

フリー写真素材ぱくたそ

QooQ